@palbase/backend 35.0.0 → 35.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/README.md +1 -1
- package/docs/database.md +97 -22
- package/docs/llms-full.txt +98 -23
- package/package.json +1 -1
- package/template/package.json +2 -2
package/docs/README.md
CHANGED
|
@@ -82,7 +82,7 @@ service the controllers call.
|
|
|
82
82
|
|
|
83
83
|
> **Never** emit `defineController`, `defineHandler`, `defineEndpoint`, `route.get(...)`,
|
|
84
84
|
> `req.input`, `req.params`, or `req.errors` — those are the removed legacy model
|
|
85
|
-
> and will not compile against `@palbase/backend`
|
|
85
|
+
> and will not compile against `@palbase/backend` 35.
|
|
86
86
|
|
|
87
87
|
### Complete CRUD example (copy-pasteable, compiles)
|
|
88
88
|
|
package/docs/database.md
CHANGED
|
@@ -825,7 +825,56 @@ continue to return their rows. Custom SQL drivers must expose an integer `count`
|
|
|
825
825
|
on a non-returning result. Empty input opens no connection. The batch remains one
|
|
826
826
|
statement and participates in its enclosing transaction's rollback.
|
|
827
827
|
|
|
828
|
-
##
|
|
828
|
+
## Query results and response contracts
|
|
829
|
+
|
|
830
|
+
Use the ordinary CRUD expression. A controller without a return annotation gets
|
|
831
|
+
its response validator, OpenAPI response and generated client type from the
|
|
832
|
+
TypeScript return type, following calls through injected services:
|
|
833
|
+
|
|
834
|
+
```ts
|
|
835
|
+
// Store/service
|
|
836
|
+
list(prefix: string) {
|
|
837
|
+
return Database.public.todos.findMany({
|
|
838
|
+
where: { title: { startsWith: prefix } },
|
|
839
|
+
select: ["id", "title"],
|
|
840
|
+
limit: 50,
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// Controller
|
|
845
|
+
@Get("/")
|
|
846
|
+
list(@QueryParams(ListInput) input: ListInput) {
|
|
847
|
+
return this.todos.list(input.prefix);
|
|
848
|
+
}
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
The generated contract contains only `id` and `title`. `findUnique` retains
|
|
852
|
+
nullability, `page` retains its rows/pageInfo shape, and `insertMany` with
|
|
853
|
+
`returning: false` returns a count. Use `updateMany` for one bulk UPDATE; a
|
|
854
|
+
command plan is useful when several dependent operations actually belong together.
|
|
855
|
+
|
|
856
|
+
Inference does not execute application code or capture a Database/transaction
|
|
857
|
+
handle. Reuse a normal service method; inside an atomic callback, use its `tx`
|
|
858
|
+
argument. A service-role handle still owns a separate transaction and identity.
|
|
859
|
+
|
|
860
|
+
The build refuses `any`, `unknown`, unresolved imports, recursive/class/tuple
|
|
861
|
+
responses and unsupported JSON shapes. Supply a named Zod response schema when
|
|
862
|
+
needed. Existing named Zod annotations remain supported. Inference describes
|
|
863
|
+
the static JSON shape; an explicit schema remains useful for semantic constraints
|
|
864
|
+
such as UUID formats or a domain-specific numeric range. Input validation still
|
|
865
|
+
uses the existing `@Body`/`@QueryParams` schema.
|
|
866
|
+
|
|
867
|
+
Invalid response values and serialization failures roll the request transaction
|
|
868
|
+
back before COMMIT. A committed independent `$atomic` call retains its existing
|
|
869
|
+
independent commit semantics; response validation cannot undo an earlier commit.
|
|
870
|
+
An explicit nullable response returns HTTP 200 with JSON `null`; a response with
|
|
871
|
+
no body remains 204. This fixes nullable client contracts previously receiving an
|
|
872
|
+
empty body.
|
|
873
|
+
|
|
874
|
+
### Existing query definitions
|
|
875
|
+
|
|
876
|
+
`defineQuery` is deprecated for normal application work. Its existing `run`,
|
|
877
|
+
SQL inspection, manifests and validation remain supported for compatibility.
|
|
829
878
|
|
|
830
879
|
`defineQuery` combines an explicit selection with a reusable input/filter. The
|
|
831
880
|
result is also a Zod array schema: a controller can return
|
|
@@ -934,35 +983,60 @@ one invoker helper call with sequential statements inside PostgreSQL. The helper
|
|
|
934
983
|
adds JSON/dynamic-SQL processing; measure the actual plan and network before
|
|
935
984
|
selecting it for throughput. Fewer round trips alone do not guarantee a speedup.
|
|
936
985
|
|
|
937
|
-
|
|
986
|
+
Core owns admission for ordinary CRUD, raw queries, request transactions,
|
|
987
|
+
`$atomic`, service-role operations, upload authorization and scheduled database
|
|
988
|
+
work. Nested savepoints reuse their parent's admitted connection. Application
|
|
989
|
+
code does not declare concurrency or queue capacities:
|
|
938
990
|
|
|
939
991
|
```ts
|
|
940
|
-
const transfers = defineWorkload("transfers", {
|
|
941
|
-
concurrency: 16, queueLimit: 128,
|
|
942
|
-
tenantConcurrency: 8, tenantQueueLimit: 32, queueTimeoutMs: 1000,
|
|
943
|
-
});
|
|
944
|
-
|
|
945
992
|
await Database.$atomic(async tx => {
|
|
946
993
|
// Decision reads, idempotency claim, writes, ledger and outbox belong here.
|
|
947
994
|
}, {
|
|
948
|
-
workload: transfers,
|
|
949
|
-
conflictKeys: [fromAccountId, toAccountId],
|
|
950
995
|
timeoutMs: 3000, signal, retry: 2,
|
|
951
996
|
});
|
|
952
997
|
```
|
|
953
998
|
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
999
|
+
The core process shares one physical pool across live and candidate applications.
|
|
1000
|
+
Reloading or discarding an application does not create another pool. A retiring
|
|
1001
|
+
application drains its requests/jobs through transaction settlement before
|
|
1002
|
+
closing its own resources.
|
|
1003
|
+
|
|
1004
|
+
Before independent `$atomic` work, core releases an implicit transaction that
|
|
1005
|
+
contains only its authorization reads. This prevents requests holding the whole
|
|
1006
|
+
pool while each waits for a second connection. Role/permission decisions still
|
|
1007
|
+
come from verified identity and current database state, once per request. Once
|
|
1008
|
+
application SQL runs, its enclosing transaction is retained; atomic work never
|
|
1009
|
+
silently commits an application's earlier write or changes its identity.
|
|
1010
|
+
|
|
1011
|
+
Admission happens before BEGIN and its lease lasts through COMMIT/ROLLBACK.
|
|
1012
|
+
Full/expired queues return an explicit 429; cancellation removes queued work.
|
|
1013
|
+
The private runtime `/_internal/database` probe exposes current admission counts,
|
|
1014
|
+
rejection reasons and PostgreSQL connection groups. It is on the internal probe
|
|
1015
|
+
port, not the public edge.
|
|
1016
|
+
|
|
1017
|
+
These are resource protections, not product quotas or a transactions-per-second
|
|
1018
|
+
limit. `DB_POOL_MAX` remains a deployment setting, not a business-code parameter.
|
|
1019
|
+
It is a per-process maximum; PostgreSQL's server limit is shared by every
|
|
1020
|
+
process. Operators must account for every runtime application pool, control
|
|
1021
|
+
pool, palsvc pool, migration/admin connection and replication connection in
|
|
1022
|
+
their server resource plan. An instance-local queue is not a cluster-wide
|
|
1023
|
+
admission guarantee. Core contains no Free/Pro tier logic.
|
|
1024
|
+
|
|
1025
|
+
Optional deployment settings are `PALBASE_DB_QUEUE_LIMIT` (default: eight times
|
|
1026
|
+
the application pool size, bounded to 32–512), `PALBASE_DB_QUEUE_TIMEOUT_MS`
|
|
1027
|
+
(default: 1000), and `PALBASE_DB_TENANT_CLAIM`. These defaults bound transient
|
|
1028
|
+
memory/waiting; they are not a measured optimum for every installation. Fairness
|
|
1029
|
+
uses verified `sub` by default, with round robin under contention. One identity
|
|
1030
|
+
can use the entire available pool. A custom tenant claim must be signed and
|
|
1031
|
+
issuer-controlled; do not use editable user metadata. Missing metadata does not
|
|
1032
|
+
reduce an installation to a fraction of its pool.
|
|
1033
|
+
|
|
1034
|
+
Existing `defineWorkload` options retain their old behavior, including conflict
|
|
1035
|
+
ordering and any explicit tenant cap. They are deprecated, not silently ignored.
|
|
1036
|
+
Removing them removes that application-local scheduling policy; sorted database
|
|
1037
|
+
row locks and constraints remain the authority across all runtime instances.
|
|
1038
|
+
Do not place external service calls in a retryable callback. Use an idempotency
|
|
1039
|
+
claim and transactional outbox for effects that must survive retries.
|
|
966
1040
|
|
|
967
1041
|
`timeoutMs` includes queueing, callback work and retries. The default driver uses
|
|
968
1042
|
a separate control pool of at most two connections to cancel active PostgreSQL
|
|
@@ -1002,8 +1076,9 @@ disabled; the completion callback includes settlement timings. Only the first
|
|
|
1002
1076
|
|
|
1003
1077
|
| Metric | Meaning |
|
|
1004
1078
|
| --- | --- |
|
|
1005
|
-
| `queueMs` |
|
|
1079
|
+
| `queueMs` | Core admission and legacy workload wait, including refused waits |
|
|
1006
1080
|
| `poolMs` | Driver acquisition through transaction callback entry, including BEGIN |
|
|
1081
|
+
| `connectionMs` | Connection held from transaction callback entry through COMMIT/ROLLBACK settlement |
|
|
1007
1082
|
| `setupMs` | Isolation, cancellation ticket and role/claims setup |
|
|
1008
1083
|
| `sqlMs` | Awaited driver time, including execution, lock and network waits |
|
|
1009
1084
|
| `commitMs` / `rollbackMs` | Observed settlement waits |
|
package/docs/llms-full.txt
CHANGED
|
@@ -90,7 +90,7 @@ service the controllers call.
|
|
|
90
90
|
|
|
91
91
|
> **Never** emit `defineController`, `defineHandler`, `defineEndpoint`, `route.get(...)`,
|
|
92
92
|
> `req.input`, `req.params`, or `req.errors` — those are the removed legacy model
|
|
93
|
-
> and will not compile against `@palbase/backend`
|
|
93
|
+
> and will not compile against `@palbase/backend` 35.
|
|
94
94
|
|
|
95
95
|
### Complete CRUD example (copy-pasteable, compiles)
|
|
96
96
|
|
|
@@ -1929,7 +1929,56 @@ continue to return their rows. Custom SQL drivers must expose an integer `count`
|
|
|
1929
1929
|
on a non-returning result. Empty input opens no connection. The batch remains one
|
|
1930
1930
|
statement and participates in its enclosing transaction's rollback.
|
|
1931
1931
|
|
|
1932
|
-
##
|
|
1932
|
+
## Query results and response contracts
|
|
1933
|
+
|
|
1934
|
+
Use the ordinary CRUD expression. A controller without a return annotation gets
|
|
1935
|
+
its response validator, OpenAPI response and generated client type from the
|
|
1936
|
+
TypeScript return type, following calls through injected services:
|
|
1937
|
+
|
|
1938
|
+
```ts
|
|
1939
|
+
// Store/service
|
|
1940
|
+
list(prefix: string) {
|
|
1941
|
+
return Database.public.todos.findMany({
|
|
1942
|
+
where: { title: { startsWith: prefix } },
|
|
1943
|
+
select: ["id", "title"],
|
|
1944
|
+
limit: 50,
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
// Controller
|
|
1949
|
+
@Get("/")
|
|
1950
|
+
list(@QueryParams(ListInput) input: ListInput) {
|
|
1951
|
+
return this.todos.list(input.prefix);
|
|
1952
|
+
}
|
|
1953
|
+
```
|
|
1954
|
+
|
|
1955
|
+
The generated contract contains only `id` and `title`. `findUnique` retains
|
|
1956
|
+
nullability, `page` retains its rows/pageInfo shape, and `insertMany` with
|
|
1957
|
+
`returning: false` returns a count. Use `updateMany` for one bulk UPDATE; a
|
|
1958
|
+
command plan is useful when several dependent operations actually belong together.
|
|
1959
|
+
|
|
1960
|
+
Inference does not execute application code or capture a Database/transaction
|
|
1961
|
+
handle. Reuse a normal service method; inside an atomic callback, use its `tx`
|
|
1962
|
+
argument. A service-role handle still owns a separate transaction and identity.
|
|
1963
|
+
|
|
1964
|
+
The build refuses `any`, `unknown`, unresolved imports, recursive/class/tuple
|
|
1965
|
+
responses and unsupported JSON shapes. Supply a named Zod response schema when
|
|
1966
|
+
needed. Existing named Zod annotations remain supported. Inference describes
|
|
1967
|
+
the static JSON shape; an explicit schema remains useful for semantic constraints
|
|
1968
|
+
such as UUID formats or a domain-specific numeric range. Input validation still
|
|
1969
|
+
uses the existing `@Body`/`@QueryParams` schema.
|
|
1970
|
+
|
|
1971
|
+
Invalid response values and serialization failures roll the request transaction
|
|
1972
|
+
back before COMMIT. A committed independent `$atomic` call retains its existing
|
|
1973
|
+
independent commit semantics; response validation cannot undo an earlier commit.
|
|
1974
|
+
An explicit nullable response returns HTTP 200 with JSON `null`; a response with
|
|
1975
|
+
no body remains 204. This fixes nullable client contracts previously receiving an
|
|
1976
|
+
empty body.
|
|
1977
|
+
|
|
1978
|
+
### Existing query definitions
|
|
1979
|
+
|
|
1980
|
+
`defineQuery` is deprecated for normal application work. Its existing `run`,
|
|
1981
|
+
SQL inspection, manifests and validation remain supported for compatibility.
|
|
1933
1982
|
|
|
1934
1983
|
`defineQuery` combines an explicit selection with a reusable input/filter. The
|
|
1935
1984
|
result is also a Zod array schema: a controller can return
|
|
@@ -2038,35 +2087,60 @@ one invoker helper call with sequential statements inside PostgreSQL. The helper
|
|
|
2038
2087
|
adds JSON/dynamic-SQL processing; measure the actual plan and network before
|
|
2039
2088
|
selecting it for throughput. Fewer round trips alone do not guarantee a speedup.
|
|
2040
2089
|
|
|
2041
|
-
|
|
2090
|
+
Core owns admission for ordinary CRUD, raw queries, request transactions,
|
|
2091
|
+
`$atomic`, service-role operations, upload authorization and scheduled database
|
|
2092
|
+
work. Nested savepoints reuse their parent's admitted connection. Application
|
|
2093
|
+
code does not declare concurrency or queue capacities:
|
|
2042
2094
|
|
|
2043
2095
|
```ts
|
|
2044
|
-
const transfers = defineWorkload("transfers", {
|
|
2045
|
-
concurrency: 16, queueLimit: 128,
|
|
2046
|
-
tenantConcurrency: 8, tenantQueueLimit: 32, queueTimeoutMs: 1000,
|
|
2047
|
-
});
|
|
2048
|
-
|
|
2049
2096
|
await Database.$atomic(async tx => {
|
|
2050
2097
|
// Decision reads, idempotency claim, writes, ledger and outbox belong here.
|
|
2051
2098
|
}, {
|
|
2052
|
-
workload: transfers,
|
|
2053
|
-
conflictKeys: [fromAccountId, toAccountId],
|
|
2054
2099
|
timeoutMs: 3000, signal, retry: 2,
|
|
2055
2100
|
});
|
|
2056
2101
|
```
|
|
2057
2102
|
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2103
|
+
The core process shares one physical pool across live and candidate applications.
|
|
2104
|
+
Reloading or discarding an application does not create another pool. A retiring
|
|
2105
|
+
application drains its requests/jobs through transaction settlement before
|
|
2106
|
+
closing its own resources.
|
|
2107
|
+
|
|
2108
|
+
Before independent `$atomic` work, core releases an implicit transaction that
|
|
2109
|
+
contains only its authorization reads. This prevents requests holding the whole
|
|
2110
|
+
pool while each waits for a second connection. Role/permission decisions still
|
|
2111
|
+
come from verified identity and current database state, once per request. Once
|
|
2112
|
+
application SQL runs, its enclosing transaction is retained; atomic work never
|
|
2113
|
+
silently commits an application's earlier write or changes its identity.
|
|
2114
|
+
|
|
2115
|
+
Admission happens before BEGIN and its lease lasts through COMMIT/ROLLBACK.
|
|
2116
|
+
Full/expired queues return an explicit 429; cancellation removes queued work.
|
|
2117
|
+
The private runtime `/_internal/database` probe exposes current admission counts,
|
|
2118
|
+
rejection reasons and PostgreSQL connection groups. It is on the internal probe
|
|
2119
|
+
port, not the public edge.
|
|
2120
|
+
|
|
2121
|
+
These are resource protections, not product quotas or a transactions-per-second
|
|
2122
|
+
limit. `DB_POOL_MAX` remains a deployment setting, not a business-code parameter.
|
|
2123
|
+
It is a per-process maximum; PostgreSQL's server limit is shared by every
|
|
2124
|
+
process. Operators must account for every runtime application pool, control
|
|
2125
|
+
pool, palsvc pool, migration/admin connection and replication connection in
|
|
2126
|
+
their server resource plan. An instance-local queue is not a cluster-wide
|
|
2127
|
+
admission guarantee. Core contains no Free/Pro tier logic.
|
|
2128
|
+
|
|
2129
|
+
Optional deployment settings are `PALBASE_DB_QUEUE_LIMIT` (default: eight times
|
|
2130
|
+
the application pool size, bounded to 32–512), `PALBASE_DB_QUEUE_TIMEOUT_MS`
|
|
2131
|
+
(default: 1000), and `PALBASE_DB_TENANT_CLAIM`. These defaults bound transient
|
|
2132
|
+
memory/waiting; they are not a measured optimum for every installation. Fairness
|
|
2133
|
+
uses verified `sub` by default, with round robin under contention. One identity
|
|
2134
|
+
can use the entire available pool. A custom tenant claim must be signed and
|
|
2135
|
+
issuer-controlled; do not use editable user metadata. Missing metadata does not
|
|
2136
|
+
reduce an installation to a fraction of its pool.
|
|
2137
|
+
|
|
2138
|
+
Existing `defineWorkload` options retain their old behavior, including conflict
|
|
2139
|
+
ordering and any explicit tenant cap. They are deprecated, not silently ignored.
|
|
2140
|
+
Removing them removes that application-local scheduling policy; sorted database
|
|
2141
|
+
row locks and constraints remain the authority across all runtime instances.
|
|
2142
|
+
Do not place external service calls in a retryable callback. Use an idempotency
|
|
2143
|
+
claim and transactional outbox for effects that must survive retries.
|
|
2070
2144
|
|
|
2071
2145
|
`timeoutMs` includes queueing, callback work and retries. The default driver uses
|
|
2072
2146
|
a separate control pool of at most two connections to cancel active PostgreSQL
|
|
@@ -2106,8 +2180,9 @@ disabled; the completion callback includes settlement timings. Only the first
|
|
|
2106
2180
|
|
|
2107
2181
|
| Metric | Meaning |
|
|
2108
2182
|
| --- | --- |
|
|
2109
|
-
| `queueMs` |
|
|
2183
|
+
| `queueMs` | Core admission and legacy workload wait, including refused waits |
|
|
2110
2184
|
| `poolMs` | Driver acquisition through transaction callback entry, including BEGIN |
|
|
2185
|
+
| `connectionMs` | Connection held from transaction callback entry through COMMIT/ROLLBACK settlement |
|
|
2111
2186
|
| `setupMs` | Isolation, cancellation ticket and role/claims setup |
|
|
2112
2187
|
| `sqlMs` | Awaited driver time, including execution, lock and network waits |
|
|
2113
2188
|
| `commitMs` / `rollbackMs` | Observed settlement waits |
|
package/package.json
CHANGED
package/template/package.json
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
"version": "0.1.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
|
-
"description": "A Palbase backend
|
|
6
|
+
"description": "A Palbase backend \u2014 class controllers, a declared database, and the secrets it needs.",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"test": "./scripts/test.sh",
|
|
9
9
|
"typecheck": "tsc --noEmit"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@palbase/backend": "^
|
|
12
|
+
"@palbase/backend": "^35.0.0",
|
|
13
13
|
"reflect-metadata": "^0.2.2"
|
|
14
14
|
},
|
|
15
15
|
"engines": {
|