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