@run402/functions 1.51.0 → 1.51.1

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.
Files changed (2) hide show
  1. package/README.md +182 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,182 @@
1
+ # @run402/functions
2
+
3
+ In-function helper library for [Run402](https://run402.com) serverless functions. Imported _inside_ a deployed function — gives you typed access to the caller's database (RLS-respecting) and the project's admin database, the caller's auth, the project's mailbox, and AI helpers.
4
+
5
+ ```ts
6
+ import { db, adminDb, getUser, email, ai } from "@run402/functions";
7
+
8
+ export default async (req: Request) => {
9
+ const user = await getUser(req);
10
+ if (!user) return new Response("unauthorized", { status: 401 });
11
+
12
+ const mine = await db(req).from("items").select("*").eq("user_id", user.id);
13
+ return Response.json(mine);
14
+ };
15
+ ```
16
+
17
+ This package is **auto-bundled into every deployed function zip at deploy time** — you don't need to declare it in `--deps`. Install it locally only when you want **TypeScript autocomplete** in your editor while authoring function code.
18
+
19
+ ## Install (local autocomplete)
20
+
21
+ ```bash
22
+ npm install @run402/functions
23
+ ```
24
+
25
+ ## The two DB clients
26
+
27
+ The most important distinction in this library: **`db(req)` runs as the caller, `adminDb()` bypasses RLS.**
28
+
29
+ ### `db(req).from(table)` — caller-context
30
+
31
+ Forwards the request's `Authorization` header to PostgREST. Row-Level Security policies evaluate against the caller's role — `anon`, `authenticated`, `project_admin`, or whatever the JWT carries. **This is the default choice.** Routes to `/rest/v1/*`.
32
+
33
+ ```ts
34
+ // Reads everything the caller is authorized to see — could be 0 rows for unauthenticated callers.
35
+ const mine = await db(req).from("items").select("title, done").eq("user_id", user.id);
36
+
37
+ // Writes go through RLS too. If the policy says the caller can't insert, it errors.
38
+ const [created] = await db(req).from("items").insert({ title: "New", done: false });
39
+ ```
40
+
41
+ ### `adminDb().from(table)` — bypass RLS
42
+
43
+ Uses the project's `service_key`. Returns *all* rows regardless of RLS. Routes to `/admin/v1/rest/*` (the gateway rejects `role=service_role` on `/rest/v1/*`, so bypass traffic lives on its own surface).
44
+
45
+ Use only when the function acts on behalf of the **platform**, not the **caller** — audit logs, cron cleanup, webhook handlers, fan-out writes after a Stripe event.
46
+
47
+ ```ts
48
+ // Audit log — capture every event regardless of who triggered the function.
49
+ await adminDb().from("audit_log").insert({ event: "payment.succeeded", user_id: userId });
50
+
51
+ // Cron cleanup — there's no caller to evaluate RLS against.
52
+ await adminDb()
53
+ .from("sessions")
54
+ .delete()
55
+ .lt("expires_at", new Date().toISOString());
56
+ ```
57
+
58
+ ### Fluent surface (same on both clients)
59
+
60
+ ```ts
61
+ .select(cols?)
62
+ .eq(col, val) / .neq() / .gt() / .lt() / .gte() / .lte()
63
+ .like(col, pattern) / .ilike(col, pattern)
64
+ .in(col, [vals])
65
+ .order(col, { ascending? })
66
+ .limit(n) / .offset(n)
67
+
68
+ // Writes return arrays of affected rows.
69
+ .insert(obj | obj[])
70
+ .update(obj) // chain with .eq() to scope
71
+ .delete() // chain with .eq() to scope
72
+
73
+ // Column narrowing on writes:
74
+ .insert({ title: "x" }).select("id, title")
75
+ ```
76
+
77
+ ### `adminDb().sql(query, params?)` — raw SQL, always BYPASSRLS
78
+
79
+ ```ts
80
+ const { rows, rowCount } = await adminDb().sql(
81
+ "SELECT count(*)::int AS n FROM items WHERE user_id = $1",
82
+ [userId],
83
+ );
84
+ // { status: "ok", schema: "p0001", rows: [{ n: 42 }], rowCount: 1 }
85
+ ```
86
+
87
+ For SELECT, `rows` is the result set and `rowCount` is the row count. For INSERT/UPDATE/DELETE, `rows` is `[]` and `rowCount` is the affected count.
88
+
89
+ ## `getUser(req)` — caller identity
90
+
91
+ Verifies the caller's JWT and returns the user, or `null` for unauthenticated requests.
92
+
93
+ ```ts
94
+ const user = await getUser(req);
95
+ if (!user) return new Response("unauthorized", { status: 401 });
96
+ // user: { id: string, email: string, role: "authenticated" | "project_admin" | ... }
97
+ ```
98
+
99
+ The function's own `RUN402_PROJECT_ID` is used to scope the verification.
100
+
101
+ ## `email.send(...)` — send mail from the project's mailbox
102
+
103
+ Auto-discovers the project's mailbox on first call (the project must already have one — create it once with `run402 email create <slug>` or the `create_mailbox` MCP tool). After that the mailbox id is cached for the function's lifetime.
104
+
105
+ ```ts
106
+ // Template mode
107
+ await email.send({
108
+ to: "user@example.com",
109
+ template: "notification",
110
+ variables: { project_name: "My App", message: "Hello!" },
111
+ });
112
+
113
+ // Raw HTML mode
114
+ await email.send({
115
+ to: "user@example.com",
116
+ subject: "Welcome!",
117
+ html: "<h1>Hi</h1>",
118
+ from_name: "My App",
119
+ });
120
+ ```
121
+
122
+ Templates: `project_invite` (`project_name`, `invite_url`), `magic_link` (`project_name`, `link_url`, `expires_in`), `notification` (`project_name`, `message` ≤ 500 chars). Throws on rate limit, suppression, or no-mailbox.
123
+
124
+ ## `ai.translate` / `ai.moderate`
125
+
126
+ ```ts
127
+ const { text, from } = await ai.translate("Hello world", {
128
+ to: "es",
129
+ context: "marketing tagline",
130
+ });
131
+
132
+ const { flagged, categories } = await ai.moderate("Some user-generated text");
133
+ ```
134
+
135
+ Translation requires the AI Translation add-on on the project; moderation is free for all projects.
136
+
137
+ ## Static-site generation (build-time use)
138
+
139
+ The same library works at build time for static-site generation if you set `RUN402_SERVICE_KEY` and `RUN402_PROJECT_ID` in your `.env`:
140
+
141
+ ```ts
142
+ // build-time render — feed the page with current data
143
+ const items = await adminDb().from("items").select("title, slug").order("created_at", { ascending: false });
144
+ ```
145
+
146
+ Use `adminDb()` (not `db(req)`) here — there's no incoming request to forward.
147
+
148
+ ## Imports auto-resolved
149
+
150
+ Inside a deployed function you can `import { ... } from "@run402/functions"` directly — the gateway bundles this library plus any `--deps` you declared at deploy time. **Do not list `@run402/functions` in your `--deps`** — it's rejected. Native binary modules (`sharp`, `canvas`, native `bcrypt`, etc.) are also rejected.
151
+
152
+ The bundled version lands in the deploy response's `runtime_version` field; resolved `--deps` versions land in `deps_resolved`.
153
+
154
+ ## Errors
155
+
156
+ All helpers throw on non-2xx responses. The error message includes the HTTP status and the response body so you can branch on `code` / `category` / `retryable` (the v1.34+ agent-operable error envelope).
157
+
158
+ ## Engines
159
+
160
+ Node 22 in deployed functions. `>=18` for local use (autocomplete and SSG).
161
+
162
+ ## Other interfaces
163
+
164
+ `@run402/functions` is one of five surfaces in the [run402](https://github.com/kychee-com/run402) monorepo:
165
+
166
+ - **`@run402/functions`** (this) — in-function helper, auto-bundled
167
+ - [`@run402/sdk`](https://www.npmjs.com/package/@run402/sdk) — typed TypeScript client for the platform API
168
+ - [`run402`](https://www.npmjs.com/package/run402) — the CLI
169
+ - [`run402-mcp`](https://www.npmjs.com/package/run402-mcp) — MCP server for Claude Desktop / Cursor / Cline / Claude Code
170
+ - OpenClaw skill — script-based skill for OpenClaw agents
171
+
172
+ All five release in lockstep at the same version.
173
+
174
+ ## Links
175
+
176
+ - Run402: <https://run402.com>
177
+ - HTTP API reference: <https://run402.com/llms.txt>
178
+ - CLI reference: <https://run402.com/llms-cli.txt>
179
+
180
+ ## License
181
+
182
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@run402/functions",
3
- "version": "1.51.0",
3
+ "version": "1.51.1",
4
4
  "description": "In-function helper library for Run402 serverless functions — db, adminDb, getUser, email, ai. Auto-bundled into deployed functions; also installable for local TypeScript autocomplete.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",