@ogygie/generate 0.32.0 → 0.33.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/cli/run.js CHANGED
@@ -37,7 +37,7 @@ import {
37
37
  throwOgygieError,
38
38
  verifyGeneration,
39
39
  writeExtensionRegistries
40
- } from "../chunk-KQ7RGIBV.js";
40
+ } from "../chunk-MI725PT3.js";
41
41
 
42
42
  // src/cli/check-command.ts
43
43
  import { existsSync, readFileSync, readdirSync } from "node:fs";
package/dist/emit/db.d.ts CHANGED
@@ -17,7 +17,6 @@
17
17
  * empty marker pair the bench fails on.
18
18
  */
19
19
  import type { FeaturePlan, TablePlan } from "./plan.js";
20
- /** Every table of the feature, resource first, then slots, then the references with a schema — adopted ones included (D36). */
21
20
  export declare function tablesOf(plan: FeaturePlan): TablePlan[];
22
21
  /** The tables the generator CREATES and owns: every table of the feature but the adopted ones (D36). */
23
22
  export declare function ownedTables(plan: FeaturePlan): TablePlan[];
@@ -25,4 +25,6 @@ export declare function holeDeclaresClock(hole: {
25
25
  effects?: readonly string[];
26
26
  };
27
27
  }): boolean;
28
+ /** `sumInPeriod` → `SUM_IN_PERIOD`: the constant name of a hole's examples in factories.gen.ts. */
29
+ export declare function constantNameOf(identifier: string): string;
28
30
  export declare function emitHoleTests(plan: FeaturePlan): string;
@@ -10,8 +10,10 @@
10
10
  * `OUTPUT INSERTED` / `OUTPUT DELETED` so a miss is an empty result, a limit
11
11
  * is `OFFSET … FETCH`.
12
12
  */
13
- import type { ColumnPlan, FeaturePlan, SlotPlan } from "../plan.js";
14
- export type StatementKind = "selectAll" | "selectByKey" | "selectByUniqueKey" | "countByColumn" | "countByKey" | "insert" | "updateByKey" | "selectSlot" | "insertSlot" | "updateSlotById" | "updateSlotByKey" | "deleteById" | "deleteSlotByKey" | "deleteByKey" | "selectView";
13
+ import type { ColumnPlan, FeaturePlan, SharedTablePlan, SlotPlan } from "../plan.js";
14
+ export type StatementKind = "selectAll" | "selectByKey" | "selectByUniqueKey" | "countByColumn" | "countByKey" | "insert" | "updateByKey" | "selectSlot" | "insertSlot" | "updateSlotById" | "updateSlotByKey" | "deleteById" | "deleteSlotByKey" | "deleteByKey" | "selectView"
15
+ /** D60: the rows of a shared table, read as a view is — the stub answers what the test arranged. */
16
+ | "selectShared";
15
17
  export interface Statement {
16
18
  /** Key in the exported `SQL` constant, e.g. `selectListAll`. */
17
19
  key: string;
@@ -51,5 +53,7 @@ export declare function selectList(plan: FeaturePlan, prefix: string, columns: r
51
53
  export declare function mssqlStatements(plan: FeaturePlan): Statement[];
52
54
  /** The statement of a key, or a loud failure — the emitters never guess a name. */
53
55
  export declare function statementNamed(statements: readonly Statement[], key: string): Statement;
56
+ /** D60: the shared table plan a statement reads, when it reads one. */
57
+ export declare function sharedTableOf(plan: FeaturePlan, statement: Statement): SharedTablePlan | null;
54
58
  /** The slot plan a statement belongs to. */
55
59
  export declare function slotOf(plan: FeaturePlan, statement: Statement): SlotPlan | null;
@@ -9,7 +9,7 @@
9
9
  * this runs; the plan therefore throws only on an invariant `check` already
10
10
  * guarantees.
11
11
  */
12
- import { type DeriveHole, type GenerationBlock, type HttpOperation, type ProjectHole, type TransformHole, type ValidateHole, type ViewHole } from "../generation-block.schema.js";
12
+ import { type DeriveHole, type GenerationBlock, type HttpOperation, type ProjectHole, type TransformHole, type ValidateHole, type ViewHole, type AggregateHole } from "../generation-block.schema.js";
13
13
  import type { ParsedBehavior } from "../behavior-grammar.js";
14
14
  import { type GenerationTarget, type GuardRegistryEntry, type ManifestGeneration, type ValidationMode } from "../manifest-generation.schema.js";
15
15
  import { type HttpOperationKind, type Motifs } from "../motif-catalog.js";
@@ -142,7 +142,46 @@ export interface ViewHolePlan {
142
142
  columns: ColumnPlan[];
143
143
  from: string[];
144
144
  }
145
- export type HolePlan = ValidateHolePlan | TransformHolePlan | DeriveHolePlan | ProjectHolePlan | ViewHolePlan;
145
+ /** D60: a shared table of the product an aggregate hole reads, typed by the schema of its rows — a read-only mapping, never created nor written. */
146
+ export interface SharedTablePlan {
147
+ sqlName: string;
148
+ /** Drizzle constant / stub key of its read-only mapping, e.g. `siteTargets`. */
149
+ tsName: string;
150
+ schemaName: string;
151
+ /** The row type of the schema, e.g. `SiteTarget` — what the hole receives. */
152
+ typeName: string;
153
+ /** The database row type, e.g. `SiteTargetRow`. */
154
+ rowType: string;
155
+ model: FieldModel;
156
+ fields: ResourceFieldPlan[];
157
+ /** Tenant column, then the fields of the schema. */
158
+ columns: ColumnPlan[];
159
+ }
160
+ /** D60: computes ONE root field of an aggregate read's output from the input pick, the resource rows and the shared rows it names. */
161
+ export interface AggregateHolePlan {
162
+ kind: "aggregate";
163
+ id: string;
164
+ hole: AggregateHole;
165
+ reads: string[];
166
+ /** The input type the reads are picked from, e.g. `ReadBalanceInput`. */
167
+ inputTypeName: string;
168
+ /** The resource type the rows carry, e.g. `Movement`. */
169
+ resourceTypeName: string;
170
+ /** The read's output type, e.g. `ReadBalanceOutput` — the hole answers `Output["<writes>"]`. */
171
+ outputTypeName: string;
172
+ writes: string;
173
+ /** The model of the written output field — the type the hole answers. */
174
+ writesModel: FieldModel;
175
+ /** The shared tables the hole receives, in `from` order. */
176
+ from: SharedTablePlan[];
177
+ accepts: Array<{
178
+ input: Record<string, unknown>;
179
+ rows: Record<string, unknown>[];
180
+ shared: Record<string, Record<string, unknown>[]>;
181
+ value: unknown;
182
+ }>;
183
+ }
184
+ export type HolePlan = ValidateHolePlan | TransformHolePlan | DeriveHolePlan | ProjectHolePlan | ViewHolePlan | AggregateHolePlan;
146
185
  /** D36: one replayable step of the bounded ALTER an adopted table receives. */
147
186
  export type AlterPlan = {
148
187
  kind: "dropCheck";
@@ -294,6 +333,10 @@ export interface OperationPlan {
294
333
  deriveHoles: DeriveHolePlan[];
295
334
  projectHoles: ProjectHolePlan[];
296
335
  viewHoles: ViewHolePlan[];
336
+ /** D60: the aggregate holes of a read, in declared order. */
337
+ aggregateHoles: AggregateHolePlan[];
338
+ /** D60: an aggregate read — no collection of the resource, every root field an echo or a hole's value; `outputField` is then "". */
339
+ report: boolean;
297
340
  order: OrderPlan | null;
298
341
  limit: number | null;
299
342
  range: string | null;
@@ -387,6 +430,8 @@ export interface FeaturePlan {
387
430
  } | null;
388
431
  slots: SlotPlan[];
389
432
  references: ReferencePlan[];
433
+ /** D60: the shared tables declared with a schema, read by the aggregate holes. */
434
+ sharedTables: SharedTablePlan[];
390
435
  /** D31: the four motifs of lot 3, as instantiated. */
391
436
  signed: {
392
437
  by: string;
@@ -254,6 +254,16 @@ export declare const ReferenceSchema: z.ZodObject<{
254
254
  countField: z.ZodOptional<z.ZodString>;
255
255
  }, z.core.$strict>;
256
256
  export type Reference = z.infer<typeof ReferenceSchema>;
257
+ /** D60: a table of the product outside the block, read by an aggregate hole, typed by the schema of its rows — declared, never created nor written. */
258
+ export declare const SharedTableSchema: z.ZodObject<{
259
+ name: z.ZodString;
260
+ schema: z.ZodString;
261
+ }, z.core.$strict>;
262
+ /** A shared table: its name alone (read by views only), or its name with the schema of its rows (D60). */
263
+ export declare const SharedTableDeclarationSchema: z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
264
+ name: z.ZodString;
265
+ schema: z.ZodString;
266
+ }, z.core.$strict>]>;
257
267
  export declare const ContractsSchema: z.ZodObject<{
258
268
  module: z.ZodString;
259
269
  resource: z.ZodOptional<z.ZodString>;
@@ -379,8 +389,19 @@ export declare const ContractsSchema: z.ZodObject<{
379
389
  schema: z.ZodOptional<z.ZodString>;
380
390
  countField: z.ZodOptional<z.ZodString>;
381
391
  }, z.core.$strict>>>;
382
- sharedTables: z.ZodOptional<z.ZodArray<z.ZodString>>;
392
+ sharedTables: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
393
+ name: z.ZodString;
394
+ schema: z.ZodString;
395
+ }, z.core.$strict>]>>>;
383
396
  }, z.core.$strict>;
397
+ /** D60: a shared table whose rows an aggregate hole receives — its name and the schema of one row. */
398
+ export type SharedTable = z.infer<typeof SharedTableSchema>;
399
+ export type SharedTableDeclaration = z.infer<typeof SharedTableDeclarationSchema>;
400
+ export declare function sharedTableNameOf(declaration: SharedTableDeclaration): string;
401
+ /** The shared tables declared with a schema — the only ones an aggregate hole may read (D60). */
402
+ export declare function typedSharedTablesOf(contracts: {
403
+ sharedTables?: readonly SharedTableDeclaration[] | undefined;
404
+ }): SharedTable[];
384
405
  export declare const OrderBySchema: z.ZodUnion<readonly [z.ZodString, z.ZodTuple<[z.ZodString, z.ZodEnum<{
385
406
  asc: "asc";
386
407
  desc: "desc";
@@ -770,6 +791,23 @@ export declare const ViewHoleSchema: z.ZodObject<{
770
791
  value: z.ZodUnknown;
771
792
  }, z.core.$strict>;
772
793
  }, z.core.$strict>;
794
+ /** D60: computes ONE root field of an aggregate read's output from the input and the rows — pure, a value, never a refusal. */
795
+ export declare const AggregateHoleSchema: z.ZodObject<{
796
+ id: z.ZodString;
797
+ kind: z.ZodLiteral<"aggregate">;
798
+ reads: z.ZodDefault<z.ZodArray<z.ZodString>>;
799
+ from: z.ZodOptional<z.ZodArray<z.ZodString>>;
800
+ writes: z.ZodString;
801
+ effects: z.ZodDefault<z.ZodArray<z.ZodLiteral<"clock">>>;
802
+ examples: z.ZodObject<{
803
+ accept: z.ZodArray<z.ZodObject<{
804
+ input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
805
+ rows: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
806
+ shared: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>>;
807
+ value: z.ZodUnknown;
808
+ }, z.core.$strict>>;
809
+ }, z.core.$strict>;
810
+ }, z.core.$strict>;
773
811
  export declare const HoleSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
774
812
  id: z.ZodString;
775
813
  kind: z.ZodLiteral<"validate">;
@@ -848,6 +886,21 @@ export declare const HoleSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
848
886
  rows: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
849
887
  value: z.ZodUnknown;
850
888
  }, z.core.$strict>;
889
+ }, z.core.$strict>, z.ZodObject<{
890
+ id: z.ZodString;
891
+ kind: z.ZodLiteral<"aggregate">;
892
+ reads: z.ZodDefault<z.ZodArray<z.ZodString>>;
893
+ from: z.ZodOptional<z.ZodArray<z.ZodString>>;
894
+ writes: z.ZodString;
895
+ effects: z.ZodDefault<z.ZodArray<z.ZodLiteral<"clock">>>;
896
+ examples: z.ZodObject<{
897
+ accept: z.ZodArray<z.ZodObject<{
898
+ input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
899
+ rows: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
900
+ shared: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>>;
901
+ value: z.ZodUnknown;
902
+ }, z.core.$strict>>;
903
+ }, z.core.$strict>;
851
904
  }, z.core.$strict>], "kind">;
852
905
  export type Hole = z.infer<typeof HoleSchema>;
853
906
  export type ValidateHole = Extract<Hole, {
@@ -865,6 +918,9 @@ export type ProjectHole = Extract<Hole, {
865
918
  export type ViewHole = Extract<Hole, {
866
919
  kind: "view";
867
920
  }>;
921
+ export type AggregateHole = Extract<Hole, {
922
+ kind: "aggregate";
923
+ }>;
868
924
  /** The accept examples of a validate hole, always as a list (first = the valid input overlay). */
869
925
  export declare function acceptExamplesOf(hole: ValidateHole): Record<string, unknown>[];
870
926
  /** The kinds of operation each kind of hole may serve (D22, D32). */
@@ -997,7 +1053,10 @@ export declare const GenerationBlockSchema: z.ZodObject<{
997
1053
  schema: z.ZodOptional<z.ZodString>;
998
1054
  countField: z.ZodOptional<z.ZodString>;
999
1055
  }, z.core.$strict>>>;
1000
- sharedTables: z.ZodOptional<z.ZodArray<z.ZodString>>;
1056
+ sharedTables: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
1057
+ name: z.ZodString;
1058
+ schema: z.ZodString;
1059
+ }, z.core.$strict>]>>>;
1001
1060
  }, z.core.$strict>;
1002
1061
  operations: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
1003
1062
  kind: z.ZodLiteral<"read">;
@@ -1244,6 +1303,21 @@ export declare const GenerationBlockSchema: z.ZodObject<{
1244
1303
  rows: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1245
1304
  value: z.ZodUnknown;
1246
1305
  }, z.core.$strict>;
1306
+ }, z.core.$strict>, z.ZodObject<{
1307
+ id: z.ZodString;
1308
+ kind: z.ZodLiteral<"aggregate">;
1309
+ reads: z.ZodDefault<z.ZodArray<z.ZodString>>;
1310
+ from: z.ZodOptional<z.ZodArray<z.ZodString>>;
1311
+ writes: z.ZodString;
1312
+ effects: z.ZodDefault<z.ZodArray<z.ZodLiteral<"clock">>>;
1313
+ examples: z.ZodObject<{
1314
+ accept: z.ZodArray<z.ZodObject<{
1315
+ input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1316
+ rows: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1317
+ shared: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>>;
1318
+ value: z.ZodUnknown;
1319
+ }, z.core.$strict>>;
1320
+ }, z.core.$strict>;
1247
1321
  }, z.core.$strict>], "kind">>>;
1248
1322
  }, z.core.$strict>;
1249
1323
  export type GenerationBlock = z.infer<typeof GenerationBlockSchema>;
@@ -10,6 +10,7 @@ export declare const HoleShapeSchema: z.ZodObject<{
10
10
  derive: "derive";
11
11
  project: "project";
12
12
  view: "view";
13
+ aggregate: "aggregate";
13
14
  "custom-view": "custom-view";
14
15
  }>;
15
16
  reads: z.ZodArray<z.ZodString>;
@@ -48,6 +49,7 @@ export declare const HoleLibraryEntrySchema: z.ZodObject<{
48
49
  derive: "derive";
49
50
  project: "project";
50
51
  view: "view";
52
+ aggregate: "aggregate";
51
53
  "custom-view": "custom-view";
52
54
  }>;
53
55
  reads: z.ZodArray<z.ZodString>;
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  export { bindBehavior, BEHAVIOR_UNBOUND, type BindingContext, type BindingOutcome, } from "./behavior-binding.js";
8
8
  export { isLiteralValue, parseBehavior, parseExpectation, OPENING_KEYWORD, type BehaviorCase, type BehaviorExpectation, type BehaviorOutcome, type BehaviorParseResult, type ParsedBehavior, } from "./behavior-grammar.js";
9
9
  export { checkSpec, type CheckSpecInput, type CheckSpecReport, type CheckTally, type CheckedBehavior } from "./check-spec.js";
10
- export { GENERATION_BLOCK_INVALID, GENERATION_BLOCK_KEYS, GenerationBlockSchema, HoleSchema, OperationSchema, SUCCESS_STATUS_BY_KIND, HOLE_KIND_SERVES, METHOD_BY_KIND, acceptExamplesOf, isHttpOperation, isKeyedOperation, isWriteOperation, parseGenerationBlock, pathParametersOf, validateGenerationBlock, type BlockLocation, type DeriveHole, type GenerationBlock, type GenerationBlockParse, type Hole, type HttpOperation, type KeyedOperation, type Operation, type ProjectHole, type ReadOperation, type Reference, type Slot, type TransformHole, type ValidateHole, type ViewHole, type WriteOperation, AdoptedColumnSchema, AlterStepSchema, TableDeclarationSchema, TableRefSchema, parentKeyFieldOf, tableNameOf, tableRefOf, type AdoptedColumn, type AlterStep, type TableDeclaration, type TableRef, } from "./generation-block.schema.js";
10
+ export { GENERATION_BLOCK_INVALID, GENERATION_BLOCK_KEYS, GenerationBlockSchema, HoleSchema, OperationSchema, SUCCESS_STATUS_BY_KIND, HOLE_KIND_SERVES, METHOD_BY_KIND, acceptExamplesOf, isHttpOperation, isKeyedOperation, isWriteOperation, parseGenerationBlock, pathParametersOf, validateGenerationBlock, type BlockLocation, type DeriveHole, type GenerationBlock, type GenerationBlockParse, type Hole, type HttpOperation, type KeyedOperation, type Operation, type ProjectHole, type ReadOperation, type Reference, type Slot, type TransformHole, type ValidateHole, type ViewHole, type AggregateHole, type SharedTable, type SharedTableDeclaration, AggregateHoleSchema, SharedTableSchema, sharedTableNameOf, typedSharedTablesOf, type WriteOperation, AdoptedColumnSchema, AlterStepSchema, TableDeclarationSchema, TableRefSchema, parentKeyFieldOf, tableNameOf, tableRefOf, type AdoptedColumn, type AlterStep, type TableDeclaration, type TableRef, } from "./generation-block.schema.js";
11
11
  export { EMPTY_GUARD_REGISTRY, GenerationTargetSchema, GuardDenialSchema, GuardRegistryEntrySchema, GuardVariantSchema, ManifestGenerationSchema, ModuleExportSchema, ModuleTypeSchema, caseKeyOfPhrase, readManifestGeneration, type GenerationTarget, type GuardDenial, type GuardRegistry, type GuardRegistryEntry, type GuardVariant, type ManifestGeneration, type ModuleExport, type ModuleType, contractViolationCodeOf, validationOf, type ValidationMode, } from "./manifest-generation.schema.js";
12
12
  export { checkBlockAgainstSchemas, type SchemaCheckInput } from "./schema-checks.js";
13
13
  export { fieldNamesOf, hasSameFieldsAs, invalidSampleOf, modelOf, modelsOfModule, sampleOf, type FieldModel, type InvalidSampleResult, type SampleResult, type SchemaModel, type SchemaModels, } from "./zod-introspection.js";
@@ -15,7 +15,7 @@ export { HTTP_OPERATION_KINDS, KEYED_OPERATION_KINDS, MOTIF_CATALOG, MOTIF_NAMES
15
15
  export { extractBehaviorParagraphs, extractGenerationSection, splitTopLevelSections, type BehaviorParagraph, type GenerationSectionLookup, type SpecSection, } from "./spec-sections.js";
16
16
  export { CONTRACT_VALIDATION_FAILED, vocabularyOf, type OperationVocabulary } from "./vocabulary.js";
17
17
  export { GENERATED_DRIFT, GENERATED_HEADER, GENERATOR_VERSION, verifyGeneration, type GeneratedFileClaim, type SpecOwnerFolder, type VerifiedSpec, type VerifyGenerationInput, type VerifyGenerationReport, } from "./verify-generation.js";
18
- export { buildFeaturePlan, type DeriveHolePlan, type ElementPlan, type FeaturePlan, type FeaturePlanInput, type HolePlan, type OperationPlan, type ProjectHolePlan, type ReferencePlan, type SlotPlan, type TablePlan, type TransformHolePlan, type ValidateHolePlan, type ViewHolePlan, } from "./emit/plan.js";
18
+ export { buildFeaturePlan, type AggregateHolePlan, type SharedTablePlan, type DeriveHolePlan, type ElementPlan, type FeaturePlan, type FeaturePlanInput, type HolePlan, type OperationPlan, type ProjectHolePlan, type ReferencePlan, type SlotPlan, type TablePlan, type TransformHolePlan, type ValidateHolePlan, type ViewHolePlan, } from "./emit/plan.js";
19
19
  export { GENERATED_FEATURE_FILES, generateFeature, migrationFileNameFor, type GenerateOptions, type GeneratedFile, } from "./generate-feature.js";
20
20
  export { HOLES_LIBRARY_DIR, HoleLibraryEntrySchema, HoleProvenanceSchema, HoleShapeSchema, bodyTemplateOf, instantiateBody, libraryEntryOf, libraryFileNameOf, loadHolesLibrary, neighboursOfShape, normalizedTypeOf, shapeHashOf, shapeOfHole, signatureHashOf, signatureTextOf, type HoleLibraryEntry, type HoleProvenance, type HoleShape, } from "./hole-shape.js";
21
21
  export { bodyLinesFor, effectParameterLines, holeSignatureLines, isAsyncHole, parseLogicSections, type LogicSection } from "./emit/runtime.js";
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  AdoptedColumnSchema,
3
+ AggregateHoleSchema,
3
4
  AlterStepSchema,
4
5
  BEHAVIOR_UNBOUND,
5
6
  BODY_BANS,
@@ -58,6 +59,7 @@ import {
58
59
  SUCCESS_STATUS_BY_KIND,
59
60
  ScreenBlockSchema,
60
61
  ScreenHoleSchema,
62
+ SharedTableSchema,
61
63
  TableDeclarationSchema,
62
64
  TableRefSchema,
63
65
  VIEW_BODY_BANS,
@@ -173,6 +175,7 @@ import {
173
175
  serializeManifest,
174
176
  shapeHashOf,
175
177
  shapeOfHole,
178
+ sharedTableNameOf,
176
179
  signatureHashOf,
177
180
  signatureTextOf,
178
181
  specGeneratedStart,
@@ -181,6 +184,7 @@ import {
181
184
  tableNameOf,
182
185
  tableRefOf,
183
186
  tablesOf,
187
+ typedSharedTablesOf,
184
188
  validateGenerationBlock,
185
189
  validateScreenBlock,
186
190
  validationOf,
@@ -189,7 +193,7 @@ import {
189
193
  viewCandidateTextOf,
190
194
  viewExampleTestName,
191
195
  vocabularyOf
192
- } from "./chunk-KQ7RGIBV.js";
196
+ } from "./chunk-MI725PT3.js";
193
197
 
194
198
  // src/prompt-card.ts
195
199
  var phrase = (caseKey) => caseKey.replace(/^with:/u, "avec ").replace(/^without:/u, "sans ");
@@ -225,10 +229,10 @@ function generationGrammarCard(generation) {
225
229
  "Une spec qui porte `# G\xE9n\xE9ration` est G\xC9N\xC9R\xC9E : routes, base, tests et \xE9crans se d\xE9rivent sans mod\xE8le ; ce qui se pense devient un TROU typ\xE9, rempli ensuite. Alors ses `# Comportements` s'\xE9crivent dans la GRAMMAIRE FERM\xC9E ci-dessous, jamais au format \xC9TANT DONN\xC9 / QUAND / ALORS. Sans `# G\xE9n\xE9ration`, garde le format en trois lignes.",
226
230
  "",
227
231
  `Bloc d'une FEATURE API \u2014 un seul bloc \`\`\`yaml, cl\xE9s ${GENERATION_BLOCK_KEYS.map((key) => `\`${key}\``).join(", ")} :`,
228
- "- `contracts: { module: ./schemas.ts, resource: <Nom>Schema, table: snake_case, key?: champ (cl\xE9 m\xE9tier des \xE9critures par cl\xE9), columns?: { champ: \"type\" }, sharedTables?: [snake_case] }` \u2014 les sch\xE9mas sont des NOMS (suffixe Schema), jamais du code ; `columns` est FACULTATIF et ne force un type que quand le sch\xE9ma ne suffit pas, parmi la liste ferm\xE9e `text`, `integer`, `bigint`, `boolean`, `date`, `timestamptz`, `uuid` ; `sharedTables` nomme les tables du produit HORS du bloc qu'un trou `view` lit (`from`) \u2014 d\xE9clar\xE9es, jamais cr\xE9\xE9es ni \xE9crites ; sans cette d\xE9claration, la vue est refus\xE9e, `jsonb`, `numeric(p,s)`, `varchar(n)`, `char(n)` (jamais un type SQL Server, jamais `not null` : la cible traduit) ; `id` n'est jamais une cl\xE9 ni une colonne d\xE9clar\xE9e.",
229
- `- \`operations\` : ${operationKinds.join(", ")} ; chaque op\xE9ration : \`{ id: camelCase, kind, method, path: /api/\u2026, guard: <un guard ci-dessous>, input: <Nom>Schema, output: <Nom>Schema, holes?: [ids], respondsWith?: <lecture du bloc>, invalidates?: [racine-kebab] }\` ; une \xE9criture par cl\xE9 ajoute \`by: <cl\xE9>\` et \`notFound: CODE\`, son path finit par \`/:<cl\xE9>\` ; une \`read\` ne prend AUCUN param\xE8tre de chemin : le filtre (une soci\xE9t\xE9, une p\xE9riode) passe par les champs de son \`input\`.`,
232
+ "- `contracts: { module: ./schemas.ts, resource: <Nom>Schema, table: snake_case, key?: champ (cl\xE9 m\xE9tier des \xE9critures par cl\xE9), columns?: { champ: \"type\" }, sharedTables?: [snake_case | { name: snake_case, schema: <Nom>Schema }] }` \u2014 les sch\xE9mas sont des NOMS (suffixe Schema), jamais du code ; `columns` est FACULTATIF et ne force un type que quand le sch\xE9ma ne suffit pas, parmi la liste ferm\xE9e `text`, `integer`, `bigint`, `boolean`, `date`, `timestamptz`, `uuid` ; `sharedTables` nomme les tables du produit HORS du bloc qu'un trou `view` lit (`from`) \u2014 d\xE9clar\xE9es, jamais cr\xE9\xE9es ni \xE9crites ; sans cette d\xE9claration, la vue est refus\xE9e, `jsonb`, `numeric(p,s)`, `varchar(n)`, `char(n)` (jamais un type SQL Server, jamais `not null` : la cible traduit) ; `id` n'est jamais une cl\xE9 ni une colonne d\xE9clar\xE9e.",
233
+ `- \`operations\` : ${operationKinds.join(", ")} ; chaque op\xE9ration : \`{ id: camelCase, kind, method, path: /api/\u2026, guard: <un guard ci-dessous>, input: <Nom>Schema, output: <Nom>Schema, holes?: [ids], respondsWith?: <lecture du bloc>, invalidates?: [racine-kebab] }\` ; une \xE9criture par cl\xE9 ajoute \`by: <cl\xE9>\` et \`notFound: CODE\`, son path finit par \`/:<cl\xE9>\` ; une \`read\` ne prend AUCUN param\xE8tre de chemin : le filtre (une soci\xE9t\xE9, une p\xE9riode) passe par les champs de son \`input\`. Deux formes de \`read\` (D60) : la LISTE, dont la sortie porte UNE collection de la ressource (\`{ <collection>: [\xE9l\xE9ments], ...\xE9chos de l'entr\xE9e }\`) ; la LECTURE AGR\xC9G\xC9E \u2014 un rapport, des totaux, une s\xE9rie, un top \u2014 dont la sortie ne porte AUCUNE collection et dont chaque champ est un \xE9cho de l'entr\xE9e ou le \`writes\` d'un trou \`aggregate\` : c'est la forme d'un chiffre sur une p\xE9riode, jamais une vue.`,
230
234
  `- \`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: {}\`.`,
231
- `- \`holes\` : ${holeKinds.join(", ")} ; un trou \`view\` = \`{ id, kind: view, name: snake_case, from: [tables ou vues lues], writes: <champ de la ressource>, examples: { rows: [{\u2026}], value } }\` \u2014 \`from\` ne lit que la table de la ressource, une table de slot ou de r\xE9f\xE9rence, une autre vue du bloc ou une table de \`contracts.sharedTables\` ; 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).`,
235
+ `- \`holes\` : ${holeKinds.join(", ")} ; un trou \`view\` = \`{ id, kind: view, name: snake_case, from: [tables ou vues lues], writes: <champ de la ressource>, examples: { rows: [{\u2026}], value } }\` \u2014 \`from\` ne lit que la table de la ressource, une table de slot ou de r\xE9f\xE9rence, une autre vue du bloc ou une table de \`contracts.sharedTables\` ; 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 } } }\` ; un \`transform\` (une ligne d'import, D22) = \`{ id, kind: transform, from: text/csv, columns: [colonnes lues], effects: [], error?: CODE, examples: { accept: { input, output }, reject: { input, line? } } }\` ; un \`project\` (la forme de la sortie d'une \`read\` selon l'appelant, D32) = \`{ id, kind: project, reads: [champs du CONTEXTE du guard \u2014 un r\xF4le, un e-mail \u2014 jamais de l'entr\xE9e], writes: [chemins de la sortie, ex. rows[].field], examples: { accept: [{ context: { <champ du contexte>: \u2026 }, input, output }] } }\` \u2014 chaque exemple \`accept\` d'un \`project\` porte son \`context\`, OBLIGATOIRE ; un \`aggregate\` (un champ d'une lecture agr\xE9g\xE9e calcul\xE9 sur les lignes sous l'entr\xE9e, D60) = \`{ id, kind: aggregate, reads: [champs de l'ENTR\xC9E lus \u2014 une p\xE9riode, un filtre], from?: [tables partag\xE9es d\xE9clar\xE9es AVEC sch\xE9ma], writes: <champ racine de la sortie>, effects: [], examples: { accept: [{ input: { <reads>: \u2026 }, rows: [{ <champs de la ressource>: \u2026 }], shared?: { <table>: [{\u2026}] }, value: <la valeur attendue> }] } }\` \u2014 le trou re\xE7oit TOUTES les lignes et filtre lui-m\xEAme ; un \`view\` ne convient JAMAIS \xE0 un total sur une p\xE9riode : il remplit un champ de chaque ligne. Les exemples sont OBLIGATOIRES ; une date d'exemple se rapporte au 2026-01-01 (horloge fig\xE9e des tests).`,
232
236
  "",
233
237
  `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).`,
234
238
  "",
@@ -256,11 +260,25 @@ function generationGrammarCard(generation) {
256
260
  "```",
257
261
  "- \xC9TANT DONN\xC9 listAll avec lignes ALORS 200 et orders = lignes, ordre placedOn desc",
258
262
  "- \xC9TANT DONN\xC9 create avec entr\xE9e valide ALORS 201 et created.code = code d\xE9riv\xE9, liste \xE0 jour [hole:deriveOrderCode]",
259
- "- \xC9TANT DONN\xC9 create avec entr\xE9e invalide ALORS 400 CONTRACT_VALIDATION_FAILED et suggestedFix"
263
+ "- \xC9TANT DONN\xC9 create avec entr\xE9e invalide ALORS 400 CONTRACT_VALIDATION_FAILED et suggestedFix",
264
+ "",
265
+ "Exemple minimal d'une LECTURE AGR\xC9G\xC9E (D60) \u2014 un total sur une p\xE9riode, sans collection :",
266
+ "```yaml",
267
+ "contracts: { module: ./schemas.ts, resource: OrderSchema, table: orders, sharedTables: [{ name: yearly_goals, schema: YearlyGoalSchema }] }",
268
+ "operations:",
269
+ " - { id: readSummary, kind: read, method: GET, path: /api/orders/summary, guard: <guard>, input: ReadOrdersSummaryInputSchema, output: OrdersSummarySchema, holes: [countInPeriod, goalReached] }",
270
+ "motifs: {}",
271
+ "holes:",
272
+ ' - { id: countInPeriod, kind: aggregate, reads: [from, to], writes: orderCount, effects: [], examples: { accept: [{ input: { from: "2026-01-01", to: "2026-01-31" }, rows: [{ placedOn: "2026-01-10" }, { placedOn: "2026-02-03" }], value: 1 }] } }',
273
+ ' - { id: goalReached, kind: aggregate, reads: [to], from: [yearly_goals], writes: goalReached, effects: [], examples: { accept: [{ input: { to: "2026-01-31" }, rows: [{ placedOn: "2026-01-10" }], shared: { yearly_goals: [{ year: 2026, orderGoal: 1 }] }, value: true }] } }',
274
+ "```",
275
+ "- \xC9TANT DONN\xC9 readSummary avec lignes ALORS 200 et orderCount = orderCount d\xE9riv\xE9 [hole:countInPeriod]",
276
+ "- \xC9TANT DONN\xC9 readSummary sans ligne ALORS 200 et orderCount = 0"
260
277
  ].join("\n");
261
278
  }
262
279
  export {
263
280
  AdoptedColumnSchema,
281
+ AggregateHoleSchema,
264
282
  AlterStepSchema,
265
283
  BEHAVIOR_UNBOUND,
266
284
  BODY_BANS,
@@ -316,6 +334,7 @@ export {
316
334
  SUCCESS_STATUS_BY_KIND,
317
335
  ScreenBlockSchema,
318
336
  ScreenHoleSchema,
337
+ SharedTableSchema,
319
338
  TableDeclarationSchema,
320
339
  TableRefSchema,
321
340
  VIEW_BODY_BANS,
@@ -432,6 +451,7 @@ export {
432
451
  serializeManifest,
433
452
  shapeHashOf,
434
453
  shapeOfHole,
454
+ sharedTableNameOf,
435
455
  signatureHashOf,
436
456
  signatureTextOf,
437
457
  specGeneratedStart,
@@ -440,6 +460,7 @@ export {
440
460
  tableNameOf,
441
461
  tableRefOf,
442
462
  tablesOf,
463
+ typedSharedTablesOf,
443
464
  validateGenerationBlock,
444
465
  validateScreenBlock,
445
466
  validationOf,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ogygie/generate",
3
- "version": "0.32.0",
3
+ "version": "0.33.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.0",
32
+ "@ogygie/core": "0.33.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.32.0",
41
+ "@ogygie/ui": "0.33.0",
42
42
  "@starter/api": "0.1.0",
43
43
  "@tanstack/react-query": "^5.102.2",
44
44
  "@tanstack/react-router": "^1.170.32",