@lark-apaas/fullstack-cli 1.1.25 → 1.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +285 -42
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1041,6 +1041,8 @@ async function fetchColumnComments(connectionString, options = {}) {
1041
1041
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
1042
1042
  const url = new URL(connectionString);
1043
1043
  const schemaName = url.searchParams.get("schema") ?? "public";
1044
+ const start = Date.now();
1045
+ console.log(`[fetchColumnComments] \u2192 Querying pg_description for schema=${schemaName} (timeout=${timeoutMs}ms)`);
1044
1046
  const sql = postgres(connectionString, {
1045
1047
  connect_timeout: Math.ceil(timeoutMs / 1e3),
1046
1048
  idle_timeout: Math.ceil(timeoutMs / 1e3)
@@ -1069,6 +1071,7 @@ async function fetchColumnComments(connectionString, options = {}) {
1069
1071
  const key = `${row.tableName}.${row.columnName}`;
1070
1072
  commentMap.set(key, row.comment);
1071
1073
  }
1074
+ console.log(`[fetchColumnComments] \u2190 Fetched ${commentMap.size} column comments (${Date.now() - start}ms)`);
1072
1075
  return commentMap;
1073
1076
  } finally {
1074
1077
  await sql.end().catch(() => {
@@ -1167,6 +1170,79 @@ function addJsonbTypeComments(source, columnComments) {
1167
1170
  return { text: result.join("\n"), added };
1168
1171
  }
1169
1172
 
1173
+ // src/commands/db/gen-dbschema/transforms/text/synced-table-comments.ts
1174
+ var TABLE_COMMENT = "Synced table: data is auto-synced from external source. Do not rename or delete this table.";
1175
+ var FIELD_COMMENT = "Synced field: auto-synced, do not modify or delete";
1176
+ var TABLE_DEF_REGEX = /^(export const\s+\w+\s*=\s*(?:pgTable|pgView|pgMaterializedView)\(\s*["'`])([^"'`]+)(["'`])/;
1177
+ var FIELD_WITH_NAME_REGEX = /^\s*[\w"']+\s*:\s*\w+\(\s*["'`]([^"'`]+)["'`]/;
1178
+ var FIELD_PROP_NAME_REGEX = /^\s*([\w]+)\s*:/;
1179
+ function addSyncedTableComments(source, syncedTableMap) {
1180
+ if (!syncedTableMap || syncedTableMap.size === 0) {
1181
+ return { text: source, added: 0 };
1182
+ }
1183
+ const lines = source.split("\n");
1184
+ const result = [];
1185
+ let added = 0;
1186
+ let currentSyncedFields = null;
1187
+ let insideTableBody = false;
1188
+ let braceDepth = 0;
1189
+ for (let i = 0; i < lines.length; i++) {
1190
+ const line = lines[i];
1191
+ const tableMatch = line.match(TABLE_DEF_REGEX);
1192
+ if (tableMatch) {
1193
+ const tableName = tableMatch[2];
1194
+ const syncedFields = syncedTableMap.get(tableName);
1195
+ if (syncedFields) {
1196
+ currentSyncedFields = syncedFields;
1197
+ insideTableBody = true;
1198
+ braceDepth = 0;
1199
+ const prevLine = result[result.length - 1]?.trim() ?? "";
1200
+ if (!prevLine.includes("Synced table")) {
1201
+ const indentMatch = line.match(/^\s*/);
1202
+ const indent = indentMatch ? indentMatch[0] : "";
1203
+ result.push(`${indent}// ${TABLE_COMMENT}`);
1204
+ added++;
1205
+ }
1206
+ }
1207
+ }
1208
+ if (insideTableBody) {
1209
+ for (const ch of line) {
1210
+ if (ch === "{") braceDepth++;
1211
+ if (ch === "}") braceDepth--;
1212
+ }
1213
+ if (braceDepth <= 0) {
1214
+ insideTableBody = false;
1215
+ currentSyncedFields = null;
1216
+ }
1217
+ if (currentSyncedFields && braceDepth >= 1 && !tableMatch) {
1218
+ const columnName = extractColumnName2(line);
1219
+ if (columnName && currentSyncedFields.has(columnName)) {
1220
+ const prevLine = result[result.length - 1]?.trim() ?? "";
1221
+ if (!prevLine.includes("Synced field")) {
1222
+ const indentMatch = line.match(/^\s*/);
1223
+ const indent = indentMatch ? indentMatch[0] : "";
1224
+ result.push(`${indent}// ${FIELD_COMMENT}`);
1225
+ added++;
1226
+ }
1227
+ }
1228
+ }
1229
+ }
1230
+ result.push(line);
1231
+ }
1232
+ return { text: result.join("\n"), added };
1233
+ }
1234
+ function extractColumnName2(line) {
1235
+ const withNameMatch = line.match(FIELD_WITH_NAME_REGEX);
1236
+ if (withNameMatch) {
1237
+ return withNameMatch[1];
1238
+ }
1239
+ const propMatch = line.match(FIELD_PROP_NAME_REGEX);
1240
+ if (propMatch) {
1241
+ return propMatch[1];
1242
+ }
1243
+ return null;
1244
+ }
1245
+
1170
1246
  // src/commands/db/gen-dbschema/transforms/text/table-aliases.ts
1171
1247
  var TABLE_ALIAS_MARKER = "// table aliases";
1172
1248
  function generateTableAliases(source) {
@@ -1215,13 +1291,16 @@ function postprocessSchema(rawSource, options = {}) {
1215
1291
  source = inlineCustomTypes(source);
1216
1292
  const jsonbCommentsResult = addJsonbTypeComments(source, options.columnComments);
1217
1293
  source = jsonbCommentsResult.text;
1294
+ const syncedCommentsResult = addSyncedTableComments(source, options.syncedTableMap);
1295
+ source = syncedCommentsResult.text;
1218
1296
  source = generateTableAliases(source);
1219
1297
  source = formatSource(source);
1220
1298
  return {
1221
1299
  source,
1222
1300
  astStats,
1223
1301
  patchedDefects: patchResult.fixed,
1224
- addedJsonbComments: jsonbCommentsResult.added
1302
+ addedJsonbComments: jsonbCommentsResult.added,
1303
+ addedSyncedComments: syncedCommentsResult.added
1225
1304
  };
1226
1305
  }
1227
1306
  function logStats(result, prefix = "[postprocess]") {
@@ -1271,6 +1350,9 @@ function logStats(result, prefix = "[postprocess]") {
1271
1350
  if (result.addedJsonbComments > 0) {
1272
1351
  console.info(`${prefix} Added ${result.addedJsonbComments} JSDoc comments for jsonb fields`);
1273
1352
  }
1353
+ if (result.addedSyncedComments > 0) {
1354
+ console.info(`${prefix} Added ${result.addedSyncedComments} comments for synced tables/fields`);
1355
+ }
1274
1356
  }
1275
1357
 
1276
1358
  // src/commands/db/gen-dbschema/index.ts
@@ -1281,7 +1363,10 @@ async function postprocessDrizzleSchema(targetPath, options = {}) {
1281
1363
  return void 0;
1282
1364
  }
1283
1365
  const rawSource = fs3.readFileSync(resolvedPath, "utf8");
1284
- const result = postprocessSchema(rawSource, { columnComments: options.columnComments });
1366
+ const result = postprocessSchema(rawSource, {
1367
+ columnComments: options.columnComments,
1368
+ syncedTableMap: options.syncedTableMap
1369
+ });
1285
1370
  fs3.writeFileSync(resolvedPath, result.source, "utf8");
1286
1371
  logStats(result, "[postprocess-drizzle-schema]");
1287
1372
  return {
@@ -1291,10 +1376,112 @@ async function postprocessDrizzleSchema(targetPath, options = {}) {
1291
1376
  patchedDefects: result.patchedDefects,
1292
1377
  replacedTimestamps: result.astStats.replacedTimestamp,
1293
1378
  replacedDefaultNow: result.astStats.replacedDefaultNow,
1294
- addedJsonbComments: result.addedJsonbComments
1379
+ addedJsonbComments: result.addedJsonbComments,
1380
+ addedSyncedComments: result.addedSyncedComments
1295
1381
  };
1296
1382
  }
1297
1383
 
1384
+ // src/utils/http-client.ts
1385
+ import { HttpClient } from "@lark-apaas/http-client";
1386
+ var clientInstance = null;
1387
+ function getHttpClient() {
1388
+ if (!clientInstance) {
1389
+ clientInstance = new HttpClient({
1390
+ timeout: 3e4,
1391
+ platform: {
1392
+ enabled: true
1393
+ }
1394
+ });
1395
+ const canaryEnv = process.env.FORCE_FRAMEWORK_CLI_CANARY_ENV;
1396
+ if (canaryEnv) {
1397
+ clientInstance.interceptors.request.use((req) => {
1398
+ req.headers["x-tt-env"] = canaryEnv;
1399
+ return req;
1400
+ });
1401
+ }
1402
+ }
1403
+ return clientInstance;
1404
+ }
1405
+
1406
+ // src/commands/db/gen-dbschema/utils/fetch-synced-tables.ts
1407
+ var DEFAULT_TIMEOUT_MS2 = 1e4;
1408
+ async function fetchSyncedTables(appId, workspace) {
1409
+ try {
1410
+ const client = getHttpClient();
1411
+ const timeoutPromise = new Promise((_, reject) => {
1412
+ setTimeout(
1413
+ () => reject(new Error(`Timeout after ${DEFAULT_TIMEOUT_MS2}ms`)),
1414
+ DEFAULT_TIMEOUT_MS2
1415
+ );
1416
+ });
1417
+ const start = Date.now();
1418
+ console.log(
1419
+ `[fetchSyncedTables] \u2192 GET listTableView (dbBranch=main) appId=${appId ? "set" : "unset"} workspace=${workspace ? "set" : "unset"}`
1420
+ );
1421
+ const response = await Promise.race([
1422
+ client.get(
1423
+ `/api/v1/dataloom/inner/app/${appId}/workspaces/${workspace}/listTableView`,
1424
+ { params: { dbBranch: "main" }, headers: { "x-supaas-bizsource": "miaoda" } }
1425
+ ),
1426
+ timeoutPromise
1427
+ ]);
1428
+ console.log(
1429
+ `[fetchSyncedTables] \u2190 listTableView response: ${response.status} ${response.statusText} (${Date.now() - start}ms) with logId=${response.headers.get("x-tt-logid")}`
1430
+ );
1431
+ if (!response.ok) {
1432
+ throw new Error(
1433
+ `listTableView API failed: ${response.status} ${response.statusText}`
1434
+ );
1435
+ }
1436
+ let json;
1437
+ try {
1438
+ json = await response.json();
1439
+ } catch (error) {
1440
+ console.warn(
1441
+ "[fetchSyncedTables] \u26A0 Failed to parse listTableView response JSON, returning empty map:",
1442
+ error instanceof Error ? error.message : String(error)
1443
+ );
1444
+ return /* @__PURE__ */ new Map();
1445
+ }
1446
+ const tableView = json?.data?.data;
1447
+ if (!tableView) {
1448
+ console.warn(
1449
+ "[fetchSyncedTables] \u26A0 listTableView response missing data.data, returning empty map"
1450
+ );
1451
+ return /* @__PURE__ */ new Map();
1452
+ }
1453
+ const syncedMap = extractSyncedTableMap(tableView);
1454
+ const totalCount = (tableView.table?.data?.length ?? 0) + (tableView.view?.data?.length ?? 0) + (tableView.materializedView?.data?.length ?? 0);
1455
+ console.log(
1456
+ `[fetchSyncedTables] \u2713 Extracted synced tables: ${syncedMap.size}/${totalCount} (elapsed ${Date.now() - start}ms)`
1457
+ );
1458
+ return syncedMap;
1459
+ } catch (error) {
1460
+ console.error(
1461
+ "[fetchSyncedTables] \u274C Error fetching synced tables:",
1462
+ error
1463
+ );
1464
+ return /* @__PURE__ */ new Map();
1465
+ }
1466
+ }
1467
+ function extractSyncedTableMap(tableView) {
1468
+ const syncedMap = /* @__PURE__ */ new Map();
1469
+ const allTables = [
1470
+ ...tableView.table?.data ?? [],
1471
+ ...tableView.view?.data ?? [],
1472
+ ...tableView.materializedView?.data ?? []
1473
+ ];
1474
+ for (const table of allTables) {
1475
+ if (table.bitableSyncTask && table.bitableSyncTask.fieldApiNameList?.length > 0) {
1476
+ syncedMap.set(
1477
+ table.tableName,
1478
+ new Set(table.bitableSyncTask.fieldApiNameList)
1479
+ );
1480
+ }
1481
+ }
1482
+ return syncedMap;
1483
+ }
1484
+
1298
1485
  // src/commands/db/gen-nest-resource/generator.ts
1299
1486
  import { pluralize } from "inflection";
1300
1487
 
@@ -1973,7 +2160,9 @@ async function run(options = {}) {
1973
2160
  }
1974
2161
  const databaseUrl = process.env.SUDA_DATABASE_URL;
1975
2162
  if (!databaseUrl) {
1976
- console.error("[gen-db-schema] Error: SUDA_DATABASE_URL environment variable is required");
2163
+ console.error(
2164
+ "[gen-db-schema] Error: SUDA_DATABASE_URL environment variable is required"
2165
+ );
1977
2166
  process.exit(1);
1978
2167
  }
1979
2168
  const outputPath = options.output || process.env.DB_SCHEMA_OUTPUT || "server/database/schema.ts";
@@ -1988,9 +2177,14 @@ async function run(options = {}) {
1988
2177
  path2.resolve(__dirname2, "../../../dist/config/drizzle.config.js")
1989
2178
  ];
1990
2179
  const configPath = configPathCandidates.find((p) => fs4.existsSync(p));
1991
- console.log("[gen-db-schema] Using drizzle config from:", configPath ?? "(not found)");
2180
+ console.log(
2181
+ "[gen-db-schema] Using drizzle config from:",
2182
+ configPath ?? "(not found)"
2183
+ );
1992
2184
  if (!configPath) {
1993
- console.error("[gen-db-schema] Error: drizzle config not found in CLI package");
2185
+ console.error(
2186
+ "[gen-db-schema] Error: drizzle config not found in CLI package"
2187
+ );
1994
2188
  process.exit(1);
1995
2189
  }
1996
2190
  const resolveDrizzleKitBin = () => {
@@ -2006,7 +2200,9 @@ async function run(options = {}) {
2006
2200
  const binField = pkgJson.bin;
2007
2201
  const binRelPath = typeof binField === "string" ? binField : binField?.["drizzle-kit"];
2008
2202
  if (!binRelPath) {
2009
- throw new Error("Unable to resolve drizzle-kit binary from package.json");
2203
+ throw new Error(
2204
+ "Unable to resolve drizzle-kit binary from package.json"
2205
+ );
2010
2206
  }
2011
2207
  return path2.resolve(currentDir, binRelPath);
2012
2208
  }
@@ -2017,15 +2213,66 @@ async function run(options = {}) {
2017
2213
  throw new Error("Unable to locate drizzle-kit package root");
2018
2214
  };
2019
2215
  let columnComments;
2020
- try {
2021
- columnComments = await fetchColumnComments(databaseUrl, { timeoutMs: 1e4 });
2022
- console.log(`[gen-db-schema] \u2713 Fetched ${columnComments.size} column comments`);
2023
- } catch (err) {
2216
+ let syncedTableMap;
2217
+ const appId = process.env.app_id;
2218
+ const workspace = process.env.suda_workspace_id;
2219
+ console.log(
2220
+ `[gen-db-schema] Pre-fetch info: columnComments=enabled, syncedTables=${appId && workspace ? "enabled" : "skipped"} (app_id=${appId ? "set" : "unset"}, suda_workspace_id=${workspace ? "set" : "unset"})`
2221
+ );
2222
+ const columnCommentsTask = (async () => {
2223
+ const start = Date.now();
2224
+ console.log("[gen-db-schema] \u2192 Fetching column comments...");
2225
+ const res = await fetchColumnComments(databaseUrl, { timeoutMs: 1e4 });
2226
+ console.log(
2227
+ `[gen-db-schema] \u2190 Fetched column comments: ${res.size} items (${Date.now() - start}ms)`
2228
+ );
2229
+ return res;
2230
+ })();
2231
+ const syncedTablesTask = appId && workspace ? (async () => {
2232
+ const start = Date.now();
2233
+ console.log(
2234
+ "[gen-db-schema] \u2192 Fetching synced tables from listTableView..."
2235
+ );
2236
+ const res = await fetchSyncedTables(appId, workspace);
2237
+ console.log(
2238
+ `[gen-db-schema] \u2190 Fetched synced tables: ${res.size} tables (${Date.now() - start}ms)`
2239
+ );
2240
+ return res;
2241
+ })() : void 0;
2242
+ const fetchTasks = await Promise.allSettled([
2243
+ columnCommentsTask,
2244
+ ...syncedTablesTask ? [syncedTablesTask] : []
2245
+ ]);
2246
+ if (fetchTasks[0].status === "fulfilled") {
2247
+ columnComments = fetchTasks[0].value;
2248
+ console.log(
2249
+ `[gen-db-schema] \u2713 Column comments ready: ${columnComments.size}`
2250
+ );
2251
+ } else {
2024
2252
  console.warn(
2025
2253
  "[gen-db-schema] \u26A0 Failed to fetch column comments (skipping):",
2026
- err instanceof Error ? err.message : String(err)
2254
+ fetchTasks[0].reason instanceof Error ? fetchTasks[0].reason.message : String(fetchTasks[0].reason)
2027
2255
  );
2028
2256
  }
2257
+ if (appId && workspace) {
2258
+ if (fetchTasks[1]?.status === "fulfilled") {
2259
+ syncedTableMap = fetchTasks[1].value;
2260
+ console.log(
2261
+ `[gen-db-schema] \u2713 Synced tables ready: ${syncedTableMap.size}`
2262
+ );
2263
+ } else if (fetchTasks[1]?.status === "rejected") {
2264
+ console.warn(
2265
+ "[gen-db-schema] \u26A0 Failed to fetch synced tables (skipping):",
2266
+ fetchTasks[1].reason instanceof Error ? fetchTasks[1].reason.message : String(fetchTasks[1].reason)
2267
+ );
2268
+ syncedTableMap = /* @__PURE__ */ new Map();
2269
+ }
2270
+ } else {
2271
+ console.info(
2272
+ "[gen-db-schema] \u2139 Skipping synced table detection (app_id or suda_workspace_id not set)"
2273
+ );
2274
+ syncedTableMap = /* @__PURE__ */ new Map();
2275
+ }
2029
2276
  try {
2030
2277
  const env = {
2031
2278
  ...process.env,
@@ -2036,13 +2283,19 @@ async function run(options = {}) {
2036
2283
  };
2037
2284
  const drizzleKitBin = resolveDrizzleKitBin();
2038
2285
  const spawnArgs = [drizzleKitBin, "introspect", "--config", configPath];
2039
- const result = spawnSync(process.execPath, spawnArgs, { stdio: "inherit", env, cwd: process.cwd() });
2286
+ const result = spawnSync(process.execPath, spawnArgs, {
2287
+ stdio: "inherit",
2288
+ env,
2289
+ cwd: process.cwd()
2290
+ });
2040
2291
  if (result.error) {
2041
2292
  console.error("[gen-db-schema] Execution failed:", result.error);
2042
2293
  throw result.error;
2043
2294
  }
2044
2295
  if ((result.status ?? 0) !== 0) {
2045
- throw new Error(`drizzle-kit introspect failed with status ${result.status}`);
2296
+ throw new Error(
2297
+ `drizzle-kit introspect failed with status ${result.status}`
2298
+ );
2046
2299
  }
2047
2300
  const generatedSchema = path2.join(OUT_DIR, "schema.ts");
2048
2301
  if (!fs4.existsSync(generatedSchema)) {
@@ -2050,10 +2303,14 @@ async function run(options = {}) {
2050
2303
  throw new Error("drizzle-kit introspect failed to generate schema.ts");
2051
2304
  }
2052
2305
  const stats = await postprocessDrizzleSchema(generatedSchema, {
2053
- columnComments
2306
+ columnComments,
2307
+ syncedTableMap
2054
2308
  });
2055
2309
  if (stats?.unmatchedUnknown?.length) {
2056
- console.warn("[gen-db-schema] Unmatched custom types detected:", stats.unmatchedUnknown);
2310
+ console.warn(
2311
+ "[gen-db-schema] Unmatched custom types detected:",
2312
+ stats.unmatchedUnknown
2313
+ );
2057
2314
  }
2058
2315
  console.log("[gen-db-schema] \u2713 Postprocessed schema");
2059
2316
  fs4.mkdirSync(path2.dirname(SCHEMA_FILE), { recursive: true });
@@ -2068,14 +2325,22 @@ async function run(options = {}) {
2068
2325
  schemaFilePath,
2069
2326
  moduleOutputDir: path2.resolve(process.cwd(), "server/modules")
2070
2327
  });
2071
- console.log("[gen-db-schema] \u2713 Generate NestJS Module Boilerplate Successfully");
2328
+ console.log(
2329
+ "[gen-db-schema] \u2713 Generate NestJS Module Boilerplate Successfully"
2330
+ );
2072
2331
  }
2073
2332
  } catch (error) {
2074
- console.warn("[gen-db-schema] Generate NestJS Module Boilerplate failed:", error instanceof Error ? error.message : String(error));
2333
+ console.warn(
2334
+ "[gen-db-schema] Generate NestJS Module Boilerplate failed:",
2335
+ error instanceof Error ? error.message : String(error)
2336
+ );
2075
2337
  }
2076
2338
  console.log("[gen-db-schema] \u2713 Complete");
2077
2339
  } catch (err) {
2078
- console.error("[gen-db-schema] Failed:", err instanceof Error ? err.message : String(err));
2340
+ console.error(
2341
+ "[gen-db-schema] Failed:",
2342
+ err instanceof Error ? err.message : String(err)
2343
+ );
2079
2344
  exitCode = 1;
2080
2345
  } finally {
2081
2346
  if (fs4.existsSync(OUT_DIR)) {
@@ -2419,28 +2684,6 @@ var syncCommand = {
2419
2684
  }
2420
2685
  };
2421
2686
 
2422
- // src/utils/http-client.ts
2423
- import { HttpClient } from "@lark-apaas/http-client";
2424
- var clientInstance = null;
2425
- function getHttpClient() {
2426
- if (!clientInstance) {
2427
- clientInstance = new HttpClient({
2428
- timeout: 3e4,
2429
- platform: {
2430
- enabled: true
2431
- }
2432
- });
2433
- const canaryEnv = process.env.FORCE_FRAMEWORK_CLI_CANARY_ENV;
2434
- if (canaryEnv) {
2435
- clientInstance.interceptors.request.use((req) => {
2436
- req.headers["x-tt-env"] = canaryEnv;
2437
- return req;
2438
- });
2439
- }
2440
- }
2441
- return clientInstance;
2442
- }
2443
-
2444
2687
  // src/utils/telemetry.ts
2445
2688
  async function reportEvents(events) {
2446
2689
  if (events.length === 0) {
@@ -2595,7 +2838,7 @@ function autoCommitUpgradeChanges(version, cwd, filesToStage, commitMessage) {
2595
2838
  - Cleanup package.json config
2596
2839
  - Upgrade @lark-apaas dependencies (if any)
2597
2840
 
2598
- Auto-committed by fullstack-cli v${version}`;
2841
+ Auto-committed by fullstack-cli`;
2599
2842
  gitCommit(message, cwd);
2600
2843
  console.log(`[fullstack-cli] \u2713 Auto-committed ${changedFiles.length} changed file(s)`);
2601
2844
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/fullstack-cli",
3
- "version": "1.1.25",
3
+ "version": "1.1.26",
4
4
  "description": "CLI tool for fullstack template management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",