@pulse-compute/wasm-compiler 1.0.0-beta.7 → 1.0.0-beta.8
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 +3 -3
- package/package.json +9 -9
- package/src/canonical-native-compiler.js +12 -3
- package/src/canonical-native-plan.js +1 -0
- package/src/report-reference-provenance.js +121 -0
- package/src/report-response-payloads.js +62 -0
- package/src/report-route-behavior.js +15 -0
- package/src/spine/canonical-router.js +1 -0
package/README.md
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
> **Stability:** Internal compiler interface; no application-author compatibility guarantee.<br>
|
|
9
9
|
> **npm:** [`@pulse-compute/wasm-compiler`](https://www.npmjs.com/package/@pulse-compute/wasm-compiler)<br>
|
|
10
10
|
> **Canonical replacement:** `@pulse-compute/cli`<br>
|
|
11
|
-
> **Canonical documentation:** [Package guide](https://pulsecompute.io/v1.0.0-beta.
|
|
11
|
+
> **Canonical documentation:** [Package guide](https://pulsecompute.io/v1.0.0-beta.8/packages/implementation-packages/)
|
|
12
12
|
>
|
|
13
|
-
> This release-status block is generated from the synchronized `Pulse 1.0.0-beta.
|
|
13
|
+
> This release-status block is generated from the synchronized `Pulse 1.0.0-beta.8` package policy.
|
|
14
14
|
<!-- pulse-package-status:end -->
|
|
15
15
|
|
|
16
16
|
Whole-project compiler implementation used by the `pulse` workflow. Application projects should use `@pulse-compute/cli`, not import this package directly.
|
|
@@ -63,4 +63,4 @@ The package exports its root implementation plus focused compiler, CLI, extracto
|
|
|
63
63
|
|
|
64
64
|
Compiler changes should be covered by focused static/lowering tests, canonical project inspection, provider-plan parity, packed-package discovery where relevant, and the release documentation gates.
|
|
65
65
|
|
|
66
|
-
See the installed [implementation package guide](https://pulsecompute.io/v1.0.0-beta.
|
|
66
|
+
See the installed [implementation package guide](https://pulsecompute.io/v1.0.0-beta.8/packages/implementation-packages/) and [compilation concepts](https://pulsecompute.io/v1.0.0-beta.8/concepts/compilation-and-lowering/).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pulse-compute/wasm-compiler",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.8",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "PulseWasm compiler package inside the Pulse monorepo.",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
"pulsewasm": "./bin/pulsewasm-extract.js"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@pulse-compute/wasm-build-support": "1.0.0-beta.
|
|
13
|
-
"@pulse-compute/wasm-contracts": "1.0.0-beta.
|
|
14
|
-
"@pulse-compute/wasm-guest-link": "1.0.0-beta.
|
|
15
|
-
"@pulse-compute/wasm-host-runtime": "1.0.0-beta.
|
|
16
|
-
"@pulse-compute/wasm-library-kit": "1.0.0-beta.
|
|
17
|
-
"@pulse-compute/wasm-runtime-core-as": "1.0.0-beta.
|
|
18
|
-
"@pulse-compute/wasm-schema-json": "1.0.0-beta.
|
|
12
|
+
"@pulse-compute/wasm-build-support": "1.0.0-beta.8",
|
|
13
|
+
"@pulse-compute/wasm-contracts": "1.0.0-beta.8",
|
|
14
|
+
"@pulse-compute/wasm-guest-link": "1.0.0-beta.8",
|
|
15
|
+
"@pulse-compute/wasm-host-runtime": "1.0.0-beta.8",
|
|
16
|
+
"@pulse-compute/wasm-library-kit": "1.0.0-beta.8",
|
|
17
|
+
"@pulse-compute/wasm-runtime-core-as": "1.0.0-beta.8",
|
|
18
|
+
"@pulse-compute/wasm-schema-json": "1.0.0-beta.8",
|
|
19
19
|
"assemblyscript": "0.28.18",
|
|
20
20
|
"json-as": "1.5.0",
|
|
21
21
|
"typescript": "5.9.3"
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"url": "git+https://github.com/pulse-compute/pulse.git",
|
|
57
57
|
"directory": "wasm/packages/compiler"
|
|
58
58
|
},
|
|
59
|
-
"homepage": "https://pulsecompute.io/v1.0.0-beta.
|
|
59
|
+
"homepage": "https://pulsecompute.io/v1.0.0-beta.8/packages/implementation-packages/",
|
|
60
60
|
"bugs": {
|
|
61
61
|
"url": "https://github.com/pulse-compute/pulse/issues"
|
|
62
62
|
},
|
|
@@ -221,9 +221,10 @@ function realizeCanonicalNativePlan(plan, options = {}, providerRequirements) {
|
|
|
221
221
|
'--path', jsonAs.dependencyRoot
|
|
222
222
|
);
|
|
223
223
|
}
|
|
224
|
+
require('@pulse-compute/wasm-build-support/native-optimization').appendJsonCacheArenaArgs(args, options.elideJsonCacheArena, schemaCodecsActive);
|
|
224
225
|
const readReportCapture = options.reportCapture === false ? () => null : prepareReportCapture(args, stagingDir, generated.manifest,
|
|
225
226
|
require(path.join(asc.packageRoot, 'package.json')).version, 'canonical-native.as/');
|
|
226
|
-
const startedAt =
|
|
227
|
+
const startedAt = process.hrtime.bigint();
|
|
227
228
|
const result = spawnSync(asc.executable, args, {
|
|
228
229
|
cwd: stagingDir,
|
|
229
230
|
encoding: 'utf8',
|
|
@@ -233,7 +234,7 @@ function realizeCanonicalNativePlan(plan, options = {}, providerRequirements) {
|
|
|
233
234
|
? { ...process.env, JSON_STRICT: 'true', JSON_USE_FAST_PATH: '0', JSON_MODE: 'NAIVE' }
|
|
234
235
|
: process.env
|
|
235
236
|
});
|
|
236
|
-
const durationMs =
|
|
237
|
+
const durationMs = Math.round(Number(process.hrtime.bigint() - startedAt) / 1e6);
|
|
237
238
|
if (result.error || result.status !== 0 || !fs.existsSync(wasmFile)) {
|
|
238
239
|
throw new CanonicalNativeCompileError('AssemblyScript failed to compile the canonical native plan.', {
|
|
239
240
|
status: result.status,
|
|
@@ -253,9 +254,15 @@ function realizeCanonicalNativePlan(plan, options = {}, providerRequirements) {
|
|
|
253
254
|
guestUnits,
|
|
254
255
|
generated,
|
|
255
256
|
reportAttribution: readReportCapture(wasm),
|
|
257
|
+
buildObservation: require('@pulse-compute/wasm-build-support/native-optimization').observeAssemblyScript(args, {
|
|
258
|
+
version: require(path.join(asc.packageRoot, 'package.json')).version,
|
|
259
|
+
assemblyScriptRoot: asc.packageRoot, timeoutMs: Number(options.timeoutMs || 120000),
|
|
260
|
+
compilerVersion: runtimeContract.CANONICAL_NATIVE_COMPILER_VERSION, durationMs, jsonAsVersion: jsonAs && jsonAs.version
|
|
261
|
+
}),
|
|
256
262
|
wasm,
|
|
257
263
|
wat: emitWat ? fs.readFileSync(watFile, 'utf8') : '',
|
|
258
264
|
textEmitted: emitWat,
|
|
265
|
+
elideJsonCacheArena: options.elideJsonCacheArena === true,
|
|
259
266
|
assemblyScriptVersion: require(path.join(asc.packageRoot, 'package.json')).version,
|
|
260
267
|
jsonAsVersion: jsonAs && jsonAs.version,
|
|
261
268
|
optimization,
|
|
@@ -286,8 +293,9 @@ function verifyCanonicalNativeRealization(realization) {
|
|
|
286
293
|
compilerVersion: runtimeContract.CANONICAL_NATIVE_COMPILER_VERSION,
|
|
287
294
|
assemblyScript: Object.freeze({ package: 'assemblyscript', version: realization.assemblyScriptVersion }),
|
|
288
295
|
optimization: resolveNativeOptimization(realization.optimization),
|
|
296
|
+
elideJsonCacheArena: realization.elideJsonCacheArena,
|
|
289
297
|
jsonAs: realization.jsonAsVersion
|
|
290
|
-
? Object.freeze({ package: 'json-as', version: realization.jsonAsVersion, transform: true, strict: true, mode: 'NAIVE', fastPath: false })
|
|
298
|
+
? Object.freeze({ package: 'json-as', version: realization.jsonAsVersion, transform: true, strict: true, mode: 'NAIVE', fastPath: false, elideDisabledCacheArena: realization.elideJsonCacheArena })
|
|
291
299
|
: null,
|
|
292
300
|
wasm: Object.freeze({ bytes: inspection.bytes, sha256: inspection.sha256, magic: inspection.magic }),
|
|
293
301
|
wat: Object.freeze(realization.textEmitted
|
|
@@ -321,6 +329,7 @@ function verifyCanonicalNativeRealization(realization) {
|
|
|
321
329
|
wat: realization.wat,
|
|
322
330
|
inspection,
|
|
323
331
|
manifest,
|
|
332
|
+
buildObservation: realization.buildObservation,
|
|
324
333
|
durationMs: realization.durationMs,
|
|
325
334
|
output: realization.output
|
|
326
335
|
});
|
|
@@ -1905,6 +1905,7 @@ module.exports = Object.freeze({
|
|
|
1905
1905
|
CANONICAL_NATIVE_PLAN_OWNERSHIP_VERSION: contract.CANONICAL_NATIVE_PLAN_OWNERSHIP_VERSION,
|
|
1906
1906
|
CanonicalNativePlanError,
|
|
1907
1907
|
buildCanonicalNativePlan,
|
|
1908
|
+
collectCanonicalReportReferences: require('./report-reference-provenance').collectCanonicalReportReferences,
|
|
1908
1909
|
validateCanonicalNativePlan: assertCanonicalNativePlan,
|
|
1909
1910
|
writeCanonicalNativePlan,
|
|
1910
1911
|
stableStringify
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { packageOperationRecognitionForCompiled } = require('./spine/package-operation-seam');
|
|
4
|
+
|
|
5
|
+
// Build-time projection of compiler facts. This record never enters the Native
|
|
6
|
+
// plan, executable source, recipe or provider configuration. Unknown expressions
|
|
7
|
+
// are not evaluated or serialized, and reference resolution is not host binding.
|
|
8
|
+
function collectCanonicalReportReferences(compiled, plan) {
|
|
9
|
+
const raw = compiled.metadata.applicationEntries || plan.routing?.entries || [];
|
|
10
|
+
const entries = new Set(raw.map(row => row.stableId));
|
|
11
|
+
if (!raw.length) entries.add('default');
|
|
12
|
+
const helpers = new Map(), rows = new Map();
|
|
13
|
+
const schemas = new Set((compiled.schema?.registry || plan.schemas?.registry)?.schemas?.map(row => row.id) || []);
|
|
14
|
+
const unique = values => [...new Set(values)].sort();
|
|
15
|
+
function calls(value, stageConsumers) {
|
|
16
|
+
if (!value || typeof value !== 'object') return;
|
|
17
|
+
if (value.kind === 'helper-call' || value.kind === 'pure-helper-call') {
|
|
18
|
+
if (!helpers.has(value.helperId)) helpers.set(value.helperId, new Set());
|
|
19
|
+
for (const entry of stageConsumers || [value.callerEntryId]) helpers.get(value.helperId).add(entry);
|
|
20
|
+
}
|
|
21
|
+
for (const child of Object.values(value)) if (child && typeof child === 'object') {
|
|
22
|
+
if (Array.isArray(child)) child.forEach(row => calls(row, stageConsumers)); else calls(child, stageConsumers);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
for (const body of [plan.entry.body, ...(plan.handlers || []).map(row => row.body)]) calls(body);
|
|
26
|
+
for (const stage of plan.stages || []) calls(stage.body, stage.registrations.map(row => row.entryId));
|
|
27
|
+
// Package provenance comes from the trusted recognition bundle, never a file
|
|
28
|
+
// name, schema name or usage-string guess. Keep it outside executable metadata.
|
|
29
|
+
const packageSources = new Map();
|
|
30
|
+
const referenceKey = row => JSON.stringify([row.id, row.usage, row.capability,
|
|
31
|
+
row.file || compiled.metadata.file || '', row.position?.offset]);
|
|
32
|
+
for (const source of packageOperationRecognitionForCompiled(compiled)?.publicExtensions?.plans || []) {
|
|
33
|
+
for (const reference of source.schemaReferences || []) {
|
|
34
|
+
const key = referenceKey(reference);
|
|
35
|
+
if (!packageSources.has(key)) packageSources.set(key, new Set());
|
|
36
|
+
packageSources.get(key).add(source.package);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function owners(effect) {
|
|
40
|
+
if (effect.helperId) return [...(helpers.get(effect.helperId) || [])];
|
|
41
|
+
const entry = effect.applicationEntryStableId || effect.routerEntryStableId;
|
|
42
|
+
return entry ? [entry] : raw.length ? [] : ['default'];
|
|
43
|
+
}
|
|
44
|
+
function add(kind, canonicalId, bindingKind, consumers, observationId, detail = {}) {
|
|
45
|
+
const state = canonicalId === null ? detail.dynamic ? 'dynamic' : 'unknown' : detail.unresolved ? 'unknown' : 'resolved';
|
|
46
|
+
const key = JSON.stringify([kind, bindingKind, state, state === 'resolved' ? canonicalId : observationId]);
|
|
47
|
+
let row = rows.get(key);
|
|
48
|
+
if (!row) {
|
|
49
|
+
row = { kind, canonicalId, bindingKind, state, observationId: state === 'resolved' ? null : observationId,
|
|
50
|
+
entryIds: [], entriesComplete: true, ...(detail.resource ? { resource: detail.resource } : {}) };
|
|
51
|
+
rows.set(key, row);
|
|
52
|
+
}
|
|
53
|
+
row.entryIds.push(...consumers.filter(id => entries.has(id)));
|
|
54
|
+
row.entriesComplete &&= consumers.length > 0 && consumers.every(id => entries.has(id));
|
|
55
|
+
if (detail.externalPackages?.length) row.externalPackages = unique([...(row.externalPackages || []), ...detail.externalPackages]);
|
|
56
|
+
}
|
|
57
|
+
const literal = value => value?.kind === 'literal' && typeof value.value === 'string' ? value.value : null;
|
|
58
|
+
const input = (effect, name) => effect.inputs?.find(row => row.name === name)?.value;
|
|
59
|
+
const field = (object, name) => object?.kind === 'object'
|
|
60
|
+
? object.entries?.find(row => row.key?.kind === 'literal' && row.key.value === name)?.value : null;
|
|
61
|
+
for (const effect of plan.effects || []) {
|
|
62
|
+
const consumers = owners(effect), kind = effect.providerKind;
|
|
63
|
+
if (['config', 'secret', 'kv', 's3'].includes(kind)) {
|
|
64
|
+
const expression = kind === 's3' ? field(input(effect, 'resource'), 'binding')
|
|
65
|
+
: input(effect, kind === 'kv' ? 'store' : 'name');
|
|
66
|
+
const resolved = literal(expression);
|
|
67
|
+
const name = resolved ?? (effect.resource?.kind === 'literal' && typeof effect.resource.value === 'string' ? effect.resource.value : null);
|
|
68
|
+
add('binding', name, kind, consumers, effect.id, {
|
|
69
|
+
dynamic: effect.resource?.kind === 'dynamic' || Boolean(expression && expression.kind !== 'literal')
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (effect.kind === 'event.emit') {
|
|
73
|
+
const schema = literal(field(input(effect, 'emission'), 'schema'));
|
|
74
|
+
if (schema !== null) add('schema', schema, null, consumers, effect.id, { unresolved: !schemas.has(schema) });
|
|
75
|
+
}
|
|
76
|
+
if (effect.kind !== 'assets.lookup') continue;
|
|
77
|
+
const payload = input(effect, 'payload'), embeddedId = literal(field(payload, 'embeddedId'));
|
|
78
|
+
const key = literal(field(payload, 'key'));
|
|
79
|
+
const found = field(payload, 'embeddedFound')?.value === true;
|
|
80
|
+
const bytes = field(payload, 'embeddedLength')?.value;
|
|
81
|
+
const encoded = literal(field(payload, 'embeddedData'));
|
|
82
|
+
const known = embeddedId !== null && key !== null && found && Number.isSafeInteger(bytes) && bytes >= 0;
|
|
83
|
+
add('resource', embeddedId !== null && key !== null ? embeddedId + ':' + key : null, null, consumers, effect.id, {
|
|
84
|
+
unresolved: !known,
|
|
85
|
+
dynamic: Boolean(field(payload, 'key') && field(payload, 'key').kind !== 'literal'),
|
|
86
|
+
...(known ? { resource: { name: key, mediaType: literal(field(payload, 'embeddedType')), inputBytes: bytes,
|
|
87
|
+
encodedBytes: encoded === null ? null : Buffer.byteLength(encoded, 'utf8') } } : {})
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// These offsets are compiler-generated entry/helper ranges, not a reader's source
|
|
91
|
+
// heuristic. Authored package references from another file cannot borrow an
|
|
92
|
+
// unrelated generated offset. Unassociated references remain observations.
|
|
93
|
+
for (const [index, reference] of (compiled.metadata.schemaReferences || []).entries()) {
|
|
94
|
+
const sameFile = !reference.file || reference.file === compiled.metadata.file;
|
|
95
|
+
const offset = reference.position?.offset;
|
|
96
|
+
const contains = row => row.generatedRange && offset >= row.generatedRange.start && offset < row.generatedRange.end;
|
|
97
|
+
const located = sameFile && Number.isSafeInteger(offset);
|
|
98
|
+
const matches = located ? raw.filter(contains) : [];
|
|
99
|
+
const helperMatches = located ? (plan.routing?.helpers || []).filter(contains) : [];
|
|
100
|
+
const consumers = !raw.length && sameFile ? ['default']
|
|
101
|
+
: matches.length === 1 && !helperMatches.length ? [matches[0].stableId]
|
|
102
|
+
: !matches.length && helperMatches.length === 1 ? [...(helpers.get(helperMatches[0].id) || [])] : [];
|
|
103
|
+
const name = typeof reference.id === 'string' && reference.id ? reference.id : null;
|
|
104
|
+
add('schema', name, null, consumers, 'schema:' + index, { unresolved: !schemas.has(name),
|
|
105
|
+
externalPackages: [...(packageSources.get(referenceKey(reference)) || [])] });
|
|
106
|
+
}
|
|
107
|
+
for (const event of compiled.eventCatalog?.events || []) if (event.schemaId) {
|
|
108
|
+
add('schema', event.schemaId, null, [event.stableId], event.stableId, { unresolved: !schemas.has(event.schemaId) });
|
|
109
|
+
}
|
|
110
|
+
// Only compact allowlisted data crosses into Report. No provider metadata,
|
|
111
|
+
// effect payloads, binding values, resource bodies or expression text survives.
|
|
112
|
+
const routeBehaviors = require('./report-route-behavior').routeBehaviorForMetadata(compiled.metadata.router);
|
|
113
|
+
return Object.freeze({ version: 'pulse.compiler-report-references.v1',
|
|
114
|
+
...(routeBehaviors ? { routeBehaviors } : {}),
|
|
115
|
+
responsePayloads: require('./report-response-payloads').collectResponsePayloads(plan, entries, helpers),
|
|
116
|
+
references: Object.freeze([...rows.values()].map(row => Object.freeze({ ...row, entryIds: Object.freeze(unique(row.entryIds)),
|
|
117
|
+
...(row.externalPackages ? { externalPackages: Object.freeze(row.externalPackages) } : {}),
|
|
118
|
+
...(row.resource ? { resource: Object.freeze(row.resource) } : {}) }))) });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = { collectCanonicalReportReferences };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Observational projection of canonical text-response sites, after emission.
|
|
4
|
+
// Never evaluate application expressions or expose literal contents/hashes.
|
|
5
|
+
function collectResponsePayloads(plan, entryIds, helperConsumers) {
|
|
6
|
+
const sites = [];
|
|
7
|
+
const unique = values => [...new Set(values)].sort();
|
|
8
|
+
const literal = value => value?.kind === 'literal' && typeof value.value === 'string' ? value.value : null;
|
|
9
|
+
const objectFields = value => value?.kind === 'object' && value.entries.every(row => row.kind === 'property'
|
|
10
|
+
&& row.key?.kind === 'literal' && typeof row.key.value === 'string') ? value.entries : null;
|
|
11
|
+
const field = (rows, key) => rows?.filter(row => row.key.value === key).at(-1)?.value;
|
|
12
|
+
function mediaType(options) {
|
|
13
|
+
if (!options || options.kind === 'undefined') return 'text/plain';
|
|
14
|
+
const fields = objectFields(options);
|
|
15
|
+
if (!fields) return null;
|
|
16
|
+
const headers = field(fields, 'headers');
|
|
17
|
+
if (!headers) return 'text/plain';
|
|
18
|
+
const values = objectFields(headers);
|
|
19
|
+
if (!values) return null;
|
|
20
|
+
const types = values.filter(row => row.key.value.toLowerCase() === 'content-type');
|
|
21
|
+
if (!types.length) return 'text/plain';
|
|
22
|
+
// Case variants can be merged by the runtime header adapter; do not guess.
|
|
23
|
+
if (types.length !== 1) return null;
|
|
24
|
+
const type = literal(types[0].value)?.split(';')[0].trim().toLowerCase();
|
|
25
|
+
return ['text/plain', 'text/css', 'text/html', 'text/javascript', 'application/javascript'].includes(type) ? type : null;
|
|
26
|
+
}
|
|
27
|
+
function scan(body, owner, consumers) {
|
|
28
|
+
const constants = new Map();
|
|
29
|
+
function visit(value, fn, path = 'body') {
|
|
30
|
+
if (!value || typeof value !== 'object') return;
|
|
31
|
+
fn(value, path);
|
|
32
|
+
for (const [key, child] of Object.entries(value)) if (child && typeof child === 'object') {
|
|
33
|
+
if (Array.isArray(child)) child.forEach((row, i) => visit(row, fn, path + '.' + key + '[' + i + ']'));
|
|
34
|
+
else visit(child, fn, path + '.' + key);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
visit(body, node => {
|
|
38
|
+
if (node.kind === 'local' && node.declaration === 'const' && literal(node.value) !== null)
|
|
39
|
+
constants.set(node.localId, node.value.value);
|
|
40
|
+
});
|
|
41
|
+
visit(body, (node, path) => {
|
|
42
|
+
if (node.kind !== 'intrinsic' || node.name !== 'response.text') return;
|
|
43
|
+
const input = node.arguments[0];
|
|
44
|
+
const value = literal(input) ?? (input?.kind === 'local' ? constants.get(input.id) : null);
|
|
45
|
+
const known = typeof value === 'string';
|
|
46
|
+
const state = known ? 'resolved' : !input || ['literal', 'undefined'].includes(input.kind) ? 'unknown' : 'dynamic';
|
|
47
|
+
const valid = unique(consumers.filter(id => entryIds.has(id)));
|
|
48
|
+
sites.push(Object.freeze({ canonicalId: JSON.stringify([owner, path]),
|
|
49
|
+
state, inputBytes: known ? Buffer.byteLength(value, 'utf8') : null,
|
|
50
|
+
mediaType: mediaType(node.arguments[1]), entryIds: Object.freeze(valid),
|
|
51
|
+
entriesComplete: consumers.length > 0 && consumers.every(id => entryIds.has(id)) }));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
scan(plan.entry.body, 'entry', plan.entry.kind === 'router' ? [] : ['default']);
|
|
55
|
+
for (const row of plan.handlers || []) scan(row.body, 'handler:' + row.id, [row.id]);
|
|
56
|
+
for (const row of plan.stages || []) scan(row.body, 'stage:' + row.id, row.registrations.map(item => item.entryId));
|
|
57
|
+
for (const row of plan.helpers || []) scan(row.body, 'helper:' + row.id, [...(helperConsumers.get(row.id) || [])]);
|
|
58
|
+
sites.sort((a, b) => a.canonicalId < b.canonicalId ? -1 : a.canonicalId > b.canonicalId ? 1 : 0);
|
|
59
|
+
return Object.freeze({ version: 'pulse.report-text-responses.v1', sites: Object.freeze(sites) });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = { collectResponsePayloads };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Observational only: keep IR facts out of serialized compiler metadata, source
|
|
4
|
+
// and plan hashes. A deserialized/foreign metadata object supplies no evidence.
|
|
5
|
+
const records = new WeakMap();
|
|
6
|
+
function retainRouteBehavior(metadata, handlers) {
|
|
7
|
+
const routes = new Set(metadata.entries.filter(row => row.kind === 'route').map(row => row.stableId));
|
|
8
|
+
records.set(metadata, Object.freeze(handlers.filter(row => routes.has(row.entryStableId)).map(row => Object.freeze({
|
|
9
|
+
entryId: row.entryStableId,
|
|
10
|
+
kind: row.operationIr.summary.operationKinds['router-transfer'] ? 'continuing' : 'terminal',
|
|
11
|
+
basis: 'canonical-handler-ir'
|
|
12
|
+
}))));
|
|
13
|
+
}
|
|
14
|
+
function routeBehaviorForMetadata(metadata) { return records.get(metadata); }
|
|
15
|
+
module.exports = { retainRouteBehavior, routeBehaviorForMetadata };
|
|
@@ -118,6 +118,7 @@ function executeCanonicalRouterSpine(sourceText, options = {}) {
|
|
|
118
118
|
if (output && typeof output === 'object') {
|
|
119
119
|
traces.set(output, result.trace);
|
|
120
120
|
handlerIrBundles.set(output, bundle.handlers);
|
|
121
|
+
require('../report-route-behavior').retainRouteBehavior(output.metadata, bundle.handlers);
|
|
121
122
|
surfaceFacts.set(output, Object.freeze({
|
|
122
123
|
recognition: bundle.recognition,
|
|
123
124
|
classification: bundle.classification,
|