@palbase/backend 8.6.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-WORMYTK3.js → chunk-SO6YV35D.js} +5 -1
- package/dist/chunk-SO6YV35D.js.map +1 -0
- package/dist/db/index.cjs +4 -0
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +2 -2
- package/dist/db/index.d.ts +2 -2
- package/dist/db/index.js +1 -1
- package/dist/{endpoint-fW9yWla0.d.cts → endpoint-CGI5cZMg.d.cts} +1 -1
- package/dist/{endpoint-fW9yWla0.d.ts → endpoint-CGI5cZMg.d.ts} +1 -1
- package/dist/{index-CFD7RDMY.d.ts → index--dH5i4Nu.d.ts} +2 -2
- package/dist/{index-B0KV1w1a.d.cts → index-osv2jMmZ.d.cts} +2 -2
- package/dist/index.cjs +8 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -9
- package/dist/index.d.ts +12 -9
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/test/index.d.cts +1 -1
- package/dist/test/index.d.ts +1 -1
- package/docs/README.md +1 -1
- package/docs/endpoints.md +24 -7
- package/docs/getting-started.md +3 -3
- package/docs/llms-full.txt +89 -20
- package/docs/llms.txt +1 -1
- package/docs/routing.md +10 -6
- package/package.json +2 -2
- package/dist/chunk-WORMYTK3.js.map +0 -1
package/dist/test/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as DBClient, P as PBRequest, a as PalbaseModuleClients, L as Logger, C as CacheClient, Q as QueueClient, U as User } from '../endpoint-
|
|
1
|
+
import { D as DBClient, P as PBRequest, a as PalbaseModuleClients, L as Logger, C as CacheClient, Q as QueueClient, U as User } from '../endpoint-CGI5cZMg.cjs';
|
|
2
2
|
import 'zod';
|
|
3
3
|
|
|
4
4
|
/** Mock DB client with tracking and seed data support. */
|
package/dist/test/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as DBClient, P as PBRequest, a as PalbaseModuleClients, L as Logger, C as CacheClient, Q as QueueClient, U as User } from '../endpoint-
|
|
1
|
+
import { D as DBClient, P as PBRequest, a as PalbaseModuleClients, L as Logger, C as CacheClient, Q as QueueClient, U as User } from '../endpoint-CGI5cZMg.js';
|
|
2
2
|
import 'zod';
|
|
3
3
|
|
|
4
4
|
/** Mock DB client with tracking and seed data support. */
|
package/docs/README.md
CHANGED
|
@@ -212,7 +212,7 @@ The **only difference** is the trigger argument:
|
|
|
212
212
|
|
|
213
213
|
| You are writing… | Handler signature | Trigger arg |
|
|
214
214
|
|------------------|-------------------|-------------|
|
|
215
|
-
| **Endpoints** (`controllers/` class controllers) | method `(…params)` | parameter decorators `@Body`/`@
|
|
215
|
+
| **Endpoints** (`controllers/` class controllers) | method `(…params)` | parameter decorators `@Body`/`@QueryParams`/`@Param`/`@User`/… — [endpoints.md](./endpoints.md) |
|
|
216
216
|
| **Workers** (`workers/**`) | `(payload, meta)` | typed payload + `WorkerMeta` |
|
|
217
217
|
| **Jobs** (`jobs/**`) | `(meta)` | `JobMeta` |
|
|
218
218
|
| **Hooks** (`hooks/**`) | `(event, meta)` | typed event + `HookMeta` |
|
package/docs/endpoints.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
An endpoint is a **method on a class controller** — a class decorated with
|
|
4
4
|
`@Controller(basePath)` whose methods are decorated with `@Get`/`@Post`/`@Put`/
|
|
5
|
-
`@Patch`/`@Delete`. Controller files live under `controllers/` and are
|
|
6
|
-
automatically (see [routing.md](./routing.md)). Request input + context
|
|
7
|
-
injected into the method via **parameter decorators** (`@Body`/`@
|
|
8
|
-
`@User`/…), each piece direct — no `req` god-object. Services are reached via the
|
|
5
|
+
`@Patch`/`@Delete`/`@Query`. Controller files live under `controllers/` and are
|
|
6
|
+
mounted automatically (see [routing.md](./routing.md)). Request input + context
|
|
7
|
+
are injected into the method via **parameter decorators** (`@Body`/`@QueryParams`/
|
|
8
|
+
`@Param`/`@User`/…), each piece direct — no `req` god-object. Services are reached via the
|
|
9
9
|
imported singletons (see [services.md](./services.md)).
|
|
10
10
|
|
|
11
11
|
## A controller
|
|
@@ -49,7 +49,7 @@ export default class RoomsController {
|
|
|
49
49
|
|
|
50
50
|
## Method decorators
|
|
51
51
|
|
|
52
|
-
`@Get`/`@Post`/`@Put`/`@Patch`/`@Delete` take `(subpath, options?)`:
|
|
52
|
+
`@Get`/`@Post`/`@Put`/`@Patch`/`@Delete`/`@Query` take `(subpath, options?)`:
|
|
53
53
|
|
|
54
54
|
```ts
|
|
55
55
|
interface RouteOptions {
|
|
@@ -62,12 +62,29 @@ Input and output are NOT method-decorator options: input comes from the
|
|
|
62
62
|
parameter decorators, output from the method's return type. The method decorator
|
|
63
63
|
only carries route concerns (path, auth, rateLimit).
|
|
64
64
|
|
|
65
|
+
### `@Query` — the HTTP QUERY method (RFC 10008)
|
|
66
|
+
|
|
67
|
+
`@Query(subpath, options?)` declares an HTTP **QUERY** route: safe + idempotent
|
|
68
|
+
like GET, body-carrying like POST. Use it for reads whose input is too rich for
|
|
69
|
+
a query string — complex search/filter endpoints that would otherwise be a
|
|
70
|
+
"fake-read" POST. The input rides `@Body(schema)`:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
@Query("/search")
|
|
74
|
+
async search(@Body(SearchTodosBody) body: SearchTodosBody): Promise<TodoSchema[]> {
|
|
75
|
+
return todoService.search(body);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Do NOT combine `@Body` with `@QueryParams` on a QUERY route — the body IS the
|
|
80
|
+
query (RFC semantics), so a QUERY route carries its whole input there.
|
|
81
|
+
|
|
65
82
|
## Parameter decorators
|
|
66
83
|
|
|
67
84
|
| Decorator | Injects | Typed by | OpenAPI |
|
|
68
85
|
|---|---|---|---|
|
|
69
86
|
| `@Body(schema)` | request body | `: T` (`z.infer<schema>`, same name) | requestBody |
|
|
70
|
-
| `@
|
|
87
|
+
| `@QueryParams(schema)` | query params | `: T` | query parameters |
|
|
71
88
|
| `@Param("id")` | one path param | `: string` | path parameter |
|
|
72
89
|
| `@Headers(schema?)` | request headers | `Record<string,string>` (+ typed with schema) | header parameters |
|
|
73
90
|
| `@User()` | authenticated user | `: UserT` | — (from token) |
|
|
@@ -76,7 +93,7 @@ only carries route concerns (path, auth, rateLimit).
|
|
|
76
93
|
| `@RequestId()` / `@TraceId()` | correlation ids | `: string` | — |
|
|
77
94
|
| `@Req()` | raw request (escape hatch) | `: PBRequest` | — |
|
|
78
95
|
|
|
79
|
-
`@Body`/`@
|
|
96
|
+
`@Body`/`@QueryParams`/`@Headers` take a zod schema (validation + codegen source); the
|
|
80
97
|
developer writes the matching type annotation (same name). `UserT` is the
|
|
81
98
|
authenticated-user type (`{ id: string; email?: string; role: string; metadata:
|
|
82
99
|
Record<string, unknown> }`) — exported under the alias `UserT` because the value
|
package/docs/getting-started.md
CHANGED
|
@@ -69,7 +69,7 @@ export type HelloResponse = z.infer<typeof HelloResponse>;
|
|
|
69
69
|
Then write the controller in `controllers/hello.controller.ts`:
|
|
70
70
|
|
|
71
71
|
```ts
|
|
72
|
-
import { Controller, Get,
|
|
72
|
+
import { Controller, Get, QueryParams, OptionalUser } from "@palbase/backend";
|
|
73
73
|
import type { UserT } from "@palbase/backend";
|
|
74
74
|
import { GreetQuery } from "../models/hello/greet.js";
|
|
75
75
|
import type { HelloResponse } from "../models/hello/greet.js"; // the return TYPE names the 200 schema
|
|
@@ -77,13 +77,13 @@ import type { HelloResponse } from "../models/hello/greet.js"; // the return TY
|
|
|
77
77
|
@Controller("/hello", { auth: false })
|
|
78
78
|
export default class HelloController {
|
|
79
79
|
@Get("")
|
|
80
|
-
greet(@
|
|
80
|
+
greet(@QueryParams(GreetQuery) q: GreetQuery, @OptionalUser() user: UserT | null): HelloResponse {
|
|
81
81
|
return { message: `hello, ${q.name ?? "world"}!`, user: user?.id ?? null };
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
This is served at `GET /hello`. The `@
|
|
86
|
+
This is served at `GET /hello`. The `@QueryParams` schema validates the query string;
|
|
87
87
|
the method's RETURN TYPE (`: HelloResponse`) names the response schema — codegen
|
|
88
88
|
+ the runtime read it to validate and describe the 200 response. There is no
|
|
89
89
|
`@Returns` decorator; a body route with no named return type is a build error.
|
package/docs/llms-full.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Palbase Backend SDK (`@palbase/backend`)
|
|
2
2
|
|
|
3
|
-
> TypeScript backend SDK. NestJS-style class controllers: `@Controller` classes with `@Get`/`@Post`/… methods and `@Body`/`@
|
|
3
|
+
> TypeScript backend SDK. NestJS-style class controllers: `@Controller` classes with `@Get`/`@Post`/`@Query`/… methods and `@Body`/`@QueryParams`/`@Param`/`@User` parameter decorators. All handler types import service singletons (`Database`, `Cache`, …). Trigger arg differs by type: endpoints use parameter decorators, workers `(payload, meta)`, jobs `(meta)`, hooks/webhooks `(event, meta)`. Middleware is the one exception (`ctx`). Not Express, not Supabase Edge Functions.
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
@@ -220,7 +220,7 @@ The **only difference** is the trigger argument:
|
|
|
220
220
|
|
|
221
221
|
| You are writing… | Handler signature | Trigger arg |
|
|
222
222
|
|------------------|-------------------|-------------|
|
|
223
|
-
| **Endpoints** (`controllers/` class controllers) | method `(…params)` | parameter decorators `@Body`/`@
|
|
223
|
+
| **Endpoints** (`controllers/` class controllers) | method `(…params)` | parameter decorators `@Body`/`@QueryParams`/`@Param`/`@User`/… — [endpoints.md](./endpoints.md) |
|
|
224
224
|
| **Workers** (`workers/**`) | `(payload, meta)` | typed payload + `WorkerMeta` |
|
|
225
225
|
| **Jobs** (`jobs/**`) | `(meta)` | `JobMeta` |
|
|
226
226
|
| **Hooks** (`hooks/**`) | `(event, meta)` | typed event + `HookMeta` |
|
|
@@ -353,7 +353,7 @@ export type HelloResponse = z.infer<typeof HelloResponse>;
|
|
|
353
353
|
Then write the controller in `controllers/hello.controller.ts`:
|
|
354
354
|
|
|
355
355
|
```ts
|
|
356
|
-
import { Controller, Get,
|
|
356
|
+
import { Controller, Get, QueryParams, OptionalUser } from "@palbase/backend";
|
|
357
357
|
import type { UserT } from "@palbase/backend";
|
|
358
358
|
import { GreetQuery } from "../models/hello/greet.js";
|
|
359
359
|
import type { HelloResponse } from "../models/hello/greet.js"; // the return TYPE names the 200 schema
|
|
@@ -361,13 +361,13 @@ import type { HelloResponse } from "../models/hello/greet.js"; // the return TY
|
|
|
361
361
|
@Controller("/hello", { auth: false })
|
|
362
362
|
export default class HelloController {
|
|
363
363
|
@Get("")
|
|
364
|
-
greet(@
|
|
364
|
+
greet(@QueryParams(GreetQuery) q: GreetQuery, @OptionalUser() user: UserT | null): HelloResponse {
|
|
365
365
|
return { message: `hello, ${q.name ?? "world"}!`, user: user?.id ?? null };
|
|
366
366
|
}
|
|
367
367
|
}
|
|
368
368
|
```
|
|
369
369
|
|
|
370
|
-
This is served at `GET /hello`. The `@
|
|
370
|
+
This is served at `GET /hello`. The `@QueryParams` schema validates the query string;
|
|
371
371
|
the method's RETURN TYPE (`: HelloResponse`) names the response schema — codegen
|
|
372
372
|
+ the runtime read it to validate and describe the 200 response. There is no
|
|
373
373
|
`@Returns` decorator; a body route with no named return type is a build error.
|
|
@@ -394,13 +394,13 @@ class-controller model.
|
|
|
394
394
|
|
|
395
395
|
Routes are declared in code with **class controllers**. A controller is a class
|
|
396
396
|
decorated with `@Controller(basePath)`; each route is a method decorated with
|
|
397
|
-
`@Get`/`@Post`/`@Put`/`@Patch`/`@Delete`. Request input + context are
|
|
398
|
-
into the method via **parameter decorators** (`@Body`/`@
|
|
399
|
-
|
|
400
|
-
central router and no manual registration.
|
|
397
|
+
`@Get`/`@Post`/`@Put`/`@Patch`/`@Delete`/`@Query`. Request input + context are
|
|
398
|
+
injected into the method via **parameter decorators** (`@Body`/`@QueryParams`/
|
|
399
|
+
`@Param`/`@User`/…). Putting a controller file under `controllers/` mounts it —
|
|
400
|
+
there is no central router and no manual registration.
|
|
401
401
|
|
|
402
402
|
```ts
|
|
403
|
-
import { Controller, Get, Post, Body,
|
|
403
|
+
import { Controller, Get, Post, Body, QueryParams, Param, User } from "@palbase/backend";
|
|
404
404
|
```
|
|
405
405
|
|
|
406
406
|
## Controllers — class + method decorators
|
|
@@ -441,7 +441,7 @@ Rules:
|
|
|
441
441
|
- The full path of a route is `basePath + subpath` (`"/places" + "/import"`).
|
|
442
442
|
- A `{segment}` in a path becomes a path param, injected via `@Param("segment")`.
|
|
443
443
|
- Input is declared with the parameter decorators — `@Body(schema)`,
|
|
444
|
-
`@
|
|
444
|
+
`@QueryParams(schema)`, `@Param("id")`, `@Headers(schema?)`. The success response is
|
|
445
445
|
the method's RETURN TYPE (`: PlaceSchema` or `: z.infer<typeof PlaceSchema>`):
|
|
446
446
|
codegen + the runtime read that named type to drive the OpenAPI 200 response.
|
|
447
447
|
A body route with no named return type is a build error — annotate `: void`
|
|
@@ -452,6 +452,10 @@ Rules:
|
|
|
452
452
|
is used as-is (`places.importNearby`). The generated clients group routes by
|
|
453
453
|
that namespace (`pb.places.importNearby(...)`). Rename the class or the
|
|
454
454
|
method and the operationId changes; the verb/path do not affect it.
|
|
455
|
+
- `@Query` declares an HTTP **QUERY** route (RFC 10008): safe + idempotent like
|
|
456
|
+
GET, body-carrying like POST — the right verb for complex search/filter
|
|
457
|
+
reads. Its input rides `@Body(schema)`; do NOT combine `@Body` with
|
|
458
|
+
`@QueryParams` on a QUERY route. See [endpoints.md](./endpoints.md).
|
|
455
459
|
|
|
456
460
|
See [endpoints.md](./endpoints.md) for the full decorator reference (`@Controller`
|
|
457
461
|
options, the parameter decorators, auth cascade, and error classes).
|
|
@@ -464,10 +468,10 @@ options, the parameter decorators, auth cascade, and error classes).
|
|
|
464
468
|
|
|
465
469
|
An endpoint is a **method on a class controller** — a class decorated with
|
|
466
470
|
`@Controller(basePath)` whose methods are decorated with `@Get`/`@Post`/`@Put`/
|
|
467
|
-
`@Patch`/`@Delete`. Controller files live under `controllers/` and are
|
|
468
|
-
automatically (see [routing.md](./routing.md)). Request input + context
|
|
469
|
-
injected into the method via **parameter decorators** (`@Body`/`@
|
|
470
|
-
`@User`/…), each piece direct — no `req` god-object. Services are reached via the
|
|
471
|
+
`@Patch`/`@Delete`/`@Query`. Controller files live under `controllers/` and are
|
|
472
|
+
mounted automatically (see [routing.md](./routing.md)). Request input + context
|
|
473
|
+
are injected into the method via **parameter decorators** (`@Body`/`@QueryParams`/
|
|
474
|
+
`@Param`/`@User`/…), each piece direct — no `req` god-object. Services are reached via the
|
|
471
475
|
imported singletons (see [services.md](./services.md)).
|
|
472
476
|
|
|
473
477
|
## A controller
|
|
@@ -511,7 +515,7 @@ export default class RoomsController {
|
|
|
511
515
|
|
|
512
516
|
## Method decorators
|
|
513
517
|
|
|
514
|
-
`@Get`/`@Post`/`@Put`/`@Patch`/`@Delete` take `(subpath, options?)`:
|
|
518
|
+
`@Get`/`@Post`/`@Put`/`@Patch`/`@Delete`/`@Query` take `(subpath, options?)`:
|
|
515
519
|
|
|
516
520
|
```ts
|
|
517
521
|
interface RouteOptions {
|
|
@@ -524,12 +528,29 @@ Input and output are NOT method-decorator options: input comes from the
|
|
|
524
528
|
parameter decorators, output from the method's return type. The method decorator
|
|
525
529
|
only carries route concerns (path, auth, rateLimit).
|
|
526
530
|
|
|
531
|
+
### `@Query` — the HTTP QUERY method (RFC 10008)
|
|
532
|
+
|
|
533
|
+
`@Query(subpath, options?)` declares an HTTP **QUERY** route: safe + idempotent
|
|
534
|
+
like GET, body-carrying like POST. Use it for reads whose input is too rich for
|
|
535
|
+
a query string — complex search/filter endpoints that would otherwise be a
|
|
536
|
+
"fake-read" POST. The input rides `@Body(schema)`:
|
|
537
|
+
|
|
538
|
+
```ts
|
|
539
|
+
@Query("/search")
|
|
540
|
+
async search(@Body(SearchTodosBody) body: SearchTodosBody): Promise<TodoSchema[]> {
|
|
541
|
+
return todoService.search(body);
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
Do NOT combine `@Body` with `@QueryParams` on a QUERY route — the body IS the
|
|
546
|
+
query (RFC semantics), so a QUERY route carries its whole input there.
|
|
547
|
+
|
|
527
548
|
## Parameter decorators
|
|
528
549
|
|
|
529
550
|
| Decorator | Injects | Typed by | OpenAPI |
|
|
530
551
|
|---|---|---|---|
|
|
531
552
|
| `@Body(schema)` | request body | `: T` (`z.infer<schema>`, same name) | requestBody |
|
|
532
|
-
| `@
|
|
553
|
+
| `@QueryParams(schema)` | query params | `: T` | query parameters |
|
|
533
554
|
| `@Param("id")` | one path param | `: string` | path parameter |
|
|
534
555
|
| `@Headers(schema?)` | request headers | `Record<string,string>` (+ typed with schema) | header parameters |
|
|
535
556
|
| `@User()` | authenticated user | `: UserT` | — (from token) |
|
|
@@ -538,7 +559,7 @@ only carries route concerns (path, auth, rateLimit).
|
|
|
538
559
|
| `@RequestId()` / `@TraceId()` | correlation ids | `: string` | — |
|
|
539
560
|
| `@Req()` | raw request (escape hatch) | `: PBRequest` | — |
|
|
540
561
|
|
|
541
|
-
`@Body`/`@
|
|
562
|
+
`@Body`/`@QueryParams`/`@Headers` take a zod schema (validation + codegen source); the
|
|
542
563
|
developer writes the matching type annotation (same name). `UserT` is the
|
|
543
564
|
authenticated-user type (`{ id: string; email?: string; role: string; metadata:
|
|
544
565
|
Record<string, unknown> }`) — exported under the alias `UserT` because the value
|
|
@@ -1028,8 +1049,56 @@ regenerates on change) after editing the schema to refresh `palbase-env.d.ts` so
|
|
|
1028
1049
|
`db/migrations/*.sql` is plain SQL applied in filename order and tracked in
|
|
1029
1050
|
`schema_migrations` (idempotent — a migration runs once). `palbase db diff`
|
|
1030
1051
|
generates them for you, but you can also hand-write one for anything the diff
|
|
1031
|
-
can't express
|
|
1032
|
-
trigger
|
|
1052
|
+
can't express: a data backfill, a complex type change with a `USING` cast, a
|
|
1053
|
+
trigger, a `CHECK` constraint, an `EXCLUDE` constraint, a composite/multi-column
|
|
1054
|
+
`UNIQUE`, an index (`CREATE INDEX`), or `CREATE EXTENSION` for an extension that
|
|
1055
|
+
isn't in the DSL allowlist (e.g. `btree_gist`). The DSL column builders cover
|
|
1056
|
+
single-column constraints only; everything above is hand-written SQL. Where the
|
|
1057
|
+
DSL *can* model the table, keep `db/schema.ts` as the declared end-state and add
|
|
1058
|
+
only the extra object in raw SQL so the drift gate passes (see below); a table
|
|
1059
|
+
the DSL can't model at all lives entirely in raw SQL.
|
|
1060
|
+
|
|
1061
|
+
Migrations run on the privileged DDL connection, so `CREATE EXTENSION` and
|
|
1062
|
+
`ALTER TABLE ... ADD CONSTRAINT` work. Example — race-free, DB-enforced
|
|
1063
|
+
double-booking prevention (no instructor in two overlapping sessions), which the
|
|
1064
|
+
DSL can't express. The `during` column is a `tstzrange` (no DSL builder), so the
|
|
1065
|
+
whole table is created in raw SQL — it can't come from `db/schema.ts`:
|
|
1066
|
+
|
|
1067
|
+
```sql
|
|
1068
|
+
CREATE EXTENSION IF NOT EXISTS btree_gist;
|
|
1069
|
+
|
|
1070
|
+
CREATE TABLE instructor_sessions (
|
|
1071
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
1072
|
+
instructor_id text NOT NULL,
|
|
1073
|
+
during tstzrange NOT NULL,
|
|
1074
|
+
CONSTRAINT no_instructor_overlap
|
|
1075
|
+
EXCLUDE USING gist (instructor_id WITH =, during WITH &&)
|
|
1076
|
+
);
|
|
1077
|
+
```
|
|
1078
|
+
|
|
1079
|
+
An overlapping insert for the same instructor is rejected by Postgres
|
|
1080
|
+
(`SQLSTATE 23P01`, `exclusion_violation`); adjacent/non-overlapping ranges and a
|
|
1081
|
+
different instructor in the same slot are accepted. No application-level check.
|
|
1082
|
+
|
|
1083
|
+
### Drift gate and raw-SQL objects
|
|
1084
|
+
|
|
1085
|
+
The deploy-time drift gate only asserts that everything **declared** in
|
|
1086
|
+
`db/schema.ts` exists in the live DB (declared ⊆ live), and it inspects tables,
|
|
1087
|
+
columns, and column types **only** — not constraints or indexes. So a
|
|
1088
|
+
hand-written constraint or index is invisible to the gate, and a live object you
|
|
1089
|
+
never declared is tolerated (not drift, not dropped — it survives redeploys).
|
|
1090
|
+
|
|
1091
|
+
Two patterns follow from this:
|
|
1092
|
+
|
|
1093
|
+
- **Extra constraint/index on a DSL-expressible table** — declare the table in
|
|
1094
|
+
`db/schema.ts` and add **only** the constraint/index via the migration. The
|
|
1095
|
+
base table stays modeled, the gate ignores the constraint, and `palbase db
|
|
1096
|
+
check` stays clean.
|
|
1097
|
+
- **A whole object the DSL can't model** (like `instructor_sessions` above — its
|
|
1098
|
+
`tstzrange` column has no DSL builder) — the table lives entirely in raw SQL,
|
|
1099
|
+
not in `db/schema.ts`. The deploy still succeeds, but `palbase db check` (the
|
|
1100
|
+
read-only client warning) will print a `- table <name>` would-be-drop line for
|
|
1101
|
+
it; that's expected and the deploy-time gate does not block on it.
|
|
1033
1102
|
|
|
1034
1103
|
## Adding a NOT NULL column to a table with rows
|
|
1035
1104
|
|
package/docs/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Palbase Backend SDK (`@palbase/backend`)
|
|
2
2
|
|
|
3
|
-
> TypeScript backend SDK. NestJS-style class controllers: `@Controller` classes with `@Get`/`@Post`/… methods and `@Body`/`@
|
|
3
|
+
> TypeScript backend SDK. NestJS-style class controllers: `@Controller` classes with `@Get`/`@Post`/`@Query`/… methods and `@Body`/`@QueryParams`/`@Param`/`@User` parameter decorators. All handler types import service singletons (`Database`, `Cache`, …). Trigger arg differs by type: endpoints use parameter decorators, workers `(payload, meta)`, jobs `(meta)`, hooks/webhooks `(event, meta)`. Middleware is the one exception (`ctx`). Not Express, not Supabase Edge Functions.
|
|
4
4
|
|
|
5
5
|
## Docs
|
|
6
6
|
|
package/docs/routing.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Routes are declared in code with **class controllers**. A controller is a class
|
|
4
4
|
decorated with `@Controller(basePath)`; each route is a method decorated with
|
|
5
|
-
`@Get`/`@Post`/`@Put`/`@Patch`/`@Delete`. Request input + context are
|
|
6
|
-
into the method via **parameter decorators** (`@Body`/`@
|
|
7
|
-
|
|
8
|
-
central router and no manual registration.
|
|
5
|
+
`@Get`/`@Post`/`@Put`/`@Patch`/`@Delete`/`@Query`. Request input + context are
|
|
6
|
+
injected into the method via **parameter decorators** (`@Body`/`@QueryParams`/
|
|
7
|
+
`@Param`/`@User`/…). Putting a controller file under `controllers/` mounts it —
|
|
8
|
+
there is no central router and no manual registration.
|
|
9
9
|
|
|
10
10
|
```ts
|
|
11
|
-
import { Controller, Get, Post, Body,
|
|
11
|
+
import { Controller, Get, Post, Body, QueryParams, Param, User } from "@palbase/backend";
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## Controllers — class + method decorators
|
|
@@ -49,7 +49,7 @@ Rules:
|
|
|
49
49
|
- The full path of a route is `basePath + subpath` (`"/places" + "/import"`).
|
|
50
50
|
- A `{segment}` in a path becomes a path param, injected via `@Param("segment")`.
|
|
51
51
|
- Input is declared with the parameter decorators — `@Body(schema)`,
|
|
52
|
-
`@
|
|
52
|
+
`@QueryParams(schema)`, `@Param("id")`, `@Headers(schema?)`. The success response is
|
|
53
53
|
the method's RETURN TYPE (`: PlaceSchema` or `: z.infer<typeof PlaceSchema>`):
|
|
54
54
|
codegen + the runtime read that named type to drive the OpenAPI 200 response.
|
|
55
55
|
A body route with no named return type is a build error — annotate `: void`
|
|
@@ -60,6 +60,10 @@ Rules:
|
|
|
60
60
|
is used as-is (`places.importNearby`). The generated clients group routes by
|
|
61
61
|
that namespace (`pb.places.importNearby(...)`). Rename the class or the
|
|
62
62
|
method and the operationId changes; the verb/path do not affect it.
|
|
63
|
+
- `@Query` declares an HTTP **QUERY** route (RFC 10008): safe + idempotent like
|
|
64
|
+
GET, body-carrying like POST — the right verb for complex search/filter
|
|
65
|
+
reads. Its input rides `@Body(schema)`; do NOT combine `@Body` with
|
|
66
|
+
`@QueryParams` on a QUERY route. See [endpoints.md](./endpoints.md).
|
|
63
67
|
|
|
64
68
|
See [endpoints.md](./endpoints.md) for the full decorator reference (`@Controller`
|
|
65
69
|
options, the parameter decorators, auth cascade, and error classes).
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@palbase/backend",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Palbase Backend SDK — class controllers (@Controller/@Get/@Post + @Body/@
|
|
3
|
+
"version": "9.0.0",
|
|
4
|
+
"description": "Palbase Backend SDK — class controllers (@Controller/@Get/@Post + @Body/@QueryParams/@Param), error classes, schema DSL",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/db/policy.ts","../src/db/schema.ts","../src/db/extensions.ts","../src/db/columns.ts","../src/db/raw.ts","../src/db/typed-db.ts"],"sourcesContent":["/**\n * policy.ts — the RLS policy authoring DSL.\n *\n * `policy(name)` returns a fluent builder that mirrors the `ColumnBuilder`\n * style in columns.ts: each chainable method mutates the underlying\n * definition and returns the builder so calls compose. The terminal value is\n * a plain {@link PolicyDef} — the exact JSON shape the runtime's\n * `schema_extract.js` reads off the bundled module and the Go side parses into\n * `PolicyJSON` (CONTRACT-POLICY).\n *\n * @example\n * import { policy } from \"@palbase/backend\";\n *\n * policy(\"owner_select\")\n * .for(\"select\")\n * .to(\"authenticated\")\n * .using(\"owner = (select auth.uid())\");\n */\n\n/** The SQL command a policy applies to. `\"all\"` covers SELECT/INSERT/UPDATE/DELETE. */\nexport type PolicyCommand = \"all\" | \"select\" | \"insert\" | \"update\" | \"delete\";\n\n/** Whether a policy is permissive (OR-combined, the default) or restrictive\n * (AND-combined). Mirrors Postgres `CREATE POLICY ... AS PERMISSIVE|RESTRICTIVE`. */\nexport type PolicyMode = \"permissive\" | \"restrictive\";\n\n/**\n * The compiled, serializable policy definition — the EXACT shape consumed by\n * `schema_extract.js` → Go `PolicyJSON` (CONTRACT-POLICY).\n *\n * - `roles`: the DB roles this policy applies to (`TO` clause). An empty array\n * means the policy applies to PUBLIC (all roles) — the Postgres default.\n * - `using`: the `USING (...)` row-visibility expression, or `null` when none.\n * - `withCheck`: the `WITH CHECK (...)` write-validation expression, or `null`.\n * - `permissive`: `true` for `AS PERMISSIVE` (default), `false` for restrictive.\n */\nexport interface PolicyDef {\n name: string;\n command: PolicyCommand;\n roles: string[];\n using: string | null;\n withCheck: string | null;\n permissive: boolean;\n}\n\n/**\n * Fluent RLS policy builder.\n *\n * Defaults (documented, applied at construction):\n * - `command`: `\"all\"` — applies to every SQL command unless `.for(...)` narrows it.\n * - `roles`: `[\"authenticated\"]` — the common case is \"rule applies to signed-in\n * users\". Call `.to(...)` to override; pass `.to()` with no roles (or never\n * call it after a reset) to target PUBLIC.\n * - `using` / `withCheck`: `null` — no row filter / write check until set.\n * - `permissive`: `true` — `AS PERMISSIVE` (policies OR together).\n *\n * Each method mutates `_def` in place and returns `this`, so the chain is a\n * single builder instance (no per-call allocation, like a tagged-template\n * compile target). The terminal `PolicyDef` is read directly off `_def` by\n * `schema_extract.js`.\n */\nexport class PolicyBuilder {\n readonly _def: PolicyDef;\n\n constructor(name: string) {\n this._def = {\n name,\n command: \"all\",\n roles: [\"authenticated\"],\n using: null,\n withCheck: null,\n permissive: true,\n };\n }\n\n /** Restrict the policy to a single SQL command (default `\"all\"`). */\n for(command: PolicyCommand): this {\n this._def.command = command;\n return this;\n }\n\n /**\n * Set the DB roles the policy applies to (the `TO` clause), replacing any\n * previously-set roles. Call with no arguments to target PUBLIC (all roles).\n *\n * @example\n * policy(\"p\").to(\"authenticated\")\n * policy(\"p\").to(\"authenticated\", \"service_role\")\n * policy(\"p\").to() // PUBLIC\n */\n to(...roles: string[]): this {\n this._def.roles = roles;\n return this;\n }\n\n /** Set the `USING (...)` row-visibility expression (raw SQL). */\n using(sqlExpr: string): this {\n this._def.using = sqlExpr;\n return this;\n }\n\n /** Set the `WITH CHECK (...)` write-validation expression (raw SQL). */\n withCheck(sqlExpr: string): this {\n this._def.withCheck = sqlExpr;\n return this;\n }\n\n /** Set the policy mode: `\"permissive\"` (default, OR-combined) or\n * `\"restrictive\"` (AND-combined). */\n as(mode: PolicyMode): this {\n this._def.permissive = mode === \"permissive\";\n return this;\n }\n}\n\n/**\n * Start authoring an RLS policy. Returns a {@link PolicyBuilder}; the resulting\n * `PolicyBuilder` is accepted directly in a table's `policies: [...]` array\n * (its `_def` is read at schema-extract time).\n *\n * @param name The policy name. Palbase reconciliation keys policies by\n * `(table, name)`, so names must be unique per table.\n */\nexport function policy(name: string): PolicyBuilder {\n return new PolicyBuilder(name);\n}\n","import type { ColumnBuilder } from \"./columns.js\";\nimport { PolicyBuilder } from \"./policy.js\";\nimport type { PolicyDef } from \"./policy.js\";\nimport type { PalbaseExtension } from \"./extensions.js\";\nimport type { RawConstraintDef } from \"./raw.js\";\n\n/**\n * A map of column builders keyed by column name — the value you write under\n * the `columns` key of `defineSchema({ tables: { <name>: { columns } } })`.\n *\n * The default `Record<string, ColumnBuilder>` keeps bare references compiling\n * without a type argument.\n */\nexport type ColumnMap = Record<string, ColumnBuilder>;\n\n/**\n * The author-facing value written under each table key:\n * `{ columns, rls?, policies? }`.\n *\n * - `columns`: the column map (required).\n * - `rls`: enable + FORCE row-level security on this table. Implied when\n * `policies` is non-empty; set it explicitly to enable RLS with no policies\n * yet (deny-all — useful only as an intermediate step).\n * - `policies`: the RLS policies for this table, authored with `policy(name)`.\n * Each entry may be a {@link PolicyBuilder} (the normal `policy(...)` chain)\n * or a raw {@link PolicyDef} object.\n *\n * The `C` type parameter preserves the precise per-column phantom types so the\n * typed `Database.tables.*` surface keeps inferring insert/row shapes.\n */\nexport interface TableInput<C extends ColumnMap = ColumnMap> {\n columns: C;\n rls?: boolean;\n policies?: (PolicyBuilder | PolicyDef)[];\n /** Composite/named primary key (ordered column names). Omit for single-column inline .primaryKey(). */\n primaryKey?: string[];\n /** Named multi-column UNIQUE constraints. */\n unique?: { name: string; columns: string[] }[];\n /** Named raw-SQL DDL objects (EXCLUDE, triggers, views) that the typed DSL cannot express. */\n raw?: RawConstraintDef[];\n /**\n * Named first-class CHECK constraints. Diffed by NAME with a BODY compare:\n * a changed `expr` (after pg normalization) recreates the constraint\n * (DROP + ADD). `expr` is trusted SQL emitted verbatim (like policy USING),\n * `name` is identifier-validated.\n */\n checks?: { name: string; expr: string }[];\n /**\n * Plain (non-unique) btree indexes over an ordered column list, emitted as\n * standalone `CREATE INDEX [IF NOT EXISTS] name ON table (col1, col2)`\n * statements (NOT a table clause — a separate migration statement category).\n * Structural compare by NAME (no expression normalization). `name` and each\n * column are identifier-validated by the Go differ.\n *\n * Scope: columns-only plain btree. Partial (`where`) and expression indexes\n * are a deliberate follow-up — modelling them needs the same raw-SQL\n * normalization round-trip CHECK uses (Task 10), so they are NOT in this\n * type yet to avoid a half-working partial-index path.\n */\n indexes?: { name: string; columns: string[] }[];\n}\n\n/**\n * A table definition — the runtime value the Go runtime's `schema_extract.js`\n * reads. It keys tables by `tableDef.name`, reads `tableDef.columns` for the\n * column DDL, and `tableDef.rls` + `tableDef.policies` for RLS.\n *\n * `defineSchema` derives `name` from the object key, so authors never repeat\n * the table name. `rls`/`policies` are always present after normalization\n * (defaulted to `false`/`[]`).\n *\n * The `C` type parameter preserves the precise per-column phantom types so that\n * downstream mapped types (InsertShape, RowShape) can discriminate on them.\n */\nexport interface TableDef<C extends ColumnMap = ColumnMap> {\n name: string;\n columns: C;\n rls: boolean;\n policies: PolicyDef[];\n primaryKey?: string[];\n unique?: { name: string; columns: string[] }[];\n /** Named raw-SQL DDL objects emitted verbatim on deploy. Tracked by name. */\n raw?: RawConstraintDef[];\n /** Named first-class CHECK constraints. Diffed by name + (normalized) body. */\n checks?: { name: string; expr: string }[];\n /** Plain btree indexes (columns-only), emitted as standalone CREATE INDEX. Diffed by name. */\n indexes?: { name: string; columns: string[] }[];\n}\n\n/**\n * A schema definition containing multiple tables, keyed by table name.\n *\n * The `T` type parameter preserves the exact `TableDef<...>` type for each\n * table so that `SchemaDef[\"tables\"][\"rooms\"]` resolves to the precise\n * `TableDef<{ id: ColumnBuilder<'uuid', false, true, never>; ... }>`.\n */\nexport interface SchemaDef<\n T extends Record<string, TableDef> = Record<string, TableDef>,\n> {\n tables: T;\n /** Postgres extensions to install on deploy. Normalized to `[]` when absent. */\n extensions: PalbaseExtension[];\n}\n\n/** The author-facing input to `defineSchema` — a `tables` map whose keys are\n * the table names and whose values are `{ columns, rls?, policies? }`, plus an\n * optional `extensions` allowlist. */\nexport interface SchemaInput<\n T extends Record<string, TableInput> = Record<string, TableInput>,\n> {\n tables: T;\n /**\n * Postgres extensions to enable for this project, e.g. `[\"vector\"]`.\n * Config-as-code: installed by the deploy (CREATE EXTENSION … SCHEMA\n * extensions) with the privileged deploy connection. The type is an\n * allowlist union, so unsupported names fail typecheck.\n */\n extensions?: PalbaseExtension[];\n}\n\n/** Map the author's `{ tables: { <name>: { columns } } }` input to the\n * `{ tables: { <name>: TableDef<columns> } }` runtime/type shape, threading the\n * per-table column map `T[K][\"columns\"]` so column-level inference survives. */\ntype TablesFromInput<T extends Record<string, TableInput>> = {\n [K in keyof T]: TableDef<T[K][\"columns\"]>;\n};\n\n/** Normalize a single `policies` entry into a plain `PolicyDef` (read off a\n * `PolicyBuilder._def`, or passed through when already a `PolicyDef`). */\nfunction toPolicyDef(p: PolicyBuilder | PolicyDef): PolicyDef {\n return p instanceof PolicyBuilder ? p._def : p;\n}\n\n/**\n * Define a schema. The table NAME comes from the object key. Each table value\n * is `{ columns, rls?, policies? }`:\n *\n * export default defineSchema({\n * tables: {\n * todos: {\n * columns: {\n * id: uuid().primaryKey().defaultRandom(),\n * owner: text().notNull(),\n * title: text().notNull(),\n * },\n * rls: true,\n * policies: [\n * policy(\"owner_all\").for(\"all\").to(\"authenticated\")\n * .using(\"owner = (select auth.uid())\")\n * .withCheck(\"owner = (select auth.uid())\"),\n * ],\n * },\n * },\n * });\n *\n * The returned value is\n * `{ tables: { todos: { name, columns, rls, policies } } }` — the exact shape\n * the runtime schema extractor parses. Per-column phantom types are preserved\n * so `Database.tables.todos.insert({...})` stays typed.\n *\n * RLS normalization: `rls` defaults to `false`, `policies` to `[]`. When\n * `policies` is non-empty, `rls` is forced on (ENABLE + FORCE) regardless of\n * the declared `rls` flag — a table with policies must have RLS enabled or the\n * policies would be inert.\n */\nexport function defineSchema<T extends Record<string, TableInput>>(\n input: SchemaInput<T>,\n): SchemaDef<TablesFromInput<T>> {\n const tables = {} as TablesFromInput<T>;\n for (const name of Object.keys(input.tables) as (keyof T)[]) {\n const table = input.tables[name];\n // `noUncheckedIndexedAccess` widens the index access to `… | undefined`,\n // but `name` comes straight from `Object.keys(input.tables)`, so the entry\n // always exists. Guard to narrow without a cast.\n if (table === undefined) continue;\n const policies = (table.policies ?? []).map(toPolicyDef);\n // A table with policies must have RLS enabled, otherwise the policies are\n // inert. Enabling RLS without policies (deny-all) is allowed explicitly.\n const rls = table.rls === true || policies.length > 0;\n const tableDef: TableDef<T[typeof name][\"columns\"]> = {\n name: name as string,\n columns: table.columns,\n rls,\n policies,\n };\n if (table.primaryKey !== undefined) tableDef.primaryKey = table.primaryKey;\n if (table.unique !== undefined) tableDef.unique = table.unique;\n if (table.raw !== undefined && table.raw.length > 0) tableDef.raw = table.raw.slice();\n if (table.checks !== undefined && table.checks.length > 0) tableDef.checks = table.checks.slice();\n if (table.indexes !== undefined && table.indexes.length > 0) tableDef.indexes = table.indexes.slice();\n tables[name] = tableDef;\n }\n // Dedupe + normalize extensions (order-independent; deploy resolves deps).\n const extensions = [...new Set(input.extensions ?? [])];\n return { tables, extensions };\n}\n","/**\n * Postgres extensions a Palbase project can enable from its schema.\n *\n * Extensions are config-as-code: declare them in `defineSchema({ extensions })`\n * and the deploy installs them (CREATE EXTENSION … SCHEMA extensions) using the\n * deploy path's privileged connection. They are NOT toggled live from Studio —\n * CREATE EXTENSION requires a superuser role that only the deploy path holds.\n *\n * The list is an allowlist (a string-literal union) so editors autocomplete the\n * supported names and a typo fails typecheck. It is intentionally extensible:\n * add a name here (+ confirm the base image ships it) to support more.\n */\nexport const PALBASE_EXTENSIONS = [\n // Search & text\n \"vector\", // pgvector: AI embeddings + vector similarity search (semantic search / RAG).\n // NB: the Postgres extension is named \"vector\", not \"pgvector\" — declare \"vector\".\n \"pg_trgm\", // trigram fuzzy / typo-tolerant text search\n \"unaccent\", // accent-insensitive text search\n \"citext\", // case-insensitive text type\n // Geospatial / location\n \"postgis\", // geospatial types + queries (maps, \"near me\")\n \"cube\", // multi-dimensional cubes (dependency of earthdistance)\n \"earthdistance\", // great-circle distance (needs cube)\n // Data types & structures\n \"hstore\", // key/value pairs in a single column\n \"ltree\", // hierarchical tree-structured labels\n // Scheduling\n \"pg_cron\", // schedule jobs inside the database\n // Crypto / ids (also installed by default; listable for explicitness)\n \"pgcrypto\", // cryptographic functions (hashing, encryption)\n \"uuid-ossp\", // UUID generation functions\n] as const;\n\n/** A Postgres extension supported by Palbase (allowlist union). */\nexport type PalbaseExtension = (typeof PALBASE_EXTENSIONS)[number];\n\n/**\n * Extensions that depend on another extension. The deploy installs\n * dependencies first; declaring `earthdistance` without `cube` still works\n * because the deploy resolves the order, but listing both is clearer.\n */\nexport const EXTENSION_DEPENDENCIES: Partial<Record<PalbaseExtension, PalbaseExtension[]>> = {\n earthdistance: [\"cube\"],\n};\n\n/** Runtime guard: is `name` a supported Palbase extension? */\nexport function isPalbaseExtension(name: string): name is PalbaseExtension {\n return (PALBASE_EXTENSIONS as readonly string[]).includes(name);\n}\n","/** On delete action for foreign key references. */\nexport type OnDeleteAction = 'cascade' | 'set null' | 'restrict' | 'no action';\n\n/** Column type identifiers. */\nexport type ColumnType =\n | 'uuid'\n | 'text'\n | 'integer'\n | 'bigint'\n | 'numeric'\n | 'boolean'\n | 'timestamp'\n | 'jsonb'\n | 'enum';\n\n/** Base column definition shared by all column types. */\nexport interface ColumnDef {\n type: ColumnType;\n nullable: boolean;\n primaryKey: boolean;\n defaultValue?: unknown;\n defaultRandom?: boolean;\n defaultNow?: boolean;\n references?: { table: string; column: string };\n onDeleteAction?: OnDeleteAction;\n enumName?: string;\n enumValues?: string[];\n unique?: boolean;\n}\n\n// Phantom brand symbols — never have runtime values; exist only to force\n// TypeScript's structural type system to distinguish ColumnBuilder instances\n// with different type-param combinations. Without these, TS sees all\n// ColumnBuilder<K,...> as structurally identical and the first branch of\n// ColValue matches everything.\ndeclare const __colKind: unique symbol;\ndeclare const __colNullable: unique symbol;\ndeclare const __colHasDefault: unique symbol;\ndeclare const __colEnumValues: unique symbol;\ndeclare const __colPayload: unique symbol;\n\n/**\n * Fluent column builder with phantom type params:\n * K — ColumnType literal (e.g. \"text\", \"integer\")\n * N — boolean: true when nullable() has been called last (false = NOT NULL)\n * D — boolean: true when a default has been set\n * E — enum value union (never for non-enum columns)\n * P — jsonb payload shape (unknown unless jsonb<T>() supplied one)\n *\n * All five params have defaults so bare `ColumnBuilder` (no args) still\n * satisfies `Record<string, ColumnBuilder>` in schema.ts without modification.\n *\n * The five `declare readonly` brand fields carry the phantom types into the\n * structural shape so that conditional types like ColValue<C> can discriminate\n * on K without requiring runtime values on those fields.\n */\nexport class ColumnBuilder<\n K extends ColumnType = ColumnType,\n N extends boolean = boolean,\n D extends boolean = boolean,\n E = unknown,\n P = unknown,\n> {\n // These fields exist only in the type layer (declared, never initialised at\n // runtime — TypeScript allows declared class members without an initializer\n // in strict mode as long as they're never read at runtime).\n declare readonly [__colKind]: K;\n declare readonly [__colNullable]: N;\n declare readonly [__colHasDefault]: D;\n declare readonly [__colEnumValues]: E;\n declare readonly [__colPayload]: P;\n\n readonly _def: ColumnDef;\n\n constructor(type: K, existingDef?: ColumnDef) {\n this._def = existingDef ?? {\n type,\n nullable: false,\n primaryKey: false,\n };\n }\n\n /** Mark this column as the primary key. */\n primaryKey(): ColumnBuilder<K, N, D, E, P> {\n this._def.primaryKey = true;\n return new ColumnBuilder<K, N, D, E, P>(this._def.type as K, this._def);\n }\n\n /** Mark this column as NOT NULL (default). */\n notNull(): ColumnBuilder<K, false, D, E, P> {\n this._def.nullable = false;\n return new ColumnBuilder<K, false, D, E, P>(this._def.type as K, this._def);\n }\n\n /** Allow NULL values. */\n nullable(): ColumnBuilder<K, true, D, E, P> {\n this._def.nullable = true;\n return new ColumnBuilder<K, true, D, E, P>(this._def.type as K, this._def);\n }\n\n /** Set a default value. */\n default(value: unknown): ColumnBuilder<K, N, true, E, P> {\n this._def.defaultValue = value;\n return new ColumnBuilder<K, N, true, E, P>(this._def.type as K, this._def);\n }\n\n /** UUID: generate a random default (gen_random_uuid()). */\n defaultRandom(): ColumnBuilder<K, N, true, E, P> {\n this._def.defaultRandom = true;\n return new ColumnBuilder<K, N, true, E, P>(this._def.type as K, this._def);\n }\n\n /** Timestamp: default to now(). */\n defaultNow(): ColumnBuilder<K, N, true, E, P> {\n this._def.defaultNow = true;\n return new ColumnBuilder<K, N, true, E, P>(this._def.type as K, this._def);\n }\n\n /** Add a foreign key reference. */\n references(table: string, column: string): ColumnBuilder<K, N, D, E, P> {\n this._def.references = { table, column };\n return new ColumnBuilder<K, N, D, E, P>(this._def.type as K, this._def);\n }\n\n /**\n * Add a real DB-level foreign key to the built-in auth users\n * (`REFERENCES auth.users(id)`), so a column like `user_id` gets true\n * database cascade/integrity instead of app-layer-only. Sugar for\n * `.references(\"auth.users\", \"id\")`.\n *\n * `auth.users` lives in the SAME tenant database (palauth-owned), so this is\n * a genuine cross-schema integrity constraint scoped to THIS tenant's users.\n * The referenced `auth.users.id` is `text` (palauth ids are `usr_<uuid>`), so\n * the referencing column must be `text()` too. Combine with `.onDelete(...)`\n * for cascade behaviour, e.g. a `user_id` column defined as\n * `text().notNull().referencesAuthUser().onDelete(\"cascade\")`.\n */\n referencesAuthUser(): ColumnBuilder<K, N, D, E, P> {\n return this.references('auth.users', 'id');\n }\n\n /** Set the ON DELETE action for a foreign key reference. */\n onDelete(action: OnDeleteAction): ColumnBuilder<K, N, D, E, P> {\n this._def.onDeleteAction = action;\n return new ColumnBuilder<K, N, D, E, P>(this._def.type as K, this._def);\n }\n\n /** Add a single-column UNIQUE constraint. */\n unique(): ColumnBuilder<K, N, D, E, P> {\n this._def.unique = true;\n return new ColumnBuilder<K, N, D, E, P>(this._def.type as K, this._def);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Type extractors — imported by Task 2 to derive insert/row shapes.\n// ---------------------------------------------------------------------------\n\n/**\n * Extracts the TypeScript value type for a column, respecting nullability.\n * - \"uuid\" | \"text\" | \"timestamp\" | \"bigint\" | \"numeric\" → string (or string | null when N = true)\n * Note: bigint/numeric surface as string — JS number loses precision past 2^53,\n * and pgx/PostgREST serialize int8/numeric as strings. App code uses\n * BigInt(row.amount) for bigint, or a decimal lib for numeric.\n * - \"integer\" → number\n * - \"boolean\" → boolean\n * - \"jsonb\" → P (the dev-supplied payload shape from jsonb<T>(), else unknown)\n * - \"enum\" → E (the union of literal values)\n */\nexport type ColValue<C> =\n C extends ColumnBuilder<'uuid' | 'text' | 'timestamp' | 'bigint' | 'numeric', infer N, infer _D, infer _E, infer _P>\n ? N extends true\n ? string | null\n : string\n : C extends ColumnBuilder<'integer', infer N, infer _D, infer _E, infer _P>\n ? N extends true\n ? number | null\n : number\n : C extends ColumnBuilder<'boolean', infer N, infer _D, infer _E, infer _P>\n ? N extends true\n ? boolean | null\n : boolean\n : C extends ColumnBuilder<'jsonb', infer N, infer _D, infer _E, infer P>\n ? N extends true\n ? P | null\n : P\n : C extends ColumnBuilder<'enum', infer N, infer _D, infer E, infer _P>\n ? N extends true\n ? E | null\n : E\n : never;\n\n/**\n * True when a column is optional on INSERT:\n * - nullable columns (N = true) — the DB allows NULL so the field may be omitted\n * - columns with a default (D = true) — the DB fills in the value when absent\n */\nexport type ColIsOptionalOnInsert<C> =\n C extends ColumnBuilder<infer _K, true, infer _D, infer _E>\n ? true\n : C extends ColumnBuilder<infer _K, infer _N, true, infer _E>\n ? true\n : false;\n\n// ---------------------------------------------------------------------------\n// Factory functions\n// ---------------------------------------------------------------------------\n\n/** Create a UUID column. */\nexport function uuid(): ColumnBuilder<'uuid', false, false, never> {\n return new ColumnBuilder('uuid');\n}\n\n/** Create a TEXT column. */\nexport function text(): ColumnBuilder<'text', false, false, never> {\n return new ColumnBuilder('text');\n}\n\n/** Create an INTEGER column. Emits int4 (max ~2.1B). */\nexport function integer(): ColumnBuilder<'integer', false, false, never> {\n return new ColumnBuilder('integer');\n}\n\n/**\n * Create a BIGINT column (Postgres int8, max ~9.2×10^18).\n * Surfaces as `string` in row/insert types — JS number loses precision past 2^53\n * and pgx/PostgREST serialize int8 as a JSON string. Use BigInt(row.column) in app code.\n */\nexport function bigint(): ColumnBuilder<'bigint', false, false, never> {\n return new ColumnBuilder('bigint');\n}\n\n/**\n * Create a NUMERIC column (Postgres `numeric`/`decimal`, arbitrary precision).\n * For exact fractional values (money with cents as a decimal, rates, weights)\n * where int4/int8 don't fit. Surfaces as `string` in row/insert types — JS\n * number can't hold arbitrary-precision decimals without rounding, and\n * pgx/PostgREST serialize numeric as a JSON string. Parse with a decimal lib\n * (or BigInt for scaled integers) in app code.\n */\nexport function numeric(): ColumnBuilder<'numeric', false, false, never> {\n return new ColumnBuilder('numeric');\n}\n\n/** Create a BOOLEAN column. */\nexport function boolean(): ColumnBuilder<'boolean', false, false, never> {\n return new ColumnBuilder('boolean');\n}\n\n/** Create a TIMESTAMP column. */\nexport function timestamp(): ColumnBuilder<'timestamp', false, false, never> {\n return new ColumnBuilder('timestamp');\n}\n\n/**\n * Create a JSONB column. Pass a payload type to make the generated row/insert\n * type concrete instead of `unknown`:\n *\n * tags: jsonb<string[]>() // row.tags: string[]\n * meta: jsonb<{ tier: string }>() // row.meta: { tier: string }\n * raw: jsonb() // row.raw: unknown (back-compat)\n *\n * The runtime accepts a plain JS object/array directly (no JSON.stringify); the\n * generic only refines the TYPE the env codegen emits.\n */\nexport function jsonb<T = unknown>(): ColumnBuilder<'jsonb', false, false, never, T> {\n return new ColumnBuilder('jsonb');\n}\n\n/**\n * Create an ENUM column.\n * @param name The PostgreSQL enum type name (used in DDL).\n * @param values A readonly tuple of valid string values — kept `const` so the\n * union `V[number]` is as narrow as possible.\n */\nexport function enumType<const V extends readonly string[]>(\n name: string,\n values: V,\n): ColumnBuilder<'enum', false, false, V[number]> {\n const builder = new ColumnBuilder<'enum', false, false, V[number]>('enum');\n builder._def.enumName = name;\n builder._def.enumValues = [...values];\n return builder;\n}\n","/**\n * A named raw-SQL DDL object declared in db/schema.ts for anything the typed DSL\n * cannot express (EXCLUDE, CHECK, partial/expression indexes, triggers, views).\n * The deploy emits `up` verbatim on the privileged DDL connection — same trust\n * posture as policy().using(). Tracked by NAME (not by diffing the body), so a\n * changed body needs a new name or an explicit drop+add.\n */\nexport interface RawConstraintDef {\n name: string;\n up: string;\n down?: string;\n}\n\nexport function raw(name: string, up: string, opts?: { down?: string }): RawConstraintDef {\n return { name, up, ...(opts?.down != null ? { down: opts.down } : {}) };\n}\n","/**\n * typed-db.ts — Task 2: TypedDB schema-derived insert/row shapes.\n *\n * Derives INSERT and full-row TypeScript types from a `defineSchema()` result\n * and wraps the untyped runtime `DBClient` with a typed facade.\n *\n * No value-any. No `as unknown as X`. The two narrow `as` casts in\n * `makeTypedTable` are safe because:\n * - `data as Record<string, unknown>`: InsertShape<T> maps string keys to\n * typed values; all value types are subsets of `unknown`, so the cast is\n * structurally sound.\n * - `result as RowShape<T>`: The runtime DBClient returns `Record<string,\n * unknown>` which is the erased form of the typed row; we're narrowing back\n * to the precise shape that the schema declared.\n * Both casts are narrowing only (not widening) and correctness is guaranteed\n * by the schema the caller provides.\n */\n\nimport type { ColValue, ColIsOptionalOnInsert, ColumnBuilder } from \"./columns.js\";\nimport type { TableDef, SchemaDef } from \"./schema.js\";\nimport type { Tables, TableTypes } from \"./env.js\";\nimport type { DBClient, TxClient } from \"../endpoint.js\";\n\n// ---------------------------------------------------------------------------\n// Key discriminators — split a column map into required vs optional keys.\n// ---------------------------------------------------------------------------\n\n/** Keys of C whose columns are required on INSERT (not nullable, no default). */\ntype RequiredKeys<C> = {\n [K in keyof C]: ColIsOptionalOnInsert<C[K]> extends true ? never : K;\n}[keyof C];\n\n/** Keys of C whose columns are optional on INSERT (nullable or has a default). */\ntype OptionalKeys<C> = {\n [K in keyof C]: ColIsOptionalOnInsert<C[K]> extends true ? K : never;\n}[keyof C];\n\n// ---------------------------------------------------------------------------\n// Public shape types — exported so callers can reference them directly.\n// ---------------------------------------------------------------------------\n\n/**\n * The TypeScript type for an INSERT payload for table `T`.\n * - Required: columns that are NOT NULL and have no DB-level default.\n * - Optional: columns that are nullable or carry a default.\n *\n * When all columns are optional, `RequiredKeys<C>` resolves to `never` and\n * the first part becomes `{}`, which is a neutral element for `&`.\n */\nexport type InsertShape<T extends TableDef> = {\n [K in RequiredKeys<T[\"columns\"]>]: ColValue<T[\"columns\"][K]>;\n} & {\n [K in OptionalKeys<T[\"columns\"]>]?: ColValue<T[\"columns\"][K]>;\n};\n\n/**\n * The TypeScript type for a full row returned by the DB for table `T`.\n * Every column is present; nullable columns resolve to `T | null`.\n */\nexport type RowShape<T extends TableDef> = {\n [K in keyof T[\"columns\"]]: ColValue<T[\"columns\"][K]>;\n};\n\n// ---------------------------------------------------------------------------\n// TypedTable + TypedDB interfaces.\n// ---------------------------------------------------------------------------\n\n/** A typed table accessor that mirrors the runtime DBClient surface. */\nexport interface TypedTable<T extends TableDef> {\n insert(data: InsertShape<T>): Promise<RowShape<T>>;\n /** Update the row by id; resolves to the updated row, or `null` if no row\n * matched (absent or RLS-hidden) — an idempotent outcome, mirroring\n * `findById`. The runtime returns a null row rather than throwing. */\n update(id: string, data: Partial<InsertShape<T>>): Promise<RowShape<T> | null>;\n delete(id: string): Promise<void>;\n findById(id: string): Promise<RowShape<T> | null>;\n findMany(query?: Partial<RowShape<T>>): Promise<RowShape<T>[]>;\n}\n\n/** A typed DB facade covering all tables declared in schema `S`. */\nexport interface TypedDB<S extends SchemaDef> {\n tables: {\n [K in keyof S[\"tables\"]]: TypedTable<S[\"tables\"][K]>;\n };\n transaction<T>(fn: (tx: TypedTx<S>) => Promise<T>): Promise<T>;\n}\n\n/** Transaction-scoped typed facade: same typed tables, no nested transaction. */\nexport interface TypedTx<S extends SchemaDef> {\n tables: {\n [K in keyof S[\"tables\"]]: TypedTable<S[\"tables\"][K]>;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Runtime factory.\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a typed table accessor that delegates every call to `raw` using the\n * runtime table name string. Two narrow `as` casts bridge the mapped-type\n * shapes to/from `Record<string, unknown>` — see module-level doc comment.\n *\n * The `raw` param is typed `TxClient` (the op surface shared by `DBClient` and\n * the transaction-scoped client) because this only ever calls the five\n * insert/update/delete/findById/findMany ops — never `transaction`. This lets\n * the same factory wrap both the top-level db and a tx without any cast.\n */\nfunction makeTypedTable<T extends TableDef<Record<string, ColumnBuilder>>>(\n name: string,\n raw: TxClient,\n): TypedTable<T> {\n return {\n insert: (data: InsertShape<T>) =>\n raw.insert(name, data as Record<string, unknown>) as Promise<RowShape<T>>,\n\n update: (id: string, data: Partial<InsertShape<T>>) =>\n raw.update(name, id, data as Record<string, unknown>) as Promise<RowShape<T> | null>,\n\n delete: (id: string) => raw.delete(name, id),\n\n findById: (id: string) =>\n raw.findById(name, id) as Promise<RowShape<T> | null>,\n\n findMany: (query?: Partial<RowShape<T>>) =>\n raw.findMany(name, query as Record<string, unknown> | undefined) as Promise<RowShape<T>[]>,\n };\n}\n\n/**\n * Wraps a raw `DBClient` with the type-safe `TypedDB<S>` facade derived from\n * the provided schema. No behavior change — all calls delegate to `raw` with\n * the table name as a plain string.\n *\n * `buildTables` is the reusable factory that wraps any op-bearing client\n * (`TxClient` — the surface shared by `DBClient` and the transaction-scoped\n * client) into the typed tables map. It is used both for the top-level db\n * (wrapping `raw`) and inside `transaction`, where it wraps the raw `TxClient`\n * the runtime yields so the callback sees the same typed `.tables` API.\n *\n * `transaction` delegates straight to `raw.transaction`; the two narrow\n * `as TypedTx<S>` / `as TypedDB<S>` casts are single structural narrowings\n * from the dynamically-built tables object to the precise mapped type (TS\n * cannot infer through `Object.keys` iteration) — see module-level doc comment.\n */\nexport function makeTypedDB<S extends SchemaDef>(\n schema: S,\n raw: DBClient,\n): TypedDB<S> {\n function buildTables(client: TxClient): Record<string, TypedTable<TableDef>> {\n const tables = {} as Record<string, TypedTable<TableDef>>;\n for (const key of Object.keys(schema.tables)) {\n const tableDef = schema.tables[key];\n if (tableDef !== undefined) {\n tables[key] = makeTypedTable(tableDef.name, client);\n }\n }\n return tables;\n }\n\n const result = {\n tables: buildTables(raw),\n transaction: <T>(fn: (tx: TypedTx<S>) => Promise<T>): Promise<T> =>\n raw.transaction((rawTx) => fn({ tables: buildTables(rawTx) } as TypedTx<S>)),\n };\n\n // Narrow cast: `result.tables` is structurally identical to\n // TypedDB<S>[\"tables\"] — each key maps to a TypedTable for the matching\n // TableDef. TS cannot infer the mapped-type result through Object.keys\n // iteration, so a single `as` bridges the gap.\n return result as TypedDB<S>;\n}\n\n// ---------------------------------------------------------------------------\n// Env-augmentation-driven typed surface — the typed-by-default `Database`.\n//\n// These types read the globally-augmented `Tables` interface from\n// `@palbase/backend/env` (filled by the generated `palbase-env.d.ts`). They\n// back `Database.tables.<name>` so handler code is typed with no import and no\n// generic (C5). They DELIBERATELY do not reference `ColumnBuilder` — the env\n// `Tables` interface carries flat `row`/`insert` object types.\n// ---------------------------------------------------------------------------\n\n/** A typed table accessor derived from one env `Tables` entry's flat shapes. */\nexport interface EnvTypedTable<T extends TableTypes> {\n insert(data: T[\"insert\"]): Promise<T[\"row\"]>;\n /** Update the row by id; resolves to the updated row, or `null` if no row\n * matched (absent or RLS-hidden) — an idempotent outcome, mirroring\n * `findById`. The runtime returns a null row rather than throwing. */\n update(id: string, data: Partial<T[\"insert\"]>): Promise<T[\"row\"] | null>;\n delete(id: string): Promise<void>;\n findById(id: string): Promise<T[\"row\"] | null>;\n findMany(query?: Partial<T[\"row\"]>): Promise<T[\"row\"][]>;\n}\n\n/** The `tables` map exposed on `Database`/`tx`, keyed by the env `Tables`\n * interface. When no schema is declared `Tables` is empty, so `tables` is an\n * empty object — accessing `.tables.foo` is then a compile error (no member). */\nexport type EnvTables = {\n [K in keyof Tables]: EnvTypedTable<Tables[K]>;\n};\n\n/** Transaction-scoped typed facade for the env-augmented surface: same typed\n * tables, no nested transaction. */\nexport interface EnvTypedTx {\n tables: EnvTables;\n}\n\n/**\n * The RLS-bypass sibling returned by `Database.asService()`. Same typed surface\n * as {@link EnvTypedDatabase} — `tables`, the raw string ops, and a typed\n * `transaction` — but it does NOT re-expose `asService` (no double-bypass).\n * Every op it performs runs as the `service_role` (BYPASSRLS).\n */\nexport interface EnvServiceDatabase extends Omit<DBClient, \"transaction\" | \"asService\"> {\n tables: EnvTables;\n transaction<T>(fn: (tx: EnvTypedTx) => Promise<T>): Promise<T>;\n}\n\n/**\n * The typed-by-default Database surface: the raw string-keyed `DBClient` ops\n * PLUS a `tables` map typed against the project's generated `palbase-env.d.ts`,\n * a `transaction` whose callback receives the typed tables, and `asService()`\n * for the explicit RLS-bypass sibling.\n *\n * `transaction` is declared here (overriding `DBClient[\"transaction\"]`) so the\n * `tx` the callback receives carries the typed `.tables` API. `asService` is\n * re-typed to return the typed {@link EnvServiceDatabase} sibling.\n */\nexport interface EnvTypedDatabase extends Omit<DBClient, \"transaction\" | \"asService\"> {\n tables: EnvTables;\n transaction<T>(fn: (tx: EnvTypedTx) => Promise<T>): Promise<T>;\n /**\n * Return a sibling that bypasses RLS by running as the `service_role`. Use\n * sparingly and explicitly — the default `Database.*` path is RLS-enforced.\n *\n * @example\n * const all = await Database.asService().tables.todos.findMany({});\n * const rows = await Database.asService().query(\"SELECT * FROM todos\");\n */\n asService(): EnvServiceDatabase;\n}\n"],"mappings":"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","names":["raw"]}
|