@ogygie/generate 0.22.0 → 0.24.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/dist/{chunk-ASXSTGY5.js → chunk-ZPRW6QDW.js} +460 -122
- package/dist/cli/extension-registries.d.ts +9 -0
- package/dist/cli/run.js +14 -9
- package/dist/emit/screen/content.d.ts +10 -0
- package/dist/emit/screen/extensions.d.ts +18 -0
- package/dist/emit/screen/views.d.ts +3 -1
- package/dist/emit/screen-plan.d.ts +29 -6
- package/dist/generation-block.schema.d.ts +1 -1
- package/dist/index.js +12 -4
- package/dist/manifest-generation.schema.d.ts +13 -0
- package/dist/screen-block.schema.d.ts +69 -7
- package/dist/screen-checks.d.ts +6 -1
- package/dist/screen-vocabulary.d.ts +6 -2
- package/dist/verify-generation.d.ts +50 -0
- package/package.json +3 -3
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ExtensionContribution } from "../emit/screen/extensions.js";
|
|
2
|
+
import type { CommandOutput } from "./output.js";
|
|
3
|
+
import { type GenerateProject } from "./project.js";
|
|
4
|
+
/** Every screen of the map that mounts on a point, with what the registry needs of it. */
|
|
5
|
+
export declare function mountedScreensOf(project: GenerateProject): Array<ExtensionContribution & {
|
|
6
|
+
point: string;
|
|
7
|
+
}>;
|
|
8
|
+
/** Writes the registry of the given points (every declared point when `pointIds` is undefined); says what moved. Code 1 when a point names no front app. */
|
|
9
|
+
export declare function writeExtensionRegistries(project: GenerateProject, out: CommandOutput, pointIds?: readonly string[]): number;
|
package/dist/cli/run.js
CHANGED
|
@@ -35,8 +35,9 @@ import {
|
|
|
35
35
|
tableNameOf,
|
|
36
36
|
targetKindOf,
|
|
37
37
|
throwOgygieError,
|
|
38
|
-
verifyGeneration
|
|
39
|
-
|
|
38
|
+
verifyGeneration,
|
|
39
|
+
writeExtensionRegistries
|
|
40
|
+
} from "../chunk-ZPRW6QDW.js";
|
|
40
41
|
|
|
41
42
|
// src/cli/check-command.ts
|
|
42
43
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
@@ -256,26 +257,29 @@ function generatedFeatureIdsOf(project) {
|
|
|
256
257
|
async function runGenerateAllCommand(project, out) {
|
|
257
258
|
const ids = generatedFeatureIdsOf(project);
|
|
258
259
|
if (ids.length === 0) {
|
|
260
|
+
const registries2 = writeExtensionRegistries(project, out);
|
|
259
261
|
out.log("ogygie generate --all \u2014 aucune spec de la carte ne porte de bloc # G\xE9n\xE9ration, rien \xE0 r\xE9g\xE9n\xE9rer.");
|
|
260
|
-
return
|
|
262
|
+
return registries2;
|
|
261
263
|
}
|
|
262
264
|
const refused = [];
|
|
263
265
|
let current = project;
|
|
264
266
|
for (const [index, id] of ids.entries()) {
|
|
265
267
|
if (index > 0) {
|
|
266
|
-
const
|
|
267
|
-
if (!
|
|
268
|
-
for (const problem of
|
|
268
|
+
const reloaded2 = loadGenerateProject(project.root);
|
|
269
|
+
if (!reloaded2.ok) {
|
|
270
|
+
for (const problem of reloaded2.problems) out.error(formatProblem(problem));
|
|
269
271
|
out.error(`ogygie generate --all \u2014 le projet ne se relit plus apr\xE8s ${ids[index - 1]}, arr\xEAt.`);
|
|
270
272
|
return 1;
|
|
271
273
|
}
|
|
272
|
-
current =
|
|
274
|
+
current = reloaded2.project;
|
|
273
275
|
}
|
|
274
276
|
const code = await runGenerateFeatureCommand(current, id, out);
|
|
275
277
|
if (code !== 0) refused.push(id);
|
|
276
278
|
}
|
|
279
|
+
const reloaded = loadGenerateProject(project.root);
|
|
280
|
+
const registries = reloaded.ok ? writeExtensionRegistries(reloaded.project, out) : 1;
|
|
277
281
|
out.log(`ogygie generate --all \u2014 ${ids.length} spec(s) g\xE9n\xE9r\xE9e(s) de la carte : ${ids.length - refused.length} r\xE9g\xE9n\xE9r\xE9e(s)${refused.length === 0 ? "" : `, ${refused.length} refus\xE9e(s) (${refused.join(", ")})`}.`);
|
|
278
|
-
return refused.length === 0 ? 0 : 1;
|
|
282
|
+
return refused.length === 0 && registries === 0 ? 0 : 1;
|
|
279
283
|
}
|
|
280
284
|
async function runGenerateFeatureCommand(project, featureId, out) {
|
|
281
285
|
if (featureId === void 0 || featureId === "") {
|
|
@@ -953,7 +957,8 @@ async function runVerifyCommand(project, out) {
|
|
|
953
957
|
...project.apiApp === void 0 ? {} : { apiAppPath: project.apiApp.path },
|
|
954
958
|
...project.manifest.tenancy === void 0 ? {} : { tenantColumn: project.manifest.tenancy.column },
|
|
955
959
|
library: loadHolesLibrary(),
|
|
956
|
-
loadConsumed: (block) => loadConsumedFeatures(project, block)
|
|
960
|
+
loadConsumed: (block) => loadConsumedFeatures(project, block),
|
|
961
|
+
frontApps: project.manifest.apps.filter((app) => app.type === "front").map((app) => ({ name: app.name, path: app.path }))
|
|
957
962
|
});
|
|
958
963
|
const elapsedMs = Math.round(performance2.now() - started);
|
|
959
964
|
if (!report.ok) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @intent Emits `content/<id>.gen.ts` of a screen (D56): one content list of
|
|
3
|
+
* the block — the MATERIAL of a rubric, written in the spec in the product's
|
|
4
|
+
* source language — as a typed constant the content view renders. Plain text
|
|
5
|
+
* and one optional link per entry, exactly as the spec wrote them: a change of
|
|
6
|
+
* wording is a change of the spec, regenerated, never an edit here.
|
|
7
|
+
*/
|
|
8
|
+
import type { ContentListPlan, ScreenPlan } from "../screen-plan.js";
|
|
9
|
+
export declare function contentFilePath(list: ContentListPlan): string;
|
|
10
|
+
export declare function emitContentList(plan: ScreenPlan, list: ContentListPlan): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ExtensionPoint } from "../../manifest-generation.schema.js";
|
|
2
|
+
export declare const EXTENSION_REGISTRY_FILE = "extensions.gen.ts";
|
|
3
|
+
export interface ExtensionContribution {
|
|
4
|
+
/** The mounted screen's feature id, its folder relative to the repo root, and the id of its contribution on the point. */
|
|
5
|
+
featureId: string;
|
|
6
|
+
folder: string;
|
|
7
|
+
mountId: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ExtensionRegistryInput {
|
|
10
|
+
point: ExtensionPoint;
|
|
11
|
+
/** The host screen's folder, relative to the repo root (`<front app>/<point.folder>`). */
|
|
12
|
+
hostFolder: string;
|
|
13
|
+
contributions: readonly ExtensionContribution[];
|
|
14
|
+
generatorVersion: string;
|
|
15
|
+
}
|
|
16
|
+
/** Repo-relative path of a point's registry. */
|
|
17
|
+
export declare function extensionRegistryPath(appPath: string, point: ExtensionPoint): string;
|
|
18
|
+
export declare function emitExtensionRegistry(input: ExtensionRegistryInput): string;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { CustomViewPlan, FormViewPlan, ListViewPlan, ScreenPlan } from "../screen-plan.js";
|
|
1
|
+
import type { ContentViewPlan, CustomViewPlan, FormViewPlan, ListViewPlan, ScreenPlan } from "../screen-plan.js";
|
|
2
2
|
export declare function emitListView(plan: ScreenPlan, view: ListViewPlan): string;
|
|
3
3
|
export declare function emitFormView(plan: ScreenPlan, view: FormViewPlan): string;
|
|
4
|
+
/** D56: a content view — a DisclosureList over the constant of its list; the link label is the one text of the view, translated here; no operation, no fetch. */
|
|
5
|
+
export declare function emitContentView(plan: ScreenPlan, view: ContentViewPlan): string;
|
|
4
6
|
/** The props of a custom view: the received fields of the output, and one MutationLike per action. */
|
|
5
7
|
export declare function customViewPropsLines(view: CustomViewPlan): string[];
|
|
6
8
|
/** The signature line of a custom view — what the markers protect and compare. */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type ConsumedRoute } from "../consumed-feature.js";
|
|
2
|
-
import type { GuardDenial, GuardRegistry, FrontTarget } from "../manifest-generation.schema.js";
|
|
2
|
+
import type { GuardDenial, GuardRegistry, FrontTarget, ExtensionPoint } from "../manifest-generation.schema.js";
|
|
3
3
|
import { type GenerationBlock, type HttpOperation } from "../generation-block.schema.js";
|
|
4
4
|
import type { HttpOperationKind } from "../motif-catalog.js";
|
|
5
|
-
import { type CustomView, type FormView, type ListView, type ScreenBlock, type ScreenHole } from "../screen-block.schema.js";
|
|
5
|
+
import { type CustomView, type FormView, type ListView, type ScreenBlock, type ScreenHole, type ContentEntry, type ContentView, isOperationView } from "../screen-block.schema.js";
|
|
6
6
|
import type { ParsedScreenBehavior } from "../screen-grammar.js";
|
|
7
7
|
import { type FieldModel, type SchemaModels } from "../zod-introspection.js";
|
|
8
8
|
export type { ConsumedRoute };
|
|
@@ -128,7 +128,20 @@ export interface CustomViewPlan {
|
|
|
128
128
|
/** The output overlaid by the hole's example — what its test renders. */
|
|
129
129
|
exampleInput: unknown;
|
|
130
130
|
}
|
|
131
|
-
|
|
131
|
+
/** A content list of the block (D56): what `content/<id>.gen.ts` exports. */
|
|
132
|
+
export interface ContentListPlan {
|
|
133
|
+
id: string;
|
|
134
|
+
/** `faqEntries` → `FAQ_ENTRIES`, the exported constant. */
|
|
135
|
+
constName: string;
|
|
136
|
+
entries: ContentEntry[];
|
|
137
|
+
}
|
|
138
|
+
export interface ContentViewPlan {
|
|
139
|
+
kind: "content";
|
|
140
|
+
id: string;
|
|
141
|
+
componentName: string;
|
|
142
|
+
list: ContentListPlan;
|
|
143
|
+
}
|
|
144
|
+
export type ViewPlan = ListViewPlan | FormViewPlan | CustomViewPlan | ContentViewPlan;
|
|
132
145
|
export interface ScreenBehaviorPlan {
|
|
133
146
|
raw: string;
|
|
134
147
|
behavior: ParsedScreenBehavior;
|
|
@@ -151,8 +164,15 @@ export interface ScreenPlan {
|
|
|
151
164
|
bodyName: string;
|
|
152
165
|
propsName: string;
|
|
153
166
|
routeFactoryName: string;
|
|
154
|
-
/** The read the container's ScreenState waits for. */
|
|
155
|
-
primary: ListViewPlan | CustomViewPlan;
|
|
167
|
+
/** The read the container's ScreenState waits for — null for a screen of content alone (D56): the container renders the body directly. */
|
|
168
|
+
primary: ListViewPlan | CustomViewPlan | null;
|
|
169
|
+
/** The content lists of the block, in order (D56). */
|
|
170
|
+
contentLists: ContentListPlan[];
|
|
171
|
+
/** D56: the extension point the screen mounts on, and the id of its contribution there — null when the screen has a route or mounts nowhere. */
|
|
172
|
+
mount: {
|
|
173
|
+
point: ExtensionPoint;
|
|
174
|
+
id: string;
|
|
175
|
+
} | null;
|
|
156
176
|
operations: ScreenOperationPlan[];
|
|
157
177
|
reads: ScreenOperationPlan[];
|
|
158
178
|
writes: ScreenOperationPlan[];
|
|
@@ -183,5 +203,8 @@ export interface ScreenPlan {
|
|
|
183
203
|
export declare function labelOf(fieldName: string): string;
|
|
184
204
|
/** A distinct sample of a model at `variant`: uuids and dates differ by variant so two rows never share a key. */
|
|
185
205
|
export declare function distinctSampleOf(model: FieldModel, variant: number): unknown;
|
|
206
|
+
/** `faqEntries` → `FAQ_ENTRIES`. */
|
|
207
|
+
export declare function contentConstNameOf(listId: string): string;
|
|
186
208
|
export declare function buildScreenPlan(input: ScreenPlanInput): ScreenPlan;
|
|
187
|
-
export type { CustomView, FormView, ListView };
|
|
209
|
+
export type { ContentView, CustomView, FormView, ListView };
|
|
210
|
+
export { isOperationView };
|
|
@@ -1273,7 +1273,7 @@ export type YamlBlockRead = {
|
|
|
1273
1273
|
};
|
|
1274
1274
|
/** Reads the YAML text of a `# Génération` block into a raw value — one refusal per YAML error, at its line. */
|
|
1275
1275
|
export declare function readYamlBlock(yamlText: string, location: BlockLocation): YamlBlockRead;
|
|
1276
|
-
/** D41: a raw block is a SCREEN block when it carries `consumes` — a feature block carries `contracts`; `check` refuses a block that carries both or neither. */
|
|
1276
|
+
/** D41: a raw block is a SCREEN block when it carries `consumes` or `views` (a content screen consumes nothing, D56) — a feature block carries `contracts`; `check` refuses a block that carries both or neither. */
|
|
1277
1277
|
export declare function blockKindOf(raw: unknown): "feature" | "screen" | "both" | "neither";
|
|
1278
1278
|
/** Reads the YAML text of the block, then validates it. */
|
|
1279
1279
|
export declare function parseGenerationBlock(yamlText: string, location: BlockLocation): GenerationBlockParse;
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,8 @@ import {
|
|
|
3
3
|
AlterStepSchema,
|
|
4
4
|
BEHAVIOR_UNBOUND,
|
|
5
5
|
BODY_BANS,
|
|
6
|
+
CONTENT_RENDERS,
|
|
7
|
+
CONTENT_STATES,
|
|
6
8
|
CONTRACTS_SECTION_TITLE,
|
|
7
9
|
CONTRACT_VALIDATION_FAILED,
|
|
8
10
|
ConsumedFeatureSchema,
|
|
@@ -187,7 +189,7 @@ import {
|
|
|
187
189
|
viewCandidateTextOf,
|
|
188
190
|
viewExampleTestName,
|
|
189
191
|
vocabularyOf
|
|
190
|
-
} from "./chunk-
|
|
192
|
+
} from "./chunk-ZPRW6QDW.js";
|
|
191
193
|
|
|
192
194
|
// src/prompt-card.ts
|
|
193
195
|
var phrase = (caseKey) => caseKey.replace(/^with:/u, "avec ").replace(/^without:/u, "sans ");
|
|
@@ -205,7 +207,13 @@ function targetLines(generation) {
|
|
|
205
207
|
const lines = [`- base ${target.database}, tenancy ${target.tenancy}, tests ${target.tests}, colonnes en ${target.naming} ; code d'un contrat viol\xE9 : ${contractViolationCodeOf(target)} (\xE9cris CONTRACT_VALIDATION_FAILED dans les lignes, il d\xE9signe ce code).`];
|
|
206
208
|
if (target.database === "mssql-raw") lines.push("- cette cible n'\xE9met ni `import` ni `appendOnlySupersedes` ni `allOrNothingImport` : lectures, create, update, setOrClear, delete et les quatre autres motifs.");
|
|
207
209
|
if (generation.front === void 0) lines.push("- aucune cible `front` : pas de spec d'\xE9cran (bloc `consumes`), seulement des features API.");
|
|
208
|
-
else
|
|
210
|
+
else {
|
|
211
|
+
lines.push(`- cible front ${generation.front.tests} : une spec d'\xE9cran est possible (bloc \`consumes\`, ou \`views\` de contenu seules).`);
|
|
212
|
+
const points = generation.front.extensionPoints ?? [];
|
|
213
|
+
lines.push(
|
|
214
|
+
points.length === 0 ? "- aucun point d'extension d\xE9clar\xE9 : un \xE9cran nouveau a sa `route`, jamais `mounts`." : `- points d'extension o\xF9 un \xE9cran peut se MONTER au lieu d'avoir une route (\`mounts: { point, id }\`) : ${points.map((point) => `\`${point.id}\` (${point.kind}, \xE9cran h\xF4te \`${point.folder}\`)`).join(", ")} \u2014 un onglet de plus dans un \xE9cran existant se d\xE9clare ainsi.`
|
|
215
|
+
);
|
|
216
|
+
}
|
|
209
217
|
return lines;
|
|
210
218
|
}
|
|
211
219
|
function generationGrammarCard(generation) {
|
|
@@ -222,7 +230,7 @@ function generationGrammarCard(generation) {
|
|
|
222
230
|
`- \`motifs\` : ${motifs.join(", ")} \u2014 une instance par motif, et SEULEMENT si une op\xE9ration l'emploie (\`uses: <motif>\` sur l'op\xE9ration, sauf \`signedWrite\` qui s'applique \xE0 toutes les \xE9critures) ; sinon \`motifs: {}\`.`,
|
|
223
231
|
`- \`holes\` : ${holeKinds.join(", ")} ; un trou \`validate\` = \`{ id, kind: validate, reads: [champs de l'entr\xE9e], error: CODE, effects: [] | [clock], suggestedFix: true, examples: { accept: {\u2026}, reject: {\u2026} } }\` ; un \`derive\` = \`{ id, kind: derive, reads, writes: <champ de la ressource>, error: CODE, effects: [], examples: { accept: [{ input, output }], reject: { input } } }\`. Les exemples sont OBLIGATOIRES ; une date d'exemple se rapporte au 2026-01-01 (horloge fig\xE9e des tests).`,
|
|
224
232
|
"",
|
|
225
|
-
`Bloc d'un \xC9CRAN \u2014 cl\xE9s ${SCREEN_BLOCK_KEYS.map((key) => `\`${key}\``).join(", ")} : \`consumes: [{ feature, operations: [ids] }]\` (features DE LA CARTE), \`views: [{ id, kind: list, operation: <lecture> } | { id, kind: form, operation: <\xE9criture>, fields: [champs] } | { id, kind: custom, operation, hole }]\`, \`holes: [{ id, receives: [champs de la sortie], actions: [\xE9critures], examples: { input, renders: ["texte"] } }]\`, \`route?: /chemin
|
|
233
|
+
`Bloc d'un \xC9CRAN \u2014 cl\xE9s ${SCREEN_BLOCK_KEYS.map((key) => `\`${key}\``).join(", ")} : \`consumes: [{ feature, operations: [ids] }]\` (features DE LA CARTE ; vide ou absent quand toutes les vues sont de contenu), \`views: [{ id, kind: list, operation: <lecture> } | { id, kind: form, operation: <\xE9criture>, fields: [champs] } | { id, kind: custom, operation, hole } | { id, kind: content, content: <id d'une liste> }]\`, \`holes: [{ id, receives: [champs de la sortie], actions: [\xE9critures], examples: { input, renders: ["texte"] } }]\`, \`content: [{ id: camelCase, entries: [{ id: kebab-case, title, body, href? }] }]\` (la MATI\xC8RE d'une rubrique \u2014 une aide, une FAQ : du texte brut \xE9crit ici m\xEAme, dans la langue source du produit, un lien au plus par entr\xE9e, jamais un libell\xE9 d'interface), et UNE des deux : \`route?: /chemin\` (une page) ou \`mounts?: { point, id }\` (un emplacement d'un \xE9cran existant, parmi les points d'extension de la cible ci-dessous).`,
|
|
226
234
|
"",
|
|
227
235
|
"Les LIGNES de comportement (une par puce, deux lignes au plus, recoll\xE9es) :",
|
|
228
236
|
"`- \xC9TANT DONN\xC9 <op\xE9ration> avec <cas>[, (avec|sans) <cas>]* ALORS <statut> [<CODE>] [et <attendu>[, <attendu>]*] [[hole:<id>]]`",
|
|
@@ -231,7 +239,7 @@ function generationGrammarCard(generation) {
|
|
|
231
239
|
...guardLines(generation),
|
|
232
240
|
"- statut : le code HTTP (200 pour une lecture, 201 pour create, 200 pour update) ou le refus (400 avec le code du trou ou CONTRACT_VALIDATION_FAILED, 401/403 du guard, 404 du notFound, 409 d'un motif).",
|
|
233
241
|
"- attendus : `<collection> = lignes`, `<collection> = []`, `ordre <champ> asc|desc`, `updated.<champ> = cl\xE9 vis\xE9e`, `champs envoy\xE9s \xE9crits`, `liste \xE0 jour`, `suggestedFix`, `rien n'est \xE9crit`, `<writes> d\xE9riv\xE9` ; TOUT mot hors de cette liste \u2014 une valeur qu'un trou calcule (`created.<champ d\xE9riv\xE9> = \u2026`), un refus qu'un trou d\xE9cide \u2014 nomme son trou en fin de ligne : `[hole:<id>]`, sinon la ligne est refus\xE9e (OGYGIE_BEHAVIOR_UNBOUND).",
|
|
234
|
-
`- \xE9cran : \`- \xC9TANT DONN\xC9 <vue|\xE9cran> avec <\xE9tat> ALORS <rendu>\` ; \xE9tats ${[...READ_STATES, ...FORM_STATES].map((key) => `\xAB ${phrase(key)} \xBB`).join(", ")} ; rendus ${[...READ_RENDERS, ...FORM_RENDERS].map((render) => `\xAB ${render} \xBB`).join(", ")}.`,
|
|
242
|
+
`- \xE9cran : \`- \xC9TANT DONN\xC9 <vue|\xE9cran> avec <\xE9tat> ALORS <rendu>\` ; \xE9tats ${[...READ_STATES, ...FORM_STATES].map((key) => `\xAB ${phrase(key)} \xBB`).join(", ")} ; rendus ${[...READ_RENDERS, ...FORM_RENDERS].map((render) => `\xAB ${render} \xBB`).join(", ")} ; une vue \`content\` : \xE9tat ${CONTENT_STATES.map((key) => `\xAB ${phrase(key)} \xBB`).join(", ")}, rendus ${CONTENT_RENDERS.map((render) => `\xAB ${render} \xBB`).join(", ")}, \xAB ancre <id d'entr\xE9e> \xBB.`,
|
|
235
243
|
"",
|
|
236
244
|
"Cible du produit :",
|
|
237
245
|
...targetLines(generation),
|
|
@@ -95,8 +95,15 @@ export declare const FrontTargetSchema: z.ZodObject<{
|
|
|
95
95
|
export: z.ZodString;
|
|
96
96
|
}, z.core.$strict>;
|
|
97
97
|
stylesheet: z.ZodOptional<z.ZodString>;
|
|
98
|
+
extensionPoints: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
99
|
+
id: z.ZodString;
|
|
100
|
+
kind: z.ZodLiteral<"tab">;
|
|
101
|
+
folder: z.ZodString;
|
|
102
|
+
app: z.ZodOptional<z.ZodString>;
|
|
103
|
+
}, z.core.$strict>>>;
|
|
98
104
|
}, z.core.$strict>;
|
|
99
105
|
export type FrontTarget = z.infer<typeof FrontTargetSchema>;
|
|
106
|
+
export type ExtensionPoint = NonNullable<FrontTarget["extensionPoints"]>[number];
|
|
100
107
|
export declare const GuardDenialSchema: z.ZodObject<{
|
|
101
108
|
status: z.ZodNumber;
|
|
102
109
|
code: z.ZodString;
|
|
@@ -221,6 +228,12 @@ export declare const ManifestGenerationSchema: z.ZodObject<{
|
|
|
221
228
|
export: z.ZodString;
|
|
222
229
|
}, z.core.$strict>;
|
|
223
230
|
stylesheet: z.ZodOptional<z.ZodString>;
|
|
231
|
+
extensionPoints: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
232
|
+
id: z.ZodString;
|
|
233
|
+
kind: z.ZodLiteral<"tab">;
|
|
234
|
+
folder: z.ZodString;
|
|
235
|
+
app: z.ZodOptional<z.ZodString>;
|
|
236
|
+
}, z.core.$strict>>>;
|
|
224
237
|
}, z.core.$strict>>;
|
|
225
238
|
}, z.core.$strict>;
|
|
226
239
|
export type ManifestGeneration = z.infer<typeof ManifestGenerationSchema>;
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
* @intent Zod schema AND parser of the `# Génération` block of a SCREEN spec
|
|
3
3
|
* (DECISIONS.md D41): one fenced YAML block, three keys — `consumes` (the API
|
|
4
4
|
* features and the operations the screen uses), `views` (`list`, `form`,
|
|
5
|
-
* `custom`, each on ONE operation
|
|
6
|
-
* receive, which writes they may trigger,
|
|
7
|
-
* an OPTIONAL `route`, the path the screen
|
|
5
|
+
* `custom`, each on ONE operation, or `content`, on a content list) and
|
|
6
|
+
* `holes` (the custom views: what they receive, which writes they may trigger,
|
|
7
|
+
* what their render must show) — plus an OPTIONAL `route`, the path the screen
|
|
8
|
+
* mounts on, and, since D56, `content` (lists of entries, the material of a
|
|
9
|
+
* rubric) and `mounts` (an extension point of a host screen, exclusive of
|
|
10
|
+
* `route`). A screen whose every view is content consumes nothing. What the shape alone
|
|
8
11
|
* cannot check — do the operations exist in the consumed blocks, are the
|
|
9
12
|
* fields of the output or the input — is `screen-checks.ts`, which needs the
|
|
10
13
|
* consumed features; this module checks the block against itself and refuses
|
|
@@ -14,7 +17,7 @@
|
|
|
14
17
|
import { z } from "zod";
|
|
15
18
|
import type { OgygieError } from "@ogygie/core/errors";
|
|
16
19
|
import { type BlockLocation } from "./generation-block.schema.js";
|
|
17
|
-
export declare const SCREEN_BLOCK_KEYS: readonly ["route", "consumes", "views", "holes"];
|
|
20
|
+
export declare const SCREEN_BLOCK_KEYS: readonly ["route", "mounts", "consumes", "content", "views", "holes"];
|
|
18
21
|
/** A feature id of the map, kebab-case (French ids allowed, as everywhere). */
|
|
19
22
|
export declare const FeatureIdSchema: z.ZodString;
|
|
20
23
|
/** A front path: `/`, `/rates`, `/settings/archetypes` — no parameter in M1. */
|
|
@@ -50,6 +53,12 @@ export declare const CustomViewSchema: z.ZodObject<{
|
|
|
50
53
|
id: z.ZodString;
|
|
51
54
|
operation: z.ZodString;
|
|
52
55
|
}, z.core.$strict>;
|
|
56
|
+
/** A view of CONTENT (D56): a DisclosureList over one of the block's content lists — no operation. */
|
|
57
|
+
export declare const ContentViewSchema: z.ZodObject<{
|
|
58
|
+
id: z.ZodString;
|
|
59
|
+
kind: z.ZodLiteral<"content">;
|
|
60
|
+
content: z.ZodString;
|
|
61
|
+
}, z.core.$strict>;
|
|
53
62
|
export declare const ViewSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
54
63
|
kind: z.ZodLiteral<"list">;
|
|
55
64
|
fields: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -70,6 +79,10 @@ export declare const ViewSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
70
79
|
hole: z.ZodString;
|
|
71
80
|
id: z.ZodString;
|
|
72
81
|
operation: z.ZodString;
|
|
82
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
83
|
+
id: z.ZodString;
|
|
84
|
+
kind: z.ZodLiteral<"content">;
|
|
85
|
+
content: z.ZodString;
|
|
73
86
|
}, z.core.$strict>], "kind">;
|
|
74
87
|
export type View = z.infer<typeof ViewSchema>;
|
|
75
88
|
export type ListView = Extract<View, {
|
|
@@ -81,6 +94,38 @@ export type FormView = Extract<View, {
|
|
|
81
94
|
export type CustomView = Extract<View, {
|
|
82
95
|
kind: "custom";
|
|
83
96
|
}>;
|
|
97
|
+
export type ContentView = Extract<View, {
|
|
98
|
+
kind: "content";
|
|
99
|
+
}>;
|
|
100
|
+
/** A view bound to an operation — every kind but content. */
|
|
101
|
+
export type OperationView = ListView | FormView | CustomView;
|
|
102
|
+
export declare const isOperationView: (view: View) => view is OperationView;
|
|
103
|
+
/** An entry of a content list (D56): plain text in the product's source language, one optional link; the id anchors it. */
|
|
104
|
+
export declare const ContentEntrySchema: z.ZodObject<{
|
|
105
|
+
id: z.ZodString;
|
|
106
|
+
title: z.ZodString;
|
|
107
|
+
body: z.ZodString;
|
|
108
|
+
href: z.ZodOptional<z.ZodString>;
|
|
109
|
+
}, z.core.$strict>;
|
|
110
|
+
export type ContentEntry = z.infer<typeof ContentEntrySchema>;
|
|
111
|
+
export declare const ContentListSchema: z.ZodObject<{
|
|
112
|
+
id: z.ZodString;
|
|
113
|
+
entries: z.ZodArray<z.ZodObject<{
|
|
114
|
+
id: z.ZodString;
|
|
115
|
+
title: z.ZodString;
|
|
116
|
+
body: z.ZodString;
|
|
117
|
+
href: z.ZodOptional<z.ZodString>;
|
|
118
|
+
}, z.core.$strict>>;
|
|
119
|
+
}, z.core.$strict>;
|
|
120
|
+
export type ContentList = z.infer<typeof ContentListSchema>;
|
|
121
|
+
/** The id of an extension point, as the manifest's front target declares it: `fiche-societe.tabs`. */
|
|
122
|
+
export declare const ExtensionPointIdSchema: z.ZodString;
|
|
123
|
+
/** D56: where a screen mounts instead of a route — a point of a host screen, and the id of this contribution there. */
|
|
124
|
+
export declare const MountSchema: z.ZodObject<{
|
|
125
|
+
point: z.ZodString;
|
|
126
|
+
id: z.ZodString;
|
|
127
|
+
}, z.core.$strict>;
|
|
128
|
+
export type Mount = z.infer<typeof MountSchema>;
|
|
84
129
|
export declare const ScreenHoleSchema: z.ZodObject<{
|
|
85
130
|
id: z.ZodString;
|
|
86
131
|
receives: z.ZodArray<z.ZodString>;
|
|
@@ -93,10 +138,23 @@ export declare const ScreenHoleSchema: z.ZodObject<{
|
|
|
93
138
|
export type ScreenHole = z.infer<typeof ScreenHoleSchema>;
|
|
94
139
|
export declare const ScreenBlockSchema: z.ZodObject<{
|
|
95
140
|
route: z.ZodOptional<z.ZodString>;
|
|
96
|
-
|
|
141
|
+
mounts: z.ZodOptional<z.ZodObject<{
|
|
142
|
+
point: z.ZodString;
|
|
143
|
+
id: z.ZodString;
|
|
144
|
+
}, z.core.$strict>>;
|
|
145
|
+
consumes: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
97
146
|
feature: z.ZodString;
|
|
98
147
|
operations: z.ZodArray<z.ZodString>;
|
|
99
|
-
}, z.core.$strict
|
|
148
|
+
}, z.core.$strict>>>;
|
|
149
|
+
content: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
150
|
+
id: z.ZodString;
|
|
151
|
+
entries: z.ZodArray<z.ZodObject<{
|
|
152
|
+
id: z.ZodString;
|
|
153
|
+
title: z.ZodString;
|
|
154
|
+
body: z.ZodString;
|
|
155
|
+
href: z.ZodOptional<z.ZodString>;
|
|
156
|
+
}, z.core.$strict>>;
|
|
157
|
+
}, z.core.$strict>>>;
|
|
100
158
|
views: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
101
159
|
kind: z.ZodLiteral<"list">;
|
|
102
160
|
fields: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -117,6 +175,10 @@ export declare const ScreenBlockSchema: z.ZodObject<{
|
|
|
117
175
|
hole: z.ZodString;
|
|
118
176
|
id: z.ZodString;
|
|
119
177
|
operation: z.ZodString;
|
|
178
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
179
|
+
id: z.ZodString;
|
|
180
|
+
kind: z.ZodLiteral<"content">;
|
|
181
|
+
content: z.ZodString;
|
|
120
182
|
}, z.core.$strict>], "kind">>;
|
|
121
183
|
holes: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
122
184
|
id: z.ZodString;
|
|
@@ -142,5 +204,5 @@ export declare function validateScreenBlock(raw: unknown, location: BlockLocatio
|
|
|
142
204
|
export declare function parseScreenBlock(yamlText: string, location: BlockLocation): ScreenBlockParse;
|
|
143
205
|
/** The consumed feature an operation belongs to, or null when the block does not consume it. */
|
|
144
206
|
export declare function consumerOf(block: ScreenBlock, operationId: string): ConsumedFeature | null;
|
|
145
|
-
/** The read whose output the screen's ScreenState waits for: the first list or custom view (D40). */
|
|
207
|
+
/** The read whose output the screen's ScreenState waits for: the first list or custom view (D40) — a content view (D56) waits for nothing. */
|
|
146
208
|
export declare function primaryViewOf(block: ScreenBlock): ListView | CustomView | null;
|
package/dist/screen-checks.d.ts
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
import { type OgygieError } from "@ogygie/core/errors";
|
|
13
13
|
import { type ConsumedRoute } from "./consumed-feature.js";
|
|
14
14
|
import { type GenerationBlock } from "./generation-block.schema.js";
|
|
15
|
-
import type {
|
|
15
|
+
import type { FrontTarget } from "./manifest-generation.schema.js";
|
|
16
|
+
import { type ScreenBlock } from "./screen-block.schema.js";
|
|
16
17
|
import type { FieldModel, SchemaModels } from "./zod-introspection.js";
|
|
17
18
|
export interface ConsumedFeatureCheckInput {
|
|
18
19
|
featureId: string;
|
|
@@ -28,6 +29,10 @@ export interface ScreenCheckInput {
|
|
|
28
29
|
specPath: string;
|
|
29
30
|
/** The consumed features, as the project resolved them — absent when the map does not know the feature. */
|
|
30
31
|
consumed: ReadonlyMap<string, ConsumedFeatureCheckInput>;
|
|
32
|
+
/** The front target of the manifest, when known: its extension points are what `mounts.point` may name (D56). */
|
|
33
|
+
front?: FrontTarget | null;
|
|
34
|
+
/** Only the mount is checked (the consumed features were not resolved). */
|
|
35
|
+
mountOnly?: boolean;
|
|
31
36
|
}
|
|
32
37
|
/** The element fields of a read output — the one array of objects it carries. */
|
|
33
38
|
export declare function elementFieldsOf(outputModel: FieldModel | undefined): string[] | null;
|
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
* the error banner, the access explanation, the empty state, the rows, their
|
|
7
7
|
* order; a `form` view speaks of a valid or invalid form, of a refused write,
|
|
8
8
|
* and renders the write it triggers, the error sentences, or nothing written.
|
|
9
|
-
* A
|
|
10
|
-
*
|
|
9
|
+
* A `content` view (D56) speaks of its one state — with content — and renders
|
|
10
|
+
* its entries, folded, each under its anchor. A line whose every word is in
|
|
11
|
+
* here is derived without a model; a word outside it needs a hole — a custom
|
|
12
|
+
* view.
|
|
11
13
|
*/
|
|
12
14
|
import { type OgygieError } from "@ogygie/core/errors";
|
|
13
15
|
import { type ScreenParseResult } from "./screen-grammar.js";
|
|
@@ -21,6 +23,8 @@ export declare const READ_STATES: readonly ["with:chargement", "with:erreur", "w
|
|
|
21
23
|
export declare const READ_RENDERS: readonly ["squelette", "bandeau d'erreur", "explication d'accès", "état vide", "lignes"];
|
|
22
24
|
export declare const FORM_STATES: readonly ["with:formulaire valide", "with:formulaire invalide", "with:écriture refusée"];
|
|
23
25
|
export declare const FORM_RENDERS: readonly ["phrases d'erreur", "rien n'est écrit", "bandeau d'erreur"];
|
|
26
|
+
export declare const CONTENT_STATES: readonly ["with:contenu"];
|
|
27
|
+
export declare const CONTENT_RENDERS: readonly ["entrées", "repliées"];
|
|
24
28
|
export declare function screenCaseKey(polarity: "with" | "without", phrase: string): string;
|
|
25
29
|
/** What a line may say of a view — the whole screen speaks like its primary read view. */
|
|
26
30
|
export declare function screenVocabularyOf(view: View | null, block: ScreenBlock, sortableFields?: readonly string[]): ScreenVocabulary;
|
|
@@ -6,10 +6,14 @@ import type { HoleLibraryEntry } from "./hole-shape.js";
|
|
|
6
6
|
import { type ManifestGeneration } from "./manifest-generation.schema.js";
|
|
7
7
|
import { type DeclaredManifestEntry } from "./emit/manifest-entry.js";
|
|
8
8
|
import type { SchemaModels } from "./zod-introspection.js";
|
|
9
|
+
import { type ExtensionContribution } from "./emit/screen/extensions.js";
|
|
10
|
+
import type { GeneratedFile } from "./generate-feature.js";
|
|
9
11
|
import type { ScreenBlock } from "./screen-block.schema.js";
|
|
10
12
|
import type { ConsumedFeatureCheckInput } from "./screen-checks.js";
|
|
11
13
|
import { type ParsedScreenBehavior } from "./screen-grammar.js";
|
|
12
14
|
export declare const GENERATED_DRIFT = "OGYGIE_GENERATED_DRIFT";
|
|
15
|
+
/** D56: an extension point declared at the manifest whose host folder never imports its registry. */
|
|
16
|
+
export declare const EXTENSION_POINT_UNWIRED = "OGYGIE_EXTENSION_POINT_UNWIRED";
|
|
13
17
|
/** The version every emitted header carries — the package's own, so a bump of the lockstep is a drift every product regenerates (D27). */
|
|
14
18
|
export declare const GENERATOR_VERSION: string;
|
|
15
19
|
/**
|
|
@@ -22,6 +26,8 @@ export declare function normalizeForComparison(text: string): string;
|
|
|
22
26
|
export declare const GENERATED_HEADER: RegExp;
|
|
23
27
|
/** A feature of the map that owns a spec: where its code lives. */
|
|
24
28
|
export interface SpecOwnerFolder {
|
|
29
|
+
/** The feature id of the map entry (D56: a mounted screen contributes under it). */
|
|
30
|
+
featureId?: string;
|
|
25
31
|
specPath: string;
|
|
26
32
|
/** Folder of the feature, relative to the repo root. */
|
|
27
33
|
folder: string;
|
|
@@ -53,7 +59,51 @@ export interface VerifyGenerationInput {
|
|
|
53
59
|
apiAppPath?: string;
|
|
54
60
|
/** D41: resolves the features a screen block consumes (their block, schemas and routes); absent, no screen is regenerated. */
|
|
55
61
|
loadConsumed?: (block: ScreenBlock) => Promise<ReadonlyMap<string, LoadedConsumedForVerify>>;
|
|
62
|
+
/** D56: the front apps of the map (name → path), where the extension points' host folders live; absent, no registry is verified. */
|
|
63
|
+
frontApps?: ReadonlyArray<{
|
|
64
|
+
name: string;
|
|
65
|
+
path: string;
|
|
66
|
+
}>;
|
|
56
67
|
}
|
|
68
|
+
/** D56: the repo-relative host folder of an extension point — the one front app, or the one the point names. */
|
|
69
|
+
export declare function extensionHostAppPathOf(point: {
|
|
70
|
+
app?: string | undefined;
|
|
71
|
+
}, frontApps: ReadonlyArray<{
|
|
72
|
+
name: string;
|
|
73
|
+
path: string;
|
|
74
|
+
}>): string | null;
|
|
75
|
+
/** D56: the registry of every declared point — one file each, empty when nothing mounts — from the screens that mount (their feature id, folder and mount id). */
|
|
76
|
+
export declare function extensionRegistriesOf(input: {
|
|
77
|
+
points: ReadonlyArray<{
|
|
78
|
+
id: string;
|
|
79
|
+
kind: "tab";
|
|
80
|
+
folder: string;
|
|
81
|
+
app?: string | undefined;
|
|
82
|
+
}>;
|
|
83
|
+
frontApps: ReadonlyArray<{
|
|
84
|
+
name: string;
|
|
85
|
+
path: string;
|
|
86
|
+
}>;
|
|
87
|
+
mounted: ReadonlyArray<ExtensionContribution & {
|
|
88
|
+
point: string;
|
|
89
|
+
}>;
|
|
90
|
+
generatorVersion: string;
|
|
91
|
+
}): Array<{
|
|
92
|
+
point: {
|
|
93
|
+
id: string;
|
|
94
|
+
kind: "tab";
|
|
95
|
+
folder: string;
|
|
96
|
+
app?: string | undefined;
|
|
97
|
+
};
|
|
98
|
+
hostFolder: string;
|
|
99
|
+
file: GeneratedFile;
|
|
100
|
+
} | {
|
|
101
|
+
point: {
|
|
102
|
+
id: string;
|
|
103
|
+
};
|
|
104
|
+
hostFolder: null;
|
|
105
|
+
file: null;
|
|
106
|
+
}>;
|
|
57
107
|
export interface VerifiedSpec {
|
|
58
108
|
specPath: string;
|
|
59
109
|
generated: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ogygie/generate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
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.
|
|
32
|
+
"@ogygie/core": "0.24.0",
|
|
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.
|
|
41
|
+
"@ogygie/ui": "0.24.0",
|
|
42
42
|
"@starter/api": "0.1.0",
|
|
43
43
|
"@tanstack/react-query": "^5.102.2",
|
|
44
44
|
"@tanstack/react-router": "^1.170.32",
|