@palbase/backend 25.1.0 → 27.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.
Files changed (104) hide show
  1. package/dist/bin/palbase-backend.cjs +2401 -1039
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +87 -51
  4. package/dist/bin/palbase-backend.js.map +1 -1
  5. package/dist/chunk-CGNN2PUH.js +213 -0
  6. package/dist/chunk-CGNN2PUH.js.map +1 -0
  7. package/dist/chunk-DRZFQRJI.js +370 -0
  8. package/dist/chunk-DRZFQRJI.js.map +1 -0
  9. package/dist/chunk-GYK6QYS4.js +708 -0
  10. package/dist/chunk-GYK6QYS4.js.map +1 -0
  11. package/dist/chunk-I3C4PFIW.js +930 -0
  12. package/dist/chunk-I3C4PFIW.js.map +1 -0
  13. package/dist/{chunk-VDF2T4AS.js → chunk-OO7R25AI.js} +1213 -591
  14. package/dist/chunk-OO7R25AI.js.map +1 -0
  15. package/dist/chunk-TS4U7NBD.js +885 -0
  16. package/dist/chunk-TS4U7NBD.js.map +1 -0
  17. package/dist/{chunk-YOY5DFQS.js → chunk-TWX6JTGJ.js} +76 -34
  18. package/dist/{chunk-YOY5DFQS.js.map → chunk-TWX6JTGJ.js.map} +1 -1
  19. package/dist/{chunk-35PNTIRN.js → chunk-VVMJEVQP.js} +63 -162
  20. package/dist/chunk-VVMJEVQP.js.map +1 -0
  21. package/dist/{chunk-7D4SUZUM.js → chunk-VXPNPVAG.js} +3 -1
  22. package/dist/db/env.cjs.map +1 -1
  23. package/dist/db/env.d.cts +2 -2
  24. package/dist/db/env.d.ts +2 -2
  25. package/dist/db/index.cjs +754 -344
  26. package/dist/db/index.cjs.map +1 -1
  27. package/dist/db/index.d.cts +2 -2
  28. package/dist/db/index.d.ts +2 -2
  29. package/dist/db/index.js +7 -4
  30. package/dist/engine/index.cjs +2339 -1006
  31. package/dist/engine/index.cjs.map +1 -1
  32. package/dist/engine/index.d.cts +6 -6
  33. package/dist/engine/index.d.ts +6 -6
  34. package/dist/engine/index.js +7 -6
  35. package/dist/index-BrvvxSpn.d.ts +4844 -0
  36. package/dist/index-Bve7BBTL.d.cts +4844 -0
  37. package/dist/{index-CUomTA3e.d.ts → index-NuzRCuxe.d.ts} +171 -296
  38. package/dist/{index-ClpDeSos.d.cts → index-VtToZmUm.d.cts} +171 -296
  39. package/dist/index.cjs +2694 -1169
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.cts +165 -19
  42. package/dist/index.d.ts +165 -19
  43. package/dist/index.js +738 -477
  44. package/dist/index.js.map +1 -1
  45. package/dist/module-Dl1KFVtc.d.cts +54 -0
  46. package/dist/module-Dl1KFVtc.d.ts +54 -0
  47. package/dist/openapi/index.cjs +1330 -484
  48. package/dist/openapi/index.cjs.map +1 -1
  49. package/dist/openapi/index.d.cts +4 -2
  50. package/dist/openapi/index.d.ts +4 -2
  51. package/dist/openapi/index.js +1264 -474
  52. package/dist/openapi/index.js.map +1 -1
  53. package/dist/{registry-dZZ5JKYg.d.ts → registry-B0eyOF9x.d.ts} +1 -1
  54. package/dist/{registry-CC0WBQq6.d.cts → registry-Bk9_rbNd.d.cts} +1 -1
  55. package/dist/stack.cjs.map +1 -1
  56. package/dist/test/index.cjs +705 -141
  57. package/dist/test/index.cjs.map +1 -1
  58. package/dist/test/index.d.cts +30 -4
  59. package/dist/test/index.d.ts +30 -4
  60. package/dist/test/index.js +472 -124
  61. package/dist/test/index.js.map +1 -1
  62. package/docs/README.md +33 -18
  63. package/docs/auth.md +1 -1
  64. package/docs/background.md +2 -2
  65. package/docs/database.md +221 -50
  66. package/docs/endpoints.md +3 -4
  67. package/docs/events.md +3 -3
  68. package/docs/getting-started.md +1 -1
  69. package/docs/llms-full.txt +401 -117
  70. package/docs/migrations.md +2 -2
  71. package/docs/schema.md +19 -10
  72. package/docs/services.md +116 -26
  73. package/package.json +8 -4
  74. package/stager/generics.js +205 -0
  75. package/stager/stage.js +39 -3
  76. package/template/AGENTS.md +110 -72
  77. package/template/db/public.ts +1 -1
  78. package/template/{controllers → modules/health}/health.controller.ts +1 -1
  79. package/template/modules/health/health.module.ts +24 -0
  80. package/template/modules/notes/note.service.test.ts +49 -0
  81. package/template/modules/notes/note.service.ts +108 -0
  82. package/template/{controllers → modules/notes}/notes.controller.ts +17 -11
  83. package/template/modules/notes/notes.module.ts +37 -0
  84. package/template/package.json +5 -3
  85. package/template/scripts/test.sh +33 -0
  86. package/template/tsconfig.json +29 -30
  87. package/dist/chunk-35PNTIRN.js.map +0 -1
  88. package/dist/chunk-CJSKYY76.js +0 -627
  89. package/dist/chunk-CJSKYY76.js.map +0 -1
  90. package/dist/chunk-CRQKCRGF.js +0 -276
  91. package/dist/chunk-CRQKCRGF.js.map +0 -1
  92. package/dist/chunk-G4R6BTLV.js +0 -662
  93. package/dist/chunk-G4R6BTLV.js.map +0 -1
  94. package/dist/chunk-VDF2T4AS.js.map +0 -1
  95. package/dist/chunk-XABBC7JP.js +0 -55
  96. package/dist/chunk-XABBC7JP.js.map +0 -1
  97. package/dist/endpoint-CTEHhb7A.d.ts +0 -2386
  98. package/dist/endpoint-DYHMo6cC.d.cts +0 -2386
  99. package/dist/index-CW21M9Z3.d.ts +0 -1222
  100. package/dist/index-CmBK76nx.d.cts +0 -1222
  101. package/template/services/note.service.test.ts +0 -45
  102. package/template/services/note.service.ts +0 -76
  103. /package/dist/{chunk-7D4SUZUM.js.map → chunk-VXPNPVAG.js.map} +0 -0
  104. /package/template/{models/notes → modules/notes/dto}/create.ts +0 -0
@@ -1,45 +0,0 @@
1
- import { test } from "node:test";
2
- import assert from "node:assert/strict";
3
-
4
- import { NoteService } from "./note.service.ts";
5
-
6
- // WHY THIS TEST NEEDS NO DATABASE
7
- //
8
- // `NoteService` is handed the table it works on rather than reaching for the
9
- // singleton itself. That constructor is the seam: a stand-in goes in here, and
10
- // the logic — which rows, whose, in what order — is exercised without a
11
- // database. Test your own services the same way.
12
- //
13
- // When you want the whole database surface instead of one table, `fakeDatabase()`
14
- // from `@palbase/backend/test` is the stand-in.
15
- //
16
- // Node's ESM resolver wants the extension on a relative import inside a test
17
- // (`./note.service.ts`); this scaffold's `tsconfig.json` allows it.
18
-
19
- test("list asks only for the caller's notes", async () => {
20
- const seen: unknown[] = [];
21
- const notes = {
22
- findMany: async (where: unknown) => {
23
- seen.push(where);
24
- return [];
25
- },
26
- };
27
-
28
- await new NoteService(notes as never).list("u_1");
29
-
30
- assert.deepEqual(seen, [{ user_id: "u_1" }]);
31
- });
32
-
33
- test("create writes ownership from the argument, never from the body", async () => {
34
- const written: unknown[] = [];
35
- const notes = {
36
- insert: async (row: unknown) => {
37
- written.push(row);
38
- return row;
39
- },
40
- };
41
-
42
- await new NoteService(notes as never).create("u_1", "hello");
43
-
44
- assert.deepEqual(written, [{ user_id: "u_1", body: "hello" }]);
45
- });
@@ -1,76 +0,0 @@
1
- import { Database } from "@palbase/backend";
2
- import type { Tables } from "@palbase/backend/env";
3
-
4
- // A SERVICE is where the logic lives. It is a plain class — no decorator, no DI
5
- // container, nothing to register. What makes the layer real is the two lines at
6
- // the bottom of this file, not the folder name.
7
- //
8
- // THE SEAM IS THE CONSTRUCTOR. The class is handed the table it works on rather
9
- // than reaching for the singleton itself, so a test constructs it with a
10
- // stand-in and never needs a database:
11
- //
12
- // const svc = new NoteService(fakeNotesTable);
13
- // assert.deepEqual(await svc.list("u_1"), []);
14
- //
15
- // The module then wires the real one ONCE, on the last line, and that instance
16
- // is what a controller imports. That module-level singleton is also the
17
- // supported way to hold a dependency ANYWHERE in this runtime: a controller,
18
- // hook, job or webhook is constructed with no arguments, and one that declares
19
- // a constructor parameter is REFUSED, with the class named — there is no
20
- // injector to fill it, so the field would otherwise be `undefined` in
21
- // production. The refusal fires at decoration time for a job, hook or webhook,
22
- // and when the route table is built at boot for a controller; either way the
23
- // release never serves.
24
- //
25
- // `Database.tables.notes` is typed from `db/public.ts` through the generated
26
- // `palbase-env.d.ts`, which `palbase build` writes. Before the first build that
27
- // file does not exist yet and the table is unknown to the type checker; build
28
- // once and the whole surface below is typed with no import and no generic.
29
-
30
- /** One row of `notes`, exactly as `db/public.ts` declares it. */
31
- export type Note = Tables["notes"]["row"];
32
-
33
- /** The typed surface of one table: `insert`, `update`, `delete`, `findById`,
34
- * `findMany`. Naming it here is what keeps the seam ONE table wide — a test
35
- * fake implements five methods, not the whole `Database`. */
36
- type NotesTable = typeof Database.tables.notes;
37
-
38
- export class NoteService {
39
- private readonly notes: NotesTable;
40
-
41
- // Assigned in the BODY, never as a parameter property (`constructor(private
42
- // notes: NotesTable)`). `npm test` runs Node's type-stripping test runner,
43
- // which refuses parameter properties — and refuses them for the whole FILE,
44
- // so one of them anywhere in a test's import graph reads like a dozen broken
45
- // tests and is one keyword.
46
- constructor(notes: NotesTable) {
47
- this.notes = notes;
48
- }
49
-
50
- /** The caller's notes. The filter is written out even though the table's RLS
51
- * policy already scopes the read to `auth.uid()`: the policy is the backstop
52
- * that holds when a query forgets, not a reason to stop writing the query. */
53
- list(userId: string): Promise<Note[]> {
54
- return this.notes.findMany({ user_id: userId });
55
- }
56
-
57
- /** `user_id` is `notNull` with no default, so ownership is written here —
58
- * it is not something the request body may carry. */
59
- create(userId: string, body: string): Promise<Note> {
60
- return this.notes.insert({ user_id: userId, body });
61
- }
62
-
63
- /** No `userId` argument, and that is not an oversight: another user's note is
64
- * invisible to this read, so it comes back `null` exactly as a missing id
65
- * does. Postgres enforces it, not this method. */
66
- get(id: string): Promise<Note | null> {
67
- return this.notes.findById(id);
68
- }
69
-
70
- remove(id: string): Promise<void> {
71
- return this.notes.delete(id);
72
- }
73
- }
74
-
75
- /** The wired instance. Controllers import THIS, never the class. */
76
- export const noteService = new NoteService(Database.tables.notes);