@objectstack/runtime 1.0.10 → 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 +18 -0
- package/dist/index.cjs +315 -40
- 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 +315 -40
- 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 +322 -44
- package/src/rest-server.ts +45 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/runtime@1.0.
|
|
2
|
+
> @objectstack/runtime@1.0.12 build /home/runner/work/spec/spec/packages/runtime
|
|
3
3
|
> tsup --config ../../tsup.config.ts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
14
|
-
[32mESM[39m [1mdist/index.js.map [22m[
|
|
15
|
-
[32mESM[39m ⚡️ Build success in
|
|
16
|
-
[32mCJS[39m [1mdist/index.cjs [22m[
|
|
17
|
-
[32mCJS[39m [1mdist/index.cjs.map [22m[
|
|
18
|
-
[32mCJS[39m ⚡️ Build success in
|
|
13
|
+
[32mESM[39m [1mdist/index.js [22m[32m60.52 KB[39m
|
|
14
|
+
[32mESM[39m [1mdist/index.js.map [22m[32m140.22 KB[39m
|
|
15
|
+
[32mESM[39m ⚡️ Build success in 134ms
|
|
16
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m62.25 KB[39m
|
|
17
|
+
[32mCJS[39m [1mdist/index.cjs.map [22m[32m140.32 KB[39m
|
|
18
|
+
[32mCJS[39m ⚡️ Build success in 138ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
21
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.cts [22m[
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 8283ms
|
|
21
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m19.53 KB[39m
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.cts [22m[32m19.53 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
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
|
+
|
|
13
|
+
## 1.0.11
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- @objectstack/spec@1.0.11
|
|
18
|
+
- @objectstack/core@1.0.11
|
|
19
|
+
- @objectstack/types@1.0.11
|
|
20
|
+
|
|
3
21
|
## 1.0.10
|
|
4
22
|
|
|
5
23
|
### 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
|
|
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
|
-
|
|
885
|
-
|
|
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
|
-
|
|
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(
|
|
941
|
+
await ql.insert(objectFQN, record);
|
|
891
942
|
} catch (err) {
|
|
892
|
-
ctx.logger.warn(`[Seeder] Failed to insert ${
|
|
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
|
-
|
|
1059
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
1159
|
-
return { handled: true, response: this.success(
|
|
1246
|
+
const data2 = await broker.call("metadata.objects", { packageId }, { request: context.request });
|
|
1247
|
+
return { handled: true, response: this.success(data2) };
|
|
1160
1248
|
}
|
|
1161
|
-
const
|
|
1162
|
-
|
|
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
|
|
1169
|
-
|
|
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
|
|
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
|
|
1198
|
-
return { handled: true, response: this.success(
|
|
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
|
|
1203
|
-
return { handled: true, response: this.success(
|
|
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(
|
|
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
|
|
1317
|
+
return { handled: true, response: this.success(result) };
|
|
1214
1318
|
}
|
|
1215
1319
|
if (m === "POST") {
|
|
1216
|
-
const
|
|
1217
|
-
const res = this.success(
|
|
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
|
-
if (cleanPath.startsWith("/
|
|
1390
|
-
return this.handleMetadata(cleanPath.substring(
|
|
1658
|
+
if (cleanPath.startsWith("/meta")) {
|
|
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 {
|