@deterministic-code/datasources-cli 0.1.10 → 0.1.12

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.
Files changed (30) hide show
  1. package/dist/generate/CSharpRepositories.js +28 -18
  2. package/dist/generate/DeterministicProvider.js +35 -1
  3. package/dist/generate/RepositoryPlan.js +7 -0
  4. package/dist/generate/RustRepositories.js +34 -17
  5. package/dist/generate/TypeScriptRepositories.js +24 -11
  6. package/dist/generate/applyGenerate.js +6 -1
  7. package/dist/generate/applyMigrations.js +5 -1
  8. package/dist/generate/datasourceYaml.js +22 -0
  9. package/dist/generate/loadSpec.js +6 -1
  10. package/dist/generate/storedProcedures.js +5 -0
  11. package/dist/generate/writeLists.js +19 -0
  12. package/dist/parse.js +8 -2
  13. package/dist/run.js +6 -10
  14. package/dist/setup.js +4 -2
  15. package/node_modules/@deterministic-code/datasources-code-generators/dist/nestedSelectTypes.d.ts +5 -0
  16. package/node_modules/@deterministic-code/datasources-code-generators/dist/nestedSelectTypes.js +64 -0
  17. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/generate-procedures.js +22 -10
  18. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/generate-sql.d.ts +0 -1
  19. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/generate-sql.js +18 -4
  20. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/procedures/helpers.d.ts +5 -8
  21. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/procedures/helpers.js +2 -29
  22. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/write-inclusion.d.ts +9 -0
  23. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/write-inclusion.js +9 -0
  24. package/node_modules/@deterministic-code/datasources-code-generators/package.json +9 -8
  25. package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/dist/specification-parser.js +67 -2
  26. package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/parser/specification-parser.ts +82 -2
  27. package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/specification.ts +2 -0
  28. package/node_modules/@deterministic-code/deterministic-specifications-typescript/typescript/parser/parser/specification-parser.ts +82 -2
  29. package/node_modules/@deterministic-code/deterministic-specifications-typescript/typescript/parser/specification.ts +2 -0
  30. package/package.json +8 -8
@@ -5,9 +5,10 @@ import { createCasing } from "./default-casing.js";
5
5
  import { byGroupsFromDatasource } from "./datasource-by-fields.js";
6
6
  import { SqlMapping } from "./sql-mapping.js";
7
7
  import { mapColumnType, q, renderSql } from "./sql-dialect.js";
8
- import { buildLiveTables, datasourceSettings, hasAuditColumns, overlayOf, sqlTablesFrom, } from "./sql-schema.js";
8
+ import { buildLiveTables, datasourceSettings, hasAuditColumns, overlayOf, sqlTablesFrom, tableIdentity, } from "./sql-schema.js";
9
9
  import { procedureSpecs } from "./procedures/driver.js";
10
- import { createParamFields, pkFieldOf, writableNonAuditFields, } from "./procedures/helpers.js";
10
+ import { createParamFields, pkFieldOf, updateParamFields } from "./procedures/helpers.js";
11
+ import { writeInclusionOf } from "./write-inclusion.js";
11
12
  import { createBulkCreateStoredProcedureOf, createBulkDeleteStoredProcedureOf, createBulkUpdateStoredProcedureOf, createCreateStoredProcedureOf, createDeleteStoredProcedureOf, createReadStoredProcedureOf, createUpdateStoredProcedureOf, dropRoutineStatementOf, } from "../statements.js";
12
13
  const withNl = (text, casing) => {
13
14
  const out = casing.applyKeywords(text);
@@ -22,26 +23,38 @@ const payloadType = (dialect) => ({
22
23
  }[dialect] ?? "JSON");
23
24
  /** Create / read / update / delete / bulk procedure statements for one live table. */
24
25
  export const procedureStatementsForTable = (dialect, t, mapping, casing, groups) => {
26
+ const overlay = overlayOf(t);
27
+ const ids = tableIdentity(t);
25
28
  const table = {
26
29
  name: t.tableName,
27
30
  entityName: t.name,
28
- fields: t.fields.map((f) => ({ name: f.name, type: f.type })),
29
- pkName: pkName(t, overlayOf(t)),
31
+ fields: t.fields.map((f) => {
32
+ const flags = writeInclusionOf(f, ids, overlay.fields.find((item) => item.name === f.name));
33
+ return { name: f.name, type: f.type, ...flags };
34
+ }),
35
+ pkName: pkName(t, overlay),
30
36
  };
31
37
  const pk = pkFieldOf(table);
32
38
  const columnName = (logical) => mapping.columnName(t, logical);
33
39
  const columns = table.fields.map((f) => columnName(f.name));
34
- const writable = writableNonAuditFields(table).map((f) => columnName(f.name));
40
+ const writable = updateParamFields(table).map((f) => columnName(f.name));
35
41
  const paramType = (field) => mapColumnType(dialect, field);
36
42
  const createdParams = createParamFields(table).map((f) => ({
37
43
  name: columnName(f.name),
38
44
  type: paramType(f),
39
45
  }));
40
46
  const typedColumns = table.fields.map((f) => ({ name: columnName(f.name), type: paramType(f) }));
47
+ const createColumns = createParamFields(table).map((f) => ({ name: columnName(f.name), type: paramType(f) }));
48
+ const updateColumns = [
49
+ { name: columnName(pk.name), type: paramType(pk) },
50
+ ...updateParamFields(table)
51
+ .filter((f) => f.name !== pk.name)
52
+ .map((f) => ({ name: columnName(f.name), type: paramType(f) })),
53
+ ];
41
54
  const payload = { name: "payload", type: payloadType(dialect) };
42
55
  const key = columnName(pk.name);
43
56
  const keyType = paramType(pk);
44
- const audit = table.fields.some((f) => f.name === "updated") ? columnName("updated") : undefined;
57
+ const audit = table.fields.some((f) => f.name === "updated" && f.includeInUpdate) ? columnName("updated") : undefined;
45
58
  const generatedKey = pk.type === "uuid";
46
59
  const quoted = q(dialect, t.tableName);
47
60
  const statements = [];
@@ -55,18 +68,17 @@ export const procedureStatementsForTable = (dialect, t, mapping, casing, groups)
55
68
  });
56
69
  const updateParams = [
57
70
  ...byParams,
58
- ...writableNonAuditFields(table)
71
+ ...updateParamFields(table)
59
72
  .filter((f) => !proc.byProperties.includes(f.name))
60
73
  .map((f) => ({ name: columnName(f.name), type: paramType(f) })),
61
- ...(audit === undefined ? [] : [{ name: columnName("new_updated"), type: paramType(table.fields.find((f) => f.name === "updated")) }]),
62
74
  ];
63
75
  statements.push(({
64
76
  create: () => createCreateStoredProcedureOf(proc.name, quoted, columns, createdParams, key, keyType, writable, generatedKey, audit),
65
77
  read: () => createReadStoredProcedureOf(proc.name, quoted, columns, byParams, key, keyType, writable, by, generatedKey, audit),
66
78
  update: () => createUpdateStoredProcedureOf(proc.name, quoted, columns, updateParams, key, keyType, writable, by, generatedKey, audit),
67
79
  delete: () => createDeleteStoredProcedureOf(proc.name, quoted, columns, byParams, key, keyType, writable, by, generatedKey, audit),
68
- bulkCreate: () => createBulkCreateStoredProcedureOf(proc.name, quoted, typedColumns, payload, key, keyType),
69
- bulkUpdate: () => createBulkUpdateStoredProcedureOf(proc.name, quoted, typedColumns, payload, key, keyType),
80
+ bulkCreate: () => createBulkCreateStoredProcedureOf(proc.name, quoted, createColumns, payload, key, keyType),
81
+ bulkUpdate: () => createBulkUpdateStoredProcedureOf(proc.name, quoted, updateColumns, payload, key, keyType),
70
82
  bulkDelete: () => createBulkDeleteStoredProcedureOf(proc.name, quoted, typedColumns, payload, key, keyType),
71
83
  }[proc.op])());
72
84
  }
@@ -1,5 +1,4 @@
1
1
  import type { GenerateContext } from "@deterministic-code/generators-common/generate-context";
2
2
  import { type GenerateEntry } from "@deterministic-code/generators-common/generate-entry";
3
3
  import { type SqlDialect } from "./sql-dialect.js";
4
- /** DDL initial migration (+ optional custom migrations and stored procedures) for one SQL dialect. */
5
4
  export declare const generateSqlFor: (dialect: SqlDialect, ctx: GenerateContext) => Promise<GenerateEntry[]>;
@@ -76,15 +76,29 @@ const loadSchema = async (ctx) => {
76
76
  };
77
77
  };
78
78
  /** DDL initial migration (+ optional custom migrations and stored procedures) for one SQL dialect. */
79
+ const readerFor = async (dialect, ctx) => {
80
+ const named = `datasource.${dialect}.yaml`;
81
+ if (!(await ctx.reader.exists(named)))
82
+ return ctx;
83
+ const yaml = await ctx.reader.read(named);
84
+ return {
85
+ ...ctx,
86
+ reader: {
87
+ read: (name) => (name === "datasource.yaml" ? Promise.resolve(yaml) : ctx.reader.read(name)),
88
+ exists: (name) => (name === "datasource.yaml" ? Promise.resolve(true) : ctx.reader.exists(name)),
89
+ },
90
+ };
91
+ };
79
92
  export const generateSqlFor = async (dialect, ctx) => {
80
93
  const key = requireDialect(dialect);
81
- const casing = createCasing(ctx.settings);
82
- const dir = ctx.settings["paths.deterministic"];
83
- const schema = await schemaFiles(key, ctx, casing);
94
+ const scoped = await readerFor(key, ctx);
95
+ const casing = createCasing(scoped.settings);
96
+ const dir = scoped.settings["paths.deterministic"];
97
+ const schema = await schemaFiles(key, scoped, casing);
84
98
  return [
85
99
  ...schema.entries,
86
100
  ...(await customEntries(key, dir)),
87
- ...(await generateProceduresForDialect(dialect, ctx, schema.count + 1)),
101
+ ...(await generateProceduresForDialect(dialect, scoped, schema.count + 1)),
88
102
  ];
89
103
  };
90
104
  const schemaFiles = async (key, ctx, casing) => {
@@ -1,25 +1,22 @@
1
1
  /** Helpers shared by stored-procedure generation. */
2
- interface ProcField {
2
+ export interface ProcField {
3
3
  name: string;
4
4
  type: string;
5
5
  size?: number;
6
+ includeInCreate?: boolean;
7
+ includeInUpdate?: boolean;
6
8
  }
7
- interface ProcTable {
9
+ export interface ProcTable {
8
10
  fields: ProcField[];
9
11
  pkName?: string;
10
12
  }
11
13
  /** The entity's primary key: `pkName` when set, else a field named `id`. */
12
14
  export declare const pkFieldOf: (table: ProcTable) => ProcField;
13
- /** The writable, non-audit columns: everything but the pk, the system `uuid`, and `created`/`updated`. */
14
- export declare const writableNonAuditFields: (table: ProcTable) => ProcField[];
15
- /** CREATE IN-params from expanded columns, in the stored-procedure call order: `uuid` (if present), writable fields, `created`, `updated`. */
16
15
  export declare const createParamFields: (table: ProcTable) => ProcField[];
17
- /** The expanded `updated` column — present on every table that gets procedures. */
18
- export declare const updatedFieldOf: (table: ProcTable) => ProcField;
16
+ export declare const updateParamFields: (table: ProcTable) => ProcField[];
19
17
  /** Expanded columns qualified with a table alias (default `t`). */
20
18
  export declare const aliasedColumns: (table: ProcTable, alias?: string, columnName?: (field: string) => string) => string;
21
19
  export declare const paramAlignWidth: (params: {
22
20
  name: string;
23
21
  }[]) => number;
24
22
  export declare const pad: (s: string, w: number) => string;
25
- export {};
@@ -4,35 +4,8 @@ export const pkFieldOf = (table) => (table.pkName
4
4
  ? table.fields.find((f) => f.name === table.pkName)
5
5
  : undefined) ??
6
6
  table.fields.find((f) => f.name === "id") ?? { name: "id", type: "integer" };
7
- /** The writable, non-audit columns: everything but the pk, the system `uuid`, and `created`/`updated`. */
8
- export const writableNonAuditFields = (table) => {
9
- const pk = pkFieldOf(table);
10
- return table.fields.filter((f) => f.name !== pk.name &&
11
- f.name !== "uuid" &&
12
- f.name !== "created" &&
13
- f.name !== "updated");
14
- };
15
- /** CREATE IN-params from expanded columns, in the stored-procedure call order: `uuid` (if present), writable fields, `created`, `updated`. */
16
- export const createParamFields = (table) => {
17
- const field = (name) => {
18
- const found = table.fields.find((f) => f.name === name);
19
- return found ? [found] : [];
20
- };
21
- return [
22
- ...field("uuid"),
23
- ...writableNonAuditFields(table),
24
- ...field("created"),
25
- ...field("updated"),
26
- ];
27
- };
28
- /** The expanded `updated` column — present on every table that gets procedures. */
29
- export const updatedFieldOf = (table) => {
30
- const field = table.fields.find((f) => f.name === "updated");
31
- if (!field) {
32
- throw new Error("procedure table is missing expanded `updated` column");
33
- }
34
- return field;
35
- };
7
+ export const createParamFields = (table) => table.fields.filter((f) => f.includeInCreate !== false);
8
+ export const updateParamFields = (table) => table.fields.filter((f) => f.includeInUpdate === true);
36
9
  /** Expanded columns qualified with a table alias (default `t`). */
37
10
  export const aliasedColumns = (table, alias = "t", columnName = (field) => field) => table.fields.map((f) => `${alias}.${columnName(f.name)}`).join(", ");
38
11
  export const paramAlignWidth = (params) => params.reduce((m, p) => Math.max(m, p.name.length), 0);
@@ -0,0 +1,9 @@
1
+ import type { DatasourceFieldOverlay, TypeField } from "@deterministic-code/deterministic-generators-common/parser";
2
+ export declare const resolveWriteInclusion: (create: boolean | undefined, update: boolean | undefined, identity: boolean, fixedId: boolean, singleGeneratedId: boolean) => {
3
+ includeInCreate: boolean;
4
+ includeInUpdate: boolean;
5
+ };
6
+ export declare const writeInclusionOf: (field: TypeField, ids: readonly string[], overlay?: DatasourceFieldOverlay) => {
7
+ includeInCreate: boolean;
8
+ includeInUpdate: boolean;
9
+ };
@@ -0,0 +1,9 @@
1
+ export const resolveWriteInclusion = (create, update, identity, fixedId, singleGeneratedId) => ({
2
+ includeInCreate: create ?? (!identity || fixedId || !singleGeneratedId),
3
+ includeInUpdate: update ?? (!identity || (!fixedId && !singleGeneratedId)),
4
+ });
5
+ export const writeInclusionOf = (field, ids, overlay) => {
6
+ const identity = ids.includes(field.name);
7
+ const fixedId = overlay?.isFixedId === true;
8
+ return resolveWriteInclusion(overlay?.includeInCreate, overlay?.includeInUpdate, identity, fixedId, identity && ids.length === 1 && !fixedId);
9
+ };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deterministic-code/datasources-code-generators",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dist/generateSql.js",
@@ -8,15 +8,16 @@
8
8
  "./sql/generate-sql": "./dist/sql/generate-sql.js",
9
9
  "./generate-stored-procedures": "./dist/generate-stored-procedures.js",
10
10
  "./sql/generate-stored-procedures": "./dist/sql/generate-stored-procedures.js",
11
- "./common/sql-dialect": "./dist/sql/common/sql-dialect.js"
11
+ "./common/sql-dialect": "./dist/sql/common/sql-dialect.js",
12
+ "./sql/common/write-inclusion": "./dist/sql/common/write-inclusion.js"
12
13
  },
13
14
  "dependencies": {
14
- "@deterministic-code/datasources": "^0.1.10",
15
- "@deterministic-code/datasources-mysql": "^0.1.10",
16
- "@deterministic-code/datasources-oracle": "^0.1.10",
17
- "@deterministic-code/datasources-postgres": "^0.1.10",
18
- "@deterministic-code/datasources-sqlite": "^0.1.10",
19
- "@deterministic-code/datasources-sqlserver": "^0.1.10",
15
+ "@deterministic-code/datasources": "^0.1.12",
16
+ "@deterministic-code/datasources-mysql": "^0.1.12",
17
+ "@deterministic-code/datasources-oracle": "^0.1.12",
18
+ "@deterministic-code/datasources-postgres": "^0.1.12",
19
+ "@deterministic-code/datasources-sqlite": "^0.1.12",
20
+ "@deterministic-code/datasources-sqlserver": "^0.1.12",
20
21
  "@deterministic-code/deterministic-generators-common": "0.1.0",
21
22
  "@deterministic-code/generators-common": "0.0.1",
22
23
  "pluralize": "^8.0.0",
@@ -557,6 +557,65 @@ var inheritsChain = (name, typeByName) => {
557
557
  }
558
558
  return chain;
559
559
  };
560
+ var firstAuthoredInclusion = (walk, overlays, field, key) => {
561
+ for (const typeName of walk) {
562
+ const value = overlays.get(typeName)?.get(field)?.[key];
563
+ if (value !== void 0) return value;
564
+ }
565
+ return void 0;
566
+ };
567
+ var mergeWriteInclusion = (tables, types, authored) => {
568
+ const typeByName = new Map(types.map((type) => [type.name, type]));
569
+ const overlays = new Map(
570
+ authored.map((table) => [
571
+ table.name,
572
+ new Map(table.fields.map((field) => [field.name, field]))
573
+ ])
574
+ );
575
+ return tables.map((table) => {
576
+ const walk = [table.name, ...inheritsChain(table.name, typeByName)];
577
+ const names = [];
578
+ const seen = /* @__PURE__ */ new Set();
579
+ const add = (name) => {
580
+ if (seen.has(name)) return;
581
+ seen.add(name);
582
+ names.push(name);
583
+ };
584
+ for (const field of typeByName.get(table.name)?.fields ?? []) add(field.name);
585
+ for (const field of table.fields) add(field.name);
586
+ const fields = table.fields.map((field) => ({ ...field }));
587
+ const byName = new Map(fields.map((field) => [field.name, field]));
588
+ for (const name of names) {
589
+ const includeInCreate = firstAuthoredInclusion(
590
+ walk,
591
+ overlays,
592
+ name,
593
+ "includeInCreate"
594
+ );
595
+ const includeInUpdate = firstAuthoredInclusion(
596
+ walk,
597
+ overlays,
598
+ name,
599
+ "includeInUpdate"
600
+ );
601
+ if (includeInCreate === void 0 && includeInUpdate === void 0) continue;
602
+ const existing = byName.get(name);
603
+ if (existing) {
604
+ if (includeInCreate !== void 0) existing.includeInCreate = includeInCreate;
605
+ if (includeInUpdate !== void 0) existing.includeInUpdate = includeInUpdate;
606
+ } else {
607
+ const created = {
608
+ name,
609
+ ...includeInCreate !== void 0 ? { includeInCreate } : {},
610
+ ...includeInUpdate !== void 0 ? { includeInUpdate } : {}
611
+ };
612
+ fields.push(created);
613
+ byName.set(name, created);
614
+ }
615
+ }
616
+ return { ...table, fields };
617
+ });
618
+ };
560
619
  var parseSeedKey = (rowKey) => {
561
620
  const m = /^id(\d+)$/.exec(rowKey);
562
621
  if (!m) {
@@ -738,6 +797,8 @@ var Parser = class {
738
797
  ...fnode.has("is_unique") ? { isUnique: fnode.bool("is_unique") } : {},
739
798
  ...fnode.str("mapping") ? { mapping: fnode.str("mapping") } : {},
740
799
  ...fnode.has("is_fixed_id") ? { isFixedId: fnode.bool("is_fixed_id") } : {},
800
+ ...fnode.has("include_in_create") ? { includeInCreate: fnode.bool("include_in_create") } : {},
801
+ ...fnode.has("include_in_update") ? { includeInUpdate: fnode.bool("include_in_update") } : {},
741
802
  ...fnode.has("is_optimistic_concurrency") ? {
742
803
  isOptimisticConcurrency: fnode.bool(
743
804
  "is_optimistic_concurrency"
@@ -759,7 +820,7 @@ var Parser = class {
759
820
  };
760
821
  });
761
822
  const filter = this.#typesFilter(root);
762
- if (!filter) return tables;
823
+ if (!filter) return mergeWriteInclusion(tables, types, tables);
763
824
  const selected = new Set(
764
825
  types.filter((t) => filter(t)).map((t) => t.name)
765
826
  );
@@ -772,7 +833,11 @@ var Parser = class {
772
833
  uniqueIndexFields: []
773
834
  })
774
835
  );
775
- return [...tables.filter((t) => selected.has(t.name)), ...extras];
836
+ return mergeWriteInclusion(
837
+ [...tables.filter((t) => selected.has(t.name)), ...extras],
838
+ types,
839
+ tables
840
+ );
776
841
  }
777
842
  #parseDatasourceSeeds(yaml) {
778
843
  const byTable = /* @__PURE__ */ new Map();
@@ -97,6 +97,76 @@ export const inheritsChain = (
97
97
  return chain;
98
98
  };
99
99
 
100
+ const firstAuthoredInclusion = (
101
+ walk: string[],
102
+ overlays: Map<string, Map<string, DatasourceFieldOverlay>>,
103
+ field: string,
104
+ key: "includeInCreate" | "includeInUpdate",
105
+ ): boolean | undefined => {
106
+ for (const typeName of walk) {
107
+ const value = overlays.get(typeName)?.get(field)?.[key];
108
+ if (value !== undefined) return value;
109
+ }
110
+ return undefined;
111
+ };
112
+
113
+ const mergeWriteInclusion = (
114
+ tables: DatasourceTable[],
115
+ types: Type[],
116
+ authored: DatasourceTable[],
117
+ ): DatasourceTable[] => {
118
+ const typeByName = new Map(types.map((type) => [type.name, type]));
119
+ const overlays = new Map(
120
+ authored.map((table) => [
121
+ table.name,
122
+ new Map(table.fields.map((field) => [field.name, field])),
123
+ ]),
124
+ );
125
+ return tables.map((table) => {
126
+ const walk = [table.name, ...inheritsChain(table.name, typeByName)];
127
+ const names: string[] = [];
128
+ const seen = new Set<string>();
129
+ const add = (name: string) => {
130
+ if (seen.has(name)) return;
131
+ seen.add(name);
132
+ names.push(name);
133
+ };
134
+ for (const field of typeByName.get(table.name)?.fields ?? []) add(field.name);
135
+ for (const field of table.fields) add(field.name);
136
+ const fields = table.fields.map((field) => ({ ...field }));
137
+ const byName = new Map(fields.map((field) => [field.name, field]));
138
+ for (const name of names) {
139
+ const includeInCreate = firstAuthoredInclusion(
140
+ walk,
141
+ overlays,
142
+ name,
143
+ "includeInCreate",
144
+ );
145
+ const includeInUpdate = firstAuthoredInclusion(
146
+ walk,
147
+ overlays,
148
+ name,
149
+ "includeInUpdate",
150
+ );
151
+ if (includeInCreate === undefined && includeInUpdate === undefined) continue;
152
+ const existing = byName.get(name);
153
+ if (existing) {
154
+ if (includeInCreate !== undefined) existing.includeInCreate = includeInCreate;
155
+ if (includeInUpdate !== undefined) existing.includeInUpdate = includeInUpdate;
156
+ } else {
157
+ const created: DatasourceFieldOverlay = {
158
+ name,
159
+ ...(includeInCreate !== undefined ? { includeInCreate } : {}),
160
+ ...(includeInUpdate !== undefined ? { includeInUpdate } : {}),
161
+ };
162
+ fields.push(created);
163
+ byName.set(name, created);
164
+ }
165
+ }
166
+ return { ...table, fields };
167
+ });
168
+ };
169
+
100
170
  const parseSeedKey = (rowKey: string): number => {
101
171
  const m = /^id(\d+)$/.exec(rowKey);
102
172
  if (!m) {
@@ -339,6 +409,12 @@ class Parser {
339
409
  ...(fnode.has("is_fixed_id")
340
410
  ? { isFixedId: fnode.bool("is_fixed_id") }
341
411
  : {}),
412
+ ...(fnode.has("include_in_create")
413
+ ? { includeInCreate: fnode.bool("include_in_create") }
414
+ : {}),
415
+ ...(fnode.has("include_in_update")
416
+ ? { includeInUpdate: fnode.bool("include_in_update") }
417
+ : {}),
342
418
  ...(fnode.has("is_optimistic_concurrency")
343
419
  ? {
344
420
  isOptimisticConcurrency: fnode.bool(
@@ -367,7 +443,7 @@ class Parser {
367
443
  });
368
444
 
369
445
  const filter = this.#typesFilter(root);
370
- if (!filter) return tables;
446
+ if (!filter) return mergeWriteInclusion(tables, types, tables);
371
447
  const selected = new Set(
372
448
  types.filter((t) => filter(t)).map((t) => t.name),
373
449
  );
@@ -382,7 +458,11 @@ class Parser {
382
458
  uniqueIndexFields: [],
383
459
  }),
384
460
  );
385
- return [...tables.filter((t) => selected.has(t.name)), ...extras];
461
+ return mergeWriteInclusion(
462
+ [...tables.filter((t) => selected.has(t.name)), ...extras],
463
+ types,
464
+ tables,
465
+ );
386
466
  }
387
467
 
388
468
  #parseDatasourceSeeds(yaml: string): Map<string, SeedRow[]> {
@@ -46,6 +46,8 @@ export type DatasourceFieldOverlay = {
46
46
  isUnique?: boolean;
47
47
  mapping?: string;
48
48
  isFixedId?: boolean;
49
+ includeInCreate?: boolean;
50
+ includeInUpdate?: boolean;
49
51
  isOptimisticConcurrency?: boolean;
50
52
  useNativeRowVersion?: boolean;
51
53
  };
@@ -96,6 +96,76 @@ export const inheritsChain = (
96
96
  return chain;
97
97
  };
98
98
 
99
+ const firstAuthoredInclusion = (
100
+ walk: string[],
101
+ overlays: Map<string, Map<string, DatasourceFieldOverlay>>,
102
+ field: string,
103
+ key: "includeInCreate" | "includeInUpdate",
104
+ ): boolean | undefined => {
105
+ for (const typeName of walk) {
106
+ const value = overlays.get(typeName)?.get(field)?.[key];
107
+ if (value !== undefined) return value;
108
+ }
109
+ return undefined;
110
+ };
111
+
112
+ const mergeWriteInclusion = (
113
+ tables: DatasourceTable[],
114
+ types: Type[],
115
+ authored: DatasourceTable[],
116
+ ): DatasourceTable[] => {
117
+ const typeByName = new Map(types.map((type) => [type.name, type]));
118
+ const overlays = new Map(
119
+ authored.map((table) => [
120
+ table.name,
121
+ new Map(table.fields.map((field) => [field.name, field])),
122
+ ]),
123
+ );
124
+ return tables.map((table) => {
125
+ const walk = [table.name, ...inheritsChain(table.name, typeByName)];
126
+ const names: string[] = [];
127
+ const seen = new Set<string>();
128
+ const add = (name: string) => {
129
+ if (seen.has(name)) return;
130
+ seen.add(name);
131
+ names.push(name);
132
+ };
133
+ for (const field of typeByName.get(table.name)?.fields ?? []) add(field.name);
134
+ for (const field of table.fields) add(field.name);
135
+ const fields = table.fields.map((field) => ({ ...field }));
136
+ const byName = new Map(fields.map((field) => [field.name, field]));
137
+ for (const name of names) {
138
+ const includeInCreate = firstAuthoredInclusion(
139
+ walk,
140
+ overlays,
141
+ name,
142
+ "includeInCreate",
143
+ );
144
+ const includeInUpdate = firstAuthoredInclusion(
145
+ walk,
146
+ overlays,
147
+ name,
148
+ "includeInUpdate",
149
+ );
150
+ if (includeInCreate === undefined && includeInUpdate === undefined) continue;
151
+ const existing = byName.get(name);
152
+ if (existing) {
153
+ if (includeInCreate !== undefined) existing.includeInCreate = includeInCreate;
154
+ if (includeInUpdate !== undefined) existing.includeInUpdate = includeInUpdate;
155
+ } else {
156
+ const created: DatasourceFieldOverlay = {
157
+ name,
158
+ ...(includeInCreate !== undefined ? { includeInCreate } : {}),
159
+ ...(includeInUpdate !== undefined ? { includeInUpdate } : {}),
160
+ };
161
+ fields.push(created);
162
+ byName.set(name, created);
163
+ }
164
+ }
165
+ return { ...table, fields };
166
+ });
167
+ };
168
+
99
169
  const parseSeedKey = (rowKey: string): number => {
100
170
  const m = /^id(\d+)$/.exec(rowKey);
101
171
  if (!m) {
@@ -338,6 +408,12 @@ class Parser {
338
408
  ...(fnode.has("is_fixed_id")
339
409
  ? { isFixedId: fnode.bool("is_fixed_id") }
340
410
  : {}),
411
+ ...(fnode.has("include_in_create")
412
+ ? { includeInCreate: fnode.bool("include_in_create") }
413
+ : {}),
414
+ ...(fnode.has("include_in_update")
415
+ ? { includeInUpdate: fnode.bool("include_in_update") }
416
+ : {}),
341
417
  ...(fnode.has("is_optimistic_concurrency")
342
418
  ? {
343
419
  isOptimisticConcurrency: fnode.bool(
@@ -366,7 +442,7 @@ class Parser {
366
442
  });
367
443
 
368
444
  const filter = this.#typesFilter(root);
369
- if (!filter) return tables;
445
+ if (!filter) return mergeWriteInclusion(tables, types, tables);
370
446
  const selected = new Set(
371
447
  types.filter((t) => filter(t)).map((t) => t.name),
372
448
  );
@@ -381,7 +457,11 @@ class Parser {
381
457
  uniqueIndexFields: [],
382
458
  }),
383
459
  );
384
- return [...tables.filter((t) => selected.has(t.name)), ...extras];
460
+ return mergeWriteInclusion(
461
+ [...tables.filter((t) => selected.has(t.name)), ...extras],
462
+ types,
463
+ tables,
464
+ );
385
465
  }
386
466
 
387
467
  #parseDatasourceSeeds(yaml: string): Map<string, SeedRow[]> {
@@ -46,6 +46,8 @@ export type DatasourceFieldOverlay = {
46
46
  isUnique?: boolean;
47
47
  mapping?: string;
48
48
  isFixedId?: boolean;
49
+ includeInCreate?: boolean;
50
+ includeInUpdate?: boolean;
49
51
  isOptimisticConcurrency?: boolean;
50
52
  useNativeRowVersion?: boolean;
51
53
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deterministic-code/datasources-cli",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "datasources": "./bin/datasources.mjs"
@@ -13,7 +13,7 @@
13
13
  "access": "public"
14
14
  },
15
15
  "dependencies": {
16
- "@deterministic-code/datasources-code-generators": "0.1.10",
16
+ "@deterministic-code/datasources-code-generators": "0.1.12",
17
17
  "@deterministic-code/generators-common": "0.0.1",
18
18
  "@deterministic-code/deterministic-generators-common": "0.1.0",
19
19
  "@deterministic-code/typescript-generators": "0.0.1",
@@ -21,12 +21,12 @@
21
21
  "@deterministic-code/rust-generators": "0.0.1",
22
22
  "@deterministic-code/generators-openapi": "0.0.1",
23
23
  "@deterministic-code/patch-merger": "0.1.0",
24
- "@deterministic-code/datasources": "^0.1.10",
25
- "@deterministic-code/datasources-sqlite": "^0.1.10",
26
- "@deterministic-code/datasources-mysql": "^0.1.10",
27
- "@deterministic-code/datasources-postgres": "^0.1.10",
28
- "@deterministic-code/datasources-sqlserver": "^0.1.10",
29
- "@deterministic-code/datasources-oracle": "^0.1.10",
24
+ "@deterministic-code/datasources": "^0.1.12",
25
+ "@deterministic-code/datasources-sqlite": "^0.1.12",
26
+ "@deterministic-code/datasources-mysql": "^0.1.12",
27
+ "@deterministic-code/datasources-postgres": "^0.1.12",
28
+ "@deterministic-code/datasources-sqlserver": "^0.1.12",
29
+ "@deterministic-code/datasources-oracle": "^0.1.12",
30
30
  "@inquirer/prompts": "^7.8.0",
31
31
  "yaml": "^2.6.0",
32
32
  "mustache": "^4.2.0",