acrylblends 0.7.7 → 0.7.8

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.
Files changed (92) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.js +59 -19
  3. package/dist/index.js.map +1 -1
  4. package/docs/framework/README.md +1 -1
  5. package/docs/framework/UPSTREAM.md +1 -1
  6. package/docs/framework/agents/index.md +18 -3
  7. package/docs/framework/getting-started/desktop.md +2 -2
  8. package/docs/framework/guides/client-modules.md +4 -3
  9. package/docs/framework/guides/data.md +6 -6
  10. package/docs/framework/helpers/streaming.md +12 -27
  11. package/docs/framework/upstream.json +2 -2
  12. package/lib/browser/read-sse.js +84 -0
  13. package/lib/db/sqlite.d.ts +1 -1
  14. package/lib/db.d.ts +1 -84
  15. package/lib/index-BLTYsyc1.d.ts +93 -0
  16. package/lib/index-D4FAsBZu.d.ts +85 -0
  17. package/lib/kv/db.d.ts +2 -2
  18. package/lib/kv/json.d.ts +1 -1
  19. package/lib/kv/json.js +87 -24
  20. package/lib/kv.d.ts +1 -92
  21. package/lib/node-server.d.ts +3 -3
  22. package/lib/node-server.js +13 -8
  23. package/lib/read-sse.d.ts +33 -0
  24. package/lib/read-sse.js +84 -0
  25. package/package.json +5 -1
  26. package/plugins/acryl-db/plugin.json +1 -1
  27. package/plugins/acryl-db/upstream.json +2 -2
  28. package/plugins/acryl-db-sqlite/upstream.json +1 -1
  29. package/plugins/acryl-inspect/upstream.json +1 -1
  30. package/plugins/acryl-kv/plugin.json +1 -1
  31. package/plugins/acryl-kv/upstream.json +1 -1
  32. package/plugins/acryl-kv-db/upstream.json +1 -1
  33. package/plugins/acryl-kv-json/src/open.ts +86 -22
  34. package/plugins/acryl-kv-json/upstream.json +2 -2
  35. package/plugins/acryl-node-server/src/client-modules.ts +10 -9
  36. package/plugins/acryl-node-server/upstream.json +2 -2
  37. package/plugins/acryl-router/src/browser/read-sse.ts +3 -0
  38. package/plugins/acryl-router/src/helpers/read-sse.ts +111 -0
  39. package/plugins/acryl-router/upstream.json +2 -2
  40. package/plugins/acryl-router-http/upstream.json +1 -1
  41. package/plugins/acryl-state/upstream.json +1 -1
  42. package/plugins/builder/index.ts +2 -2
  43. package/plugins/builder/plugin.json +1 -1
  44. package/plugins/desktop/main.ts +3 -3
  45. package/plugins/desktop/plugin.json +2 -2
  46. package/plugins/desktop/upstream.json +1 -1
  47. package/plugins/desktop-web/main.ts +2 -2
  48. package/plugins/desktop-web/plugin.json +2 -2
  49. package/plugins/desktop-web/upstream.json +1 -1
  50. package/plugins/extensions/index.ts +19 -1
  51. package/plugins/extensions/plugin.json +1 -1
  52. package/plugins/extensions/resolve.ts +1 -1
  53. package/plugins/manifest/lock.ts +7 -3
  54. package/plugins/manifest/upgrade-cli.ts +1 -1
  55. package/plugins/save/cli.ts +1 -1
  56. package/plugins/save/plugin.json +1 -1
  57. package/shared/AGENTS.core.md +10 -10
  58. package/shared/DEFAULTS.md +40 -0
  59. package/shared/SKILL.md +14 -0
  60. package/templates/agent/AGENTS.md +1 -1
  61. package/templates/agent/README.md +6 -6
  62. package/templates/agent/gitignore +1 -0
  63. package/templates/agent/package.json +8 -8
  64. package/templates/agent/src/app.ts +28 -15
  65. package/templates/agent/src/main.ts +2 -2
  66. package/templates/agent/src/routes.ts +5 -5
  67. package/templates/agent/test/agent.test.ts +1 -1
  68. package/templates/agent/test/app.test.ts +1 -1
  69. package/templates/agent/test/builder.test.ts +1 -1
  70. package/templates/agent/test/model.test.ts +1 -1
  71. package/templates/agent/tsconfig.json +48 -50
  72. package/templates/blank/AGENTS.md +1 -1
  73. package/templates/blank/README.md +2 -2
  74. package/templates/blank/gitignore +1 -0
  75. package/templates/blank/package.json +8 -8
  76. package/templates/blank/src/about.tsx +2 -2
  77. package/templates/blank/src/app.ts +4 -4
  78. package/templates/blank/src/main.ts +1 -1
  79. package/templates/blank/src/routes.ts +1 -1
  80. package/templates/blank/test/app.test.ts +1 -1
  81. package/templates/blank/tsconfig.json +1 -1
  82. package/templates/todo/README.md +1 -1
  83. package/templates/todo/gitignore +1 -0
  84. package/templates/todo/package.json +8 -8
  85. package/templates/todo/src/app.ts +5 -5
  86. package/templates/todo/src/main.ts +1 -1
  87. package/templates/todo/src/todo.ts +4 -4
  88. package/templates/todo/test/todo.test.ts +1 -1
  89. package/templates/todo/tsconfig.json +1 -1
  90. package/templates/todo/upstream.json +2 -2
  91. package/lib/database-D_UN6We8.d.ts +0 -44
  92. package/lib/kv-DWDm0Lja.d.ts +0 -39
package/dist/index.d.ts CHANGED
@@ -102,7 +102,7 @@ export declare function libraryRoot(): string;
102
102
  export declare function readLibrary(root?: string): Map<string, LibraryPlugin>;
103
103
  /** The plugins to copy for `names`: each one after everything it requires, no duplicates. */
104
104
  export declare function closure(names: readonly string[], library: Map<string, LibraryPlugin>): LibraryPlugin[];
105
- /** Copy one library plugin's source into `<app>/src/plugins/<name>/`. Throws when that folder already exists. */
105
+ /** Copy one library plugin's source into `<app>/plugins/<name>/`. Throws when that folder already exists. */
106
106
  export declare function copyPlugin(plugin: LibraryPlugin, appDir: string, root?: string): string;
107
107
  //#endregion
108
108
  //#region src/project.d.ts
package/dist/index.js CHANGED
@@ -84,10 +84,33 @@ function closure(names, library) {
84
84
  for (const name of names) visit(name, []);
85
85
  return ordered;
86
86
  }
87
- /** Copy one library plugin's source into `<app>/src/plugins/<name>/`. Throws when that folder already exists. */
87
+ /** The one folder every plugin of an app lives in, library copies and the ones an agent writes. */
88
+ const PLUGINS_DIR = "plugins";
89
+ /** Where apps made before 0.7.8 kept library copies; still understood for one release. */
90
+ const LEGACY_PLUGINS_DIR = join("src", "plugins");
91
+ /** The list of library copies in the plugins folder; the extensions plugin installs every other folder there. */
92
+ const LIBRARY_LIST = "library.json";
93
+ /** The plugins folder of an app: `plugins/`, or `src/plugins/` for an app that still has the old layout (and no `plugins/`). */
94
+ function pluginsFolder(appDir) {
95
+ const legacy = existsSync(join(appDir, LEGACY_PLUGINS_DIR)) && !existsSync(join(appDir, "plugins"));
96
+ return {
97
+ dir: join(appDir, legacy ? LEGACY_PLUGINS_DIR : PLUGINS_DIR),
98
+ legacy
99
+ };
100
+ }
101
+ /** The warning for an app with the old layout. */
102
+ const LEGACY_WARNING = "warning: this app keeps its plugins in src/plugins/ (and extensions/); plugins now live in one folder, plugins/. Move them with \"git mv src/plugins plugins\", fix the paths in tsconfig.json, package.json and src/, and see docs/framework/agents/index.md. The old layout is read for this release only.";
103
+ function recordLibraryCopy(folder, name) {
104
+ const file = join(folder, LIBRARY_LIST);
105
+ const names = existsSync(file) ? JSON.parse(readFileSync(file, "utf8")).plugins : [];
106
+ if (!names.includes(name)) names.push(name);
107
+ writeFileSync(file, `${JSON.stringify({ plugins: names.sort() }, null, 2)}\n`);
108
+ }
109
+ /** Copy one library plugin's source into `<app>/plugins/<name>/`. Throws when that folder already exists. */
88
110
  function copyPlugin(plugin, appDir, root = libraryRoot()) {
89
- const target = join(appDir, "src", "plugins", plugin.name);
90
- if (existsSync(target)) throw new LibraryError(`src/plugins/${plugin.name} already exists; remove it first to copy a fresh one`);
111
+ const folder = pluginsFolder(appDir);
112
+ const target = join(folder.dir, plugin.name);
113
+ if (existsSync(target)) throw new LibraryError(`${relativeName(appDir, target)} already exists; remove it first to copy a fresh one`);
91
114
  const source = join(root, plugin.name);
92
115
  cpSync(source, target, {
93
116
  recursive: true,
@@ -96,8 +119,10 @@ function copyPlugin(plugin, appDir, root = libraryRoot()) {
96
119
  return !(dirname(path) === source && LIBRARY_ONLY.has(inside)) && !isOmitted(inside, plugin.omit);
97
120
  }
98
121
  });
122
+ if (!folder.legacy) recordLibraryCopy(folder.dir, plugin.name);
99
123
  return target;
100
124
  }
125
+ const relativeName = (appDir, path) => path.slice(appDir.length + 1).split(sep).join("/");
101
126
  //#endregion
102
127
  //#region src/project.ts
103
128
  /** Thrown when the target directory exists and is not empty. Nothing has been written. */
@@ -159,7 +184,7 @@ function readTemplateDesktopPath(templateDir) {
159
184
  if (!existsSync(file)) return void 0;
160
185
  return JSON.parse(readFileSync(file, "utf8")).desktopPath;
161
186
  }
162
- /** Record where the desktop window opens in package.json (`acrylblends.desktopPath`), read by `src/plugins/desktop/main.ts`. */
187
+ /** Record where the desktop window opens in package.json (`acrylblends.desktopPath`), read by `plugins/desktop/main.ts`. */
163
188
  function writeDesktopPath(appDir, path) {
164
189
  const file = join(appDir, "package.json");
165
190
  const manifest = JSON.parse(readFileSync(file, "utf8"));
@@ -241,9 +266,17 @@ function appendGitignore(appDir, lines) {
241
266
  function writeAgentFiles(sharedDir, templateDir, target, name) {
242
267
  const core = readFileSync(join(sharedDir, "AGENTS.core.md"), "utf8");
243
268
  const own = existsSync(join(templateDir, "AGENTS.md")) ? readFileSync(join(templateDir, "AGENTS.md"), "utf8") : "";
244
- writeFileSync(join(target, "AGENTS.md"), `${core}${own}`.replaceAll("__APP_NAME__", name));
245
- writeFileSync(join(target, "CLAUDE.md"), "@AGENTS.md\n");
246
- return ["AGENTS.md", "CLAUDE.md"];
269
+ const text = `${core}${readFileSync(join(sharedDir, "DEFAULTS.md"), "utf8")}${own}`.replaceAll("__APP_NAME__", name);
270
+ writeFileSync(join(target, "AGENTS.md"), text);
271
+ writeFileSync(join(target, "CLAUDE.md"), text);
272
+ const skill = readFileSync(join(sharedDir, "SKILL.md"), "utf8").replaceAll("__APP_NAME__", name);
273
+ const written = ["AGENTS.md", "CLAUDE.md"];
274
+ for (const folder of [".claude/skills/app-guide", ".agents/skills/app-guide"]) {
275
+ mkdirSync(join(target, folder), { recursive: true });
276
+ writeFileSync(join(target, folder, "SKILL.md"), skill);
277
+ written.push(`${folder}/SKILL.md`);
278
+ }
279
+ return written;
247
280
  }
248
281
  /**
249
282
  * Copy the framework's docs (a generated copy of ACRYL's `docs/framework`, already checked against its digest) into `<app>/docs/framework/`,
@@ -434,7 +467,7 @@ const parse$1 = (argv) => parseArgs({
434
467
  strict: true,
435
468
  options: { dir: { type: "string" } }
436
469
  });
437
- const present = (appDir, name) => existsSync(join(appDir, "src", "plugins", name));
470
+ const present = (appDir, name) => existsSync(join(pluginsFolder(appDir).dir, name));
438
471
  /**
439
472
  * Copy library plugins into an existing app, with the plugins they require, and add their npm packages to its package.json.
440
473
  * Refuses a plugin the app already has. Returns the exit code.
@@ -445,12 +478,13 @@ async function add(options, io) {
445
478
  io.stderr.write(`error: ${appDir} is not an app (no package.json and src/); run "acrylblends init" first\n`);
446
479
  return 1;
447
480
  }
481
+ if (pluginsFolder(appDir).legacy) io.stderr.write(`${LEGACY_WARNING}\n`);
448
482
  const external = options.names.filter((name) => /[\\/]/.test(name) || name.startsWith("."));
449
483
  for (const folder of external) {
450
484
  const code = addExternal(resolve(io.cwd, folder), appDir, io);
451
485
  if (code !== 0) return code;
452
486
  }
453
- const plain = !existsSync(join(appDir, "src", "plugins", "instance"));
487
+ const plain = !existsSync(join(pluginsFolder(appDir).dir, "instance"));
454
488
  if (plain && options.names.includes("db")) {
455
489
  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");
456
490
  return 1;
@@ -463,7 +497,7 @@ async function add(options, io) {
463
497
  if (names.length === 0) return 0;
464
498
  const already = names.filter((name) => present(appDir, name));
465
499
  if (already.length > 0) {
466
- io.stderr.write(`error: the app already has ${already.map((name) => `src/plugins/${name}`).join(", ")}\n`);
500
+ io.stderr.write(`error: the app already has ${already.map((name) => `${relative(appDir, join(pluginsFolder(appDir).dir, name)).split(sep).join("/")}`).join(", ")}\n`);
467
501
  return 1;
468
502
  }
469
503
  const toCopy = closure(names, readLibrary()).filter((plugin) => !present(appDir, plugin.name));
@@ -479,13 +513,13 @@ async function add(options, io) {
479
513
  }
480
514
  io.stdout.write(`added ${toCopy.map((plugin) => plugin.name).join(", ")} to ${appDir}\n`);
481
515
  if (addedDependencies.length > 0) io.stdout.write(`new dependencies: ${addedDependencies.join(", ")} (run npm install)\n`);
482
- for (const plugin of toCopy.filter((plugin) => names.includes(plugin.name))) io.stdout.write(`\n${plugin.name}: ${plugin.mount ?? `import it from './plugins/${plugin.name}/index.ts' and mount it in src/app.ts`}\n`);
516
+ for (const plugin of toCopy.filter((plugin) => names.includes(plugin.name))) io.stdout.write(`\n${plugin.name}: ${plugin.mount ?? `import it from '../plugins/${plugin.name}/index.ts' and mount it in src/app.ts`}\n`);
483
517
  return 0;
484
518
  }
485
519
  const PLUGIN_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
486
520
  /**
487
521
  * Bring an external plugin folder (a package made with the builder's package_plugin, or any folder with an index.ts that exports a
488
- * Cordis plugin) into the app's `extensions/`, where the extensions plugin installs it like any other: checked, staged, started,
522
+ * Cordis plugin) into the app's `plugins/`, where the extensions plugin installs it like any other: checked, staged, started,
489
523
  * verified. Its runtime dependencies are not installed here; they are reported so the user adds them deliberately.
490
524
  */
491
525
  function addExternal(folder, appDir, io) {
@@ -508,16 +542,16 @@ function addExternal(folder, appDir, io) {
508
542
  io.stderr.write(`error: "${name}" is not a plugin name (lowercase letters, digits and dashes)\n`);
509
543
  return 1;
510
544
  }
511
- const target = join(appDir, "extensions", name);
545
+ const target = join(pluginsFolder(appDir).legacy ? join(appDir, "extensions") : join(appDir, PLUGINS_DIR), name);
512
546
  if (existsSync(target)) {
513
- io.stderr.write(`error: the app already has extensions/${name}\n`);
547
+ io.stderr.write(`error: the app already has ${relative(appDir, target).split(sep).join("/")}\n`);
514
548
  return 1;
515
549
  }
516
550
  cpSync(folder, target, {
517
551
  recursive: true,
518
552
  filter: (path) => !/[\\/](node_modules|\.git)([\\/]|$)/.test(path.slice(folder.length))
519
553
  });
520
- io.stdout.write(`added external plugin ${name} to ${join(appDir, "extensions")}; it installs at the next start\n`);
554
+ io.stdout.write(`added external plugin ${name} to ${dirname(target)}; it installs at the next start\n`);
521
555
  const dependencies = Object.keys(manifest.dependencies ?? {});
522
556
  if (dependencies.length > 0) io.stdout.write(`it uses ${dependencies.join(", ")}: add them to the app with npm install first\n`);
523
557
  return 0;
@@ -596,9 +630,14 @@ const DRIVERS = [
596
630
  const OPENERS = ["acrylblends/db/sqlite", "acryl-db-sqlite"];
597
631
  /** The desktop shell is where Electron lives (`acrylblends add desktop` copies it). */
598
632
  const ELECTRON = "electron";
599
- const ELECTRON_HOMES = ["src/plugins/desktop/", "src/plugins/desktop-web/"];
633
+ const ELECTRON_HOMES = [
634
+ "plugins/desktop/",
635
+ "plugins/desktop-web/",
636
+ "src/plugins/desktop/",
637
+ "src/plugins/desktop-web/"
638
+ ];
600
639
  /** The provider plugins themselves may import what they wrap. */
601
- const PROVIDER_HOMES = ["src/plugins/acryl-db-sqlite/"];
640
+ const PROVIDER_HOMES = ["plugins/acryl-db-sqlite/", "src/plugins/acryl-db-sqlite/"];
602
641
  const SOURCE = /\.(?:[cm]?[jt]s|[jt]sx)$/u;
603
642
  const SKIP_DIRS = /* @__PURE__ */ new Set([
604
643
  "node_modules",
@@ -611,6 +650,7 @@ const ROOTS = [
611
650
  "src",
612
651
  "test",
613
652
  "tests",
653
+ "plugins",
614
654
  "extensions"
615
655
  ];
616
656
  const matches = (specifier, name) => specifier === name || specifier.startsWith(`${name}/`);
@@ -709,7 +749,7 @@ function findViolations(appDir) {
709
749
  }
710
750
  const GUIDE = "docs/framework/guides/data.md#why-the-database-is-opened-in-one-place";
711
751
  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`;
752
+ if (violation.kind === "electron") return `"${violation.specifier}" is the desktop shell's: only plugins/desktop (added with "acrylblends add desktop") imports electron`;
713
753
  const where = openers.length > 0 ? openers.join(", ") : "a file named in package.json \"acryl\": { \"opener\": \"...\" }";
714
754
  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
755
  }
@@ -878,7 +918,7 @@ const HELP = `acrylblends ${VERSION} - scaffold Cordis apps
878
918
  Usage:
879
919
  acrylblends init [dir] [options] Create a Cordis app in an empty or new directory
880
920
  acrylblends add <plugin...> [--dir d] Copy library plugins (and what they require) into an app;
881
- a folder path brings an external plugin into its extensions/
921
+ a folder path brings an external plugin into its plugins/
882
922
  acrylblends plugins List the plugin library
883
923
  acrylblends build-client [root] [out] Compile the client modules once for production (src/client to dist/client)
884
924
  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]
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["parse","parse"],"sources":["../src/library.ts","../src/project.ts","../src/templates.ts","../src/init.ts","../src/add.ts","../src/build-client.ts","../src/check-deps.ts","../src/spawn.ts","../src/io.ts","../src/kv-import.ts","../src/cli.ts"],"sourcesContent":["import { cpSync, existsSync, readdirSync, readFileSync } from 'node:fs'\nimport { dirname, join, sep } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n/** One entry of the plugin library: `plugins/<name>/plugin.json` in this repository. */\nexport interface LibraryPlugin {\n readonly name: string\n readonly description: string\n /** Library plugins this one imports; they are copied with it. */\n readonly requires: readonly string[]\n /** npm packages an app needs at run time for this plugin. */\n readonly dependencies: Readonly<Record<string, string>>\n /** npm packages an app needs only to develop or run this plugin's tooling (for example `electron` for the desktop shell). */\n readonly devDependencies?: Readonly<Record<string, string>>\n /** `package.json` scripts the plugin adds; a script the app already has is kept. */\n readonly scripts?: Readonly<Record<string, string>>\n /** Lines the app's `.gitignore` needs for this plugin (added when missing, if the app has a `.gitignore`). */\n readonly gitignore?: readonly string[]\n /** How to mount it, shown after `acrylblends add`. */\n readonly mount?: string\n /**\n * Paths inside the plugin folder that are not copied into an app (for example a React hook an app without React cannot type-check, or colocated test\n * files). A pattern is an exact path or a glob: `*` matches within one folder name, `**` across folders, `?` one character; `**` followed by a slash also matches no folder.\n */\n readonly omit?: readonly string[]\n}\n\n/** A glob as a regular expression: `*` within a name, `**` across folders, `**` plus a slash may match no folder, `?` one character. */\nfunction globToRegExp(glob: string): RegExp {\n let source = ''\n for (let i = 0; i < glob.length; i += 1) {\n const c = glob[i] as string\n if (c === '*' && glob[i + 1] === '*') {\n if (glob[i + 2] === '/') {\n source += '(?:.*/)?'\n i += 2\n } else {\n source += '.*'\n i += 1\n }\n } else if (c === '*') source += '[^/]*'\n else if (c === '?') source += '[^/]'\n else source += c.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n }\n return new RegExp(`^${source}$`)\n}\n\n/** Whether a path inside a plugin folder (relative, any separator) is named by one of its `omit` patterns. */\nexport function isOmitted(inside: string, patterns: readonly string[] | undefined): boolean {\n if (!patterns || patterns.length === 0) return false\n const path = inside.split(sep).join('/')\n return patterns.some((pattern) =>\n /[*?]/.test(pattern) ? globToRegExp(pattern).test(path) : pattern === path,\n )\n}\n\n/** Files of a library plugin that stay in the repository: its manifest and its tests. */\nconst LIBRARY_ONLY = new Set(['plugin.json', 'test', 'tests'])\n\nexport class LibraryError extends Error {\n override name = 'LibraryError'\n}\n\n/** The library folder (`plugins/` at the repository root): walk up from this module, in source or in the build. */\nexport function libraryRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (;;) {\n const candidate = join(dir, 'plugins')\n if (existsSync(join(candidate, 'instance', 'plugin.json'))) return candidate\n const parent = dirname(dir)\n if (parent === dir)\n throw new LibraryError('acrylblends cannot find its plugin library (plugins/)')\n dir = parent\n }\n}\n\n/** Every plugin in the library, by name. */\nexport function readLibrary(root: string = libraryRoot()): Map<string, LibraryPlugin> {\n const plugins = new Map<string, LibraryPlugin>()\n for (const entry of readdirSync(root, { withFileTypes: true })) {\n const manifest = join(root, entry.name, 'plugin.json')\n if (!entry.isDirectory() || !existsSync(manifest)) continue\n const plugin = JSON.parse(readFileSync(manifest, 'utf8')) as LibraryPlugin\n if (plugin.name !== entry.name)\n throw new LibraryError(`plugins/${entry.name}/plugin.json names \"${plugin.name}\"`)\n plugins.set(plugin.name, {\n ...plugin,\n requires: plugin.requires ?? [],\n dependencies: plugin.dependencies ?? {},\n })\n }\n return plugins\n}\n\n/** The plugins to copy for `names`: each one after everything it requires, no duplicates. */\nexport function closure(\n names: readonly string[],\n library: Map<string, LibraryPlugin>,\n): LibraryPlugin[] {\n const ordered: LibraryPlugin[] = []\n const visiting = new Set<string>()\n const visit = (name: string, trail: string[]) => {\n if (ordered.some((plugin) => plugin.name === name)) return\n const plugin = library.get(name)\n if (!plugin)\n throw new LibraryError(\n `no plugin \"${name}\" in the library${trail.length ? ` (required by ${trail.at(-1)})` : ''}`,\n )\n if (visiting.has(name))\n throw new LibraryError(\n `plugins require each other in a cycle: ${[...trail, name].join(' -> ')}`,\n )\n visiting.add(name)\n for (const required of plugin.requires) visit(required, [...trail, name])\n visiting.delete(name)\n ordered.push(plugin)\n }\n for (const name of names) visit(name, [])\n return ordered\n}\n\n/** Copy one library plugin's source into `<app>/src/plugins/<name>/`. Throws when that folder already exists. */\nexport function copyPlugin(\n plugin: LibraryPlugin,\n appDir: string,\n root: string = libraryRoot(),\n): string {\n const target = join(appDir, 'src', 'plugins', plugin.name)\n if (existsSync(target))\n throw new LibraryError(\n `src/plugins/${plugin.name} already exists; remove it first to copy a fresh one`,\n )\n const source = join(root, plugin.name)\n cpSync(source, target, {\n recursive: true,\n filter: (path) => {\n const inside = path.slice(source.length + 1)\n return (\n !(dirname(path) === source && LIBRARY_ONLY.has(inside)) && !isOmitted(inside, plugin.omit)\n )\n },\n })\n return target\n}\n","import {\n cpSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n statSync,\n writeFileSync,\n} from 'node:fs'\nimport { join } from 'node:path'\n\n/** Thrown when the target directory exists and is not empty. Nothing has been written. */\nexport class TargetNotEmptyError extends Error {\n constructor(readonly target: string) {\n super(\n `refusing to write into \"${target}\": the directory is not empty. Choose an empty or new directory.`,\n )\n this.name = 'TargetNotEmptyError'\n }\n}\n\n/** A project name npm accepts: lower case letters, digits and dashes, starting with a letter. */\nexport function isValidName(name: string): boolean {\n return /^[a-z][a-z0-9-]*$/.test(name) && name.length <= 214\n}\n\n/** A valid default project name derived from a directory path. */\nexport function nameFrom(directory: string): string {\n const base = directory.split(/[\\\\/]/).filter(Boolean).pop() ?? ''\n const cleaned = base\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, '-')\n .replace(/^[^a-z]+/, '')\n .replace(/-+$/, '')\n return cleaned || 'my-app'\n}\n\n/** Refuse a target that exists and is not an empty directory. A missing target is fine: it is created. */\nexport function assertTargetFree(target: string): void {\n if (!existsSync(target)) return\n if (!statSync(target).isDirectory() || readdirSync(target).length > 0)\n throw new TargetNotEmptyError(target)\n}\n\n/**\n * Copy a template into `target`, replacing `__APP_NAME__` with `name`. Checks the target before writing anything.\n *\n * @returns the copied files, relative to `target`\n */\nexport function copyTemplate(source: string, target: string, name: string): string[] {\n assertTargetFree(target)\n mkdirSync(target, { recursive: true })\n const written: string[] = []\n const walk = (from: string, to: string, prefix: string) => {\n for (const entry of readdirSync(from, { withFileTypes: true })) {\n if (\n entry.name === 'node_modules' ||\n (prefix === '' &&\n (entry.name === TEMPLATE_MANIFEST ||\n entry.name === 'upstream.json' ||\n entry.name === 'AGENTS.md'))\n )\n continue\n const sourcePath = join(from, entry.name)\n // npm and git treat a file named .gitignore specially, so templates keep it as `gitignore` (and `dockerignore`).\n const targetName =\n prefix === '' && (entry.name === 'gitignore' || entry.name === 'dockerignore')\n ? `.${entry.name}`\n : entry.name\n const targetPath = join(to, targetName)\n if (entry.isDirectory()) {\n mkdirSync(targetPath, { recursive: true })\n walk(sourcePath, targetPath, `${prefix}${entry.name}/`)\n continue\n }\n const text = readFileSync(sourcePath, 'utf8')\n if (text.includes('__APP_NAME__'))\n writeFileSync(targetPath, text.replaceAll('__APP_NAME__', name))\n else cpSync(sourcePath, targetPath)\n written.push(`${prefix}${targetName}`)\n }\n }\n walk(source, target, '')\n return written\n}\n\n/** A template's own manifest, `template.json`: the library plugins it is built from. Never copied (nor is `upstream.json`, the record of what was synced from ACRYL, nor the template's `AGENTS.md`, which `writeAgentFiles` joins to the shared core). */\nexport const TEMPLATE_MANIFEST = 'template.json'\n\n/** The address a template's desktop window opens on, when it is not the root (`template.json`: `desktopPath`). */\nexport function readTemplateDesktopPath(templateDir: string): string | undefined {\n const file = join(templateDir, TEMPLATE_MANIFEST)\n if (!existsSync(file)) return undefined\n return (JSON.parse(readFileSync(file, 'utf8')) as { desktopPath?: string }).desktopPath\n}\n\n/** Record where the desktop window opens in package.json (`acrylblends.desktopPath`), read by `src/plugins/desktop/main.ts`. */\nexport function writeDesktopPath(appDir: string, path: string): void {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as {\n acrylblends?: Record<string, unknown>\n }\n manifest.acrylblends = { ...manifest.acrylblends, desktopPath: path }\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n}\n\nexport function readTemplatePlugins(templateDir: string): string[] {\n const file = join(templateDir, TEMPLATE_MANIFEST)\n if (!existsSync(file)) return []\n const { plugins } = JSON.parse(readFileSync(file, 'utf8')) as { plugins?: string[] }\n return plugins ?? []\n}\n\n/** Add run-time dependencies to an app's package.json, keeping what is there and sorting the keys. Returns the names added. */\nexport function mergeDependencies(\n appDir: string,\n dependencies: Readonly<Record<string, string>>,\n): string[] {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as {\n dependencies?: Record<string, string>\n }\n const current = manifest.dependencies ?? {}\n const added = Object.keys(dependencies).filter((name) => !(name in current))\n const merged = { ...dependencies, ...current }\n manifest.dependencies = Object.fromEntries(\n Object.entries(merged).sort(([a], [b]) => a.localeCompare(b)),\n )\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n return added\n}\n\n/** Declare a file as an opener in package.json (`\"acryl\": { \"opener\": [...] }`): the one place that may import a database provider. */\nexport function mergeOpener(appDir: string, opener: string): void {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as {\n acryl?: { opener?: string | string[] }\n }\n const current = manifest.acryl?.opener\n const list = typeof current === 'string' ? [current] : (current ?? [])\n if (list.includes(opener)) return\n manifest.acryl = { ...manifest.acryl, opener: [...list, opener] }\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n}\n\n/**\n * 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\n * and scripts added.\n */\nexport function mergeTooling(\n appDir: string,\n tooling: {\n devDependencies?: Readonly<Record<string, string>> | undefined\n scripts?: Readonly<Record<string, string>> | undefined\n },\n): { devDependencies: string[]; scripts: string[] } {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as {\n devDependencies?: Record<string, string>\n scripts?: Record<string, string>\n }\n const dev = manifest.devDependencies ?? {}\n const scripts = manifest.scripts ?? {}\n const addedDev = Object.keys(tooling.devDependencies ?? {}).filter((name) => !(name in dev))\n const addedScripts = Object.keys(tooling.scripts ?? {}).filter((name) => !(name in scripts))\n manifest.devDependencies = Object.fromEntries(\n Object.entries({ ...tooling.devDependencies, ...dev }).sort(([a], [b]) => a.localeCompare(b)),\n )\n manifest.scripts = { ...tooling.scripts, ...scripts }\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n return { devDependencies: addedDev, scripts: addedScripts }\n}\n\n/** Add the lines a plugin needs to the app's `.gitignore` when it has one and they are missing. Returns the lines added. */\nexport function appendGitignore(appDir: string, lines: readonly string[] | undefined): string[] {\n const file = join(appDir, '.gitignore')\n if (!lines || lines.length === 0 || !existsSync(file)) return []\n const current = readFileSync(file, 'utf8')\n const present = new Set(current.split('\\n').map((line) => line.trim()))\n const missing = lines.filter((line) => !present.has(line))\n if (missing.length > 0)\n writeFileSync(\n file,\n `${current}${current.endsWith('\\n') || current === '' ? '' : '\\n'}${missing.join('\\n')}\\n`,\n )\n return missing\n}\n\n/** The generated app's guide for a coding agent: the shared core, then the template's own part. `CLAUDE.md` is only an import of it. */\nexport function writeAgentFiles(\n sharedDir: string,\n templateDir: string,\n target: string,\n name: string,\n): string[] {\n const core = readFileSync(join(sharedDir, 'AGENTS.core.md'), 'utf8')\n const own = existsSync(join(templateDir, 'AGENTS.md'))\n ? readFileSync(join(templateDir, 'AGENTS.md'), 'utf8')\n : ''\n writeFileSync(join(target, 'AGENTS.md'), `${core}${own}`.replaceAll('__APP_NAME__', name))\n writeFileSync(join(target, 'CLAUDE.md'), '@AGENTS.md\\n')\n return ['AGENTS.md', 'CLAUDE.md']\n}\n\n/**\n * Copy the framework's docs (a generated copy of ACRYL's `docs/framework`, already checked against its digest) into `<app>/docs/framework/`,\n * so a coding agent working in the app reads the API from the folder it is in. The generated-copy notes and the digest record stay behind.\n *\n * @returns the copied files, relative to `target`\n */\nexport function copyFrameworkDocs(source: string, target: string): string[] {\n const written: string[] = []\n const walk = (from: string, to: string, prefix: string) => {\n for (const entry of readdirSync(from, { withFileTypes: true })) {\n if (entry.name === 'upstream.json' || entry.name === 'UPSTREAM.md') continue\n const sourcePath = join(from, entry.name)\n const targetPath = join(to, entry.name)\n if (entry.isDirectory()) {\n mkdirSync(targetPath, { recursive: true })\n walk(sourcePath, targetPath, `${prefix}${entry.name}/`)\n continue\n }\n cpSync(sourcePath, targetPath)\n written.push(`docs/framework/${prefix}${entry.name}`)\n }\n }\n mkdirSync(join(target, 'docs', 'framework'), { recursive: true })\n walk(source, join(target, 'docs', 'framework'), '')\n return written\n}\n","import { existsSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n/** The templates: a blank Cordis app on the router, a todo list built only on the router and state, and the blank app with the offline agent. */\nexport const TEMPLATES = ['blank', 'todo', 'agent'] as const\nexport type Template = (typeof TEMPLATES)[number]\n\nexport function isTemplate(value: string): value is Template {\n return (TEMPLATES as readonly string[]).includes(value)\n}\n\n/**\n * The `templates/` folder of this package. The module sits at different depths in source (`src/`) and in the build\n * (`dist/`), so walk up until a `templates` folder holds the `blank` template.\n */\nexport function templatesRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (;;) {\n const candidate = join(dir, 'templates')\n if (existsSync(join(candidate, 'blank'))) return candidate\n const parent = dirname(dir)\n if (parent === dir) throw new Error('acrylblends cannot find its templates folder')\n dir = parent\n }\n}\n\n/** Absolute path of a template folder. */\nexport function templateDir(template: Template): string {\n return join(templatesRoot(), template)\n}\n\n/** The text every generated app's `AGENTS.md` starts with (`shared/`, beside `templates/`). */\nexport function sharedDir(): string {\n return join(dirname(templatesRoot()), 'shared')\n}\n\n/**\n * The framework's docs, as ACRYL wrote them: `docs/framework` of the repository, or the copy `prepack` puts inside the package. The module sits at\n * different depths in source and in the build, so walk up until a `docs/framework` folder holds the README.\n */\nexport function frameworkDocsRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (;;) {\n const candidate = join(dir, 'docs', 'framework')\n if (existsSync(join(candidate, 'README.md'))) return candidate\n const parent = dirname(dir)\n if (parent === dir)\n throw new Error('acrylblends cannot find the framework docs (docs/framework)')\n dir = parent\n }\n}\n","import { resolve } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport type { Io } from './io'\nimport { closure, copyPlugin, readLibrary } from './library'\nimport {\n copyFrameworkDocs,\n copyTemplate,\n isValidName,\n mergeDependencies,\n mergeTooling,\n nameFrom,\n readTemplateDesktopPath,\n readTemplatePlugins,\n TargetNotEmptyError,\n writeAgentFiles,\n writeDesktopPath,\n} from './project'\nimport {\n frameworkDocsRoot,\n isTemplate,\n sharedDir,\n TEMPLATES,\n type Template,\n templateDir,\n} from './templates'\n\n/** A command line mistake. The CLI prints it with the help text and exits 2. */\nexport class UsageError extends Error {\n override name = 'UsageError'\n}\n\n/** What `init` was asked for. Anything left undefined is asked for on a terminal, or defaulted. */\nexport interface InitOptions {\n directory: string | undefined\n name: string | undefined\n template: Template | undefined\n install: boolean | undefined\n /** Also add the desktop shell (`add desktop`): an Electron window on the app's own server. */\n desktop: boolean\n yes: boolean\n}\n\n/** Parse `init [dir] [--template t] [--agent|--no-agent] [--name n] [--install] [-y]`. `--agent` means `--template agent`, `--no-agent` means `--template blank`. */\nexport function parseInitOptions(argv: readonly string[]): InitOptions {\n let parsed: ReturnType<typeof parse>\n try {\n parsed = parse(argv)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n const { values, positionals } = parsed\n if (positionals.length > 1) throw new UsageError('init takes at most one directory')\n if (values.agent && values['no-agent'])\n throw new UsageError('use --agent or --no-agent, not both')\n if (values.template !== undefined && !isTemplate(values.template))\n throw new UsageError(`unknown template \"${values.template}\" (${TEMPLATES.join(', ')})`)\n const alias: Template | undefined = values.agent\n ? 'agent'\n : values['no-agent']\n ? 'blank'\n : undefined\n if (values.template !== undefined && alias !== undefined && values.template !== alias)\n throw new UsageError(\n `--template ${values.template} contradicts --${alias === 'agent' ? 'agent' : 'no-agent'}`,\n )\n return {\n directory: positionals[0],\n name: values.name,\n template: (values.template as Template | undefined) ?? alias,\n install: values.install,\n desktop: values.desktop === true,\n yes: values.yes === true,\n }\n}\n\nconst parse = (argv: readonly string[]) =>\n parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: {\n name: { type: 'string' },\n template: { type: 'string' },\n agent: { type: 'boolean' },\n 'no-agent': { type: 'boolean' },\n install: { type: 'boolean' },\n desktop: { type: 'boolean' },\n yes: { type: 'boolean', short: 'y' },\n },\n })\n\nconst yes = (answer: string) => ['y', 'yes'].includes(answer.trim().toLowerCase())\n\nasync function askTemplate(ask: (question: string) => Promise<string>): Promise<Template> {\n const answer = (await ask(`Template (${TEMPLATES.join(', ')}) [blank]: `)).trim().toLowerCase()\n if (answer === '') return 'blank'\n if (isTemplate(answer)) return answer\n throw new UsageError(`unknown template \"${answer}\" (${TEMPLATES.join(', ')})`)\n}\n\n/** Scaffold a Cordis app. Returns the exit code: 0 done, 1 refused or failed. Never overwrites anything. */\nexport async function init(options: InitOptions, io: Io): Promise<number> {\n const relative = options.directory ?? '.'\n const directory = resolve(io.cwd, relative)\n const ask = options.yes ? undefined : io.ask\n\n let name = options.name ?? nameFrom(directory)\n if (options.name === undefined && ask)\n name = (await ask(`Project name (${name}): `)).trim() || name\n if (!isValidName(name)) {\n throw new UsageError(\n `\"${name}\" is not a valid project name (lower case letters, digits and dashes, starting with a letter)`,\n )\n }\n const template = options.template ?? (ask ? await askTemplate(ask) : 'blank')\n const install =\n options.install ?? (ask ? yes(await ask('Install dependencies with npm now? (y/N): ')) : false)\n\n let files: string[]\n try {\n files = copyTemplate(templateDir(template), directory, name)\n files.push(...writeAgentFiles(sharedDir(), templateDir(template), directory, name))\n files.push(...copyFrameworkDocs(frameworkDocsRoot(), directory))\n } catch (error) {\n if (!(error instanceof TargetNotEmptyError)) throw error\n io.stderr.write(`error: ${error.message}\\n`)\n return 1\n }\n const plugins = closure(\n [...readTemplatePlugins(templateDir(template)), ...(options.desktop ? ['desktop'] : [])],\n readLibrary(),\n )\n for (const plugin of plugins) {\n copyPlugin(plugin, directory)\n mergeDependencies(directory, plugin.dependencies)\n mergeTooling(directory, plugin)\n }\n const desktopPath = options.desktop ? readTemplateDesktopPath(templateDir(template)) : undefined\n if (desktopPath !== undefined) writeDesktopPath(directory, desktopPath)\n io.stdout.write(\n `created ${name} (${template}) in ${directory}: ${files.length} files, plugins ${plugins.map((plugin) => plugin.name).join(', ')}\\n`,\n )\n\n if (install && (await io.install(directory)) !== 0) {\n io.stderr.write('error: npm install failed; run it yourself in the new directory\\n')\n return 1\n }\n const steps = [\n relative === '.' ? '' : ` cd ${relative}\\n`,\n install ? '' : ' npm install\\n',\n ' npm run dev\\n npm test\\n',\n ]\n io.stdout.write(`\\nNext:\\n${steps.join('')}`)\n return 0\n}\n","import { cpSync, existsSync, readFileSync, statSync } from 'node:fs'\nimport { basename, join, resolve } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport { UsageError } from './init'\nimport type { Io } from './io'\nimport { closure, copyPlugin, type LibraryPlugin, readLibrary } from './library'\nimport { appendGitignore, mergeDependencies, mergeOpener, mergeTooling } from './project'\n\nexport interface AddOptions {\n readonly names: readonly string[]\n readonly directory: string | undefined\n}\n\n/** Parse `add <plugin...> [--dir <app>]`. */\nexport function parseAddOptions(argv: readonly string[]): AddOptions {\n let parsed: ReturnType<typeof parse>\n try {\n parsed = parse(argv)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n if (parsed.positionals.length === 0)\n throw new UsageError('add needs at least one plugin name (see \"acrylblends plugins\")')\n return { names: parsed.positionals, directory: parsed.values.dir }\n}\n\nconst parse = (argv: readonly string[]) =>\n parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: { dir: { type: 'string' } },\n })\n\nconst present = (appDir: string, name: string) => existsSync(join(appDir, 'src', 'plugins', name))\n\n/**\n * Copy library plugins into an existing app, with the plugins they require, and add their npm packages to its package.json.\n * Refuses a plugin the app already has. Returns the exit code.\n */\nexport async function add(options: AddOptions, io: Io): Promise<number> {\n const appDir = resolve(io.cwd, options.directory ?? '.')\n if (!existsSync(join(appDir, 'package.json')) || !existsSync(join(appDir, 'src'))) {\n io.stderr.write(\n `error: ${appDir} is not an app (no package.json and src/); run \"acrylblends init\" first\\n`,\n )\n return 1\n }\n // A path is an external plugin folder; anything else is a library plugin name.\n const external = options.names.filter((name) => /[\\\\/]/.test(name) || name.startsWith('.'))\n for (const folder of external) {\n const code = addExternal(resolve(io.cwd, folder), appDir, io)\n if (code !== 0) return code\n }\n // The desktop shell has two forms: the Blend app (it needs the `instance` plugin) and a plain Acryl app (`export default app`); `desktop` means the one that fits.\n const plain = !existsSync(join(appDir, 'src', 'plugins', 'instance'))\n if (plain && options.names.includes('db')) {\n io.stderr.write(\n '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',\n )\n return 1\n }\n if (plain && options.names.includes('kv')) {\n io.stderr.write(\n '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',\n )\n return 1\n }\n const names = options.names\n .filter((name) => !external.includes(name))\n // `db` is the SQLite provider of the db port (it brings the port); a plain app opens its database with `acrylblends/db/sqlite` instead.\n .map((name) => (name === 'db' ? 'acryl-db-sqlite' : name))\n // `kv` is the JSON-file provider of the kv port (it brings the port); `acryl-kv-db` is the one over db.\n .map((name) => (name === 'kv' ? 'acryl-kv-json' : name))\n .map((name) => (name === 'desktop' && plain ? 'desktop-web' : name))\n if (names.length === 0) return 0\n const already = names.filter((name) => present(appDir, name))\n if (already.length > 0) {\n io.stderr.write(\n `error: the app already has ${already.map((name) => `src/plugins/${name}`).join(', ')}\\n`,\n )\n return 1\n }\n const toCopy: LibraryPlugin[] = closure(names, readLibrary()).filter(\n (plugin) => !present(appDir, plugin.name),\n )\n const addedDependencies: string[] = []\n for (const plugin of toCopy) {\n copyPlugin(plugin, appDir)\n addedDependencies.push(...mergeDependencies(appDir, plugin.dependencies))\n const tooling = mergeTooling(appDir, plugin)\n appendGitignore(appDir, plugin.gitignore)\n // The provider is mounted from src/app.ts, which is therefore the one file that may import it (`acrylblends check-deps`).\n if (plugin.name === 'acryl-db-sqlite') mergeOpener(appDir, 'src/app.ts')\n addedDependencies.push(...tooling.devDependencies)\n if (tooling.scripts.length > 0)\n io.stdout.write(\n `new scripts: ${tooling.scripts.map((name) => `npm run ${name}`).join(', ')}\\n`,\n )\n }\n io.stdout.write(`added ${toCopy.map((plugin) => plugin.name).join(', ')} to ${appDir}\\n`)\n if (addedDependencies.length > 0)\n io.stdout.write(`new dependencies: ${addedDependencies.join(', ')} (run npm install)\\n`)\n for (const plugin of toCopy.filter((plugin) => names.includes(plugin.name))) {\n io.stdout.write(\n `\\n${plugin.name}: ${plugin.mount ?? `import it from './plugins/${plugin.name}/index.ts' and mount it in src/app.ts`}\\n`,\n )\n }\n return 0\n}\n\nconst PLUGIN_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/\n\n/**\n * Bring an external plugin folder (a package made with the builder's package_plugin, or any folder with an index.ts that exports a\n * Cordis plugin) into the app's `extensions/`, where the extensions plugin installs it like any other: checked, staged, started,\n * verified. Its runtime dependencies are not installed here; they are reported so the user adds them deliberately.\n */\nfunction addExternal(folder: string, appDir: string, io: Io): number {\n if (!existsSync(folder) || !statSync(folder).isDirectory()) {\n io.stderr.write(`error: ${folder} is not a folder\\n`)\n return 1\n }\n if (!['index.ts', 'index.js', 'index.mjs'].some((entry) => existsSync(join(folder, entry)))) {\n io.stderr.write(`error: ${folder} has no index.ts that exports a Cordis plugin\\n`)\n return 1\n }\n const manifestFile = join(folder, 'package.json')\n const manifest = existsSync(manifestFile)\n ? (JSON.parse(readFileSync(manifestFile, 'utf8')) as {\n name?: string\n dependencies?: Record<string, string>\n })\n : {}\n const name = (manifest.name ?? basename(folder)).replace(/^@[^/]+\\//, '')\n if (!PLUGIN_NAME.test(name)) {\n io.stderr.write(\n `error: \"${name}\" is not a plugin name (lowercase letters, digits and dashes)\\n`,\n )\n return 1\n }\n const target = join(appDir, 'extensions', name)\n if (existsSync(target)) {\n io.stderr.write(`error: the app already has extensions/${name}\\n`)\n return 1\n }\n cpSync(folder, target, {\n recursive: true,\n filter: (path) => !/[\\\\/](node_modules|\\.git)([\\\\/]|$)/.test(path.slice(folder.length)),\n })\n io.stdout.write(\n `added external plugin ${name} to ${join(appDir, 'extensions')}; it installs at the next start\\n`,\n )\n const dependencies = Object.keys(manifest.dependencies ?? {})\n if (dependencies.length > 0)\n io.stdout.write(\n `it uses ${dependencies.join(', ')}: add them to the app with npm install first\\n`,\n )\n return 0\n}\n\n/** `acrylblends plugins`: the library, one line per plugin. */\nexport function listPlugins(io: Io): number {\n for (const plugin of readLibrary().values()) {\n const requires = plugin.requires.length > 0 ? ` (requires ${plugin.requires.join(', ')})` : ''\n io.stdout.write(`${plugin.name.padEnd(12)} ${plugin.description}${requires}\\n`)\n }\n return 0\n}\n","import { existsSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { relative, resolve } from 'node:path'\nimport { UsageError } from './init'\nimport type { Io } from './io'\n\n/** Parse `build-client [root] [outDir]`. */\nexport function parseBuildClientOptions(argv: readonly string[]): { root: string; outDir: string } {\n const args = argv.filter((a) => !a.startsWith('-'))\n if (args.length !== argv.length)\n throw new UsageError('build-client takes [root] [outDir] and no options')\n if (args.length > 2)\n throw new UsageError('build-client takes at most two arguments: [root] [outDir]')\n return { root: args[0] ?? 'src/client', outDir: args[1] ?? 'dist/client' }\n}\n\n/**\n * Compile the app's client modules once (`buildClientModules` of the framework), for a production install that has no compiler at run time.\n * The framework is the one installed in the app (so the compiled files match the server that serves them), else this package's own.\n */\nexport async function buildClient(\n options: { root: string; outDir: string },\n io: Io,\n): Promise<number> {\n const root = resolve(io.cwd, options.root)\n if (!existsSync(root)) {\n io.stderr.write(\n `error: ${root} does not exist; the client modules live in src/client by default\\n`,\n )\n return 1\n }\n const outDir = resolve(io.cwd, options.outDir)\n const here = createRequire(resolve(io.cwd, 'package.json'))\n let specifier = 'acrylblends/node-server'\n try {\n here.resolve(specifier)\n } catch {\n specifier = new URL('../lib/node-server.js', import.meta.url).href\n }\n const mod = (await import(\n specifier.startsWith('file:') ? specifier : here.resolve(specifier)\n )) as typeof import('../lib/node-server')\n const written = mod.buildClientModules({ root, outDir })\n for (const file of written) io.stdout.write(`${relative(io.cwd, resolve(outDir, file))}\\n`)\n io.stdout.write(`built ${written.length} files in ${relative(io.cwd, outDir) || '.'}\\n`)\n return 0\n}\n","import { existsSync, readdirSync, readFileSync } from 'node:fs'\nimport { join, relative, resolve, sep } from 'node:path'\nimport type { Io } from './io'\n\n/**\n * 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\n * 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.\n */\n\n/** A package that is a database driver. An import of it, or of anything below it, is a provider import. */\nconst DRIVERS = [\n 'node:sqlite',\n 'sqlite',\n 'sqlite3',\n 'better-sqlite3',\n '@libsql/client',\n 'libsql',\n 'pg',\n 'pg-pool',\n 'pg-native',\n 'postgres',\n '@electric-sql/pglite',\n '@neondatabase/serverless',\n '@vercel/postgres',\n 'mysql',\n 'mysql2',\n]\n/** The openers a provider ships: importing one of them is opening a database. */\nconst OPENERS = ['acrylblends/db/sqlite', 'acryl-db-sqlite']\n/** The desktop shell is where Electron lives (`acrylblends add desktop` copies it). */\nconst ELECTRON = 'electron'\nconst ELECTRON_HOMES = ['src/plugins/desktop/', 'src/plugins/desktop-web/']\n/** The provider plugins themselves may import what they wrap. */\nconst PROVIDER_HOMES = ['src/plugins/acryl-db-sqlite/']\n\nconst SOURCE = /\\.(?:[cm]?[jt]s|[jt]sx)$/u\nconst SKIP_DIRS = new Set(['node_modules', 'dist', 'lib', 'build', 'coverage'])\nconst ROOTS = ['src', 'test', 'tests', 'extensions']\n\nexport interface Violation {\n readonly file: string\n readonly line: number\n readonly specifier: string\n readonly kind: 'driver' | 'opener' | 'electron'\n}\n\nconst matches = (specifier: string, name: string): boolean =>\n specifier === name || specifier.startsWith(`${name}/`)\n\n/** What a specifier is to the rule, or undefined when it is not a provider. */\nexport function classifySpecifier(specifier: string): Violation['kind'] | undefined {\n if (DRIVERS.some((name) => matches(specifier, name))) return 'driver'\n if (OPENERS.some((name) => matches(specifier, name))) return 'opener'\n if (matches(specifier, ELECTRON)) return 'electron'\n return undefined\n}\n\n/** 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. */\nexport function blankComments(source: string): string {\n let out = ''\n let i = 0\n while (i < source.length) {\n const c = source[i] as string\n const next = source[i + 1]\n if (c === '/' && next === '/') {\n const end = source.indexOf('\\n', i)\n const stop = end === -1 ? source.length : end\n out += ' '.repeat(stop - i)\n i = stop\n } else if (c === '/' && next === '*') {\n const end = source.indexOf('*/', i + 2)\n const stop = end === -1 ? source.length : end + 2\n out += source.slice(i, stop).replace(/[^\\n]/gu, ' ')\n i = stop\n } else if (c === \"'\" || c === '\"' || c === '`') {\n let j = i + 1\n while (j < source.length && source[j] !== c) {\n if (source[j] === '\\\\') j += 1\n else if (c !== '`' && source[j] === '\\n') break\n j += 1\n }\n out += source.slice(i, Math.min(j + 1, source.length))\n i = j + 1\n } else {\n out += c\n i += 1\n }\n }\n return out\n}\n\n/** `import x from 'a'`, `export * from 'a'`, `import 'a'`, `import('a')` and `require('a')`, with the line of each. */\nconst IMPORT =\n /(?:^|[\\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\n\nexport function importsOf(source: string): { specifier: string; line: number }[] {\n const code = blankComments(source)\n const found: { specifier: string; line: number }[] = []\n for (const match of code.matchAll(IMPORT)) {\n const specifier = match[2] as string\n const at = (match.index ?? 0) + match[0].length - specifier.length - 2\n found.push({ specifier, line: code.slice(0, at).split('\\n').length })\n }\n return found\n}\n\nfunction listSources(dir: string, out: string[] = []): string[] {\n if (!existsSync(dir)) return out\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.name.startsWith('.') || SKIP_DIRS.has(entry.name)) continue\n const path = join(dir, entry.name)\n if (entry.isDirectory()) listSources(path, out)\n else if (SOURCE.test(entry.name) && !entry.name.endsWith('.d.ts')) out.push(path)\n }\n return out\n}\n\n/** The opener files the app declares: `\"acryl\": { \"opener\": \"src/db.ts\" }` (or a list) in package.json. */\nexport function openersOf(appDir: string): string[] {\n const file = join(appDir, 'package.json')\n if (!existsSync(file)) return []\n const declared = (JSON.parse(readFileSync(file, 'utf8')) as { acryl?: { opener?: unknown } })\n .acryl?.opener\n const list = typeof declared === 'string' ? [declared] : Array.isArray(declared) ? declared : []\n return list.filter((entry): entry is string => typeof entry === 'string')\n}\n\n/** Every provider import outside the declared opener (and the homes that may hold one), in file and line order. */\nexport function findViolations(appDir: string): Violation[] {\n const openers = new Set(openersOf(appDir).map((entry) => entry.split(sep).join('/')))\n const violations: Violation[] = []\n for (const root of ROOTS) {\n for (const path of listSources(join(appDir, root)).sort()) {\n const file = relative(appDir, path).split(sep).join('/')\n if (openers.has(file)) continue\n for (const { specifier, line } of importsOf(readFileSync(path, 'utf8'))) {\n const kind = classifySpecifier(specifier)\n if (kind === undefined) continue\n if (kind === 'electron' && ELECTRON_HOMES.some((home) => file.startsWith(home))) continue\n if (kind !== 'electron' && PROVIDER_HOMES.some((home) => file.startsWith(home))) continue\n violations.push({ file, line, specifier, kind })\n }\n }\n }\n return violations\n}\n\nconst GUIDE = 'docs/framework/guides/data.md#why-the-database-is-opened-in-one-place'\n\nfunction advice(violation: Violation, openers: readonly string[]): string {\n if (violation.kind === 'electron')\n return `\"${violation.specifier}\" is the desktop shell's: only src/plugins/desktop (added with \"acrylblends add desktop\") imports electron`\n const where =\n openers.length > 0\n ? openers.join(', ')\n : 'a file named in package.json \"acryl\": { \"opener\": \"...\" }'\n return violation.kind === 'driver'\n ? `\"${violation.specifier}\" is a database driver; take a Db (import type { Db } from \"acrylblends/db\") as an argument and let ${where} open the database`\n : `\"${violation.specifier}\" opens a database, and only ${where} may; take a Db (import type { Db } from \"acrylblends/db\") as an argument`\n}\n\n/** Parse `check-deps [dir]`. */\nexport function parseCheckDepsOptions(argv: readonly string[]): { directory: string | undefined } {\n const args = argv.filter((a) => !a.startsWith('-'))\n if (args.length !== argv.length || args.length > 1)\n throw new Error('check-deps takes at most one argument: the app folder')\n return { directory: args[0] }\n}\n\n/** Report each provider import outside the opener, as `file:line: why`. Returns the exit code: 1 when there is one. */\nexport function checkDeps(options: { directory: string | undefined }, io: Io): number {\n const appDir = resolve(io.cwd, options.directory ?? '.')\n const violations = findViolations(appDir)\n if (violations.length === 0) {\n io.stdout.write('check-deps: no app code imports a database provider outside the opener\\n')\n return 0\n }\n const openers = openersOf(appDir)\n for (const violation of violations)\n io.stderr.write(`${violation.file}:${violation.line}: ${advice(violation, openers)}\\n`)\n io.stderr.write(`See ${GUIDE}\\n`)\n return 1\n}\n","import { existsSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\n\nexport interface Invocation {\n file: string\n args: string[]\n}\n\n/**\n * 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).\n * Off Windows the command runs as it is. On Windows npm runs as `node npm-cli.js` (the one npm is running from, else the one beside `node`); any other\n * manager goes through `cmd.exe /d /s /c <name>.cmd`, whose arguments stay an array.\n */\nexport function packageManagerInvocation(\n command: string,\n args: readonly string[],\n platform: NodeJS.Platform = process.platform,\n env: NodeJS.ProcessEnv = process.env,\n exists: (path: string) => boolean = existsSync,\n execPath: string = process.execPath,\n): Invocation {\n if (platform !== 'win32') return { file: command, args: [...args] }\n if (command === 'npm') {\n const running = env.npm_execpath\n const cli =\n running !== undefined && /npm-cli\\.c?js$/.test(running)\n ? running\n : join(dirname(execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js')\n if (exists(cli)) return { file: execPath, args: [cli, ...args] }\n }\n return { file: env.ComSpec ?? 'cmd.exe', args: ['/d', '/s', '/c', `${command}.cmd`, ...args] }\n}\n","import { spawn } from 'node:child_process'\nimport { createInterface } from 'node:readline/promises'\nimport { packageManagerInvocation } from './spawn'\n\n/** Everything the CLI touches outside the filesystem. Tests pass their own. */\nexport interface Io {\n stdout: { write(text: string): unknown }\n stderr: { write(text: string): unknown }\n cwd: string\n /** Ask one question; `undefined` when there is no terminal to ask on. */\n ask: ((question: string) => Promise<string>) | undefined\n /** Install dependencies in a directory; resolves to the exit code. */\n install(directory: string): Promise<number>\n}\n\n/** The real terminal and `npm install`. */\nexport function processIo(): Io {\n const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY)\n return {\n stdout: process.stdout,\n stderr: process.stderr,\n cwd: process.cwd(),\n ask: interactive\n ? async (question) => {\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n try {\n return await rl.question(question)\n } finally {\n rl.close()\n }\n }\n : undefined,\n install: (directory) =>\n new Promise((done) => {\n const { file, args } = packageManagerInvocation('npm', ['install'])\n const child = spawn(file, args, { cwd: directory, stdio: 'inherit' })\n child.on('error', () => done(1))\n child.on('exit', (code) => done(code ?? 1))\n }),\n }\n}\n","import { existsSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { resolve } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport { UsageError } from './init'\nimport type { Io } from './io'\n\nexport interface KvImportOptions {\n readonly from: string\n readonly to: string\n readonly overwrite: boolean\n readonly table: string | undefined\n readonly migrations: string | undefined\n}\n\n/** Parse `kv import <kv.json> <database.sqlite> [--overwrite] [--table name] [--migrations dir]`. */\nexport function parseKvOptions(argv: readonly string[]): KvImportOptions {\n let parsed: ReturnType<typeof parse>\n try {\n parsed = parse(argv)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n const [action, from, to, ...rest] = parsed.positionals\n if (action !== 'import')\n throw new UsageError('kv takes one subcommand: kv import <kv.json> <database.sqlite>')\n if (from === undefined || to === undefined || rest.length > 0)\n throw new UsageError('kv import takes two arguments: the JSON file and the SQLite file')\n return {\n from,\n to,\n overwrite: parsed.values.overwrite ?? false,\n table: parsed.values.table,\n migrations: parsed.values.migrations,\n }\n}\n\nconst parse = (argv: readonly string[]) =>\n parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: {\n overwrite: { type: 'boolean' },\n table: { type: 'string' },\n migrations: { type: 'string' },\n },\n })\n\n/** 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. */\nasync function framework<T>(io: Io, subpath: string, own: string): Promise<T> {\n const here = createRequire(resolve(io.cwd, 'package.json'))\n try {\n return (await import(here.resolve(`acrylblends/${subpath}`))) as T\n } catch {\n return (await import(new URL(`../lib/${own}`, import.meta.url).href)) as T\n }\n}\n\n/**\n * 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.\n * 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),\n * and the table is created when missing. Refuses to overwrite a key the table already has unless `--overwrite`.\n */\nexport async function kvImport(options: KvImportOptions, io: Io): Promise<number> {\n const from = resolve(io.cwd, options.from)\n const to = resolve(io.cwd, options.to)\n if (!existsSync(from)) {\n io.stderr.write(`error: ${from} does not exist\\n`)\n return 1\n }\n const kvCore = await framework<typeof import('../lib/kv')>(io, 'kv', 'kv.js')\n const json = await framework<typeof import('../lib/kv/json')>(io, 'kv/json', 'kv/json.js')\n const onDb = await framework<typeof import('../lib/kv/db')>(io, 'kv/db', 'kv/db.js')\n const sqlite = await framework<typeof import('../lib/db/sqlite')>(io, 'db/sqlite', 'db/sqlite.js')\n const migrations =\n options.migrations !== undefined\n ? resolve(io.cwd, options.migrations)\n : existsSync(resolve(io.cwd, 'migrations'))\n ? resolve(io.cwd, 'migrations')\n : undefined\n const source = await json.openKv({ path: from })\n try {\n const database = await sqlite.openDb({\n path: to,\n ...(migrations === undefined ? {} : { migrations }),\n })\n try {\n const target = onDb.kvOnDb(\n database.db,\n options.table === undefined ? {} : { table: options.table },\n )\n const result = await kvCore.copyKv(source.kv, target, { overwrite: options.overwrite })\n io.stdout.write(\n `imported ${result.copied} ${result.copied === 1 ? 'key' : 'keys'} from ${options.from} into ${options.to}` +\n `${options.table === undefined ? '' : ` (table ${options.table})`}` +\n `${result.overwritten > 0 ? `, ${result.overwritten} overwritten` : ''}\\n`,\n )\n return 0\n } finally {\n await database.close()\n }\n } finally {\n await source.close()\n }\n}\n","import { createRequire } from 'node:module'\nimport { add, listPlugins, parseAddOptions } from './add'\nimport { buildClient, parseBuildClientOptions } from './build-client'\nimport { checkDeps, parseCheckDepsOptions } from './check-deps'\nimport { init, parseInitOptions, UsageError } from './init'\nimport { type Io, processIo } from './io'\nimport { kvImport, parseKvOptions } from './kv-import'\n\n/** The version of this package. */\nexport const VERSION: string = (\n createRequire(import.meta.url)('../package.json') as { version: string }\n).version\n\nexport const HELP = `acrylblends ${VERSION} - scaffold Cordis apps\n\nUsage:\n acrylblends init [dir] [options] Create a Cordis app in an empty or new directory\n acrylblends add <plugin...> [--dir d] Copy library plugins (and what they require) into an app;\n a folder path brings an external plugin into its extensions/\n acrylblends plugins List the plugin library\n acrylblends build-client [root] [out] Compile the client modules once for production (src/client to dist/client)\n 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]\n acrylblends check-deps [dir] Fail when app code imports a database driver or opener outside the file named in\n package.json \"acryl\": { \"opener\": \"src/db.ts\" } (a port is imported, a provider is not)\n\nOptions for init:\n --template <name> blank or agent (asked when not given); --agent means --template agent,\n --no-agent means --template blank\n --name <name> the project name (default: the directory name)\n --desktop also add the desktop shell: npm run desktop opens a window on the app\n --install run npm install after copying\n -y, --yes ask nothing; use defaults (the blank template, no install)\n\n -h, --help show this help\n -v, --version show the version\n`\n\n/**\n * Run the CLI and return the exit code: 0 done, 1 failed or refused, 2 a usage error. Never calls `process.exit`.\n */\nexport async function runCli(argv: readonly string[], io: Io = processIo()): Promise<number> {\n const [command, ...args] = argv\n try {\n if (command === undefined || command === 'help' || command === '-h' || command === '--help') {\n io.stdout.write(HELP)\n return command === undefined ? 2 : 0\n }\n if (command === 'version' || command === '-v' || command === '--version') {\n io.stdout.write(`${VERSION}\\n`)\n return 0\n }\n if (command === 'init') return await init(parseInitOptions(args), io)\n if (command === 'add') return await add(parseAddOptions(args), io)\n if (command === 'build-client') return await buildClient(parseBuildClientOptions(args), io)\n if (command === 'kv') return await kvImport(parseKv(args), io)\n if (command === 'check-deps') return checkDeps(parseCheckDeps(args), io)\n if (command === 'plugins') return listPlugins(io)\n throw new UsageError(`unknown command \"${command}\"`)\n } catch (error) {\n if (error instanceof UsageError) {\n io.stderr.write(`error: ${error.message}\\n\\n${HELP}`)\n return 2\n }\n io.stderr.write(`error: ${error instanceof Error ? error.message : String(error)}\\n`)\n return 1\n }\n}\n\nfunction parseCheckDeps(args: readonly string[]) {\n try {\n return parseCheckDepsOptions(args)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n}\n\nfunction parseKv(args: readonly string[]) {\n return parseKvOptions(args)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["parse","parse"],"sources":["../src/library.ts","../src/project.ts","../src/templates.ts","../src/init.ts","../src/add.ts","../src/build-client.ts","../src/check-deps.ts","../src/spawn.ts","../src/io.ts","../src/kv-import.ts","../src/cli.ts"],"sourcesContent":["import { cpSync, existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join, sep } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n/** One entry of the plugin library: `plugins/<name>/plugin.json` in this repository. */\nexport interface LibraryPlugin {\n readonly name: string\n readonly description: string\n /** Library plugins this one imports; they are copied with it. */\n readonly requires: readonly string[]\n /** npm packages an app needs at run time for this plugin. */\n readonly dependencies: Readonly<Record<string, string>>\n /** npm packages an app needs only to develop or run this plugin's tooling (for example `electron` for the desktop shell). */\n readonly devDependencies?: Readonly<Record<string, string>>\n /** `package.json` scripts the plugin adds; a script the app already has is kept. */\n readonly scripts?: Readonly<Record<string, string>>\n /** Lines the app's `.gitignore` needs for this plugin (added when missing, if the app has a `.gitignore`). */\n readonly gitignore?: readonly string[]\n /** How to mount it, shown after `acrylblends add`. */\n readonly mount?: string\n /**\n * Paths inside the plugin folder that are not copied into an app (for example a React hook an app without React cannot type-check, or colocated test\n * files). A pattern is an exact path or a glob: `*` matches within one folder name, `**` across folders, `?` one character; `**` followed by a slash also matches no folder.\n */\n readonly omit?: readonly string[]\n}\n\n/** A glob as a regular expression: `*` within a name, `**` across folders, `**` plus a slash may match no folder, `?` one character. */\nfunction globToRegExp(glob: string): RegExp {\n let source = ''\n for (let i = 0; i < glob.length; i += 1) {\n const c = glob[i] as string\n if (c === '*' && glob[i + 1] === '*') {\n if (glob[i + 2] === '/') {\n source += '(?:.*/)?'\n i += 2\n } else {\n source += '.*'\n i += 1\n }\n } else if (c === '*') source += '[^/]*'\n else if (c === '?') source += '[^/]'\n else source += c.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n }\n return new RegExp(`^${source}$`)\n}\n\n/** Whether a path inside a plugin folder (relative, any separator) is named by one of its `omit` patterns. */\nexport function isOmitted(inside: string, patterns: readonly string[] | undefined): boolean {\n if (!patterns || patterns.length === 0) return false\n const path = inside.split(sep).join('/')\n return patterns.some((pattern) =>\n /[*?]/.test(pattern) ? globToRegExp(pattern).test(path) : pattern === path,\n )\n}\n\n/** Files of a library plugin that stay in the repository: its manifest and its tests. */\nconst LIBRARY_ONLY = new Set(['plugin.json', 'test', 'tests'])\n\nexport class LibraryError extends Error {\n override name = 'LibraryError'\n}\n\n/** The library folder (`plugins/` at the repository root): walk up from this module, in source or in the build. */\nexport function libraryRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (;;) {\n const candidate = join(dir, 'plugins')\n if (existsSync(join(candidate, 'instance', 'plugin.json'))) return candidate\n const parent = dirname(dir)\n if (parent === dir)\n throw new LibraryError('acrylblends cannot find its plugin library (plugins/)')\n dir = parent\n }\n}\n\n/** Every plugin in the library, by name. */\nexport function readLibrary(root: string = libraryRoot()): Map<string, LibraryPlugin> {\n const plugins = new Map<string, LibraryPlugin>()\n for (const entry of readdirSync(root, { withFileTypes: true })) {\n const manifest = join(root, entry.name, 'plugin.json')\n if (!entry.isDirectory() || !existsSync(manifest)) continue\n const plugin = JSON.parse(readFileSync(manifest, 'utf8')) as LibraryPlugin\n if (plugin.name !== entry.name)\n throw new LibraryError(`plugins/${entry.name}/plugin.json names \"${plugin.name}\"`)\n plugins.set(plugin.name, {\n ...plugin,\n requires: plugin.requires ?? [],\n dependencies: plugin.dependencies ?? {},\n })\n }\n return plugins\n}\n\n/** The plugins to copy for `names`: each one after everything it requires, no duplicates. */\nexport function closure(\n names: readonly string[],\n library: Map<string, LibraryPlugin>,\n): LibraryPlugin[] {\n const ordered: LibraryPlugin[] = []\n const visiting = new Set<string>()\n const visit = (name: string, trail: string[]) => {\n if (ordered.some((plugin) => plugin.name === name)) return\n const plugin = library.get(name)\n if (!plugin)\n throw new LibraryError(\n `no plugin \"${name}\" in the library${trail.length ? ` (required by ${trail.at(-1)})` : ''}`,\n )\n if (visiting.has(name))\n throw new LibraryError(\n `plugins require each other in a cycle: ${[...trail, name].join(' -> ')}`,\n )\n visiting.add(name)\n for (const required of plugin.requires) visit(required, [...trail, name])\n visiting.delete(name)\n ordered.push(plugin)\n }\n for (const name of names) visit(name, [])\n return ordered\n}\n\n/** The one folder every plugin of an app lives in, library copies and the ones an agent writes. */\nexport const PLUGINS_DIR = 'plugins'\n/** Where apps made before 0.7.8 kept library copies; still understood for one release. */\nexport const LEGACY_PLUGINS_DIR = join('src', 'plugins')\n/** The list of library copies in the plugins folder; the extensions plugin installs every other folder there. */\nexport const LIBRARY_LIST = 'library.json'\n\n/** The plugins folder of an app: `plugins/`, or `src/plugins/` for an app that still has the old layout (and no `plugins/`). */\nexport function pluginsFolder(appDir: string): { dir: string; legacy: boolean } {\n const legacy =\n existsSync(join(appDir, LEGACY_PLUGINS_DIR)) && !existsSync(join(appDir, PLUGINS_DIR))\n return { dir: join(appDir, legacy ? LEGACY_PLUGINS_DIR : PLUGINS_DIR), legacy }\n}\n\n/** The warning for an app with the old layout. */\nexport const LEGACY_WARNING =\n 'warning: this app keeps its plugins in src/plugins/ (and extensions/); plugins now live in one folder, plugins/. Move them with \"git mv src/plugins plugins\", fix the paths in tsconfig.json, package.json and src/, and see docs/framework/agents/index.md. The old layout is read for this release only.'\n\nfunction recordLibraryCopy(folder: string, name: string): void {\n const file = join(folder, LIBRARY_LIST)\n const names: string[] = existsSync(file)\n ? (JSON.parse(readFileSync(file, 'utf8')) as { plugins: string[] }).plugins\n : []\n if (!names.includes(name)) names.push(name)\n writeFileSync(file, `${JSON.stringify({ plugins: names.sort() }, null, 2)}\\n`)\n}\n\n/** Copy one library plugin's source into `<app>/plugins/<name>/`. Throws when that folder already exists. */\nexport function copyPlugin(\n plugin: LibraryPlugin,\n appDir: string,\n root: string = libraryRoot(),\n): string {\n const folder = pluginsFolder(appDir)\n const target = join(folder.dir, plugin.name)\n if (existsSync(target))\n throw new LibraryError(\n `${relativeName(appDir, target)} already exists; remove it first to copy a fresh one`,\n )\n const source = join(root, plugin.name)\n cpSync(source, target, {\n recursive: true,\n filter: (path) => {\n const inside = path.slice(source.length + 1)\n return (\n !(dirname(path) === source && LIBRARY_ONLY.has(inside)) && !isOmitted(inside, plugin.omit)\n )\n },\n })\n if (!folder.legacy) recordLibraryCopy(folder.dir, plugin.name)\n return target\n}\n\nconst relativeName = (appDir: string, path: string) =>\n path\n .slice(appDir.length + 1)\n .split(sep)\n .join('/')\n","import {\n cpSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n statSync,\n writeFileSync,\n} from 'node:fs'\nimport { join } from 'node:path'\n\n/** Thrown when the target directory exists and is not empty. Nothing has been written. */\nexport class TargetNotEmptyError extends Error {\n constructor(readonly target: string) {\n super(\n `refusing to write into \"${target}\": the directory is not empty. Choose an empty or new directory.`,\n )\n this.name = 'TargetNotEmptyError'\n }\n}\n\n/** A project name npm accepts: lower case letters, digits and dashes, starting with a letter. */\nexport function isValidName(name: string): boolean {\n return /^[a-z][a-z0-9-]*$/.test(name) && name.length <= 214\n}\n\n/** A valid default project name derived from a directory path. */\nexport function nameFrom(directory: string): string {\n const base = directory.split(/[\\\\/]/).filter(Boolean).pop() ?? ''\n const cleaned = base\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, '-')\n .replace(/^[^a-z]+/, '')\n .replace(/-+$/, '')\n return cleaned || 'my-app'\n}\n\n/** Refuse a target that exists and is not an empty directory. A missing target is fine: it is created. */\nexport function assertTargetFree(target: string): void {\n if (!existsSync(target)) return\n if (!statSync(target).isDirectory() || readdirSync(target).length > 0)\n throw new TargetNotEmptyError(target)\n}\n\n/**\n * Copy a template into `target`, replacing `__APP_NAME__` with `name`. Checks the target before writing anything.\n *\n * @returns the copied files, relative to `target`\n */\nexport function copyTemplate(source: string, target: string, name: string): string[] {\n assertTargetFree(target)\n mkdirSync(target, { recursive: true })\n const written: string[] = []\n const walk = (from: string, to: string, prefix: string) => {\n for (const entry of readdirSync(from, { withFileTypes: true })) {\n if (\n entry.name === 'node_modules' ||\n (prefix === '' &&\n (entry.name === TEMPLATE_MANIFEST ||\n entry.name === 'upstream.json' ||\n entry.name === 'AGENTS.md'))\n )\n continue\n const sourcePath = join(from, entry.name)\n // npm and git treat a file named .gitignore specially, so templates keep it as `gitignore` (and `dockerignore`).\n const targetName =\n prefix === '' && (entry.name === 'gitignore' || entry.name === 'dockerignore')\n ? `.${entry.name}`\n : entry.name\n const targetPath = join(to, targetName)\n if (entry.isDirectory()) {\n mkdirSync(targetPath, { recursive: true })\n walk(sourcePath, targetPath, `${prefix}${entry.name}/`)\n continue\n }\n const text = readFileSync(sourcePath, 'utf8')\n if (text.includes('__APP_NAME__'))\n writeFileSync(targetPath, text.replaceAll('__APP_NAME__', name))\n else cpSync(sourcePath, targetPath)\n written.push(`${prefix}${targetName}`)\n }\n }\n walk(source, target, '')\n return written\n}\n\n/** A template's own manifest, `template.json`: the library plugins it is built from. Never copied (nor is `upstream.json`, the record of what was synced from ACRYL, nor the template's `AGENTS.md`, which `writeAgentFiles` joins to the shared core). */\nexport const TEMPLATE_MANIFEST = 'template.json'\n\n/** The address a template's desktop window opens on, when it is not the root (`template.json`: `desktopPath`). */\nexport function readTemplateDesktopPath(templateDir: string): string | undefined {\n const file = join(templateDir, TEMPLATE_MANIFEST)\n if (!existsSync(file)) return undefined\n return (JSON.parse(readFileSync(file, 'utf8')) as { desktopPath?: string }).desktopPath\n}\n\n/** Record where the desktop window opens in package.json (`acrylblends.desktopPath`), read by `plugins/desktop/main.ts`. */\nexport function writeDesktopPath(appDir: string, path: string): void {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as {\n acrylblends?: Record<string, unknown>\n }\n manifest.acrylblends = { ...manifest.acrylblends, desktopPath: path }\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n}\n\nexport function readTemplatePlugins(templateDir: string): string[] {\n const file = join(templateDir, TEMPLATE_MANIFEST)\n if (!existsSync(file)) return []\n const { plugins } = JSON.parse(readFileSync(file, 'utf8')) as { plugins?: string[] }\n return plugins ?? []\n}\n\n/** Add run-time dependencies to an app's package.json, keeping what is there and sorting the keys. Returns the names added. */\nexport function mergeDependencies(\n appDir: string,\n dependencies: Readonly<Record<string, string>>,\n): string[] {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as {\n dependencies?: Record<string, string>\n }\n const current = manifest.dependencies ?? {}\n const added = Object.keys(dependencies).filter((name) => !(name in current))\n const merged = { ...dependencies, ...current }\n manifest.dependencies = Object.fromEntries(\n Object.entries(merged).sort(([a], [b]) => a.localeCompare(b)),\n )\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n return added\n}\n\n/** Declare a file as an opener in package.json (`\"acryl\": { \"opener\": [...] }`): the one place that may import a database provider. */\nexport function mergeOpener(appDir: string, opener: string): void {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as {\n acryl?: { opener?: string | string[] }\n }\n const current = manifest.acryl?.opener\n const list = typeof current === 'string' ? [current] : (current ?? [])\n if (list.includes(opener)) return\n manifest.acryl = { ...manifest.acryl, opener: [...list, opener] }\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n}\n\n/**\n * 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\n * and scripts added.\n */\nexport function mergeTooling(\n appDir: string,\n tooling: {\n devDependencies?: Readonly<Record<string, string>> | undefined\n scripts?: Readonly<Record<string, string>> | undefined\n },\n): { devDependencies: string[]; scripts: string[] } {\n const file = join(appDir, 'package.json')\n const manifest = JSON.parse(readFileSync(file, 'utf8')) as {\n devDependencies?: Record<string, string>\n scripts?: Record<string, string>\n }\n const dev = manifest.devDependencies ?? {}\n const scripts = manifest.scripts ?? {}\n const addedDev = Object.keys(tooling.devDependencies ?? {}).filter((name) => !(name in dev))\n const addedScripts = Object.keys(tooling.scripts ?? {}).filter((name) => !(name in scripts))\n manifest.devDependencies = Object.fromEntries(\n Object.entries({ ...tooling.devDependencies, ...dev }).sort(([a], [b]) => a.localeCompare(b)),\n )\n manifest.scripts = { ...tooling.scripts, ...scripts }\n writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\\n`)\n return { devDependencies: addedDev, scripts: addedScripts }\n}\n\n/** Add the lines a plugin needs to the app's `.gitignore` when it has one and they are missing. Returns the lines added. */\nexport function appendGitignore(appDir: string, lines: readonly string[] | undefined): string[] {\n const file = join(appDir, '.gitignore')\n if (!lines || lines.length === 0 || !existsSync(file)) return []\n const current = readFileSync(file, 'utf8')\n const present = new Set(current.split('\\n').map((line) => line.trim()))\n const missing = lines.filter((line) => !present.has(line))\n if (missing.length > 0)\n writeFileSync(\n file,\n `${current}${current.endsWith('\\n') || current === '' ? '' : '\\n'}${missing.join('\\n')}\\n`,\n )\n return missing\n}\n\n/** The generated app's guide for a coding agent: the shared core, then the template's own part. `CLAUDE.md` is only an import of it. */\nexport function writeAgentFiles(\n sharedDir: string,\n templateDir: string,\n target: string,\n name: string,\n): string[] {\n const core = readFileSync(join(sharedDir, 'AGENTS.core.md'), 'utf8')\n const own = existsSync(join(templateDir, 'AGENTS.md'))\n ? readFileSync(join(templateDir, 'AGENTS.md'), 'utf8')\n : ''\n const defaults = readFileSync(join(sharedDir, 'DEFAULTS.md'), 'utf8')\n const text = `${core}${defaults}${own}`.replaceAll('__APP_NAME__', name)\n // AGENTS.md and CLAUDE.md are the same text; the one skill is written where each kind of agent looks for skills.\n writeFileSync(join(target, 'AGENTS.md'), text)\n writeFileSync(join(target, 'CLAUDE.md'), text)\n const skill = readFileSync(join(sharedDir, 'SKILL.md'), 'utf8').replaceAll('__APP_NAME__', name)\n const written = ['AGENTS.md', 'CLAUDE.md']\n for (const folder of ['.claude/skills/app-guide', '.agents/skills/app-guide']) {\n mkdirSync(join(target, folder), { recursive: true })\n writeFileSync(join(target, folder, 'SKILL.md'), skill)\n written.push(`${folder}/SKILL.md`)\n }\n return written\n}\n\n/**\n * Copy the framework's docs (a generated copy of ACRYL's `docs/framework`, already checked against its digest) into `<app>/docs/framework/`,\n * so a coding agent working in the app reads the API from the folder it is in. The generated-copy notes and the digest record stay behind.\n *\n * @returns the copied files, relative to `target`\n */\nexport function copyFrameworkDocs(source: string, target: string): string[] {\n const written: string[] = []\n const walk = (from: string, to: string, prefix: string) => {\n for (const entry of readdirSync(from, { withFileTypes: true })) {\n if (entry.name === 'upstream.json' || entry.name === 'UPSTREAM.md') continue\n const sourcePath = join(from, entry.name)\n const targetPath = join(to, entry.name)\n if (entry.isDirectory()) {\n mkdirSync(targetPath, { recursive: true })\n walk(sourcePath, targetPath, `${prefix}${entry.name}/`)\n continue\n }\n cpSync(sourcePath, targetPath)\n written.push(`docs/framework/${prefix}${entry.name}`)\n }\n }\n mkdirSync(join(target, 'docs', 'framework'), { recursive: true })\n walk(source, join(target, 'docs', 'framework'), '')\n return written\n}\n","import { existsSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n/** The templates: a blank Cordis app on the router, a todo list built only on the router and state, and the blank app with the offline agent. */\nexport const TEMPLATES = ['blank', 'todo', 'agent'] as const\nexport type Template = (typeof TEMPLATES)[number]\n\nexport function isTemplate(value: string): value is Template {\n return (TEMPLATES as readonly string[]).includes(value)\n}\n\n/**\n * The `templates/` folder of this package. The module sits at different depths in source (`src/`) and in the build\n * (`dist/`), so walk up until a `templates` folder holds the `blank` template.\n */\nexport function templatesRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (;;) {\n const candidate = join(dir, 'templates')\n if (existsSync(join(candidate, 'blank'))) return candidate\n const parent = dirname(dir)\n if (parent === dir) throw new Error('acrylblends cannot find its templates folder')\n dir = parent\n }\n}\n\n/** Absolute path of a template folder. */\nexport function templateDir(template: Template): string {\n return join(templatesRoot(), template)\n}\n\n/** The text every generated app's `AGENTS.md` starts with (`shared/`, beside `templates/`). */\nexport function sharedDir(): string {\n return join(dirname(templatesRoot()), 'shared')\n}\n\n/**\n * The framework's docs, as ACRYL wrote them: `docs/framework` of the repository, or the copy `prepack` puts inside the package. The module sits at\n * different depths in source and in the build, so walk up until a `docs/framework` folder holds the README.\n */\nexport function frameworkDocsRoot(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (;;) {\n const candidate = join(dir, 'docs', 'framework')\n if (existsSync(join(candidate, 'README.md'))) return candidate\n const parent = dirname(dir)\n if (parent === dir)\n throw new Error('acrylblends cannot find the framework docs (docs/framework)')\n dir = parent\n }\n}\n","import { resolve } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport type { Io } from './io'\nimport { closure, copyPlugin, readLibrary } from './library'\nimport {\n copyFrameworkDocs,\n copyTemplate,\n isValidName,\n mergeDependencies,\n mergeTooling,\n nameFrom,\n readTemplateDesktopPath,\n readTemplatePlugins,\n TargetNotEmptyError,\n writeAgentFiles,\n writeDesktopPath,\n} from './project'\nimport {\n frameworkDocsRoot,\n isTemplate,\n sharedDir,\n TEMPLATES,\n type Template,\n templateDir,\n} from './templates'\n\n/** A command line mistake. The CLI prints it with the help text and exits 2. */\nexport class UsageError extends Error {\n override name = 'UsageError'\n}\n\n/** What `init` was asked for. Anything left undefined is asked for on a terminal, or defaulted. */\nexport interface InitOptions {\n directory: string | undefined\n name: string | undefined\n template: Template | undefined\n install: boolean | undefined\n /** Also add the desktop shell (`add desktop`): an Electron window on the app's own server. */\n desktop: boolean\n yes: boolean\n}\n\n/** Parse `init [dir] [--template t] [--agent|--no-agent] [--name n] [--install] [-y]`. `--agent` means `--template agent`, `--no-agent` means `--template blank`. */\nexport function parseInitOptions(argv: readonly string[]): InitOptions {\n let parsed: ReturnType<typeof parse>\n try {\n parsed = parse(argv)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n const { values, positionals } = parsed\n if (positionals.length > 1) throw new UsageError('init takes at most one directory')\n if (values.agent && values['no-agent'])\n throw new UsageError('use --agent or --no-agent, not both')\n if (values.template !== undefined && !isTemplate(values.template))\n throw new UsageError(`unknown template \"${values.template}\" (${TEMPLATES.join(', ')})`)\n const alias: Template | undefined = values.agent\n ? 'agent'\n : values['no-agent']\n ? 'blank'\n : undefined\n if (values.template !== undefined && alias !== undefined && values.template !== alias)\n throw new UsageError(\n `--template ${values.template} contradicts --${alias === 'agent' ? 'agent' : 'no-agent'}`,\n )\n return {\n directory: positionals[0],\n name: values.name,\n template: (values.template as Template | undefined) ?? alias,\n install: values.install,\n desktop: values.desktop === true,\n yes: values.yes === true,\n }\n}\n\nconst parse = (argv: readonly string[]) =>\n parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: {\n name: { type: 'string' },\n template: { type: 'string' },\n agent: { type: 'boolean' },\n 'no-agent': { type: 'boolean' },\n install: { type: 'boolean' },\n desktop: { type: 'boolean' },\n yes: { type: 'boolean', short: 'y' },\n },\n })\n\nconst yes = (answer: string) => ['y', 'yes'].includes(answer.trim().toLowerCase())\n\nasync function askTemplate(ask: (question: string) => Promise<string>): Promise<Template> {\n const answer = (await ask(`Template (${TEMPLATES.join(', ')}) [blank]: `)).trim().toLowerCase()\n if (answer === '') return 'blank'\n if (isTemplate(answer)) return answer\n throw new UsageError(`unknown template \"${answer}\" (${TEMPLATES.join(', ')})`)\n}\n\n/** Scaffold a Cordis app. Returns the exit code: 0 done, 1 refused or failed. Never overwrites anything. */\nexport async function init(options: InitOptions, io: Io): Promise<number> {\n const relative = options.directory ?? '.'\n const directory = resolve(io.cwd, relative)\n const ask = options.yes ? undefined : io.ask\n\n let name = options.name ?? nameFrom(directory)\n if (options.name === undefined && ask)\n name = (await ask(`Project name (${name}): `)).trim() || name\n if (!isValidName(name)) {\n throw new UsageError(\n `\"${name}\" is not a valid project name (lower case letters, digits and dashes, starting with a letter)`,\n )\n }\n const template = options.template ?? (ask ? await askTemplate(ask) : 'blank')\n const install =\n options.install ?? (ask ? yes(await ask('Install dependencies with npm now? (y/N): ')) : false)\n\n let files: string[]\n try {\n files = copyTemplate(templateDir(template), directory, name)\n files.push(...writeAgentFiles(sharedDir(), templateDir(template), directory, name))\n files.push(...copyFrameworkDocs(frameworkDocsRoot(), directory))\n } catch (error) {\n if (!(error instanceof TargetNotEmptyError)) throw error\n io.stderr.write(`error: ${error.message}\\n`)\n return 1\n }\n const plugins = closure(\n [...readTemplatePlugins(templateDir(template)), ...(options.desktop ? ['desktop'] : [])],\n readLibrary(),\n )\n for (const plugin of plugins) {\n copyPlugin(plugin, directory)\n mergeDependencies(directory, plugin.dependencies)\n mergeTooling(directory, plugin)\n }\n const desktopPath = options.desktop ? readTemplateDesktopPath(templateDir(template)) : undefined\n if (desktopPath !== undefined) writeDesktopPath(directory, desktopPath)\n io.stdout.write(\n `created ${name} (${template}) in ${directory}: ${files.length} files, plugins ${plugins.map((plugin) => plugin.name).join(', ')}\\n`,\n )\n\n if (install && (await io.install(directory)) !== 0) {\n io.stderr.write('error: npm install failed; run it yourself in the new directory\\n')\n return 1\n }\n const steps = [\n relative === '.' ? '' : ` cd ${relative}\\n`,\n install ? '' : ' npm install\\n',\n ' npm run dev\\n npm test\\n',\n ]\n io.stdout.write(`\\nNext:\\n${steps.join('')}`)\n return 0\n}\n","import { cpSync, existsSync, readFileSync, statSync } from 'node:fs'\nimport { basename, dirname, join, relative, resolve, sep } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport { UsageError } from './init'\nimport type { Io } from './io'\nimport {\n closure,\n copyPlugin,\n LEGACY_WARNING,\n type LibraryPlugin,\n PLUGINS_DIR,\n pluginsFolder,\n readLibrary,\n} from './library'\nimport { appendGitignore, mergeDependencies, mergeOpener, mergeTooling } from './project'\n\nexport interface AddOptions {\n readonly names: readonly string[]\n readonly directory: string | undefined\n}\n\n/** Parse `add <plugin...> [--dir <app>]`. */\nexport function parseAddOptions(argv: readonly string[]): AddOptions {\n let parsed: ReturnType<typeof parse>\n try {\n parsed = parse(argv)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n if (parsed.positionals.length === 0)\n throw new UsageError('add needs at least one plugin name (see \"acrylblends plugins\")')\n return { names: parsed.positionals, directory: parsed.values.dir }\n}\n\nconst parse = (argv: readonly string[]) =>\n parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: { dir: { type: 'string' } },\n })\n\nconst present = (appDir: string, name: string) => existsSync(join(pluginsFolder(appDir).dir, name))\n\n/**\n * Copy library plugins into an existing app, with the plugins they require, and add their npm packages to its package.json.\n * Refuses a plugin the app already has. Returns the exit code.\n */\nexport async function add(options: AddOptions, io: Io): Promise<number> {\n const appDir = resolve(io.cwd, options.directory ?? '.')\n if (!existsSync(join(appDir, 'package.json')) || !existsSync(join(appDir, 'src'))) {\n io.stderr.write(\n `error: ${appDir} is not an app (no package.json and src/); run \"acrylblends init\" first\\n`,\n )\n return 1\n }\n if (pluginsFolder(appDir).legacy) io.stderr.write(`${LEGACY_WARNING}\\n`)\n // A path is an external plugin folder; anything else is a library plugin name.\n const external = options.names.filter((name) => /[\\\\/]/.test(name) || name.startsWith('.'))\n for (const folder of external) {\n const code = addExternal(resolve(io.cwd, folder), appDir, io)\n if (code !== 0) return code\n }\n // The desktop shell has two forms: the Blend app (it needs the `instance` plugin) and a plain Acryl app (`export default app`); `desktop` means the one that fits.\n const plain = !existsSync(join(pluginsFolder(appDir).dir, 'instance'))\n if (plain && options.names.includes('db')) {\n io.stderr.write(\n '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',\n )\n return 1\n }\n if (plain && options.names.includes('kv')) {\n io.stderr.write(\n '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',\n )\n return 1\n }\n const names = options.names\n .filter((name) => !external.includes(name))\n // `db` is the SQLite provider of the db port (it brings the port); a plain app opens its database with `acrylblends/db/sqlite` instead.\n .map((name) => (name === 'db' ? 'acryl-db-sqlite' : name))\n // `kv` is the JSON-file provider of the kv port (it brings the port); `acryl-kv-db` is the one over db.\n .map((name) => (name === 'kv' ? 'acryl-kv-json' : name))\n .map((name) => (name === 'desktop' && plain ? 'desktop-web' : name))\n if (names.length === 0) return 0\n const already = names.filter((name) => present(appDir, name))\n if (already.length > 0) {\n io.stderr.write(\n `error: the app already has ${already\n .map(\n (name) =>\n `${relative(appDir, join(pluginsFolder(appDir).dir, name))\n .split(sep)\n .join('/')}`,\n )\n .join(', ')}\\n`,\n )\n return 1\n }\n const toCopy: LibraryPlugin[] = closure(names, readLibrary()).filter(\n (plugin) => !present(appDir, plugin.name),\n )\n const addedDependencies: string[] = []\n for (const plugin of toCopy) {\n copyPlugin(plugin, appDir)\n addedDependencies.push(...mergeDependencies(appDir, plugin.dependencies))\n const tooling = mergeTooling(appDir, plugin)\n appendGitignore(appDir, plugin.gitignore)\n // The provider is mounted from src/app.ts, which is therefore the one file that may import it (`acrylblends check-deps`).\n if (plugin.name === 'acryl-db-sqlite') mergeOpener(appDir, 'src/app.ts')\n addedDependencies.push(...tooling.devDependencies)\n if (tooling.scripts.length > 0)\n io.stdout.write(\n `new scripts: ${tooling.scripts.map((name) => `npm run ${name}`).join(', ')}\\n`,\n )\n }\n io.stdout.write(`added ${toCopy.map((plugin) => plugin.name).join(', ')} to ${appDir}\\n`)\n if (addedDependencies.length > 0)\n io.stdout.write(`new dependencies: ${addedDependencies.join(', ')} (run npm install)\\n`)\n for (const plugin of toCopy.filter((plugin) => names.includes(plugin.name))) {\n io.stdout.write(\n `\\n${plugin.name}: ${plugin.mount ?? `import it from '../plugins/${plugin.name}/index.ts' and mount it in src/app.ts`}\\n`,\n )\n }\n return 0\n}\n\nconst PLUGIN_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/\n\n/**\n * Bring an external plugin folder (a package made with the builder's package_plugin, or any folder with an index.ts that exports a\n * Cordis plugin) into the app's `plugins/`, where the extensions plugin installs it like any other: checked, staged, started,\n * verified. Its runtime dependencies are not installed here; they are reported so the user adds them deliberately.\n */\nfunction addExternal(folder: string, appDir: string, io: Io): number {\n if (!existsSync(folder) || !statSync(folder).isDirectory()) {\n io.stderr.write(`error: ${folder} is not a folder\\n`)\n return 1\n }\n if (!['index.ts', 'index.js', 'index.mjs'].some((entry) => existsSync(join(folder, entry)))) {\n io.stderr.write(`error: ${folder} has no index.ts that exports a Cordis plugin\\n`)\n return 1\n }\n const manifestFile = join(folder, 'package.json')\n const manifest = existsSync(manifestFile)\n ? (JSON.parse(readFileSync(manifestFile, 'utf8')) as {\n name?: string\n dependencies?: Record<string, string>\n })\n : {}\n const name = (manifest.name ?? basename(folder)).replace(/^@[^/]+\\//, '')\n if (!PLUGIN_NAME.test(name)) {\n io.stderr.write(\n `error: \"${name}\" is not a plugin name (lowercase letters, digits and dashes)\\n`,\n )\n return 1\n }\n const target = join(\n pluginsFolder(appDir).legacy ? join(appDir, 'extensions') : join(appDir, PLUGINS_DIR),\n name,\n )\n if (existsSync(target)) {\n io.stderr.write(`error: the app already has ${relative(appDir, target).split(sep).join('/')}\\n`)\n return 1\n }\n cpSync(folder, target, {\n recursive: true,\n filter: (path) => !/[\\\\/](node_modules|\\.git)([\\\\/]|$)/.test(path.slice(folder.length)),\n })\n io.stdout.write(\n `added external plugin ${name} to ${dirname(target)}; it installs at the next start\\n`,\n )\n const dependencies = Object.keys(manifest.dependencies ?? {})\n if (dependencies.length > 0)\n io.stdout.write(\n `it uses ${dependencies.join(', ')}: add them to the app with npm install first\\n`,\n )\n return 0\n}\n\n/** `acrylblends plugins`: the library, one line per plugin. */\nexport function listPlugins(io: Io): number {\n for (const plugin of readLibrary().values()) {\n const requires = plugin.requires.length > 0 ? ` (requires ${plugin.requires.join(', ')})` : ''\n io.stdout.write(`${plugin.name.padEnd(12)} ${plugin.description}${requires}\\n`)\n }\n return 0\n}\n","import { existsSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { relative, resolve } from 'node:path'\nimport { UsageError } from './init'\nimport type { Io } from './io'\n\n/** Parse `build-client [root] [outDir]`. */\nexport function parseBuildClientOptions(argv: readonly string[]): { root: string; outDir: string } {\n const args = argv.filter((a) => !a.startsWith('-'))\n if (args.length !== argv.length)\n throw new UsageError('build-client takes [root] [outDir] and no options')\n if (args.length > 2)\n throw new UsageError('build-client takes at most two arguments: [root] [outDir]')\n return { root: args[0] ?? 'src/client', outDir: args[1] ?? 'dist/client' }\n}\n\n/**\n * Compile the app's client modules once (`buildClientModules` of the framework), for a production install that has no compiler at run time.\n * The framework is the one installed in the app (so the compiled files match the server that serves them), else this package's own.\n */\nexport async function buildClient(\n options: { root: string; outDir: string },\n io: Io,\n): Promise<number> {\n const root = resolve(io.cwd, options.root)\n if (!existsSync(root)) {\n io.stderr.write(\n `error: ${root} does not exist; the client modules live in src/client by default\\n`,\n )\n return 1\n }\n const outDir = resolve(io.cwd, options.outDir)\n const here = createRequire(resolve(io.cwd, 'package.json'))\n let specifier = 'acrylblends/node-server'\n try {\n here.resolve(specifier)\n } catch {\n specifier = new URL('../lib/node-server.js', import.meta.url).href\n }\n const mod = (await import(\n specifier.startsWith('file:') ? specifier : here.resolve(specifier)\n )) as typeof import('../lib/node-server')\n const written = mod.buildClientModules({ root, outDir })\n for (const file of written) io.stdout.write(`${relative(io.cwd, resolve(outDir, file))}\\n`)\n io.stdout.write(`built ${written.length} files in ${relative(io.cwd, outDir) || '.'}\\n`)\n return 0\n}\n","import { existsSync, readdirSync, readFileSync } from 'node:fs'\nimport { join, relative, resolve, sep } from 'node:path'\nimport type { Io } from './io'\n\n/**\n * 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\n * 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.\n */\n\n/** A package that is a database driver. An import of it, or of anything below it, is a provider import. */\nconst DRIVERS = [\n 'node:sqlite',\n 'sqlite',\n 'sqlite3',\n 'better-sqlite3',\n '@libsql/client',\n 'libsql',\n 'pg',\n 'pg-pool',\n 'pg-native',\n 'postgres',\n '@electric-sql/pglite',\n '@neondatabase/serverless',\n '@vercel/postgres',\n 'mysql',\n 'mysql2',\n]\n/** The openers a provider ships: importing one of them is opening a database. */\nconst OPENERS = ['acrylblends/db/sqlite', 'acryl-db-sqlite']\n/** The desktop shell is where Electron lives (`acrylblends add desktop` copies it). */\nconst ELECTRON = 'electron'\nconst ELECTRON_HOMES = [\n 'plugins/desktop/',\n 'plugins/desktop-web/',\n 'src/plugins/desktop/',\n 'src/plugins/desktop-web/',\n]\n/** The provider plugins themselves may import what they wrap. */\nconst PROVIDER_HOMES = ['plugins/acryl-db-sqlite/', 'src/plugins/acryl-db-sqlite/']\n\nconst SOURCE = /\\.(?:[cm]?[jt]s|[jt]sx)$/u\nconst SKIP_DIRS = new Set(['node_modules', 'dist', 'lib', 'build', 'coverage'])\nconst ROOTS = ['src', 'test', 'tests', 'plugins', 'extensions']\n\nexport interface Violation {\n readonly file: string\n readonly line: number\n readonly specifier: string\n readonly kind: 'driver' | 'opener' | 'electron'\n}\n\nconst matches = (specifier: string, name: string): boolean =>\n specifier === name || specifier.startsWith(`${name}/`)\n\n/** What a specifier is to the rule, or undefined when it is not a provider. */\nexport function classifySpecifier(specifier: string): Violation['kind'] | undefined {\n if (DRIVERS.some((name) => matches(specifier, name))) return 'driver'\n if (OPENERS.some((name) => matches(specifier, name))) return 'opener'\n if (matches(specifier, ELECTRON)) return 'electron'\n return undefined\n}\n\n/** 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. */\nexport function blankComments(source: string): string {\n let out = ''\n let i = 0\n while (i < source.length) {\n const c = source[i] as string\n const next = source[i + 1]\n if (c === '/' && next === '/') {\n const end = source.indexOf('\\n', i)\n const stop = end === -1 ? source.length : end\n out += ' '.repeat(stop - i)\n i = stop\n } else if (c === '/' && next === '*') {\n const end = source.indexOf('*/', i + 2)\n const stop = end === -1 ? source.length : end + 2\n out += source.slice(i, stop).replace(/[^\\n]/gu, ' ')\n i = stop\n } else if (c === \"'\" || c === '\"' || c === '`') {\n let j = i + 1\n while (j < source.length && source[j] !== c) {\n if (source[j] === '\\\\') j += 1\n else if (c !== '`' && source[j] === '\\n') break\n j += 1\n }\n out += source.slice(i, Math.min(j + 1, source.length))\n i = j + 1\n } else {\n out += c\n i += 1\n }\n }\n return out\n}\n\n/** `import x from 'a'`, `export * from 'a'`, `import 'a'`, `import('a')` and `require('a')`, with the line of each. */\nconst IMPORT =\n /(?:^|[\\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\n\nexport function importsOf(source: string): { specifier: string; line: number }[] {\n const code = blankComments(source)\n const found: { specifier: string; line: number }[] = []\n for (const match of code.matchAll(IMPORT)) {\n const specifier = match[2] as string\n const at = (match.index ?? 0) + match[0].length - specifier.length - 2\n found.push({ specifier, line: code.slice(0, at).split('\\n').length })\n }\n return found\n}\n\nfunction listSources(dir: string, out: string[] = []): string[] {\n if (!existsSync(dir)) return out\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.name.startsWith('.') || SKIP_DIRS.has(entry.name)) continue\n const path = join(dir, entry.name)\n if (entry.isDirectory()) listSources(path, out)\n else if (SOURCE.test(entry.name) && !entry.name.endsWith('.d.ts')) out.push(path)\n }\n return out\n}\n\n/** The opener files the app declares: `\"acryl\": { \"opener\": \"src/db.ts\" }` (or a list) in package.json. */\nexport function openersOf(appDir: string): string[] {\n const file = join(appDir, 'package.json')\n if (!existsSync(file)) return []\n const declared = (JSON.parse(readFileSync(file, 'utf8')) as { acryl?: { opener?: unknown } })\n .acryl?.opener\n const list = typeof declared === 'string' ? [declared] : Array.isArray(declared) ? declared : []\n return list.filter((entry): entry is string => typeof entry === 'string')\n}\n\n/** Every provider import outside the declared opener (and the homes that may hold one), in file and line order. */\nexport function findViolations(appDir: string): Violation[] {\n const openers = new Set(openersOf(appDir).map((entry) => entry.split(sep).join('/')))\n const violations: Violation[] = []\n for (const root of ROOTS) {\n for (const path of listSources(join(appDir, root)).sort()) {\n const file = relative(appDir, path).split(sep).join('/')\n if (openers.has(file)) continue\n for (const { specifier, line } of importsOf(readFileSync(path, 'utf8'))) {\n const kind = classifySpecifier(specifier)\n if (kind === undefined) continue\n if (kind === 'electron' && ELECTRON_HOMES.some((home) => file.startsWith(home))) continue\n if (kind !== 'electron' && PROVIDER_HOMES.some((home) => file.startsWith(home))) continue\n violations.push({ file, line, specifier, kind })\n }\n }\n }\n return violations\n}\n\nconst GUIDE = 'docs/framework/guides/data.md#why-the-database-is-opened-in-one-place'\n\nfunction advice(violation: Violation, openers: readonly string[]): string {\n if (violation.kind === 'electron')\n return `\"${violation.specifier}\" is the desktop shell's: only plugins/desktop (added with \"acrylblends add desktop\") imports electron`\n const where =\n openers.length > 0\n ? openers.join(', ')\n : 'a file named in package.json \"acryl\": { \"opener\": \"...\" }'\n return violation.kind === 'driver'\n ? `\"${violation.specifier}\" is a database driver; take a Db (import type { Db } from \"acrylblends/db\") as an argument and let ${where} open the database`\n : `\"${violation.specifier}\" opens a database, and only ${where} may; take a Db (import type { Db } from \"acrylblends/db\") as an argument`\n}\n\n/** Parse `check-deps [dir]`. */\nexport function parseCheckDepsOptions(argv: readonly string[]): { directory: string | undefined } {\n const args = argv.filter((a) => !a.startsWith('-'))\n if (args.length !== argv.length || args.length > 1)\n throw new Error('check-deps takes at most one argument: the app folder')\n return { directory: args[0] }\n}\n\n/** Report each provider import outside the opener, as `file:line: why`. Returns the exit code: 1 when there is one. */\nexport function checkDeps(options: { directory: string | undefined }, io: Io): number {\n const appDir = resolve(io.cwd, options.directory ?? '.')\n const violations = findViolations(appDir)\n if (violations.length === 0) {\n io.stdout.write('check-deps: no app code imports a database provider outside the opener\\n')\n return 0\n }\n const openers = openersOf(appDir)\n for (const violation of violations)\n io.stderr.write(`${violation.file}:${violation.line}: ${advice(violation, openers)}\\n`)\n io.stderr.write(`See ${GUIDE}\\n`)\n return 1\n}\n","import { existsSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\n\nexport interface Invocation {\n file: string\n args: string[]\n}\n\n/**\n * 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).\n * Off Windows the command runs as it is. On Windows npm runs as `node npm-cli.js` (the one npm is running from, else the one beside `node`); any other\n * manager goes through `cmd.exe /d /s /c <name>.cmd`, whose arguments stay an array.\n */\nexport function packageManagerInvocation(\n command: string,\n args: readonly string[],\n platform: NodeJS.Platform = process.platform,\n env: NodeJS.ProcessEnv = process.env,\n exists: (path: string) => boolean = existsSync,\n execPath: string = process.execPath,\n): Invocation {\n if (platform !== 'win32') return { file: command, args: [...args] }\n if (command === 'npm') {\n const running = env.npm_execpath\n const cli =\n running !== undefined && /npm-cli\\.c?js$/.test(running)\n ? running\n : join(dirname(execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js')\n if (exists(cli)) return { file: execPath, args: [cli, ...args] }\n }\n return { file: env.ComSpec ?? 'cmd.exe', args: ['/d', '/s', '/c', `${command}.cmd`, ...args] }\n}\n","import { spawn } from 'node:child_process'\nimport { createInterface } from 'node:readline/promises'\nimport { packageManagerInvocation } from './spawn'\n\n/** Everything the CLI touches outside the filesystem. Tests pass their own. */\nexport interface Io {\n stdout: { write(text: string): unknown }\n stderr: { write(text: string): unknown }\n cwd: string\n /** Ask one question; `undefined` when there is no terminal to ask on. */\n ask: ((question: string) => Promise<string>) | undefined\n /** Install dependencies in a directory; resolves to the exit code. */\n install(directory: string): Promise<number>\n}\n\n/** The real terminal and `npm install`. */\nexport function processIo(): Io {\n const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY)\n return {\n stdout: process.stdout,\n stderr: process.stderr,\n cwd: process.cwd(),\n ask: interactive\n ? async (question) => {\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n try {\n return await rl.question(question)\n } finally {\n rl.close()\n }\n }\n : undefined,\n install: (directory) =>\n new Promise((done) => {\n const { file, args } = packageManagerInvocation('npm', ['install'])\n const child = spawn(file, args, { cwd: directory, stdio: 'inherit' })\n child.on('error', () => done(1))\n child.on('exit', (code) => done(code ?? 1))\n }),\n }\n}\n","import { existsSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { resolve } from 'node:path'\nimport { parseArgs } from 'node:util'\nimport { UsageError } from './init'\nimport type { Io } from './io'\n\nexport interface KvImportOptions {\n readonly from: string\n readonly to: string\n readonly overwrite: boolean\n readonly table: string | undefined\n readonly migrations: string | undefined\n}\n\n/** Parse `kv import <kv.json> <database.sqlite> [--overwrite] [--table name] [--migrations dir]`. */\nexport function parseKvOptions(argv: readonly string[]): KvImportOptions {\n let parsed: ReturnType<typeof parse>\n try {\n parsed = parse(argv)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n const [action, from, to, ...rest] = parsed.positionals\n if (action !== 'import')\n throw new UsageError('kv takes one subcommand: kv import <kv.json> <database.sqlite>')\n if (from === undefined || to === undefined || rest.length > 0)\n throw new UsageError('kv import takes two arguments: the JSON file and the SQLite file')\n return {\n from,\n to,\n overwrite: parsed.values.overwrite ?? false,\n table: parsed.values.table,\n migrations: parsed.values.migrations,\n }\n}\n\nconst parse = (argv: readonly string[]) =>\n parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: {\n overwrite: { type: 'boolean' },\n table: { type: 'string' },\n migrations: { type: 'string' },\n },\n })\n\n/** 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. */\nasync function framework<T>(io: Io, subpath: string, own: string): Promise<T> {\n const here = createRequire(resolve(io.cwd, 'package.json'))\n try {\n return (await import(here.resolve(`acrylblends/${subpath}`))) as T\n } catch {\n return (await import(new URL(`../lib/${own}`, import.meta.url).href)) as T\n }\n}\n\n/**\n * 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.\n * 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),\n * and the table is created when missing. Refuses to overwrite a key the table already has unless `--overwrite`.\n */\nexport async function kvImport(options: KvImportOptions, io: Io): Promise<number> {\n const from = resolve(io.cwd, options.from)\n const to = resolve(io.cwd, options.to)\n if (!existsSync(from)) {\n io.stderr.write(`error: ${from} does not exist\\n`)\n return 1\n }\n const kvCore = await framework<typeof import('../lib/kv')>(io, 'kv', 'kv.js')\n const json = await framework<typeof import('../lib/kv/json')>(io, 'kv/json', 'kv/json.js')\n const onDb = await framework<typeof import('../lib/kv/db')>(io, 'kv/db', 'kv/db.js')\n const sqlite = await framework<typeof import('../lib/db/sqlite')>(io, 'db/sqlite', 'db/sqlite.js')\n const migrations =\n options.migrations !== undefined\n ? resolve(io.cwd, options.migrations)\n : existsSync(resolve(io.cwd, 'migrations'))\n ? resolve(io.cwd, 'migrations')\n : undefined\n const source = await json.openKv({ path: from })\n try {\n const database = await sqlite.openDb({\n path: to,\n ...(migrations === undefined ? {} : { migrations }),\n })\n try {\n const target = onDb.kvOnDb(\n database.db,\n options.table === undefined ? {} : { table: options.table },\n )\n const result = await kvCore.copyKv(source.kv, target, { overwrite: options.overwrite })\n io.stdout.write(\n `imported ${result.copied} ${result.copied === 1 ? 'key' : 'keys'} from ${options.from} into ${options.to}` +\n `${options.table === undefined ? '' : ` (table ${options.table})`}` +\n `${result.overwritten > 0 ? `, ${result.overwritten} overwritten` : ''}\\n`,\n )\n return 0\n } finally {\n await database.close()\n }\n } finally {\n await source.close()\n }\n}\n","import { createRequire } from 'node:module'\nimport { add, listPlugins, parseAddOptions } from './add'\nimport { buildClient, parseBuildClientOptions } from './build-client'\nimport { checkDeps, parseCheckDepsOptions } from './check-deps'\nimport { init, parseInitOptions, UsageError } from './init'\nimport { type Io, processIo } from './io'\nimport { kvImport, parseKvOptions } from './kv-import'\n\n/** The version of this package. */\nexport const VERSION: string = (\n createRequire(import.meta.url)('../package.json') as { version: string }\n).version\n\nexport const HELP = `acrylblends ${VERSION} - scaffold Cordis apps\n\nUsage:\n acrylblends init [dir] [options] Create a Cordis app in an empty or new directory\n acrylblends add <plugin...> [--dir d] Copy library plugins (and what they require) into an app;\n a folder path brings an external plugin into its plugins/\n acrylblends plugins List the plugin library\n acrylblends build-client [root] [out] Compile the client modules once for production (src/client to dist/client)\n 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]\n acrylblends check-deps [dir] Fail when app code imports a database driver or opener outside the file named in\n package.json \"acryl\": { \"opener\": \"src/db.ts\" } (a port is imported, a provider is not)\n\nOptions for init:\n --template <name> blank or agent (asked when not given); --agent means --template agent,\n --no-agent means --template blank\n --name <name> the project name (default: the directory name)\n --desktop also add the desktop shell: npm run desktop opens a window on the app\n --install run npm install after copying\n -y, --yes ask nothing; use defaults (the blank template, no install)\n\n -h, --help show this help\n -v, --version show the version\n`\n\n/**\n * Run the CLI and return the exit code: 0 done, 1 failed or refused, 2 a usage error. Never calls `process.exit`.\n */\nexport async function runCli(argv: readonly string[], io: Io = processIo()): Promise<number> {\n const [command, ...args] = argv\n try {\n if (command === undefined || command === 'help' || command === '-h' || command === '--help') {\n io.stdout.write(HELP)\n return command === undefined ? 2 : 0\n }\n if (command === 'version' || command === '-v' || command === '--version') {\n io.stdout.write(`${VERSION}\\n`)\n return 0\n }\n if (command === 'init') return await init(parseInitOptions(args), io)\n if (command === 'add') return await add(parseAddOptions(args), io)\n if (command === 'build-client') return await buildClient(parseBuildClientOptions(args), io)\n if (command === 'kv') return await kvImport(parseKv(args), io)\n if (command === 'check-deps') return checkDeps(parseCheckDeps(args), io)\n if (command === 'plugins') return listPlugins(io)\n throw new UsageError(`unknown command \"${command}\"`)\n } catch (error) {\n if (error instanceof UsageError) {\n io.stderr.write(`error: ${error.message}\\n\\n${HELP}`)\n return 2\n }\n io.stderr.write(`error: ${error instanceof Error ? error.message : String(error)}\\n`)\n return 1\n }\n}\n\nfunction parseCheckDeps(args: readonly string[]) {\n try {\n return parseCheckDepsOptions(args)\n } catch (error) {\n throw new UsageError((error as Error).message)\n }\n}\n\nfunction parseKv(args: readonly string[]) {\n return parseKvOptions(args)\n}\n"],"mappings":"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"}
@@ -29,7 +29,7 @@ The starter asks which template you want (`nodejs`, `bun` (experimental), or `ac
29
29
  See [Built-in middleware](middleware/builtin/basic-auth.md) and [Middleware](concepts/middleware.md).
30
30
  - **Helpers** for cookies, streaming and server-sent events, content negotiation, HTML templates, WebSockets and testing. See [Helpers](guides/helpers.md).
31
31
  - **Typed routes and a typed client.** Route types flow from the server to `acrylblends/client`. See [RPC](guides/rpc.md).
32
- - **Data** through one small `db` interface: SQL with numbered migrations on SQLite today, the same code on Postgres later. See [Data](guides/data.md).
32
+ - **Data** through one small `db` interface: SQL with numbered migrations on SQLite; the same code runs on Postgres through an optional plugin. See [Data](guides/data.md).
33
33
  - **Validation** with a validator function or any Standard Schema library. See [Validation](guides/validation.md).
34
34
  - **A Cordis plugin around it.** A plugin mounts an app with `ctx.router.route`, the mount goes away when the plugin unloads, and a coding agent can write such plugins
35
35
  into a running app. See [Acryl in a Cordis app](cordis/acryl-in-a-cordis-app.md) and [Agents](agents/index.md).
@@ -1,3 +1,3 @@
1
1
  # Generated copy
2
2
 
3
- This folder is a copy of `docs/framework` of acryldev/acryl at commit `f8e385e4389fe2fdc6fd2a4366a7f2a7389faacb`, made by `scripts/sync-framework.mjs`. Do not edit it here: change the text in ACRYL and sync again. `pnpm run verify:framework` fails when a page differs from the recorded digest.
3
+ This folder is a copy of `docs/framework` of acryldev/acryl at commit `f132283c43337f5e1d58dfbbbc15786efb5e756c`, made by `scripts/sync-framework.mjs`. Do not edit it here: change the text in ACRYL and sync again. `pnpm run verify:framework` fails when a page differs from the recorded digest.