@deterministic-code/datasources-cli 0.1.13 → 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 (54) 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-migrations.js +25 -14
  33. package/node_modules/@deterministic-code/datasources-code-generators/package.json +4 -2
  34. package/node_modules/@deterministic-code/deterministic-generators-common/README.md +61 -1
  35. package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/dist/specification-parser.js +128 -1
  36. package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/specification-parser.ts +1 -0
  37. package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/version-snapshot.ts +154 -0
  38. package/node_modules/@deterministic-code/deterministic-generators-common/typescript/parser/yaml-node.ts +2 -2
  39. package/node_modules/@deterministic-code/rust-generators/dist/import-generator.js +25 -15
  40. package/node_modules/@deterministic-code/rust-generators/src/import-generator.test.ts +11 -0
  41. package/node_modules/@deterministic-code/rust-generators/src/import-generator.ts +36 -18
  42. package/node_modules/@deterministic-code/typescript-generators/dist/generate-frontend-types.js +6 -1
  43. package/node_modules/@deterministic-code/typescript-generators/dist/generate-frontend-validators.js +6 -1
  44. package/node_modules/@deterministic-code/typescript-generators/dist/generate-routes.js +1 -1
  45. package/node_modules/@deterministic-code/typescript-generators/dist/generate-services.js +1 -1
  46. package/node_modules/@deterministic-code/typescript-generators/dist/import-generator.js +29 -21
  47. package/node_modules/@deterministic-code/typescript-generators/src/emit.ts +2 -5
  48. package/node_modules/@deterministic-code/typescript-generators/src/generate-frontend-types.ts +7 -1
  49. package/node_modules/@deterministic-code/typescript-generators/src/generate-frontend-validators.ts +7 -1
  50. package/node_modules/@deterministic-code/typescript-generators/src/generate-routes.ts +1 -1
  51. package/node_modules/@deterministic-code/typescript-generators/src/generate-services.ts +1 -1
  52. package/node_modules/@deterministic-code/typescript-generators/src/import-generator.test.ts +16 -0
  53. package/node_modules/@deterministic-code/typescript-generators/src/import-generator.ts +37 -22
  54. package/package.json +2 -2
@@ -45,7 +45,7 @@ const applyMigrateUp = async (command, flags, deps, env = process.env) => {
45
45
  const connection = values.DATASOURCE_URL ?? "";
46
46
  const provider = canonical(values.PROVIDER || scheme(connection) || "");
47
47
  if (!provider || !connection) {
48
- deps.log("migrate-up skipped: set PROVIDER and DATASOURCE_URL, or pass --no-migrate");
48
+ deps.log("migrate-up skipped: set PROVIDER and DATASOURCE_URL, or pass --no-auto-migrate-up");
49
49
  return;
50
50
  }
51
51
  if (inMemory(connection)) {
@@ -61,9 +61,6 @@ const applyMigrateUp = async (command, flags, deps, env = process.env) => {
61
61
  `migrate-up: provider ${provider} is not in backend.datasources (${dialects.join(", ") || "none"})`
62
62
  );
63
63
  const npx = (verb, extra = []) => [
64
- "--yes",
65
- "--package",
66
- "@deterministic-code/migraters",
67
64
  verb,
68
65
  "--provider",
69
66
  provider,
@@ -72,10 +69,7 @@ const applyMigrateUp = async (command, flags, deps, env = process.env) => {
72
69
  ...extra
73
70
  ];
74
71
  const setup = npx("migrate-setup");
75
- const up = npx(
76
- "migrate-up",
77
- command.migrationsPath ? ["--migrations-path", command.migrationsPath] : ["--migrations-root", command.migrationsRoot]
78
- );
72
+ const up = npx("migrate-up", ["--migrations-root", command.migrationsRoot]);
79
73
  deps.log(`$ npx ${setup.join(" ")}`);
80
74
  if (!flags.dryRun) await deps.run("npx", setup, flags.cwd);
81
75
  deps.log(`$ npx ${up.join(" ")}`);
@@ -0,0 +1,27 @@
1
+ const letterCases = ["Upper", "Lower", "Auto"];
2
+ const parseLetterCaseFlag = (raw, name) => {
3
+ const parsed = letterCases.find((format) => format.toLowerCase() === raw.toLowerCase());
4
+ if (parsed === void 0) throw new Error(`${name} must be one of [${letterCases.join(", ")}] (got "${raw}").`);
5
+ return parsed;
6
+ };
7
+ const overlaySqlFlags = (settings, flags) => ({
8
+ ...settings,
9
+ ...flags.pluralizeTableNames === void 0 ? {} : { "datasource.pluralize_datatable_names": flags.pluralizeTableNames ? "true" : "false" },
10
+ ...flags.tableNames === void 0 ? {} : { "datasource.casing.types": flags.tableNames },
11
+ ...flags.columnNames === void 0 ? {} : { "datasource.casing.fields": flags.columnNames },
12
+ ...flags.objectNames === void 0 ? {} : { "datasource.casing.objects": flags.objectNames },
13
+ ...flags.sqlKeywords === void 0 ? {} : { "datasource.casing.keywords": flags.sqlKeywords }
14
+ });
15
+ const sqlFlagsOf = (flags) => ({
16
+ pluralizeTableNames: flags.pluralizeTableNames,
17
+ tableNames: flags.tableNames,
18
+ columnNames: flags.columnNames,
19
+ objectNames: flags.objectNames,
20
+ sqlKeywords: flags.sqlKeywords
21
+ });
22
+ export {
23
+ letterCases,
24
+ overlaySqlFlags,
25
+ parseLetterCaseFlag,
26
+ sqlFlagsOf
27
+ };
@@ -1,14 +1,15 @@
1
1
  const migrateProviders = ["sqlite", "postgres", "mysql", "sqlserver", "oracle"];
2
2
  const migrateLanguage = (ecosystem) => ecosystem === "ts" ? "typescript" : ecosystem;
3
3
  const sqlMigrateProviders = (names) => migrateProviders.filter((name) => names.includes(name));
4
- const migrateInitCommands = (dialects, language) => dialects.length === 0 ? [] : [
5
- { command: "npm", args: ["install", "@deterministic-code/migraters"] },
4
+ const migratersPackage = (env = process.env) => env.MIGRATERS_PACKAGE || "@deterministic-code/migraters";
5
+ const migrateInitCommands = (dialects, language, env = process.env) => dialects.length === 0 ? [] : [
6
+ { command: "npm", args: ["install", migratersPackage(env)] },
6
7
  {
7
8
  command: "npx",
8
9
  args: [
9
10
  "--yes",
10
11
  "--package",
11
- "@deterministic-code/migraters",
12
+ migratersPackage(env),
12
13
  "migrate-init",
13
14
  "--language",
14
15
  language,
package/dist/parse.js CHANGED
@@ -1,42 +1,62 @@
1
1
  import { isEcosystem } from "./detect.js";
2
+ import { parseCaseFlag } from "./generate/casing.js";
3
+ import { parseLetterCaseFlag } from "./generate/sqlFlags.js";
2
4
  import { isDialect } from "./generate/GenerateRequest.js";
3
5
  const generateTargets = ["all", "types", "repositories", "datasource"];
4
6
  const help = `Usage: datasources <command> [options]
5
7
 
6
- setup [provider...] Interactive .env, provider install, and optional deterministic/
8
+ setup [name...] Interactive .env, datasource install, and optional deterministic/
7
9
  --cwd <dir> Consumer project directory (default: .)
8
- --ecosystem ts|rust|csharp Required when more than one project type exists
10
+ --languages ts|rust|csharp Project languages (default: every type found)
11
+ --datasources <name...> Datasources to install (default: existing .env)
9
12
  --dry-run Print package-manager commands without running them
10
13
  --yes Setup without prompts (defaults / existing .env)
11
14
 
12
- add <provider...> Add provider packages
15
+ add [name...] Add datasource packages
13
16
  --cwd <dir> Consumer project directory (default: .)
14
- --ecosystem ts|rust|csharp Required when more than one project type exists
17
+ --languages ts|rust|csharp Project languages (default: every type found)
18
+ --datasources <name...> Datasources to add
15
19
  --dry-run Print package-manager commands without running them
16
20
 
17
- remove <provider...> Remove provider packages
21
+ remove [name...] Remove datasource packages
18
22
  --cwd <dir> Consumer project directory (default: .)
19
- --ecosystem ts|rust|csharp Required when more than one project type exists
23
+ --languages ts|rust|csharp Project languages (default: every type found)
24
+ --datasources <name...> Datasources to remove
20
25
  --dry-run Print package-manager commands without running them
21
26
 
22
27
  generate <all|types|repositories|datasource>
23
28
  Types, repository classes, and SQL
29
+ https://deterministic-code.github.io/datasources/cli.html#generate
24
30
  --dialect csharp|typescript|rust
25
31
  --provider <name> Spec provider (default: deterministic)
26
32
  --input <dir> Deterministic project (default: ./deterministic)
27
33
  --output <dir> Generated files (default: ./generated)
28
34
  --no-migrations Skip SQL migrations (generate all writes them)
29
- --no-migrate Skip migrate-up (generate all applies them)
35
+ --no-auto-migrate-up Skip migrate-up (generate all applies them)
30
36
  --migrations-root <dir> Root prefix for generate all (default: sql)
31
- --migrations-path <dir> One migrations directory (single SQL dialect)
32
37
  --stored-procedures Emit stored-procedure migrations when the dialect can
38
+ --pluralize-table-names Plural table names in SQL and runtime queries (default)
39
+ --no-pluralize-table-names Singular table names (procedure names stay plural)
40
+ --table-names Pascal|Camel|Snake|Kebab|Auto
41
+ --column-names Pascal|Camel|Snake|Kebab|Auto
42
+ --object-names Upper|Lower|Auto
43
+ --sql-keywords Upper|Lower|Auto
44
+ --file-names Pascal|Camel|Snake|Kebab|Auto
45
+ --type-names|--types Pascal|Camel|Snake|Kebab|Auto
46
+ --field-names|--fields Pascal|Camel|Snake|Kebab|Auto
47
+ --folder-names|--directories Pascal|Camel|Snake|Kebab|Auto
33
48
 
34
49
  migrations create SQL migrations from the spec (migraters layout)
35
50
  --provider <name> Spec provider (default: deterministic)
36
51
  --input <dir> Deterministic project (default: ./deterministic)
37
52
  --migrations-root <dir> Root prefix (default: sql)
38
- --migrations-path <dir> One migrations directory (single SQL dialect)
39
53
  --stored-procedures Emit stored-procedure migrations when the dialect can
54
+ --pluralize-table-names Plural table names in SQL (default)
55
+ --no-pluralize-table-names Singular table names (procedure names stay plural)
56
+ --table-names Pascal|Camel|Snake|Kebab|Auto
57
+ --column-names Pascal|Camel|Snake|Kebab|Auto
58
+ --object-names Upper|Lower|Auto
59
+ --sql-keywords Upper|Lower|Auto
40
60
 
41
61
  version Snapshot top-level YAML as the next version-*/diff.yaml
42
62
  --cwd <dir> Consumer project directory (default: .)
@@ -48,7 +68,7 @@ const take = (argv, index, name) => argv[index] ?? (() => {
48
68
  throw new Error(`Missing value for ${name}`);
49
69
  })();
50
70
  const parseArgs = (argv, cwd = ".") => {
51
- const flags = { cwd, dryRun: false, yes: false };
71
+ const flags = { cwd, languages: [], datasources: [], dryRun: false, yes: false };
52
72
  const generate = {
53
73
  dialect: "",
54
74
  provider: "deterministic",
@@ -58,15 +78,29 @@ const parseArgs = (argv, cwd = ".") => {
58
78
  migrate: true,
59
79
  storedProcedures: false,
60
80
  migrationsRoot: "sql",
61
- migrationsPath: ""
81
+ fileNames: void 0,
82
+ types: void 0,
83
+ fields: void 0,
84
+ directories: void 0,
85
+ pluralizeTableNames: void 0,
86
+ tableNames: void 0,
87
+ columnNames: void 0,
88
+ objectNames: void 0,
89
+ sqlKeywords: void 0
62
90
  };
63
91
  const rest = [];
64
92
  for (let i = 0; i < argv.length; i++) {
65
93
  const arg = argv[i];
66
94
  if (arg === "--cwd") flags.cwd = argv[++i] ?? flags.cwd;
67
95
  else if (arg.startsWith("--cwd=")) flags.cwd = arg.slice("--cwd=".length);
68
- else if (arg === "--ecosystem") flags.ecosystem = ecosystem(argv[++i]);
69
- else if (arg.startsWith("--ecosystem=")) flags.ecosystem = ecosystem(arg.slice("--ecosystem=".length));
96
+ else if (arg === "--languages") {
97
+ i = takeList(argv, ++i, "--languages", (value) => flags.languages.push(language(value)));
98
+ } else if (arg.startsWith("--languages="))
99
+ splitList(arg.slice("--languages=".length), "--languages").forEach((value) => flags.languages.push(language(value)));
100
+ else if (arg === "--datasources") {
101
+ i = takeList(argv, ++i, "--datasources", (value) => flags.datasources.push(value));
102
+ } else if (arg.startsWith("--datasources="))
103
+ flags.datasources.push(...splitList(arg.slice("--datasources=".length), "--datasources"));
70
104
  else if (arg === "--dry-run") flags.dryRun = true;
71
105
  else if (arg === "--yes" || arg === "-y") flags.yes = true;
72
106
  else if (arg === "--dialect") generate.dialect = take(argv, ++i, "--dialect");
@@ -78,23 +112,41 @@ const parseArgs = (argv, cwd = ".") => {
78
112
  else if (arg === "--output") generate.output = take(argv, ++i, "--output");
79
113
  else if (arg.startsWith("--output=")) generate.output = arg.slice("--output=".length);
80
114
  else if (arg === "--no-migrations") generate.migrations = false;
81
- else if (arg === "--no-migrate") generate.migrate = false;
115
+ else if (arg === "--no-auto-migrate-up") generate.migrate = false;
82
116
  else if (arg === "--stored-procedures") generate.storedProcedures = true;
117
+ else if (arg === "--pluralize-table-names") generate.pluralizeTableNames = true;
118
+ else if (arg === "--no-pluralize-table-names") generate.pluralizeTableNames = false;
119
+ else if (arg === "--table-names") generate.tableNames = parseCaseFlag(take(argv, ++i, arg), arg);
120
+ else if (arg.startsWith("--table-names=")) generate.tableNames = parseCaseFlag(arg.slice("--table-names=".length), "--table-names");
121
+ else if (arg === "--column-names") generate.columnNames = parseCaseFlag(take(argv, ++i, arg), arg);
122
+ else if (arg.startsWith("--column-names=")) generate.columnNames = parseCaseFlag(arg.slice("--column-names=".length), "--column-names");
123
+ else if (arg === "--object-names") generate.objectNames = parseLetterCaseFlag(take(argv, ++i, arg), arg);
124
+ else if (arg.startsWith("--object-names=")) generate.objectNames = parseLetterCaseFlag(arg.slice("--object-names=".length), "--object-names");
125
+ else if (arg === "--sql-keywords") generate.sqlKeywords = parseLetterCaseFlag(take(argv, ++i, arg), arg);
126
+ else if (arg.startsWith("--sql-keywords=")) generate.sqlKeywords = parseLetterCaseFlag(arg.slice("--sql-keywords=".length), "--sql-keywords");
83
127
  else if (arg === "--migrations-root") generate.migrationsRoot = take(argv, ++i, "--migrations-root");
84
128
  else if (arg.startsWith("--migrations-root=")) generate.migrationsRoot = arg.slice("--migrations-root=".length);
85
- else if (arg === "--migrations-path") generate.migrationsPath = take(argv, ++i, "--migrations-path");
86
- else if (arg.startsWith("--migrations-path=")) generate.migrationsPath = arg.slice("--migrations-path=".length);
129
+ else if (arg === "--file-names" || arg === "--type-names" || arg === "--types" || arg === "--field-names" || arg === "--fields" || arg === "--folder-names" || arg === "--directories") {
130
+ const key = arg === "--file-names" ? "fileNames" : arg === "--type-names" || arg === "--types" ? "types" : arg === "--field-names" || arg === "--fields" ? "fields" : "directories";
131
+ generate[key] = parseCaseFlag(take(argv, ++i, arg), arg);
132
+ } else if (arg.startsWith("--file-names=")) generate.fileNames = parseCaseFlag(arg.slice("--file-names=".length), "--file-names");
133
+ else if (arg.startsWith("--type-names=")) generate.types = parseCaseFlag(arg.slice("--type-names=".length), "--type-names");
134
+ else if (arg.startsWith("--types=")) generate.types = parseCaseFlag(arg.slice("--types=".length), "--types");
135
+ else if (arg.startsWith("--field-names=")) generate.fields = parseCaseFlag(arg.slice("--field-names=".length), "--field-names");
136
+ else if (arg.startsWith("--fields=")) generate.fields = parseCaseFlag(arg.slice("--fields=".length), "--fields");
137
+ else if (arg.startsWith("--folder-names=")) generate.directories = parseCaseFlag(arg.slice("--folder-names=".length), "--folder-names");
138
+ else if (arg.startsWith("--directories=")) generate.directories = parseCaseFlag(arg.slice("--directories=".length), "--directories");
87
139
  else if (arg === "--help" || arg === "-h") return { command: { kind: "help" }, flags };
88
140
  else rest.push(arg);
89
141
  }
90
142
  const [verb, ...names] = rest;
91
- return { command: commandOf(verb, names, generate), flags };
143
+ return { command: commandOf(verb, names, flags.datasources, generate), flags };
92
144
  };
93
- const commandOf = (verb, names, generate) => {
145
+ const commandOf = (verb, names, datasources, generate) => {
94
146
  if (verb === void 0 || verb === "help") return { kind: "help" };
95
- if (verb === "setup") return { kind: "setup", providers: names };
96
- if (verb === "add") return { kind: "add", providers: names };
97
- if (verb === "remove") return { kind: "remove", providers: names };
147
+ if (verb === "setup") return { kind: "setup", providers: unique([...datasources, ...names]) };
148
+ if (verb === "add") return { kind: "add", providers: unique([...datasources, ...names]) };
149
+ if (verb === "remove") return { kind: "remove", providers: unique([...datasources, ...names]) };
98
150
  if (verb === "generate") return generateCommand(names, generate);
99
151
  if (verb === "migrations") return migrationsCommand(names, generate);
100
152
  if (verb === "version") return versionCommand(names, generate.input);
@@ -111,7 +163,11 @@ ${help}`);
111
163
  input: generate.input,
112
164
  storedProcedures: generate.storedProcedures,
113
165
  migrationsRoot: generate.migrationsRoot,
114
- ...generate.migrationsPath === "" ? {} : { migrationsPath: generate.migrationsPath }
166
+ pluralizeTableNames: generate.pluralizeTableNames,
167
+ tableNames: generate.tableNames,
168
+ columnNames: generate.columnNames,
169
+ objectNames: generate.objectNames,
170
+ sqlKeywords: generate.sqlKeywords
115
171
  };
116
172
  };
117
173
  const versionCommand = (names, input) => {
@@ -137,12 +193,33 @@ ${help}`);
137
193
  migrate: generate.migrate,
138
194
  storedProcedures: generate.storedProcedures,
139
195
  migrationsRoot: generate.migrationsRoot,
140
- ...generate.migrationsPath === "" ? {} : { migrationsPath: generate.migrationsPath }
196
+ fileNames: generate.fileNames,
197
+ types: generate.types,
198
+ fields: generate.fields,
199
+ directories: generate.directories,
200
+ pluralizeTableNames: generate.pluralizeTableNames,
201
+ tableNames: generate.tableNames,
202
+ columnNames: generate.columnNames,
203
+ objectNames: generate.objectNames,
204
+ sqlKeywords: generate.sqlKeywords
141
205
  };
142
206
  };
143
- const ecosystem = (value) => value && isEcosystem(value) ? value : (() => {
144
- throw new Error(`Unknown --ecosystem ${value}. Use ts|rust|csharp`);
207
+ const language = (value) => value === "typescript" ? "ts" : isEcosystem(value) ? value : (() => {
208
+ throw new Error(`Unknown --languages ${value}. Use ts|rust|csharp`);
145
209
  })();
210
+ const unique = (names) => [...new Set(names)];
211
+ const splitList = (value, name) => {
212
+ const names = value.split(",").map((part) => part.trim()).filter(Boolean);
213
+ return names.length ? names : (() => {
214
+ throw new Error(`Missing value for ${name}`);
215
+ })();
216
+ };
217
+ const takeList = (argv, index, name, push) => {
218
+ if (index >= argv.length || argv[index].startsWith("-")) throw new Error(`Missing value for ${name}`);
219
+ let i = index;
220
+ while (i < argv.length && !argv[i].startsWith("-")) push(argv[i++]);
221
+ return i - 1;
222
+ };
146
223
  export {
147
224
  generateTargets,
148
225
  help,
package/dist/run.js CHANGED
@@ -1,8 +1,7 @@
1
- import { access, copyFile, mkdir } from "node:fs/promises";
2
1
  import { join } from "node:path";
3
- import { catalog, requireProviders } from "./catalog.js";
2
+ import { requireProviders } from "./catalog.js";
4
3
  import { addCommands, removeCommands } from "./commands.js";
5
- import { csharpTarget, detect, resolveEcosystem } from "./detect.js";
4
+ import { csharpTarget, detect, requireProjects, resolveLanguages } from "./detect.js";
6
5
  import { parseEnv, serializeEnv, updateEnvProviders } from "./env.js";
7
6
  import { defaultDatasourceYaml } from "./generate/loadSpec.js";
8
7
  import {
@@ -15,9 +14,11 @@ import { applyMigrations } from "./generate/applyMigrations.js";
15
14
  import { applyMigrateUp } from "./generate/migrateUp.js";
16
15
  import { migrateInitCommands, migrateLanguage, sqlMigrateProviders } from "./migrateInit.js";
17
16
  import { help, parseArgs } from "./parse.js";
17
+ import { sqlFlagsOf } from "./generate/sqlFlags.js";
18
18
  import { applyVersion } from "./version/applyVersion.js";
19
19
  import { answersForSetup, envFromAnswers, promptSetup, starterTypesYaml } from "./setup.js";
20
20
  import { wireRegister } from "./wireRegister.js";
21
+ import { wireRustProviders } from "./wireRustProviders.js";
21
22
  const run = async (argv, deps, cwd = ".") => {
22
23
  try {
23
24
  const parsed = parseArgs(argv, cwd);
@@ -45,7 +46,7 @@ const dispatch = async (command, flags, deps) => {
45
46
  input: command.input,
46
47
  storedProcedures: command.storedProcedures,
47
48
  migrationsRoot: command.migrationsRoot,
48
- ...command.migrationsPath === void 0 ? {} : { migrationsPath: command.migrationsPath }
49
+ ...sqlFlagsOf(command)
49
50
  },
50
51
  flags,
51
52
  deps
@@ -53,33 +54,41 @@ const dispatch = async (command, flags, deps) => {
53
54
  if (command.migrate) await applyMigrateUp(command, flags, deps);
54
55
  };
55
56
  const needNames = (names, verb) => names.length ? names : (() => {
56
- throw new Error(`Usage: datasources ${verb} <provider...>`);
57
+ throw new Error(`Usage: datasources ${verb} --datasources <name...>`);
57
58
  })();
58
59
  const applySetup = async (flags, deps, requested) => {
59
60
  const entries = await deps.fs.readdir(flags.cwd);
60
61
  const found = detect(entries);
61
- const existing = await readEnv(deps.fs, flags.cwd);
62
+ const existing = {
63
+ ...await readEnv(deps.fs, flags.cwd),
64
+ ...process.env.INSTALLED_PROVIDERS ? { INSTALLED_PROVIDERS: process.env.INSTALLED_PROVIDERS } : {},
65
+ ...process.env.PROVIDER ? { PROVIDER: process.env.PROVIDER } : {},
66
+ ...process.env.DATASOURCE_URL ? { DATASOURCE_URL: process.env.DATASOURCE_URL } : {}
67
+ };
62
68
  const specDir = join(flags.cwd, "deterministic");
63
69
  const typesPath = join(specDir, "types.yaml");
64
70
  const datasourcePath = join(specDir, "datasource.yaml");
65
71
  const missingSpec = await optional(deps.fs, typesPath) === void 0;
66
- const answers = flags.yes ? { ...answersForSetup(existing, requested), ecosystem: flags.ecosystem, scaffold: false } : await promptSetup(deps.prompt, existing, found, flags.ecosystem, missingSpec, requested);
67
- const ecosystem = resolveEcosystem(found, answers.ecosystem ?? flags.ecosystem);
72
+ const answers = flags.yes ? { ...answersForSetup(existing, requested), languages: flags.languages, scaffold: false } : await promptSetup(deps.prompt, existing, found, flags.languages, missingSpec, requested);
73
+ const languages = resolveLanguages(found, answers.languages ?? flags.languages);
74
+ requireProjects(languages, entries);
68
75
  const selected = requireProviders(answers.providers);
69
- const installed = await readInstalled(deps.fs, flags.cwd, ecosystem, entries);
70
76
  if (!flags.dryRun) await writeEnv(deps.fs, flags.cwd, { ...existing, ...envFromAnswers(answers) });
71
- await applyPackages(
72
- flags,
73
- deps,
74
- ecosystem,
75
- entries,
76
- selected.filter((name) => !installed.includes(name)),
77
- installed.filter((name) => !selected.includes(name)),
78
- selected
79
- );
80
- await applyMigrateInit(flags, deps, ecosystem, selected);
81
- const dialect = migrateLanguage(ecosystem);
82
- await wireRegister(flags.cwd, "generated", dialect, flags.dryRun, deps);
77
+ for (const language of languages) {
78
+ const installed = await readInstalled(deps.fs, flags.cwd, language, entries);
79
+ await applyPackages(
80
+ flags,
81
+ deps,
82
+ language,
83
+ entries,
84
+ selected.filter((name) => !installed.includes(name)),
85
+ installed.filter((name) => !selected.includes(name)),
86
+ selected
87
+ );
88
+ await applyMigrateInit(flags, deps, language, selected);
89
+ const dialect = migrateLanguage(language);
90
+ await wireRegister(flags.cwd, "generated", dialect, flags.dryRun, deps);
91
+ }
83
92
  if (!answers.scaffold) return;
84
93
  if (flags.dryRun) {
85
94
  deps.log(typesPath);
@@ -93,16 +102,19 @@ const applySetup = async (flags, deps, requested) => {
93
102
  const applyAddRemove = async (flags, deps, add, remove) => {
94
103
  const entries = await deps.fs.readdir(flags.cwd);
95
104
  const found = detect(entries);
96
- const ecosystem = resolveEcosystem(found, flags.ecosystem);
97
- const installed = await readInstalled(deps.fs, flags.cwd, ecosystem, entries);
98
- const remaining = [
99
- ...installed.filter((name) => !remove.includes(name)),
100
- ...add.filter((name) => !installed.includes(name))
101
- ];
105
+ const languages = resolveLanguages(found, flags.languages);
106
+ requireProjects(languages, entries);
102
107
  const env = await readEnvFile(deps.fs, join(flags.cwd, ".env"));
103
108
  if (env && !flags.dryRun) await writeEnv(deps.fs, flags.cwd, updateEnvProviders(env, add, remove));
104
- await applyPackages(flags, deps, ecosystem, entries, add, remove, remaining);
105
- await applyMigrateInit(flags, deps, ecosystem, remaining);
109
+ for (const language of languages) {
110
+ const installed = await readInstalled(deps.fs, flags.cwd, language, entries);
111
+ const remaining = [
112
+ ...installed.filter((name) => !remove.includes(name)),
113
+ ...add.filter((name) => !installed.includes(name))
114
+ ];
115
+ await applyPackages(flags, deps, language, entries, add, remove, remaining);
116
+ await applyMigrateInit(flags, deps, language, remaining);
117
+ }
106
118
  };
107
119
  const applyPackages = async (flags, deps, ecosystem, entries, add, remove, remaining) => {
108
120
  const procs = [
@@ -113,17 +125,7 @@ const applyPackages = async (flags, deps, ecosystem, entries, add, remove, remai
113
125
  deps.log(`$ ${proc.command} ${proc.args.join(" ")}`);
114
126
  if (!flags.dryRun) await deps.run(proc.command, proc.args, flags.cwd);
115
127
  }
116
- if (ecosystem === "rust") {
117
- for (const name of add) {
118
- const proc = { command: "cargo", args: ["build", "-p", catalog[name].cargo] };
119
- deps.log(`$ ${proc.command} ${proc.args.join(" ")}`);
120
- if (!flags.dryRun) {
121
- await deps.run(proc.command, proc.args, flags.cwd);
122
- const copied = await copyPlugin(flags.cwd, catalog[name].cargo);
123
- if (copied) deps.log(`copy ${copied}`);
124
- }
125
- }
126
- }
128
+ if (ecosystem === "rust") await wireRustProviders(flags.cwd, remaining, flags.dryRun, deps);
127
129
  };
128
130
  const applyMigrateInit = async (flags, deps, ecosystem, remaining) => {
129
131
  for (const proc of migrateInitCommands(sqlMigrateProviders(remaining), migrateLanguage(ecosystem))) {
@@ -161,33 +163,6 @@ const optional = async (fs, path) => {
161
163
  return void 0;
162
164
  }
163
165
  };
164
- const pluginFileName = (cargoName, platform = process.platform) => {
165
- const stem = cargoName.replaceAll("-", "_");
166
- return platform === "win32" ? `${stem}.dll` : platform === "darwin" ? `lib${stem}.dylib` : `lib${stem}.so`;
167
- };
168
- const copyPlugin = async (cwd, cargoName) => {
169
- const file = pluginFileName(cargoName);
170
- const source = await firstFile(
171
- [0, 1, 2].map((up) => join(cwd, ...Array.from({ length: up }, () => ".."), "target", "debug", file))
172
- );
173
- if (!source) return void 0;
174
- const dest = join(cwd, "plugins", file);
175
- await mkdir(join(cwd, "plugins"), { recursive: true });
176
- await copyFile(source, dest);
177
- return dest;
178
- };
179
- const firstFile = async (paths) => {
180
- for (const path of paths) {
181
- try {
182
- await access(path);
183
- return path;
184
- } catch {
185
- continue;
186
- }
187
- }
188
- return void 0;
189
- };
190
166
  export {
191
- pluginFileName,
192
167
  run
193
168
  };
package/dist/setup.js CHANGED
@@ -15,16 +15,15 @@ types:
15
15
  size: 128
16
16
  `;
17
17
  const answersForSetup = (env, requested = []) => requested.length ? answersFromEnv({ ...env, INSTALLED_PROVIDERS: requested.join(",") }) : answersFromEnv(env);
18
- const promptSetup = async (prompt, env, found, explicit, askScaffold = false, requested = []) => {
18
+ const promptSetup = async (prompt, env, found, explicit = [], askScaffold = false, requested = []) => {
19
19
  const defaults = answersForSetup(env, requested);
20
- const ecosystem = explicit ?? (found.length === 1 ? found[0] : found.length === 0 ? void 0 : ecosystemOr(await prompt.select({
21
- message: "Project type",
22
- choices: found.map((value) => ({ name: value, value })),
23
- default: found[0]
24
- })));
20
+ const picked = explicit.length ? explicit : found.length === 1 ? found : found.length === 0 ? [] : (await prompt.checkbox({
21
+ message: "Languages",
22
+ choices: found.map((value) => ({ name: value, value, checked: true }))
23
+ })).filter(isEcosystem);
25
24
  const fromUrl = scheme(defaults.url);
26
25
  const selected = await prompt.checkbox({
27
- message: "Providers",
26
+ message: "Datasources",
28
27
  choices: providers.map((value) => ({
29
28
  name: value,
30
29
  value,
@@ -37,9 +36,8 @@ const promptSetup = async (prompt, env, found, explicit, askScaffold = false, re
37
36
  });
38
37
  const schema = await prompt.input({ message: "schema", default: defaults.schema });
39
38
  const scaffold = askScaffold ? await prompt.confirm({ message: "Create deterministic directory", default: true }) : false;
40
- return { ecosystem, providers: selected, url, schema, scaffold };
39
+ return { languages: picked.length ? picked : void 0, providers: selected, url, schema, scaffold };
41
40
  };
42
- const ecosystemOr = (value) => isEcosystem(value) ? value : void 0;
43
41
  const envFromAnswers = (answers) => {
44
42
  const provider = scheme(answers.url) ?? answers.providers[0] ?? "sqlite";
45
43
  return {
@@ -1,17 +1,21 @@
1
1
  import { isAbsolute, join } from "node:path";
2
- import { applyDocument, asDocument, parseYaml, render } from "./render.js";
2
+ import { VersionSnapshot } from "@deterministic-code/deterministic-generators-common/parser";
3
+ import { parseYaml, render } from "./render.js";
3
4
  const versionDir = /^version-\d{8}T\d{6}Z-(\d{3})$/;
4
5
  const at = (root, path) => isAbsolute(path) ? path : join(root, path);
5
6
  const applyVersion = async (command, flags, deps, now = () => /* @__PURE__ */ new Date()) => {
6
7
  const root = at(flags.cwd, command.input);
7
8
  const names = await deps.fs.readdir(root);
8
9
  const versions = names.filter((name) => versionDir.test(name)).sort();
9
- let before = {};
10
- for (const dir of versions) {
11
- const text = await optional(deps.fs, join(root, dir, "diff.yaml"));
12
- if (text === void 0 || text === "") continue;
13
- before = applyDocument(before, asDocument(parseYaml(text), `${dir}/diff.yaml`));
14
- }
10
+ const diffs = (await Promise.all(
11
+ versions.map(async (dir) => {
12
+ const listed = await deps.fs.readdir(join(root, dir));
13
+ if (!listed.includes("diff.yaml")) return void 0;
14
+ const text = await deps.fs.readFile(join(root, dir, "diff.yaml"));
15
+ return text === "" ? void 0 : parseYaml(text);
16
+ })
17
+ )).filter((diff) => diff !== void 0);
18
+ const before = VersionSnapshot.combine(diffs).documents;
15
19
  const current = names.filter((name) => name.endsWith(".yaml")).sort();
16
20
  const paths = [.../* @__PURE__ */ new Set([...Object.keys(before), ...current])];
17
21
  const yaml = render(
@@ -39,13 +43,6 @@ const nextSeq = (versions) => {
39
43
  };
40
44
  const pad = (n) => String(n).padStart(3, "0");
41
45
  const stamp = (date) => date.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}/, "");
42
- const optional = async (fs, path) => {
43
- try {
44
- return await fs.readFile(path);
45
- } catch {
46
- return void 0;
47
- }
48
- };
49
46
  export {
50
47
  applyVersion
51
48
  };
@@ -4,20 +4,6 @@ const delta = (before, after) => {
4
4
  const value = build(before, after);
5
5
  return value === unchanged ? void 0 : value;
6
6
  };
7
- const apply = (before, next) => {
8
- if (next == null) return before;
9
- if (isKeyedCollection(before) && isKeyedSplit(next)) return applyKeyed(before, next);
10
- if (isObject(before) && isObject(next)) return applyObject(before, next);
11
- return next;
12
- };
13
- const applyFile = (before, envelope) => {
14
- if (envelope == null) return before;
15
- if (isObject(envelope)) {
16
- if (envelope.removed === true && Object.keys(envelope).length === 1) return void 0;
17
- if (envelope.added === true) return envelope.content;
18
- }
19
- return apply(before ?? null, envelope);
20
- };
21
7
  const build = (before, after) => {
22
8
  if (isDeepStrictEqual(before, after)) return unchanged;
23
9
  if (isKeyedCollection(before) && isKeyedCollection(after)) return keyedSplit(before, after);
@@ -60,40 +46,6 @@ const objectDelta = (beforeObj, afterObj) => {
60
46
  if (removed.length) result.removed = removed;
61
47
  return Object.keys(result).length === 0 ? unchanged : result;
62
48
  };
63
- const applyKeyed = (beforeVal, split) => {
64
- const removed = new Set(split.removed ?? []);
65
- const changed = /* @__PURE__ */ new Map();
66
- for (const entry of split.changed ?? []) {
67
- const key = Object.keys(entry)[0];
68
- changed.set(key, entry[key]);
69
- }
70
- return [
71
- ...beforeVal.flatMap((entry) => {
72
- const key = Object.keys(entry)[0];
73
- if (removed.has(key)) return [];
74
- return changed.has(key) ? [{ [key]: apply(entry[key], changed.get(key)) }] : [entry];
75
- }),
76
- ...split.added ?? []
77
- ];
78
- };
79
- const applyObject = (beforeVal, next) => {
80
- const removed = new Set(Array.isArray(next.removed) ? next.removed : []);
81
- const out = {};
82
- for (const key of Object.keys(beforeVal)) {
83
- if (removed.has(key)) continue;
84
- out[key] = key in next ? apply(beforeVal[key], next[key]) : beforeVal[key];
85
- }
86
- for (const key of Object.keys(next)) {
87
- if (key === "removed" && Array.isArray(next.removed)) continue;
88
- if (!(key in beforeVal)) out[key] = next[key];
89
- }
90
- return out;
91
- };
92
- const isKeyedSplit = (value) => {
93
- if (!isObject(value)) return false;
94
- const allowed = /* @__PURE__ */ new Set(["added", "changed", "removed"]);
95
- return Object.keys(value).every((key) => allowed.has(key));
96
- };
97
49
  const isObject = (value) => value !== null && value !== void 0 && typeof value === "object" && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
98
50
  const isKeyedCollection = (value) => Array.isArray(value) && value.length > 0 && value.every(
99
51
  (item) => item !== null && typeof item === "object" && !Array.isArray(item) && Object.keys(item).length === 1
@@ -107,8 +59,6 @@ const keyedMap = (arr) => {
107
59
  return map;
108
60
  };
109
61
  export {
110
- apply,
111
- applyFile,
112
62
  delta,
113
63
  isObject
114
64
  };
@@ -1,5 +1,5 @@
1
1
  import { parse, stringify } from "yaml";
2
- import { applyFile, delta, isObject } from "./delta.js";
2
+ import { delta, isObject } from "./delta.js";
3
3
  const render = (files) => {
4
4
  const out = {};
5
5
  for (const { path, before, after } of files) {
@@ -18,20 +18,10 @@ const render = (files) => {
18
18
  return Object.keys(out).length === 0 ? "" : stringify(out);
19
19
  };
20
20
  const parseYaml = (text) => parse(text);
21
- const applyDocument = (before, doc) => {
22
- const out = { ...before };
23
- for (const [path, envelope] of Object.entries(doc)) {
24
- const next = applyFile(out[path], envelope);
25
- if (next === void 0) delete out[path];
26
- else out[path] = next;
27
- }
28
- return out;
29
- };
30
21
  const asDocument = (value, source) => isObject(value) ? value : (() => {
31
22
  throw new Error(`${source}: expected a mapping`);
32
23
  })();
33
24
  export {
34
- applyDocument,
35
25
  asDocument,
36
26
  parseYaml,
37
27
  render