@adep/cli 0.1.8 → 0.1.10

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/vite.js CHANGED
@@ -4,8 +4,8 @@ import { adepPlugin } from "@adep/vite-plugin";
4
4
  // packages/cli/src/dev.ts
5
5
  import { createServer } from "node:http";
6
6
  import { watch } from "node:fs";
7
- import { mkdir as mkdir3, readdir as readdir2, readFile as readFile5, writeFile as writeFile3 } from "node:fs/promises";
8
- import { basename, join as join8, resolve as resolve2 } from "node:path";
7
+ import { mkdir as mkdir3, readFile as readFile6, writeFile as writeFile3 } from "node:fs/promises";
8
+ import { basename, join as join9, resolve as resolve2 } from "node:path";
9
9
 
10
10
  // packages/runtime/src/shared/capability-keys.ts
11
11
  var RPC_CAPABILITY_KEY = "__adepRpc";
@@ -1106,7 +1106,7 @@ function createDbCapability(driver, options = {}) {
1106
1106
  };
1107
1107
  }
1108
1108
 
1109
- // packages/cli/src/sim/sql-engine.ts
1109
+ // packages/runtime/src/sim/sql-engine.ts
1110
1110
  var SimDbError = class extends Error {
1111
1111
  constructor(code, message) {
1112
1112
  super(message);
@@ -1138,6 +1138,7 @@ function stripQuotes(raw) {
1138
1138
  const t = raw.trim();
1139
1139
  if (/^'.*'$/.test(t)) return t.slice(1, -1);
1140
1140
  if (/^".*"$/.test(t)) return t.slice(1, -1);
1141
+ if (/^NULL$/i.test(t)) return null;
1141
1142
  return t;
1142
1143
  }
1143
1144
  function parseCreateTable(ddl) {
@@ -1146,7 +1147,7 @@ function parseCreateTable(ddl) {
1146
1147
  );
1147
1148
  if (m === null) return null;
1148
1149
  const name = ident(m[1]);
1149
- const body = m[2];
1150
+ const body = m[3];
1150
1151
  const columns = [];
1151
1152
  for (const part of body.split(",")) {
1152
1153
  const tokens = part.trim().split(/\s+/);
@@ -1158,50 +1159,109 @@ function parseCreateTable(ddl) {
1158
1159
  }
1159
1160
  return { name, columns };
1160
1161
  }
1161
- function parseWhereClauses(whereRaw, cursor) {
1162
- const clauses = [];
1163
- for (const part of whereRaw.split(/\s+AND\s+/i)) {
1164
- const t = part.trim();
1165
- if (t.length === 0) continue;
1166
- if (/\bIS\s+NULL\b/i.test(t)) {
1167
- const col2 = ident(t.split(/\s+IS\s+NULL\b/i)[0] ?? "");
1168
- clauses.push({ column: col2, operator: "IS NULL", value: null, list: false });
1169
- continue;
1170
- }
1171
- if (/\bIS\s+NOT\s+NULL\b/i.test(t)) {
1172
- const col2 = ident(t.split(/\s+IS\s+NOT\s+NULL\b/i)[0] ?? "");
1173
- clauses.push({ column: col2, operator: "IS NOT NULL", value: null, list: false });
1174
- continue;
1175
- }
1176
- const opMatch = /^\s*((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_]*))\s*(=|!=|>=|<=|>|<|like|in|not\s+in)\s*(.+)$/i.exec(
1177
- t
1178
- );
1179
- if (opMatch === null) continue;
1180
- const col = ident(opMatch[1] ?? "");
1181
- const op = (opMatch[3] ?? opMatch[2] ?? "").toLowerCase();
1182
- const rhs = (opMatch[4] ?? "").trim();
1183
- if (op === "in" || op === "not in") {
1184
- const inner = rhs.replace(/^\(|\)$/g, "");
1185
- const items = splitListItems(inner);
1186
- const list = items.map((item) => {
1187
- if (item.trim() === "?") return cursor.take();
1188
- return stripQuotes(item);
1189
- });
1190
- clauses.push({ column: col, operator: op, value: list, list: true });
1162
+ function splitTopLevel(raw, token) {
1163
+ const parts = [];
1164
+ let depth = 0;
1165
+ let inQuote = false;
1166
+ let buffer = "";
1167
+ const n = raw.length;
1168
+ const isTokenAt = (i) => {
1169
+ const word = raw.slice(i, i + token.length);
1170
+ if (word.toUpperCase() !== token) return false;
1171
+ const before = i === 0 ? "" : raw[i - 1] ?? "";
1172
+ const after = raw[i + token.length] ?? "";
1173
+ return !/[A-Za-z0-9_.]/.test(before) && !/[A-Za-z0-9_.]/.test(after);
1174
+ };
1175
+ for (let i = 0; i < n; i += 1) {
1176
+ const ch = raw[i] ?? "";
1177
+ if (ch === "'") {
1178
+ if (inQuote && raw[i + 1] === "'") {
1179
+ buffer += ch + (raw[i + 1] ?? "");
1180
+ i += 1;
1181
+ continue;
1182
+ }
1183
+ inQuote = !inQuote;
1184
+ buffer += ch;
1191
1185
  continue;
1192
1186
  }
1193
- let value;
1194
- if (rhs === "?") {
1195
- value = cursor.take();
1187
+ if (!inQuote && ch === "(") depth += 1;
1188
+ else if (!inQuote && ch === ")") depth -= 1;
1189
+ if (!inQuote && depth === 0 && isTokenAt(i)) {
1190
+ parts.push(buffer);
1191
+ buffer = "";
1192
+ i += token.length - 1;
1196
1193
  } else {
1197
- value = stripQuotes(rhs);
1194
+ buffer += ch;
1198
1195
  }
1199
- clauses.push({ column: col, operator: op, value, list: false });
1200
1196
  }
1201
- return clauses;
1197
+ parts.push(buffer);
1198
+ return parts;
1199
+ }
1200
+ function unqualifyColumn(raw) {
1201
+ const t = raw.trim();
1202
+ const dot = t.lastIndexOf(".");
1203
+ if (dot > 0 && /^[A-Za-z_][A-Za-z0-9_]*\./.test(t)) return t.slice(dot + 1);
1204
+ return t;
1205
+ }
1206
+ function parseLeftJoins(tail) {
1207
+ const joins = [];
1208
+ const re = /LEFT\s+JOIN\s+((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_]*))\s+([A-Za-z_][A-Za-z0-9_]*)\s+ON\s+([A-Za-z_][A-Za-z0-9_.]*)\s*=\s*([A-Za-z_][A-Za-z0-9_.]*)/gi;
1209
+ let m;
1210
+ while ((m = re.exec(tail)) !== null) {
1211
+ joins.push({
1212
+ table: ident(m[1] ?? ""),
1213
+ alias: m[3] ?? "",
1214
+ leftCol: unqualifyColumn(m[4] ?? ""),
1215
+ rightCol: unqualifyColumn(m[5] ?? "")
1216
+ });
1217
+ }
1218
+ return joins;
1219
+ }
1220
+ function parseWhereClauses(whereRaw, cursor) {
1221
+ const parseAndGroup = (andRaw) => {
1222
+ const clauses = [];
1223
+ for (const part of splitTopLevel(andRaw, "AND")) {
1224
+ const t = part.trim();
1225
+ if (t.length === 0) continue;
1226
+ if (/\bIS\s+NULL\b/i.test(t)) {
1227
+ clauses.push({ column: unqualifyColumn(ident(t.split(/\s+IS\s+NULL\b/i)[0] ?? "")), operator: "IS NULL", value: null, list: false });
1228
+ continue;
1229
+ }
1230
+ if (/\bIS\s+NOT\s+NULL\b/i.test(t)) {
1231
+ clauses.push({ column: unqualifyColumn(ident(t.split(/\s+IS\s+NOT\s+NULL\b/i)[0] ?? "")), operator: "IS NOT NULL", value: null, list: false });
1232
+ continue;
1233
+ }
1234
+ const opMatch = /^\s*((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_.]*))\s*(=|!=|>=|<=|>|<|like|in|not\s+in)\s*(.+)$/i.exec(
1235
+ t
1236
+ );
1237
+ if (opMatch === null) continue;
1238
+ const col = unqualifyColumn(ident(opMatch[1] ?? ""));
1239
+ const op = (opMatch[3] ?? opMatch[2] ?? "").toLowerCase();
1240
+ const rhs = (opMatch[4] ?? "").trim();
1241
+ if (op === "in" || op === "not in") {
1242
+ const inner = rhs.replace(/^\(|\)$/g, "");
1243
+ const items = splitListItems(inner);
1244
+ const list = items.map((item) => {
1245
+ if (item.trim() === "?") return cursor.take();
1246
+ return stripQuotes(item);
1247
+ });
1248
+ clauses.push({ column: col, operator: op, value: list, list: true });
1249
+ continue;
1250
+ }
1251
+ let value;
1252
+ if (rhs === "?") {
1253
+ value = cursor.take();
1254
+ } else {
1255
+ value = stripQuotes(rhs);
1256
+ }
1257
+ clauses.push({ column: col, operator: op, value, list: false });
1258
+ }
1259
+ return clauses;
1260
+ };
1261
+ return splitTopLevel(whereRaw, "OR").map((group) => group.trim()).filter((group) => group.length > 0).map(parseAndGroup);
1202
1262
  }
1203
- function matchWhere(clauses, row) {
1204
- return clauses.every((c) => matchValue(row[c.column], c));
1263
+ function matchWhere(groups, row) {
1264
+ return groups.some((clauses) => clauses.every((c) => matchValue(row[c.column], c)));
1205
1265
  }
1206
1266
  function splitListItems(inner) {
1207
1267
  const items = [];
@@ -1221,6 +1281,168 @@ function splitListItems(inner) {
1221
1281
  items.push(buffer);
1222
1282
  return items;
1223
1283
  }
1284
+ var ExcludedRef = class {
1285
+ constructor(column) {
1286
+ this.column = column;
1287
+ }
1288
+ };
1289
+ var ColumnRef = class {
1290
+ constructor(column) {
1291
+ this.column = column;
1292
+ }
1293
+ };
1294
+ var ExprRef = class {
1295
+ constructor(source) {
1296
+ this.source = source;
1297
+ }
1298
+ };
1299
+ function resolveSetValue(value, existing, row) {
1300
+ if (value instanceof ExcludedRef) return row[value.column];
1301
+ if (value instanceof ColumnRef) return existing[value.column];
1302
+ if (value instanceof ExprRef) return evalArithmetic(value.source, existing, row);
1303
+ return value;
1304
+ }
1305
+ function evalArithmetic(src, existing, row) {
1306
+ const num = (v) => {
1307
+ const n2 = Number(v);
1308
+ return Number.isFinite(n2) ? n2 : 0;
1309
+ };
1310
+ let i = 0;
1311
+ const n = src.length;
1312
+ const skip = () => {
1313
+ while (i < n && /\s/.test(src[i] ?? "")) i += 1;
1314
+ };
1315
+ const peek = () => {
1316
+ skip();
1317
+ return i < n ? src[i] ?? "" : null;
1318
+ };
1319
+ const parseIdent = () => {
1320
+ skip();
1321
+ const m = /^([A-Za-z_][A-Za-z0-9_]*)/.exec(src.slice(i));
1322
+ if (m === null) return null;
1323
+ i += (m[1] ?? "").length;
1324
+ skip();
1325
+ let col = ident(m[1] ?? "");
1326
+ let excluded = false;
1327
+ if (src[i] === ".") {
1328
+ const m2 = /^\.([A-Za-z_][A-Za-z0-9_]*)/.exec(src.slice(i));
1329
+ if (m2 !== null) {
1330
+ i += (m2[0] ?? "").length;
1331
+ if (col.toLowerCase() === "excluded") excluded = true;
1332
+ col = ident(m2[1] ?? "");
1333
+ }
1334
+ }
1335
+ return { col, excluded };
1336
+ };
1337
+ const parseFactor = () => {
1338
+ const ch = peek();
1339
+ if (ch === "(") {
1340
+ i += 1;
1341
+ const v = parseExpr();
1342
+ skip();
1343
+ if (src[i] === ")") i += 1;
1344
+ return v;
1345
+ }
1346
+ const numMatch = /^(\d+(?:\.\d+)?)/.exec(src.slice(i));
1347
+ if (numMatch !== null) {
1348
+ i += (numMatch[1] ?? "").length;
1349
+ return Number(numMatch[1]);
1350
+ }
1351
+ const id = parseIdent();
1352
+ if (id !== null) {
1353
+ const v = id.excluded ? row[id.col] : existing[id.col];
1354
+ return num(v);
1355
+ }
1356
+ return 0;
1357
+ };
1358
+ const parseTerm = () => {
1359
+ let v = parseFactor();
1360
+ for (; ; ) {
1361
+ const ch = peek();
1362
+ if (ch !== "*" && ch !== "/") return v;
1363
+ i += 1;
1364
+ const rhs = parseFactor();
1365
+ v = ch === "*" ? v * rhs : rhs === 0 ? v : v / rhs;
1366
+ }
1367
+ };
1368
+ const parseExpr = () => {
1369
+ let v = parseTerm();
1370
+ for (; ; ) {
1371
+ const ch = peek();
1372
+ if (ch !== "+" && ch !== "-") return v;
1373
+ i += 1;
1374
+ const rhs = parseTerm();
1375
+ v = ch === "+" ? v + rhs : v - rhs;
1376
+ }
1377
+ };
1378
+ return parseExpr();
1379
+ }
1380
+ function splitInsertTuples(body) {
1381
+ const tuples = [];
1382
+ let i = 0;
1383
+ const n = body.length;
1384
+ while (i < n) {
1385
+ while (i < n && (body[i] === " " || body[i] === " " || body[i] === "\n" || body[i] === "\r" || body[i] === ",")) {
1386
+ i += 1;
1387
+ }
1388
+ if (i >= n) break;
1389
+ if (body[i] !== "(") {
1390
+ return { tuples, conflict: body.slice(i).trim() };
1391
+ }
1392
+ let depth = 0;
1393
+ let j = i;
1394
+ for (; j < n; j += 1) {
1395
+ if (body[j] === "(") depth += 1;
1396
+ else if (body[j] === ")") {
1397
+ depth -= 1;
1398
+ if (depth === 0) break;
1399
+ }
1400
+ }
1401
+ if (j >= n) return { tuples, conflict: null };
1402
+ tuples.push(body.slice(i + 1, j));
1403
+ i = j + 1;
1404
+ }
1405
+ return { tuples, conflict: null };
1406
+ }
1407
+ function parseConflictClause(clause, cursor, fallbackCols) {
1408
+ const m = /^ON\s+CONFLICT(?:\s*\(([^)]*)\))?\s+DO\s+(UPDATE\s+SET\s+([\s\S]+)|NOTHING\s*)$/i.exec(
1409
+ clause.trim()
1410
+ );
1411
+ if (m === null) {
1412
+ throw new SimDbError("DB_UNSAFE_OP", `\u65E0\u6CD5\u89E3\u6790 ON CONFLICT \u5B50\u53E5\uFF1A${clause.trim().slice(0, 60)}\u2026`);
1413
+ }
1414
+ const colsRaw = m[1];
1415
+ const cols = colsRaw === void 0 ? fallbackCols : colsRaw.split(",").map(ident);
1416
+ if (cols.length === 0) throw new SimDbError("DB_UNSAFE_OP", "ON CONFLICT \u51B2\u7A81\u5217\u4E3A\u7A7A");
1417
+ const action = m[2] ?? "";
1418
+ if (/^NOTHING/i.test(action)) return { cols, sets: [] };
1419
+ const setRaw = m[3] ?? "";
1420
+ const sets = splitListItems(setRaw).map((part) => {
1421
+ const eq = /^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([\s\S]+)$/i.exec(part.trim());
1422
+ if (eq === null) {
1423
+ throw new SimDbError("DB_UNSAFE_OP", `\u65E0\u6CD5\u89E3\u6790 SET \u8D4B\u503C\uFF1A${part.trim().slice(0, 60)}\u2026`);
1424
+ }
1425
+ const col = ident(eq[1] ?? "");
1426
+ const rhs = (eq[2] ?? "").trim();
1427
+ let value;
1428
+ if (rhs === "?") {
1429
+ value = cursor.take();
1430
+ } else {
1431
+ const ex = /^excluded\.([A-Za-z_][A-Za-z0-9_]*)$/i.exec(rhs);
1432
+ if (ex !== null) {
1433
+ value = new ExcludedRef(ident(ex[1] ?? ""));
1434
+ } else if (/^([A-Za-z_][A-Za-z0-9_]*)$/.test(rhs) && !/^NULL$/i.test(rhs)) {
1435
+ value = new ColumnRef(ident(rhs));
1436
+ } else if (/^'.*'$/.test(rhs) || /^".*"$/.test(rhs) || /^NULL$/i.test(rhs)) {
1437
+ value = stripQuotes(rhs);
1438
+ } else {
1439
+ value = new ExprRef(rhs);
1440
+ }
1441
+ }
1442
+ return { col, value };
1443
+ });
1444
+ return { cols, sets };
1445
+ }
1224
1446
  function matchValue(value, clause) {
1225
1447
  const op = clause.operator;
1226
1448
  if (op === "IS NULL") return value === null || value === void 0;
@@ -1236,9 +1458,9 @@ function matchValue(value, clause) {
1236
1458
  const right = clause.value;
1237
1459
  switch (op) {
1238
1460
  case "=":
1239
- return left === right;
1461
+ return left !== null && right !== null && left === right;
1240
1462
  case "!=":
1241
- return left !== right;
1463
+ return left !== null && right !== null && left !== right;
1242
1464
  case ">":
1243
1465
  return left !== null && right !== null && left > right;
1244
1466
  case ">=":
@@ -1374,16 +1596,36 @@ var SimSqlEngine = class {
1374
1596
  const valueBody = match[4].trim();
1375
1597
  const cursor = new ParamCursor(params);
1376
1598
  const table = this.ensureTable(tableName);
1599
+ const { tuples, conflict } = splitInsertTuples(valueBody);
1600
+ if (tuples.length === 0) throw new SimDbError("DB_UNSAFE_OP", "INSERT \u7F3A\u5C11 VALUES \u5143\u7EC4");
1601
+ const conflictSpec = conflict === null ? null : parseConflictClause(
1602
+ conflict,
1603
+ cursor,
1604
+ table.columns.filter((c) => c.primaryKey).map((c) => c.name)
1605
+ );
1377
1606
  let inserted = 0;
1378
- for (const group of splitListItems(valueBody)) {
1379
- const inner = group.trim().replace(/^\(|\)$/g, "");
1380
- const values = splitListItems(inner).map(
1607
+ for (const tuple of tuples) {
1608
+ const values = splitListItems(tuple).map(
1381
1609
  (item) => item.trim() === "?" ? cursor.take() : stripQuotes(item)
1382
1610
  );
1383
1611
  const row = {};
1384
1612
  cols.forEach((col, i) => {
1385
1613
  row[col] = values[i] ?? null;
1386
1614
  });
1615
+ if (conflictSpec !== null) {
1616
+ const existing = table.rows.find(
1617
+ (r) => conflictSpec.cols.every((c) => r[c] === row[c])
1618
+ );
1619
+ if (existing !== void 0) {
1620
+ const oldRow = { ...existing };
1621
+ for (const set of conflictSpec.sets) {
1622
+ existing[set.col] = resolveSetValue(set.value, oldRow, row);
1623
+ }
1624
+ inserted += 1;
1625
+ void this.persist();
1626
+ continue;
1627
+ }
1628
+ }
1387
1629
  this.applyAutoincrement(table, row);
1388
1630
  table.rows.push(row);
1389
1631
  inserted += 1;
@@ -1452,6 +1694,20 @@ var SimSqlEngine = class {
1452
1694
  return project(this.catalog(), selectRaw);
1453
1695
  }
1454
1696
  const table = this.ensureTable(tableName);
1697
+ let rows = table.rows;
1698
+ const joins = parseLeftJoins(tail);
1699
+ if (joins.length > 0) {
1700
+ rows = table.rows.map((row) => {
1701
+ const merged = { ...row };
1702
+ for (const j of joins) {
1703
+ const joined = this.tables[j.table]?.rows.find((jr) => jr[j.leftCol] === merged[j.rightCol]);
1704
+ if (joined !== void 0) {
1705
+ for (const [k, v] of Object.entries(joined)) merged[`${j.alias}.${k}`] = v;
1706
+ }
1707
+ }
1708
+ return merged;
1709
+ });
1710
+ }
1455
1711
  const whereMatch = /\bWHERE\b/i.exec(tail);
1456
1712
  const orderMatch = /\bORDER\s+BY\b/i.exec(tail);
1457
1713
  const limitMatch = /\bLIMIT\b/i.exec(tail);
@@ -1460,7 +1716,6 @@ var SimSqlEngine = class {
1460
1716
  whereMatch.index + whereMatch[0].length,
1461
1717
  indexAfter(whereMatch.index, [orderMatch, limitMatch, offsetMatch], tail)
1462
1718
  );
1463
- let rows = table.rows;
1464
1719
  if (whereRaw.trim().length > 0) {
1465
1720
  const clauses = parseWhereClauses(whereRaw, cursor);
1466
1721
  rows = rows.filter((row) => matchWhere(clauses, row));
@@ -1470,9 +1725,9 @@ var SimSqlEngine = class {
1470
1725
  orderMatch.index + orderMatch[0].length,
1471
1726
  indexAfter(orderMatch.index, [limitMatch, offsetMatch], tail)
1472
1727
  );
1473
- const oc = /^\s*((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_]*))\s+(asc|desc)/i.exec(orderRaw);
1728
+ const oc = /^\s*((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_.]*))\s+(asc|desc)/i.exec(orderRaw);
1474
1729
  if (oc !== null) {
1475
- const col = ident(oc[1]);
1730
+ const col = unqualifyColumn(ident(oc[1]));
1476
1731
  const dir = oc[3].toLowerCase();
1477
1732
  rows = [...rows].toSorted((a, b) => {
1478
1733
  const av = a[col];
@@ -1517,6 +1772,15 @@ function indexAfter(start, matches, tail) {
1517
1772
  const end = candidates.length === 0 ? -1 : Math.min(...candidates);
1518
1773
  return end === -1 ? tail.length : end;
1519
1774
  }
1775
+ function parseSelectItem(raw) {
1776
+ const t = raw.trim();
1777
+ const as = /\s+AS\s+([A-Za-z_][A-Za-z0-9_]*)$/i.exec(t);
1778
+ let name = as === null ? t : t.slice(0, as.index).trim();
1779
+ if (unqualifyColumn(name) === "*") return { name: "", qualified: "", alias: "", star: true };
1780
+ const qualified = /^[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_]*$/.test(name) ? ident(name) : "";
1781
+ const plain = unqualifyColumn(ident(name));
1782
+ return { name: plain, qualified, alias: as === null ? plain : ident(as[1] ?? ""), star: false };
1783
+ }
1520
1784
  function project(rows, selectRaw) {
1521
1785
  if (/^count\s*\(\s*\*/i.test(selectRaw) || /^count\s*\(\s*1\)/i.test(selectRaw)) {
1522
1786
  const alias = selectRaw.match(/\bAS\s+([A-Za-z_][A-Za-z0-9_]*)/i)?.[1] ?? "n";
@@ -1527,10 +1791,21 @@ function project(rows, selectRaw) {
1527
1791
  return [{ [constMatch[2]]: Number(constMatch[1]) }];
1528
1792
  }
1529
1793
  if (selectRaw.trim() === "*") return rows;
1530
- const cols = selectRaw.split(",").map((s) => ident(s.trim()));
1794
+ const items = selectRaw.split(",").map(parseSelectItem);
1795
+ const valueOf = (row, item) => item.qualified !== "" && row[item.qualified] !== void 0 ? row[item.qualified] : row[item.name];
1796
+ if (items.some((item) => item.star)) {
1797
+ return rows.map((row) => {
1798
+ const out = { ...row };
1799
+ for (const item of items) {
1800
+ if (item.star) continue;
1801
+ out[item.alias] = valueOf(row, item);
1802
+ }
1803
+ return out;
1804
+ });
1805
+ }
1531
1806
  return rows.map((row) => {
1532
1807
  const out = {};
1533
- for (const col of cols) out[col] = row[col];
1808
+ for (const item of items) out[item.alias] = valueOf(row, item);
1534
1809
  return out;
1535
1810
  });
1536
1811
  }
@@ -1698,6 +1973,19 @@ function assertSafeStoragePath(path) {
1698
1973
  }
1699
1974
  return path;
1700
1975
  }
1976
+ function assertStoragePrefix(prefix) {
1977
+ if (prefix !== void 0 && prefix !== "") {
1978
+ const segments = prefix.split("/");
1979
+ if (prefix.startsWith("/") || /[\\]/.test(prefix) || segments.some((segment) => segment === "..")) {
1980
+ throw new StorageError(
1981
+ 400,
1982
+ STORAGE_CODES.invalidPath,
1983
+ `\u975E\u6CD5\u524D\u7F00 "${prefix}"\uFF08\u7981\u6B62 / \u5F00\u5934\u3001\u53CD\u659C\u6760\u6216 .. \u7A7F\u8D8A\uFF09`
1984
+ );
1985
+ }
1986
+ }
1987
+ return prefix ?? "";
1988
+ }
1701
1989
 
1702
1990
  // packages/runtime/src/storage/hmac-sha256.ts
1703
1991
  var K = new Uint32Array([
@@ -2081,16 +2369,7 @@ function createLocalStorageDriver(options) {
2081
2369
  await unlink(metaPath(projectId, path)).catch(() => void 0);
2082
2370
  },
2083
2371
  async list(projectId, prefix) {
2084
- if (prefix !== void 0 && prefix !== "") {
2085
- const segments = prefix.split("/");
2086
- if (prefix.startsWith("/") || /[\\]/.test(prefix) || segments.some((segment) => segment === "..")) {
2087
- throw new StorageError(
2088
- 400,
2089
- STORAGE_CODES.invalidPath,
2090
- `\u975E\u6CD5\u524D\u7F00 "${prefix}"\uFF08\u7981\u6B62 / \u5F00\u5934\u3001\u53CD\u659C\u6760\u6216 .. \u7A7F\u8D8A\uFF09`
2091
- );
2092
- }
2093
- }
2372
+ assertStoragePrefix(prefix);
2094
2373
  const root = bucketDir(projectId);
2095
2374
  const metas = [];
2096
2375
  const walk = async (dir, relative) => {
@@ -2156,7 +2435,7 @@ function createSimStorageCapability(options) {
2156
2435
  return { bundle, driver };
2157
2436
  }
2158
2437
 
2159
- // packages/cli/src/sim/realtime.ts
2438
+ // packages/runtime/src/sim/realtime.ts
2160
2439
  var SimRealtime = class {
2161
2440
  seq = 0;
2162
2441
  subscribers = /* @__PURE__ */ new Map();
@@ -2278,10 +2557,7 @@ function createSimRealtimeCapability() {
2278
2557
  };
2279
2558
  }
2280
2559
 
2281
- // packages/cli/src/sim/runtime.ts
2282
- function simDir(cwd) {
2283
- return join4(cwd, ".adep", "sim");
2284
- }
2560
+ // packages/runtime/src/sim/merge.ts
2285
2561
  function mergeBundles(bundles) {
2286
2562
  const capabilities = [];
2287
2563
  const byName = /* @__PURE__ */ new Map();
@@ -2301,6 +2577,11 @@ function mergeBundles(bundles) {
2301
2577
  }
2302
2578
  return { capabilities, rpcHandlers };
2303
2579
  }
2580
+
2581
+ // packages/cli/src/sim/runtime.ts
2582
+ function simDir(cwd) {
2583
+ return join4(cwd, ".adep", "sim");
2584
+ }
2304
2585
  function jsonStorage(cwd) {
2305
2586
  const file = join4(simDir(cwd), "db.json");
2306
2587
  return {
@@ -2416,12 +2697,21 @@ function createSimInvokeHandler(options) {
2416
2697
  };
2417
2698
  }
2418
2699
 
2419
- // packages/cli/src/builtin-deps.ts
2420
- var LOCAL_STATE_OBJECTS_SPECIFIER = "@adep/runtime/state-objects";
2421
- var LOCAL_BUILTIN_DEPS = [
2422
- "@adep/cf-compat",
2423
- LOCAL_STATE_OBJECTS_SPECIFIER
2700
+ // shared/builtin-deps.ts
2701
+ var CF_COMPAT_SPECIFIER = "@adep/cf-compat";
2702
+ var STATE_OBJECTS_SPECIFIER = "@adep/runtime/state-objects";
2703
+ var BUILTIN_DEPS = [
2704
+ CF_COMPAT_SPECIFIER,
2705
+ STATE_OBJECTS_SPECIFIER,
2706
+ "axios",
2707
+ "lodash",
2708
+ "dayjs",
2709
+ "zod"
2424
2710
  ];
2711
+ var BUILTIN_DEPS_CSV = BUILTIN_DEPS.join(",");
2712
+
2713
+ // packages/cli/src/builtin-deps.ts
2714
+ var LOCAL_BUILTIN_DEPS = [CF_COMPAT_SPECIFIER, STATE_OBJECTS_SPECIFIER];
2425
2715
 
2426
2716
  // packages/cli/src/sim/boundary.ts
2427
2717
  var SIM_BOUNDARIES = [
@@ -3057,9 +3347,9 @@ function loadFunctionModule(files, entry, sandboxGlobals, resolveBare) {
3057
3347
 
3058
3348
  // packages/runtime/src/state-objects/loader.ts
3059
3349
  var STATE_OBJECTS_ENTRY = "state-objects.ts";
3060
- var STATE_OBJECTS_SPECIFIER = "@adep/runtime/state-objects";
3350
+ var STATE_OBJECTS_SPECIFIER2 = "@adep/runtime/state-objects";
3061
3351
  function resolveStateObjectSpecifier(request) {
3062
- if (request === STATE_OBJECTS_SPECIFIER) return { StateObject };
3352
+ if (request === STATE_OBJECTS_SPECIFIER2) return { StateObject };
3063
3353
  return void 0;
3064
3354
  }
3065
3355
  function defaultStateSandboxGlobals() {
@@ -3381,6 +3671,19 @@ function createSandboxFetch(options) {
3381
3671
  };
3382
3672
  }
3383
3673
 
3674
+ // packages/runtime/src/functions/runtime/rpc-wire.ts
3675
+ function wireRpcResponses(port, pending) {
3676
+ port.on?.((message) => {
3677
+ const reply = message;
3678
+ if (reply?.type !== "rpc-response" || typeof reply.id !== "number") return;
3679
+ const entry = pending.get(reply.id);
3680
+ if (entry === void 0) return;
3681
+ pending.delete(reply.id);
3682
+ if (reply.ok === true) entry.resolve(reply.result);
3683
+ else entry.reject(new Error(reply.error ?? "RPC \u80FD\u529B\u8C03\u7528\u5931\u8D25"));
3684
+ });
3685
+ }
3686
+
3384
3687
  // packages/runtime/src/functions/runtime/chain.ts
3385
3688
  function isChainCapability(value) {
3386
3689
  if (typeof value !== "object" || value === null) return false;
@@ -3452,6 +3755,11 @@ function createChainClient(capability, port, pending, nextId, spec) {
3452
3755
  return makeRoot(void 0);
3453
3756
  }
3454
3757
 
3758
+ // packages/runtime/src/shared/regexp.ts
3759
+ function escapeRegExp(text) {
3760
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3761
+ }
3762
+
3455
3763
  // packages/runtime/src/functions/runtime/worker-entry.ts
3456
3764
  function isRpcCapability(value) {
3457
3765
  return typeof value === "object" && value !== null && value[RPC_CAPABILITY_KEY] === true;
@@ -3481,9 +3789,6 @@ function errorMessageOf2(error) {
3481
3789
  }
3482
3790
  return String(error);
3483
3791
  }
3484
- function escapeRegExp(text) {
3485
- return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3486
- }
3487
3792
  function firstUserFrame(stack, files) {
3488
3793
  for (const key of Object.keys(files)) {
3489
3794
  const match = new RegExp(
@@ -3542,15 +3847,7 @@ function runWorker(input, port) {
3542
3847
  const container = createCloudContainer();
3543
3848
  const pending = /* @__PURE__ */ new Map();
3544
3849
  let rpcId = 0;
3545
- port.on?.((message) => {
3546
- const reply = message;
3547
- if (reply?.type !== "rpc-response" || typeof reply.id !== "number") return;
3548
- const entry = pending.get(reply.id);
3549
- if (entry === void 0) return;
3550
- pending.delete(reply.id);
3551
- if (reply.ok === true) entry.resolve(reply.result);
3552
- else entry.reject(new Error(reply.error ?? "RPC \u80FD\u529B\u8C03\u7528\u5931\u8D25"));
3553
- });
3850
+ wireRpcResponses(port, pending);
3554
3851
  for (const capability of input.capabilities ?? []) {
3555
3852
  container.register(
3556
3853
  capability.name,
@@ -3656,32 +3953,39 @@ if (workerPort !== null && workerData !== void 0) {
3656
3953
  runWorker(workerData, port);
3657
3954
  }
3658
3955
 
3659
- // packages/cli/src/dev.ts
3660
- async function loadConfig(cwd) {
3661
- const name = basename(resolve2(cwd));
3662
- const config = await loadAdepConfigModule(cwd);
3663
- return {
3664
- name: config?.name ?? name,
3665
- functionsDir: config?.functionsDir ?? "functions",
3666
- functionsPrefix: config?.functions_prefix ?? "",
3667
- ...config?.environment === void 0 ? {} : { environment: config.environment }
3956
+ // packages/cli/src/dev-shared.ts
3957
+ import { readdir as readdir2, readFile as readFile5 } from "node:fs/promises";
3958
+ import { join as join8 } from "node:path";
3959
+ var BEARER_TOKEN_RE = /^Bearer\s+(.+)$/i;
3960
+ function bearerUserResolverFromEnv() {
3961
+ const raw = process.env.ADEP_AUTH_ISSUER;
3962
+ if (raw === void 0 || raw.trim() === "") return void 0;
3963
+ return createBearerUserResolver(raw.trim());
3964
+ }
3965
+ function createBearerUserResolver(issuer) {
3966
+ const userinfoUrl = `${issuer.replace(/\/+$/, "")}/oauth/userinfo`;
3967
+ return async (headers) => {
3968
+ const match = BEARER_TOKEN_RE.exec((headers["authorization"] ?? "").trim());
3969
+ const token = match?.[1]?.trim();
3970
+ if (token === void 0 || token === "") return null;
3971
+ try {
3972
+ const res = await fetch(userinfoUrl, { headers: { authorization: `Bearer ${token}` } });
3973
+ if (!res.ok) return null;
3974
+ const claims = await res.json();
3975
+ if (typeof claims.sub !== "string" || claims.sub.length === 0) return null;
3976
+ return {
3977
+ id: claims.sub,
3978
+ ...typeof claims.name === "string" ? { name: claims.name } : {},
3979
+ ...typeof claims.email === "string" ? { email: claims.email } : {}
3980
+ };
3981
+ } catch {
3982
+ return null;
3983
+ }
3668
3984
  };
3669
3985
  }
3670
- function parseEnvFile(content) {
3671
- const env = {};
3672
- for (const raw of content.split(/\r?\n/)) {
3673
- const line = raw.trim();
3674
- if (line.length === 0 || line.startsWith("#")) continue;
3675
- const eq = line.indexOf("=");
3676
- if (eq === -1) continue;
3677
- const key = line.slice(0, eq).trim();
3678
- if (key.length === 0) continue;
3679
- env[key] = line.slice(eq + 1).trim();
3680
- }
3681
- return env;
3682
- }
3683
3986
  async function collectFunctions(dir) {
3684
3987
  const files = {};
3988
+ const isRuntimeSource = (name) => (name.endsWith(".ts") || name.endsWith(".js")) && !name.endsWith(".d.ts") && !name.endsWith(".test.ts");
3685
3989
  const walk = async (sub, prefix) => {
3686
3990
  let entries;
3687
3991
  try {
@@ -3694,7 +3998,7 @@ async function collectFunctions(dir) {
3694
3998
  const full = join8(sub, entry.name);
3695
3999
  if (entry.isDirectory()) {
3696
4000
  await walk(full, rel);
3697
- } else if (entry.isFile() && entry.name.endsWith(".ts")) {
4001
+ } else if (entry.isFile() && isRuntimeSource(entry.name)) {
3698
4002
  files[rel] = await readFile5(full, "utf8");
3699
4003
  }
3700
4004
  }
@@ -3721,6 +4025,42 @@ function bodyOf(req) {
3721
4025
  req.on("error", rejectBody);
3722
4026
  });
3723
4027
  }
4028
+ function writeJson(res, status, payload) {
4029
+ res.writeHead(status, { "content-type": "application/json" });
4030
+ res.end(JSON.stringify(payload));
4031
+ }
4032
+ async function requestInput(url, req, readBody) {
4033
+ const query = {};
4034
+ for (const key of new Set(url.searchParams.keys())) {
4035
+ const values = url.searchParams.getAll(key);
4036
+ query[key] = values.length > 1 ? values.join(",") : values[0] ?? "";
4037
+ }
4038
+ const headers = {};
4039
+ for (const [key, value] of Object.entries(req.headers)) {
4040
+ if (typeof value === "string") headers[key] = value;
4041
+ }
4042
+ const body = req.method === "GET" || req.method === "HEAD" ? void 0 : await readBody(req);
4043
+ return {
4044
+ method: req.method ?? "GET",
4045
+ path: url.pathname,
4046
+ query,
4047
+ headers,
4048
+ ...body === void 0 ? {} : { body }
4049
+ };
4050
+ }
4051
+
4052
+ // packages/cli/src/dev.ts
4053
+ async function loadConfig(cwd) {
4054
+ const name = basename(resolve2(cwd));
4055
+ const config = await loadAdepConfigModule(cwd);
4056
+ return {
4057
+ name: config?.name ?? name,
4058
+ functionsDir: config?.functionsDir ?? "functions",
4059
+ functionsPrefix: config?.functions_prefix ?? "",
4060
+ ...config?.environment === void 0 ? {} : { environment: config.environment }
4061
+ };
4062
+ }
4063
+ var parseEnvFile = parseEnv;
3724
4064
  function resolveRouteFnName(pathname, prefix) {
3725
4065
  const segments = pathname.split("/").filter((segment) => segment.length > 0);
3726
4066
  const normalizedPrefix = prefix.replace(/^\/+|\/+$/g, "");
@@ -3747,10 +4087,6 @@ function resolveRouteFnName(pathname, prefix) {
3747
4087
  }
3748
4088
  return { fnName: second };
3749
4089
  }
3750
- function writeJson(res, status, payload) {
3751
- res.writeHead(status, { "content-type": "application/json" });
3752
- res.end(JSON.stringify(payload));
3753
- }
3754
4090
  function printBoundaries(log) {
3755
4091
  log("[adep] \u80FD\u529B\u8FB9\u754C\uFF08\u6A21\u62DF\u8FD0\u884C\u65F6\u53EA\u66FF\u6362\u4F20\u8F93/\u6301\u4E45\u5316\uFF0C\u4E0D\u6539\u5199\u8BED\u4E49\uFF09\uFF1A");
3756
4092
  for (const b of SIM_BOUNDARIES) {
@@ -3758,8 +4094,8 @@ function printBoundaries(log) {
3758
4094
  }
3759
4095
  }
3760
4096
  async function ensureGitignore(cwd) {
3761
- const gitignorePath = join8(cwd, ".gitignore");
3762
- const existing = await readFile5(gitignorePath, "utf8").catch(() => "");
4097
+ const gitignorePath = join9(cwd, ".gitignore");
4098
+ const existing = await readFile6(gitignorePath, "utf8").catch(() => "");
3763
4099
  if (existing.split(/\r?\n/).includes(".adep/")) return;
3764
4100
  await writeFile3(gitignorePath, `${existing.replace(/\n+$/, "")}
3765
4101
  .adep/
@@ -3773,8 +4109,9 @@ async function startDevServer(options) {
3773
4109
  if (options.prefix !== void 0) {
3774
4110
  config.functionsPrefix = options.prefix.replace(/^\/+|\/+$/g, "");
3775
4111
  }
3776
- const functionsDir = join8(cwd, config.functionsDir);
4112
+ const functionsDir = join9(cwd, config.functionsDir);
3777
4113
  const coldStartAt = Date.now();
4114
+ const bearerUser = bearerUserResolverFromEnv();
3778
4115
  const executor = new WorkerFunctionExecutor({
3779
4116
  deps: { rootDir: cwd, builtin: LOCAL_BUILTIN_DEPS }
3780
4117
  });
@@ -3865,12 +4202,12 @@ async function startDevServer(options) {
3865
4202
  const reload = async () => {
3866
4203
  const [nextFiles, envText2] = await Promise.all([
3867
4204
  collectFunctions(functionsDir),
3868
- readFile5(join8(cwd, ".env.local"), "utf8").catch(() => "")
4205
+ readFile6(join9(cwd, ".env.local"), "utf8").catch(() => "")
3869
4206
  ]);
3870
4207
  files = nextFiles;
3871
4208
  env = { ...config.environment, ...parseEnvFile(envText2) };
3872
4209
  };
3873
- const envText = await readFile5(join8(cwd, ".env.local"), "utf8").catch(() => "");
4210
+ const envText = await readFile6(join9(cwd, ".env.local"), "utf8").catch(() => "");
3874
4211
  env = { ...config.environment, ...parseEnvFile(envText) };
3875
4212
  let debounceTimer;
3876
4213
  let watcher;
@@ -3885,29 +4222,15 @@ async function startDevServer(options) {
3885
4222
  watcher = void 0;
3886
4223
  }
3887
4224
  const buildInput = async (fnName, entry, url, req) => {
3888
- const query = {};
3889
- for (const key of new Set(url.searchParams.keys())) {
3890
- const values = url.searchParams.getAll(key);
3891
- query[key] = values.length > 1 ? values.join(",") : values[0] ?? "";
3892
- }
3893
- const headers = {};
3894
- for (const [key, value] of Object.entries(req.headers)) {
3895
- if (typeof value === "string") headers[key] = value;
3896
- }
3897
- const body = req.method === "GET" || req.method === "HEAD" ? void 0 : await bodyOf(req);
3898
4225
  const hasEnv = Object.keys(env).length > 0;
4226
+ const request = await requestInput(url, req, bodyOf);
4227
+ if (bearerUser !== void 0) request.user = await bearerUser(request.headers);
3899
4228
  return {
3900
4229
  project: { id: "local", slug: config.name },
3901
4230
  fn: { id: fnName, name: fnName },
3902
4231
  files,
3903
4232
  entry,
3904
- request: {
3905
- method: req.method ?? "GET",
3906
- path: url.pathname,
3907
- query,
3908
- headers,
3909
- ...body === void 0 ? {} : { body }
3910
- },
4233
+ request,
3911
4234
  // 模拟运行时能力:db / storage / realtime 各自只读复用线上装配,语义一致。
3912
4235
  capabilities: dbBundle.capabilities,
3913
4236
  rpcHandlers: {
@@ -4044,6 +4367,9 @@ async function startDevServer(options) {
4044
4367
  log(`[adep] dev server listening on ${baseUrl}\uFF08\u6A21\u62DF\u8FD0\u884C\u65F6\uFF0C\u51B7\u542F\u52A8 ${coldStartMs}ms\uFF09`);
4045
4368
  log(`[adep] curl \u793A\u4F8B\uFF1Acurl ${baseUrl}${curlPath}`);
4046
4369
  printBoundaries(log);
4370
+ if (bearerUser !== void 0) {
4371
+ log(`[adep] \u767B\u5F55\u5373\u670D\u52A1\uFF1AADEP_AUTH_ISSUER \u5DF2\u914D\u7F6E\uFF0CBearer token \u5C06\u89E3\u6790\u4E3A\u7528\u6237\u8EAB\u4EFD\uFF08userinfo \u7AEF\u70B9\uFF09`);
4372
+ }
4047
4373
  log(`[adep] \u6A21\u62DF\u6570\u636E\u76EE\u5F55\uFF1A${simDir(cwd)}`);
4048
4374
  startStateAlarmScheduler();
4049
4375
  return {