@velarscript/cli 0.14.1 → 0.14.3
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/dist/cli.js +32 -1
- package/dist/cli.js.map +1 -1
- package/dist/library-artifact-build.d.ts +19 -0
- package/dist/library-artifact-build.d.ts.map +1 -0
- package/dist/library-artifact-build.js +302 -0
- package/dist/library-artifact-build.js.map +1 -0
- package/dist/library-artifact.d.ts +63 -0
- package/dist/library-artifact.d.ts.map +1 -0
- package/dist/library-artifact.js +667 -0
- package/dist/library-artifact.js.map +1 -0
- package/dist/npm.d.ts.map +1 -1
- package/dist/npm.js +9 -0
- package/dist/npm.js.map +1 -1
- package/dist/project-semantic.d.ts +1 -1
- package/dist/project-semantic.d.ts.map +1 -1
- package/dist/project-semantic.js +3 -0
- package/dist/project-semantic.js.map +1 -1
- package/dist/project.d.ts +9 -1
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +136 -22
- package/dist/project.js.map +1 -1
- package/dist/test-output.d.ts.map +1 -1
- package/dist/test-output.js +8 -2
- package/dist/test-output.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +8 -8
- package/skill/ai-skill-node.md +5 -1
- package/skill/ai-skill-server.md +45 -1
- package/skill/ai-skill.md +36 -5
|
@@ -0,0 +1,667 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { lstat, realpath } from "node:fs/promises";
|
|
3
|
+
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
4
|
+
import { readBoundedText } from "./bounded-text.js";
|
|
5
|
+
/** The first frozen JavaScript/package-interface contract shipped by VelarScript. */
|
|
6
|
+
export const VELAR_LIBRARY_ABI_VERSION = 1;
|
|
7
|
+
const MAX_INTERFACE_BYTES = 8 * 1024 * 1024;
|
|
8
|
+
const MAX_ARTIFACT_FILE_BYTES = 64 * 1024 * 1024;
|
|
9
|
+
const MAX_WIRE_NODES = 1_000_000;
|
|
10
|
+
const MAX_WIRE_DEPTH = 128;
|
|
11
|
+
const SHA256 = /^[a-f0-9]{64}$/u;
|
|
12
|
+
const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*|[a-z0-9][a-z0-9._-]*)$/u;
|
|
13
|
+
const PACKAGE_VERSION = /^(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/u;
|
|
14
|
+
export function sha256Text(value) {
|
|
15
|
+
return createHash("sha256").update(value, "utf8").digest("hex");
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* ABI 1 is deliberately a data format, never JSON.stringify over compiler
|
|
19
|
+
* internals. Every container carries an explicit tag, so Maps/Sets survive and
|
|
20
|
+
* an extension-owned object cannot be mistaken for one of the wire wrappers.
|
|
21
|
+
*/
|
|
22
|
+
export function encodeVelarLibraryInterface(interface_) {
|
|
23
|
+
validateModuleInterface(interface_, "module interface");
|
|
24
|
+
let nodes = 0;
|
|
25
|
+
const active = new Set();
|
|
26
|
+
const encode = (value, depth) => {
|
|
27
|
+
nodes += 1;
|
|
28
|
+
if (nodes > MAX_WIRE_NODES)
|
|
29
|
+
throw new RangeError(`Velar library interface exceeds ${MAX_WIRE_NODES} values`);
|
|
30
|
+
if (depth > MAX_WIRE_DEPTH)
|
|
31
|
+
throw new RangeError(`Velar library interface exceeds ${MAX_WIRE_DEPTH} nested values`);
|
|
32
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
33
|
+
return value;
|
|
34
|
+
if (typeof value === "number") {
|
|
35
|
+
if (!Number.isFinite(value))
|
|
36
|
+
throw new Error("Velar library interface cannot contain a non-finite number");
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
if (value === undefined)
|
|
40
|
+
throw new Error("Velar library interface cannot contain an explicit undefined value");
|
|
41
|
+
if (typeof value !== "object")
|
|
42
|
+
throw new Error(`Velar library interface cannot contain ${typeof value}`);
|
|
43
|
+
if (active.has(value))
|
|
44
|
+
throw new Error("Velar library interface cannot contain an object cycle");
|
|
45
|
+
active.add(value);
|
|
46
|
+
let output;
|
|
47
|
+
if (Array.isArray(value)) {
|
|
48
|
+
output = { tag: "array", value: value.map((item) => encode(item, depth + 1)) };
|
|
49
|
+
}
|
|
50
|
+
else if (value instanceof Map) {
|
|
51
|
+
output = { tag: "map", value: [...value].map(([key, item]) => [encode(key, depth + 1), encode(item, depth + 1)]) };
|
|
52
|
+
}
|
|
53
|
+
else if (value instanceof Set) {
|
|
54
|
+
output = { tag: "set", value: [...value].map((item) => encode(item, depth + 1)) };
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const entries = Object.entries(value)
|
|
58
|
+
.filter(([, item]) => item !== undefined)
|
|
59
|
+
.map(([key, item]) => [key, encode(item, depth + 1)]);
|
|
60
|
+
output = { tag: "object", value: entries };
|
|
61
|
+
}
|
|
62
|
+
active.delete(value);
|
|
63
|
+
return output;
|
|
64
|
+
};
|
|
65
|
+
return `${JSON.stringify({ formatVersion: 1, abiVersion: VELAR_LIBRARY_ABI_VERSION, interface: encode(interface_, 0) }, null, 2)}\n`;
|
|
66
|
+
}
|
|
67
|
+
export function decodeVelarLibraryInterface(text) {
|
|
68
|
+
if (Buffer.byteLength(text, "utf8") > MAX_INTERFACE_BYTES)
|
|
69
|
+
throw new RangeError(`Velar library interface exceeds ${MAX_INTERFACE_BYTES} bytes`);
|
|
70
|
+
const envelope = record(JSON.parse(text), "Velar library interface");
|
|
71
|
+
exactKeys(envelope, ["formatVersion", "abiVersion", "interface"], "Velar library interface");
|
|
72
|
+
if (envelope.formatVersion !== 1)
|
|
73
|
+
throw new Error("Velar library interface formatVersion must be 1");
|
|
74
|
+
if (envelope.abiVersion !== VELAR_LIBRARY_ABI_VERSION)
|
|
75
|
+
throw new Error(`Velar library interface ABI ${String(envelope.abiVersion)} is not supported`);
|
|
76
|
+
let nodes = 0;
|
|
77
|
+
const decode = (value, depth) => {
|
|
78
|
+
nodes += 1;
|
|
79
|
+
if (nodes > MAX_WIRE_NODES)
|
|
80
|
+
throw new RangeError(`Velar library interface exceeds ${MAX_WIRE_NODES} values`);
|
|
81
|
+
if (depth > MAX_WIRE_DEPTH)
|
|
82
|
+
throw new RangeError(`Velar library interface exceeds ${MAX_WIRE_DEPTH} nested values`);
|
|
83
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
84
|
+
return value;
|
|
85
|
+
if (typeof value === "number") {
|
|
86
|
+
if (!Number.isFinite(value))
|
|
87
|
+
throw new Error("Velar library interface contains a non-finite number");
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
const wrapper = record(value, "Velar library interface wire value");
|
|
91
|
+
exactKeys(wrapper, ["tag", "value"], "Velar library interface wire value");
|
|
92
|
+
if (!Array.isArray(wrapper.value))
|
|
93
|
+
throw new Error("Velar library interface wire container value must be a list");
|
|
94
|
+
if (wrapper.tag === "array")
|
|
95
|
+
return wrapper.value.map((item) => decode(item, depth + 1));
|
|
96
|
+
if (wrapper.tag === "set")
|
|
97
|
+
return new Set(wrapper.value.map((item) => decode(item, depth + 1)));
|
|
98
|
+
if (wrapper.tag === "map" || wrapper.tag === "object") {
|
|
99
|
+
const entries = wrapper.value.map((entry, index) => {
|
|
100
|
+
if (!Array.isArray(entry) || entry.length !== 2)
|
|
101
|
+
throw new Error(`Velar library interface ${String(wrapper.tag)} entry ${index} must contain a key and value`);
|
|
102
|
+
return [decode(entry[0], depth + 1), decode(entry[1], depth + 1)];
|
|
103
|
+
});
|
|
104
|
+
if (wrapper.tag === "map")
|
|
105
|
+
return new Map(entries);
|
|
106
|
+
const output = Object.create(null);
|
|
107
|
+
for (const [key, item] of entries) {
|
|
108
|
+
if (typeof key !== "string" || key === "__proto__" || key === "prototype" || key === "constructor") {
|
|
109
|
+
throw new Error("Velar library interface object keys must be safe strings");
|
|
110
|
+
}
|
|
111
|
+
if (Object.hasOwn(output, key))
|
|
112
|
+
throw new Error(`Velar library interface object repeats key '${key}'`);
|
|
113
|
+
output[key] = item;
|
|
114
|
+
}
|
|
115
|
+
return output;
|
|
116
|
+
}
|
|
117
|
+
throw new Error(`Velar library interface wire tag '${String(wrapper.tag)}' is not supported`);
|
|
118
|
+
};
|
|
119
|
+
const decoded = decode(envelope.interface, 0);
|
|
120
|
+
validateModuleInterface(decoded, "Velar library interface");
|
|
121
|
+
return decoded;
|
|
122
|
+
}
|
|
123
|
+
/** Replaces physical module paths in every nominal identity with package-stable paths. */
|
|
124
|
+
export function rebaseModuleInterfaceIdentities(interface_, replacements) {
|
|
125
|
+
const normalized = replacements
|
|
126
|
+
.map((item) => ({ physical: item.physical.replaceAll("\\", "/"), logical: item.logical }))
|
|
127
|
+
.sort((left, right) => right.physical.length - left.physical.length);
|
|
128
|
+
const replace = (text) => {
|
|
129
|
+
let output = text.replaceAll("\\", "/");
|
|
130
|
+
for (const item of normalized)
|
|
131
|
+
output = output.replaceAll(item.physical, item.logical);
|
|
132
|
+
return output;
|
|
133
|
+
};
|
|
134
|
+
const visit = (value) => {
|
|
135
|
+
if (typeof value === "string")
|
|
136
|
+
return replace(value);
|
|
137
|
+
if (value === null || typeof value !== "object")
|
|
138
|
+
return value;
|
|
139
|
+
if (Array.isArray(value))
|
|
140
|
+
return value.map(visit);
|
|
141
|
+
if (value instanceof Map)
|
|
142
|
+
return new Map([...value].map(([key, item]) => [visit(key), visit(item)]));
|
|
143
|
+
if (value instanceof Set)
|
|
144
|
+
return new Set([...value].map(visit));
|
|
145
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, visit(item)]));
|
|
146
|
+
};
|
|
147
|
+
const rebased = visit(interface_);
|
|
148
|
+
validateModuleInterface(rebased, "rebased module interface");
|
|
149
|
+
return rebased;
|
|
150
|
+
}
|
|
151
|
+
export function packageStableModulePath(name, version, relativeSourcePath) {
|
|
152
|
+
return `package:${name}@${version}/${relativeSourcePath.replaceAll("\\", "/")}`;
|
|
153
|
+
}
|
|
154
|
+
export async function loadVelarLibraryArtifact(options) {
|
|
155
|
+
const receiptPath = artifactPath(options.packageRoot, options.descriptor, "velar.artifacts receipt");
|
|
156
|
+
const receiptText = await readArtifactText(options.packageRoot, receiptPath, 4 * 1024 * 1024, "Velar library artifact receipt");
|
|
157
|
+
const receipt = validateReceipt(JSON.parse(receiptText));
|
|
158
|
+
if (receipt.package.name !== options.packageName || receipt.package.version !== options.packageVersion) {
|
|
159
|
+
throw new Error(`Velar library artifact identifies '${receipt.package.name}@${receipt.package.version}', expected '${options.packageName}@${options.packageVersion}'`);
|
|
160
|
+
}
|
|
161
|
+
if (receipt.target !== options.target)
|
|
162
|
+
throw new Error(`Velar library artifact target '${receipt.target}' does not match manifest key '${options.target}'`);
|
|
163
|
+
if (receipt.sourceEntry !== options.sourceEntry)
|
|
164
|
+
throw new Error(`Velar library artifact sourceEntry '${receipt.sourceEntry}' does not match velar.entry '${options.sourceEntry}'`);
|
|
165
|
+
const receiptRoot = dirname(receiptPath);
|
|
166
|
+
const entryPath = artifactPath(receiptRoot, receipt.entry.javascript, "artifact JavaScript entry");
|
|
167
|
+
const sourceMapPath = artifactPath(receiptRoot, receipt.entry.sourceMap, "artifact source map");
|
|
168
|
+
const interfacePath = artifactPath(receiptRoot, receipt.entry.interface, "artifact interface");
|
|
169
|
+
const exported = packageExportTargets(options.packageExports, ".");
|
|
170
|
+
const expectedExport = `./${relative(options.packageRoot, entryPath).replaceAll("\\", "/")}`;
|
|
171
|
+
if (exported.length === 0 || exported.some((target) => target !== expectedExport)) {
|
|
172
|
+
throw new Error(`Package '${options.packageName}' must export its Velar artifact entry as '${expectedExport}' in every package.json export condition`);
|
|
173
|
+
}
|
|
174
|
+
const [javascript, sourceMap, interfaceText] = await Promise.all([
|
|
175
|
+
readArtifactText(options.packageRoot, entryPath, MAX_ARTIFACT_FILE_BYTES, "Velar library JavaScript artifact"),
|
|
176
|
+
readArtifactText(options.packageRoot, sourceMapPath, MAX_ARTIFACT_FILE_BYTES, "Velar library source map"),
|
|
177
|
+
readArtifactText(options.packageRoot, interfacePath, MAX_INTERFACE_BYTES, "Velar library interface"),
|
|
178
|
+
]);
|
|
179
|
+
for (const [label, content, expected] of [
|
|
180
|
+
["JavaScript", javascript, receipt.entry.sha256.javascript],
|
|
181
|
+
["source map", sourceMap, receipt.entry.sha256.sourceMap],
|
|
182
|
+
["interface", interfaceText, receipt.entry.sha256.interface],
|
|
183
|
+
]) {
|
|
184
|
+
const actual = sha256Text(content);
|
|
185
|
+
if (actual !== expected)
|
|
186
|
+
throw new Error(`Velar library artifact ${label} hash mismatch; the package is incomplete or was modified after its receipt was written`);
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
abiVersion: 1,
|
|
190
|
+
target: receipt.target,
|
|
191
|
+
compilerVersion: receipt.compilerVersion,
|
|
192
|
+
receiptPath,
|
|
193
|
+
entryPath,
|
|
194
|
+
interfacePath,
|
|
195
|
+
moduleInterface: decodeVelarLibraryInterface(interfaceText),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function validateReceipt(value) {
|
|
199
|
+
const receipt = record(value, "Velar library artifact receipt");
|
|
200
|
+
exactKeys(receipt, ["formatVersion", "kind", "abiVersion", "package", "target", "compilerVersion", "sourceEntry", "sources", "entry"], "Velar library artifact receipt");
|
|
201
|
+
if (receipt.formatVersion !== 1 || receipt.kind !== "velar-library-artifact" || receipt.abiVersion !== 1) {
|
|
202
|
+
throw new Error("Velar library artifact receipt must declare formatVersion 1, kind 'velar-library-artifact', and ABI 1");
|
|
203
|
+
}
|
|
204
|
+
const package_ = record(receipt.package, "Velar library artifact package identity");
|
|
205
|
+
exactKeys(package_, ["name", "version"], "Velar library artifact package identity");
|
|
206
|
+
if (typeof package_.name !== "string" || !PACKAGE_NAME.test(package_.name) || typeof package_.version !== "string" || !PACKAGE_VERSION.test(package_.version)) {
|
|
207
|
+
throw new Error("Velar library artifact package identity must contain a package name and semantic version");
|
|
208
|
+
}
|
|
209
|
+
if (receipt.target !== "core" && receipt.target !== "node")
|
|
210
|
+
throw new Error("Velar library ABI 1 target must be 'core' or 'node'");
|
|
211
|
+
if (typeof receipt.compilerVersion !== "string" || !PACKAGE_VERSION.test(receipt.compilerVersion)) {
|
|
212
|
+
throw new Error("Velar library artifact compilerVersion must be a semantic version");
|
|
213
|
+
}
|
|
214
|
+
normalizedRelativePath(receipt.sourceEntry, "Velar library artifact sourceEntry");
|
|
215
|
+
if (!Array.isArray(receipt.sources) || receipt.sources.length === 0 || receipt.sources.length > 10_000) {
|
|
216
|
+
throw new Error("Velar library artifact sources must be a non-empty bounded list");
|
|
217
|
+
}
|
|
218
|
+
const sourcePaths = new Set();
|
|
219
|
+
for (const source of receipt.sources) {
|
|
220
|
+
const item = record(source, "Velar library artifact source");
|
|
221
|
+
exactKeys(item, ["path", "sha256"], "Velar library artifact source");
|
|
222
|
+
normalizedRelativePath(item.path, "Velar library artifact source path");
|
|
223
|
+
if (sourcePaths.has(item.path))
|
|
224
|
+
throw new Error(`Velar library artifact repeats source '${String(item.path)}'`);
|
|
225
|
+
sourcePaths.add(item.path);
|
|
226
|
+
hash(item.sha256, "Velar library artifact source hash");
|
|
227
|
+
}
|
|
228
|
+
const entry = record(receipt.entry, "Velar library artifact entry");
|
|
229
|
+
exactKeys(entry, ["javascript", "sourceMap", "interface", "sha256"], "Velar library artifact entry");
|
|
230
|
+
normalizedRelativePath(entry.javascript, "Velar library artifact JavaScript path");
|
|
231
|
+
normalizedRelativePath(entry.sourceMap, "Velar library artifact source map path");
|
|
232
|
+
normalizedRelativePath(entry.interface, "Velar library artifact interface path");
|
|
233
|
+
if (new Set([entry.javascript, entry.sourceMap, entry.interface]).size !== 3) {
|
|
234
|
+
throw new Error("Velar library artifact JavaScript, source map, and interface paths must be distinct");
|
|
235
|
+
}
|
|
236
|
+
const hashes = record(entry.sha256, "Velar library artifact hashes");
|
|
237
|
+
exactKeys(hashes, ["javascript", "sourceMap", "interface"], "Velar library artifact hashes");
|
|
238
|
+
hash(hashes.javascript, "Velar library JavaScript hash");
|
|
239
|
+
hash(hashes.sourceMap, "Velar library source map hash");
|
|
240
|
+
hash(hashes.interface, "Velar library interface hash");
|
|
241
|
+
return receipt;
|
|
242
|
+
}
|
|
243
|
+
function validateModuleInterface(value, label) {
|
|
244
|
+
const interface_ = record(value, label);
|
|
245
|
+
exactKeys(interface_, [
|
|
246
|
+
"exports", "mutableExports", "reactiveExports", "reExports", "hoistedExports", "namedTypes",
|
|
247
|
+
"namedTypeReadonlyFields", "namedTypeIdentities", "namedTypeBases", "genericTypes", "typeAliases",
|
|
248
|
+
"enums", "classes", "tests", "extensionExports", "extensionData",
|
|
249
|
+
], label, true);
|
|
250
|
+
stringMap(interface_.exports, `${label}.exports`, validateValueType);
|
|
251
|
+
stringSet(interface_.mutableExports, `${label}.mutableExports`);
|
|
252
|
+
stringMap(interface_.reactiveExports, `${label}.reactiveExports`, (item, itemLabel) => {
|
|
253
|
+
if (item !== "state")
|
|
254
|
+
throw new Error(`${itemLabel} must be 'state'`);
|
|
255
|
+
});
|
|
256
|
+
stringMap(interface_.reExports, `${label}.reExports`, (item, itemLabel) => {
|
|
257
|
+
const target = record(item, itemLabel);
|
|
258
|
+
exactKeys(target, ["source", "imported"], itemLabel);
|
|
259
|
+
nonEmptyString(target.source, `${itemLabel}.source`);
|
|
260
|
+
nonEmptyString(target.imported, `${itemLabel}.imported`);
|
|
261
|
+
});
|
|
262
|
+
if (interface_.hoistedExports !== undefined)
|
|
263
|
+
stringSet(interface_.hoistedExports, `${label}.hoistedExports`);
|
|
264
|
+
stringMap(interface_.namedTypes, `${label}.namedTypes`, (item, itemLabel) => stringMap(item, itemLabel, validateValueType));
|
|
265
|
+
if (interface_.namedTypeReadonlyFields !== undefined)
|
|
266
|
+
stringMap(interface_.namedTypeReadonlyFields, `${label}.namedTypeReadonlyFields`, (item, itemLabel) => stringSet(item, itemLabel));
|
|
267
|
+
stringMap(interface_.namedTypeIdentities, `${label}.namedTypeIdentities`, (item, itemLabel) => nonEmptyString(item, itemLabel));
|
|
268
|
+
if (interface_.namedTypeBases !== undefined)
|
|
269
|
+
stringMap(interface_.namedTypeBases, `${label}.namedTypeBases`, validateValueType);
|
|
270
|
+
if (interface_.genericTypes !== undefined)
|
|
271
|
+
stringMap(interface_.genericTypes, `${label}.genericTypes`, validateGenericTypeInfo);
|
|
272
|
+
stringMap(interface_.typeAliases, `${label}.typeAliases`, validateValueType);
|
|
273
|
+
stringMap(interface_.enums, `${label}.enums`, validateEnumInfo);
|
|
274
|
+
stringMap(interface_.classes, `${label}.classes`, validateClassInfo);
|
|
275
|
+
if (!Array.isArray(interface_.tests) || interface_.tests.length > 100_000)
|
|
276
|
+
throw new Error(`${label}.tests must be a bounded list`);
|
|
277
|
+
for (const [index, item] of interface_.tests.entries()) {
|
|
278
|
+
const test = record(item, `${label}.tests[${index}]`);
|
|
279
|
+
exactKeys(test, ["name", "title"], `${label}.tests[${index}]`);
|
|
280
|
+
nonEmptyString(test.name, `${label}.tests[${index}].name`);
|
|
281
|
+
if (typeof test.title !== "string")
|
|
282
|
+
throw new Error(`${label}.tests[${index}].title must be a string`);
|
|
283
|
+
}
|
|
284
|
+
stringMap(interface_.extensionExports, `${label}.extensionExports`, (item, itemLabel) => stringMap(item, itemLabel, validatePortableData));
|
|
285
|
+
stringMap(interface_.extensionData, `${label}.extensionData`, validatePortableData);
|
|
286
|
+
}
|
|
287
|
+
function validateValueType(value, label, depth = 0) {
|
|
288
|
+
if (depth > MAX_WIRE_DEPTH)
|
|
289
|
+
throw new RangeError(`${label} exceeds the ABI type nesting limit`);
|
|
290
|
+
const type = record(value, label);
|
|
291
|
+
if (typeof type.kind !== "string")
|
|
292
|
+
throw new Error(`${label}.kind must be a string`);
|
|
293
|
+
const nested = (item, itemLabel) => validateValueType(item, itemLabel, depth + 1);
|
|
294
|
+
switch (type.kind) {
|
|
295
|
+
case "unknown":
|
|
296
|
+
exactKeys(type, ["kind", "restricted", "boundary"], label, true);
|
|
297
|
+
trueFlag(type.restricted, `${label}.restricted`);
|
|
298
|
+
trueFlag(type.boundary, `${label}.boundary`);
|
|
299
|
+
return;
|
|
300
|
+
case "any":
|
|
301
|
+
exactKeys(type, ["kind", "textConvertible"], label, true);
|
|
302
|
+
trueFlag(type.textConvertible, `${label}.textConvertible`);
|
|
303
|
+
return;
|
|
304
|
+
case "null":
|
|
305
|
+
case "string":
|
|
306
|
+
case "number":
|
|
307
|
+
case "bool":
|
|
308
|
+
exactKeys(type, ["kind"], label);
|
|
309
|
+
return;
|
|
310
|
+
case "optional":
|
|
311
|
+
exactKeys(type, ["kind", "inner"], label);
|
|
312
|
+
nested(type.inner, `${label}.inner`);
|
|
313
|
+
return;
|
|
314
|
+
case "list":
|
|
315
|
+
case "set":
|
|
316
|
+
exactKeys(type, ["kind", "element", "readonlyView"], label, true);
|
|
317
|
+
nested(type.element, `${label}.element`);
|
|
318
|
+
trueFlag(type.readonlyView, `${label}.readonlyView`);
|
|
319
|
+
return;
|
|
320
|
+
case "map":
|
|
321
|
+
exactKeys(type, ["kind", "key", "value", "readonlyView"], label, true);
|
|
322
|
+
nested(type.key, `${label}.key`);
|
|
323
|
+
nested(type.value, `${label}.value`);
|
|
324
|
+
trueFlag(type.readonlyView, `${label}.readonlyView`);
|
|
325
|
+
return;
|
|
326
|
+
case "record":
|
|
327
|
+
exactKeys(type, ["kind", "value", "readonlyView"], label, true);
|
|
328
|
+
nested(type.value, `${label}.value`);
|
|
329
|
+
trueFlag(type.readonlyView, `${label}.readonlyView`);
|
|
330
|
+
return;
|
|
331
|
+
case "promise":
|
|
332
|
+
case "runtimeType":
|
|
333
|
+
exactKeys(type, ["kind", "value"], label);
|
|
334
|
+
nested(type.value, `${label}.value`);
|
|
335
|
+
return;
|
|
336
|
+
case "object":
|
|
337
|
+
exactKeys(type, ["kind", "fields", "readonlyFields", "optionalFields", "readonlyView", "capabilityHandle"], label, true);
|
|
338
|
+
stringMap(type.fields, `${label}.fields`, nested);
|
|
339
|
+
if (type.readonlyFields !== undefined)
|
|
340
|
+
stringSet(type.readonlyFields, `${label}.readonlyFields`);
|
|
341
|
+
if (type.optionalFields !== undefined)
|
|
342
|
+
stringSet(type.optionalFields, `${label}.optionalFields`);
|
|
343
|
+
trueFlag(type.readonlyView, `${label}.readonlyView`);
|
|
344
|
+
trueFlag(type.capabilityHandle, `${label}.capabilityHandle`);
|
|
345
|
+
return;
|
|
346
|
+
case "parameter":
|
|
347
|
+
exactKeys(type, ["kind", "name", "index"], label);
|
|
348
|
+
nonEmptyString(type.name, `${label}.name`);
|
|
349
|
+
nonNegativeInteger(type.index, `${label}.index`);
|
|
350
|
+
return;
|
|
351
|
+
case "named":
|
|
352
|
+
exactKeys(type, ["kind", "name", "identity", "readonlyView", "application"], label, true);
|
|
353
|
+
nonEmptyString(type.name, `${label}.name`);
|
|
354
|
+
optionalString(type.identity, `${label}.identity`);
|
|
355
|
+
trueFlag(type.readonlyView, `${label}.readonlyView`);
|
|
356
|
+
if (type.application !== undefined) {
|
|
357
|
+
const application = record(type.application, `${label}.application`);
|
|
358
|
+
exactKeys(application, ["declaration", "name", "arguments"], `${label}.application`);
|
|
359
|
+
nonEmptyString(application.declaration, `${label}.application.declaration`);
|
|
360
|
+
nonEmptyString(application.name, `${label}.application.name`);
|
|
361
|
+
valueTypeList(application.arguments, `${label}.application.arguments`, nested);
|
|
362
|
+
}
|
|
363
|
+
return;
|
|
364
|
+
case "class":
|
|
365
|
+
case "classConstructor":
|
|
366
|
+
exactKeys(type, ["kind", "name", "identity"], label, true);
|
|
367
|
+
nonEmptyString(type.name, `${label}.name`);
|
|
368
|
+
optionalString(type.identity, `${label}.identity`);
|
|
369
|
+
return;
|
|
370
|
+
case "enum":
|
|
371
|
+
exactKeys(type, ["kind", "name", "identity"], label);
|
|
372
|
+
nonEmptyString(type.name, `${label}.name`);
|
|
373
|
+
nonEmptyString(type.identity, `${label}.identity`);
|
|
374
|
+
return;
|
|
375
|
+
case "enumMember":
|
|
376
|
+
exactKeys(type, ["kind", "name", "identity", "member"], label);
|
|
377
|
+
nonEmptyString(type.name, `${label}.name`);
|
|
378
|
+
nonEmptyString(type.identity, `${label}.identity`);
|
|
379
|
+
nonEmptyString(type.member, `${label}.member`);
|
|
380
|
+
return;
|
|
381
|
+
case "enumObject":
|
|
382
|
+
exactKeys(type, ["kind", "name", "identity", "members"], label);
|
|
383
|
+
nonEmptyString(type.name, `${label}.name`);
|
|
384
|
+
nonEmptyString(type.identity, `${label}.identity`);
|
|
385
|
+
stringSet(type.members, `${label}.members`);
|
|
386
|
+
return;
|
|
387
|
+
case "typeObject":
|
|
388
|
+
exactKeys(type, ["kind", "name", "value"], label, true);
|
|
389
|
+
nonEmptyString(type.name, `${label}.name`);
|
|
390
|
+
if (type.value !== undefined)
|
|
391
|
+
nested(type.value, `${label}.value`);
|
|
392
|
+
return;
|
|
393
|
+
case "function":
|
|
394
|
+
case "action":
|
|
395
|
+
case "intrinsic":
|
|
396
|
+
exactKeys(type, ["kind", "name", "typeParameterNames", "typeParameterBounds", "parameters", "parameterNames", "requiredParameters", "rest", "result"], label, true);
|
|
397
|
+
if (type.kind === "intrinsic")
|
|
398
|
+
nonEmptyString(type.name, `${label}.name`);
|
|
399
|
+
else if (type.name !== undefined)
|
|
400
|
+
throw new Error(`${label}.name is only valid on an intrinsic type`);
|
|
401
|
+
callableFields(type, label, nested);
|
|
402
|
+
return;
|
|
403
|
+
case "extension": {
|
|
404
|
+
exactKeys(type, ["kind", "extensionId", "family", "role", "nominal", "properties", "requiredProperties", "arguments", "metadata", "display"], label, true);
|
|
405
|
+
nonEmptyString(type.extensionId, `${label}.extensionId`);
|
|
406
|
+
nonEmptyString(type.family, `${label}.family`);
|
|
407
|
+
nonEmptyString(type.role, `${label}.role`);
|
|
408
|
+
optionalString(type.nominal, `${label}.nominal`);
|
|
409
|
+
stringMap(type.properties, `${label}.properties`, nested);
|
|
410
|
+
stringSet(type.requiredProperties, `${label}.requiredProperties`);
|
|
411
|
+
valueTypeList(type.arguments, `${label}.arguments`, nested);
|
|
412
|
+
if (type.metadata !== undefined)
|
|
413
|
+
stringRecord(type.metadata, `${label}.metadata`);
|
|
414
|
+
validateExtensionDisplay(type.display, `${label}.display`);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
case "union":
|
|
418
|
+
exactKeys(type, ["kind", "members"], label);
|
|
419
|
+
valueTypeList(type.members, `${label}.members`, nested);
|
|
420
|
+
return;
|
|
421
|
+
default:
|
|
422
|
+
throw new Error(`${label}.kind '${type.kind}' is not part of Velar library ABI 1`);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
function callableFields(type, label, nested) {
|
|
426
|
+
if (type.typeParameterNames !== undefined)
|
|
427
|
+
stringList(type.typeParameterNames, `${label}.typeParameterNames`);
|
|
428
|
+
if (type.typeParameterBounds !== undefined)
|
|
429
|
+
boundList(type.typeParameterBounds, `${label}.typeParameterBounds`);
|
|
430
|
+
valueTypeList(type.parameters, `${label}.parameters`, nested);
|
|
431
|
+
if (type.parameterNames !== undefined)
|
|
432
|
+
stringList(type.parameterNames, `${label}.parameterNames`, true);
|
|
433
|
+
nonNegativeInteger(type.requiredParameters, `${label}.requiredParameters`);
|
|
434
|
+
if (type.rest !== undefined)
|
|
435
|
+
nested(type.rest, `${label}.rest`);
|
|
436
|
+
nested(type.result, `${label}.result`);
|
|
437
|
+
}
|
|
438
|
+
function validateGenericTypeInfo(value, label) {
|
|
439
|
+
const info = record(value, label);
|
|
440
|
+
exactKeys(info, ["identity", "name", "parameterNames", "parameterBounds", "fields", "readonlyFields"], label, true);
|
|
441
|
+
nonEmptyString(info.identity, `${label}.identity`);
|
|
442
|
+
nonEmptyString(info.name, `${label}.name`);
|
|
443
|
+
stringList(info.parameterNames, `${label}.parameterNames`);
|
|
444
|
+
boundList(info.parameterBounds, `${label}.parameterBounds`);
|
|
445
|
+
stringMap(info.fields, `${label}.fields`, validateValueType);
|
|
446
|
+
if (info.readonlyFields !== undefined)
|
|
447
|
+
stringSet(info.readonlyFields, `${label}.readonlyFields`);
|
|
448
|
+
}
|
|
449
|
+
function validateEnumInfo(value, label) {
|
|
450
|
+
const info = record(value, label);
|
|
451
|
+
exactKeys(info, ["identity", "members"], label);
|
|
452
|
+
nonEmptyString(info.identity, `${label}.identity`);
|
|
453
|
+
stringSet(info.members, `${label}.members`);
|
|
454
|
+
}
|
|
455
|
+
function validateClassInfo(value, label) {
|
|
456
|
+
const info = record(value, label);
|
|
457
|
+
exactKeys(info, [
|
|
458
|
+
"identity", "dispose", "iterate", "iterateAsync", "parameters", "parameterNames", "requiredParameters",
|
|
459
|
+
"constructorRest", "base", "abstract", "fields", "getters", "abstractGetters", "methods", "abstractMethods",
|
|
460
|
+
"staticFields", "staticGetters", "staticMethods",
|
|
461
|
+
], label, true);
|
|
462
|
+
optionalString(info.identity, `${label}.identity`);
|
|
463
|
+
if (info.dispose !== undefined && info.dispose !== "sync" && info.dispose !== "async")
|
|
464
|
+
throw new Error(`${label}.dispose must be 'sync' or 'async'`);
|
|
465
|
+
if (info.iterate !== undefined)
|
|
466
|
+
validateValueType(info.iterate, `${label}.iterate`);
|
|
467
|
+
if (info.iterateAsync !== undefined)
|
|
468
|
+
validateValueType(info.iterateAsync, `${label}.iterateAsync`);
|
|
469
|
+
valueTypeList(info.parameters, `${label}.parameters`, validateValueType);
|
|
470
|
+
if (info.parameterNames !== undefined)
|
|
471
|
+
stringList(info.parameterNames, `${label}.parameterNames`, true);
|
|
472
|
+
nonNegativeInteger(info.requiredParameters, `${label}.requiredParameters`);
|
|
473
|
+
if (info.constructorRest !== undefined)
|
|
474
|
+
validateValueType(info.constructorRest, `${label}.constructorRest`);
|
|
475
|
+
if (info.base !== null && typeof info.base !== "string")
|
|
476
|
+
throw new Error(`${label}.base must be a string or null`);
|
|
477
|
+
if (typeof info.abstract !== "boolean")
|
|
478
|
+
throw new Error(`${label}.abstract must be a bool`);
|
|
479
|
+
stringMap(info.fields, `${label}.fields`, validateClassField);
|
|
480
|
+
stringSet(info.getters, `${label}.getters`);
|
|
481
|
+
stringSet(info.abstractGetters, `${label}.abstractGetters`);
|
|
482
|
+
stringMap(info.methods, `${label}.methods`, validateValueType);
|
|
483
|
+
stringSet(info.abstractMethods, `${label}.abstractMethods`);
|
|
484
|
+
stringMap(info.staticFields, `${label}.staticFields`, validateClassField);
|
|
485
|
+
stringSet(info.staticGetters, `${label}.staticGetters`);
|
|
486
|
+
stringMap(info.staticMethods, `${label}.staticMethods`, validateValueType);
|
|
487
|
+
}
|
|
488
|
+
function validateClassField(value, label) {
|
|
489
|
+
const field = record(value, label);
|
|
490
|
+
exactKeys(field, ["mutable", "type"], label);
|
|
491
|
+
if (typeof field.mutable !== "boolean")
|
|
492
|
+
throw new Error(`${label}.mutable must be a bool`);
|
|
493
|
+
validateValueType(field.type, `${label}.type`);
|
|
494
|
+
}
|
|
495
|
+
function validateExtensionDisplay(value, label) {
|
|
496
|
+
const display = record(value, label);
|
|
497
|
+
if (display.kind === "named") {
|
|
498
|
+
exactKeys(display, ["kind", "name"], label);
|
|
499
|
+
nonEmptyString(display.name, `${label}.name`);
|
|
500
|
+
}
|
|
501
|
+
else if (display.kind === "constructor") {
|
|
502
|
+
exactKeys(display, ["kind", "prefix", "name"], label);
|
|
503
|
+
if (typeof display.prefix !== "string")
|
|
504
|
+
throw new Error(`${label}.prefix must be a string`);
|
|
505
|
+
nonEmptyString(display.name, `${label}.name`);
|
|
506
|
+
}
|
|
507
|
+
else if (display.kind === "properties") {
|
|
508
|
+
exactKeys(display, ["kind", "name", "result", "hiddenOptionalProperties"], label, true);
|
|
509
|
+
nonEmptyString(display.name, `${label}.name`);
|
|
510
|
+
nonEmptyString(display.result, `${label}.result`);
|
|
511
|
+
if (display.hiddenOptionalProperties !== undefined)
|
|
512
|
+
stringMap(display.hiddenOptionalProperties, `${label}.hiddenOptionalProperties`, (item, itemLabel) => nonEmptyString(item, itemLabel));
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
throw new Error(`${label}.kind must be named, constructor, or properties`);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
function validatePortableData(value, label, depth = 0) {
|
|
519
|
+
if (depth > MAX_WIRE_DEPTH)
|
|
520
|
+
throw new RangeError(`${label} exceeds the ABI data nesting limit`);
|
|
521
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
522
|
+
return;
|
|
523
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
524
|
+
return;
|
|
525
|
+
if (Array.isArray(value)) {
|
|
526
|
+
for (const [index, item] of value.entries())
|
|
527
|
+
validatePortableData(item, `${label}[${index}]`, depth + 1);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
if (value instanceof Set) {
|
|
531
|
+
for (const item of value)
|
|
532
|
+
validatePortableData(item, `${label} set value`, depth + 1);
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
if (value instanceof Map) {
|
|
536
|
+
for (const [key, item] of value) {
|
|
537
|
+
if (typeof key !== "string")
|
|
538
|
+
throw new Error(`${label} map keys must be strings`);
|
|
539
|
+
validatePortableData(item, `${label}.${key}`, depth + 1);
|
|
540
|
+
}
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const object = record(value, label);
|
|
544
|
+
for (const [key, item] of Object.entries(object))
|
|
545
|
+
validatePortableData(item, `${label}.${key}`, depth + 1);
|
|
546
|
+
}
|
|
547
|
+
function record(value, label) {
|
|
548
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Map || value instanceof Set) {
|
|
549
|
+
throw new Error(`${label} must be an object`);
|
|
550
|
+
}
|
|
551
|
+
return value;
|
|
552
|
+
}
|
|
553
|
+
function exactKeys(value, allowed, label, optional = false) {
|
|
554
|
+
const allowedSet = new Set(allowed);
|
|
555
|
+
const unknown = Object.keys(value).find((key) => !allowedSet.has(key));
|
|
556
|
+
if (unknown)
|
|
557
|
+
throw new Error(`${label} has unknown field '${unknown}'`);
|
|
558
|
+
if (!optional) {
|
|
559
|
+
const missing = allowed.find((key) => !Object.hasOwn(value, key));
|
|
560
|
+
if (missing)
|
|
561
|
+
throw new Error(`${label} is missing field '${missing}'`);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
function stringMap(value, label, validate) {
|
|
565
|
+
if (!(value instanceof Map) || value.size > 100_000)
|
|
566
|
+
throw new Error(`${label} must be a bounded map`);
|
|
567
|
+
const seen = new Set();
|
|
568
|
+
for (const [key, item] of value) {
|
|
569
|
+
if (typeof key !== "string" || key === "")
|
|
570
|
+
throw new Error(`${label} keys must be non-empty strings`);
|
|
571
|
+
if (seen.has(key))
|
|
572
|
+
throw new Error(`${label} repeats '${key}'`);
|
|
573
|
+
seen.add(key);
|
|
574
|
+
validate(item, `${label}.${key}`);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
function stringSet(value, label) {
|
|
578
|
+
if (!(value instanceof Set) || value.size > 100_000 || [...value].some((item) => typeof item !== "string" || item === "")) {
|
|
579
|
+
throw new Error(`${label} must be a bounded set of non-empty strings`);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
function valueTypeList(value, label, validate) {
|
|
583
|
+
if (!Array.isArray(value) || value.length > 100_000)
|
|
584
|
+
throw new Error(`${label} must be a bounded list`);
|
|
585
|
+
value.forEach((item, index) => validate(item, `${label}[${index}]`));
|
|
586
|
+
}
|
|
587
|
+
function stringList(value, label, allowEmpty = false) {
|
|
588
|
+
if (!Array.isArray(value) || value.length > 100_000 || value.some((item) => typeof item !== "string" || (!allowEmpty && item === ""))) {
|
|
589
|
+
throw new Error(`${label} must be a bounded list of ${allowEmpty ? "strings" : "non-empty strings"}`);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function boundList(value, label) {
|
|
593
|
+
if (!Array.isArray(value) || value.length > 100_000 || value.some((item) => item !== null && item !== "Comparable" && item !== "Text" && item !== "Data")) {
|
|
594
|
+
throw new Error(`${label} must contain only Comparable, Text, Data, or null`);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function stringRecord(value, label) {
|
|
598
|
+
const object = record(value, label);
|
|
599
|
+
for (const [key, item] of Object.entries(object)) {
|
|
600
|
+
if (key === "" || typeof item !== "string")
|
|
601
|
+
throw new Error(`${label} must map non-empty strings to strings`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
function trueFlag(value, label) {
|
|
605
|
+
if (value !== undefined && value !== true)
|
|
606
|
+
throw new Error(`${label} must be true when present`);
|
|
607
|
+
}
|
|
608
|
+
function nonEmptyString(value, label) {
|
|
609
|
+
if (typeof value !== "string" || value === "")
|
|
610
|
+
throw new Error(`${label} must be a non-empty string`);
|
|
611
|
+
}
|
|
612
|
+
function optionalString(value, label) {
|
|
613
|
+
if (value !== undefined)
|
|
614
|
+
nonEmptyString(value, label);
|
|
615
|
+
}
|
|
616
|
+
function nonNegativeInteger(value, label) {
|
|
617
|
+
if (!Number.isSafeInteger(value) || value < 0)
|
|
618
|
+
throw new Error(`${label} must be a non-negative integer`);
|
|
619
|
+
}
|
|
620
|
+
function normalizedRelativePath(value, label) {
|
|
621
|
+
if (typeof value !== "string" || value === "" || /[\u0000-\u001f\u007f]/u.test(value) || isAbsolute(value) || value.includes("\\")
|
|
622
|
+
|| value.split("/").some((part) => part === "" || part === "." || part === "..")) {
|
|
623
|
+
throw new Error(`${label} must be a normalized relative path`);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
function artifactPath(root, value, label) {
|
|
627
|
+
normalizedRelativePath(value, label);
|
|
628
|
+
const path = resolve(root, ...value.split("/"));
|
|
629
|
+
const fromRoot = relative(root, path);
|
|
630
|
+
if (!fromRoot || fromRoot.startsWith("..") || isAbsolute(fromRoot))
|
|
631
|
+
throw new Error(`${label} escapes its package directory`);
|
|
632
|
+
return path;
|
|
633
|
+
}
|
|
634
|
+
async function readArtifactText(packageRoot, path, maximum, label) {
|
|
635
|
+
const [rootIdentity, metadata] = await Promise.all([realpath(packageRoot), lstat(path)]);
|
|
636
|
+
if (!metadata.isFile() || metadata.isSymbolicLink())
|
|
637
|
+
throw new Error(`${label} must be an ordinary file`);
|
|
638
|
+
const identity = await realpath(path);
|
|
639
|
+
const fromRoot = relative(rootIdentity, identity);
|
|
640
|
+
if (!fromRoot || fromRoot.startsWith("..") || isAbsolute(fromRoot))
|
|
641
|
+
throw new Error(`${label} escapes its package directory`);
|
|
642
|
+
return readBoundedText(identity, maximum, label);
|
|
643
|
+
}
|
|
644
|
+
function hash(value, label) {
|
|
645
|
+
if (typeof value !== "string" || !SHA256.test(value))
|
|
646
|
+
throw new Error(`${label} must be a lowercase SHA-256 digest`);
|
|
647
|
+
}
|
|
648
|
+
function packageExportTargets(exports, subpath) {
|
|
649
|
+
if (typeof exports === "string")
|
|
650
|
+
return subpath === "." ? [exports] : [];
|
|
651
|
+
if (exports === null || typeof exports !== "object" || Array.isArray(exports))
|
|
652
|
+
return [];
|
|
653
|
+
const fields = exports;
|
|
654
|
+
const target = Object.keys(fields).some((key) => key.startsWith(".")) ? fields[subpath] : subpath === "." ? exports : undefined;
|
|
655
|
+
const output = [];
|
|
656
|
+
const visit = (value) => {
|
|
657
|
+
if (typeof value === "string")
|
|
658
|
+
output.push(value);
|
|
659
|
+
else if (Array.isArray(value))
|
|
660
|
+
value.forEach(visit);
|
|
661
|
+
else if (value !== null && typeof value === "object")
|
|
662
|
+
Object.values(value).forEach(visit);
|
|
663
|
+
};
|
|
664
|
+
visit(target);
|
|
665
|
+
return output;
|
|
666
|
+
}
|
|
667
|
+
//# sourceMappingURL=library-artifact.js.map
|