@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.
- package/README.md +34 -2
- package/package.json +123 -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 +637 -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 +146 -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 +52 -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 +594 -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 +235 -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/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,430 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
MODULE_RESOLVER_CONTRACT_VERSION,
|
|
5
|
+
MODULE_RESOLVER_INPUT_VERSION,
|
|
6
|
+
RESOLUTION_KINDS,
|
|
7
|
+
normalizeModuleResolverInput,
|
|
8
|
+
normalizePortablePath,
|
|
9
|
+
normalizeModuleSpecifier
|
|
10
|
+
} = require('./reachable-graph-resolver.js');
|
|
11
|
+
const {
|
|
12
|
+
REACHABLE_GRAPH_IDENTITY_VERSION,
|
|
13
|
+
HANDLER_REFERENCE_V2_VERSION,
|
|
14
|
+
normalizeSourceLocation,
|
|
15
|
+
createModuleIdentity,
|
|
16
|
+
createEdgeIdentity,
|
|
17
|
+
createHandlerReferenceIdentity,
|
|
18
|
+
createCycleIdentity,
|
|
19
|
+
createUnsupportedBoundaryIdentity,
|
|
20
|
+
graphSemanticHash
|
|
21
|
+
} = require('./reachable-graph-identity.js');
|
|
22
|
+
|
|
23
|
+
const REACHABLE_GRAPH_MANIFEST_V2_VERSION = 'pulse.reachable-graph-manifest.v2';
|
|
24
|
+
const REACHABLE_GRAPH_IMPLEMENTATION_CONTRACT_VERSION = 'pulse.reachable-graph-implementation-contract.v1';
|
|
25
|
+
const MODULE_KINDS_V2 = Object.freeze(['project', 'package', 'generated', 'external']);
|
|
26
|
+
const MODULE_FORMATS_V2 = Object.freeze(['typescript', 'javascript', 'json', 'declaration', 'generated', 'external']);
|
|
27
|
+
const OWNERSHIP_KINDS_V2 = Object.freeze(['application', 'pulse-runtime', 'pulse-package', 'third-party', 'generated', 'external']);
|
|
28
|
+
const EDGE_KINDS_V2 = Object.freeze(['runtime-import', 'type-import', 're-export', 'package-contract', 'lifecycle']);
|
|
29
|
+
const HANDLER_ROLES_V2 = Object.freeze(['handler', 'route', 'middleware', 'event', 'error-middleware', 'router']);
|
|
30
|
+
const HANDLER_WRAPPERS_V2 = Object.freeze(['parentheses', 'as-cast', 'type-assertion', 'satisfies', 'non-null']);
|
|
31
|
+
const UNSUPPORTED_EDGE_KINDS_V2 = Object.freeze(['dynamic-import', 'commonjs-require', 'computed-specifier', 'loader-hook', 'unknown-runtime-side-effect']);
|
|
32
|
+
const CYCLE_EDGE_KINDS = Object.freeze(['runtime-import', 're-export', 'package-contract']);
|
|
33
|
+
|
|
34
|
+
function isPlainObject(value) {
|
|
35
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
36
|
+
const proto = Object.getPrototypeOf(value);
|
|
37
|
+
return proto === Object.prototype || proto === null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function assertKnownKeys(input, allowed, field) {
|
|
41
|
+
for (const key of Object.keys(input)) {
|
|
42
|
+
if (!allowed.has(key)) throw new TypeError(`${field} contains unsupported field ${key}.`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function assertString(value, field) {
|
|
47
|
+
if (typeof value !== 'string' || value.trim() === '') throw new TypeError(`${field} must be a non-empty string.`);
|
|
48
|
+
if (value.includes('\0')) throw new TypeError(`${field} must not contain NUL.`);
|
|
49
|
+
return value.trim();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function assertBoolean(value, field) {
|
|
53
|
+
if (typeof value !== 'boolean') throw new TypeError(`${field} must be a boolean.`);
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function assertSha256(value, field, options = {}) {
|
|
58
|
+
if (value == null && options.nullable === true) return null;
|
|
59
|
+
const text = assertString(value, field);
|
|
60
|
+
if (!/^[0-9a-f]{64}$/.test(text)) throw new TypeError(`${field} must be a lowercase SHA-256 hex string.`);
|
|
61
|
+
return text;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function optionalString(value, field) {
|
|
65
|
+
return value == null ? null : assertString(value, field);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function sortedStrings(input, field) {
|
|
69
|
+
if (input == null) return Object.freeze([]);
|
|
70
|
+
if (!Array.isArray(input)) throw new TypeError(`${field} must be an array.`);
|
|
71
|
+
return Object.freeze(Array.from(new Set(input.map((value, index) => assertString(value, `${field}[${index}]`)))).sort());
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function normalizeOptionalSource(input, field) {
|
|
75
|
+
return input == null ? null : normalizeSourceLocation(input, field);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function normalizeModuleV2(input, index) {
|
|
79
|
+
if (!isPlainObject(input)) throw new TypeError(`modules[${index}] must be an object.`);
|
|
80
|
+
assertKnownKeys(input, new Set([
|
|
81
|
+
'key', 'kind', 'owner', 'format', 'runtime', 'path', 'packageName', 'packageVersion',
|
|
82
|
+
'packageSubpath', 'packageManifestHash', 'generator', 'logicalName', 'specifier',
|
|
83
|
+
'packageContract', 'contentHash', 'exports', 'reExports', 'source'
|
|
84
|
+
]), `modules[${index}]`);
|
|
85
|
+
const key = assertString(input.key, `modules[${index}].key`);
|
|
86
|
+
const kind = assertString(input.kind, `modules[${index}].kind`);
|
|
87
|
+
const owner = assertString(input.owner, `modules[${index}].owner`);
|
|
88
|
+
const format = assertString(input.format, `modules[${index}].format`);
|
|
89
|
+
if (!MODULE_KINDS_V2.includes(kind)) throw new TypeError(`Unsupported module kind ${kind}.`);
|
|
90
|
+
if (!OWNERSHIP_KINDS_V2.includes(owner)) throw new TypeError(`Unsupported module owner ${owner}.`);
|
|
91
|
+
if (!MODULE_FORMATS_V2.includes(format)) throw new TypeError(`Unsupported module format ${format}.`);
|
|
92
|
+
const runtime = assertBoolean(input.runtime, `modules[${index}].runtime`);
|
|
93
|
+
if (format === 'declaration' && runtime) throw new TypeError(`modules[${index}] declaration modules cannot be runtime-reachable.`);
|
|
94
|
+
if (kind === 'external' && format !== 'external') throw new TypeError(`modules[${index}] external modules must use format external.`);
|
|
95
|
+
if (kind === 'generated' && format !== 'generated') throw new TypeError(`modules[${index}] generated modules must use format generated.`);
|
|
96
|
+
const identity = createModuleIdentity(input);
|
|
97
|
+
const contentHash = kind === 'external'
|
|
98
|
+
? assertSha256(input.contentHash, `modules[${index}].contentHash`, { nullable: true })
|
|
99
|
+
: assertSha256(input.contentHash, `modules[${index}].contentHash`);
|
|
100
|
+
const common = {
|
|
101
|
+
id: identity.id,
|
|
102
|
+
kind,
|
|
103
|
+
owner,
|
|
104
|
+
canonical: identity.canonical,
|
|
105
|
+
format,
|
|
106
|
+
runtime,
|
|
107
|
+
path: null,
|
|
108
|
+
packageName: null,
|
|
109
|
+
packageVersion: null,
|
|
110
|
+
packageSubpath: null,
|
|
111
|
+
packageManifestHash: null,
|
|
112
|
+
generator: null,
|
|
113
|
+
logicalName: null,
|
|
114
|
+
externalSpecifier: null,
|
|
115
|
+
packageContract: optionalString(input.packageContract, `modules[${index}].packageContract`),
|
|
116
|
+
contentHash,
|
|
117
|
+
exports: sortedStrings(input.exports, `modules[${index}].exports`),
|
|
118
|
+
reExports: sortedStrings(input.reExports, `modules[${index}].reExports`),
|
|
119
|
+
source: normalizeOptionalSource(input.source, `modules[${index}].source`)
|
|
120
|
+
};
|
|
121
|
+
if (kind === 'project') {
|
|
122
|
+
common.path = identity.stableInput.path;
|
|
123
|
+
if (owner !== 'application' && owner !== 'pulse-runtime') throw new TypeError(`modules[${index}] project modules require application or pulse-runtime ownership.`);
|
|
124
|
+
} else if (kind === 'package') {
|
|
125
|
+
common.packageName = identity.stableInput.packageName;
|
|
126
|
+
common.packageVersion = identity.stableInput.packageVersion;
|
|
127
|
+
common.packageSubpath = identity.stableInput.packageSubpath;
|
|
128
|
+
common.packageManifestHash = identity.stableInput.packageManifestHash;
|
|
129
|
+
if (!['pulse-runtime', 'pulse-package', 'third-party'].includes(owner)) throw new TypeError(`modules[${index}] package modules require package ownership.`);
|
|
130
|
+
} else if (kind === 'generated') {
|
|
131
|
+
common.generator = identity.stableInput.generator;
|
|
132
|
+
common.logicalName = identity.stableInput.logicalName;
|
|
133
|
+
if (owner !== 'generated') throw new TypeError(`modules[${index}] generated modules require generated ownership.`);
|
|
134
|
+
} else {
|
|
135
|
+
common.externalSpecifier = identity.stableInput.specifier;
|
|
136
|
+
if (owner !== 'external') throw new TypeError(`modules[${index}] external modules require external ownership.`);
|
|
137
|
+
if (common.packageContract !== null) throw new TypeError(`modules[${index}] external modules cannot own a package contract.`);
|
|
138
|
+
}
|
|
139
|
+
return Object.freeze({ key, record: Object.freeze(common) });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function expectedRuntimeForEdge(kind, input, index) {
|
|
143
|
+
if (kind === 'type-import') {
|
|
144
|
+
if (input.runtime != null && input.runtime !== false) throw new TypeError(`edges[${index}] type-import must not be runtime-reachable.`);
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
if (kind === 're-export') return assertBoolean(input.runtime, `edges[${index}].runtime`);
|
|
148
|
+
if (input.runtime != null && input.runtime !== true) throw new TypeError(`edges[${index}] ${kind} must be runtime-reachable.`);
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function validateResolutionTarget(resolutionKind, target, index) {
|
|
153
|
+
if ((resolutionKind === 'project-relative' || resolutionKind === 'tsconfig-path') && !['project', 'generated'].includes(target.kind)) {
|
|
154
|
+
throw new TypeError(`edges[${index}] ${resolutionKind} must resolve to a project or generated module.`);
|
|
155
|
+
}
|
|
156
|
+
if ((resolutionKind === 'package-root' || resolutionKind === 'package-subpath') && target.kind !== 'package') {
|
|
157
|
+
throw new TypeError(`edges[${index}] ${resolutionKind} must resolve to a package module.`);
|
|
158
|
+
}
|
|
159
|
+
if (resolutionKind === 'generated' && target.kind !== 'generated') throw new TypeError(`edges[${index}] generated resolution must target a generated module.`);
|
|
160
|
+
if (resolutionKind === 'external' && target.kind !== 'external') throw new TypeError(`edges[${index}] external resolution must target an external module.`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function normalizeEdgeV2(input, index, modulesByKey) {
|
|
164
|
+
if (!isPlainObject(input)) throw new TypeError(`edges[${index}] must be an object.`);
|
|
165
|
+
assertKnownKeys(input, new Set([
|
|
166
|
+
'kind', 'from', 'to', 'specifier', 'resolutionKind', 'runtime', 'importedNames',
|
|
167
|
+
'exportedNames', 'packageContract', 'source'
|
|
168
|
+
]), `edges[${index}]`);
|
|
169
|
+
const kind = assertString(input.kind, `edges[${index}].kind`);
|
|
170
|
+
if (!EDGE_KINDS_V2.includes(kind)) throw new TypeError(`Unsupported graph edge kind ${kind}.`);
|
|
171
|
+
const fromKey = assertString(input.from, `edges[${index}].from`);
|
|
172
|
+
const toKey = assertString(input.to, `edges[${index}].to`);
|
|
173
|
+
const from = modulesByKey.get(fromKey);
|
|
174
|
+
const to = modulesByKey.get(toKey);
|
|
175
|
+
if (!from || !to) throw new TypeError(`edges[${index}] must reference declared module keys.`);
|
|
176
|
+
const resolutionKind = assertString(input.resolutionKind, `edges[${index}].resolutionKind`);
|
|
177
|
+
if (!RESOLUTION_KINDS.includes(resolutionKind)) throw new TypeError(`Unsupported resolution kind ${resolutionKind}.`);
|
|
178
|
+
validateResolutionTarget(resolutionKind, to, index);
|
|
179
|
+
const runtime = expectedRuntimeForEdge(kind, input, index);
|
|
180
|
+
const packageContract = optionalString(input.packageContract, `edges[${index}].packageContract`);
|
|
181
|
+
if (kind === 'package-contract') {
|
|
182
|
+
if (to.kind !== 'package' || to.packageContract == null) throw new TypeError(`edges[${index}] package-contract must target a package module with a contract.`);
|
|
183
|
+
if (packageContract !== to.packageContract) throw new TypeError(`edges[${index}] package contract must match its target module.`);
|
|
184
|
+
}
|
|
185
|
+
const source = normalizeSourceLocation(input.source, `edges[${index}].source`);
|
|
186
|
+
const identity = createEdgeIdentity({
|
|
187
|
+
kind,
|
|
188
|
+
from: from.id,
|
|
189
|
+
to: to.id,
|
|
190
|
+
specifier: normalizeModuleSpecifier(input.specifier, `edges[${index}].specifier`),
|
|
191
|
+
resolutionKind,
|
|
192
|
+
runtime,
|
|
193
|
+
importedNames: input.importedNames,
|
|
194
|
+
exportedNames: input.exportedNames,
|
|
195
|
+
packageContract,
|
|
196
|
+
source
|
|
197
|
+
});
|
|
198
|
+
return Object.freeze({
|
|
199
|
+
id: identity.id,
|
|
200
|
+
kind,
|
|
201
|
+
from: from.id,
|
|
202
|
+
to: to.id,
|
|
203
|
+
specifier: identity.stableInput.specifier,
|
|
204
|
+
resolutionKind,
|
|
205
|
+
runtime,
|
|
206
|
+
importedNames: identity.stableInput.importedNames,
|
|
207
|
+
exportedNames: identity.stableInput.exportedNames,
|
|
208
|
+
packageContract,
|
|
209
|
+
source
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function normalizeHandlerV2(input, index, modulesByKey) {
|
|
214
|
+
if (!isPlainObject(input)) throw new TypeError(`handlers[${index}] must be an object.`);
|
|
215
|
+
assertKnownKeys(input, new Set(['module', 'exportName', 'localName', 'role', 'wrappers', 'source']), `handlers[${index}]`);
|
|
216
|
+
const moduleKey = assertString(input.module, `handlers[${index}].module`);
|
|
217
|
+
const moduleRecord = modulesByKey.get(moduleKey);
|
|
218
|
+
if (!moduleRecord) throw new TypeError(`handlers[${index}] references unknown module key ${moduleKey}.`);
|
|
219
|
+
const role = assertString(input.role, `handlers[${index}].role`);
|
|
220
|
+
if (!HANDLER_ROLES_V2.includes(role)) throw new TypeError(`Unsupported handler role ${role}.`);
|
|
221
|
+
const wrappers = sortedStrings(input.wrappers, `handlers[${index}].wrappers`);
|
|
222
|
+
for (const wrapper of wrappers) if (!HANDLER_WRAPPERS_V2.includes(wrapper)) throw new TypeError(`Unsupported handler wrapper ${wrapper}.`);
|
|
223
|
+
const source = normalizeSourceLocation(input.source, `handlers[${index}].source`);
|
|
224
|
+
const identity = createHandlerReferenceIdentity({
|
|
225
|
+
moduleId: moduleRecord.id,
|
|
226
|
+
exportName: input.exportName,
|
|
227
|
+
localName: input.localName,
|
|
228
|
+
role,
|
|
229
|
+
wrappers,
|
|
230
|
+
source
|
|
231
|
+
});
|
|
232
|
+
return Object.freeze({
|
|
233
|
+
version: HANDLER_REFERENCE_V2_VERSION,
|
|
234
|
+
id: identity.id,
|
|
235
|
+
moduleId: moduleRecord.id,
|
|
236
|
+
exportName: identity.stableInput.exportName,
|
|
237
|
+
localName: identity.stableInput.localName,
|
|
238
|
+
role,
|
|
239
|
+
wrappers,
|
|
240
|
+
source
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function cycleEdge(edge) {
|
|
245
|
+
return edge.runtime && CYCLE_EDGE_KINDS.includes(edge.kind);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function deriveCycles(modules, edges) {
|
|
249
|
+
const moduleIds = modules.map((module) => module.id).sort();
|
|
250
|
+
const adjacency = new Map(moduleIds.map((id) => [id, []]));
|
|
251
|
+
const selfEdges = new Set();
|
|
252
|
+
for (const edge of edges) {
|
|
253
|
+
if (!cycleEdge(edge)) continue;
|
|
254
|
+
adjacency.get(edge.from).push(edge.to);
|
|
255
|
+
if (edge.from === edge.to) selfEdges.add(edge.from);
|
|
256
|
+
}
|
|
257
|
+
for (const targets of adjacency.values()) targets.sort();
|
|
258
|
+
let index = 0;
|
|
259
|
+
const stack = [];
|
|
260
|
+
const onStack = new Set();
|
|
261
|
+
const indices = new Map();
|
|
262
|
+
const low = new Map();
|
|
263
|
+
const cycles = [];
|
|
264
|
+
|
|
265
|
+
function visit(id) {
|
|
266
|
+
indices.set(id, index);
|
|
267
|
+
low.set(id, index);
|
|
268
|
+
index += 1;
|
|
269
|
+
stack.push(id);
|
|
270
|
+
onStack.add(id);
|
|
271
|
+
for (const next of adjacency.get(id)) {
|
|
272
|
+
if (!indices.has(next)) {
|
|
273
|
+
visit(next);
|
|
274
|
+
low.set(id, Math.min(low.get(id), low.get(next)));
|
|
275
|
+
} else if (onStack.has(next)) {
|
|
276
|
+
low.set(id, Math.min(low.get(id), indices.get(next)));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (low.get(id) !== indices.get(id)) return;
|
|
280
|
+
const component = [];
|
|
281
|
+
while (stack.length > 0) {
|
|
282
|
+
const member = stack.pop();
|
|
283
|
+
onStack.delete(member);
|
|
284
|
+
component.push(member);
|
|
285
|
+
if (member === id) break;
|
|
286
|
+
}
|
|
287
|
+
component.sort();
|
|
288
|
+
if (component.length > 1 || selfEdges.has(component[0])) {
|
|
289
|
+
const identity = createCycleIdentity(component);
|
|
290
|
+
cycles.push(Object.freeze({ id: identity.id, modules: identity.stableInput.modules }));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
for (const id of moduleIds) if (!indices.has(id)) visit(id);
|
|
295
|
+
return Object.freeze(cycles.sort((a, b) => a.id.localeCompare(b.id)));
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function normalizeUnsupportedBoundaryV2(input, index, modulesByKey) {
|
|
299
|
+
if (!isPlainObject(input)) throw new TypeError(`unsupportedBoundaries[${index}] must be an object.`);
|
|
300
|
+
assertKnownKeys(input, new Set(['kind', 'from', 'specifier', 'source']), `unsupportedBoundaries[${index}]`);
|
|
301
|
+
const kind = assertString(input.kind, `unsupportedBoundaries[${index}].kind`);
|
|
302
|
+
if (!UNSUPPORTED_EDGE_KINDS_V2.includes(kind)) throw new TypeError(`Unsupported boundary kind ${kind}.`);
|
|
303
|
+
const fromKey = input.from == null ? null : assertString(input.from, `unsupportedBoundaries[${index}].from`);
|
|
304
|
+
const from = fromKey == null ? null : modulesByKey.get(fromKey);
|
|
305
|
+
if (fromKey != null && !from) throw new TypeError(`unsupportedBoundaries[${index}] references unknown module key ${fromKey}.`);
|
|
306
|
+
const source = normalizeSourceLocation(input.source, `unsupportedBoundaries[${index}].source`);
|
|
307
|
+
const identity = createUnsupportedBoundaryIdentity({
|
|
308
|
+
kind,
|
|
309
|
+
from: from ? from.id : null,
|
|
310
|
+
specifier: normalizeModuleSpecifier(input.specifier, `unsupportedBoundaries[${index}].specifier`),
|
|
311
|
+
source
|
|
312
|
+
});
|
|
313
|
+
return Object.freeze({
|
|
314
|
+
id: identity.id,
|
|
315
|
+
kind,
|
|
316
|
+
from: from ? from.id : null,
|
|
317
|
+
specifier: identity.stableInput.specifier,
|
|
318
|
+
source
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function compareBoundaries(a, b) {
|
|
323
|
+
return a.source.file.localeCompare(b.source.file)
|
|
324
|
+
|| a.source.line - b.source.line
|
|
325
|
+
|| a.source.column - b.source.column
|
|
326
|
+
|| a.id.localeCompare(b.id);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function normalizeReachableGraphManifestV2(input) {
|
|
330
|
+
if (!isPlainObject(input)) throw new TypeError('Reachable graph v2 input must be an object.');
|
|
331
|
+
assertKnownKeys(input, new Set(['resolver', 'entry', 'modules', 'edges', 'handlers', 'unsupportedBoundaries']), 'Reachable graph v2 input');
|
|
332
|
+
const resolver = normalizeModuleResolverInput(input.resolver || {});
|
|
333
|
+
const normalizedModules = [...(input.modules || [])].map(normalizeModuleV2);
|
|
334
|
+
const modulesByKey = new Map();
|
|
335
|
+
const modulesById = new Map();
|
|
336
|
+
for (const entry of normalizedModules) {
|
|
337
|
+
if (modulesByKey.has(entry.key)) throw new TypeError(`Duplicate graph module key ${entry.key}.`);
|
|
338
|
+
if (modulesById.has(entry.record.id)) throw new TypeError(`Duplicate graph module identity ${entry.record.id}.`);
|
|
339
|
+
modulesByKey.set(entry.key, entry.record);
|
|
340
|
+
modulesById.set(entry.record.id, entry.record);
|
|
341
|
+
}
|
|
342
|
+
const entryKey = assertString(input.entry, 'entry');
|
|
343
|
+
const entryModule = modulesByKey.get(entryKey);
|
|
344
|
+
if (!entryModule) throw new TypeError(`Reachable graph entry references unknown module key ${entryKey}.`);
|
|
345
|
+
if (!entryModule.runtime || !['project', 'generated'].includes(entryModule.kind)) {
|
|
346
|
+
throw new TypeError('Reachable graph entry must be a runtime project or generated module.');
|
|
347
|
+
}
|
|
348
|
+
const modules = Object.freeze(Array.from(modulesById.values()).sort((a, b) => a.id.localeCompare(b.id)));
|
|
349
|
+
const edges = Object.freeze([...(input.edges || [])].map((edge, index) => normalizeEdgeV2(edge, index, modulesByKey)).sort((a, b) => a.id.localeCompare(b.id)));
|
|
350
|
+
if (new Set(edges.map((edge) => edge.id)).size !== edges.length) throw new TypeError('Reachable graph v2 edge identities must be unique.');
|
|
351
|
+
const handlers = Object.freeze([...(input.handlers || [])].map((handler, index) => normalizeHandlerV2(handler, index, modulesByKey)).sort((a, b) => a.id.localeCompare(b.id)));
|
|
352
|
+
if (new Set(handlers.map((handler) => handler.id)).size !== handlers.length) throw new TypeError('Reachable graph v2 handler identities must be unique.');
|
|
353
|
+
const unsupportedBoundaries = Object.freeze([...(input.unsupportedBoundaries || [])]
|
|
354
|
+
.map((boundary, index) => normalizeUnsupportedBoundaryV2(boundary, index, modulesByKey))
|
|
355
|
+
.sort(compareBoundaries));
|
|
356
|
+
if (new Set(unsupportedBoundaries.map((boundary) => boundary.id)).size !== unsupportedBoundaries.length) {
|
|
357
|
+
throw new TypeError('Reachable graph v2 unsupported-boundary identities must be unique.');
|
|
358
|
+
}
|
|
359
|
+
const cycles = deriveCycles(modules, edges);
|
|
360
|
+
const semantic = Object.freeze({
|
|
361
|
+
version: REACHABLE_GRAPH_MANIFEST_V2_VERSION,
|
|
362
|
+
identityVersion: REACHABLE_GRAPH_IDENTITY_VERSION,
|
|
363
|
+
resolverVersion: MODULE_RESOLVER_CONTRACT_VERSION,
|
|
364
|
+
handlerReferenceVersion: HANDLER_REFERENCE_V2_VERSION,
|
|
365
|
+
resolver,
|
|
366
|
+
entry: entryModule.id,
|
|
367
|
+
modules,
|
|
368
|
+
edges,
|
|
369
|
+
handlers,
|
|
370
|
+
cycles,
|
|
371
|
+
unsupportedBoundaries,
|
|
372
|
+
firstUnsupportedBoundary: unsupportedBoundaries.length === 0 ? null : unsupportedBoundaries[0]
|
|
373
|
+
});
|
|
374
|
+
return Object.freeze({ ...semantic, graphHash: graphSemanticHash(semantic) });
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function defaultReachableGraphImplementationContract() {
|
|
378
|
+
return Object.freeze({
|
|
379
|
+
version: REACHABLE_GRAPH_IMPLEMENTATION_CONTRACT_VERSION,
|
|
380
|
+
manifestVersion: REACHABLE_GRAPH_MANIFEST_V2_VERSION,
|
|
381
|
+
identityVersion: REACHABLE_GRAPH_IDENTITY_VERSION,
|
|
382
|
+
resolverVersion: MODULE_RESOLVER_CONTRACT_VERSION,
|
|
383
|
+
handlerReferenceVersion: HANDLER_REFERENCE_V2_VERSION,
|
|
384
|
+
moduleKinds: MODULE_KINDS_V2,
|
|
385
|
+
moduleFormats: MODULE_FORMATS_V2,
|
|
386
|
+
ownershipKinds: OWNERSHIP_KINDS_V2,
|
|
387
|
+
edgeKinds: EDGE_KINDS_V2,
|
|
388
|
+
handlerRoles: HANDLER_ROLES_V2,
|
|
389
|
+
handlerWrappers: HANDLER_WRAPPERS_V2,
|
|
390
|
+
unsupportedEdgeKinds: UNSUPPORTED_EDGE_KINDS_V2,
|
|
391
|
+
policies: Object.freeze({
|
|
392
|
+
callerProvidedStableIdsAccepted: false,
|
|
393
|
+
absoluteFilesystemPathsInManifest: false,
|
|
394
|
+
moduleIdentityIncludesContentHash: false,
|
|
395
|
+
graphHashIncludesContentHashes: true,
|
|
396
|
+
graphHashIncludesResolverHash: true,
|
|
397
|
+
cyclesDerivedFromRuntimeDependencyEdges: true,
|
|
398
|
+
unsupportedBoundariesSortedBySource: true,
|
|
399
|
+
firstUnsupportedBoundaryDerived: true,
|
|
400
|
+
typeOnlyEdgesCreateRuntimeReachability: false,
|
|
401
|
+
runtimePackageUseFromResolvedEdgesOnly: true,
|
|
402
|
+
hostPlatformAffectsIdentity: false,
|
|
403
|
+
cacheImplemented: false,
|
|
404
|
+
recursiveWalkerImplemented: false,
|
|
405
|
+
javascriptBundlerImplemented: false
|
|
406
|
+
}),
|
|
407
|
+
consumers: Object.freeze([
|
|
408
|
+
'native-compiler',
|
|
409
|
+
'javascript-build-planner',
|
|
410
|
+
'native-eligibility',
|
|
411
|
+
'watch-inputs',
|
|
412
|
+
'package-contract-selection',
|
|
413
|
+
'application-entry-safety'
|
|
414
|
+
])
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
module.exports = Object.freeze({
|
|
419
|
+
REACHABLE_GRAPH_MANIFEST_V2_VERSION,
|
|
420
|
+
REACHABLE_GRAPH_IMPLEMENTATION_CONTRACT_VERSION,
|
|
421
|
+
MODULE_KINDS_V2,
|
|
422
|
+
MODULE_FORMATS_V2,
|
|
423
|
+
OWNERSHIP_KINDS_V2,
|
|
424
|
+
EDGE_KINDS_V2,
|
|
425
|
+
HANDLER_ROLES_V2,
|
|
426
|
+
HANDLER_WRAPPERS_V2,
|
|
427
|
+
UNSUPPORTED_EDGE_KINDS_V2,
|
|
428
|
+
normalizeReachableGraphManifestV2,
|
|
429
|
+
defaultReachableGraphImplementationContract
|
|
430
|
+
});
|