@fougere/nuxt 0.2.0-alpha.2 → 0.4.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 CHANGED
@@ -14,4 +14,4 @@ pnpm add @fougere/nuxt
14
14
 
15
15
  Part of [Fougere](https://github.com/chok/fougere) — one schema, a gradient from
16
16
  monolith to distributed, the same user code.
17
- Reference documentation: [the site](https://chok.github.io/fougere/) (en/fr).
17
+ Reference documentation: [the site](https://fougere.dev/) (en/fr).
package/dist/module.d.ts CHANGED
@@ -2,7 +2,6 @@ import type { SeedEntry, FougereConfig } from '@fougere/core';
2
2
  export interface FougereModuleOptions {
3
3
  /** Override fougere.config.ts values from nuxt.config. Optional. */
4
4
  db?: FougereConfig['db'];
5
- frondsDir?: string;
6
5
  /**
7
6
  * Where `fronds/` lives, relative to the app's rootDir. Default: the app
8
7
  * itself. Set to `../..` for an app under `apps/*` in a workspace whose
@@ -12,5 +11,7 @@ export interface FougereModuleOptions {
12
11
  }
13
12
  declare const module: import("@nuxt/schema").NuxtModule<FougereModuleOptions, FougereModuleOptions, false>;
14
13
  export default module;
15
- export declare function generateBootPlugin(config: FougereConfig, seeds: SeedEntry[], fougereAppPath: string): string;
14
+ export declare function generateBootPlugin(config: FougereConfig, seeds: SeedEntry[], fougereAppPath: string,
15
+ /** The module `emitScan` wrote. Absent only in the tests that predate it. */
16
+ scanPath?: string): string;
16
17
  //# sourceMappingURL=module.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAK9D,MAAM,WAAW,oBAAoB;IACnC,oEAAoE;IACpE,EAAE,CAAC,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAGD,QAAA,MAAM,MAAM,sFAoLV,CAAC;eAEY,MAAM;AAMrB,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,SAAS,EAAE,EAClB,cAAc,EAAE,MAAM,GACrB,MAAM,CAmER"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAK9D,MAAM,WAAW,oBAAoB;IACnC,oEAAoE;IACpE,EAAE,CAAC,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IACzB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAUD,QAAA,MAAM,MAAM,sFAgOV,CAAC;eAEY,MAAM;AAiDrB,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,SAAS,EAAE,EAClB,cAAc,EAAE,MAAM;AACtB,6EAA6E;AAC7E,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,CAsGR"}
package/dist/module.js CHANGED
@@ -3,20 +3,23 @@
3
3
  * primitives (useQuery/useCommand, useFormFor, useCurrentUser) and the
4
4
  * server surface (call envelope, session, REST bridge, auth).
5
5
  */
6
- import { defineNuxtModule, addServerHandler, addServerImportsDir, addServerPlugin, addPlugin, addTemplate, addImports, createResolver, } from '@nuxt/kit';
7
- import { scanProject, frondAliases, FROND_DIRS, setModuleLoader, loadCascadedConfig, orderSeeds } from '@fougere/core';
6
+ import { defineNuxtModule, addServerHandler, addServerImportsDir, addServerPlugin, addPlugin, addTemplate, addImports, createResolver, useLogger, } from '@nuxt/kit';
7
+ import { orderSeeds } from '@fougere/core';
8
+ import { scanProject, emitScan, frondAliases, frondPackage, watchPathsOf, resolveConventions, setModuleLoader, loadCascadedConfig, } from '@fougere/core/node';
8
9
  import { declaresStorage } from '@fougere/defaults';
9
10
  import { createJiti } from 'jiti';
10
- import { resolve } from 'node:path';
11
- import { existsSync, readFileSync } from 'node:fs';
11
+ import { resolve, relative } from 'node:path';
12
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
13
+ /**
14
+ * A frond directory as the pattern Nuxt tests for a restart: it matches the changed
15
+ * file against the path relative to the layer's app dir, so that is the base here.
16
+ */
17
+ const restartsOnFrom = (srcDir) => (dir) => new RegExp(`^${relative(srcDir, dir).replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\\\/]`);
12
18
  const module = defineNuxtModule({
13
19
  meta: {
14
20
  name: '@fougere/nuxt',
15
21
  configKey: 'fougere',
16
22
  },
17
- defaults: {
18
- frondsDir: 'fronds',
19
- },
20
23
  async setup(options, nuxt) {
21
24
  const { resolve: resolveModule } = createResolver(import.meta.url);
22
25
  const runtimeResolve = (...path) => resolveModule('../src/runtime', ...path);
@@ -43,34 +46,46 @@ const module = defineNuxtModule({
43
46
  // invoke reads the current request (state) through nitro's async context
44
47
  nitroConfig.experimental = { ...nitroConfig.experimental, asyncContext: true };
45
48
  });
46
- // ── 0. Setup TS-aware module loader before reading any user config ──
49
+ // ── 0. A TS-aware loader, installed twice on purpose ──
47
50
  // The Vite alias below covers `.vue` pages; this covers the SCAN, which loads a
48
- // frond's own sources — so `@frond/user/entities/User.js` inside a handler resolves
51
+ // frond's own sources — so `@fronds/user/entities/User.js` inside a handler resolves
49
52
  // for the same reason it does in a page.
50
- const jiti = createJiti(import.meta.url, { interopDefault: true, alias: await frondAliases(scanRoot) });
51
- setModuleLoader((filePath) => jiti.import(filePath));
53
+ //
54
+ // The FIRST install carries no aliases, because the config names the scope they are
55
+ // built from: reading it must not need them. Safe because nothing in
56
+ // `fougere.config.ts` may import `@fronds/*` — the one import that would need the
57
+ // name to read the file that declares it.
58
+ const installLoader = (alias) => {
59
+ const jiti = createJiti(import.meta.url, { interopDefault: true, ...(alias ? { alias } : {}) });
60
+ setModuleLoader((filePath) => jiti.import(filePath));
61
+ };
62
+ installLoader();
52
63
  // ── 0b. Load fougere.config.ts along the workspace→app cascade (scanRoot is
53
64
  // the workspace when the app declares `root`); module options override. ──
54
65
  const fileConfig = await loadCascadedConfig(scanRoot, rootDir);
55
66
  const optionsOverride = Object.fromEntries(Object.entries(options).filter(([, v]) => v !== undefined));
56
67
  const config = { db: 'sqlite', ...fileConfig, ...optionsOverride };
68
+ const conventions = resolveConventions(config.conventions);
69
+ // Now the names are known, so the loader can resolve them — and the scan below runs
70
+ // with them installed, which is what lets it read a frond that names its neighbour.
71
+ installLoader(await frondAliases(scanRoot, conventions));
57
72
  // ── 1. Scan fronds (filtered by FOUGERE_FRONDS env var) ──
58
73
  const frondsFilter = process.env.FOUGERE_FRONDS?.split(',').map((s) => s.trim()).filter(Boolean);
59
- const { fronds } = await scanProject(scanRoot, frondsFilter);
60
- // ── 1b. Register @frond/* aliases for all fronds, and watch them ──
74
+ const scan = await scanProject(scanRoot, frondsFilter, conventions);
75
+ const { fronds } = scan;
76
+ // ── 1b. Register @fronds/* aliases for all fronds, and watch them ──
61
77
  // The scanned fronds ARE the watch list — nothing to declare. Without this, a
62
78
  // frond under `apps/../..` sits outside rootDir, so Nuxt never restarts: the scan,
63
79
  // the additive migration (once per boot) and the seeds all keep the previous shape,
64
80
  // and a field you just added is simply absent with no error anywhere.
65
- // The root frond IS the scan root, so watching its path would match every write in
66
- // the project — `.nuxt/`, `node_modules/`, the build output. Its convention
67
- // directories are the frond, and they are what changes when the domain changes.
68
81
  for (const frond of fronds) {
69
- nuxt.options.alias[`@frond/${frond.name}`] = frond.source.path;
70
- const watched = frond.source.path === scanRoot
71
- ? FROND_DIRS.map((dir) => resolve(scanRoot, dir))
72
- : [frond.source.path];
73
- nuxt.options.watch.push(...watched);
82
+ nuxt.options.alias[frond.source.package] = frond.source.path;
83
+ const watched = watchPathsOf(frond, scanRoot, conventions);
84
+ // Nuxt reads this list TWICE and a directory serves only the first read: the
85
+ // watcher adds string entries, while the restart test is `pattern === path`
86
+ // against the changed FILE — which a directory never equals. So each dir goes
87
+ // in twice, as itself and as the pattern that matches what lives under it.
88
+ nuxt.options.watch.push(...watched, ...watched.map(restartsOnFrom(nuxt.options.srcDir)));
74
89
  }
75
90
  // ── 1b-bis. Keep entity names through minification ──────────────────────
76
91
  //
@@ -86,7 +101,22 @@ const module = defineNuxtModule({
86
101
  // The list is exact rather than guessed: the scan just ran, so these are the
87
102
  // classes themselves, read before any bundler touched them. `@fougere/vite`
88
103
  // does the same for the hosts that have no module to do it for them.
89
- const entityNames = fronds.flatMap((frond) => frond.entities.map((e) => e.entityClass.name));
104
+ // What this app can DESIGNATE, never what it hosts. A CONSUMER hosts nothing and
105
+ // designates everything through `remotes:` — and the guard below used to read
106
+ // `fronds`, so the app most in need of the reservation got none: measured on a Nuxt
107
+ // worker consuming a remote frond, `Product` minified to `f` and the call left as
108
+ // `f.list`, answered `No declared remote hosts 'f'`. The error names the mangled
109
+ // letter, which is the only reason it was findable at all.
110
+ const designated = [
111
+ ...fronds.flatMap((frond) => frond.entities.map((e) => e.entityClass.name)),
112
+ ...(await syncedEntityNames(rootDir, conventions)),
113
+ ];
114
+ const entityNames = [...new Set(designated)];
115
+ if (Object.keys(config.remotes ?? {}).length > 0 && entityNames.length === 0) {
116
+ useLogger('fougere').warn('fougere: this app declares `remotes:` and no entity name could be reserved against the minifier.\n'
117
+ + ' A class name IS the JSON-RPC method, so a mangled one leaves as `f.list` and the remote refuses it.\n'
118
+ + ' Run `fougere sync` so the remote entities land in .fougere/, or keep a frond of your own.');
119
+ }
90
120
  if (entityNames.length > 0) {
91
121
  const vite = (nuxt.options.vite ??= {});
92
122
  const build = (vite.build ??= {});
@@ -102,8 +132,9 @@ const module = defineNuxtModule({
102
132
  const remotes = JSON.parse(readFileSync(remotesPath, 'utf-8'));
103
133
  for (const [name, meta] of Object.entries(remotes)) {
104
134
  // Don't override locally scanned fronds
105
- if (!nuxt.options.alias[`@frond/${name}`]) {
106
- nuxt.options.alias[`@frond/${name}`] = meta.path;
135
+ const specifier = frondPackage(name, conventions);
136
+ if (!nuxt.options.alias[specifier]) {
137
+ nuxt.options.alias[specifier] = meta.path;
107
138
  // Ensure Vite/Nitro can resolve files inside synced remotes
108
139
  nuxt.options.build.transpile.push(meta.path);
109
140
  }
@@ -163,12 +194,27 @@ const module = defineNuxtModule({
163
194
  handler: runtimeResolve('server/auth/routes/api/me.get'),
164
195
  });
165
196
  }
166
- // ── 6. Boot plugin (virtual — lives in .nuxt/) ───
197
+ // ── 6. The scan, written down (virtual — lives in .nuxt/) ───
198
+ //
199
+ // The module ALREADY scanned, above. Without this the generated plugin scanned a
200
+ // second time, at runtime — which is right on a server and impossible on a Worker:
201
+ // measured on workerd, `readdir` throws through unenv's shim and the app boots with
202
+ // ZERO fronds. Every page renders, every door answers NOT_FOUND, and only the scan
203
+ // diagnostics say why. One scan, one value, handed to the boot.
204
+ //
205
+ // `emitScan` writes imports relative to where the module SITS, so the destination is
206
+ // settled before the source is produced — `dst` is that path.
207
+ const scanTpl = addTemplate({
208
+ filename: 'fougere-scan.ts',
209
+ write: true,
210
+ getContents: ({ nuxt: n }) => emitScan(scan, { outFile: resolve(n.options.buildDir, 'fougere-scan.ts') }),
211
+ });
212
+ // ── 6b. Boot plugin (virtual — lives in .nuxt/) ───
167
213
  const allSeeds = orderSeeds(fronds);
168
214
  const bootTpl = addTemplate({
169
215
  filename: 'fougere-boot.ts',
170
216
  write: true,
171
- getContents: () => generateBootPlugin(config, allSeeds, runtimeResolve('server/utils/boot')),
217
+ getContents: () => generateBootPlugin(config, allSeeds, runtimeResolve('server/utils/boot'), scanTpl.dst),
172
218
  });
173
219
  addServerPlugin(bootTpl.dst);
174
220
  },
@@ -177,27 +223,85 @@ export default module;
177
223
  // ── Boot plugin generation ─────────────────────────
178
224
  // Exported (not just module-internal) so its output is unit-testable without
179
225
  // spinning up a whole Nuxt build.
180
- export function generateBootPlugin(config, seeds, fougereAppPath) {
226
+ /**
227
+ * What of the config a generated plugin can carry: values, never providers.
228
+ *
229
+ * `auth` holds a live object built by `betterAuth(...)`, so it cannot be written into a
230
+ * module — and it needs a database, which a codegen'd host has already resolved its own
231
+ * way. The rest is data and travels.
232
+ */
233
+ function carried(config) {
234
+ const { remotes, adapters, sources } = config;
235
+ return {
236
+ ...(remotes ? { remotes } : {}),
237
+ ...(adapters ? { adapters } : {}),
238
+ ...(sources ? { sources } : {}),
239
+ };
240
+ }
241
+ /**
242
+ * The entity names a SYNCED remote brought in — `fougere sync` writes the classes under
243
+ * `.fougere/`, and a consumer designates them exactly as it designates its own.
244
+ *
245
+ * By filename and not by loading them: this runs before the loader is installed, and a
246
+ * class file is named after its class by the same convention the scan reads.
247
+ */
248
+ async function syncedEntityNames(rootDir, conventions) {
249
+ const remotesPath = resolve(rootDir, '.fougere', 'remotes.json');
250
+ if (!existsSync(remotesPath))
251
+ return [];
252
+ try {
253
+ const remotes = JSON.parse(readFileSync(remotesPath, 'utf-8'));
254
+ const names = [];
255
+ for (const { path } of Object.values(remotes)) {
256
+ const dir = resolve(path, conventions.dirs.entities);
257
+ if (!existsSync(dir))
258
+ continue;
259
+ for (const file of readdirSync(dir)) {
260
+ const name = file.replace(/\.(ts|js|tsx)$/, '');
261
+ if (name !== file)
262
+ names.push(name);
263
+ }
264
+ }
265
+ return names;
266
+ }
267
+ catch {
268
+ return [];
269
+ }
270
+ }
271
+ export function generateBootPlugin(config, seeds, fougereAppPath,
272
+ /** The module `emitScan` wrote. Absent only in the tests that predate it. */
273
+ scanPath) {
181
274
  const lines = [];
182
275
  lines.push(`// Auto-generated by @fougere/nuxt — do not edit`);
183
276
  // Explicit imports — nitro's auto-imports don't reach this template in a prod build
184
277
  lines.push(`import { defineNitroPlugin } from 'nitropack/runtime';`);
185
278
  lines.push(`import { configureFougere } from '${fougereAppPath}';`);
186
- if (seeds.length)
187
- lines.push(`import { runSeeds } from '@fougere/core';`);
279
+ // A STATIC import, because that is the one thing a bundler needs spelled out — and the
280
+ // whole point is that nothing reads a directory once this file is built.
281
+ if (scanPath)
282
+ lines.push(`import { scan } from '${scanPath}';`);
188
283
  const db = config.db ?? 'sqlite';
189
284
  const sources = config.sources;
190
285
  // `declaresStorage` is the canonical reader of `db:` — asked, not re-interpreted.
191
286
  // Reading `dialect` here made this codegen a SECOND reader, and the two disagreed:
192
287
  // any value but 'sqlite' emitted an empty plugin, so no config, no seeds, not a word.
193
288
  // resolveStorage now refuses an unresolvable dialect by name, at boot, out loud.
289
+ // An app with no storage still has FRONDS, and the scan is what says so. This used to
290
+ // emit an empty plugin, which threw the scan away with the storage — two unrelated
291
+ // facts, one early return. What a plugin with no storage states is exactly the scan.
194
292
  if (!declaresStorage(db)) {
195
- lines.push(`export default defineNitroPlugin(() => {});`);
293
+ lines.push(``);
294
+ lines.push(`export default defineNitroPlugin(() => {`);
295
+ if (scanPath)
296
+ lines.push(` configureFougere({ scan, config: ${JSON.stringify(carried(config))} });`);
297
+ lines.push(`});`);
196
298
  return lines.join('\n') + '\n';
197
299
  }
198
300
  // The generated plugin names no storage package — resolution lives in
199
301
  // @fougere/defaults, the one place that knows which engine backs `db:`.
200
302
  lines.push(`import { resolveStorage } from '@fougere/defaults';`);
303
+ // After the early return above: an app that declares no storage generates no imports.
304
+ lines.push(`import { migrating${seeds.length ? ', runSeeds' : ''} } from '@fougere/core';`);
201
305
  lines.push(``);
202
306
  // Seed imports
203
307
  for (let i = 0; i < seeds.length; i++) {
@@ -207,35 +311,62 @@ export function generateBootPlugin(config, seeds, fougereAppPath) {
207
311
  lines.push(``);
208
312
  // Wrap all init in the plugin callback to avoid top-level native calls
209
313
  lines.push(`export default defineNitroPlugin(async () => {`);
314
+ // The SCAN first, and on its own line: it names no engine, so nothing below can stop it
315
+ // from being stated. Measured on workerd — `resolveStorage` threw on a native driver,
316
+ // Nitro swallowed the plugin whole, and the app came up with zero fronds and not a word.
317
+ // Two unrelated facts had been sharing one failure.
318
+ // The scan AND what the config says about topology — both read at build, both stated
319
+ // here rather than re-derived. `remotes` is the whole reason a consumer boots at all:
320
+ // without it the app hosts nothing and reaches nothing, and its pages render empty.
321
+ if (scanPath)
322
+ lines.push(` configureFougere({ scan, config: ${JSON.stringify(carried(config))} });`);
323
+ lines.push(` try {`);
210
324
  // Pass `db` through unchanged — resolveStorage (@fougere/defaults → setupSqlite)
211
325
  // is the one place that defaults an absent path, so both call sites (this
212
326
  // codegen'd plugin and fougereApp.ts's own fallback) land on the same file.
213
327
  // The second argument only appears when there is something to say: an app with one
214
328
  // database generates exactly the line it generated before.
215
329
  const sourcesArg = sources ? `, ${JSON.stringify(sources)}` : '';
216
- lines.push(` const storage = resolveStorage(${JSON.stringify(db)}${sourcesArg});`);
330
+ lines.push(` const storage = resolveStorage(${JSON.stringify(db)}${sourcesArg});`);
217
331
  lines.push(``);
218
- lines.push(` configureFougere({`);
219
- lines.push(` db: storage.db,`);
220
- lines.push(` ormFactory: storage.ormFactory,`);
221
- lines.push(` async afterBoot(app) {`);
222
- lines.push(` await storage.afterBoot?.(app);`);
332
+ lines.push(` configureFougere({`);
333
+ if (scanPath)
334
+ lines.push(` scan,`);
335
+ lines.push(` config: ${JSON.stringify(carried(config))},`);
336
+ lines.push(` db: storage.db,`);
337
+ lines.push(` ormFactory: storage.ormFactory,`);
338
+ // Two members of the ascent, named — not a claim on everything after the boot. The
339
+ // storage's is core's own declaration (`migrating`), so this codegen states no order and
340
+ // cannot mistype the name it would otherwise be silently adding beside.
341
+ lines.push(` extensions: [`);
342
+ lines.push(` migrating(storage.migrate),`);
223
343
  if (seeds.length) {
224
344
  // The seeding LOOP is core's (`runSeeds`), not written out here: a second copy
225
345
  // drifted, and the one that had lost its storage fallback was this one — the one
226
346
  // that actually runs when you open the app. Codegen's only job is the static
227
347
  // imports, which is the one thing a bundler needs spelled out.
228
348
  //
349
+ // Naming it 'seeds' REPLACES core's default member rather than running beside it —
350
+ // which is what the old `afterBoot` claim achieved by taking over the whole post-boot.
351
+ //
229
352
  // `report` is passed: its default is a no-op, so the boot you actually open said
230
353
  // nothing about a skipped seed — the very silence F-12 was aggravated by.
231
- lines.push(` await runSeeds(app, [`);
354
+ lines.push(` { name: 'seeds', up: (app) => runSeeds(app, [`);
232
355
  for (let i = 0; i < seeds.length; i++) {
233
- lines.push(` { entityName: '${seeds[i].entityName}', data: seed_${i}, filePath: ${JSON.stringify(seeds[i].filePath)} },`);
356
+ lines.push(` { entityName: '${seeds[i].entityName}', data: seed_${i}, filePath: ${JSON.stringify(seeds[i].filePath)} },`);
234
357
  }
235
- lines.push(` ], (message) => console.log('[fougere:seed]' + message));`);
358
+ lines.push(` ], (message) => console.log('[fougere:seed]' + message)) },`);
236
359
  }
237
- lines.push(` },`);
238
- lines.push(` });`);
360
+ lines.push(` ],`);
361
+ lines.push(` });`);
362
+ // A storage that could not be opened is REPORTED, never swallowed. Nitro takes a
363
+ // throwing plugin quietly, and the app then serves its pages with an empty domain —
364
+ // the failure mode this whole file exists to make impossible. The scan above already
365
+ // stands, so what is lost here is the storage and nothing else, and the line says which.
366
+ lines.push(` } catch (cause) {`);
367
+ lines.push(` console.error('[fougere] storage could not be opened — the app serves its fronds with no rows.');`);
368
+ lines.push(` console.error(cause);`);
369
+ lines.push(` }`);
239
370
  lines.push(`});`);
240
371
  return lines.join('\n') + '\n';
241
372
  }
@@ -1 +1 @@
1
- {"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fougere/nuxt",
3
- "version": "0.2.0-alpha.2",
3
+ "version": "0.4.0-alpha.0",
4
4
  "description": "The Nuxt module: Fougere's client primitives and server surface.",
5
5
  "keywords": [
6
6
  "fougere",
@@ -28,14 +28,14 @@
28
28
  },
29
29
  "files": [
30
30
  "dist",
31
- "src/runtime"
31
+ "src"
32
32
  ],
33
33
  "dependencies": {
34
34
  "@nuxt/kit": "^4.5.1",
35
35
  "jiti": "^2.6.1",
36
- "@fougere/app": "0.2.0-alpha.2",
37
- "@fougere/core": "0.2.0-alpha.2",
38
- "@fougere/defaults": "0.2.0-alpha.2"
36
+ "@fougere/app": "0.4.0-alpha.0",
37
+ "@fougere/core": "0.4.0-alpha.0",
38
+ "@fougere/defaults": "0.4.0-alpha.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@nuxt/schema": "^4.5.1",
package/src/module.ts ADDED
@@ -0,0 +1,431 @@
1
+ /**
2
+ * @fougere/nuxt — Nuxt module: scans fronds, registers the four
3
+ * primitives (useQuery/useCommand, useFormFor, useCurrentUser) and the
4
+ * server surface (call envelope, session, REST bridge, auth).
5
+ */
6
+ import {
7
+ defineNuxtModule,
8
+ addServerHandler,
9
+ addServerImportsDir,
10
+ addServerPlugin,
11
+ addPlugin,
12
+ addTemplate,
13
+ addImports,
14
+ createResolver,
15
+ useLogger,
16
+ } from '@nuxt/kit';
17
+ import type { Nuxt } from '@nuxt/schema';
18
+ import { orderSeeds } from '@fougere/core';
19
+ import {
20
+ scanProject, emitScan, frondAliases, frondPackage, watchPathsOf, resolveConventions, type Conventions,
21
+ setModuleLoader, loadCascadedConfig,
22
+ } from '@fougere/core/node';
23
+ import { declaresStorage } from '@fougere/defaults';
24
+ import type { SeedEntry, FougereConfig } from '@fougere/core';
25
+ import { createJiti } from 'jiti';
26
+ import { resolve, relative } from 'node:path';
27
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
28
+
29
+ export interface FougereModuleOptions {
30
+ /** Override fougere.config.ts values from nuxt.config. Optional. */
31
+ db?: FougereConfig['db'];
32
+ /**
33
+ * Where `fronds/` lives, relative to the app's rootDir. Default: the app
34
+ * itself. Set to `../..` for an app under `apps/*` in a workspace whose
35
+ * fronds are shared at the root. Config and `.fougere` stay app-local.
36
+ */
37
+ root?: string;
38
+ }
39
+
40
+
41
+ /**
42
+ * A frond directory as the pattern Nuxt tests for a restart: it matches the changed
43
+ * file against the path relative to the layer's app dir, so that is the base here.
44
+ */
45
+ const restartsOnFrom = (srcDir: string) => (dir: string): RegExp =>
46
+ new RegExp(`^${relative(srcDir, dir).replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\\\/]`);
47
+
48
+ const module = defineNuxtModule<FougereModuleOptions>({
49
+ meta: {
50
+ name: '@fougere/nuxt',
51
+ configKey: 'fougere',
52
+ },
53
+
54
+ async setup(options: FougereModuleOptions, nuxt: Nuxt) {
55
+ const { resolve: resolveModule } = createResolver(import.meta.url);
56
+ const runtimeResolve = (...path: string[]) =>
57
+ resolveModule('../src/runtime', ...path);
58
+ const rootDir = nuxt.options.rootDir;
59
+ // Fronds may live at the workspace root (app under apps/*); config/.fougere stay app-local.
60
+ const scanRoot = options.root ? resolve(rootDir, options.root) : rootDir;
61
+ // The runtime app (fougereApp) scans fronds too; hand it the same root via
62
+ // env (the Nitro dev worker inherits the parent env, like FORCE_COLOR above).
63
+ process.env.FOUGERE_ROOT = scanRoot;
64
+
65
+ // Propagate color support to Nitro dev worker (inherits parent env but has no TTY)
66
+ if (process.stdout?.isTTY && !process.env.NO_COLOR) {
67
+ process.env.FORCE_COLOR ??= '1';
68
+ }
69
+
70
+ // Prevent Nitro from bundling the TypeScript compiler (~9 MB).
71
+ // @fougere/core lazy-imports it, but Rollup still code-splits dynamic imports
72
+ // into the bundle — only external truly excludes it.
73
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
74
+ (nuxt as any).hook('nitro:config', (nitroConfig: any) => {
75
+ nitroConfig.rollupConfig ??= {};
76
+ nitroConfig.rollupConfig.external ??= [];
77
+ if (Array.isArray(nitroConfig.rollupConfig.external)) {
78
+ nitroConfig.rollupConfig.external.push('typescript');
79
+ }
80
+ // invoke reads the current request (state) through nitro's async context
81
+ nitroConfig.experimental = { ...nitroConfig.experimental, asyncContext: true };
82
+ });
83
+
84
+ // ── 0. A TS-aware loader, installed twice on purpose ──
85
+ // The Vite alias below covers `.vue` pages; this covers the SCAN, which loads a
86
+ // frond's own sources — so `@fronds/user/entities/User.js` inside a handler resolves
87
+ // for the same reason it does in a page.
88
+ //
89
+ // The FIRST install carries no aliases, because the config names the scope they are
90
+ // built from: reading it must not need them. Safe because nothing in
91
+ // `fougere.config.ts` may import `@fronds/*` — the one import that would need the
92
+ // name to read the file that declares it.
93
+ const installLoader = (alias?: Record<string, string>): void => {
94
+ const jiti = createJiti(import.meta.url, { interopDefault: true, ...(alias ? { alias } : {}) });
95
+ setModuleLoader((filePath) => jiti.import(filePath) as Promise<Record<string, unknown>>);
96
+ };
97
+ installLoader();
98
+
99
+ // ── 0b. Load fougere.config.ts along the workspace→app cascade (scanRoot is
100
+ // the workspace when the app declares `root`); module options override. ──
101
+ const fileConfig = await loadCascadedConfig(scanRoot, rootDir);
102
+ const optionsOverride = Object.fromEntries(
103
+ Object.entries(options).filter(([, v]) => v !== undefined),
104
+ ) as Partial<FougereConfig>;
105
+ const config: FougereConfig = { db: 'sqlite', ...fileConfig, ...optionsOverride };
106
+ const conventions = resolveConventions(config.conventions);
107
+
108
+ // Now the names are known, so the loader can resolve them — and the scan below runs
109
+ // with them installed, which is what lets it read a frond that names its neighbour.
110
+ installLoader(await frondAliases(scanRoot, conventions));
111
+
112
+ // ── 1. Scan fronds (filtered by FOUGERE_FRONDS env var) ──
113
+ const frondsFilter = process.env.FOUGERE_FRONDS?.split(',').map((s) => s.trim()).filter(Boolean);
114
+ const scan = await scanProject(scanRoot, frondsFilter, conventions);
115
+ const { fronds } = scan;
116
+
117
+ // ── 1b. Register @fronds/* aliases for all fronds, and watch them ──
118
+ // The scanned fronds ARE the watch list — nothing to declare. Without this, a
119
+ // frond under `apps/../..` sits outside rootDir, so Nuxt never restarts: the scan,
120
+ // the additive migration (once per boot) and the seeds all keep the previous shape,
121
+ // and a field you just added is simply absent with no error anywhere.
122
+ for (const frond of fronds) {
123
+ nuxt.options.alias[frond.source.package] = frond.source.path;
124
+ const watched = watchPathsOf(frond, scanRoot, conventions);
125
+ // Nuxt reads this list TWICE and a directory serves only the first read: the
126
+ // watcher adds string entries, while the restart test is `pattern === path`
127
+ // against the changed FILE — which a directory never equals. So each dir goes
128
+ // in twice, as itself and as the pattern that matches what lives under it.
129
+ nuxt.options.watch.push(...watched, ...watched.map(restartsOnFrom(nuxt.options.srcDir)));
130
+ }
131
+
132
+ // ── 1b-bis. Keep entity names through minification ──────────────────────
133
+ //
134
+ // Designation is class + verb: `useQuery(Post, 'list')` reads `Post.name`, and
135
+ // that name travels — it is the JSON-RPC method (`post.list`) and the REST path.
136
+ //
137
+ // Rollup cannot keep `class Post extends entity({…})` as a hoisted declaration
138
+ // (its heritage clause is a CALL), so it emits `var Post = class extends …`.
139
+ // The class is ANONYMOUS; the name comes from JavaScript's inference on the
140
+ // variable. `keep_classnames` therefore protects nothing — measured, in
141
+ // `compress` and in `mangle` both. Reserving the IDENTIFIER is what works.
142
+ //
143
+ // The list is exact rather than guessed: the scan just ran, so these are the
144
+ // classes themselves, read before any bundler touched them. `@fougere/vite`
145
+ // does the same for the hosts that have no module to do it for them.
146
+ // What this app can DESIGNATE, never what it hosts. A CONSUMER hosts nothing and
147
+ // designates everything through `remotes:` — and the guard below used to read
148
+ // `fronds`, so the app most in need of the reservation got none: measured on a Nuxt
149
+ // worker consuming a remote frond, `Product` minified to `f` and the call left as
150
+ // `f.list`, answered `No declared remote hosts 'f'`. The error names the mangled
151
+ // letter, which is the only reason it was findable at all.
152
+ const designated = [
153
+ ...fronds.flatMap((frond) => frond.entities.map((e) => (e.entityClass as unknown as { name: string }).name)),
154
+ ...(await syncedEntityNames(rootDir, conventions)),
155
+ ];
156
+ const entityNames = [...new Set(designated)];
157
+ if (Object.keys(config.remotes ?? {}).length > 0 && entityNames.length === 0) {
158
+ useLogger('fougere').warn(
159
+ 'fougere: this app declares `remotes:` and no entity name could be reserved against the minifier.\n'
160
+ + ' A class name IS the JSON-RPC method, so a mangled one leaves as `f.list` and the remote refuses it.\n'
161
+ + ' Run `fougere sync` so the remote entities land in .fougere/, or keep a frond of your own.',
162
+ );
163
+ }
164
+ if (entityNames.length > 0) {
165
+ const vite = (nuxt.options.vite ??= {});
166
+ const build = (vite.build ??= {});
167
+ build.minify = 'terser';
168
+ const terser = (build.terserOptions ??= {});
169
+ const mangle = ((terser as Record<string, any>).mangle ??= {});
170
+ mangle.reserved = [...new Set([...(mangle.reserved ?? []), ...entityNames])];
171
+ }
172
+
173
+ // ── 1c. Register aliases for synced remote fronds (.fougere/remotes.json) ──
174
+ const remotesPath = resolve(rootDir, '.fougere', 'remotes.json');
175
+ if (existsSync(remotesPath)) {
176
+ try {
177
+ const remotes = JSON.parse(readFileSync(remotesPath, 'utf-8')) as Record<string, { url: string; path: string }>;
178
+ for (const [name, meta] of Object.entries(remotes)) {
179
+ // Don't override locally scanned fronds
180
+ const specifier = frondPackage(name, conventions);
181
+ if (!nuxt.options.alias[specifier]) {
182
+ nuxt.options.alias[specifier] = meta.path;
183
+ // Ensure Vite/Nitro can resolve files inside synced remotes
184
+ nuxt.options.build.transpile.push(meta.path);
185
+ }
186
+ }
187
+ } catch { /* corrupt remotes.json — skip */ }
188
+ }
189
+
190
+ // ── 2. Composables — the primitives, nothing else ──
191
+ addImports([
192
+ { name: 'useQuery', from: runtimeResolve('composables/useFougereData') },
193
+ { name: 'useCommand', from: runtimeResolve('composables/useFougereData') },
194
+ { name: 'useFormFor', from: runtimeResolve('composables/useFormFor') },
195
+ { name: 'useCurrentUser', from: runtimeResolve('composables/useCurrentUser') },
196
+ ]);
197
+
198
+ // ── 3. Server: call envelope endpoint + catch-all route + shared utils ────
199
+ addServerHandler({
200
+ route: '/_fougere/call',
201
+ method: 'post',
202
+ handler: runtimeResolve('server/routes/call.post'),
203
+ });
204
+ // The same door, per audience: `/_fougere/call/public` serves the surface named
205
+ // `public`, the way `generateRoutes(app, { surface })` does for REST. The handler
206
+ // reads the segment (see `surfaceOf`).
207
+ addServerHandler({
208
+ route: '/_fougere/call/**',
209
+ method: 'post',
210
+ handler: runtimeResolve('server/routes/call.post'),
211
+ });
212
+ addServerHandler({
213
+ route: '/_fougere/session',
214
+ method: 'get',
215
+ handler: runtimeResolve('server/routes/session.get'),
216
+ });
217
+ // Session hydration — the page ships with its user, no round-trip
218
+ addPlugin({ src: runtimeResolve('plugins/session.server'), mode: 'server' });
219
+ addServerHandler({
220
+ route: '/api/**',
221
+ handler: runtimeResolve('server/api/crud'),
222
+ });
223
+ addServerImportsDir(runtimeResolve('server/utils'));
224
+
225
+ // ── 5b. Auth (mounted when fougere.config.ts declares `auth`) ──
226
+ if (config.auth) {
227
+ // Session middleware — resolves user on every request
228
+ addServerHandler({
229
+ middleware: true,
230
+ handler: runtimeResolve('server/auth/middleware/auth'),
231
+ });
232
+ // Auth catch-all route (login, register, callback, etc.)
233
+ addServerHandler({
234
+ route: '/auth/**',
235
+ handler: runtimeResolve('server/auth/routes/auth/[...]'),
236
+ });
237
+ // /api/me — current user endpoint
238
+ addServerHandler({
239
+ route: '/api/me',
240
+ method: 'get',
241
+ handler: runtimeResolve('server/auth/routes/api/me.get'),
242
+ });
243
+ }
244
+
245
+ // ── 6. The scan, written down (virtual — lives in .nuxt/) ───
246
+ //
247
+ // The module ALREADY scanned, above. Without this the generated plugin scanned a
248
+ // second time, at runtime — which is right on a server and impossible on a Worker:
249
+ // measured on workerd, `readdir` throws through unenv's shim and the app boots with
250
+ // ZERO fronds. Every page renders, every door answers NOT_FOUND, and only the scan
251
+ // diagnostics say why. One scan, one value, handed to the boot.
252
+ //
253
+ // `emitScan` writes imports relative to where the module SITS, so the destination is
254
+ // settled before the source is produced — `dst` is that path.
255
+ const scanTpl = addTemplate({
256
+ filename: 'fougere-scan.ts',
257
+ write: true,
258
+ getContents: ({ nuxt: n }) =>
259
+ emitScan(scan, { outFile: resolve(n!.options.buildDir, 'fougere-scan.ts') }),
260
+ });
261
+
262
+ // ── 6b. Boot plugin (virtual — lives in .nuxt/) ───
263
+ const allSeeds = orderSeeds(fronds);
264
+ const bootTpl = addTemplate({
265
+ filename: 'fougere-boot.ts',
266
+ write: true,
267
+ getContents: () => generateBootPlugin(config, allSeeds, runtimeResolve('server/utils/boot'), scanTpl.dst),
268
+ });
269
+ addServerPlugin(bootTpl.dst);
270
+
271
+ },
272
+ });
273
+
274
+ export default module;
275
+
276
+ // ── Boot plugin generation ─────────────────────────
277
+ // Exported (not just module-internal) so its output is unit-testable without
278
+ // spinning up a whole Nuxt build.
279
+
280
+ /**
281
+ * What of the config a generated plugin can carry: values, never providers.
282
+ *
283
+ * `auth` holds a live object built by `betterAuth(...)`, so it cannot be written into a
284
+ * module — and it needs a database, which a codegen'd host has already resolved its own
285
+ * way. The rest is data and travels.
286
+ */
287
+ function carried(config: FougereConfig): Partial<FougereConfig> {
288
+ const { remotes, adapters, sources } = config as FougereConfig & { sources?: unknown };
289
+ return {
290
+ ...(remotes ? { remotes } : {}),
291
+ ...(adapters ? { adapters } : {}),
292
+ ...(sources ? { sources } : {}),
293
+ } as Partial<FougereConfig>;
294
+ }
295
+
296
+ /**
297
+ * The entity names a SYNCED remote brought in — `fougere sync` writes the classes under
298
+ * `.fougere/`, and a consumer designates them exactly as it designates its own.
299
+ *
300
+ * By filename and not by loading them: this runs before the loader is installed, and a
301
+ * class file is named after its class by the same convention the scan reads.
302
+ */
303
+ async function syncedEntityNames(rootDir: string, conventions: Conventions): Promise<string[]> {
304
+ const remotesPath = resolve(rootDir, '.fougere', 'remotes.json');
305
+ if (!existsSync(remotesPath)) return [];
306
+ try {
307
+ const remotes = JSON.parse(readFileSync(remotesPath, 'utf-8')) as Record<string, { path: string }>;
308
+ const names: string[] = [];
309
+ for (const { path } of Object.values(remotes)) {
310
+ const dir = resolve(path, conventions.dirs.entities);
311
+ if (!existsSync(dir)) continue;
312
+ for (const file of readdirSync(dir)) {
313
+ const name = file.replace(/\.(ts|js|tsx)$/, '');
314
+ if (name !== file) names.push(name);
315
+ }
316
+ }
317
+ return names;
318
+ } catch {
319
+ return [];
320
+ }
321
+ }
322
+
323
+ export function generateBootPlugin(
324
+ config: FougereConfig,
325
+ seeds: SeedEntry[],
326
+ fougereAppPath: string,
327
+ /** The module `emitScan` wrote. Absent only in the tests that predate it. */
328
+ scanPath?: string,
329
+ ): string {
330
+ const lines: string[] = [];
331
+ lines.push(`// Auto-generated by @fougere/nuxt — do not edit`);
332
+ // Explicit imports — nitro's auto-imports don't reach this template in a prod build
333
+ lines.push(`import { defineNitroPlugin } from 'nitropack/runtime';`);
334
+ lines.push(`import { configureFougere } from '${fougereAppPath}';`);
335
+ // A STATIC import, because that is the one thing a bundler needs spelled out — and the
336
+ // whole point is that nothing reads a directory once this file is built.
337
+ if (scanPath) lines.push(`import { scan } from '${scanPath}';`);
338
+
339
+ const db = config.db ?? 'sqlite';
340
+ const sources = (config as { sources?: unknown }).sources;
341
+
342
+ // `declaresStorage` is the canonical reader of `db:` — asked, not re-interpreted.
343
+ // Reading `dialect` here made this codegen a SECOND reader, and the two disagreed:
344
+ // any value but 'sqlite' emitted an empty plugin, so no config, no seeds, not a word.
345
+ // resolveStorage now refuses an unresolvable dialect by name, at boot, out loud.
346
+ // An app with no storage still has FRONDS, and the scan is what says so. This used to
347
+ // emit an empty plugin, which threw the scan away with the storage — two unrelated
348
+ // facts, one early return. What a plugin with no storage states is exactly the scan.
349
+ if (!declaresStorage(db as Parameters<typeof declaresStorage>[0])) {
350
+ lines.push(``);
351
+ lines.push(`export default defineNitroPlugin(() => {`);
352
+ if (scanPath) lines.push(` configureFougere({ scan, config: ${JSON.stringify(carried(config))} });`);
353
+ lines.push(`});`);
354
+ return lines.join('\n') + '\n';
355
+ }
356
+
357
+ // The generated plugin names no storage package — resolution lives in
358
+ // @fougere/defaults, the one place that knows which engine backs `db:`.
359
+ lines.push(`import { resolveStorage } from '@fougere/defaults';`);
360
+ // After the early return above: an app that declares no storage generates no imports.
361
+ lines.push(`import { migrating${seeds.length ? ', runSeeds' : ''} } from '@fougere/core';`);
362
+ lines.push(``);
363
+
364
+ // Seed imports
365
+ for (let i = 0; i < seeds.length; i++) {
366
+ lines.push(`import seed_${i} from '${seeds[i].filePath}';`);
367
+ }
368
+ if (seeds.length) lines.push(``);
369
+
370
+ // Wrap all init in the plugin callback to avoid top-level native calls
371
+ lines.push(`export default defineNitroPlugin(async () => {`);
372
+ // The SCAN first, and on its own line: it names no engine, so nothing below can stop it
373
+ // from being stated. Measured on workerd — `resolveStorage` threw on a native driver,
374
+ // Nitro swallowed the plugin whole, and the app came up with zero fronds and not a word.
375
+ // Two unrelated facts had been sharing one failure.
376
+ // The scan AND what the config says about topology — both read at build, both stated
377
+ // here rather than re-derived. `remotes` is the whole reason a consumer boots at all:
378
+ // without it the app hosts nothing and reaches nothing, and its pages render empty.
379
+ if (scanPath) lines.push(` configureFougere({ scan, config: ${JSON.stringify(carried(config))} });`);
380
+ lines.push(` try {`);
381
+ // Pass `db` through unchanged — resolveStorage (@fougere/defaults → setupSqlite)
382
+ // is the one place that defaults an absent path, so both call sites (this
383
+ // codegen'd plugin and fougereApp.ts's own fallback) land on the same file.
384
+ // The second argument only appears when there is something to say: an app with one
385
+ // database generates exactly the line it generated before.
386
+ const sourcesArg = sources ? `, ${JSON.stringify(sources)}` : '';
387
+ lines.push(` const storage = resolveStorage(${JSON.stringify(db)}${sourcesArg});`);
388
+ lines.push(``);
389
+ lines.push(` configureFougere({`);
390
+ if (scanPath) lines.push(` scan,`);
391
+ lines.push(` config: ${JSON.stringify(carried(config))},`);
392
+ lines.push(` db: storage.db,`);
393
+ lines.push(` ormFactory: storage.ormFactory,`);
394
+ // Two members of the ascent, named — not a claim on everything after the boot. The
395
+ // storage's is core's own declaration (`migrating`), so this codegen states no order and
396
+ // cannot mistype the name it would otherwise be silently adding beside.
397
+ lines.push(` extensions: [`);
398
+ lines.push(` migrating(storage.migrate),`);
399
+
400
+ if (seeds.length) {
401
+ // The seeding LOOP is core's (`runSeeds`), not written out here: a second copy
402
+ // drifted, and the one that had lost its storage fallback was this one — the one
403
+ // that actually runs when you open the app. Codegen's only job is the static
404
+ // imports, which is the one thing a bundler needs spelled out.
405
+ //
406
+ // Naming it 'seeds' REPLACES core's default member rather than running beside it —
407
+ // which is what the old `afterBoot` claim achieved by taking over the whole post-boot.
408
+ //
409
+ // `report` is passed: its default is a no-op, so the boot you actually open said
410
+ // nothing about a skipped seed — the very silence F-12 was aggravated by.
411
+ lines.push(` { name: 'seeds', up: (app) => runSeeds(app, [`);
412
+ for (let i = 0; i < seeds.length; i++) {
413
+ lines.push(` { entityName: '${seeds[i].entityName}', data: seed_${i}, filePath: ${JSON.stringify(seeds[i].filePath)} },`);
414
+ }
415
+ lines.push(` ], (message) => console.log('[fougere:seed]' + message)) },`);
416
+ }
417
+
418
+ lines.push(` ],`);
419
+ lines.push(` });`);
420
+ // A storage that could not be opened is REPORTED, never swallowed. Nitro takes a
421
+ // throwing plugin quietly, and the app then serves its pages with an empty domain —
422
+ // the failure mode this whole file exists to make impossible. The scan above already
423
+ // stands, so what is lost here is the storage and nothing else, and the line says which.
424
+ lines.push(` } catch (cause) {`);
425
+ lines.push(` console.error('[fougere] storage could not be opened — the app serves its fronds with no rows.');`);
426
+ lines.push(` console.error(cause);`);
427
+ lines.push(` }`);
428
+ lines.push(`});`);
429
+
430
+ return lines.join('\n') + '\n';
431
+ }
@@ -9,7 +9,7 @@
9
9
  * the same `{ path, message }` shape whoever judged.
10
10
  */
11
11
  import { reactive, computed } from 'vue';
12
- import { FougereError, ErrorCode, toRegistrationName } from '@fougere/core/contract';
12
+ import { lowerFirst, validationErrorsOf } from '@fougere/core/contract';
13
13
  import { useCommand } from './useFougereData.js';
14
14
  import { formFieldsOf, payloadOf, errorsByField, type FormEntity, type FormField } from '@fougere/app/client';
15
15
 
@@ -23,7 +23,7 @@ export interface FormOptions {
23
23
  }
24
24
 
25
25
  export function useFormFor<T = Record<string, unknown>>(entity: FormEntity, options: FormOptions = {}) {
26
- const entityKey = toRegistrationName(entity.name);
26
+ const entityKey = lowerFirst(entity.name);
27
27
  const fields: FormField[] = formFieldsOf(entity, entityKey);
28
28
 
29
29
  // `initial` wins over the declared default: editing a row shows the row, including a
@@ -59,8 +59,9 @@ export function useFormFor<T = Record<string, unknown>>(entity: FormEntity, opti
59
59
  try {
60
60
  return await command.execute({ params: options.params, body: payloadOf(values) });
61
61
  } catch (err) {
62
- if (err instanceof FougereError && err.code === ErrorCode.VALIDATION_FAILED && Array.isArray(err.details)) {
63
- Object.assign(errors, errorsByField(err.details as { path: string; message: string }[]));
62
+ const refusals = validationErrorsOf(err);
63
+ if (refusals) {
64
+ Object.assign(errors, errorsByField(refusals));
64
65
  return null;
65
66
  }
66
67
  throw err;
@@ -9,4 +9,4 @@
9
9
  * So the codegen imports this file by absolute path, and this file — which does sit
10
10
  * in `@fougere/nuxt` — does the resolving. One indirection, one reason.
11
11
  */
12
- export { configureFougere, useFougereApp, type FougereServerConfig } from '@fougere/app';
12
+ export { configureFougere, extendFougere, useFougereApp, type FougereServerConfig } from '@fougere/app';