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

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 (96) hide show
  1. package/README.md +34 -2
  2. package/package.json +123 -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 +637 -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 +146 -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 +52 -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 +594 -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 +235 -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/schema-json/compile.js +63 -0
  88. package/src/schema-json/contracts.js +10 -0
  89. package/src/schema-json/generic-parser.js +62 -0
  90. package/src/schema-json/registry.js +359 -0
  91. package/src/schema-json/semantic-trace.js +241 -0
  92. package/src/schema-json/v1.js +65 -0
  93. package/src/schema-json/v2.js +110 -0
  94. package/src/sha256.js +104 -0
  95. package/src/spine.js +381 -0
  96. package/src/stable-id.js +142 -0
@@ -0,0 +1,219 @@
1
+ 'use strict';
2
+
3
+ const { prefixedStableId, stableStringify, sha256Hex } = require('../stable-id.js');
4
+ const {
5
+ normalizePortablePath,
6
+ normalizeModuleSpecifier
7
+ } = require('./reachable-graph-resolver.js');
8
+
9
+ const REACHABLE_GRAPH_IDENTITY_VERSION = 'pulse.reachable-graph-identity.v1';
10
+ const MODULE_STABLE_INPUT_VERSION = 'pulse.reachable-module-stable-input.v1';
11
+ const EDGE_STABLE_INPUT_VERSION = 'pulse.reachable-edge-stable-input.v1';
12
+ const HANDLER_REFERENCE_V2_VERSION = 'pulse.handler-reference.v2';
13
+ const CYCLE_STABLE_INPUT_VERSION = 'pulse.reachable-cycle-stable-input.v1';
14
+ const UNSUPPORTED_BOUNDARY_STABLE_INPUT_VERSION = 'pulse.unsupported-module-boundary-stable-input.v1';
15
+
16
+ function isPlainObject(value) {
17
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
18
+ const proto = Object.getPrototypeOf(value);
19
+ return proto === Object.prototype || proto === null;
20
+ }
21
+
22
+ function assertString(value, field) {
23
+ if (typeof value !== 'string' || value.trim() === '') throw new TypeError(`${field} must be a non-empty string.`);
24
+ if (value.includes('\0')) throw new TypeError(`${field} must not contain NUL.`);
25
+ return value.trim();
26
+ }
27
+
28
+ function assertSha256(value, field) {
29
+ const text = assertString(value, field);
30
+ if (!/^[0-9a-f]{64}$/.test(text)) throw new TypeError(`${field} must be a lowercase SHA-256 hex string.`);
31
+ return text;
32
+ }
33
+
34
+ function nullableString(value, field) {
35
+ return value == null ? null : assertString(value, field);
36
+ }
37
+
38
+ function sortedStrings(input, field) {
39
+ if (input == null) return Object.freeze([]);
40
+ if (!Array.isArray(input)) throw new TypeError(`${field} must be an array.`);
41
+ return Object.freeze(Array.from(new Set(input.map((value, index) => assertString(value, `${field}[${index}]`)))).sort());
42
+ }
43
+
44
+ function normalizeSourceLocation(input, field = 'source') {
45
+ if (!isPlainObject(input)) throw new TypeError(`${field} must be an object.`);
46
+ const line = Number(input.line);
47
+ const column = Number(input.column);
48
+ if (!Number.isSafeInteger(line) || line < 1) throw new TypeError(`${field}.line must be a positive safe integer.`);
49
+ if (!Number.isSafeInteger(column) || column < 1) throw new TypeError(`${field}.column must be a positive safe integer.`);
50
+ return Object.freeze({
51
+ file: normalizePortablePath(input.file, `${field}.file`),
52
+ line,
53
+ column
54
+ });
55
+ }
56
+
57
+ function normalizePackageName(value, field = 'packageName') {
58
+ const name = assertString(value, field);
59
+ const pattern = /^(?:@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*|[a-z0-9][a-z0-9._-]*)$/i;
60
+ if (!pattern.test(name)) throw new TypeError(`${field} must be a valid package name.`);
61
+ return name;
62
+ }
63
+
64
+ function normalizePackageSubpath(value, field = 'packageSubpath') {
65
+ if (value == null || value === '' || value === '.') return '.';
66
+ const text = assertString(value, field).replace(/\\/g, '/');
67
+ const withPrefix = text.startsWith('./') ? text : `./${text}`;
68
+ const pathPart = normalizePortablePath(withPrefix.slice(2), field);
69
+ return `./${pathPart}`;
70
+ }
71
+
72
+ function moduleIdentityInput(input) {
73
+ if (!isPlainObject(input)) throw new TypeError('Module identity input must be an object.');
74
+ const kind = assertString(input.kind, 'module.kind');
75
+ if (kind === 'project') {
76
+ const modulePath = normalizePortablePath(input.path, 'module.path');
77
+ return Object.freeze({
78
+ artifact: MODULE_STABLE_INPUT_VERSION,
79
+ kind,
80
+ path: modulePath
81
+ });
82
+ }
83
+ if (kind === 'package') {
84
+ return Object.freeze({
85
+ artifact: MODULE_STABLE_INPUT_VERSION,
86
+ kind,
87
+ packageName: normalizePackageName(input.packageName),
88
+ packageVersion: assertString(input.packageVersion, 'module.packageVersion'),
89
+ packageSubpath: normalizePackageSubpath(input.packageSubpath),
90
+ packageManifestHash: assertSha256(input.packageManifestHash, 'module.packageManifestHash')
91
+ });
92
+ }
93
+ if (kind === 'generated') {
94
+ return Object.freeze({
95
+ artifact: MODULE_STABLE_INPUT_VERSION,
96
+ kind,
97
+ generator: assertString(input.generator, 'module.generator'),
98
+ logicalName: assertString(input.logicalName, 'module.logicalName')
99
+ });
100
+ }
101
+ if (kind === 'external') {
102
+ return Object.freeze({
103
+ artifact: MODULE_STABLE_INPUT_VERSION,
104
+ kind,
105
+ specifier: normalizeModuleSpecifier(input.specifier, 'module.specifier')
106
+ });
107
+ }
108
+ throw new TypeError(`Unsupported module identity kind ${kind}.`);
109
+ }
110
+
111
+ function createModuleIdentity(input) {
112
+ const stableInput = moduleIdentityInput(input);
113
+ const canonical = stableInput.kind === 'project'
114
+ ? `project:${stableInput.path}`
115
+ : stableInput.kind === 'package'
116
+ ? `package:${stableInput.packageName}@${stableInput.packageVersion}:${stableInput.packageSubpath}`
117
+ : stableInput.kind === 'generated'
118
+ ? `generated:${stableInput.generator}:${stableInput.logicalName}`
119
+ : `external:${stableInput.specifier}`;
120
+ return Object.freeze({
121
+ version: REACHABLE_GRAPH_IDENTITY_VERSION,
122
+ id: prefixedStableId(`module_${stableInput.kind}`, stableInput, { length: 32 }),
123
+ canonical,
124
+ stableInput
125
+ });
126
+ }
127
+
128
+ function createEdgeIdentity(input) {
129
+ if (!isPlainObject(input)) throw new TypeError('Edge identity input must be an object.');
130
+ const stableInput = Object.freeze({
131
+ artifact: EDGE_STABLE_INPUT_VERSION,
132
+ kind: assertString(input.kind, 'edge.kind'),
133
+ from: assertString(input.from, 'edge.from'),
134
+ to: assertString(input.to, 'edge.to'),
135
+ specifier: normalizeModuleSpecifier(input.specifier, 'edge.specifier'),
136
+ resolutionKind: assertString(input.resolutionKind, 'edge.resolutionKind'),
137
+ runtime: Boolean(input.runtime),
138
+ importedNames: sortedStrings(input.importedNames, 'edge.importedNames'),
139
+ exportedNames: sortedStrings(input.exportedNames, 'edge.exportedNames'),
140
+ packageContract: nullableString(input.packageContract, 'edge.packageContract'),
141
+ source: normalizeSourceLocation(input.source, 'edge.source')
142
+ });
143
+ return Object.freeze({
144
+ version: REACHABLE_GRAPH_IDENTITY_VERSION,
145
+ id: prefixedStableId('edge', stableInput, { length: 32 }),
146
+ stableInput
147
+ });
148
+ }
149
+
150
+ function createHandlerReferenceIdentity(input) {
151
+ if (!isPlainObject(input)) throw new TypeError('Handler reference identity input must be an object.');
152
+ const stableInput = Object.freeze({
153
+ artifact: HANDLER_REFERENCE_V2_VERSION,
154
+ moduleId: assertString(input.moduleId, 'handler.moduleId'),
155
+ exportName: nullableString(input.exportName, 'handler.exportName'),
156
+ localName: nullableString(input.localName, 'handler.localName'),
157
+ role: assertString(input.role, 'handler.role'),
158
+ wrappers: sortedStrings(input.wrappers, 'handler.wrappers'),
159
+ source: normalizeSourceLocation(input.source, 'handler.source')
160
+ });
161
+ if (stableInput.exportName === null && stableInput.localName === null) {
162
+ throw new TypeError('Handler reference requires exportName or localName.');
163
+ }
164
+ return Object.freeze({
165
+ version: REACHABLE_GRAPH_IDENTITY_VERSION,
166
+ id: prefixedStableId('handlerref', stableInput, { length: 32 }),
167
+ stableInput
168
+ });
169
+ }
170
+
171
+ function createCycleIdentity(moduleIdsInput) {
172
+ const moduleIds = sortedStrings(moduleIdsInput, 'cycle.modules');
173
+ if (moduleIds.length === 0) throw new TypeError('cycle.modules must be non-empty.');
174
+ const stableInput = Object.freeze({ artifact: CYCLE_STABLE_INPUT_VERSION, modules: moduleIds });
175
+ return Object.freeze({
176
+ version: REACHABLE_GRAPH_IDENTITY_VERSION,
177
+ id: prefixedStableId('cycle', stableInput, { length: 32 }),
178
+ stableInput
179
+ });
180
+ }
181
+
182
+ function createUnsupportedBoundaryIdentity(input) {
183
+ if (!isPlainObject(input)) throw new TypeError('Unsupported boundary identity input must be an object.');
184
+ const stableInput = Object.freeze({
185
+ artifact: UNSUPPORTED_BOUNDARY_STABLE_INPUT_VERSION,
186
+ kind: assertString(input.kind, 'boundary.kind'),
187
+ from: nullableString(input.from, 'boundary.from'),
188
+ specifier: normalizeModuleSpecifier(input.specifier, 'boundary.specifier'),
189
+ source: normalizeSourceLocation(input.source, 'boundary.source')
190
+ });
191
+ return Object.freeze({
192
+ version: REACHABLE_GRAPH_IDENTITY_VERSION,
193
+ id: prefixedStableId('boundary', stableInput, { length: 32 }),
194
+ stableInput
195
+ });
196
+ }
197
+
198
+ function graphSemanticHash(input) {
199
+ return sha256Hex(stableStringify(input));
200
+ }
201
+
202
+ module.exports = Object.freeze({
203
+ REACHABLE_GRAPH_IDENTITY_VERSION,
204
+ MODULE_STABLE_INPUT_VERSION,
205
+ EDGE_STABLE_INPUT_VERSION,
206
+ HANDLER_REFERENCE_V2_VERSION,
207
+ CYCLE_STABLE_INPUT_VERSION,
208
+ UNSUPPORTED_BOUNDARY_STABLE_INPUT_VERSION,
209
+ normalizeSourceLocation,
210
+ normalizePackageName,
211
+ normalizePackageSubpath,
212
+ moduleIdentityInput,
213
+ createModuleIdentity,
214
+ createEdgeIdentity,
215
+ createHandlerReferenceIdentity,
216
+ createCycleIdentity,
217
+ createUnsupportedBoundaryIdentity,
218
+ graphSemanticHash
219
+ });
@@ -0,0 +1,279 @@
1
+ 'use strict';
2
+
3
+ const { prefixedStableId, stableStringify, sha256Hex } = require('../stable-id.js');
4
+ const { normalizeSourceLocation, normalizePackageName, normalizePackageSubpath } = require('./reachable-graph-identity.js');
5
+
6
+ const PACKAGE_REACHABILITY_PROJECTION_VERSION = 'pulse.package-reachability-projection.v1';
7
+ const PACKAGE_BINDING_OWNERSHIP_VERSION = 'pulse.package-binding-ownership.v1';
8
+ const APPLICATION_ENTRY_SAFETY_VERSION = 'pulse.application-entry-safety.v1';
9
+ const NATIVE_ELIGIBILITY_PROJECTION_VERSION = 'pulse.native-eligibility-projection.v1';
10
+
11
+ const PACKAGE_NATIVE_STATUSES = Object.freeze(['core-authoring', 'trusted-lowerable', 'unsupported']);
12
+ const PACKAGE_BINDING_VIA = Object.freeze(['direct-import', 'direct-re-export', 'project-import', 'project-re-export']);
13
+ const ELIGIBILITY_BLOCKER_KINDS = Object.freeze([
14
+ 'unsupported-package',
15
+ 'unsupported-package-subpath',
16
+ 'package-re-export-lowering-deferred',
17
+ 'package-composition-not-realized',
18
+ 'application-entry-lifecycle-side-effect'
19
+ ]);
20
+
21
+ function isPlainObject(value) {
22
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
23
+ const proto = Object.getPrototypeOf(value);
24
+ return proto === Object.prototype || proto === null;
25
+ }
26
+
27
+ function assertKnownKeys(input, allowed, field) {
28
+ for (const key of Object.keys(input)) if (!allowed.has(key)) throw new TypeError(`${field} contains unsupported field ${key}.`);
29
+ }
30
+
31
+ function string(value, field) {
32
+ if (typeof value !== 'string' || value.trim() === '') throw new TypeError(`${field} must be a non-empty string.`);
33
+ if (value.includes('\0')) throw new TypeError(`${field} must not contain NUL.`);
34
+ return value.trim();
35
+ }
36
+
37
+ function nullableString(value, field) {
38
+ return value == null ? null : string(value, field);
39
+ }
40
+
41
+ function bool(value, field) {
42
+ if (typeof value !== 'boolean') throw new TypeError(`${field} must be a boolean.`);
43
+ return value;
44
+ }
45
+
46
+ function strings(values, field) {
47
+ if (values == null) return Object.freeze([]);
48
+ if (!Array.isArray(values)) throw new TypeError(`${field} must be an array.`);
49
+ return Object.freeze(Array.from(new Set(values.map((value, index) => string(value, `${field}[${index}]`)))).sort());
50
+ }
51
+
52
+ function sources(values, field) {
53
+ if (values == null) return Object.freeze([]);
54
+ if (!Array.isArray(values)) throw new TypeError(`${field} must be an array.`);
55
+ const normalized = values.map((value, index) => normalizeSourceLocation(value, `${field}[${index}]`));
56
+ normalized.sort((left, right) => left.file.localeCompare(right.file) || left.line - right.line || left.column - right.column);
57
+ return Object.freeze(normalized);
58
+ }
59
+
60
+ function compareSource(left, right) {
61
+ return left.source.file.localeCompare(right.source.file)
62
+ || left.source.line - right.source.line
63
+ || left.source.column - right.source.column
64
+ || String(left.id || left.edgeId || '').localeCompare(String(right.id || right.edgeId || ''));
65
+ }
66
+
67
+ function normalizePackageRecord(input, index) {
68
+ if (!isPlainObject(input)) throw new TypeError(`packages[${index}] must be an object.`);
69
+ assertKnownKeys(input, new Set([
70
+ 'moduleId', 'packageName', 'packageVersion', 'packageSubpath', 'owner', 'runtime',
71
+ 'contractId', 'lowerableSubpath', 'facadeSymbols', 'importSources', 'reExportSources', 'nativeStatus'
72
+ ]), `packages[${index}]`);
73
+ const nativeStatus = string(input.nativeStatus, `packages[${index}].nativeStatus`);
74
+ if (!PACKAGE_NATIVE_STATUSES.includes(nativeStatus)) throw new TypeError(`Unsupported package native status ${nativeStatus}.`);
75
+ return Object.freeze({
76
+ moduleId: string(input.moduleId, `packages[${index}].moduleId`),
77
+ packageName: normalizePackageName(input.packageName, `packages[${index}].packageName`),
78
+ packageVersion: string(input.packageVersion, `packages[${index}].packageVersion`),
79
+ packageSubpath: normalizePackageSubpath(input.packageSubpath, `packages[${index}].packageSubpath`),
80
+ owner: string(input.owner, `packages[${index}].owner`),
81
+ runtime: bool(input.runtime, `packages[${index}].runtime`),
82
+ contractId: nullableString(input.contractId, `packages[${index}].contractId`),
83
+ lowerableSubpath: nullableString(input.lowerableSubpath, `packages[${index}].lowerableSubpath`),
84
+ facadeSymbols: strings(input.facadeSymbols, `packages[${index}].facadeSymbols`),
85
+ importSources: sources(input.importSources, `packages[${index}].importSources`),
86
+ reExportSources: sources(input.reExportSources, `packages[${index}].reExportSources`),
87
+ nativeStatus
88
+ });
89
+ }
90
+
91
+ function normalizeBinding(input, index) {
92
+ if (!isPlainObject(input)) throw new TypeError(`bindings[${index}] must be an object.`);
93
+ assertKnownKeys(input, new Set([
94
+ 'moduleId', 'localName', 'exportName', 'importedName', 'packageModuleId', 'packageName',
95
+ 'contractId', 'source', 'via', 'direct', 'used'
96
+ ]), `bindings[${index}]`);
97
+ const via = string(input.via, `bindings[${index}].via`);
98
+ if (!PACKAGE_BINDING_VIA.includes(via)) throw new TypeError(`Unsupported package binding path ${via}.`);
99
+ const normalized = {
100
+ version: PACKAGE_BINDING_OWNERSHIP_VERSION,
101
+ moduleId: string(input.moduleId, `bindings[${index}].moduleId`),
102
+ localName: nullableString(input.localName, `bindings[${index}].localName`),
103
+ exportName: nullableString(input.exportName, `bindings[${index}].exportName`),
104
+ importedName: string(input.importedName, `bindings[${index}].importedName`),
105
+ packageModuleId: string(input.packageModuleId, `bindings[${index}].packageModuleId`),
106
+ packageName: normalizePackageName(input.packageName, `bindings[${index}].packageName`),
107
+ contractId: nullableString(input.contractId, `bindings[${index}].contractId`),
108
+ source: normalizeSourceLocation(input.source, `bindings[${index}].source`),
109
+ via,
110
+ direct: bool(input.direct, `bindings[${index}].direct`),
111
+ used: bool(input.used, `bindings[${index}].used`)
112
+ };
113
+ if (normalized.localName === null && normalized.exportName === null) throw new TypeError(`bindings[${index}] requires localName or exportName.`);
114
+ const stableInput = Object.freeze({
115
+ version: PACKAGE_BINDING_OWNERSHIP_VERSION,
116
+ moduleId: normalized.moduleId,
117
+ localName: normalized.localName,
118
+ exportName: normalized.exportName,
119
+ importedName: normalized.importedName,
120
+ packageModuleId: normalized.packageModuleId,
121
+ packageName: normalized.packageName,
122
+ contractId: normalized.contractId,
123
+ source: normalized.source,
124
+ via: normalized.via
125
+ });
126
+ return Object.freeze({ ...normalized, id: prefixedStableId('pkgbinding', stableInput, { length: 32 }) });
127
+ }
128
+
129
+ function normalizePackageReachabilityProjection(input) {
130
+ if (!isPlainObject(input)) throw new TypeError('Package reachability projection must be an object.');
131
+ assertKnownKeys(input, new Set(['graphVersion', 'graphHash', 'packages', 'bindings', 'selectedContracts']), 'Package reachability projection');
132
+ const packages = Object.freeze([...(input.packages || [])].map(normalizePackageRecord)
133
+ .sort((left, right) => left.packageName.localeCompare(right.packageName) || left.packageSubpath.localeCompare(right.packageSubpath) || left.moduleId.localeCompare(right.moduleId)));
134
+ if (new Set(packages.map((entry) => entry.moduleId)).size !== packages.length) throw new TypeError('Package reachability module identities must be unique.');
135
+ const bindings = Object.freeze([...(input.bindings || [])].map(normalizeBinding)
136
+ .sort((left, right) => left.moduleId.localeCompare(right.moduleId) || (left.localName || left.exportName).localeCompare(right.localName || right.exportName) || left.id.localeCompare(right.id)));
137
+ if (new Set(bindings.map((entry) => entry.id)).size !== bindings.length) throw new TypeError('Package binding identities must be unique.');
138
+ const semantic = Object.freeze({
139
+ version: PACKAGE_REACHABILITY_PROJECTION_VERSION,
140
+ graphVersion: string(input.graphVersion, 'graphVersion'),
141
+ graphHash: string(input.graphHash, 'graphHash'),
142
+ packages,
143
+ bindings,
144
+ selectedContracts: strings(input.selectedContracts, 'selectedContracts'),
145
+ policy: Object.freeze({
146
+ packageUseFromResolvedReachabilityOnly: true,
147
+ typeOnlyImportsActivateContracts: false,
148
+ substringSelection: false,
149
+ firstPartyLowerersOnly: true,
150
+ publicPluginApi: false
151
+ })
152
+ });
153
+ return Object.freeze({ ...semantic, projectionHash: sha256Hex(stableStringify(semantic)) });
154
+ }
155
+
156
+ function normalizeLifecycleEdge(input, index) {
157
+ if (!isPlainObject(input)) throw new TypeError(`lifecycleEdges[${index}] must be an object.`);
158
+ assertKnownKeys(input, new Set(['edgeId', 'moduleId', 'packageModuleId', 'packageName', 'symbol', 'source']), `lifecycleEdges[${index}]`);
159
+ return Object.freeze({
160
+ edgeId: string(input.edgeId, `lifecycleEdges[${index}].edgeId`),
161
+ moduleId: string(input.moduleId, `lifecycleEdges[${index}].moduleId`),
162
+ packageModuleId: string(input.packageModuleId, `lifecycleEdges[${index}].packageModuleId`),
163
+ packageName: normalizePackageName(input.packageName, `lifecycleEdges[${index}].packageName`),
164
+ symbol: string(input.symbol, `lifecycleEdges[${index}].symbol`),
165
+ source: normalizeSourceLocation(input.source, `lifecycleEdges[${index}].source`)
166
+ });
167
+ }
168
+
169
+ function normalizeApplicationEntrySafety(input) {
170
+ if (!isPlainObject(input)) throw new TypeError('Application entry safety projection must be an object.');
171
+ assertKnownKeys(input, new Set(['graphHash', 'lifecycleEdges']), 'Application entry safety projection');
172
+ const lifecycleEdges = Object.freeze([...(input.lifecycleEdges || [])].map(normalizeLifecycleEdge).sort(compareSource));
173
+ const semantic = Object.freeze({
174
+ version: APPLICATION_ENTRY_SAFETY_VERSION,
175
+ graphHash: string(input.graphHash, 'graphHash'),
176
+ importSafe: lifecycleEdges.length === 0,
177
+ lifecycleEdges,
178
+ firstLifecycleEdge: lifecycleEdges.length === 0 ? null : lifecycleEdges[0],
179
+ policy: Object.freeze({
180
+ knownPulseLifecycleOperationsOnly: true,
181
+ arbitraryPurityAnalysis: false,
182
+ lifecycleOwnership: 'provider',
183
+ configuredEntryMustBeImportSafe: true
184
+ })
185
+ });
186
+ return Object.freeze({ ...semantic, projectionHash: sha256Hex(stableStringify(semantic)) });
187
+ }
188
+
189
+ function normalizeEligibilityBlocker(input, index) {
190
+ if (!isPlainObject(input)) throw new TypeError(`blockers[${index}] must be an object.`);
191
+ assertKnownKeys(input, new Set([
192
+ 'kind', 'code', 'moduleId', 'handlerIds', 'packageName', 'packageSubpath', 'contractId',
193
+ 'specifier', 'source', 'message'
194
+ ]), `blockers[${index}]`);
195
+ const kind = string(input.kind, `blockers[${index}].kind`);
196
+ if (!ELIGIBILITY_BLOCKER_KINDS.includes(kind)) throw new TypeError(`Unsupported native eligibility blocker ${kind}.`);
197
+ const normalized = {
198
+ kind,
199
+ code: string(input.code, `blockers[${index}].code`),
200
+ moduleId: string(input.moduleId, `blockers[${index}].moduleId`),
201
+ handlerIds: strings(input.handlerIds, `blockers[${index}].handlerIds`),
202
+ packageName: nullableString(input.packageName, `blockers[${index}].packageName`),
203
+ packageSubpath: input.packageSubpath == null ? null : normalizePackageSubpath(input.packageSubpath, `blockers[${index}].packageSubpath`),
204
+ contractId: nullableString(input.contractId, `blockers[${index}].contractId`),
205
+ specifier: string(input.specifier, `blockers[${index}].specifier`),
206
+ source: normalizeSourceLocation(input.source, `blockers[${index}].source`),
207
+ message: string(input.message, `blockers[${index}].message`)
208
+ };
209
+ const stableInput = Object.freeze({ version: NATIVE_ELIGIBILITY_PROJECTION_VERSION, ...normalized });
210
+ return Object.freeze({ ...normalized, id: prefixedStableId('eligibility', stableInput, { length: 32 }) });
211
+ }
212
+
213
+ function normalizeHandlerEligibility(input, index) {
214
+ if (!isPlainObject(input)) throw new TypeError(`handlers[${index}] must be an object.`);
215
+ assertKnownKeys(input, new Set(['handlerId', 'moduleId', 'eligible', 'blockerIds']), `handlers[${index}]`);
216
+ return Object.freeze({
217
+ handlerId: string(input.handlerId, `handlers[${index}].handlerId`),
218
+ moduleId: string(input.moduleId, `handlers[${index}].moduleId`),
219
+ eligible: bool(input.eligible, `handlers[${index}].eligible`),
220
+ blockerIds: strings(input.blockerIds, `handlers[${index}].blockerIds`)
221
+ });
222
+ }
223
+
224
+ function normalizeNativeEligibilityProjection(input) {
225
+ if (!isPlainObject(input)) throw new TypeError('Native eligibility projection must be an object.');
226
+ assertKnownKeys(input, new Set(['graphHash', 'blockers', 'handlers']), 'Native eligibility projection');
227
+ const blockers = Object.freeze([...(input.blockers || [])].map(normalizeEligibilityBlocker).sort(compareSource));
228
+ if (new Set(blockers.map((entry) => entry.id)).size !== blockers.length) throw new TypeError('Native eligibility blocker identities must be unique.');
229
+ const handlers = Object.freeze([...(input.handlers || [])].map(normalizeHandlerEligibility)
230
+ .sort((left, right) => left.handlerId.localeCompare(right.handlerId)));
231
+ const semantic = Object.freeze({
232
+ version: NATIVE_ELIGIBILITY_PROJECTION_VERSION,
233
+ graphHash: string(input.graphHash, 'graphHash'),
234
+ eligible: blockers.length === 0,
235
+ blockers,
236
+ firstUnsupportedBoundary: blockers.length === 0 ? null : blockers[0],
237
+ handlers,
238
+ policy: Object.freeze({
239
+ automaticFallback: false,
240
+ selectedTargetChangesEligibility: false,
241
+ packageUseFromResolvedGraphOnly: true,
242
+ routeProjectionDeferred: true
243
+ })
244
+ });
245
+ return Object.freeze({ ...semantic, projectionHash: sha256Hex(stableStringify(semantic)) });
246
+ }
247
+
248
+ function defaultReachableGraphProjectionContract() {
249
+ return Object.freeze({
250
+ packageReachabilityVersion: PACKAGE_REACHABILITY_PROJECTION_VERSION,
251
+ packageBindingVersion: PACKAGE_BINDING_OWNERSHIP_VERSION,
252
+ applicationEntrySafetyVersion: APPLICATION_ENTRY_SAFETY_VERSION,
253
+ nativeEligibilityVersion: NATIVE_ELIGIBILITY_PROJECTION_VERSION,
254
+ packageNativeStatuses: PACKAGE_NATIVE_STATUSES,
255
+ packageBindingPaths: PACKAGE_BINDING_VIA,
256
+ eligibilityBlockerKinds: ELIGIBILITY_BLOCKER_KINDS,
257
+ policies: Object.freeze({
258
+ graphManifestVersionUnchanged: true,
259
+ projectionsDerivedFromAuthoritativeGraph: true,
260
+ packageLoweringSeamUnchanged: true,
261
+ externalCompilerPlugins: false,
262
+ automaticTargetFallback: false
263
+ })
264
+ });
265
+ }
266
+
267
+ module.exports = Object.freeze({
268
+ PACKAGE_REACHABILITY_PROJECTION_VERSION,
269
+ PACKAGE_BINDING_OWNERSHIP_VERSION,
270
+ APPLICATION_ENTRY_SAFETY_VERSION,
271
+ NATIVE_ELIGIBILITY_PROJECTION_VERSION,
272
+ PACKAGE_NATIVE_STATUSES,
273
+ PACKAGE_BINDING_VIA,
274
+ ELIGIBILITY_BLOCKER_KINDS,
275
+ normalizePackageReachabilityProjection,
276
+ normalizeApplicationEntrySafety,
277
+ normalizeNativeEligibilityProjection,
278
+ defaultReachableGraphProjectionContract
279
+ });