@objectstack/runtime 1.0.11 → 1.1.0

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @objectstack/runtime@1.0.11 build /home/runner/work/spec/spec/packages/runtime
2
+ > @objectstack/runtime@1.1.0 build /home/runner/work/spec/spec/packages/runtime
3
3
  > tsup --config ../../tsup.config.ts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -10,13 +10,13 @@
10
10
  CLI Cleaning output folder
11
11
  ESM Build start
12
12
  CJS Build start
13
- CJS dist/index.cjs 51.30 KB
14
- CJS dist/index.cjs.map 116.83 KB
15
- CJS ⚡️ Build success in 109ms
16
- ESM dist/index.js 49.57 KB
17
- ESM dist/index.js.map 116.73 KB
18
- ESM ⚡️ Build success in 114ms
13
+ ESM dist/index.js 60.52 KB
14
+ ESM dist/index.js.map 140.22 KB
15
+ ESM ⚡️ Build success in 139ms
16
+ CJS dist/index.cjs 62.25 KB
17
+ CJS dist/index.cjs.map 140.32 KB
18
+ CJS ⚡️ Build success in 141ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 7833ms
21
- DTS dist/index.d.ts 17.82 KB
22
- DTS dist/index.d.cts 17.82 KB
20
+ DTS ⚡️ Build success in 8009ms
21
+ DTS dist/index.d.ts 19.53 KB
22
+ DTS dist/index.d.cts 19.53 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @objectstack/runtime
2
2
 
3
+ ## 1.0.12
4
+
5
+ ### Patch Changes
6
+
7
+ - chore: add Vercel deployment configs, simplify console runtime configuration
8
+ - Updated dependencies
9
+ - @objectstack/spec@1.0.12
10
+ - @objectstack/core@1.0.12
11
+ - @objectstack/types@1.0.12
12
+
3
13
  ## 1.0.11
4
14
 
5
15
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -272,6 +272,7 @@ var RestServer = class {
272
272
  apiPath: api.apiPath,
273
273
  enableCrud: api.enableCrud ?? true,
274
274
  enableMetadata: api.enableMetadata ?? true,
275
+ enableUi: api.enableUi ?? true,
275
276
  enableBatch: api.enableBatch ?? true,
276
277
  enableDiscovery: api.enableDiscovery ?? true,
277
278
  documentation: api.documentation,
@@ -337,6 +338,9 @@ var RestServer = class {
337
338
  if (this.config.api.enableMetadata) {
338
339
  this.registerMetadataEndpoints(basePath);
339
340
  }
341
+ if (this.config.api.enableUi) {
342
+ this.registerUiEndpoints(basePath);
343
+ }
340
344
  if (this.config.api.enableCrud) {
341
345
  this.registerCrudEndpoints(basePath);
342
346
  }
@@ -362,6 +366,9 @@ var RestServer = class {
362
366
  if (this.config.api.enableMetadata) {
363
367
  discovery.endpoints.metadata = `${basePath}${this.config.metadata.prefix}`;
364
368
  }
369
+ if (this.config.api.enableUi) {
370
+ discovery.endpoints.ui = `${basePath}/ui`;
371
+ }
365
372
  if (discovery.endpoints.auth) {
366
373
  discovery.endpoints.auth = `${basePath}/auth`;
367
374
  }
@@ -407,7 +414,8 @@ var RestServer = class {
407
414
  path: `${metaPath}/:type`,
408
415
  handler: async (req, res) => {
409
416
  try {
410
- const items = await this.protocol.getMetaItems({ type: req.params.type });
417
+ const packageId = req.query?.package || void 0;
418
+ const items = await this.protocol.getMetaItems({ type: req.params.type, packageId });
411
419
  res.json(items);
412
420
  } catch (error) {
413
421
  res.status(404).json({ error: error.message });
@@ -491,6 +499,35 @@ var RestServer = class {
491
499
  }
492
500
  });
493
501
  }
502
+ /**
503
+ * Register UI endpoints
504
+ */
505
+ registerUiEndpoints(basePath) {
506
+ const uiPath = `${basePath}/ui`;
507
+ this.routeManager.register({
508
+ method: "GET",
509
+ path: `${uiPath}/view/:object/:type`,
510
+ handler: async (req, res) => {
511
+ try {
512
+ if (this.protocol.getUiView) {
513
+ const view = await this.protocol.getUiView({
514
+ object: req.params.object,
515
+ type: req.params.type
516
+ });
517
+ res.json(view);
518
+ } else {
519
+ res.status(501).json({ error: "UI View resolution not supported by protocol implementation" });
520
+ }
521
+ } catch (error) {
522
+ res.status(404).json({ error: error.message });
523
+ }
524
+ },
525
+ metadata: {
526
+ summary: "Resolve UI View for object",
527
+ tags: ["ui"]
528
+ }
529
+ });
530
+ }
494
531
  /**
495
532
  * Register CRUD endpoints for data operations
496
533
  */
@@ -879,17 +916,31 @@ var AppPlugin = class {
879
916
  } else {
880
917
  ctx.logger.debug("No runtime.onEnable function found", { appId });
881
918
  }
919
+ const seedDatasets = [];
920
+ if (Array.isArray(this.bundle.data)) {
921
+ seedDatasets.push(...this.bundle.data);
922
+ }
882
923
  const manifest = this.bundle.manifest || this.bundle;
883
924
  if (manifest && Array.isArray(manifest.data)) {
884
- ctx.logger.info(`[AppPlugin] Found initial data for ${appId}`, { count: manifest.data.length });
885
- for (const dataset of manifest.data) {
925
+ seedDatasets.push(...manifest.data);
926
+ }
927
+ const namespace = (this.bundle.manifest || this.bundle)?.namespace;
928
+ const RESERVED_NS = /* @__PURE__ */ new Set(["base", "system"]);
929
+ const toFQN = (name) => {
930
+ if (name.includes("__") || !namespace || RESERVED_NS.has(namespace)) return name;
931
+ return `${namespace}__${name}`;
932
+ };
933
+ if (seedDatasets.length > 0) {
934
+ ctx.logger.info(`[AppPlugin] Found ${seedDatasets.length} seed datasets for ${appId}`);
935
+ for (const dataset of seedDatasets) {
886
936
  if (dataset.object && Array.isArray(dataset.records)) {
887
- ctx.logger.info(`[Seeder] Seeding ${dataset.records.length} records for ${dataset.object}`);
937
+ const objectFQN = toFQN(dataset.object);
938
+ ctx.logger.info(`[Seeder] Seeding ${dataset.records.length} records for ${objectFQN}`);
888
939
  for (const record of dataset.records) {
889
940
  try {
890
- await ql.insert(dataset.object, record);
941
+ await ql.insert(objectFQN, record);
891
942
  } catch (err) {
892
- ctx.logger.warn(`[Seeder] Failed to insert ${dataset.object} record:`, { error: err.message });
943
+ ctx.logger.warn(`[Seeder] Failed to insert ${objectFQN} record:`, { error: err.message });
893
944
  }
894
945
  }
895
946
  }
@@ -1050,19 +1101,25 @@ var HttpDispatcher = class {
1050
1101
  const hasFiles = !!(services[import_system.CoreServiceName.enum["file-storage"]] || services["storage"]?.supportsFiles);
1051
1102
  const hasAnalytics = !!services[import_system.CoreServiceName.enum.analytics];
1052
1103
  const hasHub = !!services[import_system.CoreServiceName.enum.hub];
1104
+ const routes = {
1105
+ data: `${prefix}/data`,
1106
+ metadata: `${prefix}/meta`,
1107
+ packages: `${prefix}/packages`,
1108
+ auth: `${prefix}/auth`,
1109
+ ui: `${prefix}/ui`,
1110
+ graphql: hasGraphQL ? `${prefix}/graphql` : void 0,
1111
+ storage: hasFiles ? `${prefix}/storage` : void 0,
1112
+ analytics: hasAnalytics ? `${prefix}/analytics` : void 0,
1113
+ hub: hasHub ? `${prefix}/hub` : void 0,
1114
+ automation: `${prefix}/automation`
1115
+ };
1053
1116
  return {
1054
1117
  name: "ObjectOS",
1055
1118
  version: "1.0.0",
1056
1119
  environment: (0, import_core2.getEnv)("NODE_ENV", "development"),
1057
- routes: {
1058
- data: `${prefix}/data`,
1059
- metadata: `${prefix}/meta`,
1060
- auth: `${prefix}/auth`,
1061
- graphql: hasGraphQL ? `${prefix}/graphql` : void 0,
1062
- storage: hasFiles ? `${prefix}/storage` : void 0,
1063
- analytics: hasAnalytics ? `${prefix}/analytics` : void 0,
1064
- hub: hasHub ? `${prefix}/hub` : void 0
1065
- },
1120
+ routes,
1121
+ endpoints: routes,
1122
+ // Alias for backward compatibility with some clients
1066
1123
  features: {
1067
1124
  graphql: hasGraphQL,
1068
1125
  search: hasSearch,
@@ -1115,11 +1172,21 @@ var HttpDispatcher = class {
1115
1172
  * Standard: /metadata/:type/:name
1116
1173
  * Fallback for backward compat: /metadata (all objects), /metadata/:objectName (get object)
1117
1174
  */
1118
- async handleMetadata(path, context, method, body) {
1175
+ async handleMetadata(path, context, method, body, query) {
1119
1176
  const broker = this.ensureBroker();
1120
1177
  const parts = path.replace(/^\/+/, "").split("/").filter(Boolean);
1121
1178
  if (parts[0] === "types") {
1122
- return { handled: true, response: this.success({ types: ["objects", "apps", "plugins"] }) };
1179
+ const protocol = this.kernel?.context?.getService ? this.kernel.context.getService("protocol") : null;
1180
+ if (protocol && typeof protocol.getMetaTypes === "function") {
1181
+ const result = await protocol.getMetaTypes({});
1182
+ return { handled: true, response: this.success(result) };
1183
+ }
1184
+ try {
1185
+ const data = await broker.call("metadata.types", {}, { request: context.request });
1186
+ return { handled: true, response: this.success(data) };
1187
+ } catch {
1188
+ return { handled: true, response: this.success({ types: ["object", "app", "plugin"] }) };
1189
+ }
1123
1190
  }
1124
1191
  if (parts.length === 2) {
1125
1192
  const [type, name] = parts;
@@ -1141,11 +1208,21 @@ var HttpDispatcher = class {
1141
1208
  }
1142
1209
  }
1143
1210
  try {
1144
- if (type === "objects") {
1211
+ if (type === "objects" || type === "object") {
1145
1212
  const data2 = await broker.call("metadata.getObject", { objectName: name }, { request: context.request });
1146
1213
  return { handled: true, response: this.success(data2) };
1147
1214
  }
1148
- const data = await broker.call(`metadata.get${this.capitalize(type.slice(0, -1))}`, { name }, { request: context.request });
1215
+ const singularType = type.endsWith("s") ? type.slice(0, -1) : type;
1216
+ const protocol = this.kernel?.context?.getService ? this.kernel.context.getService("protocol") : null;
1217
+ if (protocol && typeof protocol.getMetaItem === "function") {
1218
+ try {
1219
+ const data2 = await protocol.getMetaItem({ type: singularType, name });
1220
+ return { handled: true, response: this.success(data2) };
1221
+ } catch (e) {
1222
+ }
1223
+ }
1224
+ const method2 = `metadata.get${this.capitalize(singularType)}`;
1225
+ const data = await broker.call(method2, { name }, { request: context.request });
1149
1226
  return { handled: true, response: this.success(data) };
1150
1227
  } catch (e) {
1151
1228
  return { handled: true, response: this.error(e.message, 404) };
@@ -1153,20 +1230,47 @@ var HttpDispatcher = class {
1153
1230
  }
1154
1231
  if (parts.length === 1) {
1155
1232
  const typeOrName = parts[0];
1156
- if (["objects", "apps", "plugins"].includes(typeOrName)) {
1233
+ const packageId = query?.package || void 0;
1234
+ const protocol = this.kernel?.context?.getService ? this.kernel.context.getService("protocol") : null;
1235
+ if (protocol && typeof protocol.getMetaItems === "function") {
1236
+ try {
1237
+ const data = await protocol.getMetaItems({ type: typeOrName, packageId });
1238
+ if (data && (data.items !== void 0 || Array.isArray(data))) {
1239
+ return { handled: true, response: this.success(data) };
1240
+ }
1241
+ } catch {
1242
+ }
1243
+ }
1244
+ try {
1157
1245
  if (typeOrName === "objects") {
1158
- const data3 = await broker.call("metadata.objects", {}, { request: context.request });
1159
- return { handled: true, response: this.success(data3) };
1246
+ const data2 = await broker.call("metadata.objects", { packageId }, { request: context.request });
1247
+ return { handled: true, response: this.success(data2) };
1160
1248
  }
1161
- const data2 = await broker.call(`metadata.${typeOrName}`, {}, { request: context.request });
1162
- return { handled: true, response: this.success(data2) };
1249
+ const data = await broker.call(`metadata.${typeOrName}`, { packageId }, { request: context.request });
1250
+ if (data !== null && data !== void 0) {
1251
+ return { handled: true, response: this.success(data) };
1252
+ }
1253
+ } catch {
1254
+ }
1255
+ try {
1256
+ const data = await broker.call("metadata.getObject", { objectName: typeOrName }, { request: context.request });
1257
+ return { handled: true, response: this.success(data) };
1258
+ } catch (e) {
1259
+ return { handled: true, response: this.error(e.message, 404) };
1163
1260
  }
1164
- const data = await broker.call("metadata.getObject", { objectName: typeOrName }, { request: context.request });
1165
- return { handled: true, response: this.success(data) };
1166
1261
  }
1167
1262
  if (parts.length === 0) {
1168
- const data = await broker.call("metadata.objects", {}, { request: context.request });
1169
- return { handled: true, response: this.success(data) };
1263
+ const protocol = this.kernel?.context?.getService ? this.kernel.context.getService("protocol") : null;
1264
+ if (protocol && typeof protocol.getMetaTypes === "function") {
1265
+ const result = await protocol.getMetaTypes({});
1266
+ return { handled: true, response: this.success(result) };
1267
+ }
1268
+ try {
1269
+ const data = await broker.call("metadata.types", {}, { request: context.request });
1270
+ return { handled: true, response: this.success(data) };
1271
+ } catch {
1272
+ return { handled: true, response: this.success({ types: ["object", "app", "plugin"] }) };
1273
+ }
1170
1274
  }
1171
1275
  return { handled: false };
1172
1276
  }
@@ -1186,7 +1290,7 @@ var HttpDispatcher = class {
1186
1290
  const action = parts[1];
1187
1291
  if (action === "query" && m === "POST") {
1188
1292
  const result = await broker.call("data.query", { object: objectName, ...body }, { request: context.request });
1189
- return { handled: true, response: this.success(result.data, { count: result.count, limit: body.limit, skip: body.skip }) };
1293
+ return { handled: true, response: this.success(result) };
1190
1294
  }
1191
1295
  if (action === "batch" && m === "POST") {
1192
1296
  const result = await broker.call("data.batch", { object: objectName, ...body }, { request: context.request });
@@ -1194,27 +1298,27 @@ var HttpDispatcher = class {
1194
1298
  }
1195
1299
  if (parts.length === 2 && m === "GET") {
1196
1300
  const id = parts[1];
1197
- const data = await broker.call("data.get", { object: objectName, id, ...query }, { request: context.request });
1198
- return { handled: true, response: this.success(data) };
1301
+ const result = await broker.call("data.get", { object: objectName, id, ...query }, { request: context.request });
1302
+ return { handled: true, response: this.success(result) };
1199
1303
  }
1200
1304
  if (parts.length === 2 && m === "PATCH") {
1201
1305
  const id = parts[1];
1202
- const data = await broker.call("data.update", { object: objectName, id, data: body }, { request: context.request });
1203
- return { handled: true, response: this.success(data) };
1306
+ const result = await broker.call("data.update", { object: objectName, id, data: body }, { request: context.request });
1307
+ return { handled: true, response: this.success(result) };
1204
1308
  }
1205
1309
  if (parts.length === 2 && m === "DELETE") {
1206
1310
  const id = parts[1];
1207
- await broker.call("data.delete", { object: objectName, id }, { request: context.request });
1208
- return { handled: true, response: this.success({ id, deleted: true }) };
1311
+ const result = await broker.call("data.delete", { object: objectName, id }, { request: context.request });
1312
+ return { handled: true, response: this.success(result) };
1209
1313
  }
1210
1314
  } else {
1211
1315
  if (m === "GET") {
1212
1316
  const result = await broker.call("data.query", { object: objectName, filters: query }, { request: context.request });
1213
- return { handled: true, response: this.success(result.data, { count: result.count }) };
1317
+ return { handled: true, response: this.success(result) };
1214
1318
  }
1215
1319
  if (m === "POST") {
1216
- const data = await broker.call("data.create", { object: objectName, data: body }, { request: context.request });
1217
- const res = this.success(data);
1320
+ const result = await broker.call("data.create", { object: objectName, data: body }, { request: context.request });
1321
+ const res = this.success(result);
1218
1322
  res.status = 201;
1219
1323
  return { handled: true, response: res };
1220
1324
  }
@@ -1244,6 +1348,118 @@ var HttpDispatcher = class {
1244
1348
  }
1245
1349
  return { handled: false };
1246
1350
  }
1351
+ /**
1352
+ * Handles Package Management requests
1353
+ *
1354
+ * REST Endpoints:
1355
+ * - GET /packages → list all installed packages
1356
+ * - GET /packages/:id → get a specific package
1357
+ * - POST /packages → install a new package
1358
+ * - DELETE /packages/:id → uninstall a package
1359
+ * - PATCH /packages/:id/enable → enable a package
1360
+ * - PATCH /packages/:id/disable → disable a package
1361
+ *
1362
+ * Uses ObjectQL SchemaRegistry directly (via the 'objectql' service)
1363
+ * with broker fallback for backward compatibility.
1364
+ */
1365
+ async handlePackages(path, method, body, query, context) {
1366
+ const m = method.toUpperCase();
1367
+ const parts = path.replace(/^\/+/, "").split("/").filter(Boolean);
1368
+ const qlService = this.getObjectQLService();
1369
+ const registry = qlService?.registry;
1370
+ if (!registry) {
1371
+ if (this.kernel.broker) {
1372
+ return this.handlePackagesViaBroker(parts, m, body, query, context);
1373
+ }
1374
+ return { handled: true, response: this.error("Package service not available", 503) };
1375
+ }
1376
+ try {
1377
+ if (parts.length === 0 && m === "GET") {
1378
+ let packages = registry.getAllPackages();
1379
+ if (query?.status) {
1380
+ packages = packages.filter((p) => p.status === query.status);
1381
+ }
1382
+ if (query?.type) {
1383
+ packages = packages.filter((p) => p.manifest?.type === query.type);
1384
+ }
1385
+ return { handled: true, response: this.success({ packages, total: packages.length }) };
1386
+ }
1387
+ if (parts.length === 0 && m === "POST") {
1388
+ const pkg = registry.installPackage(body.manifest || body, body.settings);
1389
+ const res = this.success(pkg);
1390
+ res.status = 201;
1391
+ return { handled: true, response: res };
1392
+ }
1393
+ if (parts.length === 2 && parts[1] === "enable" && m === "PATCH") {
1394
+ const id = decodeURIComponent(parts[0]);
1395
+ const pkg = registry.enablePackage(id);
1396
+ if (!pkg) return { handled: true, response: this.error(`Package '${id}' not found`, 404) };
1397
+ return { handled: true, response: this.success(pkg) };
1398
+ }
1399
+ if (parts.length === 2 && parts[1] === "disable" && m === "PATCH") {
1400
+ const id = decodeURIComponent(parts[0]);
1401
+ const pkg = registry.disablePackage(id);
1402
+ if (!pkg) return { handled: true, response: this.error(`Package '${id}' not found`, 404) };
1403
+ return { handled: true, response: this.success(pkg) };
1404
+ }
1405
+ if (parts.length === 1 && m === "GET") {
1406
+ const id = decodeURIComponent(parts[0]);
1407
+ const pkg = registry.getPackage(id);
1408
+ if (!pkg) return { handled: true, response: this.error(`Package '${id}' not found`, 404) };
1409
+ return { handled: true, response: this.success(pkg) };
1410
+ }
1411
+ if (parts.length === 1 && m === "DELETE") {
1412
+ const id = decodeURIComponent(parts[0]);
1413
+ const success = registry.uninstallPackage(id);
1414
+ if (!success) return { handled: true, response: this.error(`Package '${id}' not found`, 404) };
1415
+ return { handled: true, response: this.success({ success: true }) };
1416
+ }
1417
+ } catch (e) {
1418
+ return { handled: true, response: this.error(e.message, e.statusCode || 500) };
1419
+ }
1420
+ return { handled: false };
1421
+ }
1422
+ /**
1423
+ * Fallback: handle packages via broker (for backward compatibility)
1424
+ */
1425
+ async handlePackagesViaBroker(parts, m, body, query, context) {
1426
+ const broker = this.kernel.broker;
1427
+ try {
1428
+ if (parts.length === 0 && m === "GET") {
1429
+ const result = await broker.call("package.list", query || {}, { request: context.request });
1430
+ return { handled: true, response: this.success(result) };
1431
+ }
1432
+ if (parts.length === 0 && m === "POST") {
1433
+ const result = await broker.call("package.install", body, { request: context.request });
1434
+ const res = this.success(result);
1435
+ res.status = 201;
1436
+ return { handled: true, response: res };
1437
+ }
1438
+ if (parts.length === 2 && parts[1] === "enable" && m === "PATCH") {
1439
+ const id = decodeURIComponent(parts[0]);
1440
+ const result = await broker.call("package.enable", { id }, { request: context.request });
1441
+ return { handled: true, response: this.success(result) };
1442
+ }
1443
+ if (parts.length === 2 && parts[1] === "disable" && m === "PATCH") {
1444
+ const id = decodeURIComponent(parts[0]);
1445
+ const result = await broker.call("package.disable", { id }, { request: context.request });
1446
+ return { handled: true, response: this.success(result) };
1447
+ }
1448
+ if (parts.length === 1 && m === "GET") {
1449
+ const id = decodeURIComponent(parts[0]);
1450
+ const result = await broker.call("package.get", { id }, { request: context.request });
1451
+ return { handled: true, response: this.success(result) };
1452
+ }
1453
+ if (parts.length === 1 && m === "DELETE") {
1454
+ const id = decodeURIComponent(parts[0]);
1455
+ const result = await broker.call("package.uninstall", { id }, { request: context.request });
1456
+ return { handled: true, response: this.success(result) };
1457
+ }
1458
+ } catch (e) {
1459
+ return { handled: true, response: this.error(e.message, e.statusCode || 500) };
1460
+ }
1461
+ return { handled: false };
1462
+ }
1247
1463
  /**
1248
1464
  * Handles Hub requests
1249
1465
  * path: sub-path after /hub/
@@ -1343,6 +1559,29 @@ var HttpDispatcher = class {
1343
1559
  }
1344
1560
  return { handled: false };
1345
1561
  }
1562
+ /**
1563
+ * Handles UI requests
1564
+ * path: sub-path after /ui/
1565
+ */
1566
+ async handleUi(path, query, _context) {
1567
+ const parts = path.replace(/^\/+/, "").split("/").filter(Boolean);
1568
+ if (parts[0] === "view" && parts[1]) {
1569
+ const objectName = parts[1];
1570
+ const type = parts[2] || query?.type || "list";
1571
+ const protocol = this.kernel?.context?.getService ? this.kernel.context.getService("protocol") : null;
1572
+ if (protocol && typeof protocol.getUiView === "function") {
1573
+ try {
1574
+ const result = await protocol.getUiView({ object: objectName, type });
1575
+ return { handled: true, response: this.success(result) };
1576
+ } catch (e) {
1577
+ return { handled: true, response: this.error(e.message, 500) };
1578
+ }
1579
+ } else {
1580
+ return { handled: true, response: this.error("Protocol service not available", 503) };
1581
+ }
1582
+ }
1583
+ return { handled: false };
1584
+ }
1346
1585
  /**
1347
1586
  * Handles Automation requests
1348
1587
  * path: sub-path after /automation/
@@ -1374,6 +1613,29 @@ var HttpDispatcher = class {
1374
1613
  const services = this.getServicesMap();
1375
1614
  return services[name];
1376
1615
  }
1616
+ /**
1617
+ * Get the ObjectQL service which provides access to SchemaRegistry.
1618
+ * Tries multiple access patterns since kernel structure varies.
1619
+ */
1620
+ getObjectQLService() {
1621
+ if (typeof this.kernel.getService === "function") {
1622
+ try {
1623
+ const svc = this.kernel.getService("objectql");
1624
+ if (svc?.registry) return svc;
1625
+ } catch {
1626
+ }
1627
+ }
1628
+ if (this.kernel?.context?.getService) {
1629
+ try {
1630
+ const svc = this.kernel.context.getService("objectql");
1631
+ if (svc?.registry) return svc;
1632
+ } catch {
1633
+ }
1634
+ }
1635
+ const services = this.getServicesMap();
1636
+ if (services["objectql"]?.registry) return services["objectql"];
1637
+ return null;
1638
+ }
1377
1639
  capitalize(s) {
1378
1640
  return s.charAt(0).toUpperCase() + s.slice(1);
1379
1641
  }
@@ -1383,11 +1645,18 @@ var HttpDispatcher = class {
1383
1645
  */
1384
1646
  async dispatch(method, path, body, query, context) {
1385
1647
  const cleanPath = path.replace(/\/$/, "");
1648
+ if (cleanPath === "" && method === "GET") {
1649
+ const info = this.getDiscoveryInfo("");
1650
+ return {
1651
+ handled: true,
1652
+ response: this.success(info)
1653
+ };
1654
+ }
1386
1655
  if (cleanPath.startsWith("/auth")) {
1387
1656
  return this.handleAuth(cleanPath.substring(5), method, body, context);
1388
1657
  }
1389
1658
  if (cleanPath.startsWith("/meta")) {
1390
- return this.handleMetadata(cleanPath.substring(5), context);
1659
+ return this.handleMetadata(cleanPath.substring(5), context, method, body, query);
1391
1660
  }
1392
1661
  if (cleanPath.startsWith("/data")) {
1393
1662
  return this.handleData(cleanPath.substring(5), method, body, query, context);
@@ -1398,6 +1667,9 @@ var HttpDispatcher = class {
1398
1667
  if (cleanPath.startsWith("/storage")) {
1399
1668
  return this.handleStorage(cleanPath.substring(8), method, body, context);
1400
1669
  }
1670
+ if (cleanPath.startsWith("/ui")) {
1671
+ return this.handleUi(cleanPath.substring(3), query, context);
1672
+ }
1401
1673
  if (cleanPath.startsWith("/automation")) {
1402
1674
  return this.handleAutomation(cleanPath.substring(11), method, body, context);
1403
1675
  }
@@ -1407,6 +1679,9 @@ var HttpDispatcher = class {
1407
1679
  if (cleanPath.startsWith("/hub")) {
1408
1680
  return this.handleHub(cleanPath.substring(4), method, body, query, context);
1409
1681
  }
1682
+ if (cleanPath.startsWith("/packages")) {
1683
+ return this.handlePackages(cleanPath.substring(9), method, body, query, context);
1684
+ }
1410
1685
  if (cleanPath === "/openapi.json" && method === "GET") {
1411
1686
  const broker = this.ensureBroker();
1412
1687
  try {