@lark-apaas/fullstack-cli 1.1.13-alpha.1 → 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
|
}
|
|
@@ -1098,39 +2279,39 @@ var actionPluginCommandGroup = {
|
|
|
1098
2279
|
};
|
|
1099
2280
|
|
|
1100
2281
|
// src/commands/capability/utils.ts
|
|
1101
|
-
import
|
|
1102
|
-
import
|
|
2282
|
+
import fs8 from "fs";
|
|
2283
|
+
import path7 from "path";
|
|
1103
2284
|
var CAPABILITIES_DIR = "server/capabilities";
|
|
1104
2285
|
function getProjectRoot2() {
|
|
1105
2286
|
return process.cwd();
|
|
1106
2287
|
}
|
|
1107
2288
|
function getCapabilitiesDir() {
|
|
1108
|
-
return
|
|
2289
|
+
return path7.join(getProjectRoot2(), CAPABILITIES_DIR);
|
|
1109
2290
|
}
|
|
1110
2291
|
function getCapabilityPath(id) {
|
|
1111
|
-
return
|
|
2292
|
+
return path7.join(getCapabilitiesDir(), `${id}.json`);
|
|
1112
2293
|
}
|
|
1113
2294
|
function getPluginManifestPath(pluginKey) {
|
|
1114
|
-
return
|
|
2295
|
+
return path7.join(getProjectRoot2(), "node_modules", pluginKey, "manifest.json");
|
|
1115
2296
|
}
|
|
1116
2297
|
function capabilitiesDirExists() {
|
|
1117
|
-
return
|
|
2298
|
+
return fs8.existsSync(getCapabilitiesDir());
|
|
1118
2299
|
}
|
|
1119
2300
|
function listCapabilityIds() {
|
|
1120
2301
|
const dir = getCapabilitiesDir();
|
|
1121
|
-
if (!
|
|
2302
|
+
if (!fs8.existsSync(dir)) {
|
|
1122
2303
|
return [];
|
|
1123
2304
|
}
|
|
1124
|
-
const files =
|
|
2305
|
+
const files = fs8.readdirSync(dir);
|
|
1125
2306
|
return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
|
|
1126
2307
|
}
|
|
1127
2308
|
function readCapability(id) {
|
|
1128
2309
|
const filePath = getCapabilityPath(id);
|
|
1129
|
-
if (!
|
|
2310
|
+
if (!fs8.existsSync(filePath)) {
|
|
1130
2311
|
throw new Error(`Capability not found: ${id}`);
|
|
1131
2312
|
}
|
|
1132
2313
|
try {
|
|
1133
|
-
const content =
|
|
2314
|
+
const content = fs8.readFileSync(filePath, "utf-8");
|
|
1134
2315
|
return JSON.parse(content);
|
|
1135
2316
|
} catch (error) {
|
|
1136
2317
|
if (error instanceof SyntaxError) {
|
|
@@ -1145,11 +2326,11 @@ function readAllCapabilities() {
|
|
|
1145
2326
|
}
|
|
1146
2327
|
function readPluginManifest(pluginKey) {
|
|
1147
2328
|
const manifestPath = getPluginManifestPath(pluginKey);
|
|
1148
|
-
if (!
|
|
2329
|
+
if (!fs8.existsSync(manifestPath)) {
|
|
1149
2330
|
throw new Error(`Plugin not installed: ${pluginKey} (manifest.json not found)`);
|
|
1150
2331
|
}
|
|
1151
2332
|
try {
|
|
1152
|
-
const content =
|
|
2333
|
+
const content = fs8.readFileSync(manifestPath, "utf-8");
|
|
1153
2334
|
return JSON.parse(content);
|
|
1154
2335
|
} catch (error) {
|
|
1155
2336
|
if (error instanceof SyntaxError) {
|
|
@@ -1323,58 +2504,58 @@ var capabilityCommandGroup = {
|
|
|
1323
2504
|
};
|
|
1324
2505
|
|
|
1325
2506
|
// src/commands/migration/version-manager.ts
|
|
1326
|
-
import
|
|
1327
|
-
import
|
|
2507
|
+
import fs9 from "fs";
|
|
2508
|
+
import path8 from "path";
|
|
1328
2509
|
var PACKAGE_JSON = "package.json";
|
|
1329
2510
|
var VERSION_FIELD = "migrationVersion";
|
|
1330
2511
|
function getPackageJsonPath2() {
|
|
1331
|
-
return
|
|
2512
|
+
return path8.join(process.cwd(), PACKAGE_JSON);
|
|
1332
2513
|
}
|
|
1333
2514
|
function getCurrentVersion() {
|
|
1334
2515
|
const pkgPath = getPackageJsonPath2();
|
|
1335
|
-
if (!
|
|
2516
|
+
if (!fs9.existsSync(pkgPath)) {
|
|
1336
2517
|
throw new Error("package.json not found");
|
|
1337
2518
|
}
|
|
1338
|
-
const pkg2 = JSON.parse(
|
|
2519
|
+
const pkg2 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
|
|
1339
2520
|
return pkg2[VERSION_FIELD] ?? 0;
|
|
1340
2521
|
}
|
|
1341
2522
|
function setCurrentVersion(version) {
|
|
1342
2523
|
const pkgPath = getPackageJsonPath2();
|
|
1343
|
-
const pkg2 = JSON.parse(
|
|
2524
|
+
const pkg2 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
|
|
1344
2525
|
pkg2[VERSION_FIELD] = version;
|
|
1345
|
-
|
|
2526
|
+
fs9.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
1346
2527
|
}
|
|
1347
2528
|
|
|
1348
2529
|
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
1349
|
-
import
|
|
1350
|
-
import
|
|
2530
|
+
import fs11 from "fs";
|
|
2531
|
+
import path10 from "path";
|
|
1351
2532
|
|
|
1352
2533
|
// src/commands/migration/versions/v001_capability/utils.ts
|
|
1353
|
-
import
|
|
1354
|
-
import
|
|
2534
|
+
import fs10 from "fs";
|
|
2535
|
+
import path9 from "path";
|
|
1355
2536
|
var CAPABILITIES_DIR2 = "server/capabilities";
|
|
1356
2537
|
function getProjectRoot3() {
|
|
1357
2538
|
return process.cwd();
|
|
1358
2539
|
}
|
|
1359
2540
|
function getCapabilitiesDir2() {
|
|
1360
|
-
return
|
|
2541
|
+
return path9.join(getProjectRoot3(), CAPABILITIES_DIR2);
|
|
1361
2542
|
}
|
|
1362
2543
|
function getPluginManifestPath2(pluginKey) {
|
|
1363
|
-
return
|
|
2544
|
+
return path9.join(getProjectRoot3(), "node_modules", pluginKey, "manifest.json");
|
|
1364
2545
|
}
|
|
1365
2546
|
|
|
1366
2547
|
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
1367
2548
|
function detectJsonMigration() {
|
|
1368
2549
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
1369
|
-
const oldFilePath =
|
|
1370
|
-
if (!
|
|
2550
|
+
const oldFilePath = path10.join(capabilitiesDir, "capabilities.json");
|
|
2551
|
+
if (!fs11.existsSync(oldFilePath)) {
|
|
1371
2552
|
return {
|
|
1372
2553
|
needsMigration: false,
|
|
1373
2554
|
reason: "capabilities.json not found"
|
|
1374
2555
|
};
|
|
1375
2556
|
}
|
|
1376
2557
|
try {
|
|
1377
|
-
const content =
|
|
2558
|
+
const content = fs11.readFileSync(oldFilePath, "utf-8");
|
|
1378
2559
|
const parsed = JSON.parse(content);
|
|
1379
2560
|
const capabilities = Array.isArray(parsed) ? parsed : [];
|
|
1380
2561
|
return {
|
|
@@ -1414,8 +2595,8 @@ async function check(options) {
|
|
|
1414
2595
|
}
|
|
1415
2596
|
|
|
1416
2597
|
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
1417
|
-
import
|
|
1418
|
-
import
|
|
2598
|
+
import fs12 from "fs";
|
|
2599
|
+
import path11 from "path";
|
|
1419
2600
|
|
|
1420
2601
|
// src/commands/migration/versions/v001_capability/mapping.ts
|
|
1421
2602
|
var DEFAULT_PLUGIN_VERSION = "1.0.0";
|
|
@@ -1645,18 +2826,18 @@ function transformCapabilities(oldCapabilities) {
|
|
|
1645
2826
|
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
1646
2827
|
function loadExistingCapabilities() {
|
|
1647
2828
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
1648
|
-
if (!
|
|
2829
|
+
if (!fs12.existsSync(capabilitiesDir)) {
|
|
1649
2830
|
return [];
|
|
1650
2831
|
}
|
|
1651
|
-
const files =
|
|
2832
|
+
const files = fs12.readdirSync(capabilitiesDir);
|
|
1652
2833
|
const capabilities = [];
|
|
1653
2834
|
for (const file of files) {
|
|
1654
2835
|
if (file === "capabilities.json" || !file.endsWith(".json")) {
|
|
1655
2836
|
continue;
|
|
1656
2837
|
}
|
|
1657
2838
|
try {
|
|
1658
|
-
const filePath =
|
|
1659
|
-
const content =
|
|
2839
|
+
const filePath = path11.join(capabilitiesDir, file);
|
|
2840
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
1660
2841
|
const capability = JSON.parse(content);
|
|
1661
2842
|
if (capability.id && capability.pluginKey) {
|
|
1662
2843
|
capabilities.push(capability);
|
|
@@ -1714,9 +2895,9 @@ async function migrateJsonFiles(options) {
|
|
|
1714
2895
|
}
|
|
1715
2896
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
1716
2897
|
for (const cap of newCapabilities) {
|
|
1717
|
-
const filePath =
|
|
2898
|
+
const filePath = path11.join(capabilitiesDir, `${cap.id}.json`);
|
|
1718
2899
|
const content = JSON.stringify(cap, null, 2);
|
|
1719
|
-
|
|
2900
|
+
fs12.writeFileSync(filePath, content, "utf-8");
|
|
1720
2901
|
console.log(` \u2713 Created: ${cap.id}.json`);
|
|
1721
2902
|
}
|
|
1722
2903
|
return {
|
|
@@ -1728,11 +2909,11 @@ async function migrateJsonFiles(options) {
|
|
|
1728
2909
|
}
|
|
1729
2910
|
|
|
1730
2911
|
// src/commands/migration/versions/v001_capability/plugin-installer/detector.ts
|
|
1731
|
-
import
|
|
2912
|
+
import fs13 from "fs";
|
|
1732
2913
|
function isPluginInstalled2(pluginKey) {
|
|
1733
2914
|
const actionPlugins = readActionPlugins();
|
|
1734
2915
|
const manifestPath = getPluginManifestPath2(pluginKey);
|
|
1735
|
-
return
|
|
2916
|
+
return fs13.existsSync(manifestPath) && !!actionPlugins[pluginKey];
|
|
1736
2917
|
}
|
|
1737
2918
|
function detectPluginsToInstall(capabilities) {
|
|
1738
2919
|
const pluginKeys = /* @__PURE__ */ new Set();
|
|
@@ -1808,12 +2989,12 @@ async function installPlugins(capabilities, options) {
|
|
|
1808
2989
|
}
|
|
1809
2990
|
|
|
1810
2991
|
// src/commands/migration/versions/v001_capability/code-migrator/index.ts
|
|
1811
|
-
import
|
|
1812
|
-
import { Project } from "ts-morph";
|
|
2992
|
+
import path13 from "path";
|
|
2993
|
+
import { Project as Project2 } from "ts-morph";
|
|
1813
2994
|
|
|
1814
2995
|
// src/commands/migration/versions/v001_capability/code-migrator/scanner.ts
|
|
1815
|
-
import
|
|
1816
|
-
import
|
|
2996
|
+
import fs14 from "fs";
|
|
2997
|
+
import path12 from "path";
|
|
1817
2998
|
var EXCLUDED_DIRS = [
|
|
1818
2999
|
"node_modules",
|
|
1819
3000
|
"dist",
|
|
@@ -1828,9 +3009,9 @@ var EXCLUDED_PATTERNS = [
|
|
|
1828
3009
|
/\.d\.ts$/
|
|
1829
3010
|
];
|
|
1830
3011
|
function scanDirectory(dir, files = []) {
|
|
1831
|
-
const entries =
|
|
3012
|
+
const entries = fs14.readdirSync(dir, { withFileTypes: true });
|
|
1832
3013
|
for (const entry of entries) {
|
|
1833
|
-
const fullPath =
|
|
3014
|
+
const fullPath = path12.join(dir, entry.name);
|
|
1834
3015
|
if (entry.isDirectory()) {
|
|
1835
3016
|
if (EXCLUDED_DIRS.includes(entry.name)) {
|
|
1836
3017
|
continue;
|
|
@@ -1846,14 +3027,14 @@ function scanDirectory(dir, files = []) {
|
|
|
1846
3027
|
return files;
|
|
1847
3028
|
}
|
|
1848
3029
|
function scanServerFiles() {
|
|
1849
|
-
const serverDir =
|
|
1850
|
-
if (!
|
|
3030
|
+
const serverDir = path12.join(getProjectRoot3(), "server");
|
|
3031
|
+
if (!fs14.existsSync(serverDir)) {
|
|
1851
3032
|
return [];
|
|
1852
3033
|
}
|
|
1853
3034
|
return scanDirectory(serverDir);
|
|
1854
3035
|
}
|
|
1855
3036
|
function hasCapabilityImport(filePath) {
|
|
1856
|
-
const content =
|
|
3037
|
+
const content = fs14.readFileSync(filePath, "utf-8");
|
|
1857
3038
|
return /import\s+.*from\s+['"][^'"]*capabilities[^'"]*['"]/.test(content);
|
|
1858
3039
|
}
|
|
1859
3040
|
function scanFilesToMigrate() {
|
|
@@ -2228,7 +3409,7 @@ function analyzeFile(project, filePath, actionNameMap) {
|
|
|
2228
3409
|
const callSites = analyzeCallSites(sourceFile, imports);
|
|
2229
3410
|
const classInfo = analyzeClass(sourceFile);
|
|
2230
3411
|
const { canMigrate, reason } = canAutoMigrate(classInfo);
|
|
2231
|
-
const relativePath =
|
|
3412
|
+
const relativePath = path13.relative(getProjectRoot3(), filePath);
|
|
2232
3413
|
return {
|
|
2233
3414
|
filePath: relativePath,
|
|
2234
3415
|
imports,
|
|
@@ -2239,7 +3420,7 @@ function analyzeFile(project, filePath, actionNameMap) {
|
|
|
2239
3420
|
};
|
|
2240
3421
|
}
|
|
2241
3422
|
function migrateFile(project, analysis, dryRun) {
|
|
2242
|
-
const absolutePath =
|
|
3423
|
+
const absolutePath = path13.join(getProjectRoot3(), analysis.filePath);
|
|
2243
3424
|
if (!analysis.canAutoMigrate) {
|
|
2244
3425
|
return {
|
|
2245
3426
|
filePath: analysis.filePath,
|
|
@@ -2299,7 +3480,7 @@ async function migrateCode(options, capabilities) {
|
|
|
2299
3480
|
console.log(" No files need code migration.\n");
|
|
2300
3481
|
return result;
|
|
2301
3482
|
}
|
|
2302
|
-
const project = new
|
|
3483
|
+
const project = new Project2({
|
|
2303
3484
|
skipAddingFilesFromTsConfig: true,
|
|
2304
3485
|
compilerOptions: {
|
|
2305
3486
|
allowJs: true
|
|
@@ -2342,17 +3523,17 @@ function getSuggestion(analysis) {
|
|
|
2342
3523
|
}
|
|
2343
3524
|
|
|
2344
3525
|
// src/commands/migration/versions/v001_capability/cleanup.ts
|
|
2345
|
-
import
|
|
2346
|
-
import
|
|
3526
|
+
import fs15 from "fs";
|
|
3527
|
+
import path14 from "path";
|
|
2347
3528
|
function cleanupOldFiles(capabilities, dryRun) {
|
|
2348
3529
|
const deletedFiles = [];
|
|
2349
3530
|
const errors = [];
|
|
2350
3531
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
2351
|
-
const oldJsonPath =
|
|
2352
|
-
if (
|
|
3532
|
+
const oldJsonPath = path14.join(capabilitiesDir, "capabilities.json");
|
|
3533
|
+
if (fs15.existsSync(oldJsonPath)) {
|
|
2353
3534
|
try {
|
|
2354
3535
|
if (!dryRun) {
|
|
2355
|
-
|
|
3536
|
+
fs15.unlinkSync(oldJsonPath);
|
|
2356
3537
|
}
|
|
2357
3538
|
deletedFiles.push("capabilities.json");
|
|
2358
3539
|
} catch (error) {
|
|
@@ -2360,11 +3541,11 @@ function cleanupOldFiles(capabilities, dryRun) {
|
|
|
2360
3541
|
}
|
|
2361
3542
|
}
|
|
2362
3543
|
for (const cap of capabilities) {
|
|
2363
|
-
const tsFilePath =
|
|
2364
|
-
if (
|
|
3544
|
+
const tsFilePath = path14.join(capabilitiesDir, `${cap.id}.ts`);
|
|
3545
|
+
if (fs15.existsSync(tsFilePath)) {
|
|
2365
3546
|
try {
|
|
2366
3547
|
if (!dryRun) {
|
|
2367
|
-
|
|
3548
|
+
fs15.unlinkSync(tsFilePath);
|
|
2368
3549
|
}
|
|
2369
3550
|
deletedFiles.push(`${cap.id}.ts`);
|
|
2370
3551
|
} catch (error) {
|
|
@@ -2380,8 +3561,8 @@ function cleanupOldFiles(capabilities, dryRun) {
|
|
|
2380
3561
|
}
|
|
2381
3562
|
|
|
2382
3563
|
// src/commands/migration/versions/v001_capability/report-generator.ts
|
|
2383
|
-
import
|
|
2384
|
-
import
|
|
3564
|
+
import fs16 from "fs";
|
|
3565
|
+
import path15 from "path";
|
|
2385
3566
|
var REPORT_FILE = "capability-migration-report.md";
|
|
2386
3567
|
function printSummary(result) {
|
|
2387
3568
|
const { jsonMigration, pluginInstallation, codeMigration, cleanup } = result;
|
|
@@ -2544,15 +3725,15 @@ async function generateReport(result) {
|
|
|
2544
3725
|
}
|
|
2545
3726
|
lines.push("");
|
|
2546
3727
|
const logDir = process.env.LOG_DIR || "logs";
|
|
2547
|
-
if (!
|
|
3728
|
+
if (!fs16.existsSync(logDir)) {
|
|
2548
3729
|
return;
|
|
2549
3730
|
}
|
|
2550
|
-
const reportDir =
|
|
2551
|
-
if (!
|
|
2552
|
-
|
|
3731
|
+
const reportDir = path15.join(logDir, "migration");
|
|
3732
|
+
if (!fs16.existsSync(reportDir)) {
|
|
3733
|
+
fs16.mkdirSync(reportDir, { recursive: true });
|
|
2553
3734
|
}
|
|
2554
|
-
const reportPath =
|
|
2555
|
-
|
|
3735
|
+
const reportPath = path15.join(reportDir, REPORT_FILE);
|
|
3736
|
+
fs16.writeFileSync(reportPath, lines.join("\n"), "utf-8");
|
|
2556
3737
|
console.log(`\u{1F4C4} Report generated: ${reportPath}`);
|
|
2557
3738
|
}
|
|
2558
3739
|
|
|
@@ -3084,10 +4265,10 @@ var migrationCommand = {
|
|
|
3084
4265
|
};
|
|
3085
4266
|
|
|
3086
4267
|
// src/commands/read-logs/index.ts
|
|
3087
|
-
import
|
|
4268
|
+
import path16 from "path";
|
|
3088
4269
|
|
|
3089
4270
|
// src/commands/read-logs/std-utils.ts
|
|
3090
|
-
import
|
|
4271
|
+
import fs17 from "fs";
|
|
3091
4272
|
function formatStdPrefixTime(localTime) {
|
|
3092
4273
|
const match = localTime.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
|
|
3093
4274
|
if (!match) return localTime;
|
|
@@ -3117,11 +4298,11 @@ function stripPrefixFromStdLine(line) {
|
|
|
3117
4298
|
return `[${time}] ${content}`;
|
|
3118
4299
|
}
|
|
3119
4300
|
function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarker) {
|
|
3120
|
-
const stat =
|
|
4301
|
+
const stat = fs17.statSync(filePath);
|
|
3121
4302
|
if (stat.size === 0) {
|
|
3122
4303
|
return { lines: [], markerFound: false, totalLinesCount: 0 };
|
|
3123
4304
|
}
|
|
3124
|
-
const fd =
|
|
4305
|
+
const fd = fs17.openSync(filePath, "r");
|
|
3125
4306
|
const chunkSize = 64 * 1024;
|
|
3126
4307
|
let position = stat.size;
|
|
3127
4308
|
let remainder = "";
|
|
@@ -3135,7 +4316,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
|
|
|
3135
4316
|
const length = Math.min(chunkSize, position);
|
|
3136
4317
|
position -= length;
|
|
3137
4318
|
const buffer = Buffer.alloc(length);
|
|
3138
|
-
|
|
4319
|
+
fs17.readSync(fd, buffer, 0, length, position);
|
|
3139
4320
|
let chunk = buffer.toString("utf8");
|
|
3140
4321
|
if (remainder) {
|
|
3141
4322
|
chunk += remainder;
|
|
@@ -3177,7 +4358,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
|
|
|
3177
4358
|
}
|
|
3178
4359
|
}
|
|
3179
4360
|
} finally {
|
|
3180
|
-
|
|
4361
|
+
fs17.closeSync(fd);
|
|
3181
4362
|
}
|
|
3182
4363
|
return { lines: collected.reverse(), markerFound, totalLinesCount };
|
|
3183
4364
|
}
|
|
@@ -3198,21 +4379,21 @@ function readServerStdSegment(filePath, maxLines, offset) {
|
|
|
3198
4379
|
}
|
|
3199
4380
|
|
|
3200
4381
|
// src/commands/read-logs/tail.ts
|
|
3201
|
-
import
|
|
4382
|
+
import fs18 from "fs";
|
|
3202
4383
|
function fileExists(filePath) {
|
|
3203
4384
|
try {
|
|
3204
|
-
|
|
4385
|
+
fs18.accessSync(filePath, fs18.constants.F_OK | fs18.constants.R_OK);
|
|
3205
4386
|
return true;
|
|
3206
4387
|
} catch {
|
|
3207
4388
|
return false;
|
|
3208
4389
|
}
|
|
3209
4390
|
}
|
|
3210
4391
|
function readFileTailLines(filePath, maxLines) {
|
|
3211
|
-
const stat =
|
|
4392
|
+
const stat = fs18.statSync(filePath);
|
|
3212
4393
|
if (stat.size === 0) {
|
|
3213
4394
|
return [];
|
|
3214
4395
|
}
|
|
3215
|
-
const fd =
|
|
4396
|
+
const fd = fs18.openSync(filePath, "r");
|
|
3216
4397
|
const chunkSize = 64 * 1024;
|
|
3217
4398
|
const chunks = [];
|
|
3218
4399
|
let position = stat.size;
|
|
@@ -3222,13 +4403,13 @@ function readFileTailLines(filePath, maxLines) {
|
|
|
3222
4403
|
const length = Math.min(chunkSize, position);
|
|
3223
4404
|
position -= length;
|
|
3224
4405
|
const buffer = Buffer.alloc(length);
|
|
3225
|
-
|
|
4406
|
+
fs18.readSync(fd, buffer, 0, length, position);
|
|
3226
4407
|
chunks.unshift(buffer.toString("utf8"));
|
|
3227
4408
|
const chunkLines = buffer.toString("utf8").split("\n").length - 1;
|
|
3228
4409
|
collectedLines += chunkLines;
|
|
3229
4410
|
}
|
|
3230
4411
|
} finally {
|
|
3231
|
-
|
|
4412
|
+
fs18.closeSync(fd);
|
|
3232
4413
|
}
|
|
3233
4414
|
const content = chunks.join("");
|
|
3234
4415
|
const allLines = content.split("\n");
|
|
@@ -3244,11 +4425,11 @@ function readFileTailLines(filePath, maxLines) {
|
|
|
3244
4425
|
return allLines.slice(allLines.length - maxLines);
|
|
3245
4426
|
}
|
|
3246
4427
|
function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
3247
|
-
const stat =
|
|
4428
|
+
const stat = fs18.statSync(filePath);
|
|
3248
4429
|
if (stat.size === 0) {
|
|
3249
4430
|
return { lines: [], totalLinesCount: 0 };
|
|
3250
4431
|
}
|
|
3251
|
-
const fd =
|
|
4432
|
+
const fd = fs18.openSync(filePath, "r");
|
|
3252
4433
|
const chunkSize = 64 * 1024;
|
|
3253
4434
|
let position = stat.size;
|
|
3254
4435
|
let remainder = "";
|
|
@@ -3260,7 +4441,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
|
3260
4441
|
const length = Math.min(chunkSize, position);
|
|
3261
4442
|
position -= length;
|
|
3262
4443
|
const buffer = Buffer.alloc(length);
|
|
3263
|
-
|
|
4444
|
+
fs18.readSync(fd, buffer, 0, length, position);
|
|
3264
4445
|
let chunk = buffer.toString("utf8");
|
|
3265
4446
|
if (remainder) {
|
|
3266
4447
|
chunk += remainder;
|
|
@@ -3291,7 +4472,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
|
3291
4472
|
}
|
|
3292
4473
|
}
|
|
3293
4474
|
} finally {
|
|
3294
|
-
|
|
4475
|
+
fs18.closeSync(fd);
|
|
3295
4476
|
}
|
|
3296
4477
|
return { lines: collected.reverse(), totalLinesCount };
|
|
3297
4478
|
}
|
|
@@ -3393,7 +4574,7 @@ function extractClientStdSegment(lines, maxLines, offset) {
|
|
|
3393
4574
|
}
|
|
3394
4575
|
|
|
3395
4576
|
// src/commands/read-logs/json-lines.ts
|
|
3396
|
-
import
|
|
4577
|
+
import fs19 from "fs";
|
|
3397
4578
|
function normalizePid(value) {
|
|
3398
4579
|
if (typeof value === "number") {
|
|
3399
4580
|
return String(value);
|
|
@@ -3444,11 +4625,11 @@ function buildWantedLevelSet(levels) {
|
|
|
3444
4625
|
return set.size > 0 ? set : null;
|
|
3445
4626
|
}
|
|
3446
4627
|
function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
3447
|
-
const stat =
|
|
4628
|
+
const stat = fs19.statSync(filePath);
|
|
3448
4629
|
if (stat.size === 0) {
|
|
3449
4630
|
return { lines: [], totalLinesCount: 0 };
|
|
3450
4631
|
}
|
|
3451
|
-
const fd =
|
|
4632
|
+
const fd = fs19.openSync(filePath, "r");
|
|
3452
4633
|
const chunkSize = 64 * 1024;
|
|
3453
4634
|
let position = stat.size;
|
|
3454
4635
|
let remainder = "";
|
|
@@ -3463,7 +4644,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
|
3463
4644
|
const length = Math.min(chunkSize, position);
|
|
3464
4645
|
position -= length;
|
|
3465
4646
|
const buffer = Buffer.alloc(length);
|
|
3466
|
-
|
|
4647
|
+
fs19.readSync(fd, buffer, 0, length, position);
|
|
3467
4648
|
let chunk = buffer.toString("utf8");
|
|
3468
4649
|
if (remainder) {
|
|
3469
4650
|
chunk += remainder;
|
|
@@ -3525,7 +4706,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
|
3525
4706
|
}
|
|
3526
4707
|
}
|
|
3527
4708
|
} finally {
|
|
3528
|
-
|
|
4709
|
+
fs19.closeSync(fd);
|
|
3529
4710
|
}
|
|
3530
4711
|
return { lines: collected.reverse(), totalLinesCount };
|
|
3531
4712
|
}
|
|
@@ -3568,11 +4749,11 @@ function extractTraceId(obj) {
|
|
|
3568
4749
|
function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
3569
4750
|
const wanted = traceId.trim();
|
|
3570
4751
|
if (!wanted) return { lines: [], totalLinesCount: 0 };
|
|
3571
|
-
const stat =
|
|
4752
|
+
const stat = fs19.statSync(filePath);
|
|
3572
4753
|
if (stat.size === 0) {
|
|
3573
4754
|
return { lines: [], totalLinesCount: 0 };
|
|
3574
4755
|
}
|
|
3575
|
-
const fd =
|
|
4756
|
+
const fd = fs19.openSync(filePath, "r");
|
|
3576
4757
|
const chunkSize = 64 * 1024;
|
|
3577
4758
|
let position = stat.size;
|
|
3578
4759
|
let remainder = "";
|
|
@@ -3585,7 +4766,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
|
3585
4766
|
const length = Math.min(chunkSize, position);
|
|
3586
4767
|
position -= length;
|
|
3587
4768
|
const buffer = Buffer.alloc(length);
|
|
3588
|
-
|
|
4769
|
+
fs19.readSync(fd, buffer, 0, length, position);
|
|
3589
4770
|
let chunk = buffer.toString("utf8");
|
|
3590
4771
|
if (remainder) {
|
|
3591
4772
|
chunk += remainder;
|
|
@@ -3638,7 +4819,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
|
3638
4819
|
}
|
|
3639
4820
|
}
|
|
3640
4821
|
} finally {
|
|
3641
|
-
|
|
4822
|
+
fs19.closeSync(fd);
|
|
3642
4823
|
}
|
|
3643
4824
|
return { lines: collected.reverse(), totalLinesCount };
|
|
3644
4825
|
}
|
|
@@ -3647,11 +4828,11 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
3647
4828
|
if (!wantedLevelSet) {
|
|
3648
4829
|
return { lines: [], totalLinesCount: 0 };
|
|
3649
4830
|
}
|
|
3650
|
-
const stat =
|
|
4831
|
+
const stat = fs19.statSync(filePath);
|
|
3651
4832
|
if (stat.size === 0) {
|
|
3652
4833
|
return { lines: [], totalLinesCount: 0 };
|
|
3653
4834
|
}
|
|
3654
|
-
const fd =
|
|
4835
|
+
const fd = fs19.openSync(filePath, "r");
|
|
3655
4836
|
const chunkSize = 64 * 1024;
|
|
3656
4837
|
let position = stat.size;
|
|
3657
4838
|
let remainder = "";
|
|
@@ -3663,7 +4844,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
3663
4844
|
const length = Math.min(chunkSize, position);
|
|
3664
4845
|
position -= length;
|
|
3665
4846
|
const buffer = Buffer.alloc(length);
|
|
3666
|
-
|
|
4847
|
+
fs19.readSync(fd, buffer, 0, length, position);
|
|
3667
4848
|
let chunk = buffer.toString("utf8");
|
|
3668
4849
|
if (remainder) {
|
|
3669
4850
|
chunk += remainder;
|
|
@@ -3710,7 +4891,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
3710
4891
|
}
|
|
3711
4892
|
}
|
|
3712
4893
|
} finally {
|
|
3713
|
-
|
|
4894
|
+
fs19.closeSync(fd);
|
|
3714
4895
|
}
|
|
3715
4896
|
return { lines: collected.reverse(), totalLinesCount };
|
|
3716
4897
|
}
|
|
@@ -3841,21 +5022,21 @@ async function readLogsJsonResult(options) {
|
|
|
3841
5022
|
};
|
|
3842
5023
|
}
|
|
3843
5024
|
function resolveLogFilePath(logDir, type) {
|
|
3844
|
-
const base =
|
|
5025
|
+
const base = path16.isAbsolute(logDir) ? logDir : path16.join(process.cwd(), logDir);
|
|
3845
5026
|
if (type === "server") {
|
|
3846
|
-
return
|
|
5027
|
+
return path16.join(base, "server.log");
|
|
3847
5028
|
}
|
|
3848
5029
|
if (type === "trace") {
|
|
3849
|
-
return
|
|
5030
|
+
return path16.join(base, "trace.log");
|
|
3850
5031
|
}
|
|
3851
5032
|
if (type === "server-std") {
|
|
3852
|
-
return
|
|
5033
|
+
return path16.join(base, "server.std.log");
|
|
3853
5034
|
}
|
|
3854
5035
|
if (type === "client-std") {
|
|
3855
|
-
return
|
|
5036
|
+
return path16.join(base, "client.std.log");
|
|
3856
5037
|
}
|
|
3857
5038
|
if (type === "browser") {
|
|
3858
|
-
return
|
|
5039
|
+
return path16.join(base, "browser.log");
|
|
3859
5040
|
}
|
|
3860
5041
|
throw new Error(`Unsupported log type: ${type}`);
|
|
3861
5042
|
}
|
|
@@ -3930,12 +5111,12 @@ var commands = [
|
|
|
3930
5111
|
];
|
|
3931
5112
|
|
|
3932
5113
|
// src/index.ts
|
|
3933
|
-
var envPath =
|
|
3934
|
-
if (
|
|
5114
|
+
var envPath = path17.join(process.cwd(), ".env");
|
|
5115
|
+
if (fs20.existsSync(envPath)) {
|
|
3935
5116
|
dotenvConfig({ path: envPath });
|
|
3936
5117
|
}
|
|
3937
|
-
var __dirname =
|
|
3938
|
-
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"));
|
|
3939
5120
|
var cli = new FullstackCLI(pkg.version);
|
|
3940
5121
|
cli.useAll(commands);
|
|
3941
5122
|
cli.run();
|