@palbase/backend 17.4.0 → 18.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/palbase-backend.cjs +1848 -0
- package/dist/bin/palbase-backend.cjs.map +1 -0
- package/dist/bin/palbase-backend.d.cts +1 -0
- package/dist/bin/palbase-backend.d.ts +1 -0
- package/dist/bin/palbase-backend.js +168 -0
- package/dist/bin/palbase-backend.js.map +1 -0
- package/dist/chunk-7D4SUZUM.js +38 -0
- package/dist/chunk-7D4SUZUM.js.map +1 -0
- package/dist/chunk-N32VDWKH.js +172 -0
- package/dist/chunk-N32VDWKH.js.map +1 -0
- package/dist/chunk-POYAFBLF.js +189 -0
- package/dist/chunk-POYAFBLF.js.map +1 -0
- package/dist/chunk-QMVK4X3V.js +200 -0
- package/dist/chunk-QMVK4X3V.js.map +1 -0
- package/dist/chunk-SSGAMC26.js +342 -0
- package/dist/chunk-SSGAMC26.js.map +1 -0
- package/dist/chunk-VYH4U7ZQ.js +1138 -0
- package/dist/chunk-VYH4U7ZQ.js.map +1 -0
- package/dist/{chunk-AAN642N5.js → chunk-W5ODXPY3.js} +2 -336
- package/dist/chunk-W5ODXPY3.js.map +1 -0
- package/dist/chunk-YL4C5NRY.js +90 -0
- package/dist/chunk-YL4C5NRY.js.map +1 -0
- package/dist/db/env.cjs.map +1 -1
- package/dist/db/env.d.cts +21 -1
- package/dist/db/env.d.ts +21 -1
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +2 -1
- package/dist/db/index.d.ts +2 -1
- package/dist/db/index.js +9 -6
- package/dist/{index-VLrU7rSW.d.ts → endpoint-B0LpZixz.d.cts} +124 -685
- package/dist/{index-BA_oFAz9.d.cts → endpoint-B0LpZixz.d.ts} +124 -685
- package/dist/engine/index.cjs +1797 -0
- package/dist/engine/index.cjs.map +1 -0
- package/dist/engine/index.d.cts +7 -0
- package/dist/engine/index.d.ts +7 -0
- package/dist/engine/index.js +43 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/index-B46CGNvx.d.cts +839 -0
- package/dist/index-BGSCWlUa.d.cts +674 -0
- package/dist/index-DZDUMth5.d.ts +839 -0
- package/dist/index-g-EzitI-.d.ts +674 -0
- package/dist/index.cjs +1031 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +290 -532
- package/dist/index.d.ts +290 -532
- package/dist/index.js +999 -509
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.cjs +6464 -0
- package/dist/openapi/index.cjs.map +1 -0
- package/dist/openapi/index.d.cts +170 -0
- package/dist/openapi/index.d.ts +170 -0
- package/dist/openapi/index.js +6248 -0
- package/dist/openapi/index.js.map +1 -0
- package/dist/registry-3BLYv4si.d.ts +338 -0
- package/dist/registry-Cw0YEYCg.d.cts +338 -0
- package/dist/test/index.js +2 -0
- package/dist/test/index.js.map +1 -1
- package/docs/database.md +16 -3
- package/docs/llms-full.txt +16 -3
- package/package.json +43 -13
- package/stager/package.json +4 -0
- package/stager/return_types.js +338 -0
- package/stager/stage.js +78 -0
- package/stager/throw_analysis.js +726 -0
- package/template/AGENTS.md +261 -0
- package/template/config/secrets.ts +24 -0
- package/template/controllers/health.controller.ts +30 -0
- package/template/db/schema.ts +35 -0
- package/template/package.json +18 -0
- package/template/tsconfig.json +30 -0
- package/LICENSE +0 -21
- package/dist/chunk-AAN642N5.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,245 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
3
|
-
import {
|
|
1
|
+
import { g as PalbaseResult, A as AuthSpec, H as HttpError } from './endpoint-B0LpZixz.js';
|
|
2
|
+
export { h as AuthConfig, B as BadRequest, C as CacheClient, i as ClientInfo, j as Conflict, D as DBClient, k as DBOps, E as ErrorDef, l as ErrorMap, m as ErrorThrowers, F as FileContext, n as Forbidden, o as HttpMethod, L as Logger, M as Materialized, p as Middleware, q as MiddlewareContext, r as MiddlewareHandler, N as NotFound, s as PBRequest, t as PalError, u as PalbaseAnalyticsClient, v as PalbaseAnalyticsManagementNamespace, w as PalbaseAnalyticsProperties, x as PalbaseAnalyticsQueryNamespace, y as PalbaseAttestAndroidParams, z as PalbaseAttestAndroidResult, G as PalbaseAttestiOSParams, I as PalbaseAttestiOSResult, J as PalbaseAuthClient, K as PalbaseBatchOverrideOperation, O as PalbaseBatchSetOverridesResult, Q as PalbaseBindDeviceParams, e as PalbaseBucketClient, U as PalbaseClearAllOverridesResult, V as PalbaseClearOverrideResult, W as PalbaseCohortQueryInput, X as PalbaseCohortResult, Y as PalbaseCollectionRef, Z as PalbaseCountQueryInput, _ as PalbaseCountResult, $ as PalbaseCreateLinkParams, a0 as PalbaseDeviceInfo, a1 as PalbaseDeviceTokenView, P as PalbaseDocsClient, a2 as PalbaseDocumentRef, a3 as PalbaseDocumentSnapshot, a4 as PalbaseEmailClient, a5 as PalbaseEmailSendParams, a6 as PalbaseEmailSendResponse, a7 as PalbaseEventNamesResult, a8 as PalbaseEventsQueryInput, a9 as PalbaseEventsResult, aa as PalbaseFileObject, ab as PalbaseFlag, ac as PalbaseFlagContext, ad as PalbaseFlagSource, ae as PalbaseFlagValue, af as PalbaseFlagVariant, a as PalbaseFlagsClient, ag as PalbaseFlagsServiceClient, ah as PalbaseFunctionsClient, ai as PalbaseFunnelQueryInput, aj as PalbaseFunnelResult, ak as PalbaseIdentifyTraits, al as PalbaseInboxClient, am as PalbaseInboxListOptions, an as PalbaseInboxListResult, ao as PalbaseInboxMessage, ap as PalbaseInboxSendParams, aq as PalbaseInboxSendResponse, ar as PalbaseInitialLink, as as PalbaseInvokeOptions, at as PalbaseLink, au as PalbaseLinkAnalytics, av as PalbaseLinkDetails, aw as PalbaseLinksClient, ax as PalbaseListLinksOptions, ay as PalbaseListLinksResult, az as PalbaseListOptions, aA as PalbaseMatchParams, aB as PalbaseMultiChannelResponse, b as PalbaseNotificationsClient, aC as PalbaseOverviewResult, aD as PalbasePreferences, aE as PalbasePreferencesClient, aF as PalbasePushClient, aG as PalbasePushSendParams, aH as PalbasePushSendResponse, aI as PalbaseQrCodeOptions, aJ as PalbaseQuerySnapshot, c as PalbaseRealtimeClient, aK as PalbaseRegisterDeviceParams, aL as PalbaseRetentionQueryInput, aM as PalbaseRetentionResult, aN as PalbaseSession, aO as PalbaseSetOverrideResult, aP as PalbaseSetOverridesResult, aQ as PalbaseSignedUrlResponse, aR as PalbaseSmsClient, aS as PalbaseSmsSendParams, aT as PalbaseSmsSendResponse, d as PalbaseStorageClient, aU as PalbaseTransformOptions, aV as PalbaseUpdateLinkParams, aW as PalbaseUploadOptions, aX as PalbaseUser, aY as PalbaseUserDetailResult, aZ as PalbaseUsersQueryInput, a_ as PalbaseUsersResult, a$ as PalbaseVerifyRequestSignatureParams, b0 as PalbaseWhereOperator, R as RateLimitConfig, b1 as Ref, S as SecretsService, b2 as TooManyRequests, b3 as TxColumnExpr, b4 as TxInsertShape, b5 as TxInsertValue, b6 as TxNow, T as TxPlanBody, b7 as TxPlanError, b8 as TxPlanHandle, b9 as TxPlanOpResult, ba as TxPlanRejection, f as TxPlanResponse, bb as TxRefError, bc as TxRow, bd as TxRows, be as TxSelectOptions, bf as TxSetShape, bg as TxSetValue, bh as TxTable, bi as TxWhere, bj as TxWireExpr, bk as TxWireGuard, bl as TxWireOp, bm as TxWireRef, bn as TxWireValue, bo as Unauthorized, bp as UserT, bq as VerifiedDevice, br as dec, bs as defineMiddleware, bt as inc, bu as now } from './endpoint-B0LpZixz.js';
|
|
3
|
+
import { M as ModuleClients } from './index-DZDUMth5.js';
|
|
4
|
+
export { C as Cache, D as Database, a as Documents, F as Flags, L as LimitState, b as Log, N as Notifications, P as Purchases, c as PurchasesService, R as Realtime, d as RequestStore, e as RuntimeServices, S as Secrets, f as SpendOptions, g as Storage, h as StoreEnv, _ as __getRuntime, i as __requestALS, j as __runWithRuntime, k as __setRuntime } from './index-DZDUMth5.js';
|
|
4
5
|
import { EntitlementKey, LimitKey } from './purchases/keys.js';
|
|
6
|
+
import { S as SchemaDef } from './index-g-EzitI-.js';
|
|
7
|
+
export { C as ColumnBuilder, a as ColumnDef, b as ColumnMap, c as ColumnType, d as EXTENSION_DEPENDENCIES, e as EnvServiceDatabase, f as EnvTables, E as EnvTypedDatabase, g as EnvTypedTable, I as InsertShape, O as OnDeleteAction, P as PALBASE_EXTENSIONS, h as PalbaseExtension, i as PolicyBuilder, j as PolicyCommand, k as PolicyDef, l as PolicyMode, R as RawConstraintDef, m as RowShape, n as SchemaInput, T as TableDef, o as TableInput, p as TxPlan, q as TxTables, r as TypedDB, s as TypedTable, t as TypedTx, u as bigint, v as boolean, w as defineSchema, x as enumType, y as integer, z as isPalbaseExtension, A as jsonb, B as makeTypedDB, D as numeric, F as policy, G as raw, H as text, J as timestamp, K as uuid } from './index-g-EzitI-.js';
|
|
5
8
|
import { TableTypes, Tables } from './db/env.js';
|
|
9
|
+
import { a as RouteOptions } from './registry-3BLYv4si.js';
|
|
10
|
+
export { B as BucketDef, b as BucketOptions, H as HttpMethodUpper, I as ImageVariant, c as ParamKind, P as ParamMeta, R as RouteMeta, S as STORAGE_CONFIG_KIND, d as StorageConfig, e as StorageInput, T as ThrowDescriptor, f as Upload, U as UploadConfig, g as UploadedObject, h as bucket, i as defineStorage, j as getRoutes, p as parseFileSizeLimit, r as recordThrows, v as validateUploadAgainstStorage } from './registry-3BLYv4si.js';
|
|
6
11
|
import { ZodTypeAny, z } from 'zod';
|
|
7
12
|
export { z } from 'zod';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* The purchases surface the decorators need, declared as a NARROW STRUCTURAL
|
|
11
|
-
* interface rather than an import of `@palstore/purchases`.
|
|
12
|
-
*
|
|
13
|
-
* `PurchasesClient` (palstore's `sk_` backend SDK) satisfies this shape as-is,
|
|
14
|
-
* so the runtime injects the real client with no adapter — but `@palbase/backend`
|
|
15
|
-
* itself gains no dependency on it. That matters: this package is published
|
|
16
|
-
* public and baked into the br-pod image from a tarball, so a dependency on an
|
|
17
|
-
* unpublished sibling would break `npm install palbase-backend.tgz`. It is also
|
|
18
|
-
* the pattern `withSpend` already uses for the same reason (`SpendCapableClient`
|
|
19
|
-
* in palstore's own spend.ts: "a narrow structural interface … so this file has
|
|
20
|
-
* no dependency on client.ts").
|
|
21
|
-
*
|
|
22
|
-
* The methods here are a SUBSET of `PurchasesClient` — only what the two
|
|
23
|
-
* decorators call. Grants, refunds, credits and customer-info reads stay off
|
|
24
|
-
* this interface: a tenant that wants them imports the palstore SDK directly.
|
|
25
|
-
*/
|
|
26
|
-
/** Store environment a subject is fixed to. Mirrors `StoreEnv` in `@palstore/purchases`. */
|
|
27
|
-
type StoreEnv = "production" | "sandbox";
|
|
28
|
-
/** Quota/credit state carried by a 429. Mirrors `LimitState` in `@palstore/purchases`
|
|
29
|
-
* (SPEC-purchases-v1 §11) — re-declared, not imported, for the reason above. */
|
|
30
|
-
interface LimitState {
|
|
31
|
-
key: string;
|
|
32
|
-
scope: string;
|
|
33
|
-
window: string;
|
|
34
|
-
used: number;
|
|
35
|
-
reserved: number;
|
|
36
|
-
max: number;
|
|
37
|
-
remaining: number;
|
|
38
|
-
resetAt: string;
|
|
39
|
-
}
|
|
40
|
-
/** Options for one spend. `idempotencyKey` is required by the server (§9). */
|
|
41
|
-
interface SpendOptions {
|
|
42
|
-
/** Defaults to 1. */
|
|
43
|
-
count?: number;
|
|
44
|
-
idempotencyKey: string;
|
|
45
|
-
}
|
|
46
|
-
interface PurchasesService {
|
|
47
|
-
/** Map a tenant-side user reference to its palstore subject, creating one on
|
|
48
|
-
* first sight. Server-authoritative — the caller never names a subject. */
|
|
49
|
-
resolveSubject(input: {
|
|
50
|
-
userRef: string;
|
|
51
|
-
storeEnv: StoreEnv;
|
|
52
|
-
}): Promise<{
|
|
53
|
-
subjectId: string;
|
|
54
|
-
}>;
|
|
55
|
-
/** Resolve silently when `entitlementKey` is active for `subjectId`; throw
|
|
56
|
-
* `EntitlementRequiredError` otherwise. Consumes nothing. */
|
|
57
|
-
require(subjectId: string, entitlementKey: string): Promise<void>;
|
|
58
|
-
/** Reserve → run `handler` → commit on success, cancel on throw, always
|
|
59
|
-
* rethrowing the handler's own error. The whole reason the decorators are a
|
|
60
|
-
* thin layer: this lifecycle is already written and tested in palstore's SDK. */
|
|
61
|
-
withSpend<T>(subjectId: string, key: string, opts: SpendOptions, handler: () => Promise<T>): Promise<T>;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* runtime.ts — request-scoped service singletons.
|
|
66
|
-
*
|
|
67
|
-
* The backend SDK no longer threads a `ctx` god-object through every handler.
|
|
68
|
-
* Instead, controller methods import PascalCase service singletons directly:
|
|
69
|
-
*
|
|
70
|
-
* import { Controller, Post, Body, Database } from "@palbase/backend";
|
|
71
|
-
*
|
|
72
|
-
* \@Controller("/todos")
|
|
73
|
-
* export default class TodosController {
|
|
74
|
-
* \@Post("") create(\@Body(CreateTodoBody) body: CreateTodoBody): unknown {
|
|
75
|
-
* return Database.insert("todos", { title: body.title });
|
|
76
|
-
* }
|
|
77
|
-
* }
|
|
78
|
-
*
|
|
79
|
-
* The singletons are thin Proxies. Every property access forwards to the live
|
|
80
|
-
* client for the CURRENT request scope, resolved through {@link __getRuntime}.
|
|
81
|
-
*
|
|
82
|
-
* # Request-scope resolution (persistent app-server)
|
|
83
|
-
*
|
|
84
|
-
* The runtime is a long-running Node process that serves many concurrent
|
|
85
|
-
* requests on one event loop (NOT a fresh subprocess per request). A single
|
|
86
|
-
* module-global slot would let one in-flight request's services bleed into
|
|
87
|
-
* another's. So the services are carried in an {@link AsyncLocalStorage} store
|
|
88
|
-
* ({@link __requestALS}) that the runtime sets per request with
|
|
89
|
-
* {@link __runWithRuntime}; every async continuation of that request reads its
|
|
90
|
-
* own store. `__getRuntime` reads the ALS store first; the module-global slot
|
|
91
|
-
* (set by {@link __setRuntime}) is only a fallback for callers that run OUTSIDE
|
|
92
|
-
* an ALS scope (dev-server, unit tests, the legacy single-shot path). Because
|
|
93
|
-
* each `br-<ref>` pod is single-tenant, there is no cross-tenant leakage; the
|
|
94
|
-
* ALS store is what prevents cross-REQUEST leakage within the shared process.
|
|
95
|
-
*
|
|
96
|
-
* The seam that makes `import { Database } from "@palbase/backend"` resolve to
|
|
97
|
-
* the runtime-injected client: `@palbase/backend` is marked esbuild-EXTERNAL
|
|
98
|
-
* when the tenant bundle is built, and the package is installed globally in the
|
|
99
|
-
* pod (NODE_PATH=/usr/local/lib/node_modules). So worker.js's
|
|
100
|
-
* `require('@palbase/backend')` and the bundle's `import` resolve to ONE shared
|
|
101
|
-
* module instance — the ALS store and `__setRuntime` slot on that instance are
|
|
102
|
-
* visible to the singletons the bundle imported.
|
|
103
|
-
*/
|
|
104
|
-
|
|
105
|
-
/** The set of live clients the runtime injects per request scope.
|
|
106
|
-
*
|
|
107
|
-
* Realtime is BROADCAST-ONLY here (a stateless handler can push an event but
|
|
108
|
-
* cannot hold a subscription socket — `subscribe()` lives on the client SDK).
|
|
109
|
-
*
|
|
110
|
-
* EXCLUDED on purpose: Functions, Links, Analytics, Auth. They are not
|
|
111
|
-
* exposed as backend handler singletons (auth lives on the client SDK; the rest
|
|
112
|
-
* are out of scope for backend endpoints). */
|
|
113
|
-
interface RuntimeServices {
|
|
114
|
-
Database: DBClient;
|
|
115
|
-
Documents: PalbaseDocsClient;
|
|
116
|
-
Storage: PalbaseStorageClient;
|
|
117
|
-
Cache: CacheClient;
|
|
118
|
-
Log: Logger;
|
|
119
|
-
Notifications: PalbaseNotificationsClient;
|
|
120
|
-
Flags: PalbaseFlagsClient;
|
|
121
|
-
Realtime: PalbaseRealtimeClient;
|
|
122
|
-
Purchases: PurchasesService;
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* The per-request ALS box.
|
|
126
|
-
*
|
|
127
|
-
* `runtime` is the service bundle. `userId` is the request's authenticated user
|
|
128
|
-
* id, written by the runtime immediately after it assembles the request object
|
|
129
|
-
* (worker.js: `requestALS.getStore().userId = pbReq.user?.id || null`) and
|
|
130
|
-
* `null` on an anonymous request. It was already being written there for the
|
|
131
|
-
* Flags client's auto-bind; declaring it here makes the existing contract typed
|
|
132
|
-
* instead of implicit, which is what lets `currentSubjectId()` read a
|
|
133
|
-
* server-owned identity rather than trusting anything the caller sent.
|
|
134
|
-
*/
|
|
135
|
-
interface RequestStore {
|
|
136
|
-
runtime: RuntimeServices;
|
|
137
|
-
userId?: string | null;
|
|
138
|
-
/** This request's id (`req_…`), written by the runtime. Used as the spend
|
|
139
|
-
* idempotency scope when the caller supplied no `Idempotency-Key`. */
|
|
140
|
-
requestId?: string;
|
|
141
|
-
/** The caller's `Idempotency-Key` header, or `null` when absent. What makes a
|
|
142
|
-
* client's retry replay its first result instead of charging twice. */
|
|
143
|
-
idempotencyKey?: string | null;
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Per-request store. The persistent runtime runs each request inside
|
|
147
|
-
* {@link __runWithRuntime}, so every async continuation of that request reads
|
|
148
|
-
* its OWN `runtime` (and any other request-scoped fields the runtime adds).
|
|
149
|
-
*
|
|
150
|
-
* Exported with a `__` prefix so the runtime (worker.js) shares the SAME ALS
|
|
151
|
-
* instance across the one module instance — two ALS instances would silently
|
|
152
|
-
* not see each other's stores. NOT part of the public author-facing API.
|
|
153
|
-
*/
|
|
154
|
-
declare const __requestALS: AsyncLocalStorage<RequestStore>;
|
|
155
|
-
/** Install the live clients in the process-global fallback slot.
|
|
156
|
-
*
|
|
157
|
-
* Persistent-server requests should use {@link __runWithRuntime} instead; this
|
|
158
|
-
* remains for dev-server / tests / the legacy single-shot path that run without
|
|
159
|
-
* an ALS scope. NOT part of the public author-facing API. */
|
|
160
|
-
declare function __setRuntime(services: RuntimeServices): void;
|
|
161
|
-
/** Run `fn` with `services` bound as the request-scoped runtime.
|
|
162
|
-
*
|
|
163
|
-
* The persistent worker calls this once per request so concurrent requests
|
|
164
|
-
* never share a services slot. NOT part of the public author-facing API. */
|
|
165
|
-
declare function __runWithRuntime<T>(services: RuntimeServices, fn: () => T): T;
|
|
166
|
-
/** Read the live clients, throwing if accessed outside a request scope.
|
|
167
|
-
*
|
|
168
|
-
* Resolves the ALS store first (persistent server, per-request), then the
|
|
169
|
-
* process-global fallback (dev-server / tests). NOT part of the public
|
|
170
|
-
* author-facing API — used by the runtime and the singleton Proxies. */
|
|
171
|
-
declare function __getRuntime(): RuntimeServices;
|
|
172
|
-
/**
|
|
173
|
-
* The project's own Postgres (pgx, schema `env_<envId>`).
|
|
174
|
-
*
|
|
175
|
-
* Typed by default: `Database.tables.<name>.insert({...})` is typed against
|
|
176
|
-
* the project's generated `palbase-env.d.ts` with NO import and NO generic.
|
|
177
|
-
* The raw string ops (`query`/`insert`/`update`/`delete`/`findById`/`findMany`)
|
|
178
|
-
* are also available for dynamic table names and read-only SQL.
|
|
179
|
-
*
|
|
180
|
-
* RLS is enforced by default (the runtime runs each op as `authenticated` with
|
|
181
|
-
* the verified user's claims). To bypass RLS, call `Database.asService()` —
|
|
182
|
-
* explicit and greppable — which runs as the `service_role` (BYPASSRLS).
|
|
183
|
-
*
|
|
184
|
-
* @example
|
|
185
|
-
* import { Database } from "@palbase/backend";
|
|
186
|
-
*
|
|
187
|
-
* const todo = await Database.tables.todos.insert({ title: req.input.title });
|
|
188
|
-
* todo.id; // string ✓
|
|
189
|
-
* const rows = await Database.query("SELECT id FROM todos WHERE done = $1", [false]);
|
|
190
|
-
* const all = await Database.asService().tables.todos.findMany({}); // RLS bypass
|
|
191
|
-
*/
|
|
192
|
-
declare const Database: EnvTypedDatabase;
|
|
193
|
-
/** Firestore-like document client (PalDocs). */
|
|
194
|
-
declare const Documents: PalbaseDocsClient;
|
|
195
|
-
/** Object storage client (buckets, signed URLs). */
|
|
196
|
-
declare const Storage: PalbaseStorageClient;
|
|
197
|
-
/** JSON-typed cache (get/set/incr/getOrSet). */
|
|
198
|
-
declare const Cache: CacheClient;
|
|
199
|
-
/** Structured logger. */
|
|
200
|
-
declare const Log: Logger;
|
|
201
|
-
/** Push / email / SMS / in-app notifications. */
|
|
202
|
-
declare const Notifications: PalbaseNotificationsClient;
|
|
203
|
-
/**
|
|
204
|
-
* Palstore purchases (entitlements + quota/credit spend).
|
|
205
|
-
*
|
|
206
|
-
* Reached by handlers through the `@RequireEntitlement` / `@Spend` decorators
|
|
207
|
-
* rather than called directly in the common case; exposed as a singleton for
|
|
208
|
-
* the cases the decorators deliberately do not cover (a dynamic spend count,
|
|
209
|
-
* which must run BEFORE the billable side-effect).
|
|
210
|
-
*/
|
|
211
|
-
declare const Purchases: PurchasesService;
|
|
212
|
-
/**
|
|
213
|
-
* Feature flags.
|
|
214
|
-
*
|
|
215
|
-
* Mirrors the `Database` / `Database.asService()` model. The default surface is
|
|
216
|
-
* RLS-equivalent for flags: reads resolve against the CURRENT request user and
|
|
217
|
-
* `Flags.setOverride(key, value)` writes an override for that same signed-in
|
|
218
|
-
* user (no userId argument, no admin power). Cross-user admin writes
|
|
219
|
-
* (`setOverrideForUser`, …) live behind `Flags.asService()` — explicit and
|
|
220
|
-
* greppable, just like `Database.asService()`.
|
|
221
|
-
*
|
|
222
|
-
* @example
|
|
223
|
-
* import { Flags } from "@palbase/backend";
|
|
224
|
-
*
|
|
225
|
-
* if (await Flags.isEnabled("new_checkout")) { ... } // current user
|
|
226
|
-
* await Flags.setOverride("new_checkout", true); // current user
|
|
227
|
-
* await Flags.asService().setOverrideForUser("u_9", "x", true); // cross-user
|
|
228
|
-
*/
|
|
229
|
-
declare const Flags: PalbaseFlagsClient;
|
|
230
|
-
/**
|
|
231
|
-
* The Realtime broadcast singleton for the current request scope. Backend-side
|
|
232
|
-
* Realtime is BROADCAST-ONLY (a stateless handler can push but not subscribe —
|
|
233
|
-
* `subscribe()` lives on the client SDK's `pb.realtime`). Fire-and-forget:
|
|
234
|
-
* `broadcast` resolves once accepted (or with an `error`), never blocking the
|
|
235
|
-
* handler on subscribers.
|
|
236
|
-
*
|
|
237
|
-
* @example
|
|
238
|
-
* import { Realtime } from "@palbase/backend";
|
|
239
|
-
*
|
|
240
|
-
* await Realtime.broadcast("room:42", "message", { text, from: user.id });
|
|
241
|
-
*/
|
|
242
|
-
declare const Realtime: PalbaseRealtimeClient;
|
|
13
|
+
import 'node:async_hooks';
|
|
243
14
|
|
|
244
15
|
/**
|
|
245
16
|
* `@RequireEntitlement` and `@Spend` — access and metering at the decorator
|
|
@@ -260,7 +31,7 @@ declare const Realtime: PalbaseRealtimeClient;
|
|
|
260
31
|
* decorators cannot introduce it.
|
|
261
32
|
*/
|
|
262
33
|
|
|
263
|
-
type MethodDecorator$
|
|
34
|
+
type MethodDecorator$1 = (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => void;
|
|
264
35
|
/**
|
|
265
36
|
* Require an active entitlement. Answers 403 `entitlement_required` when it is
|
|
266
37
|
* absent and consumes nothing.
|
|
@@ -268,7 +39,7 @@ type MethodDecorator$2 = (target: object, propertyKey: string | symbol, descript
|
|
|
268
39
|
* The key is a member of the generated `EntitlementKey` union, so a typo is a
|
|
269
40
|
* compile error rather than a gate that silently never matches.
|
|
270
41
|
*/
|
|
271
|
-
declare function RequireEntitlement(key: EntitlementKey): MethodDecorator$
|
|
42
|
+
declare function RequireEntitlement(key: EntitlementKey): MethodDecorator$1;
|
|
272
43
|
/**
|
|
273
44
|
* Consume one unit of a limit or credit for the request's subject.
|
|
274
45
|
*
|
|
@@ -281,7 +52,115 @@ declare function RequireEntitlement(key: EntitlementKey): MethodDecorator$2;
|
|
|
281
52
|
* side-effect, which is an explicit `Purchases.withSpend(...)` call, not a
|
|
282
53
|
* decorator.
|
|
283
54
|
*/
|
|
284
|
-
declare function Spend(key: LimitKey): MethodDecorator$
|
|
55
|
+
declare function Spend(key: LimitKey): MethodDecorator$1;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* clients/index.ts — the composition root for the module clients.
|
|
59
|
+
*
|
|
60
|
+
* This is what the process calls once at boot. It builds the five clients the
|
|
61
|
+
* engine injects and nothing else.
|
|
62
|
+
*
|
|
63
|
+
* WHAT IS NOT BUILT HERE, and why each one is absent rather than forgotten:
|
|
64
|
+
*
|
|
65
|
+
* - `auth`, `functions`, `analytics`, `links` — the SDK exports no singleton
|
|
66
|
+
* for any of them (`runtime.ts` exports ten, and these four are not among
|
|
67
|
+
* them), so no handler could reach them. They were constructed on every
|
|
68
|
+
* boot and thrown away.
|
|
69
|
+
* - `Purchases` — its client talks to palstore, and v2 contains no palstore
|
|
70
|
+
* at all. The surface was unbacked before it was untyped. Dropped from v2
|
|
71
|
+
* by the user's decision on 2026-08-15; the SDK's `src/purchases/` tree is
|
|
72
|
+
* untouched and that decision is deferred, not silently taken.
|
|
73
|
+
* - The V1 host/executor transport — the isolate it existed for was removed
|
|
74
|
+
* on 2026-08-14. A single-tenant process has nobody to hide its own
|
|
75
|
+
* tenant's credentials from.
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
interface ModuleClientsConfig {
|
|
79
|
+
/** Where the platform answers. Empty means "not configured" — see below. */
|
|
80
|
+
baseUrl: string;
|
|
81
|
+
/** The publishable key. Used only when no service-role key is supplied. */
|
|
82
|
+
apiKey: string;
|
|
83
|
+
/**
|
|
84
|
+
* The service-role key, which is what a backend actually holds. Preferred
|
|
85
|
+
* over `apiKey`: this process IS the project's server.
|
|
86
|
+
*/
|
|
87
|
+
serviceRoleKey: string;
|
|
88
|
+
/** The shared Realtime secret. Empty means realtime is not provisioned. */
|
|
89
|
+
realtimeApiJwtSecret: string;
|
|
90
|
+
/** The request's user, read from the SDK's own scope — never from a caller. */
|
|
91
|
+
getCurrentUserId: () => string | null;
|
|
92
|
+
/** Injected in tests; resolved at call time in production. */
|
|
93
|
+
fetchImpl?: typeof fetch;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Build the module clients.
|
|
97
|
+
*
|
|
98
|
+
* Returns an EMPTY bundle when there is no base URL, deliberately: the engine
|
|
99
|
+
* then injects its named stubs, so a handler that reaches for `Storage` gets an
|
|
100
|
+
* error saying which module is unconfigured rather than a crash reading a
|
|
101
|
+
* property of undefined. Booting is not blocked — a project that never touches
|
|
102
|
+
* a module runs fine without one.
|
|
103
|
+
*/
|
|
104
|
+
declare function buildModuleClients(cfg: ModuleClientsConfig): ModuleClients;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* http.ts — the transport every module client speaks over.
|
|
108
|
+
*
|
|
109
|
+
* WHY IT IS HERE. This code, and the five clients built on it, spent their life
|
|
110
|
+
* in `v2/runtime/internal/runtime/module-clients.js`: 1,768 lines of untyped
|
|
111
|
+
* CommonJS that entered the SDK through an `@ts-expect-error` and left through
|
|
112
|
+
* `unknown` casts. The interfaces those clients implement were always in this
|
|
113
|
+
* package — only the implementations were outside it, where nothing checked one
|
|
114
|
+
* against the other. Two defects came through that gap on 2026-08-15 (a type
|
|
115
|
+
* that described a different server's columns; an accessor that was `undefined`
|
|
116
|
+
* in a deployed handler), and both were green on each side alone.
|
|
117
|
+
*
|
|
118
|
+
* WHAT IT IS NOT. Nothing Bun-specific lives here. The transport is a `fetch`
|
|
119
|
+
* the caller may supply, so this package stays runtime-agnostic and the process
|
|
120
|
+
* that knows which runtime it is decides what to hand in.
|
|
121
|
+
*/
|
|
122
|
+
|
|
123
|
+
/** Per-request options a module client may pass. */
|
|
124
|
+
interface RequestOptions {
|
|
125
|
+
/** JSON-serialised unless it is a string, a Uint8Array, a Blob or FormData. */
|
|
126
|
+
body?: unknown;
|
|
127
|
+
headers?: Record<string, string>;
|
|
128
|
+
signal?: AbortSignal;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* What a module client is given. An INTERFACE rather than a class, because the
|
|
132
|
+
* clients depend on this shape and not on the thing that implements it — which
|
|
133
|
+
* is what lets a test drive a whole client without a server.
|
|
134
|
+
*/
|
|
135
|
+
interface ModuleTransport {
|
|
136
|
+
/** The platform's base URL, for the rare client that must build a URL. */
|
|
137
|
+
readonly baseUrl: string;
|
|
138
|
+
request<T = unknown>(method: string, path: string, options?: RequestOptions): Promise<PalbaseResult<T>>;
|
|
139
|
+
}
|
|
140
|
+
interface TransportConfig {
|
|
141
|
+
baseUrl: string;
|
|
142
|
+
apiKey: string;
|
|
143
|
+
/**
|
|
144
|
+
* The `fetch` to use. Omitted means the ambient one — resolved at CALL time,
|
|
145
|
+
* never captured here: the egress fence replaces `globalThis.fetch` after the
|
|
146
|
+
* clients are built, and a captured reference would keep calling the
|
|
147
|
+
* un-fenced original.
|
|
148
|
+
*/
|
|
149
|
+
fetchImpl?: typeof fetch;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* A module call that failed.
|
|
153
|
+
*
|
|
154
|
+
* `code` is the platform's own error code (`forbidden`, `not_found`, …) so a
|
|
155
|
+
* caller can branch on it without matching message text.
|
|
156
|
+
*/
|
|
157
|
+
declare class PalbaseModuleError extends Error {
|
|
158
|
+
readonly code: string;
|
|
159
|
+
readonly status: number;
|
|
160
|
+
readonly details: Record<string, unknown>;
|
|
161
|
+
constructor(code: string, message: string, status: number, details?: Record<string, unknown>);
|
|
162
|
+
}
|
|
163
|
+
declare function makeHttpClient(cfg: TransportConfig): ModuleTransport;
|
|
285
164
|
|
|
286
165
|
/**
|
|
287
166
|
* keys-gen.ts — generate the `palbase-purchases.d.ts` text from a catalog
|
|
@@ -350,6 +229,72 @@ declare function entitlementFor(ctor: object, fnName: string): EntitlementKey |
|
|
|
350
229
|
/** What `fnName` spends, or undefined when it spends nothing. */
|
|
351
230
|
declare function spendFor(ctor: object, fnName: string): SpendMeta | undefined;
|
|
352
231
|
|
|
232
|
+
/** One column, flattened. Mirrors Go's `schema.ColumnJSON`. */
|
|
233
|
+
interface ColumnJSON {
|
|
234
|
+
type: string;
|
|
235
|
+
nullable: boolean;
|
|
236
|
+
primaryKey: boolean;
|
|
237
|
+
defaultValue?: unknown;
|
|
238
|
+
defaultRandom?: boolean;
|
|
239
|
+
defaultNow?: boolean;
|
|
240
|
+
renamedFrom?: string;
|
|
241
|
+
references?: {
|
|
242
|
+
table: string;
|
|
243
|
+
column: string;
|
|
244
|
+
};
|
|
245
|
+
onDeleteAction?: string;
|
|
246
|
+
enumName?: string;
|
|
247
|
+
enumValues?: string[];
|
|
248
|
+
unique?: boolean;
|
|
249
|
+
}
|
|
250
|
+
/** One RLS policy. Mirrors Go's `schema.PolicyJSON`. */
|
|
251
|
+
interface PolicyJSON {
|
|
252
|
+
name: string;
|
|
253
|
+
command: string;
|
|
254
|
+
roles: string[];
|
|
255
|
+
using: string | null;
|
|
256
|
+
withCheck: string | null;
|
|
257
|
+
permissive: boolean;
|
|
258
|
+
}
|
|
259
|
+
/** One table. Mirrors Go's `schema.TableJSON`. */
|
|
260
|
+
interface TableJSON {
|
|
261
|
+
name: string;
|
|
262
|
+
columns: Record<string, ColumnJSON>;
|
|
263
|
+
rls: boolean;
|
|
264
|
+
policies: PolicyJSON[];
|
|
265
|
+
primaryKey?: string[];
|
|
266
|
+
uniqueConstraints?: {
|
|
267
|
+
name: string;
|
|
268
|
+
columns: string[];
|
|
269
|
+
}[];
|
|
270
|
+
rawConstraints?: {
|
|
271
|
+
name: string;
|
|
272
|
+
up: string;
|
|
273
|
+
down: string | null;
|
|
274
|
+
}[];
|
|
275
|
+
checks?: {
|
|
276
|
+
name: string;
|
|
277
|
+
expr: string;
|
|
278
|
+
}[];
|
|
279
|
+
indexes?: {
|
|
280
|
+
name: string;
|
|
281
|
+
columns: string[];
|
|
282
|
+
}[];
|
|
283
|
+
}
|
|
284
|
+
/** A whole declaration. Mirrors Go's `schema.SchemaJSON`. */
|
|
285
|
+
interface SchemaJSON {
|
|
286
|
+
tables: Record<string, TableJSON>;
|
|
287
|
+
extensions: string[];
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Serialize a `defineSchema(...)` result into the JSON the deploy applies.
|
|
291
|
+
*
|
|
292
|
+
* Keyed by the SQL table name rather than by the author's object key, because
|
|
293
|
+
* that is the name the live database answers to — and the diff on the other side
|
|
294
|
+
* compares against what it introspected.
|
|
295
|
+
*/
|
|
296
|
+
declare function toSchemaJSON(schema: SchemaDef): SchemaJSON;
|
|
297
|
+
|
|
353
298
|
/**
|
|
354
299
|
* env-gen.ts — generate the `palbase-env.d.ts` text from a `defineSchema()`
|
|
355
300
|
* result.
|
|
@@ -376,143 +321,6 @@ declare function spendFor(ctor: object, fnName: string): SpendMeta | undefined;
|
|
|
376
321
|
*/
|
|
377
322
|
declare function makeEnvDts(schema: SchemaDef): string;
|
|
378
323
|
|
|
379
|
-
/**
|
|
380
|
-
* storage.ts — the storage-buckets config-as-code DSL.
|
|
381
|
-
*
|
|
382
|
-
* `defineStorage({ buckets })` is the first MODULE config-as-code surface (the
|
|
383
|
-
* sibling of `db/schema.ts`'s `defineSchema`). A `config/storage.ts` file
|
|
384
|
-
* default-exports a `defineStorage(...)` result; on deploy the br-pod evaluates
|
|
385
|
-
* it to JSON and reconciles the declared buckets against the tenant's live
|
|
386
|
-
* buckets via the Storage admin API (create missing, update changed; never
|
|
387
|
-
* auto-delete — dropping a bucket is destructive and warned only).
|
|
388
|
-
*
|
|
389
|
-
* Buckets are DECLARATIVE: name + access + size/mime limits. The FILES inside a
|
|
390
|
-
* bucket are runtime state (uploaded via the SDK), never in git.
|
|
391
|
-
*
|
|
392
|
-
* @example
|
|
393
|
-
* import { defineStorage, bucket } from "@palbase/backend";
|
|
394
|
-
*
|
|
395
|
-
* export default defineStorage({
|
|
396
|
-
* buckets: {
|
|
397
|
-
* avatars: bucket({
|
|
398
|
-
* public: true,
|
|
399
|
-
* fileSizeLimit: "5MB",
|
|
400
|
-
* allowedMimeTypes: ["image/png", "image/jpeg", "image/webp"],
|
|
401
|
-
* }),
|
|
402
|
-
* invoices: bucket({
|
|
403
|
-
* public: false,
|
|
404
|
-
* fileSizeLimit: "20MB",
|
|
405
|
-
* allowedMimeTypes: ["application/pdf"],
|
|
406
|
-
* }),
|
|
407
|
-
* },
|
|
408
|
-
* });
|
|
409
|
-
*
|
|
410
|
-
* The returned value is the EXACT JSON shape the runtime's generic config
|
|
411
|
-
* extractor (`config_extract.js`) emits and the Go apply step parses:
|
|
412
|
-
* { __config: "storage", buckets: { avatars: { public, fileSizeLimit, allowedMimeTypes }, ... } }
|
|
413
|
-
* `fileSizeLimit` is always normalized to a number of bytes (or null when
|
|
414
|
-
* omitted, meaning "no limit"); the apply step maps it to the Storage API's
|
|
415
|
-
* `file_size_limit` field. `allowedMimeTypes` is the allowlist or null (any).
|
|
416
|
-
*/
|
|
417
|
-
/** The discriminant written under `__config` so the eval/apply can tell which
|
|
418
|
-
* module config a `config/*.ts` file describes. Storage is `"storage"`. */
|
|
419
|
-
declare const STORAGE_CONFIG_KIND: "storage";
|
|
420
|
-
/**
|
|
421
|
-
* The author-facing options for a single bucket.
|
|
422
|
-
*
|
|
423
|
-
* - `public`: when true the bucket is served without a signed URL. Defaults to
|
|
424
|
-
* `false` (private — signed URLs only).
|
|
425
|
-
* - `fileSizeLimit`: the per-object upload cap. Accepts a human string
|
|
426
|
-
* (`"5MB"`, `"20MB"`, `"1GB"`) or a bare number of BYTES. Omit for no limit.
|
|
427
|
-
* - `allowedMimeTypes`: an allowlist of MIME types accepted on upload. Omit to
|
|
428
|
-
* allow any type. Each entry must look like `type/subtype` (e.g. `image/png`,
|
|
429
|
-
* `application/pdf`, or a wildcard `image/*`).
|
|
430
|
-
*/
|
|
431
|
-
interface BucketOptions {
|
|
432
|
-
public?: boolean;
|
|
433
|
-
fileSizeLimit?: string | number;
|
|
434
|
-
allowedMimeTypes?: string[];
|
|
435
|
-
variants?: Record<string, ImageVariant>;
|
|
436
|
-
}
|
|
437
|
-
/**
|
|
438
|
-
* One named rendition of an uploaded image — the same three knobs
|
|
439
|
-
* `getPublicUrl(path, ...)` takes, given a name so the size lives with the
|
|
440
|
-
* bucket instead of being retyped at every call site.
|
|
441
|
-
*
|
|
442
|
-
* At least one of `width` / `height` must be set (a variant that resizes
|
|
443
|
-
* nothing and converts nothing is a typo, not a variant).
|
|
444
|
-
*/
|
|
445
|
-
interface ImageVariant {
|
|
446
|
-
width?: number;
|
|
447
|
-
height?: number;
|
|
448
|
-
format?: "webp" | "avif" | "jpeg" | "png";
|
|
449
|
-
}
|
|
450
|
-
/**
|
|
451
|
-
* The compiled, serializable bucket definition — the EXACT shape emitted to
|
|
452
|
-
* JSON and consumed by the Go apply step.
|
|
453
|
-
*
|
|
454
|
-
* - `public`: always present (defaulted to `false`).
|
|
455
|
-
* - `fileSizeLimit`: bytes as a number, or `null` for "no limit".
|
|
456
|
-
* - `allowedMimeTypes`: the MIME allowlist, or `null` for "any type".
|
|
457
|
-
*/
|
|
458
|
-
interface BucketDef {
|
|
459
|
-
public: boolean;
|
|
460
|
-
fileSizeLimit: number | null;
|
|
461
|
-
allowedMimeTypes: string[] | null;
|
|
462
|
-
/** Named renditions, or `null` when none are declared. Rendered on request by
|
|
463
|
-
* the image-transform route and cached — nothing extra is stored. */
|
|
464
|
-
variants: Record<string, ImageVariant> | null;
|
|
465
|
-
}
|
|
466
|
-
/** A storage config definition: the discriminant + a map of bucket name →
|
|
467
|
-
* {@link BucketDef}. This is the value `defineStorage` returns and the runtime
|
|
468
|
-
* config extractor serializes. */
|
|
469
|
-
interface StorageConfig {
|
|
470
|
-
__config: typeof STORAGE_CONFIG_KIND;
|
|
471
|
-
buckets: Record<string, BucketDef>;
|
|
472
|
-
}
|
|
473
|
-
/** The author-facing input to `defineStorage`: a `buckets` map whose keys are
|
|
474
|
-
* the bucket names and whose values are `bucket({...})` builders. */
|
|
475
|
-
interface StorageInput {
|
|
476
|
-
buckets: Record<string, BucketDef>;
|
|
477
|
-
}
|
|
478
|
-
/**
|
|
479
|
-
* Parse a `fileSizeLimit` into a number of bytes.
|
|
480
|
-
*
|
|
481
|
-
* - A bare number is taken as bytes verbatim (must be a non-negative integer).
|
|
482
|
-
* - A string is `<number><unit>` (e.g. `"5MB"`, `"1.5GB"`, `"500kb"`); the unit
|
|
483
|
-
* is case-insensitive and optional (a bare numeric string = bytes). Binary
|
|
484
|
-
* units (1 MB = 1024 squared bytes) to match the Storage module's parser.
|
|
485
|
-
*
|
|
486
|
-
* Throws on a negative value, a non-finite number, or an unrecognized unit so a
|
|
487
|
-
* bad limit fails at config-author time, not silently at deploy.
|
|
488
|
-
*/
|
|
489
|
-
declare function parseFileSizeLimit(input: string | number): number;
|
|
490
|
-
/**
|
|
491
|
-
* Define a single bucket. The bucket NAME is supplied by the key in
|
|
492
|
-
* `defineStorage({ buckets: { <name>: bucket({...}) } })`, so `bucket()` takes
|
|
493
|
-
* only the options.
|
|
494
|
-
*
|
|
495
|
-
* Validates eagerly (at config-author time):
|
|
496
|
-
* - `fileSizeLimit` parses to a valid non-negative byte count.
|
|
497
|
-
* - each `allowedMimeTypes` entry is a `type/subtype` MIME string.
|
|
498
|
-
*
|
|
499
|
-
* Returns a normalized {@link BucketDef}: `public` defaulted to `false`,
|
|
500
|
-
* `fileSizeLimit` as bytes-or-null, `allowedMimeTypes` deduped-or-null.
|
|
501
|
-
*/
|
|
502
|
-
declare function bucket(opts?: BucketOptions): BucketDef;
|
|
503
|
-
/**
|
|
504
|
-
* Define the storage config for a project. The bucket NAME comes from each
|
|
505
|
-
* object key (authors never repeat the name). Returns the discriminated
|
|
506
|
-
* {@link StorageConfig} the runtime config extractor serializes and the Go
|
|
507
|
-
* apply step reconciles.
|
|
508
|
-
*
|
|
509
|
-
* @example
|
|
510
|
-
* export default defineStorage({
|
|
511
|
-
* buckets: { avatars: bucket({ public: true, fileSizeLimit: "5MB" }) },
|
|
512
|
-
* });
|
|
513
|
-
*/
|
|
514
|
-
declare function defineStorage(input: StorageInput): StorageConfig;
|
|
515
|
-
|
|
516
324
|
/**
|
|
517
325
|
* egress.ts — the tenant outbound-HTTP allowlist config-as-code DSL.
|
|
518
326
|
*
|
|
@@ -1344,180 +1152,130 @@ declare function flag(opts: FlagOptions): FlagDef;
|
|
|
1344
1152
|
*/
|
|
1345
1153
|
declare function defineFlags(input: FlagsInput): FlagsConfig;
|
|
1346
1154
|
|
|
1347
|
-
/** Options accepted by `@Controller`. */
|
|
1348
|
-
interface ControllerOptions {
|
|
1349
|
-
/** Default auth for ALL routes in this controller (route-level overrides). */
|
|
1350
|
-
auth?: AuthSpec;
|
|
1351
|
-
}
|
|
1352
1155
|
/**
|
|
1353
|
-
*
|
|
1354
|
-
*
|
|
1355
|
-
*
|
|
1156
|
+
* secrets.ts — the tenant SECRET REQUIREMENT config-as-code DSL.
|
|
1157
|
+
*
|
|
1158
|
+
* `defineSecrets({ secrets: [secret("NEO4J_PASSWORD")] })` declares WHICH secrets
|
|
1159
|
+
* a backend needs to run. It declares the requirement, never the value: the value
|
|
1160
|
+
* lives only in the vault, and nothing in this file — or in the repo it is
|
|
1161
|
+
* committed to — ever holds one.
|
|
1162
|
+
*
|
|
1163
|
+
* The gap it closes: today a backend's dependence on a secret is written nowhere
|
|
1164
|
+
* except inside the code that reads it (`process.env.NEO4J_PASSWORD`). Deploy a
|
|
1165
|
+
* project into an environment where that secret was never set and the build
|
|
1166
|
+
* passes, the pod starts, and the failure surfaces as a 500 on the first request
|
|
1167
|
+
* that touches it — measured on todoapp, whose `/graph/*` routes returned 500 for
|
|
1168
|
+
* exactly this reason. With the requirement declared, it travels WITH the code:
|
|
1169
|
+
* `push` compares this list against the environment's vault and refuses the
|
|
1170
|
+
* deploy, naming the missing secret, before anything ships.
|
|
1356
1171
|
*
|
|
1357
1172
|
* @example
|
|
1358
|
-
*
|
|
1359
|
-
*
|
|
1360
|
-
*
|
|
1361
|
-
*
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
*
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
*
|
|
1372
|
-
*
|
|
1373
|
-
*
|
|
1374
|
-
* — the client cannot exceed what it declared.
|
|
1173
|
+
* // config/secrets.ts
|
|
1174
|
+
* import { defineSecrets, secret } from "@palbase/backend";
|
|
1175
|
+
* export default defineSecrets({
|
|
1176
|
+
* secrets: [
|
|
1177
|
+
* secret("NEO4J_PASSWORD", { description: "graph store password" }),
|
|
1178
|
+
* secret("SENTRY_DSN", { required: false, description: "crash reporting" }),
|
|
1179
|
+
* ],
|
|
1180
|
+
* });
|
|
1181
|
+
*
|
|
1182
|
+
* `required` defaults to TRUE. A declaration exists to be checked, so the safe
|
|
1183
|
+
* default is the checked one; `required: false` says the code has a working path
|
|
1184
|
+
* when the secret is absent, and downgrades a missing secret to a warning.
|
|
1185
|
+
*
|
|
1186
|
+
* Emits: { __config: "secrets", secrets: [{ name, required, description }] }
|
|
1187
|
+
* sorted by name, so the same declaration always evaluates to the same bytes and
|
|
1188
|
+
* a config diff shows what an author changed rather than how they reordered it.
|
|
1375
1189
|
*/
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
* The bucket is the SINGLE SOURCE OF TRUTH for the size limit + MIME allowlist:
|
|
1381
|
-
* `bucket({ fileSizeLimit, allowedMimeTypes })`. Storage enforces those at the
|
|
1382
|
-
* actual PUT (the only guard a client cannot skip), so `@Upload` deliberately
|
|
1383
|
-
* does NOT take its own `maxSize`/`allowedTypes` — duplicating them here would
|
|
1384
|
-
* let a route declare a tighter limit than its bucket that storage would not
|
|
1385
|
-
* enforce (a real bypass: declare 10 bytes at authorize, then PUT up to the
|
|
1386
|
-
* bucket ceiling straight at the signed URL). One bucket, one limit, enforced.
|
|
1387
|
-
*/
|
|
1388
|
-
bucket: string;
|
|
1190
|
+
/** The discriminant written under `__config` so the deploy eval knows the kind. */
|
|
1191
|
+
declare const SECRETS_CONFIG_KIND: "secrets";
|
|
1192
|
+
/** The author-facing options for a single `secret()`. */
|
|
1193
|
+
interface SecretOptions {
|
|
1389
1194
|
/**
|
|
1390
|
-
*
|
|
1391
|
-
*
|
|
1392
|
-
* `{filename}` (the client-declared filename, sanitized). e.g.
|
|
1393
|
-
* `"{userId}/{uploadId}-{filename}"`.
|
|
1195
|
+
* Whether the deploy must REFUSE when this secret is absent from the target
|
|
1196
|
+
* environment's vault. Omitted ⇒ `true`.
|
|
1394
1197
|
*/
|
|
1395
|
-
|
|
1198
|
+
required?: boolean;
|
|
1199
|
+
/** What the secret is for — shown when the deploy reports it missing. */
|
|
1200
|
+
description?: string;
|
|
1396
1201
|
}
|
|
1397
1202
|
/**
|
|
1398
|
-
*
|
|
1399
|
-
*
|
|
1400
|
-
* guard + signed-URL pinning.
|
|
1203
|
+
* The compiled, serializable secret declaration — the EXACT shape emitted to
|
|
1204
|
+
* JSON and consumed by the Go deploy step.
|
|
1401
1205
|
*
|
|
1402
|
-
*
|
|
1403
|
-
*
|
|
1404
|
-
*
|
|
1405
|
-
* // The size limit + MIME allowlist come from the "docs" bucket in
|
|
1406
|
-
* // config/storage.ts — storage enforces them at the PUT.
|
|
1407
|
-
*/
|
|
1408
|
-
declare function Upload(subpath: string, config: UploadConfig & Pick<RouteOptions, "auth" | "rateLimit">): MethodDecorator$1;
|
|
1409
|
-
/**
|
|
1410
|
-
* The uploaded object, injected into an `@Upload` method body by
|
|
1411
|
-
* `@UploadedObject()` once storage confirms the upload. Bytes are NOT present
|
|
1412
|
-
* (they went straight to storage) — this is the metadata the completion handler
|
|
1413
|
-
* persists.
|
|
1206
|
+
* - `name`: the environment-variable name the code reads, verbatim.
|
|
1207
|
+
* - `required`: always present (defaulted to `true`).
|
|
1208
|
+
* - `description`: the doc string, or `null`.
|
|
1414
1209
|
*/
|
|
1415
|
-
interface
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1210
|
+
interface SecretDef {
|
|
1211
|
+
name: string;
|
|
1212
|
+
required: boolean;
|
|
1213
|
+
description: string | null;
|
|
1214
|
+
}
|
|
1215
|
+
/** A secrets config definition: the discriminant + the name-sorted declarations.
|
|
1216
|
+
* This is the value `defineSecrets` returns and the build's config extractor
|
|
1217
|
+
* serializes into `.palbase/config.json`. */
|
|
1218
|
+
interface SecretsConfig {
|
|
1219
|
+
__config: typeof SECRETS_CONFIG_KIND;
|
|
1220
|
+
secrets: SecretDef[];
|
|
1221
|
+
}
|
|
1222
|
+
/** The author-facing input to `defineSecrets`: a list of `secret(...)` builders. */
|
|
1223
|
+
interface SecretsInput {
|
|
1224
|
+
secrets: SecretDef[];
|
|
1426
1225
|
}
|
|
1427
1226
|
/**
|
|
1428
|
-
*
|
|
1429
|
-
*
|
|
1430
|
-
*
|
|
1431
|
-
*
|
|
1227
|
+
* Declare a single required (or optional) secret. Unlike `flag()` / `bucket()`,
|
|
1228
|
+
* the NAME is an argument rather than an object key: a secret's name is not a
|
|
1229
|
+
* label an author chooses, it is the exact env-var identifier the code already
|
|
1230
|
+
* reads, so it belongs where it can be validated as one.
|
|
1432
1231
|
*
|
|
1433
|
-
*
|
|
1434
|
-
* `
|
|
1232
|
+
* Validates eagerly (at config-author time):
|
|
1233
|
+
* - `name` is `[A-Z][A-Z0-9_]*` — anything else throws.
|
|
1234
|
+
* - `required` is a boolean; `description` is a string.
|
|
1235
|
+
*
|
|
1236
|
+
* Returns a normalized {@link SecretDef}: `required` defaulted to `true`,
|
|
1237
|
+
* `description` trimmed-or-null.
|
|
1435
1238
|
*/
|
|
1436
|
-
declare function
|
|
1239
|
+
declare function secret(name: string, opts?: SecretOptions): SecretDef;
|
|
1437
1240
|
/**
|
|
1438
|
-
*
|
|
1439
|
-
* {@link
|
|
1440
|
-
*
|
|
1441
|
-
*
|
|
1442
|
-
* That is the ONLY cross-check: the size limit + MIME allowlist live on the
|
|
1443
|
-
* bucket (`bucket({ fileSizeLimit, allowedMimeTypes })`) and are enforced by
|
|
1444
|
-
* storage at the PUT. `@Upload` carries no `maxSize`/`allowedTypes` of its own,
|
|
1445
|
-
* so there is nothing to compare — one bucket, one limit, no drift.
|
|
1241
|
+
* Define the secrets a backend needs. Returns the discriminated
|
|
1242
|
+
* {@link SecretsConfig} the build's config extractor serializes and the deploy
|
|
1243
|
+
* checks against the target environment's vault BEFORE shipping anything.
|
|
1446
1244
|
*
|
|
1447
|
-
*
|
|
1245
|
+
* A name declared twice throws: two declarations of one name disagree about
|
|
1246
|
+
* something (usually `required`), and silently keeping either one is how a
|
|
1247
|
+
* secret stops being checked without anybody editing the check.
|
|
1448
1248
|
*/
|
|
1449
|
-
declare function
|
|
1249
|
+
declare function defineSecrets(input: SecretsInput): SecretsConfig;
|
|
1450
1250
|
|
|
1451
|
-
/**
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
/** Route-level options accepted by the method decorators (`@Get`/`@Post`/…). */
|
|
1455
|
-
interface RouteOptions {
|
|
1456
|
-
/** OVERRIDES the controller-level default auth for this one route. */
|
|
1251
|
+
/** Options accepted by `@Controller`. */
|
|
1252
|
+
interface ControllerOptions {
|
|
1253
|
+
/** Default auth for ALL routes in this controller (route-level overrides). */
|
|
1457
1254
|
auth?: AuthSpec;
|
|
1458
|
-
/** Per-route rate limit. */
|
|
1459
|
-
rateLimit?: RateLimitConfig;
|
|
1460
|
-
/** Direct-storage upload config — present ONLY on `@Upload` routes (the
|
|
1461
|
-
* `@Get`/`@Post`/… decorators never set it). Its presence is what MARKS a
|
|
1462
|
-
* route as an upload route through the whole pipeline (registry → flatten →
|
|
1463
|
-
* openapi → codegen). The bytes go client→storage directly; the method body
|
|
1464
|
-
* runs as the completion handler. See {@link UploadConfig} (decorators/upload.ts). */
|
|
1465
|
-
uploadConfig?: UploadConfig;
|
|
1466
|
-
}
|
|
1467
|
-
/** The kind of value a parameter decorator injects. Drives both dispatch
|
|
1468
|
-
* (which request slice to inject) and codegen (which OpenAPI parameter source a
|
|
1469
|
-
* schema-bearing kind maps to). */
|
|
1470
|
-
type ParamKind = "body" | "query" | "param" | "headers" | "user" | "optionalUser" | "client" | "requestId" | "traceId" | "req" | "uploadedObject";
|
|
1471
|
-
/** One parameter decorator's recorded metadata. `index` is the parameter
|
|
1472
|
-
* position esbuild/tsc preserve; `schema` is present for the schema-bearing
|
|
1473
|
-
* kinds (`body`/`query`/`headers`); `name` is the path-param name for `param`. */
|
|
1474
|
-
interface ParamMeta {
|
|
1475
|
-
index: number;
|
|
1476
|
-
kind: ParamKind;
|
|
1477
|
-
/** Zod schema for `body`/`query`/`headers` (validation + codegen source). */
|
|
1478
|
-
schema?: ZodTypeAny;
|
|
1479
|
-
/** Path-param name for `@Param("id")`. */
|
|
1480
|
-
name?: string;
|
|
1481
|
-
}
|
|
1482
|
-
/** One inferred throw site: the error CLASS name (e.g. "TodoLocked") and its
|
|
1483
|
-
* wire code (e.g. "todo_locked"). `status`, `hasData`, and the data JSON schema
|
|
1484
|
-
* are NOT carried here — they resolve from the error registry by `code` at
|
|
1485
|
-
* extract/openapi time (single source of truth). */
|
|
1486
|
-
interface ThrowDescriptor {
|
|
1487
|
-
name: string;
|
|
1488
|
-
code: string;
|
|
1489
1255
|
}
|
|
1490
|
-
/**
|
|
1491
|
-
*
|
|
1492
|
-
*
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
*
|
|
1511
|
-
* `recordReturn` twin). If the route does not exist yet, the descriptors are
|
|
1512
|
-
* buffered (THROWS_BUFFER) and drained into the route by `recordRoute` when the
|
|
1513
|
-
* method decorator runs. */
|
|
1514
|
-
declare function recordThrows(target: object, fnName: string, throws: ThrowDescriptor[]): void;
|
|
1515
|
-
/** Read the route metadata for a controller class (the deploy/dispatch entry
|
|
1516
|
-
* point). Applies any buffered return schemas + throw descriptors (for the
|
|
1517
|
-
* recordReturn/recordThrows-runs-before orderings) and returns a defensive copy
|
|
1518
|
-
* so callers cannot mutate the registry.
|
|
1256
|
+
/**
|
|
1257
|
+
* The controller classes this process has loaded, in decoration order.
|
|
1258
|
+
*
|
|
1259
|
+
* Decoration order is import order, which the bundler fixes by sorting the
|
|
1260
|
+
* files it emits imports for — so two builds of one tree produce the same
|
|
1261
|
+
* route table, and route precedence is not a function of module-resolution
|
|
1262
|
+
* accidents.
|
|
1263
|
+
*/
|
|
1264
|
+
declare function getRegisteredControllers(): readonly unknown[];
|
|
1265
|
+
/** Empty the registry. For tests, which load controllers repeatedly. */
|
|
1266
|
+
declare function __resetRegisteredControllers(): void;
|
|
1267
|
+
/**
|
|
1268
|
+
* Mark a class as a Palbase backend controller. `basePath` is the mount path
|
|
1269
|
+
* for every route the class declares; `options.auth` sets the controller-level
|
|
1270
|
+
* default auth (a route's own `auth` overrides it; absent ⇒ secure-by-default).
|
|
1271
|
+
*
|
|
1272
|
+
* @example
|
|
1273
|
+
* \@Controller("/todos", { auth: false })
|
|
1274
|
+
* export class TodosController {
|
|
1275
|
+
* \@Get("") list(\@QueryParams(ListTodosQuery) q: ListTodosQuery): TodoSchema[] { … }
|
|
1276
|
+
* }
|
|
1519
1277
|
*/
|
|
1520
|
-
declare function
|
|
1278
|
+
declare function Controller(basePath: string, options?: ControllerOptions): <T extends abstract new (...args: never[]) => object>(ctor: T) => T;
|
|
1521
1279
|
|
|
1522
1280
|
/** A legacy method decorator. */
|
|
1523
1281
|
type MethodDecorator = (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => void;
|
|
@@ -1898,4 +1656,4 @@ declare const documents: {
|
|
|
1898
1656
|
onDocumentDeleted(handler: HookHandler<DocumentDeletedEvent>): ResolvedHook<DocumentDeletedEvent>;
|
|
1899
1657
|
};
|
|
1900
1658
|
|
|
1901
|
-
export { type AcsOptions, type ApnsOptions, Body,
|
|
1659
|
+
export { type AcsOptions, type ApnsOptions, Body, Client, type ColumnJSON, Controller, type ControllerOptions, DEFAULT_TEMPLATE_LOCALE, type DefinedError, type DefinedErrorWithData, Delete, type DocumentCreatedEvent, type DocumentDeletedEvent, type DocumentUpdatedEvent, EGRESS_CONFIG_KIND, EGRESS_TIMEOUT_DEFAULT_MS, EGRESS_TIMEOUT_MAX_MS, EGRESS_TIMEOUT_MIN_MS, type EgressConfig, type EgressInput, type EmailTemplateContent, type EmailTemplateDef, type EmailTemplateOptions, EntitlementKey, FLAGS_CONFIG_KIND, type FcmOptions, type FileDeletedEvent, type FileUploadedEvent, type FlagDef, type FlagJsonObject, type FlagJsonValue, type FlagOptions, type FlagType, type FlagValue, type FlagsConfig, type FlagsInput, Get, Headers, type HookHandler, type HookMeta, HttpError, Job, type JobMeta, type JobOptions, LimitKey, type ModuleClientsConfig, type ModuleTransport, NOTIFICATIONS_CONFIG_KIND, type NotificationsConfig, type NotificationsInput, On, OptionalUser, type OwnedTablesOf, PROVIDER_CATALOG, PalbaseModuleError, PalbaseResult, Param, type PasswordResetEvent, Patch, type PolicyJSON, Post, type ProviderCatalogEntry, type ProviderDef, type ProviderName, type ProviderOptions, type PurchasesManifest, Put, Query, QueryParams, RESERVED_SECRET_PREFIX, type RegisteredError, Req, RequestId, type RequestOptions, RequireEntitlement, type ResolvedHook, type ResolvedJob, type ResolvedWebhook, Resource, type ResourceEnv, RouteOptions, SECRETS_CONFIG_KIND, SchemaDef, type SchemaJSON, type SecretDef, type SecretOptions, type SecretsConfig, type SecretsInput, type Seed, type SeedFor, type SeedJson, type SeedRowFor, type SendgridOptions, type SesOptions, type SignInEvent, type SignOutEvent, type SignatureSpec, type SmsTemplateContent, type SmsTemplateDef, type SmsTemplateOptions, type SmtpOptions, Spend, type SpendMeta, TEST_USERS_CONFIG_KIND, type TableGraph, type TableJSON, TableTypes, Tables, type TemplatesConfig, type TemplatesInput, type TestUserDef, type TestUserOptions, type TestUsersConfig, type TestUsersInput, TraceId, type TransportConfig, type TwilioOptions, User, type UserCreatedEvent, Webhook, type WebhookEventHandler, type WebhookMeta, type WebhookOptions, type WebhookProvider, type WebhookRequest, __registerResource, __resetRegisteredControllers, __runResourceBoot, __shutdownResources, auth, buildModuleClients, buildProvider, defineEgress, defineError, defineFlags, defineNotifications, defineSecrets, defineTestUsers, documents, entitlementFor, extractVariables, flag, getErrorRegistry, getJobConfig, getRegisteredControllers, getWebhookConfig, makeEnvDts, makeHttpClient, makePurchasesDts, reservedSecretKey, secret, spendFor, storage, testUser, toSchemaJSON };
|