palbase 0.0.0-stage → 0.2.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.
Files changed (109) hide show
  1. package/LICENSE +27 -0
  2. package/README.md +2 -2
  3. package/dist/ai/index.js +17 -0
  4. package/dist/backend/engine.js +2 -0
  5. package/dist/backend/env.js +1 -0
  6. package/dist/backend/index.js +12 -0
  7. package/dist/backend/stack.js +1 -0
  8. package/dist/backend/test.js +2 -0
  9. package/dist/chunks/5W6XJM46.js +13 -0
  10. package/dist/chunks/6OPBXB7Z.js +2 -0
  11. package/dist/chunks/7LOMYBLN.js +20 -0
  12. package/dist/chunks/AN5LCF5K.js +55 -0
  13. package/dist/chunks/ECUYQ73P.js +2 -0
  14. package/dist/chunks/HDX6YUAB.js +2 -0
  15. package/dist/chunks/HVC2C35F.js +2 -0
  16. package/dist/chunks/I3JNX6MZ.js +4 -0
  17. package/dist/chunks/JEJ5CFTK.js +2 -0
  18. package/dist/chunks/K3C2IEDS.js +2 -0
  19. package/dist/chunks/KRBVYYME.js +2 -0
  20. package/dist/chunks/NIJHTHNF.js +2 -0
  21. package/dist/chunks/TL4H6V3P.js +2 -0
  22. package/dist/chunks/UTRQWCMD.js +6 -0
  23. package/dist/chunks/ZFPDYRRY.js +2 -0
  24. package/dist/cli.js +815 -0
  25. package/dist/refuse/ai.js +2 -0
  26. package/dist/refuse/backend.js +2 -0
  27. package/dist/refuse/server.js +2 -0
  28. package/dist/server/index.js +2 -0
  29. package/dist/server/testing.js +2 -0
  30. package/dist/web/index.js +2 -0
  31. package/dist/web/palbe.js +2 -0
  32. package/dist/web/paywall.js +2 -0
  33. package/dist/web/vite.js +3 -0
  34. package/package.json +111 -4
  35. package/types/ai/src/ai.d.ts +178 -0
  36. package/types/ai/src/index.d.ts +5 -0
  37. package/types/ai/src/partial.d.ts +1 -0
  38. package/types/ai/src/schema.d.ts +47 -0
  39. package/types/backend/src/bootstrap.d.ts +72 -0
  40. package/types/backend/src/cache.d.ts +32 -0
  41. package/types/backend/src/container.d.ts +49 -0
  42. package/types/backend/src/database.d.ts +148 -0
  43. package/types/backend/src/db-env.d.ts +4 -0
  44. package/types/backend/src/decorators.d.ts +293 -0
  45. package/types/backend/src/engine-db.d.ts +24 -0
  46. package/types/backend/src/engine-exports.d.ts +3 -0
  47. package/types/backend/src/engine.d.ts +124 -0
  48. package/types/backend/src/env.d.ts +27 -0
  49. package/types/backend/src/errors.d.ts +70 -0
  50. package/types/backend/src/i18n.d.ts +21 -0
  51. package/types/backend/src/index.d.ts +23 -0
  52. package/types/backend/src/openapi.d.ts +73 -0
  53. package/types/backend/src/platform-calls.d.ts +41 -0
  54. package/types/backend/src/requests.d.ts +81 -0
  55. package/types/backend/src/rooms.d.ts +168 -0
  56. package/types/backend/src/router.d.ts +22 -0
  57. package/types/backend/src/runtime.d.ts +56 -0
  58. package/types/backend/src/services.d.ts +262 -0
  59. package/types/backend/src/stack.d.ts +20 -0
  60. package/types/backend/src/strings-files.d.ts +3 -0
  61. package/types/backend/src/testing/index.d.ts +3 -0
  62. package/types/backend/src/testing/unit.d.ts +66 -0
  63. package/types/backend/src/upload.d.ts +125 -0
  64. package/types/backend/src/webhooks.d.ts +113 -0
  65. package/types/internal/src/event.d.ts +9 -0
  66. package/types/internal/src/modules/ai.d.ts +678 -0
  67. package/types/internal/src/modules/analytics.d.ts +1414 -0
  68. package/types/internal/src/modules/auth.d.ts +5660 -0
  69. package/types/internal/src/modules/cache.d.ts +158 -0
  70. package/types/internal/src/modules/events.d.ts +19 -0
  71. package/types/internal/src/modules/flags.d.ts +957 -0
  72. package/types/internal/src/modules/index.d.ts +13 -0
  73. package/types/internal/src/modules/machines.d.ts +1584 -0
  74. package/types/internal/src/modules/notify.d.ts +1450 -0
  75. package/types/internal/src/modules/purchases.d.ts +4113 -0
  76. package/types/internal/src/modules/realtime.d.ts +326 -0
  77. package/types/internal/src/modules/secrets.d.ts +275 -0
  78. package/types/internal/src/modules/storage.d.ts +691 -0
  79. package/types/internal/src/modules/stores.d.ts +1516 -0
  80. package/types/internal/src/retry.d.ts +14 -0
  81. package/types/internal/src/transport.d.ts +106 -0
  82. package/types/server/src/auth/dpop.d.ts +47 -0
  83. package/types/server/src/auth/jwt.d.ts +115 -0
  84. package/types/server/src/index.d.ts +9 -0
  85. package/types/server/src/pages.d.ts +4 -0
  86. package/types/server/src/purchases.d.ts +51 -0
  87. package/types/server/src/secrets-env.d.ts +22 -0
  88. package/types/server/src/server.d.ts +40 -0
  89. package/types/server/src/testing.d.ts +91 -0
  90. package/types/server/src/webhooks.d.ts +72 -0
  91. package/types/web/src/analytics.d.ts +87 -0
  92. package/types/web/src/auth.d.ts +155 -0
  93. package/types/web/src/backend-error.d.ts +38 -0
  94. package/types/web/src/backend-names.d.ts +25 -0
  95. package/types/web/src/backends.d.ts +84 -0
  96. package/types/web/src/client.d.ts +51 -0
  97. package/types/web/src/index.d.ts +13 -0
  98. package/types/web/src/palbe.d.ts +2 -0
  99. package/types/web/src/paywall/icons.d.ts +5 -0
  100. package/types/web/src/paywall/index.d.ts +5 -0
  101. package/types/web/src/paywall/placement.d.ts +85 -0
  102. package/types/web/src/paywall/prices.d.ts +40 -0
  103. package/types/web/src/paywall/render.d.ts +54 -0
  104. package/types/web/src/paywall/resolve.d.ts +149 -0
  105. package/types/web/src/purchases.d.ts +86 -0
  106. package/types/web/src/rooms.d.ts +61 -0
  107. package/types/web/src/session.d.ts +91 -0
  108. package/types/web/src/socket.d.ts +87 -0
  109. package/types/web/src/vite.d.ts +49 -0
@@ -0,0 +1,148 @@
1
+ import { type AppDatabase, type Binding, type Embed, type Plan, type Pool, type Schema, type SearchWorker, type SqlDriver } from 'palgres';
2
+ import { type SchemaDef } from 'palgres/dsl';
3
+ import type { Schemas, Tables } from './db-env.js';
4
+ /** A connection pool: Bun's SQL and postgres.js both are one. */
5
+ export interface SqlPool extends Pool {
6
+ unsafe(sql: string, params?: unknown[]): PromiseLike<unknown>;
7
+ begin<T>(fn: (tx: {
8
+ unsafe(sql: string, params?: unknown[]): PromiseLike<unknown>;
9
+ }) => Promise<T>): Promise<T>;
10
+ }
11
+ /**
12
+ * A database the app reaches: its pool (migrations), the engine's driver over
13
+ * it — what everything the serving app sends goes through: the version's
14
+ * lookup, requests, forgetting a user, cancels — and how it closes.
15
+ */
16
+ export interface DatabaseDriver {
17
+ pool: SqlPool;
18
+ engine: SqlDriver;
19
+ /** The connections the engine's pool holds: what the requests' share and their further transactions' share are cut from. */
20
+ poolSize: number;
21
+ close(): Promise<void>;
22
+ }
23
+ export interface DatabaseConfig {
24
+ /** The app's schemas: each db/*.ts file's defineSchema(…). */
25
+ schemas: SchemaDef | SchemaDef[];
26
+ /** The app's database address (default PALBASE_DB_URL). */
27
+ url?: string;
28
+ /**
29
+ * Who a request is to the database: its caller (default) — authenticated
30
+ * with the token's claims, anon without one — or always the service role,
31
+ * for an operator's app whose tables are all service_role's (Studio).
32
+ */
33
+ requests?: 'caller' | 'service';
34
+ /**
35
+ * How a database is opened (default Bun's SQL; on Node give one: postgres.js
36
+ * with { types: POSTGRES_JS_TYPES }, its engine postgresJsDriver(pool,
37
+ * control)), saying its pool's size (poolSize):
38
+ * a third of it is the requests', two thirds their further transactions'.
39
+ * The default, Bun's, holds what the app may hold (connectionsOf) but the
40
+ * one it cancels with; one given here holds no more either.
41
+ */
42
+ driver?: (url: string) => DatabaseDriver;
43
+ /**
44
+ * What embeds for the tables that declare search — a search's query, and
45
+ * each row's text in the background: texts in, their vectors out, by the
46
+ * model and in the dimensions the table says. Default: the project's ai
47
+ * module (a table's model is "default", or a catalogue embedding model as
48
+ * provider:model), with nothing for the app to write.
49
+ */
50
+ embed?: Embed;
51
+ }
52
+ /**
53
+ * The deploy's steps (start, complete, rollback), and two that change
54
+ * nothing — `palbase db:plan` and `db:status` ask the app's own Palgres what a
55
+ * start would do and what the database's ledger says.
56
+ */
57
+ export type MigrateMode = 'start' | 'complete' | 'rollback' | 'plan' | 'status';
58
+ /** What the deploy asked of this run: a migration step, or nothing (serve). */
59
+ export declare function migrateModeOf(env: Record<string, string | undefined>): MigrateMode | undefined;
60
+ /**
61
+ * The schemas the app's database holds: the app's, and the framework's own
62
+ * (its writes' Idempotency-Keys) — migrated with them, in one version.
63
+ */
64
+ export declare function schemasOf(config: DatabaseConfig): SchemaDef[];
65
+ export declare function modelOf(config: DatabaseConfig): Schema;
66
+ /** Whether a table of the schema declares search: such an app embeds its rows in the background. */
67
+ export declare function searches(model: Schema): boolean;
68
+ /** Whether the schema keeps a user's id anywhere: such an app must hear when a user is deleted. */
69
+ export declare function keepsUsers(model: Schema): boolean;
70
+ /**
71
+ * How many connections the app may hold to its database. On the platform it
72
+ * is told (PALBASE_DB_CONNECTIONS) its share of the database's: the copies of
73
+ * every machine given the database — a new version's beside the one that
74
+ * serves — hold no more than the database has between them, and one that
75
+ * held more left the next version's requests waiting for the connections of
76
+ * the one before (measured 2026-10-07: 31 a copy, 38 in all, 12 s). Told
77
+ * nothing (a laptop, a server of one's own), 31: ten requests' transactions
78
+ * at once and the two further ones each may hold ($asService, $atomic —
79
+ * palgres' admission), and one to cancel with.
80
+ */
81
+ export declare function connectionsOf(env: Record<string, string | undefined>): number;
82
+ /**
83
+ * The admission cut from a pool: a third to requests, two thirds to their
84
+ * further transactions. Cut from the pool's own size — a share larger than
85
+ * the pool would make a request in its first transaction wait in the driver,
86
+ * with no deadline, for a connection only a finished request frees.
87
+ */
88
+ export declare function admissionFor(poolSize: number): {
89
+ concurrency: number;
90
+ nestedConcurrency: number;
91
+ };
92
+ /**
93
+ * How a migration's run ends, which is all the platform reads of it:
94
+ * 0 — done, or nothing to do; 1 — the migration's own verdict: it is refused,
95
+ * or what the tables hold does not fit it, and running it again changes
96
+ * nothing (the deploy stops, and what start began is taken back); 75 — it was
97
+ * cut off: the database unreachable or restarting, a lock not had in time.
98
+ * Run again — the platform does — it picks up where it stopped.
99
+ */
100
+ export type MigrationExit = 0 | 1 | 75;
101
+ /**
102
+ * The deploy's migration step on an app that declares no database: it has
103
+ * nothing to migrate, and began nothing a rollback could take back. A start
104
+ * — or anything else asked of it — is its verdict, said on its last line; a
105
+ * rollback, of what a start of this app began, has nothing to do. A database
106
+ * given to such a machine with its migrations on is refused as its release
107
+ * starts, and the release's rollback then ends — the machine is not held by it.
108
+ */
109
+ export declare function migrateWithoutDatabase(mode: MigrateMode): MigrationExit;
110
+ /** The deploy's migration step. */
111
+ export declare function runMigration(config: DatabaseConfig, env: Record<string, string | undefined>, mode: MigrateMode): Promise<MigrationExit>;
112
+ export interface OpenDatabase {
113
+ model: Schema;
114
+ binding: Binding;
115
+ /** The engine: one transaction per request, as its caller (or the service role). */
116
+ app: AppDatabase<Tables, Schemas>;
117
+ requests: 'caller' | 'service';
118
+ /** The platform's user.deleted: what the app kept of the user goes. */
119
+ forget(userId: string): Promise<{
120
+ deleted: number;
121
+ detached: number;
122
+ }>;
123
+ /** What embeds the rows of the tables that declare search, when the schema has one; not started. */
124
+ search?: SearchWorker;
125
+ close(): Promise<void>;
126
+ }
127
+ /**
128
+ * The app's schema has no version in the database, and the app runs off the
129
+ * platform — on a developer's computer, where no release is coming to
130
+ * migrate it: said at once, with what a push would do.
131
+ */
132
+ export declare class SchemaNotPushed extends Error {
133
+ readonly digest: string;
134
+ readonly plan: Plan | undefined;
135
+ constructor(digest: string, plan: Plan | undefined);
136
+ }
137
+ /** A plan as palbase db:plan says it: from what version to what, each change, each refusal. */
138
+ export declare function planLines(p: Plan): string[];
139
+ /**
140
+ * The app's connection, bound to its version. On the platform a start may
141
+ * still be finishing when the first instance boots: the version is waited
142
+ * for up to a minute. Off it none is coming, and a schema with no version is
143
+ * the developer's to push (SchemaNotPushed).
144
+ */
145
+ export declare function openDatabase(config: DatabaseConfig, env: Record<string, string | undefined>, secretKey: string, where: {
146
+ onPlatform: boolean; /** What embeds when the app names no embedder: the project's ai module. */
147
+ embed: Embed;
148
+ }): Promise<OpenDatabase>;
@@ -0,0 +1,4 @@
1
+ export interface Tables {
2
+ }
3
+ export interface Schemas {
4
+ }
@@ -0,0 +1,293 @@
1
+ import 'reflect-metadata';
2
+ import type { EventType, PalbaseEvent } from '../../server/src/index.js';
3
+ import type { UserT } from './engine.js';
4
+ import type { UploadConfig } from './upload.js';
5
+ /** A validator the framework runs: any Standard Schema (zod, valibot, arktype, …). */
6
+ export interface StandardSchemaV1<Input = unknown, Output = Input> {
7
+ readonly '~standard': {
8
+ readonly version: 1;
9
+ readonly vendor: string;
10
+ readonly validate: (value: unknown) => StandardResult<Output> | Promise<StandardResult<Output>>;
11
+ readonly types?: {
12
+ readonly input: Input;
13
+ readonly output: Output;
14
+ };
15
+ };
16
+ }
17
+ export type StandardResult<Output> = {
18
+ readonly value: Output;
19
+ readonly issues?: undefined;
20
+ } | {
21
+ readonly issues: ReadonlyArray<{
22
+ readonly message: string;
23
+ readonly path?: ReadonlyArray<PropertyKey | {
24
+ readonly key: PropertyKey;
25
+ }>;
26
+ }>;
27
+ };
28
+ export interface AuthConfig {
29
+ required: boolean;
30
+ verifiedEmail?: boolean;
31
+ /** v2 read these from its database; v3 has none yet: refused at boot. */
32
+ role?: string;
33
+ permission?: string;
34
+ }
35
+ export type AuthSpec = boolean | Partial<AuthConfig>;
36
+ export interface RateLimitConfig {
37
+ max: number;
38
+ /** Seconds. */
39
+ window: number;
40
+ }
41
+ export interface ControllerOptions {
42
+ auth?: AuthSpec;
43
+ }
44
+ export interface RouteOptions {
45
+ auth?: AuthSpec;
46
+ /** A limit for this route; false for none. */
47
+ rateLimit?: RateLimitConfig | false;
48
+ }
49
+ export type ParamKind = 'body' | 'query' | 'param' | 'user' | 'optionalUser' | 'headers' | 'req' | 'requestId' | 'client' | 'resolve' | 'uploadedObject';
50
+ export interface ParamMeta {
51
+ index: number;
52
+ kind: ParamKind;
53
+ schema?: StandardSchemaV1;
54
+ name?: string;
55
+ /** @Resolve's: the provider that makes the value, and what the decorator passed it. */
56
+ resolver?: abstract new (...args: never[]) => ParamResolver<unknown, unknown>;
57
+ options?: unknown;
58
+ }
59
+ export interface RouteMeta {
60
+ method: string;
61
+ subpath: string;
62
+ fn: string;
63
+ options: RouteOptions;
64
+ /** @Upload's: the route takes a file, through storage, into this bucket and path. */
65
+ upload?: UploadConfig;
66
+ }
67
+ export interface ControllerMeta {
68
+ basePath: string;
69
+ options: ControllerOptions;
70
+ }
71
+ /** A class as the container knows it, as v2 named the type: `providers: [BillingService as Token]`. */
72
+ export type Token<T = unknown> = abstract new (...args: never[]) => T;
73
+ export interface ModuleDef {
74
+ imports?: Function[];
75
+ controllers?: Function[];
76
+ providers?: Function[];
77
+ exports?: Function[];
78
+ }
79
+ export declare const CONTROLLER: unique symbol;
80
+ export declare const ROUTES: unique symbol;
81
+ export declare const PARAMS: unique symbol;
82
+ export declare const INJECTABLE: unique symbol;
83
+ export declare const MODULE: unique symbol;
84
+ export declare const EVENTS: unique symbol;
85
+ export declare const WEBHOOK: unique symbol;
86
+ export declare const JOBS: unique symbol;
87
+ export declare function declared(): Set<Function>;
88
+ export declare const RESERVED: Set<string>;
89
+ export declare function Controller(basePath: string, options?: ControllerOptions): ClassDecorator;
90
+ export declare const Get: (subpath: string, options?: RouteOptions) => MethodDecorator;
91
+ export declare const Post: (subpath: string, options?: RouteOptions) => MethodDecorator;
92
+ export declare const Put: (subpath: string, options?: RouteOptions) => MethodDecorator;
93
+ export declare const Patch: (subpath: string, options?: RouteOptions) => MethodDecorator;
94
+ export declare const Delete: (subpath: string, options?: RouteOptions) => MethodDecorator;
95
+ /** The HTTP QUERY method (its input is read with @Body). */
96
+ export declare const Query: (subpath: string, options?: RouteOptions) => MethodDecorator;
97
+ export declare const Body: (schema: StandardSchemaV1) => ParameterDecorator;
98
+ export declare const QueryParams: (schema: StandardSchemaV1) => ParameterDecorator;
99
+ export declare const Headers: (schema?: StandardSchemaV1) => ParameterDecorator;
100
+ export declare const Param: (name: string) => ParameterDecorator;
101
+ export declare const User: () => ParameterDecorator;
102
+ export declare const OptionalUser: () => ParameterDecorator;
103
+ export declare const Req: () => ParameterDecorator;
104
+ export declare const RequestId: () => ParameterDecorator;
105
+ export declare const TraceId: () => ParameterDecorator;
106
+ export declare const Client: () => ParameterDecorator;
107
+ /** What a resolver is given of the request: what the route's own decorators read, before its body is. */
108
+ export interface ResolveContext {
109
+ /** The path's parameters, decoded: {ref} of /environments/{ref}. */
110
+ readonly params: Readonly<Record<string, string>>;
111
+ /** The signed-in user; null on a route that does not need one and was called by nobody. */
112
+ readonly user: UserT | null;
113
+ readonly headers: globalThis.Headers;
114
+ readonly requestId: string;
115
+ }
116
+ /**
117
+ * A provider that makes a route's parameter from its request — the team, the
118
+ * environment, the account the path names, with the caller's right to it
119
+ * checked — for every route that asks with @Resolve. Its refusal (a NotFound,
120
+ * a Forbidden) is the request's answer, given before the body is read
121
+ * against its schema.
122
+ */
123
+ export interface ParamResolver<T = unknown, O = undefined> {
124
+ resolve(context: ResolveContext, options: O): T | Promise<T>;
125
+ }
126
+ /**
127
+ * A parameter the app makes: `resolver` (an @Injectable its module lists, and
128
+ * the controller's module reaches as it would a constructor's dependency)
129
+ * resolves it, with `options`, in the request's transaction — what it reads
130
+ * and what the handler then does are one transaction. An app names its own:
131
+ *
132
+ * export const CurrentTeam = (role?: TeamRole) => Resolve(TeamOfRequest, role);
133
+ * remove(@CurrentTeam("owner") team: Team, @Param("id") id: string) { … }
134
+ */
135
+ export declare function Resolve<T, O>(resolver: abstract new (...args: never[]) => ParamResolver<T, O>, ...options: undefined extends O ? [options?: O] : [options: O]): ParameterDecorator;
136
+ export declare function Injectable(): ClassDecorator;
137
+ export declare function Module(def: ModuleDef): ClassDecorator;
138
+ export declare const PLATFORM_EVENT: RegExp;
139
+ /**
140
+ * What a method under @On(event) may be. For a type the modules' contracts
141
+ * declare (x-palbase-events: purchases.entitlement.changed, …) it takes
142
+ * PalbaseEvent's member of that type — its data as the contract says — and
143
+ * the delivery's meta: a handler that reads another payload is a compile
144
+ * error. For any other type (user.deleted, a third party's in a @Webhook
145
+ * class) any method, as before.
146
+ */
147
+ export type EventHandler<E extends string> = E extends EventType ? (event: Extract<PalbaseEvent, {
148
+ type: E;
149
+ }>, meta: {
150
+ requestId: string;
151
+ }) => unknown : (...args: never[]) => unknown;
152
+ export interface OnOptions {
153
+ /**
154
+ * A storage event's (file.uploaded, file.deleted, file.moved): the bucket
155
+ * whose objects it takes — the others' events are not run here, nor sent to
156
+ * the machine when no handler of their type takes every bucket. Left out,
157
+ * every bucket's.
158
+ */
159
+ bucket?: string;
160
+ }
161
+ /** One @On: the event it takes, its method, and the bucket a storage event's is kept to. */
162
+ export interface EventDeclaration {
163
+ event: string;
164
+ fn: string;
165
+ bucket?: string;
166
+ }
167
+ /** A storage event: of an object, in a bucket — what @On's bucket keeps a handler to. */
168
+ export declare const isFileEvent: (event: string) => boolean;
169
+ /**
170
+ * A handler of an event. In a @Webhook class it takes one of that sender's
171
+ * events, by any name the sender gives it (invoice.paid,
172
+ * pull_request.opened, PING) — "" included, as in v2: the deliveries that
173
+ * name no type, Twilio's plain incoming message or call, Slack's slash
174
+ * command. On any other class it takes one of the platform's (user.deleted,
175
+ * …), and the class is a provider its module lists. A method's decorator
176
+ * runs before its class's, so which of the two it is — and so whether a
177
+ * platform event's name is one — is told at boot. Any name is taken; the
178
+ * types the modules' contracts declare (EventType) are the ones an editor
179
+ * offers, and each types its handler (EventHandler).
180
+ */
181
+ export declare function On<E extends EventType | (string & {})>(event: E, options?: OnOptions): <F extends EventHandler<E>>(target: object, key: string | symbol, descriptor: TypedPropertyDescriptor<F>) => void;
182
+ export interface JobOptions {
183
+ /**
184
+ * What its runs are kept under (Studio, the CLI) and the platform calls it
185
+ * by: letters, digits, '.', '_' and '-', at most 64. Renaming it starts a
186
+ * new job.
187
+ */
188
+ name: string;
189
+ /** Five fields of cron's — minute hour day-of-month month day-of-week — in UTC, like "0 3 * * *". */
190
+ schedule: string;
191
+ /** Seconds one run may take before it is a failed attempt: 1–300 (30). */
192
+ timeout?: number;
193
+ /** Attempts after a failed one, 10 s, 20 s, … five minutes apart: 0–10 (5). */
194
+ retry?: number;
195
+ }
196
+ export interface JobDeclaration {
197
+ name: string;
198
+ schedule: string;
199
+ timeout: number;
200
+ retries: number;
201
+ }
202
+ /** One run of a job, as the platform calls it: what its run(meta) is given. */
203
+ export interface JobMeta {
204
+ /** The app's environment variables, as it started (its keys left out). */
205
+ env: Record<string, string>;
206
+ /** The project the app belongs to (its id). */
207
+ environmentId: string;
208
+ job: string;
209
+ /** The run's id: the same for each attempt of one run — what a job keys its work on to do it once. */
210
+ runId: string;
211
+ /** The minute it was due. */
212
+ scheduledAt: Date;
213
+ /** 1 for the first, then one more each time a failed attempt is made again. */
214
+ attempt: number;
215
+ }
216
+ /**
217
+ * A provider the platform runs on `schedule` — five fields of cron's, in UTC
218
+ * — by calling its run(meta), on the machine: woken for it if asleep, one of
219
+ * its copies called, one run of a job at a time. A throw is a failed attempt,
220
+ * made again; a return is done. It runs in a transaction of its own, as the
221
+ * service role: no one called it but the app. Off the platform nothing calls
222
+ * it. Its module lists it in its providers, as any other.
223
+ */
224
+ export declare function Job(options: JobOptions): ClassDecorator;
225
+ /** The senders whose signing scheme is known by name. */
226
+ export type WebhookProvider = 'stripe' | 'github' | 'twilio' | 'sendgrid' | 'slack' | 'discord' | 'livekit';
227
+ /** A signature scheme spelled out, for a sender with no preset: an HMAC of `signs`, in `header`. */
228
+ export interface SignatureSpec {
229
+ /** The header carrying the signature. */
230
+ header: string;
231
+ /** Stripped before comparison (e.g. `sha256=`); a header without it is refused. */
232
+ prefix?: string;
233
+ algo: 'hmac-sha256' | 'hmac-sha1';
234
+ encoding: 'hex' | 'base64';
235
+ /** What the HMAC covers: `{body}` (the bytes as they arrived), `{ts}`, and literal text. `{ts}` needs `timestampHeader`, and brings the five-minute replay window — without it a captured delivery verifies whenever it is sent again. */
236
+ signs: string;
237
+ timestampHeader?: string;
238
+ }
239
+ export interface WebhookOptions {
240
+ /** The last segment of its URL, /webhooks/<name>: what the sender is configured with. */
241
+ name: string;
242
+ provider?: WebhookProvider;
243
+ signature?: SignatureSpec;
244
+ /** The env var holding the signing secret (for Discord and SendGrid, their public key): its name, never its value. */
245
+ secret: {
246
+ env: string;
247
+ };
248
+ }
249
+ export interface WebhookDeclaration extends WebhookOptions {
250
+ /** Each @On of the class: the event it takes, and its method. */
251
+ events: {
252
+ event: string;
253
+ fn: string;
254
+ }[];
255
+ }
256
+ /**
257
+ * A class whose @On methods take a third party's deliveries, POSTed to
258
+ * /webhooks/<name>: each one verified — with the provider's scheme, or the
259
+ * signature spelled out — under the secret its env var holds, then handed to
260
+ * the @On of its event. The class is a provider its module lists, its
261
+ * constructor injected as a controller's. Every misuse is refused here: a
262
+ * webhook that verifies nothing, or answers 200 and runs nothing, is not one.
263
+ *
264
+ * A handler is idempotent. Senders deliver at least once, and a delivery the
265
+ * app has run is not run again by its sender's id — but that memory is this
266
+ * copy's alone and lives in its memory: another copy of the app, or this one
267
+ * started again, runs it again. And not every delivery can be told from one
268
+ * sent again by someone else. Stripe, Slack, Discord, LiveKit and SendGrid
269
+ * sign a time and an id: a captured delivery is refused once five minutes
270
+ * have passed, and not run twice before. GitHub and Twilio sign neither —
271
+ * whoever holds one of their deliveries, from a log or a proxy, can send it
272
+ * again at any time, under a fresh delivery id, and it is run again; what is
273
+ * told apart is their own retry. A handler of theirs keys its work on the
274
+ * event's own ids (a MessageSid, a payload's ids), and checks what it decides
275
+ * on: GitHub's event name is an unsigned header, and Twilio's signature
276
+ * does not delimit its parameters (webhooks.ts). A signature spelled out
277
+ * without {ts} is replayed as theirs, and names no id.
278
+ */
279
+ export declare function Webhook(options: WebhookOptions): ClassDecorator;
280
+ export declare function metaOf<T>(target: Function, key: symbol): T | undefined;
281
+ /** The controller classes this process declared, in the order they were declared — v2's getRegisteredControllers(). */
282
+ export declare function getRegisteredControllers(): readonly Function[];
283
+ /** A route as v2 described one: its method, its path under the controller's, its method's name, its options and an upload's config. */
284
+ export interface RouteInfo {
285
+ method: string;
286
+ path: string;
287
+ fnName: string;
288
+ options: RouteOptions & {
289
+ uploadConfig?: UploadConfig;
290
+ };
291
+ }
292
+ /** A controller's routes, as v2's getRoutes(ctor) listed them. */
293
+ export declare function getRoutes(ctor: Function): RouteInfo[];
@@ -0,0 +1,24 @@
1
+ import { type DatabaseDiagnosticsOptions, type DBClient, type SqlDriver } from 'palgres';
2
+ import type { SchemaDef } from 'palgres/dsl';
3
+ export type { SqlDriver };
4
+ export interface RequestDatabaseIdentity {
5
+ /** The database role the caller's work runs as. */
6
+ role: string;
7
+ /** The role $asService() runs as. */
8
+ serviceRole: string;
9
+ /** The caller's claims, as JSON: what auth.uid() and policies read. */
10
+ claimsJson: string;
11
+ }
12
+ export interface RequestDatabaseOptions {
13
+ /** The app's schema files' default exports: what each call is compiled against. */
14
+ schemas: readonly SchemaDef[];
15
+ diagnostics?: DatabaseDiagnosticsOptions;
16
+ signal?: AbortSignal;
17
+ }
18
+ export interface RequestDatabaseHandle {
19
+ /** The string-keyed client: what withServices({ Database }) takes. */
20
+ client: DBClient;
21
+ commit(): Promise<void>;
22
+ rollback(reason: unknown): Promise<void>;
23
+ }
24
+ export declare function createRequestDatabase(sql: SqlDriver, identity: RequestDatabaseIdentity, options?: RequestDatabaseOptions): RequestDatabaseHandle;
@@ -0,0 +1,3 @@
1
+ export { makeMemoryCache, type MemoryCacheOptions } from './cache.js';
2
+ export { createApp, type App, type AppOptions } from './engine.js';
3
+ export { createRequestDatabase, type RequestDatabaseHandle, type RequestDatabaseIdentity, type RequestDatabaseOptions, type SqlDriver } from './engine-db.js';
@@ -0,0 +1,124 @@
1
+ import { type PalbaseServer, type TokenUser, type TokenVerifier, type WebhookEvent } from '../../server/src/index.js';
2
+ import { type AuthSpec, type RateLimitConfig } from './decorators.js';
3
+ import { type ContractOptions } from './openapi.js';
4
+ import type { RequestTelemetry } from './requests.js';
5
+ /** The signed-in user, as @User() gives it. */
6
+ export interface UserT {
7
+ id: string;
8
+ email: string | null;
9
+ role: string | null;
10
+ emailVerified: boolean;
11
+ metadata: Record<string, unknown>;
12
+ sessionId: string | null;
13
+ /** A test user of the project (made with its secret key; the token says test: true). */
14
+ test: boolean;
15
+ }
16
+ export interface ClientInfo {
17
+ sdkVersion: string | null;
18
+ appVersion: string | null;
19
+ platform: string | null;
20
+ osVersion: string | null;
21
+ }
22
+ /** Browsers on these origins may call the app (preflight answered, CORS headers sent). */
23
+ export interface CorsOptions {
24
+ /** Exact origins (https://app.example.com), or "*" for any. */
25
+ origins: string[] | '*';
26
+ /** Seconds a browser keeps a preflight's answer (default 600). */
27
+ maxAge?: number;
28
+ }
29
+ /**
30
+ * An app's own API tokens (a CI's, an agent's). A bearer that is no JWT and
31
+ * that accepts takes — told without the database: its shape, and a check of
32
+ * its own the app can make (a tag only it can sign) — is one of them: resolve
33
+ * says who it stands for, in a transaction of its own as a delivery's is, or
34
+ * null. Any other bearer is a Palbase session's, and the app is never asked.
35
+ */
36
+ export interface AppTokens {
37
+ accepts(token: string): boolean;
38
+ resolve(token: string): Promise<TokenUser | null>;
39
+ }
40
+ export interface AppOptions {
41
+ modules: Function[];
42
+ verifier: TokenVerifier;
43
+ server: PalbaseServer;
44
+ /** Required when a provider handles events (@On). */
45
+ webhookSecret?: string;
46
+ /** Where each @Webhook's secret is read (secret: { env }) — process.env unless given — and its handlers' meta.env. */
47
+ env?: Record<string, string | undefined>;
48
+ /** The project's id: a @Webhook handler's meta.environmentId. */
49
+ projectId?: string;
50
+ /** Each answered request's log line and analytics trace; none when not given. */
51
+ telemetry?: RequestTelemetry;
52
+ now?: () => number;
53
+ maxBodyBytes?: number;
54
+ /** Every route's auth unless it or its controller says otherwise (default: required). */
55
+ auth?: AuthSpec;
56
+ /** The app's own API tokens (a CI's, an agent's): AppTokens. */
57
+ tokens?: AppTokens;
58
+ /** Every route's limit unless it says otherwise (default 300 a minute per caller); false for none. */
59
+ rateLimit?: RateLimitConfig | false;
60
+ /** Off unless given: a backend called from browsers names their origins. */
61
+ cors?: CorsOptions;
62
+ /** The framework's own handlers of the platform's events (user.deleted → forget), run before the app's @On. */
63
+ events?: Record<string, ((event: WebhookEvent) => Promise<unknown>)[]>;
64
+ /** The app's database: each request (and each delivery) one transaction, as its caller or as the service role. */
65
+ database?: {
66
+ app: RequestDatabases;
67
+ requests: 'caller' | 'service';
68
+ };
69
+ /**
70
+ * Built to write its contract and nothing else (PALBASE_CONTRACT_OUT): its
71
+ * webhooks' secrets are not read, and whether it declares a database says
72
+ * what the contract says of its callers.
73
+ */
74
+ contractOnly?: {
75
+ database: boolean;
76
+ routes?: ContractOptions['routes'];
77
+ errors?: ContractOptions['errors'];
78
+ };
79
+ /**
80
+ * The platform's calls to the machine, for the platform alone: each
81
+ * carries Palbase-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256(secret,
82
+ * "<t>." + body)>, t at most five minutes from now; every other call is
83
+ * 401 invalid_signature, its error_description saying which of
84
+ * missing_signature, malformed_signature, stale_timestamp or
85
+ * signature_mismatch. The factory gives it the machine's
86
+ * PALBASE_PLATFORM_SECRET; unset here, none is served (404). They are:
87
+ * - the app's @Job classes: GET /_palbase/jobs lists them (the platform asks
88
+ * once a version is up), POST /_palbase/jobs/<name> runs one — its body
89
+ * {job, run_id, scheduled_at, attempt} is what the signature covers —
90
+ * and answers 204, or the failure the platform tries again;
91
+ * - its @Upload routes' two steps, from storage (upload.ts), and its
92
+ * @Room classes' joins, messages and moments, from realtime (rooms.ts):
93
+ * platform-calls.ts.
94
+ */
95
+ platform?: {
96
+ secret: string;
97
+ };
98
+ }
99
+ /** What the engine needs of Palgres' AppDatabase: a request's database, and its end. */
100
+ export interface RequestDatabases {
101
+ request(identity: {
102
+ role: 'anon' | 'authenticated' | 'service';
103
+ claims: Record<string, unknown>;
104
+ }, options?: {
105
+ signal?: AbortSignal;
106
+ heldIdle?: (waitedMs: number) => void;
107
+ }): {
108
+ database: object;
109
+ commit(): Promise<void>;
110
+ rollback(reason: unknown): Promise<void>;
111
+ };
112
+ }
113
+ export interface App {
114
+ handle(request: Request, info?: {
115
+ remoteAddress?: string;
116
+ }): Promise<Response>;
117
+ start(): Promise<void>;
118
+ stop(): Promise<void>;
119
+ readonly routes: readonly string[];
120
+ /** The app's contract (OpenAPI): what its routes answer and throw, as written for its build. */
121
+ contract(): unknown;
122
+ }
123
+ export declare const MAX_BODY_BYTES: number;
124
+ export declare function createApp(opts: AppOptions): App;
@@ -0,0 +1,27 @@
1
+ export interface PalbaseConnection {
2
+ /** https://<ref>.<domain> — PALBASE_URL. */
3
+ url?: string;
4
+ /** The project's id — PALBASE_PROJECT_ID. */
5
+ projectId?: string;
6
+ /** The project's secret key — PALBASE_SECRET_KEY, from the project's Secrets. */
7
+ secretKey?: string;
8
+ /** The webhook endpoint's secret — PALBASE_WEBHOOK_SECRET; needed on the platform when the app handles events (@On) or keeps users. */
9
+ webhookSecret?: string;
10
+ /**
11
+ * The key the platform signs its own calls to this machine with —
12
+ * PALBASE_PLATFORM_SECRET, which the platform starts every machine with.
13
+ * Off the platform there is none, and what only the platform calls (the
14
+ * app's jobs, /_palbase/jobs) is not served.
15
+ */
16
+ platformSecret?: string;
17
+ }
18
+ export interface Connection {
19
+ url: string;
20
+ projectId: string;
21
+ secretKey: string;
22
+ webhookSecret?: string;
23
+ platformSecret?: string;
24
+ }
25
+ export declare function connection(given: PalbaseConnection, env: Record<string, string | undefined>, opts: {
26
+ needsWebhookSecret: boolean;
27
+ }): Connection;