@rawsql-ts/ztd-cli 0.17.0 → 0.19.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 +532 -366
- package/package.json +26 -15
- package/templates/.editorconfig +16 -16
- package/templates/.prettierignore +2 -2
- package/templates/.prettierrc +24 -24
- package/templates/AGENTS.md +30 -52
- package/templates/CONTEXT.md +11 -0
- package/templates/CONTEXT.webapi.md +11 -0
- package/templates/DESIGN.md +17 -0
- package/templates/DEV_NOTES.md +14 -0
- package/templates/PROMPT_DOGFOOD.webapi.md +49 -0
- package/templates/README.md +46 -14
- package/templates/README.webapi.md +38 -0
- package/templates/scripts/local-source-guard.mjs +189 -0
- package/templates/src/AGENTS.md +26 -26
- package/templates/src/application/AGENTS.md +15 -0
- package/templates/src/application/README.md +6 -0
- package/templates/src/catalog/AGENTS.md +28 -37
- package/templates/src/catalog/runtime/AGENTS.md +28 -75
- package/templates/src/catalog/runtime/_coercions.local-source.ts +30 -0
- package/templates/src/catalog/runtime/_coercions.ts +30 -1
- package/templates/src/catalog/runtime/_smoke.runtime.ts +21 -21
- package/templates/src/catalog/specs/AGENTS.md +41 -48
- package/templates/src/catalog/specs/_smoke.spec.arktype.ts +21 -21
- package/templates/src/catalog/specs/_smoke.spec.zod.ts +20 -20
- package/templates/src/db/sql-client-adapters.ts +32 -0
- package/templates/src/db/sql-client.ts +24 -24
- package/templates/src/domain/AGENTS.md +15 -0
- package/templates/src/domain/README.md +6 -0
- package/templates/src/infrastructure/AGENTS.md +14 -0
- package/templates/src/infrastructure/README.md +6 -0
- package/templates/src/infrastructure/db/AGENTS.md +14 -0
- package/templates/src/infrastructure/db/sql-client-adapters.ts +34 -0
- package/templates/{dist/ztd-cli/templates/src → src/infrastructure}/db/sql-client.ts +3 -3
- package/templates/src/infrastructure/persistence/AGENTS.md +18 -0
- package/templates/src/infrastructure/persistence/README.md +8 -0
- package/templates/src/infrastructure/persistence/repositories/AGENTS.md +17 -0
- package/templates/src/infrastructure/persistence/repositories/tables/AGENTS.md +20 -0
- package/templates/src/infrastructure/persistence/repositories/tables/README.md +5 -0
- package/templates/src/infrastructure/persistence/repositories/views/AGENTS.md +16 -0
- package/templates/src/infrastructure/persistence/repositories/views/README.md +5 -0
- package/templates/src/infrastructure/telemetry/AGENTS.md +14 -0
- package/templates/src/infrastructure/telemetry/consoleRepositoryTelemetry.ts +66 -0
- package/templates/src/infrastructure/telemetry/repositoryTelemetry.ts +26 -0
- package/templates/src/infrastructure/telemetry/types.ts +48 -0
- package/templates/src/jobs/AGENTS.md +25 -26
- package/templates/src/jobs/README.md +2 -2
- package/templates/src/local/sql-contract.ts +1 -0
- package/templates/src/presentation/AGENTS.md +15 -0
- package/templates/src/presentation/http/AGENTS.md +15 -0
- package/templates/src/presentation/http/README.md +6 -0
- package/templates/src/repositories/AGENTS.md +30 -118
- package/templates/src/repositories/tables/AGENTS.md +29 -94
- package/templates/src/repositories/tables/README.md +5 -3
- package/templates/src/repositories/views/AGENTS.md +25 -25
- package/templates/src/repositories/views/README.md +5 -3
- package/templates/src/sql/AGENTS.md +30 -77
- package/templates/src/sql/README.md +5 -5
- package/templates/tests/AGENTS.md +29 -62
- package/templates/tests/generated/AGENTS.md +23 -16
- package/templates/tests/smoke.test.ts +25 -5
- package/templates/tests/smoke.validation.test.ts +34 -34
- package/templates/tests/support/AGENTS.md +24 -26
- package/templates/tests/support/global-setup.ts +15 -15
- package/templates/tests/support/testkit-client.ts +14 -14
- package/templates/tests/support/testkit-client.webapi.ts +14 -0
- package/templates/tests/ztd-layout.generated.ts +6 -6
- package/templates/tsconfig.json +15 -15
- package/templates/vitest.config.ts +13 -13
- package/templates/ztd/AGENTS.md +23 -18
- package/templates/ztd/README.md +5 -5
- package/templates/ztd/ddl/AGENTS.md +26 -34
- package/templates/ztd/ddl/demo.sql +74 -74
- package/LICENSE +0 -21
- package/dist/commands/ddl.d.ts +0 -7
- package/dist/commands/ddl.js +0 -118
- package/dist/commands/ddl.js.map +0 -1
- package/dist/commands/diff.d.ts +0 -10
- package/dist/commands/diff.js +0 -38
- package/dist/commands/diff.js.map +0 -1
- package/dist/commands/genEntities.d.ts +0 -6
- package/dist/commands/genEntities.js +0 -50
- package/dist/commands/genEntities.js.map +0 -1
- package/dist/commands/init.d.ts +0 -79
- package/dist/commands/init.js +0 -1083
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/lint.d.ts +0 -59
- package/dist/commands/lint.js +0 -358
- package/dist/commands/lint.js.map +0 -1
- package/dist/commands/options.d.ts +0 -9
- package/dist/commands/options.js +0 -48
- package/dist/commands/options.js.map +0 -1
- package/dist/commands/pull.d.ts +0 -10
- package/dist/commands/pull.js +0 -105
- package/dist/commands/pull.js.map +0 -1
- package/dist/commands/ztdConfig.d.ts +0 -23
- package/dist/commands/ztdConfig.js +0 -216
- package/dist/commands/ztdConfig.js.map +0 -1
- package/dist/commands/ztdConfigCommand.d.ts +0 -5
- package/dist/commands/ztdConfigCommand.js +0 -157
- package/dist/commands/ztdConfigCommand.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -22
- package/dist/index.js.map +0 -1
- package/dist/utils/agents.d.ts +0 -1
- package/dist/utils/agents.js +0 -48
- package/dist/utils/agents.js.map +0 -1
- package/dist/utils/collectSqlFiles.d.ts +0 -9
- package/dist/utils/collectSqlFiles.js +0 -58
- package/dist/utils/collectSqlFiles.js.map +0 -1
- package/dist/utils/connectionSummary.d.ts +0 -3
- package/dist/utils/connectionSummary.js +0 -29
- package/dist/utils/connectionSummary.js.map +0 -1
- package/dist/utils/dbConnection.d.ts +0 -29
- package/dist/utils/dbConnection.js +0 -210
- package/dist/utils/dbConnection.js.map +0 -1
- package/dist/utils/fs.d.ts +0 -1
- package/dist/utils/fs.js +0 -12
- package/dist/utils/fs.js.map +0 -1
- package/dist/utils/normalizePulledSchema.d.ts +0 -12
- package/dist/utils/normalizePulledSchema.js +0 -213
- package/dist/utils/normalizePulledSchema.js.map +0 -1
- package/dist/utils/optionalDependencies.d.ts +0 -35
- package/dist/utils/optionalDependencies.js +0 -96
- package/dist/utils/optionalDependencies.js.map +0 -1
- package/dist/utils/pgDump.d.ts +0 -11
- package/dist/utils/pgDump.js +0 -55
- package/dist/utils/pgDump.js.map +0 -1
- package/dist/utils/sqlLintHelpers.d.ts +0 -18
- package/dist/utils/sqlLintHelpers.js +0 -270
- package/dist/utils/sqlLintHelpers.js.map +0 -1
- package/dist/utils/typeMapper.d.ts +0 -4
- package/dist/utils/typeMapper.js +0 -76
- package/dist/utils/typeMapper.js.map +0 -1
- package/dist/utils/ztdProjectConfig.d.ts +0 -40
- package/dist/utils/ztdProjectConfig.js +0 -167
- package/dist/utils/ztdProjectConfig.js.map +0 -1
- package/templates/dist/drivers/pg-testkit/src/driver/PgTestkitClient.d.ts +0 -38
- package/templates/dist/drivers/pg-testkit/src/driver/PgTestkitClient.js +0 -117
- package/templates/dist/drivers/pg-testkit/src/driver/PgTestkitClient.js.map +0 -1
- package/templates/dist/drivers/pg-testkit/src/driver/createPgTestkitPool.d.ts +0 -4
- package/templates/dist/drivers/pg-testkit/src/driver/createPgTestkitPool.js +0 -71
- package/templates/dist/drivers/pg-testkit/src/driver/createPgTestkitPool.js.map +0 -1
- package/templates/dist/drivers/pg-testkit/src/index.d.ts +0 -5
- package/templates/dist/drivers/pg-testkit/src/index.js +0 -11
- package/templates/dist/drivers/pg-testkit/src/index.js.map +0 -1
- package/templates/dist/drivers/pg-testkit/src/proxy/wrapPgClient.d.ts +0 -3
- package/templates/dist/drivers/pg-testkit/src/proxy/wrapPgClient.js +0 -79
- package/templates/dist/drivers/pg-testkit/src/proxy/wrapPgClient.js.map +0 -1
- package/templates/dist/drivers/pg-testkit/src/types.d.ts +0 -69
- package/templates/dist/drivers/pg-testkit/src/types.js +0 -3
- package/templates/dist/drivers/pg-testkit/src/types.js.map +0 -1
- package/templates/dist/drivers/pg-testkit/src/utils/fixtureState.d.ts +0 -15
- package/templates/dist/drivers/pg-testkit/src/utils/fixtureState.js +0 -34
- package/templates/dist/drivers/pg-testkit/src/utils/fixtureState.js.map +0 -1
- package/templates/dist/drivers/pg-testkit/src/utils/fixtureValidation.d.ts +0 -12
- package/templates/dist/drivers/pg-testkit/src/utils/fixtureValidation.js +0 -53
- package/templates/dist/drivers/pg-testkit/src/utils/fixtureValidation.js.map +0 -1
- package/templates/dist/mapper-core/src/index.d.ts +0 -160
- package/templates/dist/mapper-core/src/index.js +0 -637
- package/templates/dist/mapper-core/src/index.js.map +0 -1
- package/templates/dist/testkit-core/src/errors/index.d.ts +0 -49
- package/templates/dist/testkit-core/src/errors/index.js +0 -111
- package/templates/dist/testkit-core/src/errors/index.js.map +0 -1
- package/templates/dist/testkit-core/src/fixtures/ColumnAffinity.d.ts +0 -5
- package/templates/dist/testkit-core/src/fixtures/ColumnAffinity.js +0 -29
- package/templates/dist/testkit-core/src/fixtures/ColumnAffinity.js.map +0 -1
- package/templates/dist/testkit-core/src/fixtures/DdlFixtureLoader.d.ts +0 -37
- package/templates/dist/testkit-core/src/fixtures/DdlFixtureLoader.js +0 -182
- package/templates/dist/testkit-core/src/fixtures/DdlFixtureLoader.js.map +0 -1
- package/templates/dist/testkit-core/src/fixtures/FixtureProvider.d.ts +0 -20
- package/templates/dist/testkit-core/src/fixtures/FixtureProvider.js +0 -121
- package/templates/dist/testkit-core/src/fixtures/FixtureProvider.js.map +0 -1
- package/templates/dist/testkit-core/src/fixtures/FixtureStore.d.ts +0 -51
- package/templates/dist/testkit-core/src/fixtures/FixtureStore.js +0 -199
- package/templates/dist/testkit-core/src/fixtures/FixtureStore.js.map +0 -1
- package/templates/dist/testkit-core/src/fixtures/TableDefinitionSchemaRegistry.d.ts +0 -10
- package/templates/dist/testkit-core/src/fixtures/TableDefinitionSchemaRegistry.js +0 -28
- package/templates/dist/testkit-core/src/fixtures/TableDefinitionSchemaRegistry.js.map +0 -1
- package/templates/dist/testkit-core/src/fixtures/TableNameResolver.d.ts +0 -18
- package/templates/dist/testkit-core/src/fixtures/TableNameResolver.js +0 -80
- package/templates/dist/testkit-core/src/fixtures/TableNameResolver.js.map +0 -1
- package/templates/dist/testkit-core/src/fixtures/ddlLint.d.ts +0 -59
- package/templates/dist/testkit-core/src/fixtures/ddlLint.js +0 -489
- package/templates/dist/testkit-core/src/fixtures/ddlLint.js.map +0 -1
- package/templates/dist/testkit-core/src/fixtures/naming.d.ts +0 -1
- package/templates/dist/testkit-core/src/fixtures/naming.js +0 -6
- package/templates/dist/testkit-core/src/fixtures/naming.js.map +0 -1
- package/templates/dist/testkit-core/src/index.d.ts +0 -17
- package/templates/dist/testkit-core/src/index.js +0 -47
- package/templates/dist/testkit-core/src/index.js.map +0 -1
- package/templates/dist/testkit-core/src/logger/NoopLogger.d.ts +0 -8
- package/templates/dist/testkit-core/src/logger/NoopLogger.js +0 -16
- package/templates/dist/testkit-core/src/logger/NoopLogger.js.map +0 -1
- package/templates/dist/testkit-core/src/provider/TestkitProvider.d.ts +0 -57
- package/templates/dist/testkit-core/src/provider/TestkitProvider.js +0 -149
- package/templates/dist/testkit-core/src/provider/TestkitProvider.js.map +0 -1
- package/templates/dist/testkit-core/src/rewriter/ResultSelectRewriter.d.ts +0 -43
- package/templates/dist/testkit-core/src/rewriter/ResultSelectRewriter.js +0 -473
- package/templates/dist/testkit-core/src/rewriter/ResultSelectRewriter.js.map +0 -1
- package/templates/dist/testkit-core/src/rewriter/SelectAnalyzer.d.ts +0 -9
- package/templates/dist/testkit-core/src/rewriter/SelectAnalyzer.js +0 -38
- package/templates/dist/testkit-core/src/rewriter/SelectAnalyzer.js.map +0 -1
- package/templates/dist/testkit-core/src/rewriter/SelectFixtureRewriter.d.ts +0 -42
- package/templates/dist/testkit-core/src/rewriter/SelectFixtureRewriter.js +0 -298
- package/templates/dist/testkit-core/src/rewriter/SelectFixtureRewriter.js.map +0 -1
- package/templates/dist/testkit-core/src/sql/SqliteValuesBuilder.d.ts +0 -12
- package/templates/dist/testkit-core/src/sql/SqliteValuesBuilder.js +0 -63
- package/templates/dist/testkit-core/src/sql/SqliteValuesBuilder.js.map +0 -1
- package/templates/dist/testkit-core/src/types/index.d.ts +0 -69
- package/templates/dist/testkit-core/src/types/index.js +0 -3
- package/templates/dist/testkit-core/src/types/index.js.map +0 -1
- package/templates/dist/testkit-core/src/utils/queryHelpers.d.ts +0 -28
- package/templates/dist/testkit-core/src/utils/queryHelpers.js +0 -81
- package/templates/dist/testkit-core/src/utils/queryHelpers.js.map +0 -1
- package/templates/dist/writer-core/src/index.d.ts +0 -34
- package/templates/dist/writer-core/src/index.js +0 -115
- package/templates/dist/writer-core/src/index.js.map +0 -1
- package/templates/dist/ztd-cli/templates/src/db/sql-client.d.ts +0 -20
- package/templates/dist/ztd-cli/templates/src/db/sql-client.js +0 -3
- package/templates/dist/ztd-cli/templates/src/db/sql-client.js.map +0 -1
- package/templates/dist/ztd-cli/templates/src/repositories/user-accounts.d.ts +0 -36
- package/templates/dist/ztd-cli/templates/src/repositories/user-accounts.js +0 -85
- package/templates/dist/ztd-cli/templates/src/repositories/user-accounts.js.map +0 -1
- package/templates/dist/ztd-cli/templates/tests/generated/ztd-row-map.generated.d.ts +0 -20
- package/templates/dist/ztd-cli/templates/tests/generated/ztd-row-map.generated.js +0 -33
- package/templates/dist/ztd-cli/templates/tests/generated/ztd-row-map.generated.js.map +0 -1
- package/templates/dist/ztd-cli/templates/tests/support/global-setup.d.ts +0 -10
- package/templates/dist/ztd-cli/templates/tests/support/global-setup.js +0 -29
- package/templates/dist/ztd-cli/templates/tests/support/global-setup.js.map +0 -1
- package/templates/dist/ztd-cli/templates/tests/support/testkit-client.d.ts +0 -66
- package/templates/dist/ztd-cli/templates/tests/support/testkit-client.js +0 -552
- package/templates/dist/ztd-cli/templates/tests/support/testkit-client.js.map +0 -1
- package/templates/dist/ztd-cli/templates/tests/user-profiles.test.d.ts +0 -1
- package/templates/dist/ztd-cli/templates/tests/user-profiles.test.js +0 -82
- package/templates/dist/ztd-cli/templates/tests/user-profiles.test.js.map +0 -1
- package/templates/dist/ztd-cli/templates/tests/writer-constraints.test.d.ts +0 -1
- package/templates/dist/ztd-cli/templates/tests/writer-constraints.test.js +0 -29
- package/templates/dist/ztd-cli/templates/tests/writer-constraints.test.js.map +0 -1
- package/templates/dist/ztd-cli/templates/tests/ztd-layout.generated.d.ts +0 -7
- package/templates/dist/ztd-cli/templates/tests/ztd-layout.generated.js +0 -10
- package/templates/dist/ztd-cli/templates/tests/ztd-layout.generated.js.map +0 -1
package/dist/commands/init.js
DELETED
|
@@ -1,1083 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.createConsolePrompter = createConsolePrompter;
|
|
7
|
-
exports.runInitCommand = runInitCommand;
|
|
8
|
-
exports.normalizeSchemaName = normalizeSchemaName;
|
|
9
|
-
exports.sanitizeSchemaFileName = sanitizeSchemaFileName;
|
|
10
|
-
exports.registerInitCommand = registerInitCommand;
|
|
11
|
-
const node_child_process_1 = require("node:child_process");
|
|
12
|
-
const node_fs_1 = require("node:fs");
|
|
13
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
14
|
-
const promises_1 = __importDefault(require("node:readline/promises"));
|
|
15
|
-
const fs_1 = require("../utils/fs");
|
|
16
|
-
const agents_1 = require("../utils/agents");
|
|
17
|
-
const ztdProjectConfig_1 = require("../utils/ztdProjectConfig");
|
|
18
|
-
const ztdConfig_1 = require("./ztdConfig");
|
|
19
|
-
const pull_1 = require("./pull");
|
|
20
|
-
/**
|
|
21
|
-
* Create a readline-backed prompter that reads from stdin/stdout.
|
|
22
|
-
*/
|
|
23
|
-
function createConsolePrompter() {
|
|
24
|
-
const rl = promises_1.default.createInterface({
|
|
25
|
-
input: process.stdin,
|
|
26
|
-
output: process.stdout,
|
|
27
|
-
terminal: process.stdout.isTTY
|
|
28
|
-
});
|
|
29
|
-
async function requestLine(question) {
|
|
30
|
-
return (await rl.question(question)).trim();
|
|
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
|
-
}
|
|
37
|
-
return {
|
|
38
|
-
async selectChoice(question, choices) {
|
|
39
|
-
while (true) {
|
|
40
|
-
console.log(question);
|
|
41
|
-
for (let i = 0; i < choices.length; i += 1) {
|
|
42
|
-
console.log(` ${i + 1}. ${choices[i]}`);
|
|
43
|
-
}
|
|
44
|
-
const answer = await requestLine('Select an option: ');
|
|
45
|
-
const selected = Number(answer);
|
|
46
|
-
if (Number.isFinite(selected) && selected >= 1 && selected <= choices.length) {
|
|
47
|
-
return selected - 1;
|
|
48
|
-
}
|
|
49
|
-
console.log('Please choose a valid option number.');
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
async promptInput(question, example) {
|
|
53
|
-
while (true) {
|
|
54
|
-
const answer = await requestLine(`${question}${example ? ` (${example})` : ''}: `);
|
|
55
|
-
if (answer.length > 0) {
|
|
56
|
-
return answer;
|
|
57
|
-
}
|
|
58
|
-
console.log('This value cannot be empty.');
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
async promptInputWithDefault(question, defaultValue, example) {
|
|
62
|
-
return requestLineWithDefault(question, defaultValue, example);
|
|
63
|
-
},
|
|
64
|
-
async confirm(question) {
|
|
65
|
-
while (true) {
|
|
66
|
-
const answer = (await requestLine(`${question} (y/N): `)).toLowerCase();
|
|
67
|
-
if (answer === '') {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
if (answer === 'y' || answer === 'yes') {
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
if (answer === 'n' || answer === 'no') {
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
console.log('Please respond with y(es) or n(o).');
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
close() {
|
|
80
|
-
rl.close();
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
}
|
|
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
|
-
}
|
|
102
|
-
const README_TEMPLATE = 'README.md';
|
|
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';
|
|
112
|
-
const TESTKIT_CLIENT_TEMPLATE = 'tests/support/testkit-client.ts';
|
|
113
|
-
const GLOBAL_SETUP_TEMPLATE = 'tests/support/global-setup.ts';
|
|
114
|
-
const VITEST_CONFIG_TEMPLATE = 'vitest.config.ts';
|
|
115
|
-
const TSCONFIG_TEMPLATE = 'tsconfig.json';
|
|
116
|
-
const SQL_CLIENT_TEMPLATE = 'src/db/sql-client.ts';
|
|
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
|
-
};
|
|
142
|
-
const AGENTS_FILE_CANDIDATES = ['AGENTS.md', 'AGENTS_ztd.md'];
|
|
143
|
-
const APP_INTERFACE_SECTION_MARKER = '## Application Interface Guidance';
|
|
144
|
-
const APP_INTERFACE_SECTION = `---
|
|
145
|
-
## Application Interface Guidance
|
|
146
|
-
|
|
147
|
-
1. Repository interfaces follow Command in, Domain out so commands capture inputs and repositories return domain shapes.
|
|
148
|
-
2. Command definitions and validation stay unified to prevent divergence between surface APIs and SQL expectations.
|
|
149
|
-
3. Input validation relies on zod v4 or later and happens at the repository boundary before any SQL runs.
|
|
150
|
-
4. Only validated inputs reach SQL execution; reject raw external objects as soon as possible.
|
|
151
|
-
5. Domain models live under a dedicated src/domain location so semantics stay centralized.
|
|
152
|
-
6. Build the CRUD behavior first and revisit repository interfaces during review, not before the SQL works.
|
|
153
|
-
7. Guard every behavioral change with unit tests so regression risks stay low.
|
|
154
|
-
`;
|
|
155
|
-
function resolveTemplateDirectory() {
|
|
156
|
-
const candidates = [
|
|
157
|
-
// Prefer the installed package layout: <pkg>/dist/commands → <pkg>/templates.
|
|
158
|
-
node_path_1.default.resolve(__dirname, '..', '..', '..', 'templates'),
|
|
159
|
-
// Support legacy layouts that copied templates into dist/.
|
|
160
|
-
node_path_1.default.resolve(__dirname, '..', '..', 'templates'),
|
|
161
|
-
// Support running tests directly from the monorepo source tree.
|
|
162
|
-
node_path_1.default.resolve(process.cwd(), 'packages', 'ztd-cli', 'templates')
|
|
163
|
-
];
|
|
164
|
-
// Pick the first directory that contains the expected template entrypoint.
|
|
165
|
-
for (const candidate of candidates) {
|
|
166
|
-
if ((0, node_fs_1.existsSync)(node_path_1.default.join(candidate, README_TEMPLATE))) {
|
|
167
|
-
return candidate;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
return candidates[0];
|
|
171
|
-
}
|
|
172
|
-
// Resolve templates from a shipped directory so `ztd init` works after `npm install`.
|
|
173
|
-
const TEMPLATE_DIRECTORY = resolveTemplateDirectory();
|
|
174
|
-
const DEFAULT_DEPENDENCIES = {
|
|
175
|
-
ensureDirectory: fs_1.ensureDirectory,
|
|
176
|
-
writeFile: (filePath, contents) => (0, node_fs_1.writeFileSync)(filePath, contents, 'utf8'),
|
|
177
|
-
fileExists: (filePath) => (0, node_fs_1.existsSync)(filePath),
|
|
178
|
-
runPullSchema: pull_1.runPullSchema,
|
|
179
|
-
runGenerateZtdConfig: ztdConfig_1.runGenerateZtdConfig,
|
|
180
|
-
checkPgDump: () => {
|
|
181
|
-
var _a;
|
|
182
|
-
const executable = (_a = process.env.PG_DUMP_PATH) !== null && _a !== void 0 ? _a : 'pg_dump';
|
|
183
|
-
const result = (0, node_child_process_1.spawnSync)(executable, ['--version'], { stdio: 'ignore' });
|
|
184
|
-
return result.status === 0 && !result.error;
|
|
185
|
-
},
|
|
186
|
-
log: (message) => {
|
|
187
|
-
console.log(message);
|
|
188
|
-
},
|
|
189
|
-
copyAgentsTemplate: agents_1.copyAgentsTemplate,
|
|
190
|
-
installPackages: ({ rootDir, kind, packages, packageManager }) => {
|
|
191
|
-
var _a, _b;
|
|
192
|
-
// Use the Windows shim executables so spawnSync finds the package manager in PATH.
|
|
193
|
-
const executable = resolvePackageManagerExecutable(packageManager);
|
|
194
|
-
const args = buildPackageManagerArgs(kind, packageManager, packages);
|
|
195
|
-
if (args.length === 0) {
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
const isWin32 = process.platform === 'win32';
|
|
199
|
-
// Prefer shell execution for .cmd/.bat on Windows to avoid a guaranteed failure.
|
|
200
|
-
const preferShell = isWin32 && /\.(cmd|bat)$/i.test(executable);
|
|
201
|
-
const baseSpawnOptions = {
|
|
202
|
-
cwd: rootDir,
|
|
203
|
-
stdio: 'inherit',
|
|
204
|
-
shell: false
|
|
205
|
-
};
|
|
206
|
-
const shellSpawnOptions = {
|
|
207
|
-
...baseSpawnOptions,
|
|
208
|
-
shell: true
|
|
209
|
-
};
|
|
210
|
-
let result = (0, node_child_process_1.spawnSync)(executable, args, preferShell ? shellSpawnOptions : baseSpawnOptions);
|
|
211
|
-
if (result.error && isWin32 && !preferShell) {
|
|
212
|
-
// Retry with cmd.exe only on Windows so .cmd shims resolve reliably.
|
|
213
|
-
result = (0, node_child_process_1.spawnSync)(executable, args, shellSpawnOptions);
|
|
214
|
-
}
|
|
215
|
-
if (result.error && executable !== packageManager) {
|
|
216
|
-
// Retry with the bare command name in case a resolved path is rejected.
|
|
217
|
-
result = (0, node_child_process_1.spawnSync)(packageManager, args, baseSpawnOptions);
|
|
218
|
-
if (result.error && isWin32) {
|
|
219
|
-
// Final fallback to shell when the bare command still fails on Windows.
|
|
220
|
-
result = (0, node_child_process_1.spawnSync)(packageManager, args, shellSpawnOptions);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
if (result.error || result.status !== 0) {
|
|
224
|
-
const base = `Failed to run ${packageManager} ${args.join(' ')}`;
|
|
225
|
-
const reason = result.error
|
|
226
|
-
? `: ${result.error.message}`
|
|
227
|
-
: ` (exit code: ${(_a = result.status) !== null && _a !== void 0 ? _a : 'unknown'}, signal: ${(_b = result.signal) !== null && _b !== void 0 ? _b : 'none'})`;
|
|
228
|
-
throw new Error(`${base}${reason}`);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
/**
|
|
233
|
-
* Run the interactive `ztd init` workflow and return the resulting summary.
|
|
234
|
-
*/
|
|
235
|
-
async function runInitCommand(prompter, options) {
|
|
236
|
-
var _a, _b, _c, _d;
|
|
237
|
-
const rootDir = (_a = options === null || options === void 0 ? void 0 : options.rootDir) !== null && _a !== void 0 ? _a : process.cwd();
|
|
238
|
-
const dependencies = {
|
|
239
|
-
...DEFAULT_DEPENDENCIES,
|
|
240
|
-
...((_b = options === null || options === void 0 ? void 0 : options.dependencies) !== null && _b !== void 0 ? _b : {})
|
|
241
|
-
};
|
|
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`;
|
|
264
|
-
const absolutePaths = {
|
|
265
|
-
schema: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.ddlDir, schemaFileName),
|
|
266
|
-
config: node_path_1.default.join(rootDir, 'ztd.config.json'),
|
|
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'),
|
|
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'),
|
|
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'),
|
|
289
|
-
sqlClient: node_path_1.default.join(rootDir, 'src', 'db', 'sql-client.ts'),
|
|
290
|
-
testkitClient: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'support', 'testkit-client.ts'),
|
|
291
|
-
globalSetup: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'support', 'global-setup.ts'),
|
|
292
|
-
vitestConfig: node_path_1.default.join(rootDir, 'vitest.config.ts'),
|
|
293
|
-
tsconfig: node_path_1.default.join(rootDir, 'tsconfig.json'),
|
|
294
|
-
ztdDocsAgent: node_path_1.default.join(rootDir, 'ztd', 'AGENTS.md'),
|
|
295
|
-
ztdDocsReadme: node_path_1.default.join(rootDir, 'ztd', 'README.md'),
|
|
296
|
-
ztdDdlAgents: node_path_1.default.join(rootDir, 'ztd', 'ddl', 'AGENTS.md'),
|
|
297
|
-
agents: node_path_1.default.join(rootDir, 'AGENTS.md'),
|
|
298
|
-
gitignore: node_path_1.default.join(rootDir, '.gitignore'),
|
|
299
|
-
editorconfig: node_path_1.default.join(rootDir, '.editorconfig'),
|
|
300
|
-
prettierignore: node_path_1.default.join(rootDir, '.prettierignore'),
|
|
301
|
-
prettier: node_path_1.default.join(rootDir, '.prettierrc'),
|
|
302
|
-
package: node_path_1.default.join(rootDir, 'package.json')
|
|
303
|
-
};
|
|
304
|
-
const relativePath = (key) => node_path_1.default.relative(rootDir, absolutePaths[key]).replace(/\\/g, '/') || absolutePaths[key];
|
|
305
|
-
const summaries = {};
|
|
306
|
-
// Ask how the user prefers to populate the initial schema.
|
|
307
|
-
if (workflow === 'pg_dump') {
|
|
308
|
-
// Database-first path: pull the schema before writing any DDL files.
|
|
309
|
-
if (!dependencies.checkPgDump()) {
|
|
310
|
-
throw new Error('Unable to find pg_dump. Install Postgres or set PG_DUMP_PATH before running ztd init.');
|
|
311
|
-
}
|
|
312
|
-
const connectionString = await prompter.promptInput('Enter the Postgres connection string for your database', 'postgres://user:pass@host:5432/db');
|
|
313
|
-
const schemaSummary = await writeFileWithConsent(absolutePaths.schema, relativePath('schema'), dependencies, prompter, overwritePolicy, async () => {
|
|
314
|
-
dependencies.ensureDirectory(node_path_1.default.dirname(absolutePaths.schema));
|
|
315
|
-
await dependencies.runPullSchema({
|
|
316
|
-
url: connectionString,
|
|
317
|
-
out: node_path_1.default.dirname(absolutePaths.schema),
|
|
318
|
-
schemas: [schemaName]
|
|
319
|
-
});
|
|
320
|
-
});
|
|
321
|
-
summaries.schema = schemaSummary;
|
|
322
|
-
}
|
|
323
|
-
else if (workflow === 'empty') {
|
|
324
|
-
// Manual path: seed the DDL directory with a starter schema so ztd-config can run.
|
|
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 () => {
|
|
333
|
-
dependencies.ensureDirectory(node_path_1.default.dirname(absolutePaths.schema));
|
|
334
|
-
dependencies.writeFile(absolutePaths.schema, DEMO_SCHEMA_TEMPLATE(schemaName));
|
|
335
|
-
});
|
|
336
|
-
summaries.schema = schemaSummary;
|
|
337
|
-
}
|
|
338
|
-
// Seed the ztd.config.json defaults so downstream tooling knows where ddl/tests live.
|
|
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
|
-
});
|
|
346
|
-
});
|
|
347
|
-
summaries.config = configSummary;
|
|
348
|
-
const optionalFeatures = await gatherOptionalFeatures(prompter, dependencies);
|
|
349
|
-
// Emit supporting documentation that describes the workflow for contributors.
|
|
350
|
-
const readmeSummary = await writeTemplateFile(rootDir, absolutePaths.readme, relativePath('readme'), README_TEMPLATE, dependencies, prompter, overwritePolicy, true);
|
|
351
|
-
if (readmeSummary) {
|
|
352
|
-
summaries.readme = readmeSummary;
|
|
353
|
-
}
|
|
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;
|
|
413
|
-
}
|
|
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);
|
|
456
|
-
if (testkitSummary) {
|
|
457
|
-
summaries.testkitClient = testkitSummary;
|
|
458
|
-
}
|
|
459
|
-
const globalSetupSummary = await writeTemplateFile(rootDir, absolutePaths.globalSetup, relativePath('globalSetup'), GLOBAL_SETUP_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
460
|
-
if (globalSetupSummary) {
|
|
461
|
-
summaries.globalSetup = globalSetupSummary;
|
|
462
|
-
}
|
|
463
|
-
const vitestConfigSummary = await writeTemplateFile(rootDir, absolutePaths.vitestConfig, relativePath('vitestConfig'), VITEST_CONFIG_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
464
|
-
if (vitestConfigSummary) {
|
|
465
|
-
summaries.vitestConfig = vitestConfigSummary;
|
|
466
|
-
}
|
|
467
|
-
const tsconfigSummary = await writeTemplateFile(rootDir, absolutePaths.tsconfig, relativePath('tsconfig'), TSCONFIG_TEMPLATE, dependencies, prompter, overwritePolicy);
|
|
468
|
-
if (tsconfigSummary) {
|
|
469
|
-
summaries.tsconfig = tsconfigSummary;
|
|
470
|
-
}
|
|
471
|
-
const editorconfigSummary = copyTemplateFileIfMissing(rootDir, relativePath('editorconfig'), '.editorconfig', dependencies);
|
|
472
|
-
if (editorconfigSummary) {
|
|
473
|
-
summaries.editorconfig = editorconfigSummary;
|
|
474
|
-
}
|
|
475
|
-
const prettierSummary = copyTemplateFileIfMissing(rootDir, relativePath('prettier'), '.prettierrc', dependencies);
|
|
476
|
-
if (prettierSummary) {
|
|
477
|
-
summaries.prettier = prettierSummary;
|
|
478
|
-
}
|
|
479
|
-
const gitignoreSummary = copyTemplateFileIfMissing(rootDir, relativePath('gitignore'), '.gitignore', dependencies);
|
|
480
|
-
if (gitignoreSummary) {
|
|
481
|
-
summaries.gitignore = gitignoreSummary;
|
|
482
|
-
}
|
|
483
|
-
const prettierignoreSummary = copyTemplateFileIfMissing(rootDir, relativePath('prettierignore'), '.prettierignore', dependencies);
|
|
484
|
-
if (prettierignoreSummary) {
|
|
485
|
-
summaries.prettierignore = prettierignoreSummary;
|
|
486
|
-
}
|
|
487
|
-
const packageSummary = ensurePackageJsonFormatting(rootDir, relativePath('package'), dependencies, optionalFeatures);
|
|
488
|
-
if (packageSummary) {
|
|
489
|
-
summaries.package = packageSummary;
|
|
490
|
-
}
|
|
491
|
-
// Copy the AGENTS template so every project ships the same AI guardrails.
|
|
492
|
-
const agentsRelative = await ensureAgentsFile(rootDir, relativePath('agents'), dependencies);
|
|
493
|
-
if (agentsRelative) {
|
|
494
|
-
summaries.agents = agentsRelative;
|
|
495
|
-
}
|
|
496
|
-
await ensureTemplateDependenciesInstalled(rootDir, absolutePaths, summaries, dependencies);
|
|
497
|
-
const nextSteps = buildNextSteps(normalizeRelative(rootDir, absolutePaths.schema), workflow);
|
|
498
|
-
const summaryLines = buildSummaryLines(summaries, optionalFeatures, nextSteps);
|
|
499
|
-
summaryLines.forEach(dependencies.log);
|
|
500
|
-
return {
|
|
501
|
-
summary: summaryLines.join('\n'),
|
|
502
|
-
files: Object.values(summaries)
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
async function ensureAgentsFile(rootDir, fallbackRelative, dependencies) {
|
|
506
|
-
const resolution = resolveOrCreateAgentsFile(rootDir, dependencies);
|
|
507
|
-
if (!resolution) {
|
|
508
|
-
return null;
|
|
509
|
-
}
|
|
510
|
-
const relative = normalizeRelative(rootDir, resolution.absolutePath);
|
|
511
|
-
return {
|
|
512
|
-
relativePath: relative || fallbackRelative,
|
|
513
|
-
outcome: resolution.created ? 'created' : 'unchanged'
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
function resolvePackageManagerExecutable(packageManager) {
|
|
517
|
-
var _a;
|
|
518
|
-
const override = process.env.ZTD_PACKAGE_MANAGER_PATH;
|
|
519
|
-
if (override) {
|
|
520
|
-
return override;
|
|
521
|
-
}
|
|
522
|
-
if (process.platform !== 'win32') {
|
|
523
|
-
return packageManager;
|
|
524
|
-
}
|
|
525
|
-
// Prefer .cmd shims first on Windows so they take precedence over extension-less files.
|
|
526
|
-
const cmdFallbacks = {
|
|
527
|
-
npm: 'npm.cmd',
|
|
528
|
-
pnpm: 'pnpm.cmd',
|
|
529
|
-
yarn: 'yarn.cmd'
|
|
530
|
-
};
|
|
531
|
-
const cmdResolved = resolveExecutableInPath(cmdFallbacks[packageManager]);
|
|
532
|
-
if (cmdResolved) {
|
|
533
|
-
return cmdResolved;
|
|
534
|
-
}
|
|
535
|
-
// Fall back to the extension-less name if no cmd shim is found.
|
|
536
|
-
const resolved = resolveExecutableInPath(packageManager);
|
|
537
|
-
if (resolved) {
|
|
538
|
-
return resolved;
|
|
539
|
-
}
|
|
540
|
-
return (_a = cmdFallbacks[packageManager]) !== null && _a !== void 0 ? _a : packageManager;
|
|
541
|
-
}
|
|
542
|
-
function resolveExecutableInPath(executable) {
|
|
543
|
-
var _a, _b, _c;
|
|
544
|
-
const pathValue = (_b = (_a = process.env.PATH) !== null && _a !== void 0 ? _a : process.env.Path) !== null && _b !== void 0 ? _b : '';
|
|
545
|
-
if (!pathValue) {
|
|
546
|
-
return null;
|
|
547
|
-
}
|
|
548
|
-
const pathEntries = pathValue
|
|
549
|
-
.split(node_path_1.default.delimiter)
|
|
550
|
-
.map((entry) => {
|
|
551
|
-
const trimmed = entry.trim();
|
|
552
|
-
if (process.platform !== 'win32') {
|
|
553
|
-
return trimmed;
|
|
554
|
-
}
|
|
555
|
-
if (trimmed.startsWith('"') && trimmed.endsWith('"') && trimmed.length >= 2) {
|
|
556
|
-
return trimmed.slice(1, -1);
|
|
557
|
-
}
|
|
558
|
-
return trimmed;
|
|
559
|
-
})
|
|
560
|
-
.filter(Boolean);
|
|
561
|
-
const hasExtension = node_path_1.default.extname(executable).length > 0;
|
|
562
|
-
const extensions = process.platform === 'win32'
|
|
563
|
-
? ((_c = process.env.PATHEXT) !== null && _c !== void 0 ? _c : '.EXE;.CMD;.BAT;.COM')
|
|
564
|
-
.split(';')
|
|
565
|
-
.map((ext) => ext.trim())
|
|
566
|
-
.filter(Boolean)
|
|
567
|
-
: [''];
|
|
568
|
-
// Check each PATH entry with PATHEXT so we can resolve shim executables.
|
|
569
|
-
for (const entry of pathEntries) {
|
|
570
|
-
if (hasExtension) {
|
|
571
|
-
const candidate = node_path_1.default.join(entry, executable);
|
|
572
|
-
if ((0, node_fs_1.existsSync)(candidate)) {
|
|
573
|
-
return candidate;
|
|
574
|
-
}
|
|
575
|
-
continue;
|
|
576
|
-
}
|
|
577
|
-
for (const ext of extensions) {
|
|
578
|
-
const candidate = node_path_1.default.join(entry, `${executable}${ext}`);
|
|
579
|
-
if ((0, node_fs_1.existsSync)(candidate)) {
|
|
580
|
-
return candidate;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
return null;
|
|
585
|
-
}
|
|
586
|
-
function buildPackageManagerArgs(kind, packageManager, packages) {
|
|
587
|
-
if (kind === 'install') {
|
|
588
|
-
return ['install'];
|
|
589
|
-
}
|
|
590
|
-
if (packages.length === 0) {
|
|
591
|
-
return [];
|
|
592
|
-
}
|
|
593
|
-
if (packageManager === 'npm') {
|
|
594
|
-
return ['install', '-D', ...packages];
|
|
595
|
-
}
|
|
596
|
-
return ['add', '-D', ...packages];
|
|
597
|
-
}
|
|
598
|
-
function detectPackageManager(rootDir) {
|
|
599
|
-
// Prefer lockfiles to avoid guessing when multiple package managers are installed.
|
|
600
|
-
if ((0, node_fs_1.existsSync)(node_path_1.default.join(rootDir, 'pnpm-lock.yaml'))) {
|
|
601
|
-
return 'pnpm';
|
|
602
|
-
}
|
|
603
|
-
if ((0, node_fs_1.existsSync)(node_path_1.default.join(rootDir, 'yarn.lock'))) {
|
|
604
|
-
return 'yarn';
|
|
605
|
-
}
|
|
606
|
-
if ((0, node_fs_1.existsSync)(node_path_1.default.join(rootDir, 'package-lock.json'))) {
|
|
607
|
-
return 'npm';
|
|
608
|
-
}
|
|
609
|
-
// Fall back to pnpm because rawsql-ts itself standardizes on pnpm.
|
|
610
|
-
return 'pnpm';
|
|
611
|
-
}
|
|
612
|
-
function extractPackageName(specifier) {
|
|
613
|
-
if (specifier.startsWith('.') ||
|
|
614
|
-
specifier.startsWith('/') ||
|
|
615
|
-
specifier.startsWith('node:') ||
|
|
616
|
-
specifier.startsWith('#')) {
|
|
617
|
-
return null;
|
|
618
|
-
}
|
|
619
|
-
if (specifier.startsWith('@')) {
|
|
620
|
-
const [scope, name] = specifier.split('/');
|
|
621
|
-
if (!scope || !name) {
|
|
622
|
-
return null;
|
|
623
|
-
}
|
|
624
|
-
return `${scope}/${name}`;
|
|
625
|
-
}
|
|
626
|
-
const [name] = specifier.split('/');
|
|
627
|
-
return name || null;
|
|
628
|
-
}
|
|
629
|
-
function listReferencedPackagesFromSource(source) {
|
|
630
|
-
const packages = new Set();
|
|
631
|
-
const patterns = [
|
|
632
|
-
// Capture ESM imports and re-exports, including `import type`.
|
|
633
|
-
/\bfrom\s+['"]([^'"]+)['"]/g,
|
|
634
|
-
// Capture dynamic imports.
|
|
635
|
-
/\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
636
|
-
// Capture CommonJS requires.
|
|
637
|
-
/\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/g
|
|
638
|
-
];
|
|
639
|
-
for (const pattern of patterns) {
|
|
640
|
-
for (const match of source.matchAll(pattern)) {
|
|
641
|
-
const specifier = match[1];
|
|
642
|
-
if (!specifier) {
|
|
643
|
-
continue;
|
|
644
|
-
}
|
|
645
|
-
const packageName = extractPackageName(specifier);
|
|
646
|
-
if (!packageName) {
|
|
647
|
-
continue;
|
|
648
|
-
}
|
|
649
|
-
packages.add(packageName);
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
return [...packages];
|
|
653
|
-
}
|
|
654
|
-
function listDeclaredPackages(rootDir) {
|
|
655
|
-
const packagePath = node_path_1.default.join(rootDir, 'package.json');
|
|
656
|
-
if (!(0, node_fs_1.existsSync)(packagePath)) {
|
|
657
|
-
return new Set();
|
|
658
|
-
}
|
|
659
|
-
const parsed = JSON.parse((0, node_fs_1.readFileSync)(packagePath, 'utf8'));
|
|
660
|
-
const keys = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
|
|
661
|
-
const declared = new Set();
|
|
662
|
-
for (const key of keys) {
|
|
663
|
-
const record = parsed[key];
|
|
664
|
-
if (!record || typeof record !== 'object') {
|
|
665
|
-
continue;
|
|
666
|
-
}
|
|
667
|
-
for (const name of Object.keys(record)) {
|
|
668
|
-
declared.add(name);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
return declared;
|
|
672
|
-
}
|
|
673
|
-
function listTemplateReferencedPackages(absolutePaths, summaries) {
|
|
674
|
-
const packages = new Set();
|
|
675
|
-
const touchedKeys = Object.entries(summaries)
|
|
676
|
-
.filter((entry) => Boolean(entry[1]))
|
|
677
|
-
.filter(([, summary]) => summary.outcome === 'created' || summary.outcome === 'overwritten')
|
|
678
|
-
.map(([key]) => key);
|
|
679
|
-
for (const key of touchedKeys) {
|
|
680
|
-
const filePath = absolutePaths[key];
|
|
681
|
-
if (!filePath.endsWith('.ts') && !filePath.endsWith('.tsx') && !filePath.endsWith('.js')) {
|
|
682
|
-
continue;
|
|
683
|
-
}
|
|
684
|
-
if (!(0, node_fs_1.existsSync)(filePath)) {
|
|
685
|
-
continue;
|
|
686
|
-
}
|
|
687
|
-
// Parse template output after it is written so the detected packages match the emitted scaffold exactly.
|
|
688
|
-
const contents = (0, node_fs_1.readFileSync)(filePath, 'utf8');
|
|
689
|
-
listReferencedPackagesFromSource(contents).forEach((name) => packages.add(name));
|
|
690
|
-
}
|
|
691
|
-
return [...packages].sort();
|
|
692
|
-
}
|
|
693
|
-
async function ensureTemplateDependenciesInstalled(rootDir, absolutePaths, summaries, dependencies) {
|
|
694
|
-
var _a;
|
|
695
|
-
const packageJsonPath = node_path_1.default.join(rootDir, 'package.json');
|
|
696
|
-
if (!dependencies.fileExists(packageJsonPath)) {
|
|
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.');
|
|
698
|
-
return;
|
|
699
|
-
}
|
|
700
|
-
const packageManager = detectPackageManager(rootDir);
|
|
701
|
-
const referencedPackages = listTemplateReferencedPackages(absolutePaths, summaries);
|
|
702
|
-
const declaredPackages = listDeclaredPackages(rootDir);
|
|
703
|
-
// Install only packages that are not declared yet to avoid unintentionally bumping pinned versions.
|
|
704
|
-
const missingPackages = referencedPackages.filter((name) => !declaredPackages.has(name));
|
|
705
|
-
if (missingPackages.length > 0) {
|
|
706
|
-
dependencies.log(`Installing devDependencies referenced by templates (${packageManager}): ${missingPackages.join(', ')}`);
|
|
707
|
-
await dependencies.installPackages({
|
|
708
|
-
rootDir,
|
|
709
|
-
kind: 'devDependencies',
|
|
710
|
-
packages: missingPackages,
|
|
711
|
-
packageManager
|
|
712
|
-
});
|
|
713
|
-
return;
|
|
714
|
-
}
|
|
715
|
-
// If package.json was updated earlier in the init run, run install so the new entries resolve in node_modules.
|
|
716
|
-
if (((_a = summaries.package) === null || _a === void 0 ? void 0 : _a.outcome) === 'overwritten') {
|
|
717
|
-
dependencies.log(`Running ${packageManager} install to sync dependencies.`);
|
|
718
|
-
await dependencies.installPackages({ rootDir, kind: 'install', packages: [], packageManager });
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
function copyTemplateFileIfMissing(rootDir, relative, templateName, dependencies) {
|
|
722
|
-
const templatePath = node_path_1.default.join(TEMPLATE_DIRECTORY, templateName);
|
|
723
|
-
// Skip copying when the CLI package does not include the requested template.
|
|
724
|
-
if (!(0, node_fs_1.existsSync)(templatePath)) {
|
|
725
|
-
return null;
|
|
726
|
-
}
|
|
727
|
-
const targetPath = node_path_1.default.join(rootDir, relative);
|
|
728
|
-
// Avoid overwriting a file that the project already maintains.
|
|
729
|
-
if (dependencies.fileExists(targetPath)) {
|
|
730
|
-
return null;
|
|
731
|
-
}
|
|
732
|
-
dependencies.ensureDirectory(node_path_1.default.dirname(targetPath));
|
|
733
|
-
// Emit the template content so the generated project gets the same formatting defaults.
|
|
734
|
-
dependencies.writeFile(targetPath, (0, node_fs_1.readFileSync)(templatePath, 'utf8'));
|
|
735
|
-
return { relativePath: relative, outcome: 'created' };
|
|
736
|
-
}
|
|
737
|
-
function writeOptionalTemplateFile(absolutePath, relative, templateName, dependencies) {
|
|
738
|
-
const templatePath = node_path_1.default.join(TEMPLATE_DIRECTORY, templateName);
|
|
739
|
-
// Skip when the template is missing from the installed package.
|
|
740
|
-
if (!(0, node_fs_1.existsSync)(templatePath)) {
|
|
741
|
-
return null;
|
|
742
|
-
}
|
|
743
|
-
if (dependencies.fileExists(absolutePath)) {
|
|
744
|
-
// Preserve existing files for opt-in scaffolds without prompting.
|
|
745
|
-
dependencies.log(`Skipping ${relative} because the file already exists.`);
|
|
746
|
-
return { relativePath: relative, outcome: 'unchanged' };
|
|
747
|
-
}
|
|
748
|
-
dependencies.ensureDirectory(node_path_1.default.dirname(absolutePath));
|
|
749
|
-
dependencies.writeFile(absolutePath, (0, node_fs_1.readFileSync)(templatePath, 'utf8'));
|
|
750
|
-
return { relativePath: relative, outcome: 'created' };
|
|
751
|
-
}
|
|
752
|
-
function ensurePackageJsonFormatting(rootDir, relative, dependencies, optionalFeatures) {
|
|
753
|
-
var _a, _b;
|
|
754
|
-
const packagePath = node_path_1.default.join(rootDir, 'package.json');
|
|
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
|
-
};
|
|
763
|
-
let changed = false;
|
|
764
|
-
const scripts = (_a = parsed.scripts) !== null && _a !== void 0 ? _a : {};
|
|
765
|
-
const requiredScripts = {
|
|
766
|
-
test: 'vitest run',
|
|
767
|
-
typecheck: 'tsc --noEmit',
|
|
768
|
-
format: 'prettier . --write',
|
|
769
|
-
lint: 'eslint .',
|
|
770
|
-
'lint:fix': 'eslint . --fix'
|
|
771
|
-
};
|
|
772
|
-
// Ensure the canonical formatting and lint scripts exist without overwriting custom commands.
|
|
773
|
-
for (const [name, value] of Object.entries(requiredScripts)) {
|
|
774
|
-
if (name in scripts) {
|
|
775
|
-
continue;
|
|
776
|
-
}
|
|
777
|
-
scripts[name] = value;
|
|
778
|
-
changed = true;
|
|
779
|
-
}
|
|
780
|
-
if (changed) {
|
|
781
|
-
parsed.scripts = scripts;
|
|
782
|
-
}
|
|
783
|
-
// Provide lint-staged wiring for the formatting pipeline when no configuration is present.
|
|
784
|
-
if (!('lint-staged' in parsed)) {
|
|
785
|
-
parsed['lint-staged'] = {
|
|
786
|
-
'*.{ts,tsx,js,jsx,json,md,sql}': ['pnpm format']
|
|
787
|
-
};
|
|
788
|
-
changed = true;
|
|
789
|
-
}
|
|
790
|
-
// Wire simple-git-hooks only if the user has not already customized it.
|
|
791
|
-
if (!('simple-git-hooks' in parsed)) {
|
|
792
|
-
parsed['simple-git-hooks'] = {
|
|
793
|
-
'pre-commit': 'pnpm lint-staged'
|
|
794
|
-
};
|
|
795
|
-
changed = true;
|
|
796
|
-
}
|
|
797
|
-
const devDependencies = (_b = parsed.devDependencies) !== null && _b !== void 0 ? _b : {};
|
|
798
|
-
const formattingDeps = {
|
|
799
|
-
eslint: '^9.22.0',
|
|
800
|
-
'lint-staged': '^16.2.7',
|
|
801
|
-
'prettier': '^3.7.4',
|
|
802
|
-
'prettier-plugin-sql': '^0.19.2',
|
|
803
|
-
'simple-git-hooks': '^2.13.1'
|
|
804
|
-
};
|
|
805
|
-
const testingDeps = {
|
|
806
|
-
vitest: '^4.0.7',
|
|
807
|
-
typescript: '^5.8.2',
|
|
808
|
-
'@types/node': '^22.13.10'
|
|
809
|
-
};
|
|
810
|
-
// Add the formatting toolchain dependencies that back the scripts and hooks.
|
|
811
|
-
for (const [dep, version] of Object.entries(formattingDeps)) {
|
|
812
|
-
if (dep in devDependencies) {
|
|
813
|
-
continue;
|
|
814
|
-
}
|
|
815
|
-
devDependencies[dep] = version;
|
|
816
|
-
changed = true;
|
|
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
|
-
}
|
|
843
|
-
if (!changed) {
|
|
844
|
-
return null;
|
|
845
|
-
}
|
|
846
|
-
parsed.devDependencies = devDependencies;
|
|
847
|
-
dependencies.ensureDirectory(node_path_1.default.dirname(packagePath));
|
|
848
|
-
// Persist the updated manifest so the new scripts and tools are available immediately.
|
|
849
|
-
dependencies.writeFile(packagePath, `${JSON.stringify(parsed, null, 2)}\n`);
|
|
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';
|
|
859
|
-
}
|
|
860
|
-
async function writeFileWithConsent(absolutePath, relative, dependencies, prompter, overwritePolicy, writer) {
|
|
861
|
-
const { existed, write } = await confirmOverwriteIfExists(absolutePath, relative, dependencies, prompter, overwritePolicy);
|
|
862
|
-
if (!write) {
|
|
863
|
-
return { relativePath: relative, outcome: 'unchanged' };
|
|
864
|
-
}
|
|
865
|
-
await writer();
|
|
866
|
-
return { relativePath: relative, outcome: existed ? 'overwritten' : 'created' };
|
|
867
|
-
}
|
|
868
|
-
async function confirmOverwriteIfExists(absolutePath, relative, dependencies, prompter, overwritePolicy) {
|
|
869
|
-
const existed = dependencies.fileExists(absolutePath);
|
|
870
|
-
if (!existed) {
|
|
871
|
-
return { existed: false, write: true };
|
|
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
|
-
}
|
|
879
|
-
const overwrite = await prompter.confirm(`File ${relative} already exists. Overwrite?`);
|
|
880
|
-
if (!overwrite) {
|
|
881
|
-
return { existed: true, write: false };
|
|
882
|
-
}
|
|
883
|
-
return { existed: true, write: true };
|
|
884
|
-
}
|
|
885
|
-
async function writeDocFile(absolutePath, relative, contents, dependencies, prompter, overwritePolicy) {
|
|
886
|
-
const summary = await writeFileWithConsent(absolutePath, relative, dependencies, prompter, overwritePolicy, () => {
|
|
887
|
-
dependencies.ensureDirectory(node_path_1.default.dirname(absolutePath));
|
|
888
|
-
dependencies.writeFile(absolutePath, contents);
|
|
889
|
-
});
|
|
890
|
-
return summary;
|
|
891
|
-
}
|
|
892
|
-
async function writeTemplateFile(rootDir, absolutePath, relative, templateName, dependencies, prompter, overwritePolicy, allowFallback) {
|
|
893
|
-
const templateTarget = resolveTemplateTarget(rootDir, absolutePath, relative, dependencies, allowFallback);
|
|
894
|
-
if (!templateTarget) {
|
|
895
|
-
return null;
|
|
896
|
-
}
|
|
897
|
-
// Load shared documentation templates so every new project gets the same guidance.
|
|
898
|
-
const contents = loadTemplate(templateName);
|
|
899
|
-
return writeDocFile(templateTarget.absolutePath, templateTarget.relativePath, contents, dependencies, prompter, overwritePolicy);
|
|
900
|
-
}
|
|
901
|
-
function resolveTemplateTarget(rootDir, absolutePath, relative, dependencies, allowFallback) {
|
|
902
|
-
if (!dependencies.fileExists(absolutePath)) {
|
|
903
|
-
return { absolutePath, relativePath: relative };
|
|
904
|
-
}
|
|
905
|
-
if (!allowFallback || !isRootMarkdown(relative)) {
|
|
906
|
-
dependencies.log(`Skipping template ${relative} because the target file already exists.`);
|
|
907
|
-
return null;
|
|
908
|
-
}
|
|
909
|
-
// When the preferred destination already exists, try emitting a sibling with a "_ztd" suffix.
|
|
910
|
-
const parsed = node_path_1.default.parse(absolutePath);
|
|
911
|
-
const fallbackAbsolute = node_path_1.default.join(parsed.dir, `${parsed.name}_ztd${parsed.ext}`);
|
|
912
|
-
if (dependencies.fileExists(fallbackAbsolute)) {
|
|
913
|
-
const existingRelative = normalizeRelative(rootDir, fallbackAbsolute);
|
|
914
|
-
dependencies.log(`Skipping template ${relative} because both ${relative} and ${existingRelative} already exist.`);
|
|
915
|
-
return null;
|
|
916
|
-
}
|
|
917
|
-
const fallbackRelative = normalizeRelative(rootDir, fallbackAbsolute);
|
|
918
|
-
dependencies.log(`Existing ${relative} preserved; writing template as ${fallbackRelative}.`);
|
|
919
|
-
return { absolutePath: fallbackAbsolute, relativePath: fallbackRelative };
|
|
920
|
-
}
|
|
921
|
-
function normalizeRelative(rootDir, absolutePath) {
|
|
922
|
-
// Normalize the path relative to the project root so summaries use forward slashes.
|
|
923
|
-
const relative = node_path_1.default.relative(rootDir, absolutePath).replace(/\\/g, '/');
|
|
924
|
-
return relative || absolutePath;
|
|
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
|
-
}
|
|
945
|
-
function resolveOrCreateAgentsFile(rootDir, dependencies) {
|
|
946
|
-
// Prefer materializing the bundled template before looking for existing attention files.
|
|
947
|
-
const templateTarget = dependencies.copyAgentsTemplate(rootDir);
|
|
948
|
-
if (templateTarget) {
|
|
949
|
-
return { absolutePath: templateTarget, created: true };
|
|
950
|
-
}
|
|
951
|
-
for (const candidate of AGENTS_FILE_CANDIDATES) {
|
|
952
|
-
const candidatePath = node_path_1.default.join(rootDir, candidate);
|
|
953
|
-
if (dependencies.fileExists(candidatePath)) {
|
|
954
|
-
return { absolutePath: candidatePath, created: false };
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
return null;
|
|
958
|
-
}
|
|
959
|
-
async function appendAppInterfaceGuidance(rootDir, dependencies) {
|
|
960
|
-
const resolution = resolveOrCreateAgentsFile(rootDir, dependencies);
|
|
961
|
-
if (!resolution) {
|
|
962
|
-
throw new Error('Failed to locate or create an AGENTS file for application guidance.');
|
|
963
|
-
}
|
|
964
|
-
const relativePath = normalizeRelative(rootDir, resolution.absolutePath);
|
|
965
|
-
const existingContents = (0, node_fs_1.readFileSync)(resolution.absolutePath, 'utf8');
|
|
966
|
-
// Skip appending when the guidance section already exists to avoid duplicates.
|
|
967
|
-
if (existingContents.includes(APP_INTERFACE_SECTION_MARKER)) {
|
|
968
|
-
return { relativePath, outcome: 'unchanged' };
|
|
969
|
-
}
|
|
970
|
-
// Ensure the appended block is separated by blank lines for readability.
|
|
971
|
-
const baseline = existingContents.endsWith('\n') ? existingContents : `${existingContents}\n`;
|
|
972
|
-
const spacer = baseline.endsWith('\n\n') ? '' : '\n';
|
|
973
|
-
dependencies.writeFile(resolution.absolutePath, `${baseline}${spacer}${APP_INTERFACE_SECTION}\n`);
|
|
974
|
-
return { relativePath, outcome: 'overwritten' };
|
|
975
|
-
}
|
|
976
|
-
function isRootMarkdown(relative) {
|
|
977
|
-
return relative.toLowerCase().endsWith('.md') && !relative.includes('/');
|
|
978
|
-
}
|
|
979
|
-
function loadTemplate(templateName) {
|
|
980
|
-
const templatePath = node_path_1.default.join(TEMPLATE_DIRECTORY, templateName);
|
|
981
|
-
// Fail fast if the template bundle was shipped without the requested file.
|
|
982
|
-
if (!(0, node_fs_1.existsSync)(templatePath)) {
|
|
983
|
-
throw new Error(`Missing template file: ${templateName}`);
|
|
984
|
-
}
|
|
985
|
-
return (0, node_fs_1.readFileSync)(templatePath, 'utf8');
|
|
986
|
-
}
|
|
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) {
|
|
999
|
-
const orderedKeys = [
|
|
1000
|
-
'schema',
|
|
1001
|
-
'config',
|
|
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',
|
|
1027
|
-
'sqlClient',
|
|
1028
|
-
'testkitClient',
|
|
1029
|
-
'globalSetup',
|
|
1030
|
-
'vitestConfig',
|
|
1031
|
-
'tsconfig',
|
|
1032
|
-
'agents',
|
|
1033
|
-
'gitignore',
|
|
1034
|
-
'editorconfig',
|
|
1035
|
-
'prettierignore',
|
|
1036
|
-
'prettier',
|
|
1037
|
-
'package'
|
|
1038
|
-
];
|
|
1039
|
-
const lines = ['ZTD project initialized.', '', 'Created:'];
|
|
1040
|
-
for (const key of orderedKeys) {
|
|
1041
|
-
const summary = summaries[key];
|
|
1042
|
-
const note = (summary === null || summary === void 0 ? void 0 : summary.outcome) === 'created'
|
|
1043
|
-
? ''
|
|
1044
|
-
: (summary === null || summary === void 0 ? void 0 : summary.outcome) === 'overwritten'
|
|
1045
|
-
? ' (overwritten existing file)'
|
|
1046
|
-
: ' (existing file preserved)';
|
|
1047
|
-
if (summary) {
|
|
1048
|
-
lines.push(` - ${summary.relativePath}${note}`);
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
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);
|
|
1058
|
-
return lines;
|
|
1059
|
-
}
|
|
1060
|
-
function registerInitCommand(program) {
|
|
1061
|
-
program
|
|
1062
|
-
.command('init')
|
|
1063
|
-
.description('Automate project setup for Zero Table Dependency workflows')
|
|
1064
|
-
.option('--with-sqlclient', 'Generate a minimal SqlClient interface for repositories')
|
|
1065
|
-
.option('--with-app-interface', 'Append application interface guidance to AGENTS.md only')
|
|
1066
|
-
.option('--yes', 'Overwrite existing scaffold files without prompting')
|
|
1067
|
-
.action(async (options) => {
|
|
1068
|
-
var _a;
|
|
1069
|
-
const prompter = createConsolePrompter();
|
|
1070
|
-
try {
|
|
1071
|
-
await runInitCommand(prompter, {
|
|
1072
|
-
withSqlClient: options.withSqlclient,
|
|
1073
|
-
withAppInterface: options.withAppInterface,
|
|
1074
|
-
forceOverwrite: (_a = options.yes) !== null && _a !== void 0 ? _a : false,
|
|
1075
|
-
nonInteractive: !process.stdin.isTTY
|
|
1076
|
-
});
|
|
1077
|
-
}
|
|
1078
|
-
finally {
|
|
1079
|
-
prompter.close();
|
|
1080
|
-
}
|
|
1081
|
-
});
|
|
1082
|
-
}
|
|
1083
|
-
//# sourceMappingURL=init.js.map
|