@objectstack/rest 11.0.0 → 11.2.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/README.md +1 -1
- package/dist/index.cjs +42 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +43 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
## Overview
|
|
9
9
|
|
|
10
|
-
`@objectstack/rest` is registered as a kernel plugin and emits HTTP routes for every object defined in the protocol metadata.
|
|
10
|
+
`@objectstack/rest` is registered as a kernel plugin and emits HTTP routes for every object defined in the protocol metadata. The Hono framework adapter (`@objectstack/hono`) invokes these routes through `HttpDispatcher` from `@objectstack/runtime`.
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
package/dist/index.cjs
CHANGED
|
@@ -500,6 +500,19 @@ var RestServer = class {
|
|
|
500
500
|
*/
|
|
501
501
|
this.hostnameCache = /* @__PURE__ */ new Map();
|
|
502
502
|
this.hostnameCacheTtlMs = 3e4;
|
|
503
|
+
/**
|
|
504
|
+
* Request-scoped memoization for `resolveExecCtx`. A single HTTP request
|
|
505
|
+
* resolves the SAME execution context (identity + RBAC/RLS + localization)
|
|
506
|
+
* many times — the data operation itself, app-nav RBAC filtering, dashboard
|
|
507
|
+
* widget gating, the auth gate, etc. Each resolution is ~16 sequential
|
|
508
|
+
* queries (the `resolveAuthzContext` aggregation plus localization), so a
|
|
509
|
+
* request that resolves twice pays for duplicate authz and repeated
|
|
510
|
+
* localization. Keyed by the per-request `req` object (a `WeakMap`, so the
|
|
511
|
+
* entry is collected with the request — naturally request-scoped, no TTL,
|
|
512
|
+
* no cross-request leak) and the input `environmentId`. We cache the
|
|
513
|
+
* in-flight Promise so concurrent callers share one resolution.
|
|
514
|
+
*/
|
|
515
|
+
this.execCtxMemo = /* @__PURE__ */ new WeakMap();
|
|
503
516
|
/**
|
|
504
517
|
* Lazily load the OpenAPI spec JSON shipped by @objectstack/spec.
|
|
505
518
|
* Cached after first read. Resilient to missing files / parse errors
|
|
@@ -673,6 +686,11 @@ var RestServer = class {
|
|
|
673
686
|
* requests (they're internal-only), so they cannot bypass this gate.
|
|
674
687
|
*/
|
|
675
688
|
enforceAuth(req, res, context) {
|
|
689
|
+
const gate = context?.authGate;
|
|
690
|
+
if (gate && req?.method !== "OPTIONS" && !(0, import_core.isAuthGateAllowlisted)(req?.path)) {
|
|
691
|
+
res.status(403).json({ error: { code: gate.code, message: gate.message } });
|
|
692
|
+
return true;
|
|
693
|
+
}
|
|
676
694
|
if (!this.config.api.requireAuth) return false;
|
|
677
695
|
if (context?.userId) return false;
|
|
678
696
|
if (req?.method === "OPTIONS") return false;
|
|
@@ -743,6 +761,21 @@ var RestServer = class {
|
|
|
743
761
|
* to the protocol layer (the SecurityPlugin treats undefined as anon).
|
|
744
762
|
*/
|
|
745
763
|
async resolveExecCtx(environmentId, req) {
|
|
764
|
+
if (!req || typeof req !== "object") return this.computeExecCtx(environmentId, req);
|
|
765
|
+
const key = environmentId ?? "\0default";
|
|
766
|
+
let perReq = this.execCtxMemo.get(req);
|
|
767
|
+
if (!perReq) {
|
|
768
|
+
perReq = /* @__PURE__ */ new Map();
|
|
769
|
+
this.execCtxMemo.set(req, perReq);
|
|
770
|
+
}
|
|
771
|
+
const cached = perReq.get(key);
|
|
772
|
+
if (cached) return cached;
|
|
773
|
+
const pending = this.computeExecCtx(environmentId, req);
|
|
774
|
+
perReq.set(key, pending);
|
|
775
|
+
return pending;
|
|
776
|
+
}
|
|
777
|
+
/** Heavy path behind `resolveExecCtx` — resolve identity + RBAC/RLS + localization. */
|
|
778
|
+
async computeExecCtx(environmentId, req) {
|
|
746
779
|
try {
|
|
747
780
|
if (!environmentId && req && this.envRegistry && this.kernelManager) {
|
|
748
781
|
const host = this.extractHostname(req);
|
|
@@ -819,6 +852,14 @@ var RestServer = class {
|
|
|
819
852
|
tenantId: authz.tenantId,
|
|
820
853
|
userId: authz.userId
|
|
821
854
|
});
|
|
855
|
+
let authGate;
|
|
856
|
+
try {
|
|
857
|
+
if (typeof authService.isAuthGateActive === "function" && authService.isAuthGateActive()) {
|
|
858
|
+
const gatedSession = await getSession(headers).catch(() => void 0);
|
|
859
|
+
if (gatedSession?.user?.authGate) authGate = gatedSession.user.authGate;
|
|
860
|
+
}
|
|
861
|
+
} catch {
|
|
862
|
+
}
|
|
822
863
|
return {
|
|
823
864
|
userId: authz.userId,
|
|
824
865
|
tenantId: authz.tenantId,
|
|
@@ -829,6 +870,7 @@ var RestServer = class {
|
|
|
829
870
|
...authz.tabPermissions ? { tabPermissions: authz.tabPermissions } : {},
|
|
830
871
|
isSystem: false,
|
|
831
872
|
org_user_ids: authz.org_user_ids,
|
|
873
|
+
...authGate ? { authGate } : {},
|
|
832
874
|
...localization.timezone ? { timezone: localization.timezone } : {},
|
|
833
875
|
...localization.locale ? { locale: localization.locale } : {},
|
|
834
876
|
...localization.currency ? { currency: localization.currency } : {},
|