semantifold 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -15
- package/build/index.d.ts +48 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +57 -8
- package/build/src/artifact-path.d.ts +23 -1
- package/build/src/artifact-path.d.ts.map +1 -1
- package/build/src/artifact-path.js +45 -3
- package/build/src/artifacts.d.ts.map +1 -1
- package/build/src/artifacts.js +66 -1
- package/build/src/backends/c-validation.d.ts.map +1 -1
- package/build/src/backends/c-validation.js +7 -3
- package/build/src/backends/cpp-validation.d.ts.map +1 -1
- package/build/src/backends/cpp-validation.js +7 -3
- package/build/src/backends/csharp.js +7 -1
- package/build/src/backends/dart-runtime.d.ts +2 -0
- package/build/src/backends/dart-runtime.d.ts.map +1 -0
- package/build/src/backends/dart-runtime.js +41 -0
- package/build/src/backends/dart.d.ts +20 -0
- package/build/src/backends/dart.d.ts.map +1 -0
- package/build/src/backends/dart.js +321 -0
- package/build/src/backends/effects.d.ts +60 -0
- package/build/src/backends/effects.d.ts.map +1 -0
- package/build/src/backends/effects.js +746 -0
- package/build/src/backends/go.js +18 -4
- package/build/src/backends/identifiers.d.ts +36 -4
- package/build/src/backends/identifiers.d.ts.map +1 -1
- package/build/src/backends/identifiers.js +154 -10
- package/build/src/backends/index.d.ts.map +1 -1
- package/build/src/backends/index.js +27 -4
- package/build/src/backends/ios.d.ts +68 -0
- package/build/src/backends/ios.d.ts.map +1 -0
- package/build/src/backends/ios.js +1503 -0
- package/build/src/backends/java.d.ts.map +1 -1
- package/build/src/backends/java.js +429 -36
- package/build/src/backends/javascript.d.ts +6 -0
- package/build/src/backends/javascript.d.ts.map +1 -1
- package/build/src/backends/javascript.js +380 -24
- package/build/src/backends/kotlin-runtime.d.ts +3 -0
- package/build/src/backends/kotlin-runtime.d.ts.map +1 -0
- package/build/src/backends/kotlin-runtime.js +29 -0
- package/build/src/backends/kotlin.d.ts +23 -0
- package/build/src/backends/kotlin.d.ts.map +1 -0
- package/build/src/backends/kotlin.js +136 -0
- package/build/src/backends/native-validation.d.ts +2 -2
- package/build/src/backends/native-validation.d.ts.map +1 -1
- package/build/src/backends/native-validation.js +2 -2
- package/build/src/backends/ordered-expressions.d.ts.map +1 -1
- package/build/src/backends/ordered-expressions.js +39 -7
- package/build/src/backends/php-stdlib-support.d.ts +19 -0
- package/build/src/backends/php-stdlib-support.d.ts.map +1 -0
- package/build/src/backends/php-stdlib-support.js +227 -0
- package/build/src/backends/php.d.ts.map +1 -1
- package/build/src/backends/php.js +448 -33
- package/build/src/backends/program.d.ts +81 -0
- package/build/src/backends/program.d.ts.map +1 -0
- package/build/src/backends/program.js +1308 -0
- package/build/src/backends/python.js +7 -1
- package/build/src/backends/ruby.d.ts.map +1 -1
- package/build/src/backends/ruby.js +310 -24
- package/build/src/backends/rust-runtime.d.ts +7 -0
- package/build/src/backends/rust-runtime.d.ts.map +1 -0
- package/build/src/backends/rust-runtime.js +32 -0
- package/build/src/backends/rust-validation.d.ts +9 -0
- package/build/src/backends/rust-validation.d.ts.map +1 -0
- package/build/src/backends/rust-validation.js +78 -0
- package/build/src/backends/rust.d.ts +24 -0
- package/build/src/backends/rust.d.ts.map +1 -0
- package/build/src/backends/rust.js +344 -0
- package/build/src/backends/scalars.d.ts +14 -6
- package/build/src/backends/scalars.d.ts.map +1 -1
- package/build/src/backends/scalars.js +167 -17
- package/build/src/backends/shared.d.ts +23 -4
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +2158 -62
- package/build/src/backends/stdlib-support.d.ts +14 -0
- package/build/src/backends/stdlib-support.d.ts.map +1 -0
- package/build/src/backends/stdlib-support.js +237 -0
- package/build/src/backends/swift-runtime.d.ts +3 -0
- package/build/src/backends/swift-runtime.d.ts.map +1 -0
- package/build/src/backends/swift-runtime.js +14 -0
- package/build/src/backends/swift.d.ts +8 -0
- package/build/src/backends/swift.d.ts.map +1 -0
- package/build/src/backends/swift.js +127 -0
- package/build/src/backends/typescript.d.ts.map +1 -1
- package/build/src/backends/typescript.js +344 -24
- package/build/src/backends/wasm.d.ts +43 -0
- package/build/src/backends/wasm.d.ts.map +1 -0
- package/build/src/backends/wasm.js +2164 -0
- package/build/src/backends/writer.d.ts +235 -4
- package/build/src/backends/writer.d.ts.map +1 -1
- package/build/src/backends/writer.js +468 -3
- package/build/src/diagnostic.d.ts +4 -3
- package/build/src/diagnostic.d.ts.map +1 -1
- package/build/src/diagnostic.js +5 -3
- package/build/src/frontends/c.d.ts.map +1 -1
- package/build/src/frontends/c.js +5 -3
- package/build/src/frontends/cpp.d.ts.map +1 -1
- package/build/src/frontends/cpp.js +5 -3
- package/build/src/frontends/csharp.d.ts.map +1 -1
- package/build/src/frontends/csharp.js +7 -3
- package/build/src/frontends/dart.d.ts +10 -0
- package/build/src/frontends/dart.d.ts.map +1 -0
- package/build/src/frontends/dart.js +913 -0
- package/build/src/frontends/index.d.ts +3 -1
- package/build/src/frontends/index.d.ts.map +1 -1
- package/build/src/frontends/index.js +15 -5
- package/build/src/frontends/java.d.ts +110 -1
- package/build/src/frontends/java.d.ts.map +1 -1
- package/build/src/frontends/java.js +1725 -70
- package/build/src/frontends/javascript-typescript.d.ts +69 -1
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
- package/build/src/frontends/javascript-typescript.js +1825 -93
- package/build/src/frontends/kotlin.d.ts +10 -0
- package/build/src/frontends/kotlin.d.ts.map +1 -0
- package/build/src/frontends/kotlin.js +685 -0
- package/build/src/frontends/php.d.ts +118 -1
- package/build/src/frontends/php.d.ts.map +1 -1
- package/build/src/frontends/php.js +1583 -76
- package/build/src/frontends/program.d.ts +74 -0
- package/build/src/frontends/program.d.ts.map +1 -0
- package/build/src/frontends/program.js +989 -0
- package/build/src/frontends/ruby.d.ts +68 -1
- package/build/src/frontends/ruby.d.ts.map +1 -1
- package/build/src/frontends/ruby.js +1446 -101
- package/build/src/frontends/rust-parser.d.ts +11 -0
- package/build/src/frontends/rust-parser.d.ts.map +1 -0
- package/build/src/frontends/rust-parser.js +15 -0
- package/build/src/frontends/rust.d.ts +14 -0
- package/build/src/frontends/rust.d.ts.map +1 -0
- package/build/src/frontends/rust.js +715 -0
- package/build/src/frontends/scalars.d.ts +10 -0
- package/build/src/frontends/scalars.d.ts.map +1 -1
- package/build/src/frontends/scalars.js +28 -2
- package/build/src/frontends/swift.d.ts +10 -0
- package/build/src/frontends/swift.d.ts.map +1 -0
- package/build/src/frontends/swift.js +860 -0
- package/build/src/frontends/types.d.ts +186 -0
- package/build/src/frontends/types.d.ts.map +1 -0
- package/build/src/frontends/types.js +634 -0
- package/build/src/language-registry.d.ts +46 -4
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +167 -13
- package/build/src/mapping.d.ts +2 -2
- package/build/src/mapping.d.ts.map +1 -1
- package/build/src/mapping.js +31 -10
- package/build/src/semantic/capabilities.d.ts +66 -0
- package/build/src/semantic/capabilities.d.ts.map +1 -0
- package/build/src/semantic/capabilities.js +649 -0
- package/build/src/semantic/effects.d.ts +26 -0
- package/build/src/semantic/effects.d.ts.map +1 -0
- package/build/src/semantic/effects.js +764 -0
- package/build/src/semantic/generics.d.ts +23 -0
- package/build/src/semantic/generics.d.ts.map +1 -0
- package/build/src/semantic/generics.js +61 -0
- package/build/src/semantic/lifetime.d.ts +37 -0
- package/build/src/semantic/lifetime.d.ts.map +1 -0
- package/build/src/semantic/lifetime.js +527 -0
- package/build/src/semantic/provenance.d.ts +9 -2
- package/build/src/semantic/provenance.d.ts.map +1 -1
- package/build/src/semantic/provenance.js +462 -20
- package/build/src/semantic/scalars.d.ts +11 -0
- package/build/src/semantic/scalars.d.ts.map +1 -1
- package/build/src/semantic/scalars.js +16 -1
- package/build/src/semantic/stdlib.d.ts +254 -0
- package/build/src/semantic/stdlib.d.ts.map +1 -0
- package/build/src/semantic/stdlib.js +880 -0
- package/build/src/semantic/types.d.ts +2952 -202
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +688 -15
- package/build/src/semantic/validate.d.ts +85 -5
- package/build/src/semantic/validate.d.ts.map +1 -1
- package/build/src/semantic/validate.js +2576 -98
- package/build/src/stdlib-facades.d.ts +304 -0
- package/build/src/stdlib-facades.d.ts.map +1 -0
- package/build/src/stdlib-facades.js +1167 -0
- package/build/src/stdlib-negotiation.d.ts +69 -0
- package/build/src/stdlib-negotiation.d.ts.map +1 -0
- package/build/src/stdlib-negotiation.js +403 -0
- package/build/src/stdlib-providers.d.ts +72 -0
- package/build/src/stdlib-providers.d.ts.map +1 -0
- package/build/src/stdlib-providers.js +339 -0
- package/build/src/toolchains.d.ts +55 -1
- package/build/src/toolchains.d.ts.map +1 -1
- package/build/src/toolchains.js +15 -4
- package/docs/architecture.md +45 -13
- package/docs/dart.md +60 -0
- package/docs/goals.md +3 -3
- package/docs/ios.md +121 -0
- package/docs/kotlin.md +42 -0
- package/docs/language-support.md +231 -26
- package/docs/multi-file-modules.md +52 -0
- package/docs/parser-qualification.md +121 -8
- package/docs/plans/2026-09-05-standard-library-portability.md +10 -10
- package/docs/rust.md +88 -0
- package/docs/source-maps.md +15 -7
- package/docs/standard-library-portability.md +39 -8
- package/docs/swift.md +44 -0
- package/docs/testing.md +366 -4
- package/docs/webassembly.md +52 -0
- package/index.js +63 -8
- package/node_modules/semantifold-tree-sitter-legacy-internal/README.md +2 -2
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/LICENSE +21 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/README.md +45 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/binding.gyp +30 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding.cc +20 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/binding_test.js +9 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.d.ts +28 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/bindings/node/index.js +11 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/grammar.js +1630 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/package.json +64 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/darwin-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/linux-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-arm64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/prebuilds/win32-x64/tree-sitter-rust.node +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/highlights.scm +159 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/injections.scm +9 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/queries/tags.scm +60 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/grammar.json +9405 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/node-types.json +5399 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/parser.c +192569 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/scanner.c +393 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/alloc.h +54 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/array.h +290 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/src/tree_sitter/parser.h +266 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter-rust.wasm +0 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/node_modules/tree-sitter-rust/tree-sitter.json +53 -0
- package/node_modules/semantifold-tree-sitter-legacy-internal/package.json +3 -2
- package/node_modules/semantifold-tree-sitter-legacy-internal/src/c.js +5 -4
- package/package.json +5 -2
- package/src/artifact-path.js +47 -2
- package/src/artifacts.js +67 -0
- package/src/backends/c-validation.js +6 -1
- package/src/backends/cpp-validation.js +6 -1
- package/src/backends/csharp.js +3 -0
- package/src/backends/dart-runtime.js +41 -0
- package/src/backends/dart.js +326 -0
- package/src/backends/effects.js +705 -0
- package/src/backends/go.js +14 -2
- package/src/backends/identifiers.js +160 -9
- package/src/backends/index.js +27 -2
- package/src/backends/ios.js +1612 -0
- package/src/backends/java.js +454 -36
- package/src/backends/javascript.js +389 -23
- package/src/backends/kotlin-runtime.js +29 -0
- package/src/backends/kotlin.js +141 -0
- package/src/backends/native-validation.js +1 -1
- package/src/backends/ordered-expressions.js +40 -6
- package/src/backends/php-stdlib-support.js +239 -0
- package/src/backends/php.js +469 -32
- package/src/backends/program.js +1407 -0
- package/src/backends/python.js +3 -0
- package/src/backends/ruby.js +326 -23
- package/src/backends/rust-runtime.js +34 -0
- package/src/backends/rust-validation.js +75 -0
- package/src/backends/rust.js +335 -0
- package/src/backends/scalars.js +141 -16
- package/src/backends/shared.js +2304 -124
- package/src/backends/stdlib-support.js +234 -0
- package/src/backends/swift-runtime.js +14 -0
- package/src/backends/swift.js +132 -0
- package/src/backends/typescript.js +349 -23
- package/src/backends/wasm.js +2208 -0
- package/src/backends/writer.js +520 -2
- package/src/diagnostic.js +4 -2
- package/src/frontends/c.js +4 -2
- package/src/frontends/cpp.js +4 -2
- package/src/frontends/csharp.js +4 -2
- package/src/frontends/dart.js +963 -0
- package/src/frontends/index.js +14 -4
- package/src/frontends/java.js +1977 -69
- package/src/frontends/javascript-typescript.js +2046 -102
- package/src/frontends/kotlin.js +713 -0
- package/src/frontends/php.js +1725 -79
- package/src/frontends/program.js +1062 -0
- package/src/frontends/ruby.js +1624 -98
- package/src/frontends/rust-parser.js +18 -0
- package/src/frontends/rust.js +714 -0
- package/src/frontends/scalars.js +31 -1
- package/src/frontends/swift.js +893 -0
- package/src/frontends/types.js +750 -0
- package/src/language-registry.js +172 -12
- package/src/mapping.js +33 -9
- package/src/semantic/capabilities.js +671 -0
- package/src/semantic/effects.js +737 -0
- package/src/semantic/generics.js +58 -0
- package/src/semantic/lifetime.js +517 -0
- package/src/semantic/provenance.js +412 -19
- package/src/semantic/scalars.js +17 -0
- package/src/semantic/stdlib.js +921 -0
- package/src/semantic/types.js +765 -14
- package/src/semantic/validate.js +2934 -209
- package/src/stdlib-facades.js +1211 -0
- package/src/stdlib-negotiation.js +446 -0
- package/src/stdlib-providers.js +377 -0
- package/src/toolchains.js +16 -3
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import {SemantifoldDiagnostic} from "../diagnostic.js"
|
|
4
|
+
import {parseStdlibVersionRange} from "./stdlib.js"
|
|
5
|
+
|
|
6
|
+
const authoritySchema = "SemantifoldCapabilityAuthority"
|
|
7
|
+
const authorityIdPattern = /^[a-z][a-z0-9.-]*$/u
|
|
8
|
+
const declarationNamePattern = /^[A-Za-z_][A-Za-z0-9_]*$/u
|
|
9
|
+
const scalarNames = new Set(["boolean", "integer", "string"])
|
|
10
|
+
const createdAuthorities = new WeakSet()
|
|
11
|
+
/**
|
|
12
|
+
* @typedef CapabilityAuthorityV2State
|
|
13
|
+
* @property {{candidate: import("./types.js").CapabilityDeclarationInput, capabilityId: string, failures: import("./types.js").EffectFailureDeclaration[], resources: import("./types.js").EffectResourceDeclaration[]}[]} capabilities - Prepared capability declarations.
|
|
14
|
+
* @property {Map<string, string | undefined>} failureNames - Module-local failures.
|
|
15
|
+
* @property {string} identity - Canonical module identity.
|
|
16
|
+
* @property {number} moduleIndex - Stable module position.
|
|
17
|
+
* @property {Map<string, string | undefined>} resourceNames - Module-local resources.
|
|
18
|
+
* @property {string} contractVersion - Canonical contract range.
|
|
19
|
+
*/
|
|
20
|
+
const task034ProbeOperations = Object.freeze([
|
|
21
|
+
Object.freeze({failures: Object.freeze(["capability:0/failure:0"]), flow: Object.freeze({kind: "none"}),
|
|
22
|
+
name: "probeEffect", parameters: Object.freeze([["label", "string"], ["value", "integer"], ["fail", "boolean"]]),
|
|
23
|
+
returnType: "integer"}),
|
|
24
|
+
Object.freeze({failures: Object.freeze(["capability:0/failure:1"]),
|
|
25
|
+
flow: Object.freeze({kind: "acquire", resourceId: "capability:0/resource:0"}), name: "probeAcquire",
|
|
26
|
+
parameters: Object.freeze([["fail", "boolean"]]), returnType: "resource"}),
|
|
27
|
+
Object.freeze({failures: Object.freeze(["capability:0/failure:2", "capability:0/failure:4"]),
|
|
28
|
+
flow: Object.freeze({kind: "borrow", parameterIndex: 0, terminalFailureId: "capability:0/failure:4"}), name: "probeRead",
|
|
29
|
+
parameters: Object.freeze([["resource", "resource"], ["fail", "boolean"]]), returnType: "optional:string"}),
|
|
30
|
+
Object.freeze({failures: Object.freeze(["capability:0/failure:3", "capability:0/failure:4"]),
|
|
31
|
+
flow: Object.freeze({kind: "close", parameterIndex: 0, terminalFailureId: "capability:0/failure:4"}), name: "probeClose",
|
|
32
|
+
parameters: Object.freeze([["resource", "resource"], ["fail", "boolean"]]), returnType: "void"}),
|
|
33
|
+
Object.freeze({failures: Object.freeze([]), flow: Object.freeze({kind: "none"}), name: "probeTrace",
|
|
34
|
+
parameters: Object.freeze([]), returnType: "string"})
|
|
35
|
+
])
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Creates an immutable, detached compiler-authority descriptor.
|
|
39
|
+
* @param {unknown} input - Untrusted authority input.
|
|
40
|
+
* @returns {Readonly<import("./types.js").CapabilityAuthority>} Normalized authority.
|
|
41
|
+
*/
|
|
42
|
+
export function createCapabilityAuthority(input) {
|
|
43
|
+
try {
|
|
44
|
+
if (createdAuthorities.has(/** @type {object} */ (input))) return /** @type {Readonly<import("./types.js").CapabilityAuthority>} */ (input)
|
|
45
|
+
requirePlain(input, "authority")
|
|
46
|
+
const authorityInput = /** @type {import("./types.js").CapabilityAuthorityInput} */ (input)
|
|
47
|
+
|
|
48
|
+
if (authorityInput.schemaVersion == 2) return createCapabilityAuthorityV2(authorityInput)
|
|
49
|
+
const hasContractVersion = Object.hasOwn(authorityInput, "contractVersion")
|
|
50
|
+
|
|
51
|
+
requireKeys(authorityInput, hasContractVersion
|
|
52
|
+
? ["capabilities", "contractVersion", "id", "schema", "schemaVersion"]
|
|
53
|
+
: ["capabilities", "id", "schema", "schemaVersion"], "authority")
|
|
54
|
+
if (hasContractVersion && parseStdlibVersionRange(authorityInput.contractVersion) === null) {
|
|
55
|
+
throw new SemantifoldDiagnostic({
|
|
56
|
+
code: "STDLIB_VERSION_MALFORMED",
|
|
57
|
+
language: "capability",
|
|
58
|
+
message: "Capability authority contractVersion is not a canonical stdlib version range."
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
if (authorityInput.schema != authoritySchema || authorityInput.schemaVersion != 1 || typeof authorityInput.id != "string" ||
|
|
62
|
+
!authorityIdPattern.test(authorityInput.id) || !dense(authorityInput.capabilities)) invalid("Authority schema, version, identity, or capability list is invalid.")
|
|
63
|
+
|
|
64
|
+
const capabilityNames = new Set()
|
|
65
|
+
const authorityResourceNames = new Set()
|
|
66
|
+
const authorityFailureNames = new Set()
|
|
67
|
+
const authorityOperationNames = new Set()
|
|
68
|
+
const capabilities = authorityInput.capabilities.map((candidate, capabilityIndex) => {
|
|
69
|
+
requirePlain(candidate, `capability ${capabilityIndex}`)
|
|
70
|
+
requireKeys(candidate, ["failures", "name", "operations", "resources"], `capability ${capabilityIndex}`)
|
|
71
|
+
requireName(candidate.name, capabilityNames, "capability")
|
|
72
|
+
if (!dense(candidate.resources) || !dense(candidate.failures) || !dense(candidate.operations)) {
|
|
73
|
+
invalid(`Capability '${candidate.name}' requires ordered resource, failure, and operation arrays.`)
|
|
74
|
+
}
|
|
75
|
+
const capabilityId = `capability:${capabilityIndex}`
|
|
76
|
+
const resourceNames = new Map()
|
|
77
|
+
const resources = candidate.resources.map((resource, resourceIndex) => {
|
|
78
|
+
requireNamed(resource, resourceNames, "resource")
|
|
79
|
+
requireName(resource.name, authorityResourceNames, "authority resource")
|
|
80
|
+
const id = `${capabilityId}/resource:${resourceIndex}`
|
|
81
|
+
resourceNames.set(resource.name, id)
|
|
82
|
+
return {id, kind: /** @type {const} */ ("EffectResourceDeclaration"), name: resource.name}
|
|
83
|
+
})
|
|
84
|
+
const failureNames = new Map()
|
|
85
|
+
const failures = candidate.failures.map((failure, failureIndex) => {
|
|
86
|
+
requireNamed(failure, failureNames, "failure")
|
|
87
|
+
requireName(failure.name, authorityFailureNames, "authority failure")
|
|
88
|
+
const id = `${capabilityId}/failure:${failureIndex}`
|
|
89
|
+
failureNames.set(failure.name, id)
|
|
90
|
+
return {id, kind: /** @type {const} */ ("EffectFailureDeclaration"), name: failure.name}
|
|
91
|
+
})
|
|
92
|
+
const operationNames = new Set()
|
|
93
|
+
const operations = candidate.operations.map((operation, operationIndex) => {
|
|
94
|
+
requirePlain(operation, `operation ${operationIndex}`)
|
|
95
|
+
requireKeys(operation, ["effects", "failures", "name", "parameters", "resourceFlow", "returnType"], `operation ${operationIndex}`)
|
|
96
|
+
requireName(operation.name, operationNames, "operation")
|
|
97
|
+
requireName(operation.name, authorityOperationNames, "authority operation")
|
|
98
|
+
if (!dense(operation.parameters) || !dense(operation.failures) || !dense(operation.effects) ||
|
|
99
|
+
operation.effects.length != 1 || operation.effects[0] != "host") invalid(`Operation '${operation.name}' has an invalid effect declaration.`)
|
|
100
|
+
const parameterNames = new Set()
|
|
101
|
+
const parameters = operation.parameters.map((parameter, parameterIndex) => {
|
|
102
|
+
requirePlain(parameter, `parameter ${parameterIndex}`)
|
|
103
|
+
requireKeys(parameter, ["name", "type"], `parameter ${parameterIndex}`)
|
|
104
|
+
requireName(parameter.name, parameterNames, "parameter")
|
|
105
|
+
return {name: parameter.name, type: normalizeValueType(parameter.type, resourceNames)}
|
|
106
|
+
})
|
|
107
|
+
const returnType = operation.returnType == "void" ? {kind: /** @type {const} */ ("TypeReference"), name: /** @type {const} */ ("void")} :
|
|
108
|
+
normalizeValueType(operation.returnType, resourceNames)
|
|
109
|
+
const failureIds = operation.failures.map((name) => {
|
|
110
|
+
if (typeof name != "string" || !failureNames.has(name)) invalid(`Operation '${operation.name}' names an undeclared failure '${String(name)}'.`)
|
|
111
|
+
return /** @type {string} */ (failureNames.get(name))
|
|
112
|
+
})
|
|
113
|
+
if (new Set(failureIds).size != failureIds.length) invalid(`Operation '${operation.name}' repeats a declared failure.`)
|
|
114
|
+
const resourceFlow = normalizeResourceFlow(operation.resourceFlow, resources, resourceNames, failureNames, parameters)
|
|
115
|
+
|
|
116
|
+
validateResourceOperation(operation.name, parameters, returnType, failureIds, resourceFlow)
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
effects: /** @type {const} */ (["host"]),
|
|
120
|
+
failureIds,
|
|
121
|
+
id: `${capabilityId}/operation:${operationIndex}`,
|
|
122
|
+
kind: /** @type {const} */ ("EffectOperationDeclaration"),
|
|
123
|
+
name: operation.name,
|
|
124
|
+
parameters,
|
|
125
|
+
resourceFlow,
|
|
126
|
+
returnType
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
authorityId: authorityInput.id,
|
|
132
|
+
failures,
|
|
133
|
+
id: capabilityId,
|
|
134
|
+
kind: /** @type {const} */ ("EffectCapabilityDeclaration"),
|
|
135
|
+
name: candidate.name,
|
|
136
|
+
operations,
|
|
137
|
+
resources
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
const result = /** @type {import("./types.js").CapabilityAuthority} */ ({
|
|
141
|
+
capabilities,
|
|
142
|
+
id: authorityInput.id,
|
|
143
|
+
schema: authoritySchema,
|
|
144
|
+
schemaVersion: 1,
|
|
145
|
+
...(hasContractVersion ? {contractVersion: /** @type {string} */ (authorityInput.contractVersion)} : {})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
deepFreeze(result)
|
|
149
|
+
createdAuthorities.add(result)
|
|
150
|
+
return result
|
|
151
|
+
} catch (error) {
|
|
152
|
+
if (error instanceof SemantifoldDiagnostic) throw error
|
|
153
|
+
return invalid("Authority payload is malformed.")
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Normalizes a strict multi-module schema-v2 authority.
|
|
159
|
+
* @param {import("./types.js").CapabilityAuthorityInput} authorityInput - Validated outer object candidate.
|
|
160
|
+
* @returns {Readonly<import("./types.js").CapabilityAuthority>} Normalized authority.
|
|
161
|
+
*/
|
|
162
|
+
function createCapabilityAuthorityV2(authorityInput) {
|
|
163
|
+
requireKeys(/** @type {Record<string, unknown>} */ (authorityInput), ["id", "modules", "schema", "schemaVersion"], "authority")
|
|
164
|
+
if (authorityInput.schema != authoritySchema || authorityInput.schemaVersion != 2 ||
|
|
165
|
+
typeof authorityInput.id != "string" || !authorityIdPattern.test(authorityInput.id) || !dense(authorityInput.modules)) {
|
|
166
|
+
invalid("Authority schema, version, identity, or module list is invalid.")
|
|
167
|
+
}
|
|
168
|
+
const identities = new Set()
|
|
169
|
+
const moduleInputs = authorityInput.modules.map((candidate, moduleIndex) => {
|
|
170
|
+
requirePlain(candidate, `module ${moduleIndex}`)
|
|
171
|
+
requireKeys(candidate, ["capabilities", "contractVersion", "identity"], `module ${moduleIndex}`)
|
|
172
|
+
if (typeof candidate.identity != "string" || !authorityIdPattern.test(candidate.identity) || identities.has(candidate.identity) ||
|
|
173
|
+
parseStdlibVersionRange(candidate.contractVersion) === null || !dense(candidate.capabilities)) {
|
|
174
|
+
invalid(`Authority module ${moduleIndex} is invalid or duplicate.`)
|
|
175
|
+
}
|
|
176
|
+
identities.add(candidate.identity)
|
|
177
|
+
return candidate
|
|
178
|
+
})
|
|
179
|
+
const states = moduleInputs.map((moduleInput, moduleIndex) => {
|
|
180
|
+
const resourceNames = new Map()
|
|
181
|
+
const failureNames = new Map()
|
|
182
|
+
const capabilityNames = new Set()
|
|
183
|
+
const capabilities = moduleInput.capabilities.map((candidate, capabilityIndex) => {
|
|
184
|
+
requirePlain(candidate, `module ${moduleIndex} capability ${capabilityIndex}`)
|
|
185
|
+
requireKeys(candidate, ["failures", "name", "operations", "resources"], `module ${moduleIndex} capability ${capabilityIndex}`)
|
|
186
|
+
requireName(candidate.name, capabilityNames, "capability")
|
|
187
|
+
if (!dense(candidate.resources) || !dense(candidate.failures) || !dense(candidate.operations)) {
|
|
188
|
+
invalid(`Capability '${candidate.name}' requires ordered resource, failure, and operation arrays.`)
|
|
189
|
+
}
|
|
190
|
+
const capabilityId = `module:${moduleIndex}/capability:${capabilityIndex}`
|
|
191
|
+
const resources = candidate.resources.map((resource, resourceIndex) => {
|
|
192
|
+
requireNamed(resource, resourceNames, "module resource")
|
|
193
|
+
const id = `${capabilityId}/resource:${resourceIndex}`
|
|
194
|
+
|
|
195
|
+
resourceNames.set(resource.name, id)
|
|
196
|
+
return {id, kind: /** @type {const} */ ("EffectResourceDeclaration"), name: resource.name}
|
|
197
|
+
})
|
|
198
|
+
const failures = candidate.failures.map((failure, failureIndex) => {
|
|
199
|
+
requireNamed(failure, failureNames, "module failure")
|
|
200
|
+
const id = `${capabilityId}/failure:${failureIndex}`
|
|
201
|
+
|
|
202
|
+
failureNames.set(failure.name, id)
|
|
203
|
+
return {id, kind: /** @type {const} */ ("EffectFailureDeclaration"), name: failure.name}
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
return {candidate, capabilityId, failures, resources}
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
return {capabilities, failureNames, identity: moduleInput.identity, moduleIndex, resourceNames,
|
|
210
|
+
contractVersion: moduleInput.contractVersion}
|
|
211
|
+
})
|
|
212
|
+
const operationNames = new Set()
|
|
213
|
+
const normalizedModules = states.map((state) => {
|
|
214
|
+
const capabilities = state.capabilities.map((capabilityState) => {
|
|
215
|
+
const localOperationNames = new Set()
|
|
216
|
+
const operations = capabilityState.candidate.operations.map((operation, operationIndex) => {
|
|
217
|
+
requirePlain(operation, `module ${state.moduleIndex} operation ${operationIndex}`)
|
|
218
|
+
requireKeys(operation, ["effects", "failures", "name", "parameters", "resourceFlow", "returnType"],
|
|
219
|
+
`module ${state.moduleIndex} operation ${operationIndex}`)
|
|
220
|
+
requireName(operation.name, localOperationNames, "operation")
|
|
221
|
+
const qualifiedOperationName = `${state.identity}\u0000${operation.name}`
|
|
222
|
+
|
|
223
|
+
if (operationNames.has(qualifiedOperationName)) invalid(`Duplicate authority operation '${operation.name}'.`)
|
|
224
|
+
operationNames.add(qualifiedOperationName)
|
|
225
|
+
if (!dense(operation.parameters) || !dense(operation.failures) || !dense(operation.effects) ||
|
|
226
|
+
operation.effects.length != 1 || operation.effects[0] != "host") {
|
|
227
|
+
invalid(`Operation '${operation.name}' has an invalid effect declaration.`)
|
|
228
|
+
}
|
|
229
|
+
const parameterNames = new Set()
|
|
230
|
+
const parameters = operation.parameters.map((parameter, parameterIndex) => {
|
|
231
|
+
requirePlain(parameter, `parameter ${parameterIndex}`)
|
|
232
|
+
requireKeys(parameter, ["name", "type"], `parameter ${parameterIndex}`)
|
|
233
|
+
requireName(parameter.name, parameterNames, "parameter")
|
|
234
|
+
return {name: parameter.name, type: normalizeV2ValueType(parameter.type, state, states)}
|
|
235
|
+
})
|
|
236
|
+
const returnType = operation.returnType == "void"
|
|
237
|
+
? {kind: /** @type {const} */ ("TypeReference"), name: /** @type {const} */ ("void")}
|
|
238
|
+
: normalizeV2ValueType(operation.returnType, state, states)
|
|
239
|
+
const failureIds = operation.failures.map((failure) => resolveV2Declaration(failure, state, states, "failure"))
|
|
240
|
+
|
|
241
|
+
if (new Set(failureIds).size != failureIds.length) invalid(`Operation '${operation.name}' repeats a declared failure.`)
|
|
242
|
+
const resourceFlow = normalizeV2ResourceFlow(operation.resourceFlow, parameters, state, states)
|
|
243
|
+
|
|
244
|
+
validateResourceOperation(operation.name, parameters, returnType, failureIds, resourceFlow)
|
|
245
|
+
return {
|
|
246
|
+
effects: /** @type {const} */ (["host"]),
|
|
247
|
+
failureIds,
|
|
248
|
+
id: `${capabilityState.capabilityId}/operation:${operationIndex}`,
|
|
249
|
+
kind: /** @type {const} */ ("EffectOperationDeclaration"),
|
|
250
|
+
name: operation.name,
|
|
251
|
+
parameters,
|
|
252
|
+
resourceFlow,
|
|
253
|
+
returnType
|
|
254
|
+
}
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
authorityId: state.identity,
|
|
259
|
+
failures: capabilityState.failures,
|
|
260
|
+
id: capabilityState.capabilityId,
|
|
261
|
+
kind: /** @type {const} */ ("EffectCapabilityDeclaration"),
|
|
262
|
+
name: capabilityState.candidate.name,
|
|
263
|
+
operations,
|
|
264
|
+
resources: capabilityState.resources
|
|
265
|
+
}
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
return {capabilities, contractVersion: state.contractVersion, identity: state.identity}
|
|
269
|
+
})
|
|
270
|
+
const result = /** @type {import("./types.js").CapabilityAuthority} */ ({
|
|
271
|
+
capabilities: normalizedModules.flatMap(({capabilities}) => capabilities),
|
|
272
|
+
id: authorityInput.id,
|
|
273
|
+
modules: normalizedModules,
|
|
274
|
+
schema: authoritySchema,
|
|
275
|
+
schemaVersion: 2
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
deepFreeze(result)
|
|
279
|
+
createdAuthorities.add(result)
|
|
280
|
+
return result
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Normalizes one schema-v2 value type.
|
|
285
|
+
* @param {unknown} candidate - Candidate type.
|
|
286
|
+
* @param {CapabilityAuthorityV2State} state - Current module state.
|
|
287
|
+
* @param {CapabilityAuthorityV2State[]} states - Complete authority states.
|
|
288
|
+
* @returns {import("./types.js").SemanticValueType} Normalized type.
|
|
289
|
+
*/
|
|
290
|
+
function normalizeV2ValueType(candidate, state, states) {
|
|
291
|
+
if (typeof candidate == "string" && scalarNames.has(candidate)) {
|
|
292
|
+
return {kind: /** @type {const} */ ("TypeReference"), name: /** @type {import("./types.js").SemanticTypeName} */ (candidate)}
|
|
293
|
+
}
|
|
294
|
+
requirePlain(candidate, "operation type")
|
|
295
|
+
if (candidate.kind == "OwnedResourceType") {
|
|
296
|
+
requireKeys(candidate, ["kind", "resource"], "owned resource type")
|
|
297
|
+
return {kind: /** @type {const} */ ("OwnedResourceType"), resourceId: resolveV2Declaration(candidate.resource, state, states, "resource")}
|
|
298
|
+
}
|
|
299
|
+
if (candidate.kind == "OptionalType") {
|
|
300
|
+
requireKeys(candidate, ["kind", "valueType"], "optional type")
|
|
301
|
+
if (typeof candidate.valueType != "string" || !scalarNames.has(candidate.valueType)) {
|
|
302
|
+
invalid("Capability optionals may contain only scalar values.")
|
|
303
|
+
}
|
|
304
|
+
return {kind: /** @type {const} */ ("OptionalType"), valueType: {kind: /** @type {const} */ ("TypeReference"),
|
|
305
|
+
name: /** @type {import("./types.js").SemanticTypeName} */ (candidate.valueType)}}
|
|
306
|
+
}
|
|
307
|
+
return invalid("Unsupported capability value type.")
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Normalizes one schema-v2 resource transition.
|
|
312
|
+
* @param {unknown} candidate - Candidate transition.
|
|
313
|
+
* @param {import("./types.js").EffectParameter[]} parameters - Normalized parameters.
|
|
314
|
+
* @param {CapabilityAuthorityV2State} state - Current module state.
|
|
315
|
+
* @param {CapabilityAuthorityV2State[]} states - Complete authority states.
|
|
316
|
+
* @returns {import("./types.js").EffectResourceFlow} Normalized transition.
|
|
317
|
+
*/
|
|
318
|
+
function normalizeV2ResourceFlow(candidate, parameters, state, states) {
|
|
319
|
+
requirePlain(candidate, "resource flow")
|
|
320
|
+
if (candidate.kind == "none") {
|
|
321
|
+
requireKeys(candidate, ["kind"], "resource flow")
|
|
322
|
+
return {kind: /** @type {const} */ ("none")}
|
|
323
|
+
}
|
|
324
|
+
if (candidate.kind == "acquire") {
|
|
325
|
+
requireKeys(candidate, ["kind", "resource"], "acquisition flow")
|
|
326
|
+
return {kind: /** @type {const} */ ("acquire"), resourceId: resolveV2Declaration(candidate.resource, state, states, "resource")}
|
|
327
|
+
}
|
|
328
|
+
if (candidate.kind == "borrow" || candidate.kind == "close") {
|
|
329
|
+
requireKeys(candidate, ["kind", "parameterIndex", "terminalFailure"], `${candidate.kind} flow`)
|
|
330
|
+
const parameterIndex = /** @type {number} */ (candidate.parameterIndex)
|
|
331
|
+
|
|
332
|
+
if (!Number.isSafeInteger(parameterIndex) || parameterIndex < 0 || parameterIndex >= parameters.length ||
|
|
333
|
+
parameters[parameterIndex].type.kind != "OwnedResourceType") invalid(`${candidate.kind} flow requires an owned resource parameter.`)
|
|
334
|
+
return {kind: /** @type {"borrow" | "close"} */ (candidate.kind), parameterIndex,
|
|
335
|
+
terminalFailureId: resolveV2Declaration(candidate.terminalFailure, state, states, "failure")}
|
|
336
|
+
}
|
|
337
|
+
return invalid("Unknown resource-flow kind.")
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Resolves one local or qualified schema-v2 resource/failure reference.
|
|
342
|
+
* @param {unknown} candidate - Candidate reference.
|
|
343
|
+
* @param {CapabilityAuthorityV2State} state - Current module state.
|
|
344
|
+
* @param {CapabilityAuthorityV2State[]} states - Complete authority states.
|
|
345
|
+
* @param {"resource" | "failure"} kind - Declaration namespace.
|
|
346
|
+
* @returns {string} Normalized declaration identity.
|
|
347
|
+
*/
|
|
348
|
+
function resolveV2Declaration(candidate, state, states, kind) {
|
|
349
|
+
/** @type {CapabilityAuthorityV2State | undefined} */
|
|
350
|
+
let owner = state
|
|
351
|
+
let name = candidate
|
|
352
|
+
|
|
353
|
+
if (candidate !== null && typeof candidate == "object" && !Array.isArray(candidate)) {
|
|
354
|
+
requirePlain(candidate, `${kind} reference`)
|
|
355
|
+
requireKeys(candidate, ["module", "name"], `${kind} reference`)
|
|
356
|
+
if (typeof candidate.module != "string" || typeof candidate.name != "string") invalid(`Invalid qualified ${kind} reference.`)
|
|
357
|
+
owner = states.find(({identity}) => identity == candidate.module)
|
|
358
|
+
name = candidate.name
|
|
359
|
+
}
|
|
360
|
+
if (!owner || typeof name != "string") invalid(`Unknown ${kind} reference.`)
|
|
361
|
+
const declarations = kind == "resource" ? owner.resourceNames : owner.failureNames
|
|
362
|
+
const id = declarations.get(name)
|
|
363
|
+
|
|
364
|
+
if (id === undefined) invalid(`Unknown ${kind} '${owner.identity}.${name}'.`)
|
|
365
|
+
return id
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Normalizes an optional parse authority.
|
|
370
|
+
* @param {unknown} input - Optional caller input.
|
|
371
|
+
* @returns {Readonly<import("./types.js").CapabilityAuthority> | undefined} Authority or legacy absence.
|
|
372
|
+
*/
|
|
373
|
+
export function normalizeCapabilityAuthority(input) {
|
|
374
|
+
return input === undefined ? undefined : createCapabilityAuthority(input)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Finds one source-visible operation only when an ordinary source declaration did not win resolution.
|
|
379
|
+
* @param {readonly import("./types.js").EffectCapabilityDeclaration[]} capabilities - Authorized declarations.
|
|
380
|
+
* @param {string} name - Source spelling.
|
|
381
|
+
* @returns {{capability: import("./types.js").EffectCapabilityDeclaration, operation: import("./types.js").EffectOperationDeclaration} | undefined} Match.
|
|
382
|
+
*/
|
|
383
|
+
export function authorizedOperation(capabilities, name) {
|
|
384
|
+
for (const capability of capabilities) {
|
|
385
|
+
const operation = capability.operations.find((candidate) => candidate.name == name)
|
|
386
|
+
if (operation) return {capability, operation}
|
|
387
|
+
}
|
|
388
|
+
return undefined
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Checks the bounded conformance operation namespace.
|
|
393
|
+
* @param {string} name - Candidate source name.
|
|
394
|
+
* @returns {boolean} Whether the name belongs to Task 034.
|
|
395
|
+
*/
|
|
396
|
+
export function isTask034OperationName(name) {
|
|
397
|
+
return ["probeAcquire", "probeClose", "probeEffect", "probeRead", "probeTrace"].includes(name)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Checks the exact built-in conformance capability identity.
|
|
402
|
+
* @param {readonly import("./types.js").EffectCapabilityDeclaration[]} capabilities - Candidate graph.
|
|
403
|
+
* @returns {boolean} Whether the graph has the supported identity and declaration order.
|
|
404
|
+
*/
|
|
405
|
+
export function isTask034ResourceProbe(capabilities) {
|
|
406
|
+
if (!dense(capabilities) || capabilities.length != 1) return false
|
|
407
|
+
const capability = capabilities[0]
|
|
408
|
+
|
|
409
|
+
if (!plainWithKeys(capability, ["authorityId", "failures", "id", "kind", "name", "operations", "resources"]) ||
|
|
410
|
+
capability.authorityId != "semantifold.task034.resource-probe" || capability.id != "capability:0" ||
|
|
411
|
+
capability.kind != "EffectCapabilityDeclaration" || capability.name != "ResourceProbe" ||
|
|
412
|
+
!dense(capability.resources) || !dense(capability.failures) || !dense(capability.operations) ||
|
|
413
|
+
capability.resources.length != 1 || capability.failures.length != 5 || capability.operations.length != 5) return false
|
|
414
|
+
if (!exactNamedDeclaration(capability.resources[0], "EffectResourceDeclaration", "capability:0/resource:0", "ProbeResource")) return false
|
|
415
|
+
const expectedFailures = ["ProbeOperationFailure", "ProbeAcquireFailure", "ProbeReadFailure", "ProbeCloseFailure", "ProbeResourceClosed"]
|
|
416
|
+
|
|
417
|
+
if (!capability.failures.every((failure, index) =>
|
|
418
|
+
exactNamedDeclaration(failure, "EffectFailureDeclaration", `capability:0/failure:${index}`, expectedFailures[index]))) return false
|
|
419
|
+
return capability.operations.every((operation, index) => exactProbeOperation(operation, index, task034ProbeOperations[index]))
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Checks one exact normalized declaration.
|
|
424
|
+
* @param {unknown} candidate - Candidate declaration.
|
|
425
|
+
* @param {string} kind - Expected kind.
|
|
426
|
+
* @param {string} id - Expected identity.
|
|
427
|
+
* @param {string} name - Expected source name.
|
|
428
|
+
* @returns {boolean} Whether the declaration is exact.
|
|
429
|
+
*/
|
|
430
|
+
function exactNamedDeclaration(candidate, kind, id, name) {
|
|
431
|
+
return plainWithKeys(candidate, ["id", "kind", "name"]) && candidate.kind == kind && candidate.id == id && candidate.name == name
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Checks one exact Task 034 conformance operation.
|
|
436
|
+
* @param {unknown} candidate - Candidate operation.
|
|
437
|
+
* @param {number} index - Expected operation index.
|
|
438
|
+
* @param {(typeof task034ProbeOperations)[number]} expected - Exact contract.
|
|
439
|
+
* @returns {boolean} Whether the operation matches the protected binding.
|
|
440
|
+
*/
|
|
441
|
+
function exactProbeOperation(candidate, index, expected) {
|
|
442
|
+
if (!plainWithKeys(candidate, ["effects", "failureIds", "id", "kind", "name", "parameters", "resourceFlow", "returnType"]) ||
|
|
443
|
+
candidate.kind != "EffectOperationDeclaration" || candidate.id != `capability:0/operation:${index}` ||
|
|
444
|
+
candidate.name != expected.name || !sameStringArray(candidate.effects, ["host"]) ||
|
|
445
|
+
!sameStringArray(candidate.failureIds, expected.failures) || !dense(candidate.parameters) ||
|
|
446
|
+
candidate.parameters.length != expected.parameters.length || !exactFlatObject(candidate.resourceFlow, expected.flow) ||
|
|
447
|
+
!exactProbeType(candidate.returnType, expected.returnType)) return false
|
|
448
|
+
return candidate.parameters.every((parameter, parameterIndex) => plainWithKeys(parameter, ["name", "type"]) &&
|
|
449
|
+
parameter.name == expected.parameters[parameterIndex][0] && exactProbeType(parameter.type, expected.parameters[parameterIndex][1]))
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Checks one exact normalized conformance type.
|
|
454
|
+
* @param {unknown} candidate - Candidate type.
|
|
455
|
+
* @param {string} expected - Compact expected type.
|
|
456
|
+
* @returns {boolean} Whether the type is exact.
|
|
457
|
+
*/
|
|
458
|
+
function exactProbeType(candidate, expected) {
|
|
459
|
+
if (expected == "resource") {
|
|
460
|
+
return plainWithKeys(candidate, ["kind", "resourceId"]) && candidate.kind == "OwnedResourceType" &&
|
|
461
|
+
candidate.resourceId == "capability:0/resource:0"
|
|
462
|
+
}
|
|
463
|
+
if (expected == "optional:string") {
|
|
464
|
+
return plainWithKeys(candidate, ["kind", "valueType"]) && candidate.kind == "OptionalType" &&
|
|
465
|
+
exactProbeType(candidate.valueType, "string")
|
|
466
|
+
}
|
|
467
|
+
return plainWithKeys(candidate, ["kind", "name"]) && candidate.kind == "TypeReference" && candidate.name == expected
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Checks an exact shallow object of primitive contract fields.
|
|
472
|
+
* @param {unknown} candidate - Candidate object.
|
|
473
|
+
* @param {Readonly<Record<string, unknown>>} expected - Expected fields.
|
|
474
|
+
* @returns {boolean} Whether all fields and values match.
|
|
475
|
+
*/
|
|
476
|
+
function exactFlatObject(candidate, expected) {
|
|
477
|
+
const keys = Object.keys(expected)
|
|
478
|
+
return plainWithKeys(candidate, keys) && keys.every((key) => candidate[key] == expected[key])
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Checks an exact dense string sequence.
|
|
483
|
+
* @param {unknown} candidate - Candidate sequence.
|
|
484
|
+
* @param {readonly string[]} expected - Expected strings.
|
|
485
|
+
* @returns {boolean} Whether order and values match.
|
|
486
|
+
*/
|
|
487
|
+
function sameStringArray(candidate, expected) {
|
|
488
|
+
return dense(candidate) && candidate.length == expected.length && candidate.every((value, index) => value == expected[index])
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Checks an ordinary object's exact own-key set.
|
|
493
|
+
* @param {unknown} candidate - Candidate object.
|
|
494
|
+
* @param {string[]} keys - Exact keys.
|
|
495
|
+
* @returns {candidate is Record<string, unknown>} Whether the object and fields are exact.
|
|
496
|
+
*/
|
|
497
|
+
function plainWithKeys(candidate, keys) {
|
|
498
|
+
return candidate !== null && typeof candidate == "object" && !Array.isArray(candidate) &&
|
|
499
|
+
Object.getPrototypeOf(candidate) == Object.prototype && Object.keys(candidate).sort().join(",") == [...keys].sort().join(",")
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Normalizes one closed operation value type.
|
|
504
|
+
* @param {unknown} candidate - Raw value type.
|
|
505
|
+
* @param {Map<string, string>} resourceNames - Resource identities by name.
|
|
506
|
+
* @returns {import("./types.js").SemanticValueType} Normalized type.
|
|
507
|
+
*/
|
|
508
|
+
function normalizeValueType(candidate, resourceNames) {
|
|
509
|
+
if (typeof candidate == "string" && scalarNames.has(candidate)) {
|
|
510
|
+
return {kind: /** @type {const} */ ("TypeReference"), name: /** @type {import("./types.js").SemanticTypeName} */ (candidate)}
|
|
511
|
+
}
|
|
512
|
+
requirePlain(candidate, "operation type")
|
|
513
|
+
if (candidate.kind == "OwnedResourceType") {
|
|
514
|
+
requireKeys(candidate, ["kind", "resource"], "owned resource type")
|
|
515
|
+
if (typeof candidate.resource != "string" || !resourceNames.has(candidate.resource)) invalid(`Unknown owned resource '${String(candidate.resource)}'.`)
|
|
516
|
+
return {kind: /** @type {const} */ ("OwnedResourceType"), resourceId: /** @type {string} */ (resourceNames.get(candidate.resource))}
|
|
517
|
+
}
|
|
518
|
+
if (candidate.kind == "OptionalType") {
|
|
519
|
+
requireKeys(candidate, ["kind", "valueType"], "optional type")
|
|
520
|
+
if (typeof candidate.valueType != "string" || !scalarNames.has(candidate.valueType)) invalid("Capability optionals may contain only scalar values.")
|
|
521
|
+
return {kind: /** @type {const} */ ("OptionalType"), valueType: {kind: /** @type {const} */ ("TypeReference"),
|
|
522
|
+
name: /** @type {import("./types.js").SemanticTypeName} */ (candidate.valueType)}}
|
|
523
|
+
}
|
|
524
|
+
return invalid("Unsupported capability value type.")
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Normalizes one closed resource transition.
|
|
529
|
+
* @param {unknown} candidate - Raw transition.
|
|
530
|
+
* @param {import("./types.js").EffectResourceDeclaration[]} resources - Declared resources.
|
|
531
|
+
* @param {Map<string, string>} resourceNames - Resource identities by name.
|
|
532
|
+
* @param {Map<string, string>} failureNames - Failure identities by name.
|
|
533
|
+
* @param {import("./types.js").EffectParameter[]} parameters - Normalized ordered parameters.
|
|
534
|
+
* @returns {import("./types.js").EffectResourceFlow} Normalized flow.
|
|
535
|
+
*/
|
|
536
|
+
function normalizeResourceFlow(candidate, resources, resourceNames, failureNames, parameters) {
|
|
537
|
+
requirePlain(candidate, "resource flow")
|
|
538
|
+
if (candidate.kind == "none") {
|
|
539
|
+
requireKeys(candidate, ["kind"], "resource flow")
|
|
540
|
+
return {kind: /** @type {const} */ ("none")}
|
|
541
|
+
}
|
|
542
|
+
if (candidate.kind == "acquire") {
|
|
543
|
+
requireKeys(candidate, ["kind", "resource"], "acquisition flow")
|
|
544
|
+
if (typeof candidate.resource != "string" || !resourceNames.has(candidate.resource)) invalid("Acquisition names an undeclared resource.")
|
|
545
|
+
const resourceId = resourceNames.get(candidate.resource)
|
|
546
|
+
if (!resources.some(({id}) => id == resourceId)) invalid("Acquisition resource identity is invalid.")
|
|
547
|
+
return {kind: /** @type {const} */ ("acquire"), resourceId: /** @type {string} */ (resourceId)}
|
|
548
|
+
}
|
|
549
|
+
if (candidate.kind == "borrow" || candidate.kind == "close") {
|
|
550
|
+
requireKeys(candidate, ["kind", "parameterIndex", "terminalFailure"], `${candidate.kind} flow`)
|
|
551
|
+
const parameterIndex = /** @type {number} */ (candidate.parameterIndex)
|
|
552
|
+
if (!Number.isSafeInteger(parameterIndex) || parameterIndex < 0 || parameterIndex >= parameters.length ||
|
|
553
|
+
parameters[parameterIndex].type.kind != "OwnedResourceType") invalid(`${candidate.kind} flow requires an owned resource parameter.`)
|
|
554
|
+
if (typeof candidate.terminalFailure != "string" || !failureNames.has(candidate.terminalFailure)) {
|
|
555
|
+
invalid(`${candidate.kind} flow requires a declared terminal failure.`)
|
|
556
|
+
}
|
|
557
|
+
return {kind: /** @type {"borrow" | "close"} */ (candidate.kind), parameterIndex,
|
|
558
|
+
terminalFailureId: /** @type {string} */ (failureNames.get(candidate.terminalFailure))}
|
|
559
|
+
}
|
|
560
|
+
return invalid("Unknown resource-flow kind.")
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Requires every owned parameter/result to have one complete modeled transition.
|
|
565
|
+
* @param {string} name - Operation name.
|
|
566
|
+
* @param {import("./types.js").EffectParameter[]} parameters - Ordered parameters.
|
|
567
|
+
* @param {import("./types.js").SemanticFunctionReturnType} returnType - Operation result.
|
|
568
|
+
* @param {string[]} failureIds - Declared failure identities.
|
|
569
|
+
* @param {import("./types.js").EffectResourceFlow} resourceFlow - Declared transition.
|
|
570
|
+
* @returns {void}
|
|
571
|
+
*/
|
|
572
|
+
function validateResourceOperation(name, parameters, returnType, failureIds, resourceFlow) {
|
|
573
|
+
const ownedParameters = parameters.flatMap((parameter, index) => parameter.type.kind == "OwnedResourceType" ? [index] : [])
|
|
574
|
+
|
|
575
|
+
if (resourceFlow.kind == "none") {
|
|
576
|
+
if (ownedParameters.length > 0 || returnType.kind == "OwnedResourceType") {
|
|
577
|
+
invalid(`Operation '${name}' has an owned value without a resource transition.`)
|
|
578
|
+
}
|
|
579
|
+
return
|
|
580
|
+
}
|
|
581
|
+
if (resourceFlow.kind == "acquire") {
|
|
582
|
+
if (ownedParameters.length > 0 || returnType.kind != "OwnedResourceType" || returnType.resourceId != resourceFlow.resourceId) {
|
|
583
|
+
invalid(`Operation '${name}' acquisition result does not match its resource transition.`)
|
|
584
|
+
}
|
|
585
|
+
return
|
|
586
|
+
}
|
|
587
|
+
if (ownedParameters.length != 1 || ownedParameters[0] != resourceFlow.parameterIndex ||
|
|
588
|
+
returnType.kind == "OwnedResourceType" || !failureIds.includes(resourceFlow.terminalFailureId)) {
|
|
589
|
+
invalid(`Operation '${name}' borrow/close transition is incomplete.`)
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Validates one single-field named declaration.
|
|
595
|
+
* @param {unknown} candidate - Raw declaration.
|
|
596
|
+
* @param {Map<string, string | undefined>} names - Already-declared names.
|
|
597
|
+
* @param {string} subject - Diagnostic subject.
|
|
598
|
+
* @returns {void}
|
|
599
|
+
*/
|
|
600
|
+
function requireNamed(candidate, names, subject) {
|
|
601
|
+
requirePlain(candidate, subject)
|
|
602
|
+
requireKeys(candidate, ["name"], subject)
|
|
603
|
+
if (typeof candidate.name != "string" || !declarationNamePattern.test(candidate.name) || names.has(candidate.name)) {
|
|
604
|
+
invalid(`Duplicate or invalid ${subject} name.`)
|
|
605
|
+
}
|
|
606
|
+
names.set(candidate.name, undefined)
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Validates and reserves one declaration name.
|
|
611
|
+
* @param {unknown} name - Candidate name.
|
|
612
|
+
* @param {Set<string>} names - Already-declared names.
|
|
613
|
+
* @param {string} subject - Diagnostic subject.
|
|
614
|
+
* @returns {void}
|
|
615
|
+
*/
|
|
616
|
+
function requireName(name, names, subject) {
|
|
617
|
+
if (typeof name != "string" || !declarationNamePattern.test(name) || names.has(name)) invalid(`Duplicate or invalid ${subject} name.`)
|
|
618
|
+
names.add(name)
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Requires an ordinary object.
|
|
623
|
+
* @param {unknown} value - Candidate value.
|
|
624
|
+
* @param {string} subject - Diagnostic subject.
|
|
625
|
+
* @returns {asserts value is Record<string, unknown>} Asserts an ordinary object.
|
|
626
|
+
*/
|
|
627
|
+
function requirePlain(value, subject) {
|
|
628
|
+
if (!value || typeof value != "object" || Array.isArray(value) || Object.getPrototypeOf(value) != Object.prototype) invalid(`Invalid ${subject} object.`)
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Requires one exact field set.
|
|
633
|
+
* @param {Record<string, unknown>} value - Candidate object.
|
|
634
|
+
* @param {string[]} keys - Required keys.
|
|
635
|
+
* @param {string} subject - Diagnostic subject.
|
|
636
|
+
* @returns {void}
|
|
637
|
+
*/
|
|
638
|
+
function requireKeys(value, keys, subject) {
|
|
639
|
+
if (Object.keys(value).sort().join(",") != [...keys].sort().join(",")) invalid(`Invalid ${subject} fields.`)
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Checks an ordered dense array.
|
|
644
|
+
* @param {unknown} value - Candidate array.
|
|
645
|
+
* @returns {value is unknown[]} Whether the value is dense.
|
|
646
|
+
*/
|
|
647
|
+
function dense(value) {
|
|
648
|
+
return Array.isArray(value) && value.every((_item, index) => Object.hasOwn(value, index))
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Recursively freezes a detached authority graph.
|
|
653
|
+
* @template {object} Value
|
|
654
|
+
* @param {Value} value - Object to freeze.
|
|
655
|
+
* @returns {Readonly<Value>} Frozen object.
|
|
656
|
+
*/
|
|
657
|
+
function deepFreeze(value) {
|
|
658
|
+
for (const child of Object.values(value)) {
|
|
659
|
+
if (child && typeof child == "object" && !Object.isFrozen(child)) deepFreeze(child)
|
|
660
|
+
}
|
|
661
|
+
return Object.freeze(value)
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Raises one stable authority diagnostic.
|
|
666
|
+
* @param {string} message - Diagnostic message.
|
|
667
|
+
* @returns {never} Always throws.
|
|
668
|
+
*/
|
|
669
|
+
function invalid(message) {
|
|
670
|
+
throw new SemantifoldDiagnostic({code: "INVALID_CAPABILITY_AUTHORITY", language: "capability", message})
|
|
671
|
+
}
|