@palbase/backend 2.0.2 → 4.0.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/chunk-EG7TTYHY.js +235 -0
- package/dist/chunk-EG7TTYHY.js.map +1 -0
- package/dist/chunk-WUQO76NW.js +101 -0
- package/dist/chunk-WUQO76NW.js.map +1 -0
- package/dist/db/env.cjs +19 -0
- package/dist/db/env.cjs.map +1 -0
- package/dist/db/env.d.cts +45 -0
- package/dist/db/env.d.ts +45 -0
- package/dist/db/env.js +1 -0
- package/dist/db/env.js.map +1 -0
- package/dist/db/index.cjs +143 -231
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +4 -20
- package/dist/db/index.d.ts +4 -20
- package/dist/db/index.js +13 -233
- package/dist/db/index.js.map +1 -1
- package/dist/{endpoint-Djk5L6G2.d.ts → endpoint-2d_DpASt.d.cts} +94 -96
- package/dist/{endpoint-BlcY2xNA.d.cts → endpoint-2d_DpASt.d.ts} +94 -96
- package/dist/index-DZW9CjiY.d.ts +463 -0
- package/dist/index-DzRFS3Tl.d.cts +463 -0
- package/dist/index.cjs +557 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +278 -161
- package/dist/index.d.ts +278 -161
- package/dist/index.js +343 -12
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs +57 -2
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +1 -2
- package/dist/test/index.d.ts +1 -2
- package/dist/test/index.js +10 -2
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +33 -12
- package/docs/background.md +19 -13
- package/docs/database.md +70 -17
- package/docs/endpoints.md +103 -79
- package/docs/errors.md +37 -31
- package/docs/events.md +25 -17
- package/docs/getting-started.md +38 -18
- package/docs/llms-full.txt +758 -267
- package/docs/llms.txt +3 -1
- package/docs/migrations.md +98 -0
- package/docs/resources.md +94 -0
- package/docs/routing.md +54 -27
- package/docs/schema.md +163 -42
- package/docs/services.md +17 -14
- package/package.json +12 -2
- package/dist/chunk-4J3F32SH.js +0 -96
- package/dist/chunk-4J3F32SH.js.map +0 -1
- package/dist/chunk-L36JLUPO.js +0 -97
- package/dist/chunk-L36JLUPO.js.map +0 -1
- package/dist/schema-BqfEhIC0.d.cts +0 -133
- package/dist/schema-BqfEhIC0.d.ts +0 -133
package/dist/index.d.ts
CHANGED
|
@@ -1,110 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { C as CacheClient, b as PalbaseDocsClient, c as PalbaseFlagsClient, L as Logger, d as PalbaseNotificationsClient, Q as QueueClient, D as DBClient, e as PalbaseStorageClient, A as AuthSpec, R as RateLimitConfig, U as User$1 } from './endpoint-2d_DpASt.js';
|
|
2
|
+
export { f as AuthConfig, B as BadRequest, g as ClientInfo, h as Conflict, i as DBOps, E as ErrorDef, j as ErrorMap, k as ErrorThrowers, F as FileContext, l as Forbidden, H as HttpError, m as HttpMethod, M as Middleware, n as MiddlewareContext, o as MiddlewareHandler, N as NotFound, P as PBRequest, p as PalError, q as PalbaseAnalyticsClient, r as PalbaseAnalyticsManagementNamespace, s as PalbaseAnalyticsProperties, t as PalbaseAnalyticsQueryNamespace, u as PalbaseAttestAndroidParams, v as PalbaseAttestAndroidResult, w as PalbaseAttestiOSParams, x as PalbaseAttestiOSResult, y as PalbaseAuthClient, z as PalbaseBindDeviceParams, G as PalbaseBucketClient, I as PalbaseCmsClient, J as PalbaseCmsFindOneOptions, K as PalbaseCmsFindOptions, O as PalbaseCohortQueryInput, S as PalbaseCohortResult, T as PalbaseCollectionRef, V as PalbaseCountQueryInput, W as PalbaseCountResult, X as PalbaseCreateLinkParams, Y as PalbaseDeviceInfo, Z as PalbaseDeviceTokenView, _ as PalbaseDocumentRef, $ as PalbaseDocumentSnapshot, a0 as PalbaseEmailClient, a1 as PalbaseEmailSendParams, a2 as PalbaseEmailSendResponse, a3 as PalbaseEventNamesResult, a4 as PalbaseEventsQueryInput, a5 as PalbaseEventsResult, a6 as PalbaseFileObject, a7 as PalbaseFlag, a8 as PalbaseFlagContext, a9 as PalbaseFlagVariant, aa as PalbaseFunctionsClient, ab as PalbaseFunnelQueryInput, ac as PalbaseFunnelResult, ad as PalbaseIdentifyTraits, ae as PalbaseInboxClient, af as PalbaseInboxListOptions, ag as PalbaseInboxListResult, ah as PalbaseInboxMessage, ai as PalbaseInboxSendParams, aj as PalbaseInboxSendResponse, ak as PalbaseInitialLink, al as PalbaseInvokeOptions, am as PalbaseLink, an as PalbaseLinkAnalytics, ao as PalbaseLinkDetails, ap as PalbaseLinksClient, aq as PalbaseListLinksOptions, ar as PalbaseListLinksResult, as as PalbaseListOptions, at as PalbaseMatchParams, au as PalbaseMultiChannelResponse, av as PalbaseOverviewResult, aw as PalbasePreferences, ax as PalbasePreferencesClient, ay as PalbasePublicUrlResponse, az as PalbasePushClient, aA as PalbasePushSendParams, aB as PalbasePushSendResponse, aC as PalbaseQrCodeOptions, aD as PalbaseQuerySnapshot, aE as PalbaseRealtimeChannel, aF as PalbaseRealtimeClient, aG as PalbaseRealtimeMessage, aH as PalbaseRegisterDeviceParams, aI as PalbaseResult, aJ as PalbaseRetentionQueryInput, aK as PalbaseRetentionResult, aL as PalbaseSession, aM as PalbaseSignedUrlResponse, aN as PalbaseSmsClient, aO as PalbaseSmsSendParams, aP as PalbaseSmsSendResponse, aQ as PalbaseTransformOptions, aR as PalbaseUpdateLinkParams, aS as PalbaseUploadOptions, aT as PalbaseUser, aU as PalbaseUserDetailResult, aV as PalbaseUsersQueryInput, aW as PalbaseUsersResult, aX as PalbaseVerifyRequestSignatureParams, aY as PalbaseWhereOperator, aZ as TooManyRequests, a_ as TxClient, a$ as Unauthorized, b0 as defineMiddleware } from './endpoint-2d_DpASt.js';
|
|
3
3
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
4
|
-
import {
|
|
5
|
-
export {
|
|
4
|
+
import { E as EnvTypedDatabase, S as SchemaDef } from './index-DZW9CjiY.js';
|
|
5
|
+
export { C as ColumnBuilder, a as ColumnDef, b as ColumnMap, c as ColumnType, d as EXTENSION_DEPENDENCIES, e as EnvServiceDatabase, f as EnvTables, g as EnvTypedTable, h as EnvTypedTx, I as InsertShape, O as OnDeleteAction, P as PALBASE_EXTENSIONS, i as PalbaseExtension, j as PolicyBuilder, k as PolicyCommand, l as PolicyDef, m as PolicyMode, R as RowShape, n as SchemaInput, T as TableDef, o as TableInput, p as TypedDB, q as TypedTable, r as TypedTx, s as boolean, t as defineSchema, u as enumType, v as integer, w as isPalbaseExtension, x as jsonb, y as makeTypedDB, z as policy, A as text, B as timestamp, D as uuid } from './index-DZW9CjiY.js';
|
|
6
|
+
export { TableTypes, Tables } from './db/env.js';
|
|
7
|
+
import { ZodTypeAny } from 'zod';
|
|
6
8
|
export { z } from 'zod';
|
|
7
9
|
|
|
8
|
-
/**
|
|
9
|
-
* typed-db.ts — Task 2: TypedDB schema-derived insert/row shapes.
|
|
10
|
-
*
|
|
11
|
-
* Derives INSERT and full-row TypeScript types from a `defineSchema()` result
|
|
12
|
-
* and wraps the untyped runtime `DBClient` with a typed facade.
|
|
13
|
-
*
|
|
14
|
-
* No value-any. No `as unknown as X`. The two narrow `as` casts in
|
|
15
|
-
* `makeTypedTable` are safe because:
|
|
16
|
-
* - `data as Record<string, unknown>`: InsertShape<T> maps string keys to
|
|
17
|
-
* typed values; all value types are subsets of `unknown`, so the cast is
|
|
18
|
-
* structurally sound.
|
|
19
|
-
* - `result as RowShape<T>`: The runtime DBClient returns `Record<string,
|
|
20
|
-
* unknown>` which is the erased form of the typed row; we're narrowing back
|
|
21
|
-
* to the precise shape that the schema declared.
|
|
22
|
-
* Both casts are narrowing only (not widening) and correctness is guaranteed
|
|
23
|
-
* by the schema the caller provides.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
/** Keys of C whose columns are required on INSERT (not nullable, no default). */
|
|
27
|
-
type RequiredKeys<C> = {
|
|
28
|
-
[K in keyof C]: ColIsOptionalOnInsert<C[K]> extends true ? never : K;
|
|
29
|
-
}[keyof C];
|
|
30
|
-
/** Keys of C whose columns are optional on INSERT (nullable or has a default). */
|
|
31
|
-
type OptionalKeys<C> = {
|
|
32
|
-
[K in keyof C]: ColIsOptionalOnInsert<C[K]> extends true ? K : never;
|
|
33
|
-
}[keyof C];
|
|
34
|
-
/**
|
|
35
|
-
* The TypeScript type for an INSERT payload for table `T`.
|
|
36
|
-
* - Required: columns that are NOT NULL and have no DB-level default.
|
|
37
|
-
* - Optional: columns that are nullable or carry a default.
|
|
38
|
-
*
|
|
39
|
-
* When all columns are optional, `RequiredKeys<C>` resolves to `never` and
|
|
40
|
-
* the first part becomes `{}`, which is a neutral element for `&`.
|
|
41
|
-
*/
|
|
42
|
-
type InsertShape<T extends TableDef> = {
|
|
43
|
-
[K in RequiredKeys<T["columns"]>]: ColValue<T["columns"][K]>;
|
|
44
|
-
} & {
|
|
45
|
-
[K in OptionalKeys<T["columns"]>]?: ColValue<T["columns"][K]>;
|
|
46
|
-
};
|
|
47
|
-
/**
|
|
48
|
-
* The TypeScript type for a full row returned by the DB for table `T`.
|
|
49
|
-
* Every column is present; nullable columns resolve to `T | null`.
|
|
50
|
-
*/
|
|
51
|
-
type RowShape<T extends TableDef> = {
|
|
52
|
-
[K in keyof T["columns"]]: ColValue<T["columns"][K]>;
|
|
53
|
-
};
|
|
54
|
-
/** A typed table accessor that mirrors the runtime DBClient surface. */
|
|
55
|
-
interface TypedTable<T extends TableDef> {
|
|
56
|
-
insert(data: InsertShape<T>): Promise<RowShape<T>>;
|
|
57
|
-
update(id: string, data: Partial<InsertShape<T>>): Promise<RowShape<T>>;
|
|
58
|
-
delete(id: string): Promise<void>;
|
|
59
|
-
findById(id: string): Promise<RowShape<T> | null>;
|
|
60
|
-
findMany(query?: Partial<RowShape<T>>): Promise<RowShape<T>[]>;
|
|
61
|
-
}
|
|
62
|
-
/** A typed DB facade covering all tables declared in schema `S`. */
|
|
63
|
-
interface TypedDB<S extends SchemaDef> {
|
|
64
|
-
tables: {
|
|
65
|
-
[K in keyof S["tables"]]: TypedTable<S["tables"][K]>;
|
|
66
|
-
};
|
|
67
|
-
transaction<T>(fn: (tx: TypedTx<S>) => Promise<T>): Promise<T>;
|
|
68
|
-
}
|
|
69
|
-
/** Transaction-scoped typed facade: same typed tables, no nested transaction. */
|
|
70
|
-
interface TypedTx<S extends SchemaDef> {
|
|
71
|
-
tables: {
|
|
72
|
-
[K in keyof S["tables"]]: TypedTable<S["tables"][K]>;
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Wraps a raw `DBClient` with the type-safe `TypedDB<S>` facade derived from
|
|
77
|
-
* the provided schema. No behavior change — all calls delegate to `raw` with
|
|
78
|
-
* the table name as a plain string.
|
|
79
|
-
*
|
|
80
|
-
* `buildTables` is the reusable factory that wraps any op-bearing client
|
|
81
|
-
* (`TxClient` — the surface shared by `DBClient` and the transaction-scoped
|
|
82
|
-
* client) into the typed tables map. It is used both for the top-level db
|
|
83
|
-
* (wrapping `raw`) and inside `transaction`, where it wraps the raw `TxClient`
|
|
84
|
-
* the runtime yields so the callback sees the same typed `.tables` API.
|
|
85
|
-
*
|
|
86
|
-
* `transaction` delegates straight to `raw.transaction`; the two narrow
|
|
87
|
-
* `as TypedTx<S>` / `as TypedDB<S>` casts are single structural narrowings
|
|
88
|
-
* from the dynamically-built tables object to the precise mapped type (TS
|
|
89
|
-
* cannot infer through `Object.keys` iteration) — see module-level doc comment.
|
|
90
|
-
*/
|
|
91
|
-
declare function makeTypedDB<S extends SchemaDef>(schema: S, raw: DBClient): TypedDB<S>;
|
|
92
|
-
|
|
93
10
|
/**
|
|
94
11
|
* runtime.ts — request-scoped service singletons.
|
|
95
12
|
*
|
|
96
13
|
* The backend SDK no longer threads a `ctx` god-object through every handler.
|
|
97
|
-
* Instead,
|
|
14
|
+
* Instead, controller methods import PascalCase service singletons directly:
|
|
98
15
|
*
|
|
99
|
-
* import {
|
|
16
|
+
* import { Controller, Post, Body, Database } from "@palbase/backend";
|
|
100
17
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* });
|
|
18
|
+
* \@Controller("/todos")
|
|
19
|
+
* export default class TodosController {
|
|
20
|
+
* \@Post("") create(\@Body(CreateTodoBody) body: CreateTodoBody): unknown {
|
|
21
|
+
* return Database.insert("todos", { title: body.title });
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
108
24
|
*
|
|
109
25
|
* The singletons are thin Proxies. Every property access forwards to the live
|
|
110
26
|
* client for the CURRENT request scope, resolved through {@link __getRuntime}.
|
|
@@ -176,10 +92,27 @@ declare function __runWithRuntime<T>(services: RuntimeServices, fn: () => T): T;
|
|
|
176
92
|
* process-global fallback (dev-server / tests). NOT part of the public
|
|
177
93
|
* author-facing API — used by the runtime and the singleton Proxies. */
|
|
178
94
|
declare function __getRuntime(): RuntimeServices;
|
|
179
|
-
/**
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
|
|
95
|
+
/**
|
|
96
|
+
* The project's own Postgres (pgx, schema `env_<envId>`).
|
|
97
|
+
*
|
|
98
|
+
* Typed by default: `Database.tables.<name>.insert({...})` is typed against
|
|
99
|
+
* the project's generated `palbase-env.d.ts` with NO import and NO generic.
|
|
100
|
+
* The raw string ops (`query`/`insert`/`update`/`delete`/`findById`/`findMany`)
|
|
101
|
+
* are also available for dynamic table names and read-only SQL.
|
|
102
|
+
*
|
|
103
|
+
* RLS is enforced by default (the runtime runs each op as `authenticated` with
|
|
104
|
+
* the verified user's claims). To bypass RLS, call `Database.asService()` —
|
|
105
|
+
* explicit and greppable — which runs as the `service_role` (BYPASSRLS).
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* import { Database } from "@palbase/backend";
|
|
109
|
+
*
|
|
110
|
+
* const todo = await Database.tables.todos.insert({ title: req.input.title });
|
|
111
|
+
* todo.id; // string ✓
|
|
112
|
+
* const rows = await Database.query("SELECT id FROM todos WHERE done = $1", [false]);
|
|
113
|
+
* const all = await Database.asService().tables.todos.findMany({}); // RLS bypass
|
|
114
|
+
*/
|
|
115
|
+
declare const Database: EnvTypedDatabase;
|
|
183
116
|
/** Firestore-like document client (PalDocs). */
|
|
184
117
|
declare const Documents: PalbaseDocsClient;
|
|
185
118
|
/** Object storage client (buckets, signed URLs). */
|
|
@@ -194,31 +127,123 @@ declare const Log: Logger;
|
|
|
194
127
|
declare const Notifications: PalbaseNotificationsClient;
|
|
195
128
|
/** Feature flags. */
|
|
196
129
|
declare const Flags: PalbaseFlagsClient;
|
|
130
|
+
|
|
197
131
|
/**
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
132
|
+
* env-gen.ts — generate the `palbase-env.d.ts` text from a `defineSchema()`
|
|
133
|
+
* result.
|
|
134
|
+
*
|
|
135
|
+
* The CLI (`palbase serve` / codegen) and the deploy pipeline call
|
|
136
|
+
* {@link makeEnvDts} with the project's schema and write the returned string to
|
|
137
|
+
* `palbase-env.d.ts` at the project root. That file AUGMENTS the
|
|
138
|
+
* `@palbase/backend/env` `Tables` interface (controlled global augmentation,
|
|
139
|
+
* C5) so `Database.tables.<name>` is typed with no import and no generic.
|
|
201
140
|
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
141
|
+
* The output is FLAT: each table gets a `{ row: {...}; insert: {...} }` entry
|
|
142
|
+
* with plain TypeScript object types. The phantom `ColumnBuilder<...>` type
|
|
143
|
+
* NEVER appears in the generated `.d.ts` — that type only lives at authoring
|
|
144
|
+
* time inside `db/schema.ts`.
|
|
145
|
+
*/
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Generate the full `palbase-env.d.ts` text for a schema.
|
|
204
149
|
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
150
|
+
* @example
|
|
151
|
+
* import { makeEnvDts } from "@palbase/backend";
|
|
152
|
+
* import schema from "./db/schema.js";
|
|
153
|
+
* writeFileSync("palbase-env.d.ts", makeEnvDts(schema));
|
|
207
154
|
*/
|
|
208
|
-
declare function
|
|
155
|
+
declare function makeEnvDts(schema: SchemaDef): string;
|
|
209
156
|
|
|
210
|
-
/**
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
157
|
+
/** Options accepted by `@Controller`. */
|
|
158
|
+
interface ControllerOptions {
|
|
159
|
+
/** Default auth for ALL routes in this controller (route-level overrides). */
|
|
160
|
+
auth?: AuthSpec;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Mark a class as a Palbase backend controller. `basePath` is the mount path
|
|
164
|
+
* for every route the class declares; `options.auth` sets the controller-level
|
|
165
|
+
* default auth (a route's own `auth` overrides it; absent ⇒ secure-by-default).
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* \@Controller("/todos", { auth: false })
|
|
169
|
+
* export class TodosController {
|
|
170
|
+
* \@Get("") list(\@Query(ListTodosQuery) q: ListTodosQuery): TodoSchema[] { … }
|
|
171
|
+
* }
|
|
172
|
+
*/
|
|
173
|
+
declare function Controller(basePath: string, options?: ControllerOptions): <T extends abstract new (...args: never[]) => object>(ctor: T) => T;
|
|
174
|
+
|
|
175
|
+
/** The HTTP verbs a route may declare, upper-cased (the runtime router +
|
|
176
|
+
* OpenAPI lower-case on their own). */
|
|
177
|
+
type HttpMethodUpper = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
178
|
+
/** Route-level options accepted by the method decorators (`@Get`/`@Post`/…). */
|
|
179
|
+
interface RouteOptions {
|
|
180
|
+
/** OVERRIDES the controller-level default auth for this one route. */
|
|
181
|
+
auth?: AuthSpec;
|
|
182
|
+
/** Per-route rate limit. */
|
|
183
|
+
rateLimit?: RateLimitConfig;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** A legacy method decorator. */
|
|
187
|
+
type MethodDecorator = (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => void;
|
|
188
|
+
/** `@Get(subpath, options?)` — declare a GET route. */
|
|
189
|
+
declare const Get: (subpath: string, options?: RouteOptions) => MethodDecorator;
|
|
190
|
+
/** `@Post(subpath, options?)` — declare a POST route. */
|
|
191
|
+
declare const Post: (subpath: string, options?: RouteOptions) => MethodDecorator;
|
|
192
|
+
/** `@Put(subpath, options?)` — declare a PUT route. */
|
|
193
|
+
declare const Put: (subpath: string, options?: RouteOptions) => MethodDecorator;
|
|
194
|
+
/** `@Patch(subpath, options?)` — declare a PATCH route. */
|
|
195
|
+
declare const Patch: (subpath: string, options?: RouteOptions) => MethodDecorator;
|
|
196
|
+
/** `@Delete(subpath, options?)` — declare a DELETE route. */
|
|
197
|
+
declare const Delete: (subpath: string, options?: RouteOptions) => MethodDecorator;
|
|
198
|
+
/**
|
|
199
|
+
* `@Returns(schema)` — declare the route's success-response zod schema. The
|
|
200
|
+
* method's return TYPE drives compile-time checking; `@Returns` supplies the
|
|
201
|
+
* matching zod VALUE the OpenAPI generator reads for the 200 response. Optional:
|
|
202
|
+
* a `void` method (no response body) omits it.
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* \@Get("/{id}")
|
|
206
|
+
* \@Returns(TodoSchema)
|
|
207
|
+
* getOne(\@Param("id") id: string): TodoSchema { … }
|
|
208
|
+
*/
|
|
209
|
+
declare function Returns(schema: ZodTypeAny): MethodDecorator;
|
|
210
|
+
|
|
211
|
+
/** A legacy parameter decorator. */
|
|
212
|
+
type ParameterDecorator = (target: object, propertyKey: string | symbol, parameterIndex: number) => void;
|
|
213
|
+
/** `@Body(schema)` — inject the request body, validated against `schema`. The
|
|
214
|
+
* developer writes `: T` (= `z.infer<schema>`, same name) for autocomplete. */
|
|
215
|
+
declare function Body(schema: ZodTypeAny): ParameterDecorator;
|
|
216
|
+
/** `@Query(schema)` — inject the parsed query params, validated against
|
|
217
|
+
* `schema`. */
|
|
218
|
+
declare function Query(schema: ZodTypeAny): ParameterDecorator;
|
|
219
|
+
/** `@Headers(schema?)` — inject the request headers (lowercase keys). With a
|
|
220
|
+
* schema, headers are validated + the codegen emits header parameters. */
|
|
221
|
+
declare function Headers(schema?: ZodTypeAny): ParameterDecorator;
|
|
222
|
+
/** `@Param("id")` — inject one matched path param by name. */
|
|
223
|
+
declare function Param(name: string): ParameterDecorator;
|
|
224
|
+
/** `@User()` — inject the authenticated user (`: User`, non-null for an
|
|
225
|
+
* effective-required route). The runtime resolves the effective auth. */
|
|
226
|
+
declare function User(): ParameterDecorator;
|
|
227
|
+
/** `@OptionalUser()` — inject the user as `User | null` (for routes whose
|
|
228
|
+
* effective auth is `false` / `{ required: false }`). */
|
|
229
|
+
declare function OptionalUser(): ParameterDecorator;
|
|
230
|
+
/** `@Client()` — inject the parsed calling-client metadata (`: ClientInfo`). */
|
|
231
|
+
declare function Client(): ParameterDecorator;
|
|
232
|
+
/** `@RequestId()` — inject the per-request id (`: string`). */
|
|
233
|
+
declare function RequestId(): ParameterDecorator;
|
|
234
|
+
/** `@TraceId()` — inject the W3C trace id (`: string`). */
|
|
235
|
+
declare function TraceId(): ParameterDecorator;
|
|
236
|
+
/** `@Req()` — inject the raw request object (escape hatch, `: PBRequest`). */
|
|
237
|
+
declare function Req(): ParameterDecorator;
|
|
238
|
+
|
|
239
|
+
/** Non-service, per-invocation data for background worker handlers.
|
|
240
|
+
* Services (Database, Log, …) are imported as singletons, not passed here. */
|
|
241
|
+
interface WorkerMeta {
|
|
242
|
+
/** Branch-scoped env vars. */
|
|
217
243
|
env: Record<string, string>;
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
errors: ErrorThrowers<undefined>;
|
|
244
|
+
/** The user that enqueued the job, if any (background jobs are usually system-initiated). */
|
|
245
|
+
user: User$1 | null;
|
|
246
|
+
/** Per-invocation id for correlation. */
|
|
222
247
|
requestId: string;
|
|
223
248
|
projectId: string;
|
|
224
249
|
environmentId: string;
|
|
@@ -236,7 +261,7 @@ interface WorkerConfig<TPayload = unknown> {
|
|
|
236
261
|
/** Backoff strategy between retries. Defaults to "exponential". */
|
|
237
262
|
backoff?: BackoffStrategy;
|
|
238
263
|
/** Handler function that processes the job payload. */
|
|
239
|
-
handler: (
|
|
264
|
+
handler: (payload: TPayload, meta: WorkerMeta) => Promise<void>;
|
|
240
265
|
}
|
|
241
266
|
/** Resolved worker configuration with defaults applied. */
|
|
242
267
|
interface ResolvedWorkerConfig<TPayload = unknown> {
|
|
@@ -244,7 +269,7 @@ interface ResolvedWorkerConfig<TPayload = unknown> {
|
|
|
244
269
|
retry: number;
|
|
245
270
|
timeout: number;
|
|
246
271
|
backoff: BackoffStrategy;
|
|
247
|
-
handler: (
|
|
272
|
+
handler: (payload: TPayload, meta: WorkerMeta) => Promise<void>;
|
|
248
273
|
}
|
|
249
274
|
/**
|
|
250
275
|
* Define a background worker that processes jobs from the queue.
|
|
@@ -258,7 +283,7 @@ interface ResolvedWorkerConfig<TPayload = unknown> {
|
|
|
258
283
|
* retry: 5,
|
|
259
284
|
* timeout: 60,
|
|
260
285
|
* backoff: "exponential",
|
|
261
|
-
* handler: async (
|
|
286
|
+
* handler: async (payload: { to: string; subject: string }, meta) => {
|
|
262
287
|
* await sendEmail(payload.to, payload.subject);
|
|
263
288
|
* },
|
|
264
289
|
* });
|
|
@@ -266,13 +291,11 @@ interface ResolvedWorkerConfig<TPayload = unknown> {
|
|
|
266
291
|
*/
|
|
267
292
|
declare function defineWorker<TPayload = unknown>(config: WorkerConfig<TPayload>): ResolvedWorkerConfig<TPayload>;
|
|
268
293
|
|
|
269
|
-
/**
|
|
270
|
-
|
|
271
|
-
|
|
294
|
+
/** Non-service, per-invocation data for job handlers.
|
|
295
|
+
* Services (Database, Log, …) are imported as singletons, not passed here. */
|
|
296
|
+
interface JobMeta {
|
|
297
|
+
/** Branch-scoped env vars. */
|
|
272
298
|
env: Record<string, string>;
|
|
273
|
-
log: Logger;
|
|
274
|
-
cache: CacheClient;
|
|
275
|
-
queue: QueueClient;
|
|
276
299
|
projectId: string;
|
|
277
300
|
environmentId: string;
|
|
278
301
|
}
|
|
@@ -285,14 +308,14 @@ interface JobConfig {
|
|
|
285
308
|
/** Execution timeout in seconds. Defaults to 30. */
|
|
286
309
|
timeout?: number;
|
|
287
310
|
/** Job handler function. */
|
|
288
|
-
handler: (
|
|
311
|
+
handler: (meta: JobMeta) => Promise<void>;
|
|
289
312
|
}
|
|
290
313
|
/** Resolved job configuration with defaults applied. */
|
|
291
314
|
interface ResolvedJobConfig {
|
|
292
315
|
name: string;
|
|
293
316
|
schedule: string;
|
|
294
317
|
timeout: number;
|
|
295
|
-
handler: (
|
|
318
|
+
handler: (meta: JobMeta) => Promise<void>;
|
|
296
319
|
}
|
|
297
320
|
/**
|
|
298
321
|
* Define a scheduled cron job.
|
|
@@ -305,9 +328,9 @@ interface ResolvedJobConfig {
|
|
|
305
328
|
* name: "cleanup-expired",
|
|
306
329
|
* schedule: "0 3 * * *", // every day at 3 AM
|
|
307
330
|
* timeout: 60,
|
|
308
|
-
* handler: async (
|
|
309
|
-
* await
|
|
310
|
-
*
|
|
331
|
+
* handler: async (meta) => {
|
|
332
|
+
* await Database.delete("sessions", "expired < NOW()");
|
|
333
|
+
* Log.info("Cleaned up expired sessions");
|
|
311
334
|
* },
|
|
312
335
|
* });
|
|
313
336
|
* ```
|
|
@@ -316,16 +339,15 @@ declare function defineJob(config: JobConfig): ResolvedJobConfig;
|
|
|
316
339
|
|
|
317
340
|
/** Supported webhook provider types. */
|
|
318
341
|
type WebhookProvider = "stripe" | "github" | "twilio" | "sendgrid" | "slack" | "discord" | "livekit";
|
|
319
|
-
/**
|
|
320
|
-
|
|
321
|
-
|
|
342
|
+
/** Non-service, per-invocation data for webhook handlers.
|
|
343
|
+
* Services (Database, Log, …) are imported as singletons, not passed here. */
|
|
344
|
+
interface WebhookMeta {
|
|
345
|
+
/** Branch-scoped env vars. */
|
|
322
346
|
env: Record<string, string>;
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
queue: QueueClient;
|
|
347
|
+
/** Per-invocation id for correlation. */
|
|
348
|
+
requestId: string;
|
|
326
349
|
projectId: string;
|
|
327
350
|
environmentId: string;
|
|
328
|
-
requestId: string;
|
|
329
351
|
}
|
|
330
352
|
/** Secret reference — resolves from environment variables at runtime. */
|
|
331
353
|
interface EnvSecretRef {
|
|
@@ -386,7 +408,7 @@ interface ProviderEventMap {
|
|
|
386
408
|
};
|
|
387
409
|
}
|
|
388
410
|
/** Event handler function type. */
|
|
389
|
-
type WebhookEventHandler<TEvent = Record<string, unknown>> = (
|
|
411
|
+
type WebhookEventHandler<TEvent = Record<string, unknown>> = (event: TEvent, meta: WebhookMeta) => Promise<void>;
|
|
390
412
|
/** Provider-based webhook configuration. */
|
|
391
413
|
interface ProviderWebhookConfig<P extends WebhookProvider = WebhookProvider> {
|
|
392
414
|
provider: P;
|
|
@@ -406,7 +428,7 @@ interface WebhookRequest {
|
|
|
406
428
|
interface CustomWebhookConfig {
|
|
407
429
|
path: string;
|
|
408
430
|
verify?: (req: WebhookRequest) => Promise<boolean> | boolean;
|
|
409
|
-
handler: (
|
|
431
|
+
handler: (payload: Record<string, unknown>, meta: WebhookMeta) => Promise<void>;
|
|
410
432
|
}
|
|
411
433
|
/** Resolved provider webhook (internal). */
|
|
412
434
|
interface ResolvedProviderWebhook<P extends WebhookProvider = WebhookProvider> {
|
|
@@ -420,7 +442,7 @@ interface ResolvedCustomWebhook {
|
|
|
420
442
|
type: "custom";
|
|
421
443
|
path: string;
|
|
422
444
|
verify?: (req: WebhookRequest) => Promise<boolean> | boolean;
|
|
423
|
-
handler: (
|
|
445
|
+
handler: (payload: Record<string, unknown>, meta: WebhookMeta) => Promise<void>;
|
|
424
446
|
}
|
|
425
447
|
/** Union of resolved webhook types. */
|
|
426
448
|
type ResolvedWebhookConfig = ResolvedProviderWebhook | ResolvedCustomWebhook;
|
|
@@ -433,8 +455,8 @@ type ResolvedWebhookConfig = ResolvedProviderWebhook | ResolvedCustomWebhook;
|
|
|
433
455
|
* provider: "stripe",
|
|
434
456
|
* secret: { env: "STRIPE_WEBHOOK_SECRET" },
|
|
435
457
|
* events: {
|
|
436
|
-
* "checkout.session.completed": async (
|
|
437
|
-
* await
|
|
458
|
+
* "checkout.session.completed": async (event, meta) => {
|
|
459
|
+
* await Database.insert("orders", { status: "paid" });
|
|
438
460
|
* },
|
|
439
461
|
* },
|
|
440
462
|
* });
|
|
@@ -449,21 +471,111 @@ declare function defineWebhook<P extends WebhookProvider>(config: ProviderWebhoo
|
|
|
449
471
|
* export default defineWebhook({
|
|
450
472
|
* path: "/webhooks/livekit",
|
|
451
473
|
* verify: async (req) => req.headers["x-api-key"] === "secret",
|
|
452
|
-
* handler: async (
|
|
453
|
-
*
|
|
474
|
+
* handler: async (payload, meta) => {
|
|
475
|
+
* Log.info("Received webhook", payload);
|
|
454
476
|
* },
|
|
455
477
|
* });
|
|
456
478
|
* ```
|
|
457
479
|
*/
|
|
458
480
|
declare function defineWebhook(config: CustomWebhookConfig): ResolvedCustomWebhook;
|
|
459
481
|
|
|
460
|
-
/**
|
|
461
|
-
|
|
462
|
-
|
|
482
|
+
/**
|
|
483
|
+
* resource.ts — external connections as lifecycle-managed classes.
|
|
484
|
+
*
|
|
485
|
+
* A `Resource` subclass models one external connection (a pooled datastore, a
|
|
486
|
+
* stateless API client, or a per-user factory). The framework discovers each
|
|
487
|
+
* instance, calls `init(env)` ONCE at boot with the declared secret subset, and
|
|
488
|
+
* `shutdown()` (reverse order) on SIGTERM. On top of that lifecycle the author
|
|
489
|
+
* exposes their own clean facade methods.
|
|
490
|
+
*
|
|
491
|
+
* // resources/neo4j.ts
|
|
492
|
+
* import { Resource } from "@palbase/backend";
|
|
493
|
+
* import neo4j, { type Driver, type Session } from "neo4j-driver";
|
|
494
|
+
*
|
|
495
|
+
* export class Neo4jResource extends Resource {
|
|
496
|
+
* static secrets = ["NEO4J_URL", "NEO4J_USER", "NEO4J_PASSWORD"] as const;
|
|
497
|
+
* private driver!: Driver;
|
|
498
|
+
* async init(env: { NEO4J_URL: string; NEO4J_USER: string; NEO4J_PASSWORD: string }) {
|
|
499
|
+
* this.driver = neo4j.driver(env.NEO4J_URL, neo4j.auth.basic(env.NEO4J_USER, env.NEO4J_PASSWORD));
|
|
500
|
+
* }
|
|
501
|
+
* async shutdown() { await this.driver.close(); }
|
|
502
|
+
* session(): Session { return this.driver.session(); }
|
|
503
|
+
* }
|
|
504
|
+
* export const neo4j = new Neo4jResource(); // framework finds + manages it
|
|
505
|
+
*
|
|
506
|
+
* # Boot scope (NOT request-ALS)
|
|
507
|
+
*
|
|
508
|
+
* Resources are instantiated once at process boot. This is deliberately NOT the
|
|
509
|
+
* per-request {@link AsyncLocalStorage} scope used for `Database`/`Cache`/… — a
|
|
510
|
+
* connection pool must outlive a single request. The runtime discovers
|
|
511
|
+
* resources from the project's `resources/` directory, registers each via
|
|
512
|
+
* {@link __registerResource}, then calls {@link __runResourceBoot} before
|
|
513
|
+
* serving and {@link __shutdownResources} on SIGTERM. Discovery is a runtime
|
|
514
|
+
* concern; the SDK provides the base class + registry + boot/shutdown hooks.
|
|
515
|
+
*/
|
|
516
|
+
/** Map a declared `secrets` tuple to the `init(env)` argument type — a record
|
|
517
|
+
* over the secret names, each `string`. An empty tuple maps to an empty
|
|
518
|
+
* record. */
|
|
519
|
+
type ResourceEnv<Secrets extends readonly string[]> = {
|
|
520
|
+
[K in Secrets[number]]: string;
|
|
521
|
+
};
|
|
522
|
+
/**
|
|
523
|
+
* Base class for external connections.
|
|
524
|
+
*
|
|
525
|
+
* - `static secrets` — OPTIONAL readonly tuple of env-var names this resource
|
|
526
|
+
* needs. Drives (a) the `env` type passed to `init` and (b) the
|
|
527
|
+
* missing-secret check at boot (deploy fails naming the absent secret).
|
|
528
|
+
* - `init(env)` — called ONCE at boot with the declared secret subset. May be
|
|
529
|
+
* sync (`void`) or async (`Promise<void>`).
|
|
530
|
+
* - `shutdown()` — OPTIONAL drain hook, called on SIGTERM in reverse boot
|
|
531
|
+
* order.
|
|
532
|
+
*
|
|
533
|
+
* @example
|
|
534
|
+
* import { Resource } from "@palbase/backend";
|
|
535
|
+
* import { Client } from "@googlemaps/google-maps-services-js";
|
|
536
|
+
*
|
|
537
|
+
* export class GoogleResource extends Resource {
|
|
538
|
+
* static secrets = ["GOOGLE_MAPS_KEY"] as const;
|
|
539
|
+
* private client = new Client();
|
|
540
|
+
* private key = "";
|
|
541
|
+
* init(env: { GOOGLE_MAPS_KEY: string }) { this.key = env.GOOGLE_MAPS_KEY; }
|
|
542
|
+
* }
|
|
543
|
+
* export const google = new GoogleResource();
|
|
544
|
+
*/
|
|
545
|
+
declare abstract class Resource {
|
|
546
|
+
/** The env-var names this resource needs. Optional; omit for none. */
|
|
547
|
+
static secrets?: readonly string[];
|
|
548
|
+
/** Set up the connection from the declared secrets. Called once at boot. */
|
|
549
|
+
abstract init(env: Record<string, string>): void | Promise<void>;
|
|
550
|
+
/** Drain/close the connection on SIGTERM. Optional. */
|
|
551
|
+
shutdown?(): void | Promise<void>;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Register a resource instance with the boot registry. The runtime calls this
|
|
555
|
+
* for each instance discovered under the project's `resources/` directory.
|
|
556
|
+
* NOT part of the public author-facing API (prefixed `__`).
|
|
557
|
+
*/
|
|
558
|
+
declare function __registerResource(resource: Resource): void;
|
|
559
|
+
/**
|
|
560
|
+
* Boot every registered resource that has not yet been booted: resolve its
|
|
561
|
+
* declared secret subset from `envMap`, then await its `init(env)`. Idempotent
|
|
562
|
+
* — an already-booted resource is skipped. Throws (failing deploy/boot) when a
|
|
563
|
+
* declared secret is absent, naming the missing secret. NOT part of the public
|
|
564
|
+
* author-facing API.
|
|
565
|
+
*/
|
|
566
|
+
declare function __runResourceBoot(envMap: Record<string, string>): Promise<void>;
|
|
567
|
+
/**
|
|
568
|
+
* Shut down every booted resource in REVERSE registration order, awaiting each
|
|
569
|
+
* `shutdown()` (a no-op when undefined). Clears the registry afterwards so a
|
|
570
|
+
* second call is a no-op. NOT part of the public author-facing API.
|
|
571
|
+
*/
|
|
572
|
+
declare function __shutdownResources(): Promise<void>;
|
|
573
|
+
|
|
574
|
+
/** Non-service, per-invocation data for hook handlers.
|
|
575
|
+
* Services (Database, Log, …) are imported as singletons, not passed here. */
|
|
576
|
+
interface HookMeta {
|
|
577
|
+
/** Branch-scoped env vars. */
|
|
463
578
|
env: Record<string, string>;
|
|
464
|
-
log: Logger;
|
|
465
|
-
cache: CacheClient;
|
|
466
|
-
queue: QueueClient;
|
|
467
579
|
projectId: string;
|
|
468
580
|
environmentId: string;
|
|
469
581
|
}
|
|
@@ -471,7 +583,8 @@ interface HookContext extends PalbaseModuleClients {
|
|
|
471
583
|
interface UserCreatedEvent {
|
|
472
584
|
user: {
|
|
473
585
|
id: string;
|
|
474
|
-
email
|
|
586
|
+
/** User's email, if they signed up with one (absent for phone-only users). */
|
|
587
|
+
email?: string;
|
|
475
588
|
role: string;
|
|
476
589
|
metadata: Record<string, unknown>;
|
|
477
590
|
createdAt: string;
|
|
@@ -481,7 +594,8 @@ interface UserCreatedEvent {
|
|
|
481
594
|
interface SignInEvent {
|
|
482
595
|
user: {
|
|
483
596
|
id: string;
|
|
484
|
-
email
|
|
597
|
+
/** User's email, if they have one (absent for phone-only users). */
|
|
598
|
+
email?: string;
|
|
485
599
|
role: string;
|
|
486
600
|
};
|
|
487
601
|
provider: string;
|
|
@@ -491,11 +605,14 @@ interface SignInEvent {
|
|
|
491
605
|
interface SignOutEvent {
|
|
492
606
|
user: {
|
|
493
607
|
id: string;
|
|
494
|
-
email
|
|
608
|
+
/** User's email, if they have one (absent for phone-only users). */
|
|
609
|
+
email?: string;
|
|
495
610
|
};
|
|
496
611
|
timestamp: string;
|
|
497
612
|
}
|
|
498
|
-
/** Payload for auth.onPasswordReset hook.
|
|
613
|
+
/** Payload for auth.onPasswordReset hook.
|
|
614
|
+
* Password reset is inherently email-based, so `email` is always present here
|
|
615
|
+
* (a phone-only passwordless user cannot trigger this event). */
|
|
499
616
|
interface PasswordResetEvent {
|
|
500
617
|
user: {
|
|
501
618
|
id: string;
|
|
@@ -548,7 +665,7 @@ interface DocumentDeletedEvent {
|
|
|
548
665
|
data: Record<string, unknown>;
|
|
549
666
|
};
|
|
550
667
|
}
|
|
551
|
-
type HookHandler<TEvent> = (
|
|
668
|
+
type HookHandler<TEvent> = (event: TEvent, meta: HookMeta) => Promise<void>;
|
|
552
669
|
/** Resolved hook configuration (internal). */
|
|
553
670
|
interface ResolvedHook<TEvent = unknown> {
|
|
554
671
|
module: string;
|
|
@@ -571,4 +688,4 @@ declare const documents: {
|
|
|
571
688
|
onDocumentDeleted(handler: HookHandler<DocumentDeletedEvent>): ResolvedHook<DocumentDeletedEvent>;
|
|
572
689
|
};
|
|
573
690
|
|
|
574
|
-
export { type BackoffStrategy, Cache, CacheClient, type CustomWebhookConfig, DBClient, Database, type DocumentCreatedEvent, type DocumentDeletedEvent, type DocumentUpdatedEvent, Documents, type EnvSecretRef,
|
|
691
|
+
export { type BackoffStrategy, Body, Cache, CacheClient, Client, Controller, type ControllerOptions, type CustomWebhookConfig, DBClient, Database, Delete, type DocumentCreatedEvent, type DocumentDeletedEvent, type DocumentUpdatedEvent, Documents, type EnvSecretRef, EnvTypedDatabase, type FileDeletedEvent, type FileUploadedEvent, Flags, Get, Headers, type HookHandler, type HookMeta, type HttpMethodUpper, type JobConfig, type JobMeta, Log, Logger, Notifications, OptionalUser, PalbaseDocsClient, PalbaseFlagsClient, PalbaseNotificationsClient, PalbaseStorageClient, Param, type PasswordResetEvent, Patch, Post, type ProviderEventMap, type ProviderWebhookConfig, Put, Query, Queue, QueueClient, RateLimitConfig, Req, RequestId, type ResolvedCustomWebhook, type ResolvedHook, type ResolvedJobConfig, type ResolvedProviderWebhook, type ResolvedWebhookConfig, type ResolvedWorkerConfig, Resource, type ResourceEnv, Returns, type RouteOptions, type RuntimeServices, SchemaDef, type SignInEvent, type SignOutEvent, Storage, TraceId, User, type UserCreatedEvent, User$1 as UserT, type WebhookEventHandler, type WebhookMeta, type WebhookProvider, type WebhookRequest, type WorkerConfig, type WorkerMeta, __getRuntime, __registerResource, __requestALS, __runResourceBoot, __runWithRuntime, __setRuntime, __shutdownResources, auth, defineJob, defineWebhook, defineWorker, documents, makeEnvDts, storage };
|