@palbase/backend 28.0.0 → 30.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/bin/palbase-backend.cjs +166 -12
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +3 -3
- package/dist/{chunk-RVP6BTEZ.js → chunk-5J5W75A6.js} +15 -4
- package/dist/chunk-5J5W75A6.js.map +1 -0
- package/dist/{chunk-IVZERLTM.js → chunk-ENZ2RFFJ.js} +2 -2
- package/dist/{chunk-SNDXY565.js → chunk-SG4UTNOP.js} +5 -2
- package/dist/chunk-SG4UTNOP.js.map +1 -0
- package/dist/{chunk-75YROPRZ.js → chunk-VN7NMDUH.js} +166 -15
- package/dist/chunk-VN7NMDUH.js.map +1 -0
- package/dist/db/index.cjs +4 -1
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +2 -2
- package/dist/engine/index.cjs +166 -12
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +3 -3
- package/dist/engine/index.d.ts +3 -3
- package/dist/engine/index.js +3 -3
- package/dist/{index-dTTLlHIn.d.ts → index-8qy3kIuA.d.ts} +228 -12
- package/dist/{index-DtISj9QX.d.cts → index-DtCgaZAg.d.cts} +228 -12
- package/dist/{index-9C3JHxg-.d.cts → index-Zi7MptvP.d.cts} +30 -8
- package/dist/{index-BbvOoZFr.d.ts → index-b-Q3l7W5.d.ts} +30 -8
- package/dist/index.cjs +66 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -11
- package/dist/index.d.ts +51 -11
- package/dist/index.js +52 -3
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.d.cts +2 -2
- package/dist/openapi/index.d.ts +2 -2
- package/dist/{registry-JQNIX-eA.d.ts → registry-BGJ-Al6F.d.ts} +1 -1
- package/dist/{registry-qIPM5BQe.d.cts → registry-CEYLH-Iz.d.cts} +1 -1
- package/dist/test/index.cjs +56 -0
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.js +56 -0
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +1 -1
- package/docs/database.md +49 -0
- package/docs/llms-full.txt +50 -1
- package/package.json +1 -1
- package/template/package.json +1 -1
- package/dist/chunk-75YROPRZ.js.map +0 -1
- package/dist/chunk-RVP6BTEZ.js.map +0 -1
- package/dist/chunk-SNDXY565.js.map +0 -1
- /package/dist/{chunk-IVZERLTM.js.map → chunk-ENZ2RFFJ.js.map} +0 -0
package/docs/README.md
CHANGED
|
@@ -73,7 +73,7 @@ service the controllers call.
|
|
|
73
73
|
|
|
74
74
|
> **Never** emit `defineController`, `defineHandler`, `defineEndpoint`, `route.get(...)`,
|
|
75
75
|
> `req.input`, `req.params`, or `req.errors` — those are the removed legacy model
|
|
76
|
-
> and will not compile against `@palbase/backend`
|
|
76
|
+
> and will not compile against `@palbase/backend` 30.
|
|
77
77
|
|
|
78
78
|
### Complete CRUD example (copy-pasteable, compiles)
|
|
79
79
|
|
package/docs/database.md
CHANGED
|
@@ -261,8 +261,57 @@ shift into the wrong column — both silently. A differing row is refused with
|
|
|
261
261
|
its index and the columns that differ. Write `null` for a column you do not
|
|
262
262
|
have, or insert differently-shaped rows in separate calls.
|
|
263
263
|
|
|
264
|
+
### Totals — `aggregate`
|
|
265
|
+
|
|
266
|
+
`count` was the only aggregate, so "the total on this account" meant raw SQL or
|
|
267
|
+
pulling every row and adding it up in JavaScript. The second is wrong twice:
|
|
268
|
+
it is an N-row read, and money loses precision the moment it becomes a JS
|
|
269
|
+
number.
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
const r = await Database.public.entries.aggregate({
|
|
273
|
+
where: { account_id },
|
|
274
|
+
sum: ["amount_kurus"],
|
|
275
|
+
count: true,
|
|
276
|
+
});
|
|
277
|
+
r.sum.amount_kurus; // string | null — exact
|
|
278
|
+
r.count; // number
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
`sum` and `avg` return **strings**, always. Measured on live pg16:
|
|
282
|
+
`sum(numeric)` = `1000041.50821234567890124`, which as a JS number becomes
|
|
283
|
+
`1000041.5082123457` — seven digits gone; `sum(bigint)` = `18014398509481987`
|
|
284
|
+
becomes `18014398509481988`, a different number. The engine casts both to text
|
|
285
|
+
so the driver never gets the chance.
|
|
286
|
+
|
|
287
|
+
`| null` is not decoration: Postgres answers an empty set with NULL, not 0
|
|
288
|
+
(measured: `sum(...) IS NULL` while `count(*)` = 0). "No rows" and "the total
|
|
289
|
+
was zero" are different answers, and the type makes you say which one you mean.
|
|
290
|
+
|
|
291
|
+
`sum` and `avg` are offered on numeric columns only — the column's Postgres type
|
|
292
|
+
is in the type, so `sum(["note"])` on a text column is a compile error rather
|
|
293
|
+
than `function sum(text) does not exist`.
|
|
294
|
+
|
|
295
|
+
Add `groupBy` and the result becomes an array, each row carrying its group:
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
const rows = await Database.public.entries.aggregate({
|
|
299
|
+
where: { household_id },
|
|
300
|
+
groupBy: ["category"],
|
|
301
|
+
sum: ["amount_kurus"],
|
|
302
|
+
});
|
|
303
|
+
// rows[0].category, rows[0].sum.amount_kurus
|
|
304
|
+
```
|
|
305
|
+
|
|
264
306
|
### Bulk writes refuse an empty filter
|
|
265
307
|
|
|
308
|
+
For a bulk job, pass `returning: false` and `updateMany` resolves to a COUNT
|
|
309
|
+
instead of the rows — the same shape `deleteMany` already has. Measured at
|
|
310
|
+
200k rows on live pg16: `RETURNING *` costs 55% more server time (7.2s → 11.1s)
|
|
311
|
+
and 45 MB of JS heap, 236 bytes per row, so a million-row update carries 225 MB
|
|
312
|
+
for rows nobody reads. Reading the returned rows is right for most calls and
|
|
313
|
+
stays the default.
|
|
314
|
+
|
|
266
315
|
`updateMany({}, …)` and `deleteMany({})` throw with the table named. `UPDATE …
|
|
267
316
|
WHERE true` is a whole-table write, and the way to reach it by accident — a
|
|
268
317
|
filter object built from request input that happened to come back empty — is
|
package/docs/llms-full.txt
CHANGED
|
@@ -81,7 +81,7 @@ service the controllers call.
|
|
|
81
81
|
|
|
82
82
|
> **Never** emit `defineController`, `defineHandler`, `defineEndpoint`, `route.get(...)`,
|
|
83
83
|
> `req.input`, `req.params`, or `req.errors` — those are the removed legacy model
|
|
84
|
-
> and will not compile against `@palbase/backend`
|
|
84
|
+
> and will not compile against `@palbase/backend` 30.
|
|
85
85
|
|
|
86
86
|
### Complete CRUD example (copy-pasteable, compiles)
|
|
87
87
|
|
|
@@ -1147,8 +1147,57 @@ shift into the wrong column — both silently. A differing row is refused with
|
|
|
1147
1147
|
its index and the columns that differ. Write `null` for a column you do not
|
|
1148
1148
|
have, or insert differently-shaped rows in separate calls.
|
|
1149
1149
|
|
|
1150
|
+
### Totals — `aggregate`
|
|
1151
|
+
|
|
1152
|
+
`count` was the only aggregate, so "the total on this account" meant raw SQL or
|
|
1153
|
+
pulling every row and adding it up in JavaScript. The second is wrong twice:
|
|
1154
|
+
it is an N-row read, and money loses precision the moment it becomes a JS
|
|
1155
|
+
number.
|
|
1156
|
+
|
|
1157
|
+
```ts
|
|
1158
|
+
const r = await Database.public.entries.aggregate({
|
|
1159
|
+
where: { account_id },
|
|
1160
|
+
sum: ["amount_kurus"],
|
|
1161
|
+
count: true,
|
|
1162
|
+
});
|
|
1163
|
+
r.sum.amount_kurus; // string | null — exact
|
|
1164
|
+
r.count; // number
|
|
1165
|
+
```
|
|
1166
|
+
|
|
1167
|
+
`sum` and `avg` return **strings**, always. Measured on live pg16:
|
|
1168
|
+
`sum(numeric)` = `1000041.50821234567890124`, which as a JS number becomes
|
|
1169
|
+
`1000041.5082123457` — seven digits gone; `sum(bigint)` = `18014398509481987`
|
|
1170
|
+
becomes `18014398509481988`, a different number. The engine casts both to text
|
|
1171
|
+
so the driver never gets the chance.
|
|
1172
|
+
|
|
1173
|
+
`| null` is not decoration: Postgres answers an empty set with NULL, not 0
|
|
1174
|
+
(measured: `sum(...) IS NULL` while `count(*)` = 0). "No rows" and "the total
|
|
1175
|
+
was zero" are different answers, and the type makes you say which one you mean.
|
|
1176
|
+
|
|
1177
|
+
`sum` and `avg` are offered on numeric columns only — the column's Postgres type
|
|
1178
|
+
is in the type, so `sum(["note"])` on a text column is a compile error rather
|
|
1179
|
+
than `function sum(text) does not exist`.
|
|
1180
|
+
|
|
1181
|
+
Add `groupBy` and the result becomes an array, each row carrying its group:
|
|
1182
|
+
|
|
1183
|
+
```ts
|
|
1184
|
+
const rows = await Database.public.entries.aggregate({
|
|
1185
|
+
where: { household_id },
|
|
1186
|
+
groupBy: ["category"],
|
|
1187
|
+
sum: ["amount_kurus"],
|
|
1188
|
+
});
|
|
1189
|
+
// rows[0].category, rows[0].sum.amount_kurus
|
|
1190
|
+
```
|
|
1191
|
+
|
|
1150
1192
|
### Bulk writes refuse an empty filter
|
|
1151
1193
|
|
|
1194
|
+
For a bulk job, pass `returning: false` and `updateMany` resolves to a COUNT
|
|
1195
|
+
instead of the rows — the same shape `deleteMany` already has. Measured at
|
|
1196
|
+
200k rows on live pg16: `RETURNING *` costs 55% more server time (7.2s → 11.1s)
|
|
1197
|
+
and 45 MB of JS heap, 236 bytes per row, so a million-row update carries 225 MB
|
|
1198
|
+
for rows nobody reads. Reading the returned rows is right for most calls and
|
|
1199
|
+
stays the default.
|
|
1200
|
+
|
|
1152
1201
|
`updateMany({}, …)` and `deleteMany({})` throw with the table named. `UPDATE …
|
|
1153
1202
|
WHERE true` is a whole-table write, and the way to reach it by accident — a
|
|
1154
1203
|
filter object built from request input that happened to come back empty — is
|
package/package.json
CHANGED