@lark-apaas/fullstack-cli 1.1.12-alpha.12 → 1.1.12-alpha.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1387 -234
  2. package/package.json +1 -2
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
- import fs18 from "fs";
3
- import path16 from "path";
2
+ import fs20 from "fs";
3
+ import path18 from "path";
4
4
  import { fileURLToPath as fileURLToPath3 } from "url";
5
5
  import { config as dotenvConfig } from "dotenv";
6
6
 
@@ -116,17 +116,1172 @@ Command "${ctx.commandName}" completed in ${elapsed}ms`);
116
116
  };
117
117
 
118
118
  // src/commands/db/schema.handler.ts
119
- import path from "path";
120
- import fs from "fs";
119
+ import path3 from "path";
120
+ import fs3 from "fs";
121
121
  import { fileURLToPath } from "url";
122
122
  import { spawnSync } from "child_process";
123
123
  import { createRequire } from "module";
124
124
  import { config as loadEnv } from "dotenv";
125
+
126
+ // src/commands/db/gen-dbschema/postprocess.ts
127
+ import fs2 from "fs";
128
+ import path2 from "path";
129
+
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}`;
145
+ }
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);
150
+ }
151
+ return current;
152
+ }
153
+ function collapseExtraBlankLines(text) {
154
+ return text.replace(/\n{3,}/g, "\n\n");
155
+ }
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(";
174
+ });
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
+ }
185
+
186
+ // src/commands/db/gen-dbschema/helper/table-rename.ts
187
+ 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
+ }
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
+ }
219
+ function toCamelCase(str) {
220
+ const words = str.split(/[_\-\s]+/).filter(Boolean);
221
+ if (words.length === 0) {
222
+ return "";
223
+ }
224
+ return words.map((word, index) => {
225
+ if (index === 0) {
226
+ return word.toLowerCase();
227
+ }
228
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
229
+ }).join("");
230
+ }
231
+ function sanitizeIdentifier(name) {
232
+ const asciiName = toAsciiName(name);
233
+ let sanitized = asciiName.replace(/[^A-Za-z0-9_]/g, "_");
234
+ sanitized = sanitized.replace(/_+/g, "_");
235
+ sanitized = sanitized.replace(/^_|_$/g, "");
236
+ sanitized = toCamelCase(sanitized);
237
+ if (!sanitized) {
238
+ sanitized = "table";
239
+ }
240
+ if (!/^[A-Za-z_]/.test(sanitized)) {
241
+ sanitized = `_${sanitized}`;
242
+ }
243
+ return sanitized;
244
+ }
245
+ function toAsciiName(name) {
246
+ if (!/[^\x00-\x7F]/.test(name)) {
247
+ return name;
248
+ }
249
+ try {
250
+ const transliterated = pinyin(name, { toneType: "none", type: "array" }).join("_");
251
+ return transliterated || name;
252
+ } catch (error) {
253
+ return name;
254
+ }
255
+ }
256
+
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;
275
+ } else {
276
+ unmatched.push(line.trim());
277
+ result.push(line);
278
+ }
279
+ continue;
280
+ }
281
+ if (line.includes("unknown(")) {
282
+ unmatched.push(line.trim());
283
+ }
284
+ result.push(line);
285
+ }
286
+ return {
287
+ text: result.join("\n"),
288
+ replaced,
289
+ unmatched
290
+ };
291
+ }
292
+ function replaceFollowingUnknown(nextLine, factory) {
293
+ if (!nextLine || !nextLine.includes("unknown(")) {
294
+ return void 0;
295
+ }
296
+ return nextLine.replace("unknown(", `${factory}(`);
297
+ }
298
+
299
+ // src/commands/db/gen-dbschema/helper/imports.ts
300
+ import fs from "fs";
301
+ import path from "path";
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;
307
+ }
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);
313
+ }
314
+ return true;
315
+ });
316
+ if (source.includes("pgTable(") && !filteredIdentifiers.includes("pgTable")) {
317
+ filteredIdentifiers.push("pgTable");
318
+ }
319
+ if (source.includes("pgView(") && !filteredIdentifiers.includes("pgView")) {
320
+ filteredIdentifiers.push("pgView");
321
+ }
322
+ if (source.includes("pgMaterializedView(") && !filteredIdentifiers.includes("pgMaterializedView")) {
323
+ filteredIdentifiers.push("pgMaterializedView");
324
+ }
325
+ if (source.includes("pgEnum(") && !filteredIdentifiers.includes("pgEnum")) {
326
+ filteredIdentifiers.push("pgEnum");
327
+ }
328
+ if (source.includes("pgSequence(") && !filteredIdentifiers.includes("pgSequence")) {
329
+ filteredIdentifiers.push("pgSequence");
330
+ }
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);
334
+ }
335
+ function inlineCustomTypes(source) {
336
+ let text = source.replace(/import \{[^}]*\} from ["']\.\/types["'];?\n*/g, "");
337
+ const possiblePaths = [
338
+ // When bundled by tsup - __dirname points to dist/
339
+ path.resolve(__dirname, "template", "types.ts"),
340
+ // When running from source (development) - relative to helper/imports.ts
341
+ path.resolve(__dirname, "../template", "types.ts"),
342
+ // Alternative paths
343
+ path.resolve(__dirname, "../../template", "types.ts"),
344
+ path.resolve(__dirname, "../../../template", "types.ts")
345
+ ];
346
+ let templatePath;
347
+ for (const possiblePath of possiblePaths) {
348
+ if (fs.existsSync(possiblePath)) {
349
+ templatePath = possiblePath;
350
+ break;
351
+ }
352
+ }
353
+ if (!templatePath) {
354
+ console.warn("[postprocess-drizzle-schema] Template types file not found. Tried paths:", possiblePaths);
355
+ return text;
356
+ }
357
+ return inlineFromTemplate(text, templatePath);
358
+ }
359
+ function inlineFromTemplate(text, templatePath) {
360
+ const templateContent = fs.readFileSync(templatePath, "utf8");
361
+ const typeDefinitions = templateContent.replace(/^import\s+.*;\r?\n*/gm, "").trim();
362
+ const needsSql = typeDefinitions.includes("sql`") && !text.includes("from 'drizzle-orm'") && !text.includes('from "drizzle-orm"');
363
+ const needsCustomType = typeDefinitions.includes("customType<") && !text.includes("customType");
364
+ if (needsCustomType) {
365
+ text = ensureImportIdentifier(text, "drizzle-orm/pg-core", "customType");
366
+ }
367
+ if (needsSql && !text.includes("from 'drizzle-orm'") && !text.includes('from "drizzle-orm"')) {
368
+ const importMatch = text.match(/^import [\s\S]*?from ["']drizzle-orm\/pg-core["'];?\n/m);
369
+ if (importMatch) {
370
+ const insertPoint = text.indexOf(importMatch[0]) + importMatch[0].length;
371
+ text = text.slice(0, insertPoint) + "import { sql } from 'drizzle-orm';\n" + text.slice(insertPoint);
372
+ }
373
+ }
374
+ const headerPrefix = `${HEADER_COMMENT}
375
+ `;
376
+ let insertionPoint = 0;
377
+ if (text.startsWith(headerPrefix)) {
378
+ insertionPoint = headerPrefix.length;
379
+ }
380
+ const importSectionMatch = text.slice(insertionPoint).match(/^(?:import [^\n]+\n)+/);
381
+ if (importSectionMatch) {
382
+ insertionPoint += importSectionMatch[0].length;
383
+ }
384
+ const typeBlock = `
385
+ ${typeDefinitions}
386
+
387
+ `;
388
+ return text.slice(0, insertionPoint) + typeBlock + text.slice(insertionPoint);
389
+ }
390
+ function ensureImportIdentifier(source, packageName, identifier) {
391
+ const escapedPackage = packageName.replace(/\//g, "\\/");
392
+ const importRegex = new RegExp(`import \\{([^}]*)\\} from ["']${escapedPackage}["'];?`);
393
+ const match = source.match(importRegex);
394
+ if (!match) {
395
+ return source;
396
+ }
397
+ const identifiers = match[1].split(",").map((id) => id.trim()).filter(Boolean);
398
+ if (identifiers.includes(identifier)) {
399
+ return source;
400
+ }
401
+ identifiers.push(identifier);
402
+ const unique = Array.from(new Set(identifiers));
403
+ const replacement = `import { ${unique.join(", ")} } from "${packageName}"`;
404
+ return source.replace(importRegex, replacement);
405
+ }
406
+
407
+ // src/commands/db/gen-dbschema/helper/system-fields.ts
408
+ function addSystemFieldComments(source) {
409
+ const commentMap = {
410
+ "_created_at": "Creation time",
411
+ "_created_by": "Creator",
412
+ "_updated_at": "Update time",
413
+ "_updated_by": "Updater"
414
+ };
415
+ const lines = source.split("\n");
416
+ for (let i = 0; i < lines.length; i += 1) {
417
+ const line = lines[i];
418
+ const entry = Object.entries(commentMap).find(([key]) => line.includes(`"${key}"`));
419
+ if (!entry) {
420
+ continue;
421
+ }
422
+ const [, description] = entry;
423
+ const previousLine = lines[i - 1]?.trim() ?? "";
424
+ if (previousLine.startsWith("//") && previousLine.includes("System field")) {
425
+ continue;
426
+ }
427
+ const indentMatch = line.match(/^\s*/);
428
+ const indent = indentMatch ? indentMatch[0] : "";
429
+ const comment = `${indent}// System field: ${description} (auto-filled, do not modify)`;
430
+ lines.splice(i, 0, comment);
431
+ i += 1;
432
+ }
433
+ return lines.join("\n");
434
+ }
435
+ function removeConflictingSystemFields(source) {
436
+ const systemFieldMap = {
437
+ "_created_at": "created_at",
438
+ "_created_by": "created_by",
439
+ "_updated_at": "updated_at",
440
+ "_updated_by": "updated_by"
441
+ };
442
+ const lines = source.split("\n");
443
+ const result = [];
444
+ let inTable = false;
445
+ let tableStartLine = -1;
446
+ const tableBusinessFields = /* @__PURE__ */ new Set();
447
+ let bracketDepth = 0;
448
+ for (let i = 0; i < lines.length; i += 1) {
449
+ const line = lines[i];
450
+ if (!inTable && /=\s*(pgTable|pgView|pgMaterializedView)\s*\(/.test(line)) {
451
+ inTable = true;
452
+ tableStartLine = result.length;
453
+ tableBusinessFields.clear();
454
+ bracketDepth = 0;
455
+ }
456
+ if (inTable) {
457
+ for (const char of line) {
458
+ if (char === "{") bracketDepth++;
459
+ if (char === "}") bracketDepth--;
460
+ }
461
+ for (const businessField of Object.values(systemFieldMap)) {
462
+ if (line.includes(`"${businessField}"`) || line.includes(`'${businessField}'`)) {
463
+ tableBusinessFields.add(businessField);
464
+ }
465
+ }
466
+ if (bracketDepth === 0 && line.includes(");")) {
467
+ inTable = false;
468
+ const tableEndLine = result.length;
469
+ for (let j = tableStartLine; j <= tableEndLine; j++) {
470
+ const tableLine = result[j] || "";
471
+ let shouldRemove = false;
472
+ for (const [systemField, businessField] of Object.entries(systemFieldMap)) {
473
+ if (tableBusinessFields.has(businessField)) {
474
+ if (tableLine.includes(`"${systemField}"`) || tableLine.includes(`'${systemField}'`)) {
475
+ shouldRemove = true;
476
+ if (j > 0 && result[j - 1]?.includes("// System field:")) {
477
+ result[j - 1] = null;
478
+ }
479
+ break;
480
+ }
481
+ }
482
+ }
483
+ if (shouldRemove) {
484
+ result[j] = null;
485
+ }
486
+ }
487
+ }
488
+ }
489
+ result.push(line);
490
+ }
491
+ return result.filter((line) => line !== null).join("\n");
492
+ }
493
+
494
+ // src/commands/db/gen-dbschema/helper/patch-helper.ts
495
+ function patchDrizzleKitDefects(source) {
496
+ let fixed = 0;
497
+ const text = source.replace(/\.default\('\)/g, () => {
498
+ fixed += 1;
499
+ return `.default('')`;
500
+ });
501
+ return { text, fixed };
502
+ }
503
+
504
+ // src/commands/db/gen-dbschema/helper/timestamp-replacement.ts
505
+ function replaceTimestampWithCustomTypes(source) {
506
+ let replaced = 0;
507
+ const pattern = /timestamp\((['"])(.*?)\1,\s*(\{[^}]*\})\)/g;
508
+ const text = source.replace(pattern, (match, quote, fieldName, options) => {
509
+ const hasWithTimezone = /withTimezone:\s*true/.test(options);
510
+ const hasModeString = /mode:\s*['"]string['"]/.test(options);
511
+ if (hasWithTimezone && hasModeString) {
512
+ replaced += 1;
513
+ return `customTimestamptz(${quote}${fieldName}${quote})`;
514
+ }
515
+ return match;
516
+ });
517
+ return { text, replaced };
518
+ }
519
+ function replaceDefaultNowWithSql(source) {
520
+ let replaced = 0;
521
+ const pattern = /\.defaultNow\(\)/g;
522
+ const text = source.replace(pattern, () => {
523
+ replaced += 1;
524
+ return ".default(sql`CURRENT_TIMESTAMP`)";
525
+ });
526
+ return { text, replaced };
527
+ }
528
+
529
+ // src/commands/db/gen-dbschema/helper/appendTableAliases.ts
530
+ var TABLE_ALIAS_MARKER = "// table aliases";
531
+ function appendTableAliases(source) {
532
+ const markerIndex = source.indexOf(`
533
+ ${TABLE_ALIAS_MARKER}`);
534
+ const base = markerIndex === -1 ? source : source.slice(0, markerIndex);
535
+ const exportRegex = /export const\s+([A-Za-z_$][\w$]*)\s*=\s*pgTable\s*\(/g;
536
+ const tableExports = /* @__PURE__ */ new Set();
537
+ for (const match of base.matchAll(exportRegex)) {
538
+ const name = match[1];
539
+ tableExports.add(name);
540
+ }
541
+ if (tableExports.size === 0) {
542
+ return base;
543
+ }
544
+ const aliasLines = Array.from(tableExports).sort().map((name) => `export const ${name}Table = ${name};`).join("\n");
545
+ const prefix = base.trimEnd();
546
+ return `${prefix}
547
+
548
+ ${TABLE_ALIAS_MARKER}
549
+ ${aliasLines}
550
+ `;
551
+ }
552
+
553
+ // src/commands/db/gen-dbschema/postprocess.ts
554
+ function postprocessDrizzleSchema(targetPath) {
555
+ const resolvedPath = path2.resolve(targetPath);
556
+ if (!fs2.existsSync(resolvedPath)) {
557
+ console.warn(`[postprocess-drizzle-schema] File not found: ${resolvedPath}`);
558
+ return void 0;
559
+ }
560
+ let text = fs2.readFileSync(resolvedPath, "utf8");
561
+ text = ensureHeaderComment(text);
562
+ const patchResult = patchDrizzleKitDefects(text);
563
+ text = patchResult.text;
564
+ text = removePgSchemaDeclarations(text);
565
+ const tableConversion = convertSchemaTableInvocations(text);
566
+ text = tableConversion.text;
567
+ const renameResult = renamePgTableConstants(text);
568
+ text = renameResult.text;
569
+ text = updateTableReferenceIdentifiers(text, renameResult.renames);
570
+ const replacement = replaceUnknownColumns(text);
571
+ text = replacement.text;
572
+ const timestampReplacement = replaceTimestampWithCustomTypes(text);
573
+ text = timestampReplacement.text;
574
+ const defaultNowReplacement = replaceDefaultNowWithSql(text);
575
+ text = defaultNowReplacement.text;
576
+ text = removeConflictingSystemFields(text);
577
+ text = addSystemFieldComments(text);
578
+ text = tweakImports(text);
579
+ text = inlineCustomTypes(text);
580
+ text = appendTableAliases(text);
581
+ text = text.replace(/\r?\n/g, "\n");
582
+ text = collapseExtraBlankLines(text);
583
+ fs2.writeFileSync(resolvedPath, text, "utf8");
584
+ if (patchResult.fixed > 0) {
585
+ console.info(`[postprocess-drizzle-schema] Patched ${patchResult.fixed} drizzle-kit defects (.default(') -> .default(''))`);
586
+ }
587
+ if (replacement.replaced > 0) {
588
+ console.info(`[postprocess-drizzle-schema] Replaced ${replacement.replaced} unknown columns`);
589
+ }
590
+ if (replacement.unmatched.length > 0) {
591
+ console.warn("[postprocess-drizzle-schema] Unmatched custom types:", replacement.unmatched.length);
592
+ replacement.unmatched.forEach((line) => console.warn(` ${line}`));
593
+ }
594
+ if (tableConversion.converted > 0) {
595
+ console.info(`[postprocess-drizzle-schema] Converted ${tableConversion.converted} schema.table invocations to pgTable`);
596
+ }
597
+ if (timestampReplacement.replaced > 0) {
598
+ console.info(`[postprocess-drizzle-schema] Replaced ${timestampReplacement.replaced} timestamp fields with customTimestamptz`);
599
+ }
600
+ if (defaultNowReplacement.replaced > 0) {
601
+ console.info(`[postprocess-drizzle-schema] Replaced ${defaultNowReplacement.replaced} .defaultNow() with .default(sql\`CURRENT_TIMESTAMP\`)`);
602
+ }
603
+ return {
604
+ replacedUnknown: replacement.replaced,
605
+ unmatchedUnknown: replacement.unmatched,
606
+ patchedDefects: patchResult.fixed,
607
+ replacedTimestamps: timestampReplacement.replaced,
608
+ replacedDefaultNow: defaultNowReplacement.replaced
609
+ };
610
+ }
611
+
612
+ // src/commands/db/gen-nest-resource/generator.ts
613
+ import { pluralize } from "inflection";
614
+
615
+ // src/commands/db/gen-nest-resource/utils.ts
616
+ function mapDrizzleTypeToTS(field) {
617
+ const typeMap = {
618
+ // String types
619
+ char: "string",
620
+ varchar: "string",
621
+ text: "string",
622
+ // Numeric types
623
+ smallint: "number",
624
+ integer: "number",
625
+ int: "number",
626
+ bigint: "string",
627
+ // bigint 在 JS 中通常作为 string 处理
628
+ serial: "number",
629
+ smallserial: "number",
630
+ bigserial: "string",
631
+ // Decimal types
632
+ decimal: "string",
633
+ // 精确数值通常用 string
634
+ numeric: "string",
635
+ real: "number",
636
+ doublePrecision: "number",
637
+ // Boolean
638
+ boolean: "boolean",
639
+ // Date/Time types
640
+ timestamp: "Date",
641
+ timestamptz: "Date",
642
+ date: "Date",
643
+ time: "string",
644
+ timetz: "string",
645
+ interval: "string",
646
+ // UUID
647
+ uuid: "string",
648
+ // JSON types
649
+ json: "any",
650
+ jsonb: "any",
651
+ // Binary
652
+ bytea: "Buffer",
653
+ // Network types
654
+ inet: "string",
655
+ cidr: "string",
656
+ macaddr: "string",
657
+ macaddr8: "string",
658
+ // Geometric types
659
+ point: "{ x: number; y: number }",
660
+ line: "string",
661
+ lseg: "string",
662
+ box: "string",
663
+ path: "string",
664
+ polygon: "string",
665
+ circle: "string",
666
+ // Array types (handled by isArray flag)
667
+ array: "any[]",
668
+ // Custom types
669
+ customType: "any",
670
+ customTimestamptz: "Date",
671
+ userProfile: "string",
672
+ fileAttachment: "FileAttachment",
673
+ // Enum (handled separately)
674
+ pgEnum: "string"
675
+ };
676
+ let baseType = typeMap[field.type] || "any";
677
+ if (field.isArray) {
678
+ baseType = baseType.endsWith("[]") ? baseType : `${baseType}[]`;
679
+ }
680
+ if (field.enumValues && field.enumValues.length > 0) {
681
+ baseType = field.enumValues.map((v) => `'${v}'`).join(" | ");
682
+ }
683
+ return baseType;
684
+ }
685
+ function toPascalCase(str) {
686
+ return str.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[-_\s]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
687
+ }
688
+ function toKebabCase(str) {
689
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase().replace(/[_\s]/g, "-");
690
+ }
691
+ function toSnakeCase(str) {
692
+ return str.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase().replace(/[-\s]/g, "_");
693
+ }
694
+
695
+ // src/commands/db/gen-nest-resource/generator.ts
696
+ function generateDTO(table) {
697
+ const className = toPascalCase(table.variableName);
698
+ let dto = `// \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
699
+ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
700
+ import { IsDefined, IsNumber, IsOptional, IsString, MaxLength, IsInt, IsBoolean, IsUUID, IsDate, IsObject, IsArray } from 'class-validator';
701
+ import { Type } from 'class-transformer';
702
+ import { FileAttachment } from '../../../database/schema';
703
+
704
+ `;
705
+ dto += `export class Create${className}Dto {
706
+ `;
707
+ for (const field of table.fields) {
708
+ if (field.isPrimaryKey || field.name === "id" || field.name.startsWith("_") || field.name.startsWith("created") || field.name.startsWith("updated")) {
709
+ continue;
710
+ }
711
+ const tsType = mapDrizzleTypeToTS(field);
712
+ const optional = field.nullable || field.hasDefault ? "?" : "";
713
+ const decorators = generateValidationDecorators(field);
714
+ if (decorators) {
715
+ dto += decorators;
716
+ }
717
+ dto += ` ${field.name}${optional}: ${tsType};
718
+
719
+ `;
720
+ }
721
+ dto += "}\n\n";
722
+ dto += `export class Update${className}Dto {
723
+ `;
724
+ for (const field of table.fields) {
725
+ if (field.name.startsWith("_") || field.name.startsWith("created") || field.name.startsWith("updated") || field.isPrimaryKey || field.name === "id") {
726
+ continue;
727
+ }
728
+ const tsType = mapDrizzleTypeToTS(field);
729
+ const decorators = generateValidationDecorators(field, {
730
+ isUpdate: true
731
+ });
732
+ if (decorators) {
733
+ dto += decorators;
734
+ }
735
+ dto += ` ${field.name}?: ${tsType};
736
+
737
+ `;
738
+ }
739
+ dto += "}\n\n";
740
+ dto += `export class ${className}ResponseDto {
741
+ `;
742
+ for (const field of table.fields) {
743
+ const tsType = mapDrizzleTypeToTS(field);
744
+ const optional = field.nullable ? "?" : "";
745
+ const decorators = generateValidationDecorators(field, {
746
+ isResponse: true
747
+ });
748
+ if (decorators) {
749
+ dto += decorators;
750
+ }
751
+ dto += ` ${field.name}${optional}: ${tsType};
752
+
753
+ `;
754
+ }
755
+ dto += "}\n";
756
+ return dto;
757
+ }
758
+ function generateValidationDecorators(field, {
759
+ isUpdate = false,
760
+ isResponse = false
761
+ } = {}) {
762
+ let decorators = " // \u8BF7\u6309\u7528\u6237\u9700\u6C42\u4FEE\u6539\u4EE5\u4E0B\u88C5\u9970\u5668\u6CE8\u91CA\n";
763
+ if (field.nullable || !isResponse && field.hasDefault || isUpdate) {
764
+ decorators += ` @ApiPropertyOptional({ description: '${field.comment || field.name}' })
765
+ `;
766
+ if (isResponse) {
767
+ return decorators;
768
+ }
769
+ decorators += " @IsOptional()\n";
770
+ } else {
771
+ decorators += ` @ApiProperty({ description: '${field.comment || field.name}' })
772
+ `;
773
+ if (isResponse) {
774
+ return decorators;
775
+ }
776
+ decorators += " @IsDefined()\n";
777
+ }
778
+ switch (field.type) {
779
+ case "varchar":
780
+ case "char":
781
+ case "text":
782
+ decorators += " @IsString()\n";
783
+ if (field.length) {
784
+ decorators += ` @MaxLength(${field.length})
785
+ `;
786
+ }
787
+ break;
788
+ case "integer":
789
+ case "smallint":
790
+ case "serial":
791
+ case "smallserial":
792
+ decorators += " @IsInt()\n";
793
+ break;
794
+ case "decimal":
795
+ case "numeric":
796
+ case "real":
797
+ case "doublePrecision":
798
+ decorators += " @IsNumber()\n";
799
+ break;
800
+ case "boolean":
801
+ decorators += " @IsBoolean()\n";
802
+ break;
803
+ case "uuid":
804
+ decorators += " @IsUUID()\n";
805
+ break;
806
+ case "timestamp":
807
+ case "timestamptz":
808
+ case "date":
809
+ case "customTimestamptz":
810
+ decorators += " @IsDate()\n";
811
+ break;
812
+ case "json":
813
+ case "jsonb":
814
+ decorators += " @IsObject()\n";
815
+ break;
816
+ }
817
+ if (field.isArray) {
818
+ decorators += " @IsArray()\n";
819
+ }
820
+ return decorators;
821
+ }
822
+ function generateController(table) {
823
+ const className = toPascalCase(table.variableName);
824
+ const routePath = toKebabCase(pluralize(table.variableName));
825
+ const filePath = toSnakeCase(table.variableName);
826
+ const pkField = table.fields.find((f) => f.isPrimaryKey);
827
+ const pkType = pkField ? mapDrizzleTypeToTS(pkField) : "string";
828
+ const pkName = pkField ? pkField.name : "id";
829
+ const controller = `
830
+ // \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
831
+ import {
832
+ Controller,
833
+ Get,
834
+ Post,
835
+ Put,
836
+ Delete,
837
+ Body,
838
+ Param,
839
+ Query,
840
+ } from '@nestjs/common';
841
+ import {
842
+ ApiTags,
843
+ ApiOperation,
844
+ ApiOkResponse,
845
+ ApiCreatedResponse,
846
+ } from '@nestjs/swagger';
847
+ import {
848
+ Create${className}Dto,
849
+ Update${className}Dto,
850
+ ${className}ResponseDto
851
+ } from './dtos/${filePath}.dto';
852
+ import { ${className}Service } from './${filePath}.service';
853
+
854
+ @ApiTags('${toPascalCase(table.variableName)}')
855
+ @Controller('api/${routePath}')
856
+ export class ${className}Controller {
857
+ constructor(private readonly ${table.variableName}Service: ${className}Service) {}
858
+
859
+ @Post()
860
+ @ApiOperation({
861
+ summary: '\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
862
+ description: '\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
863
+ })
864
+ @ApiCreatedResponse({
865
+ description: '\u6210\u529F\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55',
866
+ type: ${className}ResponseDto,
867
+ })
868
+ async create(
869
+ @Body() createDto: Create${className}Dto
870
+ ): Promise<${className}ResponseDto> {
871
+ return this.${table.variableName}Service.create(createDto);
872
+ }
873
+
874
+ @ApiOperation({
875
+ summary: '\u6839\u636E\u4E3B\u952E\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
876
+ description: '\u6839\u636E\u4E3B\u952E\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
877
+ })
878
+ @ApiOkResponse({
879
+ description: '\u6210\u529F\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55',
880
+ type: ${className}ResponseDto,
881
+ })
882
+ @Get(':${pkName}')
883
+ async findOne(
884
+ @Param('${pkName}') ${pkName}: ${pkType}
885
+ ): Promise<${className}ResponseDto> {
886
+ return this.${table.variableName}Service.findOne(${pkName});
887
+ }
888
+
889
+ @ApiOperation({
890
+ summary: '\u6839\u636E\u4E3B\u952E\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
891
+ description: '\u6839\u636E\u4E3B\u952E\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
892
+ })
893
+ @ApiOkResponse({
894
+ description: '\u6210\u529F\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55',
895
+ type: ${className}ResponseDto,
896
+ })
897
+ @Put(':${pkName}')
898
+ async update(
899
+ @Param('${pkName}') ${pkName}: ${pkType},
900
+ @Body() updateDto: Update${className}Dto
901
+ ): Promise<${className}ResponseDto> {
902
+ return this.${table.variableName}Service.update(${pkName}, updateDto);
903
+ }
904
+
905
+ @ApiOperation({
906
+ summary: '\u6839\u636E\u4E3B\u952E\u5220\u9664\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
907
+ description: '\u6839\u636E\u4E3B\u952E\u5220\u9664\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
908
+ })
909
+ @ApiOkResponse({
910
+ description: '\u6210\u529F\u5220\u9664\u4E00\u6761\u8BB0\u5F55',
911
+ })
912
+ @Delete(':${pkName}')
913
+ async remove(
914
+ @Param('${pkName}') ${pkName}: ${pkType}
915
+ ): Promise<void> {
916
+ return this.${table.variableName}Service.remove(${pkName});
917
+ }
918
+ }
919
+ `;
920
+ return controller;
921
+ }
922
+ function generateService(table) {
923
+ const className = toPascalCase(table.variableName);
924
+ const filePath = toSnakeCase(table.variableName);
925
+ const pkField = table.fields.find((f) => f.isPrimaryKey);
926
+ const pkType = pkField ? mapDrizzleTypeToTS(pkField) : "string";
927
+ const pkName = pkField ? pkField.name : "id";
928
+ const service = `
929
+ // \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
930
+ import { Injectable, Inject, Logger, NotFoundException } from '@nestjs/common';
931
+ import { eq } from 'drizzle-orm';
932
+ import { DRIZZLE_DATABASE, type PostgresJsDatabase } from '@lark-apaas/fullstack-nestjs-core';
933
+ import { ${table.variableName} } from '../../database/schema';
934
+ import {
935
+ Create${className}Dto,
936
+ Update${className}Dto,
937
+ ${className}ResponseDto
938
+ } from './dtos/${filePath}.dto';
939
+
940
+ @Injectable()
941
+ export class ${className}Service {
942
+ private readonly logger = new Logger(${className}Service.name);
943
+
944
+ constructor(@Inject(DRIZZLE_DATABASE) private readonly db: PostgresJsDatabase) {}
945
+
946
+ async create(createDto: Create${className}Dto): Promise<${className}ResponseDto> {
947
+ const [result] = await this.db
948
+ .insert(${table.variableName})
949
+ .values(createDto)
950
+ .returning();
951
+
952
+ this.logger.log(\`Created ${className} with ${pkName} \${result.${pkName}}\`);
953
+
954
+ return result;
955
+ }
956
+
957
+ async findAll(options?: { page?: number; limit?: number }): Promise<${className}ResponseDto[]> {
958
+ const { page = 1, limit = 10 } = options || {};
959
+ const offset = (page - 1) * limit;
960
+
961
+ return this.db
962
+ .select()
963
+ .from(${table.variableName})
964
+ .limit(limit)
965
+ .offset(offset);
966
+ }
967
+
968
+ async findOne(${pkName}: ${pkType}): Promise<${className}ResponseDto> {
969
+ const [result] = await this.db
970
+ .select()
971
+ .from(${table.variableName})
972
+ .where(eq(${table.variableName}.${pkName}, ${pkName}))
973
+ .limit(1);
974
+
975
+ if (!result) {
976
+ throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
977
+ }
978
+
979
+ return result;
980
+ }
981
+
982
+ async update(${pkName}: ${pkType}, updateDto: Update${className}Dto): Promise<${className}ResponseDto> {
983
+ const [result] = await this.db
984
+ .update(${table.variableName})
985
+ .set(updateDto)
986
+ .where(eq(${table.variableName}.${pkName}, ${pkName}))
987
+ .returning();
988
+
989
+ if (!result) {
990
+ throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
991
+ }
992
+
993
+ return result;
994
+ }
995
+
996
+ async remove(${pkName}: ${pkType}): Promise<void> {
997
+ const result = await this.db
998
+ .delete(${table.variableName})
999
+ .where(eq(${table.variableName}.${pkName}, ${pkName}))
1000
+ .returning();
1001
+
1002
+ if (result.length === 0) {
1003
+ throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
1004
+ }
1005
+
1006
+ this.logger.log(\`Deleted ${className} with ${pkName} \${${pkName}}\`);
1007
+ }
1008
+ }
1009
+ `;
1010
+ return service;
1011
+ }
1012
+ function generateModule(table) {
1013
+ const className = toPascalCase(table.variableName);
1014
+ const filePath = toSnakeCase(table.variableName);
1015
+ const module = `
1016
+ import { Module } from '@nestjs/common';
1017
+ import { ${className}Controller } from './${filePath}.controller';
1018
+ import { ${className}Service } from './${filePath}.service';
1019
+
1020
+ @Module({
1021
+ controllers: [${className}Controller],
1022
+ providers: [${className}Service],
1023
+ })
1024
+ export class ${className}Module {}
1025
+ `;
1026
+ return module;
1027
+ }
1028
+
1029
+ // src/commands/db/gen-nest-resource/schema-parser.ts
1030
+ import { Project, Node } from "ts-morph";
1031
+ var DrizzleSchemaParser = class {
1032
+ constructor(projectOptions) {
1033
+ this.project = new Project(projectOptions);
1034
+ }
1035
+ parseSchemaFile(filePath) {
1036
+ const sourceFile = this.project.addSourceFileAtPath(filePath);
1037
+ const tables = [];
1038
+ const variableStatements = sourceFile.getVariableStatements();
1039
+ for (const statement of variableStatements) {
1040
+ const declarations = statement.getDeclarations();
1041
+ for (const declaration of declarations) {
1042
+ const initializer = declaration.getInitializer();
1043
+ if (initializer && Node.isCallExpression(initializer)) {
1044
+ const expression = initializer.getExpression();
1045
+ if (expression.getText() === "pgTable") {
1046
+ const tableInfo = this.parsePgTable(
1047
+ declaration.getName(),
1048
+ initializer
1049
+ );
1050
+ if (tableInfo) {
1051
+ tables.push(tableInfo);
1052
+ }
1053
+ }
1054
+ }
1055
+ }
1056
+ }
1057
+ return tables;
1058
+ }
1059
+ parsePgTable(variableName, callExpr) {
1060
+ const args = callExpr.getArguments();
1061
+ if (args.length < 2) {
1062
+ return null;
1063
+ }
1064
+ const tableName = args[0].getText().replace(/['"]/g, "");
1065
+ const fieldsArg = args[1];
1066
+ if (!Node.isObjectLiteralExpression(fieldsArg)) {
1067
+ return null;
1068
+ }
1069
+ const fields = [];
1070
+ const properties = fieldsArg.getProperties();
1071
+ for (const prop of properties) {
1072
+ if (Node.isPropertyAssignment(prop)) {
1073
+ const fieldName = prop.getName();
1074
+ const initializer = prop.getInitializer();
1075
+ const leadingComments = prop.getLeadingCommentRanges();
1076
+ let comment;
1077
+ if (leadingComments.length > 0) {
1078
+ comment = leadingComments.map((c) => c.getText()).join("\n").replace(/\/\//g, "").trim();
1079
+ }
1080
+ if (initializer && Node.isCallExpression(initializer)) {
1081
+ const fieldInfo = this.parseField(fieldName, initializer, comment);
1082
+ fields.push(fieldInfo);
1083
+ }
1084
+ }
1085
+ }
1086
+ return {
1087
+ tableName,
1088
+ variableName,
1089
+ fields
1090
+ };
1091
+ }
1092
+ parseField(fieldName, callExpr, comment) {
1093
+ const fieldInfo = {
1094
+ name: fieldName,
1095
+ columnName: fieldName,
1096
+ type: "",
1097
+ nullable: true,
1098
+ hasDefault: false,
1099
+ notNull: false,
1100
+ isPrimaryKey: false,
1101
+ isUnique: false,
1102
+ isArray: false,
1103
+ comment
1104
+ };
1105
+ this.parseBaseType(callExpr, fieldInfo);
1106
+ this.parseCallChain(callExpr, fieldInfo);
1107
+ return fieldInfo;
1108
+ }
1109
+ parseBaseType(callExpr, fieldInfo) {
1110
+ let current = callExpr;
1111
+ let baseCall = null;
1112
+ while (Node.isCallExpression(current)) {
1113
+ baseCall = current;
1114
+ const expression2 = current.getExpression();
1115
+ if (Node.isPropertyAccessExpression(expression2)) {
1116
+ current = expression2.getExpression();
1117
+ } else {
1118
+ break;
1119
+ }
1120
+ }
1121
+ if (!baseCall) {
1122
+ return;
1123
+ }
1124
+ const expression = baseCall.getExpression();
1125
+ let typeName = "";
1126
+ if (Node.isPropertyAccessExpression(expression)) {
1127
+ typeName = expression.getName();
1128
+ } else {
1129
+ typeName = expression.getText();
1130
+ }
1131
+ fieldInfo.type = typeName;
1132
+ const args = baseCall.getArguments();
1133
+ if (args.length > 0) {
1134
+ const firstArg = args[0];
1135
+ if (Node.isStringLiteral(firstArg)) {
1136
+ fieldInfo.columnName = firstArg.getLiteralText();
1137
+ } else if (Node.isObjectLiteralExpression(firstArg)) {
1138
+ this.parseTypeConfig(firstArg, fieldInfo);
1139
+ } else if (Node.isArrayLiteralExpression(firstArg)) {
1140
+ fieldInfo.enumValues = firstArg.getElements().map((el) => el.getText().replace(/['"]/g, ""));
1141
+ }
1142
+ }
1143
+ if (args.length > 1 && Node.isObjectLiteralExpression(args[1])) {
1144
+ this.parseTypeConfig(args[1], fieldInfo);
1145
+ }
1146
+ }
1147
+ parseTypeConfig(objLiteral, fieldInfo) {
1148
+ if (!Node.isObjectLiteralExpression(objLiteral)) {
1149
+ return;
1150
+ }
1151
+ const properties = objLiteral.getProperties();
1152
+ for (const prop of properties) {
1153
+ if (Node.isPropertyAssignment(prop)) {
1154
+ const propName = prop.getName();
1155
+ const value = prop.getInitializer()?.getText();
1156
+ switch (propName) {
1157
+ case "length":
1158
+ fieldInfo.length = value ? parseInt(value) : void 0;
1159
+ break;
1160
+ case "precision":
1161
+ fieldInfo.precision = value ? parseInt(value) : void 0;
1162
+ break;
1163
+ case "scale":
1164
+ fieldInfo.scale = value ? parseInt(value) : void 0;
1165
+ break;
1166
+ case "default":
1167
+ fieldInfo.hasDefault = true;
1168
+ fieldInfo.defaultValue = value;
1169
+ break;
1170
+ // 时间精度(用于 timestamp, time 等)
1171
+ case "withTimezone":
1172
+ fieldInfo.withTimezone = value === "true";
1173
+ break;
1174
+ case "mode":
1175
+ fieldInfo.mode = value?.replace(/['"]/g, "");
1176
+ break;
1177
+ default:
1178
+ throw new Error(`Unsupported property: ${propName}`);
1179
+ }
1180
+ }
1181
+ }
1182
+ }
1183
+ parseCallChain(callExpr, fieldInfo) {
1184
+ let current = callExpr;
1185
+ while (Node.isCallExpression(current)) {
1186
+ const expression = current.getExpression();
1187
+ if (Node.isPropertyAccessExpression(expression)) {
1188
+ const methodName = expression.getName();
1189
+ const args = current.getArguments();
1190
+ switch (methodName) {
1191
+ case "notNull":
1192
+ fieldInfo.notNull = true;
1193
+ fieldInfo.nullable = false;
1194
+ break;
1195
+ case "default":
1196
+ fieldInfo.hasDefault = true;
1197
+ if (args.length > 0) {
1198
+ fieldInfo.defaultValue = args[0].getText();
1199
+ }
1200
+ break;
1201
+ case "defaultRandom":
1202
+ fieldInfo.hasDefault = true;
1203
+ fieldInfo.defaultValue = "random";
1204
+ break;
1205
+ case "primaryKey":
1206
+ fieldInfo.isPrimaryKey = true;
1207
+ fieldInfo.notNull = true;
1208
+ fieldInfo.nullable = false;
1209
+ break;
1210
+ case "unique":
1211
+ fieldInfo.isUnique = true;
1212
+ break;
1213
+ case "array":
1214
+ fieldInfo.isArray = true;
1215
+ break;
1216
+ case "references":
1217
+ if (args.length > 0) {
1218
+ const refArg = args[0].getText();
1219
+ const match = refArg.match(/=>\s*(\w+)\.(\w+)/);
1220
+ if (match) {
1221
+ fieldInfo.references = {
1222
+ table: match[1],
1223
+ column: match[2]
1224
+ };
1225
+ }
1226
+ }
1227
+ break;
1228
+ default:
1229
+ throw new Error(`Unsupported method: ${methodName}`);
1230
+ }
1231
+ current = expression.getExpression();
1232
+ } else {
1233
+ break;
1234
+ }
1235
+ }
1236
+ }
1237
+ };
1238
+
1239
+ // src/commands/db/gen-nest-resource/index.ts
1240
+ import { join } from "path";
1241
+ import { mkdir, rm, writeFile } from "fs/promises";
1242
+ import { existsSync } from "fs";
1243
+ async function parseAndGenerateNestResourceTemplate(options) {
1244
+ const parser = new DrizzleSchemaParser({
1245
+ tsConfigFilePath: options.tsConfigFilePath
1246
+ });
1247
+ const tables = parser.parseSchemaFile(options.schemaFilePath);
1248
+ if (tables.length === 0) {
1249
+ console.warn("\u672A\u89E3\u6790\u5230\u4EFB\u4F55\u6570\u636E\u5E93\u8868\uFF0C\u65E0\u9700\u751F\u6210 Nest.js \u6A21\u5757\u6A21\u677F");
1250
+ return;
1251
+ }
1252
+ tables.sort((a, b) => b.variableName.length - a.variableName.length);
1253
+ const table = tables[0];
1254
+ console.info(`\u751F\u6210 Nest.js ${table.variableName} \u6A21\u5757`);
1255
+ const filePath = toSnakeCase(table.variableName);
1256
+ const moduleDir = join(options.moduleOutputDir, filePath);
1257
+ if (existsSync(moduleDir)) {
1258
+ console.info(`Nest.js \u6A21\u5757 ${filePath} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u751F\u6210\u4EE3\u7801`);
1259
+ return;
1260
+ }
1261
+ const dto = generateDTO(table);
1262
+ const controller = generateController(table);
1263
+ const service = generateService(table);
1264
+ const moduleFilePath = join(moduleDir, `${filePath}.module.ts`);
1265
+ const module = generateModule(table);
1266
+ try {
1267
+ await mkdir(moduleDir, { recursive: true });
1268
+ await mkdir(join(moduleDir, "dtos"), { recursive: true });
1269
+ await writeFile(join(moduleDir, "dtos", `${filePath}.dto.ts`), dto);
1270
+ await writeFile(join(moduleDir, `${filePath}.controller.ts`), controller);
1271
+ await writeFile(join(moduleDir, `${filePath}.service.ts`), service);
1272
+ await writeFile(moduleFilePath, module);
1273
+ } catch (err) {
1274
+ console.error(`\u751F\u6210 Nest.js ${filePath} \u6A21\u5757\u5931\u8D25: ${err.message}`);
1275
+ await rm(moduleDir, { recursive: true });
1276
+ }
1277
+ }
1278
+
1279
+ // src/commands/db/schema.handler.ts
125
1280
  var require2 = createRequire(import.meta.url);
126
1281
  async function run(options = {}) {
127
1282
  let exitCode = 0;
128
- const envPath2 = path.resolve(process.cwd(), ".env");
129
- if (fs.existsSync(envPath2)) {
1283
+ const envPath2 = path3.resolve(process.cwd(), ".env");
1284
+ if (fs3.existsSync(envPath2)) {
130
1285
  loadEnv({ path: envPath2 });
131
1286
  console.log("[gen-db-schema] \u2713 Loaded .env file");
132
1287
  }
@@ -136,17 +1291,17 @@ async function run(options = {}) {
136
1291
  process.exit(1);
137
1292
  }
138
1293
  const outputPath = options.output || process.env.DB_SCHEMA_OUTPUT || "server/database/schema.ts";
139
- const OUT_DIR = path.resolve(process.cwd(), "server/database/.introspect");
140
- const SCHEMA_FILE = path.resolve(process.cwd(), outputPath);
1294
+ const OUT_DIR = path3.resolve(process.cwd(), "server/database/.introspect");
1295
+ const SCHEMA_FILE = path3.resolve(process.cwd(), outputPath);
141
1296
  console.log("[gen-db-schema] Starting...");
142
1297
  const __filename = fileURLToPath(import.meta.url);
143
- const __dirname2 = path.dirname(__filename);
1298
+ const __dirname3 = path3.dirname(__filename);
144
1299
  const configPathCandidates = [
145
- path.resolve(__dirname2, "config/drizzle.config.js"),
146
- path.resolve(__dirname2, "../../config/drizzle.config.js"),
147
- path.resolve(__dirname2, "../../../dist/config/drizzle.config.js")
1300
+ path3.resolve(__dirname3, "config/drizzle.config.js"),
1301
+ path3.resolve(__dirname3, "../../config/drizzle.config.js"),
1302
+ path3.resolve(__dirname3, "../../../dist/config/drizzle.config.js")
148
1303
  ];
149
- const configPath = configPathCandidates.find((p) => fs.existsSync(p));
1304
+ const configPath = configPathCandidates.find((p) => fs3.existsSync(p));
150
1305
  console.log("[gen-db-schema] Using drizzle config from:", configPath ?? "(not found)");
151
1306
  if (!configPath) {
152
1307
  console.error("[gen-db-schema] Error: drizzle config not found in CLI package");
@@ -154,12 +1309,12 @@ async function run(options = {}) {
154
1309
  }
155
1310
  const resolveDrizzleKitBin = () => {
156
1311
  const entryPath = require2.resolve("drizzle-kit");
157
- let currentDir = path.dirname(entryPath);
1312
+ let currentDir = path3.dirname(entryPath);
158
1313
  let lastDir = null;
159
1314
  while (currentDir !== lastDir) {
160
- const pkgJsonPath = path.join(currentDir, "package.json");
161
- if (fs.existsSync(pkgJsonPath)) {
162
- const pkgJsonRaw = fs.readFileSync(pkgJsonPath, "utf8");
1315
+ const pkgJsonPath = path3.join(currentDir, "package.json");
1316
+ if (fs3.existsSync(pkgJsonPath)) {
1317
+ const pkgJsonRaw = fs3.readFileSync(pkgJsonPath, "utf8");
163
1318
  const pkgJson = JSON.parse(pkgJsonRaw);
164
1319
  if (pkgJson.name === "drizzle-kit") {
165
1320
  const binField = pkgJson.bin;
@@ -167,11 +1322,11 @@ async function run(options = {}) {
167
1322
  if (!binRelPath) {
168
1323
  throw new Error("Unable to resolve drizzle-kit binary from package.json");
169
1324
  }
170
- return path.resolve(currentDir, binRelPath);
1325
+ return path3.resolve(currentDir, binRelPath);
171
1326
  }
172
1327
  }
173
1328
  lastDir = currentDir;
174
- currentDir = path.dirname(currentDir);
1329
+ currentDir = path3.dirname(currentDir);
175
1330
  }
176
1331
  throw new Error("Unable to locate drizzle-kit package root");
177
1332
  };
@@ -193,29 +1348,27 @@ async function run(options = {}) {
193
1348
  if ((result.status ?? 0) !== 0) {
194
1349
  throw new Error(`drizzle-kit introspect failed with status ${result.status}`);
195
1350
  }
196
- const generatedSchema = path.join(OUT_DIR, "schema.ts");
197
- if (!fs.existsSync(generatedSchema)) {
1351
+ const generatedSchema = path3.join(OUT_DIR, "schema.ts");
1352
+ if (!fs3.existsSync(generatedSchema)) {
198
1353
  console.error("[gen-db-schema] schema.ts not generated");
199
1354
  throw new Error("drizzle-kit introspect failed to generate schema.ts");
200
1355
  }
201
- const { postprocessDrizzleSchema } = require2("./gen-dbschema");
202
1356
  const stats = postprocessDrizzleSchema(generatedSchema);
203
1357
  if (stats?.unmatchedUnknown?.length) {
204
1358
  console.warn("[gen-db-schema] Unmatched custom types detected:", stats.unmatchedUnknown);
205
1359
  }
206
1360
  console.log("[gen-db-schema] \u2713 Postprocessed schema");
207
- fs.mkdirSync(path.dirname(SCHEMA_FILE), { recursive: true });
208
- fs.copyFileSync(generatedSchema, SCHEMA_FILE);
1361
+ fs3.mkdirSync(path3.dirname(SCHEMA_FILE), { recursive: true });
1362
+ fs3.copyFileSync(generatedSchema, SCHEMA_FILE);
209
1363
  console.log(`[gen-db-schema] \u2713 Copied to ${outputPath}`);
210
1364
  try {
211
1365
  if (options.enableNestModuleGenerate) {
212
- const { parseAndGenerateNestResourceTemplate } = require2("./gen-nest-resource");
213
- const tsConfigFilePath = path.resolve(process.cwd(), "tsconfig.json");
1366
+ const tsConfigFilePath = path3.resolve(process.cwd(), "tsconfig.json");
214
1367
  const schemaFilePath = SCHEMA_FILE;
215
1368
  await parseAndGenerateNestResourceTemplate({
216
1369
  tsConfigFilePath,
217
1370
  schemaFilePath,
218
- moduleOutputDir: path.resolve(process.cwd(), "server/modules")
1371
+ moduleOutputDir: path3.resolve(process.cwd(), "server/modules")
219
1372
  });
220
1373
  console.log("[gen-db-schema] \u2713 Generate NestJS Module Boilerplate Successfully");
221
1374
  }
@@ -227,8 +1380,8 @@ async function run(options = {}) {
227
1380
  console.error("[gen-db-schema] Failed:", err instanceof Error ? err.message : String(err));
228
1381
  exitCode = 1;
229
1382
  } finally {
230
- if (fs.existsSync(OUT_DIR)) {
231
- fs.rmSync(OUT_DIR, { recursive: true, force: true });
1383
+ if (fs3.existsSync(OUT_DIR)) {
1384
+ fs3.rmSync(OUT_DIR, { recursive: true, force: true });
232
1385
  }
233
1386
  process.exit(exitCode);
234
1387
  }
@@ -246,8 +1399,8 @@ var genDbSchemaCommand = {
246
1399
  };
247
1400
 
248
1401
  // src/commands/sync/run.handler.ts
249
- import path3 from "path";
250
- import fs3 from "fs";
1402
+ import path5 from "path";
1403
+ import fs5 from "fs";
251
1404
  import { fileURLToPath as fileURLToPath2 } from "url";
252
1405
 
253
1406
  // src/config/sync.ts
@@ -311,23 +1464,23 @@ function genSyncConfig(perms = {}) {
311
1464
  }
312
1465
 
313
1466
  // src/utils/file-ops.ts
314
- import fs2 from "fs";
315
- import path2 from "path";
1467
+ import fs4 from "fs";
1468
+ import path4 from "path";
316
1469
  function removeLineFromFile(filePath, pattern) {
317
- if (!fs2.existsSync(filePath)) {
318
- console.log(`[fullstack-cli] \u25CB ${path2.basename(filePath)} (not found)`);
1470
+ if (!fs4.existsSync(filePath)) {
1471
+ console.log(`[fullstack-cli] \u25CB ${path4.basename(filePath)} (not found)`);
319
1472
  return false;
320
1473
  }
321
- const content = fs2.readFileSync(filePath, "utf-8");
1474
+ const content = fs4.readFileSync(filePath, "utf-8");
322
1475
  const lines = content.split("\n");
323
1476
  const trimmedPattern = pattern.trim();
324
1477
  const filteredLines = lines.filter((line) => line.trim() !== trimmedPattern);
325
1478
  if (filteredLines.length === lines.length) {
326
- console.log(`[fullstack-cli] \u25CB ${path2.basename(filePath)} (pattern not found: ${pattern})`);
1479
+ console.log(`[fullstack-cli] \u25CB ${path4.basename(filePath)} (pattern not found: ${pattern})`);
327
1480
  return false;
328
1481
  }
329
- fs2.writeFileSync(filePath, filteredLines.join("\n"));
330
- console.log(`[fullstack-cli] \u2713 ${path2.basename(filePath)} (removed: ${pattern})`);
1482
+ fs4.writeFileSync(filePath, filteredLines.join("\n"));
1483
+ console.log(`[fullstack-cli] \u2713 ${path4.basename(filePath)} (removed: ${pattern})`);
331
1484
  return true;
332
1485
  }
333
1486
 
@@ -335,14 +1488,14 @@ function removeLineFromFile(filePath, pattern) {
335
1488
  async function run2(options) {
336
1489
  const userProjectRoot = process.env.INIT_CWD || process.cwd();
337
1490
  const __filename = fileURLToPath2(import.meta.url);
338
- const __dirname2 = path3.dirname(__filename);
339
- const pluginRoot = path3.resolve(__dirname2, "..");
1491
+ const __dirname3 = path5.dirname(__filename);
1492
+ const pluginRoot = path5.resolve(__dirname3, "..");
340
1493
  if (userProjectRoot === pluginRoot) {
341
1494
  console.log("[fullstack-cli] Skip syncing (installing plugin itself)");
342
1495
  process.exit(0);
343
1496
  }
344
- const userPackageJson = path3.join(userProjectRoot, "package.json");
345
- if (!fs3.existsSync(userPackageJson)) {
1497
+ const userPackageJson = path5.join(userProjectRoot, "package.json");
1498
+ if (!fs5.existsSync(userPackageJson)) {
346
1499
  console.log("[fullstack-cli] Skip syncing (not a valid npm project)");
347
1500
  process.exit(0);
348
1501
  }
@@ -370,7 +1523,7 @@ async function run2(options) {
370
1523
  }
371
1524
  async function syncRule(rule, pluginRoot, userProjectRoot) {
372
1525
  if (rule.type === "delete-file" || rule.type === "delete-directory") {
373
- const destPath2 = path3.join(userProjectRoot, rule.to);
1526
+ const destPath2 = path5.join(userProjectRoot, rule.to);
374
1527
  if (rule.type === "delete-file") {
375
1528
  deleteFile(destPath2);
376
1529
  } else {
@@ -379,16 +1532,16 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
379
1532
  return;
380
1533
  }
381
1534
  if (rule.type === "remove-line") {
382
- const destPath2 = path3.join(userProjectRoot, rule.to);
1535
+ const destPath2 = path5.join(userProjectRoot, rule.to);
383
1536
  removeLineFromFile(destPath2, rule.pattern);
384
1537
  return;
385
1538
  }
386
1539
  if (!("from" in rule)) {
387
1540
  return;
388
1541
  }
389
- const srcPath = path3.join(pluginRoot, rule.from);
390
- const destPath = path3.join(userProjectRoot, rule.to);
391
- if (!fs3.existsSync(srcPath)) {
1542
+ const srcPath = path5.join(pluginRoot, rule.from);
1543
+ const destPath = path5.join(userProjectRoot, rule.to);
1544
+ if (!fs5.existsSync(srcPath)) {
392
1545
  console.warn(`[fullstack-cli] Source not found: ${rule.from}`);
393
1546
  return;
394
1547
  }
@@ -405,64 +1558,64 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
405
1558
  }
406
1559
  }
407
1560
  function syncFile(src, dest, overwrite = true) {
408
- const destDir = path3.dirname(dest);
409
- if (!fs3.existsSync(destDir)) {
410
- fs3.mkdirSync(destDir, { recursive: true });
1561
+ const destDir = path5.dirname(dest);
1562
+ if (!fs5.existsSync(destDir)) {
1563
+ fs5.mkdirSync(destDir, { recursive: true });
411
1564
  }
412
- if (fs3.existsSync(dest) && !overwrite) {
413
- console.log(`[fullstack-cli] \u25CB ${path3.basename(dest)} (skipped, already exists)`);
1565
+ if (fs5.existsSync(dest) && !overwrite) {
1566
+ console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, already exists)`);
414
1567
  return;
415
1568
  }
416
- fs3.copyFileSync(src, dest);
417
- console.log(`[fullstack-cli] \u2713 ${path3.basename(dest)}`);
1569
+ fs5.copyFileSync(src, dest);
1570
+ console.log(`[fullstack-cli] \u2713 ${path5.basename(dest)}`);
418
1571
  }
419
1572
  function syncDirectory(src, dest, overwrite = true) {
420
- if (!fs3.existsSync(dest)) {
421
- fs3.mkdirSync(dest, { recursive: true });
1573
+ if (!fs5.existsSync(dest)) {
1574
+ fs5.mkdirSync(dest, { recursive: true });
422
1575
  }
423
- const files = fs3.readdirSync(src);
1576
+ const files = fs5.readdirSync(src);
424
1577
  let count = 0;
425
1578
  files.forEach((file) => {
426
- const srcFile = path3.join(src, file);
427
- const destFile = path3.join(dest, file);
428
- const stats = fs3.statSync(srcFile);
1579
+ const srcFile = path5.join(src, file);
1580
+ const destFile = path5.join(dest, file);
1581
+ const stats = fs5.statSync(srcFile);
429
1582
  if (stats.isDirectory()) {
430
1583
  syncDirectory(srcFile, destFile, overwrite);
431
1584
  } else {
432
- if (overwrite || !fs3.existsSync(destFile)) {
433
- fs3.copyFileSync(srcFile, destFile);
434
- console.log(`[fullstack-cli] \u2713 ${path3.relative(dest, destFile)}`);
1585
+ if (overwrite || !fs5.existsSync(destFile)) {
1586
+ fs5.copyFileSync(srcFile, destFile);
1587
+ console.log(`[fullstack-cli] \u2713 ${path5.relative(dest, destFile)}`);
435
1588
  count++;
436
1589
  }
437
1590
  }
438
1591
  });
439
1592
  if (count > 0) {
440
- console.log(`[fullstack-cli] Synced ${count} files to ${path3.basename(dest)}/`);
1593
+ console.log(`[fullstack-cli] Synced ${count} files to ${path5.basename(dest)}/`);
441
1594
  }
442
1595
  }
443
1596
  function appendToFile(src, dest) {
444
- const content = fs3.readFileSync(src, "utf-8");
1597
+ const content = fs5.readFileSync(src, "utf-8");
445
1598
  let existingContent = "";
446
- if (fs3.existsSync(dest)) {
447
- existingContent = fs3.readFileSync(dest, "utf-8");
1599
+ if (fs5.existsSync(dest)) {
1600
+ existingContent = fs5.readFileSync(dest, "utf-8");
448
1601
  }
449
1602
  if (existingContent.includes(content.trim())) {
450
- console.log(`[fullstack-cli] \u25CB ${path3.basename(dest)} (already contains content)`);
1603
+ console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (already contains content)`);
451
1604
  return;
452
1605
  }
453
- fs3.appendFileSync(dest, content);
454
- console.log(`[fullstack-cli] \u2713 ${path3.basename(dest)} (appended)`);
1606
+ fs5.appendFileSync(dest, content);
1607
+ console.log(`[fullstack-cli] \u2713 ${path5.basename(dest)} (appended)`);
455
1608
  }
456
1609
  function setPermissions(permissions, projectRoot) {
457
1610
  for (const [pattern, mode] of Object.entries(permissions)) {
458
1611
  if (pattern === "**/*.sh") {
459
- const scriptsDir = path3.join(projectRoot, "scripts");
460
- if (fs3.existsSync(scriptsDir)) {
461
- const files = fs3.readdirSync(scriptsDir);
1612
+ const scriptsDir = path5.join(projectRoot, "scripts");
1613
+ if (fs5.existsSync(scriptsDir)) {
1614
+ const files = fs5.readdirSync(scriptsDir);
462
1615
  files.forEach((file) => {
463
1616
  if (file.endsWith(".sh")) {
464
- const filePath = path3.join(scriptsDir, file);
465
- fs3.chmodSync(filePath, mode);
1617
+ const filePath = path5.join(scriptsDir, file);
1618
+ fs5.chmodSync(filePath, mode);
466
1619
  }
467
1620
  });
468
1621
  }
@@ -470,19 +1623,19 @@ function setPermissions(permissions, projectRoot) {
470
1623
  }
471
1624
  }
472
1625
  function deleteFile(filePath) {
473
- if (fs3.existsSync(filePath)) {
474
- fs3.unlinkSync(filePath);
475
- console.log(`[fullstack-cli] \u2713 ${path3.basename(filePath)} (deleted)`);
1626
+ if (fs5.existsSync(filePath)) {
1627
+ fs5.unlinkSync(filePath);
1628
+ console.log(`[fullstack-cli] \u2713 ${path5.basename(filePath)} (deleted)`);
476
1629
  } else {
477
- console.log(`[fullstack-cli] \u25CB ${path3.basename(filePath)} (not found)`);
1630
+ console.log(`[fullstack-cli] \u25CB ${path5.basename(filePath)} (not found)`);
478
1631
  }
479
1632
  }
480
1633
  function deleteDirectory(dirPath) {
481
- if (fs3.existsSync(dirPath)) {
482
- fs3.rmSync(dirPath, { recursive: true });
483
- console.log(`[fullstack-cli] \u2713 ${path3.basename(dirPath)} (deleted)`);
1634
+ if (fs5.existsSync(dirPath)) {
1635
+ fs5.rmSync(dirPath, { recursive: true });
1636
+ console.log(`[fullstack-cli] \u2713 ${path5.basename(dirPath)} (deleted)`);
484
1637
  } else {
485
- console.log(`[fullstack-cli] \u25CB ${path3.basename(dirPath)} (not found)`);
1638
+ console.log(`[fullstack-cli] \u25CB ${path5.basename(dirPath)} (not found)`);
486
1639
  }
487
1640
  }
488
1641
 
@@ -498,8 +1651,8 @@ var syncCommand = {
498
1651
  };
499
1652
 
500
1653
  // src/commands/action-plugin/utils.ts
501
- import fs4 from "fs";
502
- import path4 from "path";
1654
+ import fs6 from "fs";
1655
+ import path6 from "path";
503
1656
  import { spawnSync as spawnSync2, execSync } from "child_process";
504
1657
  function parsePluginName(input) {
505
1658
  const match = input.match(/^(@[^/]+\/[^@]+)(?:@(.+))?$/);
@@ -517,18 +1670,18 @@ function getProjectRoot() {
517
1670
  return process.cwd();
518
1671
  }
519
1672
  function getPackageJsonPath() {
520
- return path4.join(getProjectRoot(), "package.json");
1673
+ return path6.join(getProjectRoot(), "package.json");
521
1674
  }
522
1675
  function getPluginPath(pluginName) {
523
- return path4.join(getProjectRoot(), "node_modules", pluginName);
1676
+ return path6.join(getProjectRoot(), "node_modules", pluginName);
524
1677
  }
525
1678
  function readPackageJson() {
526
1679
  const pkgPath = getPackageJsonPath();
527
- if (!fs4.existsSync(pkgPath)) {
1680
+ if (!fs6.existsSync(pkgPath)) {
528
1681
  throw new Error("package.json not found in current directory");
529
1682
  }
530
1683
  try {
531
- const content = fs4.readFileSync(pkgPath, "utf-8");
1684
+ const content = fs6.readFileSync(pkgPath, "utf-8");
532
1685
  return JSON.parse(content);
533
1686
  } catch {
534
1687
  throw new Error("Failed to parse package.json");
@@ -536,7 +1689,7 @@ function readPackageJson() {
536
1689
  }
537
1690
  function writePackageJson(pkg2) {
538
1691
  const pkgPath = getPackageJsonPath();
539
- fs4.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
1692
+ fs6.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
540
1693
  }
541
1694
  function readActionPlugins() {
542
1695
  const pkg2 = readPackageJson();
@@ -569,12 +1722,12 @@ function npmInstall(tgzPath) {
569
1722
  }
570
1723
  }
571
1724
  function getPackageVersion(pluginName) {
572
- const pkgJsonPath = path4.join(getPluginPath(pluginName), "package.json");
573
- if (!fs4.existsSync(pkgJsonPath)) {
1725
+ const pkgJsonPath = path6.join(getPluginPath(pluginName), "package.json");
1726
+ if (!fs6.existsSync(pkgJsonPath)) {
574
1727
  return null;
575
1728
  }
576
1729
  try {
577
- const content = fs4.readFileSync(pkgJsonPath, "utf-8");
1730
+ const content = fs6.readFileSync(pkgJsonPath, "utf-8");
578
1731
  const pkg2 = JSON.parse(content);
579
1732
  return pkg2.version || null;
580
1733
  } catch {
@@ -582,49 +1735,49 @@ function getPackageVersion(pluginName) {
582
1735
  }
583
1736
  }
584
1737
  function readPluginPackageJson(pluginPath) {
585
- const pkgJsonPath = path4.join(pluginPath, "package.json");
586
- if (!fs4.existsSync(pkgJsonPath)) {
1738
+ const pkgJsonPath = path6.join(pluginPath, "package.json");
1739
+ if (!fs6.existsSync(pkgJsonPath)) {
587
1740
  return null;
588
1741
  }
589
1742
  try {
590
- const content = fs4.readFileSync(pkgJsonPath, "utf-8");
1743
+ const content = fs6.readFileSync(pkgJsonPath, "utf-8");
591
1744
  return JSON.parse(content);
592
1745
  } catch {
593
1746
  return null;
594
1747
  }
595
1748
  }
596
1749
  function extractTgzToNodeModules(tgzPath, pluginName) {
597
- const nodeModulesPath = path4.join(getProjectRoot(), "node_modules");
598
- const targetDir = path4.join(nodeModulesPath, pluginName);
599
- const scopeDir = path4.dirname(targetDir);
600
- if (!fs4.existsSync(scopeDir)) {
601
- fs4.mkdirSync(scopeDir, { recursive: true });
1750
+ const nodeModulesPath = path6.join(getProjectRoot(), "node_modules");
1751
+ const targetDir = path6.join(nodeModulesPath, pluginName);
1752
+ const scopeDir = path6.dirname(targetDir);
1753
+ if (!fs6.existsSync(scopeDir)) {
1754
+ fs6.mkdirSync(scopeDir, { recursive: true });
602
1755
  }
603
- if (fs4.existsSync(targetDir)) {
604
- fs4.rmSync(targetDir, { recursive: true });
1756
+ if (fs6.existsSync(targetDir)) {
1757
+ fs6.rmSync(targetDir, { recursive: true });
605
1758
  }
606
- const tempDir = path4.join(nodeModulesPath, ".cache", "fullstack-cli", "extract-temp");
607
- if (fs4.existsSync(tempDir)) {
608
- fs4.rmSync(tempDir, { recursive: true });
1759
+ const tempDir = path6.join(nodeModulesPath, ".cache", "fullstack-cli", "extract-temp");
1760
+ if (fs6.existsSync(tempDir)) {
1761
+ fs6.rmSync(tempDir, { recursive: true });
609
1762
  }
610
- fs4.mkdirSync(tempDir, { recursive: true });
1763
+ fs6.mkdirSync(tempDir, { recursive: true });
611
1764
  try {
612
1765
  execSync(`tar -xzf "${tgzPath}" -C "${tempDir}"`, { stdio: "pipe" });
613
- const extractedDir = path4.join(tempDir, "package");
614
- if (fs4.existsSync(extractedDir)) {
615
- fs4.renameSync(extractedDir, targetDir);
1766
+ const extractedDir = path6.join(tempDir, "package");
1767
+ if (fs6.existsSync(extractedDir)) {
1768
+ fs6.renameSync(extractedDir, targetDir);
616
1769
  } else {
617
- const files = fs4.readdirSync(tempDir);
1770
+ const files = fs6.readdirSync(tempDir);
618
1771
  if (files.length === 1) {
619
- fs4.renameSync(path4.join(tempDir, files[0]), targetDir);
1772
+ fs6.renameSync(path6.join(tempDir, files[0]), targetDir);
620
1773
  } else {
621
1774
  throw new Error("Unexpected tgz structure");
622
1775
  }
623
1776
  }
624
1777
  return targetDir;
625
1778
  } finally {
626
- if (fs4.existsSync(tempDir)) {
627
- fs4.rmSync(tempDir, { recursive: true });
1779
+ if (fs6.existsSync(tempDir)) {
1780
+ fs6.rmSync(tempDir, { recursive: true });
628
1781
  }
629
1782
  }
630
1783
  }
@@ -633,10 +1786,10 @@ function checkMissingPeerDeps(peerDeps) {
633
1786
  return [];
634
1787
  }
635
1788
  const missing = [];
636
- const nodeModulesPath = path4.join(getProjectRoot(), "node_modules");
1789
+ const nodeModulesPath = path6.join(getProjectRoot(), "node_modules");
637
1790
  for (const [depName, _version] of Object.entries(peerDeps)) {
638
- const depPath = path4.join(nodeModulesPath, depName);
639
- if (!fs4.existsSync(depPath)) {
1791
+ const depPath = path6.join(nodeModulesPath, depName);
1792
+ if (!fs6.existsSync(depPath)) {
640
1793
  missing.push(depName);
641
1794
  }
642
1795
  }
@@ -660,16 +1813,16 @@ function installMissingDeps(deps) {
660
1813
  }
661
1814
  function removePluginDirectory(pluginName) {
662
1815
  const pluginPath = getPluginPath(pluginName);
663
- if (fs4.existsSync(pluginPath)) {
664
- fs4.rmSync(pluginPath, { recursive: true });
1816
+ if (fs6.existsSync(pluginPath)) {
1817
+ fs6.rmSync(pluginPath, { recursive: true });
665
1818
  console.log(`[action-plugin] Removed ${pluginName}`);
666
1819
  }
667
1820
  }
668
1821
 
669
1822
  // src/commands/action-plugin/api-client.ts
670
1823
  import { HttpClient as HttpClient2 } from "@lark-apaas/http-client";
671
- import fs5 from "fs";
672
- import path5 from "path";
1824
+ import fs7 from "fs";
1825
+ import path7 from "path";
673
1826
 
674
1827
  // src/utils/http-client.ts
675
1828
  import { HttpClient } from "@lark-apaas/http-client";
@@ -754,19 +1907,19 @@ async function downloadFromPublic(downloadURL) {
754
1907
  return Buffer.from(arrayBuffer);
755
1908
  }
756
1909
  function getPluginCacheDir() {
757
- return path5.join(process.cwd(), PLUGIN_CACHE_DIR);
1910
+ return path7.join(process.cwd(), PLUGIN_CACHE_DIR);
758
1911
  }
759
1912
  function ensureCacheDir() {
760
1913
  const cacheDir = getPluginCacheDir();
761
- if (!fs5.existsSync(cacheDir)) {
762
- fs5.mkdirSync(cacheDir, { recursive: true });
1914
+ if (!fs7.existsSync(cacheDir)) {
1915
+ fs7.mkdirSync(cacheDir, { recursive: true });
763
1916
  }
764
1917
  }
765
1918
  function getTempFilePath(pluginKey, version) {
766
1919
  ensureCacheDir();
767
1920
  const safeKey = pluginKey.replace(/[/@]/g, "_");
768
1921
  const filename = `${safeKey}-${version}.tgz`;
769
- return path5.join(getPluginCacheDir(), filename);
1922
+ return path7.join(getPluginCacheDir(), filename);
770
1923
  }
771
1924
  async function downloadPlugin(pluginKey, requestedVersion) {
772
1925
  console.log(`[action-plugin] Fetching plugin info for ${pluginKey}@${requestedVersion}...`);
@@ -782,7 +1935,7 @@ async function downloadPlugin(pluginKey, requestedVersion) {
782
1935
  tgzBuffer = await downloadFromPublic(pluginInfo.downloadURL);
783
1936
  }
784
1937
  const tgzPath = getTempFilePath(pluginKey, pluginInfo.version);
785
- fs5.writeFileSync(tgzPath, tgzBuffer);
1938
+ fs7.writeFileSync(tgzPath, tgzBuffer);
786
1939
  console.log(`[action-plugin] Downloaded to ${tgzPath} (${(tgzBuffer.length / 1024).toFixed(2)} KB)`);
787
1940
  return {
788
1941
  tgzPath,
@@ -792,8 +1945,8 @@ async function downloadPlugin(pluginKey, requestedVersion) {
792
1945
  }
793
1946
  function cleanupTempFile(tgzPath) {
794
1947
  try {
795
- if (fs5.existsSync(tgzPath)) {
796
- fs5.unlinkSync(tgzPath);
1948
+ if (fs7.existsSync(tgzPath)) {
1949
+ fs7.unlinkSync(tgzPath);
797
1950
  }
798
1951
  } catch {
799
1952
  }
@@ -1118,39 +2271,39 @@ var actionPluginCommandGroup = {
1118
2271
  };
1119
2272
 
1120
2273
  // src/commands/capability/utils.ts
1121
- import fs6 from "fs";
1122
- import path6 from "path";
2274
+ import fs8 from "fs";
2275
+ import path8 from "path";
1123
2276
  var CAPABILITIES_DIR = "server/capabilities";
1124
2277
  function getProjectRoot2() {
1125
2278
  return process.cwd();
1126
2279
  }
1127
2280
  function getCapabilitiesDir() {
1128
- return path6.join(getProjectRoot2(), CAPABILITIES_DIR);
2281
+ return path8.join(getProjectRoot2(), CAPABILITIES_DIR);
1129
2282
  }
1130
2283
  function getCapabilityPath(id) {
1131
- return path6.join(getCapabilitiesDir(), `${id}.json`);
2284
+ return path8.join(getCapabilitiesDir(), `${id}.json`);
1132
2285
  }
1133
2286
  function getPluginManifestPath(pluginKey) {
1134
- return path6.join(getProjectRoot2(), "node_modules", pluginKey, "manifest.json");
2287
+ return path8.join(getProjectRoot2(), "node_modules", pluginKey, "manifest.json");
1135
2288
  }
1136
2289
  function capabilitiesDirExists() {
1137
- return fs6.existsSync(getCapabilitiesDir());
2290
+ return fs8.existsSync(getCapabilitiesDir());
1138
2291
  }
1139
2292
  function listCapabilityIds() {
1140
2293
  const dir = getCapabilitiesDir();
1141
- if (!fs6.existsSync(dir)) {
2294
+ if (!fs8.existsSync(dir)) {
1142
2295
  return [];
1143
2296
  }
1144
- const files = fs6.readdirSync(dir);
2297
+ const files = fs8.readdirSync(dir);
1145
2298
  return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
1146
2299
  }
1147
2300
  function readCapability(id) {
1148
2301
  const filePath = getCapabilityPath(id);
1149
- if (!fs6.existsSync(filePath)) {
2302
+ if (!fs8.existsSync(filePath)) {
1150
2303
  throw new Error(`Capability not found: ${id}`);
1151
2304
  }
1152
2305
  try {
1153
- const content = fs6.readFileSync(filePath, "utf-8");
2306
+ const content = fs8.readFileSync(filePath, "utf-8");
1154
2307
  return JSON.parse(content);
1155
2308
  } catch (error) {
1156
2309
  if (error instanceof SyntaxError) {
@@ -1165,11 +2318,11 @@ function readAllCapabilities() {
1165
2318
  }
1166
2319
  function readPluginManifest(pluginKey) {
1167
2320
  const manifestPath = getPluginManifestPath(pluginKey);
1168
- if (!fs6.existsSync(manifestPath)) {
2321
+ if (!fs8.existsSync(manifestPath)) {
1169
2322
  throw new Error(`Plugin not installed: ${pluginKey} (manifest.json not found)`);
1170
2323
  }
1171
2324
  try {
1172
- const content = fs6.readFileSync(manifestPath, "utf-8");
2325
+ const content = fs8.readFileSync(manifestPath, "utf-8");
1173
2326
  return JSON.parse(content);
1174
2327
  } catch (error) {
1175
2328
  if (error instanceof SyntaxError) {
@@ -1343,58 +2496,58 @@ var capabilityCommandGroup = {
1343
2496
  };
1344
2497
 
1345
2498
  // src/commands/migration/version-manager.ts
1346
- import fs7 from "fs";
1347
- import path7 from "path";
2499
+ import fs9 from "fs";
2500
+ import path9 from "path";
1348
2501
  var PACKAGE_JSON = "package.json";
1349
2502
  var VERSION_FIELD = "migrationVersion";
1350
2503
  function getPackageJsonPath2() {
1351
- return path7.join(process.cwd(), PACKAGE_JSON);
2504
+ return path9.join(process.cwd(), PACKAGE_JSON);
1352
2505
  }
1353
2506
  function getCurrentVersion() {
1354
2507
  const pkgPath = getPackageJsonPath2();
1355
- if (!fs7.existsSync(pkgPath)) {
2508
+ if (!fs9.existsSync(pkgPath)) {
1356
2509
  throw new Error("package.json not found");
1357
2510
  }
1358
- const pkg2 = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
2511
+ const pkg2 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
1359
2512
  return pkg2[VERSION_FIELD] ?? 0;
1360
2513
  }
1361
2514
  function setCurrentVersion(version) {
1362
2515
  const pkgPath = getPackageJsonPath2();
1363
- const pkg2 = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
2516
+ const pkg2 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
1364
2517
  pkg2[VERSION_FIELD] = version;
1365
- fs7.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
2518
+ fs9.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
1366
2519
  }
1367
2520
 
1368
2521
  // src/commands/migration/versions/v001_capability/json-migrator/detector.ts
1369
- import fs9 from "fs";
1370
- import path9 from "path";
2522
+ import fs11 from "fs";
2523
+ import path11 from "path";
1371
2524
 
1372
2525
  // src/commands/migration/versions/v001_capability/utils.ts
1373
- import fs8 from "fs";
1374
- import path8 from "path";
2526
+ import fs10 from "fs";
2527
+ import path10 from "path";
1375
2528
  var CAPABILITIES_DIR2 = "server/capabilities";
1376
2529
  function getProjectRoot3() {
1377
2530
  return process.cwd();
1378
2531
  }
1379
2532
  function getCapabilitiesDir2() {
1380
- return path8.join(getProjectRoot3(), CAPABILITIES_DIR2);
2533
+ return path10.join(getProjectRoot3(), CAPABILITIES_DIR2);
1381
2534
  }
1382
2535
  function getPluginManifestPath2(pluginKey) {
1383
- return path8.join(getProjectRoot3(), "node_modules", pluginKey, "manifest.json");
2536
+ return path10.join(getProjectRoot3(), "node_modules", pluginKey, "manifest.json");
1384
2537
  }
1385
2538
 
1386
2539
  // src/commands/migration/versions/v001_capability/json-migrator/detector.ts
1387
2540
  function detectJsonMigration() {
1388
2541
  const capabilitiesDir = getCapabilitiesDir2();
1389
- const oldFilePath = path9.join(capabilitiesDir, "capabilities.json");
1390
- if (!fs9.existsSync(oldFilePath)) {
2542
+ const oldFilePath = path11.join(capabilitiesDir, "capabilities.json");
2543
+ if (!fs11.existsSync(oldFilePath)) {
1391
2544
  return {
1392
2545
  needsMigration: false,
1393
2546
  reason: "capabilities.json not found"
1394
2547
  };
1395
2548
  }
1396
2549
  try {
1397
- const content = fs9.readFileSync(oldFilePath, "utf-8");
2550
+ const content = fs11.readFileSync(oldFilePath, "utf-8");
1398
2551
  const parsed = JSON.parse(content);
1399
2552
  const capabilities = Array.isArray(parsed) ? parsed : [];
1400
2553
  return {
@@ -1434,8 +2587,8 @@ async function check(options) {
1434
2587
  }
1435
2588
 
1436
2589
  // src/commands/migration/versions/v001_capability/json-migrator/index.ts
1437
- import fs10 from "fs";
1438
- import path10 from "path";
2590
+ import fs12 from "fs";
2591
+ import path12 from "path";
1439
2592
 
1440
2593
  // src/commands/migration/versions/v001_capability/mapping.ts
1441
2594
  var DEFAULT_PLUGIN_VERSION = "1.0.0";
@@ -1665,18 +2818,18 @@ function transformCapabilities(oldCapabilities) {
1665
2818
  // src/commands/migration/versions/v001_capability/json-migrator/index.ts
1666
2819
  function loadExistingCapabilities() {
1667
2820
  const capabilitiesDir = getCapabilitiesDir2();
1668
- if (!fs10.existsSync(capabilitiesDir)) {
2821
+ if (!fs12.existsSync(capabilitiesDir)) {
1669
2822
  return [];
1670
2823
  }
1671
- const files = fs10.readdirSync(capabilitiesDir);
2824
+ const files = fs12.readdirSync(capabilitiesDir);
1672
2825
  const capabilities = [];
1673
2826
  for (const file of files) {
1674
2827
  if (file === "capabilities.json" || !file.endsWith(".json")) {
1675
2828
  continue;
1676
2829
  }
1677
2830
  try {
1678
- const filePath = path10.join(capabilitiesDir, file);
1679
- const content = fs10.readFileSync(filePath, "utf-8");
2831
+ const filePath = path12.join(capabilitiesDir, file);
2832
+ const content = fs12.readFileSync(filePath, "utf-8");
1680
2833
  const capability = JSON.parse(content);
1681
2834
  if (capability.id && capability.pluginKey) {
1682
2835
  capabilities.push(capability);
@@ -1734,9 +2887,9 @@ async function migrateJsonFiles(options) {
1734
2887
  }
1735
2888
  const capabilitiesDir = getCapabilitiesDir2();
1736
2889
  for (const cap of newCapabilities) {
1737
- const filePath = path10.join(capabilitiesDir, `${cap.id}.json`);
2890
+ const filePath = path12.join(capabilitiesDir, `${cap.id}.json`);
1738
2891
  const content = JSON.stringify(cap, null, 2);
1739
- fs10.writeFileSync(filePath, content, "utf-8");
2892
+ fs12.writeFileSync(filePath, content, "utf-8");
1740
2893
  console.log(` \u2713 Created: ${cap.id}.json`);
1741
2894
  }
1742
2895
  return {
@@ -1748,10 +2901,10 @@ async function migrateJsonFiles(options) {
1748
2901
  }
1749
2902
 
1750
2903
  // src/commands/migration/versions/v001_capability/plugin-installer/detector.ts
1751
- import fs11 from "fs";
2904
+ import fs13 from "fs";
1752
2905
  function isPluginInstalled2(pluginKey) {
1753
2906
  const manifestPath = getPluginManifestPath2(pluginKey);
1754
- return fs11.existsSync(manifestPath);
2907
+ return fs13.existsSync(manifestPath);
1755
2908
  }
1756
2909
  function detectPluginsToInstall(capabilities) {
1757
2910
  const pluginKeys = /* @__PURE__ */ new Set();
@@ -1827,12 +2980,12 @@ async function installPlugins(capabilities, options) {
1827
2980
  }
1828
2981
 
1829
2982
  // src/commands/migration/versions/v001_capability/code-migrator/index.ts
1830
- import path12 from "path";
1831
- import { Project } from "ts-morph";
2983
+ import path14 from "path";
2984
+ import { Project as Project2 } from "ts-morph";
1832
2985
 
1833
2986
  // src/commands/migration/versions/v001_capability/code-migrator/scanner.ts
1834
- import fs12 from "fs";
1835
- import path11 from "path";
2987
+ import fs14 from "fs";
2988
+ import path13 from "path";
1836
2989
  var EXCLUDED_DIRS = [
1837
2990
  "node_modules",
1838
2991
  "dist",
@@ -1847,9 +3000,9 @@ var EXCLUDED_PATTERNS = [
1847
3000
  /\.d\.ts$/
1848
3001
  ];
1849
3002
  function scanDirectory(dir, files = []) {
1850
- const entries = fs12.readdirSync(dir, { withFileTypes: true });
3003
+ const entries = fs14.readdirSync(dir, { withFileTypes: true });
1851
3004
  for (const entry of entries) {
1852
- const fullPath = path11.join(dir, entry.name);
3005
+ const fullPath = path13.join(dir, entry.name);
1853
3006
  if (entry.isDirectory()) {
1854
3007
  if (EXCLUDED_DIRS.includes(entry.name)) {
1855
3008
  continue;
@@ -1865,14 +3018,14 @@ function scanDirectory(dir, files = []) {
1865
3018
  return files;
1866
3019
  }
1867
3020
  function scanServerFiles() {
1868
- const serverDir = path11.join(getProjectRoot3(), "server");
1869
- if (!fs12.existsSync(serverDir)) {
3021
+ const serverDir = path13.join(getProjectRoot3(), "server");
3022
+ if (!fs14.existsSync(serverDir)) {
1870
3023
  return [];
1871
3024
  }
1872
3025
  return scanDirectory(serverDir);
1873
3026
  }
1874
3027
  function hasCapabilityImport(filePath) {
1875
- const content = fs12.readFileSync(filePath, "utf-8");
3028
+ const content = fs14.readFileSync(filePath, "utf-8");
1876
3029
  return /import\s+.*from\s+['"][^'"]*capabilities[^'"]*['"]/.test(content);
1877
3030
  }
1878
3031
  function scanFilesToMigrate() {
@@ -2247,7 +3400,7 @@ function analyzeFile(project, filePath, actionNameMap) {
2247
3400
  const callSites = analyzeCallSites(sourceFile, imports);
2248
3401
  const classInfo = analyzeClass(sourceFile);
2249
3402
  const { canMigrate, reason } = canAutoMigrate(classInfo);
2250
- const relativePath = path12.relative(getProjectRoot3(), filePath);
3403
+ const relativePath = path14.relative(getProjectRoot3(), filePath);
2251
3404
  return {
2252
3405
  filePath: relativePath,
2253
3406
  imports,
@@ -2258,7 +3411,7 @@ function analyzeFile(project, filePath, actionNameMap) {
2258
3411
  };
2259
3412
  }
2260
3413
  function migrateFile(project, analysis, dryRun) {
2261
- const absolutePath = path12.join(getProjectRoot3(), analysis.filePath);
3414
+ const absolutePath = path14.join(getProjectRoot3(), analysis.filePath);
2262
3415
  if (!analysis.canAutoMigrate) {
2263
3416
  return {
2264
3417
  filePath: analysis.filePath,
@@ -2318,7 +3471,7 @@ async function migrateCode(options, capabilities) {
2318
3471
  console.log(" No files need code migration.\n");
2319
3472
  return result;
2320
3473
  }
2321
- const project = new Project({
3474
+ const project = new Project2({
2322
3475
  skipAddingFilesFromTsConfig: true,
2323
3476
  compilerOptions: {
2324
3477
  allowJs: true
@@ -2361,17 +3514,17 @@ function getSuggestion(analysis) {
2361
3514
  }
2362
3515
 
2363
3516
  // src/commands/migration/versions/v001_capability/cleanup.ts
2364
- import fs13 from "fs";
2365
- import path13 from "path";
3517
+ import fs15 from "fs";
3518
+ import path15 from "path";
2366
3519
  function cleanupOldFiles(capabilities, dryRun) {
2367
3520
  const deletedFiles = [];
2368
3521
  const errors = [];
2369
3522
  const capabilitiesDir = getCapabilitiesDir2();
2370
- const oldJsonPath = path13.join(capabilitiesDir, "capabilities.json");
2371
- if (fs13.existsSync(oldJsonPath)) {
3523
+ const oldJsonPath = path15.join(capabilitiesDir, "capabilities.json");
3524
+ if (fs15.existsSync(oldJsonPath)) {
2372
3525
  try {
2373
3526
  if (!dryRun) {
2374
- fs13.unlinkSync(oldJsonPath);
3527
+ fs15.unlinkSync(oldJsonPath);
2375
3528
  }
2376
3529
  deletedFiles.push("capabilities.json");
2377
3530
  } catch (error) {
@@ -2379,11 +3532,11 @@ function cleanupOldFiles(capabilities, dryRun) {
2379
3532
  }
2380
3533
  }
2381
3534
  for (const cap of capabilities) {
2382
- const tsFilePath = path13.join(capabilitiesDir, `${cap.id}.ts`);
2383
- if (fs13.existsSync(tsFilePath)) {
3535
+ const tsFilePath = path15.join(capabilitiesDir, `${cap.id}.ts`);
3536
+ if (fs15.existsSync(tsFilePath)) {
2384
3537
  try {
2385
3538
  if (!dryRun) {
2386
- fs13.unlinkSync(tsFilePath);
3539
+ fs15.unlinkSync(tsFilePath);
2387
3540
  }
2388
3541
  deletedFiles.push(`${cap.id}.ts`);
2389
3542
  } catch (error) {
@@ -2399,8 +3552,8 @@ function cleanupOldFiles(capabilities, dryRun) {
2399
3552
  }
2400
3553
 
2401
3554
  // src/commands/migration/versions/v001_capability/report-generator.ts
2402
- import fs14 from "fs";
2403
- import path14 from "path";
3555
+ import fs16 from "fs";
3556
+ import path16 from "path";
2404
3557
  var REPORT_FILE = "capability-migration-report.md";
2405
3558
  function printSummary(result) {
2406
3559
  const { jsonMigration, pluginInstallation, codeMigration, cleanup } = result;
@@ -2563,15 +3716,15 @@ async function generateReport(result) {
2563
3716
  }
2564
3717
  lines.push("");
2565
3718
  const logDir = process.env.LOG_DIR || "logs";
2566
- if (!fs14.existsSync(logDir)) {
3719
+ if (!fs16.existsSync(logDir)) {
2567
3720
  return;
2568
3721
  }
2569
- const reportDir = path14.join(logDir, "migration");
2570
- if (!fs14.existsSync(reportDir)) {
2571
- fs14.mkdirSync(reportDir, { recursive: true });
3722
+ const reportDir = path16.join(logDir, "migration");
3723
+ if (!fs16.existsSync(reportDir)) {
3724
+ fs16.mkdirSync(reportDir, { recursive: true });
2572
3725
  }
2573
- const reportPath = path14.join(reportDir, REPORT_FILE);
2574
- fs14.writeFileSync(reportPath, lines.join("\n"), "utf-8");
3726
+ const reportPath = path16.join(reportDir, REPORT_FILE);
3727
+ fs16.writeFileSync(reportPath, lines.join("\n"), "utf-8");
2575
3728
  console.log(`\u{1F4C4} Report generated: ${reportPath}`);
2576
3729
  }
2577
3730
 
@@ -3103,10 +4256,10 @@ var migrationCommand = {
3103
4256
  };
3104
4257
 
3105
4258
  // src/commands/read-logs/index.ts
3106
- import path15 from "path";
4259
+ import path17 from "path";
3107
4260
 
3108
4261
  // src/commands/read-logs/std-utils.ts
3109
- import fs15 from "fs";
4262
+ import fs17 from "fs";
3110
4263
  function formatStdPrefixTime(localTime) {
3111
4264
  const match = localTime.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
3112
4265
  if (!match) return localTime;
@@ -3136,11 +4289,11 @@ function stripPrefixFromStdLine(line) {
3136
4289
  return `[${time}] ${content}`;
3137
4290
  }
3138
4291
  function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarker) {
3139
- const stat = fs15.statSync(filePath);
4292
+ const stat = fs17.statSync(filePath);
3140
4293
  if (stat.size === 0) {
3141
4294
  return { lines: [], markerFound: false, totalLinesCount: 0 };
3142
4295
  }
3143
- const fd = fs15.openSync(filePath, "r");
4296
+ const fd = fs17.openSync(filePath, "r");
3144
4297
  const chunkSize = 64 * 1024;
3145
4298
  let position = stat.size;
3146
4299
  let remainder = "";
@@ -3154,7 +4307,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
3154
4307
  const length = Math.min(chunkSize, position);
3155
4308
  position -= length;
3156
4309
  const buffer = Buffer.alloc(length);
3157
- fs15.readSync(fd, buffer, 0, length, position);
4310
+ fs17.readSync(fd, buffer, 0, length, position);
3158
4311
  let chunk = buffer.toString("utf8");
3159
4312
  if (remainder) {
3160
4313
  chunk += remainder;
@@ -3196,7 +4349,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
3196
4349
  }
3197
4350
  }
3198
4351
  } finally {
3199
- fs15.closeSync(fd);
4352
+ fs17.closeSync(fd);
3200
4353
  }
3201
4354
  return { lines: collected.reverse(), markerFound, totalLinesCount };
3202
4355
  }
@@ -3217,21 +4370,21 @@ function readServerStdSegment(filePath, maxLines, offset) {
3217
4370
  }
3218
4371
 
3219
4372
  // src/commands/read-logs/tail.ts
3220
- import fs16 from "fs";
4373
+ import fs18 from "fs";
3221
4374
  function fileExists(filePath) {
3222
4375
  try {
3223
- fs16.accessSync(filePath, fs16.constants.F_OK | fs16.constants.R_OK);
4376
+ fs18.accessSync(filePath, fs18.constants.F_OK | fs18.constants.R_OK);
3224
4377
  return true;
3225
4378
  } catch {
3226
4379
  return false;
3227
4380
  }
3228
4381
  }
3229
4382
  function readFileTailLines(filePath, maxLines) {
3230
- const stat = fs16.statSync(filePath);
4383
+ const stat = fs18.statSync(filePath);
3231
4384
  if (stat.size === 0) {
3232
4385
  return [];
3233
4386
  }
3234
- const fd = fs16.openSync(filePath, "r");
4387
+ const fd = fs18.openSync(filePath, "r");
3235
4388
  const chunkSize = 64 * 1024;
3236
4389
  const chunks = [];
3237
4390
  let position = stat.size;
@@ -3241,13 +4394,13 @@ function readFileTailLines(filePath, maxLines) {
3241
4394
  const length = Math.min(chunkSize, position);
3242
4395
  position -= length;
3243
4396
  const buffer = Buffer.alloc(length);
3244
- fs16.readSync(fd, buffer, 0, length, position);
4397
+ fs18.readSync(fd, buffer, 0, length, position);
3245
4398
  chunks.unshift(buffer.toString("utf8"));
3246
4399
  const chunkLines = buffer.toString("utf8").split("\n").length - 1;
3247
4400
  collectedLines += chunkLines;
3248
4401
  }
3249
4402
  } finally {
3250
- fs16.closeSync(fd);
4403
+ fs18.closeSync(fd);
3251
4404
  }
3252
4405
  const content = chunks.join("");
3253
4406
  const allLines = content.split("\n");
@@ -3263,11 +4416,11 @@ function readFileTailLines(filePath, maxLines) {
3263
4416
  return allLines.slice(allLines.length - maxLines);
3264
4417
  }
3265
4418
  function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
3266
- const stat = fs16.statSync(filePath);
4419
+ const stat = fs18.statSync(filePath);
3267
4420
  if (stat.size === 0) {
3268
4421
  return { lines: [], totalLinesCount: 0 };
3269
4422
  }
3270
- const fd = fs16.openSync(filePath, "r");
4423
+ const fd = fs18.openSync(filePath, "r");
3271
4424
  const chunkSize = 64 * 1024;
3272
4425
  let position = stat.size;
3273
4426
  let remainder = "";
@@ -3279,7 +4432,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
3279
4432
  const length = Math.min(chunkSize, position);
3280
4433
  position -= length;
3281
4434
  const buffer = Buffer.alloc(length);
3282
- fs16.readSync(fd, buffer, 0, length, position);
4435
+ fs18.readSync(fd, buffer, 0, length, position);
3283
4436
  let chunk = buffer.toString("utf8");
3284
4437
  if (remainder) {
3285
4438
  chunk += remainder;
@@ -3310,7 +4463,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
3310
4463
  }
3311
4464
  }
3312
4465
  } finally {
3313
- fs16.closeSync(fd);
4466
+ fs18.closeSync(fd);
3314
4467
  }
3315
4468
  return { lines: collected.reverse(), totalLinesCount };
3316
4469
  }
@@ -3412,7 +4565,7 @@ function extractClientStdSegment(lines, maxLines, offset) {
3412
4565
  }
3413
4566
 
3414
4567
  // src/commands/read-logs/json-lines.ts
3415
- import fs17 from "fs";
4568
+ import fs19 from "fs";
3416
4569
  function normalizePid(value) {
3417
4570
  if (typeof value === "number") {
3418
4571
  return String(value);
@@ -3463,11 +4616,11 @@ function buildWantedLevelSet(levels) {
3463
4616
  return set.size > 0 ? set : null;
3464
4617
  }
3465
4618
  function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
3466
- const stat = fs17.statSync(filePath);
4619
+ const stat = fs19.statSync(filePath);
3467
4620
  if (stat.size === 0) {
3468
4621
  return { lines: [], totalLinesCount: 0 };
3469
4622
  }
3470
- const fd = fs17.openSync(filePath, "r");
4623
+ const fd = fs19.openSync(filePath, "r");
3471
4624
  const chunkSize = 64 * 1024;
3472
4625
  let position = stat.size;
3473
4626
  let remainder = "";
@@ -3482,7 +4635,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
3482
4635
  const length = Math.min(chunkSize, position);
3483
4636
  position -= length;
3484
4637
  const buffer = Buffer.alloc(length);
3485
- fs17.readSync(fd, buffer, 0, length, position);
4638
+ fs19.readSync(fd, buffer, 0, length, position);
3486
4639
  let chunk = buffer.toString("utf8");
3487
4640
  if (remainder) {
3488
4641
  chunk += remainder;
@@ -3544,7 +4697,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
3544
4697
  }
3545
4698
  }
3546
4699
  } finally {
3547
- fs17.closeSync(fd);
4700
+ fs19.closeSync(fd);
3548
4701
  }
3549
4702
  return { lines: collected.reverse(), totalLinesCount };
3550
4703
  }
@@ -3587,11 +4740,11 @@ function extractTraceId(obj) {
3587
4740
  function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
3588
4741
  const wanted = traceId.trim();
3589
4742
  if (!wanted) return { lines: [], totalLinesCount: 0 };
3590
- const stat = fs17.statSync(filePath);
4743
+ const stat = fs19.statSync(filePath);
3591
4744
  if (stat.size === 0) {
3592
4745
  return { lines: [], totalLinesCount: 0 };
3593
4746
  }
3594
- const fd = fs17.openSync(filePath, "r");
4747
+ const fd = fs19.openSync(filePath, "r");
3595
4748
  const chunkSize = 64 * 1024;
3596
4749
  let position = stat.size;
3597
4750
  let remainder = "";
@@ -3604,7 +4757,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
3604
4757
  const length = Math.min(chunkSize, position);
3605
4758
  position -= length;
3606
4759
  const buffer = Buffer.alloc(length);
3607
- fs17.readSync(fd, buffer, 0, length, position);
4760
+ fs19.readSync(fd, buffer, 0, length, position);
3608
4761
  let chunk = buffer.toString("utf8");
3609
4762
  if (remainder) {
3610
4763
  chunk += remainder;
@@ -3657,7 +4810,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
3657
4810
  }
3658
4811
  }
3659
4812
  } finally {
3660
- fs17.closeSync(fd);
4813
+ fs19.closeSync(fd);
3661
4814
  }
3662
4815
  return { lines: collected.reverse(), totalLinesCount };
3663
4816
  }
@@ -3666,11 +4819,11 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
3666
4819
  if (!wantedLevelSet) {
3667
4820
  return { lines: [], totalLinesCount: 0 };
3668
4821
  }
3669
- const stat = fs17.statSync(filePath);
4822
+ const stat = fs19.statSync(filePath);
3670
4823
  if (stat.size === 0) {
3671
4824
  return { lines: [], totalLinesCount: 0 };
3672
4825
  }
3673
- const fd = fs17.openSync(filePath, "r");
4826
+ const fd = fs19.openSync(filePath, "r");
3674
4827
  const chunkSize = 64 * 1024;
3675
4828
  let position = stat.size;
3676
4829
  let remainder = "";
@@ -3682,7 +4835,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
3682
4835
  const length = Math.min(chunkSize, position);
3683
4836
  position -= length;
3684
4837
  const buffer = Buffer.alloc(length);
3685
- fs17.readSync(fd, buffer, 0, length, position);
4838
+ fs19.readSync(fd, buffer, 0, length, position);
3686
4839
  let chunk = buffer.toString("utf8");
3687
4840
  if (remainder) {
3688
4841
  chunk += remainder;
@@ -3729,7 +4882,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
3729
4882
  }
3730
4883
  }
3731
4884
  } finally {
3732
- fs17.closeSync(fd);
4885
+ fs19.closeSync(fd);
3733
4886
  }
3734
4887
  return { lines: collected.reverse(), totalLinesCount };
3735
4888
  }
@@ -3860,21 +5013,21 @@ async function readLogsJsonResult(options) {
3860
5013
  };
3861
5014
  }
3862
5015
  function resolveLogFilePath(logDir, type) {
3863
- const base = path15.isAbsolute(logDir) ? logDir : path15.join(process.cwd(), logDir);
5016
+ const base = path17.isAbsolute(logDir) ? logDir : path17.join(process.cwd(), logDir);
3864
5017
  if (type === "server") {
3865
- return path15.join(base, "server.log");
5018
+ return path17.join(base, "server.log");
3866
5019
  }
3867
5020
  if (type === "trace") {
3868
- return path15.join(base, "trace.log");
5021
+ return path17.join(base, "trace.log");
3869
5022
  }
3870
5023
  if (type === "server-std") {
3871
- return path15.join(base, "server.std.log");
5024
+ return path17.join(base, "server.std.log");
3872
5025
  }
3873
5026
  if (type === "client-std") {
3874
- return path15.join(base, "client.std.log");
5027
+ return path17.join(base, "client.std.log");
3875
5028
  }
3876
5029
  if (type === "browser") {
3877
- return path15.join(base, "browser.log");
5030
+ return path17.join(base, "browser.log");
3878
5031
  }
3879
5032
  throw new Error(`Unsupported log type: ${type}`);
3880
5033
  }
@@ -3949,12 +5102,12 @@ var commands = [
3949
5102
  ];
3950
5103
 
3951
5104
  // src/index.ts
3952
- var envPath = path16.join(process.cwd(), ".env");
3953
- if (fs18.existsSync(envPath)) {
5105
+ var envPath = path18.join(process.cwd(), ".env");
5106
+ if (fs20.existsSync(envPath)) {
3954
5107
  dotenvConfig({ path: envPath });
3955
5108
  }
3956
- var __dirname = path16.dirname(fileURLToPath3(import.meta.url));
3957
- var pkg = JSON.parse(fs18.readFileSync(path16.join(__dirname, "../package.json"), "utf-8"));
5109
+ var __dirname2 = path18.dirname(fileURLToPath3(import.meta.url));
5110
+ var pkg = JSON.parse(fs20.readFileSync(path18.join(__dirname2, "../package.json"), "utf-8"));
3958
5111
  var cli = new FullstackCLI(pkg.version);
3959
5112
  cli.useAll(commands);
3960
5113
  cli.run();