@objectstack/runtime 1.0.11 → 1.0.12
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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +10 -0
- package/dist/index.cjs +314 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -1
- package/dist/index.d.ts +49 -1
- package/dist/index.js +314 -39
- package/dist/index.js.map +1 -1
- package/package.json +11 -4
- package/src/app-plugin.ts +29 -6
- package/src/http-dispatcher.root.test.ts +58 -0
- package/src/http-dispatcher.ts +320 -42
- package/src/rest-server.ts +45 -1
package/dist/index.js
CHANGED
|
@@ -237,6 +237,7 @@ var RestServer = class {
|
|
|
237
237
|
apiPath: api.apiPath,
|
|
238
238
|
enableCrud: api.enableCrud ?? true,
|
|
239
239
|
enableMetadata: api.enableMetadata ?? true,
|
|
240
|
+
enableUi: api.enableUi ?? true,
|
|
240
241
|
enableBatch: api.enableBatch ?? true,
|
|
241
242
|
enableDiscovery: api.enableDiscovery ?? true,
|
|
242
243
|
documentation: api.documentation,
|
|
@@ -302,6 +303,9 @@ var RestServer = class {
|
|
|
302
303
|
if (this.config.api.enableMetadata) {
|
|
303
304
|
this.registerMetadataEndpoints(basePath);
|
|
304
305
|
}
|
|
306
|
+
if (this.config.api.enableUi) {
|
|
307
|
+
this.registerUiEndpoints(basePath);
|
|
308
|
+
}
|
|
305
309
|
if (this.config.api.enableCrud) {
|
|
306
310
|
this.registerCrudEndpoints(basePath);
|
|
307
311
|
}
|
|
@@ -327,6 +331,9 @@ var RestServer = class {
|
|
|
327
331
|
if (this.config.api.enableMetadata) {
|
|
328
332
|
discovery.endpoints.metadata = `${basePath}${this.config.metadata.prefix}`;
|
|
329
333
|
}
|
|
334
|
+
if (this.config.api.enableUi) {
|
|
335
|
+
discovery.endpoints.ui = `${basePath}/ui`;
|
|
336
|
+
}
|
|
330
337
|
if (discovery.endpoints.auth) {
|
|
331
338
|
discovery.endpoints.auth = `${basePath}/auth`;
|
|
332
339
|
}
|
|
@@ -372,7 +379,8 @@ var RestServer = class {
|
|
|
372
379
|
path: `${metaPath}/:type`,
|
|
373
380
|
handler: async (req, res) => {
|
|
374
381
|
try {
|
|
375
|
-
const
|
|
382
|
+
const packageId = req.query?.package || void 0;
|
|
383
|
+
const items = await this.protocol.getMetaItems({ type: req.params.type, packageId });
|
|
376
384
|
res.json(items);
|
|
377
385
|
} catch (error) {
|
|
378
386
|
res.status(404).json({ error: error.message });
|
|
@@ -456,6 +464,35 @@ var RestServer = class {
|
|
|
456
464
|
}
|
|
457
465
|
});
|
|
458
466
|
}
|
|
467
|
+
/**
|
|
468
|
+
* Register UI endpoints
|
|
469
|
+
*/
|
|
470
|
+
registerUiEndpoints(basePath) {
|
|
471
|
+
const uiPath = `${basePath}/ui`;
|
|
472
|
+
this.routeManager.register({
|
|
473
|
+
method: "GET",
|
|
474
|
+
path: `${uiPath}/view/:object/:type`,
|
|
475
|
+
handler: async (req, res) => {
|
|
476
|
+
try {
|
|
477
|
+
if (this.protocol.getUiView) {
|
|
478
|
+
const view = await this.protocol.getUiView({
|
|
479
|
+
object: req.params.object,
|
|
480
|
+
type: req.params.type
|
|
481
|
+
});
|
|
482
|
+
res.json(view);
|
|
483
|
+
} else {
|
|
484
|
+
res.status(501).json({ error: "UI View resolution not supported by protocol implementation" });
|
|
485
|
+
}
|
|
486
|
+
} catch (error) {
|
|
487
|
+
res.status(404).json({ error: error.message });
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
metadata: {
|
|
491
|
+
summary: "Resolve UI View for object",
|
|
492
|
+
tags: ["ui"]
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
}
|
|
459
496
|
/**
|
|
460
497
|
* Register CRUD endpoints for data operations
|
|
461
498
|
*/
|
|
@@ -844,17 +881,31 @@ var AppPlugin = class {
|
|
|
844
881
|
} else {
|
|
845
882
|
ctx.logger.debug("No runtime.onEnable function found", { appId });
|
|
846
883
|
}
|
|
884
|
+
const seedDatasets = [];
|
|
885
|
+
if (Array.isArray(this.bundle.data)) {
|
|
886
|
+
seedDatasets.push(...this.bundle.data);
|
|
887
|
+
}
|
|
847
888
|
const manifest = this.bundle.manifest || this.bundle;
|
|
848
889
|
if (manifest && Array.isArray(manifest.data)) {
|
|
849
|
-
|
|
850
|
-
|
|
890
|
+
seedDatasets.push(...manifest.data);
|
|
891
|
+
}
|
|
892
|
+
const namespace = (this.bundle.manifest || this.bundle)?.namespace;
|
|
893
|
+
const RESERVED_NS = /* @__PURE__ */ new Set(["base", "system"]);
|
|
894
|
+
const toFQN = (name) => {
|
|
895
|
+
if (name.includes("__") || !namespace || RESERVED_NS.has(namespace)) return name;
|
|
896
|
+
return `${namespace}__${name}`;
|
|
897
|
+
};
|
|
898
|
+
if (seedDatasets.length > 0) {
|
|
899
|
+
ctx.logger.info(`[AppPlugin] Found ${seedDatasets.length} seed datasets for ${appId}`);
|
|
900
|
+
for (const dataset of seedDatasets) {
|
|
851
901
|
if (dataset.object && Array.isArray(dataset.records)) {
|
|
852
|
-
|
|
902
|
+
const objectFQN = toFQN(dataset.object);
|
|
903
|
+
ctx.logger.info(`[Seeder] Seeding ${dataset.records.length} records for ${objectFQN}`);
|
|
853
904
|
for (const record of dataset.records) {
|
|
854
905
|
try {
|
|
855
|
-
await ql.insert(
|
|
906
|
+
await ql.insert(objectFQN, record);
|
|
856
907
|
} catch (err) {
|
|
857
|
-
ctx.logger.warn(`[Seeder] Failed to insert ${
|
|
908
|
+
ctx.logger.warn(`[Seeder] Failed to insert ${objectFQN} record:`, { error: err.message });
|
|
858
909
|
}
|
|
859
910
|
}
|
|
860
911
|
}
|
|
@@ -1015,19 +1066,25 @@ var HttpDispatcher = class {
|
|
|
1015
1066
|
const hasFiles = !!(services[CoreServiceName.enum["file-storage"]] || services["storage"]?.supportsFiles);
|
|
1016
1067
|
const hasAnalytics = !!services[CoreServiceName.enum.analytics];
|
|
1017
1068
|
const hasHub = !!services[CoreServiceName.enum.hub];
|
|
1069
|
+
const routes = {
|
|
1070
|
+
data: `${prefix}/data`,
|
|
1071
|
+
metadata: `${prefix}/meta`,
|
|
1072
|
+
packages: `${prefix}/packages`,
|
|
1073
|
+
auth: `${prefix}/auth`,
|
|
1074
|
+
ui: `${prefix}/ui`,
|
|
1075
|
+
graphql: hasGraphQL ? `${prefix}/graphql` : void 0,
|
|
1076
|
+
storage: hasFiles ? `${prefix}/storage` : void 0,
|
|
1077
|
+
analytics: hasAnalytics ? `${prefix}/analytics` : void 0,
|
|
1078
|
+
hub: hasHub ? `${prefix}/hub` : void 0,
|
|
1079
|
+
automation: `${prefix}/automation`
|
|
1080
|
+
};
|
|
1018
1081
|
return {
|
|
1019
1082
|
name: "ObjectOS",
|
|
1020
1083
|
version: "1.0.0",
|
|
1021
1084
|
environment: getEnv("NODE_ENV", "development"),
|
|
1022
|
-
routes
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
auth: `${prefix}/auth`,
|
|
1026
|
-
graphql: hasGraphQL ? `${prefix}/graphql` : void 0,
|
|
1027
|
-
storage: hasFiles ? `${prefix}/storage` : void 0,
|
|
1028
|
-
analytics: hasAnalytics ? `${prefix}/analytics` : void 0,
|
|
1029
|
-
hub: hasHub ? `${prefix}/hub` : void 0
|
|
1030
|
-
},
|
|
1085
|
+
routes,
|
|
1086
|
+
endpoints: routes,
|
|
1087
|
+
// Alias for backward compatibility with some clients
|
|
1031
1088
|
features: {
|
|
1032
1089
|
graphql: hasGraphQL,
|
|
1033
1090
|
search: hasSearch,
|
|
@@ -1080,11 +1137,21 @@ var HttpDispatcher = class {
|
|
|
1080
1137
|
* Standard: /metadata/:type/:name
|
|
1081
1138
|
* Fallback for backward compat: /metadata (all objects), /metadata/:objectName (get object)
|
|
1082
1139
|
*/
|
|
1083
|
-
async handleMetadata(path, context, method, body) {
|
|
1140
|
+
async handleMetadata(path, context, method, body, query) {
|
|
1084
1141
|
const broker = this.ensureBroker();
|
|
1085
1142
|
const parts = path.replace(/^\/+/, "").split("/").filter(Boolean);
|
|
1086
1143
|
if (parts[0] === "types") {
|
|
1087
|
-
|
|
1144
|
+
const protocol = this.kernel?.context?.getService ? this.kernel.context.getService("protocol") : null;
|
|
1145
|
+
if (protocol && typeof protocol.getMetaTypes === "function") {
|
|
1146
|
+
const result = await protocol.getMetaTypes({});
|
|
1147
|
+
return { handled: true, response: this.success(result) };
|
|
1148
|
+
}
|
|
1149
|
+
try {
|
|
1150
|
+
const data = await broker.call("metadata.types", {}, { request: context.request });
|
|
1151
|
+
return { handled: true, response: this.success(data) };
|
|
1152
|
+
} catch {
|
|
1153
|
+
return { handled: true, response: this.success({ types: ["object", "app", "plugin"] }) };
|
|
1154
|
+
}
|
|
1088
1155
|
}
|
|
1089
1156
|
if (parts.length === 2) {
|
|
1090
1157
|
const [type, name] = parts;
|
|
@@ -1106,11 +1173,21 @@ var HttpDispatcher = class {
|
|
|
1106
1173
|
}
|
|
1107
1174
|
}
|
|
1108
1175
|
try {
|
|
1109
|
-
if (type === "objects") {
|
|
1176
|
+
if (type === "objects" || type === "object") {
|
|
1110
1177
|
const data2 = await broker.call("metadata.getObject", { objectName: name }, { request: context.request });
|
|
1111
1178
|
return { handled: true, response: this.success(data2) };
|
|
1112
1179
|
}
|
|
1113
|
-
const
|
|
1180
|
+
const singularType = type.endsWith("s") ? type.slice(0, -1) : type;
|
|
1181
|
+
const protocol = this.kernel?.context?.getService ? this.kernel.context.getService("protocol") : null;
|
|
1182
|
+
if (protocol && typeof protocol.getMetaItem === "function") {
|
|
1183
|
+
try {
|
|
1184
|
+
const data2 = await protocol.getMetaItem({ type: singularType, name });
|
|
1185
|
+
return { handled: true, response: this.success(data2) };
|
|
1186
|
+
} catch (e) {
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
const method2 = `metadata.get${this.capitalize(singularType)}`;
|
|
1190
|
+
const data = await broker.call(method2, { name }, { request: context.request });
|
|
1114
1191
|
return { handled: true, response: this.success(data) };
|
|
1115
1192
|
} catch (e) {
|
|
1116
1193
|
return { handled: true, response: this.error(e.message, 404) };
|
|
@@ -1118,20 +1195,47 @@ var HttpDispatcher = class {
|
|
|
1118
1195
|
}
|
|
1119
1196
|
if (parts.length === 1) {
|
|
1120
1197
|
const typeOrName = parts[0];
|
|
1121
|
-
|
|
1198
|
+
const packageId = query?.package || void 0;
|
|
1199
|
+
const protocol = this.kernel?.context?.getService ? this.kernel.context.getService("protocol") : null;
|
|
1200
|
+
if (protocol && typeof protocol.getMetaItems === "function") {
|
|
1201
|
+
try {
|
|
1202
|
+
const data = await protocol.getMetaItems({ type: typeOrName, packageId });
|
|
1203
|
+
if (data && (data.items !== void 0 || Array.isArray(data))) {
|
|
1204
|
+
return { handled: true, response: this.success(data) };
|
|
1205
|
+
}
|
|
1206
|
+
} catch {
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
try {
|
|
1122
1210
|
if (typeOrName === "objects") {
|
|
1123
|
-
const
|
|
1124
|
-
return { handled: true, response: this.success(
|
|
1211
|
+
const data2 = await broker.call("metadata.objects", { packageId }, { request: context.request });
|
|
1212
|
+
return { handled: true, response: this.success(data2) };
|
|
1125
1213
|
}
|
|
1126
|
-
const
|
|
1127
|
-
|
|
1214
|
+
const data = await broker.call(`metadata.${typeOrName}`, { packageId }, { request: context.request });
|
|
1215
|
+
if (data !== null && data !== void 0) {
|
|
1216
|
+
return { handled: true, response: this.success(data) };
|
|
1217
|
+
}
|
|
1218
|
+
} catch {
|
|
1219
|
+
}
|
|
1220
|
+
try {
|
|
1221
|
+
const data = await broker.call("metadata.getObject", { objectName: typeOrName }, { request: context.request });
|
|
1222
|
+
return { handled: true, response: this.success(data) };
|
|
1223
|
+
} catch (e) {
|
|
1224
|
+
return { handled: true, response: this.error(e.message, 404) };
|
|
1128
1225
|
}
|
|
1129
|
-
const data = await broker.call("metadata.getObject", { objectName: typeOrName }, { request: context.request });
|
|
1130
|
-
return { handled: true, response: this.success(data) };
|
|
1131
1226
|
}
|
|
1132
1227
|
if (parts.length === 0) {
|
|
1133
|
-
const
|
|
1134
|
-
|
|
1228
|
+
const protocol = this.kernel?.context?.getService ? this.kernel.context.getService("protocol") : null;
|
|
1229
|
+
if (protocol && typeof protocol.getMetaTypes === "function") {
|
|
1230
|
+
const result = await protocol.getMetaTypes({});
|
|
1231
|
+
return { handled: true, response: this.success(result) };
|
|
1232
|
+
}
|
|
1233
|
+
try {
|
|
1234
|
+
const data = await broker.call("metadata.types", {}, { request: context.request });
|
|
1235
|
+
return { handled: true, response: this.success(data) };
|
|
1236
|
+
} catch {
|
|
1237
|
+
return { handled: true, response: this.success({ types: ["object", "app", "plugin"] }) };
|
|
1238
|
+
}
|
|
1135
1239
|
}
|
|
1136
1240
|
return { handled: false };
|
|
1137
1241
|
}
|
|
@@ -1151,7 +1255,7 @@ var HttpDispatcher = class {
|
|
|
1151
1255
|
const action = parts[1];
|
|
1152
1256
|
if (action === "query" && m === "POST") {
|
|
1153
1257
|
const result = await broker.call("data.query", { object: objectName, ...body }, { request: context.request });
|
|
1154
|
-
return { handled: true, response: this.success(result
|
|
1258
|
+
return { handled: true, response: this.success(result) };
|
|
1155
1259
|
}
|
|
1156
1260
|
if (action === "batch" && m === "POST") {
|
|
1157
1261
|
const result = await broker.call("data.batch", { object: objectName, ...body }, { request: context.request });
|
|
@@ -1159,27 +1263,27 @@ var HttpDispatcher = class {
|
|
|
1159
1263
|
}
|
|
1160
1264
|
if (parts.length === 2 && m === "GET") {
|
|
1161
1265
|
const id = parts[1];
|
|
1162
|
-
const
|
|
1163
|
-
return { handled: true, response: this.success(
|
|
1266
|
+
const result = await broker.call("data.get", { object: objectName, id, ...query }, { request: context.request });
|
|
1267
|
+
return { handled: true, response: this.success(result) };
|
|
1164
1268
|
}
|
|
1165
1269
|
if (parts.length === 2 && m === "PATCH") {
|
|
1166
1270
|
const id = parts[1];
|
|
1167
|
-
const
|
|
1168
|
-
return { handled: true, response: this.success(
|
|
1271
|
+
const result = await broker.call("data.update", { object: objectName, id, data: body }, { request: context.request });
|
|
1272
|
+
return { handled: true, response: this.success(result) };
|
|
1169
1273
|
}
|
|
1170
1274
|
if (parts.length === 2 && m === "DELETE") {
|
|
1171
1275
|
const id = parts[1];
|
|
1172
|
-
await broker.call("data.delete", { object: objectName, id }, { request: context.request });
|
|
1173
|
-
return { handled: true, response: this.success(
|
|
1276
|
+
const result = await broker.call("data.delete", { object: objectName, id }, { request: context.request });
|
|
1277
|
+
return { handled: true, response: this.success(result) };
|
|
1174
1278
|
}
|
|
1175
1279
|
} else {
|
|
1176
1280
|
if (m === "GET") {
|
|
1177
1281
|
const result = await broker.call("data.query", { object: objectName, filters: query }, { request: context.request });
|
|
1178
|
-
return { handled: true, response: this.success(result
|
|
1282
|
+
return { handled: true, response: this.success(result) };
|
|
1179
1283
|
}
|
|
1180
1284
|
if (m === "POST") {
|
|
1181
|
-
const
|
|
1182
|
-
const res = this.success(
|
|
1285
|
+
const result = await broker.call("data.create", { object: objectName, data: body }, { request: context.request });
|
|
1286
|
+
const res = this.success(result);
|
|
1183
1287
|
res.status = 201;
|
|
1184
1288
|
return { handled: true, response: res };
|
|
1185
1289
|
}
|
|
@@ -1209,6 +1313,118 @@ var HttpDispatcher = class {
|
|
|
1209
1313
|
}
|
|
1210
1314
|
return { handled: false };
|
|
1211
1315
|
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Handles Package Management requests
|
|
1318
|
+
*
|
|
1319
|
+
* REST Endpoints:
|
|
1320
|
+
* - GET /packages → list all installed packages
|
|
1321
|
+
* - GET /packages/:id → get a specific package
|
|
1322
|
+
* - POST /packages → install a new package
|
|
1323
|
+
* - DELETE /packages/:id → uninstall a package
|
|
1324
|
+
* - PATCH /packages/:id/enable → enable a package
|
|
1325
|
+
* - PATCH /packages/:id/disable → disable a package
|
|
1326
|
+
*
|
|
1327
|
+
* Uses ObjectQL SchemaRegistry directly (via the 'objectql' service)
|
|
1328
|
+
* with broker fallback for backward compatibility.
|
|
1329
|
+
*/
|
|
1330
|
+
async handlePackages(path, method, body, query, context) {
|
|
1331
|
+
const m = method.toUpperCase();
|
|
1332
|
+
const parts = path.replace(/^\/+/, "").split("/").filter(Boolean);
|
|
1333
|
+
const qlService = this.getObjectQLService();
|
|
1334
|
+
const registry = qlService?.registry;
|
|
1335
|
+
if (!registry) {
|
|
1336
|
+
if (this.kernel.broker) {
|
|
1337
|
+
return this.handlePackagesViaBroker(parts, m, body, query, context);
|
|
1338
|
+
}
|
|
1339
|
+
return { handled: true, response: this.error("Package service not available", 503) };
|
|
1340
|
+
}
|
|
1341
|
+
try {
|
|
1342
|
+
if (parts.length === 0 && m === "GET") {
|
|
1343
|
+
let packages = registry.getAllPackages();
|
|
1344
|
+
if (query?.status) {
|
|
1345
|
+
packages = packages.filter((p) => p.status === query.status);
|
|
1346
|
+
}
|
|
1347
|
+
if (query?.type) {
|
|
1348
|
+
packages = packages.filter((p) => p.manifest?.type === query.type);
|
|
1349
|
+
}
|
|
1350
|
+
return { handled: true, response: this.success({ packages, total: packages.length }) };
|
|
1351
|
+
}
|
|
1352
|
+
if (parts.length === 0 && m === "POST") {
|
|
1353
|
+
const pkg = registry.installPackage(body.manifest || body, body.settings);
|
|
1354
|
+
const res = this.success(pkg);
|
|
1355
|
+
res.status = 201;
|
|
1356
|
+
return { handled: true, response: res };
|
|
1357
|
+
}
|
|
1358
|
+
if (parts.length === 2 && parts[1] === "enable" && m === "PATCH") {
|
|
1359
|
+
const id = decodeURIComponent(parts[0]);
|
|
1360
|
+
const pkg = registry.enablePackage(id);
|
|
1361
|
+
if (!pkg) return { handled: true, response: this.error(`Package '${id}' not found`, 404) };
|
|
1362
|
+
return { handled: true, response: this.success(pkg) };
|
|
1363
|
+
}
|
|
1364
|
+
if (parts.length === 2 && parts[1] === "disable" && m === "PATCH") {
|
|
1365
|
+
const id = decodeURIComponent(parts[0]);
|
|
1366
|
+
const pkg = registry.disablePackage(id);
|
|
1367
|
+
if (!pkg) return { handled: true, response: this.error(`Package '${id}' not found`, 404) };
|
|
1368
|
+
return { handled: true, response: this.success(pkg) };
|
|
1369
|
+
}
|
|
1370
|
+
if (parts.length === 1 && m === "GET") {
|
|
1371
|
+
const id = decodeURIComponent(parts[0]);
|
|
1372
|
+
const pkg = registry.getPackage(id);
|
|
1373
|
+
if (!pkg) return { handled: true, response: this.error(`Package '${id}' not found`, 404) };
|
|
1374
|
+
return { handled: true, response: this.success(pkg) };
|
|
1375
|
+
}
|
|
1376
|
+
if (parts.length === 1 && m === "DELETE") {
|
|
1377
|
+
const id = decodeURIComponent(parts[0]);
|
|
1378
|
+
const success = registry.uninstallPackage(id);
|
|
1379
|
+
if (!success) return { handled: true, response: this.error(`Package '${id}' not found`, 404) };
|
|
1380
|
+
return { handled: true, response: this.success({ success: true }) };
|
|
1381
|
+
}
|
|
1382
|
+
} catch (e) {
|
|
1383
|
+
return { handled: true, response: this.error(e.message, e.statusCode || 500) };
|
|
1384
|
+
}
|
|
1385
|
+
return { handled: false };
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Fallback: handle packages via broker (for backward compatibility)
|
|
1389
|
+
*/
|
|
1390
|
+
async handlePackagesViaBroker(parts, m, body, query, context) {
|
|
1391
|
+
const broker = this.kernel.broker;
|
|
1392
|
+
try {
|
|
1393
|
+
if (parts.length === 0 && m === "GET") {
|
|
1394
|
+
const result = await broker.call("package.list", query || {}, { request: context.request });
|
|
1395
|
+
return { handled: true, response: this.success(result) };
|
|
1396
|
+
}
|
|
1397
|
+
if (parts.length === 0 && m === "POST") {
|
|
1398
|
+
const result = await broker.call("package.install", body, { request: context.request });
|
|
1399
|
+
const res = this.success(result);
|
|
1400
|
+
res.status = 201;
|
|
1401
|
+
return { handled: true, response: res };
|
|
1402
|
+
}
|
|
1403
|
+
if (parts.length === 2 && parts[1] === "enable" && m === "PATCH") {
|
|
1404
|
+
const id = decodeURIComponent(parts[0]);
|
|
1405
|
+
const result = await broker.call("package.enable", { id }, { request: context.request });
|
|
1406
|
+
return { handled: true, response: this.success(result) };
|
|
1407
|
+
}
|
|
1408
|
+
if (parts.length === 2 && parts[1] === "disable" && m === "PATCH") {
|
|
1409
|
+
const id = decodeURIComponent(parts[0]);
|
|
1410
|
+
const result = await broker.call("package.disable", { id }, { request: context.request });
|
|
1411
|
+
return { handled: true, response: this.success(result) };
|
|
1412
|
+
}
|
|
1413
|
+
if (parts.length === 1 && m === "GET") {
|
|
1414
|
+
const id = decodeURIComponent(parts[0]);
|
|
1415
|
+
const result = await broker.call("package.get", { id }, { request: context.request });
|
|
1416
|
+
return { handled: true, response: this.success(result) };
|
|
1417
|
+
}
|
|
1418
|
+
if (parts.length === 1 && m === "DELETE") {
|
|
1419
|
+
const id = decodeURIComponent(parts[0]);
|
|
1420
|
+
const result = await broker.call("package.uninstall", { id }, { request: context.request });
|
|
1421
|
+
return { handled: true, response: this.success(result) };
|
|
1422
|
+
}
|
|
1423
|
+
} catch (e) {
|
|
1424
|
+
return { handled: true, response: this.error(e.message, e.statusCode || 500) };
|
|
1425
|
+
}
|
|
1426
|
+
return { handled: false };
|
|
1427
|
+
}
|
|
1212
1428
|
/**
|
|
1213
1429
|
* Handles Hub requests
|
|
1214
1430
|
* path: sub-path after /hub/
|
|
@@ -1308,6 +1524,29 @@ var HttpDispatcher = class {
|
|
|
1308
1524
|
}
|
|
1309
1525
|
return { handled: false };
|
|
1310
1526
|
}
|
|
1527
|
+
/**
|
|
1528
|
+
* Handles UI requests
|
|
1529
|
+
* path: sub-path after /ui/
|
|
1530
|
+
*/
|
|
1531
|
+
async handleUi(path, query, _context) {
|
|
1532
|
+
const parts = path.replace(/^\/+/, "").split("/").filter(Boolean);
|
|
1533
|
+
if (parts[0] === "view" && parts[1]) {
|
|
1534
|
+
const objectName = parts[1];
|
|
1535
|
+
const type = parts[2] || query?.type || "list";
|
|
1536
|
+
const protocol = this.kernel?.context?.getService ? this.kernel.context.getService("protocol") : null;
|
|
1537
|
+
if (protocol && typeof protocol.getUiView === "function") {
|
|
1538
|
+
try {
|
|
1539
|
+
const result = await protocol.getUiView({ object: objectName, type });
|
|
1540
|
+
return { handled: true, response: this.success(result) };
|
|
1541
|
+
} catch (e) {
|
|
1542
|
+
return { handled: true, response: this.error(e.message, 500) };
|
|
1543
|
+
}
|
|
1544
|
+
} else {
|
|
1545
|
+
return { handled: true, response: this.error("Protocol service not available", 503) };
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
return { handled: false };
|
|
1549
|
+
}
|
|
1311
1550
|
/**
|
|
1312
1551
|
* Handles Automation requests
|
|
1313
1552
|
* path: sub-path after /automation/
|
|
@@ -1339,6 +1578,29 @@ var HttpDispatcher = class {
|
|
|
1339
1578
|
const services = this.getServicesMap();
|
|
1340
1579
|
return services[name];
|
|
1341
1580
|
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Get the ObjectQL service which provides access to SchemaRegistry.
|
|
1583
|
+
* Tries multiple access patterns since kernel structure varies.
|
|
1584
|
+
*/
|
|
1585
|
+
getObjectQLService() {
|
|
1586
|
+
if (typeof this.kernel.getService === "function") {
|
|
1587
|
+
try {
|
|
1588
|
+
const svc = this.kernel.getService("objectql");
|
|
1589
|
+
if (svc?.registry) return svc;
|
|
1590
|
+
} catch {
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
if (this.kernel?.context?.getService) {
|
|
1594
|
+
try {
|
|
1595
|
+
const svc = this.kernel.context.getService("objectql");
|
|
1596
|
+
if (svc?.registry) return svc;
|
|
1597
|
+
} catch {
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
const services = this.getServicesMap();
|
|
1601
|
+
if (services["objectql"]?.registry) return services["objectql"];
|
|
1602
|
+
return null;
|
|
1603
|
+
}
|
|
1342
1604
|
capitalize(s) {
|
|
1343
1605
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
1344
1606
|
}
|
|
@@ -1348,11 +1610,18 @@ var HttpDispatcher = class {
|
|
|
1348
1610
|
*/
|
|
1349
1611
|
async dispatch(method, path, body, query, context) {
|
|
1350
1612
|
const cleanPath = path.replace(/\/$/, "");
|
|
1613
|
+
if (cleanPath === "" && method === "GET") {
|
|
1614
|
+
const info = this.getDiscoveryInfo("");
|
|
1615
|
+
return {
|
|
1616
|
+
handled: true,
|
|
1617
|
+
response: this.success(info)
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1351
1620
|
if (cleanPath.startsWith("/auth")) {
|
|
1352
1621
|
return this.handleAuth(cleanPath.substring(5), method, body, context);
|
|
1353
1622
|
}
|
|
1354
1623
|
if (cleanPath.startsWith("/meta")) {
|
|
1355
|
-
return this.handleMetadata(cleanPath.substring(5), context);
|
|
1624
|
+
return this.handleMetadata(cleanPath.substring(5), context, method, body, query);
|
|
1356
1625
|
}
|
|
1357
1626
|
if (cleanPath.startsWith("/data")) {
|
|
1358
1627
|
return this.handleData(cleanPath.substring(5), method, body, query, context);
|
|
@@ -1363,6 +1632,9 @@ var HttpDispatcher = class {
|
|
|
1363
1632
|
if (cleanPath.startsWith("/storage")) {
|
|
1364
1633
|
return this.handleStorage(cleanPath.substring(8), method, body, context);
|
|
1365
1634
|
}
|
|
1635
|
+
if (cleanPath.startsWith("/ui")) {
|
|
1636
|
+
return this.handleUi(cleanPath.substring(3), query, context);
|
|
1637
|
+
}
|
|
1366
1638
|
if (cleanPath.startsWith("/automation")) {
|
|
1367
1639
|
return this.handleAutomation(cleanPath.substring(11), method, body, context);
|
|
1368
1640
|
}
|
|
@@ -1372,6 +1644,9 @@ var HttpDispatcher = class {
|
|
|
1372
1644
|
if (cleanPath.startsWith("/hub")) {
|
|
1373
1645
|
return this.handleHub(cleanPath.substring(4), method, body, query, context);
|
|
1374
1646
|
}
|
|
1647
|
+
if (cleanPath.startsWith("/packages")) {
|
|
1648
|
+
return this.handlePackages(cleanPath.substring(9), method, body, query, context);
|
|
1649
|
+
}
|
|
1375
1650
|
if (cleanPath === "/openapi.json" && method === "GET") {
|
|
1376
1651
|
const broker = this.ensureBroker();
|
|
1377
1652
|
try {
|