@fougere/cli 0.2.0-alpha.1 → 0.3.0-alpha.0

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 (74) hide show
  1. package/README.md +10 -1
  2. package/app/commands/BuildCommand.ts +39 -0
  3. package/app/commands/CallCommand.ts +4 -4
  4. package/app/commands/CheckCommand.ts +3 -2
  5. package/app/commands/ExplainCommand.ts +77 -0
  6. package/app/commands/FreezeCommand.ts +107 -0
  7. package/app/commands/GrantCommand.ts +44 -0
  8. package/app/commands/GraphCommand.ts +1 -1
  9. package/app/commands/KeysCommand.ts +56 -0
  10. package/app/commands/MigrateCommand.ts +54 -0
  11. package/app/commands/NewCommand.ts +6 -6
  12. package/app/commands/ServeCommand.ts +85 -9
  13. package/app/commands/grant-material.ts +5 -0
  14. package/dist/bin.js +54 -8
  15. package/dist/bin.js.map +1 -1
  16. package/dist/bridge.d.ts.map +1 -1
  17. package/dist/bridge.js +6 -6
  18. package/dist/bridge.js.map +1 -1
  19. package/dist/runner.d.ts.map +1 -1
  20. package/dist/runner.js +7 -7
  21. package/dist/runner.js.map +1 -1
  22. package/dist/theme.d.ts +10 -0
  23. package/dist/theme.d.ts.map +1 -0
  24. package/dist/theme.js +10 -0
  25. package/dist/theme.js.map +1 -0
  26. package/dist/ui.d.ts +74 -0
  27. package/dist/ui.d.ts.map +1 -0
  28. package/dist/ui.js +111 -0
  29. package/dist/ui.js.map +1 -0
  30. package/fronds/analysis/entities/Build.ts +7 -0
  31. package/fronds/analysis/entities/Explain.ts +8 -0
  32. package/fronds/analysis/entities/Freeze.ts +7 -0
  33. package/fronds/analysis/entities/Migrate.ts +7 -0
  34. package/fronds/analysis/handlers/BuildHandler.ts +60 -0
  35. package/fronds/analysis/handlers/CheckHandler.ts +61 -35
  36. package/fronds/analysis/handlers/ExplainHandler.ts +214 -0
  37. package/fronds/analysis/handlers/FreezeHandler.ts +172 -0
  38. package/fronds/analysis/handlers/MigrateHandler.ts +97 -0
  39. package/fronds/analysis/services/ProjectScan.ts +23 -7
  40. package/fronds/analysis/versions.ts +58 -0
  41. package/fronds/scaffold/entities/Grant.ts +6 -0
  42. package/fronds/scaffold/entities/Keys.ts +4 -0
  43. package/fronds/scaffold/entities/Serve.ts +2 -1
  44. package/fronds/scaffold/handlers/BuildFrondHandler.ts +11 -13
  45. package/fronds/scaffold/handlers/GrantHandler.ts +8 -0
  46. package/fronds/scaffold/handlers/KeysHandler.ts +8 -0
  47. package/fronds/scaffold/handlers/SyncHandler.ts +27 -24
  48. package/fronds/scaffold/services/ProjectWriter.ts +29 -10
  49. package/package.json +10 -8
  50. package/templates/admin/fronds/admin/handlers/UserHandler.ts +3 -5
  51. package/templates/admin/fronds/admin/package.json +1 -1
  52. package/templates/api/fronds/api/handlers/TaskHandler.ts +3 -5
  53. package/templates/api/fronds/api/package.json +1 -1
  54. package/templates/apps/nuxt/app/pages/index.vue +1 -1
  55. package/templates/apps/nuxt/package.json +1 -1
  56. package/templates/blog/app/pages/posts/index.vue +1 -1
  57. package/templates/blog/app/pages/posts/manage.vue +1 -1
  58. package/templates/blog/app/pages/posts/new.vue +1 -1
  59. package/templates/blog/fronds/blog/handlers/PostHandler.ts +3 -5
  60. package/templates/blog/fronds/blog/package.json +1 -1
  61. package/templates/flat/AGENTS.md +14 -0
  62. package/templates/flat/CLAUDE.md +27 -5
  63. package/templates/flat/package.json +1 -1
  64. package/templates/frond/AGENTS.md +14 -0
  65. package/templates/frond/CLAUDE.md +27 -5
  66. package/templates/frond/fronds/__name__/handlers/PostHandler.ts +3 -5
  67. package/templates/frond/fronds/__name__/package.json +1 -1
  68. package/templates/frond/package.json +1 -1
  69. package/templates/frond/serve.mjs +4 -3
  70. package/templates/fronds/blank/package.json +1 -1
  71. package/templates/fronds/blog/handlers/PostHandler.ts +2 -4
  72. package/templates/fronds/blog/package.json +1 -1
  73. package/templates/workspace/AGENTS.md +14 -0
  74. package/templates/workspace/CLAUDE.md +27 -5
package/dist/bin.js CHANGED
@@ -6,23 +6,69 @@
6
6
  * fronds/ → loaded at runtime by jiti (domain)
7
7
  * app/ → loaded at runtime by jiti (presentation)
8
8
  */
9
- import { createApp, setModuleLoader } from '@fougere/core';
9
+ import { createApp, setLogLevel, envLevel } from '@fougere/core';
10
+ import { scanProject, setModuleLoader, frondDirsOf, DEFAULT_CONVENTIONS } from '@fougere/core/node';
11
+ import { readdir, stat } from 'node:fs/promises';
12
+ import { join } from 'node:path';
10
13
  import { createContainer } from '@fougere/container';
11
- import { ui } from '@fougere/cli-ui';
14
+ import { ui } from './ui.js';
12
15
  import { run } from './runner.js';
13
16
  const { createJiti } = await import('jiti');
14
17
  const jiti = createJiti(import.meta.url, { interopDefault: true });
15
18
  setModuleLoader((filePath) => jiti.import(filePath));
16
19
  const cliRoot = new URL('..', import.meta.url).pathname;
17
20
  const container = createContainer();
18
- container.registerValue('ui', ui());
21
+ const terminal = ui();
22
+ container.registerValue('ui', terminal);
19
23
  container.registerValue('cwd', process.cwd());
20
- // The CLI is a Fougere app — silence its boot chatter unless explicitly asked.
24
+ // The CLI is a Fougere app — silence its boot chatter unless explicitly asked. The
25
+ // threshold is SET, not only announced: a static import evaluates the logger module,
26
+ // its env read included, before this line runs.
21
27
  process.env.FOUGERE_LOG_LEVEL ??= 'warn';
22
- const app = await createApp({
23
- root: cliRoot,
24
- createContainer: () => container,
25
- });
28
+ setLogLevel(envLevel() ?? 'warn');
29
+ /** The newest declaration under `fronds/`, or 0 when there is none to compare against. */
30
+ async function newestDeclaration(root) {
31
+ const frondsDir = join(root, DEFAULT_CONVENTIONS.fronds);
32
+ const names = await readdir(frondsDir, { withFileTypes: true }).catch(() => []);
33
+ const dirs = names.filter((entry) => entry.isDirectory())
34
+ .flatMap((entry) => [
35
+ join(frondsDir, entry.name),
36
+ ...frondDirsOf(DEFAULT_CONVENTIONS).map((dir) => join(frondsDir, entry.name, dir)),
37
+ ]);
38
+ const times = await Promise.all(dirs.map(async (dir) => {
39
+ const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
40
+ const stats = await Promise.all(entries
41
+ .filter((entry) => entry.isFile() && entry.name.endsWith('.ts'))
42
+ .map((entry) => stat(join(dir, entry.name)).then((s) => s.mtimeMs).catch(() => 0)));
43
+ return Math.max(0, ...stats);
44
+ }));
45
+ return Math.max(0, ...times);
46
+ }
47
+ /**
48
+ * The CLI is a Fougere app, so it reads its own written-down scan like any deployment —
49
+ * producing the description reads the project, consuming it does not.
50
+ *
51
+ * Reading its own 44 declarations through the compiler cost 617 ms on every invocation,
52
+ * for a domain fixed at publish time. Staleness is decided by mtime rather than by a flag:
53
+ * editing a frond must not need a command, and a published package has nothing newer than
54
+ * its artefact.
55
+ */
56
+ async function scanOf(root) {
57
+ const written = join(root, '.fougere/scan.generated.ts');
58
+ const writtenAt = await stat(written).then((s) => s.mtimeMs).catch(() => 0);
59
+ if (writtenAt > 0 && writtenAt >= await newestDeclaration(root)) {
60
+ return (await jiti.import(written)).scan;
61
+ }
62
+ // The only slow phase, and it announced nothing: the boot states it at `info`, which the
63
+ // threshold above lowers to `warn`. The terminal says it instead, and a pipe keeps its
64
+ // output parsable.
65
+ const spin = process.stdout.isTTY ? terminal.spinner('reading fronds') : undefined;
66
+ const scan = await scanProject(root);
67
+ spin?.stop(`${scan.fronds.length} frond(s)`);
68
+ return scan;
69
+ }
70
+ const scan = await scanOf(cliRoot);
71
+ const app = await createApp({ scan, createContainer: () => container });
26
72
  container.registerValue('app', app);
27
73
  await run(app);
28
74
  //# sourceMappingURL=bin.js.map
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AACH,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AACrC,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAElC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;AAC5C,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;AACnE,eAAe,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAqC,CAAC,CAAC;AAEzF,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;AACxD,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC;AACpC,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;AACpC,SAAS,CAAC,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;AAE9C,+EAA+E;AAC/E,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,MAAM,CAAC;AAEzC,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC;IAC1B,IAAI,EAAE,OAAO;IACb,eAAe,EAAE,GAAG,EAAE,CAAC,SAAS;CACjC,CAAC,CAAC;AAEH,SAAS,CAAC,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAEpC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC"}
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,KAAK,EAAE,OAAO,EAAU,MAAM,OAAO,CAAC;AAM7C,6DAA6D;AAC7D,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAgDpD"}
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AACA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,KAAK,EAAE,OAAO,EAAU,MAAM,OAAO,CAAC;AAM7C,6DAA6D;AAC7D,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CA+CpD"}
package/dist/bridge.js CHANGED
@@ -1,4 +1,5 @@
1
- import { anatomy, inputFields } from '@fougere/schema';
1
+ import { Lifecycle, Role } from '@fougere/schema';
2
+ import { Anatomy, inputFields } from '@fougere/schema';
2
3
  function toKebab(name) {
3
4
  return name.replace(/[A-Z]/g, (c) => '-' + c.toLowerCase());
4
5
  }
@@ -9,16 +10,15 @@ export function entityToArgs(fields) {
9
10
  // Axes-derived ingress membership; the CLI additionally skips ALL relations
10
11
  // (a ref is not a flag — supplying related rows is not a CLI gesture).
11
12
  for (const [key, field] of Object.entries(inputFields(fields))) {
12
- if (field.role?.relation)
13
+ if (Role.of(field).relation)
13
14
  continue;
14
15
  // A `default(v)` travels as the create rule `{ value }` — citty shows it.
15
- const create = field.lifecycle?.create;
16
- const defaultValue = typeof create === 'object' && 'value' in create ? create.value : undefined;
17
- const { base: shape, nullable } = anatomy(field.shape);
16
+ const defaultValue = Lifecycle.of(field).literal?.value;
17
+ const { base: shape, nullable } = Anatomy.of(field.shape);
18
18
  const kebab = toKebab(key);
19
19
  const def = {
20
20
  description: field.meta?.description,
21
- required: !nullable && field.lifecycle?.create === undefined,
21
+ required: !nullable && Lifecycle.of(field).requiredAtCreate,
22
22
  };
23
23
  switch (shape?.type) {
24
24
  case 'boolean':
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGvD,SAAS,OAAO,CAAC,IAAY;IAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,IAAI,GAAY,EAAE,CAAC;IACzB,IAAI,eAAe,GAAG,CAAC,CAAC;IAExB,4EAA4E;IAC5E,uEAAuE;IACvE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QAC/D,IAAI,KAAK,CAAC,IAAI,EAAE,QAAQ;YAAE,SAAS;QAEnC,0EAA0E;QAC1E,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,EAAE,MAAM,CAAC;QACvC,MAAM,YAAY,GAAG,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;QAChG,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAEvD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC3B,MAAM,GAAG,GAAW;YAClB,WAAW,EAAE,KAAK,CAAC,IAAI,EAAE,WAAW;YACpC,QAAQ,EAAE,CAAC,QAAQ,IAAI,KAAK,CAAC,SAAS,EAAE,MAAM,KAAK,SAAS;SAC7D,CAAC;QAEF,QAAQ,KAAK,EAAE,IAAI,EAAE,CAAC;YACpB,KAAK,SAAS;gBACX,GAA+B,CAAC,IAAI,GAAG,SAAS,CAAC;gBAClD,IAAI,YAAY,KAAK,SAAS;oBAAE,GAAG,CAAC,OAAO,GAAG,YAAuB,CAAC;gBACtE,MAAM;YACR,KAAK,QAAQ,CAAC;YACd,KAAK,SAAS,CAAC;YACf,KAAK,QAAQ;gBACX,oEAAoE;gBACpE,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;oBAC3D,GAA+B,CAAC,IAAI,GAAG,QAAQ,CAAC;gBACnD,CAAC;qBAAM,IAAI,eAAe,KAAK,CAAC,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;oBACpE,mDAAmD;oBAClD,GAA+B,CAAC,IAAI,GAAG,YAAY,CAAC;oBACrD,eAAe,EAAE,CAAC;gBACpB,CAAC;qBAAM,CAAC;oBACL,GAA+B,CAAC,IAAI,GAAG,QAAQ,CAAC;gBACnD,CAAC;gBACD,IAAI,YAAY,KAAK,SAAS;oBAAE,GAAG,CAAC,OAAO,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;gBACnE,MAAM;YACR;gBACG,GAA+B,CAAC,IAAI,GAAG,QAAQ,CAAC;QACrD,CAAC;QAED,IAAI,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC;IAC1C,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAQlD,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGvD,SAAS,OAAO,CAAC,IAAY;IAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,IAAI,GAAY,EAAE,CAAC;IACzB,IAAI,eAAe,GAAG,CAAC,CAAC;IAExB,4EAA4E;IAC5E,uEAAuE;IACvE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QAC/D,IAAI,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,QAAQ;YAAE,SAAS;QAEtC,0EAA0E;QAC1E,MAAM,YAAY,GAAG,SAAS,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC;QACxD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAE1D,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC3B,MAAM,GAAG,GAAW;YAClB,WAAW,EAAE,KAAK,CAAC,IAAI,EAAE,WAAW;YACpC,QAAQ,EAAE,CAAC,QAAQ,IAAI,SAAS,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,gBAAgB;SAC5D,CAAC;QAEF,QAAQ,KAAK,EAAE,IAAI,EAAE,CAAC;YACpB,KAAK,SAAS;gBACX,GAA+B,CAAC,IAAI,GAAG,SAAS,CAAC;gBAClD,IAAI,YAAY,KAAK,SAAS;oBAAE,GAAG,CAAC,OAAO,GAAG,YAAuB,CAAC;gBACtE,MAAM;YACR,KAAK,QAAQ,CAAC;YACd,KAAK,SAAS,CAAC;YACf,KAAK,QAAQ;gBACX,oEAAoE;gBACpE,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;oBAC3D,GAA+B,CAAC,IAAI,GAAG,QAAQ,CAAC;gBACnD,CAAC;qBAAM,IAAI,eAAe,KAAK,CAAC,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;oBACpE,mDAAmD;oBAClD,GAA+B,CAAC,IAAI,GAAG,YAAY,CAAC;oBACrD,eAAe,EAAE,CAAC;gBACpB,CAAC;qBAAM,CAAC;oBACL,GAA+B,CAAC,IAAI,GAAG,QAAQ,CAAC;gBACnD,CAAC;gBACD,IAAI,YAAY,KAAK,SAAS;oBAAE,GAAG,CAAC,OAAO,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;gBACnE,MAAM;YACR;gBACG,GAA+B,CAAC,IAAI,GAAG,QAAQ,CAAC;QACrD,CAAC;QAED,IAAI,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC;IAC1C,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AA2CzC,wBAAsB,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAsFjD"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAuCzC,wBAAsB,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAyFjD"}
package/dist/runner.js CHANGED
@@ -1,16 +1,13 @@
1
1
  import { createAppRunner } from '@fougere/core';
2
- import { toRegistrationName } from '@fougere/core/contract';
2
+ import { registrationKeyOf } from '@fougere/core/contract';
3
3
  import { defineCommand, runMain } from 'citty';
4
- import { ui } from '@fougere/cli-ui';
4
+ import { ui } from './ui.js';
5
5
  import { entityToArgs } from './bridge.js';
6
6
  import { readdir } from 'node:fs/promises';
7
7
  import { join } from 'node:path';
8
8
  function toKebab(name) {
9
9
  return name.replace(/[A-Z]/g, (c) => '-' + c.toLowerCase()).replace(/^-/, '');
10
10
  }
11
- function capitalize(name) {
12
- return name.charAt(0).toUpperCase() + name.slice(1);
13
- }
14
11
  function toCamel(kebab) {
15
12
  return kebab.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
16
13
  }
@@ -79,7 +76,10 @@ export async function run(app) {
79
76
  },
80
77
  args,
81
78
  run: async ({ args: parsed }) => {
82
- if (cmdName !== 'completion')
79
+ // JSON is a protocol: a branded intro before `{` makes it unparsable. The
80
+ // explain command owns its machine output and therefore gets a clean stdout.
81
+ const machineOutput = cmdName === 'explain' && parsed.json === true;
82
+ if (cmdName !== 'completion' && !machineOutput)
83
83
  terminal.intro();
84
84
  // citty adds `_` (raw positionals) and `--` (passthrough); strip them
85
85
  // so only the entity's own fields reach the handler.
@@ -93,7 +93,7 @@ export async function run(app) {
93
93
  }
94
94
  else {
95
95
  // Ride the call contract — the same envelope every consumer uses.
96
- await createAppRunner(app)({ entity: toRegistrationName(entity.name), op: 'execute' }, { params: {}, query: {}, body: input, state: {} });
96
+ await createAppRunner(app)({ entity: registrationKeyOf(entity.name), op: 'execute' }, { params: {}, query: {}, body: input, state: {} });
97
97
  }
98
98
  }
99
99
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,SAAS,OAAO,CAAC,IAAY;IAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAChF,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,OAAO,CAAC,KAAa;IAC5B,OAAO,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED,8CAA8C;AAC9C,KAAK,UAAU,eAAe,CAC5B,OAAe,EACf,MAA0D;IAE1D,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;IACtB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAE1E,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAAE,SAAS;QACjF,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;QAC/E,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5C,IAAI,GAAG,CAAC,OAAO,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YACrD,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,GAAQ;IAChC,MAAM,QAAQ,GAAG,EAAE,EAAE,CAAC;IACtB,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;IAExD,yCAAyC;IACzC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAqC,CAAC;IACvF,MAAM,WAAW,GAAG,MAAM,eAAe,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAE3D,MAAM,WAAW,GAAqD,EAAE,CAAC;IAEzE,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;QAC/B,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAEtE,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACpC,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACjD,IAAI,CAAC,YAAY;gBAAE,SAAS;YAE5B,MAAM,WAAW,GAAG,GAAG,MAAM,CAAC,IAAI,SAAS,CAAC;YAC5C,IAAI,OAAiC,CAAC;YACtC,IAAI,CAAC;gBACH,OAAO,GAAG,GAAG,CAAC,OAAO,CAA2B,WAAW,CAAC,CAAC;YAC/D,CAAC;YAAC,MAAM,CAAC;gBAAC,SAAS;YAAC,CAAC;YAErB,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,UAAU;gBAAE,SAAS;YAEpD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACrC,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,SAAS,EAAE,CAAC;YAC9C,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YAElC,gDAAgD;YAChD,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAE5C,gFAAgF;YAChF,IAAI,UAAU,EAAE,CAAC;gBACf,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;oBACtC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG;wBAAG,GAA+B,CAAC,QAAQ,GAAG,KAAK,CAAC;gBACxF,CAAC;YACH,CAAC;YAED,WAAW,CAAC,OAAO,CAAC,GAAG,aAAa,CAAC;gBACnC,IAAI,EAAE;oBACJ,IAAI,EAAE,OAAO;oBACb,0EAA0E;oBAC1E,yEAAyE;oBACzE,2EAA2E;oBAC3E,2EAA2E;oBAC3E,WAAW,EAAE,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,WAAW;iBACjE;gBACD,IAAI;gBACJ,GAAG,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;oBAC9B,IAAI,OAAO,KAAK,YAAY;wBAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;oBAE/C,sEAAsE;oBACtE,qDAAqD;oBACrD,MAAM,KAAK,GAAG,EAAE,GAAI,MAAkC,EAAE,CAAC;oBACzD,OAAO,KAAK,CAAC,CAAC,CAAC;oBACf,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC;oBAEnB,IAAI,CAAC;wBACH,IAAI,UAAU,EAAE,CAAC;4BACf,MAAM,GAAG,GAAG,IAAK,UAAgG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;4BACjI,MAAM,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;wBACvB,CAAC;6BAAM,CAAC;4BACN,kEAAkE;4BAClE,MAAM,eAAe,CAAC,GAAG,CAAC,CACxB,EAAE,MAAM,EAAE,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,EAC1D,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAClD,CAAC;wBACJ,CAAC;oBACH,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,QAAQ,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;wBACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;oBAClB,CAAC;gBACH,CAAC;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,aAAa,CAAC;QACzB,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE;QACrD,WAAW;KACZ,CAAC,CAAC;IAEH,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC"}
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export interface ThemeColors {
2
+ brand: (text: string) => string;
3
+ success: (text: string) => string;
4
+ error: (text: string) => string;
5
+ warn: (text: string) => string;
6
+ muted: (text: string) => string;
7
+ bold: (text: string) => string;
8
+ }
9
+ export declare const defaultTheme: ThemeColors;
10
+ //# sourceMappingURL=theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAChC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAClC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAChC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/B,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAChC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAChC;AAED,eAAO,MAAM,YAAY,EAAE,WAO1B,CAAC"}
package/dist/theme.js ADDED
@@ -0,0 +1,10 @@
1
+ import pc from 'picocolors';
2
+ export const defaultTheme = {
3
+ brand: pc.green,
4
+ success: pc.green,
5
+ error: pc.red,
6
+ warn: pc.yellow,
7
+ muted: pc.dim,
8
+ bold: pc.bold,
9
+ };
10
+ //# sourceMappingURL=theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.js","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAW5B,MAAM,CAAC,MAAM,YAAY,GAAgB;IACvC,KAAK,EAAE,EAAE,CAAC,KAAK;IACf,OAAO,EAAE,EAAE,CAAC,KAAK;IACjB,KAAK,EAAE,EAAE,CAAC,GAAG;IACb,IAAI,EAAE,EAAE,CAAC,MAAM;IACf,KAAK,EAAE,EAAE,CAAC,GAAG;IACb,IAAI,EAAE,EAAE,CAAC,IAAI;CACd,CAAC"}
package/dist/ui.d.ts ADDED
@@ -0,0 +1,74 @@
1
+ import { type ThemeColors } from './theme.js';
2
+ export interface UiTheme {
3
+ colors?: Partial<ThemeColors>;
4
+ }
5
+ export declare function ui(options?: UiTheme): {
6
+ /** Start a new CLI session with a branded header. */
7
+ intro(title?: string): void;
8
+ /** End the session with a message. */
9
+ outro(message: string): void;
10
+ /** Cancel and exit. */
11
+ cancel(message?: string): never;
12
+ /** Text input. */
13
+ text(opts: {
14
+ message: string;
15
+ placeholder?: string;
16
+ defaultValue?: string;
17
+ validate?: (value: string) => string | undefined;
18
+ }): Promise<string>;
19
+ /** Yes/no confirmation. */
20
+ confirm(opts: {
21
+ message: string;
22
+ initialValue?: boolean;
23
+ }): Promise<boolean>;
24
+ /** Select one from a list. */
25
+ select(opts: {
26
+ message: string;
27
+ options: {
28
+ value: string;
29
+ label?: string;
30
+ hint?: string;
31
+ }[];
32
+ initialValue?: string;
33
+ }): Promise<string>;
34
+ /** Multi-select from a list. */
35
+ multiselect(opts: {
36
+ message: string;
37
+ options: {
38
+ value: string;
39
+ label?: string;
40
+ hint?: string;
41
+ }[];
42
+ required?: boolean;
43
+ }): Promise<string[]>;
44
+ /** Start a spinner. Returns stop/update functions. */
45
+ spinner(message?: string): {
46
+ update: (msg: string) => void;
47
+ stop: (msg?: string) => void;
48
+ };
49
+ /** Informational message. */
50
+ info(message: string): void;
51
+ /** Success message. */
52
+ success(message: string): void;
53
+ /** Warning message. */
54
+ warn(message: string): void;
55
+ /** Error message. */
56
+ error(message: string): void;
57
+ /** Step indicator. */
58
+ step(message: string): void;
59
+ /** Note box — multiline content in a box. */
60
+ note(message: string, title?: string): void;
61
+ colors: {
62
+ brand: (text: string) => string;
63
+ success: (text: string) => string;
64
+ error: (text: string) => string;
65
+ warn: (text: string) => string;
66
+ muted: (text: string) => string;
67
+ bold: (text: string) => string;
68
+ };
69
+ pc: import("picocolors/types.js").Colors & {
70
+ createColors: (enabled?: boolean) => import("picocolors/types.js").Colors;
71
+ };
72
+ consola: import("consola").ConsolaInstance;
73
+ };
74
+ //# sourceMappingURL=ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAcA,OAAO,EAAgB,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,WAAW,OAAO;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;CAC/B;AAED,wBAAgB,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO;IAMhC,qDAAqD;;IAKrD,sCAAsC;mBACvB,MAAM;IAIrB,uBAAuB;;IAQvB,kBAAkB;eACD;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;KAAE;IAMnI,2BAA2B;kBACP;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE;IAM/D,8BAA8B;iBACX;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB;IAMD,gCAAgC;sBACR;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAC5D,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB;IAQD,sDAAsD;sBACpC,MAAM;QAIpB,MAAM,QAAQ,MAAM;QACpB,IAAI,SAAS,MAAM;;IAMvB,6BAA6B;kBACf,MAAM;IAIpB,uBAAuB;qBACN,MAAM;IAIvB,uBAAuB;kBACT,MAAM;IAIpB,qBAAqB;mBACN,MAAM;IAIrB,sBAAsB;kBACR,MAAM;IAIpB,6CAA6C;kBAC/B,MAAM,UAAU,MAAM;;;;;;;;;;;;;EAUvC"}
package/dist/ui.js ADDED
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Fougere CLI UI — beautiful terminal interface.
3
+ *
4
+ * Wraps @clack/prompts + picocolors + consola into a cohesive API.
5
+ *
6
+ * This was `@fougere/cli-ui`, a published package with exactly one consumer —
7
+ * the CLI it is named after. A second name in the registry that nobody would
8
+ * ever install on purpose is a name, not a boundary. Same dependency profile,
9
+ * so it folds in as a module; a subpath export is one line the day something
10
+ * outside the CLI wants it.
11
+ */
12
+ import * as clack from '@clack/prompts';
13
+ import pc from 'picocolors';
14
+ import { consola } from 'consola';
15
+ import { defaultTheme } from './theme.js';
16
+ export function ui(options) {
17
+ const c = { ...defaultTheme, ...options?.colors };
18
+ return {
19
+ // ── Lifecycle ─────────────────────────────────
20
+ /** Start a new CLI session with a branded header. */
21
+ intro(title = 'Fougere') {
22
+ clack.intro(c.brand(title));
23
+ },
24
+ /** End the session with a message. */
25
+ outro(message) {
26
+ clack.outro(c.success(message));
27
+ },
28
+ /** Cancel and exit. */
29
+ cancel(message = 'Cancelled.') {
30
+ clack.cancel(c.muted(message));
31
+ process.exit(0);
32
+ },
33
+ // ── Prompts ───────────────────────────────────
34
+ /** Text input. */
35
+ async text(opts) {
36
+ const result = await clack.text(opts);
37
+ if (clack.isCancel(result)) {
38
+ this.cancel();
39
+ return '';
40
+ }
41
+ return result;
42
+ },
43
+ /** Yes/no confirmation. */
44
+ async confirm(opts) {
45
+ const result = await clack.confirm(opts);
46
+ if (clack.isCancel(result)) {
47
+ this.cancel();
48
+ return false;
49
+ }
50
+ return result;
51
+ },
52
+ /** Select one from a list. */
53
+ async select(opts) {
54
+ const result = await clack.select(opts);
55
+ if (clack.isCancel(result)) {
56
+ this.cancel();
57
+ return '';
58
+ }
59
+ return result;
60
+ },
61
+ /** Multi-select from a list. */
62
+ async multiselect(opts) {
63
+ const result = await clack.multiselect(opts);
64
+ if (clack.isCancel(result)) {
65
+ this.cancel();
66
+ return [];
67
+ }
68
+ return result;
69
+ },
70
+ // ── Spinner ───────────────────────────────────
71
+ /** Start a spinner. Returns stop/update functions. */
72
+ spinner(message) {
73
+ const s = clack.spinner();
74
+ s.start(message);
75
+ return {
76
+ update: (msg) => s.message(msg),
77
+ stop: (msg) => s.stop(msg),
78
+ };
79
+ },
80
+ // ── Output ────────────────────────────────────
81
+ /** Informational message. */
82
+ info(message) {
83
+ clack.log.info(message);
84
+ },
85
+ /** Success message. */
86
+ success(message) {
87
+ clack.log.success(c.success(message));
88
+ },
89
+ /** Warning message. */
90
+ warn(message) {
91
+ clack.log.warn(c.warn(message));
92
+ },
93
+ /** Error message. */
94
+ error(message) {
95
+ clack.log.error(c.error(message));
96
+ },
97
+ /** Step indicator. */
98
+ step(message) {
99
+ clack.log.step(message);
100
+ },
101
+ /** Note box — multiline content in a box. */
102
+ note(message, title) {
103
+ clack.note(message, title);
104
+ },
105
+ // ── Raw colors/consola ────────────────────────
106
+ colors: c,
107
+ pc,
108
+ consola,
109
+ };
110
+ }
111
+ //# sourceMappingURL=ui.js.map
package/dist/ui.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { entity, text, optional } from "@fougere/schema";
2
+
3
+ /** `fougere build` — write down what a scan found, so a deployment reads no disk. */
4
+ export default class Build extends entity({
5
+ root: optional(text({ description: "Project to read. Default: the current directory" })),
6
+ out: optional(text({ description: "Where to write it. Default: .fougere/scan.generated.ts" })),
7
+ }) {}
@@ -0,0 +1,8 @@
1
+ import { bool, entity, optional, text } from '@fougere/schema';
2
+
3
+ /** `fougere explain <Operation>` — print the operation contract Fougere will serve. */
4
+ export default class Explain extends entity({
5
+ operation: text({ min: 1, description: 'Operation to inspect (e.g. Post.publish)' }),
6
+ root: optional(text({ description: 'Project to read. Default: the current directory' })),
7
+ json: bool({ default: false, description: 'Print stable machine-readable JSON' }),
8
+ }) {}
@@ -0,0 +1,7 @@
1
+ import { entity, text, optional } from "@fougere/schema";
2
+
3
+ /** `fougere freeze <version>` — record today's shapes under a name, for good. */
4
+ export default class Freeze extends entity({
5
+ version: text({ min: 1, description: "Name to record these shapes under (e.g. v2)" }),
6
+ root: optional(text({ description: "Project to read. Default: the current directory" })),
7
+ }) {}
@@ -0,0 +1,7 @@
1
+ import { entity, bool, optional, text } from "@fougere/schema";
2
+
3
+ /** `fougere migrate` — realise the frozen steps the database has not caught up with. */
4
+ export default class Migrate extends entity({
5
+ apply: bool({ default: false, description: "Run it. Without this the plan is printed and nothing moves" }),
6
+ root: optional(text({ description: "Project to read. Default: the current directory" })),
7
+ }) {}
@@ -0,0 +1,60 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { dirname, relative, resolve } from 'node:path';
3
+ import { emitScan } from '@fougere/core/node';
4
+ import ProjectScan from '../services/ProjectScan.js';
5
+ import type Build from '../entities/Build.js';
6
+
7
+ /**
8
+ * Where the module lands when nobody says otherwise.
9
+ *
10
+ * `.fougere/` because the artefact is REGENERABLE — which is exactly the test `freeze`
11
+ * failed: a recorded version is a past nobody can recompute, so it lives in the tree.
12
+ * This is the same scan the next build performs again, so it is gitignored.
13
+ */
14
+ export const DEFAULT_OUT = '.fougere/scan.generated.ts';
15
+
16
+ export interface BuildReport {
17
+ /** Absolute, so a caller can print it or read it back. */
18
+ out: string;
19
+ /** Relative to the project root — what a human recognizes. */
20
+ path: string;
21
+ fronds: string[];
22
+ entities: number;
23
+ handlers: number;
24
+ /**
25
+ * What the scan could not settle, carried rather than swallowed: the module holds the
26
+ * same diagnostics, so a boot from it warns exactly as a boot from a disk would.
27
+ */
28
+ diagnostics: string[];
29
+ }
30
+
31
+ /**
32
+ * Writing the scan down — the half of the gradient a runtime without a disk needs.
33
+ *
34
+ * Producing the description reads the project; consuming it does not. `createApp` takes
35
+ * `scan:` and never looks for a filesystem, so this command is what stands between a
36
+ * project and a Worker: it runs where `node:fs` exists, once, at build time.
37
+ */
38
+ export default class BuildHandler {
39
+ constructor(private projectScan: ProjectScan) {}
40
+
41
+ /** Write down what a scan found, so a deployment reads no disk. */
42
+ async execute(input: Build): Promise<BuildReport> {
43
+ const scan = await this.projectScan.at(input.root ?? undefined);
44
+ const out = resolve(scan.root, input.out || DEFAULT_OUT);
45
+
46
+ // The emitter writes every import relative to where the module will SIT, so the
47
+ // directory has to be settled before the source is produced, not after.
48
+ await mkdir(dirname(out), { recursive: true });
49
+ await writeFile(out, emitScan(scan, { outFile: out }));
50
+
51
+ return {
52
+ out,
53
+ path: relative(scan.root, out),
54
+ fronds: scan.fronds.map((frond) => frond.name),
55
+ entities: scan.fronds.reduce((total, frond) => total + frond.entities.length, 0),
56
+ handlers: scan.fronds.reduce((total, frond) => total + frond.handlers.length, 0),
57
+ diagnostics: scan.diagnostics.map((d) => `${d.severity} ${d.code}: ${d.message}`),
58
+ };
59
+ }
60
+ }