@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.
- package/README.md +10 -1
- package/app/commands/BuildCommand.ts +39 -0
- package/app/commands/CallCommand.ts +4 -4
- package/app/commands/CheckCommand.ts +3 -2
- package/app/commands/ExplainCommand.ts +77 -0
- package/app/commands/FreezeCommand.ts +107 -0
- package/app/commands/GrantCommand.ts +44 -0
- package/app/commands/GraphCommand.ts +1 -1
- package/app/commands/KeysCommand.ts +56 -0
- package/app/commands/MigrateCommand.ts +54 -0
- package/app/commands/NewCommand.ts +6 -6
- package/app/commands/ServeCommand.ts +85 -9
- package/app/commands/grant-material.ts +5 -0
- package/dist/bin.js +54 -8
- package/dist/bin.js.map +1 -1
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +6 -6
- package/dist/bridge.js.map +1 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +7 -7
- package/dist/runner.js.map +1 -1
- package/dist/theme.d.ts +10 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +10 -0
- package/dist/theme.js.map +1 -0
- package/dist/ui.d.ts +74 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +111 -0
- package/dist/ui.js.map +1 -0
- package/fronds/analysis/entities/Build.ts +7 -0
- package/fronds/analysis/entities/Explain.ts +8 -0
- package/fronds/analysis/entities/Freeze.ts +7 -0
- package/fronds/analysis/entities/Migrate.ts +7 -0
- package/fronds/analysis/handlers/BuildHandler.ts +60 -0
- package/fronds/analysis/handlers/CheckHandler.ts +61 -35
- package/fronds/analysis/handlers/ExplainHandler.ts +214 -0
- package/fronds/analysis/handlers/FreezeHandler.ts +172 -0
- package/fronds/analysis/handlers/MigrateHandler.ts +97 -0
- package/fronds/analysis/services/ProjectScan.ts +23 -7
- package/fronds/analysis/versions.ts +58 -0
- package/fronds/scaffold/entities/Grant.ts +6 -0
- package/fronds/scaffold/entities/Keys.ts +4 -0
- package/fronds/scaffold/entities/Serve.ts +2 -1
- package/fronds/scaffold/handlers/BuildFrondHandler.ts +11 -13
- package/fronds/scaffold/handlers/GrantHandler.ts +8 -0
- package/fronds/scaffold/handlers/KeysHandler.ts +8 -0
- package/fronds/scaffold/handlers/SyncHandler.ts +27 -24
- package/fronds/scaffold/services/ProjectWriter.ts +29 -10
- package/package.json +10 -8
- package/templates/admin/fronds/admin/handlers/UserHandler.ts +3 -5
- package/templates/admin/fronds/admin/package.json +1 -1
- package/templates/api/fronds/api/handlers/TaskHandler.ts +3 -5
- package/templates/api/fronds/api/package.json +1 -1
- package/templates/apps/nuxt/app/pages/index.vue +1 -1
- package/templates/apps/nuxt/package.json +1 -1
- package/templates/blog/app/pages/posts/index.vue +1 -1
- package/templates/blog/app/pages/posts/manage.vue +1 -1
- package/templates/blog/app/pages/posts/new.vue +1 -1
- package/templates/blog/fronds/blog/handlers/PostHandler.ts +3 -5
- package/templates/blog/fronds/blog/package.json +1 -1
- package/templates/flat/AGENTS.md +14 -0
- package/templates/flat/CLAUDE.md +27 -5
- package/templates/flat/package.json +1 -1
- package/templates/frond/AGENTS.md +14 -0
- package/templates/frond/CLAUDE.md +27 -5
- package/templates/frond/fronds/__name__/handlers/PostHandler.ts +3 -5
- package/templates/frond/fronds/__name__/package.json +1 -1
- package/templates/frond/package.json +1 -1
- package/templates/frond/serve.mjs +4 -3
- package/templates/fronds/blank/package.json +1 -1
- package/templates/fronds/blog/handlers/PostHandler.ts +2 -4
- package/templates/fronds/blog/package.json +1 -1
- package/templates/workspace/AGENTS.md +14 -0
- 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,
|
|
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 '
|
|
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
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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,
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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"}
|
package/dist/bridge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"
|
|
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 {
|
|
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.
|
|
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
|
|
16
|
-
const
|
|
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.
|
|
21
|
+
required: !nullable && Lifecycle.of(field).requiredAtCreate,
|
|
22
22
|
};
|
|
23
23
|
switch (shape?.type) {
|
|
24
24
|
case 'boolean':
|
package/dist/bridge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/runner.d.ts.map
CHANGED
|
@@ -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;
|
|
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 {
|
|
2
|
+
import { registrationKeyOf } from '@fougere/core/contract';
|
|
3
3
|
import { defineCommand, runMain } from 'citty';
|
|
4
|
-
import { ui } from '
|
|
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
|
-
|
|
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:
|
|
96
|
+
await createAppRunner(app)({ entity: registrationKeyOf(entity.name), op: 'execute' }, { params: {}, query: {}, body: input, state: {} });
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
catch (err) {
|
package/dist/runner.js.map
CHANGED
|
@@ -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,
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
package/dist/theme.d.ts
ADDED
|
@@ -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 @@
|
|
|
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
|
package/dist/ui.d.ts.map
ADDED
|
@@ -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
|
+
}
|