@palbase/backend 39.0.0 → 39.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/palbase-backend.cjs +70 -40
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +1 -1
- package/dist/{chunk-JJSR4BUX.js → chunk-C525N4OW.js} +71 -41
- package/dist/chunk-C525N4OW.js.map +1 -0
- package/dist/engine/index.cjs +70 -40
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +1 -1
- package/dist/engine/index.d.ts +1 -1
- package/dist/engine/index.js +1 -1
- package/dist/{index-B0mjUnxy.d.cts → index-Brhp8cxj.d.cts} +9 -15
- package/dist/{index-yDno9Ju9.d.ts → index-Dlu2b9-f.d.ts} +9 -15
- package/dist/index.cjs +3 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -12
- package/dist/index.d.ts +45 -12
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/docs/auth.md +20 -0
- package/docs/database.md +50 -0
- package/docs/llms-full.txt +70 -0
- package/package.json +1 -1
- package/dist/chunk-JJSR4BUX.js.map +0 -1
package/docs/auth.md
CHANGED
|
@@ -5,6 +5,26 @@ unless it opts out with `auth: false`. Client SDKs attach the user's token
|
|
|
5
5
|
automatically, so on the backend you declare what a route needs and inject the
|
|
6
6
|
user.
|
|
7
7
|
|
|
8
|
+
## Temporary verification failures
|
|
9
|
+
|
|
10
|
+
An absent or invalid access token produces `401 unauthorized` on a protected
|
|
11
|
+
route. If the runtime cannot retrieve a usable signing keyset, it instead
|
|
12
|
+
returns `503 auth_unavailable`. A network failure, an unsuccessful JWKS response,
|
|
13
|
+
or a keyset with no usable signing keys is an availability failure, not evidence
|
|
14
|
+
that the caller's token is invalid. Keep the session and retry after the service
|
|
15
|
+
recovers; signing in again is not required while the token remains valid.
|
|
16
|
+
|
|
17
|
+
The same rule applies to upload authorization. Requests that cannot be verified
|
|
18
|
+
do not reach the handler or the database, and uploads receive no grant. Public
|
|
19
|
+
requests without credentials continue to work.
|
|
20
|
+
|
|
21
|
+
Known signing keys remain usable within their trust TTL (five minutes by
|
|
22
|
+
default). Expired keysets are not trusted during an outage. Concurrent refreshes
|
|
23
|
+
share one request, and failed refreshes and unknown key IDs share a one-second
|
|
24
|
+
retry interval so recovery and key rotation do not wait for the entire TTL.
|
|
25
|
+
|
|
26
|
+
## Declaring authentication
|
|
27
|
+
|
|
8
28
|
```ts
|
|
9
29
|
import { Controller, Get, Post, Body, User, OptionalUser } from "@palbase/backend";
|
|
10
30
|
import type { UserT } from "@palbase/backend";
|
package/docs/database.md
CHANGED
|
@@ -29,6 +29,56 @@ await Database.public.todos.delete(todo.id);
|
|
|
29
29
|
|
|
30
30
|
See [schema.md](./schema.md) for the full typed-table surface.
|
|
31
31
|
|
|
32
|
+
## Tenant-scoped CRUD — `defineRepository`
|
|
33
|
+
|
|
34
|
+
Most repository classes are the same two lines per method: add the tenant column
|
|
35
|
+
to the predicate, then unwrap the result. Writing that by hand is what makes it
|
|
36
|
+
forgettable, and one forgotten predicate shows one tenant's row to another.
|
|
37
|
+
`defineRepository` produces a BASE CLASS that writes the predicate once.
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { defineRepository } from "@palbase/backend";
|
|
41
|
+
|
|
42
|
+
export class TodoRepository extends defineRepository(
|
|
43
|
+
Database.public.todos,
|
|
44
|
+
{ tenant: "household_id" },
|
|
45
|
+
) {}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The subclass gets six typed methods, all scoped to the tenant you pass first:
|
|
49
|
+
`list(tenant)`, `find(tenant, id)`, `insert(tenant, values)`,
|
|
50
|
+
`update(tenant, id, patch)`, `updateScoped(tenant, id, patch)` and
|
|
51
|
+
`delete(tenant, id)`. Add your own methods in the subclass body as usual.
|
|
52
|
+
|
|
53
|
+
`insert` does NOT take the tenant column in its payload — the repository writes
|
|
54
|
+
it. `update` throws `NotFound` when nothing matches, so the caller does not need
|
|
55
|
+
a `null` branch; `updateScoped` runs the same predicate and returns `null`
|
|
56
|
+
instead, for when absence is a value.
|
|
57
|
+
|
|
58
|
+
The base class constructor takes no arguments, so the DI container resolves the
|
|
59
|
+
subclass by its own name with no extra metadata. `Database` stays ambient rather
|
|
60
|
+
than injected: it is request-scoped, so a reference captured in a constructor
|
|
61
|
+
would carry one request's client into another.
|
|
62
|
+
|
|
63
|
+
### A row key that is not `id`
|
|
64
|
+
|
|
65
|
+
The row key defaults to `id`, but it is not fixed — `defineTable` does not
|
|
66
|
+
require an `id` column, and a primary key can sit on any column. Name it:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
export class DocRepository extends defineRepository(
|
|
70
|
+
Database.public.docs,
|
|
71
|
+
{ tenant: "org_id", key: "slug" },
|
|
72
|
+
) {}
|
|
73
|
+
|
|
74
|
+
await new DocRepository().find("org_1", "getting-started");
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The key parameter carries the column's OWN type, so a numeric key is typed
|
|
78
|
+
`number` rather than assumed to be a string. If the table has no `id` column and
|
|
79
|
+
you do not name a key, the surface refuses at compile time and the refusal says
|
|
80
|
+
what to do (`MissingRowKey`).
|
|
81
|
+
|
|
32
82
|
## Transaction boundaries and retries
|
|
33
83
|
|
|
34
84
|
Ordinary Database writes commit when the request completes. `$transaction(fn)`
|
package/docs/llms-full.txt
CHANGED
|
@@ -795,6 +795,26 @@ unless it opts out with `auth: false`. Client SDKs attach the user's token
|
|
|
795
795
|
automatically, so on the backend you declare what a route needs and inject the
|
|
796
796
|
user.
|
|
797
797
|
|
|
798
|
+
## Temporary verification failures
|
|
799
|
+
|
|
800
|
+
An absent or invalid access token produces `401 unauthorized` on a protected
|
|
801
|
+
route. If the runtime cannot retrieve a usable signing keyset, it instead
|
|
802
|
+
returns `503 auth_unavailable`. A network failure, an unsuccessful JWKS response,
|
|
803
|
+
or a keyset with no usable signing keys is an availability failure, not evidence
|
|
804
|
+
that the caller's token is invalid. Keep the session and retry after the service
|
|
805
|
+
recovers; signing in again is not required while the token remains valid.
|
|
806
|
+
|
|
807
|
+
The same rule applies to upload authorization. Requests that cannot be verified
|
|
808
|
+
do not reach the handler or the database, and uploads receive no grant. Public
|
|
809
|
+
requests without credentials continue to work.
|
|
810
|
+
|
|
811
|
+
Known signing keys remain usable within their trust TTL (five minutes by
|
|
812
|
+
default). Expired keysets are not trusted during an outage. Concurrent refreshes
|
|
813
|
+
share one request, and failed refreshes and unknown key IDs share a one-second
|
|
814
|
+
retry interval so recovery and key rotation do not wait for the entire TTL.
|
|
815
|
+
|
|
816
|
+
## Declaring authentication
|
|
817
|
+
|
|
798
818
|
```ts
|
|
799
819
|
import { Controller, Get, Post, Body, User, OptionalUser } from "@palbase/backend";
|
|
800
820
|
import type { UserT } from "@palbase/backend";
|
|
@@ -1163,6 +1183,56 @@ await Database.public.todos.delete(todo.id);
|
|
|
1163
1183
|
|
|
1164
1184
|
See [schema.md](./schema.md) for the full typed-table surface.
|
|
1165
1185
|
|
|
1186
|
+
## Tenant-scoped CRUD — `defineRepository`
|
|
1187
|
+
|
|
1188
|
+
Most repository classes are the same two lines per method: add the tenant column
|
|
1189
|
+
to the predicate, then unwrap the result. Writing that by hand is what makes it
|
|
1190
|
+
forgettable, and one forgotten predicate shows one tenant's row to another.
|
|
1191
|
+
`defineRepository` produces a BASE CLASS that writes the predicate once.
|
|
1192
|
+
|
|
1193
|
+
```ts
|
|
1194
|
+
import { defineRepository } from "@palbase/backend";
|
|
1195
|
+
|
|
1196
|
+
export class TodoRepository extends defineRepository(
|
|
1197
|
+
Database.public.todos,
|
|
1198
|
+
{ tenant: "household_id" },
|
|
1199
|
+
) {}
|
|
1200
|
+
```
|
|
1201
|
+
|
|
1202
|
+
The subclass gets six typed methods, all scoped to the tenant you pass first:
|
|
1203
|
+
`list(tenant)`, `find(tenant, id)`, `insert(tenant, values)`,
|
|
1204
|
+
`update(tenant, id, patch)`, `updateScoped(tenant, id, patch)` and
|
|
1205
|
+
`delete(tenant, id)`. Add your own methods in the subclass body as usual.
|
|
1206
|
+
|
|
1207
|
+
`insert` does NOT take the tenant column in its payload — the repository writes
|
|
1208
|
+
it. `update` throws `NotFound` when nothing matches, so the caller does not need
|
|
1209
|
+
a `null` branch; `updateScoped` runs the same predicate and returns `null`
|
|
1210
|
+
instead, for when absence is a value.
|
|
1211
|
+
|
|
1212
|
+
The base class constructor takes no arguments, so the DI container resolves the
|
|
1213
|
+
subclass by its own name with no extra metadata. `Database` stays ambient rather
|
|
1214
|
+
than injected: it is request-scoped, so a reference captured in a constructor
|
|
1215
|
+
would carry one request's client into another.
|
|
1216
|
+
|
|
1217
|
+
### A row key that is not `id`
|
|
1218
|
+
|
|
1219
|
+
The row key defaults to `id`, but it is not fixed — `defineTable` does not
|
|
1220
|
+
require an `id` column, and a primary key can sit on any column. Name it:
|
|
1221
|
+
|
|
1222
|
+
```ts
|
|
1223
|
+
export class DocRepository extends defineRepository(
|
|
1224
|
+
Database.public.docs,
|
|
1225
|
+
{ tenant: "org_id", key: "slug" },
|
|
1226
|
+
) {}
|
|
1227
|
+
|
|
1228
|
+
await new DocRepository().find("org_1", "getting-started");
|
|
1229
|
+
```
|
|
1230
|
+
|
|
1231
|
+
The key parameter carries the column's OWN type, so a numeric key is typed
|
|
1232
|
+
`number` rather than assumed to be a string. If the table has no `id` column and
|
|
1233
|
+
you do not name a key, the surface refuses at compile time and the refusal says
|
|
1234
|
+
what to do (`MissingRowKey`).
|
|
1235
|
+
|
|
1166
1236
|
## Transaction boundaries and retries
|
|
1167
1237
|
|
|
1168
1238
|
Ordinary Database writes commit when the request completes. `$transaction(fn)`
|
package/package.json
CHANGED