@openvcs/sdk 0.2.15 → 0.2.17-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/test/dist.test.js DELETED
@@ -1,689 +0,0 @@
1
- const assert = require("node:assert/strict");
2
- const fs = require("node:fs");
3
- const path = require("node:path");
4
- const test = require("node:test");
5
-
6
- const { bundlePlugin, parseDistArgs, __private } = require("../lib/dist");
7
- const {
8
- cleanupTempDir,
9
- makeTempDir,
10
- readBundleEntries,
11
- writeJson,
12
- writeText,
13
- } = require("./helpers");
14
-
15
- test("parseDistArgs uses defaults", () => {
16
- const parsed = parseDistArgs([]);
17
- assert.equal(parsed.pluginDir, process.cwd());
18
- assert.equal(parsed.outDir, path.resolve("dist"));
19
- assert.equal(parsed.verbose, false);
20
- assert.equal(parsed.noNpmDeps, false);
21
- });
22
-
23
- test("parseDistArgs parses known flags", () => {
24
- const parsed = parseDistArgs([
25
- "--plugin-dir",
26
- "some/plugin",
27
- "--out",
28
- "some/out",
29
- "--no-build",
30
- "--no-npm-deps",
31
- "--verbose",
32
- ]);
33
- assert.equal(parsed.pluginDir, path.resolve("some/plugin"));
34
- assert.equal(parsed.outDir, path.resolve("some/out"));
35
- assert.equal(parsed.noBuild, true);
36
- assert.equal(parsed.noNpmDeps, true);
37
- assert.equal(parsed.verbose, true);
38
- });
39
-
40
- test("parseDistArgs rejects unknown flag", () => {
41
- assert.throws(() => parseDistArgs(["--nope"]), /unknown flag: --nope/);
42
- });
43
-
44
- test("parseDistArgs requires plugin-dir value", () => {
45
- assert.throws(() => parseDistArgs(["--plugin-dir"]), /missing value for --plugin-dir/);
46
- });
47
-
48
- test("parseDistArgs requires out value", () => {
49
- assert.throws(() => parseDistArgs(["--out"]), /missing value for --out/);
50
- });
51
-
52
- test("parseDistArgs help returns usage error", () => {
53
- assert.throws(() => parseDistArgs(["--help"]), /openvcs dist \[args\]/);
54
- });
55
-
56
- test("readManifest parses and trims fields", () => {
57
- const root = makeTempDir("openvcs-sdk-test");
58
- const pluginDir = path.join(root, "plugin");
59
- writeText(
60
- path.join(pluginDir, "openvcs.plugin.json"),
61
- '{\n "id": " my.plugin ",\n "module": { "exec": " module.mjs " }\n}\n'
62
- );
63
-
64
- const parsed = __private.readManifest(pluginDir);
65
- assert.equal(parsed.pluginId, "my.plugin");
66
- assert.equal(parsed.moduleExec, "module.mjs");
67
-
68
- cleanupTempDir(root);
69
- });
70
-
71
- test("readManifest errors for missing manifest", () => {
72
- const root = makeTempDir("openvcs-sdk-test");
73
- const pluginDir = path.join(root, "plugin");
74
- fs.mkdirSync(pluginDir, { recursive: true });
75
- assert.throws(() => __private.readManifest(pluginDir), /missing openvcs\.plugin\.json/);
76
- cleanupTempDir(root);
77
- });
78
-
79
- test("readManifest errors for malformed JSON with path", () => {
80
- const root = makeTempDir("openvcs-sdk-test");
81
- const pluginDir = path.join(root, "plugin");
82
- writeText(path.join(pluginDir, "openvcs.plugin.json"), "{");
83
-
84
- assert.throws(
85
- () => __private.readManifest(pluginDir),
86
- new RegExp(`parse ${pluginDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`)
87
- );
88
-
89
- cleanupTempDir(root);
90
- });
91
-
92
- test("readManifest errors for empty id", () => {
93
- const root = makeTempDir("openvcs-sdk-test");
94
- const pluginDir = path.join(root, "plugin");
95
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), { id: " " });
96
- assert.throws(() => __private.readManifest(pluginDir), /missing a string 'id'/);
97
- cleanupTempDir(root);
98
- });
99
-
100
- test("readManifest rejects id with path separators", () => {
101
- const root = makeTempDir("openvcs-sdk-test");
102
- const pluginDir = path.join(root, "plugin");
103
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), { id: "bad/id" });
104
- assert.throws(() => __private.readManifest(pluginDir), /must not contain path separators/);
105
- cleanupTempDir(root);
106
- });
107
-
108
- test("validateDeclaredModuleExec accepts .js/.mjs/.cjs", () => {
109
- const root = makeTempDir("openvcs-sdk-test");
110
- const pluginDir = path.join(root, "plugin");
111
- writeText(path.join(pluginDir, "bin", "plugin.js"), "export function OnPluginStart() {}\n");
112
- writeText(path.join(pluginDir, "bin", "plugin.mjs"), "export {};\n");
113
- writeText(path.join(pluginDir, "bin", "plugin.cjs"), "module.exports = {};\n");
114
-
115
- assert.doesNotThrow(() => __private.validateDeclaredModuleExec(pluginDir, "plugin.js"));
116
- assert.doesNotThrow(() => __private.validateDeclaredModuleExec(pluginDir, "plugin.mjs"));
117
- assert.doesNotThrow(() => __private.validateDeclaredModuleExec(pluginDir, "plugin.cjs"));
118
-
119
- cleanupTempDir(root);
120
- });
121
-
122
- test("validateDeclaredModuleExec rejects non-node extension", () => {
123
- const root = makeTempDir("openvcs-sdk-test");
124
- const pluginDir = path.join(root, "plugin");
125
- writeText(path.join(pluginDir, "bin", "plugin.ts"), "export {};\n");
126
- assert.throws(
127
- () => __private.validateDeclaredModuleExec(pluginDir, "plugin.ts"),
128
- /must end with .js\/.mjs\/.cjs/
129
- );
130
- cleanupTempDir(root);
131
- });
132
-
133
- test("validateDeclaredModuleExec rejects absolute path", () => {
134
- const root = makeTempDir("openvcs-sdk-test");
135
- const pluginDir = path.join(root, "plugin");
136
- writeText(path.join(pluginDir, "bin", "plugin.js"), "export function OnPluginStart() {}\n");
137
- assert.throws(
138
- () => __private.validateDeclaredModuleExec(pluginDir, path.resolve(pluginDir, "bin", "plugin.js")),
139
- /must be a relative path under bin/
140
- );
141
- cleanupTempDir(root);
142
- });
143
-
144
- test("validateDeclaredModuleExec rejects path traversal", () => {
145
- const root = makeTempDir("openvcs-sdk-test");
146
- const pluginDir = path.join(root, "plugin");
147
- writeText(path.join(pluginDir, "secret.js"), "export {};\n");
148
- assert.throws(
149
- () => __private.validateDeclaredModuleExec(pluginDir, "../secret.js"),
150
- /must point to a file under bin/
151
- );
152
- cleanupTempDir(root);
153
- });
154
-
155
- test("validateDeclaredModuleExec errors when file missing", () => {
156
- const root = makeTempDir("openvcs-sdk-test");
157
- const pluginDir = path.join(root, "plugin");
158
- fs.mkdirSync(path.join(pluginDir, "bin"), { recursive: true });
159
- assert.throws(
160
- () => __private.validateDeclaredModuleExec(pluginDir, "missing.mjs"),
161
- /module entrypoint not found/
162
- );
163
- cleanupTempDir(root);
164
- });
165
-
166
- test("copyIcon prefers png over jpg", () => {
167
- const root = makeTempDir("openvcs-sdk-test");
168
- const pluginDir = path.join(root, "plugin");
169
- const bundleDir = path.join(root, "bundle");
170
- fs.mkdirSync(bundleDir, { recursive: true });
171
- writeText(path.join(pluginDir, "icon.jpg"), "jpg");
172
- writeText(path.join(pluginDir, "icon.png"), "png");
173
-
174
- __private.copyIcon(pluginDir, bundleDir);
175
-
176
- assert.equal(fs.readFileSync(path.join(bundleDir, "icon.png"), "utf8"), "png");
177
- assert.equal(fs.existsSync(path.join(bundleDir, "icon.jpg")), false);
178
- cleanupTempDir(root);
179
- });
180
-
181
- test("copyIcon is no-op when icon is absent", () => {
182
- const root = makeTempDir("openvcs-sdk-test");
183
- const pluginDir = path.join(root, "plugin");
184
- const bundleDir = path.join(root, "bundle");
185
- fs.mkdirSync(pluginDir, { recursive: true });
186
- fs.mkdirSync(bundleDir, { recursive: true });
187
-
188
- __private.copyIcon(pluginDir, bundleDir);
189
-
190
- for (const ext of __private.ICON_EXTENSIONS) {
191
- assert.equal(fs.existsSync(path.join(bundleDir, `icon.${ext}`)), false);
192
- }
193
-
194
- cleanupTempDir(root);
195
- });
196
-
197
- test("uniqueStagingDir uses expected prefix", () => {
198
- const staging = __private.uniqueStagingDir("/tmp/output");
199
- assert.match(path.basename(staging), /^\.openvcs-plugin-staging-/);
200
- });
201
-
202
- test("writeTarGz creates a valid gzip tar", async () => {
203
- const root = makeTempDir("openvcs-sdk-test");
204
- const baseDir = path.join(root, "base");
205
- const outPath = path.join(root, "bundle.ovcsp");
206
- writeText(path.join(baseDir, "myplugin", "file.txt"), "content");
207
- writeText(path.join(baseDir, "myplugin", "sub", "nested.txt"), "nested");
208
-
209
- await __private.writeTarGz(outPath, baseDir, "myplugin");
210
- const entries = await readBundleEntries(outPath);
211
-
212
- assert.equal(entries.has("myplugin/file.txt"), true);
213
- assert.equal(entries.has("myplugin/sub/nested.txt"), true);
214
- cleanupTempDir(root);
215
- });
216
-
217
- test("rejectNativeAddonsRecursive rejects .node files", () => {
218
- const root = makeTempDir("openvcs-sdk-test");
219
- const modulesDir = path.join(root, "node_modules");
220
- writeText(path.join(modulesDir, "pkg", "addon.node"), "binary");
221
- assert.throws(() => __private.rejectNativeAddonsRecursive(modulesDir), /native Node addon/);
222
- cleanupTempDir(root);
223
- });
224
-
225
- test("rejectNativeAddonsRecursive allows normal files", () => {
226
- const root = makeTempDir("openvcs-sdk-test");
227
- const modulesDir = path.join(root, "node_modules");
228
- writeText(path.join(modulesDir, "pkg", "index.js"), "module.exports = {};\n");
229
- assert.doesNotThrow(() => __private.rejectNativeAddonsRecursive(modulesDir));
230
- cleanupTempDir(root);
231
- });
232
-
233
- test("bundlePlugin writes a gzip .ovcsp for themes-only plugin", async () => {
234
- const root = makeTempDir("openvcs-sdk-test");
235
- const pluginDir = path.join(root, "plugin");
236
- const outDir = path.join(root, "out");
237
-
238
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), { id: "ui-only" });
239
- writeText(path.join(pluginDir, "themes", "default", "theme.json"), '{"name":"test"}\n');
240
- writeText(path.join(pluginDir, "icon.png"), "icon-bytes");
241
-
242
- const outPath = await bundlePlugin({
243
- pluginDir,
244
- outDir,
245
- verbose: false,
246
- noBuild: true,
247
- noNpmDeps: true,
248
- });
249
- const entries = await readBundleEntries(outPath);
250
-
251
- assert.equal(path.basename(outPath), "ui-only.ovcsp");
252
- assert.equal(entries.has("ui-only/openvcs.plugin.json"), true);
253
- assert.equal(entries.has("ui-only/themes/default/theme.json"), true);
254
- assert.equal(entries.has("ui-only/icon.png"), true);
255
-
256
- cleanupTempDir(root);
257
- });
258
-
259
- test("bundlePlugin rejects manifest with no module, entry, or themes", async () => {
260
- const root = makeTempDir("openvcs-sdk-test");
261
- const pluginDir = path.join(root, "plugin");
262
- const outDir = path.join(root, "out");
263
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), { id: "x" });
264
-
265
- await assert.rejects(
266
- () => bundlePlugin({ pluginDir, outDir, verbose: false, noBuild: true, noNpmDeps: true }),
267
- /manifest has no module\.exec, entry, or themes\//
268
- );
269
-
270
- cleanupTempDir(root);
271
- });
272
-
273
- test("bundlePlugin trims module.exec and includes extra bin files", async () => {
274
- const root = makeTempDir("openvcs-sdk-test");
275
- const pluginDir = path.join(root, "plugin");
276
- const outDir = path.join(root, "out");
277
-
278
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
279
- id: "x",
280
- module: { exec: " module.mjs " },
281
- });
282
- writeText(path.join(pluginDir, "bin", "plugin.js"), "export function OnPluginStart() {}\n");
283
- writeText(path.join(pluginDir, "bin", "module.mjs"), "export {};\n");
284
- writeText(path.join(pluginDir, "bin", "helpers", "util.mjs"), "export const x = 1;\n");
285
-
286
- const outPath = await bundlePlugin({
287
- pluginDir,
288
- outDir,
289
- verbose: false,
290
- noBuild: true,
291
- noNpmDeps: true,
292
- });
293
- const entries = await readBundleEntries(outPath);
294
-
295
- assert.equal(entries.has("x/bin/plugin.js"), true);
296
- assert.equal(entries.has("x/bin/module.mjs"), true);
297
- assert.equal(entries.has("x/bin/helpers/util.mjs"), true);
298
- cleanupTempDir(root);
299
- });
300
-
301
- test("bundlePlugin builds code plugins before packaging", async () => {
302
- const root = makeTempDir("openvcs-sdk-test");
303
- const pluginDir = path.join(root, "plugin");
304
- const outDir = path.join(root, "out");
305
-
306
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
307
- id: "builder",
308
- module: { exec: "openvcs-plugin.js" },
309
- });
310
- writeJson(path.join(pluginDir, "package.json"), {
311
- name: "builder",
312
- private: true,
313
- scripts: {
314
- "build:plugin": "node ./scripts/build-plugin.js",
315
- },
316
- });
317
- writeText(
318
- path.join(pluginDir, "scripts", "build-plugin.js"),
319
- "const fs = require('node:fs');\nconst path = require('node:path');\nconst out = path.join(process.cwd(), 'bin', 'plugin.js');\nfs.mkdirSync(path.dirname(out), { recursive: true });\nfs.writeFileSync(out, 'export {};\\n', 'utf8');\n"
320
- );
321
-
322
- const outPath = await bundlePlugin({
323
- pluginDir,
324
- outDir,
325
- verbose: false,
326
- noBuild: false,
327
- noNpmDeps: true,
328
- });
329
- const entries = await readBundleEntries(outPath);
330
-
331
- assert.equal(entries.has("builder/bin/plugin.js"), true);
332
- assert.equal(entries.has("builder/bin/openvcs-plugin.js"), true);
333
- cleanupTempDir(root);
334
- });
335
-
336
- test("bundlePlugin with no-build requires prebuilt module entrypoint", async () => {
337
- const root = makeTempDir("openvcs-sdk-test");
338
- const pluginDir = path.join(root, "plugin");
339
- const outDir = path.join(root, "out");
340
-
341
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
342
- id: "prebuilt",
343
- module: { exec: "openvcs-plugin.js" },
344
- });
345
-
346
- await assert.rejects(
347
- () => bundlePlugin({ pluginDir, outDir, verbose: false, noBuild: true, noNpmDeps: true }),
348
- /compiled plugin module not found/
349
- );
350
-
351
- cleanupTempDir(root);
352
- });
353
-
354
- test("bundlePlugin errors when code plugin lacks build:plugin", async () => {
355
- const root = makeTempDir("openvcs-sdk-test");
356
- const pluginDir = path.join(root, "plugin");
357
- const outDir = path.join(root, "out");
358
-
359
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
360
- id: "missing-script",
361
- module: { exec: "openvcs-plugin.js" },
362
- });
363
- writeJson(path.join(pluginDir, "package.json"), {
364
- name: "missing-script",
365
- private: true,
366
- scripts: {},
367
- });
368
-
369
- await assert.rejects(
370
- () => bundlePlugin({ pluginDir, outDir, verbose: false, noBuild: false, noNpmDeps: true }),
371
- /build:plugin/
372
- );
373
-
374
- cleanupTempDir(root);
375
- });
376
-
377
- test("bundlePlugin rejects module.exec path traversal", async () => {
378
- const root = makeTempDir("openvcs-sdk-test");
379
- const pluginDir = path.join(root, "plugin");
380
- const outDir = path.join(root, "out");
381
-
382
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
383
- id: "bad",
384
- module: { exec: "../secret.js" },
385
- });
386
- writeText(path.join(pluginDir, "secret.js"), "console.log('secret')\n");
387
-
388
- await assert.rejects(
389
- () => bundlePlugin({ pluginDir, outDir, verbose: false, noBuild: true, noNpmDeps: true }),
390
- /module\.exec/
391
- );
392
-
393
- cleanupTempDir(root);
394
- });
395
-
396
- test("bundlePlugin rejects non-node module.exec extension", async () => {
397
- const root = makeTempDir("openvcs-sdk-test");
398
- const pluginDir = path.join(root, "plugin");
399
- const outDir = path.join(root, "out");
400
-
401
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
402
- id: "bad",
403
- module: { exec: "plugin.ts" },
404
- });
405
- writeText(path.join(pluginDir, "bin", "plugin.ts"), "export {};\n");
406
-
407
- await assert.rejects(
408
- () => bundlePlugin({ pluginDir, outDir, verbose: false, noBuild: true, noNpmDeps: true }),
409
- /must end with .js\/.mjs\/.cjs/
410
- );
411
-
412
- cleanupTempDir(root);
413
- });
414
-
415
- test("bundlePlugin rejects plugin id with path separators", async () => {
416
- const root = makeTempDir("openvcs-sdk-test");
417
- const pluginDir = path.join(root, "plugin");
418
- const outDir = path.join(root, "out");
419
-
420
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
421
- id: "bad/id",
422
- module: { exec: "openvcs-plugin.js" },
423
- });
424
- writeText(path.join(pluginDir, "bin", "plugin.js"), "export function OnPluginStart() {}\n");
425
- writeText(path.join(pluginDir, "bin", "openvcs-plugin.js"), "export {};\n");
426
-
427
- await assert.rejects(
428
- () => bundlePlugin({ pluginDir, outDir, verbose: false, noBuild: true, noNpmDeps: true }),
429
- /must not contain path separators/
430
- );
431
-
432
- cleanupTempDir(root);
433
- });
434
-
435
- test("bundlePlugin rejects symlink in bin", async () => {
436
- if (process.platform === "win32") {
437
- return;
438
- }
439
-
440
- const root = makeTempDir("openvcs-sdk-test");
441
- const pluginDir = path.join(root, "plugin");
442
- const outDir = path.join(root, "out");
443
-
444
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
445
- id: "x",
446
- module: { exec: "openvcs-plugin.js" },
447
- });
448
- writeText(path.join(pluginDir, "bin", "plugin.js"), "export function OnPluginStart() {}\n");
449
- writeText(path.join(pluginDir, "bin", "openvcs-plugin.js"), "export {};\n");
450
- writeText(path.join(pluginDir, "bin", "target.js"), "export {};\n");
451
- fs.symlinkSync(path.join(pluginDir, "bin", "target.js"), path.join(pluginDir, "bin", "link.js"));
452
-
453
- await assert.rejects(
454
- () => bundlePlugin({ pluginDir, outDir, verbose: false, noBuild: true, noNpmDeps: true }),
455
- /symlink/
456
- );
457
-
458
- cleanupTempDir(root);
459
- });
460
-
461
- test("bundlePlugin output archive keeps plugin-id root directory", async () => {
462
- const root = makeTempDir("openvcs-sdk-test");
463
- const pluginDir = path.join(root, "plugin");
464
- const outDir = path.join(root, "out");
465
-
466
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
467
- id: "root-check",
468
- module: { exec: "openvcs-plugin.js" },
469
- });
470
- writeText(path.join(pluginDir, "bin", "plugin.js"), "export function OnPluginStart() {}\n");
471
- writeText(path.join(pluginDir, "bin", "openvcs-plugin.js"), "export {};\n");
472
-
473
- const outPath = await bundlePlugin({
474
- pluginDir,
475
- outDir,
476
- verbose: false,
477
- noBuild: true,
478
- noNpmDeps: true,
479
- });
480
- const entries = await readBundleEntries(outPath);
481
- for (const key of entries.keys()) {
482
- assert.equal(key.startsWith("root-check/"), true);
483
- }
484
-
485
- cleanupTempDir(root);
486
- });
487
-
488
- test("bundlePlugin overwrites existing bundle file", async () => {
489
- const root = makeTempDir("openvcs-sdk-test");
490
- const pluginDir = path.join(root, "plugin");
491
- const outDir = path.join(root, "out");
492
- fs.mkdirSync(outDir, { recursive: true });
493
-
494
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
495
- id: "replace",
496
- module: { exec: "openvcs-plugin.js" },
497
- });
498
- writeText(path.join(pluginDir, "bin", "plugin.js"), "export function OnPluginStart() {}\n");
499
- writeText(path.join(pluginDir, "bin", "openvcs-plugin.js"), "export {};\n");
500
-
501
- const existingPath = path.join(outDir, "replace.ovcsp");
502
- writeText(existingPath, "not-a-tar");
503
-
504
- const outPath = await bundlePlugin({
505
- pluginDir,
506
- outDir,
507
- verbose: false,
508
- noBuild: true,
509
- noNpmDeps: true,
510
- });
511
- const entries = await readBundleEntries(outPath);
512
- assert.equal(entries.has("replace/openvcs.plugin.json"), true);
513
-
514
- cleanupTempDir(root);
515
- });
516
-
517
- test("bundlePlugin generates package-lock in staging, not pluginDir", async () => {
518
- const root = makeTempDir("openvcs-sdk-test");
519
- const pluginDir = path.join(root, "plugin");
520
- const outDir = path.join(root, "out");
521
-
522
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
523
- id: "npm-plugin",
524
- module: { exec: "openvcs-plugin.js" },
525
- });
526
- writeText(path.join(pluginDir, "bin", "plugin.js"), "export function OnPluginStart() {}\n");
527
- writeText(path.join(pluginDir, "bin", "openvcs-plugin.js"), "export {};\n");
528
- writeJson(path.join(pluginDir, "package.json"), {
529
- name: "npm-plugin",
530
- version: "0.1.0",
531
- private: true,
532
- });
533
-
534
- const outPath = await bundlePlugin({
535
- pluginDir,
536
- outDir,
537
- verbose: false,
538
- noBuild: true,
539
- noNpmDeps: false,
540
- });
541
- const entries = await readBundleEntries(outPath);
542
-
543
- assert.equal(fs.existsSync(path.join(pluginDir, "package-lock.json")), false);
544
- assert.equal(entries.has("npm-plugin/package.json"), true);
545
- assert.equal(entries.has("npm-plugin/package-lock.json"), true);
546
-
547
- cleanupTempDir(root);
548
- });
549
-
550
- test("bundlePlugin with --no-npm-deps does not generate lockfile", async () => {
551
- const root = makeTempDir("openvcs-sdk-test");
552
- const pluginDir = path.join(root, "plugin");
553
- const outDir = path.join(root, "out");
554
-
555
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
556
- id: "no-npm",
557
- module: { exec: "openvcs-plugin.js" },
558
- });
559
- writeText(path.join(pluginDir, "bin", "plugin.js"), "export function OnPluginStart() {}\n");
560
- writeText(path.join(pluginDir, "bin", "openvcs-plugin.js"), "export {};\n");
561
- writeJson(path.join(pluginDir, "package.json"), {
562
- name: "no-npm",
563
- version: "0.1.0",
564
- private: true,
565
- });
566
-
567
- await bundlePlugin({ pluginDir, outDir, verbose: false, noBuild: true, noNpmDeps: true });
568
-
569
- assert.equal(fs.existsSync(path.join(pluginDir, "package-lock.json")), false);
570
-
571
- cleanupTempDir(root);
572
- });
573
-
574
- test("bundlePlugin bundles manifest entry file with sibling assets", async () => {
575
- const root = makeTempDir("openvcs-sdk-test");
576
- const pluginDir = path.join(root, "plugin");
577
- const outDir = path.join(root, "out");
578
-
579
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
580
- id: "ui-plugin",
581
- entry: "ui/index.html",
582
- });
583
- writeText(path.join(pluginDir, "ui", "index.html"), "<html></html>\n");
584
- writeText(path.join(pluginDir, "ui", "app.js"), "console.log('ui');\n");
585
- writeText(path.join(pluginDir, "ui", "styles.css"), "body {}\n");
586
- writeText(path.join(pluginDir, "icon.png"), "icon-bytes");
587
-
588
- const outPath = await bundlePlugin({
589
- pluginDir,
590
- outDir,
591
- verbose: false,
592
- noBuild: true,
593
- noNpmDeps: true,
594
- });
595
- const entries = await readBundleEntries(outPath);
596
-
597
- assert.equal(entries.has("ui-plugin/openvcs.plugin.json"), true);
598
- assert.equal(entries.has("ui-plugin/ui/index.html"), true);
599
- assert.equal(entries.has("ui-plugin/ui/app.js"), true);
600
- assert.equal(entries.has("ui-plugin/ui/styles.css"), true);
601
- assert.equal(entries.has("ui-plugin/icon.png"), true);
602
-
603
- cleanupTempDir(root);
604
- });
605
-
606
- test("bundlePlugin bundles root-level entry with sibling assets", async () => {
607
- const root = makeTempDir("openvcs-sdk-test");
608
- const pluginDir = path.join(root, "plugin");
609
- const outDir = path.join(root, "out");
610
-
611
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
612
- id: "root-ui",
613
- entry: "index.html",
614
- });
615
- writeText(path.join(pluginDir, "index.html"), "<html></html>\n");
616
- writeText(path.join(pluginDir, "app.js"), "console.log('ui');\n");
617
- writeText(path.join(pluginDir, "styles.css"), "body {}\n");
618
-
619
- const outPath = await bundlePlugin({
620
- pluginDir,
621
- outDir,
622
- verbose: false,
623
- noBuild: true,
624
- noNpmDeps: true,
625
- });
626
- const entries = await readBundleEntries(outPath);
627
-
628
- assert.equal(entries.has("root-ui/openvcs.plugin.json"), true);
629
- assert.equal(entries.has("root-ui/index.html"), true);
630
- assert.equal(entries.has("root-ui/app.js"), true);
631
- assert.equal(entries.has("root-ui/styles.css"), true);
632
-
633
- cleanupTempDir(root);
634
- });
635
-
636
- test("bundlePlugin rejects manifest entry path traversal", async () => {
637
- const root = makeTempDir("openvcs-sdk-test");
638
- const pluginDir = path.join(root, "plugin");
639
- const outDir = path.join(root, "out");
640
-
641
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
642
- id: "bad-entry",
643
- entry: "../secret.txt",
644
- });
645
- writeText(path.join(pluginDir, "secret.txt"), "secret");
646
-
647
- await assert.rejects(
648
- () => bundlePlugin({ pluginDir, outDir, verbose: false, noBuild: true, noNpmDeps: true }),
649
- /manifest entry must point to a file under the plugin directory/
650
- );
651
-
652
- cleanupTempDir(root);
653
- });
654
-
655
- test("bundlePlugin rejects manifest entry absolute path", async () => {
656
- const root = makeTempDir("openvcs-sdk-test");
657
- const pluginDir = path.join(root, "plugin");
658
- const outDir = path.join(root, "out");
659
-
660
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
661
- id: "abs-entry",
662
- entry: "/tmp/secret.txt",
663
- });
664
-
665
- await assert.rejects(
666
- () => bundlePlugin({ pluginDir, outDir, verbose: false, noBuild: true, noNpmDeps: true }),
667
- /manifest entry must be a relative path/
668
- );
669
-
670
- cleanupTempDir(root);
671
- });
672
-
673
- test("bundlePlugin rejects missing manifest entry file", async () => {
674
- const root = makeTempDir("openvcs-sdk-test");
675
- const pluginDir = path.join(root, "plugin");
676
- const outDir = path.join(root, "out");
677
-
678
- writeJson(path.join(pluginDir, "openvcs.plugin.json"), {
679
- id: "missing-entry",
680
- entry: "nonexistent.html",
681
- });
682
-
683
- await assert.rejects(
684
- () => bundlePlugin({ pluginDir, outDir, verbose: false, noBuild: true, noNpmDeps: true }),
685
- /manifest entry file not found/
686
- );
687
-
688
- cleanupTempDir(root);
689
- });