@jigging/jig 0.0.0 → 0.1.0-alpha.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/LICENSE +373 -0
- package/README.md +124 -1
- package/THIRD_PARTY_NOTICES +83 -0
- package/bin/jig +42 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1975 -0
- package/dist/json.d.ts +21 -0
- package/dist/project/author.d.ts +47 -0
- package/libexec/evaluator/project-authoring-1.schema.json +111 -0
- package/libexec/evaluator/project-evaluator-sdk.bundle.js +1975 -0
- package/libexec/evaluator/project-evaluator-worker.js +431 -0
- package/libexec/installed-cli.js +25840 -0
- package/libexec/linux-rootless-supervisor.js +623 -0
- package/libexec/preparation/bun-native-preparation-worker.js +388 -0
- package/package.json +68 -6
- package/bun.lock +0 -26
- package/index.ts +0 -1
- package/tsconfig.json +0 -30
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/internal/bun-native-preparation-worker.ts
|
|
3
|
+
import { spawn } from "child_process";
|
|
4
|
+
import {
|
|
5
|
+
copyFile,
|
|
6
|
+
lstat,
|
|
7
|
+
mkdir,
|
|
8
|
+
opendir,
|
|
9
|
+
readFile,
|
|
10
|
+
rm,
|
|
11
|
+
writeFile
|
|
12
|
+
} from "fs/promises";
|
|
13
|
+
import { dirname, join } from "path";
|
|
14
|
+
|
|
15
|
+
// src/internal/bun-native-lock-policy.ts
|
|
16
|
+
function requirePrivateBunLockPolicy(value) {
|
|
17
|
+
const lock = ordinaryRecord(value);
|
|
18
|
+
const workspaces = ordinaryRecord(lock?.workspaces);
|
|
19
|
+
const rootWorkspace = ordinaryRecord(workspaces?.[""]);
|
|
20
|
+
const packages = ordinaryRecord(lock?.packages);
|
|
21
|
+
if (lock?.lockfileVersion !== 1 || workspaces === undefined || rootWorkspace === undefined || packages === undefined || Object.keys(workspaces).length !== 1 || lock.patchedDependencies !== undefined) {
|
|
22
|
+
throw new TypeError("unsupported Bun lock source");
|
|
23
|
+
}
|
|
24
|
+
for (const resolution of Object.values(packages)) {
|
|
25
|
+
if (!Array.isArray(resolution) || resolution.length !== 4 || !isExactRegistryResolution(resolution[0]) || typeof resolution[1] !== "string" || resolution[1] !== "" && resolution[1] !== "https://registry.npmjs.org" && resolution[1] !== "https://registry.npmjs.org/" || ordinaryRecord(resolution[2]) === undefined || !isExactIntegrity(resolution[3])) {
|
|
26
|
+
throw new TypeError("unsupported Bun lock source");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function isExactRegistryResolution(value) {
|
|
31
|
+
if (typeof value !== "string")
|
|
32
|
+
return false;
|
|
33
|
+
const separator = value.lastIndexOf("@");
|
|
34
|
+
if (separator < 1)
|
|
35
|
+
return false;
|
|
36
|
+
const name = value.slice(0, separator);
|
|
37
|
+
const version = value.slice(separator + 1);
|
|
38
|
+
const packageName = name.startsWith("@") ? /^@[a-z0-9][a-z0-9._~-]*\/[a-z0-9][a-z0-9._~-]*$/.test(name) : /^[a-z0-9][a-z0-9._~-]*$/.test(name);
|
|
39
|
+
return packageName && Buffer.byteLength(name) <= 214 && /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.test(version);
|
|
40
|
+
}
|
|
41
|
+
function isExactIntegrity(value) {
|
|
42
|
+
if (typeof value !== "string")
|
|
43
|
+
return false;
|
|
44
|
+
const match = /^(sha256|sha512)-([A-Za-z0-9+/]+={0,2})$/.exec(value);
|
|
45
|
+
if (match === null)
|
|
46
|
+
return false;
|
|
47
|
+
const bytes = Buffer.from(match[2], "base64");
|
|
48
|
+
const expectedBytes = match[1] === "sha256" ? 32 : 64;
|
|
49
|
+
return bytes.byteLength === expectedBytes && bytes.toString("base64") === match[2];
|
|
50
|
+
}
|
|
51
|
+
function ordinaryRecord(value) {
|
|
52
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || Object.getPrototypeOf(value) !== Object.prototype)
|
|
53
|
+
return;
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/package/paths.ts
|
|
58
|
+
var encoder = new TextEncoder;
|
|
59
|
+
var PACKAGE_1_MAX_PATH_BYTES = 1024;
|
|
60
|
+
|
|
61
|
+
// src/internal/bun-native-preparation-protocol.ts
|
|
62
|
+
var MIB = 1024 * 1024;
|
|
63
|
+
var MAX_JSON_BYTES_PER_PATH_BYTE = 6;
|
|
64
|
+
var PRIVATE_BUN_PREPARATION_LIMITS = Object.freeze({
|
|
65
|
+
sourceFiles: 4096,
|
|
66
|
+
sourceBytes: 16 * MIB,
|
|
67
|
+
preparedFiles: 4096,
|
|
68
|
+
preparedBytes: 32 * MIB
|
|
69
|
+
});
|
|
70
|
+
function maximumPrivateBunFileMessageBytes(type, maximumFiles, maximumContentBytes) {
|
|
71
|
+
requireNonnegativeInteger(maximumFiles, "file count");
|
|
72
|
+
requireNonnegativeInteger(maximumContentBytes, "content bytes");
|
|
73
|
+
const emptyMessageBytes = Buffer.byteLength(JSON.stringify({ type, files: [] }));
|
|
74
|
+
const emptyFileBytes = Buffer.byteLength(JSON.stringify({ path: "", content: "" }));
|
|
75
|
+
const pathBytes = maximumFiles * PACKAGE_1_MAX_PATH_BYTES * MAX_JSON_BYTES_PER_PATH_BYTE;
|
|
76
|
+
const contentBytes = 4 * (Math.ceil(maximumContentBytes / 3) + maximumFiles);
|
|
77
|
+
const separators = Math.max(0, maximumFiles - 1);
|
|
78
|
+
const result = emptyMessageBytes + maximumFiles * emptyFileBytes + pathBytes + contentBytes + separators;
|
|
79
|
+
if (!Number.isSafeInteger(result))
|
|
80
|
+
throw new RangeError("Bun preparation message bound is too large");
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
var PRIVATE_BUN_SOURCE_MESSAGE_BYTES = maximumPrivateBunFileMessageBytes("source", PRIVATE_BUN_PREPARATION_LIMITS.sourceFiles, PRIVATE_BUN_PREPARATION_LIMITS.sourceBytes);
|
|
84
|
+
var PRIVATE_BUN_PREPARED_MESSAGE_BYTES = maximumPrivateBunFileMessageBytes("prepared", PRIVATE_BUN_PREPARATION_LIMITS.preparedFiles, PRIVATE_BUN_PREPARATION_LIMITS.preparedBytes);
|
|
85
|
+
function privateBunMessageFits(bytes, maximum) {
|
|
86
|
+
return Number.isSafeInteger(bytes) && bytes >= 0 && bytes <= maximum;
|
|
87
|
+
}
|
|
88
|
+
function encodePrivateBunMessage(value) {
|
|
89
|
+
const json = JSON.stringify(value);
|
|
90
|
+
if (json === undefined)
|
|
91
|
+
throw new TypeError("Bun preparation message is not JSON-serializable");
|
|
92
|
+
return new TextEncoder().encode(`${json}
|
|
93
|
+
`);
|
|
94
|
+
}
|
|
95
|
+
function requireNonnegativeInteger(value, label) {
|
|
96
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
97
|
+
throw new TypeError(`${label} must be a nonnegative safe integer`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/internal/bun-native-preparation-worker.ts
|
|
102
|
+
var PACKAGE_ROOT = "/work/package";
|
|
103
|
+
var CACHE_ROOT = "/work/cache";
|
|
104
|
+
var PATH = /^(?!\/)(?!.*(?:^|\/)\.\.?(?:\/|$))(?!.*\/\/)[^\0]+$/;
|
|
105
|
+
|
|
106
|
+
class WorkerFailure extends Error {
|
|
107
|
+
code;
|
|
108
|
+
constructor(code, message) {
|
|
109
|
+
super(message);
|
|
110
|
+
this.code = code;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
var outputQueue = Promise.resolve();
|
|
114
|
+
try {
|
|
115
|
+
const iterator = jsonLines(process.stdin, PRIVATE_BUN_SOURCE_MESSAGE_BYTES)[Symbol.asyncIterator]();
|
|
116
|
+
const first = await iterator.next();
|
|
117
|
+
if (first.done)
|
|
118
|
+
throw new WorkerFailure("PACKAGE_BUN_PROTOCOL", "preparation source is missing");
|
|
119
|
+
const source = requireSource(first.value);
|
|
120
|
+
if (!(await iterator.next()).done) {
|
|
121
|
+
throw new WorkerFailure("PACKAGE_BUN_PROTOCOL", "preparation source has trailing data");
|
|
122
|
+
}
|
|
123
|
+
await materializeSource(source.files);
|
|
124
|
+
await requireSupportedLock();
|
|
125
|
+
await install();
|
|
126
|
+
await verifySource(source.files);
|
|
127
|
+
const prepared = await capturePrepared();
|
|
128
|
+
await sendPrepared(prepared);
|
|
129
|
+
await outputQueue;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
const failure = error instanceof WorkerFailure ? error : new WorkerFailure("PACKAGE_BUN_PREPARATION_FAILED", "locked Bun dependency preparation failed");
|
|
132
|
+
await send({ type: "failure", code: failure.code, message: failure.message }).catch(() => {
|
|
133
|
+
return;
|
|
134
|
+
});
|
|
135
|
+
await outputQueue.catch(() => {
|
|
136
|
+
return;
|
|
137
|
+
});
|
|
138
|
+
process.exitCode = 1;
|
|
139
|
+
}
|
|
140
|
+
async function materializeSource(files) {
|
|
141
|
+
await mkdir(PACKAGE_ROOT, { recursive: true, mode: 448 });
|
|
142
|
+
for (const file of files) {
|
|
143
|
+
const bytes = decodeBase64(file.content, file.path);
|
|
144
|
+
const destination = join(PACKAGE_ROOT, ...file.path.split("/"));
|
|
145
|
+
await mkdir(dirname(destination), { recursive: true, mode: 448 });
|
|
146
|
+
await writeFile(destination, bytes, { mode: 384, flag: "wx" });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function verifySource(files) {
|
|
150
|
+
for (const file of files) {
|
|
151
|
+
const expected = decodeBase64(file.content, file.path);
|
|
152
|
+
const actual = new Uint8Array(await readFile(join(PACKAGE_ROOT, ...file.path.split("/"))));
|
|
153
|
+
if (!Buffer.from(actual).equals(Buffer.from(expected))) {
|
|
154
|
+
throw new WorkerFailure("PACKAGE_BUN_SOURCE_CHANGED", "Bun preparation changed authored package bytes");
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async function requireSupportedLock() {
|
|
159
|
+
const lockCopy = "/work/bun-lock.jsonc";
|
|
160
|
+
await copyFile(join(PACKAGE_ROOT, "bun.lock"), lockCopy);
|
|
161
|
+
let value;
|
|
162
|
+
try {
|
|
163
|
+
const result = await runChild([
|
|
164
|
+
"--no-env-file",
|
|
165
|
+
"--no-install",
|
|
166
|
+
"--config=/dev/null",
|
|
167
|
+
"-e",
|
|
168
|
+
"const value=(await import(process.argv[1])).default;process.stdout.write(JSON.stringify(value));",
|
|
169
|
+
lockCopy
|
|
170
|
+
], {
|
|
171
|
+
cwd: "/work",
|
|
172
|
+
env: { LD_LIBRARY_PATH: "/jig-runtime/lib" }
|
|
173
|
+
}, 2 * 1024 * 1024, 64 * 1024);
|
|
174
|
+
if (result.exit !== 0 || result.stderr !== "")
|
|
175
|
+
throw new Error("lock parser failed");
|
|
176
|
+
value = JSON.parse(result.stdout);
|
|
177
|
+
} catch {
|
|
178
|
+
throw new WorkerFailure("PACKAGE_BUN_LOCK_INVALID", "bun.lock is not valid for the pinned Bun runtime");
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
requirePrivateBunLockPolicy(value);
|
|
182
|
+
} catch {
|
|
183
|
+
unsupportedSource();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function unsupportedSource() {
|
|
187
|
+
throw new WorkerFailure("PACKAGE_BUN_SOURCE_UNSUPPORTED", "bun.lock contains a dependency source unsupported by this Jig alpha; only the default npm registry is supported");
|
|
188
|
+
}
|
|
189
|
+
async function install() {
|
|
190
|
+
const result = await runChild([
|
|
191
|
+
"--no-env-file",
|
|
192
|
+
"--config=/dev/null",
|
|
193
|
+
"install",
|
|
194
|
+
"--frozen-lockfile",
|
|
195
|
+
"--production",
|
|
196
|
+
"--ignore-scripts",
|
|
197
|
+
"--backend=copyfile",
|
|
198
|
+
"--linker=hoisted",
|
|
199
|
+
`--cache-dir=${CACHE_ROOT}`,
|
|
200
|
+
"--registry=https://registry.npmjs.org",
|
|
201
|
+
"--no-progress",
|
|
202
|
+
"--no-summary"
|
|
203
|
+
], {
|
|
204
|
+
cwd: PACKAGE_ROOT,
|
|
205
|
+
env: {
|
|
206
|
+
LD_LIBRARY_PATH: "/jig-runtime/lib"
|
|
207
|
+
}
|
|
208
|
+
}, 64 * 1024, 64 * 1024);
|
|
209
|
+
if (result.exit !== 0) {
|
|
210
|
+
const output = `${result.stdout}
|
|
211
|
+
${result.stderr}`;
|
|
212
|
+
if (/lockfile|frozen|package\.json/i.test(output)) {
|
|
213
|
+
throw new WorkerFailure("PACKAGE_BUN_LOCK_STALE", "package.json and bun.lock disagree; regenerate bun.lock with bun install --lockfile-only");
|
|
214
|
+
}
|
|
215
|
+
throw new WorkerFailure("PACKAGE_BUN_PREPARATION_FAILED", "the locked production dependencies could not be prepared by the pinned Bun runtime");
|
|
216
|
+
}
|
|
217
|
+
await rm(join(PACKAGE_ROOT, "node_modules", ".bin"), { recursive: true, force: true });
|
|
218
|
+
}
|
|
219
|
+
async function capturePrepared() {
|
|
220
|
+
const files = [];
|
|
221
|
+
let total = 0;
|
|
222
|
+
const visit = async (root, prefix) => {
|
|
223
|
+
const directory = await opendir(root);
|
|
224
|
+
const names = [];
|
|
225
|
+
for await (const entry of directory)
|
|
226
|
+
names.push(entry.name);
|
|
227
|
+
names.sort((left, right) => Buffer.from(left).compare(Buffer.from(right)));
|
|
228
|
+
for (const name of names) {
|
|
229
|
+
const path = prefix === "" ? name : `${prefix}/${name}`;
|
|
230
|
+
requirePath(path);
|
|
231
|
+
const physical = join(root, name);
|
|
232
|
+
const information = await lstat(physical);
|
|
233
|
+
if (information.isDirectory() && !information.isSymbolicLink()) {
|
|
234
|
+
await visit(physical, path);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (!information.isFile() || information.isSymbolicLink() || information.nlink !== 1) {
|
|
238
|
+
throw new WorkerFailure("PACKAGE_BUN_OUTPUT_UNSUPPORTED", "prepared dependencies contain a link or special file");
|
|
239
|
+
}
|
|
240
|
+
if (files.length >= PRIVATE_BUN_PREPARATION_LIMITS.preparedFiles) {
|
|
241
|
+
throw new WorkerFailure("PACKAGE_BUN_OUTPUT_LIMIT", "prepared dependency tree has too many files");
|
|
242
|
+
}
|
|
243
|
+
const bytes = new Uint8Array(await readFile(physical));
|
|
244
|
+
total += bytes.byteLength;
|
|
245
|
+
if (total > PRIVATE_BUN_PREPARATION_LIMITS.preparedBytes) {
|
|
246
|
+
throw new WorkerFailure("PACKAGE_BUN_OUTPUT_LIMIT", "prepared dependency tree is too large");
|
|
247
|
+
}
|
|
248
|
+
files.push(Object.freeze({ path, content: Buffer.from(bytes).toString("base64") }));
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
await visit(PACKAGE_ROOT, "");
|
|
252
|
+
files.sort((left, right) => Buffer.from(left.path).compare(Buffer.from(right.path)));
|
|
253
|
+
return Object.freeze(files);
|
|
254
|
+
}
|
|
255
|
+
function requireSource(value) {
|
|
256
|
+
const root = ordinaryRecord2(value);
|
|
257
|
+
if (root?.type !== "source" || !Array.isArray(root.files) || root.files.length > PRIVATE_BUN_PREPARATION_LIMITS.sourceFiles) {
|
|
258
|
+
throw new WorkerFailure("PACKAGE_BUN_PROTOCOL", "preparation source is invalid");
|
|
259
|
+
}
|
|
260
|
+
const files = [];
|
|
261
|
+
let total = 0;
|
|
262
|
+
let prior;
|
|
263
|
+
for (const raw of root.files) {
|
|
264
|
+
const file = ordinaryRecord2(raw);
|
|
265
|
+
if (file === undefined || typeof file.path !== "string" || typeof file.content !== "string") {
|
|
266
|
+
throw new WorkerFailure("PACKAGE_BUN_PROTOCOL", "preparation source file is invalid");
|
|
267
|
+
}
|
|
268
|
+
requirePath(file.path);
|
|
269
|
+
if (prior !== undefined && Buffer.from(prior).compare(Buffer.from(file.path)) >= 0) {
|
|
270
|
+
throw new WorkerFailure("PACKAGE_BUN_PROTOCOL", "preparation source paths are not canonical");
|
|
271
|
+
}
|
|
272
|
+
prior = file.path;
|
|
273
|
+
const bytes = decodeBase64(file.content, file.path);
|
|
274
|
+
total += bytes.byteLength;
|
|
275
|
+
if (total > PRIVATE_BUN_PREPARATION_LIMITS.sourceBytes) {
|
|
276
|
+
throw new WorkerFailure("PACKAGE_BUN_INPUT_LIMIT", "locked Bun package is too large to prepare");
|
|
277
|
+
}
|
|
278
|
+
files.push(Object.freeze({ path: file.path, content: file.content }));
|
|
279
|
+
}
|
|
280
|
+
if (!files.some(({ path }) => path === "package.json") || !files.some(({ path }) => path === "bun.lock")) {
|
|
281
|
+
throw new WorkerFailure("PACKAGE_BUN_PROTOCOL", "locked Bun source is incomplete");
|
|
282
|
+
}
|
|
283
|
+
return Object.freeze({ files: Object.freeze(files) });
|
|
284
|
+
}
|
|
285
|
+
function requirePath(path) {
|
|
286
|
+
if (!PATH.test(path) || Buffer.byteLength(path) > 1024) {
|
|
287
|
+
throw new WorkerFailure("PACKAGE_BUN_PROTOCOL", "preparation source path is invalid");
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function decodeBase64(value, label) {
|
|
291
|
+
if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)) {
|
|
292
|
+
throw new WorkerFailure("PACKAGE_BUN_PROTOCOL", `${label} has invalid encoded bytes`);
|
|
293
|
+
}
|
|
294
|
+
return new Uint8Array(Buffer.from(value, "base64"));
|
|
295
|
+
}
|
|
296
|
+
function ordinaryRecord2(value) {
|
|
297
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || Object.getPrototypeOf(value) !== Object.prototype)
|
|
298
|
+
return;
|
|
299
|
+
return value;
|
|
300
|
+
}
|
|
301
|
+
function send(value) {
|
|
302
|
+
return enqueue(encodePrivateBunMessage(value));
|
|
303
|
+
}
|
|
304
|
+
function enqueue(bytes) {
|
|
305
|
+
outputQueue = outputQueue.then(async () => {
|
|
306
|
+
if (!process.stdout.write(bytes))
|
|
307
|
+
await new Promise((resolve) => process.stdout.once("drain", resolve));
|
|
308
|
+
});
|
|
309
|
+
return outputQueue;
|
|
310
|
+
}
|
|
311
|
+
function sendPrepared(files) {
|
|
312
|
+
const bytes = encodePrivateBunMessage({ type: "prepared", files });
|
|
313
|
+
if (!privateBunMessageFits(bytes.byteLength - 1, PRIVATE_BUN_PREPARED_MESSAGE_BYTES)) {
|
|
314
|
+
throw new WorkerFailure("PACKAGE_BUN_OUTPUT_LIMIT", "prepared dependency tree is too large");
|
|
315
|
+
}
|
|
316
|
+
return enqueue(bytes);
|
|
317
|
+
}
|
|
318
|
+
async function* jsonLines(source, maximum) {
|
|
319
|
+
let pending = [];
|
|
320
|
+
let pendingBytes = 0;
|
|
321
|
+
for await (const chunk of source) {
|
|
322
|
+
const bytes = Buffer.from(chunk);
|
|
323
|
+
let start = 0;
|
|
324
|
+
for (;; ) {
|
|
325
|
+
const end = bytes.indexOf(10, start);
|
|
326
|
+
if (end === -1)
|
|
327
|
+
break;
|
|
328
|
+
pending.push(bytes.subarray(start, end));
|
|
329
|
+
pendingBytes += end - start;
|
|
330
|
+
if (pendingBytes > maximum)
|
|
331
|
+
throw new Error("protocol line exceeds bound");
|
|
332
|
+
const line = pending.length === 1 ? pending[0] : Buffer.concat(pending, pendingBytes);
|
|
333
|
+
pending = [];
|
|
334
|
+
pendingBytes = 0;
|
|
335
|
+
yield JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(line));
|
|
336
|
+
start = end + 1;
|
|
337
|
+
}
|
|
338
|
+
if (start < bytes.byteLength) {
|
|
339
|
+
pending.push(bytes.subarray(start));
|
|
340
|
+
pendingBytes += bytes.byteLength - start;
|
|
341
|
+
if (pendingBytes > maximum)
|
|
342
|
+
throw new Error("protocol line exceeds bound");
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (pendingBytes !== 0)
|
|
346
|
+
throw new Error("protocol ended with a partial line");
|
|
347
|
+
}
|
|
348
|
+
async function collect(stream, maximum) {
|
|
349
|
+
if (stream === null)
|
|
350
|
+
return "";
|
|
351
|
+
let bytes = Buffer.alloc(0);
|
|
352
|
+
for await (const chunk of stream) {
|
|
353
|
+
bytes = Buffer.concat([bytes, Buffer.from(chunk)]);
|
|
354
|
+
if (bytes.byteLength > maximum)
|
|
355
|
+
throw new Error("preparation child diagnostic exceeds bound");
|
|
356
|
+
}
|
|
357
|
+
return bytes.toString("utf8");
|
|
358
|
+
}
|
|
359
|
+
async function runChild(arguments_, options, maximumStdout, maximumStderr) {
|
|
360
|
+
const child = spawn(process.execPath, [...arguments_], {
|
|
361
|
+
...options,
|
|
362
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
363
|
+
});
|
|
364
|
+
const exit = childExit(child);
|
|
365
|
+
let settled = false;
|
|
366
|
+
try {
|
|
367
|
+
const [stdout, stderr, code] = await Promise.all([
|
|
368
|
+
collect(child.stdout, maximumStdout),
|
|
369
|
+
collect(child.stderr, maximumStderr),
|
|
370
|
+
exit
|
|
371
|
+
]);
|
|
372
|
+
settled = true;
|
|
373
|
+
return Object.freeze({ stdout, stderr, exit: code });
|
|
374
|
+
} finally {
|
|
375
|
+
if (!settled) {
|
|
376
|
+
child.kill("SIGKILL");
|
|
377
|
+
await exit.catch(() => {
|
|
378
|
+
return;
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
function childExit(child) {
|
|
384
|
+
return new Promise((resolve, reject) => {
|
|
385
|
+
child.once("error", reject);
|
|
386
|
+
child.once("close", (code) => resolve(code));
|
|
387
|
+
});
|
|
388
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,74 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jigging/jig",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.1.0-alpha.2",
|
|
4
|
+
"description": "A local, secure host for admitted FLOW packages",
|
|
5
|
+
"license": "MPL-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/jigmd/jig.git",
|
|
9
|
+
"directory": "packages/jig"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://jig.md/",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/jigmd/jig/issues"
|
|
14
|
+
},
|
|
15
|
+
"os": [
|
|
16
|
+
"linux"
|
|
17
|
+
],
|
|
18
|
+
"cpu": [
|
|
19
|
+
"x64"
|
|
20
|
+
],
|
|
21
|
+
"libc": [
|
|
22
|
+
"glibc"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
5
27
|
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"main": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"import": "./dist/index.js"
|
|
35
|
+
}
|
|
8
36
|
},
|
|
9
|
-
"
|
|
10
|
-
"
|
|
37
|
+
"bin": {
|
|
38
|
+
"jig": "./bin/jig"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"bin/jig",
|
|
42
|
+
"dist/index.js",
|
|
43
|
+
"dist/index.d.ts",
|
|
44
|
+
"dist/project/author.d.ts",
|
|
45
|
+
"dist/json.d.ts",
|
|
46
|
+
"libexec/installed-cli.js",
|
|
47
|
+
"libexec/linux-rootless-supervisor.js",
|
|
48
|
+
"libexec/evaluator/project-evaluator-worker.js",
|
|
49
|
+
"libexec/evaluator/project-evaluator-sdk.bundle.js",
|
|
50
|
+
"libexec/evaluator/project-authoring-1.schema.json",
|
|
51
|
+
"libexec/preparation/bun-native-preparation-worker.js",
|
|
52
|
+
"README.md",
|
|
53
|
+
"LICENSE",
|
|
54
|
+
"THIRD_PARTY_NOTICES"
|
|
55
|
+
],
|
|
56
|
+
"scripts": {
|
|
57
|
+
"verify:build-bun": "bun -e \"if (Bun.version !== '1.3.3' || Bun.revision !== '274e01c737e85f8142070a9745b43a2ba09fce4c') throw new Error('Jig must be built with exact Bun 1.3.3 revision 274e01c737e85f8142070a9745b43a2ba09fce4c')\"",
|
|
58
|
+
"build": "bun run verify:build-bun && bun run clean && mkdir -p bin libexec/evaluator libexec/preparation && cp scripts/installed-jig.sh bin/jig && chmod 755 bin/jig && bun node_modules/typescript/bin/tsc -p tsconfig.json && bun build src/index.ts --target=bun --format=esm --outfile=dist/index.js && bun build src/installed-cli.ts --target=bun --format=esm --outfile=libexec/installed-cli.js && bun build src/internal/linux-rootless-supervisor.ts --target=bun --format=esm --outfile=libexec/linux-rootless-supervisor.js && bun build src/internal/project-evaluator-worker.ts --target=bun --format=esm --outfile=libexec/evaluator/project-evaluator-worker.js && bun build src/internal/bun-native-preparation-worker.ts --target=bun --format=esm --outfile=libexec/preparation/bun-native-preparation-worker.js && bun build src/project/evaluator-sdk-entry.ts --target=bun --format=esm --outfile=libexec/evaluator/project-evaluator-sdk.bundle.js && cp ../../docs/jig/spec/machine/project-authoring-1.schema.json libexec/evaluator/project-authoring-1.schema.json",
|
|
59
|
+
"clean": "bun -e \"import { rm } from 'node:fs/promises'; for (const path of ['dist', 'bin', 'libexec']) await rm(path, { recursive: true, force: true })\"",
|
|
60
|
+
"prepack": "bun run build",
|
|
61
|
+
"test": "bun test",
|
|
62
|
+
"test:package": "bun run build && bun test/package-smoke.ts",
|
|
63
|
+
"test:linux-rootless:proof-host": "bun run build && JIG_LINUX_ROOTLESS_HOSTILE=1 bun test test/linux-rootless-delegation-hostile.test.ts --timeout 30000 && JIG_LINUX_ROOTLESS_HOSTILE=1 bun test test/linux-rootless-run.test.ts --timeout 30000 && JIG_LINUX_ROOTLESS_HOSTILE=1 bun test test/bun-native-preparation.test.ts --timeout 120000 && JIG_LINUX_ROOTLESS_HOSTILE=1 bun test test/private-foreground.test.ts --timeout 30000",
|
|
64
|
+
"check": "bun run build && bun test"
|
|
65
|
+
},
|
|
66
|
+
"dependencies": {
|
|
67
|
+
"@oven/bun-linux-x64-baseline": "1.3.3"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@types/node": "24.13.3",
|
|
71
|
+
"typescript": "7.0.2",
|
|
72
|
+
"yaml": "2.9.0"
|
|
11
73
|
}
|
|
12
74
|
}
|
package/bun.lock
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"lockfileVersion": 1,
|
|
3
|
-
"configVersion": 1,
|
|
4
|
-
"workspaces": {
|
|
5
|
-
"": {
|
|
6
|
-
"name": "jig",
|
|
7
|
-
"devDependencies": {
|
|
8
|
-
"@types/bun": "latest",
|
|
9
|
-
},
|
|
10
|
-
"peerDependencies": {
|
|
11
|
-
"typescript": "^5",
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
},
|
|
15
|
-
"packages": {
|
|
16
|
-
"@types/bun": ["@types/bun@1.4.0", "", { "dependencies": { "bun-types": "1.4.0" } }, "sha512-K+lZULY23vRgK/CfTjFIV+tyifaNdSMlPh9j+6mQ/cLfpOznLyAuzgV/JQysyECpkBQLVMSyvjlr2fBUSA9wFQ=="],
|
|
17
|
-
|
|
18
|
-
"@types/node": ["@types/node@26.4.1", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-k97ENvZWtvA6yqz5/FS6a7duDgOPEeOQOc2iKS/nY6mX6qJUKtLnWzQS+Xj6tXweyj6ZcTAK2Qecetnvi9nCLA=="],
|
|
19
|
-
|
|
20
|
-
"bun-types": ["bun-types@1.4.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-iIKw23BspnQQYd3prITOBxeUsxBHnwzX6YJfGMuNOZzeNcMmVqzIIVGRm1l69ogaPQmb4wB6BN8mA5bE9YuC5Q=="],
|
|
21
|
-
|
|
22
|
-
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
23
|
-
|
|
24
|
-
"undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="],
|
|
25
|
-
}
|
|
26
|
-
}
|
package/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
console.log("Hello via Bun!");
|
package/tsconfig.json
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
// Environment setup & latest features
|
|
4
|
-
"lib": ["ESNext"],
|
|
5
|
-
"target": "ESNext",
|
|
6
|
-
"module": "Preserve",
|
|
7
|
-
"moduleDetection": "force",
|
|
8
|
-
"jsx": "react-jsx",
|
|
9
|
-
"allowJs": true,
|
|
10
|
-
"types": ["bun"],
|
|
11
|
-
|
|
12
|
-
// Bundler mode
|
|
13
|
-
"moduleResolution": "bundler",
|
|
14
|
-
"allowImportingTsExtensions": true,
|
|
15
|
-
"verbatimModuleSyntax": true,
|
|
16
|
-
"noEmit": true,
|
|
17
|
-
|
|
18
|
-
// Best practices
|
|
19
|
-
"strict": true,
|
|
20
|
-
"skipLibCheck": true,
|
|
21
|
-
"noFallthroughCasesInSwitch": true,
|
|
22
|
-
"noUncheckedIndexedAccess": true,
|
|
23
|
-
"noImplicitOverride": true,
|
|
24
|
-
|
|
25
|
-
// Some stricter flags (disabled by default)
|
|
26
|
-
"noUnusedLocals": false,
|
|
27
|
-
"noUnusedParameters": false,
|
|
28
|
-
"noPropertyAccessFromIndexSignature": false
|
|
29
|
-
}
|
|
30
|
-
}
|