@lark-apaas/fullstack-cli 1.1.13-alpha.0 → 1.1.13-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import { fileURLToPath as
|
|
2
|
+
import fs20 from "fs";
|
|
3
|
+
import path17 from "path";
|
|
4
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
5
5
|
import { config as dotenvConfig } from "dotenv";
|
|
6
6
|
|
|
7
7
|
// src/cli.ts
|
|
@@ -116,17 +116,1169 @@ Command "${ctx.commandName}" completed in ${elapsed}ms`);
|
|
|
116
116
|
};
|
|
117
117
|
|
|
118
118
|
// src/commands/db/schema.handler.ts
|
|
119
|
-
import
|
|
120
|
-
import
|
|
121
|
-
import { fileURLToPath } from "url";
|
|
119
|
+
import path2 from "path";
|
|
120
|
+
import fs3 from "fs";
|
|
121
|
+
import { fileURLToPath as fileURLToPath2 } 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 path 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 { fileURLToPath } from "url";
|
|
302
|
+
function tweakImports(source) {
|
|
303
|
+
const importRegex = /import \{([^}]*)\} from "drizzle-orm\/pg-core";?/;
|
|
304
|
+
const match = source.match(importRegex);
|
|
305
|
+
if (!match) {
|
|
306
|
+
return source;
|
|
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
|
+
const text = source.replace(/import \{[^}]*\} from ["']\.\/types["'];?\n*/g, "");
|
|
337
|
+
const templatePath = resolveTemplateTypesPath();
|
|
338
|
+
if (!templatePath) {
|
|
339
|
+
console.warn("[postprocess-drizzle-schema] Template types file not found.");
|
|
340
|
+
return text;
|
|
341
|
+
}
|
|
342
|
+
return inlineFromTemplateContent(text, fs.readFileSync(templatePath, "utf8"));
|
|
343
|
+
}
|
|
344
|
+
function inlineFromTemplateContent(text, templateContent) {
|
|
345
|
+
const typeDefinitions = templateContent.replace(/^import\s+.*;\r?\n*/gm, "").trim();
|
|
346
|
+
const needsSql = typeDefinitions.includes("sql`") && !text.includes("from 'drizzle-orm'") && !text.includes('from "drizzle-orm"');
|
|
347
|
+
const needsCustomType = typeDefinitions.includes("customType<") && !text.includes("customType");
|
|
348
|
+
if (needsCustomType) {
|
|
349
|
+
text = ensureImportIdentifier(text, "drizzle-orm/pg-core", "customType");
|
|
350
|
+
}
|
|
351
|
+
if (needsSql && !text.includes("from 'drizzle-orm'") && !text.includes('from "drizzle-orm"')) {
|
|
352
|
+
const importMatch = text.match(/^import [\s\S]*?from ["']drizzle-orm\/pg-core["'];?\n/m);
|
|
353
|
+
if (importMatch) {
|
|
354
|
+
const insertPoint = text.indexOf(importMatch[0]) + importMatch[0].length;
|
|
355
|
+
text = text.slice(0, insertPoint) + "import { sql } from 'drizzle-orm';\n" + text.slice(insertPoint);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const headerPrefix = `${HEADER_COMMENT}
|
|
359
|
+
`;
|
|
360
|
+
let insertionPoint = 0;
|
|
361
|
+
if (text.startsWith(headerPrefix)) {
|
|
362
|
+
insertionPoint = headerPrefix.length;
|
|
363
|
+
}
|
|
364
|
+
const importSectionMatch = text.slice(insertionPoint).match(/^(?:import [^\n]+\n)+/);
|
|
365
|
+
if (importSectionMatch) {
|
|
366
|
+
insertionPoint += importSectionMatch[0].length;
|
|
367
|
+
}
|
|
368
|
+
const typeBlock = `
|
|
369
|
+
${typeDefinitions}
|
|
370
|
+
|
|
371
|
+
`;
|
|
372
|
+
return text.slice(0, insertionPoint) + typeBlock + text.slice(insertionPoint);
|
|
373
|
+
}
|
|
374
|
+
function ensureImportIdentifier(source, packageName, identifier) {
|
|
375
|
+
const escapedPackage = packageName.replace(/\//g, "\\/");
|
|
376
|
+
const importRegex = new RegExp(`import \\{([^}]*)\\} from ["']${escapedPackage}["'];?`);
|
|
377
|
+
const match = source.match(importRegex);
|
|
378
|
+
if (!match) {
|
|
379
|
+
return source;
|
|
380
|
+
}
|
|
381
|
+
const identifiers = match[1].split(",").map((id) => id.trim()).filter(Boolean);
|
|
382
|
+
if (identifiers.includes(identifier)) {
|
|
383
|
+
return source;
|
|
384
|
+
}
|
|
385
|
+
identifiers.push(identifier);
|
|
386
|
+
const unique = Array.from(new Set(identifiers));
|
|
387
|
+
const replacement = `import { ${unique.join(", ")} } from "${packageName}"`;
|
|
388
|
+
return source.replace(importRegex, replacement);
|
|
389
|
+
}
|
|
390
|
+
function resolveTemplateTypesPath() {
|
|
391
|
+
const candidates = [
|
|
392
|
+
new URL("../template/types.ts", import.meta.url),
|
|
393
|
+
new URL("./gen-dbschema-template/types.ts", import.meta.url)
|
|
394
|
+
];
|
|
395
|
+
for (const url of candidates) {
|
|
396
|
+
const p = fileURLToPath(url);
|
|
397
|
+
if (fs.existsSync(p)) {
|
|
398
|
+
return p;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return void 0;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// src/commands/db/gen-dbschema/helper/system-fields.ts
|
|
405
|
+
function addSystemFieldComments(source) {
|
|
406
|
+
const commentMap = {
|
|
407
|
+
"_created_at": "Creation time",
|
|
408
|
+
"_created_by": "Creator",
|
|
409
|
+
"_updated_at": "Update time",
|
|
410
|
+
"_updated_by": "Updater"
|
|
411
|
+
};
|
|
412
|
+
const lines = source.split("\n");
|
|
413
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
414
|
+
const line = lines[i];
|
|
415
|
+
const entry = Object.entries(commentMap).find(([key]) => line.includes(`"${key}"`));
|
|
416
|
+
if (!entry) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
const [, description] = entry;
|
|
420
|
+
const previousLine = lines[i - 1]?.trim() ?? "";
|
|
421
|
+
if (previousLine.startsWith("//") && previousLine.includes("System field")) {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
const indentMatch = line.match(/^\s*/);
|
|
425
|
+
const indent = indentMatch ? indentMatch[0] : "";
|
|
426
|
+
const comment = `${indent}// System field: ${description} (auto-filled, do not modify)`;
|
|
427
|
+
lines.splice(i, 0, comment);
|
|
428
|
+
i += 1;
|
|
429
|
+
}
|
|
430
|
+
return lines.join("\n");
|
|
431
|
+
}
|
|
432
|
+
function removeConflictingSystemFields(source) {
|
|
433
|
+
const systemFieldMap = {
|
|
434
|
+
"_created_at": "created_at",
|
|
435
|
+
"_created_by": "created_by",
|
|
436
|
+
"_updated_at": "updated_at",
|
|
437
|
+
"_updated_by": "updated_by"
|
|
438
|
+
};
|
|
439
|
+
const lines = source.split("\n");
|
|
440
|
+
const result = [];
|
|
441
|
+
let inTable = false;
|
|
442
|
+
let tableStartLine = -1;
|
|
443
|
+
const tableBusinessFields = /* @__PURE__ */ new Set();
|
|
444
|
+
let bracketDepth = 0;
|
|
445
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
446
|
+
const line = lines[i];
|
|
447
|
+
if (!inTable && /=\s*(pgTable|pgView|pgMaterializedView)\s*\(/.test(line)) {
|
|
448
|
+
inTable = true;
|
|
449
|
+
tableStartLine = result.length;
|
|
450
|
+
tableBusinessFields.clear();
|
|
451
|
+
bracketDepth = 0;
|
|
452
|
+
}
|
|
453
|
+
if (inTable) {
|
|
454
|
+
for (const char of line) {
|
|
455
|
+
if (char === "{") bracketDepth++;
|
|
456
|
+
if (char === "}") bracketDepth--;
|
|
457
|
+
}
|
|
458
|
+
for (const businessField of Object.values(systemFieldMap)) {
|
|
459
|
+
if (line.includes(`"${businessField}"`) || line.includes(`'${businessField}'`)) {
|
|
460
|
+
tableBusinessFields.add(businessField);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (bracketDepth === 0 && line.includes(");")) {
|
|
464
|
+
inTable = false;
|
|
465
|
+
const tableEndLine = result.length;
|
|
466
|
+
for (let j = tableStartLine; j <= tableEndLine; j++) {
|
|
467
|
+
const tableLine = result[j] || "";
|
|
468
|
+
let shouldRemove = false;
|
|
469
|
+
for (const [systemField, businessField] of Object.entries(systemFieldMap)) {
|
|
470
|
+
if (tableBusinessFields.has(businessField)) {
|
|
471
|
+
if (tableLine.includes(`"${systemField}"`) || tableLine.includes(`'${systemField}'`)) {
|
|
472
|
+
shouldRemove = true;
|
|
473
|
+
if (j > 0 && result[j - 1]?.includes("// System field:")) {
|
|
474
|
+
result[j - 1] = null;
|
|
475
|
+
}
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
if (shouldRemove) {
|
|
481
|
+
result[j] = null;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
result.push(line);
|
|
487
|
+
}
|
|
488
|
+
return result.filter((line) => line !== null).join("\n");
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/commands/db/gen-dbschema/helper/patch-helper.ts
|
|
492
|
+
function patchDrizzleKitDefects(source) {
|
|
493
|
+
let fixed = 0;
|
|
494
|
+
const text = source.replace(/\.default\('\)/g, () => {
|
|
495
|
+
fixed += 1;
|
|
496
|
+
return `.default('')`;
|
|
497
|
+
});
|
|
498
|
+
return { text, fixed };
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/commands/db/gen-dbschema/helper/timestamp-replacement.ts
|
|
502
|
+
function replaceTimestampWithCustomTypes(source) {
|
|
503
|
+
let replaced = 0;
|
|
504
|
+
const pattern = /timestamp\((['"])(.*?)\1,\s*(\{[^}]*\})\)/g;
|
|
505
|
+
const text = source.replace(pattern, (match, quote, fieldName, options) => {
|
|
506
|
+
const hasWithTimezone = /withTimezone:\s*true/.test(options);
|
|
507
|
+
const hasModeString = /mode:\s*['"]string['"]/.test(options);
|
|
508
|
+
if (hasWithTimezone && hasModeString) {
|
|
509
|
+
replaced += 1;
|
|
510
|
+
return `customTimestamptz(${quote}${fieldName}${quote})`;
|
|
511
|
+
}
|
|
512
|
+
return match;
|
|
513
|
+
});
|
|
514
|
+
return { text, replaced };
|
|
515
|
+
}
|
|
516
|
+
function replaceDefaultNowWithSql(source) {
|
|
517
|
+
let replaced = 0;
|
|
518
|
+
const pattern = /\.defaultNow\(\)/g;
|
|
519
|
+
const text = source.replace(pattern, () => {
|
|
520
|
+
replaced += 1;
|
|
521
|
+
return ".default(sql`CURRENT_TIMESTAMP`)";
|
|
522
|
+
});
|
|
523
|
+
return { text, replaced };
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// src/commands/db/gen-dbschema/helper/appendTableAliases.ts
|
|
527
|
+
var TABLE_ALIAS_MARKER = "// table aliases";
|
|
528
|
+
function appendTableAliases(source) {
|
|
529
|
+
const markerIndex = source.indexOf(`
|
|
530
|
+
${TABLE_ALIAS_MARKER}`);
|
|
531
|
+
const base = markerIndex === -1 ? source : source.slice(0, markerIndex);
|
|
532
|
+
const exportRegex = /export const\s+([A-Za-z_$][\w$]*)\s*=\s*pgTable\s*\(/g;
|
|
533
|
+
const tableExports = /* @__PURE__ */ new Set();
|
|
534
|
+
for (const match of base.matchAll(exportRegex)) {
|
|
535
|
+
const name = match[1];
|
|
536
|
+
tableExports.add(name);
|
|
537
|
+
}
|
|
538
|
+
if (tableExports.size === 0) {
|
|
539
|
+
return base;
|
|
540
|
+
}
|
|
541
|
+
const aliasLines = Array.from(tableExports).sort().map((name) => `export const ${name}Table = ${name};`).join("\n");
|
|
542
|
+
const prefix = base.trimEnd();
|
|
543
|
+
return `${prefix}
|
|
544
|
+
|
|
545
|
+
${TABLE_ALIAS_MARKER}
|
|
546
|
+
${aliasLines}
|
|
547
|
+
`;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// src/commands/db/gen-dbschema/postprocess.ts
|
|
551
|
+
function postprocessDrizzleSchema(targetPath) {
|
|
552
|
+
const resolvedPath = path.resolve(targetPath);
|
|
553
|
+
if (!fs2.existsSync(resolvedPath)) {
|
|
554
|
+
console.warn(`[postprocess-drizzle-schema] File not found: ${resolvedPath}`);
|
|
555
|
+
return void 0;
|
|
556
|
+
}
|
|
557
|
+
let text = fs2.readFileSync(resolvedPath, "utf8");
|
|
558
|
+
text = ensureHeaderComment(text);
|
|
559
|
+
const patchResult = patchDrizzleKitDefects(text);
|
|
560
|
+
text = patchResult.text;
|
|
561
|
+
text = removePgSchemaDeclarations(text);
|
|
562
|
+
const tableConversion = convertSchemaTableInvocations(text);
|
|
563
|
+
text = tableConversion.text;
|
|
564
|
+
const renameResult = renamePgTableConstants(text);
|
|
565
|
+
text = renameResult.text;
|
|
566
|
+
text = updateTableReferenceIdentifiers(text, renameResult.renames);
|
|
567
|
+
const replacement = replaceUnknownColumns(text);
|
|
568
|
+
text = replacement.text;
|
|
569
|
+
const timestampReplacement = replaceTimestampWithCustomTypes(text);
|
|
570
|
+
text = timestampReplacement.text;
|
|
571
|
+
const defaultNowReplacement = replaceDefaultNowWithSql(text);
|
|
572
|
+
text = defaultNowReplacement.text;
|
|
573
|
+
text = removeConflictingSystemFields(text);
|
|
574
|
+
text = addSystemFieldComments(text);
|
|
575
|
+
text = tweakImports(text);
|
|
576
|
+
text = inlineCustomTypes(text);
|
|
577
|
+
text = appendTableAliases(text);
|
|
578
|
+
text = text.replace(/\r?\n/g, "\n");
|
|
579
|
+
text = collapseExtraBlankLines(text);
|
|
580
|
+
fs2.writeFileSync(resolvedPath, text, "utf8");
|
|
581
|
+
if (patchResult.fixed > 0) {
|
|
582
|
+
console.info(`[postprocess-drizzle-schema] Patched ${patchResult.fixed} drizzle-kit defects (.default(') -> .default(''))`);
|
|
583
|
+
}
|
|
584
|
+
if (replacement.replaced > 0) {
|
|
585
|
+
console.info(`[postprocess-drizzle-schema] Replaced ${replacement.replaced} unknown columns`);
|
|
586
|
+
}
|
|
587
|
+
if (replacement.unmatched.length > 0) {
|
|
588
|
+
console.warn("[postprocess-drizzle-schema] Unmatched custom types:", replacement.unmatched.length);
|
|
589
|
+
replacement.unmatched.forEach((line) => console.warn(` ${line}`));
|
|
590
|
+
}
|
|
591
|
+
if (tableConversion.converted > 0) {
|
|
592
|
+
console.info(`[postprocess-drizzle-schema] Converted ${tableConversion.converted} schema.table invocations to pgTable`);
|
|
593
|
+
}
|
|
594
|
+
if (timestampReplacement.replaced > 0) {
|
|
595
|
+
console.info(`[postprocess-drizzle-schema] Replaced ${timestampReplacement.replaced} timestamp fields with customTimestamptz`);
|
|
596
|
+
}
|
|
597
|
+
if (defaultNowReplacement.replaced > 0) {
|
|
598
|
+
console.info(`[postprocess-drizzle-schema] Replaced ${defaultNowReplacement.replaced} .defaultNow() with .default(sql\`CURRENT_TIMESTAMP\`)`);
|
|
599
|
+
}
|
|
600
|
+
return {
|
|
601
|
+
replacedUnknown: replacement.replaced,
|
|
602
|
+
unmatchedUnknown: replacement.unmatched,
|
|
603
|
+
patchedDefects: patchResult.fixed,
|
|
604
|
+
replacedTimestamps: timestampReplacement.replaced,
|
|
605
|
+
replacedDefaultNow: defaultNowReplacement.replaced
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// src/commands/db/gen-nest-resource/generator.ts
|
|
610
|
+
import { pluralize } from "inflection";
|
|
611
|
+
|
|
612
|
+
// src/commands/db/gen-nest-resource/utils.ts
|
|
613
|
+
function mapDrizzleTypeToTS(field) {
|
|
614
|
+
const typeMap = {
|
|
615
|
+
// String types
|
|
616
|
+
char: "string",
|
|
617
|
+
varchar: "string",
|
|
618
|
+
text: "string",
|
|
619
|
+
// Numeric types
|
|
620
|
+
smallint: "number",
|
|
621
|
+
integer: "number",
|
|
622
|
+
int: "number",
|
|
623
|
+
bigint: "string",
|
|
624
|
+
// bigint 在 JS 中通常作为 string 处理
|
|
625
|
+
serial: "number",
|
|
626
|
+
smallserial: "number",
|
|
627
|
+
bigserial: "string",
|
|
628
|
+
// Decimal types
|
|
629
|
+
decimal: "string",
|
|
630
|
+
// 精确数值通常用 string
|
|
631
|
+
numeric: "string",
|
|
632
|
+
real: "number",
|
|
633
|
+
doublePrecision: "number",
|
|
634
|
+
// Boolean
|
|
635
|
+
boolean: "boolean",
|
|
636
|
+
// Date/Time types
|
|
637
|
+
timestamp: "Date",
|
|
638
|
+
timestamptz: "Date",
|
|
639
|
+
date: "Date",
|
|
640
|
+
time: "string",
|
|
641
|
+
timetz: "string",
|
|
642
|
+
interval: "string",
|
|
643
|
+
// UUID
|
|
644
|
+
uuid: "string",
|
|
645
|
+
// JSON types
|
|
646
|
+
json: "any",
|
|
647
|
+
jsonb: "any",
|
|
648
|
+
// Binary
|
|
649
|
+
bytea: "Buffer",
|
|
650
|
+
// Network types
|
|
651
|
+
inet: "string",
|
|
652
|
+
cidr: "string",
|
|
653
|
+
macaddr: "string",
|
|
654
|
+
macaddr8: "string",
|
|
655
|
+
// Geometric types
|
|
656
|
+
point: "{ x: number; y: number }",
|
|
657
|
+
line: "string",
|
|
658
|
+
lseg: "string",
|
|
659
|
+
box: "string",
|
|
660
|
+
path: "string",
|
|
661
|
+
polygon: "string",
|
|
662
|
+
circle: "string",
|
|
663
|
+
// Array types (handled by isArray flag)
|
|
664
|
+
array: "any[]",
|
|
665
|
+
// Custom types
|
|
666
|
+
customType: "any",
|
|
667
|
+
customTimestamptz: "Date",
|
|
668
|
+
userProfile: "string",
|
|
669
|
+
fileAttachment: "FileAttachment",
|
|
670
|
+
// Enum (handled separately)
|
|
671
|
+
pgEnum: "string"
|
|
672
|
+
};
|
|
673
|
+
let baseType = typeMap[field.type] || "any";
|
|
674
|
+
if (field.isArray) {
|
|
675
|
+
baseType = baseType.endsWith("[]") ? baseType : `${baseType}[]`;
|
|
676
|
+
}
|
|
677
|
+
if (field.enumValues && field.enumValues.length > 0) {
|
|
678
|
+
baseType = field.enumValues.map((v) => `'${v}'`).join(" | ");
|
|
679
|
+
}
|
|
680
|
+
return baseType;
|
|
681
|
+
}
|
|
682
|
+
function toPascalCase(str) {
|
|
683
|
+
return str.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[-_\s]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
684
|
+
}
|
|
685
|
+
function toKebabCase(str) {
|
|
686
|
+
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase().replace(/[_\s]/g, "-");
|
|
687
|
+
}
|
|
688
|
+
function toSnakeCase(str) {
|
|
689
|
+
return str.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase().replace(/[-\s]/g, "_");
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// src/commands/db/gen-nest-resource/generator.ts
|
|
693
|
+
function generateDTO(table) {
|
|
694
|
+
const className = toPascalCase(table.variableName);
|
|
695
|
+
let dto = `// \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
|
|
696
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
697
|
+
import { IsDefined, IsNumber, IsOptional, IsString, MaxLength, IsInt, IsBoolean, IsUUID, IsDate, IsObject, IsArray } from 'class-validator';
|
|
698
|
+
import { Type } from 'class-transformer';
|
|
699
|
+
import { FileAttachment } from '../../../database/schema';
|
|
700
|
+
|
|
701
|
+
`;
|
|
702
|
+
dto += `export class Create${className}Dto {
|
|
703
|
+
`;
|
|
704
|
+
for (const field of table.fields) {
|
|
705
|
+
if (field.isPrimaryKey || field.name === "id" || field.name.startsWith("_") || field.name.startsWith("created") || field.name.startsWith("updated")) {
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
const tsType = mapDrizzleTypeToTS(field);
|
|
709
|
+
const optional = field.nullable || field.hasDefault ? "?" : "";
|
|
710
|
+
const decorators = generateValidationDecorators(field);
|
|
711
|
+
if (decorators) {
|
|
712
|
+
dto += decorators;
|
|
713
|
+
}
|
|
714
|
+
dto += ` ${field.name}${optional}: ${tsType};
|
|
715
|
+
|
|
716
|
+
`;
|
|
717
|
+
}
|
|
718
|
+
dto += "}\n\n";
|
|
719
|
+
dto += `export class Update${className}Dto {
|
|
720
|
+
`;
|
|
721
|
+
for (const field of table.fields) {
|
|
722
|
+
if (field.name.startsWith("_") || field.name.startsWith("created") || field.name.startsWith("updated") || field.isPrimaryKey || field.name === "id") {
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
const tsType = mapDrizzleTypeToTS(field);
|
|
726
|
+
const decorators = generateValidationDecorators(field, {
|
|
727
|
+
isUpdate: true
|
|
728
|
+
});
|
|
729
|
+
if (decorators) {
|
|
730
|
+
dto += decorators;
|
|
731
|
+
}
|
|
732
|
+
dto += ` ${field.name}?: ${tsType};
|
|
733
|
+
|
|
734
|
+
`;
|
|
735
|
+
}
|
|
736
|
+
dto += "}\n\n";
|
|
737
|
+
dto += `export class ${className}ResponseDto {
|
|
738
|
+
`;
|
|
739
|
+
for (const field of table.fields) {
|
|
740
|
+
const tsType = mapDrizzleTypeToTS(field);
|
|
741
|
+
const optional = field.nullable ? "?" : "";
|
|
742
|
+
const decorators = generateValidationDecorators(field, {
|
|
743
|
+
isResponse: true
|
|
744
|
+
});
|
|
745
|
+
if (decorators) {
|
|
746
|
+
dto += decorators;
|
|
747
|
+
}
|
|
748
|
+
dto += ` ${field.name}${optional}: ${tsType};
|
|
749
|
+
|
|
750
|
+
`;
|
|
751
|
+
}
|
|
752
|
+
dto += "}\n";
|
|
753
|
+
return dto;
|
|
754
|
+
}
|
|
755
|
+
function generateValidationDecorators(field, {
|
|
756
|
+
isUpdate = false,
|
|
757
|
+
isResponse = false
|
|
758
|
+
} = {}) {
|
|
759
|
+
let decorators = " // \u8BF7\u6309\u7528\u6237\u9700\u6C42\u4FEE\u6539\u4EE5\u4E0B\u88C5\u9970\u5668\u6CE8\u91CA\n";
|
|
760
|
+
if (field.nullable || !isResponse && field.hasDefault || isUpdate) {
|
|
761
|
+
decorators += ` @ApiPropertyOptional({ description: '${field.comment || field.name}' })
|
|
762
|
+
`;
|
|
763
|
+
if (isResponse) {
|
|
764
|
+
return decorators;
|
|
765
|
+
}
|
|
766
|
+
decorators += " @IsOptional()\n";
|
|
767
|
+
} else {
|
|
768
|
+
decorators += ` @ApiProperty({ description: '${field.comment || field.name}' })
|
|
769
|
+
`;
|
|
770
|
+
if (isResponse) {
|
|
771
|
+
return decorators;
|
|
772
|
+
}
|
|
773
|
+
decorators += " @IsDefined()\n";
|
|
774
|
+
}
|
|
775
|
+
switch (field.type) {
|
|
776
|
+
case "varchar":
|
|
777
|
+
case "char":
|
|
778
|
+
case "text":
|
|
779
|
+
decorators += " @IsString()\n";
|
|
780
|
+
if (field.length) {
|
|
781
|
+
decorators += ` @MaxLength(${field.length})
|
|
782
|
+
`;
|
|
783
|
+
}
|
|
784
|
+
break;
|
|
785
|
+
case "integer":
|
|
786
|
+
case "smallint":
|
|
787
|
+
case "serial":
|
|
788
|
+
case "smallserial":
|
|
789
|
+
decorators += " @IsInt()\n";
|
|
790
|
+
break;
|
|
791
|
+
case "decimal":
|
|
792
|
+
case "numeric":
|
|
793
|
+
case "real":
|
|
794
|
+
case "doublePrecision":
|
|
795
|
+
decorators += " @IsNumber()\n";
|
|
796
|
+
break;
|
|
797
|
+
case "boolean":
|
|
798
|
+
decorators += " @IsBoolean()\n";
|
|
799
|
+
break;
|
|
800
|
+
case "uuid":
|
|
801
|
+
decorators += " @IsUUID()\n";
|
|
802
|
+
break;
|
|
803
|
+
case "timestamp":
|
|
804
|
+
case "timestamptz":
|
|
805
|
+
case "date":
|
|
806
|
+
case "customTimestamptz":
|
|
807
|
+
decorators += " @IsDate()\n";
|
|
808
|
+
break;
|
|
809
|
+
case "json":
|
|
810
|
+
case "jsonb":
|
|
811
|
+
decorators += " @IsObject()\n";
|
|
812
|
+
break;
|
|
813
|
+
}
|
|
814
|
+
if (field.isArray) {
|
|
815
|
+
decorators += " @IsArray()\n";
|
|
816
|
+
}
|
|
817
|
+
return decorators;
|
|
818
|
+
}
|
|
819
|
+
function generateController(table) {
|
|
820
|
+
const className = toPascalCase(table.variableName);
|
|
821
|
+
const routePath = toKebabCase(pluralize(table.variableName));
|
|
822
|
+
const filePath = toSnakeCase(table.variableName);
|
|
823
|
+
const pkField = table.fields.find((f) => f.isPrimaryKey);
|
|
824
|
+
const pkType = pkField ? mapDrizzleTypeToTS(pkField) : "string";
|
|
825
|
+
const pkName = pkField ? pkField.name : "id";
|
|
826
|
+
const controller = `
|
|
827
|
+
// \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
|
|
828
|
+
import {
|
|
829
|
+
Controller,
|
|
830
|
+
Get,
|
|
831
|
+
Post,
|
|
832
|
+
Put,
|
|
833
|
+
Delete,
|
|
834
|
+
Body,
|
|
835
|
+
Param,
|
|
836
|
+
Query,
|
|
837
|
+
} from '@nestjs/common';
|
|
838
|
+
import {
|
|
839
|
+
ApiTags,
|
|
840
|
+
ApiOperation,
|
|
841
|
+
ApiOkResponse,
|
|
842
|
+
ApiCreatedResponse,
|
|
843
|
+
} from '@nestjs/swagger';
|
|
844
|
+
import {
|
|
845
|
+
Create${className}Dto,
|
|
846
|
+
Update${className}Dto,
|
|
847
|
+
${className}ResponseDto
|
|
848
|
+
} from './dtos/${filePath}.dto';
|
|
849
|
+
import { ${className}Service } from './${filePath}.service';
|
|
850
|
+
|
|
851
|
+
@ApiTags('${toPascalCase(table.variableName)}')
|
|
852
|
+
@Controller('api/${routePath}')
|
|
853
|
+
export class ${className}Controller {
|
|
854
|
+
constructor(private readonly ${table.variableName}Service: ${className}Service) {}
|
|
855
|
+
|
|
856
|
+
@Post()
|
|
857
|
+
@ApiOperation({
|
|
858
|
+
summary: '\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
859
|
+
description: '\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
860
|
+
})
|
|
861
|
+
@ApiCreatedResponse({
|
|
862
|
+
description: '\u6210\u529F\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55',
|
|
863
|
+
type: ${className}ResponseDto,
|
|
864
|
+
})
|
|
865
|
+
async create(
|
|
866
|
+
@Body() createDto: Create${className}Dto
|
|
867
|
+
): Promise<${className}ResponseDto> {
|
|
868
|
+
return this.${table.variableName}Service.create(createDto);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
@ApiOperation({
|
|
872
|
+
summary: '\u6839\u636E\u4E3B\u952E\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
873
|
+
description: '\u6839\u636E\u4E3B\u952E\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
874
|
+
})
|
|
875
|
+
@ApiOkResponse({
|
|
876
|
+
description: '\u6210\u529F\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55',
|
|
877
|
+
type: ${className}ResponseDto,
|
|
878
|
+
})
|
|
879
|
+
@Get(':${pkName}')
|
|
880
|
+
async findOne(
|
|
881
|
+
@Param('${pkName}') ${pkName}: ${pkType}
|
|
882
|
+
): Promise<${className}ResponseDto> {
|
|
883
|
+
return this.${table.variableName}Service.findOne(${pkName});
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
@ApiOperation({
|
|
887
|
+
summary: '\u6839\u636E\u4E3B\u952E\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
888
|
+
description: '\u6839\u636E\u4E3B\u952E\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
889
|
+
})
|
|
890
|
+
@ApiOkResponse({
|
|
891
|
+
description: '\u6210\u529F\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55',
|
|
892
|
+
type: ${className}ResponseDto,
|
|
893
|
+
})
|
|
894
|
+
@Put(':${pkName}')
|
|
895
|
+
async update(
|
|
896
|
+
@Param('${pkName}') ${pkName}: ${pkType},
|
|
897
|
+
@Body() updateDto: Update${className}Dto
|
|
898
|
+
): Promise<${className}ResponseDto> {
|
|
899
|
+
return this.${table.variableName}Service.update(${pkName}, updateDto);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
@ApiOperation({
|
|
903
|
+
summary: '\u6839\u636E\u4E3B\u952E\u5220\u9664\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
904
|
+
description: '\u6839\u636E\u4E3B\u952E\u5220\u9664\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
905
|
+
})
|
|
906
|
+
@ApiOkResponse({
|
|
907
|
+
description: '\u6210\u529F\u5220\u9664\u4E00\u6761\u8BB0\u5F55',
|
|
908
|
+
})
|
|
909
|
+
@Delete(':${pkName}')
|
|
910
|
+
async remove(
|
|
911
|
+
@Param('${pkName}') ${pkName}: ${pkType}
|
|
912
|
+
): Promise<void> {
|
|
913
|
+
return this.${table.variableName}Service.remove(${pkName});
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
`;
|
|
917
|
+
return controller;
|
|
918
|
+
}
|
|
919
|
+
function generateService(table) {
|
|
920
|
+
const className = toPascalCase(table.variableName);
|
|
921
|
+
const filePath = toSnakeCase(table.variableName);
|
|
922
|
+
const pkField = table.fields.find((f) => f.isPrimaryKey);
|
|
923
|
+
const pkType = pkField ? mapDrizzleTypeToTS(pkField) : "string";
|
|
924
|
+
const pkName = pkField ? pkField.name : "id";
|
|
925
|
+
const service = `
|
|
926
|
+
// \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
|
|
927
|
+
import { Injectable, Inject, Logger, NotFoundException } from '@nestjs/common';
|
|
928
|
+
import { eq } from 'drizzle-orm';
|
|
929
|
+
import { DRIZZLE_DATABASE, type PostgresJsDatabase } from '@lark-apaas/fullstack-nestjs-core';
|
|
930
|
+
import { ${table.variableName} } from '../../database/schema';
|
|
931
|
+
import {
|
|
932
|
+
Create${className}Dto,
|
|
933
|
+
Update${className}Dto,
|
|
934
|
+
${className}ResponseDto
|
|
935
|
+
} from './dtos/${filePath}.dto';
|
|
936
|
+
|
|
937
|
+
@Injectable()
|
|
938
|
+
export class ${className}Service {
|
|
939
|
+
private readonly logger = new Logger(${className}Service.name);
|
|
940
|
+
|
|
941
|
+
constructor(@Inject(DRIZZLE_DATABASE) private readonly db: PostgresJsDatabase) {}
|
|
942
|
+
|
|
943
|
+
async create(createDto: Create${className}Dto): Promise<${className}ResponseDto> {
|
|
944
|
+
const [result] = await this.db
|
|
945
|
+
.insert(${table.variableName})
|
|
946
|
+
.values(createDto)
|
|
947
|
+
.returning();
|
|
948
|
+
|
|
949
|
+
this.logger.log(\`Created ${className} with ${pkName} \${result.${pkName}}\`);
|
|
950
|
+
|
|
951
|
+
return result;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
async findAll(options?: { page?: number; limit?: number }): Promise<${className}ResponseDto[]> {
|
|
955
|
+
const { page = 1, limit = 10 } = options || {};
|
|
956
|
+
const offset = (page - 1) * limit;
|
|
957
|
+
|
|
958
|
+
return this.db
|
|
959
|
+
.select()
|
|
960
|
+
.from(${table.variableName})
|
|
961
|
+
.limit(limit)
|
|
962
|
+
.offset(offset);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
async findOne(${pkName}: ${pkType}): Promise<${className}ResponseDto> {
|
|
966
|
+
const [result] = await this.db
|
|
967
|
+
.select()
|
|
968
|
+
.from(${table.variableName})
|
|
969
|
+
.where(eq(${table.variableName}.${pkName}, ${pkName}))
|
|
970
|
+
.limit(1);
|
|
971
|
+
|
|
972
|
+
if (!result) {
|
|
973
|
+
throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
return result;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
async update(${pkName}: ${pkType}, updateDto: Update${className}Dto): Promise<${className}ResponseDto> {
|
|
980
|
+
const [result] = await this.db
|
|
981
|
+
.update(${table.variableName})
|
|
982
|
+
.set(updateDto)
|
|
983
|
+
.where(eq(${table.variableName}.${pkName}, ${pkName}))
|
|
984
|
+
.returning();
|
|
985
|
+
|
|
986
|
+
if (!result) {
|
|
987
|
+
throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
return result;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
async remove(${pkName}: ${pkType}): Promise<void> {
|
|
994
|
+
const result = await this.db
|
|
995
|
+
.delete(${table.variableName})
|
|
996
|
+
.where(eq(${table.variableName}.${pkName}, ${pkName}))
|
|
997
|
+
.returning();
|
|
998
|
+
|
|
999
|
+
if (result.length === 0) {
|
|
1000
|
+
throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
this.logger.log(\`Deleted ${className} with ${pkName} \${${pkName}}\`);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
`;
|
|
1007
|
+
return service;
|
|
1008
|
+
}
|
|
1009
|
+
function generateModule(table) {
|
|
1010
|
+
const className = toPascalCase(table.variableName);
|
|
1011
|
+
const filePath = toSnakeCase(table.variableName);
|
|
1012
|
+
const module = `
|
|
1013
|
+
import { Module } from '@nestjs/common';
|
|
1014
|
+
import { ${className}Controller } from './${filePath}.controller';
|
|
1015
|
+
import { ${className}Service } from './${filePath}.service';
|
|
1016
|
+
|
|
1017
|
+
@Module({
|
|
1018
|
+
controllers: [${className}Controller],
|
|
1019
|
+
providers: [${className}Service],
|
|
1020
|
+
})
|
|
1021
|
+
export class ${className}Module {}
|
|
1022
|
+
`;
|
|
1023
|
+
return module;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// src/commands/db/gen-nest-resource/schema-parser.ts
|
|
1027
|
+
import { Project, Node } from "ts-morph";
|
|
1028
|
+
var DrizzleSchemaParser = class {
|
|
1029
|
+
constructor(projectOptions) {
|
|
1030
|
+
this.project = new Project(projectOptions);
|
|
1031
|
+
}
|
|
1032
|
+
parseSchemaFile(filePath) {
|
|
1033
|
+
const sourceFile = this.project.addSourceFileAtPath(filePath);
|
|
1034
|
+
const tables = [];
|
|
1035
|
+
const variableStatements = sourceFile.getVariableStatements();
|
|
1036
|
+
for (const statement of variableStatements) {
|
|
1037
|
+
const declarations = statement.getDeclarations();
|
|
1038
|
+
for (const declaration of declarations) {
|
|
1039
|
+
const initializer = declaration.getInitializer();
|
|
1040
|
+
if (initializer && Node.isCallExpression(initializer)) {
|
|
1041
|
+
const expression = initializer.getExpression();
|
|
1042
|
+
if (expression.getText() === "pgTable") {
|
|
1043
|
+
const tableInfo = this.parsePgTable(
|
|
1044
|
+
declaration.getName(),
|
|
1045
|
+
initializer
|
|
1046
|
+
);
|
|
1047
|
+
if (tableInfo) {
|
|
1048
|
+
tables.push(tableInfo);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
return tables;
|
|
1055
|
+
}
|
|
1056
|
+
parsePgTable(variableName, callExpr) {
|
|
1057
|
+
const args = callExpr.getArguments();
|
|
1058
|
+
if (args.length < 2) {
|
|
1059
|
+
return null;
|
|
1060
|
+
}
|
|
1061
|
+
const tableName = args[0].getText().replace(/['"]/g, "");
|
|
1062
|
+
const fieldsArg = args[1];
|
|
1063
|
+
if (!Node.isObjectLiteralExpression(fieldsArg)) {
|
|
1064
|
+
return null;
|
|
1065
|
+
}
|
|
1066
|
+
const fields = [];
|
|
1067
|
+
const properties = fieldsArg.getProperties();
|
|
1068
|
+
for (const prop of properties) {
|
|
1069
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
1070
|
+
const fieldName = prop.getName();
|
|
1071
|
+
const initializer = prop.getInitializer();
|
|
1072
|
+
const leadingComments = prop.getLeadingCommentRanges();
|
|
1073
|
+
let comment;
|
|
1074
|
+
if (leadingComments.length > 0) {
|
|
1075
|
+
comment = leadingComments.map((c) => c.getText()).join("\n").replace(/\/\//g, "").trim();
|
|
1076
|
+
}
|
|
1077
|
+
if (initializer && Node.isCallExpression(initializer)) {
|
|
1078
|
+
const fieldInfo = this.parseField(fieldName, initializer, comment);
|
|
1079
|
+
fields.push(fieldInfo);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
return {
|
|
1084
|
+
tableName,
|
|
1085
|
+
variableName,
|
|
1086
|
+
fields
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
parseField(fieldName, callExpr, comment) {
|
|
1090
|
+
const fieldInfo = {
|
|
1091
|
+
name: fieldName,
|
|
1092
|
+
columnName: fieldName,
|
|
1093
|
+
type: "",
|
|
1094
|
+
nullable: true,
|
|
1095
|
+
hasDefault: false,
|
|
1096
|
+
notNull: false,
|
|
1097
|
+
isPrimaryKey: false,
|
|
1098
|
+
isUnique: false,
|
|
1099
|
+
isArray: false,
|
|
1100
|
+
comment
|
|
1101
|
+
};
|
|
1102
|
+
this.parseBaseType(callExpr, fieldInfo);
|
|
1103
|
+
this.parseCallChain(callExpr, fieldInfo);
|
|
1104
|
+
return fieldInfo;
|
|
1105
|
+
}
|
|
1106
|
+
parseBaseType(callExpr, fieldInfo) {
|
|
1107
|
+
let current = callExpr;
|
|
1108
|
+
let baseCall = null;
|
|
1109
|
+
while (Node.isCallExpression(current)) {
|
|
1110
|
+
baseCall = current;
|
|
1111
|
+
const expression2 = current.getExpression();
|
|
1112
|
+
if (Node.isPropertyAccessExpression(expression2)) {
|
|
1113
|
+
current = expression2.getExpression();
|
|
1114
|
+
} else {
|
|
1115
|
+
break;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
if (!baseCall) {
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
const expression = baseCall.getExpression();
|
|
1122
|
+
let typeName = "";
|
|
1123
|
+
if (Node.isPropertyAccessExpression(expression)) {
|
|
1124
|
+
typeName = expression.getName();
|
|
1125
|
+
} else {
|
|
1126
|
+
typeName = expression.getText();
|
|
1127
|
+
}
|
|
1128
|
+
fieldInfo.type = typeName;
|
|
1129
|
+
const args = baseCall.getArguments();
|
|
1130
|
+
if (args.length > 0) {
|
|
1131
|
+
const firstArg = args[0];
|
|
1132
|
+
if (Node.isStringLiteral(firstArg)) {
|
|
1133
|
+
fieldInfo.columnName = firstArg.getLiteralText();
|
|
1134
|
+
} else if (Node.isObjectLiteralExpression(firstArg)) {
|
|
1135
|
+
this.parseTypeConfig(firstArg, fieldInfo);
|
|
1136
|
+
} else if (Node.isArrayLiteralExpression(firstArg)) {
|
|
1137
|
+
fieldInfo.enumValues = firstArg.getElements().map((el) => el.getText().replace(/['"]/g, ""));
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
if (args.length > 1 && Node.isObjectLiteralExpression(args[1])) {
|
|
1141
|
+
this.parseTypeConfig(args[1], fieldInfo);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
parseTypeConfig(objLiteral, fieldInfo) {
|
|
1145
|
+
if (!Node.isObjectLiteralExpression(objLiteral)) {
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
const properties = objLiteral.getProperties();
|
|
1149
|
+
for (const prop of properties) {
|
|
1150
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
1151
|
+
const propName = prop.getName();
|
|
1152
|
+
const value = prop.getInitializer()?.getText();
|
|
1153
|
+
switch (propName) {
|
|
1154
|
+
case "length":
|
|
1155
|
+
fieldInfo.length = value ? parseInt(value) : void 0;
|
|
1156
|
+
break;
|
|
1157
|
+
case "precision":
|
|
1158
|
+
fieldInfo.precision = value ? parseInt(value) : void 0;
|
|
1159
|
+
break;
|
|
1160
|
+
case "scale":
|
|
1161
|
+
fieldInfo.scale = value ? parseInt(value) : void 0;
|
|
1162
|
+
break;
|
|
1163
|
+
case "default":
|
|
1164
|
+
fieldInfo.hasDefault = true;
|
|
1165
|
+
fieldInfo.defaultValue = value;
|
|
1166
|
+
break;
|
|
1167
|
+
// 时间精度(用于 timestamp, time 等)
|
|
1168
|
+
case "withTimezone":
|
|
1169
|
+
fieldInfo.withTimezone = value === "true";
|
|
1170
|
+
break;
|
|
1171
|
+
case "mode":
|
|
1172
|
+
fieldInfo.mode = value?.replace(/['"]/g, "");
|
|
1173
|
+
break;
|
|
1174
|
+
default:
|
|
1175
|
+
throw new Error(`Unsupported property: ${propName}`);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
parseCallChain(callExpr, fieldInfo) {
|
|
1181
|
+
let current = callExpr;
|
|
1182
|
+
while (Node.isCallExpression(current)) {
|
|
1183
|
+
const expression = current.getExpression();
|
|
1184
|
+
if (Node.isPropertyAccessExpression(expression)) {
|
|
1185
|
+
const methodName = expression.getName();
|
|
1186
|
+
const args = current.getArguments();
|
|
1187
|
+
switch (methodName) {
|
|
1188
|
+
case "notNull":
|
|
1189
|
+
fieldInfo.notNull = true;
|
|
1190
|
+
fieldInfo.nullable = false;
|
|
1191
|
+
break;
|
|
1192
|
+
case "default":
|
|
1193
|
+
fieldInfo.hasDefault = true;
|
|
1194
|
+
if (args.length > 0) {
|
|
1195
|
+
fieldInfo.defaultValue = args[0].getText();
|
|
1196
|
+
}
|
|
1197
|
+
break;
|
|
1198
|
+
case "defaultRandom":
|
|
1199
|
+
fieldInfo.hasDefault = true;
|
|
1200
|
+
fieldInfo.defaultValue = "random";
|
|
1201
|
+
break;
|
|
1202
|
+
case "primaryKey":
|
|
1203
|
+
fieldInfo.isPrimaryKey = true;
|
|
1204
|
+
fieldInfo.notNull = true;
|
|
1205
|
+
fieldInfo.nullable = false;
|
|
1206
|
+
break;
|
|
1207
|
+
case "unique":
|
|
1208
|
+
fieldInfo.isUnique = true;
|
|
1209
|
+
break;
|
|
1210
|
+
case "array":
|
|
1211
|
+
fieldInfo.isArray = true;
|
|
1212
|
+
break;
|
|
1213
|
+
case "references":
|
|
1214
|
+
if (args.length > 0) {
|
|
1215
|
+
const refArg = args[0].getText();
|
|
1216
|
+
const match = refArg.match(/=>\s*(\w+)\.(\w+)/);
|
|
1217
|
+
if (match) {
|
|
1218
|
+
fieldInfo.references = {
|
|
1219
|
+
table: match[1],
|
|
1220
|
+
column: match[2]
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
break;
|
|
1225
|
+
default:
|
|
1226
|
+
throw new Error(`Unsupported method: ${methodName}`);
|
|
1227
|
+
}
|
|
1228
|
+
current = expression.getExpression();
|
|
1229
|
+
} else {
|
|
1230
|
+
break;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
};
|
|
1235
|
+
|
|
1236
|
+
// src/commands/db/gen-nest-resource/index.ts
|
|
1237
|
+
import { join } from "path";
|
|
1238
|
+
import { mkdir, rm, writeFile } from "fs/promises";
|
|
1239
|
+
import { existsSync } from "fs";
|
|
1240
|
+
async function parseAndGenerateNestResourceTemplate(options) {
|
|
1241
|
+
const parser = new DrizzleSchemaParser({
|
|
1242
|
+
tsConfigFilePath: options.tsConfigFilePath
|
|
1243
|
+
});
|
|
1244
|
+
const tables = parser.parseSchemaFile(options.schemaFilePath);
|
|
1245
|
+
if (tables.length === 0) {
|
|
1246
|
+
console.warn("\u672A\u89E3\u6790\u5230\u4EFB\u4F55\u6570\u636E\u5E93\u8868\uFF0C\u65E0\u9700\u751F\u6210 Nest.js \u6A21\u5757\u6A21\u677F");
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
tables.sort((a, b) => b.variableName.length - a.variableName.length);
|
|
1250
|
+
const table = tables[0];
|
|
1251
|
+
console.info(`\u751F\u6210 Nest.js ${table.variableName} \u6A21\u5757`);
|
|
1252
|
+
const filePath = toSnakeCase(table.variableName);
|
|
1253
|
+
const moduleDir = join(options.moduleOutputDir, filePath);
|
|
1254
|
+
if (existsSync(moduleDir)) {
|
|
1255
|
+
console.info(`Nest.js \u6A21\u5757 ${filePath} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u751F\u6210\u4EE3\u7801`);
|
|
1256
|
+
return;
|
|
1257
|
+
}
|
|
1258
|
+
const dto = generateDTO(table);
|
|
1259
|
+
const controller = generateController(table);
|
|
1260
|
+
const service = generateService(table);
|
|
1261
|
+
const moduleFilePath = join(moduleDir, `${filePath}.module.ts`);
|
|
1262
|
+
const module = generateModule(table);
|
|
1263
|
+
try {
|
|
1264
|
+
await mkdir(moduleDir, { recursive: true });
|
|
1265
|
+
await mkdir(join(moduleDir, "dtos"), { recursive: true });
|
|
1266
|
+
await writeFile(join(moduleDir, "dtos", `${filePath}.dto.ts`), dto);
|
|
1267
|
+
await writeFile(join(moduleDir, `${filePath}.controller.ts`), controller);
|
|
1268
|
+
await writeFile(join(moduleDir, `${filePath}.service.ts`), service);
|
|
1269
|
+
await writeFile(moduleFilePath, module);
|
|
1270
|
+
} catch (err) {
|
|
1271
|
+
console.error(`\u751F\u6210 Nest.js ${filePath} \u6A21\u5757\u5931\u8D25: ${err.message}`);
|
|
1272
|
+
await rm(moduleDir, { recursive: true });
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
// src/commands/db/schema.handler.ts
|
|
125
1277
|
var require2 = createRequire(import.meta.url);
|
|
126
1278
|
async function run(options = {}) {
|
|
127
1279
|
let exitCode = 0;
|
|
128
|
-
const envPath2 =
|
|
129
|
-
if (
|
|
1280
|
+
const envPath2 = path2.resolve(process.cwd(), ".env");
|
|
1281
|
+
if (fs3.existsSync(envPath2)) {
|
|
130
1282
|
loadEnv({ path: envPath2 });
|
|
131
1283
|
console.log("[gen-db-schema] \u2713 Loaded .env file");
|
|
132
1284
|
}
|
|
@@ -136,25 +1288,56 @@ async function run(options = {}) {
|
|
|
136
1288
|
process.exit(1);
|
|
137
1289
|
}
|
|
138
1290
|
const outputPath = options.output || process.env.DB_SCHEMA_OUTPUT || "server/database/schema.ts";
|
|
139
|
-
const OUT_DIR =
|
|
140
|
-
const SCHEMA_FILE =
|
|
1291
|
+
const OUT_DIR = path2.resolve(process.cwd(), "server/database/.introspect");
|
|
1292
|
+
const SCHEMA_FILE = path2.resolve(process.cwd(), outputPath);
|
|
141
1293
|
console.log("[gen-db-schema] Starting...");
|
|
142
|
-
const __filename =
|
|
143
|
-
const __dirname2 =
|
|
144
|
-
const
|
|
145
|
-
|
|
1294
|
+
const __filename = fileURLToPath2(import.meta.url);
|
|
1295
|
+
const __dirname2 = path2.dirname(__filename);
|
|
1296
|
+
const configPathCandidates = [
|
|
1297
|
+
path2.resolve(__dirname2, "config/drizzle.config.js"),
|
|
1298
|
+
path2.resolve(__dirname2, "../../config/drizzle.config.js"),
|
|
1299
|
+
path2.resolve(__dirname2, "../../../dist/config/drizzle.config.js")
|
|
1300
|
+
];
|
|
1301
|
+
const configPath = configPathCandidates.find((p) => fs3.existsSync(p));
|
|
1302
|
+
console.log("[gen-db-schema] Using drizzle config from:", configPath ?? "(not found)");
|
|
1303
|
+
if (!configPath) {
|
|
146
1304
|
console.error("[gen-db-schema] Error: drizzle config not found in CLI package");
|
|
147
1305
|
process.exit(1);
|
|
148
1306
|
}
|
|
1307
|
+
const resolveDrizzleKitBin = () => {
|
|
1308
|
+
const entryPath = require2.resolve("drizzle-kit");
|
|
1309
|
+
let currentDir = path2.dirname(entryPath);
|
|
1310
|
+
let lastDir = null;
|
|
1311
|
+
while (currentDir !== lastDir) {
|
|
1312
|
+
const pkgJsonPath = path2.join(currentDir, "package.json");
|
|
1313
|
+
if (fs3.existsSync(pkgJsonPath)) {
|
|
1314
|
+
const pkgJsonRaw = fs3.readFileSync(pkgJsonPath, "utf8");
|
|
1315
|
+
const pkgJson = JSON.parse(pkgJsonRaw);
|
|
1316
|
+
if (pkgJson.name === "drizzle-kit") {
|
|
1317
|
+
const binField = pkgJson.bin;
|
|
1318
|
+
const binRelPath = typeof binField === "string" ? binField : binField?.["drizzle-kit"];
|
|
1319
|
+
if (!binRelPath) {
|
|
1320
|
+
throw new Error("Unable to resolve drizzle-kit binary from package.json");
|
|
1321
|
+
}
|
|
1322
|
+
return path2.resolve(currentDir, binRelPath);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
lastDir = currentDir;
|
|
1326
|
+
currentDir = path2.dirname(currentDir);
|
|
1327
|
+
}
|
|
1328
|
+
throw new Error("Unable to locate drizzle-kit package root");
|
|
1329
|
+
};
|
|
149
1330
|
try {
|
|
150
1331
|
const env = {
|
|
151
1332
|
...process.env,
|
|
152
1333
|
__DRIZZLE_OUT_DIR__: OUT_DIR,
|
|
153
|
-
__DRIZZLE_SCHEMA_PATH__: SCHEMA_FILE
|
|
1334
|
+
__DRIZZLE_SCHEMA_PATH__: SCHEMA_FILE,
|
|
1335
|
+
DRIZZLE_SCHEMA_FILTER: options.schemaFilter ?? process.env.DRIZZLE_SCHEMA_FILTER,
|
|
1336
|
+
DRIZZLE_TABLES_FILTER: options.tablesFilter ?? process.env.DRIZZLE_TABLES_FILTER
|
|
154
1337
|
};
|
|
155
|
-
const
|
|
156
|
-
const spawnArgs = [
|
|
157
|
-
const result = spawnSync(
|
|
1338
|
+
const drizzleKitBin = resolveDrizzleKitBin();
|
|
1339
|
+
const spawnArgs = [drizzleKitBin, "introspect", "--config", configPath];
|
|
1340
|
+
const result = spawnSync(process.execPath, spawnArgs, { stdio: "inherit", env, cwd: process.cwd() });
|
|
158
1341
|
if (result.error) {
|
|
159
1342
|
console.error("[gen-db-schema] Execution failed:", result.error);
|
|
160
1343
|
throw result.error;
|
|
@@ -162,29 +1345,27 @@ async function run(options = {}) {
|
|
|
162
1345
|
if ((result.status ?? 0) !== 0) {
|
|
163
1346
|
throw new Error(`drizzle-kit introspect failed with status ${result.status}`);
|
|
164
1347
|
}
|
|
165
|
-
const generatedSchema =
|
|
166
|
-
if (!
|
|
1348
|
+
const generatedSchema = path2.join(OUT_DIR, "schema.ts");
|
|
1349
|
+
if (!fs3.existsSync(generatedSchema)) {
|
|
167
1350
|
console.error("[gen-db-schema] schema.ts not generated");
|
|
168
1351
|
throw new Error("drizzle-kit introspect failed to generate schema.ts");
|
|
169
1352
|
}
|
|
170
|
-
const { postprocessDrizzleSchema } = require2("@lark-apaas/devtool-kits");
|
|
171
1353
|
const stats = postprocessDrizzleSchema(generatedSchema);
|
|
172
1354
|
if (stats?.unmatchedUnknown?.length) {
|
|
173
1355
|
console.warn("[gen-db-schema] Unmatched custom types detected:", stats.unmatchedUnknown);
|
|
174
1356
|
}
|
|
175
1357
|
console.log("[gen-db-schema] \u2713 Postprocessed schema");
|
|
176
|
-
|
|
177
|
-
|
|
1358
|
+
fs3.mkdirSync(path2.dirname(SCHEMA_FILE), { recursive: true });
|
|
1359
|
+
fs3.copyFileSync(generatedSchema, SCHEMA_FILE);
|
|
178
1360
|
console.log(`[gen-db-schema] \u2713 Copied to ${outputPath}`);
|
|
179
1361
|
try {
|
|
180
1362
|
if (options.enableNestModuleGenerate) {
|
|
181
|
-
const
|
|
182
|
-
const tsConfigFilePath = path.resolve(process.cwd(), "tsconfig.json");
|
|
1363
|
+
const tsConfigFilePath = path2.resolve(process.cwd(), "tsconfig.json");
|
|
183
1364
|
const schemaFilePath = SCHEMA_FILE;
|
|
184
1365
|
await parseAndGenerateNestResourceTemplate({
|
|
185
1366
|
tsConfigFilePath,
|
|
186
1367
|
schemaFilePath,
|
|
187
|
-
moduleOutputDir:
|
|
1368
|
+
moduleOutputDir: path2.resolve(process.cwd(), "server/modules")
|
|
188
1369
|
});
|
|
189
1370
|
console.log("[gen-db-schema] \u2713 Generate NestJS Module Boilerplate Successfully");
|
|
190
1371
|
}
|
|
@@ -196,8 +1377,8 @@ async function run(options = {}) {
|
|
|
196
1377
|
console.error("[gen-db-schema] Failed:", err instanceof Error ? err.message : String(err));
|
|
197
1378
|
exitCode = 1;
|
|
198
1379
|
} finally {
|
|
199
|
-
if (
|
|
200
|
-
|
|
1380
|
+
if (fs3.existsSync(OUT_DIR)) {
|
|
1381
|
+
fs3.rmSync(OUT_DIR, { recursive: true, force: true });
|
|
201
1382
|
}
|
|
202
1383
|
process.exit(exitCode);
|
|
203
1384
|
}
|
|
@@ -215,9 +1396,9 @@ var genDbSchemaCommand = {
|
|
|
215
1396
|
};
|
|
216
1397
|
|
|
217
1398
|
// src/commands/sync/run.handler.ts
|
|
218
|
-
import
|
|
219
|
-
import
|
|
220
|
-
import { fileURLToPath as
|
|
1399
|
+
import path4 from "path";
|
|
1400
|
+
import fs5 from "fs";
|
|
1401
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
221
1402
|
|
|
222
1403
|
// src/config/sync.ts
|
|
223
1404
|
var syncConfig = {
|
|
@@ -280,38 +1461,38 @@ function genSyncConfig(perms = {}) {
|
|
|
280
1461
|
}
|
|
281
1462
|
|
|
282
1463
|
// src/utils/file-ops.ts
|
|
283
|
-
import
|
|
284
|
-
import
|
|
1464
|
+
import fs4 from "fs";
|
|
1465
|
+
import path3 from "path";
|
|
285
1466
|
function removeLineFromFile(filePath, pattern) {
|
|
286
|
-
if (!
|
|
287
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1467
|
+
if (!fs4.existsSync(filePath)) {
|
|
1468
|
+
console.log(`[fullstack-cli] \u25CB ${path3.basename(filePath)} (not found)`);
|
|
288
1469
|
return false;
|
|
289
1470
|
}
|
|
290
|
-
const content =
|
|
1471
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
291
1472
|
const lines = content.split("\n");
|
|
292
1473
|
const trimmedPattern = pattern.trim();
|
|
293
1474
|
const filteredLines = lines.filter((line) => line.trim() !== trimmedPattern);
|
|
294
1475
|
if (filteredLines.length === lines.length) {
|
|
295
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1476
|
+
console.log(`[fullstack-cli] \u25CB ${path3.basename(filePath)} (pattern not found: ${pattern})`);
|
|
296
1477
|
return false;
|
|
297
1478
|
}
|
|
298
|
-
|
|
299
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1479
|
+
fs4.writeFileSync(filePath, filteredLines.join("\n"));
|
|
1480
|
+
console.log(`[fullstack-cli] \u2713 ${path3.basename(filePath)} (removed: ${pattern})`);
|
|
300
1481
|
return true;
|
|
301
1482
|
}
|
|
302
1483
|
|
|
303
1484
|
// src/commands/sync/run.handler.ts
|
|
304
1485
|
async function run2(options) {
|
|
305
1486
|
const userProjectRoot = process.env.INIT_CWD || process.cwd();
|
|
306
|
-
const __filename =
|
|
307
|
-
const __dirname2 =
|
|
308
|
-
const pluginRoot =
|
|
1487
|
+
const __filename = fileURLToPath3(import.meta.url);
|
|
1488
|
+
const __dirname2 = path4.dirname(__filename);
|
|
1489
|
+
const pluginRoot = path4.resolve(__dirname2, "..");
|
|
309
1490
|
if (userProjectRoot === pluginRoot) {
|
|
310
1491
|
console.log("[fullstack-cli] Skip syncing (installing plugin itself)");
|
|
311
1492
|
process.exit(0);
|
|
312
1493
|
}
|
|
313
|
-
const userPackageJson =
|
|
314
|
-
if (!
|
|
1494
|
+
const userPackageJson = path4.join(userProjectRoot, "package.json");
|
|
1495
|
+
if (!fs5.existsSync(userPackageJson)) {
|
|
315
1496
|
console.log("[fullstack-cli] Skip syncing (not a valid npm project)");
|
|
316
1497
|
process.exit(0);
|
|
317
1498
|
}
|
|
@@ -339,7 +1520,7 @@ async function run2(options) {
|
|
|
339
1520
|
}
|
|
340
1521
|
async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
341
1522
|
if (rule.type === "delete-file" || rule.type === "delete-directory") {
|
|
342
|
-
const destPath2 =
|
|
1523
|
+
const destPath2 = path4.join(userProjectRoot, rule.to);
|
|
343
1524
|
if (rule.type === "delete-file") {
|
|
344
1525
|
deleteFile(destPath2);
|
|
345
1526
|
} else {
|
|
@@ -348,16 +1529,16 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
348
1529
|
return;
|
|
349
1530
|
}
|
|
350
1531
|
if (rule.type === "remove-line") {
|
|
351
|
-
const destPath2 =
|
|
1532
|
+
const destPath2 = path4.join(userProjectRoot, rule.to);
|
|
352
1533
|
removeLineFromFile(destPath2, rule.pattern);
|
|
353
1534
|
return;
|
|
354
1535
|
}
|
|
355
1536
|
if (!("from" in rule)) {
|
|
356
1537
|
return;
|
|
357
1538
|
}
|
|
358
|
-
const srcPath =
|
|
359
|
-
const destPath =
|
|
360
|
-
if (!
|
|
1539
|
+
const srcPath = path4.join(pluginRoot, rule.from);
|
|
1540
|
+
const destPath = path4.join(userProjectRoot, rule.to);
|
|
1541
|
+
if (!fs5.existsSync(srcPath)) {
|
|
361
1542
|
console.warn(`[fullstack-cli] Source not found: ${rule.from}`);
|
|
362
1543
|
return;
|
|
363
1544
|
}
|
|
@@ -374,64 +1555,64 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
374
1555
|
}
|
|
375
1556
|
}
|
|
376
1557
|
function syncFile(src, dest, overwrite = true) {
|
|
377
|
-
const destDir =
|
|
378
|
-
if (!
|
|
379
|
-
|
|
1558
|
+
const destDir = path4.dirname(dest);
|
|
1559
|
+
if (!fs5.existsSync(destDir)) {
|
|
1560
|
+
fs5.mkdirSync(destDir, { recursive: true });
|
|
380
1561
|
}
|
|
381
|
-
if (
|
|
382
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1562
|
+
if (fs5.existsSync(dest) && !overwrite) {
|
|
1563
|
+
console.log(`[fullstack-cli] \u25CB ${path4.basename(dest)} (skipped, already exists)`);
|
|
383
1564
|
return;
|
|
384
1565
|
}
|
|
385
|
-
|
|
386
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1566
|
+
fs5.copyFileSync(src, dest);
|
|
1567
|
+
console.log(`[fullstack-cli] \u2713 ${path4.basename(dest)}`);
|
|
387
1568
|
}
|
|
388
1569
|
function syncDirectory(src, dest, overwrite = true) {
|
|
389
|
-
if (!
|
|
390
|
-
|
|
1570
|
+
if (!fs5.existsSync(dest)) {
|
|
1571
|
+
fs5.mkdirSync(dest, { recursive: true });
|
|
391
1572
|
}
|
|
392
|
-
const files =
|
|
1573
|
+
const files = fs5.readdirSync(src);
|
|
393
1574
|
let count = 0;
|
|
394
1575
|
files.forEach((file) => {
|
|
395
|
-
const srcFile =
|
|
396
|
-
const destFile =
|
|
397
|
-
const stats =
|
|
1576
|
+
const srcFile = path4.join(src, file);
|
|
1577
|
+
const destFile = path4.join(dest, file);
|
|
1578
|
+
const stats = fs5.statSync(srcFile);
|
|
398
1579
|
if (stats.isDirectory()) {
|
|
399
1580
|
syncDirectory(srcFile, destFile, overwrite);
|
|
400
1581
|
} else {
|
|
401
|
-
if (overwrite || !
|
|
402
|
-
|
|
403
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1582
|
+
if (overwrite || !fs5.existsSync(destFile)) {
|
|
1583
|
+
fs5.copyFileSync(srcFile, destFile);
|
|
1584
|
+
console.log(`[fullstack-cli] \u2713 ${path4.relative(dest, destFile)}`);
|
|
404
1585
|
count++;
|
|
405
1586
|
}
|
|
406
1587
|
}
|
|
407
1588
|
});
|
|
408
1589
|
if (count > 0) {
|
|
409
|
-
console.log(`[fullstack-cli] Synced ${count} files to ${
|
|
1590
|
+
console.log(`[fullstack-cli] Synced ${count} files to ${path4.basename(dest)}/`);
|
|
410
1591
|
}
|
|
411
1592
|
}
|
|
412
1593
|
function appendToFile(src, dest) {
|
|
413
|
-
const content =
|
|
1594
|
+
const content = fs5.readFileSync(src, "utf-8");
|
|
414
1595
|
let existingContent = "";
|
|
415
|
-
if (
|
|
416
|
-
existingContent =
|
|
1596
|
+
if (fs5.existsSync(dest)) {
|
|
1597
|
+
existingContent = fs5.readFileSync(dest, "utf-8");
|
|
417
1598
|
}
|
|
418
1599
|
if (existingContent.includes(content.trim())) {
|
|
419
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1600
|
+
console.log(`[fullstack-cli] \u25CB ${path4.basename(dest)} (already contains content)`);
|
|
420
1601
|
return;
|
|
421
1602
|
}
|
|
422
|
-
|
|
423
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1603
|
+
fs5.appendFileSync(dest, content);
|
|
1604
|
+
console.log(`[fullstack-cli] \u2713 ${path4.basename(dest)} (appended)`);
|
|
424
1605
|
}
|
|
425
1606
|
function setPermissions(permissions, projectRoot) {
|
|
426
1607
|
for (const [pattern, mode] of Object.entries(permissions)) {
|
|
427
1608
|
if (pattern === "**/*.sh") {
|
|
428
|
-
const scriptsDir =
|
|
429
|
-
if (
|
|
430
|
-
const files =
|
|
1609
|
+
const scriptsDir = path4.join(projectRoot, "scripts");
|
|
1610
|
+
if (fs5.existsSync(scriptsDir)) {
|
|
1611
|
+
const files = fs5.readdirSync(scriptsDir);
|
|
431
1612
|
files.forEach((file) => {
|
|
432
1613
|
if (file.endsWith(".sh")) {
|
|
433
|
-
const filePath =
|
|
434
|
-
|
|
1614
|
+
const filePath = path4.join(scriptsDir, file);
|
|
1615
|
+
fs5.chmodSync(filePath, mode);
|
|
435
1616
|
}
|
|
436
1617
|
});
|
|
437
1618
|
}
|
|
@@ -439,19 +1620,19 @@ function setPermissions(permissions, projectRoot) {
|
|
|
439
1620
|
}
|
|
440
1621
|
}
|
|
441
1622
|
function deleteFile(filePath) {
|
|
442
|
-
if (
|
|
443
|
-
|
|
444
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1623
|
+
if (fs5.existsSync(filePath)) {
|
|
1624
|
+
fs5.unlinkSync(filePath);
|
|
1625
|
+
console.log(`[fullstack-cli] \u2713 ${path4.basename(filePath)} (deleted)`);
|
|
445
1626
|
} else {
|
|
446
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1627
|
+
console.log(`[fullstack-cli] \u25CB ${path4.basename(filePath)} (not found)`);
|
|
447
1628
|
}
|
|
448
1629
|
}
|
|
449
1630
|
function deleteDirectory(dirPath) {
|
|
450
|
-
if (
|
|
451
|
-
|
|
452
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1631
|
+
if (fs5.existsSync(dirPath)) {
|
|
1632
|
+
fs5.rmSync(dirPath, { recursive: true });
|
|
1633
|
+
console.log(`[fullstack-cli] \u2713 ${path4.basename(dirPath)} (deleted)`);
|
|
453
1634
|
} else {
|
|
454
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1635
|
+
console.log(`[fullstack-cli] \u25CB ${path4.basename(dirPath)} (not found)`);
|
|
455
1636
|
}
|
|
456
1637
|
}
|
|
457
1638
|
|
|
@@ -467,8 +1648,8 @@ var syncCommand = {
|
|
|
467
1648
|
};
|
|
468
1649
|
|
|
469
1650
|
// src/commands/action-plugin/utils.ts
|
|
470
|
-
import
|
|
471
|
-
import
|
|
1651
|
+
import fs6 from "fs";
|
|
1652
|
+
import path5 from "path";
|
|
472
1653
|
import { spawnSync as spawnSync2, execSync } from "child_process";
|
|
473
1654
|
function parsePluginName(input) {
|
|
474
1655
|
const match = input.match(/^(@[^/]+\/[^@]+)(?:@(.+))?$/);
|
|
@@ -486,18 +1667,18 @@ function getProjectRoot() {
|
|
|
486
1667
|
return process.cwd();
|
|
487
1668
|
}
|
|
488
1669
|
function getPackageJsonPath() {
|
|
489
|
-
return
|
|
1670
|
+
return path5.join(getProjectRoot(), "package.json");
|
|
490
1671
|
}
|
|
491
1672
|
function getPluginPath(pluginName) {
|
|
492
|
-
return
|
|
1673
|
+
return path5.join(getProjectRoot(), "node_modules", pluginName);
|
|
493
1674
|
}
|
|
494
1675
|
function readPackageJson() {
|
|
495
1676
|
const pkgPath = getPackageJsonPath();
|
|
496
|
-
if (!
|
|
1677
|
+
if (!fs6.existsSync(pkgPath)) {
|
|
497
1678
|
throw new Error("package.json not found in current directory");
|
|
498
1679
|
}
|
|
499
1680
|
try {
|
|
500
|
-
const content =
|
|
1681
|
+
const content = fs6.readFileSync(pkgPath, "utf-8");
|
|
501
1682
|
return JSON.parse(content);
|
|
502
1683
|
} catch {
|
|
503
1684
|
throw new Error("Failed to parse package.json");
|
|
@@ -505,7 +1686,7 @@ function readPackageJson() {
|
|
|
505
1686
|
}
|
|
506
1687
|
function writePackageJson(pkg2) {
|
|
507
1688
|
const pkgPath = getPackageJsonPath();
|
|
508
|
-
|
|
1689
|
+
fs6.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
509
1690
|
}
|
|
510
1691
|
function readActionPlugins() {
|
|
511
1692
|
const pkg2 = readPackageJson();
|
|
@@ -538,12 +1719,12 @@ function npmInstall(tgzPath) {
|
|
|
538
1719
|
}
|
|
539
1720
|
}
|
|
540
1721
|
function getPackageVersion(pluginName) {
|
|
541
|
-
const pkgJsonPath =
|
|
542
|
-
if (!
|
|
1722
|
+
const pkgJsonPath = path5.join(getPluginPath(pluginName), "package.json");
|
|
1723
|
+
if (!fs6.existsSync(pkgJsonPath)) {
|
|
543
1724
|
return null;
|
|
544
1725
|
}
|
|
545
1726
|
try {
|
|
546
|
-
const content =
|
|
1727
|
+
const content = fs6.readFileSync(pkgJsonPath, "utf-8");
|
|
547
1728
|
const pkg2 = JSON.parse(content);
|
|
548
1729
|
return pkg2.version || null;
|
|
549
1730
|
} catch {
|
|
@@ -551,49 +1732,49 @@ function getPackageVersion(pluginName) {
|
|
|
551
1732
|
}
|
|
552
1733
|
}
|
|
553
1734
|
function readPluginPackageJson(pluginPath) {
|
|
554
|
-
const pkgJsonPath =
|
|
555
|
-
if (!
|
|
1735
|
+
const pkgJsonPath = path5.join(pluginPath, "package.json");
|
|
1736
|
+
if (!fs6.existsSync(pkgJsonPath)) {
|
|
556
1737
|
return null;
|
|
557
1738
|
}
|
|
558
1739
|
try {
|
|
559
|
-
const content =
|
|
1740
|
+
const content = fs6.readFileSync(pkgJsonPath, "utf-8");
|
|
560
1741
|
return JSON.parse(content);
|
|
561
1742
|
} catch {
|
|
562
1743
|
return null;
|
|
563
1744
|
}
|
|
564
1745
|
}
|
|
565
1746
|
function extractTgzToNodeModules(tgzPath, pluginName) {
|
|
566
|
-
const nodeModulesPath =
|
|
567
|
-
const targetDir =
|
|
568
|
-
const scopeDir =
|
|
569
|
-
if (!
|
|
570
|
-
|
|
1747
|
+
const nodeModulesPath = path5.join(getProjectRoot(), "node_modules");
|
|
1748
|
+
const targetDir = path5.join(nodeModulesPath, pluginName);
|
|
1749
|
+
const scopeDir = path5.dirname(targetDir);
|
|
1750
|
+
if (!fs6.existsSync(scopeDir)) {
|
|
1751
|
+
fs6.mkdirSync(scopeDir, { recursive: true });
|
|
571
1752
|
}
|
|
572
|
-
if (
|
|
573
|
-
|
|
1753
|
+
if (fs6.existsSync(targetDir)) {
|
|
1754
|
+
fs6.rmSync(targetDir, { recursive: true });
|
|
574
1755
|
}
|
|
575
|
-
const tempDir =
|
|
576
|
-
if (
|
|
577
|
-
|
|
1756
|
+
const tempDir = path5.join(nodeModulesPath, ".cache", "fullstack-cli", "extract-temp");
|
|
1757
|
+
if (fs6.existsSync(tempDir)) {
|
|
1758
|
+
fs6.rmSync(tempDir, { recursive: true });
|
|
578
1759
|
}
|
|
579
|
-
|
|
1760
|
+
fs6.mkdirSync(tempDir, { recursive: true });
|
|
580
1761
|
try {
|
|
581
1762
|
execSync(`tar -xzf "${tgzPath}" -C "${tempDir}"`, { stdio: "pipe" });
|
|
582
|
-
const extractedDir =
|
|
583
|
-
if (
|
|
584
|
-
|
|
1763
|
+
const extractedDir = path5.join(tempDir, "package");
|
|
1764
|
+
if (fs6.existsSync(extractedDir)) {
|
|
1765
|
+
fs6.renameSync(extractedDir, targetDir);
|
|
585
1766
|
} else {
|
|
586
|
-
const files =
|
|
1767
|
+
const files = fs6.readdirSync(tempDir);
|
|
587
1768
|
if (files.length === 1) {
|
|
588
|
-
|
|
1769
|
+
fs6.renameSync(path5.join(tempDir, files[0]), targetDir);
|
|
589
1770
|
} else {
|
|
590
1771
|
throw new Error("Unexpected tgz structure");
|
|
591
1772
|
}
|
|
592
1773
|
}
|
|
593
1774
|
return targetDir;
|
|
594
1775
|
} finally {
|
|
595
|
-
if (
|
|
596
|
-
|
|
1776
|
+
if (fs6.existsSync(tempDir)) {
|
|
1777
|
+
fs6.rmSync(tempDir, { recursive: true });
|
|
597
1778
|
}
|
|
598
1779
|
}
|
|
599
1780
|
}
|
|
@@ -602,10 +1783,10 @@ function checkMissingPeerDeps(peerDeps) {
|
|
|
602
1783
|
return [];
|
|
603
1784
|
}
|
|
604
1785
|
const missing = [];
|
|
605
|
-
const nodeModulesPath =
|
|
1786
|
+
const nodeModulesPath = path5.join(getProjectRoot(), "node_modules");
|
|
606
1787
|
for (const [depName, _version] of Object.entries(peerDeps)) {
|
|
607
|
-
const depPath =
|
|
608
|
-
if (!
|
|
1788
|
+
const depPath = path5.join(nodeModulesPath, depName);
|
|
1789
|
+
if (!fs6.existsSync(depPath)) {
|
|
609
1790
|
missing.push(depName);
|
|
610
1791
|
}
|
|
611
1792
|
}
|
|
@@ -629,16 +1810,16 @@ function installMissingDeps(deps) {
|
|
|
629
1810
|
}
|
|
630
1811
|
function removePluginDirectory(pluginName) {
|
|
631
1812
|
const pluginPath = getPluginPath(pluginName);
|
|
632
|
-
if (
|
|
633
|
-
|
|
1813
|
+
if (fs6.existsSync(pluginPath)) {
|
|
1814
|
+
fs6.rmSync(pluginPath, { recursive: true });
|
|
634
1815
|
console.log(`[action-plugin] Removed ${pluginName}`);
|
|
635
1816
|
}
|
|
636
1817
|
}
|
|
637
1818
|
|
|
638
1819
|
// src/commands/action-plugin/api-client.ts
|
|
639
1820
|
import { HttpClient as HttpClient2 } from "@lark-apaas/http-client";
|
|
640
|
-
import
|
|
641
|
-
import
|
|
1821
|
+
import fs7 from "fs";
|
|
1822
|
+
import path6 from "path";
|
|
642
1823
|
|
|
643
1824
|
// src/utils/http-client.ts
|
|
644
1825
|
import { HttpClient } from "@lark-apaas/http-client";
|
|
@@ -723,19 +1904,19 @@ async function downloadFromPublic(downloadURL) {
|
|
|
723
1904
|
return Buffer.from(arrayBuffer);
|
|
724
1905
|
}
|
|
725
1906
|
function getPluginCacheDir() {
|
|
726
|
-
return
|
|
1907
|
+
return path6.join(process.cwd(), PLUGIN_CACHE_DIR);
|
|
727
1908
|
}
|
|
728
1909
|
function ensureCacheDir() {
|
|
729
1910
|
const cacheDir = getPluginCacheDir();
|
|
730
|
-
if (!
|
|
731
|
-
|
|
1911
|
+
if (!fs7.existsSync(cacheDir)) {
|
|
1912
|
+
fs7.mkdirSync(cacheDir, { recursive: true });
|
|
732
1913
|
}
|
|
733
1914
|
}
|
|
734
1915
|
function getTempFilePath(pluginKey, version) {
|
|
735
1916
|
ensureCacheDir();
|
|
736
1917
|
const safeKey = pluginKey.replace(/[/@]/g, "_");
|
|
737
1918
|
const filename = `${safeKey}-${version}.tgz`;
|
|
738
|
-
return
|
|
1919
|
+
return path6.join(getPluginCacheDir(), filename);
|
|
739
1920
|
}
|
|
740
1921
|
async function downloadPlugin(pluginKey, requestedVersion) {
|
|
741
1922
|
console.log(`[action-plugin] Fetching plugin info for ${pluginKey}@${requestedVersion}...`);
|
|
@@ -751,7 +1932,7 @@ async function downloadPlugin(pluginKey, requestedVersion) {
|
|
|
751
1932
|
tgzBuffer = await downloadFromPublic(pluginInfo.downloadURL);
|
|
752
1933
|
}
|
|
753
1934
|
const tgzPath = getTempFilePath(pluginKey, pluginInfo.version);
|
|
754
|
-
|
|
1935
|
+
fs7.writeFileSync(tgzPath, tgzBuffer);
|
|
755
1936
|
console.log(`[action-plugin] Downloaded to ${tgzPath} (${(tgzBuffer.length / 1024).toFixed(2)} KB)`);
|
|
756
1937
|
return {
|
|
757
1938
|
tgzPath,
|
|
@@ -761,8 +1942,8 @@ async function downloadPlugin(pluginKey, requestedVersion) {
|
|
|
761
1942
|
}
|
|
762
1943
|
function cleanupTempFile(tgzPath) {
|
|
763
1944
|
try {
|
|
764
|
-
if (
|
|
765
|
-
|
|
1945
|
+
if (fs7.existsSync(tgzPath)) {
|
|
1946
|
+
fs7.unlinkSync(tgzPath);
|
|
766
1947
|
}
|
|
767
1948
|
} catch {
|
|
768
1949
|
}
|
|
@@ -897,6 +2078,7 @@ async function install(namesWithVersion) {
|
|
|
897
2078
|
}
|
|
898
2079
|
const results = [];
|
|
899
2080
|
for (const nameWithVersion of namesWithVersion) {
|
|
2081
|
+
console.log(`[action-plugin] Processing ${nameWithVersion}...`);
|
|
900
2082
|
const result = await installOne(nameWithVersion);
|
|
901
2083
|
results.push(result);
|
|
902
2084
|
}
|
|
@@ -1097,39 +2279,39 @@ var actionPluginCommandGroup = {
|
|
|
1097
2279
|
};
|
|
1098
2280
|
|
|
1099
2281
|
// src/commands/capability/utils.ts
|
|
1100
|
-
import
|
|
1101
|
-
import
|
|
2282
|
+
import fs8 from "fs";
|
|
2283
|
+
import path7 from "path";
|
|
1102
2284
|
var CAPABILITIES_DIR = "server/capabilities";
|
|
1103
2285
|
function getProjectRoot2() {
|
|
1104
2286
|
return process.cwd();
|
|
1105
2287
|
}
|
|
1106
2288
|
function getCapabilitiesDir() {
|
|
1107
|
-
return
|
|
2289
|
+
return path7.join(getProjectRoot2(), CAPABILITIES_DIR);
|
|
1108
2290
|
}
|
|
1109
2291
|
function getCapabilityPath(id) {
|
|
1110
|
-
return
|
|
2292
|
+
return path7.join(getCapabilitiesDir(), `${id}.json`);
|
|
1111
2293
|
}
|
|
1112
2294
|
function getPluginManifestPath(pluginKey) {
|
|
1113
|
-
return
|
|
2295
|
+
return path7.join(getProjectRoot2(), "node_modules", pluginKey, "manifest.json");
|
|
1114
2296
|
}
|
|
1115
2297
|
function capabilitiesDirExists() {
|
|
1116
|
-
return
|
|
2298
|
+
return fs8.existsSync(getCapabilitiesDir());
|
|
1117
2299
|
}
|
|
1118
2300
|
function listCapabilityIds() {
|
|
1119
2301
|
const dir = getCapabilitiesDir();
|
|
1120
|
-
if (!
|
|
2302
|
+
if (!fs8.existsSync(dir)) {
|
|
1121
2303
|
return [];
|
|
1122
2304
|
}
|
|
1123
|
-
const files =
|
|
2305
|
+
const files = fs8.readdirSync(dir);
|
|
1124
2306
|
return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
|
|
1125
2307
|
}
|
|
1126
2308
|
function readCapability(id) {
|
|
1127
2309
|
const filePath = getCapabilityPath(id);
|
|
1128
|
-
if (!
|
|
2310
|
+
if (!fs8.existsSync(filePath)) {
|
|
1129
2311
|
throw new Error(`Capability not found: ${id}`);
|
|
1130
2312
|
}
|
|
1131
2313
|
try {
|
|
1132
|
-
const content =
|
|
2314
|
+
const content = fs8.readFileSync(filePath, "utf-8");
|
|
1133
2315
|
return JSON.parse(content);
|
|
1134
2316
|
} catch (error) {
|
|
1135
2317
|
if (error instanceof SyntaxError) {
|
|
@@ -1144,11 +2326,11 @@ function readAllCapabilities() {
|
|
|
1144
2326
|
}
|
|
1145
2327
|
function readPluginManifest(pluginKey) {
|
|
1146
2328
|
const manifestPath = getPluginManifestPath(pluginKey);
|
|
1147
|
-
if (!
|
|
2329
|
+
if (!fs8.existsSync(manifestPath)) {
|
|
1148
2330
|
throw new Error(`Plugin not installed: ${pluginKey} (manifest.json not found)`);
|
|
1149
2331
|
}
|
|
1150
2332
|
try {
|
|
1151
|
-
const content =
|
|
2333
|
+
const content = fs8.readFileSync(manifestPath, "utf-8");
|
|
1152
2334
|
return JSON.parse(content);
|
|
1153
2335
|
} catch (error) {
|
|
1154
2336
|
if (error instanceof SyntaxError) {
|
|
@@ -1322,58 +2504,58 @@ var capabilityCommandGroup = {
|
|
|
1322
2504
|
};
|
|
1323
2505
|
|
|
1324
2506
|
// src/commands/migration/version-manager.ts
|
|
1325
|
-
import
|
|
1326
|
-
import
|
|
2507
|
+
import fs9 from "fs";
|
|
2508
|
+
import path8 from "path";
|
|
1327
2509
|
var PACKAGE_JSON = "package.json";
|
|
1328
2510
|
var VERSION_FIELD = "migrationVersion";
|
|
1329
2511
|
function getPackageJsonPath2() {
|
|
1330
|
-
return
|
|
2512
|
+
return path8.join(process.cwd(), PACKAGE_JSON);
|
|
1331
2513
|
}
|
|
1332
2514
|
function getCurrentVersion() {
|
|
1333
2515
|
const pkgPath = getPackageJsonPath2();
|
|
1334
|
-
if (!
|
|
2516
|
+
if (!fs9.existsSync(pkgPath)) {
|
|
1335
2517
|
throw new Error("package.json not found");
|
|
1336
2518
|
}
|
|
1337
|
-
const pkg2 = JSON.parse(
|
|
2519
|
+
const pkg2 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
|
|
1338
2520
|
return pkg2[VERSION_FIELD] ?? 0;
|
|
1339
2521
|
}
|
|
1340
2522
|
function setCurrentVersion(version) {
|
|
1341
2523
|
const pkgPath = getPackageJsonPath2();
|
|
1342
|
-
const pkg2 = JSON.parse(
|
|
2524
|
+
const pkg2 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
|
|
1343
2525
|
pkg2[VERSION_FIELD] = version;
|
|
1344
|
-
|
|
2526
|
+
fs9.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
1345
2527
|
}
|
|
1346
2528
|
|
|
1347
2529
|
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
1348
|
-
import
|
|
1349
|
-
import
|
|
2530
|
+
import fs11 from "fs";
|
|
2531
|
+
import path10 from "path";
|
|
1350
2532
|
|
|
1351
2533
|
// src/commands/migration/versions/v001_capability/utils.ts
|
|
1352
|
-
import
|
|
1353
|
-
import
|
|
2534
|
+
import fs10 from "fs";
|
|
2535
|
+
import path9 from "path";
|
|
1354
2536
|
var CAPABILITIES_DIR2 = "server/capabilities";
|
|
1355
2537
|
function getProjectRoot3() {
|
|
1356
2538
|
return process.cwd();
|
|
1357
2539
|
}
|
|
1358
2540
|
function getCapabilitiesDir2() {
|
|
1359
|
-
return
|
|
2541
|
+
return path9.join(getProjectRoot3(), CAPABILITIES_DIR2);
|
|
1360
2542
|
}
|
|
1361
2543
|
function getPluginManifestPath2(pluginKey) {
|
|
1362
|
-
return
|
|
2544
|
+
return path9.join(getProjectRoot3(), "node_modules", pluginKey, "manifest.json");
|
|
1363
2545
|
}
|
|
1364
2546
|
|
|
1365
2547
|
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
1366
2548
|
function detectJsonMigration() {
|
|
1367
2549
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
1368
|
-
const oldFilePath =
|
|
1369
|
-
if (!
|
|
2550
|
+
const oldFilePath = path10.join(capabilitiesDir, "capabilities.json");
|
|
2551
|
+
if (!fs11.existsSync(oldFilePath)) {
|
|
1370
2552
|
return {
|
|
1371
2553
|
needsMigration: false,
|
|
1372
2554
|
reason: "capabilities.json not found"
|
|
1373
2555
|
};
|
|
1374
2556
|
}
|
|
1375
2557
|
try {
|
|
1376
|
-
const content =
|
|
2558
|
+
const content = fs11.readFileSync(oldFilePath, "utf-8");
|
|
1377
2559
|
const parsed = JSON.parse(content);
|
|
1378
2560
|
const capabilities = Array.isArray(parsed) ? parsed : [];
|
|
1379
2561
|
return {
|
|
@@ -1413,8 +2595,8 @@ async function check(options) {
|
|
|
1413
2595
|
}
|
|
1414
2596
|
|
|
1415
2597
|
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
1416
|
-
import
|
|
1417
|
-
import
|
|
2598
|
+
import fs12 from "fs";
|
|
2599
|
+
import path11 from "path";
|
|
1418
2600
|
|
|
1419
2601
|
// src/commands/migration/versions/v001_capability/mapping.ts
|
|
1420
2602
|
var DEFAULT_PLUGIN_VERSION = "1.0.0";
|
|
@@ -1644,18 +2826,18 @@ function transformCapabilities(oldCapabilities) {
|
|
|
1644
2826
|
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
1645
2827
|
function loadExistingCapabilities() {
|
|
1646
2828
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
1647
|
-
if (!
|
|
2829
|
+
if (!fs12.existsSync(capabilitiesDir)) {
|
|
1648
2830
|
return [];
|
|
1649
2831
|
}
|
|
1650
|
-
const files =
|
|
2832
|
+
const files = fs12.readdirSync(capabilitiesDir);
|
|
1651
2833
|
const capabilities = [];
|
|
1652
2834
|
for (const file of files) {
|
|
1653
2835
|
if (file === "capabilities.json" || !file.endsWith(".json")) {
|
|
1654
2836
|
continue;
|
|
1655
2837
|
}
|
|
1656
2838
|
try {
|
|
1657
|
-
const filePath =
|
|
1658
|
-
const content =
|
|
2839
|
+
const filePath = path11.join(capabilitiesDir, file);
|
|
2840
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
1659
2841
|
const capability = JSON.parse(content);
|
|
1660
2842
|
if (capability.id && capability.pluginKey) {
|
|
1661
2843
|
capabilities.push(capability);
|
|
@@ -1713,9 +2895,9 @@ async function migrateJsonFiles(options) {
|
|
|
1713
2895
|
}
|
|
1714
2896
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
1715
2897
|
for (const cap of newCapabilities) {
|
|
1716
|
-
const filePath =
|
|
2898
|
+
const filePath = path11.join(capabilitiesDir, `${cap.id}.json`);
|
|
1717
2899
|
const content = JSON.stringify(cap, null, 2);
|
|
1718
|
-
|
|
2900
|
+
fs12.writeFileSync(filePath, content, "utf-8");
|
|
1719
2901
|
console.log(` \u2713 Created: ${cap.id}.json`);
|
|
1720
2902
|
}
|
|
1721
2903
|
return {
|
|
@@ -1727,10 +2909,11 @@ async function migrateJsonFiles(options) {
|
|
|
1727
2909
|
}
|
|
1728
2910
|
|
|
1729
2911
|
// src/commands/migration/versions/v001_capability/plugin-installer/detector.ts
|
|
1730
|
-
import
|
|
2912
|
+
import fs13 from "fs";
|
|
1731
2913
|
function isPluginInstalled2(pluginKey) {
|
|
2914
|
+
const actionPlugins = readActionPlugins();
|
|
1732
2915
|
const manifestPath = getPluginManifestPath2(pluginKey);
|
|
1733
|
-
return
|
|
2916
|
+
return fs13.existsSync(manifestPath) && !!actionPlugins[pluginKey];
|
|
1734
2917
|
}
|
|
1735
2918
|
function detectPluginsToInstall(capabilities) {
|
|
1736
2919
|
const pluginKeys = /* @__PURE__ */ new Set();
|
|
@@ -1806,12 +2989,12 @@ async function installPlugins(capabilities, options) {
|
|
|
1806
2989
|
}
|
|
1807
2990
|
|
|
1808
2991
|
// src/commands/migration/versions/v001_capability/code-migrator/index.ts
|
|
1809
|
-
import
|
|
1810
|
-
import { Project } from "ts-morph";
|
|
2992
|
+
import path13 from "path";
|
|
2993
|
+
import { Project as Project2 } from "ts-morph";
|
|
1811
2994
|
|
|
1812
2995
|
// src/commands/migration/versions/v001_capability/code-migrator/scanner.ts
|
|
1813
|
-
import
|
|
1814
|
-
import
|
|
2996
|
+
import fs14 from "fs";
|
|
2997
|
+
import path12 from "path";
|
|
1815
2998
|
var EXCLUDED_DIRS = [
|
|
1816
2999
|
"node_modules",
|
|
1817
3000
|
"dist",
|
|
@@ -1826,9 +3009,9 @@ var EXCLUDED_PATTERNS = [
|
|
|
1826
3009
|
/\.d\.ts$/
|
|
1827
3010
|
];
|
|
1828
3011
|
function scanDirectory(dir, files = []) {
|
|
1829
|
-
const entries =
|
|
3012
|
+
const entries = fs14.readdirSync(dir, { withFileTypes: true });
|
|
1830
3013
|
for (const entry of entries) {
|
|
1831
|
-
const fullPath =
|
|
3014
|
+
const fullPath = path12.join(dir, entry.name);
|
|
1832
3015
|
if (entry.isDirectory()) {
|
|
1833
3016
|
if (EXCLUDED_DIRS.includes(entry.name)) {
|
|
1834
3017
|
continue;
|
|
@@ -1844,14 +3027,14 @@ function scanDirectory(dir, files = []) {
|
|
|
1844
3027
|
return files;
|
|
1845
3028
|
}
|
|
1846
3029
|
function scanServerFiles() {
|
|
1847
|
-
const serverDir =
|
|
1848
|
-
if (!
|
|
3030
|
+
const serverDir = path12.join(getProjectRoot3(), "server");
|
|
3031
|
+
if (!fs14.existsSync(serverDir)) {
|
|
1849
3032
|
return [];
|
|
1850
3033
|
}
|
|
1851
3034
|
return scanDirectory(serverDir);
|
|
1852
3035
|
}
|
|
1853
3036
|
function hasCapabilityImport(filePath) {
|
|
1854
|
-
const content =
|
|
3037
|
+
const content = fs14.readFileSync(filePath, "utf-8");
|
|
1855
3038
|
return /import\s+.*from\s+['"][^'"]*capabilities[^'"]*['"]/.test(content);
|
|
1856
3039
|
}
|
|
1857
3040
|
function scanFilesToMigrate() {
|
|
@@ -2226,7 +3409,7 @@ function analyzeFile(project, filePath, actionNameMap) {
|
|
|
2226
3409
|
const callSites = analyzeCallSites(sourceFile, imports);
|
|
2227
3410
|
const classInfo = analyzeClass(sourceFile);
|
|
2228
3411
|
const { canMigrate, reason } = canAutoMigrate(classInfo);
|
|
2229
|
-
const relativePath =
|
|
3412
|
+
const relativePath = path13.relative(getProjectRoot3(), filePath);
|
|
2230
3413
|
return {
|
|
2231
3414
|
filePath: relativePath,
|
|
2232
3415
|
imports,
|
|
@@ -2237,7 +3420,7 @@ function analyzeFile(project, filePath, actionNameMap) {
|
|
|
2237
3420
|
};
|
|
2238
3421
|
}
|
|
2239
3422
|
function migrateFile(project, analysis, dryRun) {
|
|
2240
|
-
const absolutePath =
|
|
3423
|
+
const absolutePath = path13.join(getProjectRoot3(), analysis.filePath);
|
|
2241
3424
|
if (!analysis.canAutoMigrate) {
|
|
2242
3425
|
return {
|
|
2243
3426
|
filePath: analysis.filePath,
|
|
@@ -2297,7 +3480,7 @@ async function migrateCode(options, capabilities) {
|
|
|
2297
3480
|
console.log(" No files need code migration.\n");
|
|
2298
3481
|
return result;
|
|
2299
3482
|
}
|
|
2300
|
-
const project = new
|
|
3483
|
+
const project = new Project2({
|
|
2301
3484
|
skipAddingFilesFromTsConfig: true,
|
|
2302
3485
|
compilerOptions: {
|
|
2303
3486
|
allowJs: true
|
|
@@ -2340,17 +3523,17 @@ function getSuggestion(analysis) {
|
|
|
2340
3523
|
}
|
|
2341
3524
|
|
|
2342
3525
|
// src/commands/migration/versions/v001_capability/cleanup.ts
|
|
2343
|
-
import
|
|
2344
|
-
import
|
|
3526
|
+
import fs15 from "fs";
|
|
3527
|
+
import path14 from "path";
|
|
2345
3528
|
function cleanupOldFiles(capabilities, dryRun) {
|
|
2346
3529
|
const deletedFiles = [];
|
|
2347
3530
|
const errors = [];
|
|
2348
3531
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
2349
|
-
const oldJsonPath =
|
|
2350
|
-
if (
|
|
3532
|
+
const oldJsonPath = path14.join(capabilitiesDir, "capabilities.json");
|
|
3533
|
+
if (fs15.existsSync(oldJsonPath)) {
|
|
2351
3534
|
try {
|
|
2352
3535
|
if (!dryRun) {
|
|
2353
|
-
|
|
3536
|
+
fs15.unlinkSync(oldJsonPath);
|
|
2354
3537
|
}
|
|
2355
3538
|
deletedFiles.push("capabilities.json");
|
|
2356
3539
|
} catch (error) {
|
|
@@ -2358,11 +3541,11 @@ function cleanupOldFiles(capabilities, dryRun) {
|
|
|
2358
3541
|
}
|
|
2359
3542
|
}
|
|
2360
3543
|
for (const cap of capabilities) {
|
|
2361
|
-
const tsFilePath =
|
|
2362
|
-
if (
|
|
3544
|
+
const tsFilePath = path14.join(capabilitiesDir, `${cap.id}.ts`);
|
|
3545
|
+
if (fs15.existsSync(tsFilePath)) {
|
|
2363
3546
|
try {
|
|
2364
3547
|
if (!dryRun) {
|
|
2365
|
-
|
|
3548
|
+
fs15.unlinkSync(tsFilePath);
|
|
2366
3549
|
}
|
|
2367
3550
|
deletedFiles.push(`${cap.id}.ts`);
|
|
2368
3551
|
} catch (error) {
|
|
@@ -2378,8 +3561,8 @@ function cleanupOldFiles(capabilities, dryRun) {
|
|
|
2378
3561
|
}
|
|
2379
3562
|
|
|
2380
3563
|
// src/commands/migration/versions/v001_capability/report-generator.ts
|
|
2381
|
-
import
|
|
2382
|
-
import
|
|
3564
|
+
import fs16 from "fs";
|
|
3565
|
+
import path15 from "path";
|
|
2383
3566
|
var REPORT_FILE = "capability-migration-report.md";
|
|
2384
3567
|
function printSummary(result) {
|
|
2385
3568
|
const { jsonMigration, pluginInstallation, codeMigration, cleanup } = result;
|
|
@@ -2542,15 +3725,15 @@ async function generateReport(result) {
|
|
|
2542
3725
|
}
|
|
2543
3726
|
lines.push("");
|
|
2544
3727
|
const logDir = process.env.LOG_DIR || "logs";
|
|
2545
|
-
if (!
|
|
3728
|
+
if (!fs16.existsSync(logDir)) {
|
|
2546
3729
|
return;
|
|
2547
3730
|
}
|
|
2548
|
-
const reportDir =
|
|
2549
|
-
if (!
|
|
2550
|
-
|
|
3731
|
+
const reportDir = path15.join(logDir, "migration");
|
|
3732
|
+
if (!fs16.existsSync(reportDir)) {
|
|
3733
|
+
fs16.mkdirSync(reportDir, { recursive: true });
|
|
2551
3734
|
}
|
|
2552
|
-
const reportPath =
|
|
2553
|
-
|
|
3735
|
+
const reportPath = path15.join(reportDir, REPORT_FILE);
|
|
3736
|
+
fs16.writeFileSync(reportPath, lines.join("\n"), "utf-8");
|
|
2554
3737
|
console.log(`\u{1F4C4} Report generated: ${reportPath}`);
|
|
2555
3738
|
}
|
|
2556
3739
|
|
|
@@ -3082,10 +4265,10 @@ var migrationCommand = {
|
|
|
3082
4265
|
};
|
|
3083
4266
|
|
|
3084
4267
|
// src/commands/read-logs/index.ts
|
|
3085
|
-
import
|
|
4268
|
+
import path16 from "path";
|
|
3086
4269
|
|
|
3087
4270
|
// src/commands/read-logs/std-utils.ts
|
|
3088
|
-
import
|
|
4271
|
+
import fs17 from "fs";
|
|
3089
4272
|
function formatStdPrefixTime(localTime) {
|
|
3090
4273
|
const match = localTime.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
|
|
3091
4274
|
if (!match) return localTime;
|
|
@@ -3115,11 +4298,11 @@ function stripPrefixFromStdLine(line) {
|
|
|
3115
4298
|
return `[${time}] ${content}`;
|
|
3116
4299
|
}
|
|
3117
4300
|
function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarker) {
|
|
3118
|
-
const stat =
|
|
4301
|
+
const stat = fs17.statSync(filePath);
|
|
3119
4302
|
if (stat.size === 0) {
|
|
3120
4303
|
return { lines: [], markerFound: false, totalLinesCount: 0 };
|
|
3121
4304
|
}
|
|
3122
|
-
const fd =
|
|
4305
|
+
const fd = fs17.openSync(filePath, "r");
|
|
3123
4306
|
const chunkSize = 64 * 1024;
|
|
3124
4307
|
let position = stat.size;
|
|
3125
4308
|
let remainder = "";
|
|
@@ -3133,7 +4316,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
|
|
|
3133
4316
|
const length = Math.min(chunkSize, position);
|
|
3134
4317
|
position -= length;
|
|
3135
4318
|
const buffer = Buffer.alloc(length);
|
|
3136
|
-
|
|
4319
|
+
fs17.readSync(fd, buffer, 0, length, position);
|
|
3137
4320
|
let chunk = buffer.toString("utf8");
|
|
3138
4321
|
if (remainder) {
|
|
3139
4322
|
chunk += remainder;
|
|
@@ -3175,7 +4358,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
|
|
|
3175
4358
|
}
|
|
3176
4359
|
}
|
|
3177
4360
|
} finally {
|
|
3178
|
-
|
|
4361
|
+
fs17.closeSync(fd);
|
|
3179
4362
|
}
|
|
3180
4363
|
return { lines: collected.reverse(), markerFound, totalLinesCount };
|
|
3181
4364
|
}
|
|
@@ -3196,21 +4379,21 @@ function readServerStdSegment(filePath, maxLines, offset) {
|
|
|
3196
4379
|
}
|
|
3197
4380
|
|
|
3198
4381
|
// src/commands/read-logs/tail.ts
|
|
3199
|
-
import
|
|
4382
|
+
import fs18 from "fs";
|
|
3200
4383
|
function fileExists(filePath) {
|
|
3201
4384
|
try {
|
|
3202
|
-
|
|
4385
|
+
fs18.accessSync(filePath, fs18.constants.F_OK | fs18.constants.R_OK);
|
|
3203
4386
|
return true;
|
|
3204
4387
|
} catch {
|
|
3205
4388
|
return false;
|
|
3206
4389
|
}
|
|
3207
4390
|
}
|
|
3208
4391
|
function readFileTailLines(filePath, maxLines) {
|
|
3209
|
-
const stat =
|
|
4392
|
+
const stat = fs18.statSync(filePath);
|
|
3210
4393
|
if (stat.size === 0) {
|
|
3211
4394
|
return [];
|
|
3212
4395
|
}
|
|
3213
|
-
const fd =
|
|
4396
|
+
const fd = fs18.openSync(filePath, "r");
|
|
3214
4397
|
const chunkSize = 64 * 1024;
|
|
3215
4398
|
const chunks = [];
|
|
3216
4399
|
let position = stat.size;
|
|
@@ -3220,13 +4403,13 @@ function readFileTailLines(filePath, maxLines) {
|
|
|
3220
4403
|
const length = Math.min(chunkSize, position);
|
|
3221
4404
|
position -= length;
|
|
3222
4405
|
const buffer = Buffer.alloc(length);
|
|
3223
|
-
|
|
4406
|
+
fs18.readSync(fd, buffer, 0, length, position);
|
|
3224
4407
|
chunks.unshift(buffer.toString("utf8"));
|
|
3225
4408
|
const chunkLines = buffer.toString("utf8").split("\n").length - 1;
|
|
3226
4409
|
collectedLines += chunkLines;
|
|
3227
4410
|
}
|
|
3228
4411
|
} finally {
|
|
3229
|
-
|
|
4412
|
+
fs18.closeSync(fd);
|
|
3230
4413
|
}
|
|
3231
4414
|
const content = chunks.join("");
|
|
3232
4415
|
const allLines = content.split("\n");
|
|
@@ -3242,11 +4425,11 @@ function readFileTailLines(filePath, maxLines) {
|
|
|
3242
4425
|
return allLines.slice(allLines.length - maxLines);
|
|
3243
4426
|
}
|
|
3244
4427
|
function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
3245
|
-
const stat =
|
|
4428
|
+
const stat = fs18.statSync(filePath);
|
|
3246
4429
|
if (stat.size === 0) {
|
|
3247
4430
|
return { lines: [], totalLinesCount: 0 };
|
|
3248
4431
|
}
|
|
3249
|
-
const fd =
|
|
4432
|
+
const fd = fs18.openSync(filePath, "r");
|
|
3250
4433
|
const chunkSize = 64 * 1024;
|
|
3251
4434
|
let position = stat.size;
|
|
3252
4435
|
let remainder = "";
|
|
@@ -3258,7 +4441,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
|
3258
4441
|
const length = Math.min(chunkSize, position);
|
|
3259
4442
|
position -= length;
|
|
3260
4443
|
const buffer = Buffer.alloc(length);
|
|
3261
|
-
|
|
4444
|
+
fs18.readSync(fd, buffer, 0, length, position);
|
|
3262
4445
|
let chunk = buffer.toString("utf8");
|
|
3263
4446
|
if (remainder) {
|
|
3264
4447
|
chunk += remainder;
|
|
@@ -3289,7 +4472,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
|
3289
4472
|
}
|
|
3290
4473
|
}
|
|
3291
4474
|
} finally {
|
|
3292
|
-
|
|
4475
|
+
fs18.closeSync(fd);
|
|
3293
4476
|
}
|
|
3294
4477
|
return { lines: collected.reverse(), totalLinesCount };
|
|
3295
4478
|
}
|
|
@@ -3391,7 +4574,7 @@ function extractClientStdSegment(lines, maxLines, offset) {
|
|
|
3391
4574
|
}
|
|
3392
4575
|
|
|
3393
4576
|
// src/commands/read-logs/json-lines.ts
|
|
3394
|
-
import
|
|
4577
|
+
import fs19 from "fs";
|
|
3395
4578
|
function normalizePid(value) {
|
|
3396
4579
|
if (typeof value === "number") {
|
|
3397
4580
|
return String(value);
|
|
@@ -3442,11 +4625,11 @@ function buildWantedLevelSet(levels) {
|
|
|
3442
4625
|
return set.size > 0 ? set : null;
|
|
3443
4626
|
}
|
|
3444
4627
|
function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
3445
|
-
const stat =
|
|
4628
|
+
const stat = fs19.statSync(filePath);
|
|
3446
4629
|
if (stat.size === 0) {
|
|
3447
4630
|
return { lines: [], totalLinesCount: 0 };
|
|
3448
4631
|
}
|
|
3449
|
-
const fd =
|
|
4632
|
+
const fd = fs19.openSync(filePath, "r");
|
|
3450
4633
|
const chunkSize = 64 * 1024;
|
|
3451
4634
|
let position = stat.size;
|
|
3452
4635
|
let remainder = "";
|
|
@@ -3461,7 +4644,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
|
3461
4644
|
const length = Math.min(chunkSize, position);
|
|
3462
4645
|
position -= length;
|
|
3463
4646
|
const buffer = Buffer.alloc(length);
|
|
3464
|
-
|
|
4647
|
+
fs19.readSync(fd, buffer, 0, length, position);
|
|
3465
4648
|
let chunk = buffer.toString("utf8");
|
|
3466
4649
|
if (remainder) {
|
|
3467
4650
|
chunk += remainder;
|
|
@@ -3523,7 +4706,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
|
3523
4706
|
}
|
|
3524
4707
|
}
|
|
3525
4708
|
} finally {
|
|
3526
|
-
|
|
4709
|
+
fs19.closeSync(fd);
|
|
3527
4710
|
}
|
|
3528
4711
|
return { lines: collected.reverse(), totalLinesCount };
|
|
3529
4712
|
}
|
|
@@ -3566,11 +4749,11 @@ function extractTraceId(obj) {
|
|
|
3566
4749
|
function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
3567
4750
|
const wanted = traceId.trim();
|
|
3568
4751
|
if (!wanted) return { lines: [], totalLinesCount: 0 };
|
|
3569
|
-
const stat =
|
|
4752
|
+
const stat = fs19.statSync(filePath);
|
|
3570
4753
|
if (stat.size === 0) {
|
|
3571
4754
|
return { lines: [], totalLinesCount: 0 };
|
|
3572
4755
|
}
|
|
3573
|
-
const fd =
|
|
4756
|
+
const fd = fs19.openSync(filePath, "r");
|
|
3574
4757
|
const chunkSize = 64 * 1024;
|
|
3575
4758
|
let position = stat.size;
|
|
3576
4759
|
let remainder = "";
|
|
@@ -3583,7 +4766,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
|
3583
4766
|
const length = Math.min(chunkSize, position);
|
|
3584
4767
|
position -= length;
|
|
3585
4768
|
const buffer = Buffer.alloc(length);
|
|
3586
|
-
|
|
4769
|
+
fs19.readSync(fd, buffer, 0, length, position);
|
|
3587
4770
|
let chunk = buffer.toString("utf8");
|
|
3588
4771
|
if (remainder) {
|
|
3589
4772
|
chunk += remainder;
|
|
@@ -3636,7 +4819,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
|
3636
4819
|
}
|
|
3637
4820
|
}
|
|
3638
4821
|
} finally {
|
|
3639
|
-
|
|
4822
|
+
fs19.closeSync(fd);
|
|
3640
4823
|
}
|
|
3641
4824
|
return { lines: collected.reverse(), totalLinesCount };
|
|
3642
4825
|
}
|
|
@@ -3645,11 +4828,11 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
3645
4828
|
if (!wantedLevelSet) {
|
|
3646
4829
|
return { lines: [], totalLinesCount: 0 };
|
|
3647
4830
|
}
|
|
3648
|
-
const stat =
|
|
4831
|
+
const stat = fs19.statSync(filePath);
|
|
3649
4832
|
if (stat.size === 0) {
|
|
3650
4833
|
return { lines: [], totalLinesCount: 0 };
|
|
3651
4834
|
}
|
|
3652
|
-
const fd =
|
|
4835
|
+
const fd = fs19.openSync(filePath, "r");
|
|
3653
4836
|
const chunkSize = 64 * 1024;
|
|
3654
4837
|
let position = stat.size;
|
|
3655
4838
|
let remainder = "";
|
|
@@ -3661,7 +4844,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
3661
4844
|
const length = Math.min(chunkSize, position);
|
|
3662
4845
|
position -= length;
|
|
3663
4846
|
const buffer = Buffer.alloc(length);
|
|
3664
|
-
|
|
4847
|
+
fs19.readSync(fd, buffer, 0, length, position);
|
|
3665
4848
|
let chunk = buffer.toString("utf8");
|
|
3666
4849
|
if (remainder) {
|
|
3667
4850
|
chunk += remainder;
|
|
@@ -3708,7 +4891,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
3708
4891
|
}
|
|
3709
4892
|
}
|
|
3710
4893
|
} finally {
|
|
3711
|
-
|
|
4894
|
+
fs19.closeSync(fd);
|
|
3712
4895
|
}
|
|
3713
4896
|
return { lines: collected.reverse(), totalLinesCount };
|
|
3714
4897
|
}
|
|
@@ -3839,21 +5022,21 @@ async function readLogsJsonResult(options) {
|
|
|
3839
5022
|
};
|
|
3840
5023
|
}
|
|
3841
5024
|
function resolveLogFilePath(logDir, type) {
|
|
3842
|
-
const base =
|
|
5025
|
+
const base = path16.isAbsolute(logDir) ? logDir : path16.join(process.cwd(), logDir);
|
|
3843
5026
|
if (type === "server") {
|
|
3844
|
-
return
|
|
5027
|
+
return path16.join(base, "server.log");
|
|
3845
5028
|
}
|
|
3846
5029
|
if (type === "trace") {
|
|
3847
|
-
return
|
|
5030
|
+
return path16.join(base, "trace.log");
|
|
3848
5031
|
}
|
|
3849
5032
|
if (type === "server-std") {
|
|
3850
|
-
return
|
|
5033
|
+
return path16.join(base, "server.std.log");
|
|
3851
5034
|
}
|
|
3852
5035
|
if (type === "client-std") {
|
|
3853
|
-
return
|
|
5036
|
+
return path16.join(base, "client.std.log");
|
|
3854
5037
|
}
|
|
3855
5038
|
if (type === "browser") {
|
|
3856
|
-
return
|
|
5039
|
+
return path16.join(base, "browser.log");
|
|
3857
5040
|
}
|
|
3858
5041
|
throw new Error(`Unsupported log type: ${type}`);
|
|
3859
5042
|
}
|
|
@@ -3928,12 +5111,12 @@ var commands = [
|
|
|
3928
5111
|
];
|
|
3929
5112
|
|
|
3930
5113
|
// src/index.ts
|
|
3931
|
-
var envPath =
|
|
3932
|
-
if (
|
|
5114
|
+
var envPath = path17.join(process.cwd(), ".env");
|
|
5115
|
+
if (fs20.existsSync(envPath)) {
|
|
3933
5116
|
dotenvConfig({ path: envPath });
|
|
3934
5117
|
}
|
|
3935
|
-
var __dirname =
|
|
3936
|
-
var pkg = JSON.parse(
|
|
5118
|
+
var __dirname = path17.dirname(fileURLToPath4(import.meta.url));
|
|
5119
|
+
var pkg = JSON.parse(fs20.readFileSync(path17.join(__dirname, "../package.json"), "utf-8"));
|
|
3937
5120
|
var cli = new FullstackCLI(pkg.version);
|
|
3938
5121
|
cli.useAll(commands);
|
|
3939
5122
|
cli.run();
|