@pulse-compute/wasm-contracts 0.0.0 → 1.0.0-beta.2

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.
Files changed (97) hide show
  1. package/README.md +35 -2
  2. package/package.json +124 -6
  3. package/src/assets/contracts.js +296 -0
  4. package/src/config/provider-resolution.js +54 -0
  5. package/src/config/runtime-provider-kv.js +73 -0
  6. package/src/config/symbolic-refs.js +192 -0
  7. package/src/crypto/contracts.js +647 -0
  8. package/src/definitions/config-schema.js +30 -0
  9. package/src/definitions/handler-roles.js +66 -0
  10. package/src/definitions/path-grammar.js +11 -0
  11. package/src/definitions/router-api.js +149 -0
  12. package/src/diagnostics/codes.js +42 -0
  13. package/src/diagnostics/reporter.js +20 -0
  14. package/src/diagnostics.js +139 -0
  15. package/src/dispatch-table.js +23 -0
  16. package/src/entities/catalog.d.ts +7 -0
  17. package/src/entities/catalog.js +87 -0
  18. package/src/entities/contracts.d.ts +95 -0
  19. package/src/entities/contracts.js +122 -0
  20. package/src/entities/json-rpc.d.ts +27 -0
  21. package/src/entities/json-rpc.js +69 -0
  22. package/src/entities/runtime.d.ts +67 -0
  23. package/src/entities/runtime.js +288 -0
  24. package/src/events/contracts.js +456 -0
  25. package/src/execution-plan.js +42 -0
  26. package/src/grip/contracts.js +106 -0
  27. package/src/handler/backend-json-request-body.js +58 -0
  28. package/src/handler/beta-lifecycle-golden.js +97 -0
  29. package/src/handler/canonical-native-plan.js +143 -0
  30. package/src/handler/canonical-native-runtime.js +168 -0
  31. package/src/handler/canonical-runtime.js +13 -0
  32. package/src/handler/context.js +131 -0
  33. package/src/handler/ctx-contract.js +71 -0
  34. package/src/handler/doctor-readiness-aggregator.js +67 -0
  35. package/src/handler/effects.js +446 -0
  36. package/src/handler/io-lifecycle.js +134 -0
  37. package/src/handler/lifecycle-authoring-alignment.js +56 -0
  38. package/src/handler/lifecycle-readiness-aggregate.js +93 -0
  39. package/src/handler/reference-node-lifecycle-golden.js +73 -0
  40. package/src/handler/request-json-body.js +147 -0
  41. package/src/handler/runtime-api.js +471 -0
  42. package/src/handler/schema-decode-result.js +135 -0
  43. package/src/handler/schema-response-codec.js +141 -0
  44. package/src/handler/surface-contract.js +601 -0
  45. package/src/handler/types-contract.js +51 -0
  46. package/src/handler-table.js +28 -0
  47. package/src/host/backend-capabilities.js +24 -0
  48. package/src/host/capabilities.js +273 -0
  49. package/src/host/channel-broadcaster.js +60 -0
  50. package/src/host/json-body.js +80 -0
  51. package/src/host/request-result-headers.js +38 -0
  52. package/src/host/streaming-passthrough.js +90 -0
  53. package/src/host/wasm-host-abi.js +154 -0
  54. package/src/index.js +75 -0
  55. package/src/jwt/contracts.js +427 -0
  56. package/src/library/api-surface.js +99 -0
  57. package/src/library/contracts.js +273 -0
  58. package/src/library/manifest.js +317 -0
  59. package/src/library/second-package-owned-lowering.js +39 -0
  60. package/src/library/sidecars.js +147 -0
  61. package/src/logging.js +89 -0
  62. package/src/package/package-contract.js +975 -0
  63. package/src/package/package-contract.schema.json +303 -0
  64. package/src/package/runtime-pulse-live.js +100 -0
  65. package/src/path-table.js +12 -0
  66. package/src/path.js +275 -0
  67. package/src/project/config-factory.js +48 -0
  68. package/src/project/config-plan.js +409 -0
  69. package/src/project/config-runtime.js +46 -0
  70. package/src/project/javascript-application.js +212 -0
  71. package/src/project/javascript-core-capabilities.js +44 -0
  72. package/src/project/native-optimization.js +9 -0
  73. package/src/project/package-ownership.js +242 -0
  74. package/src/project/package-product-projection.js +171 -0
  75. package/src/project/reachable-graph-identity.js +219 -0
  76. package/src/project/reachable-graph-projections.js +279 -0
  77. package/src/project/reachable-graph-resolver.js +303 -0
  78. package/src/project/reachable-graph-v2.js +430 -0
  79. package/src/project/reachable-graph-v2.schema.json +285 -0
  80. package/src/project/reachable-graph.js +199 -0
  81. package/src/project/reachable-graph.schema.json +222 -0
  82. package/src/project/target-support-evidence.js +559 -0
  83. package/src/provider/canonical-provider.js +239 -0
  84. package/src/provider/final-wasm-policy.js +87 -0
  85. package/src/provider/toolchain.js +771 -0
  86. package/src/route-plan.js +43 -0
  87. package/src/s3/contracts.js +12 -0
  88. package/src/schema-json/compile.js +63 -0
  89. package/src/schema-json/contracts.js +10 -0
  90. package/src/schema-json/generic-parser.js +62 -0
  91. package/src/schema-json/registry.js +359 -0
  92. package/src/schema-json/semantic-trace.js +241 -0
  93. package/src/schema-json/v1.js +65 -0
  94. package/src/schema-json/v2.js +110 -0
  95. package/src/sha256.js +104 -0
  96. package/src/spine.js +381 -0
  97. package/src/stable-id.js +142 -0
@@ -0,0 +1,43 @@
1
+ 'use strict';
2
+
3
+ const ROUTE_PLAN_VERSION = 'pulsewasm.route-plan.v4';
4
+ const ROUTE_STABLE_INPUT_VERSION = 'pulsewasm.route-stable-input.v2';
5
+
6
+ const ROUTE_STABLE_ID_POLICY = Object.freeze({
7
+ algorithm: 'sha256',
8
+ prefix: 'route',
9
+ input: 'method + normalized path + params + handler IDs + inherited middleware/error/lifecycle handler IDs + channel identity',
10
+ version: ROUTE_STABLE_INPUT_VERSION
11
+ });
12
+
13
+ const ROUTE_RUNTIME_ID_POLICY = Object.freeze({
14
+ field: 'runtimeId',
15
+ dense: true,
16
+ startsAt: 0,
17
+ assignment: 'deterministic flattened route order'
18
+ });
19
+
20
+ const ROUTE_ID_COMPATIBILITY_POLICY = Object.freeze({
21
+ id: 'alias of runtimeId for Phase 1 compatibility'
22
+ });
23
+
24
+ const ROUTE_ID_POLICY = Object.freeze({
25
+ stable: ROUTE_STABLE_ID_POLICY,
26
+ runtime: ROUTE_RUNTIME_ID_POLICY,
27
+ compatibility: ROUTE_ID_COMPATIBILITY_POLICY
28
+ });
29
+
30
+ const ROUTE_PLAN_POLICY = Object.freeze({
31
+ version: ROUTE_PLAN_VERSION,
32
+ idPolicy: ROUTE_ID_POLICY
33
+ });
34
+
35
+ module.exports = {
36
+ ROUTE_PLAN_VERSION,
37
+ ROUTE_STABLE_INPUT_VERSION,
38
+ ROUTE_STABLE_ID_POLICY,
39
+ ROUTE_RUNTIME_ID_POLICY,
40
+ ROUTE_ID_COMPATIBILITY_POLICY,
41
+ ROUTE_ID_POLICY,
42
+ ROUTE_PLAN_POLICY
43
+ };
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ const S3_CONTRACT_ID = 'pulse.s3';
4
+ const S3_PACKAGE = '@pulse-compute/s3';
5
+ const S3_LOWERING_PLAN_VERSION = 'pulse.s3-lowering-plan.v1';
6
+ const S3_LIMITS = Object.freeze({ keyBytes: 1024, textBytes: 32768, metadataBytes: 1024, headerBytes: 16384, envelopeBytes: 262144 });
7
+ const S3_OPERATIONS = Object.freeze(Object.fromEntries(['head', 'getText', 'putText'].map((name) => [name, Object.freeze({
8
+ kind: `s3.${name}`, capability: `s3.${name}`, result: name === 'head' ? 's3-head-result' : name === 'getText' ? 's3-get-text-result' : 's3-put-text-result'
9
+ })])));
10
+ const S3_PROVIDER_REQUIREMENTS = Object.freeze(['s3.head', 's3.getText', 's3.putText', 'secret.get', 'time.wall-clock']);
11
+ const S3_CRYPTO_ALGORITHMS = Object.freeze(['SHA-256', 'HMAC-SHA256']);
12
+ module.exports = Object.freeze({ S3_CONTRACT_ID, S3_PACKAGE, S3_LOWERING_PLAN_VERSION, S3_LIMITS, S3_OPERATIONS, S3_PROVIDER_REQUIREMENTS, S3_CRYPTO_ALGORITHMS });
@@ -0,0 +1,63 @@
1
+ 'use strict';
2
+
3
+ const SCHEMA_JSON_COMPILE_VERSION = 'pulsewasm.schema-json-compile.v1';
4
+ const SCHEMA_JSON_COMPILE_SMOKE_VERSION = 'pulsewasm.schema-json-compile-smoke.v1';
5
+ const SCHEMA_JSON_COMPILE_PHASE = '13';
6
+ const PHASE = SCHEMA_JSON_COMPILE_PHASE;
7
+
8
+ const MARKER_TYPES = new Map([
9
+ ['string', 'string'],
10
+ ['bool', 'bool'],
11
+ ['boolean', 'bool'],
12
+ ['i32', 'i32'],
13
+ ['u32', 'u32'],
14
+ ['Int32', 'i32'],
15
+ ['Uint32', 'u32'],
16
+ ['f64', 'f64'],
17
+ ['number', 'f64']
18
+ ]);
19
+ const JSON_MARKER_MODULES = new Set([
20
+ '@pulse-compute/pulse/schema',
21
+ '@pulsewasm/json-types',
22
+ 'pulsewasm/json-types'
23
+ ]);
24
+
25
+ function plainObject(value) {
26
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
27
+ }
28
+
29
+ function jsonConfig(config) {
30
+ const runtime = plainObject(config && config.runtime);
31
+ const payload = plainObject(runtime.payload);
32
+ return plainObject(payload.json || (config && config.json));
33
+ }
34
+
35
+ function normalizeFieldType(type) {
36
+ const raw = String(type || '');
37
+ return MARKER_TYPES.get(raw) || raw;
38
+ }
39
+
40
+ function isJsonMarkerModule(moduleName) {
41
+ return JSON_MARKER_MODULES.has(String(moduleName || ''));
42
+ }
43
+
44
+ function compileNeeded(config) {
45
+ const json = jsonConfig(config);
46
+ const schemas = Array.isArray(json.schemas) ? json.schemas : [];
47
+ const target = json.target || 'generic';
48
+ const effectiveTarget = target === 'auto' && schemas.length > 0 ? 'schema' : target;
49
+ if (effectiveTarget !== 'schema') return false;
50
+ return schemas.some((schema) => Object.keys(plainObject(schema.fields)).length === 0 && typeof schema.source === 'string' && schema.source && typeof schema.type === 'string' && schema.type);
51
+ }
52
+
53
+ module.exports = {
54
+ SCHEMA_JSON_COMPILE_VERSION,
55
+ SCHEMA_JSON_COMPILE_SMOKE_VERSION,
56
+ SCHEMA_JSON_COMPILE_PHASE,
57
+ PHASE,
58
+ MARKER_TYPES,
59
+ JSON_MARKER_MODULES,
60
+ normalizeFieldType,
61
+ isJsonMarkerModule,
62
+ compileNeeded
63
+ };
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ v1: require('./v1.js'),
5
+ v2: require('./v2.js'),
6
+ compile: require('./compile.js'),
7
+ genericParser: require('./generic-parser.js'),
8
+ registry: require('./registry.js'),
9
+ semanticTrace: require('./semantic-trace.js')
10
+ };
@@ -0,0 +1,62 @@
1
+ 'use strict';
2
+
3
+ const SCHEMA_JSON_GENERIC_PARSER_VERSION = 'pulsewasm.schema-json-generic-parser.v1';
4
+ const SCHEMA_JSON_GENERIC_PARSER_PLAN_VERSION = 'pulsewasm.schema-json-generic-parser-plan.v1';
5
+ const SCHEMA_JSON_GENERIC_PARSER_ABI_VERSION = 'pulsewasm.schema-json-generic-parser-abi.v1';
6
+ const SCHEMA_JSON_GENERIC_PARSER_SMOKE_VERSION = 'pulsewasm.schema-json-generic-parser-smoke.v1';
7
+ const SCHEMA_JSON_GENERIC_PARSER_PHASE = '34';
8
+ const PHASE = SCHEMA_JSON_GENERIC_PARSER_PHASE;
9
+
10
+ const SCHEMA_JSON_GENERIC_PARSER_ARTIFACT = 'schema-json-generic-parser.json';
11
+ const SCHEMA_JSON_GENERIC_PARSER_PLAN_ARTIFACT = 'schema-json-generic-parser-plan.json';
12
+ const SCHEMA_JSON_GENERIC_PARSER_ABI_ARTIFACT = 'schema-json-generic-parser-abi.json';
13
+ const SCHEMA_JSON_GENERIC_PARSER_SMOKE_ARTIFACT = 'schema-json-generic-parser-smoke.json';
14
+ const SCHEMA_JSON_GENERIC_PARSER_NAME = 'as-json';
15
+ const SCHEMA_JSON_GENERIC_PARSER_ID = SCHEMA_JSON_GENERIC_PARSER_NAME;
16
+ const SCHEMA_JSON_GENERIC_PARSER_MODE = 'generic-as-json-parser';
17
+ const SCHEMA_JSON_GENERIC_PARSER_FILE = 'generated/as/schema-json/generic/as-json-parser.as.ts';
18
+ const SCHEMA_JSON_GENERIC_PARSER_MODULE_FILE = SCHEMA_JSON_GENERIC_PARSER_FILE;
19
+ const SCHEMA_JSON_GENERIC_PARSER_SMOKE_RUNNER_FILE = 'generated/as-smoke/schema-json-generic-parser-smoke-runner.as.ts';
20
+ const SCHEMA_JSON_GENERIC_PARSER_WASM_FILE = 'generated/schema-json-generic-parser/pulsewasm-schema-json-generic-parser.wasm';
21
+ const SCHEMA_JSON_GENERIC_PARSER_WAT_FILE = 'generated/schema-json-generic-parser/pulsewasm-schema-json-generic-parser.wat';
22
+ const SCHEMA_JSON_GENERIC_PARSER_WASM = SCHEMA_JSON_GENERIC_PARSER_WASM_FILE;
23
+ const SCHEMA_JSON_GENERIC_PARSER_WAT = SCHEMA_JSON_GENERIC_PARSER_WAT_FILE;
24
+ const SCHEMA_JSON_GENERIC_PARSER_DOC_FILE = 'generated/host/schema-json-generic-parser.md';
25
+
26
+ const SCHEMA_JSON_TARGETS = Object.freeze(['generic', 'schema', 'auto', 'parser', 'none']);
27
+ const SCHEMA_JSON_GENERIC_PARSER_TARGETS = Object.freeze(['auto', 'parser']);
28
+ const SCHEMA_JSON_PARSER_MODES = Object.freeze([SCHEMA_JSON_GENERIC_PARSER_NAME]);
29
+ const SCHEMA_JSON_GENERIC_VALUE_KINDS = Object.freeze({ null: 0, bool: 1, number: 2, string: 3, array: 4, object: 5, error: -1 });
30
+ const SCHEMA_JSON_GENERIC_PARSER_ACCESSORS = Object.freeze(['parse', 'ok', 'value', 'error', 'errorCode', 'errorMessage', 'errorStatusCode', 'kind', 'isNull', 'asBool', 'asF64', 'asString', 'objectGet', 'objectHas', 'has', 'get', 'getString', 'getI32', 'getU32', 'getF64', 'getBool', 'arrayLen', 'arrayGet', 'length', 'at', 'text']);
31
+ const SCHEMA_JSON_GENERIC_ACCESSOR_SURFACE = SCHEMA_JSON_GENERIC_PARSER_ACCESSORS;
32
+ const SCHEMA_JSON_GENERIC_RESERVED_SURFACE = Object.freeze(['automatic typed schema inference from generic refs', 'arbitrary JavaScript object materialization', 'host JSON.parse fallback', 'binary request body parsing', 'streaming request body parsing', 'mutation APIs for parsed generic refs']);
33
+ const SCHEMA_JSON_GENERIC_PARSER_POLICY = Object.freeze({ phase: SCHEMA_JSON_GENERIC_PARSER_PHASE, parser: SCHEMA_JSON_GENERIC_PARSER_NAME, explicitOptIn: true, schemaDeclarationsPreferred: true, schemaAutoTakesPrecedence: true, directJsObjects: false, hostJsonParse: false, assemblyScriptParser: true, fullJsonTree: true, valueAccess: 'opaque-ref-accessors', bodySource: 'seeded-body-text-only', supportedTargets: SCHEMA_JSON_GENERIC_PARSER_TARGETS, supportedValueKinds: Object.keys(SCHEMA_JSON_GENERIC_VALUE_KINDS).filter((kind) => kind !== 'error'), reservedSurface: SCHEMA_JSON_GENERIC_RESERVED_SURFACE });
34
+ const SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS = Object.freeze({ parserRequired: 'PULSEWASM_SCHEMA_JSON_GENERIC_PARSER_REQUIRED', genericParserRequired: 'PULSEWASM_SCHEMA_JSON_GENERIC_PARSER_REQUIRED', unsupportedParser: 'PULSEWASM_SCHEMA_JSON_GENERIC_PARSER_UNSUPPORTED', genericParserUnsupported: 'PULSEWASM_SCHEMA_JSON_GENERIC_PARSER_UNSUPPORTED', genericParserInactive: 'PULSEWASM_SCHEMA_JSON_GENERIC_PARSER_INACTIVE', schemasTakePrecedence: 'PULSEWASM_SCHEMA_JSON_SCHEMAS_TAKE_PRECEDENCE', schemasIgnored: 'PULSEWASM_SCHEMA_JSON_GENERIC_SCHEMAS_IGNORED', schemaTargetRequiresSchemas: 'PULSEWASM_SCHEMA_JSON_SCHEMAS_REQUIRED', compileFailed: 'PULSEWASM_SCHEMA_JSON_GENERIC_PARSER_COMPILE_FAILED', genericParserCompileFailed: 'PULSEWASM_SCHEMA_JSON_GENERIC_PARSER_COMPILE_FAILED', smokeFailed: 'PULSEWASM_SCHEMA_JSON_GENERIC_PARSER_SMOKE_FAILED', genericParserSmokeFailed: 'PULSEWASM_SCHEMA_JSON_GENERIC_PARSER_SMOKE_FAILED', ascMissing: 'PULSEWASM_SCHEMA_JSON_GENERIC_PARSER_ASC_MISSING' });
35
+
36
+ function plainObject(value) { return value && typeof value === 'object' && !Array.isArray(value) ? value : {}; }
37
+ function configArtifact(config) { return config && typeof config === 'object' && config.artifact ? config.artifact : config; }
38
+ function jsonConfig(config) { const root = plainObject(configArtifact(config)); const runtime = plainObject(root.runtime); const payload = plainObject(runtime.payload); return plainObject(payload.json || root.json); }
39
+ function schemas(config) { const json = jsonConfig(config); return Array.isArray(json.schemas) ? json.schemas : []; }
40
+ function normalizeSchemaJsonTarget(value) { if (typeof value !== 'string' || !value) return 'generic'; if (value === 'fallback') return 'auto'; if (value === 'generic-parser') return 'parser'; return value; }
41
+ function normalizeJsonTarget(config) { return normalizeSchemaJsonTarget(jsonConfig(config).target); }
42
+ function normalizeParserMode(value) { if (typeof value === 'string' && value) return value; if (value && typeof value === 'object' && !Array.isArray(value)) return String(value.id || value.mode || value.kind || value.name || ''); return ''; }
43
+ function normalizeSchemaJsonParser(value) { const normalized = String(normalizeParserMode(value) || '').toLowerCase(); if (!normalized) return undefined; if (normalized === 'assemblyscript-json' || normalized === 'full-as-json' || normalized === 'asjson' || normalized === 'generic-as-json') return SCHEMA_JSON_GENERIC_PARSER_ID; return normalized; }
44
+ function normalizedParserId(config) { return normalizeSchemaJsonParser(jsonConfig(config).parser); }
45
+ function normalizeJsonParserConfig(config = {}) {
46
+ const target = normalizeJsonTarget(config); const parser = normalizedParserId(config); const schemaList = schemas(config); const diagnostics = []; const warnings = []; let effectiveTarget = target; let genericParser = false; let schemaSidecar = false; let mode = 'inactive';
47
+ if (!SCHEMA_JSON_TARGETS.includes(target)) { diagnostics.push('PULSEWASM_SCHEMA_JSON_GENERIC_TARGET_UNSUPPORTED'); mode = 'error'; }
48
+ else if (target === 'schema') { effectiveTarget = 'schema'; schemaSidecar = true; mode = 'schema'; if (schemaList.length === 0) diagnostics.push(SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.schemaTargetRequiresSchemas); }
49
+ else if (target === 'auto') { if (schemaList.length > 0) { effectiveTarget = 'schema'; schemaSidecar = true; mode = 'schema'; if (parser === SCHEMA_JSON_GENERIC_PARSER_ID) warnings.push(SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.schemasTakePrecedence); } else if (parser === SCHEMA_JSON_GENERIC_PARSER_ID) { effectiveTarget = 'parser'; genericParser = true; mode = SCHEMA_JSON_GENERIC_PARSER_MODE; } else if (parser) { effectiveTarget = 'parser'; diagnostics.push(SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.unsupportedParser); mode = 'error'; } else { effectiveTarget = 'parser'; diagnostics.push(SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.parserRequired); mode = 'error'; } }
50
+ else if (target === 'parser') { effectiveTarget = 'parser'; const selectedParser = parser || SCHEMA_JSON_GENERIC_PARSER_ID; if (selectedParser === SCHEMA_JSON_GENERIC_PARSER_ID) { genericParser = true; mode = SCHEMA_JSON_GENERIC_PARSER_MODE; if (schemaList.length > 0) warnings.push(SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.schemasIgnored); } else { diagnostics.push(SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.unsupportedParser); mode = 'error'; } }
51
+ return { target, effectiveTarget, parser: target === 'parser' && !parser ? SCHEMA_JSON_GENERIC_PARSER_ID : parser, schemaCount: schemaList.length, schemasPresent: schemaList.length > 0, genericParser, schemaSidecar, mode, diagnostics, warnings, parserUnsupportedDiagnostic: diagnostics.includes(SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.unsupportedParser), parserRequiredDiagnostic: diagnostics.includes(SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.parserRequired), schemaSidecarRequested: schemaSidecar, genericParserRequested: genericParser };
52
+ }
53
+ function normalizeGenericParserConfig(config = {}) { const json = jsonConfig(config); const mode = normalizeJsonParserConfig(config); const schemaList = schemas(config); return { ...mode, schemas: schemaList, schemasSupplied: schemaList.length > 0, contentTypePolicy: typeof json.contentTypePolicy === 'string' && json.contentTypePolicy ? json.contentTypePolicy : 'accept-json-or-missing', defaultNamespace: typeof json.defaultNamespace === 'string' && json.defaultNamespace ? json.defaultNamespace : 'app', maxBytes: Number.isFinite(Number(json.maxBytes)) ? Number(json.maxBytes) : 1048576 }; }
54
+ function normalizeSchemaJsonParserMode(config = {}) { return normalizeJsonParserConfig(config); }
55
+ function normalizeSchemaJsonParserSelection(config = {}) { const mode = normalizeJsonParserConfig(config); return { target: mode.target, effectiveTarget: mode.effectiveTarget, effectiveParser: mode.parser, parserRequested: mode.genericParser, explicitParser: Boolean(mode.parser), schemasSupplied: mode.schemasPresent, schemaCount: mode.schemaCount, schemaSidecarsPreferred: mode.schemaSidecar, mode: mode.mode, diagnostics: mode.diagnostics, warnings: mode.warnings }; }
56
+ function genericParserNeeded(config = {}) { const selection = normalizeJsonParserConfig(config); if (selection.genericParser) return true; if ((selection.target === 'auto' || selection.target === 'parser') && !selection.schemaSidecar && selection.diagnostics.length > 0) return true; return false; }
57
+ function shouldUseGenericSchemaJsonParser(config = {}) { return genericParserNeeded(config); }
58
+ function shouldUseSchemaJsonGenericParser(config = {}, options = {}) { return Boolean(options && options.forceGenericParser) || genericParserNeeded(config); }
59
+ function shouldUseSchemaJsonSchemaMode(config = {}) { return normalizeJsonParserConfig(config).schemaSidecar; }
60
+ function defaultSchemaJsonGenericParserPolicy() { return { ...SCHEMA_JSON_GENERIC_PARSER_POLICY, accessorSurface: SCHEMA_JSON_GENERIC_PARSER_ACCESSORS.slice(), diagnostics: { ...SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS } }; }
61
+
62
+ module.exports = { SCHEMA_JSON_GENERIC_PARSER_VERSION, SCHEMA_JSON_GENERIC_PARSER_PLAN_VERSION, SCHEMA_JSON_GENERIC_PARSER_ABI_VERSION, SCHEMA_JSON_GENERIC_PARSER_SMOKE_VERSION, SCHEMA_JSON_GENERIC_PARSER_PHASE, PHASE, SCHEMA_JSON_GENERIC_PARSER_ARTIFACT, SCHEMA_JSON_GENERIC_PARSER_PLAN_ARTIFACT, SCHEMA_JSON_GENERIC_PARSER_ABI_ARTIFACT, SCHEMA_JSON_GENERIC_PARSER_SMOKE_ARTIFACT, SCHEMA_JSON_GENERIC_PARSER_NAME, SCHEMA_JSON_GENERIC_PARSER_ID, SCHEMA_JSON_GENERIC_PARSER_MODE, SCHEMA_JSON_GENERIC_PARSER_FILE, SCHEMA_JSON_GENERIC_PARSER_MODULE_FILE, SCHEMA_JSON_GENERIC_PARSER_SMOKE_RUNNER_FILE, SCHEMA_JSON_GENERIC_PARSER_WASM_FILE, SCHEMA_JSON_GENERIC_PARSER_WAT_FILE, SCHEMA_JSON_GENERIC_PARSER_WASM, SCHEMA_JSON_GENERIC_PARSER_WAT, SCHEMA_JSON_GENERIC_PARSER_DOC_FILE, SCHEMA_JSON_TARGETS, SCHEMA_JSON_GENERIC_PARSER_TARGETS, SCHEMA_JSON_PARSER_MODES, SCHEMA_JSON_GENERIC_VALUE_KINDS, SCHEMA_JSON_GENERIC_PARSER_ACCESSORS, SCHEMA_JSON_GENERIC_ACCESSOR_SURFACE, SCHEMA_JSON_GENERIC_RESERVED_SURFACE, SCHEMA_JSON_GENERIC_PARSER_POLICY, SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS, configArtifact, jsonConfig, schemas, normalizeSchemaJsonTarget, normalizeJsonTarget, normalizeParserMode, normalizeSchemaJsonParser, normalizedParserId, normalizeJsonParserConfig, normalizeGenericParserConfig, normalizeSchemaJsonParserMode, normalizeSchemaJsonParserSelection, genericParserNeeded, shouldUseGenericSchemaJsonParser, shouldUseSchemaJsonGenericParser, shouldUseSchemaJsonSchemaMode, defaultSchemaJsonGenericParserPolicy };
@@ -0,0 +1,359 @@
1
+ 'use strict';
2
+
3
+ const { sha256Hex, stableStringify } = require('../stable-id.js');
4
+
5
+ const SCHEMA_REGISTRY_IR_VERSION = 'pulse.schema-registry-ir.v1';
6
+ const SCHEMA_CODEC_INPUTS_VERSION = 'pulse.schema-codec-inputs.v1';
7
+ const SCHEMA_AUTHORING_VERSION = 'pulse.schema-authoring.v1';
8
+ const SCHEMA_ID_PATTERN = /^[A-Za-z][A-Za-z0-9_-]*(?:\.[A-Za-z][A-Za-z0-9_-]*)+$/;
9
+ const RESPONSE_CASE_ID_PATTERN = SCHEMA_ID_PATTERN;
10
+ const SCALAR_KINDS = Object.freeze(['string', 'boolean', 'i32', 'u32', 'f64']);
11
+ const NODE_KINDS = Object.freeze([...SCALAR_KINDS, 'object', 'array', 'string-enum', 'nullable']);
12
+
13
+ function schemaContractError(code, message, details = {}) {
14
+ const error = new TypeError(message);
15
+ error.code = code;
16
+ error.details = Object.freeze({ ...details });
17
+ return error;
18
+ }
19
+
20
+ function plainObject(value) {
21
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
22
+ const proto = Object.getPrototypeOf(value);
23
+ return proto === Object.prototype || proto === null;
24
+ }
25
+
26
+ function deepFreeze(value) {
27
+ if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value;
28
+ for (const child of Object.values(value)) deepFreeze(child);
29
+ return Object.freeze(value);
30
+ }
31
+
32
+ function requiredString(value, field) {
33
+ if (typeof value !== 'string' || value.trim() === '') {
34
+ throw schemaContractError('PULSE_SCHEMA_IR_STRING_REQUIRED', `${field} must be a non-empty string.`, { field, value });
35
+ }
36
+ return value.trim();
37
+ }
38
+
39
+ function normalizeSource(input, field) {
40
+ if (!plainObject(input)) {
41
+ throw schemaContractError('PULSE_SCHEMA_IR_SOURCE_REQUIRED', `${field} must identify a source location.`, { field });
42
+ }
43
+ const line = Number(input.line);
44
+ const column = Number(input.column);
45
+ if (!Number.isSafeInteger(line) || line < 1 || !Number.isSafeInteger(column) || column < 1) {
46
+ throw schemaContractError('PULSE_SCHEMA_IR_SOURCE_INVALID', `${field} requires positive line and column values.`, {
47
+ field,
48
+ line,
49
+ column
50
+ });
51
+ }
52
+ return Object.freeze({
53
+ file: requiredString(input.file, `${field}.file`).replace(/\\/g, '/'),
54
+ line,
55
+ column
56
+ });
57
+ }
58
+
59
+ function normalizeId(value, field, pattern, code) {
60
+ const id = requiredString(value, field);
61
+ if (!pattern.test(id)) {
62
+ throw schemaContractError(code, `${field} must be a stable dotted identifier.`, { field, id });
63
+ }
64
+ return id;
65
+ }
66
+
67
+ function normalizeSchemaNode(input, field = 'schema.root', stack = []) {
68
+ if (!plainObject(input)) {
69
+ throw schemaContractError('PULSE_SCHEMA_IR_NODE_REQUIRED', `${field} must be a normalized schema node.`, { field });
70
+ }
71
+ const kind = requiredString(input.kind, `${field}.kind`);
72
+ if (!NODE_KINDS.includes(kind)) {
73
+ throw schemaContractError('PULSE_SCHEMA_IR_NODE_KIND_UNSUPPORTED', `${field}.kind is unsupported: ${kind}.`, { field, kind });
74
+ }
75
+ if (SCALAR_KINDS.includes(kind)) return Object.freeze({ kind });
76
+ if (kind === 'array') {
77
+ return Object.freeze({ kind, element: normalizeSchemaNode(input.element, `${field}.element`, stack) });
78
+ }
79
+ if (kind === 'nullable') {
80
+ const value = normalizeSchemaNode(input.value, `${field}.value`, stack);
81
+ if (value.kind === 'nullable') {
82
+ throw schemaContractError('PULSE_SCHEMA_IR_REDUNDANT_NULLABLE', `${field} cannot contain another nullable node.`, { field });
83
+ }
84
+ return Object.freeze({ kind, value });
85
+ }
86
+ if (kind === 'string-enum') {
87
+ if (!Array.isArray(input.values) || input.values.length === 0) {
88
+ throw schemaContractError('PULSE_SCHEMA_IR_ENUM_VALUES_REQUIRED', `${field}.values must contain at least one string literal.`, { field });
89
+ }
90
+ const values = input.values.map((value, index) => requiredString(value, `${field}.values[${index}]`));
91
+ if (new Set(values).size !== values.length) {
92
+ throw schemaContractError('PULSE_SCHEMA_IR_ENUM_VALUE_DUPLICATE', `${field}.values must be unique.`, { field, values });
93
+ }
94
+ return Object.freeze({ kind, values: Object.freeze(values) });
95
+ }
96
+ if (!Array.isArray(input.fields)) {
97
+ throw schemaContractError('PULSE_SCHEMA_IR_OBJECT_FIELDS_REQUIRED', `${field}.fields must be an array.`, { field });
98
+ }
99
+ const names = new Set();
100
+ const fields = input.fields.map((entry, index) => {
101
+ if (!plainObject(entry)) {
102
+ throw schemaContractError('PULSE_SCHEMA_IR_FIELD_REQUIRED', `${field}.fields[${index}] must be an object.`, { field, index });
103
+ }
104
+ const name = requiredString(entry.name, `${field}.fields[${index}].name`);
105
+ if (names.has(name)) {
106
+ throw schemaContractError('PULSE_SCHEMA_IR_FIELD_DUPLICATE', `${field} declares field ${name} more than once.`, { field, name });
107
+ }
108
+ names.add(name);
109
+ if (entry.required !== true) {
110
+ throw schemaContractError('PULSE_SCHEMA_OPTIONAL_FIELD_RESERVED', `${field}.${name} must be required in schema IR v1.`, {
111
+ field,
112
+ name
113
+ });
114
+ }
115
+ return Object.freeze({
116
+ name,
117
+ required: true,
118
+ value: normalizeSchemaNode(entry.value, `${field}.fields[${index}].value`, [...stack, name]),
119
+ source: entry.source ? normalizeSource(entry.source, `${field}.fields[${index}].source`) : undefined
120
+ });
121
+ });
122
+ return Object.freeze({ kind, fields: Object.freeze(fields) });
123
+ }
124
+
125
+ function normalizeSchema(input, index) {
126
+ if (!plainObject(input)) {
127
+ throw schemaContractError('PULSE_SCHEMA_IR_SCHEMA_REQUIRED', `schemas[${index}] must be an object.`, { index });
128
+ }
129
+ const id = normalizeId(input.id, `schemas[${index}].id`, SCHEMA_ID_PATTERN, 'PULSE_SCHEMA_ID_INVALID');
130
+ const root = normalizeSchemaNode(input.root, `schemas[${index}].root`);
131
+ if (root.kind !== 'object') {
132
+ throw schemaContractError('PULSE_SCHEMA_ROOT_OBJECT_REQUIRED', `Schema ${id} must have an object root.`, { id, kind: root.kind });
133
+ }
134
+ return Object.freeze({
135
+ id,
136
+ typeName: requiredString(input.typeName, `schemas[${index}].typeName`),
137
+ root,
138
+ source: normalizeSource(input.source, `schemas[${index}].source`)
139
+ });
140
+ }
141
+
142
+ function normalizeResponseCase(input, index) {
143
+ if (!plainObject(input)) {
144
+ throw schemaContractError('PULSE_RESPONSE_CASE_REQUIRED', `responses[${index}] must be an object.`, { index });
145
+ }
146
+ const status = Number(input.status);
147
+ if (!Number.isSafeInteger(status) || status < 100 || status > 599) {
148
+ throw schemaContractError('PULSE_RESPONSE_STATUS_INVALID', `responses[${index}].status must be an integer from 100 through 599.`, {
149
+ index,
150
+ status
151
+ });
152
+ }
153
+ return Object.freeze({
154
+ id: normalizeId(input.id, `responses[${index}].id`, RESPONSE_CASE_ID_PATTERN, 'PULSE_RESPONSE_CASE_ID_INVALID'),
155
+ status,
156
+ schemaId: normalizeId(input.schemaId, `responses[${index}].schemaId`, SCHEMA_ID_PATTERN, 'PULSE_SCHEMA_ID_INVALID'),
157
+ source: normalizeSource(input.source, `responses[${index}].source`)
158
+ });
159
+ }
160
+
161
+ function assertUnique(entries, field, code) {
162
+ const seen = new Set();
163
+ for (const entry of entries) {
164
+ if (seen.has(entry.id)) {
165
+ throw schemaContractError(code, `${field} contains duplicate ID ${entry.id}.`, { field, id: entry.id });
166
+ }
167
+ seen.add(entry.id);
168
+ }
169
+ }
170
+
171
+ function normalizeSchemaRegistry(input) {
172
+ if (!plainObject(input)) {
173
+ throw schemaContractError('PULSE_SCHEMA_REGISTRY_REQUIRED', 'Schema registry IR must be an object.');
174
+ }
175
+ const schemas = Object.freeze((input.schemas || []).map(normalizeSchema));
176
+ const responses = Object.freeze((input.responses || []).map(normalizeResponseCase));
177
+ assertUnique(schemas, 'schemas', 'PULSE_SCHEMA_ID_DUPLICATE');
178
+ assertUnique(responses, 'responses', 'PULSE_RESPONSE_CASE_ID_DUPLICATE');
179
+ const schemaIds = new Set(schemas.map((entry) => entry.id));
180
+ for (const entry of responses) {
181
+ if (!schemaIds.has(entry.schemaId)) {
182
+ throw schemaContractError(
183
+ 'PULSE_RESPONSE_SCHEMA_UNKNOWN',
184
+ `Response case ${entry.id} references unknown schema ${entry.schemaId}.`,
185
+ { responseCaseId: entry.id, schemaId: entry.schemaId }
186
+ );
187
+ }
188
+ }
189
+ const semantic = {
190
+ version: SCHEMA_REGISTRY_IR_VERSION,
191
+ source: normalizeSource(input.source, 'source'),
192
+ schemas,
193
+ responses,
194
+ policies: deepFreeze({
195
+ objectRoots: true,
196
+ requiredFieldsOnly: true,
197
+ unknownInputFields: 'drop',
198
+ outputFields: 'declared-only',
199
+ outputFieldOrder: 'declaration',
200
+ numericPolicy: 'finite-rfc-json',
201
+ parity: 'semantic',
202
+ dynamicIds: false,
203
+ schemaTrialFallback: false,
204
+ publicDecorators: false
205
+ })
206
+ };
207
+ return deepFreeze({
208
+ ...semantic,
209
+ registryHash: sha256Hex(stableStringify(semantic))
210
+ });
211
+ }
212
+
213
+ function assemblyScriptType(node, symbols, pathParts) {
214
+ switch (node.kind) {
215
+ case 'string': return 'string';
216
+ case 'boolean': return 'bool';
217
+ case 'i32': return 'i32';
218
+ case 'u32': return 'u32';
219
+ case 'f64': return 'f64';
220
+ case 'string-enum': return 'string';
221
+ case 'array': return `Array<${assemblyScriptType(node.element, symbols, [...pathParts, 'item'])}>`;
222
+ case 'nullable': return `${assemblyScriptType(node.value, symbols, [...pathParts, 'value'])} | null`;
223
+ case 'object': {
224
+ const key = pathParts.join('.');
225
+ return symbols.get(key);
226
+ }
227
+ default: throw schemaContractError('PULSE_SCHEMA_IR_NODE_KIND_UNSUPPORTED', `Unsupported schema node kind ${node.kind}.`);
228
+ }
229
+ }
230
+
231
+ function identifier(value) {
232
+ const normalized = String(value).normalize('NFKC').replace(/[^A-Za-z0-9_$]+/g, '_');
233
+ return /^[A-Za-z_$]/.test(normalized) ? normalized : `_${normalized}`;
234
+ }
235
+
236
+ function collectNativeClasses(schema, node, symbols, classes, pathParts = []) {
237
+ if (node.kind === 'nullable') return collectNativeClasses(schema, node.value, symbols, classes, [...pathParts, 'value']);
238
+ if (node.kind === 'array') return collectNativeClasses(schema, node.element, symbols, classes, [...pathParts, 'item']);
239
+ if (node.kind !== 'object') return;
240
+ const key = pathParts.join('.');
241
+ const symbol = `__Pulse_${identifier(schema.id)}${pathParts.length === 0 ? '' : `_${pathParts.map(identifier).join('_')}`}`;
242
+ symbols.set(key, symbol);
243
+ for (const field of node.fields) collectNativeClasses(schema, field.value, symbols, classes, [...pathParts, field.name]);
244
+ classes.push(Object.freeze({
245
+ symbol,
246
+ schemaId: schema.id,
247
+ path: pathParts.length === 0 ? '' : `/${pathParts.map((part) => String(part).replace(/~/g, '~0').replace(/\//g, '~1')).join('/')}`,
248
+ fields: Object.freeze(node.fields.map((field) => Object.freeze({
249
+ name: field.name,
250
+ type: assemblyScriptType(field.value, symbols, [...pathParts, field.name]),
251
+ required: true,
252
+ constraints: field.value.kind === 'string-enum'
253
+ ? Object.freeze({ enum: field.value.values })
254
+ : Object.freeze({})
255
+ })))
256
+ }));
257
+ }
258
+
259
+ function codecInputsForRegistry(registryInput) {
260
+ const registry = normalizeSchemaRegistry(registryInput);
261
+ const nativeSchemas = registry.schemas.map((schema) => {
262
+ const schemaClasses = [];
263
+ const symbols = new Map();
264
+ collectNativeClasses(schema, schema.root, symbols, schemaClasses);
265
+ return Object.freeze({
266
+ id: schema.id,
267
+ rootClass: symbols.get(''),
268
+ classes: Object.freeze(schemaClasses),
269
+ parse: `JSON.parse<${symbols.get('')}>`,
270
+ stringify: `JSON.stringify<${symbols.get('')}>`
271
+ });
272
+ });
273
+ return deepFreeze({
274
+ version: SCHEMA_CODEC_INPUTS_VERSION,
275
+ registryVersion: registry.version,
276
+ registryHash: registry.registryHash,
277
+ javascript: {
278
+ backend: 'pulse-generated',
279
+ operation: 'direct-parse-or-serialize',
280
+ schemas: registry.schemas,
281
+ policies: registry.policies
282
+ },
283
+ native: {
284
+ backend: 'json-as',
285
+ package: 'json-as',
286
+ packageVersion: '1.5.0',
287
+ transform: 'json-as',
288
+ assemblyScriptVersion: '0.28.18',
289
+ generatedOnly: true,
290
+ publicDecorators: false,
291
+ schemas: Object.freeze(nativeSchemas)
292
+ },
293
+ responses: registry.responses
294
+ });
295
+ }
296
+
297
+ function defaultSchemaBoundaryPolicy() {
298
+ return deepFreeze({
299
+ strict: {
300
+ missingSchemaId: 'diagnostic',
301
+ knownSchemaId: 'schema-codec',
302
+ unknownSchemaId: 'diagnostic'
303
+ },
304
+ nonStrict: {
305
+ missingSchemaId: 'generic-json',
306
+ knownSchemaId: 'schema-codec',
307
+ unknownSchemaId: 'diagnostic'
308
+ },
309
+ suppliedIds: 'string-literal-only',
310
+ genericJsonFallbackForUnknownId: false,
311
+ schemaTrialFallback: false,
312
+ jsonBodyOwnership: 'pulse-semantic-encoding',
313
+ bodyOwnership: 'caller-exact-payload',
314
+ bodyAndJsonMutuallyExclusive: true
315
+ });
316
+ }
317
+
318
+ function defaultSchemaRegistryContract() {
319
+ return deepFreeze({
320
+ authoringVersion: SCHEMA_AUTHORING_VERSION,
321
+ registryVersion: SCHEMA_REGISTRY_IR_VERSION,
322
+ codecInputsVersion: SCHEMA_CODEC_INPUTS_VERSION,
323
+ schemaIdPattern: SCHEMA_ID_PATTERN.source,
324
+ responseCaseIdPattern: RESPONSE_CASE_ID_PATTERN.source,
325
+ scalarKinds: SCALAR_KINDS,
326
+ nodeKinds: NODE_KINDS,
327
+ helpers: ['defineSchemaRegistry', 'schema', 'response'],
328
+ markerTypes: ['Int32', 'Uint32'],
329
+ policies: {
330
+ canonicalEntrypoint: 'pulse.schema',
331
+ defaultExportRequired: true,
332
+ staticObjectLiteralsOnly: true,
333
+ stableStringIds: true,
334
+ exactLiteralIdsAtBoundaries: true,
335
+ oldSchemasJsonSupported: false,
336
+ optionalPropertiesSupported: false,
337
+ recursiveSchemasSupported: false,
338
+ publicJsonAsImportsSupported: false,
339
+ automaticFallback: false
340
+ },
341
+ boundaryPolicy: defaultSchemaBoundaryPolicy()
342
+ });
343
+ }
344
+
345
+ module.exports = Object.freeze({
346
+ SCHEMA_REGISTRY_IR_VERSION,
347
+ SCHEMA_CODEC_INPUTS_VERSION,
348
+ SCHEMA_AUTHORING_VERSION,
349
+ SCHEMA_ID_PATTERN,
350
+ RESPONSE_CASE_ID_PATTERN,
351
+ SCALAR_KINDS,
352
+ NODE_KINDS,
353
+ schemaContractError,
354
+ normalizeSchemaNode,
355
+ normalizeSchemaRegistry,
356
+ codecInputsForRegistry,
357
+ defaultSchemaBoundaryPolicy,
358
+ defaultSchemaRegistryContract
359
+ });