@lark-apaas/fullstack-cli 1.1.11 → 1.1.12-alpha.1
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 +1366 -228
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import fs19 from "fs";
|
|
3
|
+
import path17 from "path";
|
|
4
4
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
5
5
|
import { config as dotenvConfig } from "dotenv";
|
|
6
6
|
|
|
@@ -116,17 +116,1157 @@ Command "${ctx.commandName}" completed in ${elapsed}ms`);
|
|
|
116
116
|
};
|
|
117
117
|
|
|
118
118
|
// src/commands/db/schema.handler.ts
|
|
119
|
-
import
|
|
120
|
-
import
|
|
119
|
+
import path2 from "path";
|
|
120
|
+
import fs2 from "fs";
|
|
121
121
|
import { fileURLToPath } from "url";
|
|
122
122
|
import { spawnSync } from "child_process";
|
|
123
|
-
import { createRequire } from "module";
|
|
124
123
|
import { config as loadEnv } from "dotenv";
|
|
125
|
-
|
|
124
|
+
|
|
125
|
+
// src/internal/postprocess-drizzle-schema.ts
|
|
126
|
+
import fs from "fs";
|
|
127
|
+
import path from "path";
|
|
128
|
+
import { pinyin } from "pinyin-pro";
|
|
129
|
+
var HEADER_COMMENT = "/** auto generated, do not edit */";
|
|
130
|
+
var CUSTOM_TYPE_PATTERN = /\/\/ TODO: failed to parse database type '(?:\w+\.)?(user_profile|file_attachment)(\[\])?'/;
|
|
131
|
+
var TEMPLATE_TYPES_TS = `import { sql } from 'drizzle-orm';
|
|
132
|
+
import { customType } from 'drizzle-orm/pg-core';
|
|
133
|
+
|
|
134
|
+
export const userProfile = customType<{
|
|
135
|
+
data: string;
|
|
136
|
+
driverData: string;
|
|
137
|
+
}>({
|
|
138
|
+
dataType() {
|
|
139
|
+
return 'user_profile';
|
|
140
|
+
},
|
|
141
|
+
toDriver(value: string) {
|
|
142
|
+
return sql\`ROW(\${value})::user_profile\`;
|
|
143
|
+
},
|
|
144
|
+
fromDriver(value: string) {
|
|
145
|
+
const [userId] = value.slice(1, -1).split(',');
|
|
146
|
+
return userId.trim();
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
export type FileAttachment = {
|
|
151
|
+
bucket_id: string;
|
|
152
|
+
file_path: string;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export const fileAttachment = customType<{
|
|
156
|
+
data: FileAttachment;
|
|
157
|
+
driverData: string;
|
|
158
|
+
}>({
|
|
159
|
+
dataType() {
|
|
160
|
+
return 'file_attachment';
|
|
161
|
+
},
|
|
162
|
+
toDriver(value: FileAttachment) {
|
|
163
|
+
return sql\`ROW(\${value.bucket_id},\${value.file_path})::file_attachment\`;
|
|
164
|
+
},
|
|
165
|
+
fromDriver(value: string): FileAttachment {
|
|
166
|
+
const [bucketId, filePath] = value.slice(1, -1).split(',');
|
|
167
|
+
return { bucket_id: bucketId.trim(), file_path: filePath.trim() };
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
export const customTimestamptz = customType<{
|
|
172
|
+
data: Date;
|
|
173
|
+
driverData: string;
|
|
174
|
+
config: { precision?: number};
|
|
175
|
+
}>({
|
|
176
|
+
dataType(config) {
|
|
177
|
+
const precision = typeof config?.precision !== 'undefined'
|
|
178
|
+
? \` (\${config.precision})\`
|
|
179
|
+
: '';
|
|
180
|
+
return \`timestamptz\${precision}\`;
|
|
181
|
+
},
|
|
182
|
+
toDriver(value: Date | string | number){
|
|
183
|
+
if(value == null) return value as any;
|
|
184
|
+
if (typeof value === 'number') {
|
|
185
|
+
return new Date(value).toISOString();
|
|
186
|
+
}
|
|
187
|
+
if(typeof value === 'string') {
|
|
188
|
+
return value;
|
|
189
|
+
}
|
|
190
|
+
if (value instanceof Date) {
|
|
191
|
+
return value.toISOString();
|
|
192
|
+
}
|
|
193
|
+
throw new Error('Invalid timestamp value');
|
|
194
|
+
},
|
|
195
|
+
fromDriver(value: string | Date): Date {
|
|
196
|
+
if(value instanceof Date) return value;
|
|
197
|
+
return new Date(value);
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
`;
|
|
201
|
+
function stripLeadingNewlines(value) {
|
|
202
|
+
let current = value;
|
|
203
|
+
while (current.startsWith("\r\n") || current.startsWith("\n")) {
|
|
204
|
+
current = current.startsWith("\r\n") ? current.slice(2) : current.slice(1);
|
|
205
|
+
}
|
|
206
|
+
return current;
|
|
207
|
+
}
|
|
208
|
+
function ensureHeaderComment(source) {
|
|
209
|
+
let text = source.startsWith("\uFEFF") ? source.slice(1) : source;
|
|
210
|
+
while (text.startsWith(HEADER_COMMENT)) {
|
|
211
|
+
text = text.slice(HEADER_COMMENT.length);
|
|
212
|
+
text = stripLeadingNewlines(text);
|
|
213
|
+
}
|
|
214
|
+
const trimmed = stripLeadingNewlines(text);
|
|
215
|
+
if (trimmed.length === 0) {
|
|
216
|
+
return `${HEADER_COMMENT}
|
|
217
|
+
`;
|
|
218
|
+
}
|
|
219
|
+
return `${HEADER_COMMENT}
|
|
220
|
+
${trimmed}`;
|
|
221
|
+
}
|
|
222
|
+
function collapseExtraBlankLines(text) {
|
|
223
|
+
return text.replace(/\n{3,}/g, "\n\n");
|
|
224
|
+
}
|
|
225
|
+
function patchDrizzleKitDefects(source) {
|
|
226
|
+
let fixed = 0;
|
|
227
|
+
const text = source.replace(/\.default\('\)/g, () => {
|
|
228
|
+
fixed += 1;
|
|
229
|
+
return `.default('')`;
|
|
230
|
+
});
|
|
231
|
+
return { text, fixed };
|
|
232
|
+
}
|
|
233
|
+
function removePgSchemaDeclarations(source) {
|
|
234
|
+
return source.replace(/export const \w+ = pgSchema\([\s\S]*?\);\n*/g, "");
|
|
235
|
+
}
|
|
236
|
+
function convertSchemaTableInvocations(source) {
|
|
237
|
+
let converted = 0;
|
|
238
|
+
let text = source.replace(/([A-Za-z0-9_]+)\.table\(/g, () => {
|
|
239
|
+
converted += 1;
|
|
240
|
+
return "pgTable(";
|
|
241
|
+
});
|
|
242
|
+
text = text.replace(/([A-Za-z0-9_]+)\.view\(/g, () => {
|
|
243
|
+
converted += 1;
|
|
244
|
+
return "pgView(";
|
|
245
|
+
});
|
|
246
|
+
text = text.replace(/([A-Za-z0-9_]+)\.materializedView\(/g, () => {
|
|
247
|
+
converted += 1;
|
|
248
|
+
return "pgMaterializedView(";
|
|
249
|
+
});
|
|
250
|
+
text = text.replace(/([A-Za-z0-9_]+)\.enum\(/g, () => {
|
|
251
|
+
converted += 1;
|
|
252
|
+
return "pgEnum(";
|
|
253
|
+
});
|
|
254
|
+
text = text.replace(/([A-Za-z0-9_]+)\.sequence\(/g, () => {
|
|
255
|
+
converted += 1;
|
|
256
|
+
return "pgSequence(";
|
|
257
|
+
});
|
|
258
|
+
return { text, converted };
|
|
259
|
+
}
|
|
260
|
+
function escapeRegExp(value) {
|
|
261
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\//g, "\\/");
|
|
262
|
+
}
|
|
263
|
+
function toCamelCase(str) {
|
|
264
|
+
const words = str.split(/[_\-\s]+/).filter(Boolean);
|
|
265
|
+
if (words.length === 0) {
|
|
266
|
+
return "";
|
|
267
|
+
}
|
|
268
|
+
return words.map((word, index) => {
|
|
269
|
+
if (index === 0) {
|
|
270
|
+
return word.toLowerCase();
|
|
271
|
+
}
|
|
272
|
+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
273
|
+
}).join("");
|
|
274
|
+
}
|
|
275
|
+
function toAsciiName(name) {
|
|
276
|
+
let hasNonAscii = false;
|
|
277
|
+
for (let i = 0; i < name.length; i += 1) {
|
|
278
|
+
if (name.charCodeAt(i) > 127) {
|
|
279
|
+
hasNonAscii = true;
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (!hasNonAscii) {
|
|
284
|
+
return name;
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
const transliterated = pinyin(name, { toneType: "none", type: "array" }).join("_");
|
|
288
|
+
return transliterated || name;
|
|
289
|
+
} catch {
|
|
290
|
+
return name;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function sanitizeIdentifier(name) {
|
|
294
|
+
const asciiName = toAsciiName(name);
|
|
295
|
+
let sanitized = asciiName.replace(/[^A-Za-z0-9_]/g, "_");
|
|
296
|
+
sanitized = sanitized.replace(/_+/g, "_");
|
|
297
|
+
sanitized = sanitized.replace(/^_|_$/g, "");
|
|
298
|
+
sanitized = toCamelCase(sanitized);
|
|
299
|
+
if (!sanitized) {
|
|
300
|
+
sanitized = "table";
|
|
301
|
+
}
|
|
302
|
+
if (!/^[A-Za-z_]/.test(sanitized)) {
|
|
303
|
+
sanitized = `_${sanitized}`;
|
|
304
|
+
}
|
|
305
|
+
return sanitized;
|
|
306
|
+
}
|
|
307
|
+
function renamePgTableConstants(source) {
|
|
308
|
+
const pgTableRegex = /export const\s+([^\s=]+)\s*=\s*(pgTable|pgView|pgMaterializedView)\(\s*["'`]([^"'`]+)["'`]/gu;
|
|
309
|
+
const renames = [];
|
|
310
|
+
const updated = source.replace(pgTableRegex, (match, currentName, factory, tableName) => {
|
|
311
|
+
const sanitized = sanitizeIdentifier(tableName);
|
|
312
|
+
if (sanitized === currentName) {
|
|
313
|
+
return match;
|
|
314
|
+
}
|
|
315
|
+
renames.push({ from: currentName, to: sanitized });
|
|
316
|
+
const equalsIndex = match.indexOf("=");
|
|
317
|
+
const suffix = equalsIndex >= 0 ? match.slice(equalsIndex) : ` = ${factory}("${tableName}"`;
|
|
318
|
+
const normalizedSuffix = suffix.trimStart();
|
|
319
|
+
return `export const ${sanitized} ${normalizedSuffix}`;
|
|
320
|
+
});
|
|
321
|
+
return { text: updated, renames };
|
|
322
|
+
}
|
|
323
|
+
function updateTableReferenceIdentifiers(source, renames) {
|
|
324
|
+
if (renames.length === 0) {
|
|
325
|
+
return source;
|
|
326
|
+
}
|
|
327
|
+
return renames.reduce((acc, rename) => {
|
|
328
|
+
if (!rename.from || rename.from === rename.to) {
|
|
329
|
+
return acc;
|
|
330
|
+
}
|
|
331
|
+
const pattern = new RegExp(`\\b${escapeRegExp(rename.from)}(\\s*\\.)`, "g");
|
|
332
|
+
return acc.replace(pattern, `${rename.to}$1`);
|
|
333
|
+
}, source);
|
|
334
|
+
}
|
|
335
|
+
function replaceFollowingUnknown(nextLine, factory) {
|
|
336
|
+
if (!nextLine || !nextLine.includes("unknown(")) {
|
|
337
|
+
return void 0;
|
|
338
|
+
}
|
|
339
|
+
return nextLine.replace("unknown(", `${factory}(`);
|
|
340
|
+
}
|
|
341
|
+
function replaceUnknownColumns(source) {
|
|
342
|
+
const lines = source.split("\n");
|
|
343
|
+
const result = [];
|
|
344
|
+
let replaced = 0;
|
|
345
|
+
const unmatched = [];
|
|
346
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
347
|
+
const line = lines[i];
|
|
348
|
+
const match = line.match(CUSTOM_TYPE_PATTERN);
|
|
349
|
+
if (match) {
|
|
350
|
+
const typeName = match[1];
|
|
351
|
+
const factory = typeName === "user_profile" ? "userProfile" : "fileAttachment";
|
|
352
|
+
const replacedLine = replaceFollowingUnknown(lines[i + 1], factory);
|
|
353
|
+
if (replacedLine) {
|
|
354
|
+
result.push(replacedLine);
|
|
355
|
+
replaced += 1;
|
|
356
|
+
i += 1;
|
|
357
|
+
} else {
|
|
358
|
+
unmatched.push(line.trim());
|
|
359
|
+
result.push(line);
|
|
360
|
+
}
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
if (line.includes("unknown(")) {
|
|
364
|
+
unmatched.push(line.trim());
|
|
365
|
+
}
|
|
366
|
+
result.push(line);
|
|
367
|
+
}
|
|
368
|
+
return { text: result.join("\n"), replaced, unmatched };
|
|
369
|
+
}
|
|
370
|
+
function ensureImportIdentifier(source, packageName, identifier) {
|
|
371
|
+
const escapedPackage = packageName.replace(/\//g, "\\/");
|
|
372
|
+
const importRegex = new RegExp(`import \\{([^}]*)\\} from ["']${escapedPackage}["'];?`);
|
|
373
|
+
const match = source.match(importRegex);
|
|
374
|
+
if (!match) {
|
|
375
|
+
return source;
|
|
376
|
+
}
|
|
377
|
+
const identifiers = match[1].split(",").map((id) => id.trim()).filter(Boolean);
|
|
378
|
+
if (identifiers.includes(identifier)) {
|
|
379
|
+
return source;
|
|
380
|
+
}
|
|
381
|
+
identifiers.push(identifier);
|
|
382
|
+
const unique = Array.from(new Set(identifiers));
|
|
383
|
+
const replacement = `import { ${unique.join(", ")} } from "${packageName}"`;
|
|
384
|
+
return source.replace(importRegex, replacement);
|
|
385
|
+
}
|
|
386
|
+
function tweakImports(source) {
|
|
387
|
+
const importRegex = /import \{([^}]*)\} from "drizzle-orm\/pg-core";?/;
|
|
388
|
+
const match = source.match(importRegex);
|
|
389
|
+
if (!match) {
|
|
390
|
+
return source;
|
|
391
|
+
}
|
|
392
|
+
const identifiers = match[1].split(",").map((id) => id.trim()).filter(Boolean).filter((id) => id !== "pgSchema" && id !== "customType");
|
|
393
|
+
const filteredIdentifiers = identifiers.filter((id) => {
|
|
394
|
+
if (id === "timestamp") {
|
|
395
|
+
const timestampUsageRegex = /timestamp\s*\(/;
|
|
396
|
+
return timestampUsageRegex.test(source);
|
|
397
|
+
}
|
|
398
|
+
return true;
|
|
399
|
+
});
|
|
400
|
+
if (source.includes("pgTable(") && !filteredIdentifiers.includes("pgTable")) {
|
|
401
|
+
filteredIdentifiers.push("pgTable");
|
|
402
|
+
}
|
|
403
|
+
if (source.includes("pgView(") && !filteredIdentifiers.includes("pgView")) {
|
|
404
|
+
filteredIdentifiers.push("pgView");
|
|
405
|
+
}
|
|
406
|
+
if (source.includes("pgMaterializedView(") && !filteredIdentifiers.includes("pgMaterializedView")) {
|
|
407
|
+
filteredIdentifiers.push("pgMaterializedView");
|
|
408
|
+
}
|
|
409
|
+
if (source.includes("pgEnum(") && !filteredIdentifiers.includes("pgEnum")) {
|
|
410
|
+
filteredIdentifiers.push("pgEnum");
|
|
411
|
+
}
|
|
412
|
+
if (source.includes("pgSequence(") && !filteredIdentifiers.includes("pgSequence")) {
|
|
413
|
+
filteredIdentifiers.push("pgSequence");
|
|
414
|
+
}
|
|
415
|
+
const unique = Array.from(new Set(filteredIdentifiers));
|
|
416
|
+
const replacement = `import { ${unique.join(", ")} } from "drizzle-orm/pg-core"`;
|
|
417
|
+
return source.replace(importRegex, replacement);
|
|
418
|
+
}
|
|
419
|
+
function inlineFromTemplate(text, templateContent) {
|
|
420
|
+
const typeDefinitions = templateContent.replace(/^import\s+.*;\r?\n*/gm, "").trim();
|
|
421
|
+
const needsSql = typeDefinitions.includes("sql`") && !text.includes("from 'drizzle-orm'") && !text.includes('from "drizzle-orm"');
|
|
422
|
+
const needsCustomType = typeDefinitions.includes("customType<") && !text.includes("customType");
|
|
423
|
+
if (needsCustomType) {
|
|
424
|
+
text = ensureImportIdentifier(text, "drizzle-orm/pg-core", "customType");
|
|
425
|
+
}
|
|
426
|
+
if (needsSql && !text.includes("from 'drizzle-orm'") && !text.includes('from "drizzle-orm"')) {
|
|
427
|
+
const importMatch = text.match(/^import [\s\S]*?from ["']drizzle-orm\/pg-core["'];?\n/m);
|
|
428
|
+
if (importMatch) {
|
|
429
|
+
const insertPoint = text.indexOf(importMatch[0]) + importMatch[0].length;
|
|
430
|
+
text = text.slice(0, insertPoint) + "import { sql } from 'drizzle-orm';\n" + text.slice(insertPoint);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
const headerPrefix = `${HEADER_COMMENT}
|
|
434
|
+
`;
|
|
435
|
+
let insertionPoint = 0;
|
|
436
|
+
if (text.startsWith(headerPrefix)) {
|
|
437
|
+
insertionPoint = headerPrefix.length;
|
|
438
|
+
}
|
|
439
|
+
const importSectionMatch = text.slice(insertionPoint).match(/^(?:import [^\n]+\n)+/);
|
|
440
|
+
if (importSectionMatch) {
|
|
441
|
+
insertionPoint += importSectionMatch[0].length;
|
|
442
|
+
}
|
|
443
|
+
const typeBlock = `
|
|
444
|
+
${typeDefinitions}
|
|
445
|
+
|
|
446
|
+
`;
|
|
447
|
+
return text.slice(0, insertionPoint) + typeBlock + text.slice(insertionPoint);
|
|
448
|
+
}
|
|
449
|
+
function inlineCustomTypes(source) {
|
|
450
|
+
const text = source.replace(/import \{[^}]*\} from ["']\.\/types["'];?\n*/g, "");
|
|
451
|
+
return inlineFromTemplate(text, TEMPLATE_TYPES_TS);
|
|
452
|
+
}
|
|
453
|
+
function removeConflictingSystemFields(source) {
|
|
454
|
+
const systemFieldMap = {
|
|
455
|
+
_created_at: "created_at",
|
|
456
|
+
_created_by: "created_by",
|
|
457
|
+
_updated_at: "updated_at",
|
|
458
|
+
_updated_by: "updated_by"
|
|
459
|
+
};
|
|
460
|
+
const lines = source.split("\n");
|
|
461
|
+
const result = [];
|
|
462
|
+
let inTable = false;
|
|
463
|
+
let tableStartLine = -1;
|
|
464
|
+
const tableBusinessFields = /* @__PURE__ */ new Set();
|
|
465
|
+
let bracketDepth = 0;
|
|
466
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
467
|
+
const line = lines[i];
|
|
468
|
+
if (!inTable && /=\s*(pgTable|pgView|pgMaterializedView)\s*\(/.test(line)) {
|
|
469
|
+
inTable = true;
|
|
470
|
+
tableStartLine = result.length;
|
|
471
|
+
tableBusinessFields.clear();
|
|
472
|
+
bracketDepth = 0;
|
|
473
|
+
}
|
|
474
|
+
if (inTable) {
|
|
475
|
+
for (const char of line) {
|
|
476
|
+
if (char === "{") bracketDepth += 1;
|
|
477
|
+
if (char === "}") bracketDepth -= 1;
|
|
478
|
+
}
|
|
479
|
+
for (const businessField of Object.values(systemFieldMap)) {
|
|
480
|
+
if (line.includes(`"${businessField}"`) || line.includes(`'${businessField}'`)) {
|
|
481
|
+
tableBusinessFields.add(businessField);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (bracketDepth === 0 && line.includes(");")) {
|
|
485
|
+
inTable = false;
|
|
486
|
+
const tableEndLine = result.length;
|
|
487
|
+
for (let j = tableStartLine; j <= tableEndLine; j += 1) {
|
|
488
|
+
const tableLine = result[j] || "";
|
|
489
|
+
let shouldRemove = false;
|
|
490
|
+
for (const [systemField, businessField] of Object.entries(systemFieldMap)) {
|
|
491
|
+
if (tableBusinessFields.has(businessField)) {
|
|
492
|
+
if (tableLine.includes(`"${systemField}"`) || tableLine.includes(`'${systemField}'`)) {
|
|
493
|
+
shouldRemove = true;
|
|
494
|
+
if (j > 0 && result[j - 1]?.includes("// System field:")) {
|
|
495
|
+
result[j - 1] = null;
|
|
496
|
+
}
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
if (shouldRemove) {
|
|
502
|
+
result[j] = null;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
result.push(line);
|
|
508
|
+
}
|
|
509
|
+
return result.filter((line) => line !== null).join("\n");
|
|
510
|
+
}
|
|
511
|
+
function addSystemFieldComments(source) {
|
|
512
|
+
const commentMap = {
|
|
513
|
+
_created_at: "Creation time",
|
|
514
|
+
_created_by: "Creator",
|
|
515
|
+
_updated_at: "Update time",
|
|
516
|
+
_updated_by: "Updater"
|
|
517
|
+
};
|
|
518
|
+
const lines = source.split("\n");
|
|
519
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
520
|
+
const line = lines[i];
|
|
521
|
+
const entry = Object.entries(commentMap).find(([key]) => line.includes(`"${key}"`));
|
|
522
|
+
if (!entry) {
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
const [, description] = entry;
|
|
526
|
+
const previousLine = lines[i - 1]?.trim() ?? "";
|
|
527
|
+
if (previousLine.startsWith("//") && previousLine.includes("System field")) {
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
const indentMatch = line.match(/^\s*/);
|
|
531
|
+
const indent = indentMatch ? indentMatch[0] : "";
|
|
532
|
+
const comment = `${indent}// System field: ${description} (auto-filled, do not modify)`;
|
|
533
|
+
lines.splice(i, 0, comment);
|
|
534
|
+
i += 1;
|
|
535
|
+
}
|
|
536
|
+
return lines.join("\n");
|
|
537
|
+
}
|
|
538
|
+
function replaceTimestampWithCustomTypes(source) {
|
|
539
|
+
let replaced = 0;
|
|
540
|
+
const pattern = /timestamp\((['"])(.*?)\1,\s*(\{[^}]*\})\)/g;
|
|
541
|
+
const text = source.replace(pattern, (match, quote, fieldName, options) => {
|
|
542
|
+
const hasWithTimezone = /withTimezone:\s*true/.test(options);
|
|
543
|
+
const hasModeString = /mode:\s*['"]string['"]/.test(options);
|
|
544
|
+
if (hasWithTimezone && hasModeString) {
|
|
545
|
+
replaced += 1;
|
|
546
|
+
return `customTimestamptz(${quote}${fieldName}${quote})`;
|
|
547
|
+
}
|
|
548
|
+
return match;
|
|
549
|
+
});
|
|
550
|
+
return { text, replaced };
|
|
551
|
+
}
|
|
552
|
+
function replaceDefaultNowWithSql(source) {
|
|
553
|
+
let replaced = 0;
|
|
554
|
+
const pattern = /\.defaultNow\(\)/g;
|
|
555
|
+
const text = source.replace(pattern, () => {
|
|
556
|
+
replaced += 1;
|
|
557
|
+
return ".default(sql`CURRENT_TIMESTAMP`)";
|
|
558
|
+
});
|
|
559
|
+
return { text, replaced };
|
|
560
|
+
}
|
|
561
|
+
function appendTableAliases(source) {
|
|
562
|
+
const marker = "// table aliases";
|
|
563
|
+
const markerIndex = source.indexOf(`
|
|
564
|
+
${marker}`);
|
|
565
|
+
const base = markerIndex === -1 ? source : source.slice(0, markerIndex);
|
|
566
|
+
const exportRegex = /export const\s+([A-Za-z_$][\w$]*)\s*=\s*pgTable\s*\(/g;
|
|
567
|
+
const tableExports = /* @__PURE__ */ new Set();
|
|
568
|
+
for (const match of base.matchAll(exportRegex)) {
|
|
569
|
+
tableExports.add(match[1]);
|
|
570
|
+
}
|
|
571
|
+
if (tableExports.size === 0) {
|
|
572
|
+
return base;
|
|
573
|
+
}
|
|
574
|
+
const aliasLines = Array.from(tableExports).sort().map((name) => `export const ${name}Table = ${name};`).join("\n");
|
|
575
|
+
const prefix = base.trimEnd();
|
|
576
|
+
return `${prefix}
|
|
577
|
+
|
|
578
|
+
${marker}
|
|
579
|
+
${aliasLines}
|
|
580
|
+
`;
|
|
581
|
+
}
|
|
582
|
+
function postprocessDrizzleSchema(targetPath) {
|
|
583
|
+
const resolvedPath = path.resolve(targetPath);
|
|
584
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
585
|
+
console.warn(`[postprocess-drizzle-schema] File not found: ${resolvedPath}`);
|
|
586
|
+
return void 0;
|
|
587
|
+
}
|
|
588
|
+
let text = fs.readFileSync(resolvedPath, "utf8");
|
|
589
|
+
text = ensureHeaderComment(text);
|
|
590
|
+
const patchResult = patchDrizzleKitDefects(text);
|
|
591
|
+
text = patchResult.text;
|
|
592
|
+
text = removePgSchemaDeclarations(text);
|
|
593
|
+
const tableConversion = convertSchemaTableInvocations(text);
|
|
594
|
+
text = tableConversion.text;
|
|
595
|
+
const renameResult = renamePgTableConstants(text);
|
|
596
|
+
text = renameResult.text;
|
|
597
|
+
text = updateTableReferenceIdentifiers(text, renameResult.renames);
|
|
598
|
+
const replacement = replaceUnknownColumns(text);
|
|
599
|
+
text = replacement.text;
|
|
600
|
+
const timestampReplacement = replaceTimestampWithCustomTypes(text);
|
|
601
|
+
text = timestampReplacement.text;
|
|
602
|
+
const defaultNowReplacement = replaceDefaultNowWithSql(text);
|
|
603
|
+
text = defaultNowReplacement.text;
|
|
604
|
+
text = removeConflictingSystemFields(text);
|
|
605
|
+
text = addSystemFieldComments(text);
|
|
606
|
+
text = tweakImports(text);
|
|
607
|
+
text = inlineCustomTypes(text);
|
|
608
|
+
text = appendTableAliases(text);
|
|
609
|
+
text = text.replace(/\r?\n/g, "\n");
|
|
610
|
+
text = collapseExtraBlankLines(text);
|
|
611
|
+
fs.writeFileSync(resolvedPath, text, "utf8");
|
|
612
|
+
if (patchResult.fixed > 0) {
|
|
613
|
+
console.info(`[postprocess-drizzle-schema] Patched ${patchResult.fixed} drizzle-kit defects (.default(') -> .default(''))`);
|
|
614
|
+
}
|
|
615
|
+
if (replacement.replaced > 0) {
|
|
616
|
+
console.info(`[postprocess-drizzle-schema] Replaced ${replacement.replaced} unknown columns`);
|
|
617
|
+
}
|
|
618
|
+
if (replacement.unmatched.length > 0) {
|
|
619
|
+
console.warn("[postprocess-drizzle-schema] Unmatched custom types:", replacement.unmatched.length);
|
|
620
|
+
replacement.unmatched.forEach((line) => console.warn(` ${line}`));
|
|
621
|
+
}
|
|
622
|
+
if (tableConversion.converted > 0) {
|
|
623
|
+
console.info(`[postprocess-drizzle-schema] Converted ${tableConversion.converted} schema.table invocations to pgTable`);
|
|
624
|
+
}
|
|
625
|
+
if (timestampReplacement.replaced > 0) {
|
|
626
|
+
console.info(`[postprocess-drizzle-schema] Replaced ${timestampReplacement.replaced} timestamp fields with customTimestamptz`);
|
|
627
|
+
}
|
|
628
|
+
if (defaultNowReplacement.replaced > 0) {
|
|
629
|
+
console.info(`[postprocess-drizzle-schema] Replaced ${defaultNowReplacement.replaced} .defaultNow() with .default(sql\`CURRENT_TIMESTAMP\`)`);
|
|
630
|
+
}
|
|
631
|
+
return {
|
|
632
|
+
replacedUnknown: replacement.replaced,
|
|
633
|
+
unmatchedUnknown: replacement.unmatched,
|
|
634
|
+
patchedDefects: patchResult.fixed,
|
|
635
|
+
replacedTimestamps: timestampReplacement.replaced,
|
|
636
|
+
replacedDefaultNow: defaultNowReplacement.replaced
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// src/internal/gen-nest-resource.ts
|
|
641
|
+
import { mkdir, rm, writeFile } from "fs/promises";
|
|
642
|
+
import { existsSync } from "fs";
|
|
643
|
+
import { join } from "path";
|
|
644
|
+
import { pluralize } from "inflection";
|
|
645
|
+
import { Project, Node } from "ts-morph";
|
|
646
|
+
var DrizzleSchemaParser = class {
|
|
647
|
+
constructor(projectOptions) {
|
|
648
|
+
this.project = new Project(projectOptions);
|
|
649
|
+
}
|
|
650
|
+
parseSchemaFile(filePath) {
|
|
651
|
+
const sourceFile = this.project.addSourceFileAtPath(filePath);
|
|
652
|
+
const tables = [];
|
|
653
|
+
const variableStatements = sourceFile.getVariableStatements();
|
|
654
|
+
for (const statement of variableStatements) {
|
|
655
|
+
const declarations = statement.getDeclarations();
|
|
656
|
+
for (const declaration of declarations) {
|
|
657
|
+
const initializer = declaration.getInitializer();
|
|
658
|
+
if (!initializer || !Node.isCallExpression(initializer)) {
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
const expression = initializer.getExpression();
|
|
662
|
+
if (expression.getText() !== "pgTable") {
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
const tableInfo = this.parsePgTable(declaration.getName(), initializer);
|
|
666
|
+
if (tableInfo) {
|
|
667
|
+
tables.push(tableInfo);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return tables;
|
|
672
|
+
}
|
|
673
|
+
parsePgTable(variableName, callExpr) {
|
|
674
|
+
const args = callExpr.getArguments();
|
|
675
|
+
if (args.length < 2) {
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
const tableName = args[0].getText().replace(/['"]/g, "");
|
|
679
|
+
const fieldsArg = args[1];
|
|
680
|
+
if (!Node.isObjectLiteralExpression(fieldsArg)) {
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
const fields = [];
|
|
684
|
+
const properties = fieldsArg.getProperties();
|
|
685
|
+
for (const prop of properties) {
|
|
686
|
+
if (!Node.isPropertyAssignment(prop)) {
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
const fieldName = prop.getName();
|
|
690
|
+
const initializer = prop.getInitializer();
|
|
691
|
+
const leadingComments = prop.getLeadingCommentRanges();
|
|
692
|
+
let comment;
|
|
693
|
+
if (leadingComments.length > 0) {
|
|
694
|
+
comment = leadingComments.map((c) => c.getText()).join("\n").replace(/\/\//g, "").trim();
|
|
695
|
+
}
|
|
696
|
+
if (initializer && Node.isCallExpression(initializer)) {
|
|
697
|
+
const fieldInfo = this.parseField(fieldName, initializer, comment);
|
|
698
|
+
fields.push(fieldInfo);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return { tableName, variableName, fields };
|
|
702
|
+
}
|
|
703
|
+
parseField(fieldName, callExpr, comment) {
|
|
704
|
+
const fieldInfo = {
|
|
705
|
+
name: fieldName,
|
|
706
|
+
columnName: fieldName,
|
|
707
|
+
type: "",
|
|
708
|
+
nullable: true,
|
|
709
|
+
hasDefault: false,
|
|
710
|
+
notNull: false,
|
|
711
|
+
isPrimaryKey: false,
|
|
712
|
+
isUnique: false,
|
|
713
|
+
isArray: false,
|
|
714
|
+
comment
|
|
715
|
+
};
|
|
716
|
+
this.parseBaseType(callExpr, fieldInfo);
|
|
717
|
+
this.parseCallChain(callExpr, fieldInfo);
|
|
718
|
+
return fieldInfo;
|
|
719
|
+
}
|
|
720
|
+
parseBaseType(callExpr, fieldInfo) {
|
|
721
|
+
let current = callExpr;
|
|
722
|
+
let baseCall = null;
|
|
723
|
+
while (Node.isCallExpression(current)) {
|
|
724
|
+
baseCall = current;
|
|
725
|
+
const expression2 = current.getExpression();
|
|
726
|
+
if (Node.isPropertyAccessExpression(expression2)) {
|
|
727
|
+
current = expression2.getExpression();
|
|
728
|
+
} else {
|
|
729
|
+
break;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
if (!baseCall) {
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
const expression = baseCall.getExpression();
|
|
736
|
+
let typeName = "";
|
|
737
|
+
if (Node.isPropertyAccessExpression(expression)) {
|
|
738
|
+
typeName = expression.getName();
|
|
739
|
+
} else {
|
|
740
|
+
typeName = expression.getText();
|
|
741
|
+
}
|
|
742
|
+
fieldInfo.type = typeName;
|
|
743
|
+
const args = baseCall.getArguments();
|
|
744
|
+
if (args.length > 0) {
|
|
745
|
+
const firstArg = args[0];
|
|
746
|
+
if (Node.isStringLiteral(firstArg)) {
|
|
747
|
+
fieldInfo.columnName = firstArg.getLiteralText();
|
|
748
|
+
} else if (Node.isObjectLiteralExpression(firstArg)) {
|
|
749
|
+
this.parseTypeConfig(firstArg, fieldInfo);
|
|
750
|
+
} else if (Node.isArrayLiteralExpression(firstArg)) {
|
|
751
|
+
fieldInfo.enumValues = firstArg.getElements().map((el) => el.getText().replace(/['"]/g, ""));
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
if (args.length > 1 && Node.isObjectLiteralExpression(args[1])) {
|
|
755
|
+
this.parseTypeConfig(args[1], fieldInfo);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
parseTypeConfig(objLiteral, fieldInfo) {
|
|
759
|
+
if (!Node.isObjectLiteralExpression(objLiteral)) {
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
const properties = objLiteral.getProperties();
|
|
763
|
+
for (const prop of properties) {
|
|
764
|
+
if (!Node.isPropertyAssignment(prop)) {
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
const propName = prop.getName();
|
|
768
|
+
const value = prop.getInitializer()?.getText();
|
|
769
|
+
switch (propName) {
|
|
770
|
+
case "length":
|
|
771
|
+
fieldInfo.length = value ? parseInt(value) : void 0;
|
|
772
|
+
break;
|
|
773
|
+
case "precision":
|
|
774
|
+
fieldInfo.precision = value ? parseInt(value) : void 0;
|
|
775
|
+
break;
|
|
776
|
+
case "scale":
|
|
777
|
+
fieldInfo.scale = value ? parseInt(value) : void 0;
|
|
778
|
+
break;
|
|
779
|
+
case "default":
|
|
780
|
+
fieldInfo.hasDefault = true;
|
|
781
|
+
fieldInfo.defaultValue = value;
|
|
782
|
+
break;
|
|
783
|
+
case "withTimezone":
|
|
784
|
+
fieldInfo.withTimezone = value === "true";
|
|
785
|
+
break;
|
|
786
|
+
case "mode":
|
|
787
|
+
fieldInfo.mode = value?.replace(/['"]/g, "");
|
|
788
|
+
break;
|
|
789
|
+
default:
|
|
790
|
+
throw new Error(`Unsupported property: ${propName}`);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
parseCallChain(callExpr, fieldInfo) {
|
|
795
|
+
let current = callExpr;
|
|
796
|
+
while (Node.isCallExpression(current)) {
|
|
797
|
+
const expression = current.getExpression();
|
|
798
|
+
if (!Node.isPropertyAccessExpression(expression)) {
|
|
799
|
+
break;
|
|
800
|
+
}
|
|
801
|
+
const methodName = expression.getName();
|
|
802
|
+
const args = current.getArguments();
|
|
803
|
+
switch (methodName) {
|
|
804
|
+
case "notNull":
|
|
805
|
+
fieldInfo.notNull = true;
|
|
806
|
+
fieldInfo.nullable = false;
|
|
807
|
+
break;
|
|
808
|
+
case "default":
|
|
809
|
+
fieldInfo.hasDefault = true;
|
|
810
|
+
if (args.length > 0) {
|
|
811
|
+
fieldInfo.defaultValue = args[0].getText();
|
|
812
|
+
}
|
|
813
|
+
break;
|
|
814
|
+
case "defaultRandom":
|
|
815
|
+
fieldInfo.hasDefault = true;
|
|
816
|
+
fieldInfo.defaultValue = "random";
|
|
817
|
+
break;
|
|
818
|
+
case "primaryKey":
|
|
819
|
+
fieldInfo.isPrimaryKey = true;
|
|
820
|
+
fieldInfo.notNull = true;
|
|
821
|
+
fieldInfo.nullable = false;
|
|
822
|
+
break;
|
|
823
|
+
case "unique":
|
|
824
|
+
fieldInfo.isUnique = true;
|
|
825
|
+
break;
|
|
826
|
+
case "array":
|
|
827
|
+
fieldInfo.isArray = true;
|
|
828
|
+
break;
|
|
829
|
+
case "references":
|
|
830
|
+
if (args.length > 0) {
|
|
831
|
+
const refArg = args[0].getText();
|
|
832
|
+
const match = refArg.match(/=>\s*(\w+)\.(\w+)/);
|
|
833
|
+
if (match) {
|
|
834
|
+
fieldInfo.references = { table: match[1], column: match[2] };
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
break;
|
|
838
|
+
default:
|
|
839
|
+
throw new Error(`Unsupported method: ${methodName}`);
|
|
840
|
+
}
|
|
841
|
+
current = expression.getExpression();
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
function mapDrizzleTypeToTS(field) {
|
|
846
|
+
const typeMap = {
|
|
847
|
+
char: "string",
|
|
848
|
+
varchar: "string",
|
|
849
|
+
text: "string",
|
|
850
|
+
smallint: "number",
|
|
851
|
+
integer: "number",
|
|
852
|
+
int: "number",
|
|
853
|
+
bigint: "string",
|
|
854
|
+
serial: "number",
|
|
855
|
+
smallserial: "number",
|
|
856
|
+
bigserial: "string",
|
|
857
|
+
decimal: "string",
|
|
858
|
+
numeric: "string",
|
|
859
|
+
real: "number",
|
|
860
|
+
doublePrecision: "number",
|
|
861
|
+
boolean: "boolean",
|
|
862
|
+
timestamp: "Date",
|
|
863
|
+
timestamptz: "Date",
|
|
864
|
+
date: "Date",
|
|
865
|
+
time: "string",
|
|
866
|
+
timetz: "string",
|
|
867
|
+
interval: "string",
|
|
868
|
+
uuid: "string",
|
|
869
|
+
json: "any",
|
|
870
|
+
jsonb: "any",
|
|
871
|
+
bytea: "Buffer",
|
|
872
|
+
inet: "string",
|
|
873
|
+
cidr: "string",
|
|
874
|
+
macaddr: "string",
|
|
875
|
+
macaddr8: "string",
|
|
876
|
+
point: "{ x: number; y: number }",
|
|
877
|
+
line: "string",
|
|
878
|
+
lseg: "string",
|
|
879
|
+
box: "string",
|
|
880
|
+
path: "string",
|
|
881
|
+
polygon: "string",
|
|
882
|
+
circle: "string",
|
|
883
|
+
array: "any[]",
|
|
884
|
+
customType: "any",
|
|
885
|
+
customTimestamptz: "Date",
|
|
886
|
+
userProfile: "string",
|
|
887
|
+
fileAttachment: "FileAttachment",
|
|
888
|
+
pgEnum: "string"
|
|
889
|
+
};
|
|
890
|
+
let baseType = typeMap[field.type] || "any";
|
|
891
|
+
if (field.isArray) {
|
|
892
|
+
baseType = baseType.endsWith("[]") ? baseType : `${baseType}[]`;
|
|
893
|
+
}
|
|
894
|
+
if (field.enumValues && field.enumValues.length > 0) {
|
|
895
|
+
baseType = field.enumValues.map((v) => `'${v}'`).join(" | ");
|
|
896
|
+
}
|
|
897
|
+
return baseType;
|
|
898
|
+
}
|
|
899
|
+
function toPascalCase(str) {
|
|
900
|
+
return str.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[-_\s]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
901
|
+
}
|
|
902
|
+
function toKebabCase(str) {
|
|
903
|
+
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase().replace(/[_\s]/g, "-");
|
|
904
|
+
}
|
|
905
|
+
function toSnakeCase(str) {
|
|
906
|
+
return str.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase().replace(/[-\s]/g, "_");
|
|
907
|
+
}
|
|
908
|
+
function generateValidationDecorators(field, opts = {}) {
|
|
909
|
+
const { isUpdate = false, isResponse = false } = opts;
|
|
910
|
+
let decorators = " // \u8BF7\u6309\u7528\u6237\u9700\u6C42\u4FEE\u6539\u4EE5\u4E0B\u88C5\u9970\u5668\u6CE8\u91CA\n";
|
|
911
|
+
if (field.nullable || !isResponse && field.hasDefault || isUpdate) {
|
|
912
|
+
decorators += ` @ApiPropertyOptional({ description: '${field.comment || field.name}' })
|
|
913
|
+
`;
|
|
914
|
+
if (isResponse) {
|
|
915
|
+
return decorators;
|
|
916
|
+
}
|
|
917
|
+
decorators += " @IsOptional()\n";
|
|
918
|
+
} else {
|
|
919
|
+
decorators += ` @ApiProperty({ description: '${field.comment || field.name}' })
|
|
920
|
+
`;
|
|
921
|
+
if (isResponse) {
|
|
922
|
+
return decorators;
|
|
923
|
+
}
|
|
924
|
+
decorators += " @IsDefined()\n";
|
|
925
|
+
}
|
|
926
|
+
switch (field.type) {
|
|
927
|
+
case "varchar":
|
|
928
|
+
case "char":
|
|
929
|
+
case "text":
|
|
930
|
+
decorators += " @IsString()\n";
|
|
931
|
+
if (field.length) {
|
|
932
|
+
decorators += ` @MaxLength(${field.length})
|
|
933
|
+
`;
|
|
934
|
+
}
|
|
935
|
+
break;
|
|
936
|
+
case "integer":
|
|
937
|
+
case "smallint":
|
|
938
|
+
case "serial":
|
|
939
|
+
case "smallserial":
|
|
940
|
+
decorators += " @IsInt()\n";
|
|
941
|
+
break;
|
|
942
|
+
case "decimal":
|
|
943
|
+
case "numeric":
|
|
944
|
+
case "real":
|
|
945
|
+
case "doublePrecision":
|
|
946
|
+
decorators += " @IsNumber()\n";
|
|
947
|
+
break;
|
|
948
|
+
case "boolean":
|
|
949
|
+
decorators += " @IsBoolean()\n";
|
|
950
|
+
break;
|
|
951
|
+
case "uuid":
|
|
952
|
+
decorators += " @IsUUID()\n";
|
|
953
|
+
break;
|
|
954
|
+
case "timestamp":
|
|
955
|
+
case "timestamptz":
|
|
956
|
+
case "date":
|
|
957
|
+
case "customTimestamptz":
|
|
958
|
+
decorators += " @IsDate()\n";
|
|
959
|
+
break;
|
|
960
|
+
case "json":
|
|
961
|
+
case "jsonb":
|
|
962
|
+
decorators += " @IsObject()\n";
|
|
963
|
+
break;
|
|
964
|
+
}
|
|
965
|
+
if (field.isArray) {
|
|
966
|
+
decorators += " @IsArray()\n";
|
|
967
|
+
}
|
|
968
|
+
return decorators;
|
|
969
|
+
}
|
|
970
|
+
function generateDTO(table) {
|
|
971
|
+
const className = toPascalCase(table.variableName);
|
|
972
|
+
let dto = `// \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
|
|
973
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
974
|
+
import { IsDefined, IsNumber, IsOptional, IsString, MaxLength, IsInt, IsBoolean, IsUUID, IsDate, IsObject, IsArray } from 'class-validator';
|
|
975
|
+
import { Type } from 'class-transformer';
|
|
976
|
+
import { FileAttachment } from '../../../database/schema';
|
|
977
|
+
|
|
978
|
+
`;
|
|
979
|
+
dto += `export class Create${className}Dto {
|
|
980
|
+
`;
|
|
981
|
+
for (const field of table.fields) {
|
|
982
|
+
if (field.isPrimaryKey || field.name === "id" || field.name.startsWith("_") || field.name.startsWith("created") || field.name.startsWith("updated")) {
|
|
983
|
+
continue;
|
|
984
|
+
}
|
|
985
|
+
const tsType = mapDrizzleTypeToTS(field);
|
|
986
|
+
const optional = field.nullable || field.hasDefault ? "?" : "";
|
|
987
|
+
const decorators = generateValidationDecorators(field);
|
|
988
|
+
if (decorators) {
|
|
989
|
+
dto += decorators;
|
|
990
|
+
}
|
|
991
|
+
dto += ` ${field.name}${optional}: ${tsType};
|
|
992
|
+
|
|
993
|
+
`;
|
|
994
|
+
}
|
|
995
|
+
dto += "}\n\n";
|
|
996
|
+
dto += `export class Update${className}Dto {
|
|
997
|
+
`;
|
|
998
|
+
for (const field of table.fields) {
|
|
999
|
+
if (field.name.startsWith("_") || field.name.startsWith("created") || field.name.startsWith("updated") || field.isPrimaryKey || field.name === "id") {
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
const tsType = mapDrizzleTypeToTS(field);
|
|
1003
|
+
const decorators = generateValidationDecorators(field, { isUpdate: true });
|
|
1004
|
+
if (decorators) {
|
|
1005
|
+
dto += decorators;
|
|
1006
|
+
}
|
|
1007
|
+
dto += ` ${field.name}?: ${tsType};
|
|
1008
|
+
|
|
1009
|
+
`;
|
|
1010
|
+
}
|
|
1011
|
+
dto += "}\n\n";
|
|
1012
|
+
dto += `export class ${className}ResponseDto {
|
|
1013
|
+
`;
|
|
1014
|
+
for (const field of table.fields) {
|
|
1015
|
+
const tsType = mapDrizzleTypeToTS(field);
|
|
1016
|
+
const optional = field.nullable ? "?" : "";
|
|
1017
|
+
const decorators = generateValidationDecorators(field, { isResponse: true });
|
|
1018
|
+
if (decorators) {
|
|
1019
|
+
dto += decorators;
|
|
1020
|
+
}
|
|
1021
|
+
dto += ` ${field.name}${optional}: ${tsType};
|
|
1022
|
+
|
|
1023
|
+
`;
|
|
1024
|
+
}
|
|
1025
|
+
dto += "}\n";
|
|
1026
|
+
return dto;
|
|
1027
|
+
}
|
|
1028
|
+
function generateController(table) {
|
|
1029
|
+
const className = toPascalCase(table.variableName);
|
|
1030
|
+
const routePath = toKebabCase(pluralize(table.variableName));
|
|
1031
|
+
const filePath = toSnakeCase(table.variableName);
|
|
1032
|
+
const pkField = table.fields.find((f) => f.isPrimaryKey);
|
|
1033
|
+
const pkType = pkField ? mapDrizzleTypeToTS(pkField) : "string";
|
|
1034
|
+
const pkName = pkField ? pkField.name : "id";
|
|
1035
|
+
return `
|
|
1036
|
+
// \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
|
|
1037
|
+
import {
|
|
1038
|
+
Controller,
|
|
1039
|
+
Get,
|
|
1040
|
+
Post,
|
|
1041
|
+
Put,
|
|
1042
|
+
Delete,
|
|
1043
|
+
Body,
|
|
1044
|
+
Param,
|
|
1045
|
+
Query,
|
|
1046
|
+
} from '@nestjs/common';
|
|
1047
|
+
import {
|
|
1048
|
+
ApiTags,
|
|
1049
|
+
ApiOperation,
|
|
1050
|
+
ApiOkResponse,
|
|
1051
|
+
ApiCreatedResponse,
|
|
1052
|
+
} from '@nestjs/swagger';
|
|
1053
|
+
import {
|
|
1054
|
+
Create${className}Dto,
|
|
1055
|
+
Update${className}Dto,
|
|
1056
|
+
${className}ResponseDto
|
|
1057
|
+
} from './dtos/${filePath}.dto';
|
|
1058
|
+
import { ${className}Service } from './${filePath}.service';
|
|
1059
|
+
|
|
1060
|
+
@ApiTags('${toPascalCase(table.variableName)}')
|
|
1061
|
+
@Controller('api/${routePath}')
|
|
1062
|
+
export class ${className}Controller {
|
|
1063
|
+
constructor(private readonly ${table.variableName}Service: ${className}Service) {}
|
|
1064
|
+
|
|
1065
|
+
@Post()
|
|
1066
|
+
@ApiOperation({
|
|
1067
|
+
summary: '\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
1068
|
+
description: '\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
1069
|
+
})
|
|
1070
|
+
@ApiCreatedResponse({
|
|
1071
|
+
description: '\u6210\u529F\u521B\u5EFA\u4E00\u6761\u8BB0\u5F55',
|
|
1072
|
+
type: ${className}ResponseDto,
|
|
1073
|
+
})
|
|
1074
|
+
async create(
|
|
1075
|
+
@Body() createDto: Create${className}Dto
|
|
1076
|
+
): Promise<${className}ResponseDto> {
|
|
1077
|
+
return this.${table.variableName}Service.create(createDto);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
@ApiOperation({
|
|
1081
|
+
summary: '\u6839\u636E\u4E3B\u952E\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
1082
|
+
description: '\u6839\u636E\u4E3B\u952E\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
1083
|
+
})
|
|
1084
|
+
@ApiOkResponse({
|
|
1085
|
+
description: '\u6210\u529F\u67E5\u8BE2\u4E00\u6761\u8BB0\u5F55',
|
|
1086
|
+
type: ${className}ResponseDto,
|
|
1087
|
+
})
|
|
1088
|
+
@Get(':${pkName}')
|
|
1089
|
+
async findOne(
|
|
1090
|
+
@Param('${pkName}') ${pkName}: ${pkType}
|
|
1091
|
+
): Promise<${className}ResponseDto> {
|
|
1092
|
+
return this.${table.variableName}Service.findOne(${pkName});
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
@ApiOperation({
|
|
1096
|
+
summary: '\u6839\u636E\u4E3B\u952E\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
1097
|
+
description: '\u6839\u636E\u4E3B\u952E\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
1098
|
+
})
|
|
1099
|
+
@ApiOkResponse({
|
|
1100
|
+
description: '\u6210\u529F\u66F4\u65B0\u4E00\u6761\u8BB0\u5F55',
|
|
1101
|
+
type: ${className}ResponseDto,
|
|
1102
|
+
})
|
|
1103
|
+
@Put(':${pkName}')
|
|
1104
|
+
async update(
|
|
1105
|
+
@Param('${pkName}') ${pkName}: ${pkType},
|
|
1106
|
+
@Body() updateDto: Update${className}Dto
|
|
1107
|
+
): Promise<${className}ResponseDto> {
|
|
1108
|
+
return this.${table.variableName}Service.update(${pkName}, updateDto);
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
@ApiOperation({
|
|
1112
|
+
summary: '\u6839\u636E\u4E3B\u952E\u5220\u9664\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
1113
|
+
description: '\u6839\u636E\u4E3B\u952E\u5220\u9664\u4E00\u6761\u8BB0\u5F55\uFF08\u6A21\u677F\u5185\u5BB9\uFF0C\u8BF7\u4FEE\u6539\u6211\uFF09',
|
|
1114
|
+
})
|
|
1115
|
+
@ApiOkResponse({
|
|
1116
|
+
description: '\u6210\u529F\u5220\u9664\u4E00\u6761\u8BB0\u5F55',
|
|
1117
|
+
})
|
|
1118
|
+
@Delete(':${pkName}')
|
|
1119
|
+
async remove(
|
|
1120
|
+
@Param('${pkName}') ${pkName}: ${pkType}
|
|
1121
|
+
): Promise<void> {
|
|
1122
|
+
return this.${table.variableName}Service.remove(${pkName});
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
`;
|
|
1126
|
+
}
|
|
1127
|
+
function generateService(table) {
|
|
1128
|
+
const className = toPascalCase(table.variableName);
|
|
1129
|
+
const filePath = toSnakeCase(table.variableName);
|
|
1130
|
+
const pkField = table.fields.find((f) => f.isPrimaryKey);
|
|
1131
|
+
const pkType = pkField ? mapDrizzleTypeToTS(pkField) : "string";
|
|
1132
|
+
const pkName = pkField ? pkField.name : "id";
|
|
1133
|
+
return `
|
|
1134
|
+
// \u8BF7\u4FEE\u6539\u8BE5\u6587\u4EF6\u4EE3\u7801\u4EE5\u6EE1\u8DB3\u9700\u6C42
|
|
1135
|
+
import { Injectable, Inject, Logger, NotFoundException } from '@nestjs/common';
|
|
1136
|
+
import { eq } from 'drizzle-orm';
|
|
1137
|
+
import { DRIZZLE_DATABASE, type PostgresJsDatabase } from '@lark-apaas/fullstack-nestjs-core';
|
|
1138
|
+
import { ${table.variableName} } from '../../database/schema';
|
|
1139
|
+
import {
|
|
1140
|
+
Create${className}Dto,
|
|
1141
|
+
Update${className}Dto,
|
|
1142
|
+
${className}ResponseDto
|
|
1143
|
+
} from './dtos/${filePath}.dto';
|
|
1144
|
+
|
|
1145
|
+
@Injectable()
|
|
1146
|
+
export class ${className}Service {
|
|
1147
|
+
private readonly logger = new Logger(${className}Service.name);
|
|
1148
|
+
|
|
1149
|
+
constructor(@Inject(DRIZZLE_DATABASE) private readonly db: PostgresJsDatabase) {}
|
|
1150
|
+
|
|
1151
|
+
async create(createDto: Create${className}Dto): Promise<${className}ResponseDto> {
|
|
1152
|
+
const [result] = await this.db
|
|
1153
|
+
.insert(${table.variableName})
|
|
1154
|
+
.values(createDto)
|
|
1155
|
+
.returning();
|
|
1156
|
+
|
|
1157
|
+
this.logger.log(\`Created ${className} with ${pkName} \${result.${pkName}}\`);
|
|
1158
|
+
|
|
1159
|
+
return result;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
async findAll(options?: { page?: number; limit?: number }): Promise<${className}ResponseDto[]> {
|
|
1163
|
+
const { page = 1, limit = 10 } = options || {};
|
|
1164
|
+
const offset = (page - 1) * limit;
|
|
1165
|
+
|
|
1166
|
+
return this.db
|
|
1167
|
+
.select()
|
|
1168
|
+
.from(${table.variableName})
|
|
1169
|
+
.limit(limit)
|
|
1170
|
+
.offset(offset);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
async findOne(${pkName}: ${pkType}): Promise<${className}ResponseDto> {
|
|
1174
|
+
const [result] = await this.db
|
|
1175
|
+
.select()
|
|
1176
|
+
.from(${table.variableName})
|
|
1177
|
+
.where(eq(${table.variableName}.${pkName}, ${pkName}))
|
|
1178
|
+
.limit(1);
|
|
1179
|
+
|
|
1180
|
+
if (!result) {
|
|
1181
|
+
throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
return result;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
async update(${pkName}: ${pkType}, updateDto: Update${className}Dto): Promise<${className}ResponseDto> {
|
|
1188
|
+
const [result] = await this.db
|
|
1189
|
+
.update(${table.variableName})
|
|
1190
|
+
.set(updateDto)
|
|
1191
|
+
.where(eq(${table.variableName}.${pkName}, ${pkName}))
|
|
1192
|
+
.returning();
|
|
1193
|
+
|
|
1194
|
+
if (!result) {
|
|
1195
|
+
throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
return result;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
async remove(${pkName}: ${pkType}): Promise<void> {
|
|
1202
|
+
const result = await this.db
|
|
1203
|
+
.delete(${table.variableName})
|
|
1204
|
+
.where(eq(${table.variableName}.${pkName}, ${pkName}))
|
|
1205
|
+
.returning();
|
|
1206
|
+
|
|
1207
|
+
if (result.length === 0) {
|
|
1208
|
+
throw new NotFoundException(\`${className} with ${pkName} \${${pkName}} not found\`);
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
this.logger.log(\`Deleted ${className} with ${pkName} \${${pkName}}\`);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
`;
|
|
1215
|
+
}
|
|
1216
|
+
function generateModule(table) {
|
|
1217
|
+
const className = toPascalCase(table.variableName);
|
|
1218
|
+
const filePath = toSnakeCase(table.variableName);
|
|
1219
|
+
return `
|
|
1220
|
+
import { Module } from '@nestjs/common';
|
|
1221
|
+
import { ${className}Controller } from './${filePath}.controller';
|
|
1222
|
+
import { ${className}Service } from './${filePath}.service';
|
|
1223
|
+
|
|
1224
|
+
@Module({
|
|
1225
|
+
controllers: [${className}Controller],
|
|
1226
|
+
providers: [${className}Service],
|
|
1227
|
+
})
|
|
1228
|
+
export class ${className}Module {}
|
|
1229
|
+
`;
|
|
1230
|
+
}
|
|
1231
|
+
async function parseAndGenerateNestResourceTemplate(options) {
|
|
1232
|
+
const parser = new DrizzleSchemaParser({ tsConfigFilePath: options.tsConfigFilePath });
|
|
1233
|
+
const tables = parser.parseSchemaFile(options.schemaFilePath);
|
|
1234
|
+
if (tables.length === 0) {
|
|
1235
|
+
console.warn("\u672A\u89E3\u6790\u5230\u4EFB\u4F55\u6570\u636E\u5E93\u8868\uFF0C\u65E0\u9700\u751F\u6210 Nest.js \u6A21\u5757\u6A21\u677F");
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
tables.sort((a, b) => b.variableName.length - a.variableName.length);
|
|
1239
|
+
const table = tables[0];
|
|
1240
|
+
console.info(`\u751F\u6210 Nest.js ${table.variableName} \u6A21\u5757`);
|
|
1241
|
+
const filePath = toSnakeCase(table.variableName);
|
|
1242
|
+
const moduleDir = join(options.moduleOutputDir, filePath);
|
|
1243
|
+
if (existsSync(moduleDir)) {
|
|
1244
|
+
console.info(`Nest.js \u6A21\u5757 ${filePath} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u751F\u6210\u4EE3\u7801`);
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
const dto = generateDTO(table);
|
|
1248
|
+
const controller = generateController(table);
|
|
1249
|
+
const service = generateService(table);
|
|
1250
|
+
const moduleContent = generateModule(table);
|
|
1251
|
+
const moduleFilePath = join(moduleDir, `${filePath}.module.ts`);
|
|
1252
|
+
try {
|
|
1253
|
+
await mkdir(moduleDir, { recursive: true });
|
|
1254
|
+
await mkdir(join(moduleDir, "dtos"), { recursive: true });
|
|
1255
|
+
await writeFile(join(moduleDir, "dtos", `${filePath}.dto.ts`), dto);
|
|
1256
|
+
await writeFile(join(moduleDir, `${filePath}.controller.ts`), controller);
|
|
1257
|
+
await writeFile(join(moduleDir, `${filePath}.service.ts`), service);
|
|
1258
|
+
await writeFile(moduleFilePath, moduleContent);
|
|
1259
|
+
} catch (err) {
|
|
1260
|
+
console.error(`\u751F\u6210 Nest.js ${filePath} \u6A21\u5757\u5931\u8D25: ${err.message}`);
|
|
1261
|
+
await rm(moduleDir, { recursive: true });
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
// src/commands/db/schema.handler.ts
|
|
126
1266
|
async function run(options = {}) {
|
|
127
1267
|
let exitCode = 0;
|
|
128
|
-
const envPath2 =
|
|
129
|
-
if (
|
|
1268
|
+
const envPath2 = path2.resolve(process.cwd(), ".env");
|
|
1269
|
+
if (fs2.existsSync(envPath2)) {
|
|
130
1270
|
loadEnv({ path: envPath2 });
|
|
131
1271
|
console.log("[gen-db-schema] \u2713 Loaded .env file");
|
|
132
1272
|
}
|
|
@@ -136,13 +1276,13 @@ async function run(options = {}) {
|
|
|
136
1276
|
process.exit(1);
|
|
137
1277
|
}
|
|
138
1278
|
const outputPath = options.output || process.env.DB_SCHEMA_OUTPUT || "server/database/schema.ts";
|
|
139
|
-
const OUT_DIR =
|
|
140
|
-
const SCHEMA_FILE =
|
|
1279
|
+
const OUT_DIR = path2.resolve(process.cwd(), "server/database/.introspect");
|
|
1280
|
+
const SCHEMA_FILE = path2.resolve(process.cwd(), outputPath);
|
|
141
1281
|
console.log("[gen-db-schema] Starting...");
|
|
142
1282
|
const __filename = fileURLToPath(import.meta.url);
|
|
143
|
-
const __dirname2 =
|
|
144
|
-
const configPath =
|
|
145
|
-
if (!
|
|
1283
|
+
const __dirname2 = path2.dirname(__filename);
|
|
1284
|
+
const configPath = path2.join(__dirname2, "config", "drizzle.config.js");
|
|
1285
|
+
if (!fs2.existsSync(configPath)) {
|
|
146
1286
|
console.error("[gen-db-schema] Error: drizzle config not found in CLI package");
|
|
147
1287
|
process.exit(1);
|
|
148
1288
|
}
|
|
@@ -162,29 +1302,27 @@ async function run(options = {}) {
|
|
|
162
1302
|
if ((result.status ?? 0) !== 0) {
|
|
163
1303
|
throw new Error(`drizzle-kit introspect failed with status ${result.status}`);
|
|
164
1304
|
}
|
|
165
|
-
const generatedSchema =
|
|
166
|
-
if (!
|
|
1305
|
+
const generatedSchema = path2.join(OUT_DIR, "schema.ts");
|
|
1306
|
+
if (!fs2.existsSync(generatedSchema)) {
|
|
167
1307
|
console.error("[gen-db-schema] schema.ts not generated");
|
|
168
1308
|
throw new Error("drizzle-kit introspect failed to generate schema.ts");
|
|
169
1309
|
}
|
|
170
|
-
const { postprocessDrizzleSchema } = require2("@lark-apaas/devtool-kits");
|
|
171
1310
|
const stats = postprocessDrizzleSchema(generatedSchema);
|
|
172
1311
|
if (stats?.unmatchedUnknown?.length) {
|
|
173
1312
|
console.warn("[gen-db-schema] Unmatched custom types detected:", stats.unmatchedUnknown);
|
|
174
1313
|
}
|
|
175
1314
|
console.log("[gen-db-schema] \u2713 Postprocessed schema");
|
|
176
|
-
|
|
177
|
-
|
|
1315
|
+
fs2.mkdirSync(path2.dirname(SCHEMA_FILE), { recursive: true });
|
|
1316
|
+
fs2.copyFileSync(generatedSchema, SCHEMA_FILE);
|
|
178
1317
|
console.log(`[gen-db-schema] \u2713 Copied to ${outputPath}`);
|
|
179
1318
|
try {
|
|
180
1319
|
if (options.enableNestModuleGenerate) {
|
|
181
|
-
const
|
|
182
|
-
const tsConfigFilePath = path.resolve(process.cwd(), "tsconfig.json");
|
|
1320
|
+
const tsConfigFilePath = path2.resolve(process.cwd(), "tsconfig.json");
|
|
183
1321
|
const schemaFilePath = SCHEMA_FILE;
|
|
184
1322
|
await parseAndGenerateNestResourceTemplate({
|
|
185
1323
|
tsConfigFilePath,
|
|
186
1324
|
schemaFilePath,
|
|
187
|
-
moduleOutputDir:
|
|
1325
|
+
moduleOutputDir: path2.resolve(process.cwd(), "server/modules")
|
|
188
1326
|
});
|
|
189
1327
|
console.log("[gen-db-schema] \u2713 Generate NestJS Module Boilerplate Successfully");
|
|
190
1328
|
}
|
|
@@ -196,8 +1334,8 @@ async function run(options = {}) {
|
|
|
196
1334
|
console.error("[gen-db-schema] Failed:", err instanceof Error ? err.message : String(err));
|
|
197
1335
|
exitCode = 1;
|
|
198
1336
|
} finally {
|
|
199
|
-
if (
|
|
200
|
-
|
|
1337
|
+
if (fs2.existsSync(OUT_DIR)) {
|
|
1338
|
+
fs2.rmSync(OUT_DIR, { recursive: true, force: true });
|
|
201
1339
|
}
|
|
202
1340
|
process.exit(exitCode);
|
|
203
1341
|
}
|
|
@@ -215,8 +1353,8 @@ var genDbSchemaCommand = {
|
|
|
215
1353
|
};
|
|
216
1354
|
|
|
217
1355
|
// src/commands/sync/run.handler.ts
|
|
218
|
-
import
|
|
219
|
-
import
|
|
1356
|
+
import path4 from "path";
|
|
1357
|
+
import fs4 from "fs";
|
|
220
1358
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
221
1359
|
|
|
222
1360
|
// src/config/sync.ts
|
|
@@ -280,23 +1418,23 @@ function genSyncConfig(perms = {}) {
|
|
|
280
1418
|
}
|
|
281
1419
|
|
|
282
1420
|
// src/utils/file-ops.ts
|
|
283
|
-
import
|
|
284
|
-
import
|
|
1421
|
+
import fs3 from "fs";
|
|
1422
|
+
import path3 from "path";
|
|
285
1423
|
function removeLineFromFile(filePath, pattern) {
|
|
286
|
-
if (!
|
|
287
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1424
|
+
if (!fs3.existsSync(filePath)) {
|
|
1425
|
+
console.log(`[fullstack-cli] \u25CB ${path3.basename(filePath)} (not found)`);
|
|
288
1426
|
return false;
|
|
289
1427
|
}
|
|
290
|
-
const content =
|
|
1428
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
291
1429
|
const lines = content.split("\n");
|
|
292
1430
|
const trimmedPattern = pattern.trim();
|
|
293
1431
|
const filteredLines = lines.filter((line) => line.trim() !== trimmedPattern);
|
|
294
1432
|
if (filteredLines.length === lines.length) {
|
|
295
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1433
|
+
console.log(`[fullstack-cli] \u25CB ${path3.basename(filePath)} (pattern not found: ${pattern})`);
|
|
296
1434
|
return false;
|
|
297
1435
|
}
|
|
298
|
-
|
|
299
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1436
|
+
fs3.writeFileSync(filePath, filteredLines.join("\n"));
|
|
1437
|
+
console.log(`[fullstack-cli] \u2713 ${path3.basename(filePath)} (removed: ${pattern})`);
|
|
300
1438
|
return true;
|
|
301
1439
|
}
|
|
302
1440
|
|
|
@@ -304,14 +1442,14 @@ function removeLineFromFile(filePath, pattern) {
|
|
|
304
1442
|
async function run2(options) {
|
|
305
1443
|
const userProjectRoot = process.env.INIT_CWD || process.cwd();
|
|
306
1444
|
const __filename = fileURLToPath2(import.meta.url);
|
|
307
|
-
const __dirname2 =
|
|
308
|
-
const pluginRoot =
|
|
1445
|
+
const __dirname2 = path4.dirname(__filename);
|
|
1446
|
+
const pluginRoot = path4.resolve(__dirname2, "..");
|
|
309
1447
|
if (userProjectRoot === pluginRoot) {
|
|
310
1448
|
console.log("[fullstack-cli] Skip syncing (installing plugin itself)");
|
|
311
1449
|
process.exit(0);
|
|
312
1450
|
}
|
|
313
|
-
const userPackageJson =
|
|
314
|
-
if (!
|
|
1451
|
+
const userPackageJson = path4.join(userProjectRoot, "package.json");
|
|
1452
|
+
if (!fs4.existsSync(userPackageJson)) {
|
|
315
1453
|
console.log("[fullstack-cli] Skip syncing (not a valid npm project)");
|
|
316
1454
|
process.exit(0);
|
|
317
1455
|
}
|
|
@@ -339,7 +1477,7 @@ async function run2(options) {
|
|
|
339
1477
|
}
|
|
340
1478
|
async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
341
1479
|
if (rule.type === "delete-file" || rule.type === "delete-directory") {
|
|
342
|
-
const destPath2 =
|
|
1480
|
+
const destPath2 = path4.join(userProjectRoot, rule.to);
|
|
343
1481
|
if (rule.type === "delete-file") {
|
|
344
1482
|
deleteFile(destPath2);
|
|
345
1483
|
} else {
|
|
@@ -348,16 +1486,16 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
348
1486
|
return;
|
|
349
1487
|
}
|
|
350
1488
|
if (rule.type === "remove-line") {
|
|
351
|
-
const destPath2 =
|
|
1489
|
+
const destPath2 = path4.join(userProjectRoot, rule.to);
|
|
352
1490
|
removeLineFromFile(destPath2, rule.pattern);
|
|
353
1491
|
return;
|
|
354
1492
|
}
|
|
355
1493
|
if (!("from" in rule)) {
|
|
356
1494
|
return;
|
|
357
1495
|
}
|
|
358
|
-
const srcPath =
|
|
359
|
-
const destPath =
|
|
360
|
-
if (!
|
|
1496
|
+
const srcPath = path4.join(pluginRoot, rule.from);
|
|
1497
|
+
const destPath = path4.join(userProjectRoot, rule.to);
|
|
1498
|
+
if (!fs4.existsSync(srcPath)) {
|
|
361
1499
|
console.warn(`[fullstack-cli] Source not found: ${rule.from}`);
|
|
362
1500
|
return;
|
|
363
1501
|
}
|
|
@@ -374,64 +1512,64 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
374
1512
|
}
|
|
375
1513
|
}
|
|
376
1514
|
function syncFile(src, dest, overwrite = true) {
|
|
377
|
-
const destDir =
|
|
378
|
-
if (!
|
|
379
|
-
|
|
1515
|
+
const destDir = path4.dirname(dest);
|
|
1516
|
+
if (!fs4.existsSync(destDir)) {
|
|
1517
|
+
fs4.mkdirSync(destDir, { recursive: true });
|
|
380
1518
|
}
|
|
381
|
-
if (
|
|
382
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1519
|
+
if (fs4.existsSync(dest) && !overwrite) {
|
|
1520
|
+
console.log(`[fullstack-cli] \u25CB ${path4.basename(dest)} (skipped, already exists)`);
|
|
383
1521
|
return;
|
|
384
1522
|
}
|
|
385
|
-
|
|
386
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1523
|
+
fs4.copyFileSync(src, dest);
|
|
1524
|
+
console.log(`[fullstack-cli] \u2713 ${path4.basename(dest)}`);
|
|
387
1525
|
}
|
|
388
1526
|
function syncDirectory(src, dest, overwrite = true) {
|
|
389
|
-
if (!
|
|
390
|
-
|
|
1527
|
+
if (!fs4.existsSync(dest)) {
|
|
1528
|
+
fs4.mkdirSync(dest, { recursive: true });
|
|
391
1529
|
}
|
|
392
|
-
const files =
|
|
1530
|
+
const files = fs4.readdirSync(src);
|
|
393
1531
|
let count = 0;
|
|
394
1532
|
files.forEach((file) => {
|
|
395
|
-
const srcFile =
|
|
396
|
-
const destFile =
|
|
397
|
-
const stats =
|
|
1533
|
+
const srcFile = path4.join(src, file);
|
|
1534
|
+
const destFile = path4.join(dest, file);
|
|
1535
|
+
const stats = fs4.statSync(srcFile);
|
|
398
1536
|
if (stats.isDirectory()) {
|
|
399
1537
|
syncDirectory(srcFile, destFile, overwrite);
|
|
400
1538
|
} else {
|
|
401
|
-
if (overwrite || !
|
|
402
|
-
|
|
403
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1539
|
+
if (overwrite || !fs4.existsSync(destFile)) {
|
|
1540
|
+
fs4.copyFileSync(srcFile, destFile);
|
|
1541
|
+
console.log(`[fullstack-cli] \u2713 ${path4.relative(dest, destFile)}`);
|
|
404
1542
|
count++;
|
|
405
1543
|
}
|
|
406
1544
|
}
|
|
407
1545
|
});
|
|
408
1546
|
if (count > 0) {
|
|
409
|
-
console.log(`[fullstack-cli] Synced ${count} files to ${
|
|
1547
|
+
console.log(`[fullstack-cli] Synced ${count} files to ${path4.basename(dest)}/`);
|
|
410
1548
|
}
|
|
411
1549
|
}
|
|
412
1550
|
function appendToFile(src, dest) {
|
|
413
|
-
const content =
|
|
1551
|
+
const content = fs4.readFileSync(src, "utf-8");
|
|
414
1552
|
let existingContent = "";
|
|
415
|
-
if (
|
|
416
|
-
existingContent =
|
|
1553
|
+
if (fs4.existsSync(dest)) {
|
|
1554
|
+
existingContent = fs4.readFileSync(dest, "utf-8");
|
|
417
1555
|
}
|
|
418
1556
|
if (existingContent.includes(content.trim())) {
|
|
419
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1557
|
+
console.log(`[fullstack-cli] \u25CB ${path4.basename(dest)} (already contains content)`);
|
|
420
1558
|
return;
|
|
421
1559
|
}
|
|
422
|
-
|
|
423
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1560
|
+
fs4.appendFileSync(dest, content);
|
|
1561
|
+
console.log(`[fullstack-cli] \u2713 ${path4.basename(dest)} (appended)`);
|
|
424
1562
|
}
|
|
425
1563
|
function setPermissions(permissions, projectRoot) {
|
|
426
1564
|
for (const [pattern, mode] of Object.entries(permissions)) {
|
|
427
1565
|
if (pattern === "**/*.sh") {
|
|
428
|
-
const scriptsDir =
|
|
429
|
-
if (
|
|
430
|
-
const files =
|
|
1566
|
+
const scriptsDir = path4.join(projectRoot, "scripts");
|
|
1567
|
+
if (fs4.existsSync(scriptsDir)) {
|
|
1568
|
+
const files = fs4.readdirSync(scriptsDir);
|
|
431
1569
|
files.forEach((file) => {
|
|
432
1570
|
if (file.endsWith(".sh")) {
|
|
433
|
-
const filePath =
|
|
434
|
-
|
|
1571
|
+
const filePath = path4.join(scriptsDir, file);
|
|
1572
|
+
fs4.chmodSync(filePath, mode);
|
|
435
1573
|
}
|
|
436
1574
|
});
|
|
437
1575
|
}
|
|
@@ -439,19 +1577,19 @@ function setPermissions(permissions, projectRoot) {
|
|
|
439
1577
|
}
|
|
440
1578
|
}
|
|
441
1579
|
function deleteFile(filePath) {
|
|
442
|
-
if (
|
|
443
|
-
|
|
444
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1580
|
+
if (fs4.existsSync(filePath)) {
|
|
1581
|
+
fs4.unlinkSync(filePath);
|
|
1582
|
+
console.log(`[fullstack-cli] \u2713 ${path4.basename(filePath)} (deleted)`);
|
|
445
1583
|
} else {
|
|
446
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1584
|
+
console.log(`[fullstack-cli] \u25CB ${path4.basename(filePath)} (not found)`);
|
|
447
1585
|
}
|
|
448
1586
|
}
|
|
449
1587
|
function deleteDirectory(dirPath) {
|
|
450
|
-
if (
|
|
451
|
-
|
|
452
|
-
console.log(`[fullstack-cli] \u2713 ${
|
|
1588
|
+
if (fs4.existsSync(dirPath)) {
|
|
1589
|
+
fs4.rmSync(dirPath, { recursive: true });
|
|
1590
|
+
console.log(`[fullstack-cli] \u2713 ${path4.basename(dirPath)} (deleted)`);
|
|
453
1591
|
} else {
|
|
454
|
-
console.log(`[fullstack-cli] \u25CB ${
|
|
1592
|
+
console.log(`[fullstack-cli] \u25CB ${path4.basename(dirPath)} (not found)`);
|
|
455
1593
|
}
|
|
456
1594
|
}
|
|
457
1595
|
|
|
@@ -467,8 +1605,8 @@ var syncCommand = {
|
|
|
467
1605
|
};
|
|
468
1606
|
|
|
469
1607
|
// src/commands/action-plugin/utils.ts
|
|
470
|
-
import
|
|
471
|
-
import
|
|
1608
|
+
import fs5 from "fs";
|
|
1609
|
+
import path5 from "path";
|
|
472
1610
|
import { spawnSync as spawnSync2, execSync } from "child_process";
|
|
473
1611
|
function parsePluginName(input) {
|
|
474
1612
|
const match = input.match(/^(@[^/]+\/[^@]+)(?:@(.+))?$/);
|
|
@@ -486,18 +1624,18 @@ function getProjectRoot() {
|
|
|
486
1624
|
return process.cwd();
|
|
487
1625
|
}
|
|
488
1626
|
function getPackageJsonPath() {
|
|
489
|
-
return
|
|
1627
|
+
return path5.join(getProjectRoot(), "package.json");
|
|
490
1628
|
}
|
|
491
1629
|
function getPluginPath(pluginName) {
|
|
492
|
-
return
|
|
1630
|
+
return path5.join(getProjectRoot(), "node_modules", pluginName);
|
|
493
1631
|
}
|
|
494
1632
|
function readPackageJson() {
|
|
495
1633
|
const pkgPath = getPackageJsonPath();
|
|
496
|
-
if (!
|
|
1634
|
+
if (!fs5.existsSync(pkgPath)) {
|
|
497
1635
|
throw new Error("package.json not found in current directory");
|
|
498
1636
|
}
|
|
499
1637
|
try {
|
|
500
|
-
const content =
|
|
1638
|
+
const content = fs5.readFileSync(pkgPath, "utf-8");
|
|
501
1639
|
return JSON.parse(content);
|
|
502
1640
|
} catch {
|
|
503
1641
|
throw new Error("Failed to parse package.json");
|
|
@@ -505,7 +1643,7 @@ function readPackageJson() {
|
|
|
505
1643
|
}
|
|
506
1644
|
function writePackageJson(pkg2) {
|
|
507
1645
|
const pkgPath = getPackageJsonPath();
|
|
508
|
-
|
|
1646
|
+
fs5.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
509
1647
|
}
|
|
510
1648
|
function readActionPlugins() {
|
|
511
1649
|
const pkg2 = readPackageJson();
|
|
@@ -538,12 +1676,12 @@ function npmInstall(tgzPath) {
|
|
|
538
1676
|
}
|
|
539
1677
|
}
|
|
540
1678
|
function getPackageVersion(pluginName) {
|
|
541
|
-
const pkgJsonPath =
|
|
542
|
-
if (!
|
|
1679
|
+
const pkgJsonPath = path5.join(getPluginPath(pluginName), "package.json");
|
|
1680
|
+
if (!fs5.existsSync(pkgJsonPath)) {
|
|
543
1681
|
return null;
|
|
544
1682
|
}
|
|
545
1683
|
try {
|
|
546
|
-
const content =
|
|
1684
|
+
const content = fs5.readFileSync(pkgJsonPath, "utf-8");
|
|
547
1685
|
const pkg2 = JSON.parse(content);
|
|
548
1686
|
return pkg2.version || null;
|
|
549
1687
|
} catch {
|
|
@@ -551,49 +1689,49 @@ function getPackageVersion(pluginName) {
|
|
|
551
1689
|
}
|
|
552
1690
|
}
|
|
553
1691
|
function readPluginPackageJson(pluginPath) {
|
|
554
|
-
const pkgJsonPath =
|
|
555
|
-
if (!
|
|
1692
|
+
const pkgJsonPath = path5.join(pluginPath, "package.json");
|
|
1693
|
+
if (!fs5.existsSync(pkgJsonPath)) {
|
|
556
1694
|
return null;
|
|
557
1695
|
}
|
|
558
1696
|
try {
|
|
559
|
-
const content =
|
|
1697
|
+
const content = fs5.readFileSync(pkgJsonPath, "utf-8");
|
|
560
1698
|
return JSON.parse(content);
|
|
561
1699
|
} catch {
|
|
562
1700
|
return null;
|
|
563
1701
|
}
|
|
564
1702
|
}
|
|
565
1703
|
function extractTgzToNodeModules(tgzPath, pluginName) {
|
|
566
|
-
const nodeModulesPath =
|
|
567
|
-
const targetDir =
|
|
568
|
-
const scopeDir =
|
|
569
|
-
if (!
|
|
570
|
-
|
|
1704
|
+
const nodeModulesPath = path5.join(getProjectRoot(), "node_modules");
|
|
1705
|
+
const targetDir = path5.join(nodeModulesPath, pluginName);
|
|
1706
|
+
const scopeDir = path5.dirname(targetDir);
|
|
1707
|
+
if (!fs5.existsSync(scopeDir)) {
|
|
1708
|
+
fs5.mkdirSync(scopeDir, { recursive: true });
|
|
571
1709
|
}
|
|
572
|
-
if (
|
|
573
|
-
|
|
1710
|
+
if (fs5.existsSync(targetDir)) {
|
|
1711
|
+
fs5.rmSync(targetDir, { recursive: true });
|
|
574
1712
|
}
|
|
575
|
-
const tempDir =
|
|
576
|
-
if (
|
|
577
|
-
|
|
1713
|
+
const tempDir = path5.join(nodeModulesPath, ".cache", "fullstack-cli", "extract-temp");
|
|
1714
|
+
if (fs5.existsSync(tempDir)) {
|
|
1715
|
+
fs5.rmSync(tempDir, { recursive: true });
|
|
578
1716
|
}
|
|
579
|
-
|
|
1717
|
+
fs5.mkdirSync(tempDir, { recursive: true });
|
|
580
1718
|
try {
|
|
581
1719
|
execSync(`tar -xzf "${tgzPath}" -C "${tempDir}"`, { stdio: "pipe" });
|
|
582
|
-
const extractedDir =
|
|
583
|
-
if (
|
|
584
|
-
|
|
1720
|
+
const extractedDir = path5.join(tempDir, "package");
|
|
1721
|
+
if (fs5.existsSync(extractedDir)) {
|
|
1722
|
+
fs5.renameSync(extractedDir, targetDir);
|
|
585
1723
|
} else {
|
|
586
|
-
const files =
|
|
1724
|
+
const files = fs5.readdirSync(tempDir);
|
|
587
1725
|
if (files.length === 1) {
|
|
588
|
-
|
|
1726
|
+
fs5.renameSync(path5.join(tempDir, files[0]), targetDir);
|
|
589
1727
|
} else {
|
|
590
1728
|
throw new Error("Unexpected tgz structure");
|
|
591
1729
|
}
|
|
592
1730
|
}
|
|
593
1731
|
return targetDir;
|
|
594
1732
|
} finally {
|
|
595
|
-
if (
|
|
596
|
-
|
|
1733
|
+
if (fs5.existsSync(tempDir)) {
|
|
1734
|
+
fs5.rmSync(tempDir, { recursive: true });
|
|
597
1735
|
}
|
|
598
1736
|
}
|
|
599
1737
|
}
|
|
@@ -602,10 +1740,10 @@ function checkMissingPeerDeps(peerDeps) {
|
|
|
602
1740
|
return [];
|
|
603
1741
|
}
|
|
604
1742
|
const missing = [];
|
|
605
|
-
const nodeModulesPath =
|
|
1743
|
+
const nodeModulesPath = path5.join(getProjectRoot(), "node_modules");
|
|
606
1744
|
for (const [depName, _version] of Object.entries(peerDeps)) {
|
|
607
|
-
const depPath =
|
|
608
|
-
if (!
|
|
1745
|
+
const depPath = path5.join(nodeModulesPath, depName);
|
|
1746
|
+
if (!fs5.existsSync(depPath)) {
|
|
609
1747
|
missing.push(depName);
|
|
610
1748
|
}
|
|
611
1749
|
}
|
|
@@ -629,16 +1767,16 @@ function installMissingDeps(deps) {
|
|
|
629
1767
|
}
|
|
630
1768
|
function removePluginDirectory(pluginName) {
|
|
631
1769
|
const pluginPath = getPluginPath(pluginName);
|
|
632
|
-
if (
|
|
633
|
-
|
|
1770
|
+
if (fs5.existsSync(pluginPath)) {
|
|
1771
|
+
fs5.rmSync(pluginPath, { recursive: true });
|
|
634
1772
|
console.log(`[action-plugin] Removed ${pluginName}`);
|
|
635
1773
|
}
|
|
636
1774
|
}
|
|
637
1775
|
|
|
638
1776
|
// src/commands/action-plugin/api-client.ts
|
|
639
1777
|
import { HttpClient as HttpClient2 } from "@lark-apaas/http-client";
|
|
640
|
-
import
|
|
641
|
-
import
|
|
1778
|
+
import fs6 from "fs";
|
|
1779
|
+
import path6 from "path";
|
|
642
1780
|
|
|
643
1781
|
// src/utils/http-client.ts
|
|
644
1782
|
import { HttpClient } from "@lark-apaas/http-client";
|
|
@@ -723,19 +1861,19 @@ async function downloadFromPublic(downloadURL) {
|
|
|
723
1861
|
return Buffer.from(arrayBuffer);
|
|
724
1862
|
}
|
|
725
1863
|
function getPluginCacheDir() {
|
|
726
|
-
return
|
|
1864
|
+
return path6.join(process.cwd(), PLUGIN_CACHE_DIR);
|
|
727
1865
|
}
|
|
728
1866
|
function ensureCacheDir() {
|
|
729
1867
|
const cacheDir = getPluginCacheDir();
|
|
730
|
-
if (!
|
|
731
|
-
|
|
1868
|
+
if (!fs6.existsSync(cacheDir)) {
|
|
1869
|
+
fs6.mkdirSync(cacheDir, { recursive: true });
|
|
732
1870
|
}
|
|
733
1871
|
}
|
|
734
1872
|
function getTempFilePath(pluginKey, version) {
|
|
735
1873
|
ensureCacheDir();
|
|
736
1874
|
const safeKey = pluginKey.replace(/[/@]/g, "_");
|
|
737
1875
|
const filename = `${safeKey}-${version}.tgz`;
|
|
738
|
-
return
|
|
1876
|
+
return path6.join(getPluginCacheDir(), filename);
|
|
739
1877
|
}
|
|
740
1878
|
async function downloadPlugin(pluginKey, requestedVersion) {
|
|
741
1879
|
console.log(`[action-plugin] Fetching plugin info for ${pluginKey}@${requestedVersion}...`);
|
|
@@ -751,7 +1889,7 @@ async function downloadPlugin(pluginKey, requestedVersion) {
|
|
|
751
1889
|
tgzBuffer = await downloadFromPublic(pluginInfo.downloadURL);
|
|
752
1890
|
}
|
|
753
1891
|
const tgzPath = getTempFilePath(pluginKey, pluginInfo.version);
|
|
754
|
-
|
|
1892
|
+
fs6.writeFileSync(tgzPath, tgzBuffer);
|
|
755
1893
|
console.log(`[action-plugin] Downloaded to ${tgzPath} (${(tgzBuffer.length / 1024).toFixed(2)} KB)`);
|
|
756
1894
|
return {
|
|
757
1895
|
tgzPath,
|
|
@@ -761,8 +1899,8 @@ async function downloadPlugin(pluginKey, requestedVersion) {
|
|
|
761
1899
|
}
|
|
762
1900
|
function cleanupTempFile(tgzPath) {
|
|
763
1901
|
try {
|
|
764
|
-
if (
|
|
765
|
-
|
|
1902
|
+
if (fs6.existsSync(tgzPath)) {
|
|
1903
|
+
fs6.unlinkSync(tgzPath);
|
|
766
1904
|
}
|
|
767
1905
|
} catch {
|
|
768
1906
|
}
|
|
@@ -1087,39 +2225,39 @@ var actionPluginCommandGroup = {
|
|
|
1087
2225
|
};
|
|
1088
2226
|
|
|
1089
2227
|
// src/commands/capability/utils.ts
|
|
1090
|
-
import
|
|
1091
|
-
import
|
|
2228
|
+
import fs7 from "fs";
|
|
2229
|
+
import path7 from "path";
|
|
1092
2230
|
var CAPABILITIES_DIR = "server/capabilities";
|
|
1093
2231
|
function getProjectRoot2() {
|
|
1094
2232
|
return process.cwd();
|
|
1095
2233
|
}
|
|
1096
2234
|
function getCapabilitiesDir() {
|
|
1097
|
-
return
|
|
2235
|
+
return path7.join(getProjectRoot2(), CAPABILITIES_DIR);
|
|
1098
2236
|
}
|
|
1099
2237
|
function getCapabilityPath(id) {
|
|
1100
|
-
return
|
|
2238
|
+
return path7.join(getCapabilitiesDir(), `${id}.json`);
|
|
1101
2239
|
}
|
|
1102
2240
|
function getPluginManifestPath(pluginKey) {
|
|
1103
|
-
return
|
|
2241
|
+
return path7.join(getProjectRoot2(), "node_modules", pluginKey, "manifest.json");
|
|
1104
2242
|
}
|
|
1105
2243
|
function capabilitiesDirExists() {
|
|
1106
|
-
return
|
|
2244
|
+
return fs7.existsSync(getCapabilitiesDir());
|
|
1107
2245
|
}
|
|
1108
2246
|
function listCapabilityIds() {
|
|
1109
2247
|
const dir = getCapabilitiesDir();
|
|
1110
|
-
if (!
|
|
2248
|
+
if (!fs7.existsSync(dir)) {
|
|
1111
2249
|
return [];
|
|
1112
2250
|
}
|
|
1113
|
-
const files =
|
|
2251
|
+
const files = fs7.readdirSync(dir);
|
|
1114
2252
|
return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
|
|
1115
2253
|
}
|
|
1116
2254
|
function readCapability(id) {
|
|
1117
2255
|
const filePath = getCapabilityPath(id);
|
|
1118
|
-
if (!
|
|
2256
|
+
if (!fs7.existsSync(filePath)) {
|
|
1119
2257
|
throw new Error(`Capability not found: ${id}`);
|
|
1120
2258
|
}
|
|
1121
2259
|
try {
|
|
1122
|
-
const content =
|
|
2260
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
1123
2261
|
return JSON.parse(content);
|
|
1124
2262
|
} catch (error) {
|
|
1125
2263
|
if (error instanceof SyntaxError) {
|
|
@@ -1134,11 +2272,11 @@ function readAllCapabilities() {
|
|
|
1134
2272
|
}
|
|
1135
2273
|
function readPluginManifest(pluginKey) {
|
|
1136
2274
|
const manifestPath = getPluginManifestPath(pluginKey);
|
|
1137
|
-
if (!
|
|
2275
|
+
if (!fs7.existsSync(manifestPath)) {
|
|
1138
2276
|
throw new Error(`Plugin not installed: ${pluginKey} (manifest.json not found)`);
|
|
1139
2277
|
}
|
|
1140
2278
|
try {
|
|
1141
|
-
const content =
|
|
2279
|
+
const content = fs7.readFileSync(manifestPath, "utf-8");
|
|
1142
2280
|
return JSON.parse(content);
|
|
1143
2281
|
} catch (error) {
|
|
1144
2282
|
if (error instanceof SyntaxError) {
|
|
@@ -1312,58 +2450,58 @@ var capabilityCommandGroup = {
|
|
|
1312
2450
|
};
|
|
1313
2451
|
|
|
1314
2452
|
// src/commands/migration/version-manager.ts
|
|
1315
|
-
import
|
|
1316
|
-
import
|
|
2453
|
+
import fs8 from "fs";
|
|
2454
|
+
import path8 from "path";
|
|
1317
2455
|
var PACKAGE_JSON = "package.json";
|
|
1318
2456
|
var VERSION_FIELD = "migrationVersion";
|
|
1319
2457
|
function getPackageJsonPath2() {
|
|
1320
|
-
return
|
|
2458
|
+
return path8.join(process.cwd(), PACKAGE_JSON);
|
|
1321
2459
|
}
|
|
1322
2460
|
function getCurrentVersion() {
|
|
1323
2461
|
const pkgPath = getPackageJsonPath2();
|
|
1324
|
-
if (!
|
|
2462
|
+
if (!fs8.existsSync(pkgPath)) {
|
|
1325
2463
|
throw new Error("package.json not found");
|
|
1326
2464
|
}
|
|
1327
|
-
const pkg2 = JSON.parse(
|
|
2465
|
+
const pkg2 = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
|
|
1328
2466
|
return pkg2[VERSION_FIELD] ?? 0;
|
|
1329
2467
|
}
|
|
1330
2468
|
function setCurrentVersion(version) {
|
|
1331
2469
|
const pkgPath = getPackageJsonPath2();
|
|
1332
|
-
const pkg2 = JSON.parse(
|
|
2470
|
+
const pkg2 = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
|
|
1333
2471
|
pkg2[VERSION_FIELD] = version;
|
|
1334
|
-
|
|
2472
|
+
fs8.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n", "utf-8");
|
|
1335
2473
|
}
|
|
1336
2474
|
|
|
1337
2475
|
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
1338
|
-
import
|
|
1339
|
-
import
|
|
2476
|
+
import fs10 from "fs";
|
|
2477
|
+
import path10 from "path";
|
|
1340
2478
|
|
|
1341
2479
|
// src/commands/migration/versions/v001_capability/utils.ts
|
|
1342
|
-
import
|
|
1343
|
-
import
|
|
2480
|
+
import fs9 from "fs";
|
|
2481
|
+
import path9 from "path";
|
|
1344
2482
|
var CAPABILITIES_DIR2 = "server/capabilities";
|
|
1345
2483
|
function getProjectRoot3() {
|
|
1346
2484
|
return process.cwd();
|
|
1347
2485
|
}
|
|
1348
2486
|
function getCapabilitiesDir2() {
|
|
1349
|
-
return
|
|
2487
|
+
return path9.join(getProjectRoot3(), CAPABILITIES_DIR2);
|
|
1350
2488
|
}
|
|
1351
2489
|
function getPluginManifestPath2(pluginKey) {
|
|
1352
|
-
return
|
|
2490
|
+
return path9.join(getProjectRoot3(), "node_modules", pluginKey, "manifest.json");
|
|
1353
2491
|
}
|
|
1354
2492
|
|
|
1355
2493
|
// src/commands/migration/versions/v001_capability/json-migrator/detector.ts
|
|
1356
2494
|
function detectJsonMigration() {
|
|
1357
2495
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
1358
|
-
const oldFilePath =
|
|
1359
|
-
if (!
|
|
2496
|
+
const oldFilePath = path10.join(capabilitiesDir, "capabilities.json");
|
|
2497
|
+
if (!fs10.existsSync(oldFilePath)) {
|
|
1360
2498
|
return {
|
|
1361
2499
|
needsMigration: false,
|
|
1362
2500
|
reason: "capabilities.json not found"
|
|
1363
2501
|
};
|
|
1364
2502
|
}
|
|
1365
2503
|
try {
|
|
1366
|
-
const content =
|
|
2504
|
+
const content = fs10.readFileSync(oldFilePath, "utf-8");
|
|
1367
2505
|
const parsed = JSON.parse(content);
|
|
1368
2506
|
const capabilities = Array.isArray(parsed) ? parsed : [];
|
|
1369
2507
|
return {
|
|
@@ -1403,8 +2541,8 @@ async function check(options) {
|
|
|
1403
2541
|
}
|
|
1404
2542
|
|
|
1405
2543
|
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
1406
|
-
import
|
|
1407
|
-
import
|
|
2544
|
+
import fs11 from "fs";
|
|
2545
|
+
import path11 from "path";
|
|
1408
2546
|
|
|
1409
2547
|
// src/commands/migration/versions/v001_capability/mapping.ts
|
|
1410
2548
|
var DEFAULT_PLUGIN_VERSION = "1.0.0";
|
|
@@ -1634,18 +2772,18 @@ function transformCapabilities(oldCapabilities) {
|
|
|
1634
2772
|
// src/commands/migration/versions/v001_capability/json-migrator/index.ts
|
|
1635
2773
|
function loadExistingCapabilities() {
|
|
1636
2774
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
1637
|
-
if (!
|
|
2775
|
+
if (!fs11.existsSync(capabilitiesDir)) {
|
|
1638
2776
|
return [];
|
|
1639
2777
|
}
|
|
1640
|
-
const files =
|
|
2778
|
+
const files = fs11.readdirSync(capabilitiesDir);
|
|
1641
2779
|
const capabilities = [];
|
|
1642
2780
|
for (const file of files) {
|
|
1643
2781
|
if (file === "capabilities.json" || !file.endsWith(".json")) {
|
|
1644
2782
|
continue;
|
|
1645
2783
|
}
|
|
1646
2784
|
try {
|
|
1647
|
-
const filePath =
|
|
1648
|
-
const content =
|
|
2785
|
+
const filePath = path11.join(capabilitiesDir, file);
|
|
2786
|
+
const content = fs11.readFileSync(filePath, "utf-8");
|
|
1649
2787
|
const capability = JSON.parse(content);
|
|
1650
2788
|
if (capability.id && capability.pluginKey) {
|
|
1651
2789
|
capabilities.push(capability);
|
|
@@ -1703,9 +2841,9 @@ async function migrateJsonFiles(options) {
|
|
|
1703
2841
|
}
|
|
1704
2842
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
1705
2843
|
for (const cap of newCapabilities) {
|
|
1706
|
-
const filePath =
|
|
2844
|
+
const filePath = path11.join(capabilitiesDir, `${cap.id}.json`);
|
|
1707
2845
|
const content = JSON.stringify(cap, null, 2);
|
|
1708
|
-
|
|
2846
|
+
fs11.writeFileSync(filePath, content, "utf-8");
|
|
1709
2847
|
console.log(` \u2713 Created: ${cap.id}.json`);
|
|
1710
2848
|
}
|
|
1711
2849
|
return {
|
|
@@ -1717,10 +2855,10 @@ async function migrateJsonFiles(options) {
|
|
|
1717
2855
|
}
|
|
1718
2856
|
|
|
1719
2857
|
// src/commands/migration/versions/v001_capability/plugin-installer/detector.ts
|
|
1720
|
-
import
|
|
2858
|
+
import fs12 from "fs";
|
|
1721
2859
|
function isPluginInstalled2(pluginKey) {
|
|
1722
2860
|
const manifestPath = getPluginManifestPath2(pluginKey);
|
|
1723
|
-
return
|
|
2861
|
+
return fs12.existsSync(manifestPath);
|
|
1724
2862
|
}
|
|
1725
2863
|
function detectPluginsToInstall(capabilities) {
|
|
1726
2864
|
const pluginKeys = /* @__PURE__ */ new Set();
|
|
@@ -1796,12 +2934,12 @@ async function installPlugins(capabilities, options) {
|
|
|
1796
2934
|
}
|
|
1797
2935
|
|
|
1798
2936
|
// src/commands/migration/versions/v001_capability/code-migrator/index.ts
|
|
1799
|
-
import
|
|
1800
|
-
import { Project } from "ts-morph";
|
|
2937
|
+
import path13 from "path";
|
|
2938
|
+
import { Project as Project2 } from "ts-morph";
|
|
1801
2939
|
|
|
1802
2940
|
// src/commands/migration/versions/v001_capability/code-migrator/scanner.ts
|
|
1803
|
-
import
|
|
1804
|
-
import
|
|
2941
|
+
import fs13 from "fs";
|
|
2942
|
+
import path12 from "path";
|
|
1805
2943
|
var EXCLUDED_DIRS = [
|
|
1806
2944
|
"node_modules",
|
|
1807
2945
|
"dist",
|
|
@@ -1816,9 +2954,9 @@ var EXCLUDED_PATTERNS = [
|
|
|
1816
2954
|
/\.d\.ts$/
|
|
1817
2955
|
];
|
|
1818
2956
|
function scanDirectory(dir, files = []) {
|
|
1819
|
-
const entries =
|
|
2957
|
+
const entries = fs13.readdirSync(dir, { withFileTypes: true });
|
|
1820
2958
|
for (const entry of entries) {
|
|
1821
|
-
const fullPath =
|
|
2959
|
+
const fullPath = path12.join(dir, entry.name);
|
|
1822
2960
|
if (entry.isDirectory()) {
|
|
1823
2961
|
if (EXCLUDED_DIRS.includes(entry.name)) {
|
|
1824
2962
|
continue;
|
|
@@ -1834,14 +2972,14 @@ function scanDirectory(dir, files = []) {
|
|
|
1834
2972
|
return files;
|
|
1835
2973
|
}
|
|
1836
2974
|
function scanServerFiles() {
|
|
1837
|
-
const serverDir =
|
|
1838
|
-
if (!
|
|
2975
|
+
const serverDir = path12.join(getProjectRoot3(), "server");
|
|
2976
|
+
if (!fs13.existsSync(serverDir)) {
|
|
1839
2977
|
return [];
|
|
1840
2978
|
}
|
|
1841
2979
|
return scanDirectory(serverDir);
|
|
1842
2980
|
}
|
|
1843
2981
|
function hasCapabilityImport(filePath) {
|
|
1844
|
-
const content =
|
|
2982
|
+
const content = fs13.readFileSync(filePath, "utf-8");
|
|
1845
2983
|
return /import\s+.*from\s+['"][^'"]*capabilities[^'"]*['"]/.test(content);
|
|
1846
2984
|
}
|
|
1847
2985
|
function scanFilesToMigrate() {
|
|
@@ -2216,7 +3354,7 @@ function analyzeFile(project, filePath, actionNameMap) {
|
|
|
2216
3354
|
const callSites = analyzeCallSites(sourceFile, imports);
|
|
2217
3355
|
const classInfo = analyzeClass(sourceFile);
|
|
2218
3356
|
const { canMigrate, reason } = canAutoMigrate(classInfo);
|
|
2219
|
-
const relativePath =
|
|
3357
|
+
const relativePath = path13.relative(getProjectRoot3(), filePath);
|
|
2220
3358
|
return {
|
|
2221
3359
|
filePath: relativePath,
|
|
2222
3360
|
imports,
|
|
@@ -2227,7 +3365,7 @@ function analyzeFile(project, filePath, actionNameMap) {
|
|
|
2227
3365
|
};
|
|
2228
3366
|
}
|
|
2229
3367
|
function migrateFile(project, analysis, dryRun) {
|
|
2230
|
-
const absolutePath =
|
|
3368
|
+
const absolutePath = path13.join(getProjectRoot3(), analysis.filePath);
|
|
2231
3369
|
if (!analysis.canAutoMigrate) {
|
|
2232
3370
|
return {
|
|
2233
3371
|
filePath: analysis.filePath,
|
|
@@ -2287,7 +3425,7 @@ async function migrateCode(options, capabilities) {
|
|
|
2287
3425
|
console.log(" No files need code migration.\n");
|
|
2288
3426
|
return result;
|
|
2289
3427
|
}
|
|
2290
|
-
const project = new
|
|
3428
|
+
const project = new Project2({
|
|
2291
3429
|
skipAddingFilesFromTsConfig: true,
|
|
2292
3430
|
compilerOptions: {
|
|
2293
3431
|
allowJs: true
|
|
@@ -2330,17 +3468,17 @@ function getSuggestion(analysis) {
|
|
|
2330
3468
|
}
|
|
2331
3469
|
|
|
2332
3470
|
// src/commands/migration/versions/v001_capability/cleanup.ts
|
|
2333
|
-
import
|
|
2334
|
-
import
|
|
3471
|
+
import fs14 from "fs";
|
|
3472
|
+
import path14 from "path";
|
|
2335
3473
|
function cleanupOldFiles(capabilities, dryRun) {
|
|
2336
3474
|
const deletedFiles = [];
|
|
2337
3475
|
const errors = [];
|
|
2338
3476
|
const capabilitiesDir = getCapabilitiesDir2();
|
|
2339
|
-
const oldJsonPath =
|
|
2340
|
-
if (
|
|
3477
|
+
const oldJsonPath = path14.join(capabilitiesDir, "capabilities.json");
|
|
3478
|
+
if (fs14.existsSync(oldJsonPath)) {
|
|
2341
3479
|
try {
|
|
2342
3480
|
if (!dryRun) {
|
|
2343
|
-
|
|
3481
|
+
fs14.unlinkSync(oldJsonPath);
|
|
2344
3482
|
}
|
|
2345
3483
|
deletedFiles.push("capabilities.json");
|
|
2346
3484
|
} catch (error) {
|
|
@@ -2348,11 +3486,11 @@ function cleanupOldFiles(capabilities, dryRun) {
|
|
|
2348
3486
|
}
|
|
2349
3487
|
}
|
|
2350
3488
|
for (const cap of capabilities) {
|
|
2351
|
-
const tsFilePath =
|
|
2352
|
-
if (
|
|
3489
|
+
const tsFilePath = path14.join(capabilitiesDir, `${cap.id}.ts`);
|
|
3490
|
+
if (fs14.existsSync(tsFilePath)) {
|
|
2353
3491
|
try {
|
|
2354
3492
|
if (!dryRun) {
|
|
2355
|
-
|
|
3493
|
+
fs14.unlinkSync(tsFilePath);
|
|
2356
3494
|
}
|
|
2357
3495
|
deletedFiles.push(`${cap.id}.ts`);
|
|
2358
3496
|
} catch (error) {
|
|
@@ -2368,8 +3506,8 @@ function cleanupOldFiles(capabilities, dryRun) {
|
|
|
2368
3506
|
}
|
|
2369
3507
|
|
|
2370
3508
|
// src/commands/migration/versions/v001_capability/report-generator.ts
|
|
2371
|
-
import
|
|
2372
|
-
import
|
|
3509
|
+
import fs15 from "fs";
|
|
3510
|
+
import path15 from "path";
|
|
2373
3511
|
var REPORT_FILE = "capability-migration-report.md";
|
|
2374
3512
|
function printSummary(result) {
|
|
2375
3513
|
const { jsonMigration, pluginInstallation, codeMigration, cleanup } = result;
|
|
@@ -2532,15 +3670,15 @@ async function generateReport(result) {
|
|
|
2532
3670
|
}
|
|
2533
3671
|
lines.push("");
|
|
2534
3672
|
const logDir = process.env.LOG_DIR || "logs";
|
|
2535
|
-
if (!
|
|
3673
|
+
if (!fs15.existsSync(logDir)) {
|
|
2536
3674
|
return;
|
|
2537
3675
|
}
|
|
2538
|
-
const reportDir =
|
|
2539
|
-
if (!
|
|
2540
|
-
|
|
3676
|
+
const reportDir = path15.join(logDir, "migration");
|
|
3677
|
+
if (!fs15.existsSync(reportDir)) {
|
|
3678
|
+
fs15.mkdirSync(reportDir, { recursive: true });
|
|
2541
3679
|
}
|
|
2542
|
-
const reportPath =
|
|
2543
|
-
|
|
3680
|
+
const reportPath = path15.join(reportDir, REPORT_FILE);
|
|
3681
|
+
fs15.writeFileSync(reportPath, lines.join("\n"), "utf-8");
|
|
2544
3682
|
console.log(`\u{1F4C4} Report generated: ${reportPath}`);
|
|
2545
3683
|
}
|
|
2546
3684
|
|
|
@@ -3072,10 +4210,10 @@ var migrationCommand = {
|
|
|
3072
4210
|
};
|
|
3073
4211
|
|
|
3074
4212
|
// src/commands/read-logs/index.ts
|
|
3075
|
-
import
|
|
4213
|
+
import path16 from "path";
|
|
3076
4214
|
|
|
3077
4215
|
// src/commands/read-logs/std-utils.ts
|
|
3078
|
-
import
|
|
4216
|
+
import fs16 from "fs";
|
|
3079
4217
|
function formatStdPrefixTime(localTime) {
|
|
3080
4218
|
const match = localTime.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
|
|
3081
4219
|
if (!match) return localTime;
|
|
@@ -3105,11 +4243,11 @@ function stripPrefixFromStdLine(line) {
|
|
|
3105
4243
|
return `[${time}] ${content}`;
|
|
3106
4244
|
}
|
|
3107
4245
|
function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarker) {
|
|
3108
|
-
const stat =
|
|
4246
|
+
const stat = fs16.statSync(filePath);
|
|
3109
4247
|
if (stat.size === 0) {
|
|
3110
4248
|
return { lines: [], markerFound: false, totalLinesCount: 0 };
|
|
3111
4249
|
}
|
|
3112
|
-
const fd =
|
|
4250
|
+
const fd = fs16.openSync(filePath, "r");
|
|
3113
4251
|
const chunkSize = 64 * 1024;
|
|
3114
4252
|
let position = stat.size;
|
|
3115
4253
|
let remainder = "";
|
|
@@ -3123,7 +4261,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
|
|
|
3123
4261
|
const length = Math.min(chunkSize, position);
|
|
3124
4262
|
position -= length;
|
|
3125
4263
|
const buffer = Buffer.alloc(length);
|
|
3126
|
-
|
|
4264
|
+
fs16.readSync(fd, buffer, 0, length, position);
|
|
3127
4265
|
let chunk = buffer.toString("utf8");
|
|
3128
4266
|
if (remainder) {
|
|
3129
4267
|
chunk += remainder;
|
|
@@ -3165,7 +4303,7 @@ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarke
|
|
|
3165
4303
|
}
|
|
3166
4304
|
}
|
|
3167
4305
|
} finally {
|
|
3168
|
-
|
|
4306
|
+
fs16.closeSync(fd);
|
|
3169
4307
|
}
|
|
3170
4308
|
return { lines: collected.reverse(), markerFound, totalLinesCount };
|
|
3171
4309
|
}
|
|
@@ -3186,21 +4324,21 @@ function readServerStdSegment(filePath, maxLines, offset) {
|
|
|
3186
4324
|
}
|
|
3187
4325
|
|
|
3188
4326
|
// src/commands/read-logs/tail.ts
|
|
3189
|
-
import
|
|
4327
|
+
import fs17 from "fs";
|
|
3190
4328
|
function fileExists(filePath) {
|
|
3191
4329
|
try {
|
|
3192
|
-
|
|
4330
|
+
fs17.accessSync(filePath, fs17.constants.F_OK | fs17.constants.R_OK);
|
|
3193
4331
|
return true;
|
|
3194
4332
|
} catch {
|
|
3195
4333
|
return false;
|
|
3196
4334
|
}
|
|
3197
4335
|
}
|
|
3198
4336
|
function readFileTailLines(filePath, maxLines) {
|
|
3199
|
-
const stat =
|
|
4337
|
+
const stat = fs17.statSync(filePath);
|
|
3200
4338
|
if (stat.size === 0) {
|
|
3201
4339
|
return [];
|
|
3202
4340
|
}
|
|
3203
|
-
const fd =
|
|
4341
|
+
const fd = fs17.openSync(filePath, "r");
|
|
3204
4342
|
const chunkSize = 64 * 1024;
|
|
3205
4343
|
const chunks = [];
|
|
3206
4344
|
let position = stat.size;
|
|
@@ -3210,13 +4348,13 @@ function readFileTailLines(filePath, maxLines) {
|
|
|
3210
4348
|
const length = Math.min(chunkSize, position);
|
|
3211
4349
|
position -= length;
|
|
3212
4350
|
const buffer = Buffer.alloc(length);
|
|
3213
|
-
|
|
4351
|
+
fs17.readSync(fd, buffer, 0, length, position);
|
|
3214
4352
|
chunks.unshift(buffer.toString("utf8"));
|
|
3215
4353
|
const chunkLines = buffer.toString("utf8").split("\n").length - 1;
|
|
3216
4354
|
collectedLines += chunkLines;
|
|
3217
4355
|
}
|
|
3218
4356
|
} finally {
|
|
3219
|
-
|
|
4357
|
+
fs17.closeSync(fd);
|
|
3220
4358
|
}
|
|
3221
4359
|
const content = chunks.join("");
|
|
3222
4360
|
const allLines = content.split("\n");
|
|
@@ -3232,11 +4370,11 @@ function readFileTailLines(filePath, maxLines) {
|
|
|
3232
4370
|
return allLines.slice(allLines.length - maxLines);
|
|
3233
4371
|
}
|
|
3234
4372
|
function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
3235
|
-
const stat =
|
|
4373
|
+
const stat = fs17.statSync(filePath);
|
|
3236
4374
|
if (stat.size === 0) {
|
|
3237
4375
|
return { lines: [], totalLinesCount: 0 };
|
|
3238
4376
|
}
|
|
3239
|
-
const fd =
|
|
4377
|
+
const fd = fs17.openSync(filePath, "r");
|
|
3240
4378
|
const chunkSize = 64 * 1024;
|
|
3241
4379
|
let position = stat.size;
|
|
3242
4380
|
let remainder = "";
|
|
@@ -3248,7 +4386,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
|
3248
4386
|
const length = Math.min(chunkSize, position);
|
|
3249
4387
|
position -= length;
|
|
3250
4388
|
const buffer = Buffer.alloc(length);
|
|
3251
|
-
|
|
4389
|
+
fs17.readSync(fd, buffer, 0, length, position);
|
|
3252
4390
|
let chunk = buffer.toString("utf8");
|
|
3253
4391
|
if (remainder) {
|
|
3254
4392
|
chunk += remainder;
|
|
@@ -3279,7 +4417,7 @@ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
|
|
|
3279
4417
|
}
|
|
3280
4418
|
}
|
|
3281
4419
|
} finally {
|
|
3282
|
-
|
|
4420
|
+
fs17.closeSync(fd);
|
|
3283
4421
|
}
|
|
3284
4422
|
return { lines: collected.reverse(), totalLinesCount };
|
|
3285
4423
|
}
|
|
@@ -3381,7 +4519,7 @@ function extractClientStdSegment(lines, maxLines, offset) {
|
|
|
3381
4519
|
}
|
|
3382
4520
|
|
|
3383
4521
|
// src/commands/read-logs/json-lines.ts
|
|
3384
|
-
import
|
|
4522
|
+
import fs18 from "fs";
|
|
3385
4523
|
function normalizePid(value) {
|
|
3386
4524
|
if (typeof value === "number") {
|
|
3387
4525
|
return String(value);
|
|
@@ -3432,11 +4570,11 @@ function buildWantedLevelSet(levels) {
|
|
|
3432
4570
|
return set.size > 0 ? set : null;
|
|
3433
4571
|
}
|
|
3434
4572
|
function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
3435
|
-
const stat =
|
|
4573
|
+
const stat = fs18.statSync(filePath);
|
|
3436
4574
|
if (stat.size === 0) {
|
|
3437
4575
|
return { lines: [], totalLinesCount: 0 };
|
|
3438
4576
|
}
|
|
3439
|
-
const fd =
|
|
4577
|
+
const fd = fs18.openSync(filePath, "r");
|
|
3440
4578
|
const chunkSize = 64 * 1024;
|
|
3441
4579
|
let position = stat.size;
|
|
3442
4580
|
let remainder = "";
|
|
@@ -3451,7 +4589,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
|
3451
4589
|
const length = Math.min(chunkSize, position);
|
|
3452
4590
|
position -= length;
|
|
3453
4591
|
const buffer = Buffer.alloc(length);
|
|
3454
|
-
|
|
4592
|
+
fs18.readSync(fd, buffer, 0, length, position);
|
|
3455
4593
|
let chunk = buffer.toString("utf8");
|
|
3456
4594
|
if (remainder) {
|
|
3457
4595
|
chunk += remainder;
|
|
@@ -3513,7 +4651,7 @@ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
|
|
|
3513
4651
|
}
|
|
3514
4652
|
}
|
|
3515
4653
|
} finally {
|
|
3516
|
-
|
|
4654
|
+
fs18.closeSync(fd);
|
|
3517
4655
|
}
|
|
3518
4656
|
return { lines: collected.reverse(), totalLinesCount };
|
|
3519
4657
|
}
|
|
@@ -3556,11 +4694,11 @@ function extractTraceId(obj) {
|
|
|
3556
4694
|
function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
3557
4695
|
const wanted = traceId.trim();
|
|
3558
4696
|
if (!wanted) return { lines: [], totalLinesCount: 0 };
|
|
3559
|
-
const stat =
|
|
4697
|
+
const stat = fs18.statSync(filePath);
|
|
3560
4698
|
if (stat.size === 0) {
|
|
3561
4699
|
return { lines: [], totalLinesCount: 0 };
|
|
3562
4700
|
}
|
|
3563
|
-
const fd =
|
|
4701
|
+
const fd = fs18.openSync(filePath, "r");
|
|
3564
4702
|
const chunkSize = 64 * 1024;
|
|
3565
4703
|
let position = stat.size;
|
|
3566
4704
|
let remainder = "";
|
|
@@ -3573,7 +4711,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
|
3573
4711
|
const length = Math.min(chunkSize, position);
|
|
3574
4712
|
position -= length;
|
|
3575
4713
|
const buffer = Buffer.alloc(length);
|
|
3576
|
-
|
|
4714
|
+
fs18.readSync(fd, buffer, 0, length, position);
|
|
3577
4715
|
let chunk = buffer.toString("utf8");
|
|
3578
4716
|
if (remainder) {
|
|
3579
4717
|
chunk += remainder;
|
|
@@ -3626,7 +4764,7 @@ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
|
|
|
3626
4764
|
}
|
|
3627
4765
|
}
|
|
3628
4766
|
} finally {
|
|
3629
|
-
|
|
4767
|
+
fs18.closeSync(fd);
|
|
3630
4768
|
}
|
|
3631
4769
|
return { lines: collected.reverse(), totalLinesCount };
|
|
3632
4770
|
}
|
|
@@ -3635,11 +4773,11 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
3635
4773
|
if (!wantedLevelSet) {
|
|
3636
4774
|
return { lines: [], totalLinesCount: 0 };
|
|
3637
4775
|
}
|
|
3638
|
-
const stat =
|
|
4776
|
+
const stat = fs18.statSync(filePath);
|
|
3639
4777
|
if (stat.size === 0) {
|
|
3640
4778
|
return { lines: [], totalLinesCount: 0 };
|
|
3641
4779
|
}
|
|
3642
|
-
const fd =
|
|
4780
|
+
const fd = fs18.openSync(filePath, "r");
|
|
3643
4781
|
const chunkSize = 64 * 1024;
|
|
3644
4782
|
let position = stat.size;
|
|
3645
4783
|
let remainder = "";
|
|
@@ -3651,7 +4789,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
3651
4789
|
const length = Math.min(chunkSize, position);
|
|
3652
4790
|
position -= length;
|
|
3653
4791
|
const buffer = Buffer.alloc(length);
|
|
3654
|
-
|
|
4792
|
+
fs18.readSync(fd, buffer, 0, length, position);
|
|
3655
4793
|
let chunk = buffer.toString("utf8");
|
|
3656
4794
|
if (remainder) {
|
|
3657
4795
|
chunk += remainder;
|
|
@@ -3698,7 +4836,7 @@ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
|
|
|
3698
4836
|
}
|
|
3699
4837
|
}
|
|
3700
4838
|
} finally {
|
|
3701
|
-
|
|
4839
|
+
fs18.closeSync(fd);
|
|
3702
4840
|
}
|
|
3703
4841
|
return { lines: collected.reverse(), totalLinesCount };
|
|
3704
4842
|
}
|
|
@@ -3829,21 +4967,21 @@ async function readLogsJsonResult(options) {
|
|
|
3829
4967
|
};
|
|
3830
4968
|
}
|
|
3831
4969
|
function resolveLogFilePath(logDir, type) {
|
|
3832
|
-
const base =
|
|
4970
|
+
const base = path16.isAbsolute(logDir) ? logDir : path16.join(process.cwd(), logDir);
|
|
3833
4971
|
if (type === "server") {
|
|
3834
|
-
return
|
|
4972
|
+
return path16.join(base, "server.log");
|
|
3835
4973
|
}
|
|
3836
4974
|
if (type === "trace") {
|
|
3837
|
-
return
|
|
4975
|
+
return path16.join(base, "trace.log");
|
|
3838
4976
|
}
|
|
3839
4977
|
if (type === "server-std") {
|
|
3840
|
-
return
|
|
4978
|
+
return path16.join(base, "server.std.log");
|
|
3841
4979
|
}
|
|
3842
4980
|
if (type === "client-std") {
|
|
3843
|
-
return
|
|
4981
|
+
return path16.join(base, "client.std.log");
|
|
3844
4982
|
}
|
|
3845
4983
|
if (type === "browser") {
|
|
3846
|
-
return
|
|
4984
|
+
return path16.join(base, "browser.log");
|
|
3847
4985
|
}
|
|
3848
4986
|
throw new Error(`Unsupported log type: ${type}`);
|
|
3849
4987
|
}
|
|
@@ -3918,12 +5056,12 @@ var commands = [
|
|
|
3918
5056
|
];
|
|
3919
5057
|
|
|
3920
5058
|
// src/index.ts
|
|
3921
|
-
var envPath =
|
|
3922
|
-
if (
|
|
5059
|
+
var envPath = path17.join(process.cwd(), ".env");
|
|
5060
|
+
if (fs19.existsSync(envPath)) {
|
|
3923
5061
|
dotenvConfig({ path: envPath });
|
|
3924
5062
|
}
|
|
3925
|
-
var __dirname =
|
|
3926
|
-
var pkg = JSON.parse(
|
|
5063
|
+
var __dirname = path17.dirname(fileURLToPath3(import.meta.url));
|
|
5064
|
+
var pkg = JSON.parse(fs19.readFileSync(path17.join(__dirname, "../package.json"), "utf-8"));
|
|
3927
5065
|
var cli = new FullstackCLI(pkg.version);
|
|
3928
5066
|
cli.useAll(commands);
|
|
3929
5067
|
cli.run();
|