@objectstack/runtime 11.10.0 → 12.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.
- package/dist/index.cjs +132 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -1
- package/dist/index.d.ts +33 -1
- package/dist/index.js +132 -2
- package/dist/index.js.map +1 -1
- package/package.json +21 -21
package/dist/index.d.cts
CHANGED
|
@@ -33,7 +33,7 @@ interface RuntimeConfig {
|
|
|
33
33
|
* - `ClusterCapabilityConfigInput`: forwarded to `defineCluster()`.
|
|
34
34
|
* - `{ cluster: IClusterService }`: bring your own instance.
|
|
35
35
|
*
|
|
36
|
-
* See `content/docs/
|
|
36
|
+
* See `content/docs/kernel/cluster.mdx` for driver options.
|
|
37
37
|
*/
|
|
38
38
|
cluster?: false | ClusterCapabilityConfigInput | ClusterServicePluginOptions;
|
|
39
39
|
}
|
|
@@ -242,6 +242,27 @@ declare class AppPlugin implements Plugin {
|
|
|
242
242
|
private readonly empty;
|
|
243
243
|
constructor(bundle: any, projectContext?: AppPluginProjectContext);
|
|
244
244
|
init: (ctx: PluginContext) => Promise<void>;
|
|
245
|
+
/**
|
|
246
|
+
* Install the engine's DEFAULT hook body runner (`engine.setDefaultBodyRunner`).
|
|
247
|
+
*
|
|
248
|
+
* Hooks authored at runtime (Studio → `protocol.saveMetaItem` → publish)
|
|
249
|
+
* bind through paths that pass no explicit `bodyRunner` — notably
|
|
250
|
+
* ObjectQLPlugin's metadata-service bind — so without this default their
|
|
251
|
+
* L1/L2 `body` is silently dropped by `bindHooksToEngine` and the hook
|
|
252
|
+
* never runs (#2588). The runtime owns the sandbox bridge (objectql stays
|
|
253
|
+
* sandbox-free), so this is the boot point that wires it: same
|
|
254
|
+
* QuickJS-sandboxed, capability-gated runner the `defineStack({ hooks })`
|
|
255
|
+
* bind already uses.
|
|
256
|
+
*
|
|
257
|
+
* `OS_DISABLE_AUTHORED_HOOKS=1` opts out for deployments that want
|
|
258
|
+
* runtime-authored (DB-stored, non-code-reviewed) hook bodies to stay
|
|
259
|
+
* inert; code-shipped hooks are unaffected (AppPlugin passes its own
|
|
260
|
+
* runner explicitly).
|
|
261
|
+
*
|
|
262
|
+
* Idempotent: the first AppPlugin to run installs it; the runner is
|
|
263
|
+
* bundle-agnostic (it only closes over the engine + logger).
|
|
264
|
+
*/
|
|
265
|
+
private installDefaultHookBodyRunner;
|
|
245
266
|
start: (ctx: PluginContext) => Promise<void>;
|
|
246
267
|
stop: (ctx: PluginContext) => Promise<void>;
|
|
247
268
|
/**
|
|
@@ -965,6 +986,17 @@ declare class HttpDispatcher {
|
|
|
965
986
|
private resolveDefaultProject;
|
|
966
987
|
private success;
|
|
967
988
|
private error;
|
|
989
|
+
/**
|
|
990
|
+
* Build an error response from a THROWN service/protocol error, preserving
|
|
991
|
+
* the error's own HTTP `status` and — critically — any structured `issues`
|
|
992
|
+
* array (e.g. spec-validation `{ path, message, code }[]` from
|
|
993
|
+
* `protocol.saveMetaItem`). The plain `error(msg, code)` path collapses a
|
|
994
|
+
* validation failure to a single message, so the UI can only show a generic
|
|
995
|
+
* banner; carrying `issues` (and the semantic `code`) in `details` lets it
|
|
996
|
+
* map each error back to the offending field. Falls back to `fallbackStatus`
|
|
997
|
+
* and behaves exactly like `error()` for errors that carry neither.
|
|
998
|
+
*/
|
|
999
|
+
private errorFromThrown;
|
|
968
1000
|
/**
|
|
969
1001
|
* ADR-0046: `doc` list responses omit `content` by default — manuals
|
|
970
1002
|
* are the one metadata payload that grows unbounded, and the list
|
package/dist/index.d.ts
CHANGED
|
@@ -33,7 +33,7 @@ interface RuntimeConfig {
|
|
|
33
33
|
* - `ClusterCapabilityConfigInput`: forwarded to `defineCluster()`.
|
|
34
34
|
* - `{ cluster: IClusterService }`: bring your own instance.
|
|
35
35
|
*
|
|
36
|
-
* See `content/docs/
|
|
36
|
+
* See `content/docs/kernel/cluster.mdx` for driver options.
|
|
37
37
|
*/
|
|
38
38
|
cluster?: false | ClusterCapabilityConfigInput | ClusterServicePluginOptions;
|
|
39
39
|
}
|
|
@@ -242,6 +242,27 @@ declare class AppPlugin implements Plugin {
|
|
|
242
242
|
private readonly empty;
|
|
243
243
|
constructor(bundle: any, projectContext?: AppPluginProjectContext);
|
|
244
244
|
init: (ctx: PluginContext) => Promise<void>;
|
|
245
|
+
/**
|
|
246
|
+
* Install the engine's DEFAULT hook body runner (`engine.setDefaultBodyRunner`).
|
|
247
|
+
*
|
|
248
|
+
* Hooks authored at runtime (Studio → `protocol.saveMetaItem` → publish)
|
|
249
|
+
* bind through paths that pass no explicit `bodyRunner` — notably
|
|
250
|
+
* ObjectQLPlugin's metadata-service bind — so without this default their
|
|
251
|
+
* L1/L2 `body` is silently dropped by `bindHooksToEngine` and the hook
|
|
252
|
+
* never runs (#2588). The runtime owns the sandbox bridge (objectql stays
|
|
253
|
+
* sandbox-free), so this is the boot point that wires it: same
|
|
254
|
+
* QuickJS-sandboxed, capability-gated runner the `defineStack({ hooks })`
|
|
255
|
+
* bind already uses.
|
|
256
|
+
*
|
|
257
|
+
* `OS_DISABLE_AUTHORED_HOOKS=1` opts out for deployments that want
|
|
258
|
+
* runtime-authored (DB-stored, non-code-reviewed) hook bodies to stay
|
|
259
|
+
* inert; code-shipped hooks are unaffected (AppPlugin passes its own
|
|
260
|
+
* runner explicitly).
|
|
261
|
+
*
|
|
262
|
+
* Idempotent: the first AppPlugin to run installs it; the runner is
|
|
263
|
+
* bundle-agnostic (it only closes over the engine + logger).
|
|
264
|
+
*/
|
|
265
|
+
private installDefaultHookBodyRunner;
|
|
245
266
|
start: (ctx: PluginContext) => Promise<void>;
|
|
246
267
|
stop: (ctx: PluginContext) => Promise<void>;
|
|
247
268
|
/**
|
|
@@ -965,6 +986,17 @@ declare class HttpDispatcher {
|
|
|
965
986
|
private resolveDefaultProject;
|
|
966
987
|
private success;
|
|
967
988
|
private error;
|
|
989
|
+
/**
|
|
990
|
+
* Build an error response from a THROWN service/protocol error, preserving
|
|
991
|
+
* the error's own HTTP `status` and — critically — any structured `issues`
|
|
992
|
+
* array (e.g. spec-validation `{ path, message, code }[]` from
|
|
993
|
+
* `protocol.saveMetaItem`). The plain `error(msg, code)` path collapses a
|
|
994
|
+
* validation failure to a single message, so the UI can only show a generic
|
|
995
|
+
* banner; carrying `issues` (and the semantic `code`) in `details` lets it
|
|
996
|
+
* map each error back to the offending field. Falls back to `fallbackStatus`
|
|
997
|
+
* and behaves exactly like `error()` for errors that carry neither.
|
|
998
|
+
*/
|
|
999
|
+
private errorFromThrown;
|
|
968
1000
|
/**
|
|
969
1001
|
* ADR-0046: `doc` list responses omit `content` by default — manuals
|
|
970
1002
|
* are the one metadata payload that grows unbounded, and the list
|
package/dist/index.js
CHANGED
|
@@ -892,6 +892,7 @@ var init_app_plugin = __esm({
|
|
|
892
892
|
/** When true, init/start become no-ops — env has no app payload. */
|
|
893
893
|
this.empty = false;
|
|
894
894
|
this.init = async (ctx) => {
|
|
895
|
+
this.installDefaultHookBodyRunner(ctx);
|
|
895
896
|
if (this.empty) {
|
|
896
897
|
ctx.logger.debug("[AppPlugin] empty env \u2014 no app payload, skipping init", {
|
|
897
898
|
pluginName: this.name
|
|
@@ -1414,6 +1415,46 @@ var init_app_plugin = __esm({
|
|
|
1414
1415
|
this.name = `plugin.app.${appId}`;
|
|
1415
1416
|
this.version = sys?.version;
|
|
1416
1417
|
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Install the engine's DEFAULT hook body runner (`engine.setDefaultBodyRunner`).
|
|
1420
|
+
*
|
|
1421
|
+
* Hooks authored at runtime (Studio → `protocol.saveMetaItem` → publish)
|
|
1422
|
+
* bind through paths that pass no explicit `bodyRunner` — notably
|
|
1423
|
+
* ObjectQLPlugin's metadata-service bind — so without this default their
|
|
1424
|
+
* L1/L2 `body` is silently dropped by `bindHooksToEngine` and the hook
|
|
1425
|
+
* never runs (#2588). The runtime owns the sandbox bridge (objectql stays
|
|
1426
|
+
* sandbox-free), so this is the boot point that wires it: same
|
|
1427
|
+
* QuickJS-sandboxed, capability-gated runner the `defineStack({ hooks })`
|
|
1428
|
+
* bind already uses.
|
|
1429
|
+
*
|
|
1430
|
+
* `OS_DISABLE_AUTHORED_HOOKS=1` opts out for deployments that want
|
|
1431
|
+
* runtime-authored (DB-stored, non-code-reviewed) hook bodies to stay
|
|
1432
|
+
* inert; code-shipped hooks are unaffected (AppPlugin passes its own
|
|
1433
|
+
* runner explicitly).
|
|
1434
|
+
*
|
|
1435
|
+
* Idempotent: the first AppPlugin to run installs it; the runner is
|
|
1436
|
+
* bundle-agnostic (it only closes over the engine + logger).
|
|
1437
|
+
*/
|
|
1438
|
+
installDefaultHookBodyRunner(ctx) {
|
|
1439
|
+
if (process.env.OS_DISABLE_AUTHORED_HOOKS === "1") {
|
|
1440
|
+
ctx.logger.info("[AppPlugin] OS_DISABLE_AUTHORED_HOOKS=1 \u2014 runtime-authored hook bodies will not execute");
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
let ql;
|
|
1444
|
+
try {
|
|
1445
|
+
ql = ctx.getService("objectql");
|
|
1446
|
+
} catch {
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
if (!ql || typeof ql.setDefaultBodyRunner !== "function") return;
|
|
1450
|
+
if (ql._defaultBodyRunner) return;
|
|
1451
|
+
ql.setDefaultBodyRunner(hookBodyRunnerFactory(new QuickJSScriptRunner(), {
|
|
1452
|
+
ql,
|
|
1453
|
+
logger: ctx.logger,
|
|
1454
|
+
appId: "runtime-authored"
|
|
1455
|
+
}));
|
|
1456
|
+
ctx.logger.info("[AppPlugin] Installed default hook body runner (runtime-authored hooks can execute)");
|
|
1457
|
+
}
|
|
1417
1458
|
/**
|
|
1418
1459
|
* Emit a kernel hook so the control-plane `AppCatalogService` can
|
|
1419
1460
|
* upsert / delete the corresponding `sys_app` row. Silently no-ops
|
|
@@ -2167,6 +2208,22 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
2167
2208
|
body: { success: false, error: { message, code, details } }
|
|
2168
2209
|
};
|
|
2169
2210
|
}
|
|
2211
|
+
/**
|
|
2212
|
+
* Build an error response from a THROWN service/protocol error, preserving
|
|
2213
|
+
* the error's own HTTP `status` and — critically — any structured `issues`
|
|
2214
|
+
* array (e.g. spec-validation `{ path, message, code }[]` from
|
|
2215
|
+
* `protocol.saveMetaItem`). The plain `error(msg, code)` path collapses a
|
|
2216
|
+
* validation failure to a single message, so the UI can only show a generic
|
|
2217
|
+
* banner; carrying `issues` (and the semantic `code`) in `details` lets it
|
|
2218
|
+
* map each error back to the offending field. Falls back to `fallbackStatus`
|
|
2219
|
+
* and behaves exactly like `error()` for errors that carry neither.
|
|
2220
|
+
*/
|
|
2221
|
+
errorFromThrown(e, fallbackStatus = 500) {
|
|
2222
|
+
const status = typeof e?.status === "number" ? e.status : typeof e?.statusCode === "number" ? e.statusCode : fallbackStatus;
|
|
2223
|
+
const issues = Array.isArray(e?.issues) ? e.issues : void 0;
|
|
2224
|
+
const details = issues || e?.code ? { ...e?.code ? { code: e.code } : {}, ...issues ? { issues } : {} } : void 0;
|
|
2225
|
+
return this.error(e?.message ?? String(e), status, details);
|
|
2226
|
+
}
|
|
2170
2227
|
/**
|
|
2171
2228
|
* ADR-0046: `doc` list responses omit `content` by default — manuals
|
|
2172
2229
|
* are the one metadata payload that grows unbounded, and the list
|
|
@@ -3238,7 +3295,7 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3238
3295
|
const result = await protocol.saveMetaItem({ type, name, item: body, organizationId, ...packageId ? { packageId } : {} });
|
|
3239
3296
|
return { handled: true, response: this.success(result) };
|
|
3240
3297
|
} catch (e) {
|
|
3241
|
-
return { handled: true, response: this.
|
|
3298
|
+
return { handled: true, response: this.errorFromThrown(e, 400) };
|
|
3242
3299
|
}
|
|
3243
3300
|
}
|
|
3244
3301
|
const metaSvc = await this.resolveService("metadata", _context.environmentId);
|
|
@@ -3721,9 +3778,20 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3721
3778
|
} catch (e) {
|
|
3722
3779
|
result.unhideError = e?.message ?? "visibility flip failed";
|
|
3723
3780
|
}
|
|
3781
|
+
try {
|
|
3782
|
+
const changed = [
|
|
3783
|
+
...(result?.published ?? []).map((p) => `${p.type}/${p.name}`),
|
|
3784
|
+
...(result?.unhiddenApps ?? []).map((n) => `app/${n}`)
|
|
3785
|
+
];
|
|
3786
|
+
if (changed.length > 0 && this.kernel?.context?.trigger) {
|
|
3787
|
+
await this.kernel.context.trigger("metadata:reloaded", { changed });
|
|
3788
|
+
}
|
|
3789
|
+
} catch (e) {
|
|
3790
|
+
result.rebindError = e?.message ?? "metadata:reloaded announce failed";
|
|
3791
|
+
}
|
|
3724
3792
|
return { handled: true, response: this.success(result) };
|
|
3725
3793
|
} catch (e) {
|
|
3726
|
-
return { handled: true, response: this.
|
|
3794
|
+
return { handled: true, response: this.errorFromThrown(e, 500) };
|
|
3727
3795
|
}
|
|
3728
3796
|
}
|
|
3729
3797
|
return { handled: true, response: this.error("Draft publishing not supported", 501) };
|
|
@@ -4237,6 +4305,14 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
4237
4305
|
}
|
|
4238
4306
|
return { handled: true, response: this.success({ connectors: [], total: 0 }) };
|
|
4239
4307
|
}
|
|
4308
|
+
if (parts[0] === "_status" && parts.length === 1 && m === "GET") {
|
|
4309
|
+
const svc = automationService;
|
|
4310
|
+
if (typeof svc.getFlowRuntimeStates === "function") {
|
|
4311
|
+
const flows = svc.getFlowRuntimeStates();
|
|
4312
|
+
return { handled: true, response: this.success({ flows, total: flows.length }) };
|
|
4313
|
+
}
|
|
4314
|
+
return { handled: true, response: this.success({ flows: [], total: 0 }) };
|
|
4315
|
+
}
|
|
4240
4316
|
if (parts.length >= 1) {
|
|
4241
4317
|
const name = parts[0];
|
|
4242
4318
|
if (parts[1] === "trigger" && m === "POST") {
|
|
@@ -5696,6 +5772,60 @@ function createDispatcherPlugin(config = {}) {
|
|
|
5696
5772
|
errorResponse(err, res);
|
|
5697
5773
|
}
|
|
5698
5774
|
});
|
|
5775
|
+
server.post(`${prefix}/packages/:id/duplicate`, async (req, res) => {
|
|
5776
|
+
try {
|
|
5777
|
+
const result = await dispatcher.handlePackages(`/${req.params.id}/duplicate`, "POST", req.body, {}, { request: req });
|
|
5778
|
+
sendResult(result, res);
|
|
5779
|
+
} catch (err) {
|
|
5780
|
+
errorResponse(err, res);
|
|
5781
|
+
}
|
|
5782
|
+
});
|
|
5783
|
+
server.post(`${prefix}/packages/:id/adopt-orphans`, async (req, res) => {
|
|
5784
|
+
try {
|
|
5785
|
+
const result = await dispatcher.handlePackages(`/${req.params.id}/adopt-orphans`, "POST", req.body, {}, { request: req });
|
|
5786
|
+
sendResult(result, res);
|
|
5787
|
+
} catch (err) {
|
|
5788
|
+
errorResponse(err, res);
|
|
5789
|
+
}
|
|
5790
|
+
});
|
|
5791
|
+
server.post(`${prefix}/packages/:id/discard-drafts`, async (req, res) => {
|
|
5792
|
+
try {
|
|
5793
|
+
const result = await dispatcher.handlePackages(`/${req.params.id}/discard-drafts`, "POST", req.body, {}, { request: req });
|
|
5794
|
+
sendResult(result, res);
|
|
5795
|
+
} catch (err) {
|
|
5796
|
+
errorResponse(err, res);
|
|
5797
|
+
}
|
|
5798
|
+
});
|
|
5799
|
+
server.get(`${prefix}/packages/:id/commits`, async (req, res) => {
|
|
5800
|
+
try {
|
|
5801
|
+
const result = await dispatcher.handlePackages(`/${req.params.id}/commits`, "GET", void 0, req.query ?? {}, { request: req });
|
|
5802
|
+
sendResult(result, res);
|
|
5803
|
+
} catch (err) {
|
|
5804
|
+
errorResponse(err, res);
|
|
5805
|
+
}
|
|
5806
|
+
});
|
|
5807
|
+
server.post(`${prefix}/packages/:id/commits/:commitId/revert`, async (req, res) => {
|
|
5808
|
+
try {
|
|
5809
|
+
const result = await dispatcher.handlePackages(
|
|
5810
|
+
`/${req.params.id}/commits/${req.params.commitId}/revert`,
|
|
5811
|
+
"POST",
|
|
5812
|
+
req.body,
|
|
5813
|
+
{},
|
|
5814
|
+
{ request: req }
|
|
5815
|
+
);
|
|
5816
|
+
sendResult(result, res);
|
|
5817
|
+
} catch (err) {
|
|
5818
|
+
errorResponse(err, res);
|
|
5819
|
+
}
|
|
5820
|
+
});
|
|
5821
|
+
server.post(`${prefix}/packages/:id/rollback`, async (req, res) => {
|
|
5822
|
+
try {
|
|
5823
|
+
const result = await dispatcher.handlePackages(`/${req.params.id}/rollback`, "POST", req.body, {}, { request: req });
|
|
5824
|
+
sendResult(result, res);
|
|
5825
|
+
} catch (err) {
|
|
5826
|
+
errorResponse(err, res);
|
|
5827
|
+
}
|
|
5828
|
+
});
|
|
5699
5829
|
server.post(`${prefix}/storage/upload`, async (req, res) => {
|
|
5700
5830
|
try {
|
|
5701
5831
|
const result = await dispatcher.handleStorage("upload", "POST", req.body, { request: req });
|