@ogygie/generate 0.24.0 → 0.25.1

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.
@@ -0,0 +1,32 @@
1
+ import { type OgygieError } from "@ogygie/core/errors";
2
+ import { type GenerateProject } from "./project.js";
3
+ export interface ScreenMapEntry {
4
+ name: string;
5
+ intent: string;
6
+ app: string;
7
+ folder: string;
8
+ spec: string;
9
+ routes: never[];
10
+ dbTables: never[];
11
+ dependsOn: string[];
12
+ status: "skeleton" | "in-progress";
13
+ }
14
+ /** The features folder of an app, as its sibling features are laid out (`src/features` when none says otherwise). */
15
+ export declare function featureFolderOfApp(project: GenerateProject, appName: string, featureId: string): string;
16
+ export type ScreenEntryOutcome = {
17
+ ok: true;
18
+ entry: ScreenMapEntry;
19
+ } | {
20
+ ok: false;
21
+ problem: OgygieError;
22
+ } | {
23
+ ok: false;
24
+ problem: null;
25
+ };
26
+ /**
27
+ * The entry a screen spec off the map would get — or `{ ok: false, problem: null }` when the spec is not a screen's
28
+ * (nothing to add: the feature pipeline says what is missing), or a refusal when the front app cannot be told.
29
+ */
30
+ export declare function screenMapEntryOf(project: GenerateProject, featureId: string, markdown: string, specPath: string): ScreenEntryOutcome;
31
+ /** Adds the entry to the map on disk when the spec is a screen off the map; says what it did. Null entry: nothing to add. */
32
+ export declare function ensureScreenOnMap(project: GenerateProject, featureId: string, log: (line: string) => void): ScreenEntryOutcome | null;
package/dist/cli/run.js CHANGED
@@ -71,7 +71,10 @@ function printReport(specPath, report, out) {
71
71
  }
72
72
  async function runCheckCommand(project, featureId, out) {
73
73
  const specPaths = [];
74
- if (featureId !== void 0) {
74
+ const offMapSpec = featureId === void 0 ? void 0 : `${project.specsFolder.replace(/\/+$/u, "")}/${featureId}.spec.md`;
75
+ if (featureId !== void 0 && offMapSpec !== void 0 && !project.manifest.features.some((feature) => feature.name === featureId) && existsSync(join(project.root, offMapSpec))) {
76
+ specPaths.push(offMapSpec);
77
+ } else if (featureId !== void 0) {
75
78
  const target = targetKindOf(project, featureId) === "screen" ? screenTargetOf(project, featureId) : featureTargetOf(project, featureId);
76
79
  if (!target.ok) {
77
80
  out.error(formatProblem(target.problem));
@@ -101,8 +104,8 @@ async function runCheckCommand(project, featureId, out) {
101
104
  }
102
105
 
103
106
  // src/cli/generate-command.ts
104
- import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, readdirSync as readdirSync2, writeFileSync } from "node:fs";
105
- import { dirname, join as join2 } from "node:path";
107
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
108
+ import { dirname, join as join3 } from "node:path";
106
109
  import { performance } from "node:perf_hooks";
107
110
 
108
111
  // ../ogygie-core/src/spec.schema.ts
@@ -240,6 +243,69 @@ function compositionOfSpec(content) {
240
243
  return COMPOSITION_LINE.exec(section)?.[1] ?? null;
241
244
  }
242
245
 
246
+ // src/cli/map-entry.ts
247
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "node:fs";
248
+ import { join as join2 } from "node:path";
249
+ function featureFolderOfApp(project, appName, featureId) {
250
+ const parents = project.manifest.features.filter((feature) => feature.app === appName).map((feature) => feature.folder.split("/").slice(0, -1).join("/")).filter((parent) => parent !== "");
251
+ const counts = /* @__PURE__ */ new Map();
252
+ for (const parent of parents) counts.set(parent, (counts.get(parent) ?? 0) + 1);
253
+ const common = [...counts.entries()].sort((left, right) => right[1] - left[1])[0]?.[0] ?? "src/features";
254
+ return `${common}/${featureId}`;
255
+ }
256
+ function screenMapEntryOf(project, featureId, markdown, specPath) {
257
+ const first = checkSpec({ markdown, specPath, generation: project.generation });
258
+ if (first.kind !== "screen" || first.screen === null) return { ok: false, problem: null };
259
+ const frontApps = project.manifest.apps.filter((app) => app.type === "front");
260
+ const point = first.screen.mounts === void 0 ? void 0 : (project.generation?.front?.extensionPoints ?? []).find((candidate) => candidate.id === first.screen?.mounts?.point);
261
+ const appName = point?.app ?? (frontApps.length === 1 ? frontApps[0]?.name : void 0);
262
+ if (appName === void 0) {
263
+ return {
264
+ ok: false,
265
+ problem: ogygieError({
266
+ code: "OGYGIE_GENERATE_PROJECT_INVALID",
267
+ message: `The screen "${featureId}" is not on the map and the map has ${frontApps.length} front apps: nothing says which one carries it.`,
268
+ file: MANIFEST_FILE,
269
+ cause: `no features[] entry is named "${featureId}" and the front app cannot be told (${frontApps.map((app) => app.name).join(", ") || "none"})`,
270
+ suggestedFix: "Declare the entry on the map with its `app`, or mount the screen on an extension point that names its app.",
271
+ details: { featureId, frontApps: frontApps.map((app) => app.name) }
272
+ })
273
+ };
274
+ }
275
+ const frontmatter = parseSpecDocument(markdown).frontmatter;
276
+ const known = new Set(project.manifest.features.map((feature) => feature.name));
277
+ const dependsOn = (frontmatter?.depends_on ?? []).filter((dependency) => known.has(dependency));
278
+ return {
279
+ ok: true,
280
+ entry: {
281
+ name: featureId,
282
+ intent: intentForManifest(intentOfSpec(markdown), specPath),
283
+ app: appName,
284
+ folder: featureFolderOfApp(project, appName, featureId),
285
+ spec: specPath,
286
+ routes: [],
287
+ dbTables: [],
288
+ dependsOn,
289
+ status: entryStatusForSpec(frontmatter?.status)
290
+ }
291
+ };
292
+ }
293
+ function ensureScreenOnMap(project, featureId, log) {
294
+ if (project.manifest.features.some((feature) => feature.name === featureId)) return null;
295
+ const specPath = `${project.specsFolder.replace(/\/+$/u, "")}/${featureId}.spec.md`;
296
+ const absolute = join2(project.root, specPath);
297
+ if (!existsSync2(absolute)) return null;
298
+ const outcome = screenMapEntryOf(project, featureId, readFileSync2(absolute, "utf8"), specPath);
299
+ if (!outcome.ok) return outcome;
300
+ const raw = project.rawManifest;
301
+ const features = Array.isArray(raw["features"]) ? raw["features"] : [];
302
+ raw["features"] = features;
303
+ features.push(outcome.entry);
304
+ writeFileSync(join2(project.root, MANIFEST_FILE), serializeManifest(raw), "utf8");
305
+ log(` ajout\xE9e ${MANIFEST_FILE} \u203A features[${featureId}] (\xE9cran hors carte : app ${outcome.entry.app}, dossier ${outcome.entry.folder}, status ${outcome.entry.status}${outcome.entry.status === "skeleton" ? " tant que la spec est draft" : ""})`);
306
+ return outcome;
307
+ }
308
+
243
309
  // src/cli/generate-command.ts
244
310
  function generatedFeatureIdsOf(project) {
245
311
  const kinds = [];
@@ -247,9 +313,9 @@ function generatedFeatureIdsOf(project) {
247
313
  if (feature.spec === null) continue;
248
314
  const kind = targetKindOf(project, feature.name);
249
315
  if (kind === null) continue;
250
- const specPath = join2(project.root, feature.spec);
251
- if (!existsSync2(specPath)) continue;
252
- const first = checkSpec({ markdown: readFileSync2(specPath, "utf8"), specPath: feature.spec, generation: project.generation });
316
+ const specPath = join3(project.root, feature.spec);
317
+ if (!existsSync3(specPath)) continue;
318
+ const first = checkSpec({ markdown: readFileSync3(specPath, "utf8"), specPath: feature.spec, generation: project.generation });
253
319
  if (kind === "feature" ? first.block !== null : first.screen !== null) kinds.push({ id: feature.name, kind });
254
320
  }
255
321
  return [...kinds.filter((entry) => entry.kind === "feature"), ...kinds.filter((entry) => entry.kind === "screen")].map((entry) => entry.id);
@@ -286,6 +352,21 @@ async function runGenerateFeatureCommand(project, featureId, out) {
286
352
  out.error("usage: ogygie generate generate <feature-id> | --all");
287
353
  return 1;
288
354
  }
355
+ const joined = ensureScreenOnMap(project, featureId, (line) => out.log(line));
356
+ if (joined !== null) {
357
+ if (!joined.ok && joined.problem !== null) {
358
+ out.error(formatProblem(joined.problem));
359
+ return 1;
360
+ }
361
+ if (joined.ok) {
362
+ const reloaded = loadGenerateProject(project.root);
363
+ if (!reloaded.ok) {
364
+ for (const problem of reloaded.problems) out.error(formatProblem(problem));
365
+ return 1;
366
+ }
367
+ return runGenerateScreenCommand(reloaded.project, featureId, out);
368
+ }
369
+ }
289
370
  if (targetKindOf(project, featureId) === "screen") return runGenerateScreenCommand(project, featureId, out);
290
371
  const lookup = featureTargetOf(project, featureId);
291
372
  if (!lookup.ok) {
@@ -293,7 +374,7 @@ async function runGenerateFeatureCommand(project, featureId, out) {
293
374
  return 1;
294
375
  }
295
376
  const { target } = lookup;
296
- const markdown = readFileSync2(join2(project.root, target.specPath), "utf8");
377
+ const markdown = readFileSync3(join3(project.root, target.specPath), "utf8");
297
378
  const firstPass = checkSpec({ markdown, specPath: target.specPath, generation: target.generation });
298
379
  if (firstPass.block === null) {
299
380
  for (const problem of firstPass.problems) out.error(formatProblem(problem));
@@ -313,8 +394,8 @@ async function runGenerateFeatureCommand(project, featureId, out) {
313
394
  return 1;
314
395
  }
315
396
  const started = performance.now();
316
- const migrationsDir = join2(project.root, target.appPath, target.generation.target.migrations);
317
- const existingMigrations = existsSync2(migrationsDir) ? readdirSync2(migrationsDir) : [];
397
+ const migrationsDir = join3(project.root, target.appPath, target.generation.target.migrations);
398
+ const existingMigrations = existsSync3(migrationsDir) ? readdirSync2(migrationsDir) : [];
318
399
  const plan = buildFeaturePlan({
319
400
  featureId,
320
401
  folder: target.folder,
@@ -328,10 +409,10 @@ async function runGenerateFeatureCommand(project, featureId, out) {
328
409
  generatorVersion: GENERATOR_VERSION,
329
410
  behaviors: boundBehaviorsOf(report.behaviors)
330
411
  });
331
- const logicPath = join2(project.root, target.folder, "logic.ts");
332
- const existingLogic = existsSync2(logicPath) ? readFileSync2(logicPath, "utf8") : null;
333
- const migrationPath = join2(project.root, plan.migrationPath);
334
- const existingMigration = existsSync2(migrationPath) ? readFileSync2(migrationPath, "utf8") : null;
412
+ const logicPath = join3(project.root, target.folder, "logic.ts");
413
+ const existingLogic = existsSync3(logicPath) ? readFileSync3(logicPath, "utf8") : null;
414
+ const migrationPath = join3(project.root, plan.migrationPath);
415
+ const existingMigration = existsSync3(migrationPath) ? readFileSync3(migrationPath, "utf8") : null;
335
416
  const files = generateFeature(plan, { existingLogic, existingMigration, library: loadHolesLibrary() });
336
417
  const manifestEntry = applyManifestEntry(project.rawManifest, plan, intentOfSpec(markdown), entryStatusForSpec(parseSpecDocument(markdown).frontmatter?.status));
337
418
  const specContracts = applySpecContracts(markdown, plan);
@@ -339,22 +420,22 @@ async function runGenerateFeatureCommand(project, featureId, out) {
339
420
  let written = 0;
340
421
  let emitted = 0;
341
422
  for (const file of files) {
342
- const absolute = join2(project.root, file.path);
343
- const exists = existsSync2(absolute);
423
+ const absolute = join3(project.root, file.path);
424
+ const exists = existsSync3(absolute);
344
425
  emitted += file.content.split("\n").length - 1;
345
- if (exists && readFileSync2(absolute, "utf8") === file.content) {
426
+ if (exists && readFileSync3(absolute, "utf8") === file.content) {
346
427
  out.log(` identique ${file.path}`);
347
428
  continue;
348
429
  }
349
430
  mkdirSync(dirname(absolute), { recursive: true });
350
- writeFileSync(absolute, file.content, "utf8");
431
+ writeFileSync2(absolute, file.content, "utf8");
351
432
  written += 1;
352
433
  out.log(` ${exists ? "r\xE9\xE9crit " : "\xE9crit "} ${file.path}`);
353
434
  }
354
435
  if (manifestEntry.outcome === "unchanged") {
355
436
  out.log(` identique ${MANIFEST_FILE} \u203A features[${featureId}].routes, dbTables`);
356
437
  } else {
357
- writeFileSync(join2(project.root, MANIFEST_FILE), serializeManifest(manifestEntry.manifest), "utf8");
438
+ writeFileSync2(join3(project.root, MANIFEST_FILE), serializeManifest(manifestEntry.manifest), "utf8");
358
439
  out.log(
359
440
  ` ${manifestEntry.outcome === "created" ? "ajout\xE9e " : "r\xE9\xE9crite "} ${MANIFEST_FILE} \u203A features[${featureId}] (${plan.operations.length} route(s), ${1 + plan.slots.length + plan.references.filter((reference) => reference.table !== null).length} table(s)${manifestEntry.outcome === "created" ? `, status ${entryStatusForSpec(parseSpecDocument(markdown).frontmatter?.status)} : \xE0 vous de dire delivered` : ""})`
360
441
  );
@@ -367,7 +448,7 @@ async function runGenerateFeatureCommand(project, featureId, out) {
367
448
  out.log(` identique ${target.specPath} \u203A bloc @generated de # Contrats de donn\xE9es`);
368
449
  } else {
369
450
  const addedLines = specContracts.markdown.split("\n").length - markdown.split("\n").length;
370
- writeFileSync(join2(project.root, target.specPath), specContracts.markdown, "utf8");
451
+ writeFileSync2(join3(project.root, target.specPath), specContracts.markdown, "utf8");
371
452
  out.log(
372
453
  ` ${specContracts.outcome === "inserted" ? "ins\xE9r\xE9 " : "r\xE9\xE9crit "} ${target.specPath} \u203A bloc @generated de # Contrats de donn\xE9es${specContracts.outcome === "inserted" ? ` (+${addedLines} lignes)` : ""}`
373
454
  );
@@ -380,8 +461,8 @@ async function runGenerateFeatureCommand(project, featureId, out) {
380
461
 
381
462
  // src/cli/preview-command.ts
382
463
  import { spawnSync } from "node:child_process";
383
- import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync3 } from "node:fs";
384
- import { join as join3 } from "node:path";
464
+ import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync3 } from "node:fs";
465
+ import { join as join4 } from "node:path";
385
466
  var PREVIEWS_ROOT = "previews";
386
467
  function previewsDirOf(featureId, composition) {
387
468
  return composition === null ? `${PREVIEWS_ROOT}/${featureId}` : `${PREVIEWS_ROOT}/${featureId}/${composition}`;
@@ -412,29 +493,29 @@ async function runPreviewCommand(project, featureId, out, runner = vitestPreview
412
493
  return 0;
413
494
  }
414
495
  const testPath = `${target.folder}/${pascalCaseOf(featureId)}Screen${PREVIEWS_FILE_SUFFIX}`;
415
- if (!existsSync3(join3(project.root, testPath))) {
496
+ if (!existsSync4(join4(project.root, testPath))) {
416
497
  out.error(`ogygie generate preview \u2014 ${testPath} n'existe pas : lancez \`ogygie generate generate ${featureId}\` d'abord, le test des aper\xE7us est \xE9mis avec l'\xE9cran.`);
417
498
  return 1;
418
499
  }
419
- const specAbsolute = join3(project.root, target.specPath);
420
- const composition = existsSync3(specAbsolute) ? compositionOfSpec(readFileSync3(specAbsolute, "utf8")) : null;
500
+ const specAbsolute = join4(project.root, target.specPath);
501
+ const composition = existsSync4(specAbsolute) ? compositionOfSpec(readFileSync4(specAbsolute, "utf8")) : null;
421
502
  const relativeDir = previewsDirOf(featureId, composition);
422
- const outDir = join3(project.root, relativeDir);
503
+ const outDir = join4(project.root, relativeDir);
423
504
  const run = runner({ projectRoot: project.root, testPath, outDir });
424
505
  for (const line of run.lines) out.log(` ${line}`);
425
506
  if (run.code !== 0) {
426
507
  out.error(`ogygie generate preview \u2014 le rendu des aper\xE7us de ${featureId} a \xE9chou\xE9 (code ${run.code}) : rien n'est publi\xE9 tant qu'un \xE9tat ne rend pas.`);
427
508
  return run.code;
428
509
  }
429
- const pages = existsSync3(outDir) ? readdirSync3(outDir).filter((name) => name.endsWith(".html")).sort() : [];
510
+ const pages = existsSync4(outDir) ? readdirSync3(outDir).filter((name) => name.endsWith(".html")).sort() : [];
430
511
  out.log(`ogygie generate preview \u2014 ${featureId} : ${pages.length} page(s) dans ${relativeDir}/ (${pages.map((name) => name.replace(/\.html$/u, "")).join(", ")})${composition === null ? "" : ` \u2014 composition ${composition}`}.`);
431
512
  return pages.length === 0 ? 1 : 0;
432
513
  }
433
514
 
434
515
  // src/cli/schemas-command.ts
435
516
  import { spawnSync as spawnSync2 } from "node:child_process";
436
- import { appendFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
437
- import { dirname as dirname2, join as join4 } from "node:path";
517
+ import { appendFileSync, existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
518
+ import { dirname as dirname2, join as join5 } from "node:path";
438
519
  import { pathToFileURL } from "node:url";
439
520
 
440
521
  // src/schemas/draft.ts
@@ -653,9 +734,9 @@ function featureFolderFor(project, featureId) {
653
734
  return `${common}/${featureId}`;
654
735
  }
655
736
  function availableGateScripts(projectRoot) {
656
- const path = join4(projectRoot, "package.json");
657
- if (!existsSync4(path)) return [];
658
- const scripts = JSON.parse(readFileSync4(path, "utf8")).scripts ?? {};
737
+ const path = join5(projectRoot, "package.json");
738
+ if (!existsSync5(path)) return [];
739
+ const scripts = JSON.parse(readFileSync5(path, "utf8")).scripts ?? {};
659
740
  return SCHEMAS_GATE_SCRIPTS.filter((script) => script in scripts);
660
741
  }
661
742
  async function runSchemasCommand(project, featureId, argv, out, deps = {}) {
@@ -665,8 +746,8 @@ async function runSchemasCommand(project, featureId, argv, out, deps = {}) {
665
746
  }
666
747
  const onMap = project.manifest.features.find((feature) => feature.name === featureId);
667
748
  const specPath = onMap?.spec ?? `${project.specsFolder.replace(/\/+$/u, "")}/${featureId}.spec.md`;
668
- const specAbsolute = join4(project.root, specPath);
669
- if (!existsSync4(specAbsolute)) {
749
+ const specAbsolute = join5(project.root, specPath);
750
+ if (!existsSync5(specAbsolute)) {
670
751
  out.error(formatProblem(ogygieError({ code: "OGYGIE_GENERATE_PROJECT_INVALID", message: `No spec carries the feature "${featureId}".`, file: specPath, cause: `${specPath} does not exist`, suggestedFix: "Write the spec first: a schemas module derives from a spec, never the reverse." })));
671
752
  return 1;
672
753
  }
@@ -674,7 +755,7 @@ async function runSchemasCommand(project, featureId, argv, out, deps = {}) {
674
755
  out.error(formatProblem(ogygieError({ code: "OGYGIE_GENERATE_PROJECT_INVALID", message: `The feature "${featureId}" cannot be generated from this map.`, file: MANIFEST_FILE, cause: project.apiApp === void 0 ? "apps[] has no entry with type=api" : "the manifest declares no generation block", suggestedFix: "Declare the api app and the `generation` block in the manifest." })));
675
756
  return 1;
676
757
  }
677
- const markdown = readFileSync4(specAbsolute, "utf8");
758
+ const markdown = readFileSync5(specAbsolute, "utf8");
678
759
  const first = checkSpec({ markdown, specPath, generation: project.generation });
679
760
  if (first.kind !== "feature" || first.block === null) {
680
761
  out.log(`ogygie generate schemas \u2014 ${specPath} ${first.kind === "screen" ? "est un \xE9cran : ses contrats sont ceux des features qu'il consomme" : "n'a pas de bloc # G\xE9n\xE9ration de feature"}, rien \xE0 \xE9crire.`);
@@ -683,8 +764,8 @@ async function runSchemasCommand(project, featureId, argv, out, deps = {}) {
683
764
  const block = first.block;
684
765
  const folder = onMap === void 0 ? `${project.apiApp.path}/${featureFolderFor(project, featureId)}` : `${project.apiApp.path}/${onMap.folder}`;
685
766
  const modulePath = `${folder}/${block.contracts.module.replace(/^\.\//u, "")}`;
686
- const moduleAbsolute = join4(project.root, modulePath);
687
- if (existsSync4(moduleAbsolute)) {
767
+ const moduleAbsolute = join5(project.root, modulePath);
768
+ if (existsSync5(moduleAbsolute)) {
688
769
  out.log(`ogygie generate schemas \u2014 ${modulePath} existe : il appartient au repo, jamais r\xE9g\xE9n\xE9r\xE9 (D52).`);
689
770
  return 0;
690
771
  }
@@ -711,7 +792,7 @@ async function runSchemasCommand(project, featureId, argv, out, deps = {}) {
711
792
  },
712
793
  async judge(source, attempt) {
713
794
  mkdirSync2(dirname2(moduleAbsolute), { recursive: true });
714
- writeFileSync2(moduleAbsolute, source, "utf8");
795
+ writeFileSync3(moduleAbsolute, source, "utf8");
715
796
  let moduleExports;
716
797
  try {
717
798
  moduleExports = await importProductModule(`${pathToFileURL(moduleAbsolute).href}?attempt=${attempt}`);
@@ -772,7 +853,7 @@ ${rows.join("\n")}
772
853
  raw["features"] = features;
773
854
  const status = entryStatusForSpec(parseSpecDocument(markdown).frontmatter?.status);
774
855
  features.push({ name: featureId, intent: intentForManifest(intentOfSpec(markdown), specPath), folder: featureFolderFor(project, featureId), spec: specPath, routes: [], dbTables: [], dependsOn: [], status });
775
- writeFileSync2(join4(project.root, MANIFEST_FILE), serializeManifest(raw), "utf8");
856
+ writeFileSync3(join5(project.root, MANIFEST_FILE), serializeManifest(raw), "utf8");
776
857
  out.log(` ajout\xE9e ${MANIFEST_FILE} \u203A features[${featureId}] (routes et tables viendront de \`generate\`, status ${status}${status === "skeleton" ? " tant que la spec est draft" : ""})`);
777
858
  }
778
859
  out.log(`ogygie generate schemas \u2014 ${featureId} : module \xE9crit en ${result.attempts} tentative(s) par ${result.model ?? "?"}, ${result.usage.length} appel(s) de mod\xE8le (${totalInput} jetons d'entr\xE9e, ${totalOutput} de sortie) ; commit\xE9 une fois, jamais r\xE9g\xE9n\xE9r\xE9 (D52).`);
@@ -784,7 +865,7 @@ import { performance as performance2 } from "node:perf_hooks";
784
865
 
785
866
  // ../ogygie-core/src/repo-adapter.ts
786
867
  import { readFile as readFileFromDisk, readdir, realpath, stat } from "node:fs/promises";
787
- import { isAbsolute, join as join5, resolve, sep } from "node:path";
868
+ import { isAbsolute, join as join6, resolve, sep } from "node:path";
788
869
  import { z as z2 } from "zod";
789
870
  var RepoFileEntrySchema = z2.object({
790
871
  /** Path relative to the LISTED directory, "/" separated, never absolute. */
@@ -846,16 +927,16 @@ async function listFilesRecursively(absoluteDir, prefix) {
846
927
  const relativePath = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
847
928
  if (entry.isDirectory()) {
848
929
  if (isSkippedDirectory(entry.name)) continue;
849
- files.push(...await listFilesRecursively(join5(absoluteDir, entry.name), relativePath));
930
+ files.push(...await listFilesRecursively(join6(absoluteDir, entry.name), relativePath));
850
931
  } else if (entry.isFile()) {
851
- const stats = await stat(join5(absoluteDir, entry.name));
932
+ const stats = await stat(join6(absoluteDir, entry.name));
852
933
  files.push({ path: relativePath, sizeBytes: stats.size });
853
934
  }
854
935
  }
855
936
  return files.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
856
937
  }
857
938
  async function resolveGitDirs(rootDir) {
858
- const dotGit = join5(rootDir, ".git");
939
+ const dotGit = join6(rootDir, ".git");
859
940
  let gitDir;
860
941
  try {
861
942
  const stats = await stat(dotGit);
@@ -871,7 +952,7 @@ async function resolveGitDirs(rootDir) {
871
952
  return null;
872
953
  }
873
954
  try {
874
- const commonPointer = (await readFileFromDisk(join5(gitDir, "commondir"), "utf8")).trim();
955
+ const commonPointer = (await readFileFromDisk(join6(gitDir, "commondir"), "utf8")).trim();
875
956
  return { gitDir, commonDir: isAbsolute(commonPointer) ? commonPointer : resolve(gitDir, commonPointer) };
876
957
  } catch {
877
958
  return { gitDir, commonDir: gitDir };
@@ -880,13 +961,13 @@ async function resolveGitDirs(rootDir) {
880
961
  async function readRefSha(dirs, refName) {
881
962
  for (const base of [dirs.gitDir, dirs.commonDir]) {
882
963
  try {
883
- const sha = (await readFileFromDisk(join5(base, refName), "utf8")).trim();
964
+ const sha = (await readFileFromDisk(join6(base, refName), "utf8")).trim();
884
965
  if (sha !== "") return sha;
885
966
  } catch {
886
967
  }
887
968
  }
888
969
  try {
889
- const packed = await readFileFromDisk(join5(dirs.commonDir, "packed-refs"), "utf8");
970
+ const packed = await readFileFromDisk(join6(dirs.commonDir, "packed-refs"), "utf8");
890
971
  for (const line of packed.split("\n")) {
891
972
  if (line.startsWith("#") || line.startsWith("^")) continue;
892
973
  const [sha, name] = line.trim().split(" ");
@@ -934,7 +1015,7 @@ function createFilesystemRepoAdapter(rootDir) {
934
1015
  if (dirs === null) return null;
935
1016
  let head;
936
1017
  try {
937
- head = (await readFileFromDisk(join5(dirs.gitDir, "HEAD"), "utf8")).trim();
1018
+ head = (await readFileFromDisk(join6(dirs.gitDir, "HEAD"), "utf8")).trim();
938
1019
  } catch {
939
1020
  return null;
940
1021
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ogygie/generate",
3
- "version": "0.24.0",
3
+ "version": "0.25.1",
4
4
  "description": "Ogygie generator: reads the `# Génération` block of a spec and its schemas.ts, derives everything deterministic without a model (routes, pipeline, database, factories, tests, screens, previews), fills the typed holes of logic.ts by library, draw and escalation, and draws the first schemas.ts of a new feature — judged by the gates. Sixth package of the lockstep (D55).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -29,7 +29,7 @@
29
29
  }
30
30
  },
31
31
  "dependencies": {
32
- "@ogygie/core": "0.24.0",
32
+ "@ogygie/core": "0.25.1",
33
33
  "tsx": "^4.20.0",
34
34
  "yaml": "^2.9.0",
35
35
  "zod": "^4.0.0"
@@ -38,7 +38,7 @@
38
38
  "@lingui/babel-plugin-lingui-macro": "^5.9.5",
39
39
  "@lingui/core": "^5.9.5",
40
40
  "@lingui/react": "^5.9.5",
41
- "@ogygie/ui": "0.24.0",
41
+ "@ogygie/ui": "0.25.1",
42
42
  "@starter/api": "0.1.0",
43
43
  "@tanstack/react-query": "^5.102.2",
44
44
  "@tanstack/react-router": "^1.170.32",