@deterministic-code/datasources-cli 0.1.12 → 0.1.14

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 (56) hide show
  1. package/dist/detect.js +21 -7
  2. package/dist/generate/CSharpRepositories.js +18 -14
  3. package/dist/generate/DeterministicProvider.js +23 -7
  4. package/dist/generate/RepositoryModel.js +10 -4
  5. package/dist/generate/RepositoryPlan.js +12 -5
  6. package/dist/generate/RustRepositories.js +42 -31
  7. package/dist/generate/TypeScriptRepositories.js +22 -18
  8. package/dist/generate/applyGenerate.js +9 -2
  9. package/dist/generate/applyMigrations.js +5 -13
  10. package/dist/generate/casing.js +28 -0
  11. package/dist/generate/migrateUp.js +2 -8
  12. package/dist/generate/sqlFlags.js +27 -0
  13. package/dist/migrateInit.js +4 -3
  14. package/dist/parse.js +102 -25
  15. package/dist/run.js +42 -67
  16. package/dist/setup.js +7 -9
  17. package/dist/version/applyVersion.js +11 -14
  18. package/dist/version/delta.js +0 -50
  19. package/dist/version/render.js +1 -11
  20. package/dist/wireRegister.js +29 -11
  21. package/dist/wireRustProviders.js +42 -0
  22. package/node_modules/@deterministic-code/csharp-generators/dist/import-generator.js +16 -12
  23. package/node_modules/@deterministic-code/csharp-generators/src/import-generator.test.ts +22 -11
  24. package/node_modules/@deterministic-code/csharp-generators/src/import-generator.ts +21 -13
  25. package/node_modules/@deterministic-code/datasources-code-generators/dist/nestedSelectTypes.js +2 -6
  26. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/infer-reference-types.d.ts +2 -0
  27. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/infer-reference-types.js +37 -0
  28. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/reference-target.d.ts +2 -0
  29. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/reference-target.js +6 -0
  30. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/sql-schema.d.ts +1 -1
  31. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/sql-schema.js +3 -6
  32. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/version-apply.d.ts +1 -0
  33. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/version-apply.js +38 -1
  34. package/node_modules/@deterministic-code/datasources-code-generators/dist/sql/common/version-migrations.js +25 -14
  35. package/node_modules/@deterministic-code/datasources-code-generators/package.json +4 -2
  36. package/node_modules/@deterministic-code/deterministic-generators-common/README.md +61 -1
  37. package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/dist/specification-parser.js +128 -1
  38. package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/specification-parser.ts +1 -0
  39. package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/version-snapshot.ts +154 -0
  40. package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/yaml-node.ts +2 -2
  41. package/node_modules/@deterministic-code/rust-generators/dist/import-generator.js +25 -15
  42. package/node_modules/@deterministic-code/rust-generators/src/import-generator.test.ts +11 -0
  43. package/node_modules/@deterministic-code/rust-generators/src/import-generator.ts +36 -18
  44. package/node_modules/@deterministic-code/typescript-generators/dist/generate-frontend-types.js +6 -1
  45. package/node_modules/@deterministic-code/typescript-generators/dist/generate-frontend-validators.js +6 -1
  46. package/node_modules/@deterministic-code/typescript-generators/dist/generate-routes.js +1 -1
  47. package/node_modules/@deterministic-code/typescript-generators/dist/generate-services.js +1 -1
  48. package/node_modules/@deterministic-code/typescript-generators/dist/import-generator.js +29 -21
  49. package/node_modules/@deterministic-code/typescript-generators/src/emit.ts +2 -5
  50. package/node_modules/@deterministic-code/typescript-generators/src/generate-frontend-types.ts +7 -1
  51. package/node_modules/@deterministic-code/typescript-generators/src/generate-frontend-validators.ts +7 -1
  52. package/node_modules/@deterministic-code/typescript-generators/src/generate-routes.ts +1 -1
  53. package/node_modules/@deterministic-code/typescript-generators/src/generate-services.ts +1 -1
  54. package/node_modules/@deterministic-code/typescript-generators/src/import-generator.test.ts +16 -0
  55. package/node_modules/@deterministic-code/typescript-generators/src/import-generator.ts +37 -22
  56. package/package.json +2 -2
package/dist/detect.js CHANGED
@@ -1,14 +1,25 @@
1
+ import { host } from "./catalog.js";
1
2
  const detect = (entries) => [
2
3
  ...entries.includes("package.json") ? ["ts"] : [],
3
4
  ...entries.includes("Cargo.toml") ? ["rust"] : [],
4
5
  ...entries.some((e) => e.endsWith(".csproj") || e.endsWith(".sln") || e.endsWith(".slnx")) ? ["csharp"] : []
5
6
  ];
6
- const resolveEcosystem = (found, explicit) => {
7
- if (explicit) return explicit;
8
- if (found.length === 1) return found[0];
9
- throw new Error(
10
- found.length === 0 ? "No package.json, Cargo.toml, or .csproj/.sln in this directory" : "Multiple project types found. Pass --ecosystem ts|rust|csharp"
11
- );
7
+ const resolveLanguages = (found, explicit = []) => explicit.length ? explicit : found.length ? found : (() => {
8
+ throw new Error("No package.json, Cargo.toml, or .csproj/.sln in this directory");
9
+ })();
10
+ const hasProject = (language, entries) => ({
11
+ csharp: csharpTarget(entries) !== ".",
12
+ rust: entries.includes("Cargo.toml"),
13
+ ts: entries.includes("package.json")
14
+ })[language];
15
+ const missingProject = (language) => ({
16
+ csharp: `No dotnet packages installed for language csharp. Create a project, or run dotnet add package ${host.csharp.core}.`,
17
+ rust: `No cargo packages installed for language rust. Create a project, or run cargo add ${host.rust.core}.`,
18
+ ts: `No npm packages installed for language typescript. Create a project, or run npm install ${host.ts.core}.`
19
+ })[language];
20
+ const requireProjects = (languages, entries) => {
21
+ const missing = languages.filter((language) => !hasProject(language, entries));
22
+ if (missing.length) throw new Error(missing.map(missingProject).join("\n"));
12
23
  };
13
24
  const csharpTarget = (entries) => {
14
25
  const projects = entries.filter((e) => e.endsWith(".csproj")).sort();
@@ -18,6 +29,9 @@ const isEcosystem = (value) => value === "ts" || value === "rust" || value === "
18
29
  export {
19
30
  csharpTarget,
20
31
  detect,
32
+ hasProject,
21
33
  isEcosystem,
22
- resolveEcosystem
34
+ missingProject,
35
+ requireProjects,
36
+ resolveLanguages
23
37
  };
@@ -1,3 +1,4 @@
1
+ import { createCasingStrategy } from "@deterministic-code/generators-common/casing-strategy";
1
2
  import { content } from "@deterministic-code/generators-common/generate-entry";
2
3
  import {
3
4
  childClass,
@@ -5,6 +6,7 @@ import {
5
6
  isLinked,
6
7
  children as childModels,
7
8
  pluralPascal,
9
+ registryFile,
8
10
  repositoryFile
9
11
  } from "./RepositoryModel.js";
10
12
  import { dialectsOf, pad, quote, writeList } from "./writeLists.js";
@@ -30,11 +32,11 @@ const optionsMethod = (model, type) => {
30
32
  .Add("${quote(engine)}", ${csharpOptions({ entity: model.entity, ...planned }, 12)})`).join("")}
31
33
  .For(connection);`;
32
34
  };
33
- const enrichment = (item, host = "ctor") => {
34
- const via = childField("csharp", item.via, item.viaType);
35
+ const enrichment = (item, casing, host = "ctor") => {
36
+ const via = childField(casing, item.via);
35
37
  const head = `"${quote(item.destination)}", "${quote(item.source)}", "${quote(item.relatedKey)}"`;
36
38
  if (host === "ctor" && isLinked(item)) {
37
- const link = childField("csharp", item.link, item.linkType);
39
+ const link = childField(casing, item.link);
38
40
  return `AddLinked(new(${head}, Association: new("${quote(item.parentFk)}", "${quote(item.relatedFk)}")), ${via}, ${link});`;
39
41
  }
40
42
  const related = item.relatedProperty === void 0 ? "" : `, "${quote(item.relatedProperty)}"`;
@@ -68,13 +70,13 @@ const occMethods = (model, type) => {
68
70
  `;
69
71
  };
70
72
  const optionsCall = (model) => dialectsOf(model).length === 0 ? "Options()" : "Options(connection)";
71
- const createReadonly = (model, type) => {
73
+ const createReadonly = (model, type, casing) => {
72
74
  const children = childModels(model);
73
75
  if (children.length === 0 && model.enrichments.length === 0)
74
76
  return ` public static IReadonlyRepository<${type}> CreateReadonly(IConnection connection) =>
75
77
  new ReadonlyRepository<${type}>(connection, ${optionsCall(model)});`;
76
- const locals = children.map((child) => ` var ${childField("csharp", child.specName, child.typeName)} = new ${childClass(child.typeName)}(connection);`);
77
- const enrich = model.enrichments.map((item) => ` ${enrichment(item, "readonly")}`);
78
+ const locals = children.map((child) => ` var ${childField(casing, child.specName)} = new ${childClass(child.typeName)}(connection);`);
79
+ const enrich = model.enrichments.map((item) => ` ${enrichment(item, casing, "readonly")}`);
78
80
  return ` public static IReadonlyRepository<${type}> CreateReadonly(IConnection connection)
79
81
  {
80
82
  var repo = new ReadonlyRepository<${type}>(connection, ${optionsCall(model)});
@@ -83,14 +85,16 @@ ${enrich.join("\n")}
83
85
  return repo;
84
86
  }`;
85
87
  };
86
- const csharpRepositories = (models) => {
88
+ const csharpRepositories = (models, settings) => {
89
+ const casing = createCasingStrategy("csharp", settings);
90
+ const registryClass = casing.convertTypes("repository_registry");
87
91
  const files = models.map((model) => {
88
92
  const type = `${model.typeHome === "view" ? "Backend.Types.View" : "Backend.Types.Datasource"}.${model.typeName}`;
89
93
  const children = childModels(model);
90
- const fields = children.map((child) => ` public ${childClass(child.typeName)} ${childField("csharp", child.specName, child.typeName)} { get; }`);
94
+ const fields = children.map((child) => ` public ${childClass(child.typeName)} ${childField(casing, child.specName)} { get; }`);
91
95
  const lines = [
92
- ...children.map((child) => ` ${childField("csharp", child.specName, child.typeName)} = new ${childClass(child.typeName)}(connection);`),
93
- ...model.enrichments.map((item) => ` ${enrichment(item)}`)
96
+ ...children.map((child) => ` ${childField(casing, child.specName)} = new ${childClass(child.typeName)}(connection);`),
97
+ ...model.enrichments.map((item) => ` ${enrichment(item, casing)}`)
94
98
  ];
95
99
  const constructor = lines.length === 0 ? ` public ${model.typeName}Repository(IConnection connection) : base(connection, ${optionsCall(model)}) { }` : ` public ${model.typeName}Repository(IConnection connection) : base(connection, ${optionsCall(model)})
96
100
  {
@@ -99,7 +103,7 @@ ${lines.join("\n")}
99
103
  const implementsOcc = model.optimisticConcurrency === void 0 ? "" : `, ICRUDRepository<${type}>`;
100
104
  const usings = model.optimisticConcurrency === void 0 ? "using Datasources;\n" : "using Datasources;\nusing static Datasources.Cmp;\n";
101
105
  return content(
102
- repositoryFile("csharp", model),
106
+ repositoryFile("cs", model.specName, casing),
103
107
  `${usings}
104
108
  public sealed class ${model.typeName}Repository : CRUDRepository<${type}>${implementsOcc}
105
109
  {
@@ -107,7 +111,7 @@ ${fields.length === 0 ? "" : `${fields.join("\n")}
107
111
 
108
112
  `}${constructor}
109
113
 
110
- ${createReadonly(model, type)}
114
+ ${createReadonly(model, type, casing)}
111
115
  ${occMethods(model, type)}
112
116
  ${optionsMethod(model, type)}
113
117
  }
@@ -121,11 +125,11 @@ ${optionsMethod(model, type)}
121
125
  return [
122
126
  ...files,
123
127
  content(
124
- "RepositoryRegistry.cs",
128
+ registryFile("cs", casing),
125
129
  `using System.Runtime.CompilerServices;
126
130
  using Datasources;
127
131
 
128
- public static class RepositoryRegistry
132
+ public static class ${registryClass}
129
133
  {
130
134
  [ModuleInitializer]
131
135
  internal static void Bind() => RepositoryFactory.Bind(Register);
@@ -5,13 +5,19 @@ import { generate as rustDatasourceTypes } from "@deterministic-code/rust-genera
5
5
  import { generate as rustViewTypes } from "@deterministic-code/rust-generators/generate-view-types";
6
6
  import { generate as typescriptDatasourceTypes } from "@deterministic-code/typescript-generators/generate-datasource-types";
7
7
  import { generate as typescriptViewTypes } from "@deterministic-code/typescript-generators/generate-view-types";
8
+ import { createCasingStrategy } from "@deterministic-code/generators-common/casing-strategy";
8
9
  import { DeterministicParser } from "@deterministic-code/deterministic-generators-common/parser";
10
+ import { caseDirectories, repositoriesPrefix, typesPrefix } from "./casing.js";
9
11
  import { csharpRepositories } from "./CSharpRepositories.js";
10
12
  import { rustRepositories } from "./RustRepositories.js";
11
13
  import { typescriptRepositories } from "./TypeScriptRepositories.js";
12
14
  import { planRepositories } from "./RepositoryPlan.js";
13
15
  import { datasourceEnginesOf, engineDatasourceOf, overlayDatasource } from "./datasourceYaml.js";
14
16
  const place = (dir, entries) => entries.map((entry) => ({ ...entry, filename: `${dir}/${entry.filename}` }));
17
+ const remap = (entries, settings, dialect) => {
18
+ const casing = createCasingStrategy(dialect, settings);
19
+ return entries.map((entry) => ({ ...entry, filename: caseDirectories(entry.filename, casing) }));
20
+ };
15
21
  const typePacks = {
16
22
  csharp: { datasource: csharpDatasourceTypes, views: csharpViewTypes },
17
23
  typescript: { datasource: typescriptDatasourceTypes, views: typescriptViewTypes },
@@ -65,19 +71,29 @@ const deterministic = {
65
71
  generateTypes: async (request) => {
66
72
  const pack = typePacks[request.dialect];
67
73
  const ctx = context(request);
74
+ const casing = createCasingStrategy(request.dialect, request.settings);
68
75
  const datasource = await pack.datasource(ctx);
69
76
  const views = await pack.views(ctx);
70
- return request.dialect === "typescript" ? [
71
- ...place("typescript/types/generated/datasource", datasource),
72
- ...place("typescript/types/generated/views", views)
73
- ] : place(request.dialect, [...datasource, ...views]);
77
+ return remap(
78
+ request.dialect === "typescript" ? [
79
+ ...place(typesPrefix(request.dialect, casing, "datasource"), datasource),
80
+ ...place(typesPrefix(request.dialect, casing, "views"), views)
81
+ ] : place(request.dialect, [...datasource, ...views]),
82
+ request.settings,
83
+ request.dialect
84
+ );
74
85
  },
75
86
  generateRepositories: async (request) => {
76
87
  const spec = await DeterministicParser(request.reader).parse(request.settings);
77
88
  const models = planRepositories(spec, request.dialect, request.settings);
78
- return place(
79
- `${request.dialect}/repositories`,
80
- repositoryPacks[request.dialect](await withProviderWrites(request, models), request.settings)
89
+ const casing = createCasingStrategy(request.dialect, request.settings);
90
+ return remap(
91
+ place(
92
+ repositoriesPrefix(request.dialect, casing),
93
+ repositoryPacks[request.dialect](await withProviderWrites(request, models), request.settings)
94
+ ),
95
+ request.settings,
96
+ request.dialect
81
97
  );
82
98
  },
83
99
  generateDatasource: async (request) => place("sql", await generateSql(context(request), dialectsOf(request.settings)))
@@ -1,5 +1,9 @@
1
- const repositoryFile = (dialect, model) => dialect === "csharp" ? `${model.typeName}Repository.cs` : dialect === "typescript" ? `${model.specName.replaceAll("_", "-")}-repository.ts` : `${model.specName}_repository.rs`;
2
- const childField = (dialect, specName, typeName) => dialect === "csharp" ? typeName : dialect === "typescript" ? camel(specName) : specName;
1
+ const repositoryFile = (ext, specName, casing) => `${casing.convertFileName(`${specName}_repository`)}.${ext}`;
2
+ const registryFile = (ext, casing) => `${casing.convertFileName("repository_registry")}.${ext}`;
3
+ const rustIdent = (stem) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(stem) ? stem : stem.replaceAll("-", "_");
4
+ const rustModLine = (stem) => rustIdent(stem) === stem ? `pub mod ${stem};` : `#[path = "${stem}.rs"]
5
+ pub mod ${rustIdent(stem)};`;
6
+ const childField = (casing, specName) => casing.convertFields(specName);
3
7
  const childClass = (typeName) => `${typeName}Repository`;
4
8
  const isLinked = (item) => item.kind === "linked";
5
9
  const children = (model) => {
@@ -10,7 +14,6 @@ const children = (model) => {
10
14
  }
11
15
  return [...rows].map(([specName, typeName]) => ({ specName, typeName }));
12
16
  };
13
- const camel = (name) => name.split("_").map((word, index) => index === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1)).join("");
14
17
  const uncountable = /* @__PURE__ */ new Set(["sheep", "fish", "series", "species", "deer", "news"]);
15
18
  const irregular = {
16
19
  person: "people",
@@ -60,5 +63,8 @@ export {
60
63
  pluralCamel,
61
64
  pluralPascal,
62
65
  pluralSnake,
63
- repositoryFile
66
+ registryFile,
67
+ repositoryFile,
68
+ rustIdent,
69
+ rustModLine
64
70
  };
@@ -1,5 +1,6 @@
1
+ import { createCasing } from "@deterministic-code/datasources-code-generators/sql/common/default-casing";
2
+ import { inferReferenceFieldTypes } from "@deterministic-code/datasources-code-generators/sql/common/infer-reference-types";
1
3
  import { createCasingStrategy } from "@deterministic-code/generators-common/casing-strategy";
2
- import { createDatasourceNaming } from "@deterministic-code/generators-common/datasource-naming";
3
4
  import { identityColumns, isCollectionField, persistedColumnFields } from "@deterministic-code/generators-common/spec-types";
4
5
  import { writeInclusionOf } from "@deterministic-code/datasources-code-generators/sql/common/write-inclusion";
5
6
  const referenceOf = (references) => {
@@ -23,7 +24,12 @@ const guardCycles = (models) => {
23
24
  };
24
25
  const planRepositories = (spec, dialect, settings) => {
25
26
  const casing = createCasingStrategy(dialect, settings);
26
- const naming = createDatasourceNaming(settings);
27
+ const naming = createCasing(settings);
28
+ spec = {
29
+ ...spec,
30
+ types: inferReferenceFieldTypes(spec.types),
31
+ expandedTypes: inferReferenceFieldTypes(spec.expandedTypes)
32
+ };
27
33
  const types = new Map(spec.types.map((type) => [type.name, type]));
28
34
  const expanded = new Map(spec.expandedTypes.map((type) => [type.name, type]));
29
35
  const referenced = /* @__PURE__ */ new Set();
@@ -63,10 +69,10 @@ const planRepositories = (spec, dialect, settings) => {
63
69
  typeName: casing.convertTypes(authored.name),
64
70
  typeHome: authored.tags.includes("view_type") ? "view" : "datasource",
65
71
  entity: casing.convertTypes(authored.name),
66
- table: naming.resolveTable(table.name, table.mapping),
72
+ table: naming.tableName(table.mapping ?? table.name),
67
73
  propertyMappings: columns.flatMap((field) => {
68
74
  const from = property(field.name);
69
- const to = naming.resolveColumn(field.name, overlays.get(field.name)?.mapping);
75
+ const to = naming.columnName(overlays.get(field.name)?.mapping ?? field.name);
70
76
  return from === to ? [] : [{ from, to }];
71
77
  }),
72
78
  ids: identityColumns(tableType, table).map(property),
@@ -164,7 +170,8 @@ const relations = (authored, full, casing, types, expanded, repoName, identity)
164
170
  for (const [key, dest] of Object.entries(authored.mapping ?? {})) {
165
171
  const dotted = key.includes(".") ? key.split(".") : void 0;
166
172
  const memberName = dotted?.[0] ?? (authored.union ?? []).find((name) => expanded.get(name).fields.some((field) => field.name === key));
167
- const fk = memberName === void 0 ? void 0 : full.fields.find((field) => referenceOf(field.references)?.type === memberName);
173
+ const parent = types.get(authored.inherits ?? "");
174
+ const fk = memberName === void 0 ? void 0 : full.fields.find((field) => referenceOf(field.references)?.type === memberName) ?? parent?.fields.find((field) => referenceOf(field.references)?.type === memberName);
168
175
  if (memberName === void 0 || fk === void 0) continue;
169
176
  const via = named(memberName);
170
177
  planned.push({
@@ -1,5 +1,6 @@
1
+ import { createCasingStrategy } from "@deterministic-code/generators-common/casing-strategy";
1
2
  import { content } from "@deterministic-code/generators-common/generate-entry";
2
- import { childField, children as childModels, isLinked, pluralSnake, repositoryFile } from "./RepositoryModel.js";
3
+ import { childField, children as childModels, isLinked, pluralSnake, registryFile, repositoryFile, rustIdent, rustModLine } from "./RepositoryModel.js";
3
4
  import { dialectsOf, pad, quote, writeList } from "./writeLists.js";
4
5
  const rustString = (value) => `"${quote(value)}"`;
5
6
  const uses = (model) => dialectsOf(model).length === 0 ? `use datasources::{Association, Connection, CRUDRepository, CRUDRepositoryOptions, DictionaryMapper, Enrichment, EnrichmentPropertyType, ICRUDRepository, ReadonlyRepository, ReadonlyRepositoryMappingOptions, Result};
@@ -48,31 +49,37 @@ ${mapping} options.read_properties = vec![${model.readProperties.map((name) =
48
49
  };
49
50
  const nestedOf = (models) => new Set(models.filter((model) => childModels(model).length > 0).map((model) => model.specName));
50
51
  const viaClone = (name, specName, nested) => nested.has(specName) ? `Arc::clone(&${name}.repo)` : `Arc::clone(&${name})`;
51
- const enrichment = (item, linked, nested) => {
52
- const via = childField("rust", item.via, item.viaType);
52
+ const fileStem = (specName, casing) => casing.convertFileName(`${specName}_repository`);
53
+ const moduleStem = (specName, casing) => rustIdent(fileStem(specName, casing));
54
+ const repoFn = (specName, casing) => casing.convertFields(`${specName}_repository`);
55
+ const readonlyFnName = (specName, casing) => casing.convertFields(`${specName}_readonly_repository`);
56
+ const enrichment = (item, linked, nested, casing) => {
57
+ const via = childField(casing, item.via);
53
58
  if (linked && isLinked(item)) {
54
- const link = childField("rust", item.link, item.linkType);
59
+ const link = childField(casing, item.link);
55
60
  return `repo.add_linked(Enrichment::new(${rustString(item.destination)}, ${rustString(item.source)}, ${rustString(item.relatedKey)}, None, EnrichmentPropertyType::List).linked(Association::new(${rustString(item.parentFk)}, ${rustString(item.relatedFk)})), ${viaClone(via, item.via, nested)}, ${viaClone(link, item.link, nested)} as Arc<dyn ICRUDRepository>, None);`;
56
61
  }
57
62
  const related = item.relatedProperty === void 0 ? "None" : `Some(${rustString(item.relatedProperty)}.into())`;
58
63
  const type = item.kind === "object" ? "EnrichmentPropertyType::Object" : item.kind === "dictionary" ? "EnrichmentPropertyType::Dictionary" : "EnrichmentPropertyType::List";
59
64
  return `repo.add_enriched(Enrichment::new(${rustString(item.destination)}, ${rustString(item.source)}, ${rustString(item.relatedKey)}, ${related}, ${type}), ${viaClone(via, item.via, nested)}, None);`;
60
65
  };
61
- const childLets = (children, nested) => children.map(
62
- (child) => nested.has(child.specName) ? ` let ${child.specName} = Arc::new(super::${child.specName}_repository::${child.typeName}Repository::new(Arc::clone(&connection))?);` : ` let ${child.specName} = Arc::new(super::${child.specName}_repository::${child.specName}_repository(Arc::clone(&connection))?);`
63
- ).join("\n");
66
+ const childLets = (children, nested, casing) => children.map((child) => {
67
+ const field = childField(casing, child.specName);
68
+ const module = moduleStem(child.specName, casing);
69
+ return nested.has(child.specName) ? ` let ${field} = Arc::new(super::${module}::${child.typeName}Repository::new(Arc::clone(&connection))?);` : ` let ${field} = Arc::new(super::${module}::${repoFn(child.specName, casing)}(Arc::clone(&connection))?);`;
70
+ }).join("\n");
64
71
  const readonlyChildren = (model) => {
65
72
  const rows = /* @__PURE__ */ new Map();
66
73
  for (const item of model.enrichments) rows.set(item.via, item.viaType);
67
74
  return [...rows].map(([specName, typeName]) => ({ specName, typeName }));
68
75
  };
69
- const readonlyFn = (model, nested) => {
70
- const lets = childLets(readonlyChildren(model), nested);
71
- const enrich = model.enrichments.map((item) => ` ${enrichment(item, false, nested)}`).join("\n");
76
+ const readonlyFn = (model, nested, casing) => {
77
+ const lets = childLets(readonlyChildren(model), nested, casing);
78
+ const enrich = model.enrichments.map((item) => ` ${enrichment(item, false, nested, casing)}`).join("\n");
72
79
  const body = `${readonlyOptions(model)}
73
80
  ${lets}${lets.length === 0 ? "" : "\n"} let ${model.enrichments.length === 0 ? "repo" : "mut repo"} = ReadonlyRepository::new(connection, options)?;
74
81
  ${enrich}${enrich.length === 0 ? "" : "\n"} Ok(repo)`;
75
- return `pub fn ${model.specName}_readonly_repository(connection: Arc<dyn Connection>) -> Result<ReadonlyRepository> {
82
+ return `pub fn ${readonlyFnName(model.specName, casing)}(connection: Arc<dyn Connection>) -> Result<ReadonlyRepository> {
76
83
  ${body}
77
84
  }`;
78
85
  };
@@ -180,23 +187,25 @@ ${where}
180
187
  }
181
188
  `;
182
189
  };
183
- const plain = (model, nested) => `${uses(model)}
190
+ const plain = (model, nested, casing) => `${uses(model)}
184
191
 
185
- pub fn ${model.specName}_repository(connection: Arc<dyn Connection>) -> Result<CRUDRepository> {
192
+ pub fn ${repoFn(model.specName, casing)}(connection: Arc<dyn Connection>) -> Result<CRUDRepository> {
186
193
  ${crudOptions(model)}
187
194
  CRUDRepository::new(connection, options)
188
195
  }
189
196
 
190
- ${readonlyFn(model, nested)}
197
+ ${readonlyFn(model, nested, casing)}
191
198
  ${rustOcc(model)}`;
192
- const wrapped = (model, nested) => {
199
+ const wrapped = (model, nested, casing) => {
193
200
  const type = `${model.typeName}Repository`;
194
201
  const children = childModels(model);
195
- const fields = children.map(
196
- (child) => nested.has(child.specName) ? ` pub ${child.specName}: Arc<super::${child.specName}_repository::${child.typeName}Repository>,` : ` pub ${child.specName}: Arc<CRUDRepository>,`
197
- ).join("\n");
198
- const assign = children.map((child) => child.specName).join(", ");
199
- const enrich = model.enrichments.map((item) => ` ${enrichment(item, true, nested)}`).join("\n");
202
+ const fields = children.map((child) => {
203
+ const field = childField(casing, child.specName);
204
+ const module = moduleStem(child.specName, casing);
205
+ return nested.has(child.specName) ? ` pub ${field}: Arc<super::${module}::${child.typeName}Repository>,` : ` pub ${field}: Arc<CRUDRepository>,`;
206
+ }).join("\n");
207
+ const assign = children.map((child) => childField(casing, child.specName)).join(", ");
208
+ const enrich = model.enrichments.map((item) => ` ${enrichment(item, true, nested, casing)}`).join("\n");
200
209
  return `${uses(model)}
201
210
  use std::ops::Deref;
202
211
 
@@ -208,7 +217,7 @@ ${fields}
208
217
  impl ${type} {
209
218
  pub fn new(connection: Arc<dyn Connection>) -> Result<Self> {
210
219
  ${pad(crudOptions(model), 4)}
211
- ${childLets(children, nested)}
220
+ ${childLets(children, nested, casing)}
212
221
  let mut repo = CRUDRepository::new(Arc::clone(&connection), options)?;
213
222
  ${enrich}
214
223
  Ok(Self { repo: Arc::new(repo), ${assign} })
@@ -222,26 +231,28 @@ impl Deref for ${type} {
222
231
  }
223
232
  }
224
233
 
225
- pub fn ${model.specName}_repository(connection: Arc<dyn Connection>) -> Result<CRUDRepository> {
234
+ pub fn ${repoFn(model.specName, casing)}(connection: Arc<dyn Connection>) -> Result<CRUDRepository> {
226
235
  Arc::try_unwrap(${type}::new(connection)?.repo).map_err(|_| datasources::DatasourceError::message("repository"))
227
236
  }
228
237
 
229
- ${readonlyFn(model, nested)}
238
+ ${readonlyFn(model, nested, casing)}
230
239
  ${rustOcc(model)}`;
231
240
  };
232
- const rustRepositories = (models) => {
241
+ const rustRepositories = (models, settings) => {
242
+ const casing = createCasingStrategy("rust", settings);
233
243
  const nested = nestedOf(models);
234
244
  const files = models.map(
235
- (model) => content(repositoryFile("rust", model), childModels(model).length === 0 ? plain(model, nested) : wrapped(model, nested))
245
+ (model) => content(repositoryFile("rs", model.specName, casing), childModels(model).length === 0 ? plain(model, nested, casing) : wrapped(model, nested, casing))
236
246
  );
237
247
  const register = models.map((model) => {
238
- const module = `${model.specName}_repository`;
239
- const crud = model.optimisticConcurrency === void 0 ? `Arc::new(super::${module}::${model.specName}_repository(connection).expect("repository")) as Arc<dyn ICRUDRepository>` : `Arc::new(super::${module}::${model.typeName}Optimistic::new(super::${module}::${model.specName}_repository(connection).expect("repository"))) as Arc<dyn ICRUDRepository>`;
248
+ const module = moduleStem(model.specName, casing);
249
+ const fn = repoFn(model.specName, casing);
250
+ const crud = model.optimisticConcurrency === void 0 ? `Arc::new(super::${module}::${fn}(connection).expect("repository")) as Arc<dyn ICRUDRepository>` : `Arc::new(super::${module}::${model.typeName}Optimistic::new(super::${module}::${fn}(connection).expect("repository"))) as Arc<dyn ICRUDRepository>`;
240
251
  return ` factory.register_named(
241
252
  ${rustString(model.entity)},
242
253
  ${rustString(pluralSnake(model.specName))},
243
254
  |connection| ${crud},
244
- |connection| Arc::new(super::${module}::${model.specName}_readonly_repository(connection).expect("repository")) as Arc<dyn IReadonlyRepository>,
255
+ |connection| Arc::new(super::${module}::${readonlyFnName(model.specName, casing)}(connection).expect("repository")) as Arc<dyn IReadonlyRepository>,
245
256
  );`;
246
257
  });
247
258
  const methods = models.map((model) => {
@@ -276,9 +287,9 @@ ${impls.join("\n")}
276
287
  `;
277
288
  return [
278
289
  ...files,
279
- content("repository_registry.rs", registry),
280
- content("mod.rs", `${models.map((model) => `pub mod ${model.specName}_repository;`).join("\n")}
281
- pub mod repository_registry;
290
+ content(registryFile("rs", casing), registry),
291
+ content("mod.rs", `${models.map((model) => rustModLine(fileStem(model.specName, casing))).join("\n")}
292
+ ${rustModLine(casing.convertFileName("repository_registry"))}
282
293
  `)
283
294
  ];
284
295
  };
@@ -1,6 +1,6 @@
1
1
  import { createCasingStrategy } from "@deterministic-code/generators-common/casing-strategy";
2
2
  import { content } from "@deterministic-code/generators-common/generate-entry";
3
- import { childClass, childField, children as childModels, isLinked, pluralCamel, repositoryFile } from "./RepositoryModel.js";
3
+ import { childClass, childField, children as childModels, isLinked, pluralCamel, registryFile, repositoryFile } from "./RepositoryModel.js";
4
4
  import { dialectsOf, quote, writeList } from "./writeLists.js";
5
5
  const withEntity = (model, planned) => ({ entity: model.entity, ...planned });
6
6
  const entityAndMapping = (model) => {
@@ -18,23 +18,23 @@ const tsBuilder = (name, model, write) => `const ${name} = (connection: IConnect
18
18
 
19
19
  `;
20
20
  const tsOptionsFn = (model) => dialectsOf(model).length === 0 ? "" : `${tsBuilder("options", model, tsOptions)}${tsBuilder("readonlyOptions", model, tsReadonly)}`;
21
- const enrichment = (item, host) => {
22
- const via = host === "this" ? `this.${childField("typescript", item.via, item.viaType)}` : childField("typescript", item.via, item.viaType);
21
+ const enrichment = (item, casing, host) => {
22
+ const via = host === "this" ? `this.${childField(casing, item.via)}` : childField(casing, item.via);
23
23
  const related = item.relatedProperty === void 0 ? "" : `, relatedProperty: "${quote(item.relatedProperty)}"`;
24
24
  const type = item.kind === "object" ? "object" : item.kind === "dictionary" ? "dictionary" : "list";
25
25
  if (host === "this" && isLinked(item)) {
26
- const link = `this.${childField("typescript", item.link, item.linkType)}`;
26
+ const link = `this.${childField(casing, item.link)}`;
27
27
  return `this.addLinked({ destinationProperty: "${quote(item.destination)}", sourceProperty: "${quote(item.source)}", relatedKey: "${quote(item.relatedKey)}"${related}, association: { parent: "${quote(item.parentFk)}", related: "${quote(item.relatedFk)}" }, type: "${type}" }, ${via}, ${link});`;
28
28
  }
29
29
  return `${host}.addEnriched({ destinationProperty: "${quote(item.destination)}", sourceProperty: "${quote(item.source)}", relatedKey: "${quote(item.relatedKey)}"${related}, type: "${type}" }, ${via});`;
30
30
  };
31
- const createReadonly = (model) => {
31
+ const createReadonly = (model, casing) => {
32
32
  const options = dialectsOf(model).length === 0 ? tsReadonly(model) : "readonlyOptions(connection)";
33
33
  const children = childModels(model);
34
34
  if (children.length === 0 && model.enrichments.length === 0)
35
35
  return ` static createReadonly = (connection: IConnection) => new ReadonlyRepository<${model.typeName}>(connection, ${options});`;
36
- const locals = children.map((child) => ` const ${childField("typescript", child.specName, child.typeName)} = new ${childClass(child.typeName)}(connection);`);
37
- const enrich = model.enrichments.map((item) => ` ${enrichment(item, "repo")}`);
36
+ const locals = children.map((child) => ` const ${childField(casing, child.specName)} = new ${childClass(child.typeName)}(connection);`);
37
+ const enrich = model.enrichments.map((item) => ` ${enrichment(item, casing, "repo")}`);
38
38
  return ` static createReadonly(connection: IConnection) {
39
39
  const repo = new ReadonlyRepository<${model.typeName}>(connection, ${options});
40
40
  ${locals.join("\n")}
@@ -67,24 +67,28 @@ ${bump} const response = await update(item, byWhere ?? ${tokenWhere(model,
67
67
  };
68
68
  `;
69
69
  };
70
- const repoImport = (specName) => `./${specName.replaceAll("_", "-")}-repository`;
70
+ const repoImport = (specName, casing) => `./${casing.convertFileName(`${specName}_repository`)}`;
71
+ const typeImport = (model, casing) => {
72
+ const folder = model.typeHome === "view" ? "views" : "datasource";
73
+ const dirs = ["types", "generated", folder].map((segment) => casing.convertDirectories(segment)).join("/");
74
+ return `../${dirs}/${casing.convertFileName(model.specName)}`;
75
+ };
71
76
  const typescriptRepositories = (models, settings) => {
72
77
  const casing = createCasingStrategy("typescript", settings);
73
78
  const files = models.map((model) => {
74
- const folder = model.typeHome === "view" ? "views" : "datasource";
75
79
  const children = childModels(model);
76
- const fields = children.map((child) => ` readonly ${childField("typescript", child.specName, child.typeName)}: ${childClass(child.typeName)};`);
80
+ const fields = children.map((child) => ` readonly ${childField(casing, child.specName)}: ${childClass(child.typeName)};`);
77
81
  const wrap = occWrap(model);
78
82
  const lines = [
79
- ...children.map((child) => ` this.${childField("typescript", child.specName, child.typeName)} = new ${childClass(child.typeName)}(connection);`),
80
- ...model.enrichments.map((item) => ` ${enrichment(item, "this")}`)
83
+ ...children.map((child) => ` this.${childField(casing, child.specName)} = new ${childClass(child.typeName)}(connection);`),
84
+ ...model.enrichments.map((item) => ` ${enrichment(item, casing, "this")}`)
81
85
  ];
82
- const imports = children.map((child) => `import { ${childClass(child.typeName)} } from "${repoImport(child.specName)}";`);
86
+ const imports = children.map((child) => `import { ${childClass(child.typeName)} } from "${repoImport(child.specName, casing)}";`);
83
87
  const builder = dialectsOf(model).length === 0 ? "" : "CRUDRepositoryBuilder, ";
84
88
  const core = model.optimisticConcurrency === void 0 ? `import { CRUDRepository, ${builder}DictionaryMapper, ReadonlyRepository, type IConnection } from "@deterministic-code/datasources";` : `import { CRUDRepository, ${builder}DatasourceError, DictionaryMapper, eq, ReadonlyRepository, Where, type IConnection } from "@deterministic-code/datasources";`;
85
89
  return content(
86
- repositoryFile("typescript", model),
87
- `import type { ${model.typeName} } from "../types/generated/${folder}/${casing.convertFileName(model.specName)}";
90
+ repositoryFile("ts", model.specName, casing),
91
+ `import type { ${model.typeName} } from "${typeImport(model, casing)}";
88
92
  ${core}
89
93
  ${imports.length === 0 ? "" : `${imports.join("\n")}
90
94
  `}
@@ -94,18 +98,18 @@ ${fields.length === 0 ? "" : `${fields.join("\n")}
94
98
  super(connection, ${tsOptionsExpr(model)});
95
99
  ${wrap}${lines.length === 0 ? "" : `${lines.join("\n")}
96
100
  `} }
97
- ${createReadonly(model)}
101
+ ${createReadonly(model, casing)}
98
102
  }
99
103
  `
100
104
  );
101
105
  });
102
- const registerImports = models.map((model) => `import { ${childClass(model.typeName)} } from "${repoImport(model.specName)}";`);
106
+ const registerImports = models.map((model) => `import { ${childClass(model.typeName)} } from "${repoImport(model.specName, casing)}";`);
103
107
  const registerCalls = models.map((model) => ` factory.register("${quote(model.entity)}", "${quote(pluralCamel(model.typeName))}", (connection) => new ${childClass(model.typeName)}(connection), (connection) => ${childClass(model.typeName)}.createReadonly(connection));`);
104
108
  const props = models.map((model) => ` readonly ${pluralCamel(model.typeName)}: ${childClass(model.typeName)};`);
105
109
  return [
106
110
  ...files,
107
111
  content(
108
- "repository-registry.ts",
112
+ registryFile("ts", casing),
109
113
  `import { RepositoryFactory } from "@deterministic-code/datasources";
110
114
  ${registerImports.join("\n")}
111
115
 
@@ -4,15 +4,22 @@ import { loadSpec } from "./loadSpec.js";
4
4
  import { providerByName } from "./providers.js";
5
5
  import { withStoredProcedures } from "./storedProcedures.js";
6
6
  import { writeEntries } from "./writeEntries.js";
7
+ import { overlayCasingFlags } from "./casing.js";
8
+ import { overlaySqlFlags } from "./sqlFlags.js";
7
9
  import { wireRegister } from "../wireRegister.js";
8
10
  const at = (root, path) => isAbsolute(path) ? path : join(root, path);
9
11
  const applyGenerate = async (command, flags, deps) => {
10
12
  const provider = providerByName(command.provider);
11
13
  const loaded = await loadSpec(deps.fs, at(flags.cwd, command.input));
14
+ const settings = overlayCasingFlags(
15
+ overlaySqlFlags(withStoredProcedures(loaded.settings, command.storedProcedures), command),
16
+ command.dialect,
17
+ command
18
+ );
12
19
  const request = {
13
20
  dialect: command.dialect,
14
21
  reader: memoryReader(loaded.files),
15
- settings: withStoredProcedures(loaded.settings, command.storedProcedures)
22
+ settings
16
23
  };
17
24
  const entries = await {
18
25
  all: async () => [
@@ -26,7 +33,7 @@ const applyGenerate = async (command, flags, deps) => {
26
33
  }[command.target]();
27
34
  for (const path of await writeEntries(deps.fs, at(flags.cwd, command.output), entries)) deps.log(path);
28
35
  if (command.target === "all" || command.target === "repositories")
29
- await wireRegister(flags.cwd, command.output, command.dialect, flags.dryRun, deps);
36
+ await wireRegister(flags.cwd, command.output, command.dialect, flags.dryRun, deps, settings);
30
37
  };
31
38
  export {
32
39
  applyGenerate
@@ -3,31 +3,23 @@ import { generate as generateSql } from "@deterministic-code/datasources-code-ge
3
3
  import { memoryReader } from "@deterministic-code/generators-common/deterministic-reader";
4
4
  import { loadSpec } from "./loadSpec.js";
5
5
  import { providerByName } from "./providers.js";
6
+ import { overlaySqlFlags } from "./sqlFlags.js";
6
7
  import { withStoredProcedures } from "./storedProcedures.js";
7
8
  import { writeEntries } from "./writeEntries.js";
8
9
  const at = (root, path) => isAbsolute(path) ? path : join(root, path);
9
- const afterMigrations = (filename) => filename.replace(/^[^/]+\/migrations\//, "");
10
10
  const applyMigrations = async (command, flags, deps) => {
11
11
  providerByName(command.provider);
12
12
  const loaded = await loadSpec(deps.fs, at(flags.cwd, command.input));
13
- const listed = [
14
- ...new Set(
15
- loaded.settings["backend.datasources"].split(",").map((name) => name.trim()).filter((name) => name !== "")
16
- )
17
- ];
18
- if (command.migrationsPath !== void 0 && listed.length > 1)
19
- throw new Error("--migrations-path requires a single SQL dialect in backend.datasources");
20
13
  const entries = await generateSql({
21
14
  reader: memoryReader(loaded.files),
22
- settings: withStoredProcedures(loaded.settings, command.storedProcedures)
15
+ settings: overlaySqlFlags(withStoredProcedures(loaded.settings, command.storedProcedures), command)
23
16
  });
24
- const remapped = command.migrationsPath === void 0 ? entries : entries.map((entry) => ({ ...entry, filename: afterMigrations(entry.filename) }));
25
- const root = at(flags.cwd, command.migrationsPath ?? command.migrationsRoot);
17
+ const root = at(flags.cwd, command.migrationsRoot);
26
18
  if (flags.dryRun) {
27
- for (const entry of remapped) deps.log(join(root, entry.filename));
19
+ for (const entry of entries) deps.log(join(root, entry.filename));
28
20
  return;
29
21
  }
30
- for (const path of await writeEntries(deps.fs, root, remapped)) deps.log(path);
22
+ for (const path of await writeEntries(deps.fs, root, entries)) deps.log(path);
31
23
  };
32
24
  export {
33
25
  applyMigrations
@@ -0,0 +1,28 @@
1
+ const caseFormats = ["Pascal", "Camel", "Snake", "Kebab", "Auto"];
2
+ const LITERAL_SEGMENTS = /* @__PURE__ */ new Set(["csharp", "typescript", "rust", "sql", "custom", "__tests__", "frontend"]);
3
+ const parseCaseFlag = (raw, name) => {
4
+ const parsed = caseFormats.find((format) => format.toLowerCase() === raw.toLowerCase());
5
+ if (parsed === void 0) throw new Error(`${name} must be one of [${caseFormats.join(", ")}] (got "${raw}").`);
6
+ return parsed;
7
+ };
8
+ const overlayCasingFlags = (settings, dialect, flags) => {
9
+ const prefix = `languages.${dialect}.casing`;
10
+ return {
11
+ ...settings,
12
+ ...flags.fileNames === void 0 ? {} : { [`${prefix}.file_names`]: flags.fileNames },
13
+ ...flags.types === void 0 ? {} : { [`${prefix}.types`]: flags.types },
14
+ ...flags.fields === void 0 ? {} : { [`${prefix}.fields`]: flags.fields },
15
+ ...flags.directories === void 0 ? {} : { [`${prefix}.directories`]: flags.directories }
16
+ };
17
+ };
18
+ const caseDirectories = (path, casing) => path.split("/").map((segment) => LITERAL_SEGMENTS.has(segment) || segment.includes(".") ? segment : casing.convertDirectories(segment)).join("/");
19
+ const typesPrefix = (dialect, casing, kind) => dialect === "typescript" ? ["typescript", casing.convertDirectories("types"), casing.convertDirectories("generated"), casing.convertDirectories(kind)].join("/") : dialect;
20
+ const repositoriesPrefix = (dialect, casing) => `${dialect}/${casing.convertDirectories("repositories")}`;
21
+ export {
22
+ caseDirectories,
23
+ caseFormats,
24
+ overlayCasingFlags,
25
+ parseCaseFlag,
26
+ repositoriesPrefix,
27
+ typesPrefix
28
+ };