diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f34e060..6ba74d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,3 +47,5 @@ jobs: npm run benchmark:uploads npm run benchmark:transfer-queue npm run benchmark:devtools + - name: Combined workload regression guard + run: npm run benchmark:combined diff --git a/API_SUPPORT.json b/API_SUPPORT.json new file mode 100644 index 0000000..f593182 --- /dev/null +++ b/API_SUPPORT.json @@ -0,0 +1,1382 @@ +{ + "schemaVersion": 1, + "sdkVersion": "1.0.0-rc.1", + "releaseStatus": "candidate", + "pluginPeerRange": "1.0.0-rc.1", + "policy": "docs/SUPPORT_POLICY.md", + "exports": [ + { + "entry": ".", + "stability": "stable", + "availability": "library", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "CODE_ACTION_KINDS", + "ErrorCode", + "LANGUAGE_CAPABILITIES", + "LANGUAGE_DISPLAY_LIMITS", + "LANGUAGE_EDIT_LIMITS", + "LANGUAGE_FEATURES", + "LANGUAGE_LIMITS", + "LIMITS", + "PROTOCOL_VERSION", + "PluginSdkError", + "SEMANTIC_STYLE_KEYS", + "applySemanticTokensDelta", + "createDiagnosticsRegistry", + "createDiagnosticsResult", + "createLanguageRegistry", + "createLanguageResult", + "createPluginHost", + "decodeSemanticTokens", + "definePlugin", + "parseCodeAction", + "parseCommandDefinition", + "parseCommandInput", + "parseCommandOutput", + "parseCompletionItem", + "parseDiagnosticsRequest", + "parseDiagnosticsResult", + "parseDocumentSnapshot", + "parseDocumentSymbolTree", + "parseFoldingRanges", + "parseFormattingOptions", + "parseInlayHints", + "parseJsonValue", + "parseLanguageRequest", + "parseLanguageResult", + "parseLicenseExpression", + "parseManifest", + "parseSemanticLegend", + "parseSemanticTokens", + "parseSemanticTokensDelta", + "parseSignatureHelp", + "parseSnippet", + "parseWorkspacePath", + "validateSemanticTokens" + ], + "declarationNames": [ + "BackendHandler", + "BackendsApi", + "CODE_ACTION_KINDS", + "CodeAction", + "CodeActionContext", + "CodeActionKind", + "CommandDefinition", + "CommandHandler", + "CommandParameter", + "CompletionContext", + "CompletionInsertion", + "CompletionItem", + "DecodedSemanticToken", + "Diagnostic", + "DiagnosticsProvider", + "DiagnosticsRegistry", + "DiagnosticsRequest", + "DiagnosticsResult", + "Disposable", + "DocumentSnapshot", + "DocumentSymbol", + "DocumentSymbolTree", + "ErrorCode", + "FoldingRange", + "FormattingFeature", + "FormattingOptions", + "HostId", + "HostInspection", + "HostRuntime", + "Hover", + "InlayHint", + "JobArtifactContent", + "JobCapabilities", + "JobEvents", + "JobOptions", + "JobReporter", + "JobSnapshot", + "JsonValue", + "LANGUAGE_CAPABILITIES", + "LANGUAGE_DISPLAY_LIMITS", + "LANGUAGE_EDIT_LIMITS", + "LANGUAGE_FEATURES", + "LANGUAGE_LIMITS", + "LIMITS", + "LanguageCapabilities", + "LanguageCapability", + "LanguageData", + "LanguageDisplayFeature", + "LanguageFeature", + "LanguageInput", + "LanguageLocation", + "LanguageProvider", + "LanguageRegistry", + "LanguageRequest", + "LanguageResult", + "PROTOCOL_VERSION", + "ParsedSnippet", + "Permission", + "Plugin", + "PluginContext", + "PluginErrorCode", + "PluginHost", + "PluginHostOptions", + "PluginManifest", + "PluginManifestV1", + "PluginManifestV2", + "PluginSdkError", + "Position", + "PreviousSemanticTokens", + "ProviderSelector", + "Range", + "RegisteredCommand", + "RenamePreparation", + "RequestOptions", + "SEMANTIC_STYLE_KEYS", + "Scope", + "SemanticLegend", + "SemanticStyle", + "SemanticTokens", + "SemanticTokensDelta", + "SignatureHelp", + "SignatureHelpContext", + "SignatureInformation", + "SnapshotIdentity", + "WorkspaceApi", + "WorkspaceEntry", + "WorkspaceFile", + "WorkspacePort", + "WorkspaceWriteResult", + "applySemanticTokensDelta", + "createDiagnosticsRegistry", + "createDiagnosticsResult", + "createLanguageRegistry", + "createLanguageResult", + "createPluginHost", + "decodeSemanticTokens", + "definePlugin", + "parseCodeAction", + "parseCommandDefinition", + "parseCommandInput", + "parseCommandOutput", + "parseCompletionItem", + "parseDiagnosticsRequest", + "parseDiagnosticsResult", + "parseDocumentSnapshot", + "parseDocumentSymbolTree", + "parseFoldingRanges", + "parseFormattingOptions", + "parseInlayHints", + "parseJsonValue", + "parseLanguageRequest", + "parseLanguageResult", + "parseLicenseExpression", + "parseManifest", + "parseSemanticLegend", + "parseSemanticTokens", + "parseSemanticTokensDelta", + "parseSignatureHelp", + "parseSnippet", + "parseWorkspacePath", + "validateSemanticTokens" + ], + "declarationsSha256": "cd22225a003e141ad65cfd131d410732ed0368315d45c7cd601333362f28ff4b" + }, + { + "entry": "./accessibility", + "stability": "stable", + "availability": "library", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "contrastRatio", + "inspectAccessibility", + "inspectThemeContrast", + "normalizeShortcut" + ], + "declarationNames": [ + "ThemeContrastCheck", + "contrastRatio", + "inspectAccessibility", + "inspectThemeContrast", + "normalizeShortcut" + ], + "declarationsSha256": "7dc49297fdf2a992360039f05d3735b4b40af511739085c08a9216f867c7ba35" + }, + { + "entry": "./artifact-resume-browser", + "stability": "experimental", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "BROWSER_ARTIFACT_RESUME_LIMITS", + "DEFAULT_BROWSER_ARTIFACT_RETENTION_MS", + "createOpfsArtifactSink", + "getBrowserArtifactResumeSupport", + "getOpfsArtifactPartialNames", + "parseBrowserArtifactCheckpoint" + ], + "declarationNames": [ + "BROWSER_ARTIFACT_RESUME_LIMITS", + "BrowserArtifactCheckpoint", + "BrowserCheckpointLocks", + "DEFAULT_BROWSER_ARTIFACT_RETENTION_MS", + "OpfsArtifactSink", + "OpfsArtifactSinkOptions", + "OpfsCheckpointDirectory", + "OpfsCheckpointFileHandle", + "OpfsSyncAccessHandle", + "createOpfsArtifactSink", + "getBrowserArtifactResumeSupport", + "getOpfsArtifactPartialNames", + "parseBrowserArtifactCheckpoint" + ], + "declarationsSha256": "76441e081e78bfbb391c0e7f12e1288aa3ff8cd2738cb7e4961a8d972a2c374b" + }, + { + "entry": "./artifact-storage", + "stability": "stable", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "ARTIFACT_STORE_LIMITS", + "DEFAULT_ARTIFACT_STORE_BYTES", + "parseArtifactStorageCapabilities", + "parseArtifactStoreLimits", + "parseStoredArtifact", + "parseStoredArtifactChunk", + "parseStoredArtifactList", + "parseStoredArtifactReference" + ], + "declarationNames": [ + "ARTIFACT_STORE_LIMITS", + "ArtifactStorageCapabilities", + "ArtifactStore", + "ArtifactStoreLimits", + "DEFAULT_ARTIFACT_STORE_BYTES", + "StoredArtifact", + "StoredArtifactAvailability", + "StoredArtifactChunk", + "StoredArtifactList", + "StoredArtifactListEntry", + "StoredArtifactReference", + "parseArtifactStorageCapabilities", + "parseArtifactStoreLimits", + "parseStoredArtifact", + "parseStoredArtifactChunk", + "parseStoredArtifactList", + "parseStoredArtifactReference" + ], + "declarationsSha256": "baf33f117c97a85a35c3a2ccef98684115ed6792b1e76c555522dab4a9a132b5" + }, + { + "entry": "./artifact-storage-node", + "stability": "stable", + "availability": "optional", + "environments": [ + "node" + ], + "runtimeNames": [ + "createNodeArtifactStore" + ], + "declarationNames": [ + "NodeArtifactStore", + "NodeArtifactStoreOptions", + "createNodeArtifactStore" + ], + "declarationsSha256": "7da5ba978a2078951365d4544670379e3f074ade03f0f80976b81d0d2768efdd" + }, + { + "entry": "./artifact-transfer", + "stability": "stable", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "ARTIFACT_TRANSFER_LIMITS", + "ArtifactTransferError", + "createIncrementalSha256", + "parseArtifactSinkCapabilities", + "parseArtifactTransferReceipt", + "streamJobBinaryArtifact", + "streamStoredJobArtifact" + ], + "declarationNames": [ + "ARTIFACT_TRANSFER_LIMITS", + "ArtifactSink", + "ArtifactSinkCapabilities", + "ArtifactSinkReader", + "ArtifactSinkSession", + "ArtifactTransferError", + "ArtifactTransferMetrics", + "ArtifactTransferOptions", + "ArtifactTransferPartial", + "ArtifactTransferProgress", + "ArtifactTransferReceipt", + "BinaryArtifactClient", + "IncrementalSha256", + "StoredArtifactClient", + "createIncrementalSha256", + "parseArtifactSinkCapabilities", + "parseArtifactTransferReceipt", + "streamJobBinaryArtifact", + "streamStoredJobArtifact" + ], + "declarationsSha256": "98443ec979f56ab5a4be8e153c4ab9dc425af8d27f251f59439a7a70b8e13c8e" + }, + { + "entry": "./artifact-transfer-browser", + "stability": "stable", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "createBrowserFileSink", + "createOpfsFileSink", + "getBrowserArtifactStorageSupport" + ], + "declarationNames": [ + "BrowserDirectoryHandle", + "BrowserFileHandle", + "BrowserSinkOptions", + "createBrowserFileSink", + "createOpfsFileSink", + "getBrowserArtifactStorageSupport" + ], + "declarationsSha256": "44bae9160b1b8026b0feff25e473b7d74c6b99a0532fa50ca87d18b770a8efa9" + }, + { + "entry": "./artifacts", + "stability": "stable", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "BINARY_ARTIFACT_LIMITS", + "decodeBinaryArtifactData", + "parseBinaryArtifact", + "parseBinaryArtifactCapabilities", + "parseBinaryArtifactChunk", + "parseBinaryArtifactList", + "parseBinaryArtifactRange", + "parseBinaryArtifactReference", + "parseBinaryArtifactSource", + "parseBinaryChunk" + ], + "declarationNames": [ + "BINARY_ARTIFACT_LIMITS", + "BinaryArtifact", + "BinaryArtifactCapabilities", + "BinaryArtifactChunk", + "BinaryArtifactList", + "BinaryArtifactReference", + "BinaryArtifactSource", + "BinaryChunk", + "decodeBinaryArtifactData", + "parseBinaryArtifact", + "parseBinaryArtifactCapabilities", + "parseBinaryArtifactChunk", + "parseBinaryArtifactList", + "parseBinaryArtifactRange", + "parseBinaryArtifactReference", + "parseBinaryArtifactSource", + "parseBinaryChunk" + ], + "declarationsSha256": "40b8a3d52a39b6654336636ffa4367f5ccf26c0fa273f91cf2a852dc71d1c05b" + }, + { + "entry": "./conformance", + "stability": "stable", + "availability": "library", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "CONFORMANCE_CASES", + "CONFORMANCE_FEATURES", + "CONFORMANCE_LIMITS", + "formatConformanceReport", + "parseConformanceReport", + "runConformance" + ], + "declarationNames": [ + "CONFORMANCE_CASES", + "CONFORMANCE_FEATURES", + "CONFORMANCE_LIMITS", + "ConformanceAdapters", + "ConformanceFeature", + "ConformanceIdentity", + "ConformanceOptions", + "ConformanceReason", + "ConformanceReport", + "ConformanceResult", + "ConformanceStatus", + "ConformanceWorkspace", + "formatConformanceReport", + "parseConformanceReport", + "runConformance" + ], + "declarationsSha256": "02f271192b18770aa03985a84d97873f3a806d35759f516382ab8e5678a0a128" + }, + { + "entry": "./conformance-node", + "stability": "stable", + "availability": "library", + "environments": [ + "node" + ], + "runtimeNames": [ + "createConformanceWorkspace", + "createSdkConformanceOptions" + ], + "declarationNames": [ + "NodeConformanceWorkspace", + "createConformanceWorkspace", + "createSdkConformanceOptions" + ], + "declarationsSha256": "e9bd0fc921d067af363f75ca688c6a21d6e04f724732292b3d5e44c995b9a2a1" + }, + { + "entry": "./consent", + "stability": "stable", + "availability": "library", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "CONSENT_SCHEMA_VERSION", + "NOTICE_SCHEMA_VERSION", + "createConsentReceipt", + "createNotice", + "createPermissionGrant", + "isPermissionGranted", + "parseConsentReceipt", + "parseNotice", + "revokeConsentReceipt" + ], + "declarationNames": [ + "CONSENT_SCHEMA_VERSION", + "ConsentHost", + "ConsentPluginIdentity", + "ConsentReceipt", + "ConsentReceiptInput", + "ConsentRecordBase", + "ConsentScope", + "CreatePermissionGrantInput", + "ExpectedPermissionIdentity", + "NOTICE_SCHEMA_VERSION", + "Notice", + "NoticeAcknowledgementInput", + "NoticeInput", + "OptionalPersonalDataConsentInput", + "PermissionGrantInput", + "PermissionGrantReceipt", + "PersonalDataProcessing", + "PluginLicenseAcceptanceInput", + "PublisherAgreementAcceptanceInput", + "RevocationAction", + "createConsentReceipt", + "createNotice", + "createPermissionGrant", + "isPermissionGranted", + "parseConsentReceipt", + "parseNotice", + "revokeConsentReceipt" + ], + "declarationsSha256": "f3619110fd7590e96cfe23950e7101489a8d7e1f768d9e3406247b242681560a" + }, + { + "entry": "./data-schema", + "stability": "stable", + "availability": "library", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "DATA_SCHEMA_LIMITS", + "DataValidationError", + "collectSecretReferences", + "describeDataForm", + "parseDataSchema", + "parseDisplayMetadata", + "parseSecretReference", + "validateDataValue" + ], + "declarationNames": [ + "AccessibilityMetadata", + "DATA_SCHEMA_LIMITS", + "DataFormField", + "DataSchema", + "DataSchemaNode", + "DataValidationError", + "DisplayMetadata", + "SecretReference", + "collectSecretReferences", + "describeDataForm", + "parseDataSchema", + "parseDisplayMetadata", + "parseSecretReference", + "validateDataValue" + ], + "declarationsSha256": "5768fb4296ce33c61b848094be04dadd3e0c561c174aaa8f61219a326ab44e49" + }, + { + "entry": "./devtools", + "stability": "stable", + "availability": "library", + "environments": [ + "node" + ], + "runtimeNames": [ + "DEVTOOLS_LIMITS", + "PLUGIN_TEMPLATES", + "doctorPlugin", + "generatePluginContracts", + "initPlugin", + "parseDevelopmentDefinition", + "planPlugin", + "runPluginDev" + ], + "declarationNames": [ + "DEVTOOLS_LIMITS", + "DevEvent", + "DevelopmentDefinition", + "DoctorCheck", + "DoctorReport", + "GeneratedFile", + "GenerationReport", + "InitPluginOptions", + "InitializedPlugin", + "PLUGIN_TEMPLATES", + "PluginPlan", + "PluginTemplate", + "doctorPlugin", + "generatePluginContracts", + "initPlugin", + "parseDevelopmentDefinition", + "planPlugin", + "runPluginDev" + ], + "declarationsSha256": "301f020f4f2dd5b69f8540b38415aea8d634f32b7210a28ba968dea35a3079ad" + }, + { + "entry": "./diagnostics", + "stability": "stable", + "availability": "library", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "DIAGNOSTIC_LIMITS", + "DIAGNOSTIC_OPERATIONS", + "DIAGNOSTIC_RESOURCES", + "createDiagnosticSession", + "parseDiagnosticReport", + "profileHost", + "profileTransport" + ], + "declarationNames": [ + "DIAGNOSTIC_LIMITS", + "DIAGNOSTIC_OPERATIONS", + "DIAGNOSTIC_RESOURCES", + "DiagnosticEnd", + "DiagnosticErrorCode", + "DiagnosticEvent", + "DiagnosticOperation", + "DiagnosticReport", + "DiagnosticResource", + "DiagnosticSession", + "DiagnosticSpan", + "DiagnosticStart", + "DiagnosticStatus", + "createDiagnosticSession", + "parseDiagnosticReport", + "profileHost", + "profileTransport" + ], + "declarationsSha256": "6a9a233c2b1a13fed34bdf0e1865d371b8c5992da2e9747dd2ee55191ead45c9" + }, + { + "entry": "./job-history", + "stability": "stable", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "JOB_HISTORY_LIMITS", + "parseJobHistoryItem", + "parseJobHistoryPage", + "parseJobHistoryQuery" + ], + "declarationNames": [ + "JOB_HISTORY_LIMITS", + "JobHistoryDisposition", + "JobHistoryItem", + "JobHistoryPage", + "JobHistoryQuery", + "parseJobHistoryItem", + "parseJobHistoryPage", + "parseJobHistoryQuery" + ], + "declarationsSha256": "d6a2f243c00625a3dad2c1ac78e6dcb9940e447322afcf52dd578c42bb06e760" + }, + { + "entry": "./job-storage", + "stability": "stable", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "DEFAULT_JOB_RETENTION_MS", + "JOB_STORAGE_VERSION", + "JOB_STORE_LIMITS", + "parseJobRecovery", + "parseJobStorageCapabilities", + "parseJobStoreIdentity", + "parseJobStoreLimits", + "parseStoredJob" + ], + "declarationNames": [ + "DEFAULT_JOB_RETENTION_MS", + "JOB_STORAGE_VERSION", + "JOB_STORE_LIMITS", + "JobRecovery", + "JobStorageCapabilities", + "JobStorageOptions", + "JobStore", + "JobStoreIdentity", + "JobStoreLimits", + "StoredJob", + "parseJobRecovery", + "parseJobStorageCapabilities", + "parseJobStoreIdentity", + "parseJobStoreLimits", + "parseStoredJob" + ], + "declarationsSha256": "107b06a0b8feadb4879f9ac0e768221ce9c76750f375ee85333f796c49cc5c38" + }, + { + "entry": "./job-storage-node", + "stability": "stable", + "availability": "optional", + "environments": [ + "node" + ], + "runtimeNames": [ + "createNodeJobStore" + ], + "declarationNames": [ + "NodeJobStore", + "NodeJobStoreOptions", + "createNodeJobStore" + ], + "declarationsSha256": "5e1451a260ef7728a74e332f0c47c4c8d90833d67e52e66451787a50fd5a097f" + }, + { + "entry": "./jobs", + "stability": "stable", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "JOB_LIMITS", + "JOB_PROTOCOL_VERSION", + "JOB_STATES", + "parseJobArtifact", + "parseJobArtifactContent", + "parseJobCapabilities", + "parseJobEvent", + "parseJobEvents", + "parseJobId", + "parseJobOptions", + "parseJobProgress", + "parseJobSnapshot" + ], + "declarationNames": [ + "JOB_LIMITS", + "JOB_PROTOCOL_VERSION", + "JOB_STATES", + "JobArtifact", + "JobArtifactContent", + "JobCapabilities", + "JobEvent", + "JobEvents", + "JobOptions", + "JobProgress", + "JobReporter", + "JobSnapshot", + "JobState", + "parseJobArtifact", + "parseJobArtifactContent", + "parseJobCapabilities", + "parseJobEvent", + "parseJobEvents", + "parseJobId", + "parseJobOptions", + "parseJobProgress", + "parseJobSnapshot" + ], + "declarationsSha256": "7484ea8c8687216fc9309a57b43283735694d1189237ad89341f14e1f94a0c97" + }, + { + "entry": "./localization", + "stability": "stable", + "availability": "library", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "LOCALIZATION_LIMITS", + "canonicalLocale", + "formatLocaleDate", + "formatLocaleNumber", + "formatMessage", + "localeDirection", + "localizeDisplay", + "messagePlaceholders", + "negotiateLocale", + "parseLocaleCatalog", + "resolveMessage", + "selectLocalePlural", + "validateLocalizedSurfaces" + ], + "declarationNames": [ + "LOCALIZATION_LIMITS", + "LocaleCatalog", + "LocalizedMessage", + "MessageArguments", + "canonicalLocale", + "formatLocaleDate", + "formatLocaleNumber", + "formatMessage", + "localeDirection", + "localizeDisplay", + "messagePlaceholders", + "negotiateLocale", + "parseLocaleCatalog", + "resolveMessage", + "selectLocalePlural", + "validateLocalizedSurfaces" + ], + "declarationsSha256": "8a18db17f11049dcaace7bd98aae453b572ed126689f1f88fd4b8c8bd57582c6" + }, + { + "entry": "./package.json", + "stability": "metadata", + "availability": "data", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [], + "declarationNames": [], + "declarationsSha256": null + }, + { + "entry": "./project-query", + "stability": "stable", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "PROJECT_QUERY_LIMITS", + "PROJECT_QUERY_REASONS", + "parseProjectQueryCapabilities", + "parseProjectQueryCursor", + "parseProjectQueryOptions", + "parseProjectQueryPage" + ], + "declarationNames": [ + "PROJECT_QUERY_LIMITS", + "PROJECT_QUERY_REASONS", + "ProjectFileSearchOptions", + "ProjectQueryCapabilities", + "ProjectQueryItem", + "ProjectQueryOptions", + "ProjectQueryPage", + "ProjectQueryReason", + "ProjectTextMatch", + "ProjectTextSearchOptions", + "ProjectTreeOptions", + "parseProjectQueryCapabilities", + "parseProjectQueryCursor", + "parseProjectQueryOptions", + "parseProjectQueryPage" + ], + "declarationsSha256": "fce3fad2bc40fce8dec39b5e532668a841607ce96a36ea8523a45826da066d3a" + }, + { + "entry": "./project-query-node", + "stability": "stable", + "availability": "optional", + "environments": [ + "node" + ], + "runtimeNames": [ + "createNodeProjectQueries" + ], + "declarationNames": [ + "NodeProjectQueries", + "NodeProjectQueryCapabilities", + "createNodeProjectQueries" + ], + "declarationsSha256": "aa7065f63c243fb444ce933e4acd6a97239cbcdbcc383d9a0b3de748d8500a91" + }, + { + "entry": "./project-watch", + "stability": "stable", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "PROJECT_RESYNC_REASONS", + "PROJECT_SCAN_REASONS", + "PROJECT_WATCH_LIMITS", + "parseProjectEntry", + "parseProjectPatterns", + "parseProjectSnapshot", + "parseProjectWatchCapabilities", + "parseProjectWatchEvent", + "parseProjectWatchOptions" + ], + "declarationNames": [ + "PROJECT_RESYNC_REASONS", + "PROJECT_SCAN_REASONS", + "PROJECT_WATCH_LIMITS", + "ProjectChange", + "ProjectEntry", + "ProjectResyncReason", + "ProjectScanReason", + "ProjectSnapshot", + "ProjectWatchCapabilities", + "ProjectWatchEvent", + "ProjectWatchOptions", + "parseProjectEntry", + "parseProjectPatterns", + "parseProjectSnapshot", + "parseProjectWatchCapabilities", + "parseProjectWatchEvent", + "parseProjectWatchOptions" + ], + "declarationsSha256": "4b4095ab2e48261c98fa385d9c4e85c0eeb308cb4a1b876b42c020413ceaf101" + }, + { + "entry": "./project-watch-node", + "stability": "stable", + "availability": "optional", + "environments": [ + "node" + ], + "runtimeNames": [ + "createNodeProjectWatcher" + ], + "declarationNames": [ + "NodeProjectWatchCapabilities", + "NodeProjectWatcher", + "createNodeProjectWatcher" + ], + "declarationsSha256": "aa142b1ad12968a67e8bca9881c919841a11b2d9e47bab29b323156eb2511cbf" + }, + { + "entry": "./publishing", + "stability": "stable", + "availability": "library", + "environments": [ + "node" + ], + "runtimeNames": [ + "packPlugin", + "validatePluginPackage", + "verifyPluginArchive" + ], + "declarationNames": [ + "PackageFile", + "PackageReport", + "PackedPlugin", + "VerifiedPluginArchive", + "packPlugin", + "validatePluginPackage", + "verifyPluginArchive" + ], + "declarationsSha256": "0fbeb14838e64dd879e77c169d8db6f61222b4e6f62b04073089b6aceae0e7c5" + }, + { + "entry": "./schemas", + "stability": "stable", + "availability": "library", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "SCHEMAS" + ], + "declarationNames": [ + "SCHEMAS" + ], + "declarationsSha256": "a25b1a2abe5e8f398cb39f954cedc77927e8a77a035dbafc570818f42ef1be97" + }, + { + "entry": "./secrets", + "stability": "stable", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "SECRET_LIMITS", + "createSecretExecution", + "createSecretResolver", + "parseSecretReference" + ], + "declarationNames": [ + "SECRET_LIMITS", + "SecretExecution", + "SecretExecutionApi", + "SecretExecutionScope", + "SecretLeaseCallback", + "SecretReference", + "SecretResolver", + "SecretResolverPort", + "createSecretExecution", + "createSecretResolver", + "parseSecretReference" + ], + "declarationsSha256": "e3e66bb38c0abcfdcaa04e283b40b0af6ba788afe6dbcca4d817301d3ee57daa" + }, + { + "entry": "./settings", + "stability": "stable", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "SETTINGS_LIMITS", + "createSettingsStore", + "parseSettingsDefinition", + "parseSettingsSnapshot" + ], + "declarationNames": [ + "SETTINGS_LIMITS", + "SettingDefinition", + "SettingsApi", + "SettingsDefinition", + "SettingsLayers", + "SettingsReadPort", + "SettingsScope", + "SettingsSnapshot", + "SettingsState", + "SettingsStore", + "SettingsValues", + "createSettingsStore", + "parseSettingsDefinition", + "parseSettingsSnapshot" + ], + "declarationsSha256": "4c22ce8b4f44d58313e49da3c63b85054225fd940b67043b6114132126c53bb3" + }, + { + "entry": "./testing", + "stability": "stable", + "availability": "library", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "TEST_FAULTS", + "TEST_LIMITS", + "TEST_RESOURCE_KINDS", + "TRACE_OPERATIONS", + "createFaultController", + "createMemoryJobStore", + "createMemoryWorkspace", + "createScenarioHost", + "createTestClock", + "createTestHost", + "createTestRecorder", + "createTestReplay", + "createTestResources", + "flushTestMicrotasks", + "parseTestTrace" + ], + "declarationNames": [ + "FaultController", + "MemoryJobSnapshot", + "MemoryJobStore", + "MemoryWorkspace", + "ScenarioHost", + "ScenarioInspection", + "ScenarioOptions", + "TEST_FAULTS", + "TEST_LIMITS", + "TEST_RESOURCE_KINDS", + "TRACE_OPERATIONS", + "TestClock", + "TestFault", + "TestFixture", + "TestHost", + "TestRecorder", + "TestReplay", + "TestResourceKind", + "TestResources", + "TestTrace", + "TestTraceEvent", + "TestTraceOptions", + "TraceOperation", + "createFaultController", + "createMemoryJobStore", + "createMemoryWorkspace", + "createScenarioHost", + "createTestClock", + "createTestHost", + "createTestRecorder", + "createTestReplay", + "createTestResources", + "flushTestMicrotasks", + "parseTestTrace" + ], + "declarationsSha256": "602ab99fde3e350fb7cfdcd39751fa5812d0e22f5cdc7e48a1145200a8df17d3" + }, + { + "entry": "./themes", + "stability": "stable", + "availability": "library", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "THEME_COLOR_KEYS", + "THEME_METRICS", + "THEME_SCHEMA", + "THEME_TOKEN_COLOR_KEYS", + "THEME_XML_MAX_BYTES", + "parseTheme", + "serializeThemeXml" + ], + "declarationNames": [ + "DdsTheme", + "THEME_COLOR_KEYS", + "THEME_METRICS", + "THEME_SCHEMA", + "THEME_TOKEN_COLOR_KEYS", + "THEME_XML_MAX_BYTES", + "ThemeColorKey", + "ThemeMetricKey", + "ThemeMode", + "ThemeTokenColorKey", + "parseTheme", + "serializeThemeXml" + ], + "declarationsSha256": "3d67f76fc0f5003d57b931d8104079d17e31cf5c30c8369085357377e734440b" + }, + { + "entry": "./transfer-queue", + "stability": "stable", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "TRANSFER_DEFAULTS", + "TRANSFER_PHASES", + "TRANSFER_QUEUE_LIMITS", + "TRANSFER_STATES", + "createTransferQueue", + "parseTransferQueueOptions", + "parseTransferSnapshot" + ], + "declarationNames": [ + "TRANSFER_DEFAULTS", + "TRANSFER_PHASES", + "TRANSFER_QUEUE_LIMITS", + "TRANSFER_STATES", + "TransferHandle", + "TransferPhase", + "TransferQueue", + "TransferQueueOptions", + "TransferScheduling", + "TransferSnapshot", + "TransferState", + "createTransferQueue", + "parseTransferQueueOptions", + "parseTransferSnapshot" + ], + "declarationsSha256": "a501435b8d1b356ee7ec29e145956a90714a442eb9d5f05844276d7264c406a5" + }, + { + "entry": "./uploads", + "stability": "stable", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "DEFAULT_UPLOAD_LIMITS", + "EMPTY_UPLOAD_SHA256", + "UPLOAD_LIMITS", + "UPLOAD_STATES", + "parseUploadCapabilities", + "parseUploadChunk", + "parseUploadCommitReceipt", + "parseUploadIdentity", + "parseUploadLimits", + "parseUploadQuery", + "parseUploadReference", + "parseUploadRequest", + "parseUploadResult", + "parseUploadSpec", + "parseUploadStatus", + "uploadFile", + "uploadInteger", + "uploadUuid" + ], + "declarationNames": [ + "DEFAULT_UPLOAD_LIMITS", + "EMPTY_UPLOAD_SHA256", + "UPLOAD_LIMITS", + "UPLOAD_STATES", + "UploadCapabilities", + "UploadChunk", + "UploadCommitReceipt", + "UploadFileOptions", + "UploadIdentity", + "UploadLimits", + "UploadProgress", + "UploadQuery", + "UploadReference", + "UploadSource", + "UploadSpec", + "UploadState", + "UploadStatus", + "parseUploadCapabilities", + "parseUploadChunk", + "parseUploadCommitReceipt", + "parseUploadIdentity", + "parseUploadLimits", + "parseUploadQuery", + "parseUploadReference", + "parseUploadRequest", + "parseUploadResult", + "parseUploadSpec", + "parseUploadStatus", + "uploadFile", + "uploadInteger", + "uploadUuid" + ], + "declarationsSha256": "1db1f3508b58d7a5341ebd4ea1acab60e196e2cf0ea7e1afb3330368db020f5c" + }, + { + "entry": "./uploads-browser", + "stability": "stable", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "createBrowserUploadSource" + ], + "declarationNames": [ + "createBrowserUploadSource" + ], + "declarationsSha256": "7870d15593a9940c5dd23faab30bcaa30f15988f7ba9ac9104cb594036cba3a6" + }, + { + "entry": "./uploads-node", + "stability": "stable", + "availability": "optional", + "environments": [ + "node" + ], + "runtimeNames": [ + "createNodeUploadSource", + "createNodeUploadStore" + ], + "declarationNames": [ + "NodeUploadConfiguration", + "NodeUploadStore", + "createNodeUploadSource", + "createNodeUploadStore" + ], + "declarationsSha256": "a04f718b4464f94d15742b7a7b05c759d0fda551224c847c955d3539d0061d13" + }, + { + "entry": "./workspace-client", + "stability": "stable", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "WORKSPACE_OBSERVATION_LIMITS", + "WORKSPACE_PROJECT_LIMITS", + "applyTextEdits", + "createWorkspaceClient", + "createWorkspaceProject", + "normalizeWorkspaceUrl", + "parseWorkspaceProjectCapabilities", + "parseWorkspaceProjectPoll" + ], + "declarationNames": [ + "EditSnapshot", + "TextEdit", + "WORKSPACE_OBSERVATION_LIMITS", + "WORKSPACE_PROJECT_LIMITS", + "WorkspaceClient", + "WorkspaceConditionalFile", + "WorkspaceEditSession", + "WorkspaceFileCapabilities", + "WorkspaceFileChange", + "WorkspaceFileRevision", + "WorkspaceFileWatchOptions", + "WorkspaceJobUpdate", + "WorkspaceJobWatchOptions", + "WorkspaceObservationOptions", + "WorkspaceObserver", + "WorkspaceProject", + "WorkspaceProjectCapabilities", + "WorkspaceProjectPoll", + "WorkspaceProjectWatchOptions", + "WorkspaceRequestOptions", + "applyTextEdits", + "createWorkspaceClient", + "createWorkspaceProject", + "normalizeWorkspaceUrl", + "parseWorkspaceProjectCapabilities", + "parseWorkspaceProjectPoll" + ], + "declarationsSha256": "ba7753be442440704b1f5607d6da5af21199a65d3df19b04423b1b2acde3e710" + }, + { + "entry": "./workspace-edits", + "stability": "stable", + "availability": "optional", + "environments": [ + "node", + "browser" + ], + "runtimeNames": [ + "WORKSPACE_EDIT_CAPABILITIES", + "WORKSPACE_EDIT_LIMITS", + "applyWorkspaceEdit", + "createWorkspaceCompensation", + "parseWorkspaceEdit", + "parseWorkspaceEditPreview", + "parseWorkspaceEditReceipt", + "parseWorkspaceEditRecord", + "previewWorkspaceEdit", + "recoverWorkspaceEdit" + ], + "declarationNames": [ + "WORKSPACE_EDIT_CAPABILITIES", + "WORKSPACE_EDIT_LIMITS", + "WorkspaceEdit", + "WorkspaceEditApplyOptions", + "WorkspaceEditAuthorization", + "WorkspaceEditChange", + "WorkspaceEditFileState", + "WorkspaceEditJournal", + "WorkspaceEditObservedState", + "WorkspaceEditPathState", + "WorkspaceEditPreview", + "WorkspaceEditPreviewStep", + "WorkspaceEditReceipt", + "WorkspaceEditRecord", + "applyWorkspaceEdit", + "createWorkspaceCompensation", + "parseWorkspaceEdit", + "parseWorkspaceEditPreview", + "parseWorkspaceEditReceipt", + "parseWorkspaceEditRecord", + "previewWorkspaceEdit", + "recoverWorkspaceEdit" + ], + "declarationsSha256": "dd29986893ff1d5888f8d7ec43ab8799afc316c2c297d8383870ad244270ab72" + }, + { + "entry": "./workspace-edits-node", + "stability": "stable", + "availability": "optional", + "environments": [ + "node" + ], + "runtimeNames": [ + "createNodeWorkspaceEditJournal" + ], + "declarationNames": [ + "NodeWorkspaceEditJournal", + "createNodeWorkspaceEditJournal" + ], + "declarationsSha256": "9eced8881f30721d622b6d421996f7daee38b2d06b3fc2009df2c221fa9c247b" + }, + { + "entry": "./workspace-node", + "stability": "stable", + "availability": "optional", + "environments": [ + "node" + ], + "runtimeNames": [ + "createNodeWorkspace", + "createProcessBackend", + "createWorkspaceServer", + "downloadJobBinaryArtifact", + "downloadStoredJobArtifact" + ], + "declarationNames": [ + "ArtifactDownloadOptions", + "ArtifactDownloadProgress", + "ArtifactDownloadReceipt", + "ConfiguredWorkspacePlugin", + "NodeWorkspace", + "ProcessBackend", + "StoredArtifactDownloadReceipt", + "WorkspaceServer", + "WorkspaceServerOptions", + "createNodeWorkspace", + "createProcessBackend", + "createWorkspaceServer", + "downloadJobBinaryArtifact", + "downloadStoredJobArtifact" + ], + "declarationsSha256": "f86bf2a795090933bc41b182982db8013b3e6fbbe865c10baba54515bdef708c" + } + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b48d6f..88691ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 1.0.0-rc.1 + +- Add a versioned public host/port conformance suite with machine and human reports, + exact runtime identity, unsupported-feature reporting, bounded deadlines and cleanup. +- Inventory all package exports and declarations, distinguish optional/experimental + surfaces, and publish SemVer, runtime maintenance and migration policy. +- Exercise concurrent language/settings/edits/jobs/watch/transfers through actual + HTTP and disk, with interruption, malformed response, revoke and restart fixtures. +- Measure repeatable resource/performance baselines and retain independent packed + Node/browser examples. RC plugin archives pin the exact prerelease peer; prior + 0.3.x–0.13.x archives retain their original verified metadata. + ## 0.13.0 — 2026-10-05 - Add command/language/theme/job/browser/configuration scaffolds with preview, diff --git a/PUBLIC_SURFACE.json b/PUBLIC_SURFACE.json index 1970ff5..febb208 100644 --- a/PUBLIC_SURFACE.json +++ b/PUBLIC_SURFACE.json @@ -2,6 +2,7 @@ "schemaVersion": 1, "description": "Explicitly reviewed files for the public DDS Plugin SDK package. Changes require publication and rights review.", "packageFiles": [ + "API_SUPPORT.json", "bin/dds-plugin.mjs", "CHANGELOG.md", "CONTRIBUTING.md", @@ -11,6 +12,7 @@ "docs/BROWSER_ARTIFACTS.md", "docs/BROWSER_RESUME.md", "docs/COMPATIBILITY.md", + "docs/CONFORMANCE.md", "docs/CONSENT.md", "docs/DATA_SCHEMAS.md", "docs/DEVTOOLS.md", @@ -34,8 +36,10 @@ "docs/PROJECTS.md", "docs/PUBLIC_SCOPE.md", "docs/PUBLISHING.md", + "docs/RC_ACCEPTANCE.md", "docs/SECRETS.md", "docs/SETTINGS.md", + "docs/SUPPORT_POLICY.md", "docs/testing.md", "docs/THEMES.md", "docs/TRANSFER_QUEUE.md", @@ -56,6 +60,7 @@ "examples/browser-resume/host.mjs", "examples/browser-resume/serve.mjs", "examples/browser-resume/worker.mjs", + "examples/combined-workload/run.mjs", "examples/command-jobs/run.mjs", "examples/configuration/browser.html", "examples/configuration/browser.mjs", @@ -64,6 +69,11 @@ "examples/configuration/plugin.mjs", "examples/configuration/run.mjs", "examples/configuration/serve.mjs", + "examples/conformance/browser.css", + "examples/conformance/browser.html", + "examples/conformance/browser.mjs", + "examples/conformance/run.mjs", + "examples/conformance/serve.mjs", "examples/consent/prompt.mjs", "examples/consent/README.md", "examples/development-tools/run.mjs", @@ -144,6 +154,7 @@ "schemas/code-action.schema.json", "schemas/command.schema.json", "schemas/completion-item.schema.json", + "schemas/conformance-report.schema.json", "schemas/data-schema.schema.json", "schemas/development-definition.schema.json", "schemas/diagnostic-report.schema.json", @@ -239,6 +250,15 @@ "src/artifacts.mjs", "src/configuration-operations.mjs", "src/configuration-values.mjs", + "src/conformance-catalog.mjs", + "src/conformance-development.mjs", + "src/conformance-host.mjs", + "src/conformance-node.d.mts", + "src/conformance-node.mjs", + "src/conformance-schemas.mjs", + "src/conformance-workspace.mjs", + "src/conformance.d.mts", + "src/conformance.mjs", "src/consent.d.mts", "src/consent.mjs", "src/contracts.mjs", diff --git a/README.md b/README.md index ecc69bf..a855533 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,14 @@ a small ESM package, TypeScript declarations and versioned contracts. Connect your own workspace and tools with the Node.js host and HTTP client. The SDK has **zero runtime dependencies**. +Version **1.0.0-rc.1** is the stable API release candidate. The +[API inventory](API_SUPPORT.json), [support and migration policy](docs/SUPPORT_POLICY.md) +and [host conformance suite](docs/CONFORMANCE.md) make its scope reviewable. +Run `npm run example:conformance`, `npm run example:conformance-browser` or +`npm run example:combined` to check independent Node/browser hosts and the combined +language/settings/edit/job/watch/transfer workload. RC packages pin the exact +prerelease peer; they do not establish GA or DDS product acceptance. + Version 0.13 adds six [project templates and guarded contract generation](docs/DEVTOOLS.md), [deterministic synthetic test hosts](docs/testing.md) and opt-in [local diagnostics and loopback debugging](docs/DIAGNOSTICS.md). Run @@ -47,7 +55,7 @@ application, private engines/services and production signing systems are outside this repository. Read the [public scope](docs/PUBLIC_SCOPE.md) and [compatibility table](docs/COMPATIBILITY.md) for exact boundaries. -Version **0.13.0** supports Node 22 and 24. Manifest v1 diagnostic plugins remain +Version **1.0.0-rc.1** supports Node 22 and 24. Manifest v1 diagnostic plugins remain compatible. Manifest v2 adds commands, permission-scoped workspace and backend APIs, and open-source or proprietary distribution metadata. Themes export to the XML format already supported by DDS. The workspace host runs trusted plugins @@ -107,7 +115,7 @@ mkdir my-dds-plugin cd my-dds-plugin npm init -y npm pkg set type=module -npm install --ignore-scripts --save-exact https://github.com/Altifigence/dds-plugin-sdk/releases/download/v0.13.0/altifigence-dds-plugin-sdk-0.13.0.tgz +npm install --ignore-scripts --save-exact https://github.com/Altifigence/dds-plugin-sdk/releases/download/v1.0.0-rc.1/altifigence-dds-plugin-sdk-1.0.0-rc.1.tgz ``` Copy the example's `plugin.mjs` and `run.mjs` into this directory, then run: diff --git a/SECURITY.md b/SECURITY.md index a9bccf3..8366edd 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,6 +1,6 @@ # Security -The current development line is **0.7.x**. It retains the security fixes from +The current release candidate is **1.0.0-rc.1**. It retains the security fixes from 0.3.2. Update workspace servers and clients together, using a current patched Node 22 or 24; do not use a pre-0.3.2 workspace host. Report an SDK @@ -55,7 +55,7 @@ protect storage, verify the current identity and implement revocation themselves ## 한국어 -현재 개발 버전은 **0.7.x**이며 0.3.2의 보안 수정을 유지합니다. workspace **서버와 클라이언트를 모두 +현재 출시 후보는 **1.0.0-rc.1**이며 0.3.2의 보안 수정을 유지합니다. workspace **서버와 클라이언트를 모두 0.3.2 이상**으로 갱신하고 Node 22/24의 최신 보안 패치를 사용하세요. 클라이언트만 갱신하면 오래된 서버의 파일 검사와 요청 수신 제한은 바뀌지 않습니다. @@ -74,3 +74,12 @@ The Node downloader hashes the entire stream and the actual staging file before exclusive publication. Partial files are not verified results. This is not a lock against hostile processes with write access to the workspace or download directory. Custom ports must enforce equivalent isolation and coherent-source checks. + +## 1.x review and support + +See the [support policy](docs/SUPPORT_POLICY.md) for maintained lines, runtime end +dates and deprecated APIs, and [conformance](docs/CONFORMANCE.md) for test scope. +Durable state, upload staging and workspace edit journals are operator-owned; +recovery requires current authority and does not replay execution automatically. +Synthetic tests and local diagnostic exports contain no real customer fixtures. +Conformance results and unsigned package hashes do not grant trust or permissions. diff --git a/docs/COMPATIBILITY.md b/docs/COMPATIBILITY.md index 2a3fb0b..15cb5b7 100644 --- a/docs/COMPATIBILITY.md +++ b/docs/COMPATIBILITY.md @@ -1,5 +1,18 @@ # Compatibility and availability +## 1.0 release candidate + +`1.0.0-rc.1` retains the 0.13 public runtime and protocol contracts and adds +[host conformance](CONFORMANCE.md), a complete [API inventory](../API_SUPPORT.json) +and an explicit [support/migration policy](SUPPORT_POLICY.md). New RC plugin archives +pin the exact `1.0.0-rc.1` peer. Verification still accepts exact 0.3.x–0.13.x +metadata without widening any archive's original range. Revalidate and repack to +change a plugin's installation range. Actual old-archive wire interoperability +tests do not make those SDK lines maintained or prove every optional feature exists. + +Stable 1.x policy is a GA commitment; RC acceptance and known limits are recorded +in [the acceptance report](RC_ACCEPTANCE.md). DDS product releases remain separate. + ## 0.13 development and diagnostics Six feature templates, guarded contract generation, synthetic scenario ports, diff --git a/docs/CONFORMANCE.md b/docs/CONFORMANCE.md new file mode 100644 index 0000000..cce5831 --- /dev/null +++ b/docs/CONFORMANCE.md @@ -0,0 +1,79 @@ +# Host and plugin conformance + +The browser-safe `@altifigence/dds-plugin-sdk/conformance` export runs versioned +synthetic contract probes against operator-supplied adapters. It reports every +known feature as `supported`, `unsupported` or `failed`, with exact host/version, +runtime/platform identity, a stable check ID, duration and cleanup status. JSON +readback is strict and has a published schema. A report is test evidence, not +publisher authentication, a sandbox certificate or DDS product qualification. + +```js +import {runConformance, formatConformanceReport} + from '@altifigence/dds-plugin-sdk/conformance'; +import {createPluginHost} from '@altifigence/dds-plugin-sdk'; +const report = await runConformance({ + identity: {host: 'My host', version: '1.0.0', runtime: 'Node 24', platform: 'linux-x64'}, + features: ['commands', 'permissions', 'cancellation'], + adapters: {host: options => createPluginHost(options)}, +}); +console.log(formatConformanceReport(report)); +``` + +Replace that factory with your host adapter. It receives the standard +`PluginHostOptions` and must return a fresh host, including the supplied permission, +workspace, settings, backend, runtime and storage ports. The suite supplies a real +plugin; callbacks returning `{ok:true}` cannot establish conformance. Plugin authors +run their own plugin's tests in addition to these host probes. + +The three core cases are required by default. Optional cases run only when explicitly +declared; a declared capability with a missing/broken adapter fails. Unknown optional +declarations are recorded and never probed. `requiredFeatures` can require a chosen +profile. A report with zero failures can still have unsupported features; inspect the +full matrix, not only `ok`. + +## Adapters and coverage + +| Port | Probes and independent fixture | +| --- | --- | +| `host(options)` | Commands and structured input/output, unknown capability rejection, intersected grants and operator scope, revoke, cooperative cancellation with unsettled-slot retention; hover generation/malformed response; formatting proposal freshness; folding identity; jobs/idempotence; storage recovery/history/current authority | +| `settings(definition)` | Default/user/workspace precedence, CAS, failed migration rollback and subscription disposal | +| `workspace({signal})` | Fresh authenticated client; `fixture.txt` contains `alpha 😀\r\nalpha`; `fixture.bin` is the 131089-byte sequence `(index*17+31)&255`. A `conformance-plugin` command `capture` retains text and binary results. Port also provides `artifactSha256`, `journal`, `writeFixture`, `restart`, `revokeProjects`, `revokeUploads`, `dispose` | +| `localization` | Selected locale, explicit fallback and placeholder validation | +| `testing` | Seeded clock, deadline order, payload-free synthetic trace and pure replay identity | +| `diagnostics` | Bounded local reports, body exclusion and pending cleanup, using the supplied host | +| `development()` | Disposable absent directory plus generation/doctor API; deterministic dry-run/generation, missing installation diagnosis, drift and overwrite refusal. No dependency install or plugin execution | + +`CONFORMANCE_CASES` links all 0.8–0.13 feature groups to packed examples, including +the larger language assistance/display, crash recovery and browser storage scenarios. +The quick probes sample each contract; the linked examples and release tests cover +the fuller boundary matrix. Native browser OPFS/checkpoint support must be exercised +in an actual browser using `examples/browser-resume`; it is not certified by Node +probes or memory fixtures. Filesystem crash/disk-full behavior likewise requires +the actual Node integration tests, not an in-memory host. + +For the complete reference Node profile: + +```js +import {runConformance} from '@altifigence/dds-plugin-sdk/conformance'; +import {createSdkConformanceOptions} from '@altifigence/dds-plugin-sdk/conformance-node'; +const report = await runConformance(createSdkConformanceOptions()); +``` + +The Node helper creates and removes only its fresh synthetic temporary directories, +uses ephemeral loopback servers, and starts no external tool or shell. Its fixture +digest is deliberately synthetic and is not a trusted publisher pin. Its optional +`failNextRequest('disconnect'|'malformed')` is a local test control. + +## Deadlines, cleanup and privacy + +Case deadlines default to 10 seconds, bounded by 30 seconds. Timeout or cancellation +stops later selected cases. Cleanup is observed and bounded; late factories dispose +resources when they eventually return. `cleanup: pending` means release is unverified, +never that resources were forcibly reclaimed. Adapters and fixtures are trusted +in-process code: a synchronous loop cannot be interrupted and ambient OS access is +not isolated. Run unknown hosts/plugins in an independently isolated process. + +Reports store only caller-supplied short identity labels, declared feature IDs and +fixed check/reason fields. Provider errors, stack traces, payloads, tokens and paths +are not exported. Do not put sensitive information in identity labels. Reports are +returned locally and no upload service is contacted. diff --git a/docs/PUBLISHING.md b/docs/PUBLISHING.md index 52f6747..07c5994 100644 --- a/docs/PUBLISHING.md +++ b/docs/PUBLISHING.md @@ -26,7 +26,7 @@ notes for the capabilities and hosts actually supported by that release. what data the plugin uses, which backend receives it, and how users can find updates and contact the publisher about security. 4. List the plugin's distribution files explicitly in `dds-package.json`, then - validate and pack them with the SDK 0.13.0 CLI described below. Inspect the + validate and pack them with the SDK 1.0.0-rc.1 CLI described below. Inspect the archive; do not list credentials, private project data or files that you do not intend to distribute. 5. Publish the immutable versioned `.tgz`, its external `.release.json` and @@ -44,10 +44,10 @@ file. Errors do not print the detected value. This is a conservative additional check, not a complete secret or malware scanner. Inspect your source and final archive; obfuscated credentials and confidential implementation may evade it. -## Package format and SDK 0.13.0 CLI +## Package format and SDK 1.0.0-rc.1 CLI The CLI has been available since SDK 0.2.0; SDK 0.1.0 does not provide it. -Use an installed 0.13.0 package containing the `dds-plugin` executable. +Use an installed 1.0.0-rc.1 package containing the `dds-plugin` executable. For a new project, start with [init and doctor](DEVTOOLS.md). Prepare this layout: @@ -87,9 +87,9 @@ The manifest, disclosure, manifest entry and selected license file must all be listed. The packer automatically includes `dds-package.json` and generates `package.json`; do not list either file in `files`. The generated npm metadata uses `type: "module"`, the manifest entry and a peer dependency on this SDK: -`>=0.13.0 <0.14.0`. The packer targets its current tested SDK line; it does not -infer compatibility by inspecting imports. Older archives from the 0.2.x–0.12.x -CLIs exclude SDK 0.13.0 in their peer ranges. Validate against 0.13.0 and +`1.0.0-rc.1`. The packer targets its current tested SDK line; it does not +infer compatibility by inspecting imports. Older archives from the 0.2.x–0.13.x +CLIs exclude SDK 1.0.0-rc.1 in their peer ranges. Validate against 1.0.0-rc.1 and publish a new plugin version with this packer when migrating those archives. List `NOTICE`, third-party notices, other runtime modules, assets and source material explicitly when they are applicable. The CLI does not determine which legal @@ -153,7 +153,7 @@ separate from validating and packaging your own plugin. ## Select a distribution channel -Before installing a downloaded plugin, use the SDK 0.13.0 +Before installing a downloaded plugin, use the SDK 1.0.0-rc.1 [`dds-plugin verify` command](VERIFYING.md) or `verifyPluginArchive()` API to check its archive and release metadata without running or extracting it. Supply an independently obtained expected SHA-256 when selecting exact release diff --git a/docs/RC_ACCEPTANCE.md b/docs/RC_ACCEPTANCE.md new file mode 100644 index 0000000..5556ca7 --- /dev/null +++ b/docs/RC_ACCEPTANCE.md @@ -0,0 +1,74 @@ +# 1.0 release candidate acceptance + +Candidate: **1.0.0-rc.1**. This document records the acceptance scope; exact release +asset digests and final verification receipts accompany the GitHub release. An +unpublished checkout is not a release receipt. + +Security audit and vulnerability validation are **pending** under the owner's +2026-10-05 deferral (DDS-243). Existing review records are retained. Functional, +compatibility and performance acceptance can continue, but none of those results +constitutes completed security acceptance. This candidate must not be described +as having passed the deferred audit. + +## Reproducible checks + +`npm ci --ignore-scripts` and `npm run check` cover the reviewed package allowlist, +API inventory, schemas, runtime tests, TypeScript declarations, standalone examples +and real `npm pack` installations outside this repository. The conformance example +reports all 18 SDK profiles; the browser example reports its available profiles and +explicitly marks Node paths unsupported. Every generated template has separate +runtime, type, generation, doctor and pack/verify acceptance. + +The combined workload runs actual HTTP/filesystem/storage/upload/watch/edit paths +concurrently with language and settings requests. Per three-cycle sample it uses +131089-byte binary fixtures, 120 language requests, 120 commands, six verified +transfers and three observed project changes. It restarts the workspace host, +rejects old result generations and verifies one retained job instead of re-execution. +Fault mode also tests connection interruption, malformed responses, mid-transfer +upload revocation and host grant revocation. Five repeated fault cycles run in CI. + +Existing integration fixtures cover killed child processes, durable checkpoint and +edit-journal recovery, corrupt records/chunks, failed/quota-limited writes, event +storms, expiration and ignored cancellation. These remain part of the full test +suite; memory/fake-clock tests are not substituted for their filesystem/process +evidence. Actual historical public archives are tested in both client/host +directions for their common contracts before release. + +## Measurement and regression policy + +`npm run benchmark:combined` launches five fresh child processes and reports +median, median absolute deviation, minimum and maximum for cold module import, +workspace setup, combined elapsed/CPU time, sampled process RSS/heap, watch latency, +logical disk I/O and HTTP JSON payload bytes. It tracks instrumented file handles +and compares active Node resource types before/after owned fixture cleanup. The +SDK host, queue, settings and diagnostics also check their own pending counters. +This is bounded fixture evidence, not whole-process leak detection or a latency SLA. + +Logical disk counters wrap this benchmark child's `fs.promises` calls before SDK +imports; they count successful API payload reads/writes and renames. They exclude +physical drive traffic and cache behavior. Network counters include actual JSON +request/response bodies, excluding TCP/HTTP header overhead. RSS/heap are process +samples every 5 ms, not exact peak allocation. Each result records OS/architecture, +Node patch version, payload size and concurrency. + +The checked-in performance baseline records its source run and measured spread for +each Windows/Linux × Node 22/24 profile. Thresholds compare five-sample medians to +`observed maximum + max(6 × MAD, 2 × observed range, observed maximum)`; this allows +at least one observed-maximum worth of scheduling variation and is a coarse guard +against large regressions. Resource residuals must be zero regardless of timing. +Calibration is an explicit setup action and never silently replaces a baseline. + +## Boundaries and release gate + +Required release evidence includes the exact PR/main commit and four required CI +jobs, the public SDK tarball plus SHA256SUMS, fresh download/hash/install and legacy +archive interoperability, browser profile evidence and EN/KO CMS/public readback. +The SDK is not accepted as GA until that evidence is present and blocking defects +are resolved. A candidate is published with GitHub's prerelease flag. + +Known limits: trusted plugins and adapters run with ambient process authority; +browser storage support is conditional and OPFS recovery remains experimental; +file/edit operations have per-file atomicity and readback-only recovery; diagnostics +are local and owner-scoped; digests do not authenticate a publisher. No DDS UI, +Engine, Cloud, Marketplace, registry publication or Windows installer acceptance +is asserted. See [support policy](SUPPORT_POLICY.md) and [security](../SECURITY.md). diff --git a/docs/SUPPORT_POLICY.md b/docs/SUPPORT_POLICY.md new file mode 100644 index 0000000..97c94ca --- /dev/null +++ b/docs/SUPPORT_POLICY.md @@ -0,0 +1,79 @@ +# SDK 1.x support and migration + +`1.0.0-rc.1` is a release candidate. Install the exact archive and use the exact +plugin peer `1.0.0-rc.1` during RC testing. It does not assert GA acceptance or a +DDS product release. The policy below becomes the stable-line policy at 1.0 GA. + +## Public API and optional features + +[API_SUPPORT.json](../API_SUPPORT.json) inventories every package export, its +runtime names, public declarations, declaration digest, availability and stability. +`npm run api:check` detects changes to that inventory. Public declarations and +documented wire/storage contracts define behavior; file names under `src/` that +are not package exports are implementation details and must not be imported. + +Stable exports follow [SemVer 2](https://semver.org/): fixes use patch releases, +compatible additions and deprecations use minor releases, incompatible stable API +changes require a new major. Security fixes can reject inputs already outside the +documented permission/path/resource contract. Unknown contract fields, capabilities +and versions remain rejected; do not equate protocol v1 with support for every +optional method. Discover each optional capability and retain its bounds. A failed +discovery, malformed response or denied permission is not an unsupported response. + +`artifact-resume-browser` remains **experimental**: its Worker, OPFS sync access +and Web Locks profile is browser dependent. Pin an exact SDK/browser version; +experimental changes are announced in minor-release notes and require fresh +acceptance. Its presence is not a cross-browser compatibility claim. Other browser +ports also require their documented capability and permission checks. + +No stable API is deprecated at RC1. Future deprecations appear in declarations, +the changelog and migration guide for at least two minor releases and six months +before removal in a new major. Runtime execution does not emit unsolicited warning +logs or change grants. A security emergency is documented explicitly. + +## Versions and maintenance + +GA plugin packages use `>=1.0.0 <2.0.0`; a plugin using a later minor's new API must +declare that higher minimum. The packer records its release's supported range; +it does not infer compatible source or widen an existing archive. RC packages +require an explicit prerelease peer. Old archives retain their original peer +metadata when verified. Revalidate and repack a new plugin version to change it. + +The latest stable 1.x minor receives ordinary fixes. The immediately preceding +minor remains eligible for critical security fixes for 90 days after its successor +is published, within a supported Node runtime. Preview and RC releases are replaced +by their successor and do not receive a separate maintenance branch. No commercial +response-time or availability guarantee is supplied by this open-source SDK. + +Windows and Linux on currently patched Node 22 and 24 are the tested server/CLI +families. Node 22 support ends on **2027-04-30**, and Node 24 on **2028-04-30**, +following the [Node release schedule](https://github.com/nodejs/Release/blob/main/schedule.json). +Supported families and end dates are reviewed for each release; a planned runtime +removal is announced in advance and made in a major release, except after upstream +security support ends. Node 20/23/25/26, macOS, alternative JavaScript runtimes and +other browser/storage combinations have no acceptance claim in this release. +Exact tested patch versions, OS images and browser builds are release evidence, +not inferred from the broad `engines` field. Type declarations are checked with +TypeScript 5.9.3; there are no SDK runtime npm dependencies. + +## From 0.13 and earlier + +1. Install the exact RC or GA SDK archive, inspect its SHA-256 and retain its source + release URL. A digest confirms byte identity; it does not authenticate a plugin + publisher or certify code safety. +2. Run the [conformance suite](CONFORMANCE.md) and existing plugin tests in a fresh + consumer. Review every unsupported feature before configuring a production host. +3. Regenerate declarations only after reviewing `generate --dry-run`; edited + generated files require explicit conflict resolution. Update plugin peer metadata + by publishing a newly reviewed plugin version, not editing an existing archive. +4. Upgrade clients and hosts independently and test their common features. Public + 0.3.2 through 0.13 archives are legacy **interoperability test inputs**. They are + not maintained SDK lines and are not install-compatible with a newly packed 1.x + plugin. No new capability is silently emulated through broader file or command APIs. +5. Retain workspace/store identity, request a fresh connection and current grants + after restart, and use new generation-bound result references. Recovery reads + committed state; a retry requires an explicit new job ID and reviewed input. + +SDK, workspace protocol, manifest, plugin, host, persisted schema and DDS product +versions are separate. This policy covers the public SDK and documented user-owned +host path. A passing SDK suite does not prove DDS UI, Engine, Cloud or Store acceptance. diff --git a/docs/VERIFYING.md b/docs/VERIFYING.md index 0b26011..a1850a6 100644 --- a/docs/VERIFYING.md +++ b/docs/VERIFYING.md @@ -1,6 +1,6 @@ # Verify a downloaded plugin release -SDK **0.13.0** provides a Node.js API and CLI for checking archives produced by the +SDK **1.0.0-rc.1** provides a Node.js API and CLI for checking archives produced by the **0.3.x through 0.13.x DDS plugin packers**. Keep the publisher's `.tgz` and corresponding `.tgz.release.json` together. Verification reads these local files without extracting them, importing a plugin, running a package script or installing it. @@ -33,7 +33,7 @@ const receipt = await verifyPluginArchive(process.argv[2], { console.log(receipt.pluginId, receipt.pluginVersion, receipt.artifact.sha256); ``` -Save that code as `verify.mjs` in an ESM project with SDK 0.13.0 installed. Run it +Save that code as `verify.mjs` in an ESM project with SDK 1.0.0-rc.1 installed. Run it with the archive path, metadata path and independently obtained hash as its three arguments. The API returns a frozen report, and the CLI serializes it as JSON. The report contains the plugin @@ -82,11 +82,11 @@ The receipt describes the bytes read during that call. If another process can change a download before installation or use, recheck the expected hash on the actual bytes being consumed. A report is not a grant to execute a mutable path. -0.13.0 preserves existing runtime/workspace contracts and verifies the exact -published peer metadata from 0.3.x through 0.12.x, plus new 0.13.x metadata -(`>=0.13.0 <0.14.0`). Each prior minor line retains its original minor-bounded range. +1.0.0-rc.1 preserves existing runtime/workspace contracts and verifies the exact +published peer metadata from 0.3.x through 0.13.x, plus exact RC metadata +(`1.0.0-rc.1`). Each prior minor line retains its original minor-bounded range. Verification does not alter the peer range or authorize installation on another -SDK line: validate and repack a new version for 0.13. Existing 0.3.x–0.12.x packages that +SDK line: validate and repack a new version for the selected SDK. Existing 0.3.x–0.13.x packages that satisfy the strengthened file policy can be verified unchanged. The verifier rejects private credential names and Windows device aliases; remove them and publish a new plugin version if needed. See [Security](../SECURITY.md). @@ -96,7 +96,7 @@ SDK's own npm release tarball. ## 한국어 -SDK **0.13.0**의 `dds-plugin verify`와 `verifyPluginArchive()`는 **0.3.x~0.13.x DDS +SDK **1.0.0-rc.1**의 `dds-plugin verify`와 `verifyPluginArchive()`는 **0.3.x~0.13.x 및 현재 RC DDS 플러그인 packer**가 만든 `.tgz`와 외부 `.tgz.release.json`을 검사합니다. 플러그인을 실행·설치하거나 파일을 디스크에 추출하지 않으며 네트워크 요청도 하지 않습니다. 실행 명령과 API는 위 예제와 같습니다. 별도 위치의 메타데이터는 @@ -116,9 +116,9 @@ manifest·발행자 고지·진입 모듈·라이선스 및 생성된 npm 메타 격리·설치 동의·DDS 제품 호환성 검증을 대신하지 않습니다. 실제 실행 시 파일이 바뀔 수 있는 환경에서는 소비하는 바로 그 파일의 해시를 다시 확인해야 합니다. -강화된 파일 정책을 만족하는 기존 0.3.x~0.12.x 플러그인 아카이브는 그대로 검증할 수 있고 -각각 기존 peer 범위도 유지됩니다. 새 패키지는 `>=0.13.0 <0.14.0`을 -선언하며, 기존 패키지를 0.12에 설치하려면 발행자가 검증 후 새 버전으로 패키징해야 +강화된 파일 정책을 만족하는 기존 0.3.x~0.13.x 플러그인 아카이브는 그대로 검증할 수 있고 +각각 기존 peer 범위도 유지됩니다. 새 패키지는 `1.0.0-rc.1`을 +선언하며, 기존 패키지를 새 SDK에 설치하려면 발행자가 검증 후 새 버전으로 패키징해야 합니다. 검증 자체는 호환 범위를 바꾸지 않습니다. 제외한 자격증명 경로나 Windows 장치명이 들어 있으면 해당 파일을 제거한 새 플러그인 버전을 발행하세요. 0.2.x 아카이브는 기존 마이그레이션 안내에 따라 diff --git a/examples/combined-workload/run.mjs b/examples/combined-workload/run.mjs new file mode 100644 index 0000000..f509f60 --- /dev/null +++ b/examples/combined-workload/run.mjs @@ -0,0 +1,93 @@ +import assert from 'node:assert/strict'; +import {fileURLToPath} from 'node:url'; +import path from 'node:path'; +import {randomUUID} from 'node:crypto'; +import {createPluginHost,definePlugin,createLanguageResult} from '@altifigence/dds-plugin-sdk'; +import {createConformanceWorkspace} from '@altifigence/dds-plugin-sdk/conformance-node'; +import {createSettingsStore} from '@altifigence/dds-plugin-sdk/settings'; +import {createDiagnosticSession,profileHost} from '@altifigence/dds-plugin-sdk/diagnostics'; +import {createTransferQueue} from '@altifigence/dds-plugin-sdk/transfer-queue'; +import {createIncrementalSha256} from '@altifigence/dds-plugin-sdk/artifact-transfer'; +import {previewWorkspaceEdit,applyWorkspaceEdit} from '@altifigence/dds-plugin-sdk/workspace-edits'; + +const tick=()=>new Promise(resolve=>setImmediate(resolve)); +const digest=value=>createIncrementalSha256().update(value).digest(); +function resources(){const result={};for(const name of process.getActiveResourcesInfo())result[name]=(result[name]??0)+1;return result;} +function excess(before,after){return Object.fromEntries(Object.entries(after).filter(([key,count])=>count>(before[key]??0)).map(([key,count])=>[key,count-(before[key]??0)]));} +function sink(){let output=new Uint8Array(),committed=false;return {bytes:()=>output,capabilities:{kind:'caller',seek:false,readback:true,persistence:'on-commit',abort:'discard'},async open(){return { + async write({offset,bytes}){assert.equal(offset,output.length);const next=new Uint8Array(output.length+bytes.length);next.set(output);next.set(bytes,output.length);output=next;}, + async readback(){return {byteLength:output.length,async read(offset,length){return output.slice(offset,offset+length);}};}, + async commit(){committed=true;},async abort(){if(!committed)output=new Uint8Array();}, +};}};} +const manifest={manifestVersion:2,id:'combined-plugin',name:'Combined fixture',publisher:'example',version:'1.0.0',protocolVersion:1,entry:'./plugin.mjs',runtime:'workspace',capabilities:['commands','settings','hover'],permissions:['settings.read','document.read','language.provide'],supportedHosts:['workspace-host'],license:'Apache-2.0',source:{visibility:'open',licenseFile:'LICENSE'}}; +const definition={schemaVersion:1,pluginId:manifest.id,version:1,settings:{count:{schema:{schemaVersion:1,schema:{type:'integer',default:2,minimum:1,maximum:5}},scopes:['user','workspace']}}}; + +/** Independent SDK consumer: real loopback/network/disk plus simultaneous in-process language/settings. */ +export async function runCombinedWorkload({cycles=3,faults=false}={}){ + assert.ok(Number.isInteger(cycles)&&cycles>=1&&cycles<=100);assert.equal(typeof faults,'boolean'); + await tick();const beforeResources=resources(),start=performance.now(),cpu=process.cpuUsage(),memory=process.memoryUsage(); + let peakRss=memory.rss,peakHeap=memory.heapUsed,requestBytes=0,responseBytes=0,requests=0,fixtureWriteBytes=0,languageRequests=0,commands=0,observedChanges=0,verifiedTransfers=0; + const watchLatencyMs=[],hostStartMs=[],faultChecks=[]; + const sampler=setInterval(()=>{const m=process.memoryUsage();peakRss=Math.max(peakRss,m.rss);peakHeap=Math.max(peakHeap,m.heapUsed);},5); + try{ + for(let cycle=0;cycle{ + const command=c.registerCommand({id:'read',title:'Read settings'},async()=>({count:(await c.settings.read()).values.count})); + const language=c.registerLanguageProvider('hover',{languages:['plaintext']},{provide:r=>createLanguageResult(r,{text:'Synthetic hover'})}); + return {dispose(){command.dispose();language.dispose();}}; + })); + host.setDocument({uri:'memory:///fixture.txt',languageId:'plaintext',modelVersion:1,workspaceRevision:'one',text:'alpha 😀\r\nalpha'}); + observer=fixture.client.watchProject({root:'',intervalMs:250,debounceMs:20},{timeoutMs:5000});const initial=(await observer.next()).value.snapshot; + const bytes=Uint8Array.from({length:131089},(_v,n)=>(n*17+31)&255),source={byteLength:bytes.length,async read(offset,length){return bytes.slice(offset,offset+length);}}; + const upload=queue.enqueueUpload(fixture.client,source,{uploadId:randomUUID(),pluginId:'conformance-plugin',artifactSha256:fixture.artifactSha256,path:'combined.bin',expectedRevision:null}); + const preview=await previewWorkspaceEdit(fixture.client,{formatVersion:1,id:randomUUID(),title:'Independent created file',changes:[{kind:'create',path:'edited.txt',content:'reviewed synthetic edit'}]}); + const jobId=randomUUID();await fixture.client.startCommandJob('conformance-plugin','capture',{},fixture.artifactSha256,{jobId:jobId}); + const changedAt=performance.now(); + const tasks=[ + (async()=>{for(let n=0;n<40;n++){assert.equal((await host.requestLanguage('hover',{position:{line:0,character:1}})).data.text,'Synthetic hover');languageRequests++;assert.ok([2,3].includes((await host.executeCommand(manifest.id,'read',{})).count));commands++;}})(), + fixture.writeFixture('beta '+cycle), + applyWorkspaceEdit(fixture.client,preview,{journal:fixture.journal,reviewed:{planId:preview.planId,digest:preview.digest},authorize:()=>true}).then(result=>assert.equal(result.status,'completed')), + upload.result.then(result=>{assert.equal(result.commitReceipt.revision,digest(bytes));verifiedTransfers++;}), + fixture.client.waitForJob(jobId,{intervalMs:250,timeoutMs:5000}).then(result=>assert.equal(result.state,'succeeded')), + (async()=>{for(let n=0;n<20;n++){const event=await observer.next();if(event.value?.snapshot.revision!==initial.revision){watchLatencyMs.push(performance.now()-changedAt);observedChanges++;return;}}assert.fail('No project update');})(), + ]; + store.update({workspaceId:'fixture-project',scope:'workspace',expectedRevision:0,values:{count:3}}); + const outcomes=await Promise.allSettled(tasks);for(const result of outcomes)if(result.status==='rejected')throw result.reason; + await observer.return();observer=null; + const reference=await fixture.client.getJobBinaryArtifact(jobId,'binary'),destination=sink(); + const download=queue.enqueueDownload(fixture.client,reference,{sink:destination}),received=await download.result; + assert.equal(received.verification,'stored');assert.equal(digest(destination.bytes()),digest(bytes));verifiedTransfers++; + assert.equal((await fixture.client.searchText({root:'',query:'reviewed synthetic'})).total,1); + if(faults){ + fixture.failNextRequest('disconnect');await assert.rejects(fixture.client.readFile('fixture.txt'),{code:'transport_failed'});faultChecks.push('network_interruption'); + assert.equal((await fixture.client.readFile('fixture.txt')).content,'beta '+cycle); + fixture.failNextRequest('malformed');await assert.rejects(fixture.client.readFile('fixture.txt'),{code:'invalid_request'});faultChecks.push('malformed_response'); + const denied=queue.enqueueUpload(fixture.client,source,{uploadId:randomUUID(),pluginId:'conformance-plugin',artifactSha256:fixture.artifactSha256,path:'revoked.bin',expectedRevision:null,onProgress:p=>{if(p.phase==='uploading')fixture.revokeUploads();}}); + await assert.rejects(denied.result,error=>['permission_denied','unsupported'].includes(error.code));faultChecks.push('mid_transfer_revocation'); + assert.equal((await fixture.client.listFiles()).entries.some(file=>file.path==='revoked.bin'),false); + host.replaceGrants([]);await assert.rejects(host.executeCommand(manifest.id,'read',{}),error=>['disposed','permission_denied'].includes(error.code));faultChecks.push('host_revocation'); + } + await fixture.restart(); + await assert.rejects(fixture.client.readJobBinaryArtifactChunk(reference,0),{code:'generation_mismatch'}); + const recovered=await fixture.client.recoverJob('conformance-plugin',jobId,fixture.artifactSha256);assert.equal(recovered.disposition,'completed'); + assert.equal((await fixture.client.listJobHistory('conformance-plugin',fixture.artifactSha256)).items.length,1); + assert.equal(queue.inspect().active,0);assert.equal(queue.inspect().pending,0);assert.ok(queue.inspect().peakChunkBytes<=65536); + const m=fixture.metrics();requestBytes+=m.requestBytes;responseBytes+=m.responseBytes;requests+=m.requests;fixtureWriteBytes+=m.fixtureWriteBytes; + }finally{ + await observer?.return();await queue.dispose();host.dispose();store.dispose();await tick(); + const inspection=host.inspect();for(const key of ['plugins','commands','registrations','pendingActivations','pendingOperations','providerOperations','pendingJobs','binaryOperations','pendingCheckpoints','timers'])assert.equal(inspection[key],0,key); + assert.equal(queue.inspect().pending,0);assert.equal(queue.inspect().active,0);assert.equal(diagnostics.snapshot().pending,0);assert.equal(store.inspect().subscriptions,0);diagnostics.dispose();await fixture?.dispose(); + } + } + }finally{clearInterval(sampler);} + let remaining; + for(let n=0;n<20;n++){await tick();remaining=excess(beforeResources,resources());if(Object.keys(remaining).length===0)break;await new Promise(resolve=>setTimeout(resolve,5));} + assert.deepEqual(remaining,{},'SDK fixture left an active Node resource'); + const usage=process.cpuUsage(cpu),finish=process.memoryUsage(); + return {schemaVersion:1,node:process.version,platform:process.platform,arch:process.arch,cycles,faults,fixtureBytes:131089,concurrency:{transfers:2,language:1,commands:1,watch:1,jobs:1,edits:1},elapsedMs:performance.now()-start,cpuMs:(usage.user+usage.system)/1000,peakRssBytes:peakRss,peakHeapBytes:peakHeap,heapDeltaBytes:finish.heapUsed-memory.heapUsed,requestBytes,responseBytes,requests,fixtureWriteBytes,languageRequests,commands,observedChanges,verifiedTransfers,watchLatencyMs,hostStartMs,faultChecks,remainingResources:remaining}; +} +if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url))console.log(JSON.stringify(await runCombinedWorkload({faults:process.argv.includes('--faults')}),null,2)); diff --git a/examples/conformance/browser.css b/examples/conformance/browser.css new file mode 100644 index 0000000..7d8893e --- /dev/null +++ b/examples/conformance/browser.css @@ -0,0 +1 @@ +:root{color-scheme:light;--background:#f6f8fc;--panel:#fff;--text:#182738;--muted:#4a5b6e;--border:#d5deea;--accent:#155fa0;--good:#0b694d}*{box-sizing:border-box}[hidden]{display:none!important}body{margin:0;background:var(--background);color:var(--text);font:16px/1.55 system-ui,sans-serif}main{max-width:940px;margin:48px auto;padding:40px;background:var(--panel);border:1px solid var(--border);border-radius:18px}.eyebrow{font-size:12px;letter-spacing:.1em;color:var(--accent);font-weight:700}h1{font-size:clamp(26px,5vw,38px);line-height:1.2;letter-spacing:-.02em;margin:14px 0}p{color:var(--muted)}.controls{display:flex;gap:24px;align-items:center;flex-wrap:wrap;margin:26px 0}button{border:0;border-radius:8px;padding:12px 20px;background:var(--accent);color:white;font:600 16px system-ui;cursor:pointer}button:disabled{opacity:.7;cursor:wait}:focus-visible{outline:3px solid #e59d20;outline-offset:3px}label{display:flex;align-items:center;gap:8px}input{width:18px;height:18px}#status{padding:14px 18px;border:1px solid var(--border);border-radius:8px;color:var(--text);font-weight:600}dl{display:grid;grid-template-columns:auto 1fr;gap:4px 16px;font-size:14px}dt{color:var(--muted)}dd{margin:0;overflow-wrap:anywhere}.table-wrap{margin-top:24px}table{border-collapse:collapse;width:100%;font-size:14px;table-layout:fixed}caption{text-align:left;font-size:18px;font-weight:700;margin-bottom:12px}th,td{text-align:left;padding:10px 8px;border-bottom:1px solid var(--border);overflow-wrap:anywhere}th:first-child{width:48%}td[data-status=supported]{color:var(--good);font-weight:700}td[data-status=failed]{color:#ba2933;font-weight:700}.note{font-size:13px;margin-top:26px}:root[data-theme=dark]{color-scheme:dark;--background:#101923;--panel:#162432;--text:#e6edf5;--muted:#b3c1d1;--border:#354758;--accent:#307fc3;--good:#8fe0bc}@media(max-width:600px){main{margin:12px;padding:22px 16px;border-radius:12px}.eyebrow{font-size:10px}th,td{padding:9px 4px}table{font-size:12px}.controls{gap:18px}} diff --git a/examples/conformance/browser.html b/examples/conformance/browser.html new file mode 100644 index 0000000..01e91a9 --- /dev/null +++ b/examples/conformance/browser.html @@ -0,0 +1,8 @@ + +SDK host conformance +

DIGITAL DESIGN STUDIO · PLUGIN SDK

Check your host contract

Run the synthetic browser profile. Each feature reports its own support and cleanup result.

+
+

Ready. Fixtures run locally in this page.

+ +

Node workspace and development-tool fixtures are unavailable in this browser profile. Memory storage probes do not establish filesystem durability or OPFS support. These checks run trusted code; they do not provide OS isolation.

+
diff --git a/examples/conformance/browser.mjs b/examples/conformance/browser.mjs new file mode 100644 index 0000000..61f5bd0 --- /dev/null +++ b/examples/conformance/browser.mjs @@ -0,0 +1,21 @@ +import {createPluginHost} from '/sdk/index.mjs'; +import {createSettingsStore} from '/sdk/settings.mjs'; +import * as localization from '/sdk/localization.mjs'; +import * as testing from '/sdk/testing.mjs'; +import * as diagnostics from '/sdk/diagnostics.mjs'; +import {SDK_VERSION} from '/sdk/version.mjs'; +import {runConformance,CONFORMANCE_CASES} from '/sdk/conformance.mjs'; +const button=document.querySelector('#run'),status=document.querySelector('#status'); +document.querySelector('#theme').addEventListener('change',event=>{document.documentElement.dataset.theme=event.target.checked?'dark':'light';}); +button.addEventListener('click',async()=>{ + button.disabled=true;status.textContent='Running synthetic fixtures…'; + try{ + const adapters={host:createPluginHost,settings:createSettingsStore,localization,testing,diagnostics}; + const report=await runConformance({identity:{host:'Browser SDK host',version:SDK_VERSION,runtime:'Browser ESM',platform:'browser'},features:CONFORMANCE_CASES.filter(c=>Object.hasOwn(adapters,c.port)).map(c=>c.id),adapters}); + document.querySelector('#results').replaceChildren(...report.results.map(result=>{const row=document.createElement('tr');for(const key of ['id','status','cleanup']){const cell=document.createElement('td');cell.textContent=result[key];if(key==='status')cell.dataset.status=result.status;row.append(cell);}return row;})); + const identity=document.querySelector('#identity');identity.replaceChildren(...Object.entries(report.identity).flatMap(([key,value])=>{const term=document.createElement('dt'),definition=document.createElement('dd');term.textContent=key;definition.textContent=value;return [term,definition];})); + identity.hidden=false;document.querySelector('.table-wrap').hidden=false; + status.textContent=`${report.ok?'Checks passed':'Checks failed'} · ${report.summary.supported} supported · ${report.summary.unsupported} unsupported · ${report.summary.failed} failed`; + }catch{status.textContent='Checks could not complete. Review the local host adapter.';} + finally{button.disabled=false;} +}); diff --git a/examples/conformance/run.mjs b/examples/conformance/run.mjs new file mode 100644 index 0000000..5880a58 --- /dev/null +++ b/examples/conformance/run.mjs @@ -0,0 +1,6 @@ +import assert from 'node:assert/strict'; +import {runConformance,formatConformanceReport} from '@altifigence/dds-plugin-sdk/conformance'; +import {createSdkConformanceOptions} from '@altifigence/dds-plugin-sdk/conformance-node'; +const report=await runConformance(createSdkConformanceOptions()); +console.log(process.argv.includes('--json')?JSON.stringify(report):formatConformanceReport(report)); +assert.equal(report.ok,true,'See the failed feature/check in the conformance report'); diff --git a/examples/conformance/serve.mjs b/examples/conformance/serve.mjs new file mode 100644 index 0000000..a10f9a0 --- /dev/null +++ b/examples/conformance/serve.mjs @@ -0,0 +1,16 @@ +import {readFile,readdir} from 'node:fs/promises'; +import {createServer} from 'node:http'; +const root=new URL('./',import.meta.url),source=new URL('../../src/',import.meta.url),modules=new Set((await readdir(source)).filter(name=>/^[a-z0-9-]+\.mjs$/.test(name))); +const files=new Map([['/',['browser.html','text/html; charset=utf-8']],['/browser.mjs',['browser.mjs','text/javascript; charset=utf-8']],['/browser.css',['browser.css','text/css; charset=utf-8']]]); +const server=createServer(async(request,response)=>{ + try{ + if(request.method!=='GET'){response.writeHead(405).end();return;} + let entry=files.get(request.url),base=root; + if(!entry&&request.url.startsWith('/sdk/')&&modules.has(request.url.slice(5))){entry=[request.url.slice(5),'text/javascript; charset=utf-8'];base=source;} + if(!entry){response.writeHead(404).end();return;} + response.writeHead(200,{'content-type':entry[1],'cache-control':'no-store','x-content-type-options':'nosniff','content-security-policy':"default-src 'none'; script-src 'self'; style-src 'self'; connect-src 'self'; base-uri 'none'; frame-ancestors 'none'"}).end(await readFile(new URL(entry[0],base))); + }catch{response.writeHead(404).end();} +}); +await new Promise((resolve,reject)=>{server.once('error',reject);server.listen(0,'127.0.0.1',resolve);}); +console.log('Conformance browser: http://127.0.0.1:'+server.address().port); +for(const signal of ['SIGINT','SIGTERM'])process.once(signal,()=>{server.close();server.closeAllConnections();}); diff --git a/package-lock.json b/package-lock.json index eb4e8b0..7ab81ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@altifigence/dds-plugin-sdk", - "version": "0.13.0", + "version": "1.0.0-rc.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@altifigence/dds-plugin-sdk", - "version": "0.13.0", + "version": "1.0.0-rc.1", "license": "Apache-2.0", "bin": { "dds-plugin": "bin/dds-plugin.mjs" diff --git a/package.json b/package.json index ec8cc0e..bba1654 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@altifigence/dds-plugin-sdk", - "version": "0.13.0", + "version": "1.0.0-rc.1", "description": "Plugin, theme and user-owned workspace tools for Digital Design Studio", "type": "module", "license": "Apache-2.0", @@ -156,6 +156,14 @@ "./diagnostics": { "import": "./src/diagnostics.mjs", "types": "./src/diagnostics.d.mts" + }, + "./conformance": { + "types": "./src/conformance.d.mts", + "import": "./src/conformance.mjs" + }, + "./conformance-node": { + "types": "./src/conformance-node.d.mts", + "import": "./src/conformance-node.mjs" } }, "bin": { @@ -174,7 +182,8 @@ "SECURITY.md", "LICENSE", "NOTICE", - "PUBLIC_SURFACE.json" + "PUBLIC_SURFACE.json", + "API_SUPPORT.json" ], "scripts": { "example:configuration": "node examples/configuration/run.mjs", @@ -188,7 +197,7 @@ "pack:check": "node scripts/pack-check.mjs", "public:check": "node scripts/check-public-surface.mjs", "benchmark": "node scripts/benchmark.mjs", - "check": "npm run public:check && npm run schemas:check && npm test && npm run test:types && npm run example && npm run pack:check", + "check": "npm run public:check && npm run api:check && npm run schemas:check && npm test && npm run test:types && npm run example && npm run pack:check", "example:language": "node examples/hello-language/run.mjs", "example:project": "node examples/project-session/run.mjs", "example:verify": "node examples/release-verification/run.mjs", @@ -221,7 +230,13 @@ "example:language-display": "node examples/language-display/run.mjs", "example:language-display-browser": "node examples/language-display/serve.mjs", "example:devtools": "node examples/development-tools/run.mjs", - "benchmark:devtools": "node scripts/benchmark-devtools.mjs" + "benchmark:devtools": "node scripts/benchmark-devtools.mjs", + "api:write": "node scripts/api-support.mjs --write", + "api:check": "node scripts/api-support.mjs --check", + "example:conformance": "node examples/conformance/run.mjs", + "example:conformance-browser": "node examples/conformance/serve.mjs", + "example:combined": "node examples/combined-workload/run.mjs", + "benchmark:combined": "node scripts/benchmark-combined.mjs" }, "devDependencies": { "typescript": "5.9.3" diff --git a/schemas/artifact-sink-capabilities.schema.json b/schemas/artifact-sink-capabilities.schema.json index e59170e..a578377 100644 --- a/schemas/artifact-sink-capabilities.schema.json +++ b/schemas/artifact-sink-capabilities.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/artifact-sink-capabilities.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/artifact-sink-capabilities.schema.json", "title": "artifact-sink-capabilities", "type": "object", "properties": { diff --git a/schemas/artifact-storage-capabilities.schema.json b/schemas/artifact-storage-capabilities.schema.json index 8516c04..8bcb032 100644 --- a/schemas/artifact-storage-capabilities.schema.json +++ b/schemas/artifact-storage-capabilities.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/artifact-storage-capabilities.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/artifact-storage-capabilities.schema.json", "title": "artifact-storage-capabilities", "oneOf": [ { diff --git a/schemas/artifact-transfer-receipt.schema.json b/schemas/artifact-transfer-receipt.schema.json index 453791c..ce4f27a 100644 --- a/schemas/artifact-transfer-receipt.schema.json +++ b/schemas/artifact-transfer-receipt.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/artifact-transfer-receipt.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/artifact-transfer-receipt.schema.json", "title": "artifact-transfer-receipt", "type": "object", "properties": { diff --git a/schemas/binary-artifact-capabilities.schema.json b/schemas/binary-artifact-capabilities.schema.json index d1bdaba..792a3e5 100644 --- a/schemas/binary-artifact-capabilities.schema.json +++ b/schemas/binary-artifact-capabilities.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/binary-artifact-capabilities.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/binary-artifact-capabilities.schema.json", "title": "binary-artifact-capabilities", "type": "object", "properties": { diff --git a/schemas/binary-artifact-chunk.schema.json b/schemas/binary-artifact-chunk.schema.json index e79995a..bd711ee 100644 --- a/schemas/binary-artifact-chunk.schema.json +++ b/schemas/binary-artifact-chunk.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/binary-artifact-chunk.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/binary-artifact-chunk.schema.json", "title": "binary-artifact-chunk", "type": "object", "properties": { diff --git a/schemas/binary-artifact-list.schema.json b/schemas/binary-artifact-list.schema.json index 7786f41..44fe1c2 100644 --- a/schemas/binary-artifact-list.schema.json +++ b/schemas/binary-artifact-list.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/binary-artifact-list.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/binary-artifact-list.schema.json", "title": "binary-artifact-list", "type": "object", "properties": { diff --git a/schemas/binary-artifact-reference.schema.json b/schemas/binary-artifact-reference.schema.json index d1517a3..af8b0ed 100644 --- a/schemas/binary-artifact-reference.schema.json +++ b/schemas/binary-artifact-reference.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/binary-artifact-reference.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/binary-artifact-reference.schema.json", "title": "binary-artifact-reference", "type": "object", "properties": { diff --git a/schemas/binary-artifact.schema.json b/schemas/binary-artifact.schema.json index 09f3563..8b4f681 100644 --- a/schemas/binary-artifact.schema.json +++ b/schemas/binary-artifact.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/binary-artifact.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/binary-artifact.schema.json", "title": "binary-artifact", "type": "object", "properties": { diff --git a/schemas/browser-artifact-checkpoint.schema.json b/schemas/browser-artifact-checkpoint.schema.json index 5b6b745..0b0e6d6 100644 --- a/schemas/browser-artifact-checkpoint.schema.json +++ b/schemas/browser-artifact-checkpoint.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/browser-artifact-checkpoint.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/browser-artifact-checkpoint.schema.json", "title": "browser-artifact-checkpoint", "type": "object", "properties": { diff --git a/schemas/code-action.schema.json b/schemas/code-action.schema.json index 0a8bbe7..fb4c311 100644 --- a/schemas/code-action.schema.json +++ b/schemas/code-action.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/code-action.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/code-action.schema.json", "title": "code-action", "type": "object", "properties": { diff --git a/schemas/command.schema.json b/schemas/command.schema.json index c447aa7..fdf2459 100644 --- a/schemas/command.schema.json +++ b/schemas/command.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/command.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/command.schema.json", "title": "command", "type": "object", "properties": { diff --git a/schemas/completion-item.schema.json b/schemas/completion-item.schema.json index 5f95509..0520725 100644 --- a/schemas/completion-item.schema.json +++ b/schemas/completion-item.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/completion-item.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/completion-item.schema.json", "title": "completion-item", "type": "object", "properties": { diff --git a/schemas/conformance-report.schema.json b/schemas/conformance-report.schema.json new file mode 100644 index 0000000..074a78e --- /dev/null +++ b/schemas/conformance-report.schema.json @@ -0,0 +1,226 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/conformance-report.schema.json", + "title": "conformance-report", + "type": "object", + "properties": { + "schemaVersion": { + "const": 1 + }, + "suiteVersion": { + "const": 1 + }, + "sdkVersion": { + "type": "string", + "maxLength": 128, + "pattern": "^[A-Za-z0-9][A-Za-z0-9 ._+()-]*$" + }, + "identity": { + "type": "object", + "properties": { + "host": { + "type": "string", + "maxLength": 128, + "pattern": "^[A-Za-z0-9][A-Za-z0-9 ._+()-]*$" + }, + "version": { + "type": "string", + "maxLength": 128, + "pattern": "^[A-Za-z0-9][A-Za-z0-9 ._+()-]*$" + }, + "runtime": { + "type": "string", + "maxLength": 128, + "pattern": "^[A-Za-z0-9][A-Za-z0-9 ._+()-]*$" + }, + "platform": { + "type": "string", + "maxLength": 128, + "pattern": "^[A-Za-z0-9][A-Za-z0-9 ._+()-]*$" + } + }, + "required": [ + "host", + "version", + "runtime", + "platform" + ], + "additionalProperties": false + }, + "declaredFeatures": { + "type": "array", + "items": { + "type": "string", + "maxLength": 128, + "pattern": "^[A-Za-z0-9][A-Za-z0-9 ._+()-]*$" + }, + "maxItems": 64, + "uniqueItems": true + }, + "unknownFeatures": { + "type": "array", + "items": { + "type": "string", + "maxLength": 128, + "pattern": "^[A-Za-z0-9][A-Za-z0-9 ._+()-]*$" + }, + "maxItems": 64, + "uniqueItems": true + }, + "requiredFeatures": { + "type": "array", + "items": { + "enum": [ + "commands", + "permissions", + "cancellation", + "language", + "language-editing", + "language-display", + "settings", + "jobs", + "storage-history", + "files", + "projects", + "artifacts", + "uploads-transfers", + "workspace-edits", + "localization", + "testing-replay", + "diagnostics", + "development-tools" + ] + }, + "maxItems": 64, + "uniqueItems": true + }, + "results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "enum": [ + "commands", + "permissions", + "cancellation", + "language", + "language-editing", + "language-display", + "settings", + "jobs", + "storage-history", + "files", + "projects", + "artifacts", + "uploads-transfers", + "workspace-edits", + "localization", + "testing-replay", + "diagnostics", + "development-tools" + ] + }, + "status": { + "enum": [ + "supported", + "unsupported", + "failed" + ] + }, + "reason": { + "enum": [ + "passed", + "not_declared", + "missing_adapter", + "assertion", + "provider_error", + "timed_out", + "cancelled", + "cleanup_failed", + "aborted_after_timeout" + ] + }, + "check": { + "anyOf": [ + { + "type": "string", + "maxLength": 128, + "pattern": "^[A-Za-z0-9][A-Za-z0-9 ._+()-]*$" + }, + { + "type": "null" + } + ] + }, + "durationMs": { + "type": "number", + "minimum": 0 + }, + "cleanup": { + "enum": [ + "complete", + "pending", + "failed", + "not-run" + ] + } + }, + "required": [ + "id", + "status", + "reason", + "check", + "durationMs", + "cleanup" + ], + "additionalProperties": false + }, + "minItems": 18, + "maxItems": 18 + }, + "summary": { + "type": "object", + "properties": { + "supported": { + "type": "integer", + "minimum": 0, + "maximum": 18 + }, + "unsupported": { + "type": "integer", + "minimum": 0, + "maximum": 18 + }, + "failed": { + "type": "integer", + "minimum": 0, + "maximum": 18 + } + }, + "required": [ + "supported", + "unsupported", + "failed" + ], + "additionalProperties": false + }, + "ok": { + "type": "boolean" + } + }, + "required": [ + "schemaVersion", + "suiteVersion", + "sdkVersion", + "identity", + "declaredFeatures", + "unknownFeatures", + "requiredFeatures", + "results", + "summary", + "ok" + ], + "additionalProperties": false, + "$comment": "Runtime also validates exact case order, counts, unknown/required declarations and status/cleanup/ok consistency." +} diff --git a/schemas/data-schema.schema.json b/schemas/data-schema.schema.json index 47d22f6..a91ca32 100644 --- a/schemas/data-schema.schema.json +++ b/schemas/data-schema.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/data-schema.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/data-schema.schema.json", "title": "data-schema", "type": "object", "properties": { diff --git a/schemas/development-definition.schema.json b/schemas/development-definition.schema.json index b9c824d..4a238b3 100644 --- a/schemas/development-definition.schema.json +++ b/schemas/development-definition.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/development-definition.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/development-definition.schema.json", "title": "development-definition", "type": "object", "properties": { @@ -20,18 +20,18 @@ "commands": { "type": "array", "items": { - "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/command.schema.json" + "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/command.schema.json" }, "maxItems": 64 }, "settings": { - "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/settings-definition.schema.json" + "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/settings-definition.schema.json" }, "catalog": { - "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/locale-catalog.schema.json" + "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/locale-catalog.schema.json" }, "theme": { - "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/theme.schema.json" + "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/theme.schema.json" } }, "required": [ diff --git a/schemas/diagnostic-report.schema.json b/schemas/diagnostic-report.schema.json index 33dbda3..29c411d 100644 --- a/schemas/diagnostic-report.schema.json +++ b/schemas/diagnostic-report.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/diagnostic-report.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/diagnostic-report.schema.json", "title": "diagnostic-report", "type": "object", "properties": { diff --git a/schemas/diagnostics-request.schema.json b/schemas/diagnostics-request.schema.json index 4cb7ea4..06e2d66 100644 --- a/schemas/diagnostics-request.schema.json +++ b/schemas/diagnostics-request.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/diagnostics-request.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/diagnostics-request.schema.json", "title": "diagnostics-request", "type": "object", "properties": { diff --git a/schemas/diagnostics-result.schema.json b/schemas/diagnostics-result.schema.json index abe69bb..43360e7 100644 --- a/schemas/diagnostics-result.schema.json +++ b/schemas/diagnostics-result.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/diagnostics-result.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/diagnostics-result.schema.json", "title": "diagnostics-result", "type": "object", "properties": { diff --git a/schemas/display-metadata.schema.json b/schemas/display-metadata.schema.json index 0777013..871fd9c 100644 --- a/schemas/display-metadata.schema.json +++ b/schemas/display-metadata.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/display-metadata.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/display-metadata.schema.json", "title": "display-metadata", "type": "object", "properties": { diff --git a/schemas/document-symbol-tree.schema.json b/schemas/document-symbol-tree.schema.json index aeb9557..6cf8cb8 100644 --- a/schemas/document-symbol-tree.schema.json +++ b/schemas/document-symbol-tree.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/document-symbol-tree.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/document-symbol-tree.schema.json", "title": "document-symbol-tree", "type": "array", "items": { diff --git a/schemas/error.schema.json b/schemas/error.schema.json index acef0c0..73affab 100644 --- a/schemas/error.schema.json +++ b/schemas/error.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/error.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/error.schema.json", "title": "error", "type": "object", "properties": { diff --git a/schemas/folding-ranges.schema.json b/schemas/folding-ranges.schema.json index 53b09a8..9afec1c 100644 --- a/schemas/folding-ranges.schema.json +++ b/schemas/folding-ranges.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/folding-ranges.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/folding-ranges.schema.json", "title": "folding-ranges", "type": "array", "items": { diff --git a/schemas/formatting-options.schema.json b/schemas/formatting-options.schema.json index 9ce2046..25b063a 100644 --- a/schemas/formatting-options.schema.json +++ b/schemas/formatting-options.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/formatting-options.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/formatting-options.schema.json", "title": "formatting-options", "type": "object", "properties": { diff --git a/schemas/generation-receipt.schema.json b/schemas/generation-receipt.schema.json index 16c8303..c8563c8 100644 --- a/schemas/generation-receipt.schema.json +++ b/schemas/generation-receipt.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/generation-receipt.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/generation-receipt.schema.json", "title": "generation-receipt", "type": "object", "properties": { diff --git a/schemas/generation-report.schema.json b/schemas/generation-report.schema.json index efb3d4a..c800081 100644 --- a/schemas/generation-report.schema.json +++ b/schemas/generation-report.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/generation-report.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/generation-report.schema.json", "title": "generation-report", "type": "object", "properties": { diff --git a/schemas/inlay-hints.schema.json b/schemas/inlay-hints.schema.json index a9fb456..61abf27 100644 --- a/schemas/inlay-hints.schema.json +++ b/schemas/inlay-hints.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/inlay-hints.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/inlay-hints.schema.json", "title": "inlay-hints", "type": "array", "items": { diff --git a/schemas/job-artifact-content.schema.json b/schemas/job-artifact-content.schema.json index f826629..805e446 100644 --- a/schemas/job-artifact-content.schema.json +++ b/schemas/job-artifact-content.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/job-artifact-content.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/job-artifact-content.schema.json", "title": "job-artifact-content", "type": "object", "properties": { diff --git a/schemas/job-capabilities.schema.json b/schemas/job-capabilities.schema.json index a720f6c..37784b6 100644 --- a/schemas/job-capabilities.schema.json +++ b/schemas/job-capabilities.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/job-capabilities.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/job-capabilities.schema.json", "title": "job-capabilities", "type": "object", "properties": { diff --git a/schemas/job-events.schema.json b/schemas/job-events.schema.json index a098d6f..9033a25 100644 --- a/schemas/job-events.schema.json +++ b/schemas/job-events.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/job-events.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/job-events.schema.json", "title": "job-events", "type": "object", "properties": { diff --git a/schemas/job-history-page.schema.json b/schemas/job-history-page.schema.json index 66adb78..2ef9b9d 100644 --- a/schemas/job-history-page.schema.json +++ b/schemas/job-history-page.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/job-history-page.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/job-history-page.schema.json", "title": "job-history-page", "type": "object", "properties": { diff --git a/schemas/job-history-query.schema.json b/schemas/job-history-query.schema.json index e5eb5f2..2308ce2 100644 --- a/schemas/job-history-query.schema.json +++ b/schemas/job-history-query.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/job-history-query.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/job-history-query.schema.json", "title": "job-history-query", "type": "object", "properties": { diff --git a/schemas/job-options.schema.json b/schemas/job-options.schema.json index 6cc8930..a643efb 100644 --- a/schemas/job-options.schema.json +++ b/schemas/job-options.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/job-options.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/job-options.schema.json", "title": "job-options", "type": "object", "properties": { diff --git a/schemas/job-recovery.schema.json b/schemas/job-recovery.schema.json index 7f8ca44..119d5a0 100644 --- a/schemas/job-recovery.schema.json +++ b/schemas/job-recovery.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/job-recovery.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/job-recovery.schema.json", "title": "job-recovery", "oneOf": [ { @@ -125,7 +125,7 @@ "uniqueItems": true }, "snapshot": { - "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/job-snapshot.schema.json" + "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/job-snapshot.schema.json" }, "events": { "type": "array", @@ -691,7 +691,7 @@ "uniqueItems": true }, "snapshot": { - "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/job-snapshot.schema.json" + "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/job-snapshot.schema.json" }, "events": { "type": "array", @@ -1257,7 +1257,7 @@ "uniqueItems": true }, "snapshot": { - "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/job-snapshot.schema.json" + "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/job-snapshot.schema.json" }, "events": { "type": "array", diff --git a/schemas/job-snapshot.schema.json b/schemas/job-snapshot.schema.json index 73808f7..bab8505 100644 --- a/schemas/job-snapshot.schema.json +++ b/schemas/job-snapshot.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/job-snapshot.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/job-snapshot.schema.json", "title": "job-snapshot", "oneOf": [ { @@ -305,7 +305,7 @@ "const": "succeeded" }, "result": { - "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/json-value.schema.json" + "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/json-value.schema.json" } }, "required": [ diff --git a/schemas/job-storage-capabilities.schema.json b/schemas/job-storage-capabilities.schema.json index 3aafaeb..9ec2c1e 100644 --- a/schemas/job-storage-capabilities.schema.json +++ b/schemas/job-storage-capabilities.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/job-storage-capabilities.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/job-storage-capabilities.schema.json", "title": "job-storage-capabilities", "oneOf": [ { diff --git a/schemas/job-store-identity.schema.json b/schemas/job-store-identity.schema.json index f934e3b..f198115 100644 --- a/schemas/job-store-identity.schema.json +++ b/schemas/job-store-identity.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/job-store-identity.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/job-store-identity.schema.json", "title": "job-store-identity", "type": "object", "properties": { diff --git a/schemas/json-value.schema.json b/schemas/json-value.schema.json index 0f1469c..13f6119 100644 --- a/schemas/json-value.schema.json +++ b/schemas/json-value.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/json-value.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/json-value.schema.json", "title": "json-value", "$ref": "#/$defs/value", "x-maxUtf8Bytes": 262144, diff --git a/schemas/language-capabilities.schema.json b/schemas/language-capabilities.schema.json index 8769d3c..dad147c 100644 --- a/schemas/language-capabilities.schema.json +++ b/schemas/language-capabilities.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/language-capabilities.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/language-capabilities.schema.json", "title": "language-capabilities", "type": "object", "properties": { diff --git a/schemas/language-request.schema.json b/schemas/language-request.schema.json index 3f4ba08..d16eef6 100644 --- a/schemas/language-request.schema.json +++ b/schemas/language-request.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/language-request.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/language-request.schema.json", "title": "language-request", "oneOf": [ { diff --git a/schemas/language-result.schema.json b/schemas/language-result.schema.json index 583d6f9..ca28b8c 100644 --- a/schemas/language-result.schema.json +++ b/schemas/language-result.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/language-result.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/language-result.schema.json", "title": "language-result", "oneOf": [ { @@ -3178,7 +3178,7 @@ "const": "document-symbol-tree" }, "data": { - "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/document-symbol-tree.schema.json" + "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/document-symbol-tree.schema.json" } }, "required": [ diff --git a/schemas/language-snippet.schema.json b/schemas/language-snippet.schema.json index a8917f8..2330902 100644 --- a/schemas/language-snippet.schema.json +++ b/schemas/language-snippet.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/language-snippet.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/language-snippet.schema.json", "title": "language-snippet", "type": "string", "minLength": 0, diff --git a/schemas/locale-catalog.schema.json b/schemas/locale-catalog.schema.json index 31eca23..1a0fc1f 100644 --- a/schemas/locale-catalog.schema.json +++ b/schemas/locale-catalog.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/locale-catalog.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/locale-catalog.schema.json", "title": "locale-catalog", "type": "object", "properties": { diff --git a/schemas/localized-message.schema.json b/schemas/localized-message.schema.json index da7d60d..80e3839 100644 --- a/schemas/localized-message.schema.json +++ b/schemas/localized-message.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/localized-message.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/localized-message.schema.json", "title": "localized-message", "type": "object", "properties": { diff --git a/schemas/manifest.schema.json b/schemas/manifest.schema.json index 5c188ca..0fdde73 100644 --- a/schemas/manifest.schema.json +++ b/schemas/manifest.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/manifest.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/manifest.schema.json", "title": "manifest", "properties": { "id": { diff --git a/schemas/memory-job-snapshot.schema.json b/schemas/memory-job-snapshot.schema.json index 2d929c4..56224b3 100644 --- a/schemas/memory-job-snapshot.schema.json +++ b/schemas/memory-job-snapshot.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/memory-job-snapshot.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/memory-job-snapshot.schema.json", "title": "memory-job-snapshot", "type": "object", "properties": { @@ -8,12 +8,12 @@ "const": 1 }, "identity": { - "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/job-store-identity.schema.json" + "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/job-store-identity.schema.json" }, "records": { "type": "array", "items": { - "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/stored-job.schema.json" + "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/stored-job.schema.json" }, "maxItems": 256 }, diff --git a/schemas/plugin-plan.schema.json b/schemas/plugin-plan.schema.json index 3143942..1351bd2 100644 --- a/schemas/plugin-plan.schema.json +++ b/schemas/plugin-plan.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/plugin-plan.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/plugin-plan.schema.json", "title": "plugin-plan", "type": "object", "properties": { diff --git a/schemas/project-entry.schema.json b/schemas/project-entry.schema.json index 9c50f54..556b474 100644 --- a/schemas/project-entry.schema.json +++ b/schemas/project-entry.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/project-entry.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/project-entry.schema.json", "title": "project-entry", "type": "object", "properties": { diff --git a/schemas/project-query-capabilities.schema.json b/schemas/project-query-capabilities.schema.json index 68af61c..958ad48 100644 --- a/schemas/project-query-capabilities.schema.json +++ b/schemas/project-query-capabilities.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/project-query-capabilities.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/project-query-capabilities.schema.json", "title": "project-query-capabilities", "type": "object", "properties": { diff --git a/schemas/project-query-options.schema.json b/schemas/project-query-options.schema.json index 56b7e8e..f92601e 100644 --- a/schemas/project-query-options.schema.json +++ b/schemas/project-query-options.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/project-query-options.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/project-query-options.schema.json", "title": "project-query-options", "oneOf": [ { diff --git a/schemas/project-query-page.schema.json b/schemas/project-query-page.schema.json index 06b73a0..b32e656 100644 --- a/schemas/project-query-page.schema.json +++ b/schemas/project-query-page.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/project-query-page.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/project-query-page.schema.json", "title": "project-query-page", "type": "object", "properties": { diff --git a/schemas/project-snapshot.schema.json b/schemas/project-snapshot.schema.json index 93e5f76..a6870b3 100644 --- a/schemas/project-snapshot.schema.json +++ b/schemas/project-snapshot.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/project-snapshot.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/project-snapshot.schema.json", "title": "project-snapshot", "type": "object", "properties": { diff --git a/schemas/project-watch-capabilities.schema.json b/schemas/project-watch-capabilities.schema.json index 6d53918..9ad4df2 100644 --- a/schemas/project-watch-capabilities.schema.json +++ b/schemas/project-watch-capabilities.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/project-watch-capabilities.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/project-watch-capabilities.schema.json", "title": "project-watch-capabilities", "type": "object", "properties": { diff --git a/schemas/project-watch-event.schema.json b/schemas/project-watch-event.schema.json index 45364b1..c8354b9 100644 --- a/schemas/project-watch-event.schema.json +++ b/schemas/project-watch-event.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/project-watch-event.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/project-watch-event.schema.json", "title": "project-watch-event", "type": "object", "properties": { diff --git a/schemas/project-watch-options.schema.json b/schemas/project-watch-options.schema.json index 4ce6736..0ae3e2a 100644 --- a/schemas/project-watch-options.schema.json +++ b/schemas/project-watch-options.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/project-watch-options.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/project-watch-options.schema.json", "title": "project-watch-options", "type": "object", "properties": { diff --git a/schemas/secret-reference.schema.json b/schemas/secret-reference.schema.json index c15c517..9bcd6b1 100644 --- a/schemas/secret-reference.schema.json +++ b/schemas/secret-reference.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/secret-reference.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/secret-reference.schema.json", "title": "secret-reference", "type": "object", "properties": { diff --git a/schemas/secret-scope.schema.json b/schemas/secret-scope.schema.json index db693d0..a669526 100644 --- a/schemas/secret-scope.schema.json +++ b/schemas/secret-scope.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/secret-scope.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/secret-scope.schema.json", "title": "secret-scope", "type": "object", "properties": { diff --git a/schemas/semantic-legend.schema.json b/schemas/semantic-legend.schema.json index d549254..5506c66 100644 --- a/schemas/semantic-legend.schema.json +++ b/schemas/semantic-legend.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/semantic-legend.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/semantic-legend.schema.json", "title": "semantic-legend", "type": "object", "properties": { diff --git a/schemas/semantic-tokens-delta.schema.json b/schemas/semantic-tokens-delta.schema.json index 64fa666..0ad6be9 100644 --- a/schemas/semantic-tokens-delta.schema.json +++ b/schemas/semantic-tokens-delta.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/semantic-tokens-delta.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/semantic-tokens-delta.schema.json", "title": "semantic-tokens-delta", "type": "object", "properties": { diff --git a/schemas/semantic-tokens.schema.json b/schemas/semantic-tokens.schema.json index 4f5881f..ce20e9c 100644 --- a/schemas/semantic-tokens.schema.json +++ b/schemas/semantic-tokens.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/semantic-tokens.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/semantic-tokens.schema.json", "title": "semantic-tokens", "type": "object", "properties": { diff --git a/schemas/settings-definition.schema.json b/schemas/settings-definition.schema.json index 79b55aa..73da011 100644 --- a/schemas/settings-definition.schema.json +++ b/schemas/settings-definition.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/settings-definition.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/settings-definition.schema.json", "title": "settings-definition", "type": "object", "properties": { diff --git a/schemas/settings-snapshot.schema.json b/schemas/settings-snapshot.schema.json index 13b3689..0cc38ed 100644 --- a/schemas/settings-snapshot.schema.json +++ b/schemas/settings-snapshot.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/settings-snapshot.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/settings-snapshot.schema.json", "title": "settings-snapshot", "type": "object", "properties": { diff --git a/schemas/settings-state.schema.json b/schemas/settings-state.schema.json index 6753225..80908e5 100644 --- a/schemas/settings-state.schema.json +++ b/schemas/settings-state.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/settings-state.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/settings-state.schema.json", "title": "settings-state", "type": "object", "properties": { diff --git a/schemas/signature-help.schema.json b/schemas/signature-help.schema.json index a147c35..a774eab 100644 --- a/schemas/signature-help.schema.json +++ b/schemas/signature-help.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/signature-help.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/signature-help.schema.json", "title": "signature-help", "oneOf": [ { diff --git a/schemas/stored-artifact-chunk.schema.json b/schemas/stored-artifact-chunk.schema.json index bee9a7b..4b1288c 100644 --- a/schemas/stored-artifact-chunk.schema.json +++ b/schemas/stored-artifact-chunk.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/stored-artifact-chunk.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/stored-artifact-chunk.schema.json", "title": "stored-artifact-chunk", "type": "object", "properties": { diff --git a/schemas/stored-artifact-list.schema.json b/schemas/stored-artifact-list.schema.json index bb89f7a..7d1b55a 100644 --- a/schemas/stored-artifact-list.schema.json +++ b/schemas/stored-artifact-list.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/stored-artifact-list.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/stored-artifact-list.schema.json", "title": "stored-artifact-list", "type": "object", "properties": { diff --git a/schemas/stored-artifact-reference.schema.json b/schemas/stored-artifact-reference.schema.json index 58851c9..eea3ba8 100644 --- a/schemas/stored-artifact-reference.schema.json +++ b/schemas/stored-artifact-reference.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/stored-artifact-reference.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/stored-artifact-reference.schema.json", "title": "stored-artifact-reference", "type": "object", "properties": { diff --git a/schemas/stored-artifact.schema.json b/schemas/stored-artifact.schema.json index 934b85e..bd2fbb5 100644 --- a/schemas/stored-artifact.schema.json +++ b/schemas/stored-artifact.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/stored-artifact.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/stored-artifact.schema.json", "title": "stored-artifact", "type": "object", "properties": { diff --git a/schemas/stored-job.schema.json b/schemas/stored-job.schema.json index 72e03ae..df1d9be 100644 --- a/schemas/stored-job.schema.json +++ b/schemas/stored-job.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/stored-job.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/stored-job.schema.json", "title": "stored-job", "type": "object", "properties": { @@ -79,7 +79,7 @@ "uniqueItems": true }, "snapshot": { - "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/job-snapshot.schema.json" + "$ref": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/job-snapshot.schema.json" }, "events": { "type": "array", diff --git a/schemas/test-trace.schema.json b/schemas/test-trace.schema.json index 1d1b5d6..92ce60d 100644 --- a/schemas/test-trace.schema.json +++ b/schemas/test-trace.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/test-trace.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/test-trace.schema.json", "title": "test-trace", "type": "object", "properties": { diff --git a/schemas/transfer-queue-options.schema.json b/schemas/transfer-queue-options.schema.json index a2df50f..de8c6e2 100644 --- a/schemas/transfer-queue-options.schema.json +++ b/schemas/transfer-queue-options.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/transfer-queue-options.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/transfer-queue-options.schema.json", "title": "transfer-queue-options", "type": "object", "properties": { diff --git a/schemas/transfer-snapshot.schema.json b/schemas/transfer-snapshot.schema.json index 2b02d5e..c8ed6d4 100644 --- a/schemas/transfer-snapshot.schema.json +++ b/schemas/transfer-snapshot.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/transfer-snapshot.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/transfer-snapshot.schema.json", "title": "transfer-snapshot", "type": "object", "properties": { diff --git a/schemas/upload-capabilities.schema.json b/schemas/upload-capabilities.schema.json index ab0e08e..15196e5 100644 --- a/schemas/upload-capabilities.schema.json +++ b/schemas/upload-capabilities.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/upload-capabilities.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/upload-capabilities.schema.json", "title": "upload-capabilities", "type": "object", "properties": { diff --git a/schemas/upload-chunk.schema.json b/schemas/upload-chunk.schema.json index 13d1a92..ee66578 100644 --- a/schemas/upload-chunk.schema.json +++ b/schemas/upload-chunk.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/upload-chunk.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/upload-chunk.schema.json", "title": "upload-chunk", "type": "object", "properties": { diff --git a/schemas/upload-reference.schema.json b/schemas/upload-reference.schema.json index c535ee9..dd072f7 100644 --- a/schemas/upload-reference.schema.json +++ b/schemas/upload-reference.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/upload-reference.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/upload-reference.schema.json", "title": "upload-reference", "type": "object", "properties": { diff --git a/schemas/upload-spec.schema.json b/schemas/upload-spec.schema.json index f000a6b..223aef7 100644 --- a/schemas/upload-spec.schema.json +++ b/schemas/upload-spec.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/upload-spec.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/upload-spec.schema.json", "title": "upload-spec", "type": "object", "properties": { diff --git a/schemas/upload-status.schema.json b/schemas/upload-status.schema.json index 97430af..ddfab6c 100644 --- a/schemas/upload-status.schema.json +++ b/schemas/upload-status.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/upload-status.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/upload-status.schema.json", "title": "upload-status", "type": "object", "properties": { diff --git a/schemas/workspace-conditional-file.schema.json b/schemas/workspace-conditional-file.schema.json index 71d0af9..d708f68 100644 --- a/schemas/workspace-conditional-file.schema.json +++ b/schemas/workspace-conditional-file.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/workspace-conditional-file.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/workspace-conditional-file.schema.json", "title": "workspace-conditional-file", "oneOf": [ { diff --git a/schemas/workspace-edit-preview.schema.json b/schemas/workspace-edit-preview.schema.json index c712b87..cf31dea 100644 --- a/schemas/workspace-edit-preview.schema.json +++ b/schemas/workspace-edit-preview.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/workspace-edit-preview.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/workspace-edit-preview.schema.json", "title": "workspace-edit-preview", "type": "object", "properties": { diff --git a/schemas/workspace-edit-receipt.schema.json b/schemas/workspace-edit-receipt.schema.json index c3ea446..4b9ca9f 100644 --- a/schemas/workspace-edit-receipt.schema.json +++ b/schemas/workspace-edit-receipt.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/workspace-edit-receipt.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/workspace-edit-receipt.schema.json", "title": "workspace-edit-receipt", "type": "object", "properties": { diff --git a/schemas/workspace-edit-record.schema.json b/schemas/workspace-edit-record.schema.json index 82ad500..caf01a6 100644 --- a/schemas/workspace-edit-record.schema.json +++ b/schemas/workspace-edit-record.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/workspace-edit-record.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/workspace-edit-record.schema.json", "title": "workspace-edit-record", "type": "object", "properties": { diff --git a/schemas/workspace-edit.schema.json b/schemas/workspace-edit.schema.json index cc550f7..9c69a51 100644 --- a/schemas/workspace-edit.schema.json +++ b/schemas/workspace-edit.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/workspace-edit.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/workspace-edit.schema.json", "title": "workspace-edit", "type": "object", "properties": { diff --git a/schemas/workspace-file-capabilities.schema.json b/schemas/workspace-file-capabilities.schema.json index 50e5d70..3a35bd6 100644 --- a/schemas/workspace-file-capabilities.schema.json +++ b/schemas/workspace-file-capabilities.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/workspace-file-capabilities.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/workspace-file-capabilities.schema.json", "title": "workspace-file-capabilities", "type": "object", "properties": { diff --git a/schemas/workspace-file-revision.schema.json b/schemas/workspace-file-revision.schema.json index 1530501..48ac71f 100644 --- a/schemas/workspace-file-revision.schema.json +++ b/schemas/workspace-file-revision.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/workspace-file-revision.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/workspace-file-revision.schema.json", "title": "workspace-file-revision", "type": "object", "properties": { diff --git a/schemas/workspace-project-capabilities.schema.json b/schemas/workspace-project-capabilities.schema.json index 78cf90f..1649d41 100644 --- a/schemas/workspace-project-capabilities.schema.json +++ b/schemas/workspace-project-capabilities.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/workspace-project-capabilities.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/workspace-project-capabilities.schema.json", "title": "workspace-project-capabilities", "type": "object", "properties": { diff --git a/schemas/workspace-project-poll.schema.json b/schemas/workspace-project-poll.schema.json index 215f8ca..5aa2b5c 100644 --- a/schemas/workspace-project-poll.schema.json +++ b/schemas/workspace-project-poll.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v0.13.0/schemas/workspace-project-poll.schema.json", + "$id": "https://github.com/Altifigence/dds-plugin-sdk/blob/v1.0.0-rc.1/schemas/workspace-project-poll.schema.json", "title": "workspace-project-poll", "type": "object", "properties": { diff --git a/scripts/api-support.mjs b/scripts/api-support.mjs new file mode 100644 index 0000000..19a3999 --- /dev/null +++ b/scripts/api-support.mjs @@ -0,0 +1,29 @@ +import {readFile,writeFile} from 'node:fs/promises'; +import {createHash} from 'node:crypto'; +import ts from 'typescript'; +import {SDK_PLUGIN_PEER_RANGE} from '../src/version.mjs'; +const mode=process.argv[2];if(!['--write','--check'].includes(mode))throw Error('Use --write or --check'); +const root=new URL('../',import.meta.url),pkg=JSON.parse(await readFile(new URL('package.json',root),'utf8')); +const optional=new Set(['./workspace-node','./workspace-client','./jobs','./artifacts','./job-storage','./job-history','./job-storage-node','./artifact-storage','./artifact-storage-node','./workspace-edits','./workspace-edits-node','./project-watch','./project-watch-node','./project-query','./project-query-node','./artifact-transfer','./artifact-transfer-browser','./uploads','./uploads-node','./uploads-browser','./artifact-resume-browser','./transfer-queue','./settings','./secrets']); +const nodeOnly=new Set(['./publishing','./devtools','./workspace-node','./job-storage-node','./artifact-storage-node','./workspace-edits-node','./project-watch-node','./project-query-node','./uploads-node','./conformance-node']); +function typeNames(source){ + const file=ts.createSourceFile('entry.d.mts',source,ts.ScriptTarget.Latest,true),names=new Set(); + for(const statement of file.statements){ + if(ts.isExportDeclaration(statement)&&statement.exportClause&&ts.isNamedExports(statement.exportClause))for(const entry of statement.exportClause.elements)names.add(entry.name.text); + if(statement.modifiers?.some(m=>m.kind===ts.SyntaxKind.ExportKeyword)){ + if(statement.name)names.add(statement.name.text); + if(ts.isVariableStatement(statement))for(const declaration of statement.declarationList.declarations)if(ts.isIdentifier(declaration.name))names.add(declaration.name.text); + } + } + return [...names].sort(); +} +const exports=[]; +for(const [entry,files] of Object.entries(pkg.exports).sort(([a],[b])=>a.localeCompare(b,'en'))){ + if(typeof files==='string'){exports.push({entry,stability:'metadata',availability:'data',environments:['node','browser'],runtimeNames:[],declarationNames:[],declarationsSha256:null});continue;} + const body=(await readFile(new URL(files.types,root),'utf8')).replaceAll('\r\n','\n'),runtimeNames=Object.keys(await import(new URL(files.import,root))).sort(); + exports.push({entry,stability:entry==='./artifact-resume-browser'?'experimental':'stable',availability:optional.has(entry)?'optional':'library',environments:nodeOnly.has(entry)?['node']:['node','browser'],runtimeNames,declarationNames:typeNames(body),declarationsSha256:createHash('sha256').update(body).digest('hex')}); +} +const value={schemaVersion:1,sdkVersion:pkg.version,releaseStatus:pkg.version.includes('-')?'candidate':'stable',pluginPeerRange:SDK_PLUGIN_PEER_RANGE,policy:'docs/SUPPORT_POLICY.md',exports}; +const expected=JSON.stringify(value,null,2)+'\n',destination=new URL('API_SUPPORT.json',root); +if(mode==='--write')await writeFile(destination,expected);else if(await readFile(destination,'utf8')!==expected)throw Error('Public API inventory drift; review and run npm run api:write'); +console.log(`API inventory ${mode==='--write'?'generated':'verified'}: ${exports.length} exports; ${exports.reduce((n,e)=>n+e.runtimeNames.length,0)} runtime names; ${exports.reduce((n,e)=>n+e.declarationNames.length,0)} declarations.`); diff --git a/scripts/benchmark-combined-worker.mjs b/scripts/benchmark-combined-worker.mjs new file mode 100644 index 0000000..4b27fb5 --- /dev/null +++ b/scripts/benchmark-combined-worker.mjs @@ -0,0 +1,30 @@ +import fs from 'node:fs/promises'; +import {syncBuiltinESMExports} from 'node:module'; +import {performance} from 'node:perf_hooks'; +// Instrument only this disposable child, before importing SDK modules. Count logical +// fs.promises payload I/O, not physical device traffic or operating-system cache misses. +const saved={},handles=new Set(),counts={readBytes:0,writeBytes:0,opens:0,peakHandles:0,renames:0}; +const length=value=>typeof value==='string'?Buffer.byteLength(value):ArrayBuffer.isView(value)?value.byteLength:0; +function replace(name,wrapper){saved[name]=fs[name];fs[name]=wrapper(saved[name]);} +replace('readFile',original=>async(...args)=>{const value=await original(...args);counts.readBytes+=length(value);return value;}); +replace('writeFile',original=>async(...args)=>{const result=await original(...args);counts.writeBytes+=length(args[1]);return result;}); +replace('rename',original=>async(...args)=>{const result=await original(...args);counts.renames++;return result;}); +replace('open',original=>async(...args)=>{ + const handle=await original(...args);handles.add(handle);counts.opens++;counts.peakHandles=Math.max(counts.peakHandles,handles.size); + for(const name of ['readFile','writeFile','read','write','close']){ + const own=handle[name].bind(handle);handle[name]=async(...input)=>{ + const value=await own(...input); + if(name==='readFile')counts.readBytes+=length(value);else if(name==='writeFile')counts.writeBytes+=length(input[0]); + else if(name==='read')counts.readBytes+=value.bytesRead;else if(name==='write')counts.writeBytes+=value.bytesWritten;else if(name==='close')handles.delete(handle); + return value; + }; + } + return handle; +}); +syncBuiltinESMExports(); +try{ + const began=performance.now(),{runCombinedWorkload}=await import('../examples/combined-workload/run.mjs'),importMs=performance.now()-began; + const before={...counts},report=await runCombinedWorkload(); + if(handles.size)throw Error('Instrumented file handles were not closed'); + console.log(JSON.stringify({...report,importMs,logicalDisk:{readBytes:counts.readBytes-before.readBytes,writeBytes:counts.writeBytes-before.writeBytes,opens:counts.opens-before.opens,renames:counts.renames-before.renames,peakHandles:counts.peakHandles,remainingHandles:handles.size}})); +}finally{for(const [key,value] of Object.entries(saved))fs[key]=value;syncBuiltinESMExports();} diff --git a/scripts/benchmark-combined.mjs b/scripts/benchmark-combined.mjs new file mode 100644 index 0000000..e50d6bd --- /dev/null +++ b/scripts/benchmark-combined.mjs @@ -0,0 +1,20 @@ +import {spawnSync} from 'node:child_process'; +import {readFile} from 'node:fs/promises'; +import {fileURLToPath} from 'node:url'; +import os from 'node:os'; +const samples=[]; +for(let n=0;n<5;n++){ + const result=spawnSync(process.execPath,[fileURLToPath(new URL('./benchmark-combined-worker.mjs',import.meta.url))],{encoding:'utf8',timeout:60000,maxBuffer:1_000_000,windowsHide:true}); + if(result.error||result.status!==0)throw Error(result.stderr||result.error?.message||'Combined workload failed');samples.push(JSON.parse(result.stdout)); +} +const median=list=>[...list].sort((a,b)=>a-b)[Math.floor(list.length/2)],stats=list=>{const m=median(list);return {median:m,mad:median(list.map(v=>Math.abs(v-m))),min:Math.min(...list),max:Math.max(...list)};}; +const selected={importMs:s=>s.importMs,elapsedMs:s=>s.elapsedMs,cpuMs:s=>s.cpuMs,peakRssBytes:s=>s.peakRssBytes,peakHeapBytes:s=>s.peakHeapBytes,watchLatencyMs:s=>Math.max(...s.watchLatencyMs),hostStartMs:s=>Math.max(...s.hostStartMs),requestBytes:s=>s.requestBytes,responseBytes:s=>s.responseBytes,diskReadBytes:s=>s.logicalDisk.readBytes,diskWriteBytes:s=>s.logicalDisk.writeBytes,peakHandles:s=>s.logicalDisk.peakHandles}; +const measurements=Object.fromEntries(Object.entries(selected).map(([key,select])=>[key,stats(samples.map(select))])); +const profile=`${process.platform}-${process.arch}-node${process.versions.node.split('.')[0]}`; +let baseline;try{baseline=JSON.parse(await readFile(new URL('../tests/performance-baselines.json',import.meta.url),'utf8')).profiles[profile];}catch(error){if(error.code!=='ENOENT')throw error;} +const failures=[]; +if(baseline)for(const [metric,limits] of Object.entries(baseline.metrics)){if(measurements[metric].median>limits.maximumMedian)failures.push(metric);} +const report={schemaVersion:1,profile,runtime:{node:process.version,platform:process.platform,arch:process.arch,osRelease:os.release()},sampleCount:samples.length,fixture:{cycles:3,bytes:131089,languageRequests:120,commands:120,verifiedTransfers:6,watchUpdates:3},measurements,baseline:baseline?{source:baseline.source,failures}:null,logicalDisk:'Instrumented fs.promises payload I/O; excludes physical device/cache behavior.',resourceCleanup:samples.every(s=>s.logicalDisk.remainingHandles===0&&Object.keys(s.remainingResources).length===0)}; +console.log(JSON.stringify(report,null,2)); +if(failures.length)throw Error('Combined workload regression: '+failures.join(', ')); +if(!baseline&&!process.argv.includes('--calibrate'))throw Error('Missing measured baseline for '+profile+'; run explicit calibration and review the result'); diff --git a/scripts/pack-check.mjs b/scripts/pack-check.mjs index 60d00f8..93d6516 100644 --- a/scripts/pack-check.mjs +++ b/scripts/pack-check.mjs @@ -80,6 +80,12 @@ try { assert.match(run(process.execPath, ['project-watch-example/run.mjs'], consumer), /initial snapshot, atomic save and resource cleanup verified/); await cp(join(installed, 'examples', 'workspace-observation'), join(consumer, 'observation-example'), {recursive:true}); assert.match(run(process.execPath, ['observation-example/run.mjs'], consumer), /file changes, retained draft and resumed job verified/); + await cp(join(installed,'examples','conformance'),join(consumer,'conformance-example'),{recursive:true}); + const conformance=JSON.parse(run(process.execPath,['conformance-example/run.mjs','--json'],consumer)); + assert.equal(conformance.ok,true);assert.equal(conformance.summary.supported,18); + await cp(join(installed,'examples','combined-workload'),join(consumer,'combined-example'),{recursive:true}); + const combined=JSON.parse(run(process.execPath,['combined-example/run.mjs','--faults'],consumer)); + assert.equal(combined.verifiedTransfers,6);assert.deepEqual(combined.remainingResources,{}); for (const entry of Object.values(metadata.exports)) { for (const file of typeof entry === 'string' ? [entry] : Object.values(entry)) { assert.ok(paths.includes(file.replace(/^\.\//, '')), `Export ${file} is absent from the archive`); diff --git a/src/conformance-catalog.mjs b/src/conformance-catalog.mjs new file mode 100644 index 0000000..2c15ff6 --- /dev/null +++ b/src/conformance-catalog.mjs @@ -0,0 +1,24 @@ +import {freezeConfiguration} from './configuration-values.mjs'; + +/** Case IDs and fixture paths are public, versioned review targets. */ +export const CONFORMANCE_CASES = freezeConfiguration([ + {id:'commands',since:'0.2',port:'host',required:true,fixture:'examples/publishable-plugin/plugin.mjs'}, + {id:'permissions',since:'0.2',port:'host',required:true,fixture:'examples/user-workspace/plugin.mjs'}, + {id:'cancellation',since:'0.2',port:'host',required:true,fixture:'examples/development-tools/run.mjs'}, + {id:'language',since:'0.11',port:'host',required:false,fixture:'examples/language-assistance/run.mjs'}, + {id:'language-editing',since:'0.11',port:'host',required:false,fixture:'examples/language-editing/run.mjs'}, + {id:'language-display',since:'0.11',port:'host',required:false,fixture:'examples/language-display/run.mjs'}, + {id:'settings',since:'0.12',port:'settings',required:false,fixture:'examples/configuration/run.mjs'}, + {id:'jobs',since:'0.4',port:'host',required:false,fixture:'examples/command-jobs/run.mjs'}, + {id:'storage-history',since:'0.8',port:'host',required:false,fixture:'examples/durable-jobs/run.mjs'}, + {id:'files',since:'0.6',port:'workspace',required:false,fixture:'examples/project-session/run.mjs'}, + {id:'projects',since:'0.9',port:'workspace',required:false,fixture:'examples/project-tools/run.mjs'}, + {id:'artifacts',since:'0.8',port:'workspace',required:false,fixture:'examples/stored-artifacts/run.mjs'}, + {id:'uploads-transfers',since:'0.10',port:'workspace',required:false,fixture:'examples/transfer-queue/run.mjs'}, + {id:'workspace-edits',since:'0.11',port:'workspace',required:false,fixture:'examples/workspace-edits/run.mjs'}, + {id:'localization',since:'0.12',port:'localization',required:false,fixture:'examples/configuration/run.mjs'}, + {id:'testing-replay',since:'0.13',port:'testing',required:false,fixture:'examples/development-tools/run.mjs'}, + {id:'diagnostics',since:'0.13',port:'diagnostics',required:false,fixture:'examples/development-tools/run.mjs'}, + {id:'development-tools',since:'0.13',port:'development',required:false,fixture:'examples/conformance/run.mjs'}, +]); +export const CONFORMANCE_FEATURES = Object.freeze(CONFORMANCE_CASES.map(value=>value.id)); diff --git a/src/conformance-development.mjs b/src/conformance-development.mjs new file mode 100644 index 0000000..1d44bef --- /dev/null +++ b/src/conformance-development.mjs @@ -0,0 +1,40 @@ +import {verify,rejected,fixtureManifest,fixturePlugin} from './conformance-host.mjs'; +export const DEVELOPMENT_CASES={ + async localization(ctx) { + const api=ctx.adapters.localization,catalog={schemaVersion:1,defaultLocale:'en',messages:{en:{hello:'Hello {name}'},ko:{hello:'안녕 {name}'}}}; + const ko=api.resolveMessage(catalog,'ko','hello',{name:'SDK'}),fallback=api.resolveMessage(catalog,'fr','hello',{name:'SDK'}); + verify(ko.text==='안녕 SDK'&&ko.locale==='ko','localized_message'); + verify(fallback.fallback&&fallback.locale==='en'&&fallback.text==='Hello SDK','visible_fallback'); + await rejected(()=>api.resolveMessage(catalog,'ko','hello',{}),['invalid_contract'],'missing_placeholder'); + }, + async 'testing-replay'(ctx) { + const api=ctx.adapters.testing,first=api.createTestClock({seed:12}),second=api.createTestClock({seed:12});ctx.own(()=>first.dispose());ctx.own(()=>second.dispose()); + verify(first.runtime.randomUUID()===second.runtime.randomUUID(),'seed_reproducibility'); + let calls=0;first.runtime.setTimeout(()=>calls++,10);await first.advance(9);verify(calls===0,'clock_before_deadline');await first.advance(1);verify(calls===1&&first.inspect().timers===0,'clock_at_deadline'); + const input={count:2},output={total:4},options={synthetic:true,seed:12,fixtureVersion:1,fixtures:[{id:'run',operation:'command',input,output}]},recorder=api.createTestRecorder(options); + recorder.record('run',input,output);const trace=recorder.snapshot();verify(!JSON.stringify(trace).includes('total'),'trace_excludes_payload'); + const replay=api.createTestReplay(trace,options);verify(replay.next('run',input).total===4,'pure_replay');replay.assertComplete(); + await rejected(()=>api.createTestReplay(trace,{...options,seed:13}),['version_mismatch'],'trace_seed_binding'); + }, + async diagnostics(ctx) { + const api=ctx.adapters.diagnostics,session=api.createDiagnosticSession({enabled:true,capacity:2});ctx.own(()=>session.dispose()); + const host=api.profileHost(await ctx.adapters.host(),session);ctx.own(()=>host.dispose()); + await host.activate(fixturePlugin(c=>c.registerCommand({id:'run',title:'Run'},()=>({value:'synthetic-body-never-export'})))); + for(let i=0;i<5;i++)await host.executeCommand(fixtureManifest.id,'run',{}); + const report=session.snapshot();verify(report.events.length===2&&report.attempted>=6,'bounded_diagnostics'); + verify(!JSON.stringify(report).includes('synthetic-body-never-export'),'diagnostic_payload_exclusion'); + host.dispose();verify(session.snapshot().pending===0,'diagnostic_pending_cleanup'); + }, + async 'development-tools'(ctx) { + // This operator port supplies a disposable directory and the actual API, never a success flag. + const fixture=await ctx.adapters.development();ctx.own(()=>fixture.dispose());const {api,directory}=fixture; + const plan=await api.planPlugin(directory,{template:'configuration'});verify(plan.writes===false&&plan.conflicts.length===0,'generation_dry_run'); + const result=await api.initPlugin(directory,{template:'configuration'});verify(result.generationDigest===plan.generationDigest,'deterministic_generation'); + const doctor=await api.doctorPlugin(directory); + verify(doctor.checks.some(c=>c.id==='sdk')&&doctor.checks.filter(c=>c.status==='error').every(c=>c.id==='sdk'),'generated_doctor'); + verify((await api.generatePluginContracts(directory,{check:true})).ok,'generated_contract_drift'); + let refused=false;try{await api.initPlugin(directory,{template:'configuration'});}catch{refused=true;} + verify(refused,'existing_directory_preserved'); + verify((await api.generatePluginContracts(directory,{check:true})).ok,'refused_init_preserves_contracts'); + }, +}; diff --git a/src/conformance-host.mjs b/src/conformance-host.mjs new file mode 100644 index 0000000..f63f58f --- /dev/null +++ b/src/conformance-host.mjs @@ -0,0 +1,139 @@ +import {definePlugin,createLanguageResult} from './index.mjs'; +import {createTestClock,createMemoryJobStore,flushTestMicrotasks} from './testing.mjs'; +import {editHash} from './workspace-edit-contracts.mjs'; + +export const fixtureManifest = Object.freeze({manifestVersion:2,id:'conformance-plugin',name:'Conformance fixture',publisher:'example',version:'1.0.0',protocolVersion:1,entry:'./plugin.mjs',runtime:'workspace',capabilities:['commands'],permissions:[],supportedHosts:['test-host','workspace-host'],license:'Apache-2.0',source:{visibility:'open',licenseFile:'LICENSE'}}); +export const fixtureDocument = Object.freeze({uri:'memory:///fixture.txt',languageId:'plaintext',modelVersion:1,workspaceRevision:'one',text:'alpha 😀\r\nalpha'}); +export function fixturePlugin(activate,changes={}) {return definePlugin({...fixtureManifest,...changes},activate);} +export const fixtureScope = Object.freeze({projectId:'fixture-project',sessionId:'fixture-session'}); +export const commandDefinition = Object.freeze({id:'run',title:'Run',inputSchema:{schemaVersion:1,schema:{type:'object',properties:{count:{type:'integer',minimum:1,maximum:5}},required:['count'],additionalProperties:false}},outputSchema:{schemaVersion:1,schema:{type:'object',properties:{total:{type:'integer'}},required:['total'],additionalProperties:false}}}); +export const fixtureSettings = Object.freeze({schemaVersion:1,pluginId:fixtureManifest.id,version:1,settings:{count:{schema:{schemaVersion:1,schema:{type:'integer',default:2,minimum:1,maximum:5}},scopes:['user','workspace']}}}); + +export function verify(value,check) {if(!value){const error=new Error('Conformance assertion failed');error.check=check;throw error;}} +export async function rejected(operation,codes,check) { + try {await operation();} catch(error) {verify(codes.includes(error?.code),check);return;} + verify(false,check); +} +export function gate() {let resolve;const promise=new Promise(done=>{resolve=done;});return {promise,resolve};} +export async function openHost(ctx,options={}) { + const host=await ctx.adapters.host({hostId:'workspace-host',scope:fixtureScope,...options}); + ctx.own(()=>host.dispose());return host; +} +async function settleJob(host,id) { + for(let i=0;i<128;i++){await new Promise(resolve=>setTimeout(resolve,1));const result=host.getJob(id);if(result.state!=='running'&&result.state!=='queued')return result;} + verify(false,'job_settlement'); +} +async function commandFixture(host,handler=()=>({total:2}),changes={}) { + await host.activate(fixturePlugin(ctx=>ctx.registerCommand(commandDefinition,handler),changes)); +} +export const HOST_CASES = { + async commands(ctx) { + const host=await openHost(ctx);let calls=0; + await commandFixture(host,()=>{calls++;return {total:2};}); + const result=await host.executeCommand(fixtureManifest.id,'run',{count:2}); + verify(result.total===2&&calls===1,'valid_command'); + await rejected(()=>host.executeCommand(fixtureManifest.id,'run',{count:0}),['invalid_contract'],'invalid_input'); + verify(calls===1,'invalid_input_no_execution'); + host.deactivate(fixtureManifest.id); + await commandFixture(host,()=>({total:'wrong'})); + await rejected(()=>host.executeCommand(fixtureManifest.id,'run',{count:2}),['invalid_contract','provider_failed'],'invalid_output'); + host.deactivate(fixtureManifest.id); + // Do not use definePlugin here: the adapter's own activation boundary is tested. + await rejected(()=>host.activate({manifest:{...fixtureManifest,capabilities:['future-unknown']},activate(){calls++;}}),['invalid_contract','version_mismatch'],'unknown_capability'); + verify(calls===1,'unknown_no_execution'); + }, + async permissions(ctx) { + let reads=0; + const host=await openHost(ctx,{workspace:{readFile(){reads++;return {path:'fixture.txt',content:'synthetic',revision:'one'};}}}); + const plugin=fixturePlugin(c=>c.registerCommand({id:'read',title:'Read'},()=>c.workspace.readFile('fixture.txt')),{permissions:['workspace.read']}); + await host.activate(plugin); + await rejected(()=>host.executeCommand(fixtureManifest.id,'read',{}),['permission_denied'],'missing_grant'); + verify(reads===0,'missing_grant_no_port_call'); + const scoped=await openHost(ctx,{grants:['backend.invoke'],backends:{test:(_input,options)=>({scope:options.scope})}}); + await scoped.activate(fixturePlugin(c=>c.registerCommand({id:'scope',title:'Scope'},()=>c.backends.invoke('test',{})),{permissions:['backend.invoke']})); + const value=await scoped.executeCommand(fixtureManifest.id,'scope',{}); + verify(value.scope?.projectId===fixtureScope.projectId&&value.scope?.sessionId===fixtureScope.sessionId,'operator_scope'); + scoped.replaceGrants([]); + await rejected(()=>scoped.executeCommand(fixtureManifest.id,'scope',{}),['disposed','permission_denied','capability_unavailable'],'revoked_grant'); + }, + async cancellation(ctx) { + const host=await openHost(ctx),entered=gate(),finish=gate();let providerSignal; + ctx.own(()=>finish.resolve({total:1})); + await commandFixture(host,(_input,{signal})=>{providerSignal=signal;entered.resolve();return finish.promise;}); + const controller=new AbortController(); + const request=host.executeCommand(fixtureManifest.id,'run',{count:1},{signal:controller.signal}); + const outcome=request.then(()=>({code:'unexpected_success'}),error=>({code:error?.code})); + await entered.promise;controller.abort(); + verify((await outcome).code==='cancelled','caller_cancellation'); + verify(providerSignal.aborted,'provider_cancellation'); + verify(host.inspect().pendingOperations===1,'unsettled_operation_retained'); + finish.resolve({total:1});await flushTestMicrotasks(128); + verify(host.inspect().pendingOperations===0,'settled_operation_released'); + host.dispose();verify(host.inspect().commands===0&&host.inspect().timers===0,'disposed_resources'); + }, + async language(ctx) { + const host=await openHost(ctx,{grants:['document.read','language.provide']}),entered=gate(),finish=gate();ctx.own(()=>finish.resolve()); + await host.activate(fixturePlugin(c=>c.registerLanguageProvider('hover',{languages:['plaintext']},{provide:async r=>{entered.resolve();await finish.promise;return createLanguageResult(r,{text:'synthetic hover'});}}),{runtime:'ui',capabilities:['hover'],permissions:['document.read','language.provide']})); + host.setDocument(fixtureDocument); + const pending=host.requestLanguage('hover',{position:{line:0,character:1}}).then(()=>({code:'unexpected_success'}),error=>({code:error?.code})); + await entered.promise;host.setDocument({...fixtureDocument,modelVersion:2});finish.resolve(); + verify(['stale_snapshot','cancelled'].includes((await pending).code),'changed_document_rejected'); + const result=await host.requestLanguage('hover',{position:{line:0,character:1}}); + verify(result.data.text==='synthetic hover'&&result.snapshot.modelVersion===2,'language_identity'); + host.deactivate(fixtureManifest.id); + await host.activate(fixturePlugin(c=>c.registerLanguageProvider('hover',{languages:['plaintext']},{provide:()=>({data:{text:'unbound'}})}),{runtime:'ui',capabilities:['hover'],permissions:['document.read','language.provide']})); + await rejected(()=>host.requestLanguage('hover',{position:{line:0,character:1}}),['invalid_contract','provider_failed'],'malformed_provider'); + }, + async 'language-editing'(ctx) { + const host=await openHost(ctx,{grants:['document.read','language.provide']}); + await host.activate(fixturePlugin(c=>c.registerLanguageProvider('format-document',{languages:['plaintext']},{provide:r=>createLanguageResult(r,{formatVersion:1,id:crypto.randomUUID(),title:'Synthetic formatting',changes:[{kind:'edit',path:r.path,baseRevision:editHash(r.snapshot.text),edits:[{range:{start:{line:0,character:0},end:{line:0,character:5}},text:'beta'}]}]})}),{runtime:'ui',capabilities:['format-document'],permissions:['document.read','language.provide']})); + host.setDocument(fixtureDocument);const result=await host.requestLanguage('format-document',{path:'fixture.txt',formatOptions:{tabSize:2,insertSpaces:true}}); + const edit=host.prepareFormatting(result);verify(edit.changes[0].edits[0].text==='beta','format_proposal'); + host.setDocument({...fixtureDocument,modelVersion:2}); + await rejected(()=>host.prepareFormatting(result),['stale_snapshot','conflict'],'stale_format_proposal'); + }, + async 'language-display'(ctx) { + const host=await openHost(ctx,{grants:['document.read','language.provide']}); + await host.activate(fixturePlugin(c=>c.registerLanguageProvider('folding-ranges',{languages:['plaintext']},{provide:r=>createLanguageResult(r,[{range:{start:{line:0,character:0},end:{line:1,character:5}}}])}),{runtime:'ui',capabilities:['folding-ranges'],permissions:['document.read','language.provide']})); + host.setDocument(fixtureDocument);const result=await host.requestLanguage('folding-ranges',{}); + verify(result.data.length===1&&result.data[0].range.end.line===1,'folding_result'); + host.deactivate(fixtureManifest.id); + await rejected(()=>host.validateLanguageResult(result),['stale_snapshot','disposed'],'removed_provider_result'); + }, + async settings(ctx) { + const store=await ctx.adapters.settings(fixtureSettings);ctx.own(()=>store.dispose()); + verify(store.read('first').values.count===2,'default_setting'); + store.update({workspaceId:'first',scope:'user',expectedRevision:0,values:{count:3}}); + store.update({workspaceId:'first',scope:'workspace',expectedRevision:1,values:{count:4}}); + verify(store.read('first').values.count===4&&store.read('second').values.count===3,'scoped_precedence'); + await rejected(()=>store.update({workspaceId:'first',scope:'user',expectedRevision:0,values:{count:5}}),['conflict'],'settings_revision'); + await rejected(()=>store.migrate({...fixtureSettings,version:2},()=>{throw Error('synthetic failure');},{expectedRevision:2}),['provider_failed'],'failed_migration'); + verify(store.read('first').values.count===4&&store.read('first').definitionVersion===1,'migration_rollback'); + const subscription=store.subscribe('first',()=>{});subscription.dispose();verify(store.inspect().subscriptions===0,'subscription_cleanup'); + }, + async jobs(ctx) { + const host=await openHost(ctx,{jobs:true}),clock=createTestClock();ctx.own(()=>clock.dispose()); + const entered=gate(),finish=gate();ctx.own(()=>finish.resolve({total:2})); + await commandFixture(host,(_input,{job})=>{job.log('info','Synthetic result');entered.resolve();return finish.promise;}); + const id=clock.runtime.randomUUID();host.startCommandJob(fixtureManifest.id,'run',{count:2},{jobId:id}); + await entered.promise;verify(host.getJobEvents(id).events.length>0,'job_events'); + host.cancelJob(id);verify(host.getJob(id).state==='cancelled','job_cancelled'); + finish.resolve({total:2});await flushTestMicrotasks(128);verify(host.getJob(id).state==='cancelled','late_job_result_ignored'); + verify(host.inspect().pendingJobs===0,'job_slot_cleanup'); + verify(host.startCommandJob(fixtureManifest.id,'run',{count:2},{jobId:id}).state==='cancelled','duplicate_job_idempotent'); + await rejected(()=>host.startCommandJob(fixtureManifest.id,'run',{count:3},{jobId:id}),['conflict'],'changed_job_id_conflict'); + }, + async 'storage-history'(ctx) { + const clock=createTestClock({seed:42}),store=createMemoryJobStore({identity:{schemaVersion:1,storeId:clock.runtime.randomUUID(),workspaceId:'fixture-project',workspaceIdentity:'a'.repeat(64)}});ctx.own(()=>store.dispose());ctx.own(()=>clock.dispose()); + const options={jobs:true,grants:['workspace.read'],scope:{projectId:store.identity.workspaceId,sessionId:'first'},jobStorage:{store,workspaceIdentity:store.identity.workspaceIdentity,pluginArtifacts:{[fixtureManifest.id]:'a'.repeat(64)}}}; + const first=await openHost(ctx,options);await commandFixture(first,undefined,{permissions:['workspace.read']}); + const id=clock.runtime.randomUUID();first.startCommandJob(fixtureManifest.id,'run',{count:2},{jobId:id}); + verify((await settleJob(first,id)).state==='succeeded','stored_job_succeeded');await first.flushJobStore();first.dispose(); + const second=await openHost(ctx,{...options,scope:{...options.scope,sessionId:'second'}});await commandFixture(second,undefined,{permissions:['workspace.read']}); + const recovered=second.recoverJob(fixtureManifest.id,id);verify(recovered.disposition==='completed','recovered_completed'); + verify(second.listJobHistory(fixtureManifest.id).items.some(item=>item.jobId===id),'history_membership'); + verify(second.inspect().pendingJobs===0,'no_automatic_reexecution'); + second.deactivate(fixtureManifest.id); + await rejected(()=>second.recoverJob(fixtureManifest.id,id),['disposed','permission_denied','capability_unavailable'],'recovery_current_authority'); + }, +}; diff --git a/src/conformance-node.d.mts b/src/conformance-node.d.mts new file mode 100644 index 0000000..b8c808d --- /dev/null +++ b/src/conformance-node.d.mts @@ -0,0 +1,5 @@ +import type {ConformanceOptions,ConformanceWorkspace} from './conformance.mjs'; +export interface NodeConformanceWorkspace extends ConformanceWorkspace {failNextRequest(kind:'disconnect'|'malformed'):void;metrics():Readonly<{requests:number;requestBytes:number;responseBytes:number;fixtureWriteBytes:number}>;} +/** Creates only synthetic temporary files and a loopback HTTP server; does not start external tools. */ +export function createConformanceWorkspace(options?:{readonly signal?:AbortSignal}):Promise; +export function createSdkConformanceOptions():ConformanceOptions; diff --git a/src/conformance-node.mjs b/src/conformance-node.mjs new file mode 100644 index 0000000..5554155 --- /dev/null +++ b/src/conformance-node.mjs @@ -0,0 +1,71 @@ +import * as fs from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; +import {randomUUID,randomBytes} from 'node:crypto'; +import {createPluginHost} from './index.mjs'; +import {createWorkspaceServer} from './workspace-node.mjs'; +import {createWorkspaceClient} from './workspace-client.mjs'; +import {createNodeJobStore} from './job-storage-node.mjs'; +import {createNodeArtifactStore} from './artifact-storage-node.mjs'; +import {createNodeWorkspaceEditJournal} from './workspace-edits-node.mjs'; +import {createSettingsStore} from './settings.mjs'; +import * as localization from './localization.mjs'; +import * as testing from './testing.mjs'; +import * as diagnostics from './diagnostics.mjs'; +import * as development from './devtools.mjs'; +import {fixtureBytes} from './conformance-workspace.mjs'; +import {fixturePlugin,fixtureManifest,fixtureDocument,verify} from './conformance-host.mjs'; +import {SDK_VERSION} from './version.mjs'; +import {CONFORMANCE_FEATURES} from './conformance-catalog.mjs'; + +// Call only for deliberately created temporary roots, and verify the exact parent before removal. +async function removeOwned(directory,parent){ + const stat=await fs.lstat(directory);if(stat.isSymbolicLink()||!stat.isDirectory())throw Error('Changed conformance directory'); + const resolved=await fs.realpath(directory);if(path.dirname(resolved)!==parent||!path.basename(resolved).startsWith('dds-conformance-'))throw Error('Changed conformance boundary'); + await fs.rm(resolved,{recursive:true}); +} +async function temporary(){const parent=await fs.realpath(os.tmpdir());return {parent,directory:await fs.realpath(await fs.mkdtemp(path.join(parent,'dds-conformance-')))};} + +/** Fresh synthetic real HTTP workspace; no user root, network endpoint or executable is accepted. */ +export async function createConformanceWorkspace({signal}={}){ + const {parent,directory}=await temporary(),root=path.join(directory,'workspace'),workspaceId=randomUUID(),token=randomBytes(32).toString('hex'); + const artifactSha256='a'.repeat(64);let server,client,store,artifacts,journal,disposed=false,transportFault=null; + let requestBytes=0,responseBytes=0,requests=0,writes=0; + const plugin=fixturePlugin(c=>c.registerCommand({id:'capture',title:'Capture'},async(_input,{job})=>{await job.addBinaryArtifact({id:'binary',path:'fixture.bin'});await job.addArtifact({id:'text',path:'fixture.txt'});return {captured:true};}),{permissions:['workspace.read','workspace.write']}); + async function start(){ + signal?.throwIfAborted(); + store=await createNodeJobStore({directory:path.join(directory,'jobs'),workspaceRoot:root,workspaceId}); + artifacts=await createNodeArtifactStore({jobStore:store}); + journal=await createNodeWorkspaceEditJournal({directory:path.join(directory,'edits'),workspaceRoot:root,workspaceId}); + server=await createWorkspaceServer({root,workspaceId,token,plugins:[{plugin,artifactSha256}],grants:['workspace.read','workspace.write'],jobs:true,binaryArtifacts:true,jobStorage:{store,artifacts},projects:{roots:[''],fileSystem:'local'},uploads:{directory:path.join(directory,'uploads'),principalId:'fixture-operator',roots:[''],fileSystem:'local'},notice:{id:'conformance',version:'1',text:'Disposable synthetic conformance fixture'}}); + client=createWorkspaceClient({url:server.url,token,fetch:async(url,options)=>{ + requests++;requestBytes+=Buffer.byteLength(options.body??''); + const fault=transportFault;transportFault=null; + if(fault==='disconnect')throw new TypeError('Synthetic transport interruption'); + const response=await fetch(url,options),bytes=new Uint8Array(await response.arrayBuffer());responseBytes+=bytes.byteLength; + if(fault==='malformed')return new Response('{"unbound":true}',{status:200,headers:response.headers}); + return new Response(bytes,{status:response.status,headers:response.headers}); + }}); + await client.connect();signal?.throwIfAborted(); + } + async function stop(){client?.dispose();await server?.close();await journal?.close();await artifacts?.close();await store?.close();} + try{ + await fs.mkdir(root);await fs.writeFile(path.join(root,'fixture.txt'),fixtureDocument.text);await fs.writeFile(path.join(root,'fixture.bin'),fixtureBytes()); + await start(); + return Object.freeze({get client(){return client;},get journal(){return journal;},artifactSha256, + async writeFixture(content){if(disposed)throw Error('Disposed fixture');await fs.writeFile(path.join(root,'fixture.txt'),content);writes+=Buffer.byteLength(content);}, + async restart(){if(disposed)throw Error('Disposed fixture');await stop();await start();}, + revokeProjects(){server.revokeProjects();},revokeUploads(){server.revokeUploads();}, + failNextRequest(kind){if(!['disconnect','malformed'].includes(kind))throw new TypeError('Invalid fixture fault');transportFault=kind;}, + metrics(){return {requests,requestBytes,responseBytes,fixtureWriteBytes:writes};}, + async dispose(){if(disposed)return;disposed=true;await stop();await removeOwned(directory,parent);}, + }); + }catch(error){await stop();await removeOwned(directory,parent);throw error;} +} + +export function createSdkConformanceOptions(){ + return {identity:{host:'DDS reference SDK',version:SDK_VERSION,runtime:`Node ${process.version}`,platform:`${process.platform}-${process.arch}`},features:[...CONFORMANCE_FEATURES],adapters:{ + host:createPluginHost,settings:createSettingsStore,workspace:createConformanceWorkspace,localization,testing,diagnostics, + async development(){const {parent,directory}=await temporary();return {api:development,directory:path.join(directory,'project'),async dispose(){await removeOwned(directory,parent);}};}, + }}; +} diff --git a/src/conformance-schemas.mjs b/src/conformance-schemas.mjs new file mode 100644 index 0000000..8a275c8 --- /dev/null +++ b/src/conformance-schemas.mjs @@ -0,0 +1,8 @@ +import {CONFORMANCE_FEATURES} from './conformance-catalog.mjs'; +const object=properties=>({type:'object',properties,required:Object.keys(properties),additionalProperties:false}); +const identifier={type:'string',maxLength:128,pattern:'^[A-Za-z0-9][A-Za-z0-9 ._+()-]*$'}; +const features={type:'array',items:identifier,maxItems:64,uniqueItems:true}; +const result=object({id:{enum:CONFORMANCE_FEATURES},status:{enum:['supported','unsupported','failed']},reason:{enum:['passed','not_declared','missing_adapter','assertion','provider_error','timed_out','cancelled','cleanup_failed','aborted_after_timeout']},check:{anyOf:[identifier,{type:'null'}]},durationMs:{type:'number',minimum:0},cleanup:{enum:['complete','pending','failed','not-run']}}); +export const CONFORMANCE_SCHEMAS=Object.freeze({ + 'conformance-report':{...object({schemaVersion:{const:1},suiteVersion:{const:1},sdkVersion:identifier,identity:object({host:identifier,version:identifier,runtime:identifier,platform:identifier}),declaredFeatures:features,unknownFeatures:features,requiredFeatures:{...features,items:{enum:CONFORMANCE_FEATURES}},results:{type:'array',items:result,minItems:CONFORMANCE_FEATURES.length,maxItems:CONFORMANCE_FEATURES.length},summary:object({supported:{type:'integer',minimum:0,maximum:CONFORMANCE_FEATURES.length},unsupported:{type:'integer',minimum:0,maximum:CONFORMANCE_FEATURES.length},failed:{type:'integer',minimum:0,maximum:CONFORMANCE_FEATURES.length}}),ok:{type:'boolean'}}),$comment:'Runtime also validates exact case order, counts, unknown/required declarations and status/cleanup/ok consistency.'}, +}); diff --git a/src/conformance-workspace.mjs b/src/conformance-workspace.mjs new file mode 100644 index 0000000..902dcd9 --- /dev/null +++ b/src/conformance-workspace.mjs @@ -0,0 +1,81 @@ +import {verify,rejected,fixtureManifest} from './conformance-host.mjs'; +import {createTransferQueue} from './transfer-queue.mjs'; +import {previewWorkspaceEdit,applyWorkspaceEdit} from './workspace-edits.mjs'; +import {createIncrementalSha256} from './sha256-stream.mjs'; + +export const fixtureBytes=()=>Uint8Array.from({length:131089},(_v,n)=>(n*17+31)&255); +const hash=bytes=>createIncrementalSha256().update(bytes).digest(); +export async function openWorkspace(ctx) { + const fixture=await ctx.adapters.workspace({signal:ctx.signal});ctx.own(()=>fixture.dispose());return fixture; +} +export async function runFixtureJob(f) { + const id=crypto.randomUUID();await f.client.startCommandJob(fixtureManifest.id,'capture',{},f.artifactSha256,{jobId:id}); + const value=await f.client.waitForJob(id,{intervalMs:250,timeoutMs:5000});verify(value.state==='succeeded','fixture_job_succeeded'); + // Completion of the provider and commitment of its persistent checkpoint are distinct. + for(let n=0;n<100;n++) {const r=await f.client.recoverJob(fixtureManifest.id,id,f.artifactSha256);if(r.record?.settled)return id;await new Promise(resolve=>setTimeout(resolve,5));} + verify(false,'fixture_checkpoint_settled'); +} +export function memorySink() { + let bytes=new Uint8Array(),committed=false; + return {bytes:()=>bytes,capabilities:{kind:'caller',seek:false,readback:true,persistence:'on-commit',abort:'discard'},async open(){return { + async write({offset,bytes:next}){verify(offset===bytes.length,'sequential_sink');const combined=new Uint8Array(bytes.length+next.length);combined.set(bytes);combined.set(next,bytes.length);bytes=combined;}, + async readback(){return {byteLength:bytes.length,async read(offset,length){return bytes.slice(offset,offset+length);}};}, + async commit(){committed=true;},async abort(){if(!committed)bytes=new Uint8Array();}, + };}}; +} +export const WORKSPACE_CASES={ + async files(ctx) { + const f=await openWorkspace(ctx),client=f.client; + const first=await client.readFile('fixture.txt');verify(first.content==='alpha 😀\r\nalpha','read_fixture'); + verify((await client.readFileIfChanged('fixture.txt',first.revision)).notModified===true,'conditional_read'); + await f.writeFixture('beta'); + await rejected(()=>client.writeFile('fixture.txt','stale',first.revision),['conflict'],'stale_file_write'); + verify((await client.readFile('fixture.txt')).content==='beta','stale_write_preserves_file'); + await rejected(()=>client.readFile('../outside'),['unsafe_path','invalid_request','permission_denied'],'relative_path_boundary'); + await rejected(()=>client.readFile('.env'),['unsafe_path','permission_denied','not_found'],'protected_path'); + const generation=client.binding.workspace.generation;await f.restart(); + verify(f.client.binding.workspace.generation!==generation,'workspace_generation_changed'); + }, + async projects(ctx) { + const f=await openWorkspace(ctx),client=f.client; + verify((await client.getProjectCapabilities()).enabled,'project_capability'); + const query=await client.searchText({root:'',query:'alpha'});verify(query.total===2,'bounded_text_query'); + const observer=client.watchProject({root:'',intervalMs:250,debounceMs:20},{timeoutMs:5000});ctx.own(()=>observer.return()); + const before=(await observer.next()).value.snapshot;await f.writeFixture('beta'); + let changed=false; + for(let n=0;n<20;n++){const next=await observer.next();if(next.done)break;if(next.value.snapshot.revision!==before.revision){changed=true;break;}} + verify(changed,'observed_external_change');await observer.return(); + verify((await client.searchText({root:'',query:'alpha'})).total===0,'fresh_search_after_change'); + f.revokeProjects();await rejected(()=>client.searchText({root:'',query:'beta'}),['permission_denied','unsupported','capability_unavailable'],'revoked_projects'); + }, + async artifacts(ctx) { + const f=await openWorkspace(ctx),id=await runFixtureJob(f),pin=f.artifactSha256; + const old=await f.client.getStoredJobArtifact(fixtureManifest.id,id,'binary',pin);await f.restart(); + await rejected(()=>f.client.readStoredJobArtifactChunk(old,0),['generation_mismatch'],'old_artifact_generation'); + const fresh=await f.client.getStoredJobArtifact(fixtureManifest.id,id,'binary',pin),sink=memorySink(),queue=createTransferQueue();ctx.own(()=>queue.dispose()); + const task=queue.enqueueStoredDownload(f.client,fresh,{sink}),receipt=await task.result; + verify(receipt.verification==='stored'&&hash(sink.bytes())===hash(fixtureBytes()),'retained_result_integrity'); + verify(queue.inspect().active===0&&queue.inspect().pending===0,'download_queue_settled'); + await rejected(()=>f.client.getStoredJobArtifact(fixtureManifest.id,id,'binary','0'.repeat(64)),['plugin_mismatch'],'artifact_current_pin'); + }, + async 'uploads-transfers'(ctx) { + const f=await openWorkspace(ctx),bytes=fixtureBytes(),source={byteLength:bytes.length,async read(offset,length){return bytes.slice(offset,offset+length);}},queue=createTransferQueue({concurrency:2});ctx.own(()=>queue.dispose()); + const spec={uploadId:crypto.randomUUID(),pluginId:fixtureManifest.id,artifactSha256:f.artifactSha256,path:'uploaded.bin',expectedRevision:null}; + const task=queue.enqueueUpload(f.client,source,spec),receipt=await task.result; + verify(receipt.state==='committed'&&receipt.commitReceipt.revision===hash(bytes),'upload_commit_integrity'); + verify(task.snapshot.verified&&queue.inspect().peakChunkBytes<=65536,'upload_bounded_verified'); + f.revokeUploads(); + const denied=queue.enqueueUpload(f.client,source,{...spec,uploadId:crypto.randomUUID(),path:'denied.bin'}); + await rejected(()=>denied.result,['permission_denied','unsupported','capability_unavailable'],'revoked_upload'); + verify(queue.inspect().active===0&&queue.inspect().pending===0,'upload_queue_settled'); + }, + async 'workspace-edits'(ctx) { + const f=await openWorkspace(ctx),original=await f.client.readFile('fixture.txt'),journal=f.journal; + const proposal={formatVersion:1,id:crypto.randomUUID(),title:'Synthetic edit',changes:[{kind:'edit',path:'fixture.txt',baseRevision:original.revision,edits:[{range:{start:{line:0,character:0},end:{line:0,character:5}},text:'beta'}]}]}; + const preview=await previewWorkspaceEdit(f.client,proposal); + await rejected(()=>applyWorkspaceEdit(f.client,preview,{journal,reviewed:{planId:preview.planId,digest:preview.digest},authorize:()=>false}),['permission_denied'],'edit_requires_approval'); + verify((await f.client.readFile('fixture.txt')).revision===original.revision,'denied_edit_preserves_file'); + const receipt=await applyWorkspaceEdit(f.client,preview,{journal,reviewed:{planId:preview.planId,digest:preview.digest},authorize:()=>true}); + verify(receipt.status==='completed'&&(await f.client.readFile('fixture.txt')).content==='beta 😀\r\nalpha','reviewed_edit_applied'); + }, +}; diff --git a/src/conformance.d.mts b/src/conformance.d.mts new file mode 100644 index 0000000..e77079d --- /dev/null +++ b/src/conformance.d.mts @@ -0,0 +1,28 @@ +import type {PluginHost,PluginHostOptions} from './index.mjs'; +import type {SettingsStore,SettingsDefinition} from './settings.mjs'; +import type {WorkspaceClient} from './workspace-client.mjs'; +import type {WorkspaceEditJournal} from './workspace-edits.mjs'; +export type ConformanceFeature='commands'|'permissions'|'cancellation'|'language'|'language-editing'|'language-display'|'settings'|'jobs'|'storage-history'|'files'|'projects'|'artifacts'|'uploads-transfers'|'workspace-edits'|'localization'|'testing-replay'|'diagnostics'|'development-tools'; +export interface ConformanceIdentity {readonly host:string;readonly version:string;readonly runtime:string;readonly platform:string;} +export interface ConformanceWorkspace {readonly client:WorkspaceClient;readonly artifactSha256:string;readonly journal:WorkspaceEditJournal;writeFixture(content:string):Promise;restart():Promise;revokeProjects():void;revokeUploads():void;dispose():Promise;} +/** Each factory supplies an isolated fixture. No adapter may address a real user's workspace. */ +export interface ConformanceAdapters { + readonly host?:(options?:PluginHostOptions)=>PluginHost|Promise; + readonly settings?:(definition:SettingsDefinition)=>SettingsStore|Promise; + readonly workspace?:(options:{readonly signal:AbortSignal})=>Promise; + readonly localization?:Pick; + readonly testing?:Pick; + readonly diagnostics?:Pick; + readonly development?:()=>Promise<{readonly api:Pick;readonly directory:string;dispose():Promise}>; +} +export interface ConformanceOptions {readonly identity:ConformanceIdentity;readonly features:readonly string[];readonly requiredFeatures?:readonly ConformanceFeature[];readonly adapters:ConformanceAdapters;readonly timeoutMs?:number;readonly signal?:AbortSignal;} +export type ConformanceStatus='supported'|'unsupported'|'failed'; +export type ConformanceReason='passed'|'not_declared'|'missing_adapter'|'assertion'|'provider_error'|'timed_out'|'cancelled'|'cleanup_failed'|'aborted_after_timeout'; +export interface ConformanceResult {readonly id:ConformanceFeature;readonly status:ConformanceStatus;readonly reason:ConformanceReason;readonly check:string|null;readonly durationMs:number;readonly cleanup:'complete'|'pending'|'failed'|'not-run';} +export interface ConformanceReport {readonly schemaVersion:1;readonly suiteVersion:1;readonly sdkVersion:string;readonly identity:ConformanceIdentity;readonly declaredFeatures:readonly string[];readonly unknownFeatures:readonly string[];readonly requiredFeatures:readonly ConformanceFeature[];readonly results:readonly ConformanceResult[];readonly summary:Readonly>;readonly ok:boolean;} +export const CONFORMANCE_CASES:readonly {readonly id:ConformanceFeature;readonly since:string;readonly port:keyof ConformanceAdapters;readonly required:boolean;readonly fixture:string}[]; +export const CONFORMANCE_FEATURES:readonly ConformanceFeature[]; +export const CONFORMANCE_LIMITS:Readonly<{features:64;timeoutMs:30000;reportBytes:65536}>; +export function runConformance(options:ConformanceOptions):Promise; +export function parseConformanceReport(input:unknown):ConformanceReport; +export function formatConformanceReport(input:ConformanceReport):string; diff --git a/src/conformance.mjs b/src/conformance.mjs new file mode 100644 index 0000000..2b8da15 --- /dev/null +++ b/src/conformance.mjs @@ -0,0 +1,84 @@ +import {SDK_VERSION} from './version.mjs'; +import {configurationCopy,configurationObject as object,configurationInteger as integer,freezeConfiguration} from './configuration-values.mjs'; +import {CONFORMANCE_CASES,CONFORMANCE_FEATURES} from './conformance-catalog.mjs'; +import {HOST_CASES} from './conformance-host.mjs'; +import {WORKSPACE_CASES} from './conformance-workspace.mjs'; +import {DEVELOPMENT_CASES} from './conformance-development.mjs'; +export {CONFORMANCE_CASES,CONFORMANCE_FEATURES} from './conformance-catalog.mjs'; + +export const CONFORMANCE_LIMITS=Object.freeze({features:64,timeoutMs:30000,reportBytes:65536}); +const implementations={...HOST_CASES,...WORKSPACE_CASES,...DEVELOPMENT_CASES}; +const reasons=['passed','not_declared','missing_adapter','assertion','provider_error','timed_out','cancelled','cleanup_failed','aborted_after_timeout']; +function invalid(){throw new TypeError('Invalid conformance contract');} +function token(value,max=128){if(typeof value!=='string'||value.length>max||!/^[A-Za-z0-9][A-Za-z0-9 ._+()-]*$/.test(value))invalid();return value;} +function featureList(value){if(!Array.isArray(value)||value.length>64||new Set(value).size!==value.length)invalid();return value.map(v=>token(v));} +function identity(value){const v=configurationCopy(value);object(v,['host','version','runtime','platform']);for(const field of Object.keys(v))token(v[field]);return v;} +function count(results,status){return results.filter(value=>value.status===status).length;} + +/** Strict, payload-free report readback. This report describes test evidence, not certification. */ +export function parseConformanceReport(input){ + const v=configurationCopy(input,CONFORMANCE_LIMITS.reportBytes); + object(v,['schemaVersion','suiteVersion','sdkVersion','identity','declaredFeatures','unknownFeatures','requiredFeatures','results','summary','ok']); + if(v.schemaVersion!==1||v.suiteVersion!==1)invalid();token(v.sdkVersion);identity(v.identity); + featureList(v.declaredFeatures);featureList(v.unknownFeatures);featureList(v.requiredFeatures); + if(v.requiredFeatures.some(f=>!CONFORMANCE_FEATURES.includes(f))||JSON.stringify(v.unknownFeatures)!==JSON.stringify(v.declaredFeatures.filter(f=>!CONFORMANCE_FEATURES.includes(f))))invalid(); + if(!Array.isArray(v.results)||v.results.length!==CONFORMANCE_CASES.length)invalid(); + for(let i=0;iv.results.find(r=>r.id===id)?.status==='supported'); + if(v.ok!==ok)invalid();return v; +} + +/** Adapters execute trusted local fixture code. A deadline cannot interrupt synchronous code. */ +export async function runConformance(options){ + object(options,['identity','features','adapters'],['requiredFeatures','timeoutMs','signal']); + const selectedIdentity=identity(options.identity),declared=featureList(options.features),required=featureList(options.requiredFeatures??CONFORMANCE_CASES.filter(c=>c.required).map(c=>c.id)); + if(required.some(f=>!CONFORMANCE_FEATURES.includes(f)))invalid(); + const timeoutMs=options.timeoutMs??10000;integer(timeoutMs,CONFORMANCE_LIMITS.timeoutMs,1); + if(!options.adapters||typeof options.adapters!=='object'||options.signal!==undefined&&!(options.signal instanceof AbortSignal))invalid(); + const results=[];let stop=false; + for(const spec of CONFORMANCE_CASES){ + const start=performance.now(),cleanups=[],late=[];let closed=false,reason='passed',check=null,cleanup='complete',timer,aborted; + const finish=(status,why,cleanupState='not-run')=>results.push({id:spec.id,status,reason:why,check:null,durationMs:0,cleanup:cleanupState}); + if(!declared.includes(spec.id)&&!required.includes(spec.id)){finish('unsupported','not_declared');continue;} + if(stop||options.signal?.aborted){finish('failed',options.signal?.aborted?'cancelled':'aborted_after_timeout');continue;} + if(!options.adapters[spec.port]||(spec.id==='diagnostics'&&!options.adapters.host)){finish('failed','missing_adapter');continue;} + const controller=new AbortController(); + const own=fn=>{if(typeof fn!=='function')invalid();if(closed){const work=Promise.resolve().then(fn);work.catch(()=>{});late.push(work);}else cleanups.push(fn);}; + const work=Promise.resolve().then(()=>implementations[spec.id]({adapters:options.adapters,signal:controller.signal,own})); + // The operation is always observed, including late rejection after a deadline. + const result=work.then(()=>({reason:'passed'}),error=>({reason:typeof error?.check==='string'?'assertion':'provider_error',check:typeof error?.check==='string'&&/^[a-z_]+$/.test(error.check)?error.check:null})); + try{ + const deadline=new Promise(resolve=>{timer=setTimeout(()=>resolve({reason:'timed_out'}),timeoutMs);aborted=()=>resolve({reason:'cancelled'});options.signal?.addEventListener('abort',aborted,{once:true});if(options.signal?.aborted)aborted();}); + const outcome=await Promise.race([result,deadline]);reason=outcome.reason;check=outcome.check??null; + if(['timed_out','cancelled'].includes(reason)){controller.abort();stop=true;cleanup='pending';} + }finally{ + clearTimeout(timer);options.signal?.removeEventListener('abort',aborted);closed=true;controller.abort(); + // A broken adapter must not prevent a bounded report or retain an unobserved rejection. + for(const fn of cleanups.reverse()){ + let cleanupTimer; + const pending=Promise.resolve().then(fn).then(()=>true,()=>false); + const outcome=await Promise.race([pending,new Promise(resolve=>{cleanupTimer=setTimeout(()=>resolve(null),Math.min(timeoutMs,1000));})]); + clearTimeout(cleanupTimer); + if(outcome===false){cleanup='failed';reason='cleanup_failed';}else if(outcome===null){cleanup='pending';reason='cleanup_failed';stop=true;} + } + // Late-created resources register with `own` and are disposed when they arrive. + result.then(async()=>{await Promise.allSettled(late);}).catch(()=>{}); + } + results.push({id:spec.id,status:reason==='passed'?'supported':'failed',reason,check,durationMs:Math.max(0,Math.round((performance.now()-start)*1000)/1000),cleanup}); + } + const summary=Object.fromEntries(['supported','unsupported','failed'].map(status=>[status,count(results,status)])); + return parseConformanceReport({schemaVersion:1,suiteVersion:1,sdkVersion:SDK_VERSION,identity:selectedIdentity,declaredFeatures:declared,unknownFeatures:declared.filter(f=>!CONFORMANCE_FEATURES.includes(f)),requiredFeatures:required,results,summary,ok:summary.failed===0&&required.every(id=>results.find(r=>r.id===id)?.status==='supported')}); +} +export function formatConformanceReport(input){ + const r=parseConformanceReport(input),i=r.identity; + return [`DDS Plugin SDK conformance v${r.suiteVersion} (${r.sdkVersion})`,`${i.host} ${i.version} | ${i.runtime} | ${i.platform}`,`${r.ok?'PASS':'FAIL'}: ${r.summary.supported} supported, ${r.summary.unsupported} unsupported, ${r.summary.failed} failed`,...r.results.map(v=>`${v.status.padEnd(11)} ${v.id}: ${v.reason}${v.check?` (${v.check})`:''}; cleanup=${v.cleanup}`),...(r.unknownFeatures.length?[`Unknown declarations (not probed): ${r.unknownFeatures.join(', ')}`]:[])].join('\n'); +} diff --git a/src/publishing.mjs b/src/publishing.mjs index a0a7833..8dcb415 100644 --- a/src/publishing.mjs +++ b/src/publishing.mjs @@ -163,7 +163,7 @@ function inspectContents(contents, archived = false) { if (archived) { // Only the generated inert npm metadata is accepted, including the tested // SDK line. Additional scripts or dependencies cannot be smuggled into it. - const accepted = [metadataBytes, ...['>=0.3.0 <0.4.0', '>=0.4.0 <0.5.0', '>=0.5.0 <0.6.0', '>=0.6.0 <0.7.0', '>=0.7.0 <0.8.0', '>=0.8.0 <0.9.0', '>=0.9.0 <0.10.0', '>=0.10.0 <0.11.0', '>=0.11.0 <0.12.0', '>=0.12.0 <0.13.0'].map(range => Buffer.from(`${JSON.stringify({...metadata, peerDependencies: {'@altifigence/dds-plugin-sdk': range}}, null, 2)}\n`))]; + const accepted = [metadataBytes, ...['>=0.3.0 <0.4.0', '>=0.4.0 <0.5.0', '>=0.5.0 <0.6.0', '>=0.6.0 <0.7.0', '>=0.7.0 <0.8.0', '>=0.8.0 <0.9.0', '>=0.9.0 <0.10.0', '>=0.10.0 <0.11.0', '>=0.11.0 <0.12.0', '>=0.12.0 <0.13.0', '>=0.13.0 <0.14.0'].map(range => Buffer.from(`${JSON.stringify({...metadata, peerDependencies: {'@altifigence/dds-plugin-sdk': range}}, null, 2)}\n`))]; if (!accepted.some(bytes => contents.get('package.json').equals(bytes))) fail('package.json must match the generated SDK metadata'); } else { contents.set('package.json', metadataBytes); diff --git a/src/schemas.d.mts b/src/schemas.d.mts index 2307308..75c9bc9 100644 --- a/src/schemas.d.mts +++ b/src/schemas.d.mts @@ -1,2 +1,2 @@ // Generated by npm run schemas:write. -export const SCHEMAS: Readonly>>>; +export const SCHEMAS: Readonly>>>; diff --git a/src/schemas.mjs b/src/schemas.mjs index 2c1ff61..0347540 100644 --- a/src/schemas.mjs +++ b/src/schemas.mjs @@ -22,6 +22,7 @@ import {LANGUAGE_DISPLAY_SCHEMAS, SEMANTIC_TOKENS_SCHEMA, FOLDING_SCHEMA, INLAY_ import {CONFIGURATION_SCHEMAS, DATA_SCHEMA_CONTRACT, DATA_SCHEMA_DEFINITIONS, DISPLAY_METADATA_SCHEMA} from './data-schema-schemas.mjs'; import {LOCALIZATION_SCHEMAS} from './localization-schemas.mjs'; import {DEVELOPMENT_SCHEMAS} from './development-schemas.mjs'; +import {CONFORMANCE_SCHEMAS} from './conformance-schemas.mjs'; const schema = 'https://json-schema.org/draft/2020-12/schema'; const base = `https://github.com/Altifigence/dds-plugin-sdk/blob/v${SDK_VERSION}/schemas/`; @@ -121,6 +122,7 @@ const historyDisposition = {enum:['live','completed','interrupted','expired']}; const historyCursor = {...text(40),pattern:'^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}:(?:0|[1-9][0-9]{0,2})$'}; const historyItem = object({jobId,commandId:text(128),state:{enum:JOB_STATES},disposition:historyDisposition,startedAt:integer(Number.MAX_SAFE_INTEGER),updatedAt:integer(Number.MAX_SAFE_INTEGER),expiresAt:integer(Number.MAX_SAFE_INTEGER,1),revision:integer(Number.MAX_SAFE_INTEGER,1),attemptOf:{oneOf:[jobId,{type:'null'}]},contentPolicy:{enum:['metadata-only','host-redacted']},artifactCount:integer(JOB_LIMITS.artifacts),snapshotCount:integer(JOB_LIMITS.artifacts),resultAvailability:{enum:['none','source-references','snapshot-references','mixed-references','expired']}}); export const SCHEMAS = Object.freeze({ + ...Object.fromEntries(Object.entries(CONFORMANCE_SCHEMAS).map(([name,body])=>[name,define(name,body)])), ...Object.fromEntries(Object.entries(DEVELOPMENT_SCHEMAS).map(([name,body])=>[name,define(name,body)])), ...Object.fromEntries(Object.entries(CONFIGURATION_SCHEMAS).map(([name, body]) => [name, define(name, body)])), ...Object.fromEntries(Object.entries(LOCALIZATION_SCHEMAS).map(([name, body]) => [name, define(name, body)])), diff --git a/src/version.mjs b/src/version.mjs index 55639bb..24122ef 100644 --- a/src/version.mjs +++ b/src/version.mjs @@ -1,3 +1,3 @@ // The release checks compare this value with the package metadata. -export const SDK_VERSION = '0.13.0'; -export const SDK_PLUGIN_PEER_RANGE = '>=0.13.0 <0.14.0'; +export const SDK_VERSION = '1.0.0-rc.1'; +export const SDK_PLUGIN_PEER_RANGE = '1.0.0-rc.1'; diff --git a/tests/conformance.test.mjs b/tests/conformance.test.mjs new file mode 100644 index 0000000..bc332e2 --- /dev/null +++ b/tests/conformance.test.mjs @@ -0,0 +1,51 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {runConformance,parseConformanceReport,formatConformanceReport,CONFORMANCE_CASES} from '../src/conformance.mjs'; +import {createSdkConformanceOptions} from '../src/conformance-node.mjs'; +import {createPluginHost} from '../src/index.mjs'; +const identity={host:'Custom test host',version:'1.0.0',runtime:'Node test',platform:'fixture'}; +const core={identity,features:['commands','permissions','cancellation'],adapters:{host:createPluginHost}}; + +test('reference adapters exercise real HTTP storage, grants, generations, queries and generated contracts',async()=>{ + const r=await runConformance(createSdkConformanceOptions()); + assert.equal(r.ok,true,formatConformanceReport(r));assert.equal(r.summary.supported,CONFORMANCE_CASES.length); + assert.deepEqual(parseConformanceReport(JSON.parse(JSON.stringify(r))),r); +}); +test('independent core host reports optional and unknown capabilities without probing them',async()=>{ + let unknownCalls=0;const options={...core,features:[...core.features,'future-feature'],adapters:{...core.adapters,unknown(){unknownCalls++;}}}; + const r=await runConformance(options);assert.equal(r.ok,true);assert.equal(r.summary.supported,3); + assert.equal(r.summary.unsupported,CONFORMANCE_CASES.length-3);assert.deepEqual(r.unknownFeatures,['future-feature']);assert.equal(unknownCalls,0); + assert.ok(r.results.filter(v=>v.status==='unsupported').every(v=>v.cleanup==='not-run')); +}); +test('missing or malformed custom adapters fail instead of claiming support',async()=>{ + const missing=await runConformance({...core,adapters:{}});assert.equal(missing.ok,false);assert.equal(missing.summary.failed,3); + const r=await runConformance({...core,features:['commands'],requiredFeatures:['commands'],adapters:{host:()=>({activate(){},executeCommand:()=>({total:2}),deactivate(){},dispose(){}})}}); + assert.equal(r.ok,false);assert.equal(r.results[0].reason,'assertion');assert.equal(r.results[0].check,'valid_command'); +}); +test('a host that bypasses permission checks is detected even when it forwards successful commands',async()=>{ + const r=await runConformance({...core,features:['permissions'],requiredFeatures:['permissions'],adapters:{host:options=>createPluginHost({...options,grants:['workspace.read','backend.invoke']})}}); + assert.equal(r.ok,false);assert.equal(r.results.find(r=>r.id==='permissions').check,'missing_grant'); +}); +test('timeouts remain bounded, stop later fixtures and dispose a late-created host',async()=>{ + let release,disposed=0;const pending=new Promise(resolve=>{release=resolve;}); + const r=await runConformance({...core,timeoutMs:10,adapters:{host:()=>pending}}); + assert.equal(r.results[0].reason,'timed_out');assert.equal(r.results[0].cleanup,'pending'); + assert.equal(r.results[1].reason,'aborted_after_timeout'); + release({dispose(){disposed++;},activate(){throw Error('late fixture');}});await new Promise(resolve=>setTimeout(resolve,5));assert.equal(disposed,1); +}); +test('cleanup errors fail a passed case and provider secrets never appear in a report',async()=>{ + const r=await runConformance({...core,features:['commands'],requiredFeatures:['commands'],adapters:{host:()=>{const h=createPluginHost();return {...h,dispose(){h.dispose();throw Error('private-path-and-secret');}};}}}); + assert.equal(r.results[0].reason,'cleanup_failed');assert.equal(r.results[0].cleanup,'failed');assert.ok(!JSON.stringify(r).includes('private-path')); + const failure=await runConformance({...core,adapters:{host(){throw Error('sensitive-provider-body');}}});assert.ok(!JSON.stringify(failure).includes('sensitive')); +}); +test('report readback rejects forged summary, missing cases, extras and getters',async()=>{ + const r=await runConformance(core);assert.equal(r.ok,true); + for(const changed of [{...r,ok:false},{...r,summary:{...r.summary,supported:99}},{...r,results:r.results.slice(1)},{...r,rawError:'sensitive'}])assert.throws(()=>parseConformanceReport(changed)); + let called=0;assert.throws(()=>parseConformanceReport({...r,get secret(){called++;}}));assert.equal(called,0); + assert.throws(()=>parseConformanceReport({...r,results:r.results.map((v,i)=>i?{...v,id:r.results[0].id}:v)})); +}); +test('pre-aborted runs create no fixtures and unrecognized required features are rejected',async()=>{ + const controller=new AbortController();controller.abort();let calls=0; + const r=await runConformance({...core,signal:controller.signal,adapters:{host(){calls++;}}});assert.equal(calls,0);assert.equal(r.ok,false); + await assert.rejects(runConformance({...core,requiredFeatures:['future-feature']})); +}); diff --git a/tests/performance-baselines.json b/tests/performance-baselines.json new file mode 100644 index 0000000..ae99bb6 --- /dev/null +++ b/tests/performance-baselines.json @@ -0,0 +1,512 @@ +{ + "schemaVersion": 1, + "description": "Five fresh-process combined workloads per profile; coarse regression guard, not a latency or memory guarantee.", + "threshold": "maximum + max(6 * median absolute deviation, 2 * observed range, maximum)", + "fixture": { + "cycles": 3, + "bytes": 131089, + "languageRequests": 120, + "commands": 120, + "verifiedTransfers": 6, + "watchUpdates": 3 + }, + "measurementLimits": { + "disk": "Instrumented fs.promises payload bytes, excluding physical disk/cache effects.", + "network": "HTTP JSON body bytes, excluding headers and transport overhead.", + "memory": "RSS and heap sampled every 5 ms, not exact allocation peaks." + }, + "profiles": { + "win32-x64-node24": { + "source": { + "run": "https://github.com/Altifigence/dds-plugin-sdk/actions/runs/37246613594", + "job": "https://github.com/Altifigence/dds-plugin-sdk/actions/runs/37246613594/job/111565657652", + "commit": "4e7052ee572c3a089fe21dc4b9159a3d818c5d34", + "jobName": "SDK / windows-2025 / Node 24.21.0", + "node": "v24.21.0", + "platform": "win32", + "arch": "x64", + "osRelease": "10.0.26100" + }, + "sampleCount": 5, + "metrics": { + "importMs": { + "observed": { + "median": 104.84870000000001, + "mad": 1.3260000000000218, + "min": 103.52269999999999, + "max": 114.8108 + }, + "maximumMedian": 230 + }, + "elapsedMs": { + "observed": { + "median": 2978.8111, + "mad": 32.16809999999987, + "min": 2946.643, + "max": 3018.4071 + }, + "maximumMedian": 6037 + }, + "cpuMs": { + "observed": { + "median": 2953, + "mad": 30, + "min": 2923, + "max": 3219 + }, + "maximumMedian": 6438 + }, + "peakRssBytes": { + "observed": { + "median": 134344704, + "mad": 327680, + "min": 130248704, + "max": 136564736 + }, + "maximumMedian": 273129472 + }, + "peakHeapBytes": { + "observed": { + "median": 54890576, + "mad": 450000, + "min": 47817576, + "max": 55359776 + }, + "maximumMedian": 110719552 + }, + "watchLatencyMs": { + "observed": { + "median": 116.57990000000018, + "mad": 2.558899999999767, + "min": 98.27640000000002, + "max": 119.13879999999995 + }, + "maximumMedian": 239 + }, + "hostStartMs": { + "observed": { + "median": 138.89679999999998, + "mad": 1.825099999999992, + "min": 137.0717, + "max": 144.25619999999998 + }, + "maximumMedian": 289 + }, + "requestBytes": { + "observed": { + "median": 563595, + "mad": 0, + "min": 563595, + "max": 563595 + }, + "maximumMedian": 1127190 + }, + "responseBytes": { + "observed": { + "median": 600209, + "mad": 1, + "min": 600208, + "max": 600210 + }, + "maximumMedian": 1200420 + }, + "diskReadBytes": { + "observed": { + "median": 7394741, + "mad": 21, + "min": 7001491, + "max": 7657000 + }, + "maximumMedian": 15314000 + }, + "diskWriteBytes": { + "observed": { + "median": 1644210, + "mad": 0, + "min": 1644208, + "max": 1644210 + }, + "maximumMedian": 3288420 + }, + "peakHandles": { + "observed": { + "median": 6, + "mad": 0, + "min": 6, + "max": 6 + }, + "maximumMedian": 12 + } + } + }, + "win32-x64-node22": { + "source": { + "run": "https://github.com/Altifigence/dds-plugin-sdk/actions/runs/37246613594", + "job": "https://github.com/Altifigence/dds-plugin-sdk/actions/runs/37246613594/job/111565657766", + "commit": "4e7052ee572c3a089fe21dc4b9159a3d818c5d34", + "jobName": "SDK / windows-2025 / Node 22.23.3", + "node": "v22.23.3", + "platform": "win32", + "arch": "x64", + "osRelease": "10.0.26100" + }, + "sampleCount": 5, + "metrics": { + "importMs": { + "observed": { + "median": 64.4676, + "mad": 1.9227999999999952, + "min": 61.6871, + "max": 78.1083 + }, + "maximumMedian": 157 + }, + "elapsedMs": { + "observed": { + "median": 3076.3363, + "mad": 16.164700000000266, + "min": 2894.4713, + "max": 3138.7867 + }, + "maximumMedian": 6278 + }, + "cpuMs": { + "observed": { + "median": 1906, + "mad": 62, + "min": 1719, + "max": 1985 + }, + "maximumMedian": 3970 + }, + "peakRssBytes": { + "observed": { + "median": 125227008, + "mad": 806912, + "min": 124018688, + "max": 127623168 + }, + "maximumMedian": 255246336 + }, + "peakHeapBytes": { + "observed": { + "median": 51203976, + "mad": 132672, + "min": 48606768, + "max": 51336648 + }, + "maximumMedian": 102673296 + }, + "watchLatencyMs": { + "observed": { + "median": 58.9708, + "mad": 1.9091999999999985, + "min": 57.0616, + "max": 72.60180000000003 + }, + "maximumMedian": 146 + }, + "hostStartMs": { + "observed": { + "median": 207.2952, + "mad": 21.00569999999999, + "min": 186.2895, + "max": 245.2852 + }, + "maximumMedian": 491 + }, + "requestBytes": { + "observed": { + "median": 563595, + "mad": 0, + "min": 563595, + "max": 563595 + }, + "maximumMedian": 1127190 + }, + "responseBytes": { + "observed": { + "median": 600207, + "mad": 0, + "min": 600207, + "max": 600207 + }, + "maximumMedian": 1200414 + }, + "diskReadBytes": { + "observed": { + "median": 7263665, + "mad": 262195, + "min": 7001417, + "max": 7525860 + }, + "maximumMedian": 15051720 + }, + "diskWriteBytes": { + "observed": { + "median": 1644183, + "mad": 0, + "min": 1644183, + "max": 1644189 + }, + "maximumMedian": 3288378 + }, + "peakHandles": { + "observed": { + "median": 5, + "mad": 0, + "min": 5, + "max": 6 + }, + "maximumMedian": 12 + } + } + }, + "linux-x64-node22": { + "source": { + "run": "https://github.com/Altifigence/dds-plugin-sdk/actions/runs/37246613594", + "job": "https://github.com/Altifigence/dds-plugin-sdk/actions/runs/37246613594/job/111565657790", + "commit": "4e7052ee572c3a089fe21dc4b9159a3d818c5d34", + "jobName": "SDK / ubuntu-24.04 / Node 22.23.3", + "node": "v22.23.3", + "platform": "linux", + "arch": "x64", + "osRelease": "6.17.0-1022-azure" + }, + "sampleCount": 5, + "metrics": { + "importMs": { + "observed": { + "median": 75.404455, + "mad": 0.7301629999999903, + "min": 74.67429200000001, + "max": 79.894552 + }, + "maximumMedian": 160 + }, + "elapsedMs": { + "observed": { + "median": 1726.87901, + "mad": 13.419302000000243, + "min": 1713.4597079999999, + "max": 1768.766288 + }, + "maximumMedian": 3538 + }, + "cpuMs": { + "observed": { + "median": 2073.919, + "mad": 18.010999999999967, + "min": 2055.908, + "max": 2115.317 + }, + "maximumMedian": 4231 + }, + "peakRssBytes": { + "observed": { + "median": 150831104, + "mad": 262144, + "min": 147460096, + "max": 151093248 + }, + "maximumMedian": 302186496 + }, + "peakHeapBytes": { + "observed": { + "median": 49324920, + "mad": 673480, + "min": 48651440, + "max": 50870792 + }, + "maximumMedian": 101741584 + }, + "watchLatencyMs": { + "observed": { + "median": 120.06222700000001, + "mad": 0.8086590000000342, + "min": 116.9888840000001, + "max": 121.42590700000005 + }, + "maximumMedian": 243 + }, + "hostStartMs": { + "observed": { + "median": 88.938452, + "mad": 6.289303000000004, + "min": 80.64725899999999, + "max": 105.926038 + }, + "maximumMedian": 212 + }, + "requestBytes": { + "observed": { + "median": 562056, + "mad": 0, + "min": 562056, + "max": 562056 + }, + "maximumMedian": 1124112 + }, + "responseBytes": { + "observed": { + "median": 597912, + "mad": 0, + "min": 597912, + "max": 597912 + }, + "maximumMedian": 1195824 + }, + "diskReadBytes": { + "observed": { + "median": 5689633, + "mad": 0, + "min": 5689633, + "max": 5689633 + }, + "maximumMedian": 11379266 + }, + "diskWriteBytes": { + "observed": { + "median": 1643805, + "mad": 0, + "min": 1643805, + "max": 1643805 + }, + "maximumMedian": 3287610 + }, + "peakHandles": { + "observed": { + "median": 5, + "mad": 0, + "min": 5, + "max": 5 + }, + "maximumMedian": 10 + } + } + }, + "linux-x64-node24": { + "source": { + "run": "https://github.com/Altifigence/dds-plugin-sdk/actions/runs/37246613594", + "job": "https://github.com/Altifigence/dds-plugin-sdk/actions/runs/37246613594/job/111565657824", + "commit": "4e7052ee572c3a089fe21dc4b9159a3d818c5d34", + "jobName": "SDK / ubuntu-24.04 / Node 24.21.0", + "node": "v24.21.0", + "platform": "linux", + "arch": "x64", + "osRelease": "6.17.0-1022-azure" + }, + "sampleCount": 5, + "metrics": { + "importMs": { + "observed": { + "median": 87.78970799999999, + "mad": 0.7768299999999897, + "min": 86.819623, + "max": 88.922325 + }, + "maximumMedian": 178 + }, + "elapsedMs": { + "observed": { + "median": 1731.019084, + "mad": 6.489560999999867, + "min": 1719.948972, + "max": 1791.601427 + }, + "maximumMedian": 3584 + }, + "cpuMs": { + "observed": { + "median": 2089.093, + "mad": 1.0730000000003201, + "min": 2073.953, + "max": 2148.164 + }, + "maximumMedian": 4297 + }, + "peakRssBytes": { + "observed": { + "median": 163512320, + "mad": 192512, + "min": 162611200, + "max": 165703680 + }, + "maximumMedian": 331407360 + }, + "peakHeapBytes": { + "observed": { + "median": 51277912, + "mad": 442456, + "min": 50353320, + "max": 52522784 + }, + "maximumMedian": 105045568 + }, + "watchLatencyMs": { + "observed": { + "median": 125.19743499999998, + "mad": 10.276982999999916, + "min": 113.91800999999998, + "max": 142.92998799999998 + }, + "maximumMedian": 286 + }, + "hostStartMs": { + "observed": { + "median": 80.94591199999999, + "mad": 2.435966999999991, + "min": 76.053739, + "max": 86.652951 + }, + "maximumMedian": 174 + }, + "requestBytes": { + "observed": { + "median": 562056, + "mad": 0, + "min": 562056, + "max": 562056 + }, + "maximumMedian": 1124112 + }, + "responseBytes": { + "observed": { + "median": 597912, + "mad": 0, + "min": 597912, + "max": 597912 + }, + "maximumMedian": 1195824 + }, + "diskReadBytes": { + "observed": { + "median": 5689633, + "mad": 0, + "min": 5689633, + "max": 5689633 + }, + "maximumMedian": 11379266 + }, + "diskWriteBytes": { + "observed": { + "median": 1643805, + "mad": 0, + "min": 1643805, + "max": 1643805 + }, + "maximumMedian": 3287610 + }, + "peakHandles": { + "observed": { + "median": 5, + "mad": 0, + "min": 5, + "max": 5 + }, + "maximumMedian": 10 + } + } + } + } +} diff --git a/tests/project-watch.test.mjs b/tests/project-watch.test.mjs index 41d511c..6cc3faa 100644 --- a/tests/project-watch.test.mjs +++ b/tests/project-watch.test.mjs @@ -110,7 +110,9 @@ test('slow consumers receive a bounded resync snapshot after queue overflow',asy } assert.ok(port.inspect().queuedEvents<=4);assert.ok(port.inspect().overflows>=1); const resync=await event(iterator,x=>x.reason==='consumer_overflow');assert.equal(resync.kind,'resync');assert.deepEqual(resync.changes,[]);assert.ok(resync.cursor>1); - const latest=await event(iterator,x=>x.snapshot.entries[0].revision===hash('7'));assert.equal(parseProjectWatchEvent(latest).snapshot.entries[0].revision,hash('7')); + const hasLatest=x=>x.snapshot.entries.some(entry=>entry.path==='value.txt'&&entry.revision===hash('7')); + const latest=hasLatest(resync)?resync:await event(iterator,hasLatest); + assert.ok(hasLatest(parseProjectWatchEvent(latest))); }); test('native hint loss, missing names, initial-scan races and native errors require explicit resync',async t=>{ diff --git a/tests/publishing.test.mjs b/tests/publishing.test.mjs index b8152a2..b4bdbcb 100644 --- a/tests/publishing.test.mjs +++ b/tests/publishing.test.mjs @@ -72,7 +72,7 @@ test('open and proprietary packages use explicit files, never execute code, and const npmMetadata = JSON.parse(archive.get('package/package.json')); assert.equal(npmMetadata.name, '@example/hello'); assert.equal(npmMetadata.exports, './plugin.mjs'); - assert.equal(npmMetadata.peerDependencies['@altifigence/dds-plugin-sdk'], '>=0.13.0 <0.14.0'); + assert.equal(npmMetadata.peerDependencies['@altifigence/dds-plugin-sdk'], '1.0.0-rc.1'); assert.equal(npmMetadata.scripts, undefined); assert.equal(JSON.parse(await readFile(packed.metadataPath, 'utf8')).artifact.sha256, packed.artifact.sha256); } diff --git a/tests/rc-acceptance.test.mjs b/tests/rc-acceptance.test.mjs new file mode 100644 index 0000000..b93d21b --- /dev/null +++ b/tests/rc-acceptance.test.mjs @@ -0,0 +1,13 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {runCombinedWorkload} from '../examples/combined-workload/run.mjs'; +import {createConformanceWorkspace} from '../src/conformance-node.mjs'; + +test('combined real host survives repeated language/settings/edit/job/watch/transfer workload and network/revoke faults',async()=>{ + const result=await runCombinedWorkload({cycles:5,faults:true}); + assert.equal(result.languageRequests,200);assert.equal(result.commands,200);assert.equal(result.verifiedTransfers,10); + assert.equal(result.observedChanges,5);assert.equal(result.faultChecks.length,20);assert.deepEqual(result.remainingResources,{}); +}); +test('a cancelled workspace setup removes its files and never yields a usable fixture',async()=>{ + const controller=new AbortController();controller.abort();await assert.rejects(createConformanceWorkspace({signal:controller.signal})); +}); diff --git a/tests/types/conformance.mts b/tests/types/conformance.mts new file mode 100644 index 0000000..0428dcb --- /dev/null +++ b/tests/types/conformance.mts @@ -0,0 +1,12 @@ +import {createPluginHost} from '../../src/index.mjs'; +import {runConformance,formatConformanceReport,parseConformanceReport,type ConformanceReport} from '../../src/conformance.mjs'; +import {createSdkConformanceOptions,createConformanceWorkspace} from '../../src/conformance-node.mjs'; +const options=createSdkConformanceOptions(); +const report:ConformanceReport=await runConformance(options); +formatConformanceReport(parseConformanceReport(report)); +await runConformance({identity:{host:'custom',version:'1.0.0',runtime:'Node',platform:'test'},features:['commands'],requiredFeatures:['commands'],adapters:{host:createPluginHost}}); +const fixture=await createConformanceWorkspace();fixture.metrics().requestBytes;await fixture.dispose(); +// @ts-expect-error unknown required features cannot silently pass +runConformance({...options,requiredFeatures:['unknown']}); +// @ts-expect-error a reported success flag is not a host adapter +runConformance({...options,adapters:{host:()=>({ok:true})}});