@danceroutine/tango-codegen 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,3 +2,5 @@
2
2
  * Domain boundary barrel: centralizes this subdomain's public contract.
3
3
  */
4
4
  export { registerCodegenCommands } from './registerCodegenCommands';
5
+ export { loadProjectModule } from './loadProjectModule';
6
+ export { runGenerateRelationsCommand, withGenerateRelationsCommand } from './runGenerateRelationsCommand';
@@ -1,4 +1,5 @@
1
- import "../frameworks-CfwU6n_o.js";
2
- import { registerCodegenCommands } from "../commands-lJSW2Kuf.js";
1
+ import "../relations-uMuViSQC.js";
2
+ import "../frameworks-CLOeazGj.js";
3
+ import { loadProjectModule, registerCodegenCommands, runGenerateRelationsCommand, withGenerateRelationsCommand } from "../commands-CFAq4wXC.js";
3
4
 
4
- export { registerCodegenCommands };
5
+ export { loadProjectModule, registerCodegenCommands, runGenerateRelationsCommand, withGenerateRelationsCommand };
@@ -0,0 +1,15 @@
1
+ import { ModelRegistry } from '@danceroutine/tango-schema';
2
+ type ProjectModuleLoadResult = {
3
+ loaded: Record<string, unknown>;
4
+ modelTypeAccessors: Record<string, string>;
5
+ registry: ModelRegistry;
6
+ };
7
+ /**
8
+ * Load a Tango app module and discover model export accessors suitable for
9
+ * generated ambient type references.
10
+ */
11
+ export declare function loadProjectModule(modulePath: string, options?: {
12
+ projectRoot?: string;
13
+ outputDir?: string;
14
+ }): Promise<ProjectModuleLoadResult>;
15
+ export {};
@@ -0,0 +1,11 @@
1
+ import type { Argv } from 'yargs';
2
+ type GenerateRelationsCommandArgs = {
3
+ models: string;
4
+ outDir: string;
5
+ };
6
+ /**
7
+ * Generate the app-local ambient relation registry from a finalized model graph.
8
+ */
9
+ export declare function runGenerateRelationsCommand({ models, outDir }: GenerateRelationsCommandArgs): Promise<void>;
10
+ export declare function withGenerateRelationsCommand(parser: Argv): Argv;
11
+ export {};
@@ -0,0 +1,297 @@
1
+ import { __export } from "./chunk-BkvOhyD0.js";
2
+ import { writeRelationRegistryArtifacts } from "./relations-uMuViSQC.js";
3
+ import { FrameworkScaffoldRegistry, PACKAGE_MANAGER, SCAFFOLD_DATABASE_DIALECT, SUPPORTED_FRAMEWORK, scaffoldProject } from "./frameworks-CLOeazGj.js";
4
+ import { GENERATED_RELATION_REGISTRY_DIRNAME, GENERATED_RELATION_REGISTRY_METADATA_FILENAME, GENERATED_RELATION_REGISTRY_TYPES_FILENAME, ModelRegistry } from "@danceroutine/tango-schema";
5
+ import { extname, relative, resolve as resolve$1, resolve as resolve$2 } from "node:path";
6
+ import { getLogger, getLogger as getLogger$1, getLogger as getLogger$2 } from "@danceroutine/tango-core";
7
+ import { basename, basename as basename$1, join, resolve, resolve as resolve$3 } from "path/posix";
8
+ import { spawnSync } from "child_process";
9
+ import { pathToFileURL } from "node:url";
10
+ import { createJiti } from "jiti";
11
+ import { readFile, readdir } from "fs/promises";
12
+
13
+ //#region src/commands/runInstall.ts
14
+ function runInstall(packageManager, cwd) {
15
+ const argsByPackageManager = {
16
+ pnpm: ["install"],
17
+ npm: ["install"],
18
+ yarn: ["install"],
19
+ bun: ["install"]
20
+ };
21
+ const result = spawnSync(packageManager, [...argsByPackageManager[packageManager]], {
22
+ cwd,
23
+ stdio: "inherit",
24
+ env: process.env
25
+ });
26
+ if (result.status !== 0) throw new Error(`Dependency install failed with ${packageManager}. Exit code: ${String(result.status ?? "unknown")}.`);
27
+ }
28
+
29
+ //#endregion
30
+ //#region src/commands/runNewCommand.ts
31
+ async function runNewCommand({ path, name, framework, packageManager, dialect, install, force, seed: includeSeed }) {
32
+ const registry = FrameworkScaffoldRegistry.createDefault();
33
+ const strategy = registry.get(framework);
34
+ const requestedPath = path ?? name ?? "tango-app";
35
+ const projectName = name ?? basename$1(requestedPath);
36
+ const targetDir = resolve$3(process.cwd(), requestedPath);
37
+ const context = {
38
+ projectName,
39
+ targetDir,
40
+ framework,
41
+ packageManager,
42
+ dialect,
43
+ includeSeed
44
+ };
45
+ await scaffoldProject(context, strategy, { force });
46
+ if (install) runInstall(packageManager, targetDir);
47
+ const logger$1 = getLogger$2("tango.codegen");
48
+ logger$1.info(`Scaffold complete: ${targetDir}`);
49
+ logger$1.info(`Framework: ${strategy.name}`);
50
+ logger$1.info(`Run next: cd ${targetDir} && ${packageManager} run dev`);
51
+ }
52
+ function withNewCommand(parser) {
53
+ return parser.command("new [name]", "Bootstrap a new Tango project", (builder) => builder.positional("name", {
54
+ type: "string",
55
+ describe: "Project name (used as directory when --path is omitted)."
56
+ }).option("framework", {
57
+ type: "string",
58
+ choices: Object.values(SUPPORTED_FRAMEWORK),
59
+ demandOption: true,
60
+ describe: "Host framework scaffold to generate."
61
+ }).option("path", {
62
+ type: "string",
63
+ describe: "Target directory for generated project files."
64
+ }).option("package-manager", {
65
+ type: "string",
66
+ choices: Object.values(PACKAGE_MANAGER),
67
+ default: PACKAGE_MANAGER.PNPM,
68
+ describe: "Package manager used for follow-up instructions and optional install."
69
+ }).option("dialect", {
70
+ type: "string",
71
+ choices: Object.values(SCAFFOLD_DATABASE_DIALECT),
72
+ default: SCAFFOLD_DATABASE_DIALECT.SQLITE,
73
+ describe: "Database dialect default for generated config."
74
+ }).option("install", {
75
+ type: "boolean",
76
+ default: false,
77
+ describe: "Install dependencies after scaffolding."
78
+ }).option("force", {
79
+ type: "boolean",
80
+ default: false,
81
+ describe: "Allow writing into a non-empty target directory."
82
+ }).option("seed", {
83
+ type: "boolean",
84
+ default: true,
85
+ describe: "Include seed/bootstrap sample artifacts when supported by the framework strategy."
86
+ }), async ({ name, framework, path, packageManager, dialect, install, force, seed }) => {
87
+ await runNewCommand({
88
+ name,
89
+ framework,
90
+ path,
91
+ packageManager,
92
+ dialect,
93
+ install,
94
+ force,
95
+ seed
96
+ });
97
+ });
98
+ }
99
+
100
+ //#endregion
101
+ //#region src/commands/loadProjectModule.ts
102
+ const TS_EXTENSIONS = new Set([
103
+ ".ts",
104
+ ".tsx",
105
+ ".mts",
106
+ ".cts"
107
+ ]);
108
+ function isTypeScriptModule(modulePath) {
109
+ return TS_EXTENSIONS.has(extname(modulePath).toLowerCase());
110
+ }
111
+ function isModelLike(value) {
112
+ return typeof value === "object" && value !== null && "metadata" in value && typeof value.metadata?.key === "string";
113
+ }
114
+ function toImportSpecifier(absoluteModulePath, outputDir) {
115
+ const relativePath = relative(outputDir, absoluteModulePath).replaceAll("\\", "/");
116
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
117
+ }
118
+ function collectModelTypeAccessors(loaded, importSpecifier, accessors = new Map()) {
119
+ const models = [];
120
+ const registerAccessor = (model, accessor) => {
121
+ models.push(model);
122
+ const existing = accessors.get(model.metadata.key);
123
+ if (!existing || accessor.length < existing.length) accessors.set(model.metadata.key, accessor);
124
+ };
125
+ const inspectValue = (value, accessorSegments) => {
126
+ if (isModelLike(value)) {
127
+ const accessor = accessorSegments.map((segment) => `[${JSON.stringify(segment)}]`).join("");
128
+ registerAccessor(value, `typeof import(${JSON.stringify(importSpecifier)})${accessor}`);
129
+ return;
130
+ }
131
+ if (typeof value !== "object" || value === null) return;
132
+ for (const [key, nested] of Object.entries(value)) if (isModelLike(nested)) {
133
+ const accessor = [...accessorSegments, key].map((segment) => `[${JSON.stringify(segment)}]`).join("");
134
+ registerAccessor(nested, `typeof import(${JSON.stringify(importSpecifier)})${accessor}`);
135
+ }
136
+ };
137
+ for (const [key, value] of Object.entries(loaded)) inspectValue(value, [key]);
138
+ return {
139
+ accessors: Object.fromEntries(accessors),
140
+ models
141
+ };
142
+ }
143
+ async function loadProjectModule(modulePath, options) {
144
+ const projectRoot = options?.projectRoot ?? process.cwd();
145
+ const absoluteModulePath = resolve$2(projectRoot, modulePath);
146
+ const outputDir = options?.outputDir ?? resolve$2(projectRoot, ".tango");
147
+ const registry = new ModelRegistry();
148
+ const executeImport = async () => {
149
+ if (isTypeScriptModule(absoluteModulePath)) {
150
+ const jiti = createJiti(resolve$2(projectRoot, "tango.config.ts"), {
151
+ interopDefault: true,
152
+ moduleCache: false
153
+ });
154
+ return await jiti.import(absoluteModulePath);
155
+ }
156
+ return await import(pathToFileURL(absoluteModulePath).href);
157
+ };
158
+ const loaded = await ModelRegistry.runWithRegistry(registry, executeImport);
159
+ const collected = collectModelTypeAccessors(loaded, toImportSpecifier(absoluteModulePath, outputDir));
160
+ const effectiveRegistry = registry.values().length > 0 ? registry : collected.models.length > 0 ? ModelRegistry.getOwner(collected.models[0]) : registry;
161
+ return {
162
+ loaded,
163
+ modelTypeAccessors: collected.accessors,
164
+ registry: effectiveRegistry
165
+ };
166
+ }
167
+
168
+ //#endregion
169
+ //#region src/commands/runGenerateRelationsCommand.ts
170
+ const logger = getLogger$1("tango.codegen");
171
+ async function runGenerateRelationsCommand({ models, outDir }) {
172
+ const outputDir = resolve$1(process.cwd(), outDir);
173
+ const { registry, modelTypeAccessors } = await loadProjectModule(models, { outputDir });
174
+ const written = await writeRelationRegistryArtifacts({
175
+ registry,
176
+ modelTypeAccessors,
177
+ outputDir
178
+ });
179
+ logger.info(`Generated relation registry: ${written.typesFilepath}`);
180
+ logger.info(`Generated relation metadata: ${written.metadataFilepath}`);
181
+ }
182
+ function withGenerateRelationsCommand(parser) {
183
+ return parser.command("relations", "Generate app-local ambient relation typing from Tango models", (builder) => builder.option("models", {
184
+ type: "string",
185
+ demandOption: true,
186
+ describe: "Path to the module exporting Tango Model definitions."
187
+ }).option("out-dir", {
188
+ type: "string",
189
+ default: GENERATED_RELATION_REGISTRY_DIRNAME,
190
+ describe: `Directory that receives ${GENERATED_RELATION_REGISTRY_TYPES_FILENAME} and ${GENERATED_RELATION_REGISTRY_METADATA_FILENAME}.`
191
+ }), async ({ models, outDir }) => {
192
+ await runGenerateRelationsCommand({
193
+ models,
194
+ outDir
195
+ });
196
+ });
197
+ }
198
+
199
+ //#endregion
200
+ //#region src/commands/runInitCommand.ts
201
+ async function detectPackageManager(targetDir) {
202
+ try {
203
+ const entries = await readdir(targetDir);
204
+ if (entries.includes("pnpm-lock.yaml")) return PACKAGE_MANAGER.PNPM;
205
+ if (entries.includes("package-lock.json")) return PACKAGE_MANAGER.NPM;
206
+ if (entries.includes("yarn.lock")) return PACKAGE_MANAGER.YARN;
207
+ if (entries.includes("bun.lockb") || entries.includes("bun.lock")) return PACKAGE_MANAGER.BUN;
208
+ } catch {}
209
+ return PACKAGE_MANAGER.PNPM;
210
+ }
211
+ async function runInitCommand({ framework, path, dialect, skipExisting, force }) {
212
+ const registry = FrameworkScaffoldRegistry.createDefault();
213
+ const strategy = registry.get(framework);
214
+ const targetDir = resolve(process.cwd(), path);
215
+ let projectName = basename(targetDir);
216
+ try {
217
+ const pkgPath = join(targetDir, "package.json");
218
+ const raw = await readFile(pkgPath, "utf8");
219
+ const pkg = JSON.parse(raw);
220
+ if (typeof pkg.name === "string" && pkg.name.length > 0) projectName = pkg.name;
221
+ } catch {}
222
+ const packageManager = await detectPackageManager(targetDir);
223
+ const context = {
224
+ projectName,
225
+ targetDir,
226
+ framework,
227
+ packageManager,
228
+ dialect,
229
+ includeSeed: true
230
+ };
231
+ await scaffoldProject(context, strategy, {
232
+ mode: "init",
233
+ skipExisting,
234
+ force
235
+ });
236
+ const logger$1 = getLogger("tango.codegen");
237
+ logger$1.info(`Tango init complete: ${targetDir}`);
238
+ logger$1.info(`Install dependencies: ${strategy.getTangoInstallOneLiner(packageManager, context)}`);
239
+ if (framework === SUPPORTED_FRAMEWORK.EXPRESS) logger$1.info("Mount Tango: import { registerTango } from './src/tango.js'; await registerTango(app);");
240
+ if (framework === SUPPORTED_FRAMEWORK.NUXT) logger$1.info("Wire Tango handlers: register Nitro serverHandlers in nuxt.config.ts and export NuxtAdapter-backed handlers from server/tango/.");
241
+ }
242
+ function withInitCommand(parser) {
243
+ return parser.command("init", "Add Tango to an existing project (only Tango-layer files)", (builder) => builder.option("framework", {
244
+ type: "string",
245
+ choices: Object.values(SUPPORTED_FRAMEWORK),
246
+ demandOption: true,
247
+ describe: "Host framework (express, next, or nuxt)."
248
+ }).option("path", {
249
+ type: "string",
250
+ default: ".",
251
+ describe: "Target directory (default: current directory)."
252
+ }).option("dialect", {
253
+ type: "string",
254
+ choices: Object.values(SCAFFOLD_DATABASE_DIALECT),
255
+ default: SCAFFOLD_DATABASE_DIALECT.SQLITE,
256
+ describe: "Database dialect for generated config."
257
+ }).option("skip-existing", {
258
+ type: "boolean",
259
+ default: true,
260
+ describe: "Do not overwrite existing files."
261
+ }).option("force", {
262
+ type: "boolean",
263
+ default: false,
264
+ describe: "Overwrite existing files when set."
265
+ }), async ({ framework, path, dialect, skipExisting, force }) => {
266
+ await runInitCommand({
267
+ framework,
268
+ path,
269
+ dialect,
270
+ skipExisting,
271
+ force
272
+ });
273
+ });
274
+ }
275
+
276
+ //#endregion
277
+ //#region src/commands/registerCodegenCommands.ts
278
+ function registerCodegenCommands(parser) {
279
+ const withTopLevelNew = withNewCommand(parser);
280
+ const withTopLevelInit = withInitCommand(withTopLevelNew);
281
+ const withTopLevelRelations = withGenerateRelationsCommand(withTopLevelInit);
282
+ return withTopLevelRelations.command("codegen <command>", "Code generation command group", (builder) => withGenerateRelationsCommand(withInitCommand(withNewCommand(builder))));
283
+ }
284
+
285
+ //#endregion
286
+ //#region src/commands/index.ts
287
+ var commands_exports = {};
288
+ __export(commands_exports, {
289
+ loadProjectModule: () => loadProjectModule,
290
+ registerCodegenCommands: () => registerCodegenCommands,
291
+ runGenerateRelationsCommand: () => runGenerateRelationsCommand,
292
+ withGenerateRelationsCommand: () => withGenerateRelationsCommand
293
+ });
294
+
295
+ //#endregion
296
+ export { commands_exports, loadProjectModule, registerCodegenCommands, runGenerateRelationsCommand, withGenerateRelationsCommand };
297
+ //# sourceMappingURL=commands-CFAq4wXC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands-CFAq4wXC.js","names":["packageManager: PackageManager","cwd: string","argsByPackageManager: Record<PackageManager, readonly string[]>","context: FrameworkScaffoldContext","parser: Argv","modulePath: string","value: unknown","absoluteModulePath: string","outputDir: string","loaded: Record<string, unknown>","importSpecifier: string","accessors: Map<string, string>","models: ModelLike[]","model: ModelLike","accessor: string","accessorSegments: readonly string[]","options?: { projectRoot?: string; outputDir?: string }","parser: Argv","targetDir: string","context: FrameworkScaffoldContext","parser: Argv","parser: Argv"],"sources":["../src/commands/runInstall.ts","../src/commands/runNewCommand.ts","../src/commands/loadProjectModule.ts","../src/commands/runGenerateRelationsCommand.ts","../src/commands/runInitCommand.ts","../src/commands/registerCodegenCommands.ts","../src/commands/index.ts"],"sourcesContent":["import { spawnSync } from 'child_process';\nimport type { PackageManager } from '../frameworks';\n\nexport function runInstall(packageManager: PackageManager, cwd: string): void {\n const argsByPackageManager: Record<PackageManager, readonly string[]> = {\n pnpm: ['install'],\n npm: ['install'],\n yarn: ['install'],\n bun: ['install'],\n };\n\n const result = spawnSync(packageManager, [...argsByPackageManager[packageManager]], {\n cwd,\n stdio: 'inherit',\n env: process.env,\n });\n\n if (result.status !== 0) {\n throw new Error(\n `Dependency install failed with ${packageManager}. Exit code: ${String(result.status ?? 'unknown')}.`\n );\n }\n}\n","import { getLogger } from '@danceroutine/tango-core';\nimport { basename, resolve } from 'path/posix';\nimport {\n type SupportedFramework,\n type PackageManager,\n FrameworkScaffoldRegistry,\n type FrameworkScaffoldContext,\n scaffoldProject,\n} from '../frameworks';\nimport {\n PACKAGE_MANAGER,\n SCAFFOLD_DATABASE_DIALECT,\n SUPPORTED_FRAMEWORK,\n type ScaffoldDatabaseDialect,\n} from '../frameworks/contracts/FrameworkScaffoldStrategy';\nimport { runInstall } from './runInstall';\nimport type { Argv } from 'yargs';\n\ntype NewCommandArgs = {\n name?: string;\n framework: SupportedFramework;\n path?: string;\n packageManager: PackageManager;\n dialect: ScaffoldDatabaseDialect;\n install: boolean;\n force: boolean;\n seed: boolean;\n};\nexport async function runNewCommand({\n path,\n name,\n framework,\n packageManager,\n dialect,\n install,\n force,\n seed: includeSeed,\n}: NewCommandArgs): Promise<void> {\n const registry = FrameworkScaffoldRegistry.createDefault();\n const strategy = registry.get(framework)!;\n\n const requestedPath = path ?? name ?? 'tango-app';\n const projectName = name ?? basename(requestedPath);\n const targetDir = resolve(process.cwd(), requestedPath);\n\n const context: FrameworkScaffoldContext = {\n projectName,\n targetDir,\n framework,\n packageManager,\n dialect,\n includeSeed,\n };\n\n await scaffoldProject(context, strategy, { force });\n\n if (install) {\n runInstall(packageManager, targetDir);\n }\n\n const logger = getLogger('tango.codegen');\n logger.info(`Scaffold complete: ${targetDir}`);\n logger.info(`Framework: ${strategy.name}`);\n logger.info(`Run next: cd ${targetDir} && ${packageManager} run dev`);\n}\nexport function withNewCommand(parser: Argv): Argv {\n return parser.command(\n 'new [name]',\n 'Bootstrap a new Tango project',\n (builder) =>\n builder\n .positional('name', {\n type: 'string',\n describe: 'Project name (used as directory when --path is omitted).',\n })\n .option('framework', {\n type: 'string',\n choices: Object.values(SUPPORTED_FRAMEWORK),\n demandOption: true,\n describe: 'Host framework scaffold to generate.',\n })\n .option('path', {\n type: 'string',\n describe: 'Target directory for generated project files.',\n })\n .option('package-manager', {\n type: 'string',\n choices: Object.values(PACKAGE_MANAGER),\n default: PACKAGE_MANAGER.PNPM,\n describe: 'Package manager used for follow-up instructions and optional install.',\n })\n .option('dialect', {\n type: 'string',\n choices: Object.values(SCAFFOLD_DATABASE_DIALECT),\n default: SCAFFOLD_DATABASE_DIALECT.SQLITE,\n describe: 'Database dialect default for generated config.',\n })\n .option('install', {\n type: 'boolean',\n default: false,\n describe: 'Install dependencies after scaffolding.',\n })\n .option('force', {\n type: 'boolean',\n default: false,\n describe: 'Allow writing into a non-empty target directory.',\n })\n .option('seed', {\n type: 'boolean',\n default: true,\n describe: 'Include seed/bootstrap sample artifacts when supported by the framework strategy.',\n }),\n async ({ name, framework, path, packageManager, dialect, install, force, seed }) => {\n await runNewCommand({\n name,\n framework,\n path,\n packageManager,\n dialect,\n install,\n force,\n seed,\n });\n }\n );\n}\n","import { extname, relative, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { createJiti } from 'jiti';\nimport { ModelRegistry } from '@danceroutine/tango-schema';\n\nconst TS_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.cts']);\n\ntype ProjectModuleLoadResult = {\n loaded: Record<string, unknown>;\n modelTypeAccessors: Record<string, string>;\n registry: ModelRegistry;\n};\n\ntype ModelLike = { metadata: { key: string } };\ntype CollectedModelAccessors = {\n accessors: Record<string, string>;\n models: ModelLike[];\n};\n\nfunction isTypeScriptModule(modulePath: string): boolean {\n return TS_EXTENSIONS.has(extname(modulePath).toLowerCase());\n}\n\nfunction isModelLike(value: unknown): value is ModelLike {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'metadata' in value &&\n typeof (value as { metadata?: { key?: unknown } }).metadata?.key === 'string'\n );\n}\n\nfunction toImportSpecifier(absoluteModulePath: string, outputDir: string): string {\n const relativePath = relative(outputDir, absoluteModulePath).replaceAll('\\\\', '/');\n return relativePath.startsWith('.') ? relativePath : `./${relativePath}`;\n}\n\nfunction collectModelTypeAccessors(\n loaded: Record<string, unknown>,\n importSpecifier: string,\n accessors: Map<string, string> = new Map()\n): CollectedModelAccessors {\n const models: ModelLike[] = [];\n\n const registerAccessor = (model: ModelLike, accessor: string): void => {\n models.push(model);\n const existing = accessors.get(model.metadata.key);\n if (!existing || accessor.length < existing.length) {\n accessors.set(model.metadata.key, accessor);\n }\n };\n\n const inspectValue = (value: unknown, accessorSegments: readonly string[]): void => {\n if (isModelLike(value)) {\n const accessor = accessorSegments.map((segment) => `[${JSON.stringify(segment)}]`).join('');\n registerAccessor(value, `typeof import(${JSON.stringify(importSpecifier)})${accessor}`);\n return;\n }\n\n if (typeof value !== 'object' || value === null) {\n return;\n }\n\n for (const [key, nested] of Object.entries(value)) {\n if (isModelLike(nested)) {\n const accessor = [...accessorSegments, key].map((segment) => `[${JSON.stringify(segment)}]`).join('');\n registerAccessor(nested, `typeof import(${JSON.stringify(importSpecifier)})${accessor}`);\n }\n }\n };\n\n for (const [key, value] of Object.entries(loaded)) {\n inspectValue(value, [key]);\n }\n\n return {\n accessors: Object.fromEntries(accessors),\n models,\n };\n}\n\n/**\n * Load a Tango app module and discover model export accessors suitable for\n * generated ambient type references.\n */\nexport async function loadProjectModule(\n modulePath: string,\n options?: { projectRoot?: string; outputDir?: string }\n): Promise<ProjectModuleLoadResult> {\n const projectRoot = options?.projectRoot ?? process.cwd();\n const absoluteModulePath = resolve(projectRoot, modulePath);\n const outputDir = options?.outputDir ?? resolve(projectRoot, '.tango');\n const registry = new ModelRegistry();\n\n const executeImport = async (): Promise<Record<string, unknown>> => {\n if (isTypeScriptModule(absoluteModulePath)) {\n const jiti = createJiti(resolve(projectRoot, 'tango.config.ts'), {\n interopDefault: true,\n moduleCache: false,\n });\n return (await jiti.import<Record<string, unknown>>(absoluteModulePath)) as Record<string, unknown>;\n }\n\n return (await import(pathToFileURL(absoluteModulePath).href)) as Record<string, unknown>;\n };\n\n const loaded = await ModelRegistry.runWithRegistry(registry, executeImport);\n const collected = collectModelTypeAccessors(loaded, toImportSpecifier(absoluteModulePath, outputDir));\n const effectiveRegistry =\n registry.values().length > 0\n ? registry\n : collected.models.length > 0\n ? (ModelRegistry.getOwner(collected.models[0] as never) as ModelRegistry)\n : registry;\n\n return {\n loaded,\n modelTypeAccessors: collected.accessors,\n registry: effectiveRegistry,\n };\n}\n","import { resolve } from 'node:path';\nimport { getLogger } from '@danceroutine/tango-core';\nimport {\n GENERATED_RELATION_REGISTRY_DIRNAME,\n GENERATED_RELATION_REGISTRY_METADATA_FILENAME,\n GENERATED_RELATION_REGISTRY_TYPES_FILENAME,\n} from '@danceroutine/tango-schema';\nimport type { Argv } from 'yargs';\nimport { writeRelationRegistryArtifacts } from '../generators/relations';\nimport { loadProjectModule } from './loadProjectModule';\n\nconst logger = getLogger('tango.codegen');\n\ntype GenerateRelationsCommandArgs = {\n models: string;\n outDir: string;\n};\n\n/**\n * Generate the app-local ambient relation registry from a finalized model graph.\n */\nexport async function runGenerateRelationsCommand({ models, outDir }: GenerateRelationsCommandArgs): Promise<void> {\n const outputDir = resolve(process.cwd(), outDir);\n const { registry, modelTypeAccessors } = await loadProjectModule(models, { outputDir });\n const written = await writeRelationRegistryArtifacts({\n registry,\n modelTypeAccessors,\n outputDir,\n });\n\n logger.info(`Generated relation registry: ${written.typesFilepath}`);\n logger.info(`Generated relation metadata: ${written.metadataFilepath}`);\n}\n\nexport function withGenerateRelationsCommand(parser: Argv): Argv {\n return parser.command(\n 'relations',\n 'Generate app-local ambient relation typing from Tango models',\n (builder) =>\n builder\n .option('models', {\n type: 'string',\n demandOption: true,\n describe: 'Path to the module exporting Tango Model definitions.',\n })\n .option('out-dir', {\n type: 'string',\n default: GENERATED_RELATION_REGISTRY_DIRNAME,\n describe: `Directory that receives ${GENERATED_RELATION_REGISTRY_TYPES_FILENAME} and ${GENERATED_RELATION_REGISTRY_METADATA_FILENAME}.`,\n }),\n async ({ models, outDir }) => {\n await runGenerateRelationsCommand({\n models,\n outDir,\n });\n }\n );\n}\n","import { getLogger } from '@danceroutine/tango-core';\nimport { readdir, readFile } from 'fs/promises';\nimport { resolve, basename, join } from 'path/posix';\nimport {\n type PackageManager,\n type SupportedFramework,\n FrameworkScaffoldRegistry,\n type FrameworkScaffoldContext,\n scaffoldProject,\n} from '../frameworks';\nimport {\n PACKAGE_MANAGER,\n SCAFFOLD_DATABASE_DIALECT,\n type ScaffoldDatabaseDialect,\n SUPPORTED_FRAMEWORK,\n} from '../frameworks/contracts/FrameworkScaffoldStrategy';\nimport type { Argv } from 'yargs';\n\nasync function detectPackageManager(targetDir: string): Promise<PackageManager> {\n try {\n const entries = await readdir(targetDir);\n if (entries.includes('pnpm-lock.yaml')) return PACKAGE_MANAGER.PNPM;\n if (entries.includes('package-lock.json')) return PACKAGE_MANAGER.NPM;\n if (entries.includes('yarn.lock')) return PACKAGE_MANAGER.YARN;\n if (entries.includes('bun.lockb') || entries.includes('bun.lock')) return PACKAGE_MANAGER.BUN;\n } catch {\n // ENOENT or other: fall through to default\n }\n return PACKAGE_MANAGER.PNPM;\n}\ntype InitCommandArgs = {\n framework: SupportedFramework;\n path: string;\n dialect: ScaffoldDatabaseDialect;\n skipExisting: boolean;\n force: boolean;\n};\nexport async function runInitCommand({\n framework,\n path,\n dialect,\n skipExisting,\n force,\n}: InitCommandArgs): Promise<void> {\n const registry = FrameworkScaffoldRegistry.createDefault();\n const strategy = registry.get(framework)!;\n const targetDir = resolve(process.cwd(), path);\n\n let projectName = basename(targetDir);\n try {\n const pkgPath = join(targetDir, 'package.json');\n const raw = await readFile(pkgPath, 'utf8');\n const pkg = JSON.parse(raw) as { name?: string };\n if (typeof pkg.name === 'string' && pkg.name.length > 0) {\n projectName = pkg.name;\n }\n } catch {\n // no package.json or invalid: keep basename\n }\n\n const packageManager = await detectPackageManager(targetDir);\n\n const context: FrameworkScaffoldContext = {\n projectName,\n targetDir,\n framework,\n packageManager,\n dialect,\n includeSeed: true,\n };\n\n await scaffoldProject(context, strategy, {\n mode: 'init',\n skipExisting,\n force,\n });\n\n const logger = getLogger('tango.codegen');\n logger.info(`Tango init complete: ${targetDir}`);\n logger.info(`Install dependencies: ${strategy.getTangoInstallOneLiner(packageManager, context)}`);\n if (framework === SUPPORTED_FRAMEWORK.EXPRESS) {\n logger.info(\"Mount Tango: import { registerTango } from './src/tango.js'; await registerTango(app);\");\n }\n if (framework === SUPPORTED_FRAMEWORK.NUXT) {\n logger.info(\n 'Wire Tango handlers: register Nitro serverHandlers in nuxt.config.ts and export NuxtAdapter-backed handlers from server/tango/.'\n );\n }\n}\nexport function withInitCommand(parser: Argv): Argv {\n return parser.command(\n 'init',\n 'Add Tango to an existing project (only Tango-layer files)',\n (builder) =>\n builder\n .option('framework', {\n type: 'string',\n choices: Object.values(SUPPORTED_FRAMEWORK),\n demandOption: true,\n describe: 'Host framework (express, next, or nuxt).',\n })\n .option('path', {\n type: 'string',\n default: '.',\n describe: 'Target directory (default: current directory).',\n })\n .option('dialect', {\n type: 'string',\n choices: Object.values(SCAFFOLD_DATABASE_DIALECT),\n default: SCAFFOLD_DATABASE_DIALECT.SQLITE,\n describe: 'Database dialect for generated config.',\n })\n .option('skip-existing', {\n type: 'boolean',\n default: true,\n describe: 'Do not overwrite existing files.',\n })\n .option('force', {\n type: 'boolean',\n default: false,\n describe: 'Overwrite existing files when set.',\n }),\n async ({ framework, path, dialect, skipExisting, force }) => {\n await runInitCommand({\n framework,\n path,\n dialect,\n skipExisting,\n force,\n });\n }\n );\n}\n","import type { Argv } from 'yargs';\nimport { withNewCommand } from './runNewCommand';\nimport { withGenerateRelationsCommand } from './runGenerateRelationsCommand';\nimport { withInitCommand } from './runInitCommand';\n\n/**\n * Register Tango project scaffolding commands on an existing CLI parser.\n */\nexport function registerCodegenCommands(parser: Argv): Argv {\n const withTopLevelNew = withNewCommand(parser);\n const withTopLevelInit = withInitCommand(withTopLevelNew);\n const withTopLevelRelations = withGenerateRelationsCommand(withTopLevelInit);\n return withTopLevelRelations.command('codegen <command>', 'Code generation command group', (builder) =>\n withGenerateRelationsCommand(withInitCommand(withNewCommand(builder)))\n );\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { registerCodegenCommands } from './registerCodegenCommands';\nexport { loadProjectModule } from './loadProjectModule';\nexport { runGenerateRelationsCommand, withGenerateRelationsCommand } from './runGenerateRelationsCommand';\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { ExpressScaffoldStrategy, FrameworkScaffoldRegistry, FrameworkScaffoldStrategy, NextScaffoldStrategy, NuxtScaffoldStrategy, SCAFFOLD_TEMPLATE_CATEGORY, ScaffoldTemplateDescriptor, scaffoldProject } from "../frameworks-CfwU6n_o.js";
1
+ import { ExpressScaffoldStrategy, FrameworkScaffoldRegistry, FrameworkScaffoldStrategy, NextScaffoldStrategy, NuxtScaffoldStrategy, SCAFFOLD_TEMPLATE_CATEGORY, ScaffoldTemplateDescriptor, scaffoldProject } from "../frameworks-CLOeazGj.js";
2
2
 
3
3
  export { ExpressScaffoldStrategy, FrameworkScaffoldRegistry, FrameworkScaffoldStrategy, NextScaffoldStrategy, NuxtScaffoldStrategy, SCAFFOLD_TEMPLATE_CATEGORY, ScaffoldTemplateDescriptor, scaffoldProject };
@@ -31,7 +31,7 @@ var ScaffoldTemplateDescriptor = class {
31
31
  //#endregion
32
32
  //#region package.json
33
33
  var name = "@danceroutine/tango-codegen";
34
- var version$1 = "1.3.0";
34
+ var version$1 = "1.5.0";
35
35
  var description = "CLI for generating repositories, types, migrations, and OpenAPI specs for Tango";
36
36
  var type = "module";
37
37
  var main = "./dist/index.js";
@@ -83,14 +83,18 @@ var repository = {
83
83
  };
84
84
  var dependencies = {
85
85
  "@danceroutine/tango-core": "workspace:*",
86
+ "@danceroutine/tango-schema": "workspace:*",
87
+ "jiti": "^2.6.1",
86
88
  "yargs": "^17.7.2"
87
89
  };
88
90
  var devDependencies = {
91
+ "@danceroutine/tango-testing": "workspace:*",
89
92
  "@types/yargs": "^17.0.33",
90
93
  "@types/node": "^22.9.0",
91
94
  "tsdown": "^0.4.0",
92
95
  "typescript": "^5.6.3",
93
- "vitest": "^4.0.6"
96
+ "vitest": "^4.0.6",
97
+ "zod": "^4.0.0"
94
98
  };
95
99
  var package_default = {
96
100
  name,
@@ -248,6 +252,7 @@ var PackageJsonTemplateBuilder$2 = class extends TemplateBuilder {
248
252
  typecheck: "tsc --noEmit",
249
253
  "setup:schema": "node -e \"require('node:fs').mkdirSync('./.data',{recursive:true})\" && tango migrate --config ./tango.config.ts",
250
254
  "make:migrations": "tango make:migrations --config ./tango.config.ts --models ./src/models/index.ts --name \"\${npm_config_name:-manual_change}\"",
255
+ "codegen:relations": "tango codegen relations --models ./src/models/index.ts",
251
256
  prebootstrap: "node -e \"require('node:fs').mkdirSync('./.data',{recursive:true})\" && tango migrate --config ./tango.config.ts",
252
257
  bootstrap: "tsx src/bootstrap.ts"
253
258
  },
@@ -649,10 +654,13 @@ ${context.packageManager} run make:migrations --name initial
649
654
  ${context.packageManager} run dev
650
655
  \`\`\`
651
656
 
657
+ \`make:migrations\` also refreshes the generated relation registry for the scaffolded model module. If you later change relation metadata without needing a new migration file, run \`${context.packageManager} run codegen:relations\`.
658
+
652
659
  ## Scripts
653
660
 
654
661
  - \`${context.packageManager} run dev\`
655
662
  - \`${context.packageManager} run make:migrations --name add_field\`
663
+ - \`${context.packageManager} run codegen:relations\`
656
664
  - \`${context.packageManager} run setup:schema\`
657
665
  - \`${context.packageManager} run bootstrap\`
658
666
  - \`${context.packageManager} run typecheck\`
@@ -673,6 +681,7 @@ ${context.packageManager} run dev
673
681
  - \`src/openapi.ts\` OpenAPI document generation
674
682
  - \`src/bootstrap.ts\` seed utility for a larger demo dataset
675
683
  - \`migrations/\` checked-in Tango migrations
684
+ - \`.tango/\` generated relation typing artifacts
676
685
  `;
677
686
  }
678
687
  };
@@ -757,6 +766,7 @@ var PackageJsonTemplateBuilder$1 = class extends TemplateBuilder {
757
766
  typecheck: "tsc --noEmit",
758
767
  "setup:schema": "node -e \"require('node:fs').mkdirSync('./.data',{recursive:true})\" && tango migrate --config ./tango.config.ts",
759
768
  "make:migrations": "tango make:migrations --config ./tango.config.ts --models ./src/lib/models/index.ts --name \"\${npm_config_name:-manual_change}\"",
769
+ "codegen:relations": "tango codegen relations --models ./src/lib/models/index.ts",
760
770
  prebootstrap: "node -e \"require('node:fs').mkdirSync('./.data',{recursive:true})\" && tango migrate --config ./tango.config.ts",
761
771
  bootstrap: "tsx scripts/bootstrap.ts"
762
772
  },
@@ -1205,10 +1215,13 @@ ${context.packageManager} run make:migrations --name initial
1205
1215
  ${context.packageManager} run dev
1206
1216
  \`\`\`
1207
1217
 
1218
+ \`make:migrations\` also refreshes the generated relation registry for the scaffolded model module. If you later change relation metadata without needing a new migration file, run \`${context.packageManager} run codegen:relations\`.
1219
+
1208
1220
  ## Scripts
1209
1221
 
1210
1222
  - \`${context.packageManager} run dev\`
1211
1223
  - \`${context.packageManager} run make:migrations --name add_field\`
1224
+ - \`${context.packageManager} run codegen:relations\`
1212
1225
  - \`${context.packageManager} run setup:schema\`
1213
1226
  - \`${context.packageManager} run bootstrap\`
1214
1227
  - \`${context.packageManager} run typecheck\`
@@ -1230,6 +1243,7 @@ ${context.packageManager} run dev
1230
1243
  - \`src/lib/openapi.ts\` OpenAPI document generation
1231
1244
  - \`scripts/bootstrap.ts\` seed utility for a larger demo dataset
1232
1245
  - \`migrations/\` checked-in Tango migrations
1246
+ - \`.tango/\` generated relation typing artifacts
1233
1247
  `;
1234
1248
  }
1235
1249
  };
@@ -1291,6 +1305,7 @@ var PackageJsonTemplateBuilder = class extends TemplateBuilder {
1291
1305
  typecheck: "NUXT_TELEMETRY_DISABLED=1 nuxt typecheck",
1292
1306
  "setup:schema": "pnpm exec tango migrate --config ./tango.config.ts",
1293
1307
  "make:migrations": "pnpm exec tango make:migrations --config ./tango.config.ts --models ./lib/models/index.ts --name \"\${npm_config_name:-manual_change}\"",
1308
+ "codegen:relations": "pnpm exec tango codegen relations --models ./lib/models/index.ts",
1294
1309
  prebootstrap: "pnpm run setup:schema",
1295
1310
  bootstrap: "tsx scripts/bootstrap.ts"
1296
1311
  },
@@ -1725,10 +1740,13 @@ ${context.packageManager} run make:migrations --name initial
1725
1740
  ${context.packageManager} run dev
1726
1741
  \`\`\`
1727
1742
 
1743
+ \`make:migrations\` also refreshes the generated relation registry for the scaffolded model module. If you later change relation metadata without needing a new migration file, run \`${context.packageManager} run codegen:relations\`.
1744
+
1728
1745
  ## Scripts
1729
1746
 
1730
1747
  - \`${context.packageManager} run dev\`
1731
1748
  - \`${context.packageManager} run make:migrations --name add_field\`
1749
+ - \`${context.packageManager} run codegen:relations\`
1732
1750
  - \`${context.packageManager} run setup:schema\`
1733
1751
  - \`${context.packageManager} run bootstrap\`
1734
1752
  - \`${context.packageManager} run typecheck\`
@@ -1751,6 +1769,7 @@ ${context.packageManager} run dev
1751
1769
  - \`lib/openapi.ts\` OpenAPI document generation
1752
1770
  - \`scripts/bootstrap.ts\` seed utility for a larger demo dataset
1753
1771
  - \`migrations/\` checked-in Tango migrations
1772
+ - \`.tango/\` generated relation typing artifacts
1754
1773
  `;
1755
1774
  }
1756
1775
  };
@@ -1886,4 +1905,4 @@ __export(frameworks_exports, {
1886
1905
 
1887
1906
  //#endregion
1888
1907
  export { ExpressScaffoldStrategy, FrameworkScaffoldRegistry, FrameworkScaffoldStrategy, NextScaffoldStrategy, NuxtScaffoldStrategy, PACKAGE_MANAGER, SCAFFOLD_DATABASE_DIALECT, SCAFFOLD_TEMPLATE_CATEGORY, SUPPORTED_FRAMEWORK, ScaffoldTemplateDescriptor, frameworks_exports, scaffoldProject };
1889
- //# sourceMappingURL=frameworks-CfwU6n_o.js.map
1908
+ //# sourceMappingURL=frameworks-CLOeazGj.js.map