typion 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +4 -2
  3. package/bin/typion.js +7 -0
  4. package/dist/auth/better-auth.d.ts +13 -0
  5. package/dist/auth/better-auth.js +11 -0
  6. package/dist/auth/better-auth.js.map +1 -0
  7. package/dist/cli/index.d.ts +14 -0
  8. package/dist/cli/index.js +883 -0
  9. package/dist/cli/index.js.map +1 -0
  10. package/dist/column-kinds-Bkm4NJI2.js +47 -0
  11. package/dist/column-kinds-Bkm4NJI2.js.map +1 -0
  12. package/dist/context-B4e2eREP.d.ts +2100 -0
  13. package/dist/contract/index.d.ts +137 -0
  14. package/dist/contract/index.js +42 -0
  15. package/dist/contract/index.js.map +1 -0
  16. package/dist/db/pglite.d.ts +12 -0
  17. package/dist/db/pglite.js +27 -0
  18. package/dist/db/pglite.js.map +1 -0
  19. package/dist/descriptor-Cc3mGp73.js +12 -0
  20. package/dist/descriptor-Cc3mGp73.js.map +1 -0
  21. package/dist/errors-ChDBxeS-.js +17 -0
  22. package/dist/errors-ChDBxeS-.js.map +1 -0
  23. package/dist/errors-D-CKO-px.js +114 -0
  24. package/dist/errors-D-CKO-px.js.map +1 -0
  25. package/dist/handler-BWVU8TCR.d.ts +23 -0
  26. package/dist/handler-_87bFkbP.js +565 -0
  27. package/dist/handler-_87bFkbP.js.map +1 -0
  28. package/dist/hono/index.d.ts +17 -0
  29. package/dist/hono/index.js +26 -0
  30. package/dist/hono/index.js.map +1 -0
  31. package/dist/http/index.d.ts +13 -0
  32. package/dist/http/index.js +2 -0
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.js +1324 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/issues-DLqCBQT1.js +42 -0
  37. package/dist/issues-DLqCBQT1.js.map +1 -0
  38. package/dist/next/index.d.ts +8 -0
  39. package/dist/next/index.js +18 -0
  40. package/dist/next/index.js.map +1 -0
  41. package/dist/operations-D3rOacLX.js +1811 -0
  42. package/dist/operations-D3rOacLX.js.map +1 -0
  43. package/dist/schema/index.d.ts +18 -0
  44. package/dist/schema/index.js +3 -0
  45. package/dist/schema-Rzzq97cK.js +68 -0
  46. package/dist/schema-Rzzq97cK.js.map +1 -0
  47. package/dist/seed/index.d.ts +19 -0
  48. package/dist/seed/index.js +85 -0
  49. package/dist/seed/index.js.map +1 -0
  50. package/dist/structural-BZnjFmCs.js +247 -0
  51. package/dist/structural-BZnjFmCs.js.map +1 -0
  52. package/dist/testing/index.d.ts +26 -0
  53. package/dist/testing/index.js +92 -0
  54. package/dist/testing/index.js.map +1 -0
  55. package/dist/typion-Dh2BCSRz.js +1187 -0
  56. package/dist/typion-Dh2BCSRz.js.map +1 -0
  57. package/dist/validation/zod.d.ts +21 -0
  58. package/dist/validation/zod.js +84 -0
  59. package/dist/validation/zod.js.map +1 -0
  60. package/docs/README.md +145 -0
  61. package/docs/access.md +114 -0
  62. package/docs/auth.md +195 -0
  63. package/docs/collections.md +172 -0
  64. package/docs/database.md +76 -0
  65. package/docs/features/localization.md +112 -0
  66. package/docs/features/multi-tenant.md +166 -0
  67. package/docs/features/soft-delete.md +85 -0
  68. package/docs/features/versions.md +106 -0
  69. package/docs/hooks.md +106 -0
  70. package/docs/http.md +188 -0
  71. package/docs/queries.md +200 -0
  72. package/docs/testing.md +85 -0
  73. package/package.json +125 -3
@@ -0,0 +1,565 @@
1
+ import { a as InternalError, l as TypionError, s as NotFoundError, t as BadRequestError } from "./errors-D-CKO-px.js";
2
+ import { B as pluginOptions, n as entityOperations, q as isPlainObject, t as entityOperation } from "./operations-D3rOacLX.js";
3
+ import { t as ERROR_STATUS } from "./errors-ChDBxeS-.js";
4
+ //#region src/http/codec.ts
5
+ const jsonCodec = {
6
+ contentType: "application/json",
7
+ stringify: (value) => JSON.stringify(value, (_key, v) => typeof v === "bigint" ? v.toString() : v),
8
+ parse: (text) => JSON.parse(text)
9
+ };
10
+ const SUPERJSON_TYPE = "application/superjson";
11
+ let superjson = null;
12
+ function loadSuperjson() {
13
+ superjson ??= import(
14
+ /* webpackIgnore: true */
15
+ /* turbopackIgnore: true */
16
+ "superjson"
17
+ ).then((mod) => {
18
+ const sj = mod.default ?? mod;
19
+ return {
20
+ contentType: SUPERJSON_TYPE,
21
+ stringify: (value) => sj.stringify(value),
22
+ parse: (text) => sj.parse(text)
23
+ };
24
+ });
25
+ return superjson;
26
+ }
27
+ //#endregion
28
+ //#region src/http/wire.ts
29
+ const CONFIG_KEYS = /* @__PURE__ */ new Set([
30
+ "columns",
31
+ "with",
32
+ "where",
33
+ "orderBy",
34
+ "offset",
35
+ "limit"
36
+ ]);
37
+ const FIELD_OPERATORS = /* @__PURE__ */ new Set([
38
+ "eq",
39
+ "ne",
40
+ "gt",
41
+ "gte",
42
+ "lt",
43
+ "lte",
44
+ "in",
45
+ "notIn",
46
+ "like",
47
+ "ilike",
48
+ "notLike",
49
+ "notIlike",
50
+ "isNull",
51
+ "isNotNull",
52
+ "arrayContains",
53
+ "arrayContained",
54
+ "arrayOverlaps",
55
+ "NOT",
56
+ "OR",
57
+ "AND"
58
+ ]);
59
+ function bad(message, entity) {
60
+ throw new BadRequestError(message, entity === void 0 ? {} : { entity });
61
+ }
62
+ function coerceScalar(column, value, path) {
63
+ if (column === void 0 || typeof value !== "string") return value;
64
+ if (column.columnKind?.expression !== void 0) return value;
65
+ const [type, constraint] = column.dataType.split(" ");
66
+ if (type === "object" && constraint === "date") {
67
+ const date = new Date(value);
68
+ if (Number.isNaN(date.getTime())) bad(`${path}: '${value}' is not a valid date`);
69
+ return date;
70
+ }
71
+ if (type === "bigint") {
72
+ if (!/^-?\d+$/.test(value)) bad(`${path}: '${value}' is not an integer`);
73
+ return BigInt(value);
74
+ }
75
+ if (type === "number" && column.key !== void 0 && /^-?\d+(\.\d+)?$/.test(value)) return Number(value);
76
+ if (type === "boolean" && (value === "true" || value === "false")) return value === "true";
77
+ return value;
78
+ }
79
+ function coerceRow(entity, data, path) {
80
+ if (!isPlainObject(data)) bad(`${path} must be an object`, entity.name);
81
+ const out = {};
82
+ for (const [key, value] of Object.entries(data)) out[key] = Array.isArray(value) ? value : coerceScalar(entity.columns[key], value, `${path}.${key}`);
83
+ return out;
84
+ }
85
+ function checkFieldFilter(column, value, path) {
86
+ if (!isPlainObject(value)) {
87
+ if (value === null || typeof value === "object") bad(`${path}: use an operator object ({ eq: … }, { isNull: true }) for this value`);
88
+ return coerceScalar(column, value, path);
89
+ }
90
+ const out = {};
91
+ for (const [op, v] of Object.entries(value)) {
92
+ if (!FIELD_OPERATORS.has(op)) bad(`${path}: unknown operator '${op}'`);
93
+ const at = `${path}.${op}`;
94
+ switch (op) {
95
+ case "in":
96
+ case "notIn":
97
+ case "arrayContains":
98
+ case "arrayContained":
99
+ case "arrayOverlaps":
100
+ if (!Array.isArray(v)) bad(`${at} must be an array`);
101
+ out[op] = v.map((item, i) => coerceScalar(column, item, `${at}.${i}`));
102
+ break;
103
+ case "like":
104
+ case "ilike":
105
+ case "notLike":
106
+ case "notIlike":
107
+ if (typeof v !== "string") bad(`${at} must be a string`);
108
+ out[op] = v;
109
+ break;
110
+ case "isNull":
111
+ case "isNotNull":
112
+ if (v !== true) bad(`${at} must be true`);
113
+ out[op] = true;
114
+ break;
115
+ case "NOT":
116
+ out[op] = checkFieldFilter(column, v, at);
117
+ break;
118
+ case "OR":
119
+ case "AND":
120
+ if (!Array.isArray(v)) bad(`${at} must be an array`);
121
+ out[op] = v.map((item, i) => checkFieldFilter(column, item, `${at}.${i}`));
122
+ break;
123
+ default:
124
+ if (v !== null && typeof v === "object") bad(`${at} must be a primitive value`);
125
+ out[op] = coerceScalar(column, v, at);
126
+ }
127
+ }
128
+ return out;
129
+ }
130
+ function columnsOf(schema, tableKey) {
131
+ const table = schema.relations[tableKey]?.table;
132
+ const entity = schema.entity(tableKey);
133
+ if (entity !== null) return new Set(Object.keys(entity.columns));
134
+ const columns = table?.[Symbol.for("drizzle:Columns")];
135
+ return new Set(Object.keys(columns ?? {}));
136
+ }
137
+ function checkWhere(schema, limits, tableKey, where, path, depth) {
138
+ if (!isPlainObject(where)) bad(`${path} must be an object`);
139
+ if (depth > limits.maxDepth) bad(`${path}: relation filters nest deeper than ${limits.maxDepth}`);
140
+ const entity = schema.entity(tableKey);
141
+ const columns = columnsOf(schema, tableKey);
142
+ const relations = schema.relations[tableKey]?.relations ?? {};
143
+ const out = {};
144
+ for (const [key, value] of Object.entries(where)) {
145
+ if (value === void 0) continue;
146
+ const at = `${path}.${key}`;
147
+ if (key === "AND" || key === "OR") {
148
+ if (!Array.isArray(value)) bad(`${at} must be an array`);
149
+ out[key] = value.map((v, i) => checkWhere(schema, limits, tableKey, v, `${at}.${i}`, depth));
150
+ } else if (key === "NOT") out[key] = checkWhere(schema, limits, tableKey, value, at, depth);
151
+ else if (key === "RAW") bad(`${at}: raw SQL filters are not accepted over HTTP`);
152
+ else if (columns.has(key)) out[key] = checkFieldFilter(entity?.columns[key], value, at);
153
+ else if (relations[key] !== void 0) {
154
+ const target = relations[key].targetTableName;
155
+ out[key] = typeof value === "boolean" ? value : checkWhere(schema, limits, target, value, at, depth + 1);
156
+ } else bad(`${at}: unknown field`);
157
+ }
158
+ return out;
159
+ }
160
+ function checkConfig(schema, limits, tableKey, config, path, depth, root) {
161
+ if (config === void 0) return {};
162
+ if (!isPlainObject(config)) bad(`${path} must be an object`);
163
+ const out = {};
164
+ for (const [key, value] of Object.entries(config)) {
165
+ if (value === void 0) continue;
166
+ const at = `${path}.${key}`;
167
+ const option = root ? schema.readOptions.get(key) : void 0;
168
+ if (!CONFIG_KEYS.has(key) && option === void 0) bad(`${at} is not accepted over HTTP`);
169
+ switch (key) {
170
+ case "columns":
171
+ case "orderBy":
172
+ if (!isPlainObject(value)) bad(`${at} must be an object`);
173
+ for (const [k, v] of Object.entries(value)) if (!(key === "columns" ? typeof v === "boolean" : v === "asc" || v === "desc") && v !== void 0) bad(`${at}.${k} has an invalid value`);
174
+ out[key] = value;
175
+ break;
176
+ case "where":
177
+ out[key] = checkWhere(schema, limits, tableKey, value, at, depth);
178
+ break;
179
+ case "offset":
180
+ case "limit":
181
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) bad(`${at} must be a non-negative integer`);
182
+ if (key === "limit" && value > limits.maxLimit) bad(`${at} exceeds ${limits.maxLimit}`);
183
+ out[key] = value;
184
+ break;
185
+ case "with": {
186
+ if (!isPlainObject(value)) bad(`${at} must be an object`);
187
+ if (depth + 1 > limits.maxDepth) bad(`${at}: includes nest deeper than ${limits.maxDepth}`);
188
+ const relations = schema.relations[tableKey]?.relations ?? {};
189
+ const nested = {};
190
+ for (const [rel, sub] of Object.entries(value)) {
191
+ const relation = relations[rel];
192
+ if (relation === void 0) bad(`${at}.${rel}: unknown relation`);
193
+ nested[rel] = typeof sub === "boolean" || sub === void 0 ? sub : checkConfig(schema, limits, relation.targetTableName, sub, `${at}.${rel}`, depth + 1, false);
194
+ }
195
+ out[key] = nested;
196
+ break;
197
+ }
198
+ default:
199
+ if (typeof value !== option) bad(`${at} must be a ${option}`);
200
+ out[key] = value;
201
+ }
202
+ }
203
+ return out;
204
+ }
205
+ function checkId(entity, id, path) {
206
+ if (id === void 0 || id === null || typeof id === "object") bad(`${path} is required`, entity.name);
207
+ return coerceScalar(entity.pk, id, path);
208
+ }
209
+ function pick(input, keys, path) {
210
+ for (const key of Object.keys(input)) if (!keys.includes(key) && input[key] !== void 0) bad(`${path}.${key} is not accepted`);
211
+ return input;
212
+ }
213
+ function options(value, keys, path) {
214
+ if (value === void 0) return void 0;
215
+ if (!isPlainObject(value)) bad(`${path} must be an object`);
216
+ return pick(value, keys, path);
217
+ }
218
+ function checkDescriptor(schema, limits, entity, op, rawInput) {
219
+ const input = rawInput === void 0 ? {} : rawInput;
220
+ if (!isPlainObject(input)) bad("input must be an object", entity.name);
221
+ const name = entity.name;
222
+ const wrap = (value) => ({
223
+ entity: name,
224
+ op,
225
+ input: value
226
+ });
227
+ switch (op) {
228
+ case "find":
229
+ case "page": {
230
+ const config = checkConfig(schema, limits, name, input, "input", 0, true);
231
+ return wrap({
232
+ ...config,
233
+ limit: config.limit ?? limits.maxLimit
234
+ });
235
+ }
236
+ case "findOne": return wrap(checkConfig(schema, limits, name, input, "input", 0, true));
237
+ case "count":
238
+ pick(input, ["where", ...schema.readOptions.keys()], "input");
239
+ return wrap(checkConfig(schema, limits, name, input, "input", 0, true));
240
+ case "findById": {
241
+ pick(input, ["id", "config"], "input");
242
+ const config = checkConfig(schema, limits, name, input.config, "input.config", 0, true);
243
+ if (config.where !== void 0 || config.offset !== void 0) bad("input.config: where and offset are not accepted by findById");
244
+ return wrap({
245
+ id: checkId(entity, input.id, "input.id"),
246
+ config
247
+ });
248
+ }
249
+ case "create":
250
+ pick(input, ["data", "options"], "input");
251
+ return wrap({
252
+ data: coerceRow(entity, input.data, "input.data"),
253
+ options: options(input.options, [...schema.createOptions.keys(), ...schema.writeOptions.keys()], "input.options")
254
+ });
255
+ case "update":
256
+ pick(input, [
257
+ "id",
258
+ "data",
259
+ "options"
260
+ ], "input");
261
+ return wrap({
262
+ id: checkId(entity, input.id, "input.id"),
263
+ data: coerceRow(entity, input.data, "input.data"),
264
+ options: options(input.options, [...schema.updateOptions.keys(), ...schema.writeOptions.keys()], "input.options")
265
+ });
266
+ case "updateMany":
267
+ pick(input, [
268
+ "where",
269
+ "data",
270
+ "options"
271
+ ], "input");
272
+ return wrap({
273
+ where: checkWhere(schema, limits, name, input.where ?? {}, "input.where", 0),
274
+ data: coerceRow(entity, input.data, "input.data"),
275
+ options: options(input.options, ["max", ...schema.writeOptions.keys()], "input.options")
276
+ });
277
+ case "delete":
278
+ pick(input, ["id", "options"], "input");
279
+ return wrap({
280
+ id: checkId(entity, input.id, "input.id"),
281
+ options: options(input.options, [...schema.deleteOptions.keys()], "input.options")
282
+ });
283
+ case "deleteMany":
284
+ pick(input, ["where", "options"], "input");
285
+ return wrap({
286
+ where: checkWhere(schema, limits, name, input.where ?? {}, "input.where", 0),
287
+ options: options(input.options, ["max", ...schema.deleteOptions.keys()], "input.options")
288
+ });
289
+ case "link":
290
+ case "unlink": {
291
+ pick(input, [
292
+ "id",
293
+ "relation",
294
+ "ids"
295
+ ], "input");
296
+ const relation = typeof input.relation === "string" ? entity.relations[input.relation] : void 0;
297
+ const target = relation === void 0 ? null : schema.entity(relation.target);
298
+ if (target === null) bad("input.relation must name a relation of the entity", name);
299
+ if (!Array.isArray(input.ids)) bad("input.ids must be an array", name);
300
+ return wrap({
301
+ id: checkId(entity, input.id, "input.id"),
302
+ relation: input.relation,
303
+ ids: input.ids.map((value, i) => checkId(target, value, `input.ids.${i}`))
304
+ });
305
+ }
306
+ default: {
307
+ const operation = schema.operation(entity, op);
308
+ if (operation === null) return bad(`Unknown op '${String(op)}'`, name);
309
+ pick(input, [...operation.args, ...Object.keys(operation.options ?? {})], "input");
310
+ return wrap(input.id === void 0 ? input : {
311
+ ...input,
312
+ id: checkId(entity, input.id, "input.id")
313
+ });
314
+ }
315
+ }
316
+ }
317
+ //#endregion
318
+ //#region src/http/handler.ts
319
+ const BULK_OPS = /* @__PURE__ */ new Set(["updateMany", "deleteMany"]);
320
+ const DESCRIPTOR_KEYS = /* @__PURE__ */ new Set([
321
+ "entity",
322
+ "op",
323
+ "input"
324
+ ]);
325
+ const DEFAULT_BASE = "/api";
326
+ function matchPath(path, segments) {
327
+ const pattern = path.split("/").filter(Boolean);
328
+ if (pattern.length !== segments.length) return null;
329
+ const params = {};
330
+ return pattern.every((part, i) => {
331
+ const segment = segments[i];
332
+ if (part.startsWith(":")) {
333
+ params[part.slice(1)] = segment;
334
+ return true;
335
+ }
336
+ return part === segment;
337
+ }) ? params : null;
338
+ }
339
+ function matchPluginRoute(routes, method, segments) {
340
+ for (const route of routes) {
341
+ if (route.method !== method) continue;
342
+ const params = matchPath(route.path, segments);
343
+ if (params !== null) return {
344
+ route,
345
+ params
346
+ };
347
+ }
348
+ return null;
349
+ }
350
+ function corsHeaders(cors, request, pluginHeaders) {
351
+ if (cors === void 0) return {};
352
+ const origin = request.headers.get("origin");
353
+ if (!(cors.origin === void 0 || (typeof cors.origin === "string" ? cors.origin === "*" || cors.origin === origin : typeof cors.origin === "function" ? origin !== null && cors.origin(origin) : origin !== null && cors.origin.includes(origin)))) return {};
354
+ const headers = {
355
+ "access-control-allow-origin": cors.origin === "*" || cors.origin === void 0 ? "*" : origin ?? "",
356
+ "access-control-allow-methods": (cors.methods ?? [
357
+ "GET",
358
+ "POST",
359
+ "PATCH",
360
+ "DELETE"
361
+ ]).join(", "),
362
+ "access-control-allow-headers": (cors.headers ?? [
363
+ "content-type",
364
+ "accept",
365
+ "authorization",
366
+ ...pluginHeaders
367
+ ]).join(", ")
368
+ };
369
+ if (cors.credentials) headers["access-control-allow-credentials"] = "true";
370
+ if (cors.maxAge !== void 0) headers["access-control-max-age"] = String(cors.maxAge);
371
+ if (headers["access-control-allow-origin"] !== "*") headers.vary = "origin";
372
+ return headers;
373
+ }
374
+ function toWireError(error, debug) {
375
+ if (error instanceof TypionError) {
376
+ const internal = error.status >= 500;
377
+ return {
378
+ code: error.code,
379
+ status: error.status,
380
+ message: internal && !debug ? "Internal error" : error.message,
381
+ ...error.entity === void 0 ? {} : { entity: error.entity },
382
+ ..."issues" in error ? { issues: error.issues.map((issue) => ({
383
+ path: issue.path.map((p) => typeof p === "number" ? p : String(p)),
384
+ message: issue.message
385
+ })) } : {},
386
+ ...error.details === void 0 || internal && !debug ? {} : { details: error.details }
387
+ };
388
+ }
389
+ const message = debug && error instanceof Error ? error.message : "Internal error";
390
+ return {
391
+ code: "INTERNAL",
392
+ status: ERROR_STATUS.INTERNAL,
393
+ message
394
+ };
395
+ }
396
+ function typionErrorResponse(error, options = {}) {
397
+ const debug = options.debug === true;
398
+ let wire = toWireError(error instanceof TypionError || debug ? error : new InternalError("Internal error"), debug);
399
+ if (wire.code === "FORBIDDEN" && options.caller?.$ctx.actorId === null) wire = {
400
+ ...wire,
401
+ code: "UNAUTHENTICATED",
402
+ status: 401
403
+ };
404
+ return Response.json({ error: wire }, { status: wire.status });
405
+ }
406
+ function createHandler(engine, options) {
407
+ const e = engine;
408
+ const def = e.$def;
409
+ const base = `/${(options.basePath ?? DEFAULT_BASE).split("/").filter(Boolean).join("/")}`;
410
+ const exposed = new Set(options.expose ?? Object.keys(def.entities));
411
+ for (const name of options.exclude ?? []) exposed.delete(name);
412
+ const limits = {
413
+ maxDepth: options.maxDepth ?? 4,
414
+ maxLimit: options.maxLimit ?? def.maxLimit
415
+ };
416
+ const plugins = pluginOptions(def.plugins);
417
+ const metaOf = (name) => Object.hasOwn(def.entities, name) ? def.entities[name] ?? null : null;
418
+ const wireSchema = {
419
+ relations: def.relations,
420
+ entity: metaOf,
421
+ readOptions: plugins.read,
422
+ writeOptions: plugins.write,
423
+ createOptions: plugins.create,
424
+ updateOptions: plugins.update,
425
+ deleteOptions: plugins.delete,
426
+ operation: (entity, op) => entityOperation(entity, def.plugins, op)?.spec ?? null
427
+ };
428
+ const entities = /* @__PURE__ */ new Map();
429
+ for (const name of exposed) {
430
+ const meta = metaOf(name);
431
+ if (meta === null) continue;
432
+ entities.set(name, {
433
+ name,
434
+ operations: entityOperations(meta, def.plugins).map((op) => ({
435
+ name: op.name,
436
+ args: op.spec.args,
437
+ options: op.spec.options ?? {},
438
+ route: op.spec.route
439
+ }))
440
+ });
441
+ }
442
+ const schema = {
443
+ maxLimit: limits.maxLimit,
444
+ entity: (name) => entities.get(name) ?? null,
445
+ readOptions: plugins.read,
446
+ deleteOptions: plugins.delete
447
+ };
448
+ const transports = options.transports ?? [];
449
+ const pluginHeaders = def.plugins.flatMap((plugin) => plugin.spec.requestHeaders ?? []);
450
+ const debug = options.debug === true;
451
+ const codecs = new Set(options.codecs ?? ["json"]);
452
+ const checked = /* @__PURE__ */ new WeakSet();
453
+ const descriptor = (raw) => {
454
+ if (!isPlainObject(raw) || typeof raw.entity !== "string" || typeof raw.op !== "string") throw new BadRequestError("A descriptor needs { entity, op, input }");
455
+ const extra = Object.keys(raw).find((key) => !DESCRIPTOR_KEYS.has(key));
456
+ if (extra !== void 0) throw new BadRequestError(`A descriptor takes { entity, op, input }, not '${extra}'`);
457
+ const entity = entities.has(raw.entity) ? metaOf(raw.entity) : null;
458
+ if (entity === null) throw new NotFoundError(`Unknown entity '${raw.entity}'`);
459
+ const op = raw.op;
460
+ if (BULK_OPS.has(op) && options.exposeBulk !== true) throw new NotFoundError(`'${op}' is not exposed over HTTP`);
461
+ const result = checkDescriptor(wireSchema, limits, entity, op, raw.input);
462
+ checked.add(result);
463
+ return result;
464
+ };
465
+ return async (request) => {
466
+ const url = new URL(request.url);
467
+ const cors = corsHeaders(options.cors, request, pluginHeaders);
468
+ if (!(url.pathname === base || url.pathname.startsWith(`${base}/`))) return new Response(null, {
469
+ status: 404,
470
+ headers: cors
471
+ });
472
+ if (request.method === "OPTIONS") return new Response(null, {
473
+ status: 204,
474
+ headers: cors
475
+ });
476
+ let caller;
477
+ let anonymous = false;
478
+ const signIn = (wire) => anonymous && wire.code === "FORBIDDEN" ? {
479
+ ...wire,
480
+ code: "UNAUTHENTICATED",
481
+ status: 401
482
+ } : wire;
483
+ const wantsSuperjson = codecs.has("superjson") && (request.headers.get("accept") ?? "").includes("application/superjson");
484
+ let codec = jsonCodec;
485
+ const respond = (status, payload) => new Response(status === 204 ? null : codec.stringify(payload), {
486
+ status,
487
+ headers: {
488
+ ...cors,
489
+ "content-type": codec.contentType
490
+ }
491
+ });
492
+ try {
493
+ if (wantsSuperjson) codec = await loadSuperjson();
494
+ const segments = url.pathname.slice(base.length).split("/").filter(Boolean).map((s) => decodeURIComponent(s));
495
+ let bodyCache;
496
+ const readBody = () => {
497
+ bodyCache ??= request.text().then(async (text) => {
498
+ if (text.trim() === "") return void 0;
499
+ const bodyCodec = (request.headers.get("content-type") ?? "").includes("application/superjson") && codecs.has("superjson") ? await loadSuperjson() : jsonCodec;
500
+ try {
501
+ return bodyCodec.parse(text);
502
+ } catch {
503
+ throw new BadRequestError("The request body is not valid JSON");
504
+ }
505
+ });
506
+ return bodyCache;
507
+ };
508
+ const app = () => caller ??= e.$fromRequest(request).then((bound) => {
509
+ anonymous = bound.$ctx.actorId === null;
510
+ return bound;
511
+ });
512
+ if (segments[0] === "_plugins") {
513
+ const plugin = def.plugins.find((p) => p.id === segments[1]);
514
+ const match = plugin && matchPluginRoute(plugin.routes, request.method, segments.slice(2));
515
+ if (!match) throw new NotFoundError("Unknown plugin route");
516
+ const response = await match.route.handler({
517
+ request,
518
+ params: match.params,
519
+ engine: e,
520
+ app: await app()
521
+ });
522
+ for (const [key, value] of Object.entries(cors)) response.headers.set(key, value);
523
+ return response;
524
+ }
525
+ const call = {
526
+ request,
527
+ url,
528
+ method: request.method,
529
+ segments,
530
+ schema,
531
+ body: readBody,
532
+ descriptor,
533
+ run: async (d) => {
534
+ if (!checked.has(d)) throw new InternalError("A transport ran a descriptor it did not check");
535
+ return e.$execute(d, await app());
536
+ },
537
+ respond,
538
+ error: (error) => signIn(toWireError(error, debug))
539
+ };
540
+ for (const transport of transports) {
541
+ const response = await transport.handle(call);
542
+ if (response !== void 0) return response;
543
+ }
544
+ throw new NotFoundError("Unknown route");
545
+ } catch (error) {
546
+ const wire = signIn(toWireError(error instanceof TypionError || debug ? error : new InternalError("Internal error"), debug));
547
+ if (wire.status >= 500 && !(error instanceof TypionError && error.status < 500)) (options.onError ?? console.error)(error);
548
+ return respond(wire.status, { error: wire });
549
+ }
550
+ };
551
+ }
552
+ function toRouteHandlers(handler) {
553
+ return {
554
+ GET: handler,
555
+ POST: handler,
556
+ PATCH: handler,
557
+ PUT: handler,
558
+ DELETE: handler,
559
+ OPTIONS: handler
560
+ };
561
+ }
562
+ //#endregion
563
+ export { jsonCodec as a, SUPERJSON_TYPE as i, toRouteHandlers as n, typionErrorResponse as r, createHandler as t };
564
+
565
+ //# sourceMappingURL=handler-_87bFkbP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-_87bFkbP.js","names":[],"sources":["../src/http/codec.ts","../src/http/wire.ts","../src/http/handler.ts"],"sourcesContent":["export interface Codec {\n readonly contentType: string\n stringify(value: unknown): string\n parse(text: string): unknown\n}\n\nexport const jsonCodec: Codec = {\n contentType: 'application/json',\n stringify: (value) =>\n JSON.stringify(value, (_key, v) => (typeof v === 'bigint' ? v.toString() : v)),\n parse: (text) => JSON.parse(text),\n}\n\nexport const SUPERJSON_TYPE = 'application/superjson'\n\nlet superjson: Promise<Codec> | null = null\n\n// superjson is an optional peer: it is loaded only when a client negotiates it, so plain-JSON users\n// never need it installed. Bundlers resolve even a dynamic import when they build (Turbopack fails the whole route\n// without the package), so it is left to the runtime.\nexport function loadSuperjson(): Promise<Codec> {\n superjson ??= import(/* webpackIgnore: true */ /* turbopackIgnore: true */ 'superjson').then(\n (mod) => {\n type SuperJSONLike = { stringify(value: unknown): string; parse(text: string): unknown }\n const sj = ((mod as { default?: unknown }).default ?? mod) as SuperJSONLike\n return {\n contentType: SUPERJSON_TYPE,\n stringify: (value: unknown) => sj.stringify(value),\n parse: (text: string) => sj.parse(text),\n }\n },\n )\n return superjson\n}\n","import type { AnyRelations } from 'drizzle-orm'\nimport type { AnyDescriptor, Op } from '../contract/descriptor.js'\nimport { BadRequestError } from '../errors.js'\nimport { isPlainObject } from '../runtime/filters.js'\nimport type { OptionType } from '../types/kinds.js'\nimport type { ColumnMeta, EntityMeta } from '../types/meta.js'\nimport type { AnyRow } from '../types/runtime.js'\nimport type { TraitOperation } from '../types/traits.js'\n\nexport type WireLimits = {\n readonly maxDepth: number\n readonly maxLimit: number\n}\n\nexport type WireSchema = {\n readonly relations: AnyRelations\n readonly entity: (name: string) => EntityMeta | null\n // The options of the installed plugins, with their types: read, write (the column kinds', on every write),\n // create, update and delete (the traits').\n readonly readOptions: ReadonlyMap<string, OptionType>\n readonly writeOptions: ReadonlyMap<string, OptionType>\n readonly createOptions: ReadonlyMap<string, OptionType>\n readonly updateOptions: ReadonlyMap<string, OptionType>\n readonly deleteOptions: ReadonlyMap<string, OptionType>\n // A plugin's operation on the entity (`restore`), or null where no plugin acting on it adds one.\n readonly operation: (entity: EntityMeta, op: string) => TraitOperation | null\n}\n\nconst CONFIG_KEYS = new Set(['columns', 'with', 'where', 'orderBy', 'offset', 'limit'])\nconst FIELD_OPERATORS = new Set([\n 'eq',\n 'ne',\n 'gt',\n 'gte',\n 'lt',\n 'lte',\n 'in',\n 'notIn',\n 'like',\n 'ilike',\n 'notLike',\n 'notIlike',\n 'isNull',\n 'isNotNull',\n 'arrayContains',\n 'arrayContained',\n 'arrayOverlaps',\n 'NOT',\n 'OR',\n 'AND',\n])\n\nfunction bad(message: string, entity?: string): never {\n throw new BadRequestError(message, entity === undefined ? {} : { entity })\n}\n\n// JSON cannot carry Dates or bigints, so values bound for those columns arrive as strings and are\n// converted here, from the column metadata, before anything reaches SQL.\nexport function coerceScalar(\n column: ColumnMeta | undefined,\n value: unknown,\n path: string,\n): unknown {\n if (column === undefined || typeof value !== 'string') return value\n // Filters on a column whose kind rewrites them compare with its read type, not with what it stores.\n if (column.columnKind?.expression !== undefined) return value\n const [type, constraint] = column.dataType.split(' ')\n if (type === 'object' && constraint === 'date') {\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) bad(`${path}: '${value}' is not a valid date`)\n return date\n }\n if (type === 'bigint') {\n if (!/^-?\\d+$/.test(value)) bad(`${path}: '${value}' is not an integer`)\n return BigInt(value)\n }\n if (type === 'number' && column.key !== undefined && /^-?\\d+(\\.\\d+)?$/.test(value)) {\n return Number(value)\n }\n if (type === 'boolean' && (value === 'true' || value === 'false')) return value === 'true'\n return value\n}\n\nexport function coerceRow(entity: EntityMeta, data: unknown, path: string): AnyRow {\n if (!isPlainObject(data)) bad(`${path} must be an object`, entity.name)\n const out: AnyRow = {}\n for (const [key, value] of Object.entries(data)) {\n out[key] = Array.isArray(value)\n ? value\n : coerceScalar(entity.columns[key], value, `${path}.${key}`)\n }\n return out\n}\n\nfunction checkFieldFilter(column: ColumnMeta | undefined, value: unknown, path: string): unknown {\n if (!isPlainObject(value)) {\n if (value === null || typeof value === 'object') {\n bad(`${path}: use an operator object ({ eq: … }, { isNull: true }) for this value`)\n }\n return coerceScalar(column, value, path)\n }\n const out: AnyRow = {}\n for (const [op, v] of Object.entries(value)) {\n if (!FIELD_OPERATORS.has(op)) bad(`${path}: unknown operator '${op}'`)\n const at = `${path}.${op}`\n switch (op) {\n case 'in':\n case 'notIn':\n case 'arrayContains':\n case 'arrayContained':\n case 'arrayOverlaps':\n if (!Array.isArray(v)) bad(`${at} must be an array`)\n out[op] = v.map((item, i) => coerceScalar(column, item, `${at}.${i}`))\n break\n case 'like':\n case 'ilike':\n case 'notLike':\n case 'notIlike':\n if (typeof v !== 'string') bad(`${at} must be a string`)\n out[op] = v\n break\n case 'isNull':\n case 'isNotNull':\n if (v !== true) bad(`${at} must be true`)\n out[op] = true\n break\n case 'NOT':\n out[op] = checkFieldFilter(column, v, at)\n break\n case 'OR':\n case 'AND':\n if (!Array.isArray(v)) bad(`${at} must be an array`)\n out[op] = v.map((item, i) => checkFieldFilter(column, item, `${at}.${i}`))\n break\n default:\n if (v !== null && typeof v === 'object') bad(`${at} must be a primitive value`)\n out[op] = coerceScalar(column, v, at)\n }\n }\n return out\n}\n\nfunction columnsOf(schema: WireSchema, tableKey: string): Set<string> {\n const table = schema.relations[tableKey]?.table as Record<symbol, unknown> | undefined\n const entity = schema.entity(tableKey)\n if (entity !== null) return new Set(Object.keys(entity.columns))\n const columns = table?.[Symbol.for('drizzle:Columns')] as Record<string, unknown> | undefined\n return new Set(Object.keys(columns ?? {}))\n}\n\nexport function checkWhere(\n schema: WireSchema,\n limits: WireLimits,\n tableKey: string,\n where: unknown,\n path: string,\n depth: number,\n): AnyRow {\n if (!isPlainObject(where)) bad(`${path} must be an object`)\n if (depth > limits.maxDepth) bad(`${path}: relation filters nest deeper than ${limits.maxDepth}`)\n const entity = schema.entity(tableKey)\n const columns = columnsOf(schema, tableKey)\n const relations = schema.relations[tableKey]?.relations ?? {}\n const out: AnyRow = {}\n for (const [key, value] of Object.entries(where)) {\n if (value === undefined) continue\n const at = `${path}.${key}`\n if (key === 'AND' || key === 'OR') {\n if (!Array.isArray(value)) bad(`${at} must be an array`)\n out[key] = value.map((v, i) => checkWhere(schema, limits, tableKey, v, `${at}.${i}`, depth))\n } else if (key === 'NOT') {\n out[key] = checkWhere(schema, limits, tableKey, value, at, depth)\n } else if (key === 'RAW') {\n bad(`${at}: raw SQL filters are not accepted over HTTP`)\n } else if (columns.has(key)) {\n out[key] = checkFieldFilter(entity?.columns[key], value, at)\n } else if (relations[key] !== undefined) {\n const target = (relations[key] as { targetTableName: string }).targetTableName\n out[key] =\n typeof value === 'boolean'\n ? value\n : checkWhere(schema, limits, target, value, at, depth + 1)\n } else {\n bad(`${at}: unknown field`)\n }\n }\n return out\n}\n\nexport function checkConfig(\n schema: WireSchema,\n limits: WireLimits,\n tableKey: string,\n config: unknown,\n path: string,\n depth: number,\n root: boolean,\n): AnyRow {\n if (config === undefined) return {}\n if (!isPlainObject(config)) bad(`${path} must be an object`)\n const out: AnyRow = {}\n for (const [key, value] of Object.entries(config)) {\n if (value === undefined) continue\n const at = `${path}.${key}`\n const option = root ? schema.readOptions.get(key) : undefined\n if (!CONFIG_KEYS.has(key) && option === undefined) {\n bad(`${at} is not accepted over HTTP`)\n }\n switch (key) {\n case 'columns':\n case 'orderBy':\n if (!isPlainObject(value)) bad(`${at} must be an object`)\n for (const [k, v] of Object.entries(value)) {\n const ok = key === 'columns' ? typeof v === 'boolean' : v === 'asc' || v === 'desc'\n if (!ok && v !== undefined) bad(`${at}.${k} has an invalid value`)\n }\n out[key] = value\n break\n case 'where':\n out[key] = checkWhere(schema, limits, tableKey, value, at, depth)\n break\n case 'offset':\n case 'limit':\n if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {\n bad(`${at} must be a non-negative integer`)\n }\n if (key === 'limit' && value > limits.maxLimit) bad(`${at} exceeds ${limits.maxLimit}`)\n out[key] = value\n break\n case 'with': {\n if (!isPlainObject(value)) bad(`${at} must be an object`)\n if (depth + 1 > limits.maxDepth) bad(`${at}: includes nest deeper than ${limits.maxDepth}`)\n const relations = schema.relations[tableKey]?.relations ?? {}\n const nested: AnyRow = {}\n for (const [rel, sub] of Object.entries(value)) {\n const relation = relations[rel] as { targetTableName: string } | undefined\n if (relation === undefined) bad(`${at}.${rel}: unknown relation`)\n nested[rel] =\n typeof sub === 'boolean' || sub === undefined\n ? sub\n : checkConfig(\n schema,\n limits,\n relation.targetTableName,\n sub,\n `${at}.${rel}`,\n depth + 1,\n false,\n )\n }\n out[key] = nested\n break\n }\n default:\n if (typeof value !== option) bad(`${at} must be a ${option}`)\n out[key] = value\n }\n }\n return out\n}\n\nfunction checkId(entity: EntityMeta, id: unknown, path: string): unknown {\n if (id === undefined || id === null || typeof id === 'object')\n bad(`${path} is required`, entity.name)\n return coerceScalar(entity.pk, id, path)\n}\n\nfunction pick(input: AnyRow, keys: readonly string[], path: string): AnyRow {\n for (const key of Object.keys(input)) {\n if (!keys.includes(key) && input[key] !== undefined) bad(`${path}.${key} is not accepted`)\n }\n return input\n}\n\nfunction options(value: unknown, keys: readonly string[], path: string): AnyRow | undefined {\n if (value === undefined) return undefined\n if (!isPlainObject(value)) bad(`${path} must be an object`)\n return pick(value, keys, path)\n}\n\n// Normalizes an untrusted descriptor into one the engine may execute: only the serializable subset,\n// bounded depth and limit, and values coerced for their columns.\nexport function checkDescriptor(\n schema: WireSchema,\n limits: WireLimits,\n entity: EntityMeta,\n op: Op,\n rawInput: unknown,\n): AnyDescriptor {\n const input = rawInput === undefined ? {} : rawInput\n if (!isPlainObject(input)) bad('input must be an object', entity.name)\n const name = entity.name\n const wrap = (value: AnyRow): AnyDescriptor => ({ entity: name, op, input: value })\n switch (op) {\n case 'find':\n case 'page': {\n // An unbounded list over the network is a denial-of-service lever, so HTTP reads default to the cap.\n const config = checkConfig(schema, limits, name, input, 'input', 0, true)\n return wrap({ ...config, limit: config.limit ?? limits.maxLimit })\n }\n case 'findOne':\n return wrap(checkConfig(schema, limits, name, input, 'input', 0, true))\n case 'count': {\n pick(input, ['where', ...schema.readOptions.keys()], 'input')\n return wrap(checkConfig(schema, limits, name, input, 'input', 0, true))\n }\n case 'findById': {\n pick(input, ['id', 'config'], 'input')\n const config = checkConfig(schema, limits, name, input.config, 'input.config', 0, true)\n if (config.where !== undefined || config.offset !== undefined) {\n bad('input.config: where and offset are not accepted by findById')\n }\n return wrap({ id: checkId(entity, input.id, 'input.id'), config })\n }\n case 'create':\n pick(input, ['data', 'options'], 'input')\n return wrap({\n data: coerceRow(entity, input.data, 'input.data'),\n options: options(\n input.options,\n [...schema.createOptions.keys(), ...schema.writeOptions.keys()],\n 'input.options',\n ),\n })\n case 'update':\n pick(input, ['id', 'data', 'options'], 'input')\n return wrap({\n id: checkId(entity, input.id, 'input.id'),\n data: coerceRow(entity, input.data, 'input.data'),\n options: options(\n input.options,\n [...schema.updateOptions.keys(), ...schema.writeOptions.keys()],\n 'input.options',\n ),\n })\n case 'updateMany':\n pick(input, ['where', 'data', 'options'], 'input')\n return wrap({\n where: checkWhere(schema, limits, name, input.where ?? {}, 'input.where', 0),\n data: coerceRow(entity, input.data, 'input.data'),\n options: options(input.options, ['max', ...schema.writeOptions.keys()], 'input.options'),\n })\n case 'delete':\n pick(input, ['id', 'options'], 'input')\n return wrap({\n id: checkId(entity, input.id, 'input.id'),\n options: options(input.options, [...schema.deleteOptions.keys()], 'input.options'),\n })\n case 'deleteMany':\n pick(input, ['where', 'options'], 'input')\n return wrap({\n where: checkWhere(schema, limits, name, input.where ?? {}, 'input.where', 0),\n options: options(input.options, ['max', ...schema.deleteOptions.keys()], 'input.options'),\n })\n case 'link':\n case 'unlink': {\n pick(input, ['id', 'relation', 'ids'], 'input')\n const relation =\n typeof input.relation === 'string' ? entity.relations[input.relation] : undefined\n const target = relation === undefined ? null : schema.entity(relation.target)\n if (target === null) bad('input.relation must name a relation of the entity', name)\n if (!Array.isArray(input.ids)) bad('input.ids must be an array', name)\n return wrap({\n id: checkId(entity, input.id, 'input.id'),\n relation: input.relation,\n ids: input.ids.map((value, i) => checkId(target, value, `input.ids.${i}`)),\n })\n }\n default: {\n const operation = schema.operation(entity, op)\n if (operation === null) return bad(`Unknown op '${String(op)}'`, name)\n pick(input, [...operation.args, ...Object.keys(operation.options ?? {})], 'input')\n return wrap(\n input.id === undefined ? input : { ...input, id: checkId(entity, input.id, 'input.id') },\n )\n }\n }\n}\n","import type { AnyDescriptor, Op } from '../contract/descriptor.js'\nimport { ERROR_STATUS, type WireError } from '../contract/errors.js'\nimport { BadRequestError, TypionError, InternalError, NotFoundError } from '../errors.js'\nimport { isPlainObject } from '../runtime/filters.js'\nimport { pluginOptions } from '../runtime/read/kinds.js'\nimport { entityOperation, entityOperations } from '../runtime/write/operations.js'\nimport type { CorsOptions, TypionEngine, TypionTypes, HandlerOptions } from '../types/engine.js'\nimport type { EntityMeta } from '../types/meta.js'\nimport type { PluginRoute } from '../types/plugin.js'\nimport type { AnyTypionApp, AnyTypion } from '../types/runtime.js'\nimport { type Codec, jsonCodec, loadSuperjson, SUPERJSON_TYPE } from './codec.js'\nimport type {\n CheckedDescriptor,\n TransportCall,\n TransportEntity,\n TransportSchema,\n} from './transport.js'\nimport { checkDescriptor, type WireLimits, type WireSchema } from './wire.js'\n\nconst BULK_OPS = new Set<Op>(['updateMany', 'deleteMany'])\nconst DESCRIPTOR_KEYS = new Set(['entity', 'op', 'input'])\nconst DEFAULT_BASE = '/api'\n\nfunction matchPath(path: string, segments: readonly string[]): Record<string, string> | null {\n const pattern = path.split('/').filter(Boolean)\n if (pattern.length !== segments.length) return null\n const params: Record<string, string> = {}\n const ok = pattern.every((part, i) => {\n const segment = segments[i] as string\n if (part.startsWith(':')) {\n params[part.slice(1)] = segment\n return true\n }\n return part === segment\n })\n return ok ? params : null\n}\n\nfunction matchPluginRoute(\n routes: readonly PluginRoute[],\n method: string,\n segments: readonly string[],\n): { route: PluginRoute; params: Record<string, string> } | null {\n for (const route of routes) {\n if (route.method !== method) continue\n const params = matchPath(route.path, segments)\n if (params !== null) return { route, params }\n }\n return null\n}\n\nfunction corsHeaders(\n cors: CorsOptions | undefined,\n request: Request,\n pluginHeaders: readonly string[],\n): Record<string, string> {\n if (cors === undefined) return {}\n const origin = request.headers.get('origin')\n const allowed =\n cors.origin === undefined ||\n (typeof cors.origin === 'string'\n ? cors.origin === '*' || cors.origin === origin\n : typeof cors.origin === 'function'\n ? origin !== null && cors.origin(origin)\n : origin !== null && cors.origin.includes(origin))\n if (!allowed) return {}\n const headers: Record<string, string> = {\n 'access-control-allow-origin':\n cors.origin === '*' || cors.origin === undefined ? '*' : (origin ?? ''),\n 'access-control-allow-methods': (cors.methods ?? ['GET', 'POST', 'PATCH', 'DELETE']).join(', '),\n 'access-control-allow-headers': (\n cors.headers ?? ['content-type', 'accept', 'authorization', ...pluginHeaders]\n ).join(', '),\n }\n if (cors.credentials) headers['access-control-allow-credentials'] = 'true'\n if (cors.maxAge !== undefined) headers['access-control-max-age'] = String(cors.maxAge)\n if (headers['access-control-allow-origin'] !== '*') headers.vary = 'origin'\n return headers\n}\n\nfunction toWireError(error: unknown, debug: boolean): WireError {\n if (error instanceof TypionError) {\n const internal = error.status >= 500\n return {\n code: error.code,\n status: error.status,\n message: internal && !debug ? 'Internal error' : error.message,\n ...(error.entity === undefined ? {} : { entity: error.entity }),\n ...('issues' in error\n ? {\n issues: (\n error as { issues: readonly { path: readonly PropertyKey[]; message: string }[] }\n ).issues.map((issue) => ({\n path: issue.path.map((p) => (typeof p === 'number' ? p : String(p))),\n message: issue.message,\n })),\n }\n : {}),\n ...(error.details === undefined || (internal && !debug) ? {} : { details: error.details }),\n }\n }\n const message = debug && error instanceof Error ? error.message : 'Internal error'\n return { code: 'INTERNAL', status: ERROR_STATUS.INTERNAL, message }\n}\n\n// The response a transport gives for an error, for the application's own routes (a Hono route, a Next.js route\n// handler) to fail the same way: `{ error: { code, status, message, entity?, issues? } }` with its status. An error\n// that is not Typion's is a bare `500`, its message kept out unless `debug`; given the caller, a refusal of one who\n// has not signed in is `401 UNAUTHENTICATED`, as the transports answer it.\nexport function typionErrorResponse(\n error: unknown,\n options: {\n debug?: boolean\n caller?: { readonly $ctx: { readonly actorId: string | null } }\n } = {},\n): Response {\n const debug = options.debug === true\n let wire = toWireError(\n error instanceof TypionError || debug ? error : new InternalError('Internal error'),\n debug,\n )\n if (wire.code === 'FORBIDDEN' && options.caller?.$ctx.actorId === null) {\n wire = { ...wire, code: 'UNAUTHENTICATED', status: 401 }\n }\n return Response.json({ error: wire }, { status: wire.status })\n}\n\n// The handler of an engine over HTTP: the base path, CORS, the codecs, the body, the plugins' own routes and the\n// mapping of errors, around the transports the application mounts, tried in order. With none mounted, only the\n// plugins' routes answer: nothing is exposed that the application did not ask for.\nexport function createHandler<TE extends TypionTypes>(\n engine: TypionEngine<TE>,\n options: HandlerOptions<TE>,\n): (request: Request) => Promise<Response> {\n const e = engine as unknown as AnyTypion\n const def = e.$def\n const base = `/${(options.basePath ?? DEFAULT_BASE).split('/').filter(Boolean).join('/')}`\n const exposed = new Set<string>(options.expose ?? Object.keys(def.entities))\n for (const name of options.exclude ?? []) exposed.delete(name)\n const limits: WireLimits = {\n maxDepth: options.maxDepth ?? 4,\n maxLimit: options.maxLimit ?? def.maxLimit,\n }\n const plugins = pluginOptions(def.plugins)\n const metaOf = (name: string): EntityMeta | null =>\n Object.hasOwn(def.entities, name)\n ? ((def.entities as Record<string, EntityMeta>)[name] ?? null)\n : null\n const wireSchema: WireSchema = {\n relations: def.relations,\n entity: metaOf,\n readOptions: plugins.read,\n writeOptions: plugins.write,\n createOptions: plugins.create,\n updateOptions: plugins.update,\n deleteOptions: plugins.delete,\n operation: (entity, op) => entityOperation(entity, def.plugins, op)?.spec ?? null,\n }\n const entities = new Map<string, TransportEntity>()\n for (const name of exposed) {\n const meta = metaOf(name)\n if (meta === null) continue\n entities.set(name, {\n name,\n operations: entityOperations(meta, def.plugins).map((op) => ({\n name: op.name,\n args: op.spec.args,\n options: op.spec.options ?? {},\n route: op.spec.route,\n })),\n })\n }\n const schema: TransportSchema = {\n maxLimit: limits.maxLimit,\n entity: (name) => entities.get(name) ?? null,\n readOptions: plugins.read,\n deleteOptions: plugins.delete,\n }\n const transports = options.transports ?? []\n const pluginHeaders = def.plugins.flatMap((plugin) => plugin.spec.requestHeaders ?? [])\n const debug = options.debug === true\n const codecs = new Set(options.codecs ?? ['json'])\n\n const checked = new WeakSet<AnyDescriptor>()\n const descriptor = (raw: unknown): CheckedDescriptor => {\n if (!isPlainObject(raw) || typeof raw.entity !== 'string' || typeof raw.op !== 'string') {\n throw new BadRequestError('A descriptor needs { entity, op, input }')\n }\n // A misspelt `input` would otherwise run the op with no input: an unfiltered find.\n const extra = Object.keys(raw).find((key) => !DESCRIPTOR_KEYS.has(key))\n if (extra !== undefined) {\n throw new BadRequestError(`A descriptor takes { entity, op, input }, not '${extra}'`)\n }\n const entity = entities.has(raw.entity) ? metaOf(raw.entity) : null\n if (entity === null) throw new NotFoundError(`Unknown entity '${raw.entity}'`)\n const op = raw.op as Op\n if (BULK_OPS.has(op) && options.exposeBulk !== true) {\n throw new NotFoundError(`'${op}' is not exposed over HTTP`)\n }\n const result = checkDescriptor(wireSchema, limits, entity, op, raw.input)\n checked.add(result)\n return result as CheckedDescriptor\n }\n\n return async (request) => {\n const url = new URL(request.url)\n const cors = corsHeaders(options.cors, request, pluginHeaders)\n if (!(url.pathname === base || url.pathname.startsWith(`${base}/`))) {\n return new Response(null, { status: 404, headers: cors })\n }\n if (request.method === 'OPTIONS') return new Response(null, { status: 204, headers: cors })\n\n let caller: Promise<AnyTypionApp> | undefined\n let anonymous = false\n const signIn = (wire: WireError): WireError =>\n anonymous && wire.code === 'FORBIDDEN'\n ? { ...wire, code: 'UNAUTHENTICATED', status: 401 }\n : wire\n const wantsSuperjson =\n codecs.has('superjson') && (request.headers.get('accept') ?? '').includes(SUPERJSON_TYPE)\n let codec: Codec = jsonCodec\n const respond = (status: number, payload: unknown) =>\n new Response(status === 204 ? null : codec.stringify(payload), {\n status,\n headers: { ...cors, 'content-type': codec.contentType },\n })\n\n try {\n if (wantsSuperjson) codec = await loadSuperjson()\n const segments = url.pathname\n .slice(base.length)\n .split('/')\n .filter(Boolean)\n .map((s) => decodeURIComponent(s))\n let bodyCache: Promise<unknown> | undefined\n const readBody = () => {\n bodyCache ??= request.text().then(async (text) => {\n if (text.trim() === '') return undefined\n const type = request.headers.get('content-type') ?? ''\n const bodyCodec =\n type.includes(SUPERJSON_TYPE) && codecs.has('superjson')\n ? await loadSuperjson()\n : jsonCodec\n try {\n return bodyCodec.parse(text)\n } catch {\n throw new BadRequestError('The request body is not valid JSON')\n }\n })\n return bodyCache\n }\n const app = () =>\n (caller ??= e.$fromRequest(request).then((bound) => {\n anonymous = bound.$ctx.actorId === null\n return bound\n }))\n\n if (segments[0] === '_plugins') {\n const plugin = def.plugins.find((p) => p.id === segments[1])\n const match = plugin && matchPluginRoute(plugin.routes, request.method, segments.slice(2))\n if (!match) throw new NotFoundError('Unknown plugin route')\n const response = await match.route.handler({\n request,\n params: match.params,\n engine: e,\n app: await app(),\n })\n for (const [key, value] of Object.entries(cors)) response.headers.set(key, value)\n return response\n }\n\n const call: TransportCall = {\n request,\n url,\n method: request.method,\n segments,\n schema,\n body: readBody,\n descriptor,\n run: async (d) => {\n if (!checked.has(d))\n throw new InternalError('A transport ran a descriptor it did not check')\n return e.$execute(d, await app())\n },\n respond,\n error: (error) => signIn(toWireError(error, debug)),\n }\n for (const transport of transports) {\n const response = await transport.handle(call)\n if (response !== undefined) return response\n }\n throw new NotFoundError('Unknown route')\n } catch (error) {\n const wire = signIn(\n toWireError(\n error instanceof TypionError || debug ? error : new InternalError('Internal error'),\n debug,\n ),\n )\n // The response says only \"Internal error\": without a reporter of the application's, the server's log gets the\n // error, or a misconfiguration (an unset database url) answers 500 and leaves no trace.\n if (wire.status >= 500 && !(error instanceof TypionError && error.status < 500)) {\n ;(options.onError ?? console.error)(error)\n }\n return respond(wire.status, { error: wire })\n }\n }\n}\n\nexport type RouteHandlers = {\n GET: (request: Request) => Promise<Response>\n POST: (request: Request) => Promise<Response>\n PATCH: (request: Request) => Promise<Response>\n PUT: (request: Request) => Promise<Response>\n DELETE: (request: Request) => Promise<Response>\n OPTIONS: (request: Request) => Promise<Response>\n}\n\n// Next.js route handlers and TanStack Start server routes both take one function per method.\nexport function toRouteHandlers(handler: (request: Request) => Promise<Response>): RouteHandlers {\n return {\n GET: handler,\n POST: handler,\n PATCH: handler,\n PUT: handler,\n DELETE: handler,\n OPTIONS: handler,\n }\n}\n"],"mappings":"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"}