@objectstack/runtime 4.0.2 → 4.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -781,6 +781,7 @@ var AppPlugin = class {
781
781
  // src/http-dispatcher.ts
782
782
  import { getEnv, resolveLocale } from "@objectstack/core";
783
783
  import { CoreServiceName } from "@objectstack/spec/system";
784
+ import { pluralToSingular } from "@objectstack/spec/shared";
784
785
  function randomUUID() {
785
786
  if (globalThis.crypto && typeof globalThis.crypto.randomUUID === "function") {
786
787
  return globalThis.crypto.randomUUID();
@@ -1065,18 +1066,66 @@ var HttpDispatcher = class {
1065
1066
  const broker = this.kernel.broker ?? null;
1066
1067
  const parts = path.replace(/^\/+/, "").split("/").filter(Boolean);
1067
1068
  if (parts[0] === "types") {
1069
+ console.log("[HttpDispatcher] Attempting to resolve MetadataService...");
1070
+ console.log("[HttpDispatcher] Available kernel methods:", {
1071
+ hasGetServiceAsync: typeof this.kernel.getServiceAsync === "function",
1072
+ hasGetService: typeof this.kernel.getService === "function",
1073
+ hasContext: !!this.kernel.context,
1074
+ hasContextGetService: typeof this.kernel.context?.getService === "function"
1075
+ });
1076
+ let metadataService = null;
1077
+ if (typeof this.kernel.getServiceAsync === "function") {
1078
+ try {
1079
+ metadataService = await this.kernel.getServiceAsync("metadata");
1080
+ console.log('[HttpDispatcher] kernel.getServiceAsync("metadata") returned:', !!metadataService);
1081
+ } catch (e) {
1082
+ console.log('[HttpDispatcher] kernel.getServiceAsync("metadata") failed:', e.message);
1083
+ }
1084
+ }
1085
+ if (!metadataService && typeof this.kernel.getService === "function") {
1086
+ try {
1087
+ metadataService = await this.kernel.getService("metadata");
1088
+ console.log('[HttpDispatcher] kernel.getService("metadata") returned:', !!metadataService);
1089
+ } catch (e) {
1090
+ console.log('[HttpDispatcher] kernel.getService("metadata") failed:', e.message);
1091
+ }
1092
+ }
1093
+ if (!metadataService && this.kernel.context?.getService) {
1094
+ try {
1095
+ metadataService = await this.kernel.context.getService("metadata");
1096
+ console.log('[HttpDispatcher] kernel.context.getService("metadata") returned:', !!metadataService);
1097
+ } catch (e) {
1098
+ console.log('[HttpDispatcher] kernel.context.getService("metadata") failed:', e.message);
1099
+ }
1100
+ }
1101
+ console.log("[HttpDispatcher] Final metadataService:", !!metadataService, "has getRegisteredTypes:", typeof metadataService?.getRegisteredTypes);
1102
+ if (metadataService && typeof metadataService.getRegisteredTypes === "function") {
1103
+ try {
1104
+ const types = await metadataService.getRegisteredTypes();
1105
+ console.log("[HttpDispatcher] MetadataService.getRegisteredTypes() returned:", types);
1106
+ return { handled: true, response: this.success({ types }) };
1107
+ } catch (e) {
1108
+ console.warn("[HttpDispatcher] MetadataService.getRegisteredTypes() failed:", e.message, e.stack);
1109
+ }
1110
+ } else {
1111
+ console.log("[HttpDispatcher] MetadataService not available or missing getRegisteredTypes, falling back to protocol service");
1112
+ }
1068
1113
  const protocol = await this.resolveService("protocol");
1069
1114
  if (protocol && typeof protocol.getMetaTypes === "function") {
1070
1115
  const result = await protocol.getMetaTypes({});
1116
+ console.log("[HttpDispatcher] Protocol service returned types:", result);
1071
1117
  return { handled: true, response: this.success(result) };
1072
1118
  }
1073
1119
  if (broker) {
1074
1120
  try {
1075
1121
  const data = await broker.call("metadata.types", {}, { request: context.request });
1122
+ console.log("[HttpDispatcher] Broker returned types:", data);
1076
1123
  return { handled: true, response: this.success(data) };
1077
- } catch {
1124
+ } catch (e) {
1125
+ console.log("[HttpDispatcher] Broker call failed:", e);
1078
1126
  }
1079
1127
  }
1128
+ console.warn("[HttpDispatcher] Falling back to hardcoded defaults for metadata types");
1080
1129
  return { handled: true, response: this.success({ types: ["object", "app", "plugin"] }) };
1081
1130
  }
1082
1131
  if (parts.length === 3 && parts[2] === "published" && (!method || method === "GET")) {
@@ -1133,7 +1182,7 @@ var HttpDispatcher = class {
1133
1182
  }
1134
1183
  return { handled: true, response: this.error("Not found", 404) };
1135
1184
  }
1136
- const singularType = type.endsWith("s") ? type.slice(0, -1) : type;
1185
+ const singularType = pluralToSingular(type);
1137
1186
  const protocol = await this.resolveService("protocol");
1138
1187
  if (protocol && typeof protocol.getMetaItem === "function") {
1139
1188
  try {
@@ -1165,6 +1214,18 @@ var HttpDispatcher = class {
1165
1214
  } catch {
1166
1215
  }
1167
1216
  }
1217
+ const metadataService = await this.getService(CoreServiceName.enum.metadata);
1218
+ if (metadataService && typeof metadataService.list === "function") {
1219
+ try {
1220
+ const items = await metadataService.list(typeOrName);
1221
+ if (items && items.length > 0) {
1222
+ return { handled: true, response: this.success({ type: typeOrName, items }) };
1223
+ }
1224
+ } catch (e) {
1225
+ const sanitizedType = String(typeOrName).replace(/[\r\n\t]/g, "");
1226
+ console.debug(`[HttpDispatcher] MetadataService.list() failed for type:`, sanitizedType, "error:", e.message);
1227
+ }
1228
+ }
1168
1229
  if (broker) {
1169
1230
  try {
1170
1231
  if (typeOrName === "objects") {
@@ -1931,6 +1992,64 @@ var HttpDispatcher = class {
1931
1992
  };
1932
1993
 
1933
1994
  // src/dispatcher-plugin.ts
1995
+ function mountRouteOnServer(route, server, routePath) {
1996
+ const handler = async (req, res) => {
1997
+ try {
1998
+ const result = await route.handler({
1999
+ body: req.body,
2000
+ params: req.params,
2001
+ query: req.query
2002
+ });
2003
+ if (result.stream && result.events) {
2004
+ res.status(result.status);
2005
+ if (result.headers) {
2006
+ for (const [k, v] of Object.entries(result.headers)) {
2007
+ res.header(k, String(v));
2008
+ }
2009
+ } else {
2010
+ res.header("Content-Type", "text/event-stream");
2011
+ res.header("Cache-Control", "no-cache");
2012
+ res.header("Connection", "keep-alive");
2013
+ }
2014
+ if (typeof res.write === "function" && typeof res.end === "function") {
2015
+ for await (const event of result.events) {
2016
+ res.write(typeof event === "string" ? event : `data: ${JSON.stringify(event)}
2017
+
2018
+ `);
2019
+ }
2020
+ res.end();
2021
+ } else {
2022
+ const events = [];
2023
+ for await (const event of result.events) {
2024
+ events.push(event);
2025
+ }
2026
+ res.json({ events });
2027
+ }
2028
+ } else {
2029
+ res.status(result.status);
2030
+ if (result.body !== void 0) {
2031
+ res.json(result.body);
2032
+ } else {
2033
+ res.end();
2034
+ }
2035
+ }
2036
+ } catch (err) {
2037
+ errorResponse(err, res);
2038
+ }
2039
+ };
2040
+ const m = route.method.toLowerCase();
2041
+ if (m === "get" && typeof server.get === "function") {
2042
+ server.get(routePath, handler);
2043
+ return true;
2044
+ } else if (m === "post" && typeof server.post === "function") {
2045
+ server.post(routePath, handler);
2046
+ return true;
2047
+ } else if (m === "delete" && typeof server.delete === "function") {
2048
+ server.delete(routePath, handler);
2049
+ return true;
2050
+ }
2051
+ return false;
2052
+ }
1934
2053
  function sendResult(result, res) {
1935
2054
  if (result.handled) {
1936
2055
  if (result.response) {
@@ -2225,61 +2344,23 @@ function createDispatcherPlugin(config = {}) {
2225
2344
  if (!server) return;
2226
2345
  for (const route of routes) {
2227
2346
  const routePath = route.path.startsWith("/api/v1") ? route.path : `${prefix}${route.path}`;
2228
- const handler = async (req, res) => {
2229
- try {
2230
- const result = await route.handler({
2231
- body: req.body,
2232
- params: req.params,
2233
- query: req.query
2234
- });
2235
- if (result.stream && result.events) {
2236
- res.status(result.status);
2237
- if (result.headers) {
2238
- for (const [k, v] of Object.entries(result.headers)) {
2239
- res.header(k, v);
2240
- }
2241
- } else {
2242
- res.header("Content-Type", "text/event-stream");
2243
- res.header("Cache-Control", "no-cache");
2244
- res.header("Connection", "keep-alive");
2245
- }
2246
- if (typeof res.write === "function" && typeof res.end === "function") {
2247
- for await (const event of result.events) {
2248
- res.write(typeof event === "string" ? event : `data: ${JSON.stringify(event)}
2249
-
2250
- `);
2251
- }
2252
- res.end();
2253
- } else {
2254
- const events = [];
2255
- for await (const event of result.events) {
2256
- events.push(event);
2257
- }
2258
- res.json({ events });
2259
- }
2260
- } else {
2261
- res.status(result.status);
2262
- if (result.body !== void 0) {
2263
- res.json(result.body);
2264
- } else {
2265
- res.end();
2266
- }
2267
- }
2268
- } catch (err) {
2269
- errorResponse(err, res);
2270
- }
2271
- };
2272
- const m = route.method.toLowerCase();
2273
- if (m === "get" && typeof server.get === "function") {
2274
- server.get(routePath, handler);
2275
- } else if (m === "post" && typeof server.post === "function") {
2276
- server.post(routePath, handler);
2277
- } else if (m === "delete" && typeof server.delete === "function") {
2278
- server.delete(routePath, handler);
2279
- }
2347
+ mountRouteOnServer(route, server, routePath);
2280
2348
  }
2281
2349
  ctx.logger.info(`[Dispatcher] Registered ${routes.length} AI routes`);
2282
2350
  });
2351
+ const cachedRoutes = kernel.__aiRoutes;
2352
+ if (cachedRoutes && Array.isArray(cachedRoutes) && cachedRoutes.length > 0) {
2353
+ let registered = 0;
2354
+ for (const route of cachedRoutes) {
2355
+ const routePath = route.path.startsWith("/api/v1") ? route.path : `${prefix}${route.path}`;
2356
+ if (mountRouteOnServer(route, server, routePath)) {
2357
+ registered++;
2358
+ }
2359
+ }
2360
+ if (registered > 0) {
2361
+ ctx.logger.info(`[Dispatcher] Recovered ${registered} cached AI routes (hook timing fallback)`);
2362
+ }
2363
+ }
2283
2364
  }
2284
2365
  };
2285
2366
  }