@rawsql-ts/ztd-cli 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -20
- package/dist/commands/init.d.ts +13 -0
- package/dist/commands/init.js +372 -127
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/lint.d.ts +4 -4
- package/dist/commands/lint.js +60 -40
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/ztdConfig.d.ts +2 -2
- package/dist/commands/ztdConfig.js +26 -12
- package/dist/commands/ztdConfig.js.map +1 -1
- package/dist/utils/optionalDependencies.d.ts +35 -0
- package/dist/utils/optionalDependencies.js +96 -0
- package/dist/utils/optionalDependencies.js.map +1 -0
- package/package.json +16 -9
- package/templates/AGENTS.md +36 -309
- package/templates/README.md +12 -215
- package/templates/dist/ztd-cli/templates/src/db/sql-client.ts +24 -0
- package/templates/src/AGENTS.md +26 -0
- package/templates/src/catalog/AGENTS.md +37 -0
- package/templates/src/catalog/runtime/AGENTS.md +75 -0
- package/templates/src/catalog/runtime/_coercions.ts +1 -0
- package/templates/src/catalog/runtime/_smoke.runtime.ts +21 -0
- package/templates/src/catalog/specs/AGENTS.md +48 -0
- package/templates/src/catalog/specs/_smoke.spec.arktype.ts +21 -0
- package/templates/src/catalog/specs/_smoke.spec.zod.ts +20 -0
- package/templates/src/db/sql-client.ts +5 -5
- package/templates/src/jobs/AGENTS.md +26 -0
- package/templates/src/jobs/README.md +3 -0
- package/templates/src/repositories/AGENTS.md +118 -0
- package/templates/src/repositories/tables/AGENTS.md +94 -0
- package/templates/src/repositories/tables/README.md +3 -0
- package/templates/src/repositories/views/AGENTS.md +25 -0
- package/templates/src/repositories/views/README.md +3 -0
- package/templates/src/sql/AGENTS.md +77 -0
- package/templates/src/sql/README.md +6 -0
- package/templates/tests/AGENTS.md +43 -150
- package/templates/tests/generated/AGENTS.md +16 -0
- package/templates/tests/smoke.test.ts +5 -0
- package/templates/tests/smoke.validation.test.ts +34 -0
- package/templates/tests/support/AGENTS.md +26 -0
- package/templates/tests/support/global-setup.ts +8 -23
- package/templates/tests/support/testkit-client.ts +13 -741
- package/templates/tsconfig.json +8 -1
- package/templates/ztd/AGENTS.md +11 -67
- package/templates/ztd/README.md +4 -13
- package/templates/ztd/ddl/AGENTS.md +34 -0
- package/templates/ztd/ddl/demo.sql +74 -0
- package/templates/src/repositories/user-accounts.ts +0 -179
- package/templates/tests/user-profiles.test.ts +0 -161
- package/templates/tests/writer-constraints.test.ts +0 -32
package/dist/commands/init.js
CHANGED
|
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createConsolePrompter = createConsolePrompter;
|
|
7
7
|
exports.runInitCommand = runInitCommand;
|
|
8
|
+
exports.normalizeSchemaName = normalizeSchemaName;
|
|
9
|
+
exports.sanitizeSchemaFileName = sanitizeSchemaFileName;
|
|
8
10
|
exports.registerInitCommand = registerInitCommand;
|
|
9
11
|
const node_child_process_1 = require("node:child_process");
|
|
10
12
|
const node_fs_1 = require("node:fs");
|
|
@@ -15,7 +17,6 @@ const agents_1 = require("../utils/agents");
|
|
|
15
17
|
const ztdProjectConfig_1 = require("../utils/ztdProjectConfig");
|
|
16
18
|
const ztdConfig_1 = require("./ztdConfig");
|
|
17
19
|
const pull_1 = require("./pull");
|
|
18
|
-
const options_1 = require("./options");
|
|
19
20
|
/**
|
|
20
21
|
* Create a readline-backed prompter that reads from stdin/stdout.
|
|
21
22
|
*/
|
|
@@ -28,6 +29,11 @@ function createConsolePrompter() {
|
|
|
28
29
|
async function requestLine(question) {
|
|
29
30
|
return (await rl.question(question)).trim();
|
|
30
31
|
}
|
|
32
|
+
async function requestLineWithDefault(question, defaultValue, example) {
|
|
33
|
+
const prompt = `${question}${example ? ` (${example})` : ''} [default: ${defaultValue}]: `;
|
|
34
|
+
const answer = await requestLine(prompt);
|
|
35
|
+
return answer.length > 0 ? answer : defaultValue;
|
|
36
|
+
}
|
|
31
37
|
return {
|
|
32
38
|
async selectChoice(question, choices) {
|
|
33
39
|
while (true) {
|
|
@@ -52,6 +58,9 @@ function createConsolePrompter() {
|
|
|
52
58
|
console.log('This value cannot be empty.');
|
|
53
59
|
}
|
|
54
60
|
},
|
|
61
|
+
async promptInputWithDefault(question, defaultValue, example) {
|
|
62
|
+
return requestLineWithDefault(question, defaultValue, example);
|
|
63
|
+
},
|
|
55
64
|
async confirm(question) {
|
|
56
65
|
while (true) {
|
|
57
66
|
const answer = (await requestLine(`${question} (y/N): `)).toLowerCase();
|
|
@@ -72,37 +81,64 @@ function createConsolePrompter() {
|
|
|
72
81
|
}
|
|
73
82
|
};
|
|
74
83
|
}
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
84
|
+
const MANDATORY_TESTKIT_DEPENDENCIES = {
|
|
85
|
+
'@rawsql-ts/adapter-node-pg': '^0.15.1',
|
|
86
|
+
'@rawsql-ts/testkit-postgres': '^0.15.1'
|
|
87
|
+
};
|
|
88
|
+
const SQL_CONTRACT_DEPENDENCY = {
|
|
89
|
+
'@rawsql-ts/sql-contract': '^0.1.0'
|
|
90
|
+
};
|
|
91
|
+
const ZOD_DEPENDENCY = {
|
|
92
|
+
zod: '^4.3.6'
|
|
93
|
+
};
|
|
94
|
+
const ARKTYPE_DEPENDENCY = {
|
|
95
|
+
arktype: '^2.1.29'
|
|
96
|
+
};
|
|
97
|
+
async function gatherOptionalFeatures(prompter, _dependencies) {
|
|
98
|
+
const validatorChoice = await prompter.selectChoice('Runtime DTO validation is required for ZTD tests. Which validator backend should we install?', ['Zod (zod, recommended)', 'ArkType (arktype)']);
|
|
99
|
+
const validator = validatorChoice === 0 ? 'zod' : 'arktype';
|
|
100
|
+
return { validator };
|
|
101
|
+
}
|
|
93
102
|
const README_TEMPLATE = 'README.md';
|
|
94
|
-
const TESTS_CONFIG_TEMPLATE = 'tests/ztd-layout.generated.ts';
|
|
95
103
|
const TESTS_AGENTS_TEMPLATE = 'tests/AGENTS.md';
|
|
104
|
+
const TESTS_SUPPORT_AGENTS_TEMPLATE = 'tests/support/AGENTS.md';
|
|
105
|
+
const TESTS_GENERATED_AGENTS_TEMPLATE = 'tests/generated/AGENTS.md';
|
|
106
|
+
const SMOKE_SPEC_ZOD_TEMPLATE = 'src/catalog/specs/_smoke.spec.zod.ts';
|
|
107
|
+
const SMOKE_SPEC_ARKTYPE_TEMPLATE = 'src/catalog/specs/_smoke.spec.arktype.ts';
|
|
108
|
+
const SMOKE_COERCIONS_TEMPLATE = 'src/catalog/runtime/_coercions.ts';
|
|
109
|
+
const SMOKE_RUNTIME_TEMPLATE = 'src/catalog/runtime/_smoke.runtime.ts';
|
|
110
|
+
const SMOKE_VALIDATION_TEST_TEMPLATE = 'tests/smoke.validation.test.ts';
|
|
111
|
+
const TESTS_SMOKE_TEMPLATE = 'tests/smoke.test.ts';
|
|
96
112
|
const TESTKIT_CLIENT_TEMPLATE = 'tests/support/testkit-client.ts';
|
|
97
113
|
const GLOBAL_SETUP_TEMPLATE = 'tests/support/global-setup.ts';
|
|
98
114
|
const VITEST_CONFIG_TEMPLATE = 'vitest.config.ts';
|
|
115
|
+
const TSCONFIG_TEMPLATE = 'tsconfig.json';
|
|
99
116
|
const SQL_CLIENT_TEMPLATE = 'src/db/sql-client.ts';
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
117
|
+
const SQL_README_TEMPLATE = 'src/sql/README.md';
|
|
118
|
+
const VIEWS_REPO_README_TEMPLATE = 'src/repositories/views/README.md';
|
|
119
|
+
const TABLES_REPO_README_TEMPLATE = 'src/repositories/tables/README.md';
|
|
120
|
+
const JOBS_README_TEMPLATE = 'src/jobs/README.md';
|
|
121
|
+
const SRC_AGENTS_TEMPLATE = 'src/AGENTS.md';
|
|
122
|
+
const SRC_CATALOG_AGENTS_TEMPLATE = 'src/catalog/AGENTS.md';
|
|
123
|
+
const SRC_CATALOG_RUNTIME_AGENTS_TEMPLATE = 'src/catalog/runtime/AGENTS.md';
|
|
124
|
+
const SRC_CATALOG_SPECS_AGENTS_TEMPLATE = 'src/catalog/specs/AGENTS.md';
|
|
125
|
+
const SRC_SQL_AGENTS_TEMPLATE = 'src/sql/AGENTS.md';
|
|
126
|
+
const SRC_REPOS_AGENTS_TEMPLATE = 'src/repositories/AGENTS.md';
|
|
127
|
+
const VIEWS_REPO_AGENTS_TEMPLATE = 'src/repositories/views/AGENTS.md';
|
|
128
|
+
const TABLES_REPO_AGENTS_TEMPLATE = 'src/repositories/tables/AGENTS.md';
|
|
129
|
+
const JOBS_AGENTS_TEMPLATE = 'src/jobs/AGENTS.md';
|
|
130
|
+
const ZTD_AGENTS_TEMPLATE = 'ztd/AGENTS.md';
|
|
131
|
+
const ZTD_README_TEMPLATE = 'ztd/README.md';
|
|
132
|
+
const ZTD_DDL_AGENTS_TEMPLATE = 'ztd/ddl/AGENTS.md';
|
|
133
|
+
const ZTD_DDL_DEMO_TEMPLATE = 'ztd/ddl/demo.sql';
|
|
134
|
+
const EMPTY_SCHEMA_COMMENT = (schemaName) => [
|
|
135
|
+
`-- DDL for schema "${schemaName}".`,
|
|
136
|
+
'-- Add CREATE TABLE statements here.',
|
|
137
|
+
''
|
|
138
|
+
].join('\n');
|
|
139
|
+
const DEMO_SCHEMA_TEMPLATE = (_schemaName) => {
|
|
140
|
+
return loadTemplate(ZTD_DDL_DEMO_TEMPLATE);
|
|
141
|
+
};
|
|
106
142
|
const AGENTS_FILE_CANDIDATES = ['AGENTS.md', 'AGENTS_ztd.md'];
|
|
107
143
|
const APP_INTERFACE_SECTION_MARKER = '## Application Interface Guidance';
|
|
108
144
|
const APP_INTERFACE_SECTION = `---
|
|
@@ -197,26 +233,67 @@ const DEFAULT_DEPENDENCIES = {
|
|
|
197
233
|
* Run the interactive `ztd init` workflow and return the resulting summary.
|
|
198
234
|
*/
|
|
199
235
|
async function runInitCommand(prompter, options) {
|
|
200
|
-
var _a, _b;
|
|
236
|
+
var _a, _b, _c, _d;
|
|
201
237
|
const rootDir = (_a = options === null || options === void 0 ? void 0 : options.rootDir) !== null && _a !== void 0 ? _a : process.cwd();
|
|
202
238
|
const dependencies = {
|
|
203
239
|
...DEFAULT_DEPENDENCIES,
|
|
204
240
|
...((_b = options === null || options === void 0 ? void 0 : options.dependencies) !== null && _b !== void 0 ? _b : {})
|
|
205
241
|
};
|
|
206
|
-
const
|
|
242
|
+
const overwritePolicy = {
|
|
243
|
+
force: (_c = options === null || options === void 0 ? void 0 : options.forceOverwrite) !== null && _c !== void 0 ? _c : false,
|
|
244
|
+
nonInteractive: (_d = options === null || options === void 0 ? void 0 : options.nonInteractive) !== null && _d !== void 0 ? _d : false
|
|
245
|
+
};
|
|
246
|
+
if (options === null || options === void 0 ? void 0 : options.withAppInterface) {
|
|
247
|
+
// Provide the documentation-only path before triggering any scaffolding work.
|
|
248
|
+
const summary = await appendAppInterfaceGuidance(rootDir, dependencies);
|
|
249
|
+
dependencies.log(`Appended application interface guidance to ${summary.relativePath}.`);
|
|
250
|
+
return {
|
|
251
|
+
summary: `App interface guidance appended to ${summary.relativePath}.`,
|
|
252
|
+
files: [summary]
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
// Ask how the user prefers to populate the initial schema.
|
|
256
|
+
const workflowChoice = await prompter.selectChoice('How do you want to start your database workflow?', [
|
|
257
|
+
'Pull schema from Postgres (pg_dump)',
|
|
258
|
+
'Create empty scaffold (I will write DDL)',
|
|
259
|
+
'Create scaffold with demo DDL (no app code)'
|
|
260
|
+
]);
|
|
261
|
+
const workflow = workflowChoice === 0 ? 'pg_dump' : workflowChoice === 1 ? 'empty' : 'demo';
|
|
262
|
+
const schemaName = normalizeSchemaName(ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.ddl.defaultSchema);
|
|
263
|
+
const schemaFileName = `${sanitizeSchemaFileName(schemaName)}.sql`;
|
|
207
264
|
const absolutePaths = {
|
|
208
265
|
schema: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.ddlDir, schemaFileName),
|
|
209
266
|
config: node_path_1.default.join(rootDir, 'ztd.config.json'),
|
|
210
|
-
|
|
211
|
-
|
|
267
|
+
smokeSpec: node_path_1.default.join(rootDir, 'src', 'catalog', 'specs', '_smoke.spec.ts'),
|
|
268
|
+
smokeCoercions: node_path_1.default.join(rootDir, 'src', 'catalog', 'runtime', '_coercions.ts'),
|
|
269
|
+
smokeRuntime: node_path_1.default.join(rootDir, 'src', 'catalog', 'runtime', '_smoke.runtime.ts'),
|
|
270
|
+
smokeValidationTest: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'smoke.validation.test.ts'),
|
|
212
271
|
testsAgents: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'AGENTS.md'),
|
|
272
|
+
testsSupportAgents: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'support', 'AGENTS.md'),
|
|
273
|
+
testsGeneratedAgents: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'generated', 'AGENTS.md'),
|
|
274
|
+
testsSmoke: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'smoke.test.ts'),
|
|
213
275
|
readme: node_path_1.default.join(rootDir, 'README.md'),
|
|
276
|
+
sqlReadme: node_path_1.default.join(rootDir, 'src', 'sql', 'README.md'),
|
|
277
|
+
viewsRepoReadme: node_path_1.default.join(rootDir, 'src', 'repositories', 'views', 'README.md'),
|
|
278
|
+
tablesRepoReadme: node_path_1.default.join(rootDir, 'src', 'repositories', 'tables', 'README.md'),
|
|
279
|
+
jobsReadme: node_path_1.default.join(rootDir, 'src', 'jobs', 'README.md'),
|
|
280
|
+
srcAgents: node_path_1.default.join(rootDir, 'src', 'AGENTS.md'),
|
|
281
|
+
srcCatalogAgents: node_path_1.default.join(rootDir, 'src', 'catalog', 'AGENTS.md'),
|
|
282
|
+
srcCatalogRuntimeAgents: node_path_1.default.join(rootDir, 'src', 'catalog', 'runtime', 'AGENTS.md'),
|
|
283
|
+
srcCatalogSpecsAgents: node_path_1.default.join(rootDir, 'src', 'catalog', 'specs', 'AGENTS.md'),
|
|
284
|
+
srcSqlAgents: node_path_1.default.join(rootDir, 'src', 'sql', 'AGENTS.md'),
|
|
285
|
+
srcReposAgents: node_path_1.default.join(rootDir, 'src', 'repositories', 'AGENTS.md'),
|
|
286
|
+
viewsRepoAgents: node_path_1.default.join(rootDir, 'src', 'repositories', 'views', 'AGENTS.md'),
|
|
287
|
+
tablesRepoAgents: node_path_1.default.join(rootDir, 'src', 'repositories', 'tables', 'AGENTS.md'),
|
|
288
|
+
jobsAgents: node_path_1.default.join(rootDir, 'src', 'jobs', 'AGENTS.md'),
|
|
214
289
|
sqlClient: node_path_1.default.join(rootDir, 'src', 'db', 'sql-client.ts'),
|
|
215
290
|
testkitClient: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'support', 'testkit-client.ts'),
|
|
216
291
|
globalSetup: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'support', 'global-setup.ts'),
|
|
217
292
|
vitestConfig: node_path_1.default.join(rootDir, 'vitest.config.ts'),
|
|
293
|
+
tsconfig: node_path_1.default.join(rootDir, 'tsconfig.json'),
|
|
218
294
|
ztdDocsAgent: node_path_1.default.join(rootDir, 'ztd', 'AGENTS.md'),
|
|
219
295
|
ztdDocsReadme: node_path_1.default.join(rootDir, 'ztd', 'README.md'),
|
|
296
|
+
ztdDdlAgents: node_path_1.default.join(rootDir, 'ztd', 'ddl', 'AGENTS.md'),
|
|
220
297
|
agents: node_path_1.default.join(rootDir, 'AGENTS.md'),
|
|
221
298
|
gitignore: node_path_1.default.join(rootDir, '.gitignore'),
|
|
222
299
|
editorconfig: node_path_1.default.join(rootDir, '.editorconfig'),
|
|
@@ -226,111 +303,170 @@ async function runInitCommand(prompter, options) {
|
|
|
226
303
|
};
|
|
227
304
|
const relativePath = (key) => node_path_1.default.relative(rootDir, absolutePaths[key]).replace(/\\/g, '/') || absolutePaths[key];
|
|
228
305
|
const summaries = {};
|
|
229
|
-
if (options === null || options === void 0 ? void 0 : options.withAppInterface) {
|
|
230
|
-
// Provide the documentation-only path before triggering any scaffolding work.
|
|
231
|
-
const summary = await appendAppInterfaceGuidance(rootDir, dependencies);
|
|
232
|
-
dependencies.log(`Appended application interface guidance to ${summary.relativePath}.`);
|
|
233
|
-
return {
|
|
234
|
-
summary: `App interface guidance appended to ${summary.relativePath}.`,
|
|
235
|
-
files: [summary]
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
306
|
// Ask how the user prefers to populate the initial schema.
|
|
239
|
-
|
|
240
|
-
if (workflow === 0) {
|
|
307
|
+
if (workflow === 'pg_dump') {
|
|
241
308
|
// Database-first path: pull the schema before writing any DDL files.
|
|
242
309
|
if (!dependencies.checkPgDump()) {
|
|
243
310
|
throw new Error('Unable to find pg_dump. Install Postgres or set PG_DUMP_PATH before running ztd init.');
|
|
244
311
|
}
|
|
245
312
|
const connectionString = await prompter.promptInput('Enter the Postgres connection string for your database', 'postgres://user:pass@host:5432/db');
|
|
246
|
-
const schemaSummary = await writeFileWithConsent(absolutePaths.schema, relativePath('schema'), dependencies, prompter, async () => {
|
|
313
|
+
const schemaSummary = await writeFileWithConsent(absolutePaths.schema, relativePath('schema'), dependencies, prompter, overwritePolicy, async () => {
|
|
247
314
|
dependencies.ensureDirectory(node_path_1.default.dirname(absolutePaths.schema));
|
|
248
315
|
await dependencies.runPullSchema({
|
|
249
316
|
url: connectionString,
|
|
250
|
-
out: node_path_1.default.dirname(absolutePaths.schema)
|
|
317
|
+
out: node_path_1.default.dirname(absolutePaths.schema),
|
|
318
|
+
schemas: [schemaName]
|
|
251
319
|
});
|
|
252
320
|
});
|
|
253
321
|
summaries.schema = schemaSummary;
|
|
254
322
|
}
|
|
255
|
-
else {
|
|
323
|
+
else if (workflow === 'empty') {
|
|
256
324
|
// Manual path: seed the DDL directory with a starter schema so ztd-config can run.
|
|
257
|
-
const schemaSummary = await writeFileWithConsent(absolutePaths.schema, relativePath('schema'), dependencies, prompter, async () => {
|
|
325
|
+
const schemaSummary = await writeFileWithConsent(absolutePaths.schema, relativePath('schema'), dependencies, prompter, overwritePolicy, async () => {
|
|
326
|
+
dependencies.ensureDirectory(node_path_1.default.dirname(absolutePaths.schema));
|
|
327
|
+
dependencies.writeFile(absolutePaths.schema, EMPTY_SCHEMA_COMMENT(schemaName));
|
|
328
|
+
});
|
|
329
|
+
summaries.schema = schemaSummary;
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
const schemaSummary = await writeFileWithConsent(absolutePaths.schema, relativePath('schema'), dependencies, prompter, overwritePolicy, async () => {
|
|
258
333
|
dependencies.ensureDirectory(node_path_1.default.dirname(absolutePaths.schema));
|
|
259
|
-
dependencies.writeFile(absolutePaths.schema,
|
|
334
|
+
dependencies.writeFile(absolutePaths.schema, DEMO_SCHEMA_TEMPLATE(schemaName));
|
|
260
335
|
});
|
|
261
336
|
summaries.schema = schemaSummary;
|
|
262
337
|
}
|
|
263
338
|
// Seed the ztd.config.json defaults so downstream tooling knows where ddl/tests live.
|
|
264
|
-
const configSummary = await writeFileWithConsent(absolutePaths.config, relativePath('config'), dependencies, prompter, () => {
|
|
265
|
-
(0, ztdProjectConfig_1.writeZtdProjectConfig)(rootDir
|
|
339
|
+
const configSummary = await writeFileWithConsent(absolutePaths.config, relativePath('config'), dependencies, prompter, overwritePolicy, () => {
|
|
340
|
+
(0, ztdProjectConfig_1.writeZtdProjectConfig)(rootDir, {
|
|
341
|
+
ddl: {
|
|
342
|
+
defaultSchema: schemaName,
|
|
343
|
+
searchPath: [schemaName]
|
|
344
|
+
}
|
|
345
|
+
});
|
|
266
346
|
});
|
|
267
347
|
summaries.config = configSummary;
|
|
268
|
-
const
|
|
269
|
-
const ztdConfigTarget = await confirmOverwriteIfExists(absolutePaths.ztdConfig, relativePath('ztdConfig'), dependencies, prompter);
|
|
270
|
-
if (ztdConfigTarget.write) {
|
|
271
|
-
// Regenerate tests/generated/ztd-row-map.generated.ts so TestRowMap reflects the DDL snapshot.
|
|
272
|
-
dependencies.ensureDirectory(node_path_1.default.dirname(absolutePaths.ztdConfig));
|
|
273
|
-
await dependencies.runGenerateZtdConfig({
|
|
274
|
-
directories: [node_path_1.default.resolve(node_path_1.default.dirname(absolutePaths.schema))],
|
|
275
|
-
extensions: options_1.DEFAULT_EXTENSIONS,
|
|
276
|
-
out: absolutePaths.ztdConfig,
|
|
277
|
-
defaultSchema: projectConfig.ddl.defaultSchema,
|
|
278
|
-
searchPath: projectConfig.ddl.searchPath,
|
|
279
|
-
ddlLint: projectConfig.ddlLint
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
else {
|
|
283
|
-
dependencies.log('Skipping ZTD config generation; existing tests/generated/ztd-row-map.generated.ts preserved.');
|
|
284
|
-
}
|
|
285
|
-
summaries.ztdConfig = {
|
|
286
|
-
relativePath: relativePath('ztdConfig'),
|
|
287
|
-
outcome: ztdConfigTarget.existed
|
|
288
|
-
? ztdConfigTarget.write
|
|
289
|
-
? 'overwritten'
|
|
290
|
-
: 'unchanged'
|
|
291
|
-
: 'created'
|
|
292
|
-
};
|
|
348
|
+
const optionalFeatures = await gatherOptionalFeatures(prompter, dependencies);
|
|
293
349
|
// Emit supporting documentation that describes the workflow for contributors.
|
|
294
|
-
const readmeSummary = await writeTemplateFile(rootDir, absolutePaths.readme, relativePath('readme'), README_TEMPLATE, dependencies, prompter, true);
|
|
350
|
+
const readmeSummary = await writeTemplateFile(rootDir, absolutePaths.readme, relativePath('readme'), README_TEMPLATE, dependencies, prompter, overwritePolicy, true);
|
|
295
351
|
if (readmeSummary) {
|
|
296
352
|
summaries.readme = readmeSummary;
|
|
297
353
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
354
|
+
const ztdDocsAgentSummary = await writeTemplateFile(rootDir, absolutePaths.ztdDocsAgent, relativePath('ztdDocsAgent'), ZTD_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
355
|
+
if (ztdDocsAgentSummary) {
|
|
356
|
+
summaries.ztdDocsAgent = ztdDocsAgentSummary;
|
|
357
|
+
}
|
|
358
|
+
const ztdDocsReadmeSummary = await writeTemplateFile(rootDir, absolutePaths.ztdDocsReadme, relativePath('ztdDocsReadme'), ZTD_README_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
359
|
+
if (ztdDocsReadmeSummary) {
|
|
360
|
+
summaries.ztdDocsReadme = ztdDocsReadmeSummary;
|
|
361
|
+
}
|
|
362
|
+
const ztdDdlAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.ztdDdlAgents, relativePath('ztdDdlAgents'), ZTD_DDL_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
363
|
+
if (ztdDdlAgentsSummary) {
|
|
364
|
+
summaries.ztdDdlAgents = ztdDdlAgentsSummary;
|
|
365
|
+
}
|
|
366
|
+
const srcAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.srcAgents, relativePath('srcAgents'), SRC_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
367
|
+
if (srcAgentsSummary) {
|
|
368
|
+
summaries.srcAgents = srcAgentsSummary;
|
|
369
|
+
}
|
|
370
|
+
const srcCatalogAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.srcCatalogAgents, relativePath('srcCatalogAgents'), SRC_CATALOG_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
371
|
+
if (srcCatalogAgentsSummary) {
|
|
372
|
+
summaries.srcCatalogAgents = srcCatalogAgentsSummary;
|
|
373
|
+
}
|
|
374
|
+
const srcCatalogRuntimeAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.srcCatalogRuntimeAgents, relativePath('srcCatalogRuntimeAgents'), SRC_CATALOG_RUNTIME_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
375
|
+
if (srcCatalogRuntimeAgentsSummary) {
|
|
376
|
+
summaries.srcCatalogRuntimeAgents = srcCatalogRuntimeAgentsSummary;
|
|
377
|
+
}
|
|
378
|
+
const srcCatalogSpecsAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.srcCatalogSpecsAgents, relativePath('srcCatalogSpecsAgents'), SRC_CATALOG_SPECS_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
379
|
+
if (srcCatalogSpecsAgentsSummary) {
|
|
380
|
+
summaries.srcCatalogSpecsAgents = srcCatalogSpecsAgentsSummary;
|
|
381
|
+
}
|
|
382
|
+
const srcSqlAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.srcSqlAgents, relativePath('srcSqlAgents'), SRC_SQL_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
383
|
+
if (srcSqlAgentsSummary) {
|
|
384
|
+
summaries.srcSqlAgents = srcSqlAgentsSummary;
|
|
385
|
+
}
|
|
386
|
+
const srcReposAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.srcReposAgents, relativePath('srcReposAgents'), SRC_REPOS_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
387
|
+
if (srcReposAgentsSummary) {
|
|
388
|
+
summaries.srcReposAgents = srcReposAgentsSummary;
|
|
389
|
+
}
|
|
390
|
+
const viewsRepoAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.viewsRepoAgents, relativePath('viewsRepoAgents'), VIEWS_REPO_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
391
|
+
if (viewsRepoAgentsSummary) {
|
|
392
|
+
summaries.viewsRepoAgents = viewsRepoAgentsSummary;
|
|
393
|
+
}
|
|
394
|
+
const tablesRepoAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.tablesRepoAgents, relativePath('tablesRepoAgents'), TABLES_REPO_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
395
|
+
if (tablesRepoAgentsSummary) {
|
|
396
|
+
summaries.tablesRepoAgents = tablesRepoAgentsSummary;
|
|
397
|
+
}
|
|
398
|
+
const jobsAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.jobsAgents, relativePath('jobsAgents'), JOBS_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
399
|
+
if (jobsAgentsSummary) {
|
|
400
|
+
summaries.jobsAgents = jobsAgentsSummary;
|
|
401
|
+
}
|
|
402
|
+
const sqlReadmeSummary = await writeTemplateFile(rootDir, absolutePaths.sqlReadme, relativePath('sqlReadme'), SQL_README_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
403
|
+
if (sqlReadmeSummary) {
|
|
404
|
+
summaries.sqlReadme = sqlReadmeSummary;
|
|
405
|
+
}
|
|
406
|
+
const viewsRepoReadmeSummary = await writeTemplateFile(rootDir, absolutePaths.viewsRepoReadme, relativePath('viewsRepoReadme'), VIEWS_REPO_README_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
407
|
+
if (viewsRepoReadmeSummary) {
|
|
408
|
+
summaries.viewsRepoReadme = viewsRepoReadmeSummary;
|
|
409
|
+
}
|
|
410
|
+
const tablesRepoReadmeSummary = await writeTemplateFile(rootDir, absolutePaths.tablesRepoReadme, relativePath('tablesRepoReadme'), TABLES_REPO_README_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
411
|
+
if (tablesRepoReadmeSummary) {
|
|
412
|
+
summaries.tablesRepoReadme = tablesRepoReadmeSummary;
|
|
303
413
|
}
|
|
304
|
-
const
|
|
414
|
+
const jobsReadmeSummary = await writeTemplateFile(rootDir, absolutePaths.jobsReadme, relativePath('jobsReadme'), JOBS_README_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
415
|
+
if (jobsReadmeSummary) {
|
|
416
|
+
summaries.jobsReadme = jobsReadmeSummary;
|
|
417
|
+
}
|
|
418
|
+
const smokeSpecTemplate = optionalFeatures.validator === 'zod' ? SMOKE_SPEC_ZOD_TEMPLATE : SMOKE_SPEC_ARKTYPE_TEMPLATE;
|
|
419
|
+
const smokeSpecSummary = await writeTemplateFile(rootDir, absolutePaths.smokeSpec, relativePath('smokeSpec'), smokeSpecTemplate, dependencies, prompter, overwritePolicy);
|
|
420
|
+
if (smokeSpecSummary) {
|
|
421
|
+
summaries.smokeSpec = smokeSpecSummary;
|
|
422
|
+
}
|
|
423
|
+
const smokeCoercionsSummary = await writeTemplateFile(rootDir, absolutePaths.smokeCoercions, relativePath('smokeCoercions'), SMOKE_COERCIONS_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
424
|
+
if (smokeCoercionsSummary) {
|
|
425
|
+
summaries.smokeCoercions = smokeCoercionsSummary;
|
|
426
|
+
}
|
|
427
|
+
const smokeRuntimeSummary = await writeTemplateFile(rootDir, absolutePaths.smokeRuntime, relativePath('smokeRuntime'), SMOKE_RUNTIME_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
428
|
+
if (smokeRuntimeSummary) {
|
|
429
|
+
summaries.smokeRuntime = smokeRuntimeSummary;
|
|
430
|
+
}
|
|
431
|
+
const smokeValidationTestSummary = await writeTemplateFile(rootDir, absolutePaths.smokeValidationTest, relativePath('smokeValidationTest'), SMOKE_VALIDATION_TEST_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
432
|
+
if (smokeValidationTestSummary) {
|
|
433
|
+
summaries.smokeValidationTest = smokeValidationTestSummary;
|
|
434
|
+
}
|
|
435
|
+
const sqlClientSummary = writeOptionalTemplateFile(absolutePaths.sqlClient, relativePath('sqlClient'), SQL_CLIENT_TEMPLATE, dependencies);
|
|
436
|
+
if (sqlClientSummary) {
|
|
437
|
+
summaries.sqlClient = sqlClientSummary;
|
|
438
|
+
}
|
|
439
|
+
const testsAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.testsAgents, relativePath('testsAgents'), TESTS_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
440
|
+
if (testsAgentsSummary) {
|
|
441
|
+
summaries.testsAgents = testsAgentsSummary;
|
|
442
|
+
}
|
|
443
|
+
const testsSupportAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.testsSupportAgents, relativePath('testsSupportAgents'), TESTS_SUPPORT_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
444
|
+
if (testsSupportAgentsSummary) {
|
|
445
|
+
summaries.testsSupportAgents = testsSupportAgentsSummary;
|
|
446
|
+
}
|
|
447
|
+
const testsGeneratedAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.testsGeneratedAgents, relativePath('testsGeneratedAgents'), TESTS_GENERATED_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
448
|
+
if (testsGeneratedAgentsSummary) {
|
|
449
|
+
summaries.testsGeneratedAgents = testsGeneratedAgentsSummary;
|
|
450
|
+
}
|
|
451
|
+
const testsSmokeSummary = await writeTemplateFile(rootDir, absolutePaths.testsSmoke, relativePath('testsSmoke'), TESTS_SMOKE_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
452
|
+
if (testsSmokeSummary) {
|
|
453
|
+
summaries.testsSmoke = testsSmokeSummary;
|
|
454
|
+
}
|
|
455
|
+
const testkitSummary = await writeTemplateFile(rootDir, absolutePaths.testkitClient, relativePath('testkitClient'), TESTKIT_CLIENT_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
305
456
|
if (testkitSummary) {
|
|
306
457
|
summaries.testkitClient = testkitSummary;
|
|
307
458
|
}
|
|
308
|
-
const globalSetupSummary = await writeTemplateFile(rootDir, absolutePaths.globalSetup, relativePath('globalSetup'), GLOBAL_SETUP_TEMPLATE, dependencies, prompter);
|
|
459
|
+
const globalSetupSummary = await writeTemplateFile(rootDir, absolutePaths.globalSetup, relativePath('globalSetup'), GLOBAL_SETUP_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
309
460
|
if (globalSetupSummary) {
|
|
310
461
|
summaries.globalSetup = globalSetupSummary;
|
|
311
462
|
}
|
|
312
|
-
const vitestConfigSummary = await writeTemplateFile(rootDir, absolutePaths.vitestConfig, relativePath('vitestConfig'), VITEST_CONFIG_TEMPLATE, dependencies, prompter);
|
|
463
|
+
const vitestConfigSummary = await writeTemplateFile(rootDir, absolutePaths.vitestConfig, relativePath('vitestConfig'), VITEST_CONFIG_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
313
464
|
if (vitestConfigSummary) {
|
|
314
465
|
summaries.vitestConfig = vitestConfigSummary;
|
|
315
466
|
}
|
|
316
|
-
const
|
|
317
|
-
if (
|
|
318
|
-
summaries.
|
|
319
|
-
}
|
|
320
|
-
// Ensure the generated tests guidance lands beside the generated layout config.
|
|
321
|
-
const testsAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.testsAgents, relativePath('testsAgents'), TESTS_AGENTS_TEMPLATE, dependencies, prompter);
|
|
322
|
-
if (testsAgentsSummary) {
|
|
323
|
-
summaries.testsAgents = testsAgentsSummary;
|
|
324
|
-
}
|
|
325
|
-
// Seed the shared guidance that lives inside the ztd/ directory so contributors see the new instructions.
|
|
326
|
-
const ztdDocsAgentSummary = await writeTemplateFile(rootDir, absolutePaths.ztdDocsAgent, relativePath('ztdDocsAgent'), 'ztd/AGENTS.md', dependencies, prompter);
|
|
327
|
-
if (ztdDocsAgentSummary) {
|
|
328
|
-
summaries.ztdDocsAgent = ztdDocsAgentSummary;
|
|
329
|
-
}
|
|
330
|
-
// Provide the companion README inside ztd/ so maintainers understand the schema expectations and doc layout.
|
|
331
|
-
const ztdDocsReadmeSummary = await writeTemplateFile(rootDir, absolutePaths.ztdDocsReadme, relativePath('ztdDocsReadme'), 'ztd/README.md', dependencies, prompter);
|
|
332
|
-
if (ztdDocsReadmeSummary) {
|
|
333
|
-
summaries.ztdDocsReadme = ztdDocsReadmeSummary;
|
|
467
|
+
const tsconfigSummary = await writeTemplateFile(rootDir, absolutePaths.tsconfig, relativePath('tsconfig'), TSCONFIG_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
468
|
+
if (tsconfigSummary) {
|
|
469
|
+
summaries.tsconfig = tsconfigSummary;
|
|
334
470
|
}
|
|
335
471
|
const editorconfigSummary = copyTemplateFileIfMissing(rootDir, relativePath('editorconfig'), '.editorconfig', dependencies);
|
|
336
472
|
if (editorconfigSummary) {
|
|
@@ -348,7 +484,7 @@ async function runInitCommand(prompter, options) {
|
|
|
348
484
|
if (prettierignoreSummary) {
|
|
349
485
|
summaries.prettierignore = prettierignoreSummary;
|
|
350
486
|
}
|
|
351
|
-
const packageSummary = ensurePackageJsonFormatting(rootDir, relativePath('package'), dependencies);
|
|
487
|
+
const packageSummary = ensurePackageJsonFormatting(rootDir, relativePath('package'), dependencies, optionalFeatures);
|
|
352
488
|
if (packageSummary) {
|
|
353
489
|
summaries.package = packageSummary;
|
|
354
490
|
}
|
|
@@ -358,7 +494,8 @@ async function runInitCommand(prompter, options) {
|
|
|
358
494
|
summaries.agents = agentsRelative;
|
|
359
495
|
}
|
|
360
496
|
await ensureTemplateDependenciesInstalled(rootDir, absolutePaths, summaries, dependencies);
|
|
361
|
-
const
|
|
497
|
+
const nextSteps = buildNextSteps(normalizeRelative(rootDir, absolutePaths.schema), workflow);
|
|
498
|
+
const summaryLines = buildSummaryLines(summaries, optionalFeatures, nextSteps);
|
|
362
499
|
summaryLines.forEach(dependencies.log);
|
|
363
500
|
return {
|
|
364
501
|
summary: summaryLines.join('\n'),
|
|
@@ -534,7 +671,7 @@ function listDeclaredPackages(rootDir) {
|
|
|
534
671
|
return declared;
|
|
535
672
|
}
|
|
536
673
|
function listTemplateReferencedPackages(absolutePaths, summaries) {
|
|
537
|
-
const packages = new Set(
|
|
674
|
+
const packages = new Set();
|
|
538
675
|
const touchedKeys = Object.entries(summaries)
|
|
539
676
|
.filter((entry) => Boolean(entry[1]))
|
|
540
677
|
.filter(([, summary]) => summary.outcome === 'created' || summary.outcome === 'overwritten')
|
|
@@ -557,7 +694,7 @@ async function ensureTemplateDependenciesInstalled(rootDir, absolutePaths, summa
|
|
|
557
694
|
var _a;
|
|
558
695
|
const packageJsonPath = node_path_1.default.join(rootDir, 'package.json');
|
|
559
696
|
if (!dependencies.fileExists(packageJsonPath)) {
|
|
560
|
-
dependencies.log('Skipping dependency installation because package.json is missing.');
|
|
697
|
+
dependencies.log('Skipping dependency installation because package.json is missing. Next: run pnpm init (or npm init), install dependencies, then run npx ztd ztd-config.');
|
|
561
698
|
return;
|
|
562
699
|
}
|
|
563
700
|
const packageManager = detectPackageManager(rootDir);
|
|
@@ -612,18 +749,22 @@ function writeOptionalTemplateFile(absolutePath, relative, templateName, depende
|
|
|
612
749
|
dependencies.writeFile(absolutePath, (0, node_fs_1.readFileSync)(templatePath, 'utf8'));
|
|
613
750
|
return { relativePath: relative, outcome: 'created' };
|
|
614
751
|
}
|
|
615
|
-
function ensurePackageJsonFormatting(rootDir, relative, dependencies) {
|
|
752
|
+
function ensurePackageJsonFormatting(rootDir, relative, dependencies, optionalFeatures) {
|
|
616
753
|
var _a, _b;
|
|
617
754
|
const packagePath = node_path_1.default.join(rootDir, 'package.json');
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
755
|
+
const packageExists = dependencies.fileExists(packagePath);
|
|
756
|
+
const parsed = packageExists
|
|
757
|
+
? JSON.parse((0, node_fs_1.readFileSync)(packagePath, 'utf8'))
|
|
758
|
+
: {
|
|
759
|
+
name: inferPackageName(rootDir),
|
|
760
|
+
version: '0.0.0',
|
|
761
|
+
private: true
|
|
762
|
+
};
|
|
624
763
|
let changed = false;
|
|
625
764
|
const scripts = (_a = parsed.scripts) !== null && _a !== void 0 ? _a : {};
|
|
626
765
|
const requiredScripts = {
|
|
766
|
+
test: 'vitest run',
|
|
767
|
+
typecheck: 'tsc --noEmit',
|
|
627
768
|
format: 'prettier . --write',
|
|
628
769
|
lint: 'eslint .',
|
|
629
770
|
'lint:fix': 'eslint . --fix'
|
|
@@ -661,6 +802,11 @@ function ensurePackageJsonFormatting(rootDir, relative, dependencies) {
|
|
|
661
802
|
'prettier-plugin-sql': '^0.19.2',
|
|
662
803
|
'simple-git-hooks': '^2.13.1'
|
|
663
804
|
};
|
|
805
|
+
const testingDeps = {
|
|
806
|
+
vitest: '^4.0.7',
|
|
807
|
+
typescript: '^5.8.2',
|
|
808
|
+
'@types/node': '^22.13.10'
|
|
809
|
+
};
|
|
664
810
|
// Add the formatting toolchain dependencies that back the scripts and hooks.
|
|
665
811
|
for (const [dep, version] of Object.entries(formattingDeps)) {
|
|
666
812
|
if (dep in devDependencies) {
|
|
@@ -669,6 +815,31 @@ function ensurePackageJsonFormatting(rootDir, relative, dependencies) {
|
|
|
669
815
|
devDependencies[dep] = version;
|
|
670
816
|
changed = true;
|
|
671
817
|
}
|
|
818
|
+
const stackDependencies = {
|
|
819
|
+
...MANDATORY_TESTKIT_DEPENDENCIES,
|
|
820
|
+
...SQL_CONTRACT_DEPENDENCY
|
|
821
|
+
};
|
|
822
|
+
if (optionalFeatures.validator === 'zod') {
|
|
823
|
+
Object.assign(stackDependencies, ZOD_DEPENDENCY);
|
|
824
|
+
}
|
|
825
|
+
else {
|
|
826
|
+
Object.assign(stackDependencies, ARKTYPE_DEPENDENCY);
|
|
827
|
+
}
|
|
828
|
+
// Ensure test and typecheck toolchain dependencies are present for a runnable scaffold.
|
|
829
|
+
for (const [dep, version] of Object.entries(testingDeps)) {
|
|
830
|
+
if (dep in devDependencies) {
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
devDependencies[dep] = version;
|
|
834
|
+
changed = true;
|
|
835
|
+
}
|
|
836
|
+
for (const [dep, version] of Object.entries(stackDependencies)) {
|
|
837
|
+
if (dep in devDependencies) {
|
|
838
|
+
continue;
|
|
839
|
+
}
|
|
840
|
+
devDependencies[dep] = version;
|
|
841
|
+
changed = true;
|
|
842
|
+
}
|
|
672
843
|
if (!changed) {
|
|
673
844
|
return null;
|
|
674
845
|
}
|
|
@@ -676,42 +847,56 @@ function ensurePackageJsonFormatting(rootDir, relative, dependencies) {
|
|
|
676
847
|
dependencies.ensureDirectory(node_path_1.default.dirname(packagePath));
|
|
677
848
|
// Persist the updated manifest so the new scripts and tools are available immediately.
|
|
678
849
|
dependencies.writeFile(packagePath, `${JSON.stringify(parsed, null, 2)}\n`);
|
|
679
|
-
return { relativePath: relative, outcome: 'overwritten' };
|
|
850
|
+
return { relativePath: relative, outcome: packageExists ? 'overwritten' : 'created' };
|
|
851
|
+
}
|
|
852
|
+
function inferPackageName(rootDir) {
|
|
853
|
+
const baseName = node_path_1.default.basename(rootDir).toLowerCase();
|
|
854
|
+
const normalized = baseName.replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
855
|
+
if (normalized.length > 0) {
|
|
856
|
+
return normalized;
|
|
857
|
+
}
|
|
858
|
+
return 'ztd-project';
|
|
680
859
|
}
|
|
681
|
-
async function writeFileWithConsent(absolutePath, relative, dependencies, prompter, writer) {
|
|
682
|
-
const { existed, write } = await confirmOverwriteIfExists(absolutePath, relative, dependencies, prompter);
|
|
860
|
+
async function writeFileWithConsent(absolutePath, relative, dependencies, prompter, overwritePolicy, writer) {
|
|
861
|
+
const { existed, write } = await confirmOverwriteIfExists(absolutePath, relative, dependencies, prompter, overwritePolicy);
|
|
683
862
|
if (!write) {
|
|
684
863
|
return { relativePath: relative, outcome: 'unchanged' };
|
|
685
864
|
}
|
|
686
865
|
await writer();
|
|
687
866
|
return { relativePath: relative, outcome: existed ? 'overwritten' : 'created' };
|
|
688
867
|
}
|
|
689
|
-
async function confirmOverwriteIfExists(absolutePath, relative, dependencies, prompter) {
|
|
868
|
+
async function confirmOverwriteIfExists(absolutePath, relative, dependencies, prompter, overwritePolicy) {
|
|
690
869
|
const existed = dependencies.fileExists(absolutePath);
|
|
691
870
|
if (!existed) {
|
|
692
871
|
return { existed: false, write: true };
|
|
693
872
|
}
|
|
873
|
+
if (overwritePolicy.force) {
|
|
874
|
+
return { existed: true, write: true };
|
|
875
|
+
}
|
|
876
|
+
if (overwritePolicy.nonInteractive) {
|
|
877
|
+
throw new Error(`File ${relative} already exists. Re-run with --yes to overwrite or remove the file before running ztd init.`);
|
|
878
|
+
}
|
|
694
879
|
const overwrite = await prompter.confirm(`File ${relative} already exists. Overwrite?`);
|
|
695
880
|
if (!overwrite) {
|
|
696
881
|
return { existed: true, write: false };
|
|
697
882
|
}
|
|
698
883
|
return { existed: true, write: true };
|
|
699
884
|
}
|
|
700
|
-
async function writeDocFile(absolutePath, relative, contents, dependencies, prompter) {
|
|
701
|
-
const summary = await writeFileWithConsent(absolutePath, relative, dependencies, prompter, () => {
|
|
885
|
+
async function writeDocFile(absolutePath, relative, contents, dependencies, prompter, overwritePolicy) {
|
|
886
|
+
const summary = await writeFileWithConsent(absolutePath, relative, dependencies, prompter, overwritePolicy, () => {
|
|
702
887
|
dependencies.ensureDirectory(node_path_1.default.dirname(absolutePath));
|
|
703
888
|
dependencies.writeFile(absolutePath, contents);
|
|
704
889
|
});
|
|
705
890
|
return summary;
|
|
706
891
|
}
|
|
707
|
-
async function writeTemplateFile(rootDir, absolutePath, relative, templateName, dependencies, prompter, allowFallback) {
|
|
892
|
+
async function writeTemplateFile(rootDir, absolutePath, relative, templateName, dependencies, prompter, overwritePolicy, allowFallback) {
|
|
708
893
|
const templateTarget = resolveTemplateTarget(rootDir, absolutePath, relative, dependencies, allowFallback);
|
|
709
894
|
if (!templateTarget) {
|
|
710
895
|
return null;
|
|
711
896
|
}
|
|
712
897
|
// Load shared documentation templates so every new project gets the same guidance.
|
|
713
898
|
const contents = loadTemplate(templateName);
|
|
714
|
-
return writeDocFile(templateTarget.absolutePath, templateTarget.relativePath, contents, dependencies, prompter);
|
|
899
|
+
return writeDocFile(templateTarget.absolutePath, templateTarget.relativePath, contents, dependencies, prompter, overwritePolicy);
|
|
715
900
|
}
|
|
716
901
|
function resolveTemplateTarget(rootDir, absolutePath, relative, dependencies, allowFallback) {
|
|
717
902
|
if (!dependencies.fileExists(absolutePath)) {
|
|
@@ -738,6 +923,25 @@ function normalizeRelative(rootDir, absolutePath) {
|
|
|
738
923
|
const relative = node_path_1.default.relative(rootDir, absolutePath).replace(/\\/g, '/');
|
|
739
924
|
return relative || absolutePath;
|
|
740
925
|
}
|
|
926
|
+
/**
|
|
927
|
+
* Normalizes a schema identifier into the canonical lowercase form used by ztd-cli file naming.
|
|
928
|
+
* Empty input falls back to the configured default schema.
|
|
929
|
+
*/
|
|
930
|
+
function normalizeSchemaName(value) {
|
|
931
|
+
const trimmed = value.trim();
|
|
932
|
+
if (!trimmed) {
|
|
933
|
+
return ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.ddl.defaultSchema;
|
|
934
|
+
}
|
|
935
|
+
return trimmed.replace(/^"|"$/g, '').toLowerCase();
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Sanitizes a normalized schema identifier so it can be used as a filesystem-safe file stem.
|
|
939
|
+
* Returns `schema` when all characters are stripped by sanitization.
|
|
940
|
+
*/
|
|
941
|
+
function sanitizeSchemaFileName(schemaName) {
|
|
942
|
+
const sanitized = schemaName.replace(/[^a-z0-9_-]/g, '_').replace(/^_+|_+$/g, '');
|
|
943
|
+
return sanitized || 'schema';
|
|
944
|
+
}
|
|
741
945
|
function resolveOrCreateAgentsFile(rootDir, dependencies) {
|
|
742
946
|
// Prefer materializing the bundled template before looking for existing attention files.
|
|
743
947
|
const templateTarget = dependencies.copyAgentsTemplate(rootDir);
|
|
@@ -780,20 +984,51 @@ function loadTemplate(templateName) {
|
|
|
780
984
|
}
|
|
781
985
|
return (0, node_fs_1.readFileSync)(templatePath, 'utf8');
|
|
782
986
|
}
|
|
783
|
-
function
|
|
987
|
+
function buildNextSteps(schemaRelativePath, workflow) {
|
|
988
|
+
const stepOne = workflow === 'pg_dump'
|
|
989
|
+
? ` 1. Review the dumped DDL in ${schemaRelativePath}`
|
|
990
|
+
: ` 1. If the schema file is empty, edit ${schemaRelativePath}`;
|
|
991
|
+
return [
|
|
992
|
+
stepOne,
|
|
993
|
+
' 2. Run npx ztd ztd-config',
|
|
994
|
+
' 3. Provide a SqlClient implementation (adapter or mock)',
|
|
995
|
+
' 4. Run tests (pnpm test or npx vitest run)'
|
|
996
|
+
];
|
|
997
|
+
}
|
|
998
|
+
function buildSummaryLines(summaries, optionalFeatures, nextSteps) {
|
|
784
999
|
const orderedKeys = [
|
|
785
1000
|
'schema',
|
|
786
1001
|
'config',
|
|
787
|
-
'testsConfig',
|
|
788
|
-
'testsAgents',
|
|
789
|
-
'ztdConfig',
|
|
790
1002
|
'readme',
|
|
1003
|
+
'ztdDocsAgent',
|
|
1004
|
+
'ztdDocsReadme',
|
|
1005
|
+
'ztdDdlAgents',
|
|
1006
|
+
'srcAgents',
|
|
1007
|
+
'srcCatalogAgents',
|
|
1008
|
+
'srcCatalogRuntimeAgents',
|
|
1009
|
+
'srcCatalogSpecsAgents',
|
|
1010
|
+
'srcSqlAgents',
|
|
1011
|
+
'srcReposAgents',
|
|
1012
|
+
'viewsRepoAgents',
|
|
1013
|
+
'tablesRepoAgents',
|
|
1014
|
+
'jobsAgents',
|
|
1015
|
+
'sqlReadme',
|
|
1016
|
+
'viewsRepoReadme',
|
|
1017
|
+
'tablesRepoReadme',
|
|
1018
|
+
'jobsReadme',
|
|
1019
|
+
'smokeSpec',
|
|
1020
|
+
'smokeCoercions',
|
|
1021
|
+
'smokeRuntime',
|
|
1022
|
+
'smokeValidationTest',
|
|
1023
|
+
'testsAgents',
|
|
1024
|
+
'testsSupportAgents',
|
|
1025
|
+
'testsGeneratedAgents',
|
|
1026
|
+
'testsSmoke',
|
|
791
1027
|
'sqlClient',
|
|
792
1028
|
'testkitClient',
|
|
793
1029
|
'globalSetup',
|
|
794
1030
|
'vitestConfig',
|
|
795
|
-
'
|
|
796
|
-
'ztdDocsReadme',
|
|
1031
|
+
'tsconfig',
|
|
797
1032
|
'agents',
|
|
798
1033
|
'gitignore',
|
|
799
1034
|
'editorconfig',
|
|
@@ -813,7 +1048,13 @@ function buildSummaryLines(summaries) {
|
|
|
813
1048
|
lines.push(` - ${summary.relativePath}${note}`);
|
|
814
1049
|
}
|
|
815
1050
|
}
|
|
816
|
-
lines.push('', '
|
|
1051
|
+
lines.push('', 'Validation configuration:');
|
|
1052
|
+
lines.push(' - SQL catalog/mapping support via @rawsql-ts/sql-contract (see docs/recipes/sql-contract.md)');
|
|
1053
|
+
const validatorLabel = optionalFeatures.validator === 'zod'
|
|
1054
|
+
? 'Zod (zod, docs/recipes/validation-zod.md)'
|
|
1055
|
+
: 'ArkType (arktype, docs/recipes/validation-arktype.md)';
|
|
1056
|
+
lines.push(` - Validator backend: ${validatorLabel}`);
|
|
1057
|
+
lines.push('', 'Next steps:', ...nextSteps);
|
|
817
1058
|
return lines;
|
|
818
1059
|
}
|
|
819
1060
|
function registerInitCommand(program) {
|
|
@@ -822,12 +1063,16 @@ function registerInitCommand(program) {
|
|
|
822
1063
|
.description('Automate project setup for Zero Table Dependency workflows')
|
|
823
1064
|
.option('--with-sqlclient', 'Generate a minimal SqlClient interface for repositories')
|
|
824
1065
|
.option('--with-app-interface', 'Append application interface guidance to AGENTS.md only')
|
|
1066
|
+
.option('--yes', 'Overwrite existing scaffold files without prompting')
|
|
825
1067
|
.action(async (options) => {
|
|
1068
|
+
var _a;
|
|
826
1069
|
const prompter = createConsolePrompter();
|
|
827
1070
|
try {
|
|
828
1071
|
await runInitCommand(prompter, {
|
|
829
1072
|
withSqlClient: options.withSqlclient,
|
|
830
|
-
withAppInterface: options.withAppInterface
|
|
1073
|
+
withAppInterface: options.withAppInterface,
|
|
1074
|
+
forceOverwrite: (_a = options.yes) !== null && _a !== void 0 ? _a : false,
|
|
1075
|
+
nonInteractive: !process.stdin.isTTY
|
|
831
1076
|
});
|
|
832
1077
|
}
|
|
833
1078
|
finally {
|