@an5/orm 1.3.2 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +74 -11
  2. package/dist/cleanup.d.ts +1 -0
  3. package/dist/cleanup.js +177 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +147 -0
  6. package/dist/generator/src/attributes.d.ts +57 -0
  7. package/dist/generator/src/attributes.js +225 -0
  8. package/dist/generator/src/code-generator.js +49 -16
  9. package/dist/generator/src/config.d.ts +2 -2
  10. package/dist/generator/src/config.js +4 -3
  11. package/dist/generator/src/dialect.d.ts +52 -1
  12. package/dist/generator/src/dialect.js +136 -3
  13. package/dist/generator/src/dotnet-generator.js +67 -23
  14. package/dist/generator/src/golang-generator.d.ts +8 -0
  15. package/dist/generator/src/golang-generator.js +86 -21
  16. package/dist/generator/src/java-generator.d.ts +8 -0
  17. package/dist/generator/src/java-generator.js +108 -3
  18. package/dist/generator/src/kotlin-generator.js +51 -0
  19. package/dist/generator/src/parser.d.ts +1 -0
  20. package/dist/generator/src/parser.js +90 -35
  21. package/dist/generator/src/pull-schema.d.ts +54 -0
  22. package/dist/generator/src/pull-schema.js +116 -0
  23. package/dist/generator/src/push-apply.d.ts +4 -2
  24. package/dist/generator/src/push-apply.js +53 -4
  25. package/dist/generator/src/push-schema.d.ts +21 -9
  26. package/dist/generator/src/push-schema.js +132 -32
  27. package/dist/generator/src/python-generator.js +35 -22
  28. package/dist/generator/src/rust-generator.js +79 -74
  29. package/dist/generator/src/swift-generator.js +54 -3
  30. package/dist/generator/src/types.d.ts +10 -0
  31. package/dist/generator/src/view-order.d.ts +33 -0
  32. package/dist/generator/src/view-order.js +123 -0
  33. package/dist/migrate.d.ts +12 -0
  34. package/dist/migrate.js +442 -0
  35. package/dist/migration-core.d.ts +32 -3
  36. package/dist/migration-core.js +213 -43
  37. package/dist/provider-support.d.ts +0 -1
  38. package/dist/provider-support.js +0 -1
  39. package/dist/pull.d.ts +1 -0
  40. package/dist/pull.js +425 -0
  41. package/dist/push.d.ts +1 -0
  42. package/dist/push.js +93 -0
  43. package/dist/seed.d.ts +1 -0
  44. package/dist/seed.js +33 -0
  45. package/generator/src/attributes.ts +231 -0
  46. package/generator/src/code-generator.ts +47 -16
  47. package/generator/src/config.ts +5 -3
  48. package/generator/src/dialect.ts +188 -5
  49. package/generator/src/dotnet-generator.ts +68 -24
  50. package/generator/src/golang-generator.ts +89 -22
  51. package/generator/src/java-generator.ts +109 -3
  52. package/generator/src/kotlin-generator.ts +51 -0
  53. package/generator/src/parser.ts +102 -30
  54. package/generator/src/pull-schema.ts +127 -0
  55. package/generator/src/push-apply.ts +62 -5
  56. package/generator/src/push-schema.ts +160 -32
  57. package/generator/src/python-generator.ts +33 -20
  58. package/generator/src/rust-generator.ts +79 -73
  59. package/generator/src/swift-generator.ts +55 -4
  60. package/generator/src/types.ts +10 -0
  61. package/generator/src/view-order.ts +133 -0
  62. package/package.json +19 -14
package/dist/pull.js ADDED
@@ -0,0 +1,425 @@
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
+ require("dotenv/config");
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const adapters_1 = require("@an5/adapters");
10
+ const config_1 = require("./generator/src/config");
11
+ const pull_schema_1 = require("./generator/src/pull-schema");
12
+ const provider_support_1 = require("./provider-support");
13
+ const rootDir = process.cwd();
14
+ const { config, outputs } = (0, config_1.loadConfig)();
15
+ const schemaDir = outputs.schemaDir;
16
+ let _adapter = null;
17
+ async function getDb() {
18
+ if (!_adapter) {
19
+ _adapter = new adapters_1.An5Adapter({ connectionString: (0, config_1.resolveConnectionString)(config, process.env, "db:pull") });
20
+ await _adapter.$connect();
21
+ }
22
+ return _adapter;
23
+ }
24
+ /**
25
+ * Prints why a command cannot run against this provider and exits.
26
+ *
27
+ * The decision and the wording live in `./provider-support`, which stays free of Node
28
+ * globals so it can be imported and tested anywhere; only the command files, which
29
+ * are Node programs, do the exiting.
30
+ */
31
+ function refuseIfNotSqlServer(provider, command) {
32
+ const message = (0, provider_support_1.sqlServerOnlyError)(provider, command);
33
+ if (message === null)
34
+ return;
35
+ console.error(message);
36
+ process.exit(1);
37
+ }
38
+ /**
39
+ * The block name `db:pull` used before it split snake_case and protected singular
40
+ * words (`Order_item`, `User_statu`). Kept only to match a schema that already
41
+ * contains those blocks.
42
+ */
43
+ function legacyBlockName(objectName) {
44
+ return objectName.charAt(0).toUpperCase() + objectName.slice(1).replace(/s$/, "");
45
+ }
46
+ async function pull() {
47
+ // Reads sys.tables/sys.columns and writes MSSQL-shaped type names, so another
48
+ // provider would fail on the first catalog query with a driver error that says
49
+ // nothing about the connection string being wrong.
50
+ refuseIfNotSqlServer((0, config_1.providerFromConfig)(config, process.env), "db:pull");
51
+ console.log("🔍 Pulling schema from database...");
52
+ const tables = await (await getDb()).$queryRawUnsafe(`
53
+ SELECT s.name AS schemaName, t.name AS tableName
54
+ FROM sys.tables t
55
+ JOIN sys.schemas s ON t.schema_id = s.schema_id
56
+ WHERE t.is_ms_shipped = 0
57
+ ORDER BY t.name
58
+ `);
59
+ // Load existing schema files
60
+ const fileContents = {};
61
+ const modelToFileMap = {}; // modelName -> fileName
62
+ const tableToFileMap = {}; // tableName -> fileName
63
+ const viewToFileMap = {}; // view name (bare and qualified) -> fileName
64
+ const existingViewNames = {}; // view key -> block name
65
+ if (fs_1.default.existsSync(schemaDir)) {
66
+ const files = fs_1.default.readdirSync(schemaDir).filter(f => f.endsWith(".an5"));
67
+ for (const file of files) {
68
+ const fullPath = path_1.default.join(schemaDir, file);
69
+ const text = fs_1.default.readFileSync(fullPath, "utf8");
70
+ fileContents[file] = text;
71
+ // Extract model definitions and table mappings from existing file contents
72
+ const modelRegex = /model\s+(\w+)\s*\{([^}]*)\}/g;
73
+ let match;
74
+ while ((match = modelRegex.exec(text)) !== null) {
75
+ const modelName = match[1];
76
+ const block = match[2];
77
+ modelToFileMap[modelName] = file;
78
+ // Try to find @@map
79
+ const mapMatch = block.match(/@@map\("(.+)"\)/);
80
+ if (mapMatch) {
81
+ const tableName = mapMatch[1];
82
+ tableToFileMap[tableName] = file;
83
+ }
84
+ else {
85
+ // Default pluralization mapping
86
+ const defaultTableName = modelName.toLowerCase() + "s";
87
+ tableToFileMap[defaultTableName] = file;
88
+ }
89
+ }
90
+ // Views are tracked separately: a model and a view may share a name, and a
91
+ // view without @@map is identified by its block name.
92
+ const viewHeaderRegex = /view\s+(\w+)\s*\{/g;
93
+ while ((match = viewHeaderRegex.exec(text)) !== null) {
94
+ const blockName = match[1];
95
+ const range = (0, pull_schema_1.blockRange)(text, "view", blockName);
96
+ const block = range ? text.slice(range.start, range.end) : "";
97
+ const mapMatch = block.match(/@@map\("(.+)"\)/);
98
+ const objectName = mapMatch ? mapMatch[1] : blockName.toLowerCase() + "s";
99
+ const schemaMatch = block.match(/@@schema\("(.*)"\)/);
100
+ for (const key of new Set([
101
+ blockName,
102
+ objectName,
103
+ (0, pull_schema_1.qualifiedBlockKey)(blockName, schemaMatch?.[1]),
104
+ (0, pull_schema_1.qualifiedBlockKey)(objectName, schemaMatch?.[1]),
105
+ ])) {
106
+ viewToFileMap[key] = file;
107
+ existingViewNames[key] = blockName;
108
+ }
109
+ }
110
+ }
111
+ }
112
+ else {
113
+ fs_1.default.mkdirSync(schemaDir, { recursive: true });
114
+ }
115
+ // Ensure default core.an5 exists in fileContents
116
+ const defaultFile = "core.an5";
117
+ if (!fileContents[defaultFile]) {
118
+ fileContents[defaultFile] = "";
119
+ }
120
+ const existingModelNames = new Set(Object.keys(modelToFileMap));
121
+ const generatedModels = {};
122
+ for (const table of tables) {
123
+ const { tableName, schemaName } = table;
124
+ console.log(`Introspecting table [${tableName}]...`);
125
+ // Get columns
126
+ const columns = await (await getDb()).$queryRawUnsafe(`
127
+ SELECT
128
+ c.name AS columnName,
129
+ ty.name AS dataType,
130
+ c.max_length AS maxLength,
131
+ c.precision AS precision,
132
+ c.scale AS scale,
133
+ c.is_nullable AS isNullable,
134
+ c.is_identity AS isIdentity,
135
+ pk.is_primary_key AS isPrimaryKey,
136
+ d.definition AS defaultValue
137
+ FROM sys.columns c
138
+ JOIN sys.types ty ON c.user_type_id = ty.user_type_id
139
+ LEFT JOIN (
140
+ SELECT ic.object_id, ic.column_id, i.is_primary_key
141
+ FROM sys.index_columns ic
142
+ JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
143
+ WHERE i.is_primary_key = 1
144
+ ) pk ON c.object_id = pk.object_id AND c.column_id = pk.column_id
145
+ LEFT JOIN sys.default_constraints d ON c.default_object_id = d.object_id
146
+ WHERE c.object_id = OBJECT_ID(@p_0)
147
+ ORDER BY c.column_id
148
+ `, tableName);
149
+ // Get indexes
150
+ const indexes = await (await getDb()).$queryRawUnsafe(`
151
+ SELECT
152
+ i.name AS indexName,
153
+ i.is_unique AS isUnique,
154
+ i.is_primary_key AS isPrimaryKey,
155
+ c.name AS columnName,
156
+ ic.key_ordinal AS keyOrdinal
157
+ FROM sys.indexes i
158
+ JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
159
+ JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
160
+ WHERE i.object_id = OBJECT_ID(@p_0) AND i.is_hypothetical = 0 AND i.name IS NOT NULL
161
+ ORDER BY i.name, ic.key_ordinal
162
+ `, tableName);
163
+ const indexGroups = {};
164
+ for (const idx of indexes) {
165
+ if (!indexGroups[idx.indexName]) {
166
+ indexGroups[idx.indexName] = {
167
+ isUnique: idx.isUnique === 1 || idx.isUnique === true,
168
+ isPrimaryKey: idx.isPrimaryKey === 1 || idx.isPrimaryKey === true,
169
+ columns: []
170
+ };
171
+ }
172
+ indexGroups[idx.indexName].columns.push(idx.columnName);
173
+ }
174
+ const uniqueColumns = new Set();
175
+ const compoundUniques = [];
176
+ const modelIndexes = [];
177
+ for (const [name, info] of Object.entries(indexGroups)) {
178
+ if (info.isPrimaryKey)
179
+ continue;
180
+ if (info.isUnique) {
181
+ if (info.columns.length === 1) {
182
+ uniqueColumns.add(info.columns[0]);
183
+ }
184
+ else {
185
+ compoundUniques.push(info.columns);
186
+ }
187
+ }
188
+ else {
189
+ modelIndexes.push(info.columns);
190
+ }
191
+ }
192
+ let modelName = "";
193
+ for (const [mName, file] of Object.entries(modelToFileMap)) {
194
+ const text = fileContents[file];
195
+ const modelRegex = new RegExp(`model\\s+(${mName})\\s*\\{[^}]*\\}`, 'g');
196
+ let m;
197
+ while ((m = modelRegex.exec(text)) !== null) {
198
+ const block = m[0];
199
+ const mapMatch = block.match(/@@map\("(.+)"\)/);
200
+ if (mapMatch && mapMatch[1] === tableName) {
201
+ modelName = mName;
202
+ break;
203
+ }
204
+ }
205
+ if (modelName)
206
+ break;
207
+ }
208
+ if (!modelName) {
209
+ // A schema pulled before `blockNameForObject` learned to split snake_case
210
+ // has a block named `Order_item`; keep matching it rather than adding a
211
+ // second block for the same table.
212
+ const legacy = legacyBlockName(tableName);
213
+ modelName = modelToFileMap[legacy] ? legacy : (0, pull_schema_1.blockNameForObject)(tableName);
214
+ }
215
+ // Parse existing model fields from file for merging
216
+ const existingFields = {};
217
+ const targetFile = modelToFileMap[modelName] || defaultFile;
218
+ const fileContent = fileContents[targetFile];
219
+ if (fileContent) {
220
+ const modelRegex = new RegExp(`model\\s+${modelName}\\s*\\{([^}]*)\\}`, 'g');
221
+ const match = modelRegex.exec(fileContent);
222
+ if (match) {
223
+ const block = match[1];
224
+ const lines = block.split("\n");
225
+ for (let line of lines) {
226
+ line = line.trim();
227
+ if (!line || line.startsWith("@@") || line.startsWith("model ") || line === "}")
228
+ continue;
229
+ const parts = line.split(/\s+/);
230
+ const name = parts[0];
231
+ const type = parts[1];
232
+ if (!name || !type)
233
+ continue;
234
+ const cleanType = type.replace("[]", "").replace("?", "");
235
+ const isRelation = existingModelNames.has(cleanType) || type.endsWith("[]");
236
+ existingFields[name.toLowerCase()] = {
237
+ line,
238
+ isRelation,
239
+ name
240
+ };
241
+ }
242
+ }
243
+ }
244
+ let modelStr = `model ${modelName} {\n`;
245
+ for (const col of columns) {
246
+ const { columnName, dataType, isNullable, isPrimaryKey, isIdentity, defaultValue } = col;
247
+ const sqlType = (0, pull_schema_1.sqlServerTypeForColumn)(col);
248
+ const dt = dataType.toLowerCase();
249
+ const optional = isNullable ? "?" : "";
250
+ let attributes = "";
251
+ if (isPrimaryKey)
252
+ attributes += " @id";
253
+ if (isIdentity)
254
+ attributes += " @default(autoincrement())";
255
+ if (uniqueColumns.has(columnName) && !isPrimaryKey)
256
+ attributes += " @unique";
257
+ if (defaultValue) {
258
+ if (defaultValue.includes("newid()")) {
259
+ attributes += " @default(uuid())";
260
+ }
261
+ else if (defaultValue.includes("getdate()") || defaultValue.includes("sysdatetime()")) {
262
+ attributes += " @default(now())";
263
+ }
264
+ else {
265
+ const cleaned = defaultValue.replace(/^\(+(.*?)\)+$/, '$1');
266
+ if (dt === "bit") {
267
+ if (cleaned === "1" || cleaned.toLowerCase() === "true")
268
+ attributes += " @default(true)";
269
+ else if (cleaned === "0" || cleaned.toLowerCase() === "false")
270
+ attributes += " @default(false)";
271
+ }
272
+ else if (dt === "int" || dt === "float" || dt === "bigint" || dt === "decimal" || dt === "numeric") {
273
+ if (!isNaN(Number(cleaned)))
274
+ attributes += ` @default(${cleaned})`;
275
+ }
276
+ else if (dt === "nvarchar" || dt === "varchar") {
277
+ if (cleaned.startsWith("'") && cleaned.endsWith("'")) {
278
+ const val = cleaned.slice(1, -1).replace(/"/g, '\\"');
279
+ attributes += ` @default("${val}")`;
280
+ }
281
+ }
282
+ }
283
+ }
284
+ let fieldLine = `${columnName} ${sqlType}${optional}${attributes}`;
285
+ // Merge decorators/attributes from existing schema
286
+ const existing = existingFields[columnName.toLowerCase()];
287
+ if (existing) {
288
+ const attributesMatch = existing.line.match(/(@default\(.*?\)|@updatedAt)/g);
289
+ if (attributesMatch) {
290
+ for (const attr of attributesMatch) {
291
+ if (!fieldLine.includes(attr)) {
292
+ if (attr.startsWith("@default") && fieldLine.includes("@default")) {
293
+ continue;
294
+ }
295
+ fieldLine += ` ${attr}`;
296
+ }
297
+ }
298
+ }
299
+ }
300
+ modelStr += ` ${fieldLine}\n`;
301
+ }
302
+ // Append preserved relations
303
+ for (const [lowerName, fieldInfo] of Object.entries(existingFields)) {
304
+ if (fieldInfo.isRelation) {
305
+ modelStr += ` ${fieldInfo.line}\n`;
306
+ }
307
+ }
308
+ const uniqueStrings = new Set();
309
+ for (const cols of compoundUniques) {
310
+ uniqueStrings.add(`@@unique([${cols.join(", ")}])`);
311
+ }
312
+ for (const key of uniqueStrings) {
313
+ modelStr += ` ${key}\n`;
314
+ }
315
+ const indexStrings = new Set();
316
+ for (const cols of modelIndexes) {
317
+ indexStrings.add(`@@index([${cols.join(", ")}])`);
318
+ }
319
+ for (const key of indexStrings) {
320
+ modelStr += ` ${key}\n`;
321
+ }
322
+ if (tableName !== modelName.toLowerCase() + "s") {
323
+ modelStr += `\n @@map("${tableName}")\n`;
324
+ }
325
+ // A table outside `dbo` is only the same object when the schema travels with
326
+ // it; without this, pushing the pulled schema created it in the connection's
327
+ // default schema.
328
+ if (schemaName && schemaName.toLowerCase() !== "dbo") {
329
+ modelStr += `${tableName === modelName.toLowerCase() + "s" ? "\n" : ""} @@schema("${schemaName}")\n`;
330
+ }
331
+ modelStr += "}";
332
+ generatedModels[modelName] = modelStr;
333
+ }
334
+ // Views are pulled after the tables, for the same reason push creates them
335
+ // after the tables: they may read a table this schema declares.
336
+ const views = await (await getDb()).$queryRawUnsafe(`
337
+ SELECT s.name AS schemaName, v.name AS viewName, OBJECT_DEFINITION(v.object_id) AS definition
338
+ FROM sys.views v
339
+ JOIN sys.schemas s ON v.schema_id = s.schema_id
340
+ WHERE v.is_ms_shipped = 0
341
+ ORDER BY s.name, v.name
342
+ `);
343
+ const generatedViews = {};
344
+ for (const view of views) {
345
+ const { viewName, schemaName, definition } = view;
346
+ const key = (0, pull_schema_1.qualifiedBlockKey)(viewName, schemaName);
347
+ // The name a previous pull would have chosen, kept so the block is updated
348
+ // in place instead of a duplicate appearing beside it.
349
+ const legacy = legacyBlockName(viewName);
350
+ const modelName = existingViewNames[key] ?? existingViewNames[viewName]
351
+ ?? (existingViewNames[legacy] ? legacy : (0, pull_schema_1.blockNameForObject)(viewName));
352
+ const targetFile = viewToFileMap[modelName] ?? viewToFileMap[key] ?? viewToFileMap[viewName] ?? defaultFile;
353
+ console.log(`Introspecting view [${key}]...`);
354
+ const columns = await (await getDb()).$queryRawUnsafe(`
355
+ SELECT
356
+ c.name AS columnName,
357
+ ty.name AS dataType,
358
+ c.max_length AS maxLength,
359
+ c.precision AS precision,
360
+ c.scale AS scale,
361
+ c.is_nullable AS isNullable
362
+ FROM sys.columns c
363
+ JOIN sys.types ty ON c.user_type_id = ty.user_type_id
364
+ WHERE c.object_id = OBJECT_ID(@p_0)
365
+ ORDER BY c.column_id
366
+ `, key);
367
+ const block = (0, pull_schema_1.buildViewBlock)({ modelName, viewName, schemaName, columns, definition });
368
+ if (block === null) {
369
+ // Without a definition there is nothing to put in `@@sql`, and a view block
370
+ // without one is rejected by push and migrate. Keep the database object and
371
+ // tell the user why it is the only one not in the schema.
372
+ console.warn(`⚠️ Skipped view [${key}]: its definition could not be read (VIEW DEFINITION permission?).`);
373
+ continue;
374
+ }
375
+ generatedViews[modelName] = { block, file: targetFile };
376
+ }
377
+ for (const [modelName, modelStr] of Object.entries(generatedModels)) {
378
+ const targetFile = modelToFileMap[modelName] || defaultFile;
379
+ const fileContent = fileContents[targetFile];
380
+ const modelRegex = new RegExp(`model\\s+${modelName}\\s*\\{[^}]*\\}`, 'g');
381
+ if (modelRegex.test(fileContent)) {
382
+ fileContents[targetFile] = fileContent.replace(modelRegex, modelStr);
383
+ console.log(`✏️ Updated model [${modelName}] in file [${targetFile}]`);
384
+ }
385
+ else {
386
+ fileContents[targetFile] = fileContent.trim() + "\n\n" + modelStr + "\n";
387
+ console.log(`➕ Added model [${modelName}] to file [${targetFile}]`);
388
+ }
389
+ }
390
+ for (const [viewName, entry] of Object.entries(generatedViews)) {
391
+ const fileContent = fileContents[entry.file];
392
+ const range = (0, pull_schema_1.blockRange)(fileContent, "view", viewName);
393
+ if (range) {
394
+ fileContents[entry.file] = fileContent.slice(0, range.start) + entry.block + fileContent.slice(range.end);
395
+ console.log(`✏️ Updated view [${viewName}] in file [${entry.file}]`);
396
+ }
397
+ else {
398
+ fileContents[entry.file] = fileContent.trim() + "\n\n" + entry.block + "\n";
399
+ console.log(`➕ Added view [${viewName}] to file [${entry.file}]`);
400
+ }
401
+ }
402
+ for (const [file, content] of Object.entries(fileContents)) {
403
+ const fullPath = path_1.default.join(schemaDir, file);
404
+ fs_1.default.writeFileSync(fullPath, content.trim() + "\n");
405
+ console.log(`💾 Saved updates to [${fullPath}]`);
406
+ }
407
+ // Index schema into vector store for RAG
408
+ console.log("\n📚 Indexing schema into vector store for RAG...");
409
+ try {
410
+ const { indexSchema, indexQuerySamples } = require(path_1.default.join(rootDir, "an5Agent", "dist", "rag", "indexer.js"));
411
+ await indexSchema(schemaDir);
412
+ await indexQuerySamples();
413
+ console.log("✅ Schema indexed for RAG.");
414
+ }
415
+ catch (err) {
416
+ console.warn(`⚠️ RAG indexing skipped: ${err.message || err}`);
417
+ console.warn(` Run 'npm --prefix an5Agent run rag:index' manually to build vector store.`);
418
+ }
419
+ console.log("✅ Database schema pulling completed successfully.");
420
+ process.exit(0);
421
+ }
422
+ pull().catch((err) => {
423
+ console.error("❌ Pull failed:", err);
424
+ process.exit(1);
425
+ });
package/dist/push.d.ts ADDED
@@ -0,0 +1 @@
1
+ import "dotenv/config";
package/dist/push.js ADDED
@@ -0,0 +1,93 @@
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
+ require("dotenv/config");
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const adapters_1 = require("@an5/adapters");
10
+ const config_1 = require("./generator/src/config");
11
+ const field_types_1 = require("./generator/src/field-types");
12
+ const dialect_1 = require("./generator/src/dialect");
13
+ const push_apply_1 = require("./generator/src/push-apply");
14
+ const push_schema_1 = require("./generator/src/push-schema");
15
+ const rootDir = process.cwd();
16
+ const { config, outputs } = (0, config_1.loadConfig)();
17
+ const schemaDir = outputs.schemaDir;
18
+ // The target provider comes from the connection string: the allowed types have
19
+ // to match the database about to receive the SQL, not one list shared by every
20
+ // database.
21
+ const provider = (0, config_1.providerFromConfig)(config, process.env);
22
+ let _adapter = null;
23
+ async function getDb() {
24
+ if (!_adapter) {
25
+ _adapter = new adapters_1.An5Adapter({ connectionString: (0, config_1.resolveConnectionString)(config, process.env, "db:push") });
26
+ await _adapter.$connect();
27
+ }
28
+ return _adapter;
29
+ }
30
+ async function push() {
31
+ const dialect = (0, dialect_1.dialectFor)(provider);
32
+ // Google Sheets is a grid of cells, not tables: there is no DDL to send and
33
+ // nothing to create. Say that instead of failing inside the driver.
34
+ if (!dialect.supportsDdl) {
35
+ console.error(`❌ db:push has nothing to create for ${field_types_1.PROVIDER_LABELS[provider]}: a sheet is a\n` +
36
+ ' range of cells, not a table, so there is no DDL to send.\n' +
37
+ ' Write rows through the adapter instead — the header row appears on the\n' +
38
+ ' first insert.');
39
+ process.exit(1);
40
+ }
41
+ let schemaText = "";
42
+ if (fs_1.default.existsSync(schemaDir)) {
43
+ const files = fs_1.default.readdirSync(schemaDir).filter(f => f.endsWith(".an5"));
44
+ for (const file of files) {
45
+ schemaText += fs_1.default.readFileSync(path_1.default.join(schemaDir, file), "utf8") + "\n";
46
+ }
47
+ }
48
+ else {
49
+ const schemaPath = path_1.default.join(__dirname, "schema.an5");
50
+ if (fs_1.default.existsSync(schemaPath)) {
51
+ schemaText = fs_1.default.readFileSync(schemaPath, "utf8");
52
+ }
53
+ else {
54
+ console.error(`No schema found in ${schemaDir} or schema.an5`);
55
+ process.exit(1);
56
+ }
57
+ }
58
+ const { models, views, issues } = (0, push_schema_1.parsePushSchema)(schemaText, provider);
59
+ if (issues.length > 0)
60
+ throw new field_types_1.FieldTypeError(provider, issues);
61
+ console.log(`🚀 Pushing schema to database (${field_types_1.PROVIDER_LABELS[provider]})...`);
62
+ const result = await (0, push_apply_1.applySchema)(models, dialect, {
63
+ query: async (sql) => (await getDb()).$queryRawUnsafe(sql),
64
+ execute: async (sql) => {
65
+ await (await getDb()).$executeRawUnsafe(sql);
66
+ },
67
+ log: (message) => console.log(message),
68
+ }, views);
69
+ if (result.tablesCreated > 0)
70
+ console.log(` ${result.tablesCreated} table(s) created.`);
71
+ if (result.columnsAdded > 0)
72
+ console.log(` ${result.columnsAdded} column(s) added.`);
73
+ if (result.uniqueConstraintsAdded > 0)
74
+ console.log(` ${result.uniqueConstraintsAdded} unique(s) added.`);
75
+ if (result.indexesCreated > 0)
76
+ console.log(` ${result.indexesCreated} index(es) created.`);
77
+ if (result.viewsApplied > 0)
78
+ console.log(` ${result.viewsApplied} view(s) created or replaced.`);
79
+ console.log("✅ Database push completed.");
80
+ process.exit(0);
81
+ }
82
+ push().catch((err) => {
83
+ // A field type error carries one line per offending field; the message alone
84
+ // would name the provider without saying which fields to fix.
85
+ if (err instanceof field_types_1.FieldTypeError) {
86
+ console.error(`❌ ${err.message}:`);
87
+ console.error((0, config_1.formatIssues)(err.issues));
88
+ }
89
+ else {
90
+ console.error(`❌ Push failed: ${err instanceof Error ? err.message : err}`);
91
+ }
92
+ process.exit(1);
93
+ });
package/dist/seed.d.ts ADDED
@@ -0,0 +1 @@
1
+ import "dotenv/config";
package/dist/seed.js ADDED
@@ -0,0 +1,33 @@
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
+ require("dotenv/config");
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ async function main() {
10
+ const rootDir = process.cwd();
11
+ const candidates = [
12
+ path_1.default.join(rootDir, "seed.ts"),
13
+ path_1.default.join(rootDir, "seed.js"),
14
+ path_1.default.join(rootDir, "an5", "seed.ts"),
15
+ path_1.default.join(rootDir, "an5", "seed.js"),
16
+ path_1.default.join(rootDir, "scripts", "seed.ts"),
17
+ path_1.default.join(rootDir, "scripts", "seed.js"),
18
+ ];
19
+ for (const seedPath of candidates) {
20
+ if (fs_1.default.existsSync(seedPath)) {
21
+ const relPath = path_1.default.relative(rootDir, seedPath);
22
+ console.log(`🌱 Executing project seed script: ${relPath}...`);
23
+ require(seedPath);
24
+ return;
25
+ }
26
+ }
27
+ console.log("⚠️ No project seed script found.");
28
+ console.log("👉 Create a seed.ts or seed.js file in your project root or an5/ directory to seed your database.");
29
+ }
30
+ main().catch((err) => {
31
+ console.error("❌ Seed execution failed:", err);
32
+ process.exit(1);
33
+ });