@palbase/backend 1.0.0 → 2.0.2
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 +24 -0
- package/dist/{chunk-7N5ICXCB.js → chunk-L36JLUPO.js} +11 -2
- package/dist/chunk-L36JLUPO.js.map +1 -0
- package/dist/{endpoint-yn2xcrWu.d.cts → endpoint-BlcY2xNA.d.cts} +11 -11
- package/dist/{endpoint-knNIeovM.d.ts → endpoint-Djk5L6G2.d.ts} +11 -11
- package/dist/index.cjs +14 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +44 -16
- package/dist/index.d.ts +44 -16
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs +5 -1
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.js +1 -1
- package/docs/README.md +51 -0
- package/docs/background.md +56 -0
- package/docs/database.md +60 -0
- package/docs/endpoints.md +116 -0
- package/docs/errors.md +48 -0
- package/docs/events.md +59 -0
- package/docs/getting-started.md +49 -0
- package/docs/llms-full.txt +713 -0
- package/docs/llms.txt +16 -0
- package/docs/routing.md +34 -0
- package/docs/schema.md +82 -0
- package/docs/services.md +105 -0
- package/package.json +5 -3
- package/dist/chunk-7N5ICXCB.js.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# @palbase/backend
|
|
2
|
+
|
|
3
|
+
The backend SDK for Palbase. Write file-based TypeScript endpoints, schema,
|
|
4
|
+
workers, jobs, hooks, and webhooks that run in the Palbase managed runtime.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
import { defineEndpoint, z, Database } from "@palbase/backend";
|
|
8
|
+
|
|
9
|
+
export default defineEndpoint({
|
|
10
|
+
method: "POST",
|
|
11
|
+
input: z.object({ title: z.string() }),
|
|
12
|
+
output: z.object({ id: z.string() }),
|
|
13
|
+
handler: async (req) => Database.insert("todos", { title: req.input.title }),
|
|
14
|
+
});
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Documentation
|
|
18
|
+
|
|
19
|
+
Start with [`docs/README.md`](./docs/README.md). For AI coding tools, a single
|
|
20
|
+
concatenated corpus is generated at [`docs/llms-full.txt`](./docs/llms-full.txt).
|
|
21
|
+
|
|
22
|
+
## License
|
|
23
|
+
|
|
24
|
+
MIT
|
|
@@ -27,14 +27,21 @@ function makeTypedDB(schema, raw) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
// src/runtime.ts
|
|
30
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
31
|
+
var __requestALS = new AsyncLocalStorage();
|
|
30
32
|
var runtime = null;
|
|
31
33
|
function __setRuntime(services) {
|
|
32
34
|
runtime = services;
|
|
33
35
|
}
|
|
36
|
+
function __runWithRuntime(services, fn) {
|
|
37
|
+
return __requestALS.run({ runtime: services }, fn);
|
|
38
|
+
}
|
|
34
39
|
function __getRuntime() {
|
|
40
|
+
const scoped = __requestALS.getStore();
|
|
41
|
+
if (scoped) return scoped.runtime;
|
|
35
42
|
if (runtime === null) {
|
|
36
43
|
throw new Error(
|
|
37
|
-
"Palbase services accessed outside a request scope. The Database/Documents/\u2026 singletons are only available inside an endpoint handler (or after the runtime has called __setRuntime)."
|
|
44
|
+
"Palbase services accessed outside a request scope. The Database/Documents/\u2026 singletons are only available inside an endpoint handler (or after the runtime has called __runWithRuntime / __setRuntime)."
|
|
38
45
|
);
|
|
39
46
|
}
|
|
40
47
|
return runtime;
|
|
@@ -73,7 +80,9 @@ function typedDatabase(schema) {
|
|
|
73
80
|
|
|
74
81
|
export {
|
|
75
82
|
makeTypedDB,
|
|
83
|
+
__requestALS,
|
|
76
84
|
__setRuntime,
|
|
85
|
+
__runWithRuntime,
|
|
77
86
|
__getRuntime,
|
|
78
87
|
Database,
|
|
79
88
|
Documents,
|
|
@@ -85,4 +94,4 @@ export {
|
|
|
85
94
|
Flags,
|
|
86
95
|
typedDatabase
|
|
87
96
|
};
|
|
88
|
-
//# sourceMappingURL=chunk-
|
|
97
|
+
//# sourceMappingURL=chunk-L36JLUPO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/db/typed-db.ts","../src/runtime.ts"],"sourcesContent":["/**\n * typed-db.ts — Task 2: TypedDB schema-derived insert/row shapes.\n *\n * Derives INSERT and full-row TypeScript types from a `defineSchema()` result\n * and wraps the untyped runtime `DBClient` with a typed facade.\n *\n * No value-any. No `as unknown as X`. The two narrow `as` casts in\n * `makeTypedTable` are safe because:\n * - `data as Record<string, unknown>`: InsertShape<T> maps string keys to\n * typed values; all value types are subsets of `unknown`, so the cast is\n * structurally sound.\n * - `result as RowShape<T>`: The runtime DBClient returns `Record<string,\n * unknown>` which is the erased form of the typed row; we're narrowing back\n * to the precise shape that the schema declared.\n * Both casts are narrowing only (not widening) and correctness is guaranteed\n * by the schema the caller provides.\n */\n\nimport type { ColValue, ColIsOptionalOnInsert, ColumnBuilder } from \"./columns.js\";\nimport type { TableDef, SchemaDef } from \"./schema.js\";\nimport type { DBClient, TxClient } from \"../endpoint.js\";\n\n// ---------------------------------------------------------------------------\n// Key discriminators — split a column map into required vs optional keys.\n// ---------------------------------------------------------------------------\n\n/** Keys of C whose columns are required on INSERT (not nullable, no default). */\ntype RequiredKeys<C> = {\n [K in keyof C]: ColIsOptionalOnInsert<C[K]> extends true ? never : K;\n}[keyof C];\n\n/** Keys of C whose columns are optional on INSERT (nullable or has a default). */\ntype OptionalKeys<C> = {\n [K in keyof C]: ColIsOptionalOnInsert<C[K]> extends true ? K : never;\n}[keyof C];\n\n// ---------------------------------------------------------------------------\n// Public shape types — exported so callers can reference them directly.\n// ---------------------------------------------------------------------------\n\n/**\n * The TypeScript type for an INSERT payload for table `T`.\n * - Required: columns that are NOT NULL and have no DB-level default.\n * - Optional: columns that are nullable or carry a default.\n *\n * When all columns are optional, `RequiredKeys<C>` resolves to `never` and\n * the first part becomes `{}`, which is a neutral element for `&`.\n */\nexport type InsertShape<T extends TableDef> = {\n [K in RequiredKeys<T[\"columns\"]>]: ColValue<T[\"columns\"][K]>;\n} & {\n [K in OptionalKeys<T[\"columns\"]>]?: ColValue<T[\"columns\"][K]>;\n};\n\n/**\n * The TypeScript type for a full row returned by the DB for table `T`.\n * Every column is present; nullable columns resolve to `T | null`.\n */\nexport type RowShape<T extends TableDef> = {\n [K in keyof T[\"columns\"]]: ColValue<T[\"columns\"][K]>;\n};\n\n// ---------------------------------------------------------------------------\n// TypedTable + TypedDB interfaces.\n// ---------------------------------------------------------------------------\n\n/** A typed table accessor that mirrors the runtime DBClient surface. */\nexport interface TypedTable<T extends TableDef> {\n insert(data: InsertShape<T>): Promise<RowShape<T>>;\n update(id: string, data: Partial<InsertShape<T>>): Promise<RowShape<T>>;\n delete(id: string): Promise<void>;\n findById(id: string): Promise<RowShape<T> | null>;\n findMany(query?: Partial<RowShape<T>>): Promise<RowShape<T>[]>;\n}\n\n/** A typed DB facade covering all tables declared in schema `S`. */\nexport interface TypedDB<S extends SchemaDef> {\n tables: {\n [K in keyof S[\"tables\"]]: TypedTable<S[\"tables\"][K]>;\n };\n transaction<T>(fn: (tx: TypedTx<S>) => Promise<T>): Promise<T>;\n}\n\n/** Transaction-scoped typed facade: same typed tables, no nested transaction. */\nexport interface TypedTx<S extends SchemaDef> {\n tables: {\n [K in keyof S[\"tables\"]]: TypedTable<S[\"tables\"][K]>;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Runtime factory.\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a typed table accessor that delegates every call to `raw` using the\n * runtime table name string. Two narrow `as` casts bridge the mapped-type\n * shapes to/from `Record<string, unknown>` — see module-level doc comment.\n *\n * The `raw` param is typed `TxClient` (the op surface shared by `DBClient` and\n * the transaction-scoped client) because this only ever calls the five\n * insert/update/delete/findById/findMany ops — never `transaction`. This lets\n * the same factory wrap both the top-level db and a tx without any cast.\n */\nfunction makeTypedTable<T extends TableDef<Record<string, ColumnBuilder>>>(\n name: string,\n raw: TxClient,\n): TypedTable<T> {\n return {\n insert: (data: InsertShape<T>) =>\n raw.insert(name, data as Record<string, unknown>) as Promise<RowShape<T>>,\n\n update: (id: string, data: Partial<InsertShape<T>>) =>\n raw.update(name, id, data as Record<string, unknown>) as Promise<RowShape<T>>,\n\n delete: (id: string) => raw.delete(name, id),\n\n findById: (id: string) =>\n raw.findById(name, id) as Promise<RowShape<T> | null>,\n\n findMany: (query?: Partial<RowShape<T>>) =>\n raw.findMany(name, query as Record<string, unknown> | undefined) as Promise<RowShape<T>[]>,\n };\n}\n\n/**\n * Wraps a raw `DBClient` with the type-safe `TypedDB<S>` facade derived from\n * the provided schema. No behavior change — all calls delegate to `raw` with\n * the table name as a plain string.\n *\n * `buildTables` is the reusable factory that wraps any op-bearing client\n * (`TxClient` — the surface shared by `DBClient` and the transaction-scoped\n * client) into the typed tables map. It is used both for the top-level db\n * (wrapping `raw`) and inside `transaction`, where it wraps the raw `TxClient`\n * the runtime yields so the callback sees the same typed `.tables` API.\n *\n * `transaction` delegates straight to `raw.transaction`; the two narrow\n * `as TypedTx<S>` / `as TypedDB<S>` casts are single structural narrowings\n * from the dynamically-built tables object to the precise mapped type (TS\n * cannot infer through `Object.keys` iteration) — see module-level doc comment.\n */\nexport function makeTypedDB<S extends SchemaDef>(\n schema: S,\n raw: DBClient,\n): TypedDB<S> {\n function buildTables(client: TxClient): Record<string, TypedTable<TableDef>> {\n const tables = {} as Record<string, TypedTable<TableDef>>;\n for (const key of Object.keys(schema.tables)) {\n const tableDef = schema.tables[key];\n if (tableDef !== undefined) {\n tables[key] = makeTypedTable(tableDef.name, client);\n }\n }\n return tables;\n }\n\n const result = {\n tables: buildTables(raw),\n transaction: <T>(fn: (tx: TypedTx<S>) => Promise<T>): Promise<T> =>\n raw.transaction((rawTx) => fn({ tables: buildTables(rawTx) } as TypedTx<S>)),\n };\n\n // Narrow cast: `result.tables` is structurally identical to\n // TypedDB<S>[\"tables\"] — each key maps to a TypedTable for the matching\n // TableDef. TS cannot infer the mapped-type result through Object.keys\n // iteration, so a single `as` bridges the gap.\n return result as TypedDB<S>;\n}\n","/**\n * runtime.ts — request-scoped service singletons.\n *\n * The backend SDK no longer threads a `ctx` god-object through every handler.\n * Instead, endpoint authors import PascalCase service singletons directly:\n *\n * import { Database, Documents, Cache } from \"@palbase/backend\";\n *\n * export default defineEndpoint({\n * method: \"POST\",\n * handler: async (req) => {\n * const row = await Database.insert(\"todos\", { title: req.input.title });\n * return row;\n * },\n * });\n *\n * The singletons are thin Proxies. Every property access forwards to the live\n * client for the CURRENT request scope, resolved through {@link __getRuntime}.\n *\n * # Request-scope resolution (persistent app-server)\n *\n * The runtime is a long-running Node process that serves many concurrent\n * requests on one event loop (NOT a fresh subprocess per request). A single\n * module-global slot would let one in-flight request's services bleed into\n * another's. So the services are carried in an {@link AsyncLocalStorage} store\n * ({@link __requestALS}) that the runtime sets per request with\n * {@link __runWithRuntime}; every async continuation of that request reads its\n * own store. `__getRuntime` reads the ALS store first; the module-global slot\n * (set by {@link __setRuntime}) is only a fallback for callers that run OUTSIDE\n * an ALS scope (dev-server, unit tests, the legacy single-shot path). Because\n * each `br-<ref>` pod is single-tenant, there is no cross-tenant leakage; the\n * ALS store is what prevents cross-REQUEST leakage within the shared process.\n *\n * The seam that makes `import { Database } from \"@palbase/backend\"` resolve to\n * the runtime-injected client: `@palbase/backend` is marked esbuild-EXTERNAL\n * when the tenant bundle is built, and the package is installed globally in the\n * pod (NODE_PATH=/usr/local/lib/node_modules). So worker.js's\n * `require('@palbase/backend')` and the bundle's `import` resolve to ONE shared\n * module instance — the ALS store and `__setRuntime` slot on that instance are\n * visible to the singletons the bundle imported.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nimport type {\n DBClient,\n CacheClient,\n QueueClient,\n Logger,\n PalbaseDocsClient,\n} from \"./endpoint.js\";\nimport type {\n PalbaseStorageClient,\n PalbaseNotificationsClient,\n PalbaseFlagsClient,\n} from \"./clients.js\";\nimport type { SchemaDef } from \"./db/schema.js\";\nimport type { TypedDB } from \"./db/typed-db.js\";\nimport { makeTypedDB } from \"./db/typed-db.js\";\n\n/** The set of live clients the runtime injects per request scope.\n *\n * EXCLUDED on purpose: Realtime, Functions, CMS, Links, Analytics, Auth. They\n * are not exposed as backend handler singletons (auth lives on the client SDK;\n * the rest are out of scope for backend endpoints). */\nexport interface RuntimeServices {\n Database: DBClient;\n Documents: PalbaseDocsClient;\n Storage: PalbaseStorageClient;\n Cache: CacheClient;\n Queue: QueueClient;\n Log: Logger;\n Notifications: PalbaseNotificationsClient;\n Flags: PalbaseFlagsClient;\n}\n\n/**\n * Per-request store. The persistent runtime runs each request inside\n * {@link __runWithRuntime}, so every async continuation of that request reads\n * its OWN `runtime` (and any other request-scoped fields the runtime adds).\n *\n * Exported with a `__` prefix so the runtime (worker.js) shares the SAME ALS\n * instance across the one module instance — two ALS instances would silently\n * not see each other's stores. NOT part of the public author-facing API.\n */\nexport const __requestALS = new AsyncLocalStorage<{ runtime: RuntimeServices }>();\n\n/** Process-global fallback slot. Used only OUTSIDE an ALS scope (dev-server,\n * unit tests, legacy single-shot worker). Inside the persistent server every\n * request runs in {@link __requestALS}, which takes precedence. */\nlet runtime: RuntimeServices | null = null;\n\n/** Install the live clients in the process-global fallback slot.\n *\n * Persistent-server requests should use {@link __runWithRuntime} instead; this\n * remains for dev-server / tests / the legacy single-shot path that run without\n * an ALS scope. NOT part of the public author-facing API. */\nexport function __setRuntime(services: RuntimeServices): void {\n runtime = services;\n}\n\n/** Run `fn` with `services` bound as the request-scoped runtime.\n *\n * The persistent worker calls this once per request so concurrent requests\n * never share a services slot. NOT part of the public author-facing API. */\nexport function __runWithRuntime<T>(services: RuntimeServices, fn: () => T): T {\n return __requestALS.run({ runtime: services }, fn);\n}\n\n/** Read the live clients, throwing if accessed outside a request scope.\n *\n * Resolves the ALS store first (persistent server, per-request), then the\n * process-global fallback (dev-server / tests). NOT part of the public\n * author-facing API — used by the runtime and the singleton Proxies. */\nexport function __getRuntime(): RuntimeServices {\n const scoped = __requestALS.getStore();\n if (scoped) return scoped.runtime;\n if (runtime === null) {\n throw new Error(\n \"Palbase services accessed outside a request scope. The Database/Documents/… \" +\n \"singletons are only available inside an endpoint handler (or after the \" +\n \"runtime has called __runWithRuntime / __setRuntime).\",\n );\n }\n return runtime;\n}\n\n/**\n * Build a Proxy singleton that forwards every property access to the live\n * client named `key` on the current runtime.\n *\n * The single `as RuntimeServices[K]` is the only contained cast in the surface:\n * `Reflect.get` on a typed object returns `unknown` for a `string | symbol`\n * key, but `prop` is constrained to keys of the client interface at the call\n * sites (the exported singletons are typed below), so the forward is sound.\n */\nfunction makeServiceProxy<K extends keyof RuntimeServices>(key: K): RuntimeServices[K] {\n const handler: ProxyHandler<RuntimeServices[K]> = {\n get(_target, prop, receiver) {\n const client = __getRuntime()[key];\n const value = Reflect.get(client as object, prop, receiver) as unknown;\n // Bind methods to their owning client so `this` stays correct when the\n // author destructures or calls `Database.query(...)`.\n return typeof value === \"function\" ? value.bind(client) : value;\n },\n };\n // The Proxy target is irrelevant (all access goes through `get`); the cast\n // names the surface type the singleton presents to authors.\n return new Proxy({} as RuntimeServices[K], handler);\n}\n\n/** The project's own Postgres (pgx, schema `env_<envId>`). Typed CRUD +\n * `query`/`transaction`. For a typed `.tables.<name>` API, wrap with\n * {@link typedDatabase}. */\nexport const Database: DBClient = makeServiceProxy(\"Database\");\n\n/** Firestore-like document client (PalDocs). */\nexport const Documents: PalbaseDocsClient = makeServiceProxy(\"Documents\");\n\n/** Object storage client (buckets, signed URLs). */\nexport const Storage: PalbaseStorageClient = makeServiceProxy(\"Storage\");\n\n/** JSON-typed cache (get/set/incr/getOrSet). */\nexport const Cache: CacheClient = makeServiceProxy(\"Cache\");\n\n/** Background job queue. */\nexport const Queue: QueueClient = makeServiceProxy(\"Queue\");\n\n/** Structured logger. */\nexport const Log: Logger = makeServiceProxy(\"Log\");\n\n/** Push / email / SMS / in-app notifications. */\nexport const Notifications: PalbaseNotificationsClient = makeServiceProxy(\"Notifications\");\n\n/** Feature flags. */\nexport const Flags: PalbaseFlagsClient = makeServiceProxy(\"Flags\");\n\n/**\n * Type the `Database` singleton against a `defineSchema()` result, returning a\n * facade with a typed `.tables.<name>.insert(...)` API alongside the raw\n * `query`/`transaction` ops.\n *\n * const Db = typedDatabase(schema);\n * const todo = await Db.tables.todos.insert({ title: \"x\" });\n *\n * This is per-endpoint (not global module augmentation), so one endpoint's\n * tables never leak into another's `Database` type.\n */\nexport function typedDatabase<TSchema extends SchemaDef>(\n schema: TSchema,\n): TypedDB<TSchema> & DBClient {\n // makeTypedDB wraps the raw DBClient with the typed `.tables` facade (and a\n // typed `transaction`). The full `& DBClient` surface also needs the raw ops\n // (query/insert/update/delete/findById/findMany), which we delegate straight\n // to the `Database` singleton — every call goes through its runtime Proxy.\n const typed = makeTypedDB(schema, Database);\n const rawOps: DBClient = {\n query: (sql, params) => Database.query(sql, params),\n insert: (table, data) => Database.insert(table, data),\n update: (table, id, data) => Database.update(table, id, data),\n delete: (table, id) => Database.delete(table, id),\n findById: (table, id) => Database.findById(table, id),\n findMany: (table, q) => Database.findMany(table, q),\n transaction: (fn) => Database.transaction(fn),\n };\n // typed.transaction (typed-tx) intentionally overrides rawOps.transaction so\n // the callback sees the typed `.tables` API.\n return Object.assign(rawOps, typed);\n}\n"],"mappings":";AAwGA,SAAS,eACP,MACA,KACe;AACf,SAAO;AAAA,IACL,QAAQ,CAAC,SACP,IAAI,OAAO,MAAM,IAA+B;AAAA,IAElD,QAAQ,CAAC,IAAY,SACnB,IAAI,OAAO,MAAM,IAAI,IAA+B;AAAA,IAEtD,QAAQ,CAAC,OAAe,IAAI,OAAO,MAAM,EAAE;AAAA,IAE3C,UAAU,CAAC,OACT,IAAI,SAAS,MAAM,EAAE;AAAA,IAEvB,UAAU,CAAC,UACT,IAAI,SAAS,MAAM,KAA4C;AAAA,EACnE;AACF;AAkBO,SAAS,YACd,QACA,KACY;AACZ,WAAS,YAAY,QAAwD;AAC3E,UAAM,SAAS,CAAC;AAChB,eAAW,OAAO,OAAO,KAAK,OAAO,MAAM,GAAG;AAC5C,YAAM,WAAW,OAAO,OAAO,GAAG;AAClC,UAAI,aAAa,QAAW;AAC1B,eAAO,GAAG,IAAI,eAAe,SAAS,MAAM,MAAM;AAAA,MACpD;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,SAAS;AAAA,IACb,QAAQ,YAAY,GAAG;AAAA,IACvB,aAAa,CAAI,OACf,IAAI,YAAY,CAAC,UAAU,GAAG,EAAE,QAAQ,YAAY,KAAK,EAAE,CAAe,CAAC;AAAA,EAC/E;AAMA,SAAO;AACT;;;AC7HA,SAAS,yBAAyB;AA2C3B,IAAM,eAAe,IAAI,kBAAgD;AAKhF,IAAI,UAAkC;AAO/B,SAAS,aAAa,UAAiC;AAC5D,YAAU;AACZ;AAMO,SAAS,iBAAoB,UAA2B,IAAgB;AAC7E,SAAO,aAAa,IAAI,EAAE,SAAS,SAAS,GAAG,EAAE;AACnD;AAOO,SAAS,eAAgC;AAC9C,QAAM,SAAS,aAAa,SAAS;AACrC,MAAI,OAAQ,QAAO,OAAO;AAC1B,MAAI,YAAY,MAAM;AACpB,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AACA,SAAO;AACT;AAWA,SAAS,iBAAkD,KAA4B;AACrF,QAAM,UAA4C;AAAA,IAChD,IAAI,SAAS,MAAM,UAAU;AAC3B,YAAM,SAAS,aAAa,EAAE,GAAG;AACjC,YAAM,QAAQ,QAAQ,IAAI,QAAkB,MAAM,QAAQ;AAG1D,aAAO,OAAO,UAAU,aAAa,MAAM,KAAK,MAAM,IAAI;AAAA,IAC5D;AAAA,EACF;AAGA,SAAO,IAAI,MAAM,CAAC,GAAyB,OAAO;AACpD;AAKO,IAAM,WAAqB,iBAAiB,UAAU;AAGtD,IAAM,YAA+B,iBAAiB,WAAW;AAGjE,IAAM,UAAgC,iBAAiB,SAAS;AAGhE,IAAM,QAAqB,iBAAiB,OAAO;AAGnD,IAAM,QAAqB,iBAAiB,OAAO;AAGnD,IAAM,MAAc,iBAAiB,KAAK;AAG1C,IAAM,gBAA4C,iBAAiB,eAAe;AAGlF,IAAM,QAA4B,iBAAiB,OAAO;AAa1D,SAAS,cACd,QAC6B;AAK7B,QAAM,QAAQ,YAAY,QAAQ,QAAQ;AAC1C,QAAM,SAAmB;AAAA,IACvB,OAAO,CAAC,KAAK,WAAW,SAAS,MAAM,KAAK,MAAM;AAAA,IAClD,QAAQ,CAAC,OAAO,SAAS,SAAS,OAAO,OAAO,IAAI;AAAA,IACpD,QAAQ,CAAC,OAAO,IAAI,SAAS,SAAS,OAAO,OAAO,IAAI,IAAI;AAAA,IAC5D,QAAQ,CAAC,OAAO,OAAO,SAAS,OAAO,OAAO,EAAE;AAAA,IAChD,UAAU,CAAC,OAAO,OAAO,SAAS,SAAS,OAAO,EAAE;AAAA,IACpD,UAAU,CAAC,OAAO,MAAM,SAAS,SAAS,OAAO,CAAC;AAAA,IAClD,aAAa,CAAC,OAAO,SAAS,YAAY,EAAE;AAAA,EAC9C;AAGA,SAAO,OAAO,OAAO,QAAQ,KAAK;AACpC;","names":[]}
|
|
@@ -100,7 +100,7 @@ declare class HttpError extends Error {
|
|
|
100
100
|
*
|
|
101
101
|
* Privilege note
|
|
102
102
|
* ──────────────
|
|
103
|
-
* These clients run with the project's
|
|
103
|
+
* These clients run with the project's managed-runtime (privileged) key —
|
|
104
104
|
* they bypass end-user RLS; intentional for server handlers. Treat every
|
|
105
105
|
* call as if it has admin access to the project's data.
|
|
106
106
|
*/
|
|
@@ -346,7 +346,7 @@ interface PalbaseSmsSendResponse {
|
|
|
346
346
|
message_id?: string;
|
|
347
347
|
message_ids?: string[];
|
|
348
348
|
}
|
|
349
|
-
/** Inbox send params (
|
|
349
|
+
/** Inbox send params (managed-runtime: create inbox row for a user). */
|
|
350
350
|
interface PalbaseInboxSendParams {
|
|
351
351
|
to: string;
|
|
352
352
|
title?: string;
|
|
@@ -760,7 +760,7 @@ interface PalbaseAuthClient {
|
|
|
760
760
|
verifyUserToken(jwt: string): Promise<PalbaseResult<PalbaseUser>>;
|
|
761
761
|
/**
|
|
762
762
|
* Get the current session held by the client (synchronous — no network
|
|
763
|
-
* call). On the server the
|
|
763
|
+
* call). On the server the managed-runtime client does not hold a user
|
|
764
764
|
* session; this always returns `{ data: null, error: null }`.
|
|
765
765
|
*/
|
|
766
766
|
getSession(): {
|
|
@@ -881,7 +881,7 @@ interface PalbaseBucketClient {
|
|
|
881
881
|
}
|
|
882
882
|
/**
|
|
883
883
|
* Storage client available on `ctx.storage`.
|
|
884
|
-
* Only `bucket()` is exposed —
|
|
884
|
+
* Only `bucket()` is exposed — managed-runtime callers select a bucket first.
|
|
885
885
|
*/
|
|
886
886
|
interface PalbaseStorageClient {
|
|
887
887
|
/** Get a bucket-scoped client for file operations. */
|
|
@@ -930,7 +930,7 @@ interface PalbaseFunctionsClient {
|
|
|
930
930
|
}
|
|
931
931
|
/**
|
|
932
932
|
* Flags client available on `ctx.flags`.
|
|
933
|
-
* Evaluate feature flags server-side (
|
|
933
|
+
* Evaluate feature flags server-side (managed-runtime key — user targeting
|
|
934
934
|
* is optional via context).
|
|
935
935
|
*/
|
|
936
936
|
interface PalbaseFlagsClient {
|
|
@@ -949,21 +949,21 @@ interface PalbasePushClient {
|
|
|
949
949
|
send(params: PalbasePushSendParams): Promise<PalbaseResult<PalbasePushSendResponse | PalbaseMultiChannelResponse>>;
|
|
950
950
|
}
|
|
951
951
|
/**
|
|
952
|
-
* Email sub-client surface (
|
|
952
|
+
* Email sub-client surface (managed-runtime).
|
|
953
953
|
*/
|
|
954
954
|
interface PalbaseEmailClient {
|
|
955
955
|
/** Send a transactional email. */
|
|
956
956
|
send(params: PalbaseEmailSendParams): Promise<PalbaseResult<PalbaseEmailSendResponse>>;
|
|
957
957
|
}
|
|
958
958
|
/**
|
|
959
|
-
* SMS sub-client surface (
|
|
959
|
+
* SMS sub-client surface (managed-runtime).
|
|
960
960
|
*/
|
|
961
961
|
interface PalbaseSmsClient {
|
|
962
962
|
/** Send an SMS message. */
|
|
963
963
|
send(params: PalbaseSmsSendParams): Promise<PalbaseResult<PalbaseSmsSendResponse>>;
|
|
964
964
|
}
|
|
965
965
|
/**
|
|
966
|
-
* Inbox sub-client surface (
|
|
966
|
+
* Inbox sub-client surface (managed-runtime send + user read operations).
|
|
967
967
|
*/
|
|
968
968
|
interface PalbaseInboxClient {
|
|
969
969
|
/** Service-role: create an inbox notification row for a user. */
|
|
@@ -991,7 +991,7 @@ interface PalbasePreferencesClient {
|
|
|
991
991
|
update(params: PalbasePreferences): Promise<PalbaseResult<PalbasePreferences>>;
|
|
992
992
|
}
|
|
993
993
|
/**
|
|
994
|
-
* Email-template CRUD sub-client (
|
|
994
|
+
* Email-template CRUD sub-client (managed-runtime gated server-side).
|
|
995
995
|
*/
|
|
996
996
|
interface PalbaseEmailTemplatesClient {
|
|
997
997
|
/** List all email templates. */
|
|
@@ -1006,7 +1006,7 @@ interface PalbaseEmailTemplatesClient {
|
|
|
1006
1006
|
delete(id: string): Promise<PalbaseResult<void>>;
|
|
1007
1007
|
}
|
|
1008
1008
|
/**
|
|
1009
|
-
* SMS-template CRUD sub-client (
|
|
1009
|
+
* SMS-template CRUD sub-client (managed-runtime gated server-side).
|
|
1010
1010
|
*/
|
|
1011
1011
|
interface PalbaseSMSTemplatesClient {
|
|
1012
1012
|
/** List all SMS templates. */
|
|
@@ -1047,7 +1047,7 @@ interface PalbaseNotificationsClient {
|
|
|
1047
1047
|
inbox: PalbaseInboxClient;
|
|
1048
1048
|
/** Notification preferences manager. */
|
|
1049
1049
|
preferences: PalbasePreferencesClient;
|
|
1050
|
-
/** Email + SMS template CRUD (
|
|
1050
|
+
/** Email + SMS template CRUD (managed-runtime). */
|
|
1051
1051
|
templates: PalbaseTemplatesClient;
|
|
1052
1052
|
/** Register a device for push notifications. */
|
|
1053
1053
|
registerDevice(params: PalbaseRegisterDeviceParams): Promise<PalbaseResult<PalbaseDeviceTokenView>>;
|
|
@@ -100,7 +100,7 @@ declare class HttpError extends Error {
|
|
|
100
100
|
*
|
|
101
101
|
* Privilege note
|
|
102
102
|
* ──────────────
|
|
103
|
-
* These clients run with the project's
|
|
103
|
+
* These clients run with the project's managed-runtime (privileged) key —
|
|
104
104
|
* they bypass end-user RLS; intentional for server handlers. Treat every
|
|
105
105
|
* call as if it has admin access to the project's data.
|
|
106
106
|
*/
|
|
@@ -346,7 +346,7 @@ interface PalbaseSmsSendResponse {
|
|
|
346
346
|
message_id?: string;
|
|
347
347
|
message_ids?: string[];
|
|
348
348
|
}
|
|
349
|
-
/** Inbox send params (
|
|
349
|
+
/** Inbox send params (managed-runtime: create inbox row for a user). */
|
|
350
350
|
interface PalbaseInboxSendParams {
|
|
351
351
|
to: string;
|
|
352
352
|
title?: string;
|
|
@@ -760,7 +760,7 @@ interface PalbaseAuthClient {
|
|
|
760
760
|
verifyUserToken(jwt: string): Promise<PalbaseResult<PalbaseUser>>;
|
|
761
761
|
/**
|
|
762
762
|
* Get the current session held by the client (synchronous — no network
|
|
763
|
-
* call). On the server the
|
|
763
|
+
* call). On the server the managed-runtime client does not hold a user
|
|
764
764
|
* session; this always returns `{ data: null, error: null }`.
|
|
765
765
|
*/
|
|
766
766
|
getSession(): {
|
|
@@ -881,7 +881,7 @@ interface PalbaseBucketClient {
|
|
|
881
881
|
}
|
|
882
882
|
/**
|
|
883
883
|
* Storage client available on `ctx.storage`.
|
|
884
|
-
* Only `bucket()` is exposed —
|
|
884
|
+
* Only `bucket()` is exposed — managed-runtime callers select a bucket first.
|
|
885
885
|
*/
|
|
886
886
|
interface PalbaseStorageClient {
|
|
887
887
|
/** Get a bucket-scoped client for file operations. */
|
|
@@ -930,7 +930,7 @@ interface PalbaseFunctionsClient {
|
|
|
930
930
|
}
|
|
931
931
|
/**
|
|
932
932
|
* Flags client available on `ctx.flags`.
|
|
933
|
-
* Evaluate feature flags server-side (
|
|
933
|
+
* Evaluate feature flags server-side (managed-runtime key — user targeting
|
|
934
934
|
* is optional via context).
|
|
935
935
|
*/
|
|
936
936
|
interface PalbaseFlagsClient {
|
|
@@ -949,21 +949,21 @@ interface PalbasePushClient {
|
|
|
949
949
|
send(params: PalbasePushSendParams): Promise<PalbaseResult<PalbasePushSendResponse | PalbaseMultiChannelResponse>>;
|
|
950
950
|
}
|
|
951
951
|
/**
|
|
952
|
-
* Email sub-client surface (
|
|
952
|
+
* Email sub-client surface (managed-runtime).
|
|
953
953
|
*/
|
|
954
954
|
interface PalbaseEmailClient {
|
|
955
955
|
/** Send a transactional email. */
|
|
956
956
|
send(params: PalbaseEmailSendParams): Promise<PalbaseResult<PalbaseEmailSendResponse>>;
|
|
957
957
|
}
|
|
958
958
|
/**
|
|
959
|
-
* SMS sub-client surface (
|
|
959
|
+
* SMS sub-client surface (managed-runtime).
|
|
960
960
|
*/
|
|
961
961
|
interface PalbaseSmsClient {
|
|
962
962
|
/** Send an SMS message. */
|
|
963
963
|
send(params: PalbaseSmsSendParams): Promise<PalbaseResult<PalbaseSmsSendResponse>>;
|
|
964
964
|
}
|
|
965
965
|
/**
|
|
966
|
-
* Inbox sub-client surface (
|
|
966
|
+
* Inbox sub-client surface (managed-runtime send + user read operations).
|
|
967
967
|
*/
|
|
968
968
|
interface PalbaseInboxClient {
|
|
969
969
|
/** Service-role: create an inbox notification row for a user. */
|
|
@@ -991,7 +991,7 @@ interface PalbasePreferencesClient {
|
|
|
991
991
|
update(params: PalbasePreferences): Promise<PalbaseResult<PalbasePreferences>>;
|
|
992
992
|
}
|
|
993
993
|
/**
|
|
994
|
-
* Email-template CRUD sub-client (
|
|
994
|
+
* Email-template CRUD sub-client (managed-runtime gated server-side).
|
|
995
995
|
*/
|
|
996
996
|
interface PalbaseEmailTemplatesClient {
|
|
997
997
|
/** List all email templates. */
|
|
@@ -1006,7 +1006,7 @@ interface PalbaseEmailTemplatesClient {
|
|
|
1006
1006
|
delete(id: string): Promise<PalbaseResult<void>>;
|
|
1007
1007
|
}
|
|
1008
1008
|
/**
|
|
1009
|
-
* SMS-template CRUD sub-client (
|
|
1009
|
+
* SMS-template CRUD sub-client (managed-runtime gated server-side).
|
|
1010
1010
|
*/
|
|
1011
1011
|
interface PalbaseSMSTemplatesClient {
|
|
1012
1012
|
/** List all SMS templates. */
|
|
@@ -1047,7 +1047,7 @@ interface PalbaseNotificationsClient {
|
|
|
1047
1047
|
inbox: PalbaseInboxClient;
|
|
1048
1048
|
/** Notification preferences manager. */
|
|
1049
1049
|
preferences: PalbasePreferencesClient;
|
|
1050
|
-
/** Email + SMS template CRUD (
|
|
1050
|
+
/** Email + SMS template CRUD (managed-runtime). */
|
|
1051
1051
|
templates: PalbaseTemplatesClient;
|
|
1052
1052
|
/** Register a device for push notifications. */
|
|
1053
1053
|
registerDevice(params: PalbaseRegisterDeviceParams): Promise<PalbaseResult<PalbaseDeviceTokenView>>;
|
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,8 @@ __export(src_exports, {
|
|
|
30
30
|
Queue: () => Queue,
|
|
31
31
|
Storage: () => Storage,
|
|
32
32
|
__getRuntime: () => __getRuntime,
|
|
33
|
+
__requestALS: () => __requestALS,
|
|
34
|
+
__runWithRuntime: () => __runWithRuntime,
|
|
33
35
|
__setRuntime: () => __setRuntime,
|
|
34
36
|
auth: () => auth,
|
|
35
37
|
boolean: () => boolean,
|
|
@@ -59,6 +61,9 @@ function defineEndpoint(config) {
|
|
|
59
61
|
return config;
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
// src/runtime.ts
|
|
65
|
+
var import_node_async_hooks = require("async_hooks");
|
|
66
|
+
|
|
62
67
|
// src/db/typed-db.ts
|
|
63
68
|
function makeTypedTable(name, raw) {
|
|
64
69
|
return {
|
|
@@ -88,14 +93,20 @@ function makeTypedDB(schema, raw) {
|
|
|
88
93
|
}
|
|
89
94
|
|
|
90
95
|
// src/runtime.ts
|
|
96
|
+
var __requestALS = new import_node_async_hooks.AsyncLocalStorage();
|
|
91
97
|
var runtime = null;
|
|
92
98
|
function __setRuntime(services) {
|
|
93
99
|
runtime = services;
|
|
94
100
|
}
|
|
101
|
+
function __runWithRuntime(services, fn) {
|
|
102
|
+
return __requestALS.run({ runtime: services }, fn);
|
|
103
|
+
}
|
|
95
104
|
function __getRuntime() {
|
|
105
|
+
const scoped = __requestALS.getStore();
|
|
106
|
+
if (scoped) return scoped.runtime;
|
|
96
107
|
if (runtime === null) {
|
|
97
108
|
throw new Error(
|
|
98
|
-
"Palbase services accessed outside a request scope. The Database/Documents/\u2026 singletons are only available inside an endpoint handler (or after the runtime has called __setRuntime)."
|
|
109
|
+
"Palbase services accessed outside a request scope. The Database/Documents/\u2026 singletons are only available inside an endpoint handler (or after the runtime has called __runWithRuntime / __setRuntime)."
|
|
99
110
|
);
|
|
100
111
|
}
|
|
101
112
|
return runtime;
|
|
@@ -524,6 +535,8 @@ var import_zod = require("zod");
|
|
|
524
535
|
Queue,
|
|
525
536
|
Storage,
|
|
526
537
|
__getRuntime,
|
|
538
|
+
__requestALS,
|
|
539
|
+
__runWithRuntime,
|
|
527
540
|
__setRuntime,
|
|
528
541
|
auth,
|
|
529
542
|
boolean,
|