@objectstack/runtime 11.10.0 → 12.0.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 +124 -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 +124 -2
- package/dist/index.js.map +1 -1
- package/package.json +21 -21
package/dist/index.cjs
CHANGED
|
@@ -916,6 +916,7 @@ var init_app_plugin = __esm({
|
|
|
916
916
|
/** When true, init/start become no-ops — env has no app payload. */
|
|
917
917
|
this.empty = false;
|
|
918
918
|
this.init = async (ctx) => {
|
|
919
|
+
this.installDefaultHookBodyRunner(ctx);
|
|
919
920
|
if (this.empty) {
|
|
920
921
|
ctx.logger.debug("[AppPlugin] empty env \u2014 no app payload, skipping init", {
|
|
921
922
|
pluginName: this.name
|
|
@@ -1438,6 +1439,46 @@ var init_app_plugin = __esm({
|
|
|
1438
1439
|
this.name = `plugin.app.${appId}`;
|
|
1439
1440
|
this.version = sys?.version;
|
|
1440
1441
|
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Install the engine's DEFAULT hook body runner (`engine.setDefaultBodyRunner`).
|
|
1444
|
+
*
|
|
1445
|
+
* Hooks authored at runtime (Studio → `protocol.saveMetaItem` → publish)
|
|
1446
|
+
* bind through paths that pass no explicit `bodyRunner` — notably
|
|
1447
|
+
* ObjectQLPlugin's metadata-service bind — so without this default their
|
|
1448
|
+
* L1/L2 `body` is silently dropped by `bindHooksToEngine` and the hook
|
|
1449
|
+
* never runs (#2588). The runtime owns the sandbox bridge (objectql stays
|
|
1450
|
+
* sandbox-free), so this is the boot point that wires it: same
|
|
1451
|
+
* QuickJS-sandboxed, capability-gated runner the `defineStack({ hooks })`
|
|
1452
|
+
* bind already uses.
|
|
1453
|
+
*
|
|
1454
|
+
* `OS_DISABLE_AUTHORED_HOOKS=1` opts out for deployments that want
|
|
1455
|
+
* runtime-authored (DB-stored, non-code-reviewed) hook bodies to stay
|
|
1456
|
+
* inert; code-shipped hooks are unaffected (AppPlugin passes its own
|
|
1457
|
+
* runner explicitly).
|
|
1458
|
+
*
|
|
1459
|
+
* Idempotent: the first AppPlugin to run installs it; the runner is
|
|
1460
|
+
* bundle-agnostic (it only closes over the engine + logger).
|
|
1461
|
+
*/
|
|
1462
|
+
installDefaultHookBodyRunner(ctx) {
|
|
1463
|
+
if (process.env.OS_DISABLE_AUTHORED_HOOKS === "1") {
|
|
1464
|
+
ctx.logger.info("[AppPlugin] OS_DISABLE_AUTHORED_HOOKS=1 \u2014 runtime-authored hook bodies will not execute");
|
|
1465
|
+
return;
|
|
1466
|
+
}
|
|
1467
|
+
let ql;
|
|
1468
|
+
try {
|
|
1469
|
+
ql = ctx.getService("objectql");
|
|
1470
|
+
} catch {
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1473
|
+
if (!ql || typeof ql.setDefaultBodyRunner !== "function") return;
|
|
1474
|
+
if (ql._defaultBodyRunner) return;
|
|
1475
|
+
ql.setDefaultBodyRunner(hookBodyRunnerFactory(new QuickJSScriptRunner(), {
|
|
1476
|
+
ql,
|
|
1477
|
+
logger: ctx.logger,
|
|
1478
|
+
appId: "runtime-authored"
|
|
1479
|
+
}));
|
|
1480
|
+
ctx.logger.info("[AppPlugin] Installed default hook body runner (runtime-authored hooks can execute)");
|
|
1481
|
+
}
|
|
1441
1482
|
/**
|
|
1442
1483
|
* Emit a kernel hook so the control-plane `AppCatalogService` can
|
|
1443
1484
|
* upsert / delete the corresponding `sys_app` row. Silently no-ops
|
|
@@ -2232,6 +2273,22 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
2232
2273
|
body: { success: false, error: { message, code, details } }
|
|
2233
2274
|
};
|
|
2234
2275
|
}
|
|
2276
|
+
/**
|
|
2277
|
+
* Build an error response from a THROWN service/protocol error, preserving
|
|
2278
|
+
* the error's own HTTP `status` and — critically — any structured `issues`
|
|
2279
|
+
* array (e.g. spec-validation `{ path, message, code }[]` from
|
|
2280
|
+
* `protocol.saveMetaItem`). The plain `error(msg, code)` path collapses a
|
|
2281
|
+
* validation failure to a single message, so the UI can only show a generic
|
|
2282
|
+
* banner; carrying `issues` (and the semantic `code`) in `details` lets it
|
|
2283
|
+
* map each error back to the offending field. Falls back to `fallbackStatus`
|
|
2284
|
+
* and behaves exactly like `error()` for errors that carry neither.
|
|
2285
|
+
*/
|
|
2286
|
+
errorFromThrown(e, fallbackStatus = 500) {
|
|
2287
|
+
const status = typeof e?.status === "number" ? e.status : typeof e?.statusCode === "number" ? e.statusCode : fallbackStatus;
|
|
2288
|
+
const issues = Array.isArray(e?.issues) ? e.issues : void 0;
|
|
2289
|
+
const details = issues || e?.code ? { ...e?.code ? { code: e.code } : {}, ...issues ? { issues } : {} } : void 0;
|
|
2290
|
+
return this.error(e?.message ?? String(e), status, details);
|
|
2291
|
+
}
|
|
2235
2292
|
/**
|
|
2236
2293
|
* ADR-0046: `doc` list responses omit `content` by default — manuals
|
|
2237
2294
|
* are the one metadata payload that grows unbounded, and the list
|
|
@@ -3303,7 +3360,7 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3303
3360
|
const result = await protocol.saveMetaItem({ type, name, item: body, organizationId, ...packageId ? { packageId } : {} });
|
|
3304
3361
|
return { handled: true, response: this.success(result) };
|
|
3305
3362
|
} catch (e) {
|
|
3306
|
-
return { handled: true, response: this.
|
|
3363
|
+
return { handled: true, response: this.errorFromThrown(e, 400) };
|
|
3307
3364
|
}
|
|
3308
3365
|
}
|
|
3309
3366
|
const metaSvc = await this.resolveService("metadata", _context.environmentId);
|
|
@@ -3786,9 +3843,20 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3786
3843
|
} catch (e) {
|
|
3787
3844
|
result.unhideError = e?.message ?? "visibility flip failed";
|
|
3788
3845
|
}
|
|
3846
|
+
try {
|
|
3847
|
+
const changed = [
|
|
3848
|
+
...(result?.published ?? []).map((p) => `${p.type}/${p.name}`),
|
|
3849
|
+
...(result?.unhiddenApps ?? []).map((n) => `app/${n}`)
|
|
3850
|
+
];
|
|
3851
|
+
if (changed.length > 0 && this.kernel?.context?.trigger) {
|
|
3852
|
+
await this.kernel.context.trigger("metadata:reloaded", { changed });
|
|
3853
|
+
}
|
|
3854
|
+
} catch (e) {
|
|
3855
|
+
result.rebindError = e?.message ?? "metadata:reloaded announce failed";
|
|
3856
|
+
}
|
|
3789
3857
|
return { handled: true, response: this.success(result) };
|
|
3790
3858
|
} catch (e) {
|
|
3791
|
-
return { handled: true, response: this.
|
|
3859
|
+
return { handled: true, response: this.errorFromThrown(e, 500) };
|
|
3792
3860
|
}
|
|
3793
3861
|
}
|
|
3794
3862
|
return { handled: true, response: this.error("Draft publishing not supported", 501) };
|
|
@@ -5751,6 +5819,60 @@ function createDispatcherPlugin(config = {}) {
|
|
|
5751
5819
|
errorResponse(err, res);
|
|
5752
5820
|
}
|
|
5753
5821
|
});
|
|
5822
|
+
server.post(`${prefix}/packages/:id/duplicate`, async (req, res) => {
|
|
5823
|
+
try {
|
|
5824
|
+
const result = await dispatcher.handlePackages(`/${req.params.id}/duplicate`, "POST", req.body, {}, { request: req });
|
|
5825
|
+
sendResult(result, res);
|
|
5826
|
+
} catch (err) {
|
|
5827
|
+
errorResponse(err, res);
|
|
5828
|
+
}
|
|
5829
|
+
});
|
|
5830
|
+
server.post(`${prefix}/packages/:id/adopt-orphans`, async (req, res) => {
|
|
5831
|
+
try {
|
|
5832
|
+
const result = await dispatcher.handlePackages(`/${req.params.id}/adopt-orphans`, "POST", req.body, {}, { request: req });
|
|
5833
|
+
sendResult(result, res);
|
|
5834
|
+
} catch (err) {
|
|
5835
|
+
errorResponse(err, res);
|
|
5836
|
+
}
|
|
5837
|
+
});
|
|
5838
|
+
server.post(`${prefix}/packages/:id/discard-drafts`, async (req, res) => {
|
|
5839
|
+
try {
|
|
5840
|
+
const result = await dispatcher.handlePackages(`/${req.params.id}/discard-drafts`, "POST", req.body, {}, { request: req });
|
|
5841
|
+
sendResult(result, res);
|
|
5842
|
+
} catch (err) {
|
|
5843
|
+
errorResponse(err, res);
|
|
5844
|
+
}
|
|
5845
|
+
});
|
|
5846
|
+
server.get(`${prefix}/packages/:id/commits`, async (req, res) => {
|
|
5847
|
+
try {
|
|
5848
|
+
const result = await dispatcher.handlePackages(`/${req.params.id}/commits`, "GET", void 0, req.query ?? {}, { request: req });
|
|
5849
|
+
sendResult(result, res);
|
|
5850
|
+
} catch (err) {
|
|
5851
|
+
errorResponse(err, res);
|
|
5852
|
+
}
|
|
5853
|
+
});
|
|
5854
|
+
server.post(`${prefix}/packages/:id/commits/:commitId/revert`, async (req, res) => {
|
|
5855
|
+
try {
|
|
5856
|
+
const result = await dispatcher.handlePackages(
|
|
5857
|
+
`/${req.params.id}/commits/${req.params.commitId}/revert`,
|
|
5858
|
+
"POST",
|
|
5859
|
+
req.body,
|
|
5860
|
+
{},
|
|
5861
|
+
{ request: req }
|
|
5862
|
+
);
|
|
5863
|
+
sendResult(result, res);
|
|
5864
|
+
} catch (err) {
|
|
5865
|
+
errorResponse(err, res);
|
|
5866
|
+
}
|
|
5867
|
+
});
|
|
5868
|
+
server.post(`${prefix}/packages/:id/rollback`, async (req, res) => {
|
|
5869
|
+
try {
|
|
5870
|
+
const result = await dispatcher.handlePackages(`/${req.params.id}/rollback`, "POST", req.body, {}, { request: req });
|
|
5871
|
+
sendResult(result, res);
|
|
5872
|
+
} catch (err) {
|
|
5873
|
+
errorResponse(err, res);
|
|
5874
|
+
}
|
|
5875
|
+
});
|
|
5754
5876
|
server.post(`${prefix}/storage/upload`, async (req, res) => {
|
|
5755
5877
|
try {
|
|
5756
5878
|
const result = await dispatcher.handleStorage("upload", "POST", req.body, { request: req });
|