notchapp 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.mjs +391 -28
- package/package.json +4 -2
- package/security-policy.mjs +27 -0
package/cli.mjs
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import Module from "node:module";
|
|
2
3
|
import os from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import process from "node:process";
|
|
5
6
|
import { spawn } from "node:child_process";
|
|
7
|
+
import { parse } from "acorn";
|
|
6
8
|
import esbuild from "esbuild";
|
|
9
|
+
import { ALLOWED_BUILTIN_SPECIFIERS } from "./security-policy.mjs";
|
|
7
10
|
|
|
8
11
|
const command = process.argv[2];
|
|
9
12
|
const packageDir = process.cwd();
|
|
@@ -14,11 +17,47 @@ const canonicalWidgetsRoot = path.join(
|
|
|
14
17
|
"NotchApp",
|
|
15
18
|
"Widgets",
|
|
16
19
|
);
|
|
20
|
+
const BUILTIN_ROOTS = new Set(
|
|
21
|
+
Module.builtinModules
|
|
22
|
+
.map((specifier) => normalizeBuiltinRoot(specifier))
|
|
23
|
+
.filter(Boolean)
|
|
24
|
+
);
|
|
25
|
+
const ALLOWED_BUILTIN_SPECIFIER_SET = new Set(ALLOWED_BUILTIN_SPECIFIERS);
|
|
17
26
|
|
|
18
27
|
function packageRootFromMeta(metaURL) {
|
|
19
28
|
return path.resolve(path.dirname(new URL(metaURL).pathname), "..", "..");
|
|
20
29
|
}
|
|
21
30
|
|
|
31
|
+
function normalizeBuiltinRoot(request) {
|
|
32
|
+
if (typeof request !== "string" || request.length === 0) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const withoutPrefix = request.startsWith("node:") ? request.slice(5) : request;
|
|
37
|
+
return withoutPrefix.split("/")[0] || null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function normalizeBuiltinRequest(request) {
|
|
41
|
+
if (typeof request !== "string" || request.length === 0) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const withoutPrefix = request.startsWith("node:") ? request.slice(5) : request;
|
|
46
|
+
const root = normalizeBuiltinRoot(request);
|
|
47
|
+
if (!root) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (request.startsWith("node:") || BUILTIN_ROOTS.has(withoutPrefix) || BUILTIN_ROOTS.has(root)) {
|
|
52
|
+
return {
|
|
53
|
+
specifier: withoutPrefix,
|
|
54
|
+
root,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
22
61
|
function developmentWidgetsRoot() {
|
|
23
62
|
const workspaceRoot = packageRootFromMeta(import.meta.url);
|
|
24
63
|
return path.join(workspaceRoot, "widgets");
|
|
@@ -42,6 +81,8 @@ function readManifest(targetPackageDir) {
|
|
|
42
81
|
throw new Error(`Invalid span range in ${manifestPath}`);
|
|
43
82
|
}
|
|
44
83
|
|
|
84
|
+
validatePreferences(notch.preferences ?? [], manifestPath);
|
|
85
|
+
|
|
45
86
|
const entryFile = path.join(targetPackageDir, notch.entry ?? "src/index.tsx");
|
|
46
87
|
if (!fs.existsSync(entryFile)) {
|
|
47
88
|
throw new Error(`Missing widget entry file at ${entryFile}`);
|
|
@@ -50,6 +91,67 @@ function readManifest(targetPackageDir) {
|
|
|
50
91
|
return { manifest, manifestPath, entryFile };
|
|
51
92
|
}
|
|
52
93
|
|
|
94
|
+
const SUPPORTED_PREFERENCE_TYPES = new Set([
|
|
95
|
+
"textfield",
|
|
96
|
+
"password",
|
|
97
|
+
"checkbox",
|
|
98
|
+
"dropdown",
|
|
99
|
+
]);
|
|
100
|
+
|
|
101
|
+
function validatePreferences(preferences, manifestPath) {
|
|
102
|
+
if (!Array.isArray(preferences)) {
|
|
103
|
+
throw new Error(`Invalid preferences in ${manifestPath}: preferences must be an array`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const seen = new Set();
|
|
107
|
+
for (const preference of preferences) {
|
|
108
|
+
if (!preference || typeof preference !== "object" || Array.isArray(preference)) {
|
|
109
|
+
throw new Error(`Invalid preferences in ${manifestPath}: each preference must be an object`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!preference.name || !preference.title || !preference.type) {
|
|
113
|
+
throw new Error(`Invalid preferences in ${manifestPath}: each preference must include name/title/type`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (seen.has(preference.name)) {
|
|
117
|
+
throw new Error(`Invalid preferences in ${manifestPath}: duplicate preference name '${preference.name}'`);
|
|
118
|
+
}
|
|
119
|
+
seen.add(preference.name);
|
|
120
|
+
|
|
121
|
+
if (!SUPPORTED_PREFERENCE_TYPES.has(preference.type)) {
|
|
122
|
+
throw new Error(`Invalid preferences in ${manifestPath}: unsupported preference type '${preference.type}'`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
switch (preference.type) {
|
|
126
|
+
case "textfield":
|
|
127
|
+
case "password":
|
|
128
|
+
if (Object.hasOwn(preference, "default") && typeof preference.default !== "string") {
|
|
129
|
+
throw new Error(`Invalid preferences in ${manifestPath}: '${preference.name}' must use a string default`);
|
|
130
|
+
}
|
|
131
|
+
break;
|
|
132
|
+
case "checkbox":
|
|
133
|
+
if (Object.hasOwn(preference, "default") && typeof preference.default !== "boolean") {
|
|
134
|
+
throw new Error(`Invalid preferences in ${manifestPath}: '${preference.name}' must use a boolean default`);
|
|
135
|
+
}
|
|
136
|
+
break;
|
|
137
|
+
case "dropdown":
|
|
138
|
+
if (!Array.isArray(preference.data) || preference.data.length === 0) {
|
|
139
|
+
throw new Error(`Invalid preferences in ${manifestPath}: dropdown '${preference.name}' must include data`);
|
|
140
|
+
}
|
|
141
|
+
if (preference.data.some((item) => !item?.title || !Object.hasOwn(item, "value"))) {
|
|
142
|
+
throw new Error(`Invalid preferences in ${manifestPath}: dropdown '${preference.name}' entries must include title/value`);
|
|
143
|
+
}
|
|
144
|
+
if (Object.hasOwn(preference, "default")
|
|
145
|
+
&& !preference.data.some((item) => JSON.stringify(item.value) === JSON.stringify(preference.default))) {
|
|
146
|
+
throw new Error(`Invalid preferences in ${manifestPath}: dropdown '${preference.name}' default must appear in data`);
|
|
147
|
+
}
|
|
148
|
+
break;
|
|
149
|
+
default:
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
53
155
|
function ensureCanonicalSymlink(targetPackageDir, manifest) {
|
|
54
156
|
fs.mkdirSync(canonicalWidgetsRoot, { recursive: true });
|
|
55
157
|
|
|
@@ -172,23 +274,260 @@ function snapshotsDiffer(previousSnapshot, nextSnapshot) {
|
|
|
172
274
|
return false;
|
|
173
275
|
}
|
|
174
276
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
277
|
+
const notchAppExecutableSuffix = `${path.sep}Contents${path.sep}MacOS${path.sep}NotchApp`;
|
|
278
|
+
|
|
279
|
+
function devEventURL(event, widgetID, info = "") {
|
|
280
|
+
const query = new URLSearchParams({ cwd: process.cwd() });
|
|
281
|
+
if (info) {
|
|
282
|
+
query.set("info", info);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return `notch://cli/${encodeURIComponent(widgetID)}/${event}?${query.toString()}`;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function parseRunningNotchAppBundlePaths(psOutput) {
|
|
289
|
+
const bundlePaths = new Set();
|
|
290
|
+
|
|
291
|
+
for (const rawLine of psOutput.split(/\r?\n/)) {
|
|
292
|
+
const line = rawLine.trim();
|
|
293
|
+
if (line.length === 0) {
|
|
294
|
+
continue;
|
|
180
295
|
}
|
|
181
296
|
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
297
|
+
const executableIndex = line.indexOf(notchAppExecutableSuffix);
|
|
298
|
+
if (executableIndex < 0) {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const bundlePath = line.slice(0, executableIndex);
|
|
303
|
+
if (!bundlePath.endsWith(".app")) {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
bundlePaths.add(bundlePath);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return [...bundlePaths];
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function runCommand(command, args, options = {}) {
|
|
314
|
+
return new Promise((resolve) => {
|
|
315
|
+
const child = spawn(command, args, {
|
|
316
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
317
|
+
...options,
|
|
318
|
+
});
|
|
319
|
+
let stdout = "";
|
|
320
|
+
let stderr = "";
|
|
321
|
+
|
|
322
|
+
child.stdout?.on("data", (chunk) => {
|
|
323
|
+
stdout += chunk;
|
|
324
|
+
});
|
|
325
|
+
child.stderr?.on("data", (chunk) => {
|
|
326
|
+
stderr += chunk;
|
|
327
|
+
});
|
|
328
|
+
child.on("error", (error) => {
|
|
329
|
+
resolve({
|
|
330
|
+
code: null,
|
|
331
|
+
stdout,
|
|
332
|
+
stderr,
|
|
333
|
+
error,
|
|
334
|
+
});
|
|
186
335
|
});
|
|
187
|
-
child.
|
|
188
|
-
|
|
336
|
+
child.on("close", (code) => {
|
|
337
|
+
resolve({
|
|
338
|
+
code,
|
|
339
|
+
stdout,
|
|
340
|
+
stderr,
|
|
341
|
+
error: null,
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async function runningNotchAppBundlePaths() {
|
|
348
|
+
const result = await runCommand("ps", ["-axo", "command="]);
|
|
349
|
+
if (result.error || result.code !== 0) {
|
|
350
|
+
const warning = result.error?.message ?? (result.stderr.trim() || "Failed to inspect running processes.");
|
|
351
|
+
return {
|
|
352
|
+
bundlePaths: [],
|
|
353
|
+
warning,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
bundlePaths: parseRunningNotchAppBundlePaths(result.stdout),
|
|
359
|
+
warning: null,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function notifyApp(event, widgetID, info = "") {
|
|
364
|
+
const url = devEventURL(event, widgetID, info);
|
|
365
|
+
const { bundlePaths, warning } = await runningNotchAppBundlePaths();
|
|
366
|
+
|
|
367
|
+
if (warning) {
|
|
368
|
+
console.warn(`Warning: could not inspect running NotchApp processes: ${warning}`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (bundlePaths.length === 0) {
|
|
372
|
+
console.warn("Warning: no running NotchApp installations found; no app was notified.");
|
|
373
|
+
return {
|
|
374
|
+
notifiedCount: 0,
|
|
375
|
+
failedBundlePaths: [],
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const failedBundlePaths = [];
|
|
380
|
+
let notifiedCount = 0;
|
|
381
|
+
|
|
382
|
+
for (const bundlePath of bundlePaths) {
|
|
383
|
+
const result = await runCommand("open", ["-a", bundlePath, url]);
|
|
384
|
+
if (result.error || result.code !== 0) {
|
|
385
|
+
failedBundlePaths.push(bundlePath);
|
|
386
|
+
const details = result.error?.message ?? (result.stderr.trim() || `exit code ${result.code}`);
|
|
387
|
+
console.warn(`Warning: failed to notify ${bundlePath}: ${details}`);
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
notifiedCount += 1;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (notifiedCount > 0) {
|
|
395
|
+
console.log(`Notified ${notifiedCount} running NotchApp installation(s).`);
|
|
396
|
+
} else {
|
|
397
|
+
console.warn("Warning: found running NotchApp installations, but none accepted the widget update notification.");
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
notifiedCount,
|
|
402
|
+
failedBundlePaths,
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function failOnDynamicImport(outfile) {
|
|
407
|
+
const emitted = fs.readFileSync(outfile, "utf8");
|
|
408
|
+
const sourceMapIndex = emitted.lastIndexOf("\n//# sourceMappingURL=");
|
|
409
|
+
const executableSource = sourceMapIndex >= 0 ? emitted.slice(0, sourceMapIndex) : emitted;
|
|
410
|
+
const program = parse(executableSource, {
|
|
411
|
+
ecmaVersion: "latest",
|
|
412
|
+
sourceType: "script",
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
if (containsDynamicImport(program)) {
|
|
416
|
+
throw new Error(
|
|
417
|
+
`Dynamic import is unsupported in the widget runtime. ${outfile} still contains import(...).`
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function containsDynamicImport(node) {
|
|
423
|
+
if (!node || typeof node !== "object") {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (node.type === "ImportExpression") {
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
for (const value of Object.values(node)) {
|
|
432
|
+
if (Array.isArray(value)) {
|
|
433
|
+
for (const child of value) {
|
|
434
|
+
if (containsDynamicImport(child)) {
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (containsDynamicImport(value)) {
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function builtinModulePolicyPlugin() {
|
|
450
|
+
return {
|
|
451
|
+
name: "builtin-module-policy",
|
|
452
|
+
setup(build) {
|
|
453
|
+
build.onResolve({ filter: /.*/ }, async (args) => {
|
|
454
|
+
if (args.pluginData?.skipBuiltinPolicy) {
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const builtin = normalizeBuiltinRequest(args.path);
|
|
459
|
+
if (!builtin) {
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (!args.path.startsWith("node:")) {
|
|
464
|
+
const resolved = await build.resolve(args.path, {
|
|
465
|
+
kind: args.kind,
|
|
466
|
+
importer: args.importer,
|
|
467
|
+
namespace: args.namespace,
|
|
468
|
+
resolveDir: args.resolveDir,
|
|
469
|
+
pluginData: { skipBuiltinPolicy: true },
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
if (!resolved.errors?.length && resolved.path && !resolved.external) {
|
|
473
|
+
return resolved;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (!ALLOWED_BUILTIN_SPECIFIER_SET.has(builtin.specifier)) {
|
|
478
|
+
return {
|
|
479
|
+
errors: [
|
|
480
|
+
{
|
|
481
|
+
text: `Built-in module "${args.path}" is not available in the widget runtime.`,
|
|
482
|
+
},
|
|
483
|
+
],
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return { path: args.path, external: true };
|
|
488
|
+
});
|
|
489
|
+
},
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function syncWidgetAssets(targetPackageDir, outputDir) {
|
|
494
|
+
const sourceAssetsDir = path.join(targetPackageDir, "assets");
|
|
495
|
+
const destinationAssetsDir = path.join(outputDir, "assets");
|
|
496
|
+
|
|
497
|
+
fs.rmSync(destinationAssetsDir, { recursive: true, force: true });
|
|
498
|
+
|
|
499
|
+
if (!fs.existsSync(sourceAssetsDir)) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
fs.cpSync(sourceAssetsDir, destinationAssetsDir, {
|
|
504
|
+
recursive: true,
|
|
505
|
+
dereference: true,
|
|
189
506
|
});
|
|
190
507
|
}
|
|
191
508
|
|
|
509
|
+
function replaceBuildOutput(outputDir, stagingOutputDir, backupOutputDir) {
|
|
510
|
+
fs.rmSync(backupOutputDir, { recursive: true, force: true });
|
|
511
|
+
|
|
512
|
+
let movedExistingBuild = false;
|
|
513
|
+
if (fs.existsSync(outputDir)) {
|
|
514
|
+
fs.renameSync(outputDir, backupOutputDir);
|
|
515
|
+
movedExistingBuild = true;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
try {
|
|
519
|
+
fs.renameSync(stagingOutputDir, outputDir);
|
|
520
|
+
if (movedExistingBuild) {
|
|
521
|
+
fs.rmSync(backupOutputDir, { recursive: true, force: true });
|
|
522
|
+
}
|
|
523
|
+
} catch (error) {
|
|
524
|
+
if (!fs.existsSync(outputDir) && movedExistingBuild && fs.existsSync(backupOutputDir)) {
|
|
525
|
+
fs.renameSync(backupOutputDir, outputDir);
|
|
526
|
+
}
|
|
527
|
+
throw error;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
192
531
|
async function buildWidget(targetPackageDir, options = {}) {
|
|
193
532
|
const { manifest, entryFile } = readManifest(targetPackageDir);
|
|
194
533
|
const { registerCanonicalInstall = false } = options;
|
|
@@ -197,22 +536,47 @@ async function buildWidget(targetPackageDir, options = {}) {
|
|
|
197
536
|
}
|
|
198
537
|
|
|
199
538
|
const notch = manifest.notch;
|
|
200
|
-
const
|
|
539
|
+
const outputRoot = path.join(targetPackageDir, ".notch");
|
|
540
|
+
const outputDir = path.join(outputRoot, "build");
|
|
201
541
|
const outfile = path.join(outputDir, "index.cjs");
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
542
|
+
const stagingOutputDir = path.join(outputRoot, `build.${process.pid}.staging`);
|
|
543
|
+
const backupOutputDir = path.join(outputRoot, `build.${process.pid}.backup`);
|
|
544
|
+
const stagingOutfile = path.join(stagingOutputDir, "index.cjs");
|
|
545
|
+
fs.mkdirSync(outputRoot, { recursive: true });
|
|
546
|
+
fs.rmSync(stagingOutputDir, { recursive: true, force: true });
|
|
547
|
+
fs.mkdirSync(stagingOutputDir, { recursive: true });
|
|
548
|
+
|
|
549
|
+
try {
|
|
550
|
+
await esbuild.build({
|
|
551
|
+
entryPoints: [entryFile],
|
|
552
|
+
outfile: stagingOutfile,
|
|
553
|
+
bundle: true,
|
|
554
|
+
platform: "browser",
|
|
555
|
+
format: "cjs",
|
|
556
|
+
target: "es2022",
|
|
557
|
+
jsx: "automatic",
|
|
558
|
+
jsxImportSource: "@notchapp/api",
|
|
559
|
+
alias: {
|
|
560
|
+
react: "react-shim",
|
|
561
|
+
"react/jsx-runtime": "@notchapp/api/jsx-runtime",
|
|
562
|
+
},
|
|
563
|
+
plugins: [builtinModulePolicyPlugin()],
|
|
564
|
+
external: [
|
|
565
|
+
"@notchapp/api",
|
|
566
|
+
"@notchapp/api/jsx-runtime",
|
|
567
|
+
"react-shim",
|
|
568
|
+
],
|
|
569
|
+
sourcemap: "inline",
|
|
570
|
+
logLevel: "silent",
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
failOnDynamicImport(stagingOutfile);
|
|
574
|
+
syncWidgetAssets(targetPackageDir, stagingOutputDir);
|
|
575
|
+
replaceBuildOutput(outputDir, stagingOutputDir, backupOutputDir);
|
|
576
|
+
} finally {
|
|
577
|
+
fs.rmSync(stagingOutputDir, { recursive: true, force: true });
|
|
578
|
+
fs.rmSync(backupOutputDir, { recursive: true, force: true });
|
|
579
|
+
}
|
|
216
580
|
|
|
217
581
|
console.log(`Built ${notch.id} -> ${outfile}`);
|
|
218
582
|
return manifest;
|
|
@@ -228,8 +592,7 @@ async function developWidget(targetPackageDir) {
|
|
|
228
592
|
ensureCanonicalSymlink(targetPackageDir, initial.manifest);
|
|
229
593
|
await notifyApp("start", initial.manifest.notch.id);
|
|
230
594
|
const watchRoots = ["package.json", "src", "assets"]
|
|
231
|
-
.map((relativePath) => path.join(targetPackageDir, relativePath))
|
|
232
|
-
.filter((targetPath) => fs.existsSync(targetPath));
|
|
595
|
+
.map((relativePath) => path.join(targetPackageDir, relativePath));
|
|
233
596
|
|
|
234
597
|
let buildInFlight = false;
|
|
235
598
|
let buildQueued = false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "notchapp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "CLI for developing and building NotchApp widgets",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"bin",
|
|
11
|
-
"cli.mjs"
|
|
11
|
+
"cli.mjs",
|
|
12
|
+
"security-policy.mjs"
|
|
12
13
|
],
|
|
13
14
|
"keywords": [
|
|
14
15
|
"notchapp",
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
"node": ">=20"
|
|
35
36
|
},
|
|
36
37
|
"dependencies": {
|
|
38
|
+
"acorn": "^8.15.0",
|
|
37
39
|
"esbuild": "^0.25.10"
|
|
38
40
|
}
|
|
39
41
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import Module from "node:module";
|
|
2
|
+
|
|
3
|
+
export const ALLOWED_BUILTIN_MODULES = Object.freeze([
|
|
4
|
+
"buffer",
|
|
5
|
+
"crypto",
|
|
6
|
+
"events",
|
|
7
|
+
"path",
|
|
8
|
+
"querystring",
|
|
9
|
+
"string_decoder",
|
|
10
|
+
"url",
|
|
11
|
+
"util",
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
export const ALLOWED_BUILTIN_SPECIFIERS = Object.freeze(
|
|
15
|
+
Module.builtinModules.filter((specifier) => {
|
|
16
|
+
const withoutPrefix = specifier.startsWith("node:") ? specifier.slice(5) : specifier;
|
|
17
|
+
const root = withoutPrefix.split("/")[0] || null;
|
|
18
|
+
return root ? ALLOWED_BUILTIN_MODULES.includes(root) : false;
|
|
19
|
+
})
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export const ALLOWED_BUILTIN_EXTERNALS = Object.freeze(
|
|
23
|
+
ALLOWED_BUILTIN_SPECIFIERS.flatMap((specifier) => [
|
|
24
|
+
specifier,
|
|
25
|
+
`node:${specifier}`,
|
|
26
|
+
])
|
|
27
|
+
);
|