@run402/functions 2.5.1 → 2.6.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/auth.d.ts +3 -3
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +44 -6
- package/dist/auth.js.map +1 -1
- package/dist/cache.d.ts +122 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +243 -0
- package/dist/cache.js.map +1 -0
- package/dist/db.d.ts +26 -7
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +38 -8
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/jwt.d.ts +55 -0
- package/dist/lib/jwt.d.ts.map +1 -0
- package/dist/lib/jwt.js +196 -0
- package/dist/lib/jwt.js.map +1 -0
- package/dist/runtime-context.d.ts +152 -0
- package/dist/runtime-context.d.ts.map +1 -0
- package/dist/runtime-context.js +170 -0
- package/dist/runtime-context.js.map +1 -0
- package/package.json +2 -7
package/dist/auth.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface User {
|
|
|
12
12
|
* application role from a declarative `requireRole` gate. For the
|
|
13
13
|
* gate-resolved application role, use {@link getRole}.
|
|
14
14
|
*/
|
|
15
|
-
export declare function getUser(req
|
|
15
|
+
export declare function getUser(req?: Request): User | null;
|
|
16
16
|
/**
|
|
17
17
|
* Read the gate-resolved user id from the request.
|
|
18
18
|
*
|
|
@@ -33,7 +33,7 @@ export declare function getUser(req: Request): User | null;
|
|
|
33
33
|
* `members` table via gateway-side RLS-bypass; user code does not
|
|
34
34
|
* need to re-decode the JWT.
|
|
35
35
|
*/
|
|
36
|
-
export declare function getUserId(req
|
|
36
|
+
export declare function getUserId(req?: Request): string | null;
|
|
37
37
|
/**
|
|
38
38
|
* Read the gate-resolved application role from the request.
|
|
39
39
|
*
|
|
@@ -53,5 +53,5 @@ export declare function getUserId(req: Request): string | null;
|
|
|
53
53
|
* {@link getUser}. The two are independent — see the JSDoc on
|
|
54
54
|
* `getUser` for the distinction.
|
|
55
55
|
*/
|
|
56
|
-
export declare function getRole(req
|
|
56
|
+
export declare function getRole(req?: Request): string | null;
|
|
57
57
|
//# sourceMappingURL=auth.d.ts.map
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAsClD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAOtD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,OAAO,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAOpD"}
|
package/dist/auth.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import jwt from "
|
|
1
|
+
import jwt from "./lib/jwt.js";
|
|
2
2
|
import { config } from "./config.js";
|
|
3
|
+
import { getCurrentContext, taintCacheBypass } from "./runtime-context.js";
|
|
3
4
|
/**
|
|
4
5
|
* Verify the caller's JWT and return user identity.
|
|
5
6
|
* Returns { id, role, email } or null if unauthenticated/invalid.
|
|
@@ -10,9 +11,30 @@ import { config } from "./config.js";
|
|
|
10
11
|
* gate-resolved application role, use {@link getRole}.
|
|
11
12
|
*/
|
|
12
13
|
export function getUser(req) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
// Capability `astro-ssr-runtime` (v1.52). Taint the cache-bypass flag
|
|
15
|
+
// on the active request context, regardless of whether `getUser`
|
|
16
|
+
// resolves to a user or null — the response now depends on per-request
|
|
17
|
+
// auth state and MUST NOT be cached publicly.
|
|
18
|
+
taintCacheBypass();
|
|
19
|
+
// If no `req` was passed, read auth from the ALS context (the SSR
|
|
20
|
+
// Lambda runtime's `runWithContext` populates `request.headers`).
|
|
21
|
+
// This is what makes `await getUser()` work naturally inside Astro
|
|
22
|
+
// `[slug].astro` frontmatter without any explicit plumbing.
|
|
23
|
+
let authHeader;
|
|
24
|
+
if (req !== undefined) {
|
|
25
|
+
authHeader =
|
|
26
|
+
typeof req.headers.get === "function"
|
|
27
|
+
? req.headers.get("authorization")
|
|
28
|
+
: req.headers?.authorization;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
const ctx = getCurrentContext();
|
|
32
|
+
if (ctx === undefined)
|
|
33
|
+
return null;
|
|
34
|
+
const h = ctx.request.headers;
|
|
35
|
+
const raw = h["authorization"] ?? h["Authorization"];
|
|
36
|
+
authHeader = Array.isArray(raw) ? raw[0] : raw;
|
|
37
|
+
}
|
|
16
38
|
if (!authHeader || !authHeader.startsWith("Bearer "))
|
|
17
39
|
return null;
|
|
18
40
|
const token = authHeader.slice(7);
|
|
@@ -47,7 +69,15 @@ export function getUser(req) {
|
|
|
47
69
|
* need to re-decode the JWT.
|
|
48
70
|
*/
|
|
49
71
|
export function getUserId(req) {
|
|
50
|
-
|
|
72
|
+
if (req !== undefined)
|
|
73
|
+
return req.headers.get("x-run402-user-id");
|
|
74
|
+
const ctx = getCurrentContext();
|
|
75
|
+
if (ctx === undefined)
|
|
76
|
+
return null;
|
|
77
|
+
const raw = ctx.request.headers["x-run402-user-id"];
|
|
78
|
+
if (Array.isArray(raw))
|
|
79
|
+
return raw[0] ?? null;
|
|
80
|
+
return raw ?? null;
|
|
51
81
|
}
|
|
52
82
|
/**
|
|
53
83
|
* Read the gate-resolved application role from the request.
|
|
@@ -69,6 +99,14 @@ export function getUserId(req) {
|
|
|
69
99
|
* `getUser` for the distinction.
|
|
70
100
|
*/
|
|
71
101
|
export function getRole(req) {
|
|
72
|
-
|
|
102
|
+
if (req !== undefined)
|
|
103
|
+
return req.headers.get("x-run402-user-role");
|
|
104
|
+
const ctx = getCurrentContext();
|
|
105
|
+
if (ctx === undefined)
|
|
106
|
+
return null;
|
|
107
|
+
const raw = ctx.request.headers["x-run402-user-role"];
|
|
108
|
+
if (Array.isArray(raw))
|
|
109
|
+
return raw[0] ?? null;
|
|
110
|
+
return raw ?? null;
|
|
73
111
|
}
|
|
74
112
|
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSR cache invalidation SDK — capability `ssr-isr-cache`.
|
|
3
|
+
*
|
|
4
|
+
* Server-side (function-context) only. Reads the current request's
|
|
5
|
+
* project_id and host from `getCurrentContext()` (AsyncLocalStorage) to
|
|
6
|
+
* scope invalidations safely.
|
|
7
|
+
*
|
|
8
|
+
* Four operations:
|
|
9
|
+
*
|
|
10
|
+
* - `cache.invalidate(urlOrPath)` — delete cache rows for a specific
|
|
11
|
+
* path (or absolute URL). Returns `{ deleted, generation, host, path }`.
|
|
12
|
+
* - `cache.invalidatePrefix({ host, prefix })` — delete all rows
|
|
13
|
+
* under a path prefix on the given host.
|
|
14
|
+
* - `cache.invalidateAll({ host })` — delete all rows on the given
|
|
15
|
+
* host. Atomic with a generation bump.
|
|
16
|
+
* - `cache.invalidateMany(urls)` — bulk invalidate in one round-trip.
|
|
17
|
+
*
|
|
18
|
+
* Host authorization: absolute-URL forms (or explicit `host` in the
|
|
19
|
+
* options bag) MUST be hosts owned by the caller's authenticated
|
|
20
|
+
* project. Cross-project hosts throw `R402_CACHE_INVALIDATION_HOST_FORBIDDEN`.
|
|
21
|
+
*
|
|
22
|
+
* Path-string form requires an active request context (the SDK reads
|
|
23
|
+
* the current host from ALS); calling it outside a handler throws
|
|
24
|
+
* `R402_CACHE_INVALIDATION_HOST_REQUIRED`.
|
|
25
|
+
*
|
|
26
|
+
* Tag-based invalidation is NOT in v1; future v1.5 work.
|
|
27
|
+
*
|
|
28
|
+
* @see openspec/changes/astro-ssr-runtime/specs/ssr-isr-cache/spec.md
|
|
29
|
+
*/
|
|
30
|
+
/** Result envelope returned by every invalidation API. */
|
|
31
|
+
export interface CacheInvalidateResult {
|
|
32
|
+
/** Number of `internal.ssr_cache` rows DELETEd by this call. */
|
|
33
|
+
deleted: number;
|
|
34
|
+
/** Post-increment generation. Each invalidate bumps the per-(project,
|
|
35
|
+
* host) generation counter, which gates in-flight MISS writes from
|
|
36
|
+
* populating stale content after the invalidate. */
|
|
37
|
+
generation: bigint;
|
|
38
|
+
/** The host the invalidation targeted. */
|
|
39
|
+
host: string;
|
|
40
|
+
/** The pathname targeted (single-URL form only); undefined for
|
|
41
|
+
* prefix/all/many. */
|
|
42
|
+
path?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface InvalidatePrefixOptions {
|
|
45
|
+
host: string;
|
|
46
|
+
prefix: string;
|
|
47
|
+
}
|
|
48
|
+
export interface InvalidateAllOptions {
|
|
49
|
+
host: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Structured error thrown when path-string form is called outside an
|
|
53
|
+
* active request context. The caller has no host to scope against.
|
|
54
|
+
* Use the absolute-URL form instead OR move the call into a handler.
|
|
55
|
+
*/
|
|
56
|
+
export declare class CacheInvalidationHostRequiredError extends Error {
|
|
57
|
+
readonly code = "R402_CACHE_INVALIDATION_HOST_REQUIRED";
|
|
58
|
+
readonly docs = "https://docs.run402.com/cache/errors#invalidation-host-required";
|
|
59
|
+
readonly suggestedFix = "Pass an absolute URL (e.g., `new URL('https://eagles.kychon.com/the-guys')`) OR move the call into a request handler so the SDK can resolve the current host.";
|
|
60
|
+
constructor();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Structured error thrown when an invalidation targets a host that is
|
|
64
|
+
* not owned by the caller's authenticated project. Prevents project A
|
|
65
|
+
* from invalidating project B's cache.
|
|
66
|
+
*/
|
|
67
|
+
export declare class CacheInvalidationHostForbiddenError extends Error {
|
|
68
|
+
readonly code = "R402_CACHE_INVALIDATION_HOST_FORBIDDEN";
|
|
69
|
+
readonly docs = "https://docs.run402.com/cache/errors#invalidation-host-forbidden";
|
|
70
|
+
readonly host: string;
|
|
71
|
+
readonly suggestedFix: string;
|
|
72
|
+
constructor(host: string);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Invalidate a single cached SSR response.
|
|
76
|
+
*
|
|
77
|
+
* Accepts either:
|
|
78
|
+
* - a path string (e.g., `'/the-guys'`) — the SDK reads the current
|
|
79
|
+
* host from the active request context (throws
|
|
80
|
+
* `CacheInvalidationHostRequiredError` if no context)
|
|
81
|
+
* - an absolute URL (e.g., `new URL('https://eagles.kychon.com/the-guys')`)
|
|
82
|
+
* — multi-host admin scenarios (host MUST be owned by caller's
|
|
83
|
+
* project; throws `CacheInvalidationHostForbiddenError` otherwise)
|
|
84
|
+
*
|
|
85
|
+
* Deletes GET and HEAD rows for ALL locales and ALL release ids matching
|
|
86
|
+
* the exact canonical pathname + normalized search on the target host.
|
|
87
|
+
*
|
|
88
|
+
* The DELETE and the per-(project, host) generation increment happen in
|
|
89
|
+
* the same transaction; the post-increment generation is returned.
|
|
90
|
+
*/
|
|
91
|
+
export declare function invalidate(urlOrPath: string | URL): Promise<CacheInvalidateResult>;
|
|
92
|
+
/**
|
|
93
|
+
* Invalidate all cache rows under a path prefix on the given host.
|
|
94
|
+
*/
|
|
95
|
+
export declare function invalidatePrefix(opts: InvalidatePrefixOptions): Promise<CacheInvalidateResult>;
|
|
96
|
+
/**
|
|
97
|
+
* Invalidate all cache rows for the given host (entire-host purge).
|
|
98
|
+
* Useful for catastrophic content changes (nav restructure, layout
|
|
99
|
+
* update, etc.) where targeted invalidation would be impractical.
|
|
100
|
+
*/
|
|
101
|
+
export declare function invalidateAll(opts: InvalidateAllOptions): Promise<CacheInvalidateResult>;
|
|
102
|
+
/**
|
|
103
|
+
* Bulk-invalidate many URLs in a single round-trip. Path-string forms
|
|
104
|
+
* use the current request context's host; absolute URLs are scoped
|
|
105
|
+
* individually.
|
|
106
|
+
*
|
|
107
|
+
* Returns a SUMMARY envelope with the total deleted count. Per-URL
|
|
108
|
+
* results are also available on the optional `results` field of the
|
|
109
|
+
* returned object (not in the result type return for the common case;
|
|
110
|
+
* use the internal endpoint if you need per-URL precision).
|
|
111
|
+
*/
|
|
112
|
+
export declare function invalidateMany(urls: Array<string | URL>): Promise<CacheInvalidateResult>;
|
|
113
|
+
/** The full cache namespace exported via the `cache` named export from
|
|
114
|
+
* `@run402/functions`. */
|
|
115
|
+
export declare const cache: {
|
|
116
|
+
readonly invalidate: typeof invalidate;
|
|
117
|
+
readonly invalidatePrefix: typeof invalidatePrefix;
|
|
118
|
+
readonly invalidateAll: typeof invalidateAll;
|
|
119
|
+
readonly invalidateMany: typeof invalidateMany;
|
|
120
|
+
};
|
|
121
|
+
export type Cache = typeof cache;
|
|
122
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAKH,0DAA0D;AAC1D,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB;;yDAEqD;IACrD,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb;2BACuB;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,qBAAa,kCAAmC,SAAQ,KAAK;IAC3D,QAAQ,CAAC,IAAI,2CAA2C;IACxD,QAAQ,CAAC,IAAI,qEAAqE;IAClF,QAAQ,CAAC,YAAY,mKAC6I;;CASnK;AAED;;;;GAIG;AACH,qBAAa,mCAAoC,SAAQ,KAAK;IAC5D,QAAQ,CAAC,IAAI,4CAA4C;IACzD,QAAQ,CAAC,IAAI,sEAAsE;IACnF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;gBAElB,IAAI,EAAE,MAAM;CAMzB;AA4DD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAsCxF;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAepG;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAW9F;AAED;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA0B9F;AAED;2BAC2B;AAC3B,eAAO,MAAM,KAAK;;;;;CAKR,CAAC;AAEX,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC"}
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSR cache invalidation SDK — capability `ssr-isr-cache`.
|
|
3
|
+
*
|
|
4
|
+
* Server-side (function-context) only. Reads the current request's
|
|
5
|
+
* project_id and host from `getCurrentContext()` (AsyncLocalStorage) to
|
|
6
|
+
* scope invalidations safely.
|
|
7
|
+
*
|
|
8
|
+
* Four operations:
|
|
9
|
+
*
|
|
10
|
+
* - `cache.invalidate(urlOrPath)` — delete cache rows for a specific
|
|
11
|
+
* path (or absolute URL). Returns `{ deleted, generation, host, path }`.
|
|
12
|
+
* - `cache.invalidatePrefix({ host, prefix })` — delete all rows
|
|
13
|
+
* under a path prefix on the given host.
|
|
14
|
+
* - `cache.invalidateAll({ host })` — delete all rows on the given
|
|
15
|
+
* host. Atomic with a generation bump.
|
|
16
|
+
* - `cache.invalidateMany(urls)` — bulk invalidate in one round-trip.
|
|
17
|
+
*
|
|
18
|
+
* Host authorization: absolute-URL forms (or explicit `host` in the
|
|
19
|
+
* options bag) MUST be hosts owned by the caller's authenticated
|
|
20
|
+
* project. Cross-project hosts throw `R402_CACHE_INVALIDATION_HOST_FORBIDDEN`.
|
|
21
|
+
*
|
|
22
|
+
* Path-string form requires an active request context (the SDK reads
|
|
23
|
+
* the current host from ALS); calling it outside a handler throws
|
|
24
|
+
* `R402_CACHE_INVALIDATION_HOST_REQUIRED`.
|
|
25
|
+
*
|
|
26
|
+
* Tag-based invalidation is NOT in v1; future v1.5 work.
|
|
27
|
+
*
|
|
28
|
+
* @see openspec/changes/astro-ssr-runtime/specs/ssr-isr-cache/spec.md
|
|
29
|
+
*/
|
|
30
|
+
import { config } from "./config.js";
|
|
31
|
+
import { getCurrentContext } from "./runtime-context.js";
|
|
32
|
+
/**
|
|
33
|
+
* Structured error thrown when path-string form is called outside an
|
|
34
|
+
* active request context. The caller has no host to scope against.
|
|
35
|
+
* Use the absolute-URL form instead OR move the call into a handler.
|
|
36
|
+
*/
|
|
37
|
+
export class CacheInvalidationHostRequiredError extends Error {
|
|
38
|
+
code = "R402_CACHE_INVALIDATION_HOST_REQUIRED";
|
|
39
|
+
docs = "https://docs.run402.com/cache/errors#invalidation-host-required";
|
|
40
|
+
suggestedFix = "Pass an absolute URL (e.g., `new URL('https://eagles.kychon.com/the-guys')`) OR move the call into a request handler so the SDK can resolve the current host.";
|
|
41
|
+
constructor() {
|
|
42
|
+
super("cache.invalidate(path) called outside a request context. " +
|
|
43
|
+
"Path-string form needs the current host from request scope.");
|
|
44
|
+
this.name = "CacheInvalidationHostRequiredError";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Structured error thrown when an invalidation targets a host that is
|
|
49
|
+
* not owned by the caller's authenticated project. Prevents project A
|
|
50
|
+
* from invalidating project B's cache.
|
|
51
|
+
*/
|
|
52
|
+
export class CacheInvalidationHostForbiddenError extends Error {
|
|
53
|
+
code = "R402_CACHE_INVALIDATION_HOST_FORBIDDEN";
|
|
54
|
+
docs = "https://docs.run402.com/cache/errors#invalidation-host-forbidden";
|
|
55
|
+
host;
|
|
56
|
+
suggestedFix;
|
|
57
|
+
constructor(host) {
|
|
58
|
+
super(`host ${host} is not owned by the current project`);
|
|
59
|
+
this.name = "CacheInvalidationHostForbiddenError";
|
|
60
|
+
this.host = host;
|
|
61
|
+
this.suggestedFix = `Use a host attached to your project. List your project's hosts with \`r.domains.list()\` or check Run402 dashboard. The cache layer rejects cross-project invalidations to preserve tenant isolation.`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Internal: call the gateway's cache invalidation endpoint with the
|
|
66
|
+
* resolved scope. The gateway enforces project ownership of the host
|
|
67
|
+
* server-side; this function relies on that enforcement but ALSO
|
|
68
|
+
* validates the response shape.
|
|
69
|
+
*/
|
|
70
|
+
async function callCacheInvalidate(body) {
|
|
71
|
+
const url = `${config.API_BASE}/cache/v1/invalidate`;
|
|
72
|
+
const response = await fetch(url, {
|
|
73
|
+
method: "POST",
|
|
74
|
+
headers: {
|
|
75
|
+
"content-type": "application/json",
|
|
76
|
+
// The gateway resolves the caller's project from the service key.
|
|
77
|
+
// Cache invalidation is server-side only; the SDK is bundled into
|
|
78
|
+
// user functions and runs with RUN402_SERVICE_KEY in scope.
|
|
79
|
+
Authorization: "Bearer " + config.SERVICE_KEY,
|
|
80
|
+
},
|
|
81
|
+
body: JSON.stringify(body),
|
|
82
|
+
});
|
|
83
|
+
if (response.status === 403) {
|
|
84
|
+
const errBody = (await response.json().catch(() => ({})));
|
|
85
|
+
const host = typeof errBody.host === "string" ? errBody.host : (body.host ?? "");
|
|
86
|
+
throw new CacheInvalidationHostForbiddenError(host);
|
|
87
|
+
}
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
const errBody = await response.text().catch(() => "");
|
|
90
|
+
throw new Error(`cache.invalidate(${body.kind}) failed: HTTP ${response.status} ${errBody.slice(0, 200)}`);
|
|
91
|
+
}
|
|
92
|
+
const json = (await response.json());
|
|
93
|
+
return {
|
|
94
|
+
deleted: json.deleted,
|
|
95
|
+
generation: BigInt(json.generation),
|
|
96
|
+
host: json.host,
|
|
97
|
+
path: json.path,
|
|
98
|
+
results: json.results?.map((r) => ({
|
|
99
|
+
...r,
|
|
100
|
+
generation: BigInt(r.generation),
|
|
101
|
+
})),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Invalidate a single cached SSR response.
|
|
106
|
+
*
|
|
107
|
+
* Accepts either:
|
|
108
|
+
* - a path string (e.g., `'/the-guys'`) — the SDK reads the current
|
|
109
|
+
* host from the active request context (throws
|
|
110
|
+
* `CacheInvalidationHostRequiredError` if no context)
|
|
111
|
+
* - an absolute URL (e.g., `new URL('https://eagles.kychon.com/the-guys')`)
|
|
112
|
+
* — multi-host admin scenarios (host MUST be owned by caller's
|
|
113
|
+
* project; throws `CacheInvalidationHostForbiddenError` otherwise)
|
|
114
|
+
*
|
|
115
|
+
* Deletes GET and HEAD rows for ALL locales and ALL release ids matching
|
|
116
|
+
* the exact canonical pathname + normalized search on the target host.
|
|
117
|
+
*
|
|
118
|
+
* The DELETE and the per-(project, host) generation increment happen in
|
|
119
|
+
* the same transaction; the post-increment generation is returned.
|
|
120
|
+
*/
|
|
121
|
+
export async function invalidate(urlOrPath) {
|
|
122
|
+
let host;
|
|
123
|
+
let path;
|
|
124
|
+
if (urlOrPath instanceof URL) {
|
|
125
|
+
host = urlOrPath.host.toLowerCase();
|
|
126
|
+
path = urlOrPath.pathname + urlOrPath.search;
|
|
127
|
+
}
|
|
128
|
+
else if (typeof urlOrPath === "string" && urlOrPath.startsWith("http://")) {
|
|
129
|
+
// Allow http:// only for local-dev parity, but the gateway will
|
|
130
|
+
// typically reject it.
|
|
131
|
+
const u = new URL(urlOrPath);
|
|
132
|
+
host = u.host.toLowerCase();
|
|
133
|
+
path = u.pathname + u.search;
|
|
134
|
+
}
|
|
135
|
+
else if (typeof urlOrPath === "string" && urlOrPath.startsWith("https://")) {
|
|
136
|
+
const u = new URL(urlOrPath);
|
|
137
|
+
host = u.host.toLowerCase();
|
|
138
|
+
path = u.pathname + u.search;
|
|
139
|
+
}
|
|
140
|
+
else if (typeof urlOrPath === "string" && urlOrPath.startsWith("/")) {
|
|
141
|
+
// Path-string form: requires active request context to resolve host.
|
|
142
|
+
const ctx = getCurrentContext();
|
|
143
|
+
if (ctx === undefined || !ctx.active.value) {
|
|
144
|
+
throw new CacheInvalidationHostRequiredError();
|
|
145
|
+
}
|
|
146
|
+
host = ctx.host.toLowerCase();
|
|
147
|
+
path = urlOrPath;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
throw new Error(`cache.invalidate: argument must be a URL, an "https://" URL string, or a path starting with "/". Got: ${typeof urlOrPath === "string" ? `"${urlOrPath}"` : typeof urlOrPath}`);
|
|
151
|
+
}
|
|
152
|
+
const result = await callCacheInvalidate({ kind: "exact", host, path });
|
|
153
|
+
return {
|
|
154
|
+
deleted: result.deleted,
|
|
155
|
+
generation: result.generation,
|
|
156
|
+
host: result.host,
|
|
157
|
+
path: result.path ?? path,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Invalidate all cache rows under a path prefix on the given host.
|
|
162
|
+
*/
|
|
163
|
+
export async function invalidatePrefix(opts) {
|
|
164
|
+
if (!opts.host)
|
|
165
|
+
throw new Error("cache.invalidatePrefix: host is required");
|
|
166
|
+
if (!opts.prefix || !opts.prefix.startsWith("/")) {
|
|
167
|
+
throw new Error("cache.invalidatePrefix: prefix must start with '/'");
|
|
168
|
+
}
|
|
169
|
+
const result = await callCacheInvalidate({
|
|
170
|
+
kind: "prefix",
|
|
171
|
+
host: opts.host.toLowerCase(),
|
|
172
|
+
prefix: opts.prefix,
|
|
173
|
+
});
|
|
174
|
+
return {
|
|
175
|
+
deleted: result.deleted,
|
|
176
|
+
generation: result.generation,
|
|
177
|
+
host: result.host,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Invalidate all cache rows for the given host (entire-host purge).
|
|
182
|
+
* Useful for catastrophic content changes (nav restructure, layout
|
|
183
|
+
* update, etc.) where targeted invalidation would be impractical.
|
|
184
|
+
*/
|
|
185
|
+
export async function invalidateAll(opts) {
|
|
186
|
+
if (!opts.host)
|
|
187
|
+
throw new Error("cache.invalidateAll: host is required");
|
|
188
|
+
const result = await callCacheInvalidate({
|
|
189
|
+
kind: "all",
|
|
190
|
+
host: opts.host.toLowerCase(),
|
|
191
|
+
});
|
|
192
|
+
return {
|
|
193
|
+
deleted: result.deleted,
|
|
194
|
+
generation: result.generation,
|
|
195
|
+
host: result.host,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Bulk-invalidate many URLs in a single round-trip. Path-string forms
|
|
200
|
+
* use the current request context's host; absolute URLs are scoped
|
|
201
|
+
* individually.
|
|
202
|
+
*
|
|
203
|
+
* Returns a SUMMARY envelope with the total deleted count. Per-URL
|
|
204
|
+
* results are also available on the optional `results` field of the
|
|
205
|
+
* returned object (not in the result type return for the common case;
|
|
206
|
+
* use the internal endpoint if you need per-URL precision).
|
|
207
|
+
*/
|
|
208
|
+
export async function invalidateMany(urls) {
|
|
209
|
+
if (!Array.isArray(urls) || urls.length === 0) {
|
|
210
|
+
return { deleted: 0, generation: 0n, host: "" };
|
|
211
|
+
}
|
|
212
|
+
// Resolve each URL to its absolute form, using the current context
|
|
213
|
+
// for path-only entries.
|
|
214
|
+
const ctx = getCurrentContext();
|
|
215
|
+
const resolved = urls.map((u) => {
|
|
216
|
+
if (u instanceof URL)
|
|
217
|
+
return u.toString();
|
|
218
|
+
if (u.startsWith("https://") || u.startsWith("http://"))
|
|
219
|
+
return u;
|
|
220
|
+
if (u.startsWith("/")) {
|
|
221
|
+
if (ctx === undefined || !ctx.active.value) {
|
|
222
|
+
throw new CacheInvalidationHostRequiredError();
|
|
223
|
+
}
|
|
224
|
+
return `https://${ctx.host}${u}`;
|
|
225
|
+
}
|
|
226
|
+
throw new Error(`cache.invalidateMany: invalid entry "${u}"`);
|
|
227
|
+
});
|
|
228
|
+
const result = await callCacheInvalidate({ kind: "many", urls: resolved });
|
|
229
|
+
return {
|
|
230
|
+
deleted: result.deleted,
|
|
231
|
+
generation: result.generation,
|
|
232
|
+
host: result.host ?? "",
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
/** The full cache namespace exported via the `cache` named export from
|
|
236
|
+
* `@run402/functions`. */
|
|
237
|
+
export const cache = {
|
|
238
|
+
invalidate,
|
|
239
|
+
invalidatePrefix,
|
|
240
|
+
invalidateAll,
|
|
241
|
+
invalidateMany,
|
|
242
|
+
};
|
|
243
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
package/dist/db.d.ts
CHANGED
|
@@ -30,15 +30,34 @@ interface CallerDbClient {
|
|
|
30
30
|
from(table: string): QueryBuilder;
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
|
-
* Caller-context DB client. Forwards the
|
|
34
|
-
*
|
|
35
|
-
* `apikey` is the project's anon key (routing only — does not grant bypass).
|
|
33
|
+
* Caller-context DB client. Forwards the caller's Authorization header
|
|
34
|
+
* to PostgREST so RLS policies evaluate against the caller's role.
|
|
36
35
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
36
|
+
* Capability `astro-ssr-runtime` (v1.52): `db()` now accepts the request
|
|
37
|
+
* via either path:
|
|
38
|
+
*
|
|
39
|
+
* 1. **Explicit `db(req)`** — pass a Web `Request` (or Express
|
|
40
|
+
* `req.raw` equivalent). The existing v0.x call shape.
|
|
41
|
+
*
|
|
42
|
+
* 2. **Implicit `db()`** — when called with no argument, reads the
|
|
43
|
+
* Authorization header from the active AsyncLocalStorage request
|
|
44
|
+
* context (populated by the SSR Lambda runtime in `@run402/astro`).
|
|
45
|
+
* This is what makes `await db().from(...)` work naturally inside
|
|
46
|
+
* Astro `[slug].astro` frontmatter without explicit plumbing.
|
|
47
|
+
*
|
|
48
|
+
* `apikey` is the project's anon key (routing only — does not grant
|
|
49
|
+
* bypass). If no Authorization is present (in either form), the request
|
|
50
|
+
* is sent with just the anon apikey; PostgREST resolves role=anon and
|
|
51
|
+
* RLS decides whether the query succeeds or returns 401/403.
|
|
52
|
+
*
|
|
53
|
+
* Outside an active request context (module scope, background timer
|
|
54
|
+
* past response materialization), `db()` (no arg) still works — it
|
|
55
|
+
* sends with no Authorization, exactly as the v0.x behavior with a
|
|
56
|
+
* Request that has no auth header. SDK functions that REQUIRE a context
|
|
57
|
+
* (like `cache.invalidate` path-form) throw `R402_SDK_OUTSIDE_REQUEST_CONTEXT`
|
|
58
|
+
* separately.
|
|
40
59
|
*/
|
|
41
|
-
export declare function db(req
|
|
60
|
+
export declare function db(req?: Request): CallerDbClient;
|
|
42
61
|
interface AdminDbClient {
|
|
43
62
|
from(table: string): QueryBuilder;
|
|
44
63
|
sql(query: string, params?: unknown[]): Promise<Record<string, unknown>[]>;
|
package/dist/db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAGA,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,YAAY;;gBASX,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB;IAOjD,MAAM,CAAC,OAAO,SAAM,GAAG,IAAI;IAK3B,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKhD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKjD,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKhD,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKhD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKjD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKjD,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAK3C,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAK5C,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,IAAI;IAKrD,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,SAAgB,EAAE;;KAAK,GAAG,IAAI;IAKtD,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK1B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK3B,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI;IAMvE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAM3C,MAAM,IAAI,IAAI;IAKd,IAAI,CACF,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,IAAI,EACnD,MAAM,EAAE,CAAC,MAAM,EAAE,KAAK,KAAK,IAAI,GAC9B,IAAI;CA6BR;AAgBD,UAAU,cAAc;IACtB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,cAAc,CAkBhD;AAED,UAAU,aAAa;IACrB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC;IAClC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;CAC5E;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,IAAI,aAAa,CA+BvC"}
|
package/dist/db.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { config } from "./config.js";
|
|
2
|
+
import { getCurrentContext } from "./runtime-context.js";
|
|
2
3
|
export class QueryBuilder {
|
|
3
4
|
#table;
|
|
4
5
|
#params = new URLSearchParams();
|
|
@@ -111,21 +112,50 @@ function extractAuth(req) {
|
|
|
111
112
|
const auth = req.headers.get("authorization") ?? req.headers.get("Authorization");
|
|
112
113
|
return auth ?? undefined;
|
|
113
114
|
}
|
|
115
|
+
function extractAuthFromAls() {
|
|
116
|
+
const ctx = getCurrentContext();
|
|
117
|
+
if (ctx === undefined)
|
|
118
|
+
return undefined;
|
|
119
|
+
const headers = ctx.request.headers;
|
|
120
|
+
const raw = headers["authorization"] ?? headers["Authorization"];
|
|
121
|
+
if (Array.isArray(raw))
|
|
122
|
+
return raw[0];
|
|
123
|
+
return raw ?? undefined;
|
|
124
|
+
}
|
|
114
125
|
/**
|
|
115
|
-
* Caller-context DB client. Forwards the
|
|
116
|
-
*
|
|
117
|
-
*
|
|
126
|
+
* Caller-context DB client. Forwards the caller's Authorization header
|
|
127
|
+
* to PostgREST so RLS policies evaluate against the caller's role.
|
|
128
|
+
*
|
|
129
|
+
* Capability `astro-ssr-runtime` (v1.52): `db()` now accepts the request
|
|
130
|
+
* via either path:
|
|
131
|
+
*
|
|
132
|
+
* 1. **Explicit `db(req)`** — pass a Web `Request` (or Express
|
|
133
|
+
* `req.raw` equivalent). The existing v0.x call shape.
|
|
134
|
+
*
|
|
135
|
+
* 2. **Implicit `db()`** — when called with no argument, reads the
|
|
136
|
+
* Authorization header from the active AsyncLocalStorage request
|
|
137
|
+
* context (populated by the SSR Lambda runtime in `@run402/astro`).
|
|
138
|
+
* This is what makes `await db().from(...)` work naturally inside
|
|
139
|
+
* Astro `[slug].astro` frontmatter without explicit plumbing.
|
|
140
|
+
*
|
|
141
|
+
* `apikey` is the project's anon key (routing only — does not grant
|
|
142
|
+
* bypass). If no Authorization is present (in either form), the request
|
|
143
|
+
* is sent with just the anon apikey; PostgREST resolves role=anon and
|
|
144
|
+
* RLS decides whether the query succeeds or returns 401/403.
|
|
118
145
|
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
146
|
+
* Outside an active request context (module scope, background timer
|
|
147
|
+
* past response materialization), `db()` (no arg) still works — it
|
|
148
|
+
* sends with no Authorization, exactly as the v0.x behavior with a
|
|
149
|
+
* Request that has no auth header. SDK functions that REQUIRE a context
|
|
150
|
+
* (like `cache.invalidate` path-form) throw `R402_SDK_OUTSIDE_REQUEST_CONTEXT`
|
|
151
|
+
* separately.
|
|
122
152
|
*/
|
|
123
153
|
export function db(req) {
|
|
124
154
|
if (!config.ANON_KEY) {
|
|
125
|
-
throw new Error("db(
|
|
155
|
+
throw new Error("db() requires RUN402_ANON_KEY in the Lambda environment. " +
|
|
126
156
|
"Redeploy this function via the gateway to pick up the new env var.");
|
|
127
157
|
}
|
|
128
|
-
const authorization = extractAuth(req);
|
|
158
|
+
const authorization = req !== undefined ? extractAuth(req) : extractAuthFromAls();
|
|
129
159
|
const anonKey = config.ANON_KEY;
|
|
130
160
|
return {
|
|
131
161
|
from(table) {
|
package/dist/db.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
|