@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.
- package/dist/bin/palbase-backend.cjs +2401 -1039
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +87 -51
- package/dist/bin/palbase-backend.js.map +1 -1
- package/dist/chunk-CGNN2PUH.js +213 -0
- package/dist/chunk-CGNN2PUH.js.map +1 -0
- package/dist/chunk-DRZFQRJI.js +370 -0
- package/dist/chunk-DRZFQRJI.js.map +1 -0
- package/dist/chunk-GYK6QYS4.js +708 -0
- package/dist/chunk-GYK6QYS4.js.map +1 -0
- package/dist/chunk-I3C4PFIW.js +930 -0
- package/dist/chunk-I3C4PFIW.js.map +1 -0
- package/dist/{chunk-VDF2T4AS.js → chunk-OO7R25AI.js} +1213 -591
- package/dist/chunk-OO7R25AI.js.map +1 -0
- package/dist/chunk-TS4U7NBD.js +885 -0
- package/dist/chunk-TS4U7NBD.js.map +1 -0
- package/dist/{chunk-YOY5DFQS.js → chunk-TWX6JTGJ.js} +76 -34
- package/dist/{chunk-YOY5DFQS.js.map → chunk-TWX6JTGJ.js.map} +1 -1
- package/dist/{chunk-35PNTIRN.js → chunk-VVMJEVQP.js} +63 -162
- package/dist/chunk-VVMJEVQP.js.map +1 -0
- package/dist/{chunk-7D4SUZUM.js → chunk-VXPNPVAG.js} +3 -1
- package/dist/db/env.cjs.map +1 -1
- package/dist/db/env.d.cts +2 -2
- package/dist/db/env.d.ts +2 -2
- package/dist/db/index.cjs +754 -344
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +2 -2
- package/dist/db/index.d.ts +2 -2
- package/dist/db/index.js +7 -4
- package/dist/engine/index.cjs +2339 -1006
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +6 -6
- package/dist/engine/index.d.ts +6 -6
- package/dist/engine/index.js +7 -6
- package/dist/index-BrvvxSpn.d.ts +4844 -0
- package/dist/index-Bve7BBTL.d.cts +4844 -0
- package/dist/{index-CUomTA3e.d.ts → index-NuzRCuxe.d.ts} +171 -296
- package/dist/{index-ClpDeSos.d.cts → index-VtToZmUm.d.cts} +171 -296
- package/dist/index.cjs +2694 -1169
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +165 -19
- package/dist/index.d.ts +165 -19
- package/dist/index.js +738 -477
- package/dist/index.js.map +1 -1
- package/dist/module-Dl1KFVtc.d.cts +54 -0
- package/dist/module-Dl1KFVtc.d.ts +54 -0
- package/dist/openapi/index.cjs +1330 -484
- package/dist/openapi/index.cjs.map +1 -1
- package/dist/openapi/index.d.cts +4 -2
- package/dist/openapi/index.d.ts +4 -2
- package/dist/openapi/index.js +1264 -474
- package/dist/openapi/index.js.map +1 -1
- package/dist/{registry-dZZ5JKYg.d.ts → registry-B0eyOF9x.d.ts} +1 -1
- package/dist/{registry-CC0WBQq6.d.cts → registry-Bk9_rbNd.d.cts} +1 -1
- package/dist/stack.cjs.map +1 -1
- package/dist/test/index.cjs +705 -141
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +30 -4
- package/dist/test/index.d.ts +30 -4
- package/dist/test/index.js +472 -124
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +33 -18
- package/docs/auth.md +1 -1
- package/docs/background.md +2 -2
- package/docs/database.md +221 -50
- package/docs/endpoints.md +3 -4
- package/docs/events.md +3 -3
- package/docs/getting-started.md +1 -1
- package/docs/llms-full.txt +401 -117
- package/docs/migrations.md +2 -2
- package/docs/schema.md +19 -10
- package/docs/services.md +116 -26
- package/package.json +8 -4
- package/stager/generics.js +205 -0
- package/stager/stage.js +39 -3
- package/template/AGENTS.md +110 -72
- package/template/db/public.ts +1 -1
- package/template/{controllers → modules/health}/health.controller.ts +1 -1
- package/template/modules/health/health.module.ts +24 -0
- package/template/modules/notes/note.service.test.ts +49 -0
- package/template/modules/notes/note.service.ts +108 -0
- package/template/{controllers → modules/notes}/notes.controller.ts +17 -11
- package/template/modules/notes/notes.module.ts +37 -0
- package/template/package.json +5 -3
- package/template/scripts/test.sh +33 -0
- package/template/tsconfig.json +29 -30
- package/dist/chunk-35PNTIRN.js.map +0 -1
- package/dist/chunk-CJSKYY76.js +0 -627
- package/dist/chunk-CJSKYY76.js.map +0 -1
- package/dist/chunk-CRQKCRGF.js +0 -276
- package/dist/chunk-CRQKCRGF.js.map +0 -1
- package/dist/chunk-G4R6BTLV.js +0 -662
- package/dist/chunk-G4R6BTLV.js.map +0 -1
- package/dist/chunk-VDF2T4AS.js.map +0 -1
- package/dist/chunk-XABBC7JP.js +0 -55
- package/dist/chunk-XABBC7JP.js.map +0 -1
- package/dist/endpoint-CTEHhb7A.d.ts +0 -2386
- package/dist/endpoint-DYHMo6cC.d.cts +0 -2386
- package/dist/index-CW21M9Z3.d.ts +0 -1222
- package/dist/index-CmBK76nx.d.cts +0 -1222
- package/template/services/note.service.test.ts +0 -45
- package/template/services/note.service.ts +0 -76
- /package/dist/{chunk-7D4SUZUM.js.map → chunk-VXPNPVAG.js.map} +0 -0
- /package/template/{models/notes → modules/notes/dto}/create.ts +0 -0
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__name
|
|
3
|
+
} from "./chunk-VXPNPVAG.js";
|
|
4
|
+
|
|
1
5
|
// src/decorators/registry.ts
|
|
2
6
|
var ROUTES = /* @__PURE__ */ Symbol.for("palbase.backend.routes");
|
|
3
7
|
var PARAM_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.paramBuffer");
|
|
@@ -9,24 +13,33 @@ function carrierOf(target) {
|
|
|
9
13
|
const ctor = typeof target === "function" ? target : target.constructor ?? target;
|
|
10
14
|
return ctor;
|
|
11
15
|
}
|
|
16
|
+
__name(carrierOf, "carrierOf");
|
|
12
17
|
function ownRoutes(carrier) {
|
|
13
18
|
if (!Object.prototype.hasOwnProperty.call(carrier, ROUTES)) {
|
|
14
19
|
carrier[ROUTES] = [];
|
|
15
20
|
}
|
|
16
21
|
return carrier[ROUTES];
|
|
17
22
|
}
|
|
23
|
+
__name(ownRoutes, "ownRoutes");
|
|
18
24
|
function ownParamBuffer(carrier) {
|
|
19
25
|
if (!Object.prototype.hasOwnProperty.call(carrier, PARAM_BUFFER)) {
|
|
20
26
|
carrier[PARAM_BUFFER] = {};
|
|
21
27
|
}
|
|
22
28
|
return carrier[PARAM_BUFFER];
|
|
23
29
|
}
|
|
30
|
+
__name(ownParamBuffer, "ownParamBuffer");
|
|
24
31
|
function recordRoute(target, fnName, method, subpath, options) {
|
|
25
32
|
const carrier = carrierOf(target);
|
|
26
33
|
const routes = ownRoutes(carrier);
|
|
27
34
|
const buffer = ownParamBuffer(carrier);
|
|
28
35
|
const params = (buffer[fnName] ?? []).slice().sort((a, b) => a.index - b.index);
|
|
29
|
-
const route = {
|
|
36
|
+
const route = {
|
|
37
|
+
method,
|
|
38
|
+
subpath,
|
|
39
|
+
fnName,
|
|
40
|
+
options,
|
|
41
|
+
params
|
|
42
|
+
};
|
|
30
43
|
const returnBuffer = carrier[RETURN_BUFFER];
|
|
31
44
|
if (returnBuffer && returnBuffer[fnName] !== void 0) {
|
|
32
45
|
route.returnSchema = returnBuffer[fnName];
|
|
@@ -37,6 +50,7 @@ function recordRoute(target, fnName, method, subpath, options) {
|
|
|
37
50
|
}
|
|
38
51
|
routes.push(route);
|
|
39
52
|
}
|
|
53
|
+
__name(recordRoute, "recordRoute");
|
|
40
54
|
function recordParam(target, fnName, meta) {
|
|
41
55
|
const carrier = carrierOf(target);
|
|
42
56
|
const buffer = ownParamBuffer(carrier);
|
|
@@ -50,6 +64,7 @@ function recordParam(target, fnName, meta) {
|
|
|
50
64
|
}
|
|
51
65
|
}
|
|
52
66
|
}
|
|
67
|
+
__name(recordParam, "recordParam");
|
|
53
68
|
function recordThrows(target, fnName, throws) {
|
|
54
69
|
const carrier = carrierOf(target);
|
|
55
70
|
const routes = carrier[ROUTES];
|
|
@@ -64,6 +79,7 @@ function recordThrows(target, fnName, throws) {
|
|
|
64
79
|
const throwsBuffer = carrier[THROWS_BUFFER];
|
|
65
80
|
if (throwsBuffer) throwsBuffer[fnName] = throws;
|
|
66
81
|
}
|
|
82
|
+
__name(recordThrows, "recordThrows");
|
|
67
83
|
function getRoutes(ctor) {
|
|
68
84
|
const carrier = carrierOf(ctor);
|
|
69
85
|
const routes = carrier[ROUTES] ?? [];
|
|
@@ -88,50 +104,64 @@ function getRoutes(ctor) {
|
|
|
88
104
|
return routes.map((r) => ({
|
|
89
105
|
...r,
|
|
90
106
|
params: r.params.slice(),
|
|
91
|
-
...r.throws !== void 0 ? {
|
|
107
|
+
...r.throws !== void 0 ? {
|
|
108
|
+
throws: r.throws.slice()
|
|
109
|
+
} : {}
|
|
92
110
|
}));
|
|
93
111
|
}
|
|
112
|
+
__name(getRoutes, "getRoutes");
|
|
94
113
|
function ownRoomBuffer(carrier) {
|
|
95
114
|
if (!Object.prototype.hasOwnProperty.call(carrier, ROOM_HOOKS)) {
|
|
96
|
-
carrier[ROOM_HOOKS] = {
|
|
115
|
+
carrier[ROOM_HOOKS] = {
|
|
116
|
+
hooks: {},
|
|
117
|
+
messages: {}
|
|
118
|
+
};
|
|
97
119
|
}
|
|
98
120
|
return carrier[ROOM_HOOKS];
|
|
99
121
|
}
|
|
122
|
+
__name(ownRoomBuffer, "ownRoomBuffer");
|
|
100
123
|
function recordRoomHook(target, hook, fnName) {
|
|
101
124
|
const buffer = ownRoomBuffer(carrierOf(target));
|
|
102
125
|
const existing = buffer.hooks[hook];
|
|
103
126
|
if (existing !== void 0 && existing !== fnName) {
|
|
104
127
|
const label = `On${hook.charAt(0).toUpperCase()}${hook.slice(1)}`;
|
|
105
|
-
throw new Error(
|
|
106
|
-
`@${label} is declared twice in one room (${existing} and ${fnName}). A room has one of each hook.`
|
|
107
|
-
);
|
|
128
|
+
throw new Error(`@${label} is declared twice in one room (${existing} and ${fnName}). A room has one of each hook.`);
|
|
108
129
|
}
|
|
109
130
|
buffer.hooks[hook] = fnName;
|
|
110
131
|
}
|
|
132
|
+
__name(recordRoomHook, "recordRoomHook");
|
|
111
133
|
function recordRoomMessage(target, name, fnName, schema) {
|
|
112
134
|
const buffer = ownRoomBuffer(carrierOf(target));
|
|
113
135
|
const existing = buffer.messages[name];
|
|
114
136
|
if (existing !== void 0 && existing.fnName !== fnName) {
|
|
115
|
-
throw new Error(
|
|
116
|
-
`@OnMessage("${name}") is declared twice in one room (${existing.fnName} and ${fnName}).`
|
|
117
|
-
);
|
|
137
|
+
throw new Error(`@OnMessage("${name}") is declared twice in one room (${existing.fnName} and ${fnName}).`);
|
|
118
138
|
}
|
|
119
|
-
buffer.messages[name] = {
|
|
139
|
+
buffer.messages[name] = {
|
|
140
|
+
fnName,
|
|
141
|
+
schema
|
|
142
|
+
};
|
|
120
143
|
}
|
|
144
|
+
__name(recordRoomMessage, "recordRoomMessage");
|
|
121
145
|
function recordRoom(ctor, meta) {
|
|
122
146
|
const carrier = carrierOf(ctor);
|
|
123
147
|
const buffer = ownRoomBuffer(carrier);
|
|
124
|
-
carrier[ROOM] = {
|
|
148
|
+
carrier[ROOM] = {
|
|
149
|
+
...meta,
|
|
150
|
+
hooks: buffer.hooks,
|
|
151
|
+
messages: buffer.messages
|
|
152
|
+
};
|
|
125
153
|
const g = globalThis;
|
|
126
154
|
const REGISTRY2 = /* @__PURE__ */ Symbol.for("palbase.backend.allControllers");
|
|
127
155
|
const all = g[REGISTRY2] ??= [];
|
|
128
156
|
if (!all.includes(ctor)) all.push(ctor);
|
|
129
157
|
}
|
|
158
|
+
__name(recordRoom, "recordRoom");
|
|
130
159
|
function getRoom(ctor) {
|
|
131
160
|
const carrier = carrierOf(ctor);
|
|
132
161
|
if (!Object.prototype.hasOwnProperty.call(carrier, ROOM)) return void 0;
|
|
133
162
|
return carrier[ROOM];
|
|
134
163
|
}
|
|
164
|
+
__name(getRoom, "getRoom");
|
|
135
165
|
|
|
136
166
|
// src/decorators/controller.ts
|
|
137
167
|
var CONTROLLER_META = /* @__PURE__ */ Symbol.for("palbase.backend.controllerMeta");
|
|
@@ -144,51 +174,57 @@ function registry() {
|
|
|
144
174
|
g[REGISTRY] = fresh;
|
|
145
175
|
return fresh;
|
|
146
176
|
}
|
|
177
|
+
__name(registry, "registry");
|
|
147
178
|
function getRegisteredControllers() {
|
|
148
179
|
return registry().slice();
|
|
149
180
|
}
|
|
181
|
+
__name(getRegisteredControllers, "getRegisteredControllers");
|
|
150
182
|
function __resetRegisteredControllers() {
|
|
151
183
|
registry().length = 0;
|
|
152
184
|
}
|
|
185
|
+
__name(__resetRegisteredControllers, "__resetRegisteredControllers");
|
|
153
186
|
var APP_DEFAULT_AUTH = /* @__PURE__ */ Symbol.for("palbase.backend.appDefaultAuth");
|
|
154
187
|
function appAuthSlot() {
|
|
155
188
|
return globalThis;
|
|
156
189
|
}
|
|
190
|
+
__name(appAuthSlot, "appAuthSlot");
|
|
157
191
|
function defineDefaultAuth(auth) {
|
|
158
192
|
appAuthSlot()[APP_DEFAULT_AUTH] = auth;
|
|
159
193
|
}
|
|
194
|
+
__name(defineDefaultAuth, "defineDefaultAuth");
|
|
160
195
|
function getDefaultAuth() {
|
|
161
196
|
return appAuthSlot()[APP_DEFAULT_AUTH];
|
|
162
197
|
}
|
|
198
|
+
__name(getDefaultAuth, "getDefaultAuth");
|
|
163
199
|
function __resetDefaultAuth() {
|
|
164
200
|
delete appAuthSlot()[APP_DEFAULT_AUTH];
|
|
165
201
|
}
|
|
202
|
+
__name(__resetDefaultAuth, "__resetDefaultAuth");
|
|
166
203
|
function resolveEffectiveAuth(routeAuth, controllerAuth) {
|
|
167
204
|
return routeAuth ?? controllerAuth ?? getDefaultAuth() ?? true;
|
|
168
205
|
}
|
|
206
|
+
__name(resolveEffectiveAuth, "resolveEffectiveAuth");
|
|
169
207
|
var RESERVED_FIRST_SEGMENT = "webhooks";
|
|
170
208
|
function assertNotReserved(path, subject) {
|
|
171
209
|
const [first] = path.split("/").filter(Boolean);
|
|
172
210
|
if (first === RESERVED_FIRST_SEGMENT) {
|
|
173
|
-
throw new Error(
|
|
174
|
-
`${subject} resolves under the reserved /${RESERVED_FIRST_SEGMENT} path \u2014 inbound webhooks are served there and would shadow this route`
|
|
175
|
-
);
|
|
211
|
+
throw new Error(`${subject} resolves under the reserved /${RESERVED_FIRST_SEGMENT} path \u2014 inbound webhooks are served there and would shadow this route`);
|
|
176
212
|
}
|
|
177
213
|
}
|
|
214
|
+
__name(assertNotReserved, "assertNotReserved");
|
|
178
215
|
function Controller(basePath, options = {}) {
|
|
179
216
|
return function(ctor) {
|
|
180
217
|
assertNotReserved(basePath, `@Controller("${basePath}")`);
|
|
181
218
|
for (const route of getRoutes(ctor)) {
|
|
182
|
-
assertNotReserved(
|
|
183
|
-
`${basePath}${route.subpath}`,
|
|
184
|
-
`@${route.method}("${route.subpath}") in @Controller("${basePath}")`
|
|
185
|
-
);
|
|
219
|
+
assertNotReserved(`${basePath}${route.subpath}`, `@${route.method}("${route.subpath}") in @Controller("${basePath}")`);
|
|
186
220
|
}
|
|
187
221
|
const carrier = ctor;
|
|
188
222
|
const meta = {
|
|
189
223
|
__palbase: "controller",
|
|
190
224
|
basePath,
|
|
191
|
-
...options.auth !== void 0 ? {
|
|
225
|
+
...options.auth !== void 0 ? {
|
|
226
|
+
defaultAuth: options.auth
|
|
227
|
+
} : {}
|
|
192
228
|
};
|
|
193
229
|
Object.defineProperty(carrier, CONTROLLER_META, {
|
|
194
230
|
value: meta,
|
|
@@ -207,6 +243,7 @@ function Controller(basePath, options = {}) {
|
|
|
207
243
|
return ctor;
|
|
208
244
|
};
|
|
209
245
|
}
|
|
246
|
+
__name(Controller, "Controller");
|
|
210
247
|
function isController(value) {
|
|
211
248
|
if (typeof value !== "function" && (typeof value !== "object" || value === null)) {
|
|
212
249
|
return false;
|
|
@@ -214,149 +251,25 @@ function isController(value) {
|
|
|
214
251
|
const carrier = value;
|
|
215
252
|
return carrier.__palbase === "controller" && carrier[CONTROLLER_META] !== void 0;
|
|
216
253
|
}
|
|
254
|
+
__name(isController, "isController");
|
|
217
255
|
function resolveController(ctor) {
|
|
218
256
|
if (typeof ctor !== "function" && (typeof ctor !== "object" || ctor === null)) {
|
|
219
257
|
throw new TypeError("resolveController: value is not a class");
|
|
220
258
|
}
|
|
221
259
|
const meta = ctor[CONTROLLER_META];
|
|
222
260
|
if (!meta) {
|
|
223
|
-
throw new TypeError(
|
|
224
|
-
"resolveController: class is not a @Controller \u2014 every controller file must `export default` a @Controller-decorated class"
|
|
225
|
-
);
|
|
261
|
+
throw new TypeError("resolveController: class is not a @Controller \u2014 every controller file must `export default` a @Controller-decorated class");
|
|
226
262
|
}
|
|
227
263
|
return meta;
|
|
228
264
|
}
|
|
265
|
+
__name(resolveController, "resolveController");
|
|
229
266
|
function assertZeroArgConstructor(Ctrl, kind) {
|
|
230
267
|
const arity = Ctrl.length ?? 0;
|
|
231
268
|
if (arity === 0) return;
|
|
232
269
|
const name = Ctrl.name ?? "<anonymous>";
|
|
233
|
-
throw new Error(
|
|
234
|
-
`${kind} ${name} declares a constructor with ${arity} parameter(s). A ${kind} is constructed by the runtime with a zero-argument constructor \u2014 there is no injector to supply them, so every parameter would arrive as undefined. Hold the dependency as a module-level singleton the ${kind} imports (\`const repo = makeRepo()\` beside the class), and construct the service directly in tests (e.g. \`new TodoService(fakeDatabase().db)\`).`
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// src/errors.ts
|
|
239
|
-
var HTTP_ERROR_BRAND = /* @__PURE__ */ Symbol.for("palbase.backend.httpError");
|
|
240
|
-
var ENGINE_RAISED = /* @__PURE__ */ Symbol.for("palbase.backend.engineRaised");
|
|
241
|
-
function markEngineRaised(e) {
|
|
242
|
-
e[ENGINE_RAISED] = true;
|
|
243
|
-
return e;
|
|
244
|
-
}
|
|
245
|
-
function isEngineRaised(e) {
|
|
246
|
-
return typeof e === "object" && e !== null && e[ENGINE_RAISED] === true;
|
|
247
|
-
}
|
|
248
|
-
function isHttpError(err) {
|
|
249
|
-
if (typeof err !== "object" || err === null) return false;
|
|
250
|
-
const e = err;
|
|
251
|
-
return e[HTTP_ERROR_BRAND] === true && typeof e.status === "number" && typeof e.error === "string" && typeof e.errorDescription === "string";
|
|
252
|
-
}
|
|
253
|
-
var HttpError = class extends Error {
|
|
254
|
-
status;
|
|
255
|
-
error;
|
|
256
|
-
errorDescription;
|
|
257
|
-
data;
|
|
258
|
-
/** See {@link HTTP_ERROR_BRAND} — how the engine recognises this across SDK copies. */
|
|
259
|
-
[HTTP_ERROR_BRAND] = true;
|
|
260
|
-
constructor(status, error, errorDescription, data) {
|
|
261
|
-
super(errorDescription);
|
|
262
|
-
this.name = "HttpError";
|
|
263
|
-
this.status = status;
|
|
264
|
-
this.error = error;
|
|
265
|
-
this.errorDescription = errorDescription;
|
|
266
|
-
if (data !== void 0) {
|
|
267
|
-
this.data = data;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Serialize to the standard Palbase error response format.
|
|
272
|
-
* The `requestId` is injected by the runtime layer from the request context.
|
|
273
|
-
* When called without arguments (e.g. JSON.stringify), request_id is omitted.
|
|
274
|
-
* When `data` is set, it is appended as a strict-superset field.
|
|
275
|
-
*/
|
|
276
|
-
toJSON(requestId) {
|
|
277
|
-
const result = {
|
|
278
|
-
error: this.error,
|
|
279
|
-
error_description: this.errorDescription,
|
|
280
|
-
status: this.status
|
|
281
|
-
};
|
|
282
|
-
if (requestId) {
|
|
283
|
-
result.request_id = requestId;
|
|
284
|
-
}
|
|
285
|
-
if (this.data !== void 0) {
|
|
286
|
-
result.data = this.data;
|
|
287
|
-
}
|
|
288
|
-
return result;
|
|
289
|
-
}
|
|
290
|
-
};
|
|
291
|
-
var PalError = class extends HttpError {
|
|
292
|
-
constructor(status, code, description, data) {
|
|
293
|
-
super(status, code, description, data);
|
|
294
|
-
this.name = "PalError";
|
|
295
|
-
}
|
|
296
|
-
};
|
|
297
|
-
var NamedHttpError = class extends HttpError {
|
|
298
|
-
constructor(status, defaultCode, name, message, code, data) {
|
|
299
|
-
super(status, code ?? defaultCode, message ?? defaultMessage(name), data);
|
|
300
|
-
this.name = name;
|
|
301
|
-
}
|
|
302
|
-
};
|
|
303
|
-
function defaultMessage(name) {
|
|
304
|
-
const spaced = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
305
|
-
return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();
|
|
270
|
+
throw new Error(`${kind} ${name} declares a constructor with ${arity} parameter(s), and this path builds it with a zero-argument constructor \u2014 every parameter would arrive as undefined. If you meant to inject them, add @Injectable() to ${name} and list it in a module's providers, so the container builds it instead.`);
|
|
306
271
|
}
|
|
307
|
-
|
|
308
|
-
constructor(data, message) {
|
|
309
|
-
super(400, "bad_request", "BadRequest", message, void 0, data);
|
|
310
|
-
}
|
|
311
|
-
};
|
|
312
|
-
var Unauthorized = class extends NamedHttpError {
|
|
313
|
-
constructor(message, code, data) {
|
|
314
|
-
super(401, "unauthorized", "Unauthorized", message, code, data);
|
|
315
|
-
}
|
|
316
|
-
};
|
|
317
|
-
var Forbidden = class extends NamedHttpError {
|
|
318
|
-
constructor(message, code, data) {
|
|
319
|
-
super(403, "forbidden", "Forbidden", message, code, data);
|
|
320
|
-
}
|
|
321
|
-
};
|
|
322
|
-
var NotFound = class extends NamedHttpError {
|
|
323
|
-
constructor(message, code, data) {
|
|
324
|
-
super(404, "not_found", "NotFound", message, code, data);
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
var Conflict = class extends NamedHttpError {
|
|
328
|
-
constructor(message, code, data) {
|
|
329
|
-
super(409, "conflict", "Conflict", message, code, data);
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
var UniqueViolation = class extends Conflict {
|
|
333
|
-
/**
|
|
334
|
-
* Whether `e` is a unique violation — REGARDLESS of which copy of this SDK
|
|
335
|
-
* constructed it.
|
|
336
|
-
*
|
|
337
|
-
* Use this instead of `instanceof`. Measured on a live stack: a controller
|
|
338
|
-
* bundle INLINES its own copy of `@palbase/backend`, and the engine that
|
|
339
|
-
* raises this error is the runtime's copy. Two copies, two class identities,
|
|
340
|
-
* and `e instanceof UniqueViolation` is false in the one place a caller
|
|
341
|
-
* writes it — a check that reads as correct and silently never matches.
|
|
342
|
-
*/
|
|
343
|
-
static is(e) {
|
|
344
|
-
return typeof e === "object" && e !== null && e.name === "UniqueViolation" && typeof e.constraint === "string";
|
|
345
|
-
}
|
|
346
|
-
/** The unique constraint the statement violated, as Postgres named it.
|
|
347
|
-
* `""` when the driver did not say which — see `engine/db.ts`. */
|
|
348
|
-
constraint;
|
|
349
|
-
constructor(constraint, message, code, data) {
|
|
350
|
-
super(message ?? "Unique constraint violated", code ?? "unique_violation", data);
|
|
351
|
-
this.name = "UniqueViolation";
|
|
352
|
-
this.constraint = constraint;
|
|
353
|
-
}
|
|
354
|
-
};
|
|
355
|
-
var TooManyRequests = class extends NamedHttpError {
|
|
356
|
-
constructor(data, message) {
|
|
357
|
-
super(429, "too_many_requests", "TooManyRequests", message, void 0, data);
|
|
358
|
-
}
|
|
359
|
-
};
|
|
272
|
+
__name(assertZeroArgConstructor, "assertZeroArgConstructor");
|
|
360
273
|
|
|
361
274
|
export {
|
|
362
275
|
recordRoute,
|
|
@@ -375,18 +288,6 @@ export {
|
|
|
375
288
|
Controller,
|
|
376
289
|
isController,
|
|
377
290
|
resolveController,
|
|
378
|
-
assertZeroArgConstructor
|
|
379
|
-
markEngineRaised,
|
|
380
|
-
isEngineRaised,
|
|
381
|
-
isHttpError,
|
|
382
|
-
HttpError,
|
|
383
|
-
PalError,
|
|
384
|
-
BadRequest,
|
|
385
|
-
Unauthorized,
|
|
386
|
-
Forbidden,
|
|
387
|
-
NotFound,
|
|
388
|
-
Conflict,
|
|
389
|
-
UniqueViolation,
|
|
390
|
-
TooManyRequests
|
|
291
|
+
assertZeroArgConstructor
|
|
391
292
|
};
|
|
392
|
-
//# sourceMappingURL=chunk-
|
|
293
|
+
//# sourceMappingURL=chunk-VVMJEVQP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/decorators/registry.ts","../src/decorators/controller.ts"],"sourcesContent":["// The decorator registry — the single plain-data store the method + parameter\n// decorators write into, and the deploy/dispatch pipeline reads back. No\n// `reflect-metadata`, no `emitDecoratorMetadata`: the registry is built from the\n// decorator arguments + the parameter INDEX that esbuild/tsc preserve for legacy\n// parameter decorators (verified — see the design spec §0/§4.1).\n//\n// A controller class carries its route metadata on a symbol-keyed static\n// property (`ROUTES`). `@Get`/`@Post`/… append a {@link RouteMeta} entry;\n// `@Body`/`@User`/… append a {@link ParamMeta} entry onto the route for the\n// method they decorate. Because parameter decorators run BEFORE the method\n// decorator for the same member (TS evaluates innermost-first, params before the\n// method), the route entry may not exist yet when a param decorator fires — so\n// param metadata is buffered per method name and merged when the method\n// decorator creates the route entry.\nimport type { AuthSpec, RateLimitConfig } from \"../endpoint.js\";\nimport type { UploadConfig } from \"./upload.js\";\nimport type { SseConfig } from \"./sse.js\";\nimport type { ZodTypeAny } from \"zod\";\n\n/** The HTTP verbs a route may declare, upper-cased (the runtime router +\n * OpenAPI lower-case on their own). */\nexport type HttpMethodUpper = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\" | \"QUERY\";\n\n/** Route-level options accepted by the method decorators (`@Get`/`@Post`/…). */\nexport interface RouteOptions {\n /** OVERRIDES the controller-level default auth for this one route. */\n auth?: AuthSpec;\n /** Per-route rate limit. */\n rateLimit?: RateLimitConfig;\n /** Direct-storage upload config — present ONLY on `@Upload` routes (the\n * `@Get`/`@Post`/… decorators never set it). Its presence is what MARKS a\n * route as an upload route through the whole pipeline (registry → flatten →\n * openapi → codegen). The bytes go client→storage directly; the method body\n * runs as the completion handler. See {@link UploadConfig} (decorators/upload.ts). */\n uploadConfig?: UploadConfig;\n /** Streaming config — present ONLY on `@Sse` routes (the `@Get`/`@Post`/…\n * decorators never set it). Its presence is what MARKS a route as a streaming\n * route through the whole pipeline (registry → flatten → openapi → codegen),\n * exactly as `uploadConfig` does for uploads — never a special HTTP verb. An\n * `@Sse` route registers POST like any input-bearing route, so the verb cannot\n * carry the distinction. See {@link SseConfig} (decorators/sse.ts). */\n sseConfig?: SseConfig;\n}\n\n/** The kind of value a parameter decorator injects. Drives both dispatch\n * (which request slice to inject) and codegen (which OpenAPI parameter source a\n * schema-bearing kind maps to). */\nexport type ParamKind =\n | \"body\"\n | \"query\"\n | \"param\"\n | \"headers\"\n | \"user\"\n | \"optionalUser\"\n | \"client\"\n | \"requestId\"\n | \"traceId\"\n | \"req\"\n // `@UploadedObject()` — injects the uploaded object (completion input) on an\n // `@Upload` route. No schema (the shape is the fixed UploadedObject type).\n | \"uploadedObject\"\n // `@SseOut()` — injects the frame writer on an `@Sse` route. No schema (the\n // shape is the fixed SseWriter type).\n | \"sseOut\"\n // `@Signal()` — injects the request's AbortSignal, which aborts when the\n // client disconnects. No schema. NOT derivable from `@Req()`: PBRequest\n // carries only request-scoped data and has no signal (endpoint.ts:358-363).\n | \"signal\";\n\n/** One parameter decorator's recorded metadata. `index` is the parameter\n * position esbuild/tsc preserve; `schema` is present for the schema-bearing\n * kinds (`body`/`query`/`headers`); `name` is the path-param name for `param`. */\nexport interface ParamMeta {\n index: number;\n kind: ParamKind;\n /** Zod schema for `body`/`query`/`headers` (validation + codegen source). */\n schema?: ZodTypeAny;\n /** Path-param name for `@Param(\"id\")`. */\n name?: string;\n}\n\n/** One inferred throw site: the error CLASS name (e.g. \"TodoLocked\") and its\n * wire code (e.g. \"todo_locked\"). `status`, `hasData`, and the data JSON schema\n * are NOT carried here — they resolve from the error registry by `code` at\n * extract/openapi time (single source of truth). */\nexport interface ThrowDescriptor {\n name: string;\n code: string;\n}\n\n/** One route's recorded metadata: the verb + subpath + method name + options,\n * the ordered parameter metas, and the resolved return schema (injected by the\n * codegen step — see `returnSchema`). */\nexport interface RouteMeta {\n method: HttpMethodUpper;\n subpath: string;\n fnName: string;\n options: RouteOptions;\n params: ParamMeta[];\n /** Response schema for the route, if any. Derived from the method's RETURN\n * TYPE by codegen and written here via `recordReturn` (a generated top-level\n * IIFE injected per controller), not by an author-written decorator. */\n returnSchema?: ZodTypeAny;\n /** Error classes this route can throw, if inferred. Derived from the method\n * body + service call graph by the deploy stager's throw analysis and written\n * here via `recordThrows` (a generated top-level IIFE injected per controller,\n * the `recordReturn` twin), not by an author-written decorator. */\n throws?: ThrowDescriptor[];\n}\n\n/** Symbol the route metadata list is stored under on a controller class. Using\n * a symbol (not a string key) keeps it off the public structural surface and\n * avoids any chance of an authored property collision. */\nexport const ROUTES: unique symbol = Symbol.for(\"palbase.backend.routes\");\n\n/** Symbol the per-method buffered parameter metas are stored under while a class\n * is being decorated. Parameter decorators fire before the method decorator, so\n * they buffer here keyed by method name; the method decorator drains the buffer\n * into the route entry it creates. */\nconst PARAM_BUFFER: unique symbol = Symbol.for(\"palbase.backend.paramBuffer\");\n\n/** A room's own slots. Rooms are NOT routes — no verb, no path, no params — so\n * they get their own carrier slots instead of being squeezed into RouteMeta. */\nconst ROOM: unique symbol = Symbol.for(\"palbase.backend.room\");\nconst ROOM_HOOKS: unique symbol = Symbol.for(\"palbase.backend.roomHooks\");\n\n/** Symbol the per-method buffered return-type schemas are stored under while a\n * class's registry is being populated. The codegen-injected `recordReturn` call\n * can fire before OR after the method decorator; it buffers here keyed by method\n * name and `recordRoute` drains it into the route entry (and `recordReturn`\n * writes through if the route already exists). Buffering on BOTH sides means a\n * fully-formed route entry always carries its return schema — a raw-symbol\n * reader (the runtime) never has to re-merge. */\nconst RETURN_BUFFER: unique symbol = Symbol.for(\"palbase.backend.returnBuffer\");\n\n/** Symbol the per-method buffered throw descriptors are stored under while a\n * class's registry is being populated. The stager-injected `recordThrows` call\n * can fire before OR after the method decorator; it buffers here keyed by method\n * name and `recordRoute` drains it into the route entry (and `recordThrows`\n * writes through if the route already exists). Buffering on BOTH sides means a\n * fully-formed route entry always carries its throw descriptors — a raw-symbol\n * reader (the runtime) never has to re-merge. */\nconst THROWS_BUFFER: unique symbol = Symbol.for(\"palbase.backend.throwsBuffer\");\n\n/** A class constructor carrying the symbol-keyed registry slots. We type the\n * registry-bearing class as this so the decorators can read/write the slots\n * without `any` — a plain `Function` does not carry index signatures. */\ninterface RegistryCarrier {\n [ROUTES]?: RouteMeta[];\n [PARAM_BUFFER]?: Record<string, ParamMeta[]>;\n [ROOM]?: RoomMeta;\n [ROOM_HOOKS]?: RoomBuffer;\n [RETURN_BUFFER]?: Record<string, ZodTypeAny>;\n [THROWS_BUFFER]?: Record<string, ThrowDescriptor[]>;\n}\n\n/** Coerce a decorated target (class constructor or its prototype) into the\n * registry carrier that owns the slots. Method/param decorators receive the\n * PROTOTYPE as their target; the class decorator receives the constructor. We\n * always anchor the registry on the CONSTRUCTOR so `getRoutes(ctor)` finds it. */\nfunction carrierOf(target: object): RegistryCarrier {\n // For instance-member decorators, `target` is the prototype; its `.constructor`\n // is the class. For a static member or the class decorator, `target` is the\n // constructor already. Resolve to the constructor either way.\n const ctor =\n typeof target === \"function\"\n ? (target as unknown as RegistryCarrier)\n : (((target as { constructor?: unknown }).constructor ??\n target) as unknown as RegistryCarrier);\n return ctor;\n}\n\n/** Get (creating if absent) the own route list for a class constructor. Own —\n * not inherited — so a subclass does not mutate its base's routes. */\nfunction ownRoutes(carrier: RegistryCarrier): RouteMeta[] {\n if (!Object.prototype.hasOwnProperty.call(carrier, ROUTES)) {\n carrier[ROUTES] = [];\n }\n return carrier[ROUTES] as RouteMeta[];\n}\n\n/** Get (creating if absent) the own per-method param buffer for a class. */\nfunction ownParamBuffer(carrier: RegistryCarrier): Record<string, ParamMeta[]> {\n if (!Object.prototype.hasOwnProperty.call(carrier, PARAM_BUFFER)) {\n carrier[PARAM_BUFFER] = {};\n }\n return carrier[PARAM_BUFFER] as Record<string, ParamMeta[]>;\n}\n\n/** Record a route (called by the method decorators). Drains any parameter\n * metas already buffered for `fnName` into the new route entry, then sorts them\n * by parameter index so dispatch can inject positionally. */\nexport function recordRoute(\n target: object,\n fnName: string,\n method: HttpMethodUpper,\n subpath: string,\n options: RouteOptions,\n): void {\n const carrier = carrierOf(target);\n const routes = ownRoutes(carrier);\n const buffer = ownParamBuffer(carrier);\n const params = (buffer[fnName] ?? []).slice().sort((a, b) => a.index - b.index);\n const route: RouteMeta = { method, subpath, fnName, options, params };\n // Drain a buffered return schema (the recordReturn-ran-first ordering) so the\n // route entry is complete the moment it's created — a raw-symbol consumer\n // (the runtime extractor/worker) sees the return schema without re-merging.\n const returnBuffer = carrier[RETURN_BUFFER];\n if (returnBuffer && returnBuffer[fnName] !== undefined) {\n route.returnSchema = returnBuffer[fnName];\n }\n // Same drain for buffered throw descriptors (the recordThrows-ran-first\n // ordering) — the route entry is complete the moment it's created.\n const throwsBuffer = carrier[THROWS_BUFFER];\n if (throwsBuffer && throwsBuffer[fnName] !== undefined) {\n route.throws = throwsBuffer[fnName];\n }\n routes.push(route);\n}\n\n/** Record one parameter decorator (called by `@Body`/`@User`/…). Buffers per\n * method name; the method decorator merges the buffer into the route entry. If\n * the route already exists (method decorator ran first — TS does evaluate the\n * method decorator AFTER its parameter decorators, but we stay order-robust),\n * the meta is also appended directly so neither ordering loses it. */\nexport function recordParam(target: object, fnName: string, meta: ParamMeta): void {\n const carrier = carrierOf(target);\n const buffer = ownParamBuffer(carrier);\n (buffer[fnName] ??= []).push(meta);\n\n // Order-robust: if the route already exists, merge in place + keep sorted.\n const routes = carrier[ROUTES];\n if (routes) {\n const route = routes.find((r) => r.fnName === fnName);\n if (route) {\n route.params.push(meta);\n route.params.sort((a, b) => a.index - b.index);\n }\n }\n}\n\n/** Attach a return schema to the route for `fnName` (called by the codegen\n * injection that reads the method's return type). If the route does not exist\n * yet, the schema is buffered (RETURN_BUFFER) and drained into the route by\n * `recordRoute` when the method decorator runs. */\nexport function recordReturn(target: object, fnName: string, schema: ZodTypeAny): void {\n const carrier = carrierOf(target);\n const routes = carrier[ROUTES];\n const route = routes?.find((r) => r.fnName === fnName);\n if (route) {\n route.returnSchema = schema;\n return;\n }\n if (!Object.prototype.hasOwnProperty.call(carrier, RETURN_BUFFER)) {\n carrier[RETURN_BUFFER] = {};\n }\n const returnBuffer = carrier[RETURN_BUFFER];\n if (returnBuffer) returnBuffer[fnName] = schema;\n}\n\n/** Attach the inferred throw descriptors to the route for `fnName` (called by\n * the stager-injected IIFE that carries the throw analysis result — the\n * `recordReturn` twin). If the route does not exist yet, the descriptors are\n * buffered (THROWS_BUFFER) and drained into the route by `recordRoute` when the\n * method decorator runs. */\nexport function recordThrows(target: object, fnName: string, throws: ThrowDescriptor[]): void {\n const carrier = carrierOf(target);\n const routes = carrier[ROUTES];\n const route = routes?.find((r) => r.fnName === fnName);\n if (route) {\n route.throws = throws;\n return;\n }\n if (!Object.prototype.hasOwnProperty.call(carrier, THROWS_BUFFER)) {\n carrier[THROWS_BUFFER] = {};\n }\n const throwsBuffer = carrier[THROWS_BUFFER];\n if (throwsBuffer) throwsBuffer[fnName] = throws;\n}\n\n/** Read the route metadata for a controller class (the deploy/dispatch entry\n * point). Applies any buffered return schemas + throw descriptors (for the\n * recordReturn/recordThrows-runs-before orderings) and returns a defensive copy\n * so callers cannot mutate the registry.\n */\nexport function getRoutes(ctor: object): RouteMeta[] {\n const carrier = carrierOf(ctor);\n const routes = carrier[ROUTES] ?? [];\n const returnBuffer = carrier[RETURN_BUFFER];\n if (returnBuffer) {\n for (const route of routes) {\n const buffered = returnBuffer[route.fnName];\n if (buffered && route.returnSchema === undefined) {\n route.returnSchema = buffered;\n }\n }\n }\n const throwsBuffer = carrier[THROWS_BUFFER];\n if (throwsBuffer) {\n for (const route of routes) {\n const buffered = throwsBuffer[route.fnName];\n if (buffered && route.throws === undefined) {\n route.throws = buffered;\n }\n }\n }\n return routes.map((r) => ({\n ...r,\n params: r.params.slice(),\n ...(r.throws !== undefined ? { throws: r.throws.slice() } : {}),\n }));\n}\n\n// ── rooms ───────────────────────────────────────────────────────────────────\n\n/** Which lifecycle hook a method is bound to. */\nexport type RoomHook = \"authorize\" | \"first\" | \"join\" | \"leave\" | \"empty\";\n\nexport interface RoomMessageMeta {\n fnName: string;\n schema: ZodTypeAny;\n}\n\nexport interface RoomMeta {\n pattern: string;\n events: Record<string, ZodTypeAny>;\n graceMs: number;\n /** hook → the method name that implements it. */\n hooks: Partial<Record<RoomHook, string>>;\n /** inbound message name → its method and payload schema. */\n messages: Record<string, RoomMessageMeta>;\n}\n\ninterface RoomBuffer {\n hooks: Partial<Record<RoomHook, string>>;\n messages: Record<string, RoomMessageMeta>;\n}\n\n/** Get (creating if absent) the own hook buffer. Own — not inherited — so a\n * subclass never mutates its base's hooks. Member decorators fill this BEFORE\n * the class decorator runs; controller.ts:203 depends on the same ordering. */\nfunction ownRoomBuffer(carrier: RegistryCarrier): RoomBuffer {\n if (!Object.prototype.hasOwnProperty.call(carrier, ROOM_HOOKS)) {\n carrier[ROOM_HOOKS] = { hooks: {}, messages: {} };\n }\n return carrier[ROOM_HOOKS] as RoomBuffer;\n}\n\n/** Record a lifecycle hook. A second method for the same hook is refused: two\n * answers to \"who handles this\" cannot be resolved at dispatch, and silently\n * keeping one is how a hook stops running without anyone being told. */\nexport function recordRoomHook(target: object, hook: RoomHook, fnName: string): void {\n const buffer = ownRoomBuffer(carrierOf(target));\n const existing = buffer.hooks[hook];\n if (existing !== undefined && existing !== fnName) {\n const label = `On${hook.charAt(0).toUpperCase()}${hook.slice(1)}`;\n throw new Error(\n `@${label} is declared twice in one room (${existing} and ${fnName}). ` +\n `A room has one of each hook.`,\n );\n }\n buffer.hooks[hook] = fnName;\n}\n\n/** Record an inbound message handler. */\nexport function recordRoomMessage(\n target: object,\n name: string,\n fnName: string,\n schema: ZodTypeAny,\n): void {\n const buffer = ownRoomBuffer(carrierOf(target));\n const existing = buffer.messages[name];\n if (existing !== undefined && existing.fnName !== fnName) {\n throw new Error(\n `@OnMessage(\"${name}\") is declared twice in one room (${existing.fnName} and ${fnName}).`,\n );\n }\n buffer.messages[name] = { fnName, schema };\n}\n\n/** Record the room itself (called by the class decorator), draining the buffer\n * the member decorators already filled. */\nexport function recordRoom(\n ctor: object,\n meta: Omit<RoomMeta, \"hooks\" | \"messages\">,\n): void {\n const carrier = carrierOf(ctor);\n const buffer = ownRoomBuffer(carrier);\n carrier[ROOM] = { ...meta, hooks: buffer.hooks, messages: buffer.messages };\n // AND into the shared class registry, the same slot @Controller pushes to.\n //\n // Without this line a room compiles, bundles and deploys, and is never called:\n // the bundler's entry exports `SDK.getRegisteredControllers()` and the runtime\n // reads its rooms out of THAT list (server.ts's `collectRooms`). A room that\n // only marks its own constructor is a room nobody can find — measured, on the\n // fixture, at the last gate before it would have worked.\n //\n // Rooms and controllers share one list because they are one thing to the\n // loader: classes a bundle declared. What each IS is decided by the marker it\n // carries, never by which list it arrived in.\n const g = globalThis as unknown as Record<symbol, unknown[] | undefined>;\n const REGISTRY = Symbol.for(\"palbase.backend.allControllers\");\n const all = (g[REGISTRY] ??= []);\n if (!all.includes(ctor)) all.push(ctor);\n}\n\n/** The room a class declares, or undefined. A class with no `@Room` is not a\n * room — the marker is the CONFIG's presence, never a name or a base class\n * (the rule upload.ts:13-16 states for uploads). */\nexport function getRoom(ctor: object): RoomMeta | undefined {\n const carrier = carrierOf(ctor);\n if (!Object.prototype.hasOwnProperty.call(carrier, ROOM)) return undefined;\n return carrier[ROOM] as RoomMeta;\n}\n","// `@Controller(basePath, options?)` — the class decorator that marks a class as\n// a Palbase backend controller. It stamps a non-enumerable `__palbase`\n// discriminant + the resolved controller metadata onto the class so the\n// deploy/dispatch pipeline (and `isController`/`resolveController`) can detect\n// and read it without `reflect-metadata`.\nimport type { AuthSpec } from \"../endpoint.js\";\nimport { getRoutes } from \"./registry.js\";\n\n/** The controller metadata stamped onto a `@Controller`-decorated class. The\n * default export of a `controllers/*.controller.ts` file resolves to this via\n * {@link resolveController}. */\nexport interface ControllerMeta {\n /** Discriminant the runtime + tooling read. */\n readonly __palbase: \"controller\";\n /** The base path every route in this controller mounts under (e.g. \"/todos\"). */\n basePath: string;\n /** Controller-level default auth, applied to routes that don't set their own\n * (`@Get(\"/x\", { auth })` overrides this). `undefined` ⇒ the application\n * default ({@link defineDefaultAuth}), and secure-by-default below that —\n * see {@link resolveEffectiveAuth} for the whole cascade. */\n defaultAuth?: AuthSpec;\n}\n\n/** Options accepted by `@Controller`. */\nexport interface ControllerOptions {\n /** Default auth for ALL routes in this controller (route-level overrides;\n * omitting it falls through to the application default declared with\n * {@link defineDefaultAuth}). */\n auth?: AuthSpec;\n}\n\n/** Symbol the controller metadata is stamped under. Symbol-keyed (not a string\n * property) so it never collides with an authored member and stays off the\n * structural surface. */\nexport const CONTROLLER_META: unique symbol = Symbol.for(\"palbase.backend.controllerMeta\");\n\n/**\n * Every class `@Controller` has decorated, in decoration order.\n *\n * This is what lets a controller file need no export at all: importing the file\n * runs the decorator, the decorator records the class here, and the runtime\n * reads the list. Without it the only handle on a class is its export name, so\n * every controller had to be exported AND named in a generated entry — the\n * ceremony NestJS still charges (`export class` PLUS\n * `@Module({controllers:[…]})`).\n *\n * Keyed on a well-known Symbol against globalThis rather than held in a module\n * variable, because a deployed bundle inlines its own copy of this package: two\n * copies would keep two lists, and the runtime would read the empty one. The\n * same hazard `runtimeHooks` exists for, closed the same way — one shared slot.\n */\nconst REGISTRY: unique symbol = Symbol.for(\"palbase.backend.allControllers\") as never;\n\nfunction registry(): unknown[] {\n const g = globalThis as unknown as Record<symbol, unknown[] | undefined>;\n const existing = g[REGISTRY];\n if (existing) return existing;\n const fresh: unknown[] = [];\n g[REGISTRY] = fresh;\n return fresh;\n}\n\n/**\n * The controller classes this process has loaded, in decoration order.\n *\n * Decoration order is import order, which the bundler fixes by sorting the\n * files it emits imports for — so two builds of one tree produce the same\n * route table, and route precedence is not a function of module-resolution\n * accidents.\n */\nexport function getRegisteredControllers(): readonly unknown[] {\n return registry().slice();\n}\n\n/** Empty the registry. For tests, which load controllers repeatedly. */\nexport function __resetRegisteredControllers(): void {\n registry().length = 0;\n}\n\n/**\n * The APPLICATION-level default auth.\n *\n * Held on globalThis under a well-known Symbol for exactly the reason\n * {@link REGISTRY} is: a deployed bundle inlines its own copy of this package,\n * and two copies keeping two defaults is how a security setting silently\n * becomes two different settings.\n */\nconst APP_DEFAULT_AUTH: unique symbol = Symbol.for(\"palbase.backend.appDefaultAuth\") as never;\n\nfunction appAuthSlot(): Record<symbol, AuthSpec | undefined> {\n return globalThis as unknown as Record<symbol, AuthSpec | undefined>;\n}\n\n/**\n * Declare the default auth for EVERY route in the application — the ring the\n * cascade consults when neither the route nor its controller says anything.\n *\n * The measured problem it removes: `auth: { verifiedEmail: true }` repeated by\n * hand on ten `@Controller`s. A security setting that must be repeated is a\n * security setting that will be forgotten — the eleventh controller opens the\n * door and nothing says so.\n *\n * Call it at MODULE SCOPE in a file the application imports (the controllers'\n * own barrel, or a module a controller imports). The cascade reads this slot\n * when the route table is built and when the spec is emitted — both of which\n * run after module loading — so declaration order does not matter, but being\n * imported at all does.\n *\n * @example\n * defineDefaultAuth({ verifiedEmail: true }); // every route, unless it says otherwise\n */\nexport function defineDefaultAuth(auth: AuthSpec): void {\n appAuthSlot()[APP_DEFAULT_AUTH] = auth;\n}\n\n/** The declared application default, or `undefined` when none was declared. */\nexport function getDefaultAuth(): AuthSpec | undefined {\n return appAuthSlot()[APP_DEFAULT_AUTH];\n}\n\n/** Clear the application default. For tests, which declare it repeatedly. */\nexport function __resetDefaultAuth(): void {\n delete appAuthSlot()[APP_DEFAULT_AUTH];\n}\n\n/**\n * THE auth cascade: route → controller → application → `true`.\n *\n * One function, every caller — the route table (`engine/router.ts`) and the\n * spec emitter (`openapi/controllers.ts`) ASK for the answer instead of\n * spelling the chain themselves. Two hand-written copies of a cascade is how\n * the build-time answer and the runtime answer come to disagree about who may\n * call an endpoint, and the disagreement shows up as an open door.\n *\n * The terminal `true` is secure-by-default and is load-bearing: a route that\n * declared nothing, under a controller that declared nothing, in an\n * application that declared nothing, is CLOSED.\n */\nexport function resolveEffectiveAuth(\n routeAuth: AuthSpec | undefined,\n controllerAuth: AuthSpec | undefined,\n): AuthSpec {\n return routeAuth ?? controllerAuth ?? getDefaultAuth() ?? true;\n}\n\n/** A class carrying the stamped controller metadata + discriminant. */\ninterface ControllerCarrier {\n __palbase?: \"controller\";\n [CONTROLLER_META]?: ControllerMeta;\n}\n\n/** The one path segment the platform owns. The isolate matches\n * `^/webhooks/([^/]+)$` on the raw request path BEFORE controller dispatch, so\n * anything a controller resolves to under it answers `404 webhook_not_found`\n * and never runs. */\nconst RESERVED_FIRST_SEGMENT = \"webhooks\";\n\n/**\n * Throw if `path` resolves under the reserved segment. Segments are compared the\n * way the isolate compares them — `split(\"/\").filter(Boolean)` — NOT by string\n * prefix, because empty segments collapse there: `@Controller(\"/\")` +\n * `@Post(\"/webhooks/x\")` composes to `//webhooks/x`, which the isolate serves as\n * `/webhooks/x`. A prefix check reads that as safe; the segment check does not.\n * `/webhooksy` stays allowed for the same reason — it is a different segment.\n *\n * Every verb is refused, not just the POST the isolate currently intercepts: the\n * reservation is of the URL namespace, so a `@Get(\"/webhooks/x\")` that happens\n * to work today would be silently shadowed the moment the isolate's method gate\n * widens. Refusing at build is recoverable; discovering it as a 404 is not.\n */\nfunction assertNotReserved(path: string, subject: string): void {\n const [first] = path.split(\"/\").filter(Boolean);\n if (first === RESERVED_FIRST_SEGMENT) {\n throw new Error(\n `${subject} resolves under the reserved /${RESERVED_FIRST_SEGMENT} path — ` +\n \"inbound webhooks are served there and would shadow this route\",\n );\n }\n}\n\n/**\n * Mark a class as a Palbase backend controller. `basePath` is the mount path\n * for every route the class declares; `options.auth` sets the controller-level\n * default auth (a route's own `auth` overrides it; absent ⇒ secure-by-default).\n *\n * @example\n * \\@Controller(\"/todos\", { auth: false })\n * export class TodosController {\n * \\@Get(\"\") list(\\@QueryParams(ListTodosQuery) q: ListTodosQuery): TodoSchema[] { … }\n * }\n */\nexport function Controller(basePath: string, options: ControllerOptions = {}) {\n return function <T extends abstract new (...args: never[]) => object>(ctor: T): T {\n // /webhooks/* belongs to the platform: the isolate matches the inbound\n // webhook route before controller dispatch, so a controller mounted here\n // would never receive a request. Silent shadowing is the failure mode this\n // whole change exists to remove, so refuse it at build.\n //\n // The COMPOSED path is what gets shadowed, not the base path. `@Controller(\"\")`\n // and `@Controller(\"/\")` both pass a base-path-only check while a\n // `@Post(\"/webhooks/stripe\")` inside them resolves to exactly the path the\n // isolate intercepts. Method decorators run BEFORE the class decorator (TS\n // evaluates members first), so every route this class declares is already in\n // the registry here — which is why the composed check can live at this one\n // seam instead of on the dispatch read path. The `@Controller(\"\") +\n // @Post(\"/webhooks/stripe\")` test is the lock on that ordering: if it ever\n // stopped holding, that test goes red.\n assertNotReserved(basePath, `@Controller(\"${basePath}\")`);\n for (const route of getRoutes(ctor)) {\n assertNotReserved(\n `${basePath}${route.subpath}`,\n `@${route.method}(\"${route.subpath}\") in @Controller(\"${basePath}\")`,\n );\n }\n\n const carrier = ctor as unknown as ControllerCarrier;\n const meta: ControllerMeta = {\n __palbase: \"controller\",\n basePath,\n ...(options.auth !== undefined ? { defaultAuth: options.auth } : {}),\n };\n // Non-enumerable so it doesn't leak onto instances / structural checks.\n Object.defineProperty(carrier, CONTROLLER_META, {\n value: meta,\n enumerable: false,\n configurable: true,\n writable: false,\n });\n // The bare `__palbase` discriminant is the cheap detection marker the\n // runtime/extractor checks; keep it readable but non-enumerable.\n Object.defineProperty(carrier, \"__palbase\", {\n value: \"controller\",\n enumerable: false,\n configurable: true,\n writable: false,\n });\n // Record it, so importing the file is enough and exporting is optional.\n // Guarded against a double-decoration re-entering the same class twice.\n const all = registry();\n if (!all.includes(ctor)) all.push(ctor);\n return ctor;\n };\n}\n\n/** True when `value` is a `@Controller`-decorated class (cheap discriminant\n * check). Accepts the class constructor (the default export of a controller\n * file). */\nexport function isController(value: unknown): boolean {\n if (typeof value !== \"function\" && (typeof value !== \"object\" || value === null)) {\n return false;\n }\n const carrier = value as ControllerCarrier;\n return carrier.__palbase === \"controller\" && carrier[CONTROLLER_META] !== undefined;\n}\n\n/** Read the resolved controller metadata off a `@Controller`-decorated class.\n * Throws if the class was not decorated — callers should gate with\n * {@link isController} first (the loader does). */\nexport function resolveController(ctor: unknown): ControllerMeta {\n if (typeof ctor !== \"function\" && (typeof ctor !== \"object\" || ctor === null)) {\n throw new TypeError(\"resolveController: value is not a class\");\n }\n const meta = (ctor as ControllerCarrier)[CONTROLLER_META];\n if (!meta) {\n throw new TypeError(\n \"resolveController: class is not a @Controller — every controller file must `export default` a @Controller-decorated class\",\n );\n }\n return meta;\n}\n\n/**\n * A class the runtime constructs takes NO constructor parameters.\n *\n * ONE writer, four callers (controller, hook, job, webhook) and the build's own\n * check. Four hand-written copies of this message is how the four come to\n * disagree about what is refused — and the disagreement is silent, because a\n * class that slips past one of them still ends up with `undefined` fields.\n *\n * Why it is refused rather than injected: there is no container. The parameter\n * would arrive `undefined`, the code would compile, deploy, and fail at the\n * first request that touches the field — the most expensive place to learn it.\n */\nexport function assertZeroArgConstructor(Ctrl: unknown, kind: string): void {\n const arity = (Ctrl as { length?: number }).length ?? 0;\n if (arity === 0) return;\n\n // This function's NAME is a promise: whatever reaches it will be constructed\n // with a zero-argument constructor. Letting a class with parameters through\n // would make the name a lie — worse than the rename FR-029 guards against.\n //\n // An earlier design made it \"declaration-aware\": pass when metadata matches\n // arity. Measured, that re-opens the exact silence this exists to close.\n // ANY decorator triggers metadata emission, not just `@Injectable`:\n //\n // @Job(...) class J { constructor(repo: unknown) {} }\n // → design:paramtypes = [Object] ← matches arity\n //\n // so J would pass, then be built with `new J()`, and `repo` would be\n // `undefined` at the first scheduled run. Four tests in this repo asserted\n // that refusal and all four went red; the tests were right.\n //\n // What changed instead is the MESSAGE, and who calls this. Callers holding a\n // container resolve from it and never come here (router.ts, job/hook/webhook);\n // this stays the guard for the container-less path, and keeps its promise.\n const name = (Ctrl as { name?: string }).name ?? \"<anonymous>\";\n throw new Error(\n `${kind} ${name} declares a constructor with ${arity} parameter(s), and this path ` +\n `builds it with a zero-argument constructor — every parameter would arrive as ` +\n `undefined. If you meant to inject them, add @Injectable() to ${name} and list it ` +\n `in a module's providers, so the container builds it instead.`,\n );\n}\n"],"mappings":"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","names":["ROUTES","Symbol","for","PARAM_BUFFER","ROOM","ROOM_HOOKS","RETURN_BUFFER","THROWS_BUFFER","carrierOf","target","ctor","ownRoutes","carrier","Object","prototype","hasOwnProperty","call","ownParamBuffer","recordRoute","fnName","method","subpath","options","routes","buffer","params","slice","sort","a","b","index","route","returnBuffer","undefined","returnSchema","throwsBuffer","throws","push","recordParam","meta","find","r","recordThrows","target","fnName","throws","carrier","carrierOf","routes","ROUTES","route","find","r","Object","prototype","hasOwnProperty","call","THROWS_BUFFER","throwsBuffer","getRoutes","ctor","returnBuffer","RETURN_BUFFER","buffered","returnSchema","undefined","map","params","slice","ownRoomBuffer","ROOM_HOOKS","hooks","messages","recordRoomHook","hook","buffer","existing","label","charAt","toUpperCase","Error","recordRoomMessage","name","schema","recordRoom","meta","ROOM","g","globalThis","REGISTRY","Symbol","for","all","includes","push","getRoom","CONTROLLER_META","Symbol","for","REGISTRY","registry","g","globalThis","existing","fresh","getRegisteredControllers","slice","__resetRegisteredControllers","length","APP_DEFAULT_AUTH","appAuthSlot","defineDefaultAuth","auth","getDefaultAuth","__resetDefaultAuth","resolveEffectiveAuth","routeAuth","controllerAuth","RESERVED_FIRST_SEGMENT","assertNotReserved","path","subject","first","split","filter","Boolean","Error","Controller","basePath","options","ctor","route","getRoutes","subpath","method","carrier","meta","__palbase","undefined","defaultAuth","Object","defineProperty","value","enumerable","configurable","writable","all","includes","push","isController","resolveController","TypeError","assertZeroArgConstructor","Ctrl","kind","arity","name"]}
|
|
@@ -4,6 +4,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __getProtoOf = Object.getPrototypeOf;
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
8
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
9
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
10
|
}) : x)(function(x) {
|
|
@@ -31,8 +32,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
32
|
));
|
|
32
33
|
|
|
33
34
|
export {
|
|
35
|
+
__name,
|
|
34
36
|
__require,
|
|
35
37
|
__commonJS,
|
|
36
38
|
__toESM
|
|
37
39
|
};
|
|
38
|
-
//# sourceMappingURL=chunk-
|
|
40
|
+
//# sourceMappingURL=chunk-VXPNPVAG.js.map
|
package/dist/db/env.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/env.ts"],"sourcesContent":["/**\n * `@palbase/backend/env` — the controlled global augmentation target for the\n * project's schema.\n *\n * `Database.
|
|
1
|
+
{"version":3,"sources":["../../src/db/env.ts"],"sourcesContent":["/**\n * `@palbase/backend/env` — the controlled global augmentation target for the\n * project's schema.\n *\n * `Database.public.<name>` is typed against the `Tables` interface declared\n * here. By default `Tables` is EMPTY; the generated `palbase-env.d.ts`\n * (emitted from the project's `db/*.ts` schema files by {@link makeEnvDts})\n * augments it with one\n * member per table:\n *\n * // palbase-env.d.ts (generated — do not edit)\n * declare module \"@palbase/backend/env\" {\n * interface Tables {\n * todos: { row: {...}; insert: {...} };\n * }\n * }\n *\n * Because a `br-<ref>` pod is single-project, augmenting one global `Tables`\n * interface is safe — there is no cross-project \"leak\" to worry about (a pod\n * only ever compiles one project's schema). This is the C5 decision: typed\n * `Database.public.*` with NO import and NO generic in handler code.\n *\n * Authors who want the row type explicitly can import it:\n *\n * import type { Tables } from \"@palbase/backend/env\";\n * type Todo = Tables[\"todos\"][\"row\"];\n */\n\n/**\n * One typed table's shapes and its relations:\n *\n * - `row` — the full row, every column present.\n * - `insert` — the write payload, required vs optional columns.\n * - `relations` — every foreign key this table takes part in, named, in both\n * directions. `{ list: { to: \"lists\", kind: \"one\", via: \"list_id\" } }` on the\n * child; `{ todos: { to: \"todos\", kind: \"many\", via: \"list_id\" } }` on the\n * parent. Ownership declared with `ownedByUser()` appears as `owner`.\n *\n * The generated `palbase-env.d.ts` fills `row`/`insert` with flat object types —\n * no `ColumnBuilder` phantom types ever appear in the generated output — and\n * `relations` with string literals.\n */\nexport interface TableTypes {\n row: Record<string, unknown>;\n insert: Record<string, unknown>;\n /**\n * The table's relations, derived from its declared foreign keys.\n *\n * Replaces the old `owner` / `children` pair, which nothing ever read: `owner`\n * was whichever column happened to reference `auth.users` FIRST in declaration\n * order, and `children` silently kept only ONE foreign key per parent.\n *\n * `to` is the target's table key — bare for `public`, schema-qualified\n * otherwise, the same convention `RefJSON.table` uses.\n */\n relations: Record<string, { to: string; kind: \"one\" | \"many\"; via: string }>;\n}\n\n/**\n * The project's tables, keyed by table name. EMPTY by default; the generated\n * `palbase-env.d.ts` augments this interface (module augmentation), so\n * `Database.tables` is typed everywhere with no per-file import.\n */\n// biome-ignore lint/suspicious/noEmptyInterface: augmentation target — filled by generated palbase-env.d.ts.\nexport interface Tables {}\n\n// biome-ignore lint/suspicious/noEmptyInterface: augmentation target — filled by generated palbase-env.d.ts.\n/**\n * Tables in schemas other than `public`, reached with\n * `Database.schema(\"<name>\").tables.*`. Declaring a schema does not put it on\n * the internet — see `exposed`.\n */\nexport interface Schemas {}\n\n// BUCKETS MOVED TO `@palbase/backend/stack` (2026-08-29).\n//\n// `BucketTypes` and `Buckets` lived here because `config/storage.ts` declared\n// buckets and the deploy folded them into `palbase-env.d.ts` beside the schema's\n// tables. Buckets never came from the schema, and they do not come from a\n// declaration any more: the STACK holds them, so their types are generated into\n// `palbase-stack.d.ts` with the secrets and flags.\n//\n// Re-exported here so `Storage.buckets` keeps its one import site and no caller\n// has to learn that the source moved.\nexport type { BucketTypes, Buckets } from \"../stack.js\";\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;;","names":[]}
|
package/dist/db/env.d.cts
CHANGED
|
@@ -4,7 +4,7 @@ export { BucketTypes, Buckets } from '../stack.cjs';
|
|
|
4
4
|
* `@palbase/backend/env` — the controlled global augmentation target for the
|
|
5
5
|
* project's schema.
|
|
6
6
|
*
|
|
7
|
-
* `Database.
|
|
7
|
+
* `Database.public.<name>` is typed against the `Tables` interface declared
|
|
8
8
|
* here. By default `Tables` is EMPTY; the generated `palbase-env.d.ts`
|
|
9
9
|
* (emitted from the project's `db/*.ts` schema files by {@link makeEnvDts})
|
|
10
10
|
* augments it with one
|
|
@@ -20,7 +20,7 @@ export { BucketTypes, Buckets } from '../stack.cjs';
|
|
|
20
20
|
* Because a `br-<ref>` pod is single-project, augmenting one global `Tables`
|
|
21
21
|
* interface is safe — there is no cross-project "leak" to worry about (a pod
|
|
22
22
|
* only ever compiles one project's schema). This is the C5 decision: typed
|
|
23
|
-
* `Database.
|
|
23
|
+
* `Database.public.*` with NO import and NO generic in handler code.
|
|
24
24
|
*
|
|
25
25
|
* Authors who want the row type explicitly can import it:
|
|
26
26
|
*
|
package/dist/db/env.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { BucketTypes, Buckets } from '../stack.js';
|
|
|
4
4
|
* `@palbase/backend/env` — the controlled global augmentation target for the
|
|
5
5
|
* project's schema.
|
|
6
6
|
*
|
|
7
|
-
* `Database.
|
|
7
|
+
* `Database.public.<name>` is typed against the `Tables` interface declared
|
|
8
8
|
* here. By default `Tables` is EMPTY; the generated `palbase-env.d.ts`
|
|
9
9
|
* (emitted from the project's `db/*.ts` schema files by {@link makeEnvDts})
|
|
10
10
|
* augments it with one
|
|
@@ -20,7 +20,7 @@ export { BucketTypes, Buckets } from '../stack.js';
|
|
|
20
20
|
* Because a `br-<ref>` pod is single-project, augmenting one global `Tables`
|
|
21
21
|
* interface is safe — there is no cross-project "leak" to worry about (a pod
|
|
22
22
|
* only ever compiles one project's schema). This is the C5 decision: typed
|
|
23
|
-
* `Database.
|
|
23
|
+
* `Database.public.*` with NO import and NO generic in handler code.
|
|
24
24
|
*
|
|
25
25
|
* Authors who want the row type explicitly can import it:
|
|
26
26
|
*
|