@palbase/backend 23.0.0 → 24.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 (74) hide show
  1. package/dist/bin/palbase-backend.cjs +695 -61
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +4 -5
  4. package/dist/bin/palbase-backend.js.map +1 -1
  5. package/dist/{chunk-FSGSB42K.js → chunk-7Z6MGMXQ.js} +64 -4
  6. package/dist/chunk-7Z6MGMXQ.js.map +1 -0
  7. package/dist/{chunk-OMRTHM4X.js → chunk-H3JAISUY.js} +136 -1
  8. package/dist/chunk-H3JAISUY.js.map +1 -0
  9. package/dist/{chunk-REZU6UKT.js → chunk-NXDH6VQJ.js} +549 -42
  10. package/dist/chunk-NXDH6VQJ.js.map +1 -0
  11. package/dist/{chunk-W5ODXPY3.js → chunk-P2Q27SGP.js} +32 -3
  12. package/dist/chunk-P2Q27SGP.js.map +1 -0
  13. package/dist/{chunk-ZC6Q2BRD.js → chunk-T5IOSOE5.js} +7 -2
  14. package/dist/chunk-T5IOSOE5.js.map +1 -0
  15. package/dist/{chunk-HAF67F2H.js → chunk-ZUGY7RGS.js} +86 -3
  16. package/dist/chunk-ZUGY7RGS.js.map +1 -0
  17. package/dist/db/index.cjs +115 -3
  18. package/dist/db/index.cjs.map +1 -1
  19. package/dist/db/index.d.cts +2 -2
  20. package/dist/db/index.d.ts +2 -2
  21. package/dist/db/index.js +2 -2
  22. package/dist/{endpoint-BavvbW4P.d.ts → endpoint-0_DGBajf.d.ts} +168 -9
  23. package/dist/{endpoint-i8TTCohk.d.cts → endpoint-CcQ1a36a.d.cts} +168 -9
  24. package/dist/engine/index.cjs +684 -48
  25. package/dist/engine/index.cjs.map +1 -1
  26. package/dist/engine/index.d.cts +4 -4
  27. package/dist/engine/index.d.ts +4 -4
  28. package/dist/engine/index.js +4 -4
  29. package/dist/{index-B3jmmItD.d.ts → index-CJiJU9ux.d.ts} +209 -36
  30. package/dist/{index-Bmvx1EvJ.d.cts → index-D-4-PNuQ.d.cts} +209 -36
  31. package/dist/{index-B7YBEG5w.d.ts → index-D17r-MKb.d.ts} +177 -7
  32. package/dist/{index-E7OscPJT.d.cts → index-DRFxf07H.d.cts} +177 -7
  33. package/dist/index.cjs +269 -7
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +54 -12
  36. package/dist/index.d.ts +54 -12
  37. package/dist/index.js +54 -11
  38. package/dist/index.js.map +1 -1
  39. package/dist/openapi/index.cjs +45 -5
  40. package/dist/openapi/index.cjs.map +1 -1
  41. package/dist/openapi/index.d.cts +9 -4
  42. package/dist/openapi/index.d.ts +9 -4
  43. package/dist/openapi/index.js +35 -11
  44. package/dist/openapi/index.js.map +1 -1
  45. package/dist/{registry-C3H2uPeZ.d.cts → registry-1X-skBNu.d.cts} +101 -7
  46. package/dist/{registry-DY3d9l1k.d.ts → registry-CEod_5sz.d.ts} +101 -7
  47. package/dist/test/index.cjs +509 -9
  48. package/dist/test/index.cjs.map +1 -1
  49. package/dist/test/index.d.cts +35 -3
  50. package/dist/test/index.d.ts +35 -3
  51. package/dist/test/index.js +507 -8
  52. package/dist/test/index.js.map +1 -1
  53. package/docs/README.md +4 -4
  54. package/docs/database.md +115 -11
  55. package/docs/getting-started.md +5 -4
  56. package/docs/llms-full.txt +385 -89
  57. package/docs/migrations.md +81 -59
  58. package/docs/schema.md +82 -2
  59. package/docs/services.md +98 -9
  60. package/package.json +3 -2
  61. package/stager/return_types.js +23 -0
  62. package/template/AGENTS.md +121 -41
  63. package/template/controllers/notes.controller.ts +64 -0
  64. package/template/package.json +1 -1
  65. package/template/services/note.service.ts +74 -0
  66. package/template/tsconfig.json +11 -1
  67. package/dist/chunk-FSGSB42K.js.map +0 -1
  68. package/dist/chunk-HAF67F2H.js.map +0 -1
  69. package/dist/chunk-OMRTHM4X.js.map +0 -1
  70. package/dist/chunk-REZU6UKT.js.map +0 -1
  71. package/dist/chunk-W5ODXPY3.js.map +0 -1
  72. package/dist/chunk-Y5HXVUMP.js +0 -90
  73. package/dist/chunk-Y5HXVUMP.js.map +0 -1
  74. package/dist/chunk-ZC6Q2BRD.js.map +0 -1
@@ -1,12 +1,12 @@
1
- import { A as AuthSpec, R as RateLimitConfig } from './endpoint-i8TTCohk.cjs';
1
+ import { A as AuthSpec, R as RateLimitConfig } from './endpoint-CcQ1a36a.cjs';
2
2
  import { ZodTypeAny } from 'zod';
3
3
  import { PalbaseBucketName } from './stack.cjs';
4
4
 
5
5
  /** A legacy method decorator (`experimentalDecorators`): `(prototype, name,
6
6
  * descriptor)`. */
7
- type MethodDecorator = (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => void;
7
+ type MethodDecorator$1 = (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => void;
8
8
  /** A legacy parameter decorator: `(prototype, name, paramIndex)`. */
9
- type ParameterDecorator = (target: object, propertyKey: string | symbol, parameterIndex: number) => void;
9
+ type ParameterDecorator$1 = (target: object, propertyKey: string | symbol, parameterIndex: number) => void;
10
10
  /**
11
11
  * Direct-storage upload settings for an `@Upload` route. The br-pod validates an
12
12
  * authorize request against these (size/type), then mints a signed upload URL
@@ -51,7 +51,7 @@ interface UploadConfig {
51
51
  * // The size limit + MIME allowlist come from the "docs" bucket ON THE STACK
52
52
  * // — storage enforces them at the PUT.
53
53
  */
54
- declare function Upload(subpath: string, config: UploadConfig & Pick<RouteOptions, "auth" | "rateLimit">): MethodDecorator;
54
+ declare function Upload(subpath: string, config: UploadConfig & Pick<RouteOptions, "auth" | "rateLimit">): MethodDecorator$1;
55
55
  /**
56
56
  * The uploaded object, injected into an `@Upload` method body by
57
57
  * `@UploadedObject()` once storage confirms the upload. Bytes are NOT present
@@ -106,7 +106,94 @@ interface UploadedObject {
106
106
  * Co-located with the {@link UploadedObject} TYPE so a single exported name
107
107
  * `UploadedObject` carries BOTH the decorator value and the type annotation.
108
108
  */
109
- declare function UploadedObject(): ParameterDecorator;
109
+ declare function UploadedObject(): ParameterDecorator$1;
110
+
111
+ /** A legacy method decorator (`experimentalDecorators`): `(prototype, name,
112
+ * descriptor)`. */
113
+ type MethodDecorator = (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => void;
114
+ /** A legacy parameter decorator: `(prototype, name, paramIndex)`. */
115
+ type ParameterDecorator = (target: object, propertyKey: string | symbol, parameterIndex: number) => void;
116
+ /**
117
+ * Settings for an `@Sse` route.
118
+ *
119
+ * EMPTY in v1, and deliberately so: the pipeline keys off this object's
120
+ * PRESENCE, not its contents (see the file header). Declaring the type now means
121
+ * a later setting arrives as a field on an existing marker rather than as a
122
+ * second marker nothing downstream reads.
123
+ */
124
+ interface SseConfig {
125
+ /**
126
+ * The shape of ONE frame — what a single `out.write(value)` carries.
127
+ *
128
+ * REQUIRED, not optional, and that is the whole point. An `@Sse` handler
129
+ * returns `Promise<void>`, so the codegen-injected `recordReturn` has no type
130
+ * to record and nothing downstream can infer what a frame is. A route without
131
+ * this declares a stream whose element type is unknown, and every generated
132
+ * client from that contract is opaque — which is precisely the failure
133
+ * `x-palbase-sse` exists to prevent. Making it optional would have left that
134
+ * failure one forgotten field away.
135
+ *
136
+ * Measured live on a real pushed stack (2026-08-29): the contract gate refused
137
+ * the deploy with "2 untyped response(s) — every generated client from it is
138
+ * opaque", naming both routes that omitted it.
139
+ */
140
+ frame: ZodTypeAny;
141
+ }
142
+ /**
143
+ * The writer injected by `@SseOut()`. Each `write` emits ONE SSE `data:` frame
144
+ * carrying `value` JSON-encoded.
145
+ *
146
+ * The FIRST `write` is load-bearing beyond its frame: it settles the request's
147
+ * database transaction. A streaming response may run for minutes, and the
148
+ * handler runs inside the request's transaction — holding one open for the life
149
+ * of a stream exhausts the connection pool, a failure invisible to a unit test
150
+ * that only dies under load. So the first frame is the point at which the
151
+ * request phase is declared over, and database access after it is refused by
152
+ * name rather than silently run against a settled transaction.
153
+ *
154
+ * The practical rule for a handler: do the database work BEFORE the first write.
155
+ */
156
+ interface SseWriter {
157
+ write(value: unknown): void;
158
+ }
159
+ /**
160
+ * `@Sse(subpath, config)` — declare a streaming route.
161
+ *
162
+ * @example
163
+ * @Sse("/chat", { frame: ChatFrame })
164
+ * async chat(@Body() b: ChatInput, @SseOut() out: SseWriter, @Signal() signal: AbortSignal) {
165
+ * const stream = await openai.chat.completions.create(
166
+ * { model: "gpt-5.6", messages: b.messages, stream: true },
167
+ * { signal },
168
+ * );
169
+ * for await (const chunk of stream) out.write(chunk);
170
+ * }
171
+ * // The client disconnects → `signal` aborts → the provider stops being pulled.
172
+ * // The signal is a plain AbortSignal, so it goes wherever the provider takes
173
+ * // one: `{ signal }` for the OpenAI and Anthropic SDKs, `abortSignal:` for the
174
+ * // Vercel AI SDK, `fetch(url, { signal })` for a raw call.
175
+ */
176
+ declare function Sse(subpath: string | undefined, config: SseConfig & Pick<RouteOptions, "auth" | "rateLimit">): MethodDecorator;
177
+ /**
178
+ * `@SseOut()` — inject the frame writer (`: SseWriter`) into an `@Sse` method
179
+ * body. Only meaningful on an `@Sse` route.
180
+ */
181
+ declare function SseOut(): ParameterDecorator;
182
+ /**
183
+ * `@Signal()` — inject the request's `AbortSignal`, which enters the aborted
184
+ * state when the client disconnects.
185
+ *
186
+ * NOT derivable from `@Req()`: `PBRequest` carries only request-scoped data —
187
+ * the typed input, route/query params, headers, the authenticated user, calling
188
+ * client metadata, trace ids, and the declared error throwers — and no signal
189
+ * (endpoint.ts:358-363).
190
+ *
191
+ * Measured 2026-08-29 on Bun: an infinite producer guarded by this signal
192
+ * stopped four frames after the client was killed, and the handler's `finally`
193
+ * ran. It is the same mechanism a NestJS handler reaches through
194
+ * `req.on("close")`.
195
+ */
196
+ declare function Signal(): ParameterDecorator;
110
197
 
111
198
  /** The HTTP verbs a route may declare, upper-cased (the runtime router +
112
199
  * OpenAPI lower-case on their own). */
@@ -123,11 +210,18 @@ interface RouteOptions {
123
210
  * openapi → codegen). The bytes go client→storage directly; the method body
124
211
  * runs as the completion handler. See {@link UploadConfig} (decorators/upload.ts). */
125
212
  uploadConfig?: UploadConfig;
213
+ /** Streaming config — present ONLY on `@Sse` routes (the `@Get`/`@Post`/…
214
+ * decorators never set it). Its presence is what MARKS a route as a streaming
215
+ * route through the whole pipeline (registry → flatten → openapi → codegen),
216
+ * exactly as `uploadConfig` does for uploads — never a special HTTP verb. An
217
+ * `@Sse` route registers POST like any input-bearing route, so the verb cannot
218
+ * carry the distinction. See {@link SseConfig} (decorators/sse.ts). */
219
+ sseConfig?: SseConfig;
126
220
  }
127
221
  /** The kind of value a parameter decorator injects. Drives both dispatch
128
222
  * (which request slice to inject) and codegen (which OpenAPI parameter source a
129
223
  * schema-bearing kind maps to). */
130
- type ParamKind = "body" | "query" | "param" | "headers" | "user" | "optionalUser" | "client" | "requestId" | "traceId" | "req" | "uploadedObject";
224
+ type ParamKind = "body" | "query" | "param" | "headers" | "user" | "optionalUser" | "client" | "requestId" | "traceId" | "req" | "uploadedObject" | "sseOut" | "signal";
131
225
  /** One parameter decorator's recorded metadata. `index` is the parameter
132
226
  * position esbuild/tsc preserve; `schema` is present for the schema-bearing
133
227
  * kinds (`body`/`query`/`headers`); `name` is the path-param name for `param`. */
@@ -179,4 +273,4 @@ declare function recordThrows(target: object, fnName: string, throws: ThrowDescr
179
273
  */
180
274
  declare function getRoutes(ctor: object): RouteMeta[];
181
275
 
182
- export { type HttpMethodUpper as H, type ParamMeta as P, type RouteMeta as R, type ThrowDescriptor as T, type UploadConfig as U, type RouteOptions as a, type ParamKind as b, Upload as c, UploadedObject as d, getRoutes as g, recordThrows as r };
276
+ export { type HttpMethodUpper as H, type ParamMeta as P, type RouteMeta as R, type SseConfig as S, type ThrowDescriptor as T, type UploadConfig as U, type RouteOptions as a, type ParamKind as b, Signal as c, Sse as d, SseOut as e, type SseWriter as f, Upload as g, UploadedObject as h, getRoutes as i, recordThrows as r };
@@ -1,12 +1,12 @@
1
- import { A as AuthSpec, R as RateLimitConfig } from './endpoint-BavvbW4P.js';
1
+ import { A as AuthSpec, R as RateLimitConfig } from './endpoint-0_DGBajf.js';
2
2
  import { ZodTypeAny } from 'zod';
3
3
  import { PalbaseBucketName } from './stack.js';
4
4
 
5
5
  /** A legacy method decorator (`experimentalDecorators`): `(prototype, name,
6
6
  * descriptor)`. */
7
- type MethodDecorator = (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => void;
7
+ type MethodDecorator$1 = (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => void;
8
8
  /** A legacy parameter decorator: `(prototype, name, paramIndex)`. */
9
- type ParameterDecorator = (target: object, propertyKey: string | symbol, parameterIndex: number) => void;
9
+ type ParameterDecorator$1 = (target: object, propertyKey: string | symbol, parameterIndex: number) => void;
10
10
  /**
11
11
  * Direct-storage upload settings for an `@Upload` route. The br-pod validates an
12
12
  * authorize request against these (size/type), then mints a signed upload URL
@@ -51,7 +51,7 @@ interface UploadConfig {
51
51
  * // The size limit + MIME allowlist come from the "docs" bucket ON THE STACK
52
52
  * // — storage enforces them at the PUT.
53
53
  */
54
- declare function Upload(subpath: string, config: UploadConfig & Pick<RouteOptions, "auth" | "rateLimit">): MethodDecorator;
54
+ declare function Upload(subpath: string, config: UploadConfig & Pick<RouteOptions, "auth" | "rateLimit">): MethodDecorator$1;
55
55
  /**
56
56
  * The uploaded object, injected into an `@Upload` method body by
57
57
  * `@UploadedObject()` once storage confirms the upload. Bytes are NOT present
@@ -106,7 +106,94 @@ interface UploadedObject {
106
106
  * Co-located with the {@link UploadedObject} TYPE so a single exported name
107
107
  * `UploadedObject` carries BOTH the decorator value and the type annotation.
108
108
  */
109
- declare function UploadedObject(): ParameterDecorator;
109
+ declare function UploadedObject(): ParameterDecorator$1;
110
+
111
+ /** A legacy method decorator (`experimentalDecorators`): `(prototype, name,
112
+ * descriptor)`. */
113
+ type MethodDecorator = (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => void;
114
+ /** A legacy parameter decorator: `(prototype, name, paramIndex)`. */
115
+ type ParameterDecorator = (target: object, propertyKey: string | symbol, parameterIndex: number) => void;
116
+ /**
117
+ * Settings for an `@Sse` route.
118
+ *
119
+ * EMPTY in v1, and deliberately so: the pipeline keys off this object's
120
+ * PRESENCE, not its contents (see the file header). Declaring the type now means
121
+ * a later setting arrives as a field on an existing marker rather than as a
122
+ * second marker nothing downstream reads.
123
+ */
124
+ interface SseConfig {
125
+ /**
126
+ * The shape of ONE frame — what a single `out.write(value)` carries.
127
+ *
128
+ * REQUIRED, not optional, and that is the whole point. An `@Sse` handler
129
+ * returns `Promise<void>`, so the codegen-injected `recordReturn` has no type
130
+ * to record and nothing downstream can infer what a frame is. A route without
131
+ * this declares a stream whose element type is unknown, and every generated
132
+ * client from that contract is opaque — which is precisely the failure
133
+ * `x-palbase-sse` exists to prevent. Making it optional would have left that
134
+ * failure one forgotten field away.
135
+ *
136
+ * Measured live on a real pushed stack (2026-08-29): the contract gate refused
137
+ * the deploy with "2 untyped response(s) — every generated client from it is
138
+ * opaque", naming both routes that omitted it.
139
+ */
140
+ frame: ZodTypeAny;
141
+ }
142
+ /**
143
+ * The writer injected by `@SseOut()`. Each `write` emits ONE SSE `data:` frame
144
+ * carrying `value` JSON-encoded.
145
+ *
146
+ * The FIRST `write` is load-bearing beyond its frame: it settles the request's
147
+ * database transaction. A streaming response may run for minutes, and the
148
+ * handler runs inside the request's transaction — holding one open for the life
149
+ * of a stream exhausts the connection pool, a failure invisible to a unit test
150
+ * that only dies under load. So the first frame is the point at which the
151
+ * request phase is declared over, and database access after it is refused by
152
+ * name rather than silently run against a settled transaction.
153
+ *
154
+ * The practical rule for a handler: do the database work BEFORE the first write.
155
+ */
156
+ interface SseWriter {
157
+ write(value: unknown): void;
158
+ }
159
+ /**
160
+ * `@Sse(subpath, config)` — declare a streaming route.
161
+ *
162
+ * @example
163
+ * @Sse("/chat", { frame: ChatFrame })
164
+ * async chat(@Body() b: ChatInput, @SseOut() out: SseWriter, @Signal() signal: AbortSignal) {
165
+ * const stream = await openai.chat.completions.create(
166
+ * { model: "gpt-5.6", messages: b.messages, stream: true },
167
+ * { signal },
168
+ * );
169
+ * for await (const chunk of stream) out.write(chunk);
170
+ * }
171
+ * // The client disconnects → `signal` aborts → the provider stops being pulled.
172
+ * // The signal is a plain AbortSignal, so it goes wherever the provider takes
173
+ * // one: `{ signal }` for the OpenAI and Anthropic SDKs, `abortSignal:` for the
174
+ * // Vercel AI SDK, `fetch(url, { signal })` for a raw call.
175
+ */
176
+ declare function Sse(subpath: string | undefined, config: SseConfig & Pick<RouteOptions, "auth" | "rateLimit">): MethodDecorator;
177
+ /**
178
+ * `@SseOut()` — inject the frame writer (`: SseWriter`) into an `@Sse` method
179
+ * body. Only meaningful on an `@Sse` route.
180
+ */
181
+ declare function SseOut(): ParameterDecorator;
182
+ /**
183
+ * `@Signal()` — inject the request's `AbortSignal`, which enters the aborted
184
+ * state when the client disconnects.
185
+ *
186
+ * NOT derivable from `@Req()`: `PBRequest` carries only request-scoped data —
187
+ * the typed input, route/query params, headers, the authenticated user, calling
188
+ * client metadata, trace ids, and the declared error throwers — and no signal
189
+ * (endpoint.ts:358-363).
190
+ *
191
+ * Measured 2026-08-29 on Bun: an infinite producer guarded by this signal
192
+ * stopped four frames after the client was killed, and the handler's `finally`
193
+ * ran. It is the same mechanism a NestJS handler reaches through
194
+ * `req.on("close")`.
195
+ */
196
+ declare function Signal(): ParameterDecorator;
110
197
 
111
198
  /** The HTTP verbs a route may declare, upper-cased (the runtime router +
112
199
  * OpenAPI lower-case on their own). */
@@ -123,11 +210,18 @@ interface RouteOptions {
123
210
  * openapi → codegen). The bytes go client→storage directly; the method body
124
211
  * runs as the completion handler. See {@link UploadConfig} (decorators/upload.ts). */
125
212
  uploadConfig?: UploadConfig;
213
+ /** Streaming config — present ONLY on `@Sse` routes (the `@Get`/`@Post`/…
214
+ * decorators never set it). Its presence is what MARKS a route as a streaming
215
+ * route through the whole pipeline (registry → flatten → openapi → codegen),
216
+ * exactly as `uploadConfig` does for uploads — never a special HTTP verb. An
217
+ * `@Sse` route registers POST like any input-bearing route, so the verb cannot
218
+ * carry the distinction. See {@link SseConfig} (decorators/sse.ts). */
219
+ sseConfig?: SseConfig;
126
220
  }
127
221
  /** The kind of value a parameter decorator injects. Drives both dispatch
128
222
  * (which request slice to inject) and codegen (which OpenAPI parameter source a
129
223
  * schema-bearing kind maps to). */
130
- type ParamKind = "body" | "query" | "param" | "headers" | "user" | "optionalUser" | "client" | "requestId" | "traceId" | "req" | "uploadedObject";
224
+ type ParamKind = "body" | "query" | "param" | "headers" | "user" | "optionalUser" | "client" | "requestId" | "traceId" | "req" | "uploadedObject" | "sseOut" | "signal";
131
225
  /** One parameter decorator's recorded metadata. `index` is the parameter
132
226
  * position esbuild/tsc preserve; `schema` is present for the schema-bearing
133
227
  * kinds (`body`/`query`/`headers`); `name` is the path-param name for `param`. */
@@ -179,4 +273,4 @@ declare function recordThrows(target: object, fnName: string, throws: ThrowDescr
179
273
  */
180
274
  declare function getRoutes(ctor: object): RouteMeta[];
181
275
 
182
- export { type HttpMethodUpper as H, type ParamMeta as P, type RouteMeta as R, type ThrowDescriptor as T, type UploadConfig as U, type RouteOptions as a, type ParamKind as b, Upload as c, UploadedObject as d, getRoutes as g, recordThrows as r };
276
+ export { type HttpMethodUpper as H, type ParamMeta as P, type RouteMeta as R, type SseConfig as S, type ThrowDescriptor as T, type UploadConfig as U, type RouteOptions as a, type ParamKind as b, Signal as c, Sse as d, SseOut as e, type SseWriter as f, Upload as g, UploadedObject as h, getRoutes as i, recordThrows as r };