@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
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { PACKAGE_VERSION } = require('../diagnostics.js');
|
|
4
|
+
|
|
5
|
+
const API_SURFACE_PHASE = '12C';
|
|
6
|
+
const API_SURFACE_VERSION = 'pulsewasm.api-surface.v1';
|
|
7
|
+
const LIFECYCLE_ORDERING_VERSION = 'pulsewasm.lifecycle-ordering.v1';
|
|
8
|
+
const API_SURFACE_POLICY = {
|
|
9
|
+
driftVisibleByArtifact: true,
|
|
10
|
+
canonicalSurfaceExplicit: true,
|
|
11
|
+
reservedAliasesRecorded: true,
|
|
12
|
+
librarySurfacesRequireContracts: true,
|
|
13
|
+
lifecycleDefaultOrdering: 'serial-source-order'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const API_SURFACE_DEFINITIONS = [
|
|
17
|
+
{ symbol: 'ctx.param', kind: 'ctx-dsl', status: 'implemented-compiled-handler', canonical: true, phase: '12A', notes: 'Trusted param DSL lowered to runtime-core helpers.' },
|
|
18
|
+
{ symbol: 'ctx.result.text', kind: 'ctx-dsl', status: 'implemented-compiled-handler', canonical: true, phase: '12A', notes: 'Text result builder.' },
|
|
19
|
+
{ symbol: 'ctx.result.jsonText', kind: 'ctx-dsl', status: 'implemented-compiled-handler', canonical: true, phase: '12A.1', notes: 'JSON text result builder; schema parsing remains sidecar path.' },
|
|
20
|
+
{ symbol: 'ctx.request.header.*', kind: 'ctx-dsl', status: 'implemented-compiled-handler', canonical: true, phase: '12A.1', notes: 'Read-only ordered request header access.' },
|
|
21
|
+
{ symbol: 'ctx.response.header.*', kind: 'ctx-dsl', status: 'implemented-compiled-handler', canonical: true, phase: '12A.1', notes: 'Mutable ordered response header access.' },
|
|
22
|
+
{ symbol: 'ctx.fetch', kind: 'effect', status: 'planned-route-effect', canonical: true, phase: '30', notes: 'Declared backend-fetch effect; Pass 30 can plan ctx.resolve lifecycle, execution reserved until provider/runtime proof.' },
|
|
23
|
+
{ symbol: 'ctx.sleep', kind: 'effect', status: 'contracted-proof-target', canonical: true, phase: '11F/11G', notes: 'Sleep is the first explicit effect proof target; execution not implemented yet.' },
|
|
24
|
+
{ symbol: 'ctx.resolve', kind: 'effect-composition', status: 'planned-route-effect', canonical: true, phase: '30', notes: 'Effect submission boundary with named top-level continuation; no promise/microtask semantics.' },
|
|
25
|
+
{ symbol: 'ctx.resolved', kind: 'effect-continuation', status: 'planned-route-effect', canonical: true, phase: '30', notes: 'Continuation-only resolved fetch response handle accessor for ctx.resolve groups.' },
|
|
26
|
+
{ symbol: 'ctx.req.parse', kind: 'body-parser', status: 'contracted-reserved', canonical: true, phase: '12B.1', notes: 'Canonical parser surface; schema id selects codec/sidecar.' },
|
|
27
|
+
{ symbol: 'ctx.req.json', kind: 'body-parser-sugar', status: 'reserved-alias', canonical: false, canonicalTarget: 'ctx.req.parse', phase: '12B.1', notes: 'JSON-only sugar; not the root API.' },
|
|
28
|
+
{ symbol: 'ctx.<extension>.*', kind: 'library-ctx-extension', status: 'library-declared', canonical: true, phase: '12C', notes: 'Only valid when declared by pulse.library.json, installed in scope, and lowered by AS sidecar.' },
|
|
29
|
+
{ symbol: 'grip.hold/channel/publish', kind: 'package-owned-lowerable-facade', status: 'package-owned-plan-only', canonical: true, phase: '54', notes: '@pulse-compute/grip now ships its own PulseWasm manifest, lowerable facade, compiler builder, and sidecar declaration. Provider runtime behavior remains plan-only.' },
|
|
30
|
+
{ symbol: 'app.timeout', kind: 'router-metadata', status: 'implemented-contract', canonical: true, phase: '11G', notes: 'Static scope timeout metadata, not middleware behavior.' },
|
|
31
|
+
{ symbol: 'lifecycle.connect/disconnect', kind: 'lifecycle', status: 'serial-source-order-locked', canonical: true, phase: '12C', notes: 'Lifecycle handlers execute serially in source/scope order. Parallel lifecycle execution is reserved.' }
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
function defaultApiSurfaces() {
|
|
35
|
+
return API_SURFACE_DEFINITIONS.map((surface) => ({ ...surface }));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function summarizeApiSurfaces(surfaces) {
|
|
39
|
+
return {
|
|
40
|
+
surfaces: surfaces.length,
|
|
41
|
+
implemented: surfaces.filter((surface) => String(surface.status).startsWith('implemented')).length,
|
|
42
|
+
contractedReserved: surfaces.filter((surface) => surface.status.includes('reserved')).length,
|
|
43
|
+
libraryDeclared: surfaces.filter((surface) => surface.kind === 'library-ctx-extension').length,
|
|
44
|
+
lifecycleSerial: true
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function buildApiSurfaceContract(inputs = {}) {
|
|
49
|
+
const surfaces = defaultApiSurfaces();
|
|
50
|
+
return {
|
|
51
|
+
version: API_SURFACE_VERSION,
|
|
52
|
+
generatedBy: inputs.generatedBy || PACKAGE_VERSION,
|
|
53
|
+
phase: inputs.phase || API_SURFACE_PHASE,
|
|
54
|
+
status: 'locked',
|
|
55
|
+
policy: { ...API_SURFACE_POLICY },
|
|
56
|
+
summary: summarizeApiSurfaces(surfaces),
|
|
57
|
+
surfaces
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function buildLifecycleOrderingContract(executionPlan, inputs = {}) {
|
|
62
|
+
const scopes = Array.isArray(executionPlan?.scopes) ? executionPlan.scopes : [];
|
|
63
|
+
const lifecycleLinks = scopes.flatMap((scope) => [
|
|
64
|
+
...(scope.connect || []).map((handler, index) => ({ scopeId: scope.id, event: 'connect', order: index, handlerId: handler.handlerId || handler.id, name: handler.name })),
|
|
65
|
+
...(scope.disconnect || []).map((handler, index) => ({ scopeId: scope.id, event: 'disconnect', order: index, handlerId: handler.handlerId || handler.id, name: handler.name }))
|
|
66
|
+
]);
|
|
67
|
+
return {
|
|
68
|
+
version: LIFECYCLE_ORDERING_VERSION,
|
|
69
|
+
generatedBy: inputs.generatedBy || PACKAGE_VERSION,
|
|
70
|
+
phase: inputs.phase || API_SURFACE_PHASE,
|
|
71
|
+
status: 'locked',
|
|
72
|
+
policy: {
|
|
73
|
+
defaultOrdering: 'serial-source-order',
|
|
74
|
+
parallelLifecycleExecution: 'reserved-explicit-only',
|
|
75
|
+
hiddenScheduler: false,
|
|
76
|
+
promiseSemantics: false
|
|
77
|
+
},
|
|
78
|
+
summary: {
|
|
79
|
+
scopes: scopes.length,
|
|
80
|
+
lifecycleLinks: lifecycleLinks.length,
|
|
81
|
+
connectHandlers: lifecycleLinks.filter((link) => link.event === 'connect').length,
|
|
82
|
+
disconnectHandlers: lifecycleLinks.filter((link) => link.event === 'disconnect').length,
|
|
83
|
+
serialByDefault: true
|
|
84
|
+
},
|
|
85
|
+
lifecycleLinks
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = {
|
|
90
|
+
API_SURFACE_PHASE,
|
|
91
|
+
API_SURFACE_VERSION,
|
|
92
|
+
LIFECYCLE_ORDERING_VERSION,
|
|
93
|
+
API_SURFACE_POLICY,
|
|
94
|
+
API_SURFACE_DEFINITIONS,
|
|
95
|
+
defaultApiSurfaces,
|
|
96
|
+
summarizeApiSurfaces,
|
|
97
|
+
buildApiSurfaceContract,
|
|
98
|
+
buildLifecycleOrderingContract
|
|
99
|
+
};
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { normalizeDiagnostic } = require('../diagnostics.js');
|
|
4
|
+
const {
|
|
5
|
+
LOWERABLE_LIBRARY_MANIFEST_VERSION,
|
|
6
|
+
LOWERABLE_LIBRARY_MANIFEST_KIND,
|
|
7
|
+
clone,
|
|
8
|
+
validateLowerableLibraryManifest
|
|
9
|
+
} = require('./manifest.js');
|
|
10
|
+
|
|
11
|
+
const LIBRARY_CONTRACTS_PHASE = '11D';
|
|
12
|
+
const HANDLER_LIBRARY_CONTRACTS_PHASE = LIBRARY_CONTRACTS_PHASE;
|
|
13
|
+
const HANDLER_LIBRARY_CONTRACTS_VERSION = 'pulsewasm.handler-library-contracts.v1';
|
|
14
|
+
const HANDLER_EXECUTION_MODES_VERSION = 'pulsewasm.handler-execution-modes.v1';
|
|
15
|
+
const LIBRARY_CONTRACT_SCHEMA_VERSION = 'pulsewasm.library-contract-schema.v1';
|
|
16
|
+
const LIBRARY_CONTRACT_VERSION = 'pulsewasm.library-contract.v1';
|
|
17
|
+
const LIBRARY_CAPABILITIES_VERSION = 'pulsewasm.library-capabilities.v1';
|
|
18
|
+
const LIBRARY_COMPATIBILITY_REPORT_VERSION = 'pulsewasm.library-compatibility-report.v1';
|
|
19
|
+
|
|
20
|
+
const ALLOWED_HANDLER_EXECUTION_MODES = ['host-imports', 'compiled-wasm', 'js-engine'];
|
|
21
|
+
const ALLOWED_LIBRARY_MODES = ['typescript', 'js-engine', 'wasm-sidecar'];
|
|
22
|
+
const ALLOWED_HOST_CAPABILITIES = [
|
|
23
|
+
'headers',
|
|
24
|
+
'broadcaster',
|
|
25
|
+
'clock',
|
|
26
|
+
'assets',
|
|
27
|
+
'json',
|
|
28
|
+
'body',
|
|
29
|
+
'backend-fetch',
|
|
30
|
+
'state',
|
|
31
|
+
'params',
|
|
32
|
+
'result',
|
|
33
|
+
'error',
|
|
34
|
+
'lifecycle',
|
|
35
|
+
'channel'
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
function makeDiagnostic(code, message, hint, details) {
|
|
39
|
+
return normalizeDiagnostic({
|
|
40
|
+
phase: 'handler-library-contracts',
|
|
41
|
+
severity: 'error',
|
|
42
|
+
code,
|
|
43
|
+
message,
|
|
44
|
+
hint,
|
|
45
|
+
details,
|
|
46
|
+
loc: { file: '<library-contract>' }
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function sortedUnique(values) {
|
|
51
|
+
return Array.from(new Set((values || []).filter(Boolean))).sort();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function defaultLibraryContracts() {
|
|
55
|
+
return [
|
|
56
|
+
{
|
|
57
|
+
version: LIBRARY_CONTRACT_VERSION,
|
|
58
|
+
package: 'pulse.grip',
|
|
59
|
+
description: 'Contract fixture for GRIP/Fanout-style header and channel control-plane helpers.',
|
|
60
|
+
modes: {
|
|
61
|
+
typescript: { entry: './dist/index.js' },
|
|
62
|
+
jsEngine: { entry: './dist/index.js' },
|
|
63
|
+
wasm: {
|
|
64
|
+
mode: 'wasm-sidecar',
|
|
65
|
+
sidecar: './as/index.as.ts',
|
|
66
|
+
lowerings: [
|
|
67
|
+
{ tsSymbol: 'grip.hold', asSymbol: 'pulse_grip_hold', callShape: 'literal-mode', hostCapabilities: ['headers'] },
|
|
68
|
+
{ tsSymbol: 'grip.channel', asSymbol: 'pulse_grip_channel', callShape: 'literal-channel', hostCapabilities: ['headers', 'channel', 'broadcaster'] },
|
|
69
|
+
{ tsSymbol: 'grip.publish', asSymbol: 'pulse_grip_publish', callShape: 'declared-channel-message', hostCapabilities: ['broadcaster', 'clock'] }
|
|
70
|
+
],
|
|
71
|
+
hostCapabilities: ['headers', 'channel', 'broadcaster', 'clock']
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
policy: {
|
|
75
|
+
inferredCompatibility: false,
|
|
76
|
+
arbitraryTsCompilation: false,
|
|
77
|
+
sidecarRequiredForWasm: true,
|
|
78
|
+
effectRuntimeRequired: false,
|
|
79
|
+
driftRequiresParityTests: true
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function libraryContractFromLowerableManifest(manifest, options = {}) {
|
|
86
|
+
const validation = validateLowerableLibraryManifest(manifest);
|
|
87
|
+
const normalized = validation.manifest;
|
|
88
|
+
const wasm = normalized.modes.wasm;
|
|
89
|
+
const facadeImport = normalized.facade.import || normalized.lowerableSubpath;
|
|
90
|
+
const publicApiPackage = normalized.publicApi.package || normalized.npmPackage;
|
|
91
|
+
const policy = {
|
|
92
|
+
inferredCompatibility: false,
|
|
93
|
+
arbitraryTsCompilation: false,
|
|
94
|
+
sidecarRequiredForWasm: true,
|
|
95
|
+
effectRuntimeRequired: true,
|
|
96
|
+
driftRequiresParityTests: true,
|
|
97
|
+
...(normalized.policy || {})
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
version: options.libraryContractVersion || LIBRARY_CONTRACT_VERSION,
|
|
102
|
+
package: normalized.contractId,
|
|
103
|
+
npmPackage: normalized.npmPackage,
|
|
104
|
+
lowerableSubpath: normalized.lowerableSubpath,
|
|
105
|
+
manifest: {
|
|
106
|
+
version: normalized.version || LOWERABLE_LIBRARY_MANIFEST_VERSION,
|
|
107
|
+
kind: normalized.kind || LOWERABLE_LIBRARY_MANIFEST_KIND,
|
|
108
|
+
owner: 'package'
|
|
109
|
+
},
|
|
110
|
+
facade: {
|
|
111
|
+
namespace: normalized.facade.namespace,
|
|
112
|
+
import: facadeImport,
|
|
113
|
+
symbols: Array.isArray(normalized.facade.symbols) ? normalized.facade.symbols.slice() : []
|
|
114
|
+
},
|
|
115
|
+
publicApi: {
|
|
116
|
+
package: publicApiPackage,
|
|
117
|
+
symbols: Array.isArray(normalized.publicApi.symbols) ? normalized.publicApi.symbols.slice() : [],
|
|
118
|
+
loweringIntentionallyNarrow: Boolean(normalized.publicApi.loweringIntentionallyNarrow)
|
|
119
|
+
},
|
|
120
|
+
compiler: normalized.compiler ? clone(normalized.compiler) : undefined,
|
|
121
|
+
description: normalized.description || `Package-owned lowerable library manifest for ${normalized.contractId}.`,
|
|
122
|
+
modes: {
|
|
123
|
+
typescript: clone(normalized.modes.typescript),
|
|
124
|
+
jsEngine: clone(normalized.modes.jsEngine),
|
|
125
|
+
wasm: {
|
|
126
|
+
mode: wasm.mode || 'wasm-sidecar',
|
|
127
|
+
sidecar: wasm.sidecar,
|
|
128
|
+
lowerings: (wasm.lowerings || []).map((lowering) => ({
|
|
129
|
+
...lowering,
|
|
130
|
+
hostCapabilities: Array.isArray(lowering.hostCapabilities) ? lowering.hostCapabilities.slice() : []
|
|
131
|
+
})),
|
|
132
|
+
hostCapabilities: Array.isArray(wasm.hostCapabilities) ? wasm.hostCapabilities.slice() : []
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
policy,
|
|
136
|
+
diagnostics: validation.diagnostics
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function normalizeContractModeKeys(modes) {
|
|
141
|
+
const out = {};
|
|
142
|
+
if (modes && typeof modes === 'object') {
|
|
143
|
+
if (modes.typescript) out.typescript = modes.typescript;
|
|
144
|
+
if (modes.jsEngine || modes.js_engine || modes['js-engine']) out.jsEngine = modes.jsEngine || modes.js_engine || modes['js-engine'];
|
|
145
|
+
if (modes.wasm || modes.wasmSidecar || modes['wasm-sidecar']) out.wasm = modes.wasm || modes.wasmSidecar || modes['wasm-sidecar'];
|
|
146
|
+
}
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function validateLibraryContract(contract) {
|
|
151
|
+
const diagnostics = [];
|
|
152
|
+
const normalized = contract && typeof contract === 'object' ? contract : {};
|
|
153
|
+
const modes = normalizeContractModeKeys(normalized.modes);
|
|
154
|
+
const wasm = modes.wasm || {};
|
|
155
|
+
const lowerings = Array.isArray(wasm.lowerings) ? wasm.lowerings : [];
|
|
156
|
+
const hostCapabilities = sortedUnique([...(wasm.hostCapabilities || []), ...lowerings.flatMap((lowering) => lowering.hostCapabilities || [])]);
|
|
157
|
+
|
|
158
|
+
if (normalized.version !== LIBRARY_CONTRACT_VERSION) {
|
|
159
|
+
diagnostics.push(makeDiagnostic(
|
|
160
|
+
'PULSEWASM_LIBRARY_CONTRACT_VERSION_UNSUPPORTED',
|
|
161
|
+
`Library contract ${normalized.package || '<unknown>'} must use ${LIBRARY_CONTRACT_VERSION}.`,
|
|
162
|
+
'Update pulse.library.json to the locked v1 contract version.',
|
|
163
|
+
{ version: normalized.version }
|
|
164
|
+
));
|
|
165
|
+
}
|
|
166
|
+
if (!normalized.package || typeof normalized.package !== 'string') {
|
|
167
|
+
diagnostics.push(makeDiagnostic(
|
|
168
|
+
'PULSEWASM_LIBRARY_CONTRACT_PACKAGE_REQUIRED',
|
|
169
|
+
'Library contracts must declare a static package name.',
|
|
170
|
+
'Add "package": "your.package" to pulse.library.json.',
|
|
171
|
+
{ package: normalized.package }
|
|
172
|
+
));
|
|
173
|
+
}
|
|
174
|
+
if (!modes.typescript || typeof modes.typescript.entry !== 'string') {
|
|
175
|
+
diagnostics.push(makeDiagnostic(
|
|
176
|
+
'PULSEWASM_LIBRARY_TYPESCRIPT_ENTRY_REQUIRED',
|
|
177
|
+
`Library ${normalized.package || '<unknown>'} must declare a TypeScript entry.`,
|
|
178
|
+
'Declare modes.typescript.entry for normal TS/dev ergonomics.',
|
|
179
|
+
{ package: normalized.package }
|
|
180
|
+
));
|
|
181
|
+
}
|
|
182
|
+
if (!modes.jsEngine || typeof modes.jsEngine.entry !== 'string') {
|
|
183
|
+
diagnostics.push(makeDiagnostic(
|
|
184
|
+
'PULSEWASM_LIBRARY_JS_ENGINE_ENTRY_REQUIRED',
|
|
185
|
+
`Library ${normalized.package || '<unknown>'} must declare a JS engine entry.`,
|
|
186
|
+
'Declare modes.jsEngine.entry for the manual JS escape hatch path.',
|
|
187
|
+
{ package: normalized.package }
|
|
188
|
+
));
|
|
189
|
+
}
|
|
190
|
+
if (!wasm || typeof wasm.sidecar !== 'string') {
|
|
191
|
+
diagnostics.push(makeDiagnostic(
|
|
192
|
+
'PULSEWASM_LIBRARY_WASM_SIDECAR_REQUIRED',
|
|
193
|
+
`Library ${normalized.package || '<unknown>'} must declare a Wasm sidecar for Wasm compatibility.`,
|
|
194
|
+
'Declare modes.wasm.sidecar, or do not claim Wasm-sidecar compatibility.',
|
|
195
|
+
{ package: normalized.package }
|
|
196
|
+
));
|
|
197
|
+
}
|
|
198
|
+
if (!Array.isArray(wasm.lowerings) || wasm.lowerings.length === 0) {
|
|
199
|
+
diagnostics.push(makeDiagnostic(
|
|
200
|
+
'PULSEWASM_LIBRARY_LOWERINGS_REQUIRED',
|
|
201
|
+
`Library ${normalized.package || '<unknown>'} must declare at least one TS-to-AS lowering.`,
|
|
202
|
+
'Declare modes.wasm.lowerings with tsSymbol and asSymbol entries.',
|
|
203
|
+
{ package: normalized.package }
|
|
204
|
+
));
|
|
205
|
+
}
|
|
206
|
+
for (const lowering of lowerings) {
|
|
207
|
+
if (!lowering || typeof lowering.tsSymbol !== 'string' || typeof lowering.asSymbol !== 'string') {
|
|
208
|
+
diagnostics.push(makeDiagnostic(
|
|
209
|
+
'PULSEWASM_LIBRARY_LOWERING_INVALID',
|
|
210
|
+
`Library ${normalized.package || '<unknown>'} declares an invalid lowering.`,
|
|
211
|
+
'Each lowering must include static tsSymbol and asSymbol strings.',
|
|
212
|
+
{ package: normalized.package, lowering }
|
|
213
|
+
));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
for (const capability of hostCapabilities) {
|
|
217
|
+
if (!ALLOWED_HOST_CAPABILITIES.includes(capability)) {
|
|
218
|
+
diagnostics.push(makeDiagnostic(
|
|
219
|
+
'PULSEWASM_LIBRARY_UNKNOWN_HOST_CAPABILITY',
|
|
220
|
+
`Library ${normalized.package || '<unknown>'} requires unknown host capability ${JSON.stringify(capability)}.`,
|
|
221
|
+
`Use one of: ${ALLOWED_HOST_CAPABILITIES.join(', ')}.`,
|
|
222
|
+
{ package: normalized.package, capability }
|
|
223
|
+
));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
package: normalized.package || '<unknown>',
|
|
229
|
+
status: diagnostics.length === 0 ? 'ok' : 'error',
|
|
230
|
+
modes: {
|
|
231
|
+
typescript: Boolean(modes.typescript),
|
|
232
|
+
jsEngine: Boolean(modes.jsEngine),
|
|
233
|
+
wasmSidecar: Boolean(wasm && wasm.sidecar)
|
|
234
|
+
},
|
|
235
|
+
sidecar: wasm.sidecar,
|
|
236
|
+
lowerings: lowerings.map((lowering) => ({
|
|
237
|
+
tsSymbol: lowering.tsSymbol,
|
|
238
|
+
asSymbol: lowering.asSymbol,
|
|
239
|
+
callShape: lowering.callShape || 'unspecified',
|
|
240
|
+
hostCapabilities: sortedUnique(lowering.hostCapabilities || [])
|
|
241
|
+
})),
|
|
242
|
+
hostCapabilities,
|
|
243
|
+
policy: {
|
|
244
|
+
inferredCompatibility: false,
|
|
245
|
+
arbitraryTsCompilation: false,
|
|
246
|
+
sidecarRequiredForWasm: true,
|
|
247
|
+
contractRequiredForWasm: true,
|
|
248
|
+
debtRecordedInJsEngineMode: true,
|
|
249
|
+
effectRuntimeRequired: Boolean(normalized.policy && normalized.policy.effectRuntimeRequired)
|
|
250
|
+
},
|
|
251
|
+
diagnostics: [...(normalized.diagnostics || []), ...diagnostics]
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
module.exports = {
|
|
256
|
+
LIBRARY_CONTRACTS_PHASE,
|
|
257
|
+
HANDLER_LIBRARY_CONTRACTS_PHASE,
|
|
258
|
+
HANDLER_LIBRARY_CONTRACTS_VERSION,
|
|
259
|
+
HANDLER_EXECUTION_MODES_VERSION,
|
|
260
|
+
LIBRARY_CONTRACT_SCHEMA_VERSION,
|
|
261
|
+
LIBRARY_CONTRACT_VERSION,
|
|
262
|
+
LIBRARY_CAPABILITIES_VERSION,
|
|
263
|
+
LIBRARY_COMPATIBILITY_REPORT_VERSION,
|
|
264
|
+
ALLOWED_HANDLER_EXECUTION_MODES,
|
|
265
|
+
ALLOWED_LIBRARY_MODES,
|
|
266
|
+
ALLOWED_HOST_CAPABILITIES,
|
|
267
|
+
defaultLibraryContracts,
|
|
268
|
+
libraryContractFromLowerableManifest,
|
|
269
|
+
normalizeContractModeKeys,
|
|
270
|
+
normalizeLibraryContractModeKeys: normalizeContractModeKeys,
|
|
271
|
+
sortedUnique,
|
|
272
|
+
validateLibraryContract
|
|
273
|
+
};
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { normalizeDiagnostic } = require('../diagnostics.js');
|
|
4
|
+
|
|
5
|
+
const LOWERABLE_LIBRARY_MANIFEST_VERSION = 'pulsewasm.lowerable-library-manifest.v2';
|
|
6
|
+
const LOWERABLE_LIBRARY_MANIFEST_KIND = 'pulsewasm.lowerable-library-manifest';
|
|
7
|
+
|
|
8
|
+
const LOWERABLE_LIBRARY_PAYLOAD_MODE_STATUSES = Object.freeze([
|
|
9
|
+
'implemented-now',
|
|
10
|
+
'lowering-plan-only',
|
|
11
|
+
'reserved-with-diagnostic',
|
|
12
|
+
'provider-specific',
|
|
13
|
+
'unsupported'
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
const LOWERABLE_LIBRARY_MANIFEST_REQUIRED_FIELDS = Object.freeze([
|
|
17
|
+
'version',
|
|
18
|
+
'contractId',
|
|
19
|
+
'npmPackage',
|
|
20
|
+
'lowerableSubpath',
|
|
21
|
+
'facade.namespace',
|
|
22
|
+
'facade.symbols',
|
|
23
|
+
'modes.typescript.entry',
|
|
24
|
+
'modes.jsEngine.entry',
|
|
25
|
+
'modes.wasm.sidecar',
|
|
26
|
+
'modes.wasm.lowerings',
|
|
27
|
+
'compiler.version',
|
|
28
|
+
'compiler.entry',
|
|
29
|
+
'compiler.export',
|
|
30
|
+
'compiler.builderOwner',
|
|
31
|
+
'compiler.trust'
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
const LOWERABLE_LIBRARY_MANIFEST_POLICY = Object.freeze({
|
|
35
|
+
packageOwnsIdentity: true,
|
|
36
|
+
packageOwnsFacadeMapping: true,
|
|
37
|
+
contractsOwnSchema: true,
|
|
38
|
+
contractsOwnPayloadStatusVocabulary: true,
|
|
39
|
+
compilerMayValidateButNotDefinePackageRules: true,
|
|
40
|
+
packageOwnsCompilerBuilder: true,
|
|
41
|
+
genericLoaderMayExecuteTrustedFirstPartyBuilders: true,
|
|
42
|
+
thirdPartyManifestsRequireCorePr: false
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
function clone(value) {
|
|
46
|
+
if (Array.isArray(value)) return value.map(clone);
|
|
47
|
+
if (value && typeof value === 'object') {
|
|
48
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, clone(entry)]));
|
|
49
|
+
}
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isNonEmptyString(value) {
|
|
54
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function sortedUnique(values) {
|
|
58
|
+
return Array.from(new Set((values || []).filter(Boolean))).sort();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function makeDiagnostic(code, message, hint, details) {
|
|
62
|
+
return normalizeDiagnostic({
|
|
63
|
+
phase: 'lowerable-library-manifest',
|
|
64
|
+
severity: 'error',
|
|
65
|
+
code,
|
|
66
|
+
message,
|
|
67
|
+
hint,
|
|
68
|
+
details,
|
|
69
|
+
loc: { file: '<lowerable-library-manifest>' }
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function normalizeManifest(manifest) {
|
|
74
|
+
const normalized = manifest && typeof manifest === 'object' ? manifest : {};
|
|
75
|
+
const facade = normalized.facade && typeof normalized.facade === 'object' ? normalized.facade : {};
|
|
76
|
+
const publicApi = normalized.publicApi && typeof normalized.publicApi === 'object' ? normalized.publicApi : {};
|
|
77
|
+
const modes = normalized.modes && typeof normalized.modes === 'object' ? normalized.modes : {};
|
|
78
|
+
const wasm = modes.wasm && typeof modes.wasm === 'object' ? modes.wasm : {};
|
|
79
|
+
const compiler = normalized.compiler && typeof normalized.compiler === 'object' ? normalized.compiler : {};
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
...normalized,
|
|
83
|
+
kind: normalized.kind || LOWERABLE_LIBRARY_MANIFEST_KIND,
|
|
84
|
+
facade,
|
|
85
|
+
publicApi,
|
|
86
|
+
compiler: {
|
|
87
|
+
version: compiler.version,
|
|
88
|
+
entry: compiler.entry,
|
|
89
|
+
export: compiler.export,
|
|
90
|
+
artifact: compiler.artifact,
|
|
91
|
+
builderOwner: compiler.builderOwner,
|
|
92
|
+
trust: compiler.trust,
|
|
93
|
+
inputs: Array.isArray(compiler.inputs) ? compiler.inputs : []
|
|
94
|
+
},
|
|
95
|
+
modes: {
|
|
96
|
+
typescript: modes.typescript || {},
|
|
97
|
+
jsEngine: modes.jsEngine || modes.js_engine || modes['js-engine'] || {},
|
|
98
|
+
wasm: {
|
|
99
|
+
...wasm,
|
|
100
|
+
lowerings: Array.isArray(wasm.lowerings) ? wasm.lowerings : [],
|
|
101
|
+
hostCapabilities: Array.isArray(wasm.hostCapabilities) ? wasm.hostCapabilities : []
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function validateLowerableLibraryManifest(manifest) {
|
|
108
|
+
const diagnostics = [];
|
|
109
|
+
const normalized = normalizeManifest(manifest);
|
|
110
|
+
const facadeSymbols = normalized.facade.symbols;
|
|
111
|
+
const publicApiSymbols = normalized.publicApi.symbols;
|
|
112
|
+
const wasm = normalized.modes.wasm;
|
|
113
|
+
const compiler = normalized.compiler || {};
|
|
114
|
+
|
|
115
|
+
if (normalized.version !== LOWERABLE_LIBRARY_MANIFEST_VERSION) {
|
|
116
|
+
diagnostics.push(makeDiagnostic(
|
|
117
|
+
'PULSEWASM_LOWERABLE_LIBRARY_MANIFEST_VERSION_UNSUPPORTED',
|
|
118
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} must use ${LOWERABLE_LIBRARY_MANIFEST_VERSION}.`,
|
|
119
|
+
'Import or copy the locked manifest version for the package-owned manifest declaration.',
|
|
120
|
+
{ version: normalized.version }
|
|
121
|
+
));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!isNonEmptyString(normalized.contractId)) {
|
|
125
|
+
diagnostics.push(makeDiagnostic(
|
|
126
|
+
'PULSEWASM_LOWERABLE_LIBRARY_CONTRACT_ID_REQUIRED',
|
|
127
|
+
'Lowerable library manifests must declare a stable contractId.',
|
|
128
|
+
'Add a package-owned contractId such as "vendor.feature" or a blessed platform contract id.',
|
|
129
|
+
{ contractId: normalized.contractId }
|
|
130
|
+
));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!isNonEmptyString(normalized.npmPackage)) {
|
|
134
|
+
diagnostics.push(makeDiagnostic(
|
|
135
|
+
'PULSEWASM_LOWERABLE_LIBRARY_NPM_PACKAGE_REQUIRED',
|
|
136
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} must declare the npm package that owns the facade.`,
|
|
137
|
+
'Add npmPackage to the package-owned manifest instead of adding package identity to wasm-contracts.',
|
|
138
|
+
{ npmPackage: normalized.npmPackage }
|
|
139
|
+
));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (!isNonEmptyString(normalized.lowerableSubpath)) {
|
|
143
|
+
diagnostics.push(makeDiagnostic(
|
|
144
|
+
'PULSEWASM_LOWERABLE_LIBRARY_SUBPATH_REQUIRED',
|
|
145
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} must declare a lowerable facade subpath.`,
|
|
146
|
+
'Add lowerableSubpath to the package-owned manifest.',
|
|
147
|
+
{ lowerableSubpath: normalized.lowerableSubpath }
|
|
148
|
+
));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (!isNonEmptyString(normalized.facade.namespace)) {
|
|
152
|
+
diagnostics.push(makeDiagnostic(
|
|
153
|
+
'PULSEWASM_LOWERABLE_LIBRARY_NAMESPACE_REQUIRED',
|
|
154
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} must declare facade.namespace.`,
|
|
155
|
+
'Add the static namespace that compiler extraction may recognize.',
|
|
156
|
+
{ namespace: normalized.facade.namespace }
|
|
157
|
+
));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!Array.isArray(facadeSymbols) || facadeSymbols.length === 0 || facadeSymbols.some((symbol) => !isNonEmptyString(symbol))) {
|
|
161
|
+
diagnostics.push(makeDiagnostic(
|
|
162
|
+
'PULSEWASM_LOWERABLE_LIBRARY_SYMBOLS_REQUIRED',
|
|
163
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} must declare one or more facade symbols.`,
|
|
164
|
+
'Add facade.symbols with the narrow lowerable symbols only.',
|
|
165
|
+
{ symbols: facadeSymbols }
|
|
166
|
+
));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (publicApiSymbols !== undefined && (!Array.isArray(publicApiSymbols) || publicApiSymbols.some((symbol) => !isNonEmptyString(symbol)))) {
|
|
170
|
+
diagnostics.push(makeDiagnostic(
|
|
171
|
+
'PULSEWASM_LOWERABLE_LIBRARY_PUBLIC_API_SYMBOLS_INVALID',
|
|
172
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} has an invalid publicApi.symbols list.`,
|
|
173
|
+
'Use static strings when documenting the ergonomic public package API.',
|
|
174
|
+
{ symbols: publicApiSymbols }
|
|
175
|
+
));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!isNonEmptyString(normalized.modes.typescript.entry)) {
|
|
179
|
+
diagnostics.push(makeDiagnostic(
|
|
180
|
+
'PULSEWASM_LOWERABLE_LIBRARY_TYPESCRIPT_ENTRY_REQUIRED',
|
|
181
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} must declare modes.typescript.entry.`,
|
|
182
|
+
'Declare the package runtime TypeScript/ESM entry for normal JS ergonomics.',
|
|
183
|
+
{ entry: normalized.modes.typescript.entry }
|
|
184
|
+
));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!isNonEmptyString(normalized.modes.jsEngine.entry)) {
|
|
188
|
+
diagnostics.push(makeDiagnostic(
|
|
189
|
+
'PULSEWASM_LOWERABLE_LIBRARY_JS_ENGINE_ENTRY_REQUIRED',
|
|
190
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} must declare modes.jsEngine.entry.`,
|
|
191
|
+
'Declare the manual JS engine escape-hatch entry.',
|
|
192
|
+
{ entry: normalized.modes.jsEngine.entry }
|
|
193
|
+
));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (!isNonEmptyString(wasm.sidecar)) {
|
|
197
|
+
diagnostics.push(makeDiagnostic(
|
|
198
|
+
'PULSEWASM_LOWERABLE_LIBRARY_WASM_SIDECAR_REQUIRED',
|
|
199
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} must declare modes.wasm.sidecar.`,
|
|
200
|
+
'Declare the AssemblyScript sidecar used by compiled-Wasm compatibility.',
|
|
201
|
+
{ sidecar: wasm.sidecar }
|
|
202
|
+
));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!Array.isArray(wasm.lowerings) || wasm.lowerings.length === 0) {
|
|
206
|
+
diagnostics.push(makeDiagnostic(
|
|
207
|
+
'PULSEWASM_LOWERABLE_LIBRARY_LOWERINGS_REQUIRED',
|
|
208
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} must declare modes.wasm.lowerings.`,
|
|
209
|
+
'Declare the static facade-to-sidecar symbols that lowering may recognize.',
|
|
210
|
+
{ lowerings: wasm.lowerings }
|
|
211
|
+
));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
for (const lowering of wasm.lowerings || []) {
|
|
215
|
+
if (!lowering || !isNonEmptyString(lowering.tsSymbol) || !isNonEmptyString(lowering.asSymbol)) {
|
|
216
|
+
diagnostics.push(makeDiagnostic(
|
|
217
|
+
'PULSEWASM_LOWERABLE_LIBRARY_LOWERING_INVALID',
|
|
218
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} declares an invalid lowering.`,
|
|
219
|
+
'Each lowering must include static tsSymbol and asSymbol strings.',
|
|
220
|
+
{ lowering }
|
|
221
|
+
));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (!isNonEmptyString(compiler.version)) {
|
|
226
|
+
diagnostics.push(makeDiagnostic(
|
|
227
|
+
'PULSEWASM_LOWERABLE_LIBRARY_COMPILER_VERSION_REQUIRED',
|
|
228
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} must declare compiler.version.`,
|
|
229
|
+
'Declare the package-owned compiler builder protocol version, for example pulsewasm.lowerable-compiler-builder.v1.',
|
|
230
|
+
{ compiler }
|
|
231
|
+
));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (!isNonEmptyString(compiler.entry)) {
|
|
235
|
+
diagnostics.push(makeDiagnostic(
|
|
236
|
+
'PULSEWASM_LOWERABLE_LIBRARY_COMPILER_ENTRY_REQUIRED',
|
|
237
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} must declare compiler.entry.`,
|
|
238
|
+
'Declare the package-owned compiler builder entry path such as ./pulsewasm.compiler.cjs.',
|
|
239
|
+
{ compiler }
|
|
240
|
+
));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (!isNonEmptyString(compiler.export)) {
|
|
244
|
+
diagnostics.push(makeDiagnostic(
|
|
245
|
+
'PULSEWASM_LOWERABLE_LIBRARY_COMPILER_EXPORT_REQUIRED',
|
|
246
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} must declare compiler.export.`,
|
|
247
|
+
'Declare the exported builder function name, for example buildAssetsLoweringPlan.',
|
|
248
|
+
{ compiler }
|
|
249
|
+
));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (!isNonEmptyString(compiler.builderOwner)) {
|
|
253
|
+
diagnostics.push(makeDiagnostic(
|
|
254
|
+
'PULSEWASM_LOWERABLE_LIBRARY_COMPILER_OWNER_REQUIRED',
|
|
255
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} must declare compiler.builderOwner.`,
|
|
256
|
+
'The package that owns the facade must also own the lowerer entry for this package-out track.',
|
|
257
|
+
{ compiler }
|
|
258
|
+
));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (compiler.builderOwner && normalized.npmPackage && compiler.builderOwner !== normalized.npmPackage) {
|
|
262
|
+
diagnostics.push(makeDiagnostic(
|
|
263
|
+
'PULSEWASM_LOWERABLE_LIBRARY_COMPILER_OWNER_MISMATCH',
|
|
264
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} compiler builderOwner must match npmPackage.`,
|
|
265
|
+
'Keep package-specific lowering rules in the package that owns the lowerable facade.',
|
|
266
|
+
{ npmPackage: normalized.npmPackage, builderOwner: compiler.builderOwner }
|
|
267
|
+
));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (compiler.trust !== 'first-party') {
|
|
271
|
+
diagnostics.push(makeDiagnostic(
|
|
272
|
+
'PULSEWASM_LOWERABLE_LIBRARY_COMPILER_TRUST_REQUIRED',
|
|
273
|
+
`Lowerable library manifest ${normalized.contractId || '<unknown>'} compiler.trust must be "first-party" in this track.`,
|
|
274
|
+
'Arbitrary third-party lowerer execution is not enabled; first-party package builders must opt in explicitly.',
|
|
275
|
+
{ trust: compiler.trust }
|
|
276
|
+
));
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
status: diagnostics.length === 0 ? 'ok' : 'error',
|
|
281
|
+
manifest: normalized,
|
|
282
|
+
contractId: normalized.contractId || '<unknown>',
|
|
283
|
+
npmPackage: normalized.npmPackage,
|
|
284
|
+
lowerableSubpath: normalized.lowerableSubpath,
|
|
285
|
+
facade: {
|
|
286
|
+
namespace: normalized.facade.namespace,
|
|
287
|
+
symbols: Array.isArray(facadeSymbols) ? facadeSymbols.slice() : []
|
|
288
|
+
},
|
|
289
|
+
publicApi: {
|
|
290
|
+
package: normalized.publicApi.package || normalized.npmPackage,
|
|
291
|
+
symbols: Array.isArray(publicApiSymbols) ? publicApiSymbols.slice() : []
|
|
292
|
+
},
|
|
293
|
+
compiler: {
|
|
294
|
+
version: normalized.compiler.version,
|
|
295
|
+
entry: normalized.compiler.entry,
|
|
296
|
+
export: normalized.compiler.export,
|
|
297
|
+
artifact: normalized.compiler.artifact,
|
|
298
|
+
builderOwner: normalized.compiler.builderOwner,
|
|
299
|
+
trust: normalized.compiler.trust,
|
|
300
|
+
inputs: Array.isArray(normalized.compiler.inputs) ? normalized.compiler.inputs.slice() : []
|
|
301
|
+
},
|
|
302
|
+
hostCapabilities: sortedUnique([...(wasm.hostCapabilities || []), ...((wasm.lowerings || []).flatMap((lowering) => lowering.hostCapabilities || []))]),
|
|
303
|
+
diagnostics
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
module.exports = {
|
|
308
|
+
LOWERABLE_LIBRARY_MANIFEST_VERSION,
|
|
309
|
+
LOWERABLE_LIBRARY_MANIFEST_KIND,
|
|
310
|
+
LOWERABLE_LIBRARY_PAYLOAD_MODE_STATUSES,
|
|
311
|
+
LOWERABLE_LIBRARY_MANIFEST_REQUIRED_FIELDS,
|
|
312
|
+
LOWERABLE_LIBRARY_MANIFEST_POLICY,
|
|
313
|
+
clone,
|
|
314
|
+
sortedUnique,
|
|
315
|
+
normalizeManifest,
|
|
316
|
+
validateLowerableLibraryManifest
|
|
317
|
+
};
|