@objectstack/runtime 4.0.2 → 4.0.4

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.cjs CHANGED
@@ -665,6 +665,13 @@ var AppPlugin = class {
665
665
  return;
666
666
  }
667
667
  ctx.logger.debug("Retrieved ObjectQL engine service", { appId });
668
+ if (this.bundle.datasourceMapping && Array.isArray(this.bundle.datasourceMapping)) {
669
+ ctx.logger.info("Configuring datasource mapping rules", {
670
+ appId,
671
+ ruleCount: this.bundle.datasourceMapping.length
672
+ });
673
+ ql.setDatasourceMapping(this.bundle.datasourceMapping);
674
+ }
668
675
  const runtime = this.bundle.default || this.bundle;
669
676
  if (runtime && typeof runtime.onEnable === "function") {
670
677
  ctx.logger.info("Executing runtime.onEnable", {
@@ -828,6 +835,7 @@ var AppPlugin = class {
828
835
  // src/http-dispatcher.ts
829
836
  var import_core2 = require("@objectstack/core");
830
837
  var import_system = require("@objectstack/spec/system");
838
+ var import_shared = require("@objectstack/spec/shared");
831
839
  function randomUUID() {
832
840
  if (globalThis.crypto && typeof globalThis.crypto.randomUUID === "function") {
833
841
  return globalThis.crypto.randomUUID();
@@ -839,7 +847,7 @@ function randomUUID() {
839
847
  });
840
848
  }
841
849
  var HttpDispatcher = class {
842
- // Casting to any to access dynamic props like broker, services, graphql
850
+ // Casting to any to access dynamic props like services, graphql
843
851
  constructor(kernel) {
844
852
  this.kernel = kernel;
845
853
  }
@@ -873,11 +881,73 @@ var HttpDispatcher = class {
873
881
  }
874
882
  };
875
883
  }
876
- ensureBroker() {
877
- if (!this.kernel.broker) {
878
- throw { statusCode: 500, message: "Kernel Broker not available" };
879
- }
880
- return this.kernel.broker;
884
+ /**
885
+ * Direct data service dispatch — replaces broker.call('data.*').
886
+ * Tries protocol service first (supports expand/populate), falls back to ObjectQL.
887
+ */
888
+ async callData(action, params) {
889
+ const protocol = await this.resolveService("protocol");
890
+ const qlService = await this.getObjectQLService();
891
+ const ql = qlService ?? await this.resolveService("objectql");
892
+ if (action === "create") {
893
+ if (ql) {
894
+ const res = await ql.insert(params.object, params.data);
895
+ const record = { ...params.data, ...res };
896
+ return { object: params.object, id: record.id, record };
897
+ }
898
+ throw { statusCode: 503, message: "Data service not available" };
899
+ }
900
+ if (action === "get") {
901
+ if (protocol && typeof protocol.getData === "function") {
902
+ return await protocol.getData({ object: params.object, id: params.id, expand: params.expand, select: params.select });
903
+ }
904
+ if (ql) {
905
+ let all = await ql.find(params.object);
906
+ if (!all) all = [];
907
+ const match = all.find((i) => i.id === params.id);
908
+ return match ? { object: params.object, id: params.id, record: match } : null;
909
+ }
910
+ throw { statusCode: 503, message: "Data service not available" };
911
+ }
912
+ if (action === "update") {
913
+ if (ql && params.id) {
914
+ let all = await ql.find(params.object);
915
+ if (all && all.value) all = all.value;
916
+ if (!all) all = [];
917
+ const existing = all.find((i) => i.id === params.id);
918
+ if (!existing) throw new Error("[ObjectStack] Not Found");
919
+ await ql.update(params.object, params.data, { where: { id: params.id } });
920
+ return { object: params.object, id: params.id, record: { ...existing, ...params.data } };
921
+ }
922
+ throw { statusCode: 503, message: "Data service not available" };
923
+ }
924
+ if (action === "delete") {
925
+ if (ql) {
926
+ await ql.delete(params.object, { where: { id: params.id } });
927
+ return { object: params.object, id: params.id, deleted: true };
928
+ }
929
+ throw { statusCode: 503, message: "Data service not available" };
930
+ }
931
+ if (action === "query" || action === "find") {
932
+ if (protocol && typeof protocol.findData === "function") {
933
+ const query = params.query || (() => {
934
+ const { object, ...rest } = params;
935
+ return rest;
936
+ })();
937
+ return await protocol.findData({ object: params.object, query });
938
+ }
939
+ if (ql) {
940
+ let all = await ql.find(params.object);
941
+ if (!Array.isArray(all) && all && all.value) all = all.value;
942
+ if (!all) all = [];
943
+ return { object: params.object, records: all, total: all.length };
944
+ }
945
+ throw { statusCode: 503, message: "Data service not available" };
946
+ }
947
+ if (action === "batch") {
948
+ return { object: params.object, results: [] };
949
+ }
950
+ throw { statusCode: 400, message: `Unknown data action: ${action}` };
881
951
  }
882
952
  /**
883
953
  * Generates the discovery JSON response for the API root.
@@ -1041,18 +1111,6 @@ var HttpDispatcher = class {
1041
1111
  return { handled: true, result: response };
1042
1112
  }
1043
1113
  const normalizedPath = path.replace(/^\/+/, "");
1044
- if (normalizedPath === "login" && method.toUpperCase() === "POST") {
1045
- try {
1046
- const broker = this.ensureBroker();
1047
- const data = await broker.call("auth.login", body, { request: context.request });
1048
- return { handled: true, response: { status: 200, body: data } };
1049
- } catch (error) {
1050
- const statusCode = error?.statusCode ?? error?.status;
1051
- if (statusCode !== 500 || !error?.message?.includes("Broker not available")) {
1052
- throw error;
1053
- }
1054
- }
1055
- }
1056
1114
  return this.mockAuthFallback(normalizedPath, method, body);
1057
1115
  }
1058
1116
  /**
@@ -1108,22 +1166,23 @@ var HttpDispatcher = class {
1108
1166
  * Standard: /metadata/:type/:name
1109
1167
  * Fallback for backward compat: /metadata (all objects), /metadata/:objectName (get object)
1110
1168
  */
1111
- async handleMetadata(path, context, method, body, query) {
1112
- const broker = this.kernel.broker ?? null;
1169
+ async handleMetadata(path, _context, method, body, query) {
1113
1170
  const parts = path.replace(/^\/+/, "").split("/").filter(Boolean);
1114
1171
  if (parts[0] === "types") {
1172
+ const metadataService = await this.resolveService("metadata");
1173
+ if (metadataService && typeof metadataService.getRegisteredTypes === "function") {
1174
+ try {
1175
+ const types = await metadataService.getRegisteredTypes();
1176
+ return { handled: true, response: this.success({ types }) };
1177
+ } catch (e) {
1178
+ console.warn("[HttpDispatcher] MetadataService.getRegisteredTypes() failed:", e.message);
1179
+ }
1180
+ }
1115
1181
  const protocol = await this.resolveService("protocol");
1116
1182
  if (protocol && typeof protocol.getMetaTypes === "function") {
1117
1183
  const result = await protocol.getMetaTypes({});
1118
1184
  return { handled: true, response: this.success(result) };
1119
1185
  }
1120
- if (broker) {
1121
- try {
1122
- const data = await broker.call("metadata.types", {}, { request: context.request });
1123
- return { handled: true, response: this.success(data) };
1124
- } catch {
1125
- }
1126
- }
1127
1186
  return { handled: true, response: this.success({ types: ["object", "app", "plugin"] }) };
1128
1187
  }
1129
1188
  if (parts.length === 3 && parts[2] === "published" && (!method || method === "GET")) {
@@ -1134,12 +1193,12 @@ var HttpDispatcher = class {
1134
1193
  if (data === void 0) return { handled: true, response: this.error("Not found", 404) };
1135
1194
  return { handled: true, response: this.success(data) };
1136
1195
  }
1137
- if (broker) {
1196
+ const metaSvc = await this.resolveService("metadata");
1197
+ if (metaSvc && typeof metaSvc.getPublished === "function") {
1138
1198
  try {
1139
- const data = await broker.call("metadata.getPublished", { type, name }, { request: context.request });
1140
- return { handled: true, response: this.success(data) };
1141
- } catch (e) {
1142
- return { handled: true, response: this.error(e.message, 404) };
1199
+ const fallbackData = await metaSvc.getPublished(type, name);
1200
+ if (fallbackData !== void 0) return { handled: true, response: this.success(fallbackData) };
1201
+ } catch {
1143
1202
  }
1144
1203
  }
1145
1204
  return { handled: true, response: this.error("Not found", 404) };
@@ -1157,9 +1216,10 @@ var HttpDispatcher = class {
1157
1216
  return { handled: true, response: this.error(e.message, 400) };
1158
1217
  }
1159
1218
  }
1160
- if (broker) {
1219
+ const metaSvc = await this.resolveService("metadata");
1220
+ if (metaSvc && typeof metaSvc.saveItem === "function") {
1161
1221
  try {
1162
- const data = await broker.call("metadata.saveItem", { type, name, item: body }, { request: context.request });
1222
+ const data = await metaSvc.saveItem(type, name, body);
1163
1223
  return { handled: true, response: this.success(data) };
1164
1224
  } catch (e) {
1165
1225
  return { handled: true, response: this.error(e.message || "Save not supported", 501) };
@@ -1169,10 +1229,6 @@ var HttpDispatcher = class {
1169
1229
  }
1170
1230
  try {
1171
1231
  if (type === "objects" || type === "object") {
1172
- if (broker) {
1173
- const data = await broker.call("metadata.getObject", { objectName: name }, { request: context.request });
1174
- return { handled: true, response: this.success(data) };
1175
- }
1176
1232
  const qlService = await this.getObjectQLService();
1177
1233
  if (qlService?.registry) {
1178
1234
  const data = qlService.registry.getObject(name);
@@ -1180,7 +1236,7 @@ var HttpDispatcher = class {
1180
1236
  }
1181
1237
  return { handled: true, response: this.error("Not found", 404) };
1182
1238
  }
1183
- const singularType = type.endsWith("s") ? type.slice(0, -1) : type;
1239
+ const singularType = (0, import_shared.pluralToSingular)(type);
1184
1240
  const protocol = await this.resolveService("protocol");
1185
1241
  if (protocol && typeof protocol.getMetaItem === "function") {
1186
1242
  try {
@@ -1189,10 +1245,13 @@ var HttpDispatcher = class {
1189
1245
  } catch (e) {
1190
1246
  }
1191
1247
  }
1192
- if (broker) {
1193
- const method2 = `metadata.get${this.capitalize(singularType)}`;
1194
- const data = await broker.call(method2, { name }, { request: context.request });
1195
- return { handled: true, response: this.success(data) };
1248
+ const metaSvc = await this.resolveService("metadata");
1249
+ if (metaSvc && typeof metaSvc.getItem === "function") {
1250
+ try {
1251
+ const data = await metaSvc.getItem(singularType, name);
1252
+ if (data) return { handled: true, response: this.success(data) };
1253
+ } catch {
1254
+ }
1196
1255
  }
1197
1256
  return { handled: true, response: this.error("Not found", 404) };
1198
1257
  } catch (e) {
@@ -1212,23 +1271,19 @@ var HttpDispatcher = class {
1212
1271
  } catch {
1213
1272
  }
1214
1273
  }
1215
- if (broker) {
1274
+ const metadataService = await this.getService(import_system.CoreServiceName.enum.metadata);
1275
+ if (metadataService && typeof metadataService.list === "function") {
1216
1276
  try {
1217
- if (typeOrName === "objects") {
1218
- const data2 = await broker.call("metadata.objects", { packageId }, { request: context.request });
1219
- return { handled: true, response: this.success(data2) };
1277
+ let items = await metadataService.list(typeOrName);
1278
+ if (packageId && items && items.length > 0) {
1279
+ items = items.filter((item) => item?._packageId === packageId);
1220
1280
  }
1221
- const data = await broker.call(`metadata.${typeOrName}`, { packageId }, { request: context.request });
1222
- if (data !== null && data !== void 0) {
1223
- return { handled: true, response: this.success(data) };
1281
+ if (items && items.length > 0) {
1282
+ return { handled: true, response: this.success({ type: typeOrName, items }) };
1224
1283
  }
1225
- } catch {
1226
- }
1227
- try {
1228
- const data = await broker.call("metadata.getObject", { objectName: typeOrName }, { request: context.request });
1229
- return { handled: true, response: this.success(data) };
1230
1284
  } catch (e) {
1231
- return { handled: true, response: this.error(e.message, 404) };
1285
+ const sanitizedType = String(typeOrName).replace(/[\r\n\t]/g, "");
1286
+ console.debug(`[HttpDispatcher] MetadataService.list() failed for type:`, sanitizedType, "error:", e.message);
1232
1287
  }
1233
1288
  }
1234
1289
  const qlService = await this.getObjectQLService();
@@ -1247,18 +1302,19 @@ var HttpDispatcher = class {
1247
1302
  return { handled: true, response: this.error("Not found", 404) };
1248
1303
  }
1249
1304
  if (parts.length === 0) {
1305
+ const metadataService = await this.resolveService("metadata");
1306
+ if (metadataService && typeof metadataService.getRegisteredTypes === "function") {
1307
+ try {
1308
+ const types = await metadataService.getRegisteredTypes();
1309
+ return { handled: true, response: this.success({ types }) };
1310
+ } catch {
1311
+ }
1312
+ }
1250
1313
  const protocol = await this.resolveService("protocol");
1251
1314
  if (protocol && typeof protocol.getMetaTypes === "function") {
1252
1315
  const result = await protocol.getMetaTypes({});
1253
1316
  return { handled: true, response: this.success(result) };
1254
1317
  }
1255
- if (broker) {
1256
- try {
1257
- const data = await broker.call("metadata.types", {}, { request: context.request });
1258
- return { handled: true, response: this.success(data) };
1259
- } catch {
1260
- }
1261
- }
1262
1318
  return { handled: true, response: this.success({ types: ["object", "app", "plugin"] }) };
1263
1319
  }
1264
1320
  return { handled: false };
@@ -1267,8 +1323,7 @@ var HttpDispatcher = class {
1267
1323
  * Handles Data requests
1268
1324
  * path: sub-path after /data/ (e.g. "contacts", "contacts/123", "contacts/query")
1269
1325
  */
1270
- async handleData(path, method, body, query, context) {
1271
- const broker = this.ensureBroker();
1326
+ async handleData(path, method, body, query, _context) {
1272
1327
  const parts = path.replace(/^\/+/, "").split("/");
1273
1328
  const objectName = parts[0];
1274
1329
  if (!objectName) {
@@ -1278,11 +1333,11 @@ var HttpDispatcher = class {
1278
1333
  if (parts.length > 1) {
1279
1334
  const action = parts[1];
1280
1335
  if (action === "query" && m === "POST") {
1281
- const result = await broker.call("data.query", { object: objectName, ...body }, { request: context.request });
1336
+ const result = await this.callData("query", { object: objectName, ...body });
1282
1337
  return { handled: true, response: this.success(result) };
1283
1338
  }
1284
1339
  if (action === "batch" && m === "POST") {
1285
- const result = await broker.call("data.batch", { object: objectName, ...body }, { request: context.request });
1340
+ const result = await this.callData("batch", { object: objectName, ...body });
1286
1341
  return { handled: true, response: this.success(result) };
1287
1342
  }
1288
1343
  if (parts.length === 2 && m === "GET") {
@@ -1291,17 +1346,17 @@ var HttpDispatcher = class {
1291
1346
  const allowedParams = {};
1292
1347
  if (select != null) allowedParams.select = select;
1293
1348
  if (expand != null) allowedParams.expand = expand;
1294
- const result = await broker.call("data.get", { object: objectName, id, ...allowedParams }, { request: context.request });
1349
+ const result = await this.callData("get", { object: objectName, id, ...allowedParams });
1295
1350
  return { handled: true, response: this.success(result) };
1296
1351
  }
1297
1352
  if (parts.length === 2 && m === "PATCH") {
1298
1353
  const id = parts[1];
1299
- const result = await broker.call("data.update", { object: objectName, id, data: body }, { request: context.request });
1354
+ const result = await this.callData("update", { object: objectName, id, data: body });
1300
1355
  return { handled: true, response: this.success(result) };
1301
1356
  }
1302
1357
  if (parts.length === 2 && m === "DELETE") {
1303
1358
  const id = parts[1];
1304
- const result = await broker.call("data.delete", { object: objectName, id }, { request: context.request });
1359
+ const result = await this.callData("delete", { object: objectName, id });
1305
1360
  return { handled: true, response: this.success(result) };
1306
1361
  }
1307
1362
  } else {
@@ -1328,11 +1383,11 @@ var HttpDispatcher = class {
1328
1383
  normalized.offset = normalized.skip;
1329
1384
  delete normalized.skip;
1330
1385
  }
1331
- const result = await broker.call("data.query", { object: objectName, query: normalized }, { request: context.request });
1386
+ const result = await this.callData("query", { object: objectName, query: normalized });
1332
1387
  return { handled: true, response: this.success(result) };
1333
1388
  }
1334
1389
  if (m === "POST") {
1335
- const result = await broker.call("data.create", { object: objectName, data: body }, { request: context.request });
1390
+ const result = await this.callData("create", { object: objectName, data: body });
1336
1391
  const res = this.success(result);
1337
1392
  res.status = 201;
1338
1393
  return { handled: true, response: res };
@@ -1434,18 +1489,14 @@ var HttpDispatcher = class {
1434
1489
  * - POST /packages/:id/publish → publish a package (metadata snapshot)
1435
1490
  * - POST /packages/:id/revert → revert a package to last published state
1436
1491
  *
1437
- * Uses ObjectQL SchemaRegistry directly (via the 'objectql' service)
1438
- * with broker fallback for backward compatibility.
1492
+ * Uses ObjectQL SchemaRegistry directly (via the 'objectql' service).
1439
1493
  */
1440
- async handlePackages(path, method, body, query, context) {
1494
+ async handlePackages(path, method, body, query, _context) {
1441
1495
  const m = method.toUpperCase();
1442
1496
  const parts = path.replace(/^\/+/, "").split("/").filter(Boolean);
1443
1497
  const qlService = await this.getObjectQLService();
1444
1498
  const registry = qlService?.registry;
1445
1499
  if (!registry) {
1446
- if (this.kernel.broker) {
1447
- return this.handlePackagesViaBroker(parts, m, body, query, context);
1448
- }
1449
1500
  return { handled: true, response: this.error("Package service not available", 503) };
1450
1501
  }
1451
1502
  try {
@@ -1484,10 +1535,6 @@ var HttpDispatcher = class {
1484
1535
  const result = await metadataService.publishPackage(id, body || {});
1485
1536
  return { handled: true, response: this.success(result) };
1486
1537
  }
1487
- if (this.kernel.broker) {
1488
- const result = await this.kernel.broker.call("metadata.publishPackage", { packageId: id, ...body }, { request: context.request });
1489
- return { handled: true, response: this.success(result) };
1490
- }
1491
1538
  return { handled: true, response: this.error("Metadata service not available", 503) };
1492
1539
  }
1493
1540
  if (parts.length === 2 && parts[1] === "revert" && m === "POST") {
@@ -1497,10 +1544,6 @@ var HttpDispatcher = class {
1497
1544
  await metadataService.revertPackage(id);
1498
1545
  return { handled: true, response: this.success({ success: true }) };
1499
1546
  }
1500
- if (this.kernel.broker) {
1501
- await this.kernel.broker.call("metadata.revertPackage", { packageId: id }, { request: context.request });
1502
- return { handled: true, response: this.success({ success: true }) };
1503
- }
1504
1547
  return { handled: true, response: this.error("Metadata service not available", 503) };
1505
1548
  }
1506
1549
  if (parts.length === 1 && m === "GET") {
@@ -1520,47 +1563,6 @@ var HttpDispatcher = class {
1520
1563
  }
1521
1564
  return { handled: false };
1522
1565
  }
1523
- /**
1524
- * Fallback: handle packages via broker (for backward compatibility)
1525
- */
1526
- async handlePackagesViaBroker(parts, m, body, query, context) {
1527
- const broker = this.kernel.broker;
1528
- try {
1529
- if (parts.length === 0 && m === "GET") {
1530
- const result = await broker.call("package.list", query || {}, { request: context.request });
1531
- return { handled: true, response: this.success(result) };
1532
- }
1533
- if (parts.length === 0 && m === "POST") {
1534
- const result = await broker.call("package.install", body, { request: context.request });
1535
- const res = this.success(result);
1536
- res.status = 201;
1537
- return { handled: true, response: res };
1538
- }
1539
- if (parts.length === 2 && parts[1] === "enable" && m === "PATCH") {
1540
- const id = decodeURIComponent(parts[0]);
1541
- const result = await broker.call("package.enable", { id }, { request: context.request });
1542
- return { handled: true, response: this.success(result) };
1543
- }
1544
- if (parts.length === 2 && parts[1] === "disable" && m === "PATCH") {
1545
- const id = decodeURIComponent(parts[0]);
1546
- const result = await broker.call("package.disable", { id }, { request: context.request });
1547
- return { handled: true, response: this.success(result) };
1548
- }
1549
- if (parts.length === 1 && m === "GET") {
1550
- const id = decodeURIComponent(parts[0]);
1551
- const result = await broker.call("package.get", { id }, { request: context.request });
1552
- return { handled: true, response: this.success(result) };
1553
- }
1554
- if (parts.length === 1 && m === "DELETE") {
1555
- const id = decodeURIComponent(parts[0]);
1556
- const result = await broker.call("package.uninstall", { id }, { request: context.request });
1557
- return { handled: true, response: this.success(result) };
1558
- }
1559
- } catch (e) {
1560
- return { handled: true, response: this.error(e.message, e.statusCode || 500) };
1561
- }
1562
- return { handled: false };
1563
- }
1564
1566
  /**
1565
1567
  * Handles Storage requests
1566
1568
  * path: sub-path after /storage/
@@ -1769,9 +1771,6 @@ var HttpDispatcher = class {
1769
1771
  }
1770
1772
  return null;
1771
1773
  }
1772
- capitalize(s) {
1773
- return s.charAt(0).toUpperCase() + s.slice(1);
1774
- }
1775
1774
  /**
1776
1775
  * Handle AI service routes (/ai/chat, /ai/models, /ai/conversations, etc.)
1777
1776
  * Resolves the AI service and its built-in route handlers, then dispatches.
@@ -1908,10 +1907,12 @@ var HttpDispatcher = class {
1908
1907
  return this.handleAI(cleanPath, method, body, query, context);
1909
1908
  }
1910
1909
  if (cleanPath === "/openapi.json" && method === "GET") {
1911
- const broker = this.ensureBroker();
1912
1910
  try {
1913
- const result2 = await broker.call("metadata.generateOpenApi", {}, { request: context.request });
1914
- return { handled: true, response: this.success(result2) };
1911
+ const metaSvc = await this.resolveService("metadata");
1912
+ if (metaSvc && typeof metaSvc.generateOpenApi === "function") {
1913
+ const result2 = await metaSvc.generateOpenApi({});
1914
+ return { handled: true, response: this.success(result2) };
1915
+ }
1915
1916
  } catch (e) {
1916
1917
  }
1917
1918
  }
@@ -1926,37 +1927,48 @@ var HttpDispatcher = class {
1926
1927
  * Handles Custom API Endpoints defined in metadata
1927
1928
  */
1928
1929
  async handleApiEndpoint(path, method, body, query, context) {
1929
- const broker = this.ensureBroker();
1930
1930
  try {
1931
- const endpoint = await broker.call("metadata.matchEndpoint", { path, method });
1931
+ const metaSvc = await this.resolveService("metadata");
1932
+ if (!metaSvc || typeof metaSvc.matchEndpoint !== "function") {
1933
+ return { handled: false };
1934
+ }
1935
+ const endpoint = await metaSvc.matchEndpoint({ path, method });
1932
1936
  if (endpoint) {
1933
1937
  if (endpoint.type === "flow") {
1934
- const result = await broker.call("automation.runFlow", {
1938
+ const automationSvc = await this.resolveService("automation");
1939
+ if (!automationSvc || typeof automationSvc.runFlow !== "function") {
1940
+ return { handled: true, response: this.error("Automation service not available", 503) };
1941
+ }
1942
+ const result = await automationSvc.runFlow({
1935
1943
  flowId: endpoint.target,
1936
1944
  inputs: { ...query, ...body, _request: context.request }
1937
1945
  });
1938
1946
  return { handled: true, response: this.success(result) };
1939
1947
  }
1940
1948
  if (endpoint.type === "script") {
1941
- const result = await broker.call("automation.runScript", {
1949
+ const automationSvc = await this.resolveService("automation");
1950
+ if (!automationSvc || typeof automationSvc.runScript !== "function") {
1951
+ return { handled: true, response: this.error("Automation service not available", 503) };
1952
+ }
1953
+ const result = await automationSvc.runScript({
1942
1954
  scriptName: endpoint.target,
1943
1955
  context: { ...query, ...body, request: context.request }
1944
- }, { request: context.request });
1956
+ });
1945
1957
  return { handled: true, response: this.success(result) };
1946
1958
  }
1947
1959
  if (endpoint.type === "object_operation") {
1948
1960
  if (endpoint.objectParams) {
1949
1961
  const { object, operation } = endpoint.objectParams;
1950
1962
  if (operation === "find") {
1951
- const result = await broker.call("data.query", { object, query }, { request: context.request });
1963
+ const result = await this.callData("query", { object, query });
1952
1964
  return { handled: true, response: this.success(result.records, { total: result.total }) };
1953
1965
  }
1954
1966
  if (operation === "get" && query.id) {
1955
- const result = await broker.call("data.get", { object, id: query.id }, { request: context.request });
1967
+ const result = await this.callData("get", { object, id: query.id });
1956
1968
  return { handled: true, response: this.success(result) };
1957
1969
  }
1958
1970
  if (operation === "create") {
1959
- const result = await broker.call("data.create", { object, data: body }, { request: context.request });
1971
+ const result = await this.callData("create", { object, data: body });
1960
1972
  return { handled: true, response: this.success(result) };
1961
1973
  }
1962
1974
  }
@@ -1978,6 +1990,64 @@ var HttpDispatcher = class {
1978
1990
  };
1979
1991
 
1980
1992
  // src/dispatcher-plugin.ts
1993
+ function mountRouteOnServer(route, server, routePath) {
1994
+ const handler = async (req, res) => {
1995
+ try {
1996
+ const result = await route.handler({
1997
+ body: req.body,
1998
+ params: req.params,
1999
+ query: req.query
2000
+ });
2001
+ if (result.stream && result.events) {
2002
+ res.status(result.status);
2003
+ if (result.headers) {
2004
+ for (const [k, v] of Object.entries(result.headers)) {
2005
+ res.header(k, String(v));
2006
+ }
2007
+ } else {
2008
+ res.header("Content-Type", "text/event-stream");
2009
+ res.header("Cache-Control", "no-cache");
2010
+ res.header("Connection", "keep-alive");
2011
+ }
2012
+ if (typeof res.write === "function" && typeof res.end === "function") {
2013
+ for await (const event of result.events) {
2014
+ res.write(typeof event === "string" ? event : `data: ${JSON.stringify(event)}
2015
+
2016
+ `);
2017
+ }
2018
+ res.end();
2019
+ } else {
2020
+ const events = [];
2021
+ for await (const event of result.events) {
2022
+ events.push(event);
2023
+ }
2024
+ res.json({ events });
2025
+ }
2026
+ } else {
2027
+ res.status(result.status);
2028
+ if (result.body !== void 0) {
2029
+ res.json(result.body);
2030
+ } else {
2031
+ res.end();
2032
+ }
2033
+ }
2034
+ } catch (err) {
2035
+ errorResponse(err, res);
2036
+ }
2037
+ };
2038
+ const m = route.method.toLowerCase();
2039
+ if (m === "get" && typeof server.get === "function") {
2040
+ server.get(routePath, handler);
2041
+ return true;
2042
+ } else if (m === "post" && typeof server.post === "function") {
2043
+ server.post(routePath, handler);
2044
+ return true;
2045
+ } else if (m === "delete" && typeof server.delete === "function") {
2046
+ server.delete(routePath, handler);
2047
+ return true;
2048
+ }
2049
+ return false;
2050
+ }
1981
2051
  function sendResult(result, res) {
1982
2052
  if (result.handled) {
1983
2053
  if (result.response) {
@@ -2272,61 +2342,23 @@ function createDispatcherPlugin(config = {}) {
2272
2342
  if (!server) return;
2273
2343
  for (const route of routes) {
2274
2344
  const routePath = route.path.startsWith("/api/v1") ? route.path : `${prefix}${route.path}`;
2275
- const handler = async (req, res) => {
2276
- try {
2277
- const result = await route.handler({
2278
- body: req.body,
2279
- params: req.params,
2280
- query: req.query
2281
- });
2282
- if (result.stream && result.events) {
2283
- res.status(result.status);
2284
- if (result.headers) {
2285
- for (const [k, v] of Object.entries(result.headers)) {
2286
- res.header(k, v);
2287
- }
2288
- } else {
2289
- res.header("Content-Type", "text/event-stream");
2290
- res.header("Cache-Control", "no-cache");
2291
- res.header("Connection", "keep-alive");
2292
- }
2293
- if (typeof res.write === "function" && typeof res.end === "function") {
2294
- for await (const event of result.events) {
2295
- res.write(typeof event === "string" ? event : `data: ${JSON.stringify(event)}
2296
-
2297
- `);
2298
- }
2299
- res.end();
2300
- } else {
2301
- const events = [];
2302
- for await (const event of result.events) {
2303
- events.push(event);
2304
- }
2305
- res.json({ events });
2306
- }
2307
- } else {
2308
- res.status(result.status);
2309
- if (result.body !== void 0) {
2310
- res.json(result.body);
2311
- } else {
2312
- res.end();
2313
- }
2314
- }
2315
- } catch (err) {
2316
- errorResponse(err, res);
2317
- }
2318
- };
2319
- const m = route.method.toLowerCase();
2320
- if (m === "get" && typeof server.get === "function") {
2321
- server.get(routePath, handler);
2322
- } else if (m === "post" && typeof server.post === "function") {
2323
- server.post(routePath, handler);
2324
- } else if (m === "delete" && typeof server.delete === "function") {
2325
- server.delete(routePath, handler);
2326
- }
2345
+ mountRouteOnServer(route, server, routePath);
2327
2346
  }
2328
2347
  ctx.logger.info(`[Dispatcher] Registered ${routes.length} AI routes`);
2329
2348
  });
2349
+ const cachedRoutes = kernel.__aiRoutes;
2350
+ if (cachedRoutes && Array.isArray(cachedRoutes) && cachedRoutes.length > 0) {
2351
+ let registered = 0;
2352
+ for (const route of cachedRoutes) {
2353
+ const routePath = route.path.startsWith("/api/v1") ? route.path : `${prefix}${route.path}`;
2354
+ if (mountRouteOnServer(route, server, routePath)) {
2355
+ registered++;
2356
+ }
2357
+ }
2358
+ if (registered > 0) {
2359
+ ctx.logger.info(`[Dispatcher] Recovered ${registered} cached AI routes (hook timing fallback)`);
2360
+ }
2361
+ }
2330
2362
  }
2331
2363
  };
2332
2364
  }