@objectstack/runtime 11.0.0 → 11.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/README.md +3 -3
- package/dist/index.cjs +42 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -6
- package/dist/index.d.ts +17 -6
- package/dist/index.js +43 -2
- package/dist/index.js.map +1 -1
- package/package.json +21 -21
package/README.md
CHANGED
|
@@ -134,7 +134,7 @@ new AppPlugin(appConfig)
|
|
|
134
134
|
|
|
135
135
|
#### IHttpServer
|
|
136
136
|
|
|
137
|
-
Abstract interface for HTTP server capabilities. Allows plugins to work with any HTTP framework (
|
|
137
|
+
Abstract interface for HTTP server capabilities. Allows plugins to work with any HTTP framework (e.g. Hono) without tight coupling.
|
|
138
138
|
|
|
139
139
|
```typescript
|
|
140
140
|
import { IHttpServer, IHttpRequest, IHttpResponse } from '@objectstack/runtime';
|
|
@@ -144,7 +144,7 @@ class MyHttpServerPlugin implements Plugin {
|
|
|
144
144
|
name = 'http-server';
|
|
145
145
|
|
|
146
146
|
async init(ctx: PluginContext) {
|
|
147
|
-
const server: IHttpServer = createMyServer(); //
|
|
147
|
+
const server: IHttpServer = createMyServer(); // Hono, or any framework via IHttpServer
|
|
148
148
|
ctx.registerService('http-server', server);
|
|
149
149
|
}
|
|
150
150
|
}
|
|
@@ -599,7 +599,7 @@ createDispatcherPlugin({
|
|
|
599
599
|
|
|
600
600
|
Token-bucket `RateLimiter` with pluggable `RateLimitStore` (in-memory default,
|
|
601
601
|
Redis-friendly contract). Curated `DEFAULT_RATE_LIMITS` for auth / write / read
|
|
602
|
-
buckets.
|
|
602
|
+
buckets. A Hono recipe in
|
|
603
603
|
[`docs/HARDENING.md`](../../docs/HARDENING.md#rate-limiting).
|
|
604
604
|
|
|
605
605
|
```ts
|
package/dist/index.cjs
CHANGED
|
@@ -1575,7 +1575,7 @@ async function createStandaloneStack(config) {
|
|
|
1575
1575
|
const environmentId = cfg.environmentId ?? process.env.OS_ENVIRONMENT_ID ?? "proj_local";
|
|
1576
1576
|
const artifactPathInput = cfg.artifactPath ?? process.env.OS_ARTIFACT_PATH ?? (0, import_node_path3.resolve)(cwd, "dist/objectstack.json");
|
|
1577
1577
|
const artifactPath = isHttpUrl(artifactPathInput) ? artifactPathInput : artifactPathInput.startsWith("/") ? artifactPathInput : (0, import_node_path3.resolve)(cwd, artifactPathInput);
|
|
1578
|
-
const dbUrl = cfg.databaseUrl ?? (0, import_types2.readEnvWithDeprecation)("OS_DATABASE_URL", "DATABASE_URL")?.trim() ?? process.env.TURSO_DATABASE_URL?.trim() ?? (process.env.OS_HOME?.trim() ? `file:${(0, import_node_path3.resolve)(resolveObjectStackHome(), "data/standalone.db")}` : cfg.projectRoot ? `file:${(0, import_node_path3.resolve)(cfg.projectRoot, ".objectstack/data/standalone.db")}` : `file:${(0, import_node_path3.resolve)(resolveObjectStackHome(), "data/standalone.db")}`);
|
|
1578
|
+
const dbUrl = cfg.databaseUrl ?? (0, import_types2.readEnvWithDeprecation)("OS_DATABASE_URL", "DATABASE_URL", { silent: true })?.trim() ?? process.env.TURSO_DATABASE_URL?.trim() ?? (process.env.OS_HOME?.trim() ? `file:${(0, import_node_path3.resolve)(resolveObjectStackHome(), "data/standalone.db")}` : cfg.projectRoot ? `file:${(0, import_node_path3.resolve)(cfg.projectRoot, ".objectstack/data/standalone.db")}` : `file:${(0, import_node_path3.resolve)(resolveObjectStackHome(), "data/standalone.db")}`);
|
|
1579
1579
|
const explicitDriver = cfg.databaseDriver ?? process.env.OS_DATABASE_DRIVER?.trim();
|
|
1580
1580
|
const dbDriver = explicitDriver ?? detectDriverFromUrl(dbUrl);
|
|
1581
1581
|
let driverPlugin;
|
|
@@ -2909,6 +2909,43 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
2909
2909
|
* response object that callers should surface directly — no further
|
|
2910
2910
|
* dispatch happens.
|
|
2911
2911
|
*/
|
|
2912
|
+
/**
|
|
2913
|
+
* ADR-0069 — returns a 403 response when the resolved session is blocked by
|
|
2914
|
+
* an auth-policy gate (expired password / required MFA) on a non-allow-listed
|
|
2915
|
+
* path, else null. Mirrors the REST `enforceAuth` seam so REST + dispatcher
|
|
2916
|
+
* (MCP, GraphQL) enforce consistently. Fails open on any lookup error.
|
|
2917
|
+
*/
|
|
2918
|
+
async enforceAuthGate(context, cleanPath) {
|
|
2919
|
+
try {
|
|
2920
|
+
if ((0, import_core4.isAuthGateAllowlisted)(cleanPath)) return null;
|
|
2921
|
+
const authService = await this.resolveService("auth", context.environmentId);
|
|
2922
|
+
if (!authService || typeof authService.isAuthGateActive !== "function" || !authService.isAuthGateActive()) {
|
|
2923
|
+
return null;
|
|
2924
|
+
}
|
|
2925
|
+
let api = authService.api;
|
|
2926
|
+
if (!api && typeof authService.getApi === "function") api = await authService.getApi();
|
|
2927
|
+
if (!api?.getSession) return null;
|
|
2928
|
+
const raw = context?.request?.headers;
|
|
2929
|
+
let headers;
|
|
2930
|
+
if (raw && typeof raw.get === "function") {
|
|
2931
|
+
headers = raw;
|
|
2932
|
+
} else if (raw && typeof raw === "object") {
|
|
2933
|
+
headers = new globalThis.Headers();
|
|
2934
|
+
for (const k of Object.keys(raw)) {
|
|
2935
|
+
const v = raw[k];
|
|
2936
|
+
if (v != null) headers.set(String(k), Array.isArray(v) ? v.join(",") : String(v));
|
|
2937
|
+
}
|
|
2938
|
+
} else {
|
|
2939
|
+
return null;
|
|
2940
|
+
}
|
|
2941
|
+
const session = await api.getSession({ headers }).catch(() => void 0);
|
|
2942
|
+
const gate = (0, import_core4.evaluateAuthGate)(session?.user, cleanPath);
|
|
2943
|
+
if (!gate) return null;
|
|
2944
|
+
return this.error(gate.message, 403, { code: gate.code });
|
|
2945
|
+
} catch {
|
|
2946
|
+
return null;
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2912
2949
|
async enforceProjectMembership(context, path) {
|
|
2913
2950
|
if (!this.enforceMembership) return null;
|
|
2914
2951
|
const skipPaths = ["/auth", "/cloud", "/health", "/ready", "/discovery"];
|
|
@@ -4828,6 +4865,10 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
4828
4865
|
});
|
|
4829
4866
|
} catch {
|
|
4830
4867
|
}
|
|
4868
|
+
const authGated = await this.enforceAuthGate(context, cleanPath);
|
|
4869
|
+
if (authGated) {
|
|
4870
|
+
return { handled: true, response: authGated };
|
|
4871
|
+
}
|
|
4831
4872
|
const forbidden = await this.enforceProjectMembership(context, cleanPath);
|
|
4832
4873
|
if (forbidden) {
|
|
4833
4874
|
return { handled: true, response: forbidden };
|