@run402/functions 2.4.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-context.d.ts","sourceRoot":"","sources":["../src/runtime-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD;oCACoC;AACpC,MAAM,WAAW,iBAAiB;IAChC,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB;yBACqB;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;qDACiD;IACjD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;yDACqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb;;;uBAGmB;IACnB,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;KACxD,CAAC;IACF;;;oBAGgB;IAChB,kBAAkB,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;IACvC;;;8CAG0C;IAC1C,MAAM,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;CAC5B;AAED;oEACoE;AACpE,eAAO,MAAM,GAAG,sCAA6C,CAAC;AAE9D;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,iBAAiB,GAAG,SAAS,CAEjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,oBAAoB,GAAG,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,oBAAoB,GAAG,QAAQ,CAAC,CAAC,EACrI,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAC7B,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAOhB;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,QAAQ,CAAC,IAAI,sCAAsC;IACnD,QAAQ,CAAC,IAAI,gEAAgE;IAC7E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,kBAAkB,CAAC;gBAEtC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,kBAAkB;CAc1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB,CAS3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAKvC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAUjD,CAAC;AAEH;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO,EAC/D,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,GAChC,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAe7B"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * SSR request context primitive — capability `functions-sdk-auth-model`.
3
+ *
4
+ * The SSR Lambda runtime establishes this context via `als.run(...)`
5
+ * BEFORE importing or executing the Astro server bundle (or any other
6
+ * user-supplied code). SDK functions that need request-scoped state
7
+ * (`db()`, `getUser()`, `cache.*`, `assets.*`, `email.*`, `ai.*`) read
8
+ * from `getCurrentContext()` rather than requiring explicit context
9
+ * parameters at the call site.
10
+ *
11
+ * The `active` flag exists because Node's AsyncLocalStorage propagates
12
+ * into timers, microtasks, and unawaited promises created inside
13
+ * `als.run()`. Without an explicit flag, a `setTimeout(() => db()..., 60_000)`
14
+ * scheduled inside a handler would, when the timer fires later, still
15
+ * observe an `als.getStore()` pointing at the (now-completed) request —
16
+ * leading to stale or incorrect SDK behavior. The SSR runtime sets
17
+ * `active.value = false` IMMEDIATELY after the response body is fully
18
+ * materialized; SDK functions check the flag and throw
19
+ * `R402_SDK_OUTSIDE_REQUEST_CONTEXT` when it's false.
20
+ *
21
+ * Likewise `cacheBypassTainted.value` is set to `true` by `getUser()`
22
+ * and payment-primitive SDK calls during render; the SSR runtime returns
23
+ * its final value to the gateway in the Lambda response metadata envelope
24
+ * (NOT via in-process ALS — the gateway runs in a different process).
25
+ *
26
+ * @see openspec/changes/astro-ssr-runtime/specs/functions-sdk-auth-model/spec.md
27
+ * @see openspec/changes/astro-ssr-runtime/specs/routed-http-functions/spec.md
28
+ */
29
+ import { AsyncLocalStorage } from "node:async_hooks";
30
+ /** The shared ALS instance. Modules in @run402/functions read from
31
+ * this; the SSR Lambda runtime (in @run402/astro) writes to it. */
32
+ export const als = new AsyncLocalStorage();
33
+ /**
34
+ * Read the current request context, or `undefined` if no SSR request
35
+ * is in flight. SDK functions check both this AND `active.value` to
36
+ * decide whether to honor the call.
37
+ */
38
+ export function getCurrentContext() {
39
+ return als.getStore();
40
+ }
41
+ /**
42
+ * Establish a request context and run a callback inside it. The SSR
43
+ * Lambda runtime calls this exactly once per invocation, wrapping the
44
+ * full `App.render(request)` + body-materialization sequence.
45
+ *
46
+ * The `active` flag is set to `true` initially; the caller (the SSR
47
+ * runtime) flips it to `false` after the response body is materialized.
48
+ * Don't call this from user code — it's the runtime's primitive.
49
+ */
50
+ export function runWithContext(context, callback) {
51
+ const full = {
52
+ ...context,
53
+ cacheBypassTainted: context.cacheBypassTainted ?? { value: false },
54
+ active: context.active ?? { value: true },
55
+ };
56
+ return als.run(full, callback);
57
+ }
58
+ /**
59
+ * Throw a structured `R402_SDK_OUTSIDE_REQUEST_CONTEXT` error. Used by
60
+ * SDK functions when they're invoked with no ALS store OR while the
61
+ * context has been marked inactive.
62
+ *
63
+ * Per the api-error-envelope spec, the thrown error carries:
64
+ * - `code: 'R402_SDK_OUTSIDE_REQUEST_CONTEXT'`
65
+ * - `message`: names the SDK function and the cause
66
+ * - `suggestedFix`: recommends moving the call inside a handler OR
67
+ * not scheduling background work that outlives the response
68
+ * - `docs`: `https://docs.run402.com/sdk/errors#outside-request-context`
69
+ */
70
+ export class Run402OutsideRequestContextError extends Error {
71
+ code = "R402_SDK_OUTSIDE_REQUEST_CONTEXT";
72
+ docs = "https://docs.run402.com/sdk/errors#outside-request-context";
73
+ suggestedFix;
74
+ sdkFunction;
75
+ cause;
76
+ constructor(sdkFunction, cause) {
77
+ const causeMsg = cause === "no_context"
78
+ ? "no active request context (called from module scope or outside a handler)"
79
+ : "request context marked inactive (called from a timer or unawaited promise after response completion)";
80
+ super(`${sdkFunction}: ${causeMsg}`);
81
+ this.name = "Run402OutsideRequestContextError";
82
+ this.sdkFunction = sdkFunction;
83
+ this.cause = cause;
84
+ this.suggestedFix =
85
+ cause === "no_context"
86
+ ? `Move the ${sdkFunction} call inside an HTTP request handler or Astro frontmatter. SDK functions cannot resolve project context outside an active request.`
87
+ : `The request that called ${sdkFunction} has already returned. Don't schedule SDK calls in setTimeout / setInterval / unawaited promises that outlive the handler.`;
88
+ }
89
+ }
90
+ /**
91
+ * Assert that a request context is active. Returns the context on
92
+ * success; throws `Run402OutsideRequestContextError` on failure.
93
+ *
94
+ * SDK functions call this as the first line, e.g.:
95
+ *
96
+ * const ctx = requireActiveContext("db");
97
+ * // use ctx.projectId, ctx.request.headers, etc.
98
+ */
99
+ export function requireActiveContext(sdkFunction) {
100
+ const ctx = als.getStore();
101
+ if (ctx === undefined) {
102
+ throw new Run402OutsideRequestContextError(sdkFunction, "no_context");
103
+ }
104
+ if (!ctx.active.value) {
105
+ throw new Run402OutsideRequestContextError(sdkFunction, "context_inactive");
106
+ }
107
+ return ctx;
108
+ }
109
+ /**
110
+ * Flip the cache-bypass taint flag on the current context. Called by
111
+ * `getUser()` and payment-primitive SDK calls to signal that the
112
+ * rendered response depends on request-scoped auth state and therefore
113
+ * MUST NOT be cached publicly.
114
+ *
115
+ * No-op if there is no active context (rather than throwing — the taint
116
+ * is moot when there's no cache layer to inform).
117
+ */
118
+ export function taintCacheBypass() {
119
+ const ctx = als.getStore();
120
+ if (ctx !== undefined) {
121
+ ctx.cacheBypassTainted.value = true;
122
+ }
123
+ }
124
+ /**
125
+ * Canonical registry of SDK functions that are "payment primitives" for
126
+ * cache-taint purposes. Calling any of these inside an active request
127
+ * context MUST flip `cacheBypassTainted.value = true`. The set is
128
+ * defined here so the spec, runtime, and docs share one source of truth.
129
+ *
130
+ * To add a new payment primitive: append to this set AND ensure the
131
+ * function's implementation calls `taintCacheBypass()` on every entry.
132
+ *
133
+ * @see openspec/changes/astro-ssr-runtime/specs/functions-sdk-auth-model/spec.md
134
+ */
135
+ export const PAYMENT_PRIMITIVES = new Set([
136
+ // None implemented yet — the registry exists so the contract is
137
+ // explicit and discoverable. Payment-primitive SDK functions added
138
+ // in future changes (per Run402 x402/MPP roadmap) will register here
139
+ // as they land.
140
+ // Example future entries (commented to keep the set actually empty
141
+ // until the helpers ship):
142
+ // "payments.require",
143
+ // "payments.gate",
144
+ // "payments.fulfill",
145
+ ]);
146
+ /**
147
+ * Helper for payment-primitive implementations. Wraps the body in a
148
+ * taint-on-entry call so the registry contract is enforced at the
149
+ * SDK layer rather than relying on each implementation to remember.
150
+ *
151
+ * Example:
152
+ * export const requirePayment = withPaymentTaint("payments.require", async (opts) => {
153
+ * // ... actual implementation
154
+ * });
155
+ */
156
+ export function withPaymentTaint(name, impl) {
157
+ if (!PAYMENT_PRIMITIVES.has(name)) {
158
+ // Doesn't throw — adding a new primitive is a code change and the
159
+ // dev sees this on first invocation. Throwing would block legitimate
160
+ // experimentation. But emit a warning so the registry stays correct.
161
+ // eslint-disable-next-line no-console
162
+ console.warn(`[run402] withPaymentTaint("${name}") called but "${name}" is not in PAYMENT_PRIMITIVES. ` +
163
+ `Add it to packages/functions/src/runtime-context.ts to keep the cache-taint registry consistent.`);
164
+ }
165
+ return (...args) => {
166
+ taintCacheBypass();
167
+ return impl(...args);
168
+ };
169
+ }
170
+ //# sourceMappingURL=runtime-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-context.js","sourceRoot":"","sources":["../src/runtime-context.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@run402/functions",
3
- "version": "2.4.1",
3
+ "version": "2.6.0",
4
4
  "description": "In-function helper library for Run402 serverless functions — db, adminDb, getUser, email, ai, assets. Auto-bundled into deployed functions; also installable for local TypeScript autocomplete.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,12 +23,7 @@
23
23
  "engines": {
24
24
  "node": ">=18"
25
25
  },
26
- "dependencies": {
27
- "jsonwebtoken": "^9.0.2"
28
- },
29
- "devDependencies": {
30
- "@types/jsonwebtoken": "^9.0.9"
31
- },
26
+ "devDependencies": {},
32
27
  "repository": {
33
28
  "type": "git",
34
29
  "url": "git+https://github.com/kychee-com/run402.git",