@lark-apaas/fullstack-cli 1.1.16-alpha.8 → 1.1.16-beta.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/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- import fs20 from "fs";
2
+ import fs21 from "fs";
3
3
  import path17 from "path";
4
4
  import { fileURLToPath as fileURLToPath4 } from "url";
5
5
  import { config as dotenvConfig } from "dotenv";
@@ -63,9 +63,18 @@ var FullstackCLI = class {
63
63
  constructor(version) {
64
64
  this.program = new Command();
65
65
  this.hooks = new HooksManager();
66
- this.program.name("fullstack-cli").description("CLI tool for fullstack template management").version(version);
66
+ this.program.name("fullstack-cli").description("CLI tool for fullstack template management").version(version).option("--canary <env>", "Set canary environment (e.g., boe_canary)");
67
+ this.setupGlobalOptions();
67
68
  this.setupDefaultHooks();
68
69
  }
70
+ setupGlobalOptions() {
71
+ this.program.hook("preAction", (thisCommand) => {
72
+ const opts = thisCommand.opts();
73
+ if (opts.canary) {
74
+ process.env.FORCE_FRAMEWORK_CLI_CANARY_ENV = opts.canary;
75
+ }
76
+ });
77
+ }
69
78
  setupDefaultHooks() {
70
79
  this.hooks.afterRun((ctx, error) => {
71
80
  if (error) {
@@ -117,104 +126,94 @@ Command "${ctx.commandName}" completed in ${elapsed}ms`);
117
126
 
118
127
  // src/commands/db/schema.handler.ts
119
128
  import path2 from "path";
120
- import fs3 from "fs";
129
+ import fs4 from "fs";
121
130
  import { fileURLToPath as fileURLToPath2 } from "url";
122
131
  import { spawnSync } from "child_process";
123
132
  import { createRequire } from "module";
124
133
  import { config as loadEnv } from "dotenv";
125
134
 
135
+ // src/commands/db/gen-dbschema/index.ts
136
+ import fs3 from "fs";
137
+ import path from "path";
138
+
126
139
  // src/commands/db/gen-dbschema/postprocess.ts
127
140
  import fs2 from "fs";
128
- import path from "path";
129
141
 
130
- // src/commands/db/gen-dbschema/helper/header-format.ts
131
- var HEADER_COMMENT = "/** auto generated, do not edit */";
132
- function ensureHeaderComment(source) {
133
- let text = source.startsWith("\uFEFF") ? source.slice(1) : source;
134
- while (text.startsWith(HEADER_COMMENT)) {
135
- text = text.slice(HEADER_COMMENT.length);
136
- text = stripLeadingNewlines(text);
137
- }
138
- const trimmed = stripLeadingNewlines(text);
139
- if (trimmed.length === 0) {
140
- return `${HEADER_COMMENT}
141
- `;
142
- }
143
- return `${HEADER_COMMENT}
144
- ${trimmed}`;
142
+ // src/commands/db/gen-dbschema/transforms/core.ts
143
+ import { IndentationText, Project, QuoteKind } from "ts-morph";
144
+
145
+ // src/commands/db/gen-dbschema/transforms/types.ts
146
+ function createEmptyStats() {
147
+ return {
148
+ patchedDefects: 0,
149
+ removedPgSchemas: 0,
150
+ convertedSchemaCalls: 0,
151
+ renamedIdentifiers: [],
152
+ replacedUnknown: 0,
153
+ fallbackToText: 0,
154
+ unmatchedUnknown: [],
155
+ replacedTimestamp: 0,
156
+ replacedDefaultNow: 0,
157
+ removedSystemFields: 0,
158
+ addedImports: [],
159
+ removedImports: []
160
+ };
145
161
  }
146
- function stripLeadingNewlines(value) {
147
- let current = value;
148
- while (current.startsWith("\r\n") || current.startsWith("\n")) {
149
- current = current.startsWith("\r\n") ? current.slice(2) : current.slice(1);
162
+
163
+ // src/commands/db/gen-dbschema/transforms/core.ts
164
+ var PROJECT_OPTIONS = {
165
+ skipAddingFilesFromTsConfig: true,
166
+ skipFileDependencyResolution: true,
167
+ manipulationSettings: {
168
+ indentationText: IndentationText.TwoSpaces,
169
+ quoteKind: QuoteKind.Single
150
170
  }
151
- return current;
171
+ };
172
+ function parseSource(source, fileName = "schema.ts") {
173
+ const project = new Project({
174
+ ...PROJECT_OPTIONS,
175
+ useInMemoryFileSystem: true
176
+ });
177
+ const sourceFile = project.createSourceFile(fileName, source);
178
+ return { project, sourceFile };
152
179
  }
153
- function collapseExtraBlankLines(text) {
154
- return text.replace(/\n{3,}/g, "\n\n");
180
+ function applyTransforms(sourceFile, transforms) {
181
+ const stats = createEmptyStats();
182
+ for (const transform of transforms) {
183
+ try {
184
+ transform.transform({ sourceFile, stats });
185
+ } catch (error) {
186
+ console.error(`[ast] Transform "${transform.name}" failed:`, error);
187
+ throw error;
188
+ }
189
+ }
190
+ return stats;
155
191
  }
156
-
157
- // src/commands/db/gen-dbschema/helper/schema-conversion.ts
158
- function removePgSchemaDeclarations(source) {
159
- return source.replace(/export const \w+ = pgSchema\([\s\S]*?\);\n*/g, "");
160
- }
161
- function convertSchemaTableInvocations(source) {
162
- let converted = 0;
163
- let text = source.replace(/([A-Za-z0-9_]+)\.table\(/g, () => {
164
- converted += 1;
165
- return "pgTable(";
166
- });
167
- text = text.replace(/([A-Za-z0-9_]+)\.view\(/g, () => {
168
- converted += 1;
169
- return "pgView(";
170
- });
171
- text = text.replace(/([A-Za-z0-9_]+)\.materializedView\(/g, () => {
172
- converted += 1;
173
- return "pgMaterializedView(";
192
+ function formatSourceFile(sourceFile) {
193
+ sourceFile.formatText({
194
+ indentSize: 2,
195
+ convertTabsToSpaces: true
174
196
  });
175
- text = text.replace(/([A-Za-z0-9_]+)\.enum\(/g, () => {
176
- converted += 1;
177
- return "pgEnum(";
178
- });
179
- text = text.replace(/([A-Za-z0-9_]+)\.sequence\(/g, () => {
180
- converted += 1;
181
- return "pgSequence(";
182
- });
183
- return { text, converted };
184
197
  }
198
+ function printSourceFile(sourceFile) {
199
+ return sourceFile.getFullText();
200
+ }
201
+
202
+ // src/commands/db/gen-dbschema/transforms/ast/rename-identifiers.ts
203
+ import { Node } from "ts-morph";
185
204
 
186
- // src/commands/db/gen-dbschema/helper/table-rename.ts
205
+ // src/commands/db/gen-dbschema/utils/identifier.ts
187
206
  import { pinyin } from "pinyin-pro";
188
- function renamePgTableConstants(source) {
189
- const pgTableRegex = /export const\s+([^\s=]+)\s*=\s*(pgTable|pgView|pgMaterializedView)\(\s*["'`]([^"'`]+)["'`]/gu;
190
- const renames = [];
191
- const updated = source.replace(pgTableRegex, (match, currentName, factory, tableName) => {
192
- const sanitized = sanitizeIdentifier(tableName);
193
- if (sanitized === currentName) {
194
- return match;
195
- }
196
- renames.push({ from: currentName, to: sanitized });
197
- const equalsIndex = match.indexOf("=");
198
- const suffix = equalsIndex >= 0 ? match.slice(equalsIndex) : ` = ${factory}("${tableName}"`;
199
- const normalizedSuffix = suffix.trimStart();
200
- return `export const ${sanitized} ${normalizedSuffix}`;
201
- });
202
- return { text: updated, renames };
203
- }
204
- function updateTableReferenceIdentifiers(source, renames) {
205
- if (renames.length === 0) {
206
- return source;
207
+ function toAsciiName(name) {
208
+ if (!/[^\x00-\x7F]/.test(name)) {
209
+ return name;
210
+ }
211
+ try {
212
+ const transliterated = pinyin(name, { toneType: "none", type: "array" }).join("_");
213
+ return transliterated || name;
214
+ } catch {
215
+ return name;
207
216
  }
208
- return renames.reduce((acc, rename) => {
209
- if (!rename.from || rename.from === rename.to) {
210
- return acc;
211
- }
212
- const pattern = new RegExp(`\\b${escapeRegExp(rename.from)}(\\s*\\.)`, "g");
213
- return acc.replace(pattern, `${rename.to}$1`);
214
- }, source);
215
- }
216
- function escapeRegExp(value) {
217
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\//g, "\\/");
218
217
  }
219
218
  function toCamelCase(str) {
220
219
  const words = str.split(/[_\-\s]+/).filter(Boolean);
@@ -242,120 +241,722 @@ function sanitizeIdentifier(name) {
242
241
  }
243
242
  return sanitized;
244
243
  }
245
- function toAsciiName(name) {
246
- if (!/[^\x00-\x7F]/.test(name)) {
247
- return name;
244
+ function getUniqueIdentifier(name, usedIdentifiers) {
245
+ const base = sanitizeIdentifier(name);
246
+ if (!usedIdentifiers.has(base)) {
247
+ usedIdentifiers.add(base);
248
+ return base;
248
249
  }
249
- try {
250
- const transliterated = pinyin(name, { toneType: "none", type: "array" }).join("_");
251
- return transliterated || name;
252
- } catch (error) {
253
- return name;
250
+ let suffix = 2;
251
+ while (usedIdentifiers.has(`${base}${suffix}`)) {
252
+ suffix++;
254
253
  }
254
+ const unique = `${base}${suffix}`;
255
+ usedIdentifiers.add(unique);
256
+ return unique;
255
257
  }
256
258
 
257
- // src/commands/db/gen-dbschema/helper/custom-types.ts
258
- var CUSTOM_TYPE_PATTERN = /\/\/ TODO: failed to parse database type '(?:\w+\.)?(user_profile|file_attachment)(\[\])?'/;
259
- function replaceUnknownColumns(source) {
260
- const lines = source.split("\n");
261
- const result = [];
262
- let replaced = 0;
263
- const unmatched = [];
264
- for (let i = 0; i < lines.length; i += 1) {
265
- const line = lines[i];
266
- const match = line.match(CUSTOM_TYPE_PATTERN);
267
- if (match) {
268
- const typeName = match[1];
269
- const factory = typeName === "user_profile" ? "userProfile" : "fileAttachment";
270
- const replacedLine = replaceFollowingUnknown(lines[i + 1], factory);
271
- if (replacedLine) {
272
- result.push(replacedLine);
273
- replaced += 1;
274
- i += 1;
259
+ // src/commands/db/gen-dbschema/transforms/ast/rename-identifiers.ts
260
+ var PG_FACTORIES = [
261
+ "pgTable",
262
+ "pgView",
263
+ "pgMaterializedView",
264
+ "pgEnum",
265
+ "pgSequence",
266
+ // Also match schema.xxx() format (before conversion)
267
+ "table",
268
+ "view",
269
+ "materializedView",
270
+ "enum",
271
+ "sequence"
272
+ ];
273
+ function extractFactoryName(initializer) {
274
+ if (!Node.isCallExpression(initializer)) {
275
+ return void 0;
276
+ }
277
+ const args = initializer.getArguments();
278
+ if (args.length === 0) {
279
+ return void 0;
280
+ }
281
+ const firstArg = args[0];
282
+ if (Node.isStringLiteral(firstArg)) {
283
+ return firstArg.getLiteralText();
284
+ }
285
+ return void 0;
286
+ }
287
+ function isPgFactoryCall(initializer) {
288
+ if (!Node.isCallExpression(initializer)) {
289
+ return false;
290
+ }
291
+ const expression = initializer.getExpression();
292
+ const exprText = expression.getText();
293
+ if (PG_FACTORIES.includes(exprText)) {
294
+ return true;
295
+ }
296
+ if (Node.isPropertyAccessExpression(expression)) {
297
+ const propName = expression.getName();
298
+ if (PG_FACTORIES.includes(propName)) {
299
+ return true;
300
+ }
301
+ }
302
+ return false;
303
+ }
304
+ function getCurrentName(decl) {
305
+ const name = decl.getName();
306
+ return name.replace(/^"|"$/g, "");
307
+ }
308
+ function collectExistingIdentifiers(ctx) {
309
+ const { sourceFile } = ctx;
310
+ const identifiers = /* @__PURE__ */ new Set();
311
+ for (const statement of sourceFile.getStatements()) {
312
+ if (!Node.isVariableStatement(statement)) {
313
+ continue;
314
+ }
315
+ for (const decl of statement.getDeclarations()) {
316
+ const name = getCurrentName(decl);
317
+ if (name) {
318
+ identifiers.add(name);
319
+ }
320
+ }
321
+ }
322
+ return identifiers;
323
+ }
324
+ var renameIdentifiersTransform = {
325
+ name: "rename-identifiers",
326
+ transform(ctx) {
327
+ const { sourceFile, stats } = ctx;
328
+ const usedIdentifiers = collectExistingIdentifiers(ctx);
329
+ const renames = [];
330
+ for (const statement of sourceFile.getStatements()) {
331
+ if (!Node.isVariableStatement(statement)) {
332
+ continue;
333
+ }
334
+ if (!statement.hasExportKeyword()) {
335
+ continue;
336
+ }
337
+ for (const decl of statement.getDeclarations()) {
338
+ const initializer = decl.getInitializer();
339
+ if (!initializer) {
340
+ continue;
341
+ }
342
+ if (!isPgFactoryCall(initializer)) {
343
+ continue;
344
+ }
345
+ const tableName = extractFactoryName(initializer);
346
+ if (!tableName) {
347
+ continue;
348
+ }
349
+ const currentName = getCurrentName(decl);
350
+ const baseSanitized = sanitizeIdentifier(tableName);
351
+ if (baseSanitized === currentName) {
352
+ continue;
353
+ }
354
+ usedIdentifiers.delete(currentName);
355
+ const sanitized = getUniqueIdentifier(tableName, usedIdentifiers);
356
+ renames.push({ decl, from: currentName, to: sanitized });
357
+ }
358
+ }
359
+ for (const { decl, from, to } of renames.reverse()) {
360
+ try {
361
+ decl.rename(to);
362
+ stats.renamedIdentifiers.push({ from, to });
363
+ } catch (error) {
364
+ console.warn(`[rename-identifiers] Failed to rename "${from}" to "${to}":`, error);
365
+ }
366
+ }
367
+ stats.renamedIdentifiers.reverse();
368
+ }
369
+ };
370
+
371
+ // src/commands/db/gen-dbschema/transforms/ast/remove-pg-schema.ts
372
+ import { Node as Node2 } from "ts-morph";
373
+ var removePgSchemaTransform = {
374
+ name: "remove-pg-schema",
375
+ transform(ctx) {
376
+ const { sourceFile, stats } = ctx;
377
+ const statementsToRemove = [];
378
+ for (const statement of sourceFile.getStatements()) {
379
+ if (!Node2.isVariableStatement(statement)) {
380
+ continue;
381
+ }
382
+ for (const decl of statement.getDeclarations()) {
383
+ const initializer = decl.getInitializer();
384
+ if (!initializer || !Node2.isCallExpression(initializer)) {
385
+ continue;
386
+ }
387
+ const calleeText = initializer.getExpression().getText();
388
+ if (calleeText === "pgSchema") {
389
+ statementsToRemove.push(statement);
390
+ stats.removedPgSchemas++;
391
+ break;
392
+ }
393
+ }
394
+ }
395
+ for (const statement of statementsToRemove.reverse()) {
396
+ statement.remove();
397
+ }
398
+ }
399
+ };
400
+
401
+ // src/commands/db/gen-dbschema/transforms/ast/convert-schema-calls.ts
402
+ import { Node as Node3 } from "ts-morph";
403
+ var SCHEMA_METHOD_TO_PG = {
404
+ table: "pgTable",
405
+ view: "pgView",
406
+ materializedView: "pgMaterializedView",
407
+ enum: "pgEnum",
408
+ sequence: "pgSequence"
409
+ };
410
+ var convertSchemaCallsTransform = {
411
+ name: "convert-schema-calls",
412
+ transform(ctx) {
413
+ const { sourceFile, stats } = ctx;
414
+ sourceFile.forEachDescendant((node) => {
415
+ if (!Node3.isCallExpression(node)) {
416
+ return;
417
+ }
418
+ const expression = node.getExpression();
419
+ if (!Node3.isPropertyAccessExpression(expression)) {
420
+ return;
421
+ }
422
+ const objectExpr = expression.getExpression();
423
+ const methodName = expression.getName();
424
+ const pgFactoryName = SCHEMA_METHOD_TO_PG[methodName];
425
+ if (!pgFactoryName) {
426
+ return;
427
+ }
428
+ if (!Node3.isIdentifier(objectExpr)) {
429
+ return;
430
+ }
431
+ expression.replaceWithText(pgFactoryName);
432
+ stats.convertedSchemaCalls++;
433
+ });
434
+ }
435
+ };
436
+
437
+ // src/commands/db/gen-dbschema/transforms/ast/patch-defects.ts
438
+ import { Node as Node4 } from "ts-morph";
439
+ var patchDefectsTransform = {
440
+ name: "patch-defects",
441
+ transform(ctx) {
442
+ const { sourceFile, stats } = ctx;
443
+ sourceFile.forEachDescendant((node) => {
444
+ if (!Node4.isCallExpression(node)) {
445
+ return;
446
+ }
447
+ const expr = node.getExpression();
448
+ if (!Node4.isPropertyAccessExpression(expr)) {
449
+ return;
450
+ }
451
+ if (expr.getName() !== "default") {
452
+ return;
453
+ }
454
+ const args = node.getArguments();
455
+ if (args.length !== 1) {
456
+ return;
457
+ }
458
+ const arg = args[0];
459
+ if (Node4.isStringLiteral(arg)) {
460
+ const text = arg.getLiteralText();
461
+ if (text === "") {
462
+ stats.patchedDefects++;
463
+ }
464
+ }
465
+ });
466
+ }
467
+ };
468
+
469
+ // src/commands/db/gen-dbschema/transforms/ast/replace-unknown.ts
470
+ import { Node as Node5 } from "ts-morph";
471
+ var KNOWN_TYPE_FACTORIES = {
472
+ user_profile: "userProfile",
473
+ file_attachment: "fileAttachment"
474
+ };
475
+ var replaceUnknownTransform = {
476
+ name: "replace-unknown",
477
+ transform(ctx) {
478
+ const { sourceFile, stats } = ctx;
479
+ const fullText = sourceFile.getFullText();
480
+ sourceFile.forEachDescendant((node) => {
481
+ if (!Node5.isCallExpression(node)) {
482
+ return;
483
+ }
484
+ const expression = node.getExpression();
485
+ if (!Node5.isIdentifier(expression) || expression.getText() !== "unknown") {
486
+ return;
487
+ }
488
+ const nodeStart = node.getStart();
489
+ const textBefore = fullText.slice(Math.max(0, nodeStart - 500), nodeStart);
490
+ const lines = textBefore.split("\n");
491
+ let factoryName = "text";
492
+ let foundKnownType = false;
493
+ for (let i = lines.length - 1; i >= Math.max(0, lines.length - 5); i--) {
494
+ const line = lines[i];
495
+ const todoMatch = line.match(/\/\/ TODO: failed to parse database type '(?:\w+\.)?([\w_]+)(\[\])?'/);
496
+ if (todoMatch) {
497
+ const typeName = todoMatch[1];
498
+ if (KNOWN_TYPE_FACTORIES[typeName]) {
499
+ factoryName = KNOWN_TYPE_FACTORIES[typeName];
500
+ foundKnownType = true;
501
+ }
502
+ break;
503
+ }
504
+ }
505
+ expression.replaceWithText(factoryName);
506
+ if (foundKnownType) {
507
+ stats.replacedUnknown++;
275
508
  } else {
276
- unmatched.push(line.trim());
277
- result.push(line);
509
+ stats.fallbackToText++;
278
510
  }
511
+ });
512
+ const todoCommentRegex = /\/\/ TODO: failed to parse database type '[^']+'\s*\n/g;
513
+ const currentText = sourceFile.getFullText();
514
+ const cleanedText = currentText.replace(todoCommentRegex, "");
515
+ if (cleanedText !== currentText) {
516
+ sourceFile.replaceWithText(cleanedText);
517
+ }
518
+ }
519
+ };
520
+
521
+ // src/commands/db/gen-dbschema/transforms/ast/replace-timestamp.ts
522
+ import { Node as Node6 } from "ts-morph";
523
+ function checkTimestampOptions(optionsArg) {
524
+ let hasWithTimezone = false;
525
+ let hasModeString = false;
526
+ for (const prop of optionsArg.getProperties()) {
527
+ if (!Node6.isPropertyAssignment(prop)) {
279
528
  continue;
280
529
  }
281
- if (line.includes("unknown(")) {
282
- unmatched.push(line.trim());
530
+ const propName = prop.getName();
531
+ const initializer = prop.getInitializer();
532
+ if (propName === "withTimezone") {
533
+ if (Node6.isTrueLiteral(initializer)) {
534
+ hasWithTimezone = true;
535
+ }
536
+ }
537
+ if (propName === "mode") {
538
+ if (Node6.isStringLiteral(initializer) && initializer.getLiteralText() === "string") {
539
+ hasModeString = true;
540
+ }
283
541
  }
284
- result.push(line);
285
542
  }
286
- return {
287
- text: result.join("\n"),
288
- replaced,
289
- unmatched
290
- };
543
+ return hasWithTimezone && hasModeString;
291
544
  }
292
- function replaceFollowingUnknown(nextLine, factory) {
293
- if (!nextLine || !nextLine.includes("unknown(")) {
294
- return void 0;
545
+ var replaceTimestampTransform = {
546
+ name: "replace-timestamp",
547
+ transform(ctx) {
548
+ const { sourceFile, stats } = ctx;
549
+ const replacements = [];
550
+ sourceFile.forEachDescendant((node) => {
551
+ if (!Node6.isCallExpression(node)) {
552
+ return;
553
+ }
554
+ const expression = node.getExpression();
555
+ if (!Node6.isIdentifier(expression) || expression.getText() !== "timestamp") {
556
+ return;
557
+ }
558
+ const args = node.getArguments();
559
+ if (args.length === 2) {
560
+ const [fieldArg, optionsArg] = args;
561
+ if (!Node6.isStringLiteral(fieldArg)) {
562
+ return;
563
+ }
564
+ if (!Node6.isObjectLiteralExpression(optionsArg)) {
565
+ return;
566
+ }
567
+ if (checkTimestampOptions(optionsArg)) {
568
+ const quote = fieldArg.getQuoteKind() === 1 ? '"' : "'";
569
+ const fieldName = fieldArg.getLiteralText();
570
+ replacements.push({
571
+ node,
572
+ replacement: `customTimestamptz(${quote}${fieldName}${quote})`
573
+ });
574
+ }
575
+ return;
576
+ }
577
+ if (args.length === 1) {
578
+ const [optionsArg] = args;
579
+ if (!Node6.isObjectLiteralExpression(optionsArg)) {
580
+ return;
581
+ }
582
+ if (checkTimestampOptions(optionsArg)) {
583
+ replacements.push({
584
+ node,
585
+ replacement: "customTimestamptz()"
586
+ });
587
+ }
588
+ return;
589
+ }
590
+ });
591
+ for (const { node, replacement } of replacements.reverse()) {
592
+ node.replaceWithText(replacement);
593
+ stats.replacedTimestamp++;
594
+ }
295
595
  }
296
- return nextLine.replace("unknown(", `${factory}(`);
297
- }
596
+ };
298
597
 
299
- // src/commands/db/gen-dbschema/helper/imports.ts
300
- import fs from "fs";
301
- import { fileURLToPath } from "url";
302
- function tweakImports(source) {
303
- const importRegex = /import \{([^}]*)\} from "drizzle-orm\/pg-core";?/;
304
- const match = source.match(importRegex);
305
- if (!match) {
306
- return source;
598
+ // src/commands/db/gen-dbschema/transforms/ast/replace-default-now.ts
599
+ import { Node as Node7 } from "ts-morph";
600
+ var replaceDefaultNowTransform = {
601
+ name: "replace-default-now",
602
+ transform(ctx) {
603
+ const { sourceFile, stats } = ctx;
604
+ sourceFile.forEachDescendant((node) => {
605
+ if (!Node7.isCallExpression(node)) {
606
+ return;
607
+ }
608
+ const expression = node.getExpression();
609
+ if (!Node7.isPropertyAccessExpression(expression)) {
610
+ return;
611
+ }
612
+ if (expression.getName() !== "defaultNow") {
613
+ return;
614
+ }
615
+ if (node.getArguments().length !== 0) {
616
+ return;
617
+ }
618
+ const objectExpr = expression.getExpression();
619
+ const objectText = objectExpr.getText();
620
+ node.replaceWithText(`${objectText}.default(sql\`CURRENT_TIMESTAMP\`)`);
621
+ stats.replacedDefaultNow++;
622
+ });
307
623
  }
308
- const identifiers = match[1].split(",").map((id) => id.trim()).filter(Boolean).filter((id) => id !== "pgSchema" && id !== "customType");
309
- const filteredIdentifiers = identifiers.filter((id) => {
310
- if (id === "timestamp") {
311
- const timestampUsageRegex = /timestamp\s*\(/;
312
- return timestampUsageRegex.test(source);
624
+ };
625
+
626
+ // src/commands/db/gen-dbschema/transforms/ast/remove-system-fields.ts
627
+ import { Node as Node8 } from "ts-morph";
628
+ var SYSTEM_TO_BUSINESS = {
629
+ _created_at: ["created_at", "createdAt"],
630
+ _created_by: ["created_by", "createdBy"],
631
+ _updated_at: ["updated_at", "updatedAt"],
632
+ _updated_by: ["updated_by", "updatedBy"]
633
+ };
634
+ var SYSTEM_FIELD_COLUMNS = new Set(Object.keys(SYSTEM_TO_BUSINESS));
635
+ var TABLE_FACTORIES = ["pgTable", "pgView", "pgMaterializedView", "table", "view", "materializedView"];
636
+ function extractColumnName(prop) {
637
+ const initializer = prop.getInitializer();
638
+ if (!initializer) return void 0;
639
+ let callExpr = initializer;
640
+ while (Node8.isCallExpression(callExpr)) {
641
+ const expr = callExpr.getExpression();
642
+ if (Node8.isPropertyAccessExpression(expr)) {
643
+ const baseExpr = expr.getExpression();
644
+ if (Node8.isCallExpression(baseExpr)) {
645
+ callExpr = baseExpr;
646
+ continue;
647
+ }
313
648
  }
314
- return true;
315
- });
316
- if (source.includes("pgTable(") && !filteredIdentifiers.includes("pgTable")) {
317
- filteredIdentifiers.push("pgTable");
649
+ break;
318
650
  }
319
- if (source.includes("pgView(") && !filteredIdentifiers.includes("pgView")) {
320
- filteredIdentifiers.push("pgView");
651
+ if (!Node8.isCallExpression(callExpr)) return void 0;
652
+ const args = callExpr.getArguments();
653
+ if (args.length === 0) return void 0;
654
+ const firstArg = args[0];
655
+ if (Node8.isStringLiteral(firstArg)) {
656
+ return firstArg.getLiteralText();
321
657
  }
322
- if (source.includes("pgMaterializedView(") && !filteredIdentifiers.includes("pgMaterializedView")) {
323
- filteredIdentifiers.push("pgMaterializedView");
658
+ return void 0;
659
+ }
660
+ function getPropertyName(prop) {
661
+ const nameNode = prop.getNameNode();
662
+ if (Node8.isStringLiteral(nameNode)) {
663
+ return nameNode.getLiteralText();
664
+ } else if (Node8.isIdentifier(nameNode)) {
665
+ return nameNode.getText();
666
+ }
667
+ return "";
668
+ }
669
+ var removeSystemFieldsTransform = {
670
+ name: "remove-system-fields",
671
+ transform(ctx) {
672
+ const { sourceFile, stats } = ctx;
673
+ sourceFile.forEachDescendant((node) => {
674
+ if (!Node8.isCallExpression(node)) {
675
+ return;
676
+ }
677
+ const expression = node.getExpression();
678
+ let factoryName = "";
679
+ if (Node8.isIdentifier(expression)) {
680
+ factoryName = expression.getText();
681
+ } else if (Node8.isPropertyAccessExpression(expression)) {
682
+ factoryName = expression.getName();
683
+ }
684
+ if (!TABLE_FACTORIES.includes(factoryName)) {
685
+ return;
686
+ }
687
+ const args = node.getArguments();
688
+ if (args.length < 2) {
689
+ return;
690
+ }
691
+ const columnsArg = args[1];
692
+ if (!Node8.isObjectLiteralExpression(columnsArg)) {
693
+ return;
694
+ }
695
+ const properties = columnsArg.getProperties();
696
+ const systemFieldProps = /* @__PURE__ */ new Map();
697
+ for (const prop of properties) {
698
+ if (!Node8.isPropertyAssignment(prop)) {
699
+ continue;
700
+ }
701
+ const propName = getPropertyName(prop);
702
+ const columnName = extractColumnName(prop);
703
+ const systemFieldKey = SYSTEM_FIELD_COLUMNS.has(propName) ? propName : columnName && SYSTEM_FIELD_COLUMNS.has(columnName) ? columnName : null;
704
+ if (systemFieldKey) {
705
+ systemFieldProps.set(prop, systemFieldKey);
706
+ }
707
+ }
708
+ const businessPropertyNames = /* @__PURE__ */ new Set();
709
+ const businessColumnNames = /* @__PURE__ */ new Set();
710
+ for (const prop of properties) {
711
+ if (!Node8.isPropertyAssignment(prop)) {
712
+ continue;
713
+ }
714
+ if (systemFieldProps.has(prop)) {
715
+ continue;
716
+ }
717
+ const propName = getPropertyName(prop);
718
+ if (propName) {
719
+ businessPropertyNames.add(propName);
720
+ }
721
+ const columnName = extractColumnName(prop);
722
+ if (columnName) {
723
+ businessColumnNames.add(columnName);
724
+ }
725
+ }
726
+ const propsToRemove = [];
727
+ for (const [prop, systemFieldKey] of systemFieldProps) {
728
+ const businessFields = SYSTEM_TO_BUSINESS[systemFieldKey];
729
+ if (!businessFields) {
730
+ continue;
731
+ }
732
+ const hasBusinessField = businessFields.some(
733
+ (bf) => businessPropertyNames.has(bf) || businessColumnNames.has(bf)
734
+ );
735
+ if (hasBusinessField) {
736
+ propsToRemove.push(prop);
737
+ }
738
+ }
739
+ for (const prop of propsToRemove) {
740
+ prop.remove();
741
+ stats.removedSystemFields++;
742
+ }
743
+ });
324
744
  }
325
- if (source.includes("pgEnum(") && !filteredIdentifiers.includes("pgEnum")) {
326
- filteredIdentifiers.push("pgEnum");
745
+ };
746
+
747
+ // src/commands/db/gen-dbschema/transforms/ast/tweak-imports.ts
748
+ var REMOVE_IMPORTS = ["pgSchema", "customType"];
749
+ var PG_FACTORIES2 = ["pgTable", "pgView", "pgMaterializedView", "pgEnum", "pgSequence"];
750
+ var tweakImportsTransform = {
751
+ name: "tweak-imports",
752
+ transform(ctx) {
753
+ const { sourceFile, stats } = ctx;
754
+ const fullText = sourceFile.getFullText();
755
+ const imports = sourceFile.getImportDeclarations();
756
+ const pgCoreImport = imports.find((imp) => {
757
+ const moduleSpec = imp.getModuleSpecifierValue();
758
+ return moduleSpec === "drizzle-orm/pg-core";
759
+ });
760
+ if (!pgCoreImport) {
761
+ return;
762
+ }
763
+ const namedImports = pgCoreImport.getNamedImports();
764
+ const currentImports = namedImports.map((ni) => ni.getName());
765
+ const toRemove = [];
766
+ const toAdd = [];
767
+ for (const identifier of REMOVE_IMPORTS) {
768
+ if (currentImports.includes(identifier)) {
769
+ toRemove.push(identifier);
770
+ stats.removedImports.push(identifier);
771
+ }
772
+ }
773
+ if (currentImports.includes("timestamp")) {
774
+ const timestampUsed = /timestamp\s*\(/.test(fullText);
775
+ if (!timestampUsed) {
776
+ toRemove.push("timestamp");
777
+ stats.removedImports.push("timestamp");
778
+ }
779
+ }
780
+ for (const factory of PG_FACTORIES2) {
781
+ if (!currentImports.includes(factory)) {
782
+ const pattern = new RegExp(`${factory}\\s*\\(`);
783
+ if (pattern.test(fullText)) {
784
+ toAdd.push(factory);
785
+ stats.addedImports.push(factory);
786
+ }
787
+ }
788
+ }
789
+ for (const identifier of toRemove) {
790
+ const freshNamedImports = pgCoreImport.getNamedImports();
791
+ const namedImport = freshNamedImports.find((ni) => ni.getName() === identifier);
792
+ if (namedImport) {
793
+ namedImport.remove();
794
+ }
795
+ }
796
+ for (const identifier of toAdd) {
797
+ pgCoreImport.addNamedImport(identifier);
798
+ }
799
+ if (fullText.includes("sql`CURRENT_TIMESTAMP`")) {
800
+ const drizzleOrmImport = imports.find((imp) => {
801
+ const moduleSpec = imp.getModuleSpecifierValue();
802
+ return moduleSpec === "drizzle-orm";
803
+ });
804
+ if (!drizzleOrmImport) {
805
+ sourceFile.addImportDeclaration({
806
+ moduleSpecifier: "drizzle-orm",
807
+ namedImports: ["sql"]
808
+ });
809
+ stats.addedImports.push("sql");
810
+ } else {
811
+ const hasSql = drizzleOrmImport.getNamedImports().some((ni) => ni.getName() === "sql");
812
+ if (!hasSql) {
813
+ drizzleOrmImport.addNamedImport("sql");
814
+ stats.addedImports.push("sql");
815
+ }
816
+ }
817
+ }
327
818
  }
328
- if (source.includes("pgSequence(") && !filteredIdentifiers.includes("pgSequence")) {
329
- filteredIdentifiers.push("pgSequence");
819
+ };
820
+
821
+ // src/commands/db/gen-dbschema/transforms/ast/index.ts
822
+ var defaultTransforms = [
823
+ patchDefectsTransform,
824
+ // #2 Fix syntax errors first
825
+ removePgSchemaTransform,
826
+ // #3 Remove pgSchema declarations
827
+ convertSchemaCallsTransform,
828
+ // #4 Convert schema.xxx() to pgXxx()
829
+ renameIdentifiersTransform,
830
+ // #5+#6 Rename identifiers (auto-updates refs)
831
+ replaceUnknownTransform,
832
+ // #7 Replace unknown types
833
+ replaceTimestampTransform,
834
+ // #8 Replace timestamp
835
+ replaceDefaultNowTransform,
836
+ // #9 Replace .defaultNow()
837
+ removeSystemFieldsTransform,
838
+ // #10 Remove conflicting system fields
839
+ tweakImportsTransform
840
+ // #12 Adjust imports
841
+ ];
842
+
843
+ // src/commands/db/gen-dbschema/transforms/text/patch-defects.ts
844
+ function collectExistingIdentifiers2(source) {
845
+ const identifiers = /* @__PURE__ */ new Set();
846
+ const exportPattern = /export\s+const\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=/g;
847
+ let match;
848
+ while ((match = exportPattern.exec(source)) !== null) {
849
+ identifiers.add(match[1]);
330
850
  }
331
- const unique = Array.from(new Set(filteredIdentifiers));
332
- const replacement = `import { ${unique.join(", ")} } from "drizzle-orm/pg-core"`;
333
- return source.replace(importRegex, replacement);
851
+ return identifiers;
852
+ }
853
+ function patchDefects(source) {
854
+ let fixed = 0;
855
+ const renamedQuotedExports = [];
856
+ let text = source;
857
+ const usedIdentifiers = collectExistingIdentifiers2(source);
858
+ text = text.replace(/\.default\('\)/g, () => {
859
+ fixed += 1;
860
+ return `.default('')`;
861
+ });
862
+ const quotedExportPattern = /export const\s+"([^"]+)"\s*=/g;
863
+ text = text.replace(quotedExportPattern, (_match, quotedName) => {
864
+ const sanitized = getUniqueIdentifier(quotedName, usedIdentifiers);
865
+ renamedQuotedExports.push({ from: quotedName, to: sanitized });
866
+ fixed += 1;
867
+ return `export const ${sanitized} =`;
868
+ });
869
+ for (const { from, to } of renamedQuotedExports) {
870
+ const escaped = from.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
871
+ const callPattern = new RegExp(`"${escaped}"\\s*\\(`, "g");
872
+ text = text.replace(callPattern, `${to}(`);
873
+ const dotPattern = new RegExp(`"${escaped}"\\s*\\.`, "g");
874
+ text = text.replace(dotPattern, `${to}.`);
875
+ }
876
+ return { text, fixed, renamedQuotedExports };
877
+ }
878
+
879
+ // src/commands/db/gen-dbschema/transforms/text/header.ts
880
+ var ESLINT_DISABLE = "/* eslint-disable */";
881
+ var HEADER_COMMENT = "/** auto generated, do not edit */";
882
+ var FULL_HEADER = `${ESLINT_DISABLE}
883
+ ${HEADER_COMMENT}`;
884
+ function ensureHeader(source) {
885
+ let trimmed = source;
886
+ const headerPatterns = [
887
+ /^\/\*\s*eslint-disable\s*\*\/\s*\n?/,
888
+ /^\/\*\*\s*auto generated[^*]*\*\/\s*\n?/
889
+ ];
890
+ for (const pattern of headerPatterns) {
891
+ while (pattern.test(trimmed)) {
892
+ trimmed = trimmed.replace(pattern, "");
893
+ }
894
+ }
895
+ trimmed = trimmed.trimStart();
896
+ return `${FULL_HEADER}
897
+ ${trimmed}`;
898
+ }
899
+
900
+ // src/commands/db/gen-dbschema/transforms/text/system-comments.ts
901
+ var SYSTEM_FIELD_COMMENTS = {
902
+ _created_at: "Creation time",
903
+ _created_by: "Creator",
904
+ _updated_at: "Update time",
905
+ _updated_by: "Updater"
906
+ };
907
+ function addSystemFieldComments(source) {
908
+ const lines = source.split("\n");
909
+ for (let i = 0; i < lines.length; i += 1) {
910
+ const line = lines[i];
911
+ const entry = Object.entries(SYSTEM_FIELD_COMMENTS).find(
912
+ ([key]) => line.includes(`"${key}"`) || line.includes(`'${key}'`)
913
+ );
914
+ if (!entry) {
915
+ continue;
916
+ }
917
+ const [, description] = entry;
918
+ const previousLine = lines[i - 1]?.trim() ?? "";
919
+ if (previousLine.startsWith("//") && previousLine.includes("System field")) {
920
+ continue;
921
+ }
922
+ const indentMatch = line.match(/^\s*/);
923
+ const indent = indentMatch ? indentMatch[0] : "";
924
+ const comment = `${indent}// System field: ${description} (auto-filled, do not modify)`;
925
+ lines.splice(i, 0, comment);
926
+ i += 1;
927
+ }
928
+ return lines.join("\n");
334
929
  }
930
+
931
+ // src/commands/db/gen-dbschema/transforms/text/inline-types.ts
932
+ import fs from "fs";
933
+ import { fileURLToPath } from "url";
335
934
  function inlineCustomTypes(source) {
336
- const text = source.replace(/import \{[^}]*\} from ["']\.\/types["'];?\n*/g, "");
935
+ let text = source.replace(/import \{[^}]*\} from ["']\.\/types["'];?\n*/g, "");
337
936
  const templatePath = resolveTemplateTypesPath();
338
937
  if (!templatePath) {
339
- console.warn("[postprocess-drizzle-schema] Template types file not found.");
938
+ console.warn("[text/inline-types] Template types file not found.");
340
939
  return text;
341
940
  }
342
- return inlineFromTemplateContent(text, fs.readFileSync(templatePath, "utf8"));
941
+ const templateContent = fs.readFileSync(templatePath, "utf8");
942
+ return inlineFromTemplate(text, templateContent);
343
943
  }
344
- function inlineFromTemplateContent(text, templateContent) {
944
+ function inlineFromTemplate(source, templateContent) {
345
945
  const typeDefinitions = templateContent.replace(/^import\s+.*;\r?\n*/gm, "").trim();
946
+ let text = source;
346
947
  const needsSql = typeDefinitions.includes("sql`") && !text.includes("from 'drizzle-orm'") && !text.includes('from "drizzle-orm"');
347
948
  const needsCustomType = typeDefinitions.includes("customType<") && !text.includes("customType");
348
949
  if (needsCustomType) {
349
950
  text = ensureImportIdentifier(text, "drizzle-orm/pg-core", "customType");
350
951
  }
351
- if (needsSql && !text.includes("from 'drizzle-orm'") && !text.includes('from "drizzle-orm"')) {
952
+ if (needsSql) {
352
953
  const importMatch = text.match(/^import [\s\S]*?from ["']drizzle-orm\/pg-core["'];?\n/m);
353
954
  if (importMatch) {
354
955
  const insertPoint = text.indexOf(importMatch[0]) + importMatch[0].length;
355
956
  text = text.slice(0, insertPoint) + "import { sql } from 'drizzle-orm';\n" + text.slice(insertPoint);
356
957
  }
357
958
  }
358
- const headerPrefix = `${HEADER_COMMENT}
959
+ const headerPrefix = `${FULL_HEADER}
359
960
  `;
360
961
  let insertionPoint = 0;
361
962
  if (text.startsWith(headerPrefix)) {
@@ -389,7 +990,10 @@ function ensureImportIdentifier(source, packageName, identifier) {
389
990
  }
390
991
  function resolveTemplateTypesPath() {
391
992
  const candidates = [
993
+ // Source code paths (relative to transforms/text/)
994
+ new URL("../../template/types.ts", import.meta.url),
392
995
  new URL("../template/types.ts", import.meta.url),
996
+ // Bundled path (relative to dist/index.js)
393
997
  new URL("./gen-dbschema-template/types.ts", import.meta.url)
394
998
  ];
395
999
  for (const url of candidates) {
@@ -401,131 +1005,9 @@ function resolveTemplateTypesPath() {
401
1005
  return void 0;
402
1006
  }
403
1007
 
404
- // src/commands/db/gen-dbschema/helper/system-fields.ts
405
- function addSystemFieldComments(source) {
406
- const commentMap = {
407
- "_created_at": "Creation time",
408
- "_created_by": "Creator",
409
- "_updated_at": "Update time",
410
- "_updated_by": "Updater"
411
- };
412
- const lines = source.split("\n");
413
- for (let i = 0; i < lines.length; i += 1) {
414
- const line = lines[i];
415
- const entry = Object.entries(commentMap).find(([key]) => line.includes(`"${key}"`));
416
- if (!entry) {
417
- continue;
418
- }
419
- const [, description] = entry;
420
- const previousLine = lines[i - 1]?.trim() ?? "";
421
- if (previousLine.startsWith("//") && previousLine.includes("System field")) {
422
- continue;
423
- }
424
- const indentMatch = line.match(/^\s*/);
425
- const indent = indentMatch ? indentMatch[0] : "";
426
- const comment = `${indent}// System field: ${description} (auto-filled, do not modify)`;
427
- lines.splice(i, 0, comment);
428
- i += 1;
429
- }
430
- return lines.join("\n");
431
- }
432
- function removeConflictingSystemFields(source) {
433
- const systemFieldMap = {
434
- "_created_at": "created_at",
435
- "_created_by": "created_by",
436
- "_updated_at": "updated_at",
437
- "_updated_by": "updated_by"
438
- };
439
- const lines = source.split("\n");
440
- const result = [];
441
- let inTable = false;
442
- let tableStartLine = -1;
443
- const tableBusinessFields = /* @__PURE__ */ new Set();
444
- let bracketDepth = 0;
445
- for (let i = 0; i < lines.length; i += 1) {
446
- const line = lines[i];
447
- if (!inTable && /=\s*(pgTable|pgView|pgMaterializedView)\s*\(/.test(line)) {
448
- inTable = true;
449
- tableStartLine = result.length;
450
- tableBusinessFields.clear();
451
- bracketDepth = 0;
452
- }
453
- if (inTable) {
454
- for (const char of line) {
455
- if (char === "{") bracketDepth++;
456
- if (char === "}") bracketDepth--;
457
- }
458
- for (const businessField of Object.values(systemFieldMap)) {
459
- if (line.includes(`"${businessField}"`) || line.includes(`'${businessField}'`)) {
460
- tableBusinessFields.add(businessField);
461
- }
462
- }
463
- if (bracketDepth === 0 && line.includes(");")) {
464
- inTable = false;
465
- const tableEndLine = result.length;
466
- for (let j = tableStartLine; j <= tableEndLine; j++) {
467
- const tableLine = result[j] || "";
468
- let shouldRemove = false;
469
- for (const [systemField, businessField] of Object.entries(systemFieldMap)) {
470
- if (tableBusinessFields.has(businessField)) {
471
- if (tableLine.includes(`"${systemField}"`) || tableLine.includes(`'${systemField}'`)) {
472
- shouldRemove = true;
473
- if (j > 0 && result[j - 1]?.includes("// System field:")) {
474
- result[j - 1] = null;
475
- }
476
- break;
477
- }
478
- }
479
- }
480
- if (shouldRemove) {
481
- result[j] = null;
482
- }
483
- }
484
- }
485
- }
486
- result.push(line);
487
- }
488
- return result.filter((line) => line !== null).join("\n");
489
- }
490
-
491
- // src/commands/db/gen-dbschema/helper/patch-helper.ts
492
- function patchDrizzleKitDefects(source) {
493
- let fixed = 0;
494
- const text = source.replace(/\.default\('\)/g, () => {
495
- fixed += 1;
496
- return `.default('')`;
497
- });
498
- return { text, fixed };
499
- }
500
-
501
- // src/commands/db/gen-dbschema/helper/timestamp-replacement.ts
502
- function replaceTimestampWithCustomTypes(source) {
503
- let replaced = 0;
504
- const pattern = /timestamp\((['"])(.*?)\1,\s*(\{[^}]*\})\)/g;
505
- const text = source.replace(pattern, (match, quote, fieldName, options) => {
506
- const hasWithTimezone = /withTimezone:\s*true/.test(options);
507
- const hasModeString = /mode:\s*['"]string['"]/.test(options);
508
- if (hasWithTimezone && hasModeString) {
509
- replaced += 1;
510
- return `customTimestamptz(${quote}${fieldName}${quote})`;
511
- }
512
- return match;
513
- });
514
- return { text, replaced };
515
- }
516
- function replaceDefaultNowWithSql(source) {
517
- let replaced = 0;
518
- const pattern = /\.defaultNow\(\)/g;
519
- const text = source.replace(pattern, () => {
520
- replaced += 1;
521
- return ".default(sql`CURRENT_TIMESTAMP`)";
522
- });
523
- return { text, replaced };
524
- }
525
-
526
- // src/commands/db/gen-dbschema/helper/appendTableAliases.ts
1008
+ // src/commands/db/gen-dbschema/transforms/text/table-aliases.ts
527
1009
  var TABLE_ALIAS_MARKER = "// table aliases";
528
- function appendTableAliases(source) {
1010
+ function generateTableAliases(source) {
529
1011
  const markerIndex = source.indexOf(`
530
1012
  ${TABLE_ALIAS_MARKER}`);
531
1013
  const base = markerIndex === -1 ? source : source.slice(0, markerIndex);
@@ -547,62 +1029,100 @@ ${aliasLines}
547
1029
  `;
548
1030
  }
549
1031
 
1032
+ // src/commands/db/gen-dbschema/transforms/text/format.ts
1033
+ function formatSource(source) {
1034
+ let text = source;
1035
+ text = text.replace(/\r\n/g, "\n");
1036
+ text = text.replace(/\n{3,}/g, "\n\n");
1037
+ if (!text.endsWith("\n")) {
1038
+ text += "\n";
1039
+ }
1040
+ return text;
1041
+ }
1042
+
550
1043
  // src/commands/db/gen-dbschema/postprocess.ts
1044
+ function postprocessSchema(rawSource) {
1045
+ const patchResult = patchDefects(rawSource);
1046
+ let source = patchResult.text;
1047
+ const { sourceFile } = parseSource(source);
1048
+ const astStats = applyTransforms(sourceFile, defaultTransforms);
1049
+ formatSourceFile(sourceFile);
1050
+ source = printSourceFile(sourceFile);
1051
+ source = ensureHeader(source);
1052
+ source = addSystemFieldComments(source);
1053
+ source = inlineCustomTypes(source);
1054
+ source = generateTableAliases(source);
1055
+ source = formatSource(source);
1056
+ return {
1057
+ source,
1058
+ astStats,
1059
+ patchedDefects: patchResult.fixed
1060
+ };
1061
+ }
1062
+ function logStats(result, prefix = "[postprocess]") {
1063
+ const { astStats, patchedDefects } = result;
1064
+ if (patchedDefects > 0) {
1065
+ console.info(`${prefix} Patched ${patchedDefects} syntax defects`);
1066
+ }
1067
+ if (astStats.removedPgSchemas > 0) {
1068
+ console.info(`${prefix} Removed ${astStats.removedPgSchemas} pgSchema declarations`);
1069
+ }
1070
+ if (astStats.convertedSchemaCalls > 0) {
1071
+ console.info(`${prefix} Converted ${astStats.convertedSchemaCalls} schema.xxx() calls`);
1072
+ }
1073
+ if (astStats.renamedIdentifiers.length > 0) {
1074
+ console.info(`${prefix} Renamed ${astStats.renamedIdentifiers.length} identifiers:`);
1075
+ for (const { from, to } of astStats.renamedIdentifiers) {
1076
+ console.info(`${prefix} ${from} -> ${to}`);
1077
+ }
1078
+ }
1079
+ if (astStats.replacedUnknown > 0) {
1080
+ console.info(`${prefix} Replaced ${astStats.replacedUnknown} unknown types with custom types`);
1081
+ }
1082
+ if (astStats.fallbackToText > 0) {
1083
+ console.info(`${prefix} Replaced ${astStats.fallbackToText} unknown types with text (fallback)`);
1084
+ }
1085
+ if (astStats.unmatchedUnknown.length > 0) {
1086
+ console.warn(`${prefix} Unmatched unknown types:`);
1087
+ for (const line of astStats.unmatchedUnknown) {
1088
+ console.warn(`${prefix} ${line}`);
1089
+ }
1090
+ }
1091
+ if (astStats.replacedTimestamp > 0) {
1092
+ console.info(`${prefix} Replaced ${astStats.replacedTimestamp} timestamp with customTimestamptz`);
1093
+ }
1094
+ if (astStats.replacedDefaultNow > 0) {
1095
+ console.info(`${prefix} Replaced ${astStats.replacedDefaultNow} .defaultNow() calls`);
1096
+ }
1097
+ if (astStats.removedSystemFields > 0) {
1098
+ console.info(`${prefix} Removed ${astStats.removedSystemFields} conflicting system fields`);
1099
+ }
1100
+ if (astStats.addedImports.length > 0) {
1101
+ console.info(`${prefix} Added imports: ${astStats.addedImports.join(", ")}`);
1102
+ }
1103
+ if (astStats.removedImports.length > 0) {
1104
+ console.info(`${prefix} Removed imports: ${astStats.removedImports.join(", ")}`);
1105
+ }
1106
+ }
1107
+
1108
+ // src/commands/db/gen-dbschema/index.ts
551
1109
  function postprocessDrizzleSchema(targetPath) {
552
1110
  const resolvedPath = path.resolve(targetPath);
553
- if (!fs2.existsSync(resolvedPath)) {
1111
+ if (!fs3.existsSync(resolvedPath)) {
554
1112
  console.warn(`[postprocess-drizzle-schema] File not found: ${resolvedPath}`);
555
1113
  return void 0;
556
1114
  }
557
- let text = fs2.readFileSync(resolvedPath, "utf8");
558
- text = ensureHeaderComment(text);
559
- const patchResult = patchDrizzleKitDefects(text);
560
- text = patchResult.text;
561
- text = removePgSchemaDeclarations(text);
562
- const tableConversion = convertSchemaTableInvocations(text);
563
- text = tableConversion.text;
564
- const renameResult = renamePgTableConstants(text);
565
- text = renameResult.text;
566
- text = updateTableReferenceIdentifiers(text, renameResult.renames);
567
- const replacement = replaceUnknownColumns(text);
568
- text = replacement.text;
569
- const timestampReplacement = replaceTimestampWithCustomTypes(text);
570
- text = timestampReplacement.text;
571
- const defaultNowReplacement = replaceDefaultNowWithSql(text);
572
- text = defaultNowReplacement.text;
573
- text = removeConflictingSystemFields(text);
574
- text = addSystemFieldComments(text);
575
- text = tweakImports(text);
576
- text = inlineCustomTypes(text);
577
- text = appendTableAliases(text);
578
- text = text.replace(/\r?\n/g, "\n");
579
- text = collapseExtraBlankLines(text);
580
- fs2.writeFileSync(resolvedPath, text, "utf8");
581
- if (patchResult.fixed > 0) {
582
- console.info(`[postprocess-drizzle-schema] Patched ${patchResult.fixed} drizzle-kit defects (.default(') -> .default(''))`);
583
- }
584
- if (replacement.replaced > 0) {
585
- console.info(`[postprocess-drizzle-schema] Replaced ${replacement.replaced} unknown columns`);
586
- }
587
- if (replacement.unmatched.length > 0) {
588
- console.warn("[postprocess-drizzle-schema] Unmatched custom types:", replacement.unmatched.length);
589
- replacement.unmatched.forEach((line) => console.warn(` ${line}`));
590
- }
591
- if (tableConversion.converted > 0) {
592
- console.info(`[postprocess-drizzle-schema] Converted ${tableConversion.converted} schema.table invocations to pgTable`);
593
- }
594
- if (timestampReplacement.replaced > 0) {
595
- console.info(`[postprocess-drizzle-schema] Replaced ${timestampReplacement.replaced} timestamp fields with customTimestamptz`);
596
- }
597
- if (defaultNowReplacement.replaced > 0) {
598
- console.info(`[postprocess-drizzle-schema] Replaced ${defaultNowReplacement.replaced} .defaultNow() with .default(sql\`CURRENT_TIMESTAMP\`)`);
599
- }
1115
+ const rawSource = fs3.readFileSync(resolvedPath, "utf8");
1116
+ const result = postprocessSchema(rawSource);
1117
+ fs3.writeFileSync(resolvedPath, result.source, "utf8");
1118
+ logStats(result, "[postprocess-drizzle-schema]");
600
1119
  return {
601
- replacedUnknown: replacement.replaced,
602
- unmatchedUnknown: replacement.unmatched,
603
- patchedDefects: patchResult.fixed,
604
- replacedTimestamps: timestampReplacement.replaced,
605
- replacedDefaultNow: defaultNowReplacement.replaced
1120
+ replacedUnknown: result.astStats.replacedUnknown,
1121
+ fallbackToText: result.astStats.fallbackToText,
1122
+ unmatchedUnknown: result.astStats.unmatchedUnknown,
1123
+ patchedDefects: result.patchedDefects,
1124
+ replacedTimestamps: result.astStats.replacedTimestamp,
1125
+ replacedDefaultNow: result.astStats.replacedDefaultNow
606
1126
  };
607
1127
  }
608
1128
 
@@ -1024,10 +1544,10 @@ export class ${className}Module {}
1024
1544
  }
1025
1545
 
1026
1546
  // src/commands/db/gen-nest-resource/schema-parser.ts
1027
- import { Project, Node } from "ts-morph";
1547
+ import { Project as Project2, Node as Node9 } from "ts-morph";
1028
1548
  var DrizzleSchemaParser = class {
1029
1549
  constructor(projectOptions) {
1030
- this.project = new Project(projectOptions);
1550
+ this.project = new Project2(projectOptions);
1031
1551
  }
1032
1552
  parseSchemaFile(filePath) {
1033
1553
  const sourceFile = this.project.addSourceFileAtPath(filePath);
@@ -1037,7 +1557,7 @@ var DrizzleSchemaParser = class {
1037
1557
  const declarations = statement.getDeclarations();
1038
1558
  for (const declaration of declarations) {
1039
1559
  const initializer = declaration.getInitializer();
1040
- if (initializer && Node.isCallExpression(initializer)) {
1560
+ if (initializer && Node9.isCallExpression(initializer)) {
1041
1561
  const expression = initializer.getExpression();
1042
1562
  if (expression.getText() === "pgTable") {
1043
1563
  const tableInfo = this.parsePgTable(
@@ -1060,13 +1580,13 @@ var DrizzleSchemaParser = class {
1060
1580
  }
1061
1581
  const tableName = args[0].getText().replace(/['"]/g, "");
1062
1582
  const fieldsArg = args[1];
1063
- if (!Node.isObjectLiteralExpression(fieldsArg)) {
1583
+ if (!Node9.isObjectLiteralExpression(fieldsArg)) {
1064
1584
  return null;
1065
1585
  }
1066
1586
  const fields = [];
1067
1587
  const properties = fieldsArg.getProperties();
1068
1588
  for (const prop of properties) {
1069
- if (Node.isPropertyAssignment(prop)) {
1589
+ if (Node9.isPropertyAssignment(prop)) {
1070
1590
  const fieldName = prop.getName();
1071
1591
  const initializer = prop.getInitializer();
1072
1592
  const leadingComments = prop.getLeadingCommentRanges();
@@ -1074,7 +1594,7 @@ var DrizzleSchemaParser = class {
1074
1594
  if (leadingComments.length > 0) {
1075
1595
  comment = leadingComments.map((c) => c.getText()).join("\n").replace(/\/\//g, "").trim();
1076
1596
  }
1077
- if (initializer && Node.isCallExpression(initializer)) {
1597
+ if (initializer && Node9.isCallExpression(initializer)) {
1078
1598
  const fieldInfo = this.parseField(fieldName, initializer, comment);
1079
1599
  fields.push(fieldInfo);
1080
1600
  }
@@ -1106,10 +1626,10 @@ var DrizzleSchemaParser = class {
1106
1626
  parseBaseType(callExpr, fieldInfo) {
1107
1627
  let current = callExpr;
1108
1628
  let baseCall = null;
1109
- while (Node.isCallExpression(current)) {
1629
+ while (Node9.isCallExpression(current)) {
1110
1630
  baseCall = current;
1111
1631
  const expression2 = current.getExpression();
1112
- if (Node.isPropertyAccessExpression(expression2)) {
1632
+ if (Node9.isPropertyAccessExpression(expression2)) {
1113
1633
  current = expression2.getExpression();
1114
1634
  } else {
1115
1635
  break;
@@ -1120,7 +1640,7 @@ var DrizzleSchemaParser = class {
1120
1640
  }
1121
1641
  const expression = baseCall.getExpression();
1122
1642
  let typeName = "";
1123
- if (Node.isPropertyAccessExpression(expression)) {
1643
+ if (Node9.isPropertyAccessExpression(expression)) {
1124
1644
  typeName = expression.getName();
1125
1645
  } else {
1126
1646
  typeName = expression.getText();
@@ -1129,25 +1649,25 @@ var DrizzleSchemaParser = class {
1129
1649
  const args = baseCall.getArguments();
1130
1650
  if (args.length > 0) {
1131
1651
  const firstArg = args[0];
1132
- if (Node.isStringLiteral(firstArg)) {
1652
+ if (Node9.isStringLiteral(firstArg)) {
1133
1653
  fieldInfo.columnName = firstArg.getLiteralText();
1134
- } else if (Node.isObjectLiteralExpression(firstArg)) {
1654
+ } else if (Node9.isObjectLiteralExpression(firstArg)) {
1135
1655
  this.parseTypeConfig(firstArg, fieldInfo);
1136
- } else if (Node.isArrayLiteralExpression(firstArg)) {
1656
+ } else if (Node9.isArrayLiteralExpression(firstArg)) {
1137
1657
  fieldInfo.enumValues = firstArg.getElements().map((el) => el.getText().replace(/['"]/g, ""));
1138
1658
  }
1139
1659
  }
1140
- if (args.length > 1 && Node.isObjectLiteralExpression(args[1])) {
1660
+ if (args.length > 1 && Node9.isObjectLiteralExpression(args[1])) {
1141
1661
  this.parseTypeConfig(args[1], fieldInfo);
1142
1662
  }
1143
1663
  }
1144
1664
  parseTypeConfig(objLiteral, fieldInfo) {
1145
- if (!Node.isObjectLiteralExpression(objLiteral)) {
1665
+ if (!Node9.isObjectLiteralExpression(objLiteral)) {
1146
1666
  return;
1147
1667
  }
1148
1668
  const properties = objLiteral.getProperties();
1149
1669
  for (const prop of properties) {
1150
- if (Node.isPropertyAssignment(prop)) {
1670
+ if (Node9.isPropertyAssignment(prop)) {
1151
1671
  const propName = prop.getName();
1152
1672
  const value = prop.getInitializer()?.getText();
1153
1673
  switch (propName) {
@@ -1179,9 +1699,9 @@ var DrizzleSchemaParser = class {
1179
1699
  }
1180
1700
  parseCallChain(callExpr, fieldInfo) {
1181
1701
  let current = callExpr;
1182
- while (Node.isCallExpression(current)) {
1702
+ while (Node9.isCallExpression(current)) {
1183
1703
  const expression = current.getExpression();
1184
- if (Node.isPropertyAccessExpression(expression)) {
1704
+ if (Node9.isPropertyAccessExpression(expression)) {
1185
1705
  const methodName = expression.getName();
1186
1706
  const args = current.getArguments();
1187
1707
  switch (methodName) {
@@ -1278,7 +1798,7 @@ var require2 = createRequire(import.meta.url);
1278
1798
  async function run(options = {}) {
1279
1799
  let exitCode = 0;
1280
1800
  const envPath2 = path2.resolve(process.cwd(), ".env");
1281
- if (fs3.existsSync(envPath2)) {
1801
+ if (fs4.existsSync(envPath2)) {
1282
1802
  loadEnv({ path: envPath2 });
1283
1803
  console.log("[gen-db-schema] \u2713 Loaded .env file");
1284
1804
  }
@@ -1298,7 +1818,7 @@ async function run(options = {}) {
1298
1818
  path2.resolve(__dirname2, "../../config/drizzle.config.js"),
1299
1819
  path2.resolve(__dirname2, "../../../dist/config/drizzle.config.js")
1300
1820
  ];
1301
- const configPath = configPathCandidates.find((p) => fs3.existsSync(p));
1821
+ const configPath = configPathCandidates.find((p) => fs4.existsSync(p));
1302
1822
  console.log("[gen-db-schema] Using drizzle config from:", configPath ?? "(not found)");
1303
1823
  if (!configPath) {
1304
1824
  console.error("[gen-db-schema] Error: drizzle config not found in CLI package");
@@ -1310,8 +1830,8 @@ async function run(options = {}) {
1310
1830
  let lastDir = null;
1311
1831
  while (currentDir !== lastDir) {
1312
1832
  const pkgJsonPath = path2.join(currentDir, "package.json");
1313
- if (fs3.existsSync(pkgJsonPath)) {
1314
- const pkgJsonRaw = fs3.readFileSync(pkgJsonPath, "utf8");
1833
+ if (fs4.existsSync(pkgJsonPath)) {
1834
+ const pkgJsonRaw = fs4.readFileSync(pkgJsonPath, "utf8");
1315
1835
  const pkgJson = JSON.parse(pkgJsonRaw);
1316
1836
  if (pkgJson.name === "drizzle-kit") {
1317
1837
  const binField = pkgJson.bin;
@@ -1346,7 +1866,7 @@ async function run(options = {}) {
1346
1866
  throw new Error(`drizzle-kit introspect failed with status ${result.status}`);
1347
1867
  }
1348
1868
  const generatedSchema = path2.join(OUT_DIR, "schema.ts");
1349
- if (!fs3.existsSync(generatedSchema)) {
1869
+ if (!fs4.existsSync(generatedSchema)) {
1350
1870
  console.error("[gen-db-schema] schema.ts not generated");
1351
1871
  throw new Error("drizzle-kit introspect failed to generate schema.ts");
1352
1872
  }
@@ -1355,8 +1875,8 @@ async function run(options = {}) {
1355
1875
  console.warn("[gen-db-schema] Unmatched custom types detected:", stats.unmatchedUnknown);
1356
1876
  }
1357
1877
  console.log("[gen-db-schema] \u2713 Postprocessed schema");
1358
- fs3.mkdirSync(path2.dirname(SCHEMA_FILE), { recursive: true });
1359
- fs3.copyFileSync(generatedSchema, SCHEMA_FILE);
1878
+ fs4.mkdirSync(path2.dirname(SCHEMA_FILE), { recursive: true });
1879
+ fs4.copyFileSync(generatedSchema, SCHEMA_FILE);
1360
1880
  console.log(`[gen-db-schema] \u2713 Copied to ${outputPath}`);
1361
1881
  try {
1362
1882
  if (options.enableNestModuleGenerate) {
@@ -1377,8 +1897,8 @@ async function run(options = {}) {
1377
1897
  console.error("[gen-db-schema] Failed:", err instanceof Error ? err.message : String(err));
1378
1898
  exitCode = 1;
1379
1899
  } finally {
1380
- if (fs3.existsSync(OUT_DIR)) {
1381
- fs3.rmSync(OUT_DIR, { recursive: true, force: true });
1900
+ if (fs4.existsSync(OUT_DIR)) {
1901
+ fs4.rmSync(OUT_DIR, { recursive: true, force: true });
1382
1902
  }
1383
1903
  process.exit(exitCode);
1384
1904
  }
@@ -1397,7 +1917,7 @@ var genDbSchemaCommand = {
1397
1917
 
1398
1918
  // src/commands/sync/run.handler.ts
1399
1919
  import path4 from "path";
1400
- import fs5 from "fs";
1920
+ import fs6 from "fs";
1401
1921
  import { fileURLToPath as fileURLToPath3 } from "url";
1402
1922
 
1403
1923
  // src/config/sync.ts
@@ -1461,14 +1981,14 @@ function genSyncConfig(perms = {}) {
1461
1981
  }
1462
1982
 
1463
1983
  // src/utils/file-ops.ts
1464
- import fs4 from "fs";
1984
+ import fs5 from "fs";
1465
1985
  import path3 from "path";
1466
1986
  function removeLineFromFile(filePath, pattern) {
1467
- if (!fs4.existsSync(filePath)) {
1987
+ if (!fs5.existsSync(filePath)) {
1468
1988
  console.log(`[fullstack-cli] \u25CB ${path3.basename(filePath)} (not found)`);
1469
1989
  return false;
1470
1990
  }
1471
- const content = fs4.readFileSync(filePath, "utf-8");
1991
+ const content = fs5.readFileSync(filePath, "utf-8");
1472
1992
  const lines = content.split("\n");
1473
1993
  const trimmedPattern = pattern.trim();
1474
1994
  const filteredLines = lines.filter((line) => line.trim() !== trimmedPattern);
@@ -1476,7 +1996,7 @@ function removeLineFromFile(filePath, pattern) {
1476
1996
  console.log(`[fullstack-cli] \u25CB ${path3.basename(filePath)} (pattern not found: ${pattern})`);
1477
1997
  return false;
1478
1998
  }
1479
- fs4.writeFileSync(filePath, filteredLines.join("\n"));
1999
+ fs5.writeFileSync(filePath, filteredLines.join("\n"));
1480
2000
  console.log(`[fullstack-cli] \u2713 ${path3.basename(filePath)} (removed: ${pattern})`);
1481
2001
  return true;
1482
2002
  }
@@ -1492,7 +2012,7 @@ async function run2(options) {
1492
2012
  process.exit(0);
1493
2013
  }
1494
2014
  const userPackageJson = path4.join(userProjectRoot, "package.json");
1495
- if (!fs5.existsSync(userPackageJson)) {
2015
+ if (!fs6.existsSync(userPackageJson)) {
1496
2016
  console.log("[fullstack-cli] Skip syncing (not a valid npm project)");
1497
2017
  process.exit(0);
1498
2018
  }
@@ -1538,7 +2058,7 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
1538
2058
  }
1539
2059
  const srcPath = path4.join(pluginRoot, rule.from);
1540
2060
  const destPath = path4.join(userProjectRoot, rule.to);
1541
- if (!fs5.existsSync(srcPath)) {
2061
+ if (!fs6.existsSync(srcPath)) {
1542
2062
  console.warn(`[fullstack-cli] Source not found: ${rule.from}`);
1543
2063
  return;
1544
2064
  }
@@ -1556,31 +2076,31 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
1556
2076
  }
1557
2077
  function syncFile(src, dest, overwrite = true) {
1558
2078
  const destDir = path4.dirname(dest);
1559
- if (!fs5.existsSync(destDir)) {
1560
- fs5.mkdirSync(destDir, { recursive: true });
2079
+ if (!fs6.existsSync(destDir)) {
2080
+ fs6.mkdirSync(destDir, { recursive: true });
1561
2081
  }
1562
- if (fs5.existsSync(dest) && !overwrite) {
2082
+ if (fs6.existsSync(dest) && !overwrite) {
1563
2083
  console.log(`[fullstack-cli] \u25CB ${path4.basename(dest)} (skipped, already exists)`);
1564
2084
  return;
1565
2085
  }
1566
- fs5.copyFileSync(src, dest);
2086
+ fs6.copyFileSync(src, dest);
1567
2087
  console.log(`[fullstack-cli] \u2713 ${path4.basename(dest)}`);
1568
2088
  }
1569
2089
  function syncDirectory(src, dest, overwrite = true) {
1570
- if (!fs5.existsSync(dest)) {
1571
- fs5.mkdirSync(dest, { recursive: true });
2090
+ if (!fs6.existsSync(dest)) {
2091
+ fs6.mkdirSync(dest, { recursive: true });
1572
2092
  }
1573
- const files = fs5.readdirSync(src);
2093
+ const files = fs6.readdirSync(src);
1574
2094
  let count = 0;
1575
2095
  files.forEach((file) => {
1576
2096
  const srcFile = path4.join(src, file);
1577
2097
  const destFile = path4.join(dest, file);
1578
- const stats = fs5.statSync(srcFile);
2098
+ const stats = fs6.statSync(srcFile);
1579
2099
  if (stats.isDirectory()) {
1580
2100
  syncDirectory(srcFile, destFile, overwrite);
1581
2101
  } else {
1582
- if (overwrite || !fs5.existsSync(destFile)) {
1583
- fs5.copyFileSync(srcFile, destFile);
2102
+ if (overwrite || !fs6.existsSync(destFile)) {
2103
+ fs6.copyFileSync(srcFile, destFile);
1584
2104
  console.log(`[fullstack-cli] \u2713 ${path4.relative(dest, destFile)}`);
1585
2105
  count++;
1586
2106
  }
@@ -1591,28 +2111,28 @@ function syncDirectory(src, dest, overwrite = true) {
1591
2111
  }
1592
2112
  }
1593
2113
  function appendToFile(src, dest) {
1594
- const content = fs5.readFileSync(src, "utf-8");
2114
+ const content = fs6.readFileSync(src, "utf-8");
1595
2115
  let existingContent = "";
1596
- if (fs5.existsSync(dest)) {
1597
- existingContent = fs5.readFileSync(dest, "utf-8");
2116
+ if (fs6.existsSync(dest)) {
2117
+ existingContent = fs6.readFileSync(dest, "utf-8");
1598
2118
  }
1599
2119
  if (existingContent.includes(content.trim())) {
1600
2120
  console.log(`[fullstack-cli] \u25CB ${path4.basename(dest)} (already contains content)`);
1601
2121
  return;
1602
2122
  }
1603
- fs5.appendFileSync(dest, content);
2123
+ fs6.appendFileSync(dest, content);
1604
2124
  console.log(`[fullstack-cli] \u2713 ${path4.basename(dest)} (appended)`);
1605
2125
  }
1606
2126
  function setPermissions(permissions, projectRoot) {
1607
2127
  for (const [pattern, mode] of Object.entries(permissions)) {
1608
2128
  if (pattern === "**/*.sh") {
1609
2129
  const scriptsDir = path4.join(projectRoot, "scripts");
1610
- if (fs5.existsSync(scriptsDir)) {
1611
- const files = fs5.readdirSync(scriptsDir);
2130
+ if (fs6.existsSync(scriptsDir)) {
2131
+ const files = fs6.readdirSync(scriptsDir);
1612
2132
  files.forEach((file) => {
1613
2133
  if (file.endsWith(".sh")) {
1614
2134
  const filePath = path4.join(scriptsDir, file);
1615
- fs5.chmodSync(filePath, mode);
2135
+ fs6.chmodSync(filePath, mode);
1616
2136
  }
1617
2137
  });
1618
2138
  }
@@ -1620,16 +2140,16 @@ function setPermissions(permissions, projectRoot) {
1620
2140
  }
1621
2141
  }
1622
2142
  function deleteFile(filePath) {
1623
- if (fs5.existsSync(filePath)) {
1624
- fs5.unlinkSync(filePath);
2143
+ if (fs6.existsSync(filePath)) {
2144
+ fs6.unlinkSync(filePath);
1625
2145
  console.log(`[fullstack-cli] \u2713 ${path4.basename(filePath)} (deleted)`);
1626
2146
  } else {
1627
2147
  console.log(`[fullstack-cli] \u25CB ${path4.basename(filePath)} (not found)`);
1628
2148
  }
1629
2149
  }
1630
2150
  function deleteDirectory(dirPath) {
1631
- if (fs5.existsSync(dirPath)) {
1632
- fs5.rmSync(dirPath, { recursive: true });
2151
+ if (fs6.existsSync(dirPath)) {
2152
+ fs6.rmSync(dirPath, { recursive: true });
1633
2153
  console.log(`[fullstack-cli] \u2713 ${path4.basename(dirPath)} (deleted)`);
1634
2154
  } else {
1635
2155
  console.log(`[fullstack-cli] \u25CB ${path4.basename(dirPath)} (not found)`);
@@ -1648,7 +2168,7 @@ var syncCommand = {
1648
2168
  };
1649
2169
 
1650
2170
  // src/commands/action-plugin/utils.ts
1651
- import fs6 from "fs";
2171
+ import fs7 from "fs";
1652
2172
  import path5 from "path";
1653
2173
  import { spawnSync as spawnSync2, execSync } from "child_process";
1654
2174
  function parsePluginName(input) {
@@ -1674,11 +2194,11 @@ function getPluginPath(pluginName) {
1674
2194
  }
1675
2195
  function readPackageJson() {
1676
2196
  const pkgPath = getPackageJsonPath();
1677
- if (!fs6.existsSync(pkgPath)) {
2197
+ if (!fs7.existsSync(pkgPath)) {
1678
2198
  throw new Error("package.json not found in current directory");
1679
2199
  }
1680
2200
  try {
1681
- const content = fs6.readFileSync(pkgPath, "utf-8");
2201
+ const content = fs7.readFileSync(pkgPath, "utf-8");
1682
2202
  return JSON.parse(content);
1683
2203
  } catch {
1684
2204
  throw new Error("Failed to parse package.json");
@@ -1686,7 +2206,7 @@ function readPackageJson() {
1686
2206
  }
1687
2207
  function writePackageJson(pkg2) {
1688
2208
  const pkgPath = getPackageJsonPath();
1689
- fs6.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
2209
+ fs7.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
1690
2210
  }
1691
2211
  function readActionPlugins() {
1692
2212
  const pkg2 = readPackageJson();
@@ -1720,11 +2240,11 @@ function npmInstall(tgzPath) {
1720
2240
  }
1721
2241
  function getPackageVersion(pluginName) {
1722
2242
  const pkgJsonPath = path5.join(getPluginPath(pluginName), "package.json");
1723
- if (!fs6.existsSync(pkgJsonPath)) {
2243
+ if (!fs7.existsSync(pkgJsonPath)) {
1724
2244
  return null;
1725
2245
  }
1726
2246
  try {
1727
- const content = fs6.readFileSync(pkgJsonPath, "utf-8");
2247
+ const content = fs7.readFileSync(pkgJsonPath, "utf-8");
1728
2248
  const pkg2 = JSON.parse(content);
1729
2249
  return pkg2.version || null;
1730
2250
  } catch {
@@ -1733,11 +2253,11 @@ function getPackageVersion(pluginName) {
1733
2253
  }
1734
2254
  function readPluginPackageJson(pluginPath) {
1735
2255
  const pkgJsonPath = path5.join(pluginPath, "package.json");
1736
- if (!fs6.existsSync(pkgJsonPath)) {
2256
+ if (!fs7.existsSync(pkgJsonPath)) {
1737
2257
  return null;
1738
2258
  }
1739
2259
  try {
1740
- const content = fs6.readFileSync(pkgJsonPath, "utf-8");
2260
+ const content = fs7.readFileSync(pkgJsonPath, "utf-8");
1741
2261
  return JSON.parse(content);
1742
2262
  } catch {
1743
2263
  return null;
@@ -1747,34 +2267,34 @@ function extractTgzToNodeModules(tgzPath, pluginName) {
1747
2267
  const nodeModulesPath = path5.join(getProjectRoot(), "node_modules");
1748
2268
  const targetDir = path5.join(nodeModulesPath, pluginName);
1749
2269
  const scopeDir = path5.dirname(targetDir);
1750
- if (!fs6.existsSync(scopeDir)) {
1751
- fs6.mkdirSync(scopeDir, { recursive: true });
2270
+ if (!fs7.existsSync(scopeDir)) {
2271
+ fs7.mkdirSync(scopeDir, { recursive: true });
1752
2272
  }
1753
- if (fs6.existsSync(targetDir)) {
1754
- fs6.rmSync(targetDir, { recursive: true });
2273
+ if (fs7.existsSync(targetDir)) {
2274
+ fs7.rmSync(targetDir, { recursive: true });
1755
2275
  }
1756
2276
  const tempDir = path5.join(nodeModulesPath, ".cache", "fullstack-cli", "extract-temp");
1757
- if (fs6.existsSync(tempDir)) {
1758
- fs6.rmSync(tempDir, { recursive: true });
2277
+ if (fs7.existsSync(tempDir)) {
2278
+ fs7.rmSync(tempDir, { recursive: true });
1759
2279
  }
1760
- fs6.mkdirSync(tempDir, { recursive: true });
2280
+ fs7.mkdirSync(tempDir, { recursive: true });
1761
2281
  try {
1762
2282
  execSync(`tar -xzf "${tgzPath}" -C "${tempDir}"`, { stdio: "pipe" });
1763
2283
  const extractedDir = path5.join(tempDir, "package");
1764
- if (fs6.existsSync(extractedDir)) {
1765
- fs6.renameSync(extractedDir, targetDir);
2284
+ if (fs7.existsSync(extractedDir)) {
2285
+ fs7.renameSync(extractedDir, targetDir);
1766
2286
  } else {
1767
- const files = fs6.readdirSync(tempDir);
2287
+ const files = fs7.readdirSync(tempDir);
1768
2288
  if (files.length === 1) {
1769
- fs6.renameSync(path5.join(tempDir, files[0]), targetDir);
2289
+ fs7.renameSync(path5.join(tempDir, files[0]), targetDir);
1770
2290
  } else {
1771
2291
  throw new Error("Unexpected tgz structure");
1772
2292
  }
1773
2293
  }
1774
2294
  return targetDir;
1775
2295
  } finally {
1776
- if (fs6.existsSync(tempDir)) {
1777
- fs6.rmSync(tempDir, { recursive: true });
2296
+ if (fs7.existsSync(tempDir)) {
2297
+ fs7.rmSync(tempDir, { recursive: true });
1778
2298
  }
1779
2299
  }
1780
2300
  }
@@ -1786,7 +2306,7 @@ function checkMissingPeerDeps(peerDeps) {
1786
2306
  const nodeModulesPath = path5.join(getProjectRoot(), "node_modules");
1787
2307
  for (const [depName, _version] of Object.entries(peerDeps)) {
1788
2308
  const depPath = path5.join(nodeModulesPath, depName);
1789
- if (!fs6.existsSync(depPath)) {
2309
+ if (!fs7.existsSync(depPath)) {
1790
2310
  missing.push(depName);
1791
2311
  }
1792
2312
  }
@@ -1810,15 +2330,15 @@ function installMissingDeps(deps) {
1810
2330
  }
1811
2331
  function removePluginDirectory(pluginName) {
1812
2332
  const pluginPath = getPluginPath(pluginName);
1813
- if (fs6.existsSync(pluginPath)) {
1814
- fs6.rmSync(pluginPath, { recursive: true });
2333
+ if (fs7.existsSync(pluginPath)) {
2334
+ fs7.rmSync(pluginPath, { recursive: true });
1815
2335
  console.log(`[action-plugin] Removed ${pluginName}`);
1816
2336
  }
1817
2337
  }
1818
2338
 
1819
2339
  // src/commands/action-plugin/api-client.ts
1820
2340
  import { HttpClient as HttpClient2 } from "@lark-apaas/http-client";
1821
- import fs7 from "fs";
2341
+ import fs8 from "fs";
1822
2342
  import path6 from "path";
1823
2343
 
1824
2344
  // src/utils/http-client.ts
@@ -1832,10 +2352,13 @@ function getHttpClient() {
1832
2352
  enabled: true
1833
2353
  }
1834
2354
  });
1835
- clientInstance.interceptors.request.use((req) => {
1836
- req.headers["x-tt-env"] = "boe_miaoda_plugin";
1837
- return req;
1838
- });
2355
+ const canaryEnv = process.env.FORCE_FRAMEWORK_CLI_CANARY_ENV;
2356
+ if (canaryEnv) {
2357
+ clientInstance.interceptors.request.use((req) => {
2358
+ req.headers["x-tt-env"] = canaryEnv;
2359
+ return req;
2360
+ });
2361
+ }
1839
2362
  }
1840
2363
  return clientInstance;
1841
2364
  }
@@ -1908,8 +2431,8 @@ function getPluginCacheDir() {
1908
2431
  }
1909
2432
  function ensureCacheDir() {
1910
2433
  const cacheDir = getPluginCacheDir();
1911
- if (!fs7.existsSync(cacheDir)) {
1912
- fs7.mkdirSync(cacheDir, { recursive: true });
2434
+ if (!fs8.existsSync(cacheDir)) {
2435
+ fs8.mkdirSync(cacheDir, { recursive: true });
1913
2436
  }
1914
2437
  }
1915
2438
  function getTempFilePath(pluginKey, version) {
@@ -1932,7 +2455,7 @@ async function downloadPlugin(pluginKey, requestedVersion) {
1932
2455
  tgzBuffer = await downloadFromPublic(pluginInfo.downloadURL);
1933
2456
  }
1934
2457
  const tgzPath = getTempFilePath(pluginKey, pluginInfo.version);
1935
- fs7.writeFileSync(tgzPath, tgzBuffer);
2458
+ fs8.writeFileSync(tgzPath, tgzBuffer);
1936
2459
  console.log(`[action-plugin] Downloaded to ${tgzPath} (${(tgzBuffer.length / 1024).toFixed(2)} KB)`);
1937
2460
  return {
1938
2461
  tgzPath,
@@ -1942,8 +2465,8 @@ async function downloadPlugin(pluginKey, requestedVersion) {
1942
2465
  }
1943
2466
  function cleanupTempFile(tgzPath) {
1944
2467
  try {
1945
- if (fs7.existsSync(tgzPath)) {
1946
- fs7.unlinkSync(tgzPath);
2468
+ if (fs8.existsSync(tgzPath)) {
2469
+ fs8.unlinkSync(tgzPath);
1947
2470
  }
1948
2471
  } catch {
1949
2472
  }
@@ -2279,7 +2802,7 @@ var actionPluginCommandGroup = {
2279
2802
  };
2280
2803
 
2281
2804
  // src/commands/capability/utils.ts
2282
- import fs8 from "fs";
2805
+ import fs9 from "fs";
2283
2806
  import path7 from "path";
2284
2807
  var CAPABILITIES_DIR = "server/capabilities";
2285
2808
  function getProjectRoot2() {
@@ -2295,23 +2818,23 @@ function getPluginManifestPath(pluginKey) {
2295
2818
  return path7.join(getProjectRoot2(), "node_modules", pluginKey, "manifest.json");
2296
2819
  }
2297
2820
  function capabilitiesDirExists() {
2298
- return fs8.existsSync(getCapabilitiesDir());
2821
+ return fs9.existsSync(getCapabilitiesDir());
2299
2822
  }
2300
2823
  function listCapabilityIds() {
2301
2824
  const dir = getCapabilitiesDir();
2302
- if (!fs8.existsSync(dir)) {
2825
+ if (!fs9.existsSync(dir)) {
2303
2826
  return [];
2304
2827
  }
2305
- const files = fs8.readdirSync(dir);
2306
- return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
2828
+ const files = fs9.readdirSync(dir);
2829
+ return files.filter((f) => f.endsWith(".json") && f !== "capabilities.json").map((f) => f.replace(/\.json$/, ""));
2307
2830
  }
2308
2831
  function readCapability(id) {
2309
2832
  const filePath = getCapabilityPath(id);
2310
- if (!fs8.existsSync(filePath)) {
2833
+ if (!fs9.existsSync(filePath)) {
2311
2834
  throw new Error(`Capability not found: ${id}`);
2312
2835
  }
2313
2836
  try {
2314
- const content = fs8.readFileSync(filePath, "utf-8");
2837
+ const content = fs9.readFileSync(filePath, "utf-8");
2315
2838
  return JSON.parse(content);
2316
2839
  } catch (error) {
2317
2840
  if (error instanceof SyntaxError) {
@@ -2322,15 +2845,27 @@ function readCapability(id) {
2322
2845
  }
2323
2846
  function readAllCapabilities() {
2324
2847
  const ids = listCapabilityIds();
2325
- return ids.map((id) => readCapability(id));
2848
+ const capabilities = [];
2849
+ for (const id of ids) {
2850
+ try {
2851
+ const capability = readCapability(id);
2852
+ if (!capability.pluginKey) {
2853
+ continue;
2854
+ }
2855
+ capabilities.push(capability);
2856
+ } catch {
2857
+ continue;
2858
+ }
2859
+ }
2860
+ return capabilities;
2326
2861
  }
2327
2862
  function readPluginManifest(pluginKey) {
2328
2863
  const manifestPath = getPluginManifestPath(pluginKey);
2329
- if (!fs8.existsSync(manifestPath)) {
2864
+ if (!fs9.existsSync(manifestPath)) {
2330
2865
  throw new Error(`Plugin not installed: ${pluginKey} (manifest.json not found)`);
2331
2866
  }
2332
2867
  try {
2333
- const content = fs8.readFileSync(manifestPath, "utf-8");
2868
+ const content = fs9.readFileSync(manifestPath, "utf-8");
2334
2869
  return JSON.parse(content);
2335
2870
  } catch (error) {
2336
2871
  if (error instanceof SyntaxError) {
@@ -2504,7 +3039,7 @@ var capabilityCommandGroup = {
2504
3039
  };
2505
3040
 
2506
3041
  // src/commands/migration/version-manager.ts
2507
- import fs9 from "fs";
3042
+ import fs10 from "fs";
2508
3043
  import path8 from "path";
2509
3044
  var PACKAGE_JSON = "package.json";
2510
3045
  var VERSION_FIELD = "migrationVersion";
@@ -2513,25 +3048,25 @@ function getPackageJsonPath2() {
2513
3048
  }
2514
3049
  function getCurrentVersion() {
2515
3050
  const pkgPath = getPackageJsonPath2();
2516
- if (!fs9.existsSync(pkgPath)) {
3051
+ if (!fs10.existsSync(pkgPath)) {
2517
3052
  throw new Error("package.json not found");
2518
3053
  }
2519
- const pkg2 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
3054
+ const pkg2 = JSON.parse(fs10.readFileSync(pkgPath, "utf-8"));
2520
3055
  return pkg2[VERSION_FIELD] ?? 0;
2521
3056
  }
2522
3057
  function setCurrentVersion(version) {
2523
3058
  const pkgPath = getPackageJsonPath2();
2524
- const pkg2 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
3059
+ const pkg2 = JSON.parse(fs10.readFileSync(pkgPath, "utf-8"));
2525
3060
  pkg2[VERSION_FIELD] = version;
2526
- fs9.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
3061
+ fs10.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
2527
3062
  }
2528
3063
 
2529
3064
  // src/commands/migration/versions/v001_capability/json-migrator/detector.ts
2530
- import fs11 from "fs";
3065
+ import fs12 from "fs";
2531
3066
  import path10 from "path";
2532
3067
 
2533
3068
  // src/commands/migration/versions/v001_capability/utils.ts
2534
- import fs10 from "fs";
3069
+ import fs11 from "fs";
2535
3070
  import path9 from "path";
2536
3071
  var CAPABILITIES_DIR2 = "server/capabilities";
2537
3072
  function getProjectRoot3() {
@@ -2548,19 +3083,30 @@ function getPluginManifestPath2(pluginKey) {
2548
3083
  function detectJsonMigration() {
2549
3084
  const capabilitiesDir = getCapabilitiesDir2();
2550
3085
  const oldFilePath = path10.join(capabilitiesDir, "capabilities.json");
2551
- if (!fs11.existsSync(oldFilePath)) {
3086
+ if (!fs12.existsSync(oldFilePath)) {
2552
3087
  return {
2553
3088
  needsMigration: false,
2554
3089
  reason: "capabilities.json not found"
2555
3090
  };
2556
3091
  }
2557
3092
  try {
2558
- const content = fs11.readFileSync(oldFilePath, "utf-8");
3093
+ const content = fs12.readFileSync(oldFilePath, "utf-8");
2559
3094
  const parsed = JSON.parse(content);
2560
- const capabilities = Array.isArray(parsed) ? parsed : [];
3095
+ if (!Array.isArray(parsed)) {
3096
+ return {
3097
+ needsMigration: false,
3098
+ reason: "capabilities.json is not a valid array"
3099
+ };
3100
+ }
3101
+ if (parsed.length === 0) {
3102
+ return {
3103
+ needsMigration: false,
3104
+ reason: "capabilities.json is an empty array"
3105
+ };
3106
+ }
2561
3107
  return {
2562
3108
  needsMigration: true,
2563
- oldCapabilities: capabilities,
3109
+ oldCapabilities: parsed,
2564
3110
  oldFilePath
2565
3111
  };
2566
3112
  } catch (error) {
@@ -2595,7 +3141,7 @@ async function check(options) {
2595
3141
  }
2596
3142
 
2597
3143
  // src/commands/migration/versions/v001_capability/json-migrator/index.ts
2598
- import fs12 from "fs";
3144
+ import fs13 from "fs";
2599
3145
  import path11 from "path";
2600
3146
 
2601
3147
  // src/commands/migration/versions/v001_capability/mapping.ts
@@ -2826,10 +3372,10 @@ function transformCapabilities(oldCapabilities) {
2826
3372
  // src/commands/migration/versions/v001_capability/json-migrator/index.ts
2827
3373
  function loadExistingCapabilities() {
2828
3374
  const capabilitiesDir = getCapabilitiesDir2();
2829
- if (!fs12.existsSync(capabilitiesDir)) {
3375
+ if (!fs13.existsSync(capabilitiesDir)) {
2830
3376
  return [];
2831
3377
  }
2832
- const files = fs12.readdirSync(capabilitiesDir);
3378
+ const files = fs13.readdirSync(capabilitiesDir);
2833
3379
  const capabilities = [];
2834
3380
  for (const file of files) {
2835
3381
  if (file === "capabilities.json" || !file.endsWith(".json")) {
@@ -2837,7 +3383,7 @@ function loadExistingCapabilities() {
2837
3383
  }
2838
3384
  try {
2839
3385
  const filePath = path11.join(capabilitiesDir, file);
2840
- const content = fs12.readFileSync(filePath, "utf-8");
3386
+ const content = fs13.readFileSync(filePath, "utf-8");
2841
3387
  const capability = JSON.parse(content);
2842
3388
  if (capability.id && capability.pluginKey) {
2843
3389
  capabilities.push(capability);
@@ -2897,7 +3443,7 @@ async function migrateJsonFiles(options) {
2897
3443
  for (const cap of newCapabilities) {
2898
3444
  const filePath = path11.join(capabilitiesDir, `${cap.id}.json`);
2899
3445
  const content = JSON.stringify(cap, null, 2);
2900
- fs12.writeFileSync(filePath, content, "utf-8");
3446
+ fs13.writeFileSync(filePath, content, "utf-8");
2901
3447
  console.log(` \u2713 Created: ${cap.id}.json`);
2902
3448
  }
2903
3449
  return {
@@ -2909,11 +3455,11 @@ async function migrateJsonFiles(options) {
2909
3455
  }
2910
3456
 
2911
3457
  // src/commands/migration/versions/v001_capability/plugin-installer/detector.ts
2912
- import fs13 from "fs";
3458
+ import fs14 from "fs";
2913
3459
  function isPluginInstalled2(pluginKey) {
2914
3460
  const actionPlugins = readActionPlugins();
2915
3461
  const manifestPath = getPluginManifestPath2(pluginKey);
2916
- return fs13.existsSync(manifestPath) && !!actionPlugins[pluginKey];
3462
+ return fs14.existsSync(manifestPath) && !!actionPlugins[pluginKey];
2917
3463
  }
2918
3464
  function detectPluginsToInstall(capabilities) {
2919
3465
  const pluginKeys = /* @__PURE__ */ new Set();
@@ -2990,10 +3536,10 @@ async function installPlugins(capabilities, options) {
2990
3536
 
2991
3537
  // src/commands/migration/versions/v001_capability/code-migrator/index.ts
2992
3538
  import path13 from "path";
2993
- import { Project as Project2 } from "ts-morph";
3539
+ import { Project as Project3 } from "ts-morph";
2994
3540
 
2995
3541
  // src/commands/migration/versions/v001_capability/code-migrator/scanner.ts
2996
- import fs14 from "fs";
3542
+ import fs15 from "fs";
2997
3543
  import path12 from "path";
2998
3544
  var EXCLUDED_DIRS = [
2999
3545
  "node_modules",
@@ -3009,7 +3555,7 @@ var EXCLUDED_PATTERNS = [
3009
3555
  /\.d\.ts$/
3010
3556
  ];
3011
3557
  function scanDirectory(dir, files = []) {
3012
- const entries = fs14.readdirSync(dir, { withFileTypes: true });
3558
+ const entries = fs15.readdirSync(dir, { withFileTypes: true });
3013
3559
  for (const entry of entries) {
3014
3560
  const fullPath = path12.join(dir, entry.name);
3015
3561
  if (entry.isDirectory()) {
@@ -3028,13 +3574,13 @@ function scanDirectory(dir, files = []) {
3028
3574
  }
3029
3575
  function scanServerFiles() {
3030
3576
  const serverDir = path12.join(getProjectRoot3(), "server");
3031
- if (!fs14.existsSync(serverDir)) {
3577
+ if (!fs15.existsSync(serverDir)) {
3032
3578
  return [];
3033
3579
  }
3034
3580
  return scanDirectory(serverDir);
3035
3581
  }
3036
3582
  function hasCapabilityImport(filePath) {
3037
- const content = fs14.readFileSync(filePath, "utf-8");
3583
+ const content = fs15.readFileSync(filePath, "utf-8");
3038
3584
  return /import\s+.*from\s+['"][^'"]*capabilities[^'"]*['"]/.test(content);
3039
3585
  }
3040
3586
  function scanFilesToMigrate() {
@@ -3098,17 +3644,17 @@ function analyzeImports(sourceFile) {
3098
3644
  }
3099
3645
 
3100
3646
  // src/commands/migration/versions/v001_capability/code-migrator/analyzers/call-site-analyzer.ts
3101
- import { SyntaxKind } from "ts-morph";
3647
+ import { SyntaxKind as SyntaxKind4 } from "ts-morph";
3102
3648
  function analyzeCallSites(sourceFile, imports) {
3103
3649
  const callSites = [];
3104
3650
  const importMap = /* @__PURE__ */ new Map();
3105
3651
  for (const imp of imports) {
3106
3652
  importMap.set(imp.importName, imp.capabilityId);
3107
3653
  }
3108
- const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
3654
+ const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind4.CallExpression);
3109
3655
  for (const callExpr of callExpressions) {
3110
3656
  const expression = callExpr.getExpression();
3111
- if (expression.getKind() === SyntaxKind.Identifier) {
3657
+ if (expression.getKind() === SyntaxKind4.Identifier) {
3112
3658
  const functionName = expression.getText();
3113
3659
  const capabilityId = importMap.get(functionName);
3114
3660
  if (capabilityId) {
@@ -3121,11 +3667,11 @@ function analyzeCallSites(sourceFile, imports) {
3121
3667
  text: callExpr.getText()
3122
3668
  });
3123
3669
  }
3124
- } else if (expression.getKind() === SyntaxKind.PropertyAccessExpression) {
3125
- const propAccess = expression.asKind(SyntaxKind.PropertyAccessExpression);
3670
+ } else if (expression.getKind() === SyntaxKind4.PropertyAccessExpression) {
3671
+ const propAccess = expression.asKind(SyntaxKind4.PropertyAccessExpression);
3126
3672
  if (propAccess) {
3127
3673
  const objectExpr = propAccess.getExpression();
3128
- if (objectExpr.getKind() === SyntaxKind.Identifier) {
3674
+ if (objectExpr.getKind() === SyntaxKind4.Identifier) {
3129
3675
  const objectName = objectExpr.getText();
3130
3676
  const capabilityId = importMap.get(objectName);
3131
3677
  if (capabilityId) {
@@ -3161,13 +3707,15 @@ function analyzeClass(sourceFile) {
3161
3707
  if (!name) continue;
3162
3708
  const isInjectable = hasDecorator(classDecl, "Injectable");
3163
3709
  const isController = hasDecorator(classDecl, "Controller");
3164
- if (classInfo && classInfo.isInjectable && !isInjectable && !isController) {
3710
+ const isAutomation = hasDecorator(classDecl, "Automation");
3711
+ if (classInfo && classInfo.isInjectable && !isInjectable && !isController && !isAutomation) {
3165
3712
  continue;
3166
3713
  }
3167
3714
  const info = {
3168
3715
  name,
3169
3716
  isInjectable,
3170
3717
  isController,
3718
+ isAutomation,
3171
3719
  constructorParamCount: 0
3172
3720
  };
3173
3721
  const classBody = classDecl.getChildSyntaxListOrThrow();
@@ -3186,7 +3734,7 @@ function analyzeClass(sourceFile) {
3186
3734
  info.constructorParamsEnd = ctor.getStart();
3187
3735
  }
3188
3736
  }
3189
- if (isInjectable || isController || !classInfo) {
3737
+ if (isInjectable || isController || isAutomation || !classInfo) {
3190
3738
  classInfo = info;
3191
3739
  }
3192
3740
  }
@@ -3199,10 +3747,10 @@ function canAutoMigrate(classInfo) {
3199
3747
  reason: "No class found in file"
3200
3748
  };
3201
3749
  }
3202
- if (!classInfo.isInjectable && !classInfo.isController) {
3750
+ if (!classInfo.isInjectable && !classInfo.isController && !classInfo.isAutomation) {
3203
3751
  return {
3204
3752
  canMigrate: false,
3205
- reason: `Class "${classInfo.name}" is not @Injectable or @Controller`
3753
+ reason: `Class "${classInfo.name}" is not @Injectable, @Controller or @Automation`
3206
3754
  };
3207
3755
  }
3208
3756
  return { canMigrate: true };
@@ -3352,7 +3900,7 @@ function addInjection(sourceFile) {
3352
3900
  }
3353
3901
 
3354
3902
  // src/commands/migration/versions/v001_capability/code-migrator/transformers/call-site-transformer.ts
3355
- import { SyntaxKind as SyntaxKind2 } from "ts-morph";
3903
+ import { SyntaxKind as SyntaxKind5 } from "ts-morph";
3356
3904
  var DEFAULT_ACTION_NAME = "run";
3357
3905
  function generateNewCallText(capabilityId, actionName, args) {
3358
3906
  const argsText = args.trim() || "{}";
@@ -3367,19 +3915,19 @@ function transformCallSites(sourceFile, imports) {
3367
3915
  });
3368
3916
  }
3369
3917
  let replacedCount = 0;
3370
- const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind2.CallExpression);
3918
+ const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind5.CallExpression);
3371
3919
  const sortedCalls = [...callExpressions].sort((a, b) => b.getStart() - a.getStart());
3372
3920
  for (const callExpr of sortedCalls) {
3373
3921
  const expression = callExpr.getExpression();
3374
3922
  let importInfo;
3375
- if (expression.getKind() === SyntaxKind2.Identifier) {
3923
+ if (expression.getKind() === SyntaxKind5.Identifier) {
3376
3924
  const functionName = expression.getText();
3377
3925
  importInfo = importMap.get(functionName);
3378
- } else if (expression.getKind() === SyntaxKind2.PropertyAccessExpression) {
3379
- const propAccess = expression.asKind(SyntaxKind2.PropertyAccessExpression);
3926
+ } else if (expression.getKind() === SyntaxKind5.PropertyAccessExpression) {
3927
+ const propAccess = expression.asKind(SyntaxKind5.PropertyAccessExpression);
3380
3928
  if (propAccess) {
3381
3929
  const objectExpr = propAccess.getExpression();
3382
- if (objectExpr.getKind() === SyntaxKind2.Identifier) {
3930
+ if (objectExpr.getKind() === SyntaxKind5.Identifier) {
3383
3931
  const objectName = objectExpr.getText();
3384
3932
  importInfo = importMap.get(objectName);
3385
3933
  }
@@ -3480,7 +4028,7 @@ async function migrateCode(options, capabilities) {
3480
4028
  console.log(" No files need code migration.\n");
3481
4029
  return result;
3482
4030
  }
3483
- const project = new Project2({
4031
+ const project = new Project3({
3484
4032
  skipAddingFilesFromTsConfig: true,
3485
4033
  compilerOptions: {
3486
4034
  allowJs: true
@@ -3523,17 +4071,17 @@ function getSuggestion(analysis) {
3523
4071
  }
3524
4072
 
3525
4073
  // src/commands/migration/versions/v001_capability/cleanup.ts
3526
- import fs15 from "fs";
4074
+ import fs16 from "fs";
3527
4075
  import path14 from "path";
3528
4076
  function cleanupOldFiles(capabilities, dryRun) {
3529
4077
  const deletedFiles = [];
3530
4078
  const errors = [];
3531
4079
  const capabilitiesDir = getCapabilitiesDir2();
3532
4080
  const oldJsonPath = path14.join(capabilitiesDir, "capabilities.json");
3533
- if (fs15.existsSync(oldJsonPath)) {
4081
+ if (fs16.existsSync(oldJsonPath)) {
3534
4082
  try {
3535
4083
  if (!dryRun) {
3536
- fs15.unlinkSync(oldJsonPath);
4084
+ fs16.unlinkSync(oldJsonPath);
3537
4085
  }
3538
4086
  deletedFiles.push("capabilities.json");
3539
4087
  } catch (error) {
@@ -3542,10 +4090,10 @@ function cleanupOldFiles(capabilities, dryRun) {
3542
4090
  }
3543
4091
  for (const cap of capabilities) {
3544
4092
  const tsFilePath = path14.join(capabilitiesDir, `${cap.id}.ts`);
3545
- if (fs15.existsSync(tsFilePath)) {
4093
+ if (fs16.existsSync(tsFilePath)) {
3546
4094
  try {
3547
4095
  if (!dryRun) {
3548
- fs15.unlinkSync(tsFilePath);
4096
+ fs16.unlinkSync(tsFilePath);
3549
4097
  }
3550
4098
  deletedFiles.push(`${cap.id}.ts`);
3551
4099
  } catch (error) {
@@ -3561,7 +4109,7 @@ function cleanupOldFiles(capabilities, dryRun) {
3561
4109
  }
3562
4110
 
3563
4111
  // src/commands/migration/versions/v001_capability/report-generator.ts
3564
- import fs16 from "fs";
4112
+ import fs17 from "fs";
3565
4113
  import path15 from "path";
3566
4114
  var REPORT_FILE = "capability-migration-report.md";
3567
4115
  function printSummary(result) {
@@ -3725,15 +4273,15 @@ async function generateReport(result) {
3725
4273
  }
3726
4274
  lines.push("");
3727
4275
  const logDir = process.env.LOG_DIR || "logs";
3728
- if (!fs16.existsSync(logDir)) {
4276
+ if (!fs17.existsSync(logDir)) {
3729
4277
  return;
3730
4278
  }
3731
4279
  const reportDir = path15.join(logDir, "migration");
3732
- if (!fs16.existsSync(reportDir)) {
3733
- fs16.mkdirSync(reportDir, { recursive: true });
4280
+ if (!fs17.existsSync(reportDir)) {
4281
+ fs17.mkdirSync(reportDir, { recursive: true });
3734
4282
  }
3735
4283
  const reportPath = path15.join(reportDir, REPORT_FILE);
3736
- fs16.writeFileSync(reportPath, lines.join("\n"), "utf-8");
4284
+ fs17.writeFileSync(reportPath, lines.join("\n"), "utf-8");
3737
4285
  console.log(`\u{1F4C4} Report generated: ${reportPath}`);
3738
4286
  }
3739
4287
 
@@ -4268,7 +4816,7 @@ var migrationCommand = {
4268
4816
  import path16 from "path";
4269
4817
 
4270
4818
  // src/commands/read-logs/std-utils.ts
4271
- import fs17 from "fs";
4819
+ import fs18 from "fs";
4272
4820
  function formatStdPrefixTime(localTime) {
4273
4821
  const match = localTime.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
4274
4822
  if (!match) return localTime;
@@ -4298,11 +4846,11 @@ function stripPrefixFromStdLine(line) {
4298
4846
  return `[${time}] ${content}`;
4299
4847
  }
4300
4848
  function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarker) {
4301
- const stat = fs17.statSync(filePath);
4849
+ const stat = fs18.statSync(filePath);
4302
4850
  if (stat.size === 0) {
4303
4851
  return { lines: [], markerFound: false, totalLinesCount: 0 };
4304
4852
  }
4305
- const fd = fs17.openSync(filePath, "r");
4853
+ const fd = fs18.openSync(filePath, "r");
4306
4854
  const chunkSize = 64 * 1024;
4307
4855
  let position = stat.size;
4308
4856
  let remainder = "";
@@ -4316,7 +4864,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
4316
4864
  const length = Math.min(chunkSize, position);
4317
4865
  position -= length;
4318
4866
  const buffer = Buffer.alloc(length);
4319
- fs17.readSync(fd, buffer, 0, length, position);
4867
+ fs18.readSync(fd, buffer, 0, length, position);
4320
4868
  let chunk = buffer.toString("utf8");
4321
4869
  if (remainder) {
4322
4870
  chunk += remainder;
@@ -4358,7 +4906,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
4358
4906
  }
4359
4907
  }
4360
4908
  } finally {
4361
- fs17.closeSync(fd);
4909
+ fs18.closeSync(fd);
4362
4910
  }
4363
4911
  return { lines: collected.reverse(), markerFound, totalLinesCount };
4364
4912
  }
@@ -4379,21 +4927,21 @@ function readServerStdSegment(filePath, maxLines, offset) {
4379
4927
  }
4380
4928
 
4381
4929
  // src/commands/read-logs/tail.ts
4382
- import fs18 from "fs";
4930
+ import fs19 from "fs";
4383
4931
  function fileExists(filePath) {
4384
4932
  try {
4385
- fs18.accessSync(filePath, fs18.constants.F_OK | fs18.constants.R_OK);
4933
+ fs19.accessSync(filePath, fs19.constants.F_OK | fs19.constants.R_OK);
4386
4934
  return true;
4387
4935
  } catch {
4388
4936
  return false;
4389
4937
  }
4390
4938
  }
4391
4939
  function readFileTailLines(filePath, maxLines) {
4392
- const stat = fs18.statSync(filePath);
4940
+ const stat = fs19.statSync(filePath);
4393
4941
  if (stat.size === 0) {
4394
4942
  return [];
4395
4943
  }
4396
- const fd = fs18.openSync(filePath, "r");
4944
+ const fd = fs19.openSync(filePath, "r");
4397
4945
  const chunkSize = 64 * 1024;
4398
4946
  const chunks = [];
4399
4947
  let position = stat.size;
@@ -4403,13 +4951,13 @@ function readFileTailLines(filePath, maxLines) {
4403
4951
  const length = Math.min(chunkSize, position);
4404
4952
  position -= length;
4405
4953
  const buffer = Buffer.alloc(length);
4406
- fs18.readSync(fd, buffer, 0, length, position);
4954
+ fs19.readSync(fd, buffer, 0, length, position);
4407
4955
  chunks.unshift(buffer.toString("utf8"));
4408
4956
  const chunkLines = buffer.toString("utf8").split("\n").length - 1;
4409
4957
  collectedLines += chunkLines;
4410
4958
  }
4411
4959
  } finally {
4412
- fs18.closeSync(fd);
4960
+ fs19.closeSync(fd);
4413
4961
  }
4414
4962
  const content = chunks.join("");
4415
4963
  const allLines = content.split("\n");
@@ -4425,11 +4973,11 @@ function readFileTailLines(filePath, maxLines) {
4425
4973
  return allLines.slice(allLines.length - maxLines);
4426
4974
  }
4427
4975
  function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
4428
- const stat = fs18.statSync(filePath);
4976
+ const stat = fs19.statSync(filePath);
4429
4977
  if (stat.size === 0) {
4430
4978
  return { lines: [], totalLinesCount: 0 };
4431
4979
  }
4432
- const fd = fs18.openSync(filePath, "r");
4980
+ const fd = fs19.openSync(filePath, "r");
4433
4981
  const chunkSize = 64 * 1024;
4434
4982
  let position = stat.size;
4435
4983
  let remainder = "";
@@ -4441,7 +4989,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
4441
4989
  const length = Math.min(chunkSize, position);
4442
4990
  position -= length;
4443
4991
  const buffer = Buffer.alloc(length);
4444
- fs18.readSync(fd, buffer, 0, length, position);
4992
+ fs19.readSync(fd, buffer, 0, length, position);
4445
4993
  let chunk = buffer.toString("utf8");
4446
4994
  if (remainder) {
4447
4995
  chunk += remainder;
@@ -4472,7 +5020,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
4472
5020
  }
4473
5021
  }
4474
5022
  } finally {
4475
- fs18.closeSync(fd);
5023
+ fs19.closeSync(fd);
4476
5024
  }
4477
5025
  return { lines: collected.reverse(), totalLinesCount };
4478
5026
  }
@@ -4574,7 +5122,7 @@ function extractClientStdSegment(lines, maxLines, offset) {
4574
5122
  }
4575
5123
 
4576
5124
  // src/commands/read-logs/json-lines.ts
4577
- import fs19 from "fs";
5125
+ import fs20 from "fs";
4578
5126
  function normalizePid(value) {
4579
5127
  if (typeof value === "number") {
4580
5128
  return String(value);
@@ -4625,11 +5173,11 @@ function buildWantedLevelSet(levels) {
4625
5173
  return set.size > 0 ? set : null;
4626
5174
  }
4627
5175
  function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
4628
- const stat = fs19.statSync(filePath);
5176
+ const stat = fs20.statSync(filePath);
4629
5177
  if (stat.size === 0) {
4630
5178
  return { lines: [], totalLinesCount: 0 };
4631
5179
  }
4632
- const fd = fs19.openSync(filePath, "r");
5180
+ const fd = fs20.openSync(filePath, "r");
4633
5181
  const chunkSize = 64 * 1024;
4634
5182
  let position = stat.size;
4635
5183
  let remainder = "";
@@ -4644,7 +5192,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
4644
5192
  const length = Math.min(chunkSize, position);
4645
5193
  position -= length;
4646
5194
  const buffer = Buffer.alloc(length);
4647
- fs19.readSync(fd, buffer, 0, length, position);
5195
+ fs20.readSync(fd, buffer, 0, length, position);
4648
5196
  let chunk = buffer.toString("utf8");
4649
5197
  if (remainder) {
4650
5198
  chunk += remainder;
@@ -4706,7 +5254,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
4706
5254
  }
4707
5255
  }
4708
5256
  } finally {
4709
- fs19.closeSync(fd);
5257
+ fs20.closeSync(fd);
4710
5258
  }
4711
5259
  return { lines: collected.reverse(), totalLinesCount };
4712
5260
  }
@@ -4749,11 +5297,11 @@ function extractTraceId(obj) {
4749
5297
  function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
4750
5298
  const wanted = traceId.trim();
4751
5299
  if (!wanted) return { lines: [], totalLinesCount: 0 };
4752
- const stat = fs19.statSync(filePath);
5300
+ const stat = fs20.statSync(filePath);
4753
5301
  if (stat.size === 0) {
4754
5302
  return { lines: [], totalLinesCount: 0 };
4755
5303
  }
4756
- const fd = fs19.openSync(filePath, "r");
5304
+ const fd = fs20.openSync(filePath, "r");
4757
5305
  const chunkSize = 64 * 1024;
4758
5306
  let position = stat.size;
4759
5307
  let remainder = "";
@@ -4766,7 +5314,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
4766
5314
  const length = Math.min(chunkSize, position);
4767
5315
  position -= length;
4768
5316
  const buffer = Buffer.alloc(length);
4769
- fs19.readSync(fd, buffer, 0, length, position);
5317
+ fs20.readSync(fd, buffer, 0, length, position);
4770
5318
  let chunk = buffer.toString("utf8");
4771
5319
  if (remainder) {
4772
5320
  chunk += remainder;
@@ -4819,7 +5367,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
4819
5367
  }
4820
5368
  }
4821
5369
  } finally {
4822
- fs19.closeSync(fd);
5370
+ fs20.closeSync(fd);
4823
5371
  }
4824
5372
  return { lines: collected.reverse(), totalLinesCount };
4825
5373
  }
@@ -4828,11 +5376,11 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
4828
5376
  if (!wantedLevelSet) {
4829
5377
  return { lines: [], totalLinesCount: 0 };
4830
5378
  }
4831
- const stat = fs19.statSync(filePath);
5379
+ const stat = fs20.statSync(filePath);
4832
5380
  if (stat.size === 0) {
4833
5381
  return { lines: [], totalLinesCount: 0 };
4834
5382
  }
4835
- const fd = fs19.openSync(filePath, "r");
5383
+ const fd = fs20.openSync(filePath, "r");
4836
5384
  const chunkSize = 64 * 1024;
4837
5385
  let position = stat.size;
4838
5386
  let remainder = "";
@@ -4844,7 +5392,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
4844
5392
  const length = Math.min(chunkSize, position);
4845
5393
  position -= length;
4846
5394
  const buffer = Buffer.alloc(length);
4847
- fs19.readSync(fd, buffer, 0, length, position);
5395
+ fs20.readSync(fd, buffer, 0, length, position);
4848
5396
  let chunk = buffer.toString("utf8");
4849
5397
  if (remainder) {
4850
5398
  chunk += remainder;
@@ -4891,7 +5439,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
4891
5439
  }
4892
5440
  }
4893
5441
  } finally {
4894
- fs19.closeSync(fd);
5442
+ fs20.closeSync(fd);
4895
5443
  }
4896
5444
  return { lines: collected.reverse(), totalLinesCount };
4897
5445
  }
@@ -5189,11 +5737,11 @@ var commands = [
5189
5737
 
5190
5738
  // src/index.ts
5191
5739
  var envPath = path17.join(process.cwd(), ".env");
5192
- if (fs20.existsSync(envPath)) {
5740
+ if (fs21.existsSync(envPath)) {
5193
5741
  dotenvConfig({ path: envPath });
5194
5742
  }
5195
5743
  var __dirname = path17.dirname(fileURLToPath4(import.meta.url));
5196
- var pkg = JSON.parse(fs20.readFileSync(path17.join(__dirname, "../package.json"), "utf-8"));
5744
+ var pkg = JSON.parse(fs21.readFileSync(path17.join(__dirname, "../package.json"), "utf-8"));
5197
5745
  var cli = new FullstackCLI(pkg.version);
5198
5746
  cli.useAll(commands);
5199
5747
  cli.run();