acrylblends 0.7.5 → 0.7.7
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/index.js +277 -5
- package/dist/index.js.map +1 -1
- package/docs/framework/UPSTREAM.md +1 -1
- package/docs/framework/guides/data.md +101 -2
- package/docs/framework/upstream.json +2 -2
- package/lib/database-D_UN6We8.d.ts +44 -0
- package/lib/db/sqlite.d.ts +1 -1
- package/lib/db.d.ts +84 -1
- package/lib/kv/db.d.ts +12 -0
- package/lib/kv/db.js +57 -0
- package/lib/kv/json.d.ts +11 -0
- package/lib/kv/json.js +161 -0
- package/lib/kv-DWDm0Lja.d.ts +39 -0
- package/lib/kv.d.ts +93 -0
- package/lib/kv.js +2 -0
- package/lib/src-DRFVEQQb.js +90 -0
- package/package.json +13 -1
- package/plugins/acryl-db/upstream.json +1 -1
- package/plugins/acryl-db-sqlite/upstream.json +1 -1
- package/plugins/acryl-inspect/upstream.json +1 -1
- package/plugins/acryl-kv/LICENSE +47 -0
- package/plugins/acryl-kv/plugin.json +12 -0
- package/plugins/acryl-kv/src/check.ts +58 -0
- package/plugins/acryl-kv/src/copy.ts +39 -0
- package/plugins/acryl-kv/src/cordis.ts +13 -0
- package/plugins/acryl-kv/src/index.ts +14 -0
- package/plugins/acryl-kv/src/kv.ts +37 -0
- package/plugins/acryl-kv/upstream.json +18 -0
- package/plugins/acryl-kv-db/LICENSE +47 -0
- package/plugins/acryl-kv-db/plugin.json +13 -0
- package/plugins/acryl-kv-db/src/index.ts +37 -0
- package/plugins/acryl-kv-db/src/open.ts +80 -0
- package/plugins/acryl-kv-db/upstream.json +21 -0
- package/plugins/acryl-kv-json/LICENSE +47 -0
- package/plugins/acryl-kv-json/plugin.json +13 -0
- package/plugins/acryl-kv-json/src/index.ts +71 -0
- package/plugins/acryl-kv-json/src/open.ts +185 -0
- package/plugins/acryl-kv-json/upstream.json +22 -0
- package/plugins/acryl-node-server/src/index.ts +12 -1
- package/plugins/acryl-node-server/upstream.json +2 -2
- package/plugins/acryl-router/upstream.json +1 -1
- package/plugins/acryl-router-http/upstream.json +1 -1
- package/plugins/acryl-state/upstream.json +1 -1
- package/plugins/desktop/upstream.json +1 -1
- package/plugins/desktop-web/upstream.json +1 -1
- package/plugins/tsconfig.json +2 -0
- package/templates/todo/upstream.json +1 -1
- package/lib/index-D4FAsBZu.d.ts +0 -85
package/dist/index.js
CHANGED
|
@@ -189,6 +189,19 @@ function mergeDependencies(appDir, dependencies) {
|
|
|
189
189
|
writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
190
190
|
return added;
|
|
191
191
|
}
|
|
192
|
+
/** Declare a file as an opener in package.json (`"acryl": { "opener": [...] }`): the one place that may import a database provider. */
|
|
193
|
+
function mergeOpener(appDir, opener) {
|
|
194
|
+
const file = join(appDir, "package.json");
|
|
195
|
+
const manifest = JSON.parse(readFileSync(file, "utf8"));
|
|
196
|
+
const current = manifest.acryl?.opener;
|
|
197
|
+
const list = typeof current === "string" ? [current] : current ?? [];
|
|
198
|
+
if (list.includes(opener)) return;
|
|
199
|
+
manifest.acryl = {
|
|
200
|
+
...manifest.acryl,
|
|
201
|
+
opener: [...list, opener]
|
|
202
|
+
};
|
|
203
|
+
writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
204
|
+
}
|
|
192
205
|
/**
|
|
193
206
|
* Add a plugin's development dependencies and scripts to an app's package.json: nothing the app already has is replaced. Returns the names of the development dependencies
|
|
194
207
|
* and scripts added.
|
|
@@ -315,7 +328,7 @@ var UsageError = class extends Error {
|
|
|
315
328
|
function parseInitOptions(argv) {
|
|
316
329
|
let parsed;
|
|
317
330
|
try {
|
|
318
|
-
parsed = parse$
|
|
331
|
+
parsed = parse$2(argv);
|
|
319
332
|
} catch (error) {
|
|
320
333
|
throw new UsageError(error.message);
|
|
321
334
|
}
|
|
@@ -334,7 +347,7 @@ function parseInitOptions(argv) {
|
|
|
334
347
|
yes: values.yes === true
|
|
335
348
|
};
|
|
336
349
|
}
|
|
337
|
-
const parse$
|
|
350
|
+
const parse$2 = (argv) => parseArgs({
|
|
338
351
|
args: [...argv],
|
|
339
352
|
allowPositionals: true,
|
|
340
353
|
strict: true,
|
|
@@ -405,7 +418,7 @@ async function init(options, io) {
|
|
|
405
418
|
function parseAddOptions(argv) {
|
|
406
419
|
let parsed;
|
|
407
420
|
try {
|
|
408
|
-
parsed = parse(argv);
|
|
421
|
+
parsed = parse$1(argv);
|
|
409
422
|
} catch (error) {
|
|
410
423
|
throw new UsageError(error.message);
|
|
411
424
|
}
|
|
@@ -415,7 +428,7 @@ function parseAddOptions(argv) {
|
|
|
415
428
|
directory: parsed.values.dir
|
|
416
429
|
};
|
|
417
430
|
}
|
|
418
|
-
const parse = (argv) => parseArgs({
|
|
431
|
+
const parse$1 = (argv) => parseArgs({
|
|
419
432
|
args: [...argv],
|
|
420
433
|
allowPositionals: true,
|
|
421
434
|
strict: true,
|
|
@@ -442,7 +455,11 @@ async function add(options, io) {
|
|
|
442
455
|
io.stderr.write("error: \"db\" is the Cordis plugin for a Blend app. A plain Acryl app opens its database with openDb from \"acrylblends/db/sqlite\" (see docs/framework/guides/data.md)\n");
|
|
443
456
|
return 1;
|
|
444
457
|
}
|
|
445
|
-
|
|
458
|
+
if (plain && options.names.includes("kv")) {
|
|
459
|
+
io.stderr.write("error: \"kv\" is the Cordis plugin for a Blend app. A plain Acryl app opens its store with openKv from \"acrylblends/kv/json\" (see docs/framework/guides/data.md#a-tiny-app-kv)\n");
|
|
460
|
+
return 1;
|
|
461
|
+
}
|
|
462
|
+
const names = options.names.filter((name) => !external.includes(name)).map((name) => name === "db" ? "acryl-db-sqlite" : name).map((name) => name === "kv" ? "acryl-kv-json" : name).map((name) => name === "desktop" && plain ? "desktop-web" : name);
|
|
446
463
|
if (names.length === 0) return 0;
|
|
447
464
|
const already = names.filter((name) => present(appDir, name));
|
|
448
465
|
if (already.length > 0) {
|
|
@@ -456,6 +473,7 @@ async function add(options, io) {
|
|
|
456
473
|
addedDependencies.push(...mergeDependencies(appDir, plugin.dependencies));
|
|
457
474
|
const tooling = mergeTooling(appDir, plugin);
|
|
458
475
|
appendGitignore(appDir, plugin.gitignore);
|
|
476
|
+
if (plugin.name === "acryl-db-sqlite") mergeOpener(appDir, "src/app.ts");
|
|
459
477
|
addedDependencies.push(...tooling.devDependencies);
|
|
460
478
|
if (tooling.scripts.length > 0) io.stdout.write(`new scripts: ${tooling.scripts.map((name) => `npm run ${name}`).join(", ")}\n`);
|
|
461
479
|
}
|
|
@@ -551,6 +569,170 @@ async function buildClient(options, io) {
|
|
|
551
569
|
return 0;
|
|
552
570
|
}
|
|
553
571
|
//#endregion
|
|
572
|
+
//#region src/check-deps.ts
|
|
573
|
+
/**
|
|
574
|
+
* The dependency rule (spec 049): app code imports a port, never a provider. A port is `acrylblends/db` (the `Db` type); a provider is a
|
|
575
|
+
* driver or the one function that opens a database. Only the opener, a file the app names once in package.json, may import a provider.
|
|
576
|
+
*/
|
|
577
|
+
/** A package that is a database driver. An import of it, or of anything below it, is a provider import. */
|
|
578
|
+
const DRIVERS = [
|
|
579
|
+
"node:sqlite",
|
|
580
|
+
"sqlite",
|
|
581
|
+
"sqlite3",
|
|
582
|
+
"better-sqlite3",
|
|
583
|
+
"@libsql/client",
|
|
584
|
+
"libsql",
|
|
585
|
+
"pg",
|
|
586
|
+
"pg-pool",
|
|
587
|
+
"pg-native",
|
|
588
|
+
"postgres",
|
|
589
|
+
"@electric-sql/pglite",
|
|
590
|
+
"@neondatabase/serverless",
|
|
591
|
+
"@vercel/postgres",
|
|
592
|
+
"mysql",
|
|
593
|
+
"mysql2"
|
|
594
|
+
];
|
|
595
|
+
/** The openers a provider ships: importing one of them is opening a database. */
|
|
596
|
+
const OPENERS = ["acrylblends/db/sqlite", "acryl-db-sqlite"];
|
|
597
|
+
/** The desktop shell is where Electron lives (`acrylblends add desktop` copies it). */
|
|
598
|
+
const ELECTRON = "electron";
|
|
599
|
+
const ELECTRON_HOMES = ["src/plugins/desktop/", "src/plugins/desktop-web/"];
|
|
600
|
+
/** The provider plugins themselves may import what they wrap. */
|
|
601
|
+
const PROVIDER_HOMES = ["src/plugins/acryl-db-sqlite/"];
|
|
602
|
+
const SOURCE = /\.(?:[cm]?[jt]s|[jt]sx)$/u;
|
|
603
|
+
const SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
604
|
+
"node_modules",
|
|
605
|
+
"dist",
|
|
606
|
+
"lib",
|
|
607
|
+
"build",
|
|
608
|
+
"coverage"
|
|
609
|
+
]);
|
|
610
|
+
const ROOTS = [
|
|
611
|
+
"src",
|
|
612
|
+
"test",
|
|
613
|
+
"tests",
|
|
614
|
+
"extensions"
|
|
615
|
+
];
|
|
616
|
+
const matches = (specifier, name) => specifier === name || specifier.startsWith(`${name}/`);
|
|
617
|
+
/** What a specifier is to the rule, or undefined when it is not a provider. */
|
|
618
|
+
function classifySpecifier(specifier) {
|
|
619
|
+
if (DRIVERS.some((name) => matches(specifier, name))) return "driver";
|
|
620
|
+
if (OPENERS.some((name) => matches(specifier, name))) return "opener";
|
|
621
|
+
if (matches(specifier, ELECTRON)) return "electron";
|
|
622
|
+
}
|
|
623
|
+
/** The source with every comment replaced by spaces (newlines kept), so a line number still means what it did and a commented import is not an import. */
|
|
624
|
+
function blankComments(source) {
|
|
625
|
+
let out = "";
|
|
626
|
+
let i = 0;
|
|
627
|
+
while (i < source.length) {
|
|
628
|
+
const c = source[i];
|
|
629
|
+
const next = source[i + 1];
|
|
630
|
+
if (c === "/" && next === "/") {
|
|
631
|
+
const end = source.indexOf("\n", i);
|
|
632
|
+
const stop = end === -1 ? source.length : end;
|
|
633
|
+
out += " ".repeat(stop - i);
|
|
634
|
+
i = stop;
|
|
635
|
+
} else if (c === "/" && next === "*") {
|
|
636
|
+
const end = source.indexOf("*/", i + 2);
|
|
637
|
+
const stop = end === -1 ? source.length : end + 2;
|
|
638
|
+
out += source.slice(i, stop).replace(/[^\n]/gu, " ");
|
|
639
|
+
i = stop;
|
|
640
|
+
} else if (c === "'" || c === "\"" || c === "`") {
|
|
641
|
+
let j = i + 1;
|
|
642
|
+
while (j < source.length && source[j] !== c) {
|
|
643
|
+
if (source[j] === "\\") j += 1;
|
|
644
|
+
else if (c !== "`" && source[j] === "\n") break;
|
|
645
|
+
j += 1;
|
|
646
|
+
}
|
|
647
|
+
out += source.slice(i, Math.min(j + 1, source.length));
|
|
648
|
+
i = j + 1;
|
|
649
|
+
} else {
|
|
650
|
+
out += c;
|
|
651
|
+
i += 1;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return out;
|
|
655
|
+
}
|
|
656
|
+
/** `import x from 'a'`, `export * from 'a'`, `import 'a'`, `import('a')` and `require('a')`, with the line of each. */
|
|
657
|
+
const IMPORT = /(?:^|[\s;{}()])(?:import\s*(?:type\s+)?(?:[\w*${},\s]*?\s*from\s*)?|export\s*(?:type\s+)?[\w*${},\s]*?\s*from\s*|import\s*\(\s*|require\s*\(\s*)(['"])([^'"\n]+)\1/gu;
|
|
658
|
+
function importsOf(source) {
|
|
659
|
+
const code = blankComments(source);
|
|
660
|
+
const found = [];
|
|
661
|
+
for (const match of code.matchAll(IMPORT)) {
|
|
662
|
+
const specifier = match[2];
|
|
663
|
+
const at = (match.index ?? 0) + match[0].length - specifier.length - 2;
|
|
664
|
+
found.push({
|
|
665
|
+
specifier,
|
|
666
|
+
line: code.slice(0, at).split("\n").length
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
return found;
|
|
670
|
+
}
|
|
671
|
+
function listSources(dir, out = []) {
|
|
672
|
+
if (!existsSync(dir)) return out;
|
|
673
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
674
|
+
if (entry.name.startsWith(".") || SKIP_DIRS.has(entry.name)) continue;
|
|
675
|
+
const path = join(dir, entry.name);
|
|
676
|
+
if (entry.isDirectory()) listSources(path, out);
|
|
677
|
+
else if (SOURCE.test(entry.name) && !entry.name.endsWith(".d.ts")) out.push(path);
|
|
678
|
+
}
|
|
679
|
+
return out;
|
|
680
|
+
}
|
|
681
|
+
/** The opener files the app declares: `"acryl": { "opener": "src/db.ts" }` (or a list) in package.json. */
|
|
682
|
+
function openersOf(appDir) {
|
|
683
|
+
const file = join(appDir, "package.json");
|
|
684
|
+
if (!existsSync(file)) return [];
|
|
685
|
+
const declared = JSON.parse(readFileSync(file, "utf8")).acryl?.opener;
|
|
686
|
+
return (typeof declared === "string" ? [declared] : Array.isArray(declared) ? declared : []).filter((entry) => typeof entry === "string");
|
|
687
|
+
}
|
|
688
|
+
/** Every provider import outside the declared opener (and the homes that may hold one), in file and line order. */
|
|
689
|
+
function findViolations(appDir) {
|
|
690
|
+
const openers = new Set(openersOf(appDir).map((entry) => entry.split(sep).join("/")));
|
|
691
|
+
const violations = [];
|
|
692
|
+
for (const root of ROOTS) for (const path of listSources(join(appDir, root)).sort()) {
|
|
693
|
+
const file = relative(appDir, path).split(sep).join("/");
|
|
694
|
+
if (openers.has(file)) continue;
|
|
695
|
+
for (const { specifier, line } of importsOf(readFileSync(path, "utf8"))) {
|
|
696
|
+
const kind = classifySpecifier(specifier);
|
|
697
|
+
if (kind === void 0) continue;
|
|
698
|
+
if (kind === "electron" && ELECTRON_HOMES.some((home) => file.startsWith(home))) continue;
|
|
699
|
+
if (kind !== "electron" && PROVIDER_HOMES.some((home) => file.startsWith(home))) continue;
|
|
700
|
+
violations.push({
|
|
701
|
+
file,
|
|
702
|
+
line,
|
|
703
|
+
specifier,
|
|
704
|
+
kind
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return violations;
|
|
709
|
+
}
|
|
710
|
+
const GUIDE = "docs/framework/guides/data.md#why-the-database-is-opened-in-one-place";
|
|
711
|
+
function advice(violation, openers) {
|
|
712
|
+
if (violation.kind === "electron") return `"${violation.specifier}" is the desktop shell's: only src/plugins/desktop (added with "acrylblends add desktop") imports electron`;
|
|
713
|
+
const where = openers.length > 0 ? openers.join(", ") : "a file named in package.json \"acryl\": { \"opener\": \"...\" }";
|
|
714
|
+
return violation.kind === "driver" ? `"${violation.specifier}" is a database driver; take a Db (import type { Db } from "acrylblends/db") as an argument and let ${where} open the database` : `"${violation.specifier}" opens a database, and only ${where} may; take a Db (import type { Db } from "acrylblends/db") as an argument`;
|
|
715
|
+
}
|
|
716
|
+
/** Parse `check-deps [dir]`. */
|
|
717
|
+
function parseCheckDepsOptions(argv) {
|
|
718
|
+
const args = argv.filter((a) => !a.startsWith("-"));
|
|
719
|
+
if (args.length !== argv.length || args.length > 1) throw new Error("check-deps takes at most one argument: the app folder");
|
|
720
|
+
return { directory: args[0] };
|
|
721
|
+
}
|
|
722
|
+
/** Report each provider import outside the opener, as `file:line: why`. Returns the exit code: 1 when there is one. */
|
|
723
|
+
function checkDeps(options, io) {
|
|
724
|
+
const appDir = resolve(io.cwd, options.directory ?? ".");
|
|
725
|
+
const violations = findViolations(appDir);
|
|
726
|
+
if (violations.length === 0) {
|
|
727
|
+
io.stdout.write("check-deps: no app code imports a database provider outside the opener\n");
|
|
728
|
+
return 0;
|
|
729
|
+
}
|
|
730
|
+
const openers = openersOf(appDir);
|
|
731
|
+
for (const violation of violations) io.stderr.write(`${violation.file}:${violation.line}: ${advice(violation, openers)}\n`);
|
|
732
|
+
io.stderr.write(`See ${GUIDE}\n`);
|
|
733
|
+
return 1;
|
|
734
|
+
}
|
|
735
|
+
//#endregion
|
|
554
736
|
//#region src/spawn.ts
|
|
555
737
|
/**
|
|
556
738
|
* How to start a package manager without the shell option (Node warns about passing args to a shell, DEP0190, and a `.cmd` shim cannot be spawned directly).
|
|
@@ -613,6 +795,81 @@ function processIo() {
|
|
|
613
795
|
};
|
|
614
796
|
}
|
|
615
797
|
//#endregion
|
|
798
|
+
//#region src/kv-import.ts
|
|
799
|
+
/** Parse `kv import <kv.json> <database.sqlite> [--overwrite] [--table name] [--migrations dir]`. */
|
|
800
|
+
function parseKvOptions(argv) {
|
|
801
|
+
let parsed;
|
|
802
|
+
try {
|
|
803
|
+
parsed = parse(argv);
|
|
804
|
+
} catch (error) {
|
|
805
|
+
throw new UsageError(error.message);
|
|
806
|
+
}
|
|
807
|
+
const [action, from, to, ...rest] = parsed.positionals;
|
|
808
|
+
if (action !== "import") throw new UsageError("kv takes one subcommand: kv import <kv.json> <database.sqlite>");
|
|
809
|
+
if (from === void 0 || to === void 0 || rest.length > 0) throw new UsageError("kv import takes two arguments: the JSON file and the SQLite file");
|
|
810
|
+
return {
|
|
811
|
+
from,
|
|
812
|
+
to,
|
|
813
|
+
overwrite: parsed.values.overwrite ?? false,
|
|
814
|
+
table: parsed.values.table,
|
|
815
|
+
migrations: parsed.values.migrations
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
const parse = (argv) => parseArgs({
|
|
819
|
+
args: [...argv],
|
|
820
|
+
allowPositionals: true,
|
|
821
|
+
strict: true,
|
|
822
|
+
options: {
|
|
823
|
+
overwrite: { type: "boolean" },
|
|
824
|
+
table: { type: "string" },
|
|
825
|
+
migrations: { type: "string" }
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
/** The framework as the app has it installed (so the code that reads the file is the code the app runs), else this package's own. */
|
|
829
|
+
async function framework(io, subpath, own) {
|
|
830
|
+
const here = createRequire(resolve(io.cwd, "package.json"));
|
|
831
|
+
try {
|
|
832
|
+
return await import(here.resolve(`acrylblends/${subpath}`));
|
|
833
|
+
} catch {
|
|
834
|
+
return await import(new URL(`../lib/${own}`, import.meta.url).href);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Import a JSON-file kv store into the `kv` table of an app's SQLite database: the move from `acryl-kv-json` to SQL. The file is not changed.
|
|
839
|
+
* The database is opened with the app's `migrations/` folder when it has one (a database whose schema is newer than the files given is refused),
|
|
840
|
+
* and the table is created when missing. Refuses to overwrite a key the table already has unless `--overwrite`.
|
|
841
|
+
*/
|
|
842
|
+
async function kvImport(options, io) {
|
|
843
|
+
const from = resolve(io.cwd, options.from);
|
|
844
|
+
const to = resolve(io.cwd, options.to);
|
|
845
|
+
if (!existsSync(from)) {
|
|
846
|
+
io.stderr.write(`error: ${from} does not exist\n`);
|
|
847
|
+
return 1;
|
|
848
|
+
}
|
|
849
|
+
const kvCore = await framework(io, "kv", "kv.js");
|
|
850
|
+
const json = await framework(io, "kv/json", "kv/json.js");
|
|
851
|
+
const onDb = await framework(io, "kv/db", "kv/db.js");
|
|
852
|
+
const sqlite = await framework(io, "db/sqlite", "db/sqlite.js");
|
|
853
|
+
const migrations = options.migrations !== void 0 ? resolve(io.cwd, options.migrations) : existsSync(resolve(io.cwd, "migrations")) ? resolve(io.cwd, "migrations") : void 0;
|
|
854
|
+
const source = await json.openKv({ path: from });
|
|
855
|
+
try {
|
|
856
|
+
const database = await sqlite.openDb({
|
|
857
|
+
path: to,
|
|
858
|
+
...migrations === void 0 ? {} : { migrations }
|
|
859
|
+
});
|
|
860
|
+
try {
|
|
861
|
+
const target = onDb.kvOnDb(database.db, options.table === void 0 ? {} : { table: options.table });
|
|
862
|
+
const result = await kvCore.copyKv(source.kv, target, { overwrite: options.overwrite });
|
|
863
|
+
io.stdout.write(`imported ${result.copied} ${result.copied === 1 ? "key" : "keys"} from ${options.from} into ${options.to}${options.table === void 0 ? "" : ` (table ${options.table})`}${result.overwritten > 0 ? `, ${result.overwritten} overwritten` : ""}\n`);
|
|
864
|
+
return 0;
|
|
865
|
+
} finally {
|
|
866
|
+
await database.close();
|
|
867
|
+
}
|
|
868
|
+
} finally {
|
|
869
|
+
await source.close();
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
//#endregion
|
|
616
873
|
//#region src/cli.ts
|
|
617
874
|
/** The version of this package. */
|
|
618
875
|
const VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
@@ -624,6 +881,9 @@ Usage:
|
|
|
624
881
|
a folder path brings an external plugin into its extensions/
|
|
625
882
|
acrylblends plugins List the plugin library
|
|
626
883
|
acrylblends build-client [root] [out] Compile the client modules once for production (src/client to dist/client)
|
|
884
|
+
acrylblends kv import <kv.json> <db> Move a JSON-file kv store into the kv table of a SQLite database [--overwrite] [--table t] [--migrations dir]
|
|
885
|
+
acrylblends check-deps [dir] Fail when app code imports a database driver or opener outside the file named in
|
|
886
|
+
package.json "acryl": { "opener": "src/db.ts" } (a port is imported, a provider is not)
|
|
627
887
|
|
|
628
888
|
Options for init:
|
|
629
889
|
--template <name> blank or agent (asked when not given); --agent means --template agent,
|
|
@@ -653,6 +913,8 @@ async function runCli(argv, io = processIo()) {
|
|
|
653
913
|
if (command === "init") return await init(parseInitOptions(args), io);
|
|
654
914
|
if (command === "add") return await add(parseAddOptions(args), io);
|
|
655
915
|
if (command === "build-client") return await buildClient(parseBuildClientOptions(args), io);
|
|
916
|
+
if (command === "kv") return await kvImport(parseKv(args), io);
|
|
917
|
+
if (command === "check-deps") return checkDeps(parseCheckDeps(args), io);
|
|
656
918
|
if (command === "plugins") return listPlugins(io);
|
|
657
919
|
throw new UsageError(`unknown command "${command}"`);
|
|
658
920
|
} catch (error) {
|
|
@@ -664,6 +926,16 @@ async function runCli(argv, io = processIo()) {
|
|
|
664
926
|
return 1;
|
|
665
927
|
}
|
|
666
928
|
}
|
|
929
|
+
function parseCheckDeps(args) {
|
|
930
|
+
try {
|
|
931
|
+
return parseCheckDepsOptions(args);
|
|
932
|
+
} catch (error) {
|
|
933
|
+
throw new UsageError(error.message);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
function parseKv(args) {
|
|
937
|
+
return parseKvOptions(args);
|
|
938
|
+
}
|
|
667
939
|
//#endregion
|
|
668
940
|
export { HELP, TEMPLATES, TargetNotEmptyError, UsageError, VERSION, add, assertTargetFree, closure, copyPlugin, copyTemplate, init, isOmitted, isTemplate, isValidName, libraryRoot, listPlugins, mergeDependencies, nameFrom, parseAddOptions, parseInitOptions, readLibrary, readTemplatePlugins, runCli, templateDir, templatesRoot };
|
|
669
941
|
|