@palbase/backend 2.0.2 → 3.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-4J3F32SH.js → chunk-B7EUJP5W.js} +38 -9
- package/dist/chunk-B7EUJP5W.js.map +1 -0
- package/dist/{chunk-L36JLUPO.js → chunk-PHAFZGHN.js} +43 -46
- package/dist/chunk-PHAFZGHN.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 +28 -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 +3 -233
- package/dist/db/index.js.map +1 -1
- package/dist/{endpoint-Djk5L6G2.d.ts → endpoint-DJ98tQd6.d.cts} +30 -68
- package/dist/{endpoint-BlcY2xNA.d.cts → endpoint-DJ98tQd6.d.ts} +30 -68
- package/dist/index-CXUs9iTQ.d.ts +294 -0
- package/dist/index-CZAwpQE1.d.cts +294 -0
- package/dist/index.cjs +229 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +398 -154
- package/dist/index.d.ts +398 -154
- package/dist/index.js +147 -12
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs +41 -1
- 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 +1 -1
- package/docs/README.md +31 -10
- package/docs/background.md +19 -13
- package/docs/database.md +30 -17
- package/docs/endpoints.md +42 -24
- package/docs/errors.md +3 -4
- package/docs/events.md +25 -17
- package/docs/getting-started.md +25 -9
- package/docs/llms-full.txt +489 -164
- package/docs/llms.txt +3 -1
- package/docs/migrations.md +98 -0
- package/docs/resources.md +94 -0
- package/docs/routing.md +59 -26
- package/docs/schema.md +48 -38
- package/docs/services.md +5 -6
- package/package.json +11 -1
- package/dist/chunk-4J3F32SH.js.map +0 -1
- 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.cts
CHANGED
|
@@ -1,95 +1,12 @@
|
|
|
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, E as ErrorMap, M as Middleware, P as PBRequest, I as IsAuthed, U as User } from './endpoint-DJ98tQd6.cjs';
|
|
2
|
+
export { f as AuthConfig, g as ClientInfo, h as ErrorDef, i as ErrorThrowers, F as FileContext, H as HttpError, j as HttpMethod, k as MiddlewareContext, l as MiddlewareHandler, m as PalbaseAnalyticsClient, n as PalbaseAnalyticsManagementNamespace, o as PalbaseAnalyticsProperties, p as PalbaseAnalyticsQueryNamespace, q as PalbaseAttestAndroidParams, r as PalbaseAttestAndroidResult, s as PalbaseAttestiOSParams, t as PalbaseAttestiOSResult, u as PalbaseAuthClient, v as PalbaseBindDeviceParams, w as PalbaseBucketClient, x as PalbaseCmsClient, y as PalbaseCmsFindOneOptions, z as PalbaseCmsFindOptions, B as PalbaseCohortQueryInput, G as PalbaseCohortResult, J as PalbaseCollectionRef, K as PalbaseCountQueryInput, N as PalbaseCountResult, O as PalbaseCreateLinkParams, S as PalbaseDeviceInfo, T as PalbaseDeviceTokenView, V as PalbaseDocumentRef, W as PalbaseDocumentSnapshot, X as PalbaseEmailClient, Y as PalbaseEmailSendParams, Z as PalbaseEmailSendResponse, _ as PalbaseEventNamesResult, $ as PalbaseEventsQueryInput, a0 as PalbaseEventsResult, a1 as PalbaseFileObject, a2 as PalbaseFlag, a3 as PalbaseFlagContext, a4 as PalbaseFlagVariant, a5 as PalbaseFunctionsClient, a6 as PalbaseFunnelQueryInput, a7 as PalbaseFunnelResult, a8 as PalbaseIdentifyTraits, a9 as PalbaseInboxClient, aa as PalbaseInboxListOptions, ab as PalbaseInboxListResult, ac as PalbaseInboxMessage, ad as PalbaseInboxSendParams, ae as PalbaseInboxSendResponse, af as PalbaseInitialLink, ag as PalbaseInvokeOptions, ah as PalbaseLink, ai as PalbaseLinkAnalytics, aj as PalbaseLinkDetails, ak as PalbaseLinksClient, al as PalbaseListLinksOptions, am as PalbaseListLinksResult, an as PalbaseListOptions, ao as PalbaseMatchParams, ap as PalbaseMultiChannelResponse, aq as PalbaseOverviewResult, ar as PalbasePreferences, as as PalbasePreferencesClient, at as PalbasePublicUrlResponse, au as PalbasePushClient, av as PalbasePushSendParams, aw as PalbasePushSendResponse, ax as PalbaseQrCodeOptions, ay as PalbaseQuerySnapshot, az as PalbaseRealtimeChannel, aA as PalbaseRealtimeClient, aB as PalbaseRealtimeMessage, aC as PalbaseRegisterDeviceParams, aD as PalbaseResult, aE as PalbaseRetentionQueryInput, aF as PalbaseRetentionResult, aG as PalbaseSession, aH as PalbaseSignedUrlResponse, aI as PalbaseSmsClient, aJ as PalbaseSmsSendParams, aK as PalbaseSmsSendResponse, aL as PalbaseTransformOptions, aM as PalbaseUpdateLinkParams, aN as PalbaseUploadOptions, aO as PalbaseUser, aP as PalbaseUserDetailResult, aQ as PalbaseUsersQueryInput, aR as PalbaseUsersResult, aS as PalbaseVerifyRequestSignatureParams, aT as PalbaseWhereOperator, aU as TxClient, aV as defineMiddleware } from './endpoint-DJ98tQd6.cjs';
|
|
3
3
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
4
|
-
import {
|
|
5
|
-
export {
|
|
4
|
+
import { E as EnvTypedDatabase, S as SchemaDef } from './index-CZAwpQE1.cjs';
|
|
5
|
+
export { C as ColumnBuilder, a as ColumnDef, b as ColumnMap, c as ColumnType, d as EnvTables, e as EnvTypedTable, f as EnvTypedTx, I as InsertShape, O as OnDeleteAction, R as RowShape, g as SchemaInput, T as TableDef, h as TypedDB, i as TypedTable, j as TypedTx, k as boolean, l as defineSchema, m as enumType, n as integer, o as jsonb, p as makeTypedDB, t as text, q as timestamp, u as uuid } from './index-CZAwpQE1.cjs';
|
|
6
|
+
export { TableTypes, Tables } from './db/env.cjs';
|
|
7
|
+
import { ZodSchema, z } 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
|
*
|
|
@@ -98,8 +15,7 @@ declare function makeTypedDB<S extends SchemaDef>(schema: S, raw: DBClient): Typ
|
|
|
98
15
|
*
|
|
99
16
|
* import { Database, Documents, Cache } from "@palbase/backend";
|
|
100
17
|
*
|
|
101
|
-
* export default
|
|
102
|
-
* method: "POST",
|
|
18
|
+
* export default defineHandler({
|
|
103
19
|
* handler: async (req) => {
|
|
104
20
|
* const row = await Database.insert("todos", { title: req.input.title });
|
|
105
21
|
* return row;
|
|
@@ -176,10 +92,22 @@ 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
|
+
* @example
|
|
104
|
+
* import { Database } from "@palbase/backend";
|
|
105
|
+
*
|
|
106
|
+
* const todo = await Database.tables.todos.insert({ title: req.input.title });
|
|
107
|
+
* todo.id; // string ✓
|
|
108
|
+
* const rows = await Database.query("SELECT id FROM todos WHERE done = $1", [false]);
|
|
109
|
+
*/
|
|
110
|
+
declare const Database: EnvTypedDatabase;
|
|
183
111
|
/** Firestore-like document client (PalDocs). */
|
|
184
112
|
declare const Documents: PalbaseDocsClient;
|
|
185
113
|
/** Object storage client (buckets, signed URLs). */
|
|
@@ -194,31 +122,255 @@ declare const Log: Logger;
|
|
|
194
122
|
declare const Notifications: PalbaseNotificationsClient;
|
|
195
123
|
/** Feature flags. */
|
|
196
124
|
declare const Flags: PalbaseFlagsClient;
|
|
125
|
+
|
|
197
126
|
/**
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
* `query`/`transaction` ops.
|
|
127
|
+
* env-gen.ts — generate the `palbase-env.d.ts` text from a `defineSchema()`
|
|
128
|
+
* result.
|
|
201
129
|
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
130
|
+
* The CLI (`palbase serve` / codegen) and the deploy pipeline call
|
|
131
|
+
* {@link makeEnvDts} with the project's schema and write the returned string to
|
|
132
|
+
* `palbase-env.d.ts` at the project root. That file AUGMENTS the
|
|
133
|
+
* `@palbase/backend/env` `Tables` interface (controlled global augmentation,
|
|
134
|
+
* C5) so `Database.tables.<name>` is typed with no import and no generic.
|
|
204
135
|
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
136
|
+
* The output is FLAT: each table gets a `{ row: {...}; insert: {...} }` entry
|
|
137
|
+
* with plain TypeScript object types. The phantom `ColumnBuilder<...>` type
|
|
138
|
+
* NEVER appears in the generated `.d.ts` — that type only lives at authoring
|
|
139
|
+
* time inside `db/schema.ts`.
|
|
207
140
|
*/
|
|
208
|
-
declare function typedDatabase<TSchema extends SchemaDef>(schema: TSchema): TypedDB<TSchema> & DBClient;
|
|
209
141
|
|
|
210
|
-
/**
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
142
|
+
/**
|
|
143
|
+
* Generate the full `palbase-env.d.ts` text for a schema.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* import { makeEnvDts } from "@palbase/backend";
|
|
147
|
+
* import schema from "./db/schema.js";
|
|
148
|
+
* writeFileSync("palbase-env.d.ts", makeEnvDts(schema));
|
|
149
|
+
*/
|
|
150
|
+
declare function makeEnvDts(schema: SchemaDef): string;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* handler.ts — `defineHandler`: an endpoint UNIT (schema + thin logic), one per
|
|
154
|
+
* file, with NO routing.
|
|
155
|
+
*
|
|
156
|
+
* Everything that types `req` lives here (colocated): `auth` → `req.user`
|
|
157
|
+
* nullability, `input` → `req.input`, `errors` → `req.errors`, `output` → the
|
|
158
|
+
* return type. There is NO `method`/`path` on a handler — the HTTP verb + path
|
|
159
|
+
* are declared in a controller's route map, NOT on the handler. A handler file
|
|
160
|
+
* does:
|
|
161
|
+
*
|
|
162
|
+
* // handlers/places/import-nearby.ts
|
|
163
|
+
* export default defineHandler({ auth, input, output, errors, handler });
|
|
164
|
+
*
|
|
165
|
+
* # Runtime-readable shape (read by the Go runtime's extract_meta.js)
|
|
166
|
+
*
|
|
167
|
+
* `defineHandler` returns a {@link HandlerDef}: a plain object marked with
|
|
168
|
+
* `__palbase: "handler"` carrying the optional `auth`/`input`/`output`/`errors`
|
|
169
|
+
* and the `handler` function. There is deliberately NO `method`/`path` on it —
|
|
170
|
+
* a handler is route-agnostic.
|
|
171
|
+
*/
|
|
172
|
+
/** Configuration accepted by {@link defineHandler}. Carries the schema +
|
|
173
|
+
* auth/errors/middleware that type `req`, but NO `method` (routing lives in
|
|
174
|
+
* controllers). */
|
|
175
|
+
interface HandlerConfig<TInputSchema extends ZodSchema = ZodSchema, TOutputSchema extends ZodSchema = ZodSchema, TAuth extends AuthSpec | undefined = undefined, TErrors extends ErrorMap | undefined = undefined> {
|
|
176
|
+
auth?: TAuth;
|
|
177
|
+
rateLimit?: RateLimitConfig;
|
|
178
|
+
input?: TInputSchema;
|
|
179
|
+
output?: TOutputSchema;
|
|
180
|
+
errors?: TErrors;
|
|
181
|
+
middleware?: Middleware[];
|
|
182
|
+
handler: (req: PBRequest<z.infer<TInputSchema>, IsAuthed<TAuth>, TErrors>) => Promise<z.infer<TOutputSchema>> | z.infer<TOutputSchema>;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The runtime-readable handler descriptor returned by {@link defineHandler}.
|
|
186
|
+
*
|
|
187
|
+
* Shape (the Go runtime's `extract_meta.js` reads this off the default export):
|
|
188
|
+
*
|
|
189
|
+
* {
|
|
190
|
+
* __palbase: "handler",
|
|
191
|
+
* auth?: AuthSpec,
|
|
192
|
+
* rateLimit?: RateLimitConfig,
|
|
193
|
+
* input?: ZodSchema,
|
|
194
|
+
* output?: ZodSchema,
|
|
195
|
+
* errors?: ErrorMap,
|
|
196
|
+
* middleware?: Middleware[],
|
|
197
|
+
* handler: (req) => result, // the invoked function
|
|
198
|
+
* }
|
|
199
|
+
*
|
|
200
|
+
* NOTE: there is NO `method`/`path` — those come from the controller route map.
|
|
201
|
+
*/
|
|
202
|
+
interface HandlerDef<TInputSchema extends ZodSchema = ZodSchema, TOutputSchema extends ZodSchema = ZodSchema, TAuth extends AuthSpec | undefined = undefined, TErrors extends ErrorMap | undefined = undefined> {
|
|
203
|
+
/** Discriminant the runtime + a controller route check read. */
|
|
204
|
+
readonly __palbase: "handler";
|
|
205
|
+
auth?: TAuth;
|
|
206
|
+
rateLimit?: RateLimitConfig;
|
|
207
|
+
input?: TInputSchema;
|
|
208
|
+
output?: TOutputSchema;
|
|
209
|
+
errors?: TErrors;
|
|
210
|
+
middleware?: Middleware[];
|
|
211
|
+
handler: (req: PBRequest<z.infer<TInputSchema>, IsAuthed<TAuth>, TErrors>) => Promise<z.infer<TOutputSchema>> | z.infer<TOutputSchema>;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* A {@link HandlerDef} with its schema/auth/errors type params ERASED — the
|
|
215
|
+
* shape a controller's `route.*` stores. EVERY `defineHandler()` result (any
|
|
216
|
+
* `auth`/`input`/`output`/`errors`) is assignable to this, because `handler` is
|
|
217
|
+
* typed `(req: never) => unknown` (params are contravariant, so a handler taking
|
|
218
|
+
* any concrete `PBRequest` satisfies a `never` param). A controller only needs
|
|
219
|
+
* to route + carry the handler; the handler's own `req` typing is validated
|
|
220
|
+
* where it is defined, so erasing the params here is sound and lets a route
|
|
221
|
+
* accept a handler regardless of its `auth`/`errors` — without `any`.
|
|
222
|
+
*/
|
|
223
|
+
interface AnyHandlerDef {
|
|
224
|
+
readonly __palbase: "handler";
|
|
225
|
+
auth?: AuthSpec;
|
|
226
|
+
rateLimit?: RateLimitConfig;
|
|
227
|
+
input?: ZodSchema;
|
|
228
|
+
output?: ZodSchema;
|
|
229
|
+
errors?: ErrorMap;
|
|
230
|
+
middleware?: Middleware[];
|
|
231
|
+
handler: (req: never) => unknown;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Define an endpoint handler (schema + thin logic), with full `req` inference
|
|
235
|
+
* and NO routing. Mounted by a controller's `route.*` map.
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* import { defineHandler, z, Database } from "@palbase/backend";
|
|
239
|
+
*
|
|
240
|
+
* export default defineHandler({
|
|
241
|
+
* auth: { required: true },
|
|
242
|
+
* input: z.object({ title: z.string() }),
|
|
243
|
+
* output: z.object({ id: z.string() }),
|
|
244
|
+
* errors: { titleTaken: { status: 409, code: "title_taken" } },
|
|
245
|
+
* handler: (req) => Database.tables.todos.insert({ title: req.input.title }),
|
|
246
|
+
* });
|
|
247
|
+
*/
|
|
248
|
+
declare function defineHandler<TInputSchema extends ZodSchema, TOutputSchema extends ZodSchema, const TAuth extends AuthSpec | undefined = undefined, const TErrors extends ErrorMap | undefined = undefined>(config: HandlerConfig<TInputSchema, TOutputSchema, TAuth, TErrors>): HandlerDef<TInputSchema, TOutputSchema, TAuth, TErrors>;
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* controller.ts — `defineController` + `route.*`: the ROUTE MAP (method+path →
|
|
252
|
+
* handler). Logic cannot be written here — a controller only wires existing
|
|
253
|
+
* handlers, so the small-blast-radius / no-cross-file-type-sync property holds.
|
|
254
|
+
*
|
|
255
|
+
* ```ts
|
|
256
|
+
* // controllers/places.controller.ts
|
|
257
|
+
* import { defineController, route } from "@palbase/backend";
|
|
258
|
+
* import importNearby from "../handlers/places/import-nearby.js";
|
|
259
|
+
* import listFavorites from "../handlers/places/list-favorites.js";
|
|
260
|
+
*
|
|
261
|
+
* export default defineController("/places", {
|
|
262
|
+
* importNearby: route.post("/import", importNearby), // map key = sugar
|
|
263
|
+
* listFavorites: route.get ("/favorites", listFavorites),
|
|
264
|
+
* });
|
|
265
|
+
* ```
|
|
266
|
+
*
|
|
267
|
+
* # Runtime-readable object shapes (read by the Go runtime's extract_meta.js)
|
|
268
|
+
*
|
|
269
|
+
* The default export of a controller file is a `ControllerDef`:
|
|
270
|
+
*
|
|
271
|
+
* ControllerDef = {
|
|
272
|
+
* __palbase: "controller",
|
|
273
|
+
* basePath: string, // e.g. "/places"
|
|
274
|
+
* routes: { [mapKey: string]: RouteDef } // mapKey is AUTHORING SUGAR only
|
|
275
|
+
* }
|
|
276
|
+
*
|
|
277
|
+
* RouteDef = {
|
|
278
|
+
* __palbase: "route",
|
|
279
|
+
* method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", // upper-case
|
|
280
|
+
* path: string, // the SUBPATH passed to route.*(…), e.g. "/import"
|
|
281
|
+
* handler: HandlerDef, // the imported defineHandler() result
|
|
282
|
+
* }
|
|
283
|
+
*
|
|
284
|
+
* HandlerDef = {
|
|
285
|
+
* __palbase: "handler",
|
|
286
|
+
* auth?, rateLimit?, input?, output?, errors?, middleware?,
|
|
287
|
+
* handler: (req) => result,
|
|
288
|
+
* }
|
|
289
|
+
*
|
|
290
|
+
* The FULL path for a route is `basePath + path` (e.g. "/places" + "/import" =
|
|
291
|
+
* "/places/import"). The operationId is NOT computed here — the runtime
|
|
292
|
+
* `generator.go` derives it FLAT from `(method, full-path)` (e.g.
|
|
293
|
+
* `postPlacesImport`). The map key (`importNearby`) is authoring sugar and is
|
|
294
|
+
* NOT the operationId.
|
|
295
|
+
*/
|
|
296
|
+
/** The five HTTP verbs a route may declare, upper-cased (matches the runtime
|
|
297
|
+
* router + OpenAPI which lower-cases on its own). */
|
|
298
|
+
type HttpMethodUpper = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
299
|
+
/**
|
|
300
|
+
* A single route entry: the HTTP verb, the SUBPATH (relative to the
|
|
301
|
+
* controller's `basePath`), and the handler to invoke. Produced by `route.*`.
|
|
302
|
+
*/
|
|
303
|
+
interface RouteDef<H extends AnyHandlerDef = AnyHandlerDef, M extends HttpMethodUpper = HttpMethodUpper> {
|
|
304
|
+
/** Discriminant the runtime + `defineController` read. */
|
|
305
|
+
readonly __palbase: "route";
|
|
306
|
+
/** Upper-case HTTP verb. */
|
|
307
|
+
method: M;
|
|
308
|
+
/** The subpath passed to `route.*(subpath, handler)` (relative to basePath). */
|
|
309
|
+
path: string;
|
|
310
|
+
/** The handler (a `defineHandler()` result) this route invokes. */
|
|
311
|
+
handler: H;
|
|
312
|
+
}
|
|
313
|
+
/** The route map a controller declares: keys are authoring sugar, values must
|
|
314
|
+
* each be a `route.*()` result. The mapped-over `Record<string, RouteDef>`
|
|
315
|
+
* constraint is what makes a bare handler (or plain object) a TS error. */
|
|
316
|
+
type RouteMap = Record<string, RouteDef>;
|
|
317
|
+
/**
|
|
318
|
+
* The controller descriptor returned by {@link defineController}. The default
|
|
319
|
+
* export of a `controllers/*.ts` file.
|
|
320
|
+
*/
|
|
321
|
+
interface ControllerDef<R extends RouteMap = RouteMap> {
|
|
322
|
+
/** Discriminant the runtime reads. */
|
|
323
|
+
readonly __palbase: "controller";
|
|
324
|
+
/** The path every route in this controller is mounted under (e.g. "/places"). */
|
|
325
|
+
basePath: string;
|
|
326
|
+
/** The route map — keys are authoring sugar, values are RouteDefs. */
|
|
327
|
+
routes: R;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Route builders — one per HTTP verb. Each takes the SUBPATH (relative to the
|
|
331
|
+
* controller `basePath`) and the handler to mount there.
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* import { defineController, route } from "@palbase/backend";
|
|
335
|
+
* import create from "../handlers/todos/create.js";
|
|
336
|
+
*
|
|
337
|
+
* export default defineController("/todos", {
|
|
338
|
+
* create: route.post("/", create),
|
|
339
|
+
* });
|
|
340
|
+
*/
|
|
341
|
+
declare const route: {
|
|
342
|
+
readonly get: <H extends AnyHandlerDef>(path: string, handler: H) => RouteDef<H, "GET">;
|
|
343
|
+
readonly post: <H extends AnyHandlerDef>(path: string, handler: H) => RouteDef<H, "POST">;
|
|
344
|
+
readonly put: <H extends AnyHandlerDef>(path: string, handler: H) => RouteDef<H, "PUT">;
|
|
345
|
+
readonly patch: <H extends AnyHandlerDef>(path: string, handler: H) => RouteDef<H, "PATCH">;
|
|
346
|
+
readonly delete: <H extends AnyHandlerDef>(path: string, handler: H) => RouteDef<H, "DELETE">;
|
|
347
|
+
};
|
|
348
|
+
/**
|
|
349
|
+
* Define a controller: a base path + a route map (method+path → handler). The
|
|
350
|
+
* route-map KEY is authoring sugar only (NOT the operationId). Each route value
|
|
351
|
+
* MUST be a `route.*()` result — a bare handler or plain object is a TS error,
|
|
352
|
+
* which is what keeps logic out of controllers structurally.
|
|
353
|
+
*
|
|
354
|
+
* @example
|
|
355
|
+
* import { defineController, route } from "@palbase/backend";
|
|
356
|
+
* import create from "../handlers/todos/create.js";
|
|
357
|
+
* import list from "../handlers/todos/list.js";
|
|
358
|
+
*
|
|
359
|
+
* export default defineController("/todos", {
|
|
360
|
+
* create: route.post("/", create),
|
|
361
|
+
* list: route.get("/", list),
|
|
362
|
+
* });
|
|
363
|
+
*/
|
|
364
|
+
declare function defineController<const R extends RouteMap>(basePath: string, routes: R): ControllerDef<R>;
|
|
365
|
+
|
|
366
|
+
/** Non-service, per-invocation data for background worker handlers.
|
|
367
|
+
* Services (Database, Log, …) are imported as singletons, not passed here. */
|
|
368
|
+
interface WorkerMeta {
|
|
369
|
+
/** Branch-scoped env vars. */
|
|
217
370
|
env: Record<string, string>;
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
errors: ErrorThrowers<undefined>;
|
|
371
|
+
/** The user that enqueued the job, if any (background jobs are usually system-initiated). */
|
|
372
|
+
user: User | null;
|
|
373
|
+
/** Per-invocation id for correlation. */
|
|
222
374
|
requestId: string;
|
|
223
375
|
projectId: string;
|
|
224
376
|
environmentId: string;
|
|
@@ -236,7 +388,7 @@ interface WorkerConfig<TPayload = unknown> {
|
|
|
236
388
|
/** Backoff strategy between retries. Defaults to "exponential". */
|
|
237
389
|
backoff?: BackoffStrategy;
|
|
238
390
|
/** Handler function that processes the job payload. */
|
|
239
|
-
handler: (
|
|
391
|
+
handler: (payload: TPayload, meta: WorkerMeta) => Promise<void>;
|
|
240
392
|
}
|
|
241
393
|
/** Resolved worker configuration with defaults applied. */
|
|
242
394
|
interface ResolvedWorkerConfig<TPayload = unknown> {
|
|
@@ -244,7 +396,7 @@ interface ResolvedWorkerConfig<TPayload = unknown> {
|
|
|
244
396
|
retry: number;
|
|
245
397
|
timeout: number;
|
|
246
398
|
backoff: BackoffStrategy;
|
|
247
|
-
handler: (
|
|
399
|
+
handler: (payload: TPayload, meta: WorkerMeta) => Promise<void>;
|
|
248
400
|
}
|
|
249
401
|
/**
|
|
250
402
|
* Define a background worker that processes jobs from the queue.
|
|
@@ -258,7 +410,7 @@ interface ResolvedWorkerConfig<TPayload = unknown> {
|
|
|
258
410
|
* retry: 5,
|
|
259
411
|
* timeout: 60,
|
|
260
412
|
* backoff: "exponential",
|
|
261
|
-
* handler: async (
|
|
413
|
+
* handler: async (payload: { to: string; subject: string }, meta) => {
|
|
262
414
|
* await sendEmail(payload.to, payload.subject);
|
|
263
415
|
* },
|
|
264
416
|
* });
|
|
@@ -266,13 +418,11 @@ interface ResolvedWorkerConfig<TPayload = unknown> {
|
|
|
266
418
|
*/
|
|
267
419
|
declare function defineWorker<TPayload = unknown>(config: WorkerConfig<TPayload>): ResolvedWorkerConfig<TPayload>;
|
|
268
420
|
|
|
269
|
-
/**
|
|
270
|
-
|
|
271
|
-
|
|
421
|
+
/** Non-service, per-invocation data for job handlers.
|
|
422
|
+
* Services (Database, Log, …) are imported as singletons, not passed here. */
|
|
423
|
+
interface JobMeta {
|
|
424
|
+
/** Branch-scoped env vars. */
|
|
272
425
|
env: Record<string, string>;
|
|
273
|
-
log: Logger;
|
|
274
|
-
cache: CacheClient;
|
|
275
|
-
queue: QueueClient;
|
|
276
426
|
projectId: string;
|
|
277
427
|
environmentId: string;
|
|
278
428
|
}
|
|
@@ -285,14 +435,14 @@ interface JobConfig {
|
|
|
285
435
|
/** Execution timeout in seconds. Defaults to 30. */
|
|
286
436
|
timeout?: number;
|
|
287
437
|
/** Job handler function. */
|
|
288
|
-
handler: (
|
|
438
|
+
handler: (meta: JobMeta) => Promise<void>;
|
|
289
439
|
}
|
|
290
440
|
/** Resolved job configuration with defaults applied. */
|
|
291
441
|
interface ResolvedJobConfig {
|
|
292
442
|
name: string;
|
|
293
443
|
schedule: string;
|
|
294
444
|
timeout: number;
|
|
295
|
-
handler: (
|
|
445
|
+
handler: (meta: JobMeta) => Promise<void>;
|
|
296
446
|
}
|
|
297
447
|
/**
|
|
298
448
|
* Define a scheduled cron job.
|
|
@@ -305,9 +455,9 @@ interface ResolvedJobConfig {
|
|
|
305
455
|
* name: "cleanup-expired",
|
|
306
456
|
* schedule: "0 3 * * *", // every day at 3 AM
|
|
307
457
|
* timeout: 60,
|
|
308
|
-
* handler: async (
|
|
309
|
-
* await
|
|
310
|
-
*
|
|
458
|
+
* handler: async (meta) => {
|
|
459
|
+
* await Database.delete("sessions", "expired < NOW()");
|
|
460
|
+
* Log.info("Cleaned up expired sessions");
|
|
311
461
|
* },
|
|
312
462
|
* });
|
|
313
463
|
* ```
|
|
@@ -316,16 +466,15 @@ declare function defineJob(config: JobConfig): ResolvedJobConfig;
|
|
|
316
466
|
|
|
317
467
|
/** Supported webhook provider types. */
|
|
318
468
|
type WebhookProvider = "stripe" | "github" | "twilio" | "sendgrid" | "slack" | "discord" | "livekit";
|
|
319
|
-
/**
|
|
320
|
-
|
|
321
|
-
|
|
469
|
+
/** Non-service, per-invocation data for webhook handlers.
|
|
470
|
+
* Services (Database, Log, …) are imported as singletons, not passed here. */
|
|
471
|
+
interface WebhookMeta {
|
|
472
|
+
/** Branch-scoped env vars. */
|
|
322
473
|
env: Record<string, string>;
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
queue: QueueClient;
|
|
474
|
+
/** Per-invocation id for correlation. */
|
|
475
|
+
requestId: string;
|
|
326
476
|
projectId: string;
|
|
327
477
|
environmentId: string;
|
|
328
|
-
requestId: string;
|
|
329
478
|
}
|
|
330
479
|
/** Secret reference — resolves from environment variables at runtime. */
|
|
331
480
|
interface EnvSecretRef {
|
|
@@ -386,7 +535,7 @@ interface ProviderEventMap {
|
|
|
386
535
|
};
|
|
387
536
|
}
|
|
388
537
|
/** Event handler function type. */
|
|
389
|
-
type WebhookEventHandler<TEvent = Record<string, unknown>> = (
|
|
538
|
+
type WebhookEventHandler<TEvent = Record<string, unknown>> = (event: TEvent, meta: WebhookMeta) => Promise<void>;
|
|
390
539
|
/** Provider-based webhook configuration. */
|
|
391
540
|
interface ProviderWebhookConfig<P extends WebhookProvider = WebhookProvider> {
|
|
392
541
|
provider: P;
|
|
@@ -406,7 +555,7 @@ interface WebhookRequest {
|
|
|
406
555
|
interface CustomWebhookConfig {
|
|
407
556
|
path: string;
|
|
408
557
|
verify?: (req: WebhookRequest) => Promise<boolean> | boolean;
|
|
409
|
-
handler: (
|
|
558
|
+
handler: (payload: Record<string, unknown>, meta: WebhookMeta) => Promise<void>;
|
|
410
559
|
}
|
|
411
560
|
/** Resolved provider webhook (internal). */
|
|
412
561
|
interface ResolvedProviderWebhook<P extends WebhookProvider = WebhookProvider> {
|
|
@@ -420,7 +569,7 @@ interface ResolvedCustomWebhook {
|
|
|
420
569
|
type: "custom";
|
|
421
570
|
path: string;
|
|
422
571
|
verify?: (req: WebhookRequest) => Promise<boolean> | boolean;
|
|
423
|
-
handler: (
|
|
572
|
+
handler: (payload: Record<string, unknown>, meta: WebhookMeta) => Promise<void>;
|
|
424
573
|
}
|
|
425
574
|
/** Union of resolved webhook types. */
|
|
426
575
|
type ResolvedWebhookConfig = ResolvedProviderWebhook | ResolvedCustomWebhook;
|
|
@@ -433,8 +582,8 @@ type ResolvedWebhookConfig = ResolvedProviderWebhook | ResolvedCustomWebhook;
|
|
|
433
582
|
* provider: "stripe",
|
|
434
583
|
* secret: { env: "STRIPE_WEBHOOK_SECRET" },
|
|
435
584
|
* events: {
|
|
436
|
-
* "checkout.session.completed": async (
|
|
437
|
-
* await
|
|
585
|
+
* "checkout.session.completed": async (event, meta) => {
|
|
586
|
+
* await Database.insert("orders", { status: "paid" });
|
|
438
587
|
* },
|
|
439
588
|
* },
|
|
440
589
|
* });
|
|
@@ -449,21 +598,111 @@ declare function defineWebhook<P extends WebhookProvider>(config: ProviderWebhoo
|
|
|
449
598
|
* export default defineWebhook({
|
|
450
599
|
* path: "/webhooks/livekit",
|
|
451
600
|
* verify: async (req) => req.headers["x-api-key"] === "secret",
|
|
452
|
-
* handler: async (
|
|
453
|
-
*
|
|
601
|
+
* handler: async (payload, meta) => {
|
|
602
|
+
* Log.info("Received webhook", payload);
|
|
454
603
|
* },
|
|
455
604
|
* });
|
|
456
605
|
* ```
|
|
457
606
|
*/
|
|
458
607
|
declare function defineWebhook(config: CustomWebhookConfig): ResolvedCustomWebhook;
|
|
459
608
|
|
|
460
|
-
/**
|
|
461
|
-
|
|
462
|
-
|
|
609
|
+
/**
|
|
610
|
+
* resource.ts — external connections as lifecycle-managed classes.
|
|
611
|
+
*
|
|
612
|
+
* A `Resource` subclass models one external connection (a pooled datastore, a
|
|
613
|
+
* stateless API client, or a per-user factory). The framework discovers each
|
|
614
|
+
* instance, calls `init(env)` ONCE at boot with the declared secret subset, and
|
|
615
|
+
* `shutdown()` (reverse order) on SIGTERM. On top of that lifecycle the author
|
|
616
|
+
* exposes their own clean facade methods.
|
|
617
|
+
*
|
|
618
|
+
* // resources/neo4j.ts
|
|
619
|
+
* import { Resource } from "@palbase/backend";
|
|
620
|
+
* import neo4j, { type Driver, type Session } from "neo4j-driver";
|
|
621
|
+
*
|
|
622
|
+
* export class Neo4jResource extends Resource {
|
|
623
|
+
* static secrets = ["NEO4J_URL", "NEO4J_USER", "NEO4J_PASSWORD"] as const;
|
|
624
|
+
* private driver!: Driver;
|
|
625
|
+
* async init(env: { NEO4J_URL: string; NEO4J_USER: string; NEO4J_PASSWORD: string }) {
|
|
626
|
+
* this.driver = neo4j.driver(env.NEO4J_URL, neo4j.auth.basic(env.NEO4J_USER, env.NEO4J_PASSWORD));
|
|
627
|
+
* }
|
|
628
|
+
* async shutdown() { await this.driver.close(); }
|
|
629
|
+
* session(): Session { return this.driver.session(); }
|
|
630
|
+
* }
|
|
631
|
+
* export const neo4j = new Neo4jResource(); // framework finds + manages it
|
|
632
|
+
*
|
|
633
|
+
* # Boot scope (NOT request-ALS)
|
|
634
|
+
*
|
|
635
|
+
* Resources are instantiated once at process boot. This is deliberately NOT the
|
|
636
|
+
* per-request {@link AsyncLocalStorage} scope used for `Database`/`Cache`/… — a
|
|
637
|
+
* connection pool must outlive a single request. The runtime discovers
|
|
638
|
+
* resources from the project's `resources/` directory, registers each via
|
|
639
|
+
* {@link __registerResource}, then calls {@link __runResourceBoot} before
|
|
640
|
+
* serving and {@link __shutdownResources} on SIGTERM. Discovery is a runtime
|
|
641
|
+
* concern; the SDK provides the base class + registry + boot/shutdown hooks.
|
|
642
|
+
*/
|
|
643
|
+
/** Map a declared `secrets` tuple to the `init(env)` argument type — a record
|
|
644
|
+
* over the secret names, each `string`. An empty tuple maps to an empty
|
|
645
|
+
* record. */
|
|
646
|
+
type ResourceEnv<Secrets extends readonly string[]> = {
|
|
647
|
+
[K in Secrets[number]]: string;
|
|
648
|
+
};
|
|
649
|
+
/**
|
|
650
|
+
* Base class for external connections.
|
|
651
|
+
*
|
|
652
|
+
* - `static secrets` — OPTIONAL readonly tuple of env-var names this resource
|
|
653
|
+
* needs. Drives (a) the `env` type passed to `init` and (b) the
|
|
654
|
+
* missing-secret check at boot (deploy fails naming the absent secret).
|
|
655
|
+
* - `init(env)` — called ONCE at boot with the declared secret subset. May be
|
|
656
|
+
* sync (`void`) or async (`Promise<void>`).
|
|
657
|
+
* - `shutdown()` — OPTIONAL drain hook, called on SIGTERM in reverse boot
|
|
658
|
+
* order.
|
|
659
|
+
*
|
|
660
|
+
* @example
|
|
661
|
+
* import { Resource } from "@palbase/backend";
|
|
662
|
+
* import { Client } from "@googlemaps/google-maps-services-js";
|
|
663
|
+
*
|
|
664
|
+
* export class GoogleResource extends Resource {
|
|
665
|
+
* static secrets = ["GOOGLE_MAPS_KEY"] as const;
|
|
666
|
+
* private client = new Client();
|
|
667
|
+
* private key = "";
|
|
668
|
+
* init(env: { GOOGLE_MAPS_KEY: string }) { this.key = env.GOOGLE_MAPS_KEY; }
|
|
669
|
+
* }
|
|
670
|
+
* export const google = new GoogleResource();
|
|
671
|
+
*/
|
|
672
|
+
declare abstract class Resource {
|
|
673
|
+
/** The env-var names this resource needs. Optional; omit for none. */
|
|
674
|
+
static secrets?: readonly string[];
|
|
675
|
+
/** Set up the connection from the declared secrets. Called once at boot. */
|
|
676
|
+
abstract init(env: Record<string, string>): void | Promise<void>;
|
|
677
|
+
/** Drain/close the connection on SIGTERM. Optional. */
|
|
678
|
+
shutdown?(): void | Promise<void>;
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Register a resource instance with the boot registry. The runtime calls this
|
|
682
|
+
* for each instance discovered under the project's `resources/` directory.
|
|
683
|
+
* NOT part of the public author-facing API (prefixed `__`).
|
|
684
|
+
*/
|
|
685
|
+
declare function __registerResource(resource: Resource): void;
|
|
686
|
+
/**
|
|
687
|
+
* Boot every registered resource that has not yet been booted: resolve its
|
|
688
|
+
* declared secret subset from `envMap`, then await its `init(env)`. Idempotent
|
|
689
|
+
* — an already-booted resource is skipped. Throws (failing deploy/boot) when a
|
|
690
|
+
* declared secret is absent, naming the missing secret. NOT part of the public
|
|
691
|
+
* author-facing API.
|
|
692
|
+
*/
|
|
693
|
+
declare function __runResourceBoot(envMap: Record<string, string>): Promise<void>;
|
|
694
|
+
/**
|
|
695
|
+
* Shut down every booted resource in REVERSE registration order, awaiting each
|
|
696
|
+
* `shutdown()` (a no-op when undefined). Clears the registry afterwards so a
|
|
697
|
+
* second call is a no-op. NOT part of the public author-facing API.
|
|
698
|
+
*/
|
|
699
|
+
declare function __shutdownResources(): Promise<void>;
|
|
700
|
+
|
|
701
|
+
/** Non-service, per-invocation data for hook handlers.
|
|
702
|
+
* Services (Database, Log, …) are imported as singletons, not passed here. */
|
|
703
|
+
interface HookMeta {
|
|
704
|
+
/** Branch-scoped env vars. */
|
|
463
705
|
env: Record<string, string>;
|
|
464
|
-
log: Logger;
|
|
465
|
-
cache: CacheClient;
|
|
466
|
-
queue: QueueClient;
|
|
467
706
|
projectId: string;
|
|
468
707
|
environmentId: string;
|
|
469
708
|
}
|
|
@@ -471,7 +710,8 @@ interface HookContext extends PalbaseModuleClients {
|
|
|
471
710
|
interface UserCreatedEvent {
|
|
472
711
|
user: {
|
|
473
712
|
id: string;
|
|
474
|
-
email
|
|
713
|
+
/** User's email, if they signed up with one (absent for phone-only users). */
|
|
714
|
+
email?: string;
|
|
475
715
|
role: string;
|
|
476
716
|
metadata: Record<string, unknown>;
|
|
477
717
|
createdAt: string;
|
|
@@ -481,7 +721,8 @@ interface UserCreatedEvent {
|
|
|
481
721
|
interface SignInEvent {
|
|
482
722
|
user: {
|
|
483
723
|
id: string;
|
|
484
|
-
email
|
|
724
|
+
/** User's email, if they have one (absent for phone-only users). */
|
|
725
|
+
email?: string;
|
|
485
726
|
role: string;
|
|
486
727
|
};
|
|
487
728
|
provider: string;
|
|
@@ -491,11 +732,14 @@ interface SignInEvent {
|
|
|
491
732
|
interface SignOutEvent {
|
|
492
733
|
user: {
|
|
493
734
|
id: string;
|
|
494
|
-
email
|
|
735
|
+
/** User's email, if they have one (absent for phone-only users). */
|
|
736
|
+
email?: string;
|
|
495
737
|
};
|
|
496
738
|
timestamp: string;
|
|
497
739
|
}
|
|
498
|
-
/** Payload for auth.onPasswordReset hook.
|
|
740
|
+
/** Payload for auth.onPasswordReset hook.
|
|
741
|
+
* Password reset is inherently email-based, so `email` is always present here
|
|
742
|
+
* (a phone-only passwordless user cannot trigger this event). */
|
|
499
743
|
interface PasswordResetEvent {
|
|
500
744
|
user: {
|
|
501
745
|
id: string;
|
|
@@ -548,7 +792,7 @@ interface DocumentDeletedEvent {
|
|
|
548
792
|
data: Record<string, unknown>;
|
|
549
793
|
};
|
|
550
794
|
}
|
|
551
|
-
type HookHandler<TEvent> = (
|
|
795
|
+
type HookHandler<TEvent> = (event: TEvent, meta: HookMeta) => Promise<void>;
|
|
552
796
|
/** Resolved hook configuration (internal). */
|
|
553
797
|
interface ResolvedHook<TEvent = unknown> {
|
|
554
798
|
module: string;
|
|
@@ -571,4 +815,4 @@ declare const documents: {
|
|
|
571
815
|
onDocumentDeleted(handler: HookHandler<DocumentDeletedEvent>): ResolvedHook<DocumentDeletedEvent>;
|
|
572
816
|
};
|
|
573
817
|
|
|
574
|
-
export { type BackoffStrategy, Cache, CacheClient, type CustomWebhookConfig, DBClient, Database, type DocumentCreatedEvent, type DocumentDeletedEvent, type DocumentUpdatedEvent, Documents, type EnvSecretRef,
|
|
818
|
+
export { type BackoffStrategy, Cache, CacheClient, type ControllerDef, type CustomWebhookConfig, DBClient, Database, type DocumentCreatedEvent, type DocumentDeletedEvent, type DocumentUpdatedEvent, Documents, type EnvSecretRef, EnvTypedDatabase, ErrorMap, type FileDeletedEvent, type FileUploadedEvent, Flags, type HandlerConfig, type HandlerDef, type HookHandler, type HookMeta, type HttpMethodUpper, type JobConfig, type JobMeta, Log, Logger, Middleware, Notifications, PBRequest, PalbaseDocsClient, PalbaseFlagsClient, PalbaseNotificationsClient, PalbaseStorageClient, type PasswordResetEvent, type ProviderEventMap, type ProviderWebhookConfig, Queue, QueueClient, RateLimitConfig, type ResolvedCustomWebhook, type ResolvedHook, type ResolvedJobConfig, type ResolvedProviderWebhook, type ResolvedWebhookConfig, type ResolvedWorkerConfig, Resource, type ResourceEnv, type RouteDef, type RouteMap, type RuntimeServices, SchemaDef, type SignInEvent, type SignOutEvent, Storage, User, type UserCreatedEvent, type WebhookEventHandler, type WebhookMeta, type WebhookProvider, type WebhookRequest, type WorkerConfig, type WorkerMeta, __getRuntime, __registerResource, __requestALS, __runResourceBoot, __runWithRuntime, __setRuntime, __shutdownResources, auth, defineController, defineHandler, defineJob, defineWebhook, defineWorker, documents, makeEnvDts, route, storage };
|