@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.
- package/README.md +35 -2
- package/package.json +124 -6
- package/src/assets/contracts.js +296 -0
- package/src/config/provider-resolution.js +54 -0
- package/src/config/runtime-provider-kv.js +73 -0
- package/src/config/symbolic-refs.js +192 -0
- package/src/crypto/contracts.js +647 -0
- package/src/definitions/config-schema.js +30 -0
- package/src/definitions/handler-roles.js +66 -0
- package/src/definitions/path-grammar.js +11 -0
- package/src/definitions/router-api.js +149 -0
- package/src/diagnostics/codes.js +42 -0
- package/src/diagnostics/reporter.js +20 -0
- package/src/diagnostics.js +139 -0
- package/src/dispatch-table.js +23 -0
- package/src/entities/catalog.d.ts +7 -0
- package/src/entities/catalog.js +87 -0
- package/src/entities/contracts.d.ts +95 -0
- package/src/entities/contracts.js +122 -0
- package/src/entities/json-rpc.d.ts +27 -0
- package/src/entities/json-rpc.js +69 -0
- package/src/entities/runtime.d.ts +67 -0
- package/src/entities/runtime.js +288 -0
- package/src/events/contracts.js +456 -0
- package/src/execution-plan.js +42 -0
- package/src/grip/contracts.js +106 -0
- package/src/handler/backend-json-request-body.js +58 -0
- package/src/handler/beta-lifecycle-golden.js +97 -0
- package/src/handler/canonical-native-plan.js +143 -0
- package/src/handler/canonical-native-runtime.js +168 -0
- package/src/handler/canonical-runtime.js +13 -0
- package/src/handler/context.js +131 -0
- package/src/handler/ctx-contract.js +71 -0
- package/src/handler/doctor-readiness-aggregator.js +67 -0
- package/src/handler/effects.js +446 -0
- package/src/handler/io-lifecycle.js +134 -0
- package/src/handler/lifecycle-authoring-alignment.js +56 -0
- package/src/handler/lifecycle-readiness-aggregate.js +93 -0
- package/src/handler/reference-node-lifecycle-golden.js +73 -0
- package/src/handler/request-json-body.js +147 -0
- package/src/handler/runtime-api.js +471 -0
- package/src/handler/schema-decode-result.js +135 -0
- package/src/handler/schema-response-codec.js +141 -0
- package/src/handler/surface-contract.js +601 -0
- package/src/handler/types-contract.js +51 -0
- package/src/handler-table.js +28 -0
- package/src/host/backend-capabilities.js +24 -0
- package/src/host/capabilities.js +273 -0
- package/src/host/channel-broadcaster.js +60 -0
- package/src/host/json-body.js +80 -0
- package/src/host/request-result-headers.js +38 -0
- package/src/host/streaming-passthrough.js +90 -0
- package/src/host/wasm-host-abi.js +154 -0
- package/src/index.js +75 -0
- package/src/jwt/contracts.js +427 -0
- package/src/library/api-surface.js +99 -0
- package/src/library/contracts.js +273 -0
- package/src/library/manifest.js +317 -0
- package/src/library/second-package-owned-lowering.js +39 -0
- package/src/library/sidecars.js +147 -0
- package/src/logging.js +89 -0
- package/src/package/package-contract.js +975 -0
- package/src/package/package-contract.schema.json +303 -0
- package/src/package/runtime-pulse-live.js +100 -0
- package/src/path-table.js +12 -0
- package/src/path.js +275 -0
- package/src/project/config-factory.js +48 -0
- package/src/project/config-plan.js +409 -0
- package/src/project/config-runtime.js +46 -0
- package/src/project/javascript-application.js +212 -0
- package/src/project/javascript-core-capabilities.js +44 -0
- package/src/project/native-optimization.js +9 -0
- package/src/project/package-ownership.js +242 -0
- package/src/project/package-product-projection.js +171 -0
- package/src/project/reachable-graph-identity.js +219 -0
- package/src/project/reachable-graph-projections.js +279 -0
- package/src/project/reachable-graph-resolver.js +303 -0
- package/src/project/reachable-graph-v2.js +430 -0
- package/src/project/reachable-graph-v2.schema.json +285 -0
- package/src/project/reachable-graph.js +199 -0
- package/src/project/reachable-graph.schema.json +222 -0
- package/src/project/target-support-evidence.js +559 -0
- package/src/provider/canonical-provider.js +239 -0
- package/src/provider/final-wasm-policy.js +87 -0
- package/src/provider/toolchain.js +771 -0
- package/src/route-plan.js +43 -0
- package/src/s3/contracts.js +12 -0
- package/src/schema-json/compile.js +63 -0
- package/src/schema-json/contracts.js +10 -0
- package/src/schema-json/generic-parser.js +62 -0
- package/src/schema-json/registry.js +359 -0
- package/src/schema-json/semantic-trace.js +241 -0
- package/src/schema-json/v1.js +65 -0
- package/src/schema-json/v2.js +110 -0
- package/src/sha256.js +104 -0
- package/src/spine.js +381 -0
- package/src/stable-id.js +142 -0
package/src/spine.js
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const COMPILER_SPINE_CONTRACT_VERSION = 'pulse.compiler-spine.v1';
|
|
4
|
+
const COMPILER_SPINE_CONTRACT_ID = 'pulse.compiler-spine';
|
|
5
|
+
|
|
6
|
+
const COMPILER_RULE_KINDS = Object.freeze([
|
|
7
|
+
'root-recognizer',
|
|
8
|
+
'surface-recognizer',
|
|
9
|
+
'surface-normalizer',
|
|
10
|
+
'semantic-classifier',
|
|
11
|
+
'semantic-validator',
|
|
12
|
+
'canonical-ir-builder',
|
|
13
|
+
'native-lowerer',
|
|
14
|
+
'package-lowerer-adapter',
|
|
15
|
+
'native-realizer',
|
|
16
|
+
'guest-realizer',
|
|
17
|
+
'provider-realizer',
|
|
18
|
+
'artifact-verifier'
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
const COMPILER_SPINE_PHASES = Object.freeze([
|
|
22
|
+
Object.freeze({
|
|
23
|
+
id: 'root-extraction',
|
|
24
|
+
order: 10,
|
|
25
|
+
input: 'typescript-source-graph',
|
|
26
|
+
output: 'application-root-facts',
|
|
27
|
+
ruleKinds: Object.freeze(['root-recognizer']),
|
|
28
|
+
ownsTraversal: true,
|
|
29
|
+
mutatesSourceAst: false
|
|
30
|
+
}),
|
|
31
|
+
Object.freeze({
|
|
32
|
+
id: 'surface-recognition',
|
|
33
|
+
order: 20,
|
|
34
|
+
input: 'application-root-facts',
|
|
35
|
+
output: 'recognized-authoring-surfaces',
|
|
36
|
+
ruleKinds: Object.freeze(['surface-recognizer']),
|
|
37
|
+
ownsTraversal: true,
|
|
38
|
+
mutatesSourceAst: false
|
|
39
|
+
}),
|
|
40
|
+
Object.freeze({
|
|
41
|
+
id: 'surface-normalization',
|
|
42
|
+
order: 30,
|
|
43
|
+
input: 'recognized-authoring-surfaces',
|
|
44
|
+
output: 'normalized-handler-operations',
|
|
45
|
+
ruleKinds: Object.freeze(['surface-normalizer']),
|
|
46
|
+
ownsTraversal: false,
|
|
47
|
+
mutatesSourceAst: false
|
|
48
|
+
}),
|
|
49
|
+
Object.freeze({
|
|
50
|
+
id: 'semantic-classification',
|
|
51
|
+
order: 40,
|
|
52
|
+
input: 'normalized-handler-operations',
|
|
53
|
+
output: 'classified-handler-operations',
|
|
54
|
+
ruleKinds: Object.freeze(['semantic-classifier']),
|
|
55
|
+
ownsTraversal: false,
|
|
56
|
+
mutatesSourceAst: false
|
|
57
|
+
}),
|
|
58
|
+
Object.freeze({
|
|
59
|
+
id: 'contract-validation',
|
|
60
|
+
order: 50,
|
|
61
|
+
input: 'classified-handler-operations',
|
|
62
|
+
output: 'validated-handler-program',
|
|
63
|
+
ruleKinds: Object.freeze(['semantic-validator']),
|
|
64
|
+
ownsTraversal: false,
|
|
65
|
+
mutatesSourceAst: false
|
|
66
|
+
}),
|
|
67
|
+
Object.freeze({
|
|
68
|
+
id: 'canonical-ir',
|
|
69
|
+
order: 60,
|
|
70
|
+
input: 'validated-handler-program',
|
|
71
|
+
output: 'application-ir',
|
|
72
|
+
ruleKinds: Object.freeze(['canonical-ir-builder']),
|
|
73
|
+
ownsTraversal: false,
|
|
74
|
+
mutatesSourceAst: false
|
|
75
|
+
}),
|
|
76
|
+
Object.freeze({
|
|
77
|
+
id: 'native-lowering',
|
|
78
|
+
order: 70,
|
|
79
|
+
input: 'application-ir',
|
|
80
|
+
output: 'native-plan',
|
|
81
|
+
ruleKinds: Object.freeze(['native-lowerer']),
|
|
82
|
+
ownsTraversal: false,
|
|
83
|
+
mutatesSourceAst: false
|
|
84
|
+
}),
|
|
85
|
+
Object.freeze({
|
|
86
|
+
id: 'package-lowering',
|
|
87
|
+
order: 80,
|
|
88
|
+
input: 'native-plan-package-operations',
|
|
89
|
+
output: 'package-lowered-native-plan',
|
|
90
|
+
ruleKinds: Object.freeze(['package-lowerer-adapter']),
|
|
91
|
+
ownsTraversal: false,
|
|
92
|
+
mutatesSourceAst: false
|
|
93
|
+
}),
|
|
94
|
+
Object.freeze({
|
|
95
|
+
id: 'native-realization',
|
|
96
|
+
order: 90,
|
|
97
|
+
input: 'package-lowered-native-plan',
|
|
98
|
+
output: 'primary-native-wasm',
|
|
99
|
+
ruleKinds: Object.freeze(['native-realizer']),
|
|
100
|
+
ownsTraversal: false,
|
|
101
|
+
mutatesSourceAst: false
|
|
102
|
+
}),
|
|
103
|
+
Object.freeze({
|
|
104
|
+
id: 'guest-realization',
|
|
105
|
+
order: 100,
|
|
106
|
+
input: 'primary-native-wasm',
|
|
107
|
+
output: 'composed-native-wasm',
|
|
108
|
+
ruleKinds: Object.freeze(['guest-realizer']),
|
|
109
|
+
ownsTraversal: false,
|
|
110
|
+
mutatesSourceAst: false
|
|
111
|
+
}),
|
|
112
|
+
Object.freeze({
|
|
113
|
+
id: 'artifact-verification',
|
|
114
|
+
order: 110,
|
|
115
|
+
input: 'composed-native-wasm',
|
|
116
|
+
output: 'verified-build-evidence',
|
|
117
|
+
ruleKinds: Object.freeze(['artifact-verifier']),
|
|
118
|
+
ownsTraversal: false,
|
|
119
|
+
mutatesSourceAst: false
|
|
120
|
+
}),
|
|
121
|
+
Object.freeze({
|
|
122
|
+
id: 'provider-realization',
|
|
123
|
+
order: 120,
|
|
124
|
+
input: 'verified-build-evidence',
|
|
125
|
+
output: 'provider-artifacts',
|
|
126
|
+
ruleKinds: Object.freeze(['provider-realizer']),
|
|
127
|
+
ownsTraversal: false,
|
|
128
|
+
mutatesSourceAst: false
|
|
129
|
+
})
|
|
130
|
+
]);
|
|
131
|
+
|
|
132
|
+
const COMPILER_SPINE_POLICY = Object.freeze({
|
|
133
|
+
internalOnly: true,
|
|
134
|
+
publicPluginApi: false,
|
|
135
|
+
externalRuleRegistration: false,
|
|
136
|
+
dynamicRuleRegistration: false,
|
|
137
|
+
runtimeRuleDiscovery: false,
|
|
138
|
+
phaseOrdering: 'fixed-and-versioned',
|
|
139
|
+
traversalOwnership: 'phase-owned',
|
|
140
|
+
sharedMutableCompilerContext: false,
|
|
141
|
+
sourceAstMutation: false,
|
|
142
|
+
canonicalIrPaths: 1,
|
|
143
|
+
packageLoweringInput: 'canonical-package-operation-only',
|
|
144
|
+
packageLowererTrust: 'manifest-declared-first-party',
|
|
145
|
+
providerRealizationInput: 'provider-neutral-native-plan',
|
|
146
|
+
behaviorChangeInContractFreeze: false
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const DEFAULT_COMPILER_SPINE_RULES = Object.freeze([
|
|
150
|
+
Object.freeze({
|
|
151
|
+
id: 'pulse.root.plain-handler',
|
|
152
|
+
phase: 'root-extraction',
|
|
153
|
+
kind: 'root-recognizer',
|
|
154
|
+
owner: 'pulse.compiler-orchestration',
|
|
155
|
+
status: 'existing',
|
|
156
|
+
priority: 10,
|
|
157
|
+
dependsOn: Object.freeze([])
|
|
158
|
+
}),
|
|
159
|
+
Object.freeze({
|
|
160
|
+
id: 'pulse.root.router',
|
|
161
|
+
phase: 'root-extraction',
|
|
162
|
+
kind: 'root-recognizer',
|
|
163
|
+
owner: 'pulse.compiler-orchestration',
|
|
164
|
+
status: 'existing',
|
|
165
|
+
priority: 20,
|
|
166
|
+
dependsOn: Object.freeze([])
|
|
167
|
+
}),
|
|
168
|
+
Object.freeze({
|
|
169
|
+
id: 'pulse.root.pulse',
|
|
170
|
+
phase: 'root-extraction',
|
|
171
|
+
kind: 'root-recognizer',
|
|
172
|
+
owner: 'pulse.compiler-orchestration',
|
|
173
|
+
status: 'planned',
|
|
174
|
+
priority: 30,
|
|
175
|
+
dependsOn: Object.freeze([])
|
|
176
|
+
}),
|
|
177
|
+
Object.freeze({
|
|
178
|
+
id: 'pulse.surface.ctx',
|
|
179
|
+
phase: 'surface-recognition',
|
|
180
|
+
kind: 'surface-recognizer',
|
|
181
|
+
owner: 'pulse.compiler-orchestration',
|
|
182
|
+
status: 'contract-frozen',
|
|
183
|
+
priority: 10,
|
|
184
|
+
dependsOn: Object.freeze(['pulse.root.plain-handler', 'pulse.root.router', 'pulse.root.pulse'])
|
|
185
|
+
}),
|
|
186
|
+
Object.freeze({
|
|
187
|
+
id: 'pulse.surface.authoring-sugar',
|
|
188
|
+
phase: 'surface-normalization',
|
|
189
|
+
kind: 'surface-normalizer',
|
|
190
|
+
owner: 'pulse.compiler-orchestration',
|
|
191
|
+
status: 'planned',
|
|
192
|
+
priority: 10,
|
|
193
|
+
dependsOn: Object.freeze(['pulse.surface.ctx'])
|
|
194
|
+
}),
|
|
195
|
+
Object.freeze({
|
|
196
|
+
id: 'pulse.semantic.handler-surface',
|
|
197
|
+
phase: 'semantic-classification',
|
|
198
|
+
kind: 'semantic-classifier',
|
|
199
|
+
owner: '@pulse-compute/wasm-contracts',
|
|
200
|
+
status: 'contract-frozen',
|
|
201
|
+
priority: 10,
|
|
202
|
+
dependsOn: Object.freeze(['pulse.surface.authoring-sugar'])
|
|
203
|
+
}),
|
|
204
|
+
Object.freeze({
|
|
205
|
+
id: 'pulse.validate.handler-contract',
|
|
206
|
+
phase: 'contract-validation',
|
|
207
|
+
kind: 'semantic-validator',
|
|
208
|
+
owner: 'pulse.compiler-orchestration',
|
|
209
|
+
status: 'existing-to-converge',
|
|
210
|
+
priority: 10,
|
|
211
|
+
dependsOn: Object.freeze(['pulse.semantic.handler-surface'])
|
|
212
|
+
}),
|
|
213
|
+
Object.freeze({
|
|
214
|
+
id: 'pulse.ir.application',
|
|
215
|
+
phase: 'canonical-ir',
|
|
216
|
+
kind: 'canonical-ir-builder',
|
|
217
|
+
owner: 'pulse.compiler-orchestration',
|
|
218
|
+
status: 'existing-to-envelope',
|
|
219
|
+
priority: 10,
|
|
220
|
+
dependsOn: Object.freeze(['pulse.validate.handler-contract'])
|
|
221
|
+
}),
|
|
222
|
+
Object.freeze({
|
|
223
|
+
id: 'pulse.lower.native-effects',
|
|
224
|
+
phase: 'native-lowering',
|
|
225
|
+
kind: 'native-lowerer',
|
|
226
|
+
owner: 'pulse.compiler-orchestration',
|
|
227
|
+
status: 'existing',
|
|
228
|
+
priority: 10,
|
|
229
|
+
dependsOn: Object.freeze(['pulse.ir.application'])
|
|
230
|
+
}),
|
|
231
|
+
Object.freeze({
|
|
232
|
+
id: 'pulse.lower.package-contracts',
|
|
233
|
+
phase: 'package-lowering',
|
|
234
|
+
kind: 'package-lowerer-adapter',
|
|
235
|
+
owner: 'pulse.package-lowering',
|
|
236
|
+
status: 'existing',
|
|
237
|
+
priority: 10,
|
|
238
|
+
dependsOn: Object.freeze(['pulse.lower.native-effects'])
|
|
239
|
+
}),
|
|
240
|
+
Object.freeze({
|
|
241
|
+
id: 'pulse.realize.native-primary',
|
|
242
|
+
phase: 'native-realization',
|
|
243
|
+
kind: 'native-realizer',
|
|
244
|
+
owner: 'pulse.compiler-orchestration',
|
|
245
|
+
status: 'existing',
|
|
246
|
+
priority: 10,
|
|
247
|
+
dependsOn: Object.freeze(['pulse.lower.package-contracts'])
|
|
248
|
+
}),
|
|
249
|
+
Object.freeze({
|
|
250
|
+
id: 'pulse.realize.guest-units',
|
|
251
|
+
phase: 'guest-realization',
|
|
252
|
+
kind: 'guest-realizer',
|
|
253
|
+
owner: '@pulse-compute/wasm-guest-link',
|
|
254
|
+
status: 'implemented',
|
|
255
|
+
priority: 10,
|
|
256
|
+
dependsOn: Object.freeze(['pulse.realize.native-primary'])
|
|
257
|
+
}),
|
|
258
|
+
Object.freeze({
|
|
259
|
+
id: 'pulse.verify.final-wasm',
|
|
260
|
+
phase: 'artifact-verification',
|
|
261
|
+
kind: 'artifact-verifier',
|
|
262
|
+
owner: 'pulse.artifact-tooling',
|
|
263
|
+
status: 'existing-to-converge',
|
|
264
|
+
priority: 10,
|
|
265
|
+
dependsOn: Object.freeze(['pulse.realize.guest-units'])
|
|
266
|
+
}),
|
|
267
|
+
Object.freeze({
|
|
268
|
+
id: 'pulse.realize.provider',
|
|
269
|
+
phase: 'provider-realization',
|
|
270
|
+
kind: 'provider-realizer',
|
|
271
|
+
owner: 'pulse.selected-provider',
|
|
272
|
+
status: 'existing',
|
|
273
|
+
priority: 10,
|
|
274
|
+
dependsOn: Object.freeze(['pulse.verify.final-wasm'])
|
|
275
|
+
})
|
|
276
|
+
]);
|
|
277
|
+
|
|
278
|
+
function isNonEmptyString(value) {
|
|
279
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function phaseMap() {
|
|
283
|
+
return new Map(COMPILER_SPINE_PHASES.map((phase) => [phase.id, phase]));
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function validateRule(rule, phases) {
|
|
287
|
+
if (!rule || typeof rule !== 'object') throw new TypeError('Compiler spine rule must be an object.');
|
|
288
|
+
if (!isNonEmptyString(rule.id)) throw new TypeError('Compiler spine rule requires a stable id.');
|
|
289
|
+
if (!isNonEmptyString(rule.phase) || !phases.has(rule.phase)) throw new TypeError(`Compiler spine rule ${rule.id} references an unknown phase.`);
|
|
290
|
+
if (!COMPILER_RULE_KINDS.includes(rule.kind)) throw new TypeError(`Compiler spine rule ${rule.id} uses unsupported kind ${rule.kind}.`);
|
|
291
|
+
const phase = phases.get(rule.phase);
|
|
292
|
+
if (!phase.ruleKinds.includes(rule.kind)) throw new TypeError(`Compiler spine rule ${rule.id} kind ${rule.kind} is not allowed in phase ${rule.phase}.`);
|
|
293
|
+
if (!isNonEmptyString(rule.owner)) throw new TypeError(`Compiler spine rule ${rule.id} requires an owner.`);
|
|
294
|
+
if (!Array.isArray(rule.dependsOn)) throw new TypeError(`Compiler spine rule ${rule.id} requires a dependsOn array.`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function assertAcyclic(byId) {
|
|
298
|
+
const visiting = new Set();
|
|
299
|
+
const visited = new Set();
|
|
300
|
+
|
|
301
|
+
function visit(id, stack) {
|
|
302
|
+
if (visited.has(id)) return;
|
|
303
|
+
if (visiting.has(id)) throw new TypeError(`Compiler spine rule dependency cycle: ${[...stack, id].join(' -> ')}`);
|
|
304
|
+
const rule = byId.get(id);
|
|
305
|
+
if (!rule) throw new TypeError(`Compiler spine rule dependency is unknown: ${id}`);
|
|
306
|
+
visiting.add(id);
|
|
307
|
+
for (const dependency of rule.dependsOn) visit(dependency, [...stack, id]);
|
|
308
|
+
visiting.delete(id);
|
|
309
|
+
visited.add(id);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
for (const id of byId.keys()) visit(id, []);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function buildCompilerSpineRegistry(rules = DEFAULT_COMPILER_SPINE_RULES) {
|
|
316
|
+
const phases = phaseMap();
|
|
317
|
+
const byId = new Map();
|
|
318
|
+
for (const rule of rules) {
|
|
319
|
+
validateRule(rule, phases);
|
|
320
|
+
if (byId.has(rule.id)) throw new TypeError(`Duplicate compiler spine rule id: ${rule.id}`);
|
|
321
|
+
byId.set(rule.id, rule);
|
|
322
|
+
}
|
|
323
|
+
for (const rule of byId.values()) {
|
|
324
|
+
for (const dependency of rule.dependsOn) {
|
|
325
|
+
if (!byId.has(dependency)) throw new TypeError(`Compiler spine rule ${rule.id} depends on unknown rule ${dependency}.`);
|
|
326
|
+
const dependencyPhase = phases.get(byId.get(dependency).phase);
|
|
327
|
+
const rulePhase = phases.get(rule.phase);
|
|
328
|
+
if (dependencyPhase.order > rulePhase.order) {
|
|
329
|
+
throw new TypeError(`Compiler spine rule ${rule.id} depends on later phase rule ${dependency}.`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
assertAcyclic(byId);
|
|
334
|
+
|
|
335
|
+
const ordered = Array.from(byId.values()).sort((left, right) => {
|
|
336
|
+
const phaseOrder = phases.get(left.phase).order - phases.get(right.phase).order;
|
|
337
|
+
if (phaseOrder !== 0) return phaseOrder;
|
|
338
|
+
const priorityOrder = Number(left.priority || 0) - Number(right.priority || 0);
|
|
339
|
+
if (priorityOrder !== 0) return priorityOrder;
|
|
340
|
+
return left.id.localeCompare(right.id);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
return Object.freeze({
|
|
344
|
+
version: COMPILER_SPINE_CONTRACT_VERSION,
|
|
345
|
+
contractId: COMPILER_SPINE_CONTRACT_ID,
|
|
346
|
+
phases: COMPILER_SPINE_PHASES,
|
|
347
|
+
policy: COMPILER_SPINE_POLICY,
|
|
348
|
+
rules: Object.freeze(ordered),
|
|
349
|
+
get(id) {
|
|
350
|
+
return byId.get(id);
|
|
351
|
+
},
|
|
352
|
+
rulesForPhase(phaseId) {
|
|
353
|
+
if (!phases.has(phaseId)) throw new TypeError(`Unknown compiler spine phase: ${phaseId}`);
|
|
354
|
+
return Object.freeze(ordered.filter((rule) => rule.phase === phaseId));
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const DEFAULT_COMPILER_SPINE_REGISTRY = buildCompilerSpineRegistry();
|
|
360
|
+
|
|
361
|
+
function defaultCompilerSpineContract() {
|
|
362
|
+
return Object.freeze({
|
|
363
|
+
version: COMPILER_SPINE_CONTRACT_VERSION,
|
|
364
|
+
contractId: COMPILER_SPINE_CONTRACT_ID,
|
|
365
|
+
phases: COMPILER_SPINE_PHASES,
|
|
366
|
+
policy: COMPILER_SPINE_POLICY,
|
|
367
|
+
rules: DEFAULT_COMPILER_SPINE_REGISTRY.rules
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
module.exports = {
|
|
372
|
+
COMPILER_SPINE_CONTRACT_VERSION,
|
|
373
|
+
COMPILER_SPINE_CONTRACT_ID,
|
|
374
|
+
COMPILER_RULE_KINDS,
|
|
375
|
+
COMPILER_SPINE_PHASES,
|
|
376
|
+
COMPILER_SPINE_POLICY,
|
|
377
|
+
DEFAULT_COMPILER_SPINE_RULES,
|
|
378
|
+
DEFAULT_COMPILER_SPINE_REGISTRY,
|
|
379
|
+
buildCompilerSpineRegistry,
|
|
380
|
+
defaultCompilerSpineContract
|
|
381
|
+
};
|
package/src/stable-id.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { sha256Hex: portableSha256Hex } = require('./sha256.js');
|
|
4
|
+
const { HANDLER_STABLE_INPUT_VERSION } = require('./handler-table.js');
|
|
5
|
+
const { ROUTE_STABLE_INPUT_VERSION } = require('./route-plan.js');
|
|
6
|
+
|
|
7
|
+
function canonicalize(value) {
|
|
8
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
9
|
+
if (!value || typeof value !== 'object') return value;
|
|
10
|
+
const out = {};
|
|
11
|
+
for (const key of Object.keys(value).sort()) {
|
|
12
|
+
const child = value[key];
|
|
13
|
+
if (child !== undefined) out[key] = canonicalize(child);
|
|
14
|
+
}
|
|
15
|
+
return out;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function stableStringify(value) {
|
|
19
|
+
return JSON.stringify(canonicalize(value));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function sha256Hex(value) {
|
|
23
|
+
return portableSha256Hex(String(value));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function stableHash(value, options = {}) {
|
|
27
|
+
const hex = sha256Hex(stableStringify(value));
|
|
28
|
+
return hex.slice(0, options.length || 24);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function sourceTextHash(sourceText, options = {}) {
|
|
32
|
+
return sha256Hex(String(sourceText || '')).slice(0, options.length || 24);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function prefixedStableId(prefix, value, options = {}) {
|
|
36
|
+
return `${prefix}_${stableHash(value, options)}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function createHandlerStableInput(handlerParts) {
|
|
40
|
+
return {
|
|
41
|
+
artifact: HANDLER_STABLE_INPUT_VERSION,
|
|
42
|
+
file: handlerParts.file,
|
|
43
|
+
kind: handlerParts.kind,
|
|
44
|
+
localName: handlerParts.localName || null,
|
|
45
|
+
exportName: handlerParts.exportName || null,
|
|
46
|
+
inline: Boolean(handlerParts.inline),
|
|
47
|
+
loc: handlerParts.inline ? handlerParts.loc || null : undefined,
|
|
48
|
+
sourceTextHash: handlerParts.sourceTextHash
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createHandlerStableId(handlerParts) {
|
|
53
|
+
return prefixedStableId('handler', createHandlerStableInput(handlerParts));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function normalizeRefIdentity(ref) {
|
|
57
|
+
if (!ref) return null;
|
|
58
|
+
if (typeof ref === 'string') return { kind: 'legacy-name', value: ref };
|
|
59
|
+
if (ref.kind === 'handler') return { kind: 'handler', id: ref.id };
|
|
60
|
+
if (ref.id && ref.kind !== 'static') return { kind: 'handler', id: ref.id };
|
|
61
|
+
if (ref.kind === 'identifier') return { kind: 'identifier', value: ref.value };
|
|
62
|
+
if (ref.kind === 'inline') {
|
|
63
|
+
// Legacy Phase 2 behavior for pre-handler-table references.
|
|
64
|
+
return { kind: 'inline', value: String(ref.value || '').replace(/\s+/g, ' ').trim() };
|
|
65
|
+
}
|
|
66
|
+
if (ref.kind === 'ref' || ref.kind === 'static') return { kind: ref.kind, value: ref.value };
|
|
67
|
+
return { kind: ref.kind || 'unknown', value: ref.value === undefined ? null : String(ref.value) };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function normalizeChannelIdentity(channel) {
|
|
71
|
+
if (!channel) return null;
|
|
72
|
+
return normalizeRefIdentity(channel);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function buildRouteStableInput(routeParts) {
|
|
76
|
+
return {
|
|
77
|
+
artifact: routeParts.artifact || ROUTE_STABLE_INPUT_VERSION,
|
|
78
|
+
method: routeParts.method,
|
|
79
|
+
path: routeParts.path,
|
|
80
|
+
params: routeParts.params || [],
|
|
81
|
+
handler: normalizeRefIdentity(routeParts.handler),
|
|
82
|
+
middleware: (routeParts.middleware || []).map(normalizeRefIdentity),
|
|
83
|
+
errorHandlers: (routeParts.errorHandlers || []).map(normalizeRefIdentity),
|
|
84
|
+
connectHandlers: (routeParts.connectHandlers || []).map(normalizeRefIdentity),
|
|
85
|
+
disconnectHandlers: (routeParts.disconnectHandlers || []).map(normalizeRefIdentity),
|
|
86
|
+
channel: normalizeChannelIdentity(routeParts.channel)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function createRouteStableId(routeParts) {
|
|
91
|
+
return prefixedStableId('route', buildRouteStableInput(routeParts));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function createRouteIdMap(routePlan, options = {}) {
|
|
95
|
+
const generatedBy = options.generatedBy || routePlan.generatedBy;
|
|
96
|
+
const routes = (routePlan.routes || []).map((route) => ({
|
|
97
|
+
stableId: route.stableId,
|
|
98
|
+
runtimeId: route.runtimeId ?? route.id,
|
|
99
|
+
id: route.id,
|
|
100
|
+
method: route.method,
|
|
101
|
+
path: route.path,
|
|
102
|
+
handler: route.handler,
|
|
103
|
+
handlerName: route.handlerName,
|
|
104
|
+
params: route.params || [],
|
|
105
|
+
pathPattern: route.pathPattern,
|
|
106
|
+
sourceRouter: route.sourceRouter,
|
|
107
|
+
order: route.order
|
|
108
|
+
}));
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
version: 'pulsewasm.route-id-map.v3',
|
|
112
|
+
generatedBy,
|
|
113
|
+
source: routePlan.source,
|
|
114
|
+
entryRouter: routePlan.entryRouter,
|
|
115
|
+
idPolicy: routePlan.idPolicy,
|
|
116
|
+
pathPolicy: routePlan.pathPolicy,
|
|
117
|
+
routes,
|
|
118
|
+
summary: {
|
|
119
|
+
routes: routes.length,
|
|
120
|
+
stableIds: new Set(routes.map((route) => route.stableId)).size,
|
|
121
|
+
runtimeIds: new Set(routes.map((route) => route.runtimeId)).size,
|
|
122
|
+
handlerIds: new Set(routes.map((route) => route.handler).filter(Boolean)).size,
|
|
123
|
+
wildcardRoutes: routes.filter((route) => route.pathPattern && route.pathPattern.wildcard).length
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = {
|
|
129
|
+
buildRouteStableInput,
|
|
130
|
+
canonicalize,
|
|
131
|
+
createHandlerStableId,
|
|
132
|
+
createHandlerStableInput,
|
|
133
|
+
createRouteIdMap,
|
|
134
|
+
createRouteStableId,
|
|
135
|
+
normalizeChannelIdentity,
|
|
136
|
+
normalizeRefIdentity,
|
|
137
|
+
prefixedStableId,
|
|
138
|
+
sha256Hex,
|
|
139
|
+
sourceTextHash,
|
|
140
|
+
stableHash,
|
|
141
|
+
stableStringify
|
|
142
|
+
};
|