@metaobjectsdev/runtime-ts 0.5.0-rc.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.
- package/LICENSE +189 -0
- package/README.md +102 -0
- package/dist/constants.d.ts +5 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +6 -0
- package/dist/constants.js.map +1 -0
- package/dist/drivers/drizzle-driver.d.ts +25 -0
- package/dist/drivers/drizzle-driver.d.ts.map +1 -0
- package/dist/drivers/drizzle-driver.js +405 -0
- package/dist/drivers/drizzle-driver.js.map +1 -0
- package/dist/drivers/in-memory-driver.d.ts +11 -0
- package/dist/drivers/in-memory-driver.d.ts.map +1 -0
- package/dist/drivers/in-memory-driver.js +232 -0
- package/dist/drivers/in-memory-driver.js.map +1 -0
- package/dist/drivers/index.d.ts +4 -0
- package/dist/drivers/index.d.ts.map +1 -0
- package/dist/drivers/index.js +4 -0
- package/dist/drivers/index.js.map +1 -0
- package/dist/drivers/kysely-driver.d.ts +12 -0
- package/dist/drivers/kysely-driver.d.ts.map +1 -0
- package/dist/drivers/kysely-driver.js +203 -0
- package/dist/drivers/kysely-driver.js.map +1 -0
- package/dist/drizzle-fastify/filter-allowlist.d.ts +19 -0
- package/dist/drizzle-fastify/filter-allowlist.d.ts.map +1 -0
- package/dist/drizzle-fastify/filter-allowlist.js +10 -0
- package/dist/drizzle-fastify/filter-allowlist.js.map +1 -0
- package/dist/drizzle-fastify/filter-parser.d.ts +28 -0
- package/dist/drizzle-fastify/filter-parser.d.ts.map +1 -0
- package/dist/drizzle-fastify/filter-parser.js +185 -0
- package/dist/drizzle-fastify/filter-parser.js.map +1 -0
- package/dist/drizzle-fastify/index.d.ts +48 -0
- package/dist/drizzle-fastify/index.d.ts.map +1 -0
- package/dist/drizzle-fastify/index.js +181 -0
- package/dist/drizzle-fastify/index.js.map +1 -0
- package/dist/drizzle-fastify/mount-read-only.d.ts +17 -0
- package/dist/drizzle-fastify/mount-read-only.d.ts.map +1 -0
- package/dist/drizzle-fastify/mount-read-only.js +159 -0
- package/dist/drizzle-fastify/mount-read-only.js.map +1 -0
- package/dist/drizzle-fastify/util.d.ts +5 -0
- package/dist/drizzle-fastify/util.d.ts.map +1 -0
- package/dist/drizzle-fastify/util.js +12 -0
- package/dist/drizzle-fastify/util.js.map +1 -0
- package/dist/errors.d.ts +68 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +86 -0
- package/dist/errors.js.map +1 -0
- package/dist/fastify/index.d.ts +65 -0
- package/dist/fastify/index.d.ts.map +1 -0
- package/dist/fastify/index.js +118 -0
- package/dist/fastify/index.js.map +1 -0
- package/dist/identity-strategy.d.ts +10 -0
- package/dist/identity-strategy.d.ts.map +1 -0
- package/dist/identity-strategy.js +67 -0
- package/dist/identity-strategy.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/n2m-resolver.d.ts +27 -0
- package/dist/n2m-resolver.d.ts.map +1 -0
- package/dist/n2m-resolver.js +103 -0
- package/dist/n2m-resolver.js.map +1 -0
- package/dist/object-manager.d.ts +51 -0
- package/dist/object-manager.d.ts.map +1 -0
- package/dist/object-manager.js +355 -0
- package/dist/object-manager.js.map +1 -0
- package/dist/persistence-driver.d.ts +88 -0
- package/dist/persistence-driver.d.ts.map +1 -0
- package/dist/persistence-driver.js +5 -0
- package/dist/persistence-driver.js.map +1 -0
- package/dist/query-builder.d.ts +35 -0
- package/dist/query-builder.d.ts.map +1 -0
- package/dist/query-builder.js +178 -0
- package/dist/query-builder.js.map +1 -0
- package/dist/ref-codec.d.ts +8 -0
- package/dist/ref-codec.d.ts.map +1 -0
- package/dist/ref-codec.js +42 -0
- package/dist/ref-codec.js.map +1 -0
- package/dist/relation-resolver.d.ts +27 -0
- package/dist/relation-resolver.d.ts.map +1 -0
- package/dist/relation-resolver.js +136 -0
- package/dist/relation-resolver.js.map +1 -0
- package/dist/type-coercer.d.ts +5 -0
- package/dist/type-coercer.d.ts.map +1 -0
- package/dist/type-coercer.js +43 -0
- package/dist/type-coercer.js.map +1 -0
- package/dist/validator-runner.d.ts +14 -0
- package/dist/validator-runner.d.ts.map +1 -0
- package/dist/validator-runner.js +155 -0
- package/dist/validator-runner.js.map +1 -0
- package/dist/view.d.ts +19 -0
- package/dist/view.d.ts.map +1 -0
- package/dist/view.js +60 -0
- package/dist/view.js.map +1 -0
- package/package.json +87 -0
- package/src/constants.ts +7 -0
- package/src/drivers/drizzle-driver.ts +474 -0
- package/src/drivers/in-memory-driver.ts +256 -0
- package/src/drivers/index.ts +3 -0
- package/src/drivers/kysely-driver.ts +240 -0
- package/src/drizzle-fastify/filter-allowlist.ts +31 -0
- package/src/drizzle-fastify/filter-parser.ts +229 -0
- package/src/drizzle-fastify/index.ts +225 -0
- package/src/drizzle-fastify/mount-read-only.ts +187 -0
- package/src/drizzle-fastify/util.ts +10 -0
- package/src/errors.ts +114 -0
- package/src/fastify/index.ts +181 -0
- package/src/identity-strategy.ts +92 -0
- package/src/index.ts +24 -0
- package/src/n2m-resolver.ts +152 -0
- package/src/object-manager.ts +444 -0
- package/src/persistence-driver.ts +92 -0
- package/src/query-builder.ts +224 -0
- package/src/ref-codec.ts +64 -0
- package/src/relation-resolver.ts +171 -0
- package/src/type-coercer.ts +39 -0
- package/src/validator-runner.ts +168 -0
- package/src/view.ts +82 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// Drizzle-direct Fastify adapter — replaces the ObjectManager-flavored
|
|
2
|
+
// mountCrudRoutes for consumers that already use Drizzle directly.
|
|
3
|
+
//
|
|
4
|
+
// Why: most TS apps stay on Drizzle for SQL queries. Routing CRUD through
|
|
5
|
+
// ObjectManager + a Drizzle driver was indirection for no real win — generated
|
|
6
|
+
// routes can just call Drizzle directly and stay self-evident as Drizzle code.
|
|
7
|
+
//
|
|
8
|
+
// What this gives you: one helper, same surface as before, but the runtime
|
|
9
|
+
// dependency is Drizzle + Zod, not ObjectManager.
|
|
10
|
+
//
|
|
11
|
+
// GET {path} list with ?limit / ?offset
|
|
12
|
+
// GET {path}/:id findById, 404 if missing
|
|
13
|
+
// POST {path} create, 400 on Zod validation error
|
|
14
|
+
// PATCH {path}/:id update, 400 on validation, 404 if missing
|
|
15
|
+
// DELETE {path}/:id delete, 204 on success, 404 if missing
|
|
16
|
+
import { eq, count, and } from "drizzle-orm";
|
|
17
|
+
import qs from "qs";
|
|
18
|
+
import { parseFilterParams, FilterParseError } from "./filter-parser.js";
|
|
19
|
+
import { isTruthyFlag } from "./util.js";
|
|
20
|
+
export { isTruthyFlag } from "./util.js";
|
|
21
|
+
const ALL_VERBS = ["list", "get", "create", "update", "delete"];
|
|
22
|
+
export function mountCrudRoutes(opts) {
|
|
23
|
+
const verbs = new Set(opts.expose ?? ALL_VERBS);
|
|
24
|
+
if (verbs.has("list"))
|
|
25
|
+
mountListRoute(opts);
|
|
26
|
+
if (verbs.has("get"))
|
|
27
|
+
mountGetRoute(opts);
|
|
28
|
+
if (verbs.has("create"))
|
|
29
|
+
mountCreateRoute(opts);
|
|
30
|
+
if (verbs.has("update"))
|
|
31
|
+
mountUpdateRoute(opts);
|
|
32
|
+
if (verbs.has("delete"))
|
|
33
|
+
mountDeleteRoute(opts);
|
|
34
|
+
}
|
|
35
|
+
function routeOpts(opts) {
|
|
36
|
+
return opts.routeOptions ?? {};
|
|
37
|
+
}
|
|
38
|
+
export function mountListRoute(opts) {
|
|
39
|
+
opts.fastify.get(opts.path, routeOpts(opts), async (req, reply) => {
|
|
40
|
+
try {
|
|
41
|
+
let q = opts.db.select().from(opts.table).$dynamic();
|
|
42
|
+
// Re-parse the raw URL with qs so bracketed filter notation and the
|
|
43
|
+
// top-level withCount flag are available.
|
|
44
|
+
const rawSearch = req.raw.url?.includes("?")
|
|
45
|
+
? req.raw.url.slice(req.raw.url.indexOf("?") + 1)
|
|
46
|
+
: "";
|
|
47
|
+
const qsParsed = qs.parse(rawSearch);
|
|
48
|
+
const withCount = isTruthyFlag(qsParsed.withCount);
|
|
49
|
+
let where;
|
|
50
|
+
if (opts.filterAllowlist && opts.sortAllowlist) {
|
|
51
|
+
const parsed = parseFilterParams({
|
|
52
|
+
query: qsParsed,
|
|
53
|
+
table: opts.table,
|
|
54
|
+
allowlist: opts.filterAllowlist,
|
|
55
|
+
sortAllowlist: opts.sortAllowlist,
|
|
56
|
+
dialect: opts.dialect ?? "sqlite",
|
|
57
|
+
});
|
|
58
|
+
const combinedWhere = parsed.where && parsed.searchWhere
|
|
59
|
+
? and(parsed.where, parsed.searchWhere)
|
|
60
|
+
: (parsed.where ?? parsed.searchWhere);
|
|
61
|
+
if (combinedWhere) {
|
|
62
|
+
q = q.where(combinedWhere);
|
|
63
|
+
where = combinedWhere;
|
|
64
|
+
}
|
|
65
|
+
if (parsed.orderBy)
|
|
66
|
+
q = q.orderBy(...parsed.orderBy);
|
|
67
|
+
if (parsed.limit !== undefined)
|
|
68
|
+
q = q.limit(parsed.limit);
|
|
69
|
+
if (parsed.offset !== undefined)
|
|
70
|
+
q = q.offset(parsed.offset);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
// Legacy path — no allowlists configured. Only limit/offset.
|
|
74
|
+
const { limit, offset } = req.query;
|
|
75
|
+
if (limit !== undefined)
|
|
76
|
+
q = q.limit(Number(limit));
|
|
77
|
+
if (offset !== undefined)
|
|
78
|
+
q = q.offset(Number(offset));
|
|
79
|
+
}
|
|
80
|
+
const rows = await q.all();
|
|
81
|
+
if (!withCount)
|
|
82
|
+
return rows;
|
|
83
|
+
// Count query: same WHERE, no limit/offset/orderBy.
|
|
84
|
+
let cq = opts.db.select({ c: count() }).from(opts.table).$dynamic();
|
|
85
|
+
if (where)
|
|
86
|
+
cq = cq.where(where);
|
|
87
|
+
const countRow = (await cq.all())[0];
|
|
88
|
+
const total = countRow?.c ?? 0;
|
|
89
|
+
return { rows, total };
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
if (err instanceof FilterParseError) {
|
|
93
|
+
return reply.code(400).send({ error: err.code, ...(err.details ?? {}) });
|
|
94
|
+
}
|
|
95
|
+
throw err;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
export function mountGetRoute(opts) {
|
|
100
|
+
opts.fastify.get(`${opts.path}/:id`, routeOpts(opts), async (req, reply) => {
|
|
101
|
+
const { id } = req.params;
|
|
102
|
+
const row = await opts.db
|
|
103
|
+
.select()
|
|
104
|
+
.from(opts.table)
|
|
105
|
+
.where(eq(opts.table.id, parseId(id)))
|
|
106
|
+
.get();
|
|
107
|
+
return row ?? reply.code(404).send({ error: "not_found" });
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
export function mountCreateRoute(opts) {
|
|
111
|
+
opts.fastify.post(opts.path, routeOpts(opts), async (req, reply) => {
|
|
112
|
+
const parsed = opts.insertSchema.safeParse(req.body);
|
|
113
|
+
if (!parsed.success) {
|
|
114
|
+
return reply.code(400).send({ error: "validation", issues: parsed.error.issues });
|
|
115
|
+
}
|
|
116
|
+
const result = await opts.db.insert(opts.table).values(parsed.data).returning();
|
|
117
|
+
const row = result[0];
|
|
118
|
+
return reply.code(201).send(row);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
export function mountUpdateRoute(opts) {
|
|
122
|
+
const handler = async (req, reply) => {
|
|
123
|
+
const { id } = req.params;
|
|
124
|
+
const parsed = opts.updateSchema.safeParse(req.body);
|
|
125
|
+
if (!parsed.success) {
|
|
126
|
+
return reply.code(400).send({ error: "validation", issues: parsed.error.issues });
|
|
127
|
+
}
|
|
128
|
+
const result = await opts.db
|
|
129
|
+
.update(opts.table)
|
|
130
|
+
.set(parsed.data)
|
|
131
|
+
.where(eq(opts.table.id, parseId(id)))
|
|
132
|
+
.returning();
|
|
133
|
+
const row = result[0];
|
|
134
|
+
return row ?? reply.code(404).send({ error: "not_found" });
|
|
135
|
+
};
|
|
136
|
+
const path = `${opts.path}/:id`;
|
|
137
|
+
const ro = routeOpts(opts);
|
|
138
|
+
// biome-ignore lint/suspicious/noExplicitAny: handler signature is generic
|
|
139
|
+
if (opts.updateMethod === "put") {
|
|
140
|
+
opts.fastify.put(path, ro, handler);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
opts.fastify.patch(path, ro, handler);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
export function mountDeleteRoute(opts) {
|
|
147
|
+
opts.fastify.delete(`${opts.path}/:id`, routeOpts(opts), async (req, reply) => {
|
|
148
|
+
const { id } = req.params;
|
|
149
|
+
const result = await opts.db
|
|
150
|
+
.delete(opts.table)
|
|
151
|
+
.where(eq(opts.table.id, parseId(id)));
|
|
152
|
+
// Both libsql and pg drivers expose a rows-affected counter, in different
|
|
153
|
+
// shapes. Treat anything > 0 as "found and deleted."
|
|
154
|
+
const affected = extractRowCount(result);
|
|
155
|
+
return affected > 0
|
|
156
|
+
? reply.code(204).send()
|
|
157
|
+
: reply.code(404).send({ error: "not_found" });
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
function extractRowCount(result) {
|
|
161
|
+
if (typeof result === "number")
|
|
162
|
+
return result;
|
|
163
|
+
if (Array.isArray(result))
|
|
164
|
+
return result.length;
|
|
165
|
+
if (result && typeof result === "object") {
|
|
166
|
+
const obj = result;
|
|
167
|
+
if (typeof obj.rowsAffected === "number")
|
|
168
|
+
return obj.rowsAffected;
|
|
169
|
+
if (typeof obj.rowsAffected === "bigint")
|
|
170
|
+
return Number(obj.rowsAffected);
|
|
171
|
+
if (typeof obj.rowCount === "number")
|
|
172
|
+
return obj.rowCount;
|
|
173
|
+
}
|
|
174
|
+
return 0;
|
|
175
|
+
}
|
|
176
|
+
export function parseId(raw) {
|
|
177
|
+
const n = Number(raw);
|
|
178
|
+
return Number.isFinite(n) && raw.trim() !== "" ? n : raw;
|
|
179
|
+
}
|
|
180
|
+
export { mountReadOnlyCrudRoutes } from "./mount-read-only.js";
|
|
181
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/drizzle-fastify/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import type { FilterAllowlist, SortAllowlist } from "./filter-allowlist.js";
|
|
3
|
+
type AnyView = any;
|
|
4
|
+
export interface MountReadOnlyOptions {
|
|
5
|
+
readonly fastify: FastifyInstance;
|
|
6
|
+
readonly path: string;
|
|
7
|
+
readonly db: any;
|
|
8
|
+
readonly view: AnyView;
|
|
9
|
+
readonly filterAllowlist: FilterAllowlist;
|
|
10
|
+
readonly sortAllowlist: SortAllowlist;
|
|
11
|
+
readonly dialect: "postgres" | "sqlite";
|
|
12
|
+
/** Override default ID column name (defaults to "id"). */
|
|
13
|
+
readonly idColumn?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function mountReadOnlyCrudRoutes(opts: MountReadOnlyOptions): void;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=mount-read-only.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mount-read-only.d.ts","sourceRoot":"","sources":["../../src/drizzle-fastify/mount-read-only.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI/C,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAI5E,KAAK,OAAO,GAAG,GAAG,CAAC;AAUnB,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC;IACjB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,QAAQ,CAAC;IACxC,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AA6DD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,oBAAoB,GAAG,IAAI,CAgGxE"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { sql, eq, and, count } from "drizzle-orm";
|
|
2
|
+
import qs from "qs";
|
|
3
|
+
import { parseFilterParams, FilterParseError } from "./filter-parser.js";
|
|
4
|
+
import { isTruthyFlag } from "./util.js";
|
|
5
|
+
/**
|
|
6
|
+
* Drizzle v0.45 stores view config under this well-known Symbol.
|
|
7
|
+
* Accessing `view._` on a proxy-wrapped view (empty-column .existing()) throws
|
|
8
|
+
* because the proxy tries to spread `subquery._.selectedFields` which is undefined.
|
|
9
|
+
* Using the symbol bypasses the proxy entirely.
|
|
10
|
+
*/
|
|
11
|
+
const VIEW_BASE_CONFIG = Symbol.for("drizzle:ViewBaseConfig");
|
|
12
|
+
const REJECT_MUTATION = async (request, reply) => {
|
|
13
|
+
reply
|
|
14
|
+
.code(405)
|
|
15
|
+
.send({ error: "method_not_allowed", message: `${request.method} is not supported on a projection (read-only).` });
|
|
16
|
+
};
|
|
17
|
+
function getViewConfig(view) {
|
|
18
|
+
try {
|
|
19
|
+
const cfg = view[VIEW_BASE_CONFIG];
|
|
20
|
+
if (cfg && typeof cfg === "object")
|
|
21
|
+
return cfg;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// ignore — proxy handler may throw on unexpected shapes
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
function resolveViewName(view) {
|
|
29
|
+
const cfg = getViewConfig(view);
|
|
30
|
+
if (cfg) {
|
|
31
|
+
if (typeof cfg["name"] === "string")
|
|
32
|
+
return cfg["name"];
|
|
33
|
+
}
|
|
34
|
+
// Fallback for non-proxy shapes
|
|
35
|
+
const v = view;
|
|
36
|
+
return ((typeof v["__tableName"] === "string" ? v["__tableName"] : undefined) ??
|
|
37
|
+
(typeof v["_name"] === "string" ? v["_name"] : undefined));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Detect whether a Drizzle view was declared with `.existing()` and an empty
|
|
41
|
+
* column schema (`{}`). In that case, `db.select().from(view)` generates
|
|
42
|
+
* `SELECT FROM ...` (invalid SQL), and we must fall back to raw SQL.
|
|
43
|
+
*/
|
|
44
|
+
function isEmptyColumnView(view) {
|
|
45
|
+
const cfg = getViewConfig(view);
|
|
46
|
+
if (cfg) {
|
|
47
|
+
const fields = cfg["selectedFields"];
|
|
48
|
+
return fields !== undefined && Object.keys(fields).length === 0;
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
function snakeToCamel(s) {
|
|
53
|
+
return s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
54
|
+
}
|
|
55
|
+
/** Normalise a raw SQL result row's keys from snake_case to camelCase. */
|
|
56
|
+
function camelizeRow(row) {
|
|
57
|
+
const out = {};
|
|
58
|
+
for (const [k, v] of Object.entries(row)) {
|
|
59
|
+
out[snakeToCamel(k)] = v;
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
export function mountReadOnlyCrudRoutes(opts) {
|
|
64
|
+
const { fastify, path, db, view, filterAllowlist, sortAllowlist, dialect } = opts;
|
|
65
|
+
const idCol = opts.idColumn ?? "id";
|
|
66
|
+
const viewName = resolveViewName(view);
|
|
67
|
+
const useRawSql = isEmptyColumnView(view) && !!viewName;
|
|
68
|
+
// ── List ──────────────────────────────────────────────────────────────────
|
|
69
|
+
fastify.get(path, async (req, reply) => {
|
|
70
|
+
try {
|
|
71
|
+
if (useRawSql) {
|
|
72
|
+
// .existing() view with no column schema — use raw SQL.
|
|
73
|
+
// Simple limit/offset only; filter/sort via allowlist is not available
|
|
74
|
+
// because column refs don't exist. This is sufficient for projection
|
|
75
|
+
// endpoints that return small full-table results.
|
|
76
|
+
const url = req.raw.url ?? "";
|
|
77
|
+
const qIdx = url.indexOf("?");
|
|
78
|
+
const queryString = qIdx >= 0 ? url.slice(qIdx + 1) : "";
|
|
79
|
+
const parsed = qs.parse(queryString);
|
|
80
|
+
const limitVal = Math.min(1000, Math.max(1, Number(typeof parsed["limit"] === "string" ? parsed["limit"] : 1000)));
|
|
81
|
+
const offsetVal = Math.max(0, Number(typeof parsed["offset"] === "string" ? parsed["offset"] : 0));
|
|
82
|
+
const withCount = isTruthyFlag(parsed["withCount"]);
|
|
83
|
+
// biome-ignore lint/suspicious/noExplicitAny: dynamic raw result
|
|
84
|
+
const rows = await db.all(sql.raw(`SELECT * FROM "${viewName}" LIMIT ${limitVal} OFFSET ${offsetVal}`));
|
|
85
|
+
const camelRows = rows.map((r) => camelizeRow(r));
|
|
86
|
+
if (!withCount)
|
|
87
|
+
return camelRows;
|
|
88
|
+
// biome-ignore lint/suspicious/noExplicitAny: dynamic raw result
|
|
89
|
+
const countRows = await db.all(sql.raw(`SELECT COUNT(*) AS c FROM "${viewName}"`));
|
|
90
|
+
const total = Number(countRows[0]?.c ?? 0);
|
|
91
|
+
return { rows: camelRows, total };
|
|
92
|
+
}
|
|
93
|
+
const url = req.raw.url ?? "";
|
|
94
|
+
const qIdx = url.indexOf("?");
|
|
95
|
+
const queryString = qIdx >= 0 ? url.slice(qIdx + 1) : "";
|
|
96
|
+
const parsed = qs.parse(queryString);
|
|
97
|
+
const withCount = isTruthyFlag(parsed["withCount"]);
|
|
98
|
+
const result = parseFilterParams({
|
|
99
|
+
query: parsed,
|
|
100
|
+
table: view,
|
|
101
|
+
allowlist: filterAllowlist,
|
|
102
|
+
sortAllowlist,
|
|
103
|
+
dialect,
|
|
104
|
+
});
|
|
105
|
+
const combinedWhere = result.where && result.searchWhere
|
|
106
|
+
? and(result.where, result.searchWhere)
|
|
107
|
+
: (result.where ?? result.searchWhere);
|
|
108
|
+
let q = db.select().from(view);
|
|
109
|
+
if (combinedWhere)
|
|
110
|
+
q = q.where(combinedWhere);
|
|
111
|
+
if (result.orderBy)
|
|
112
|
+
q = q.orderBy(...result.orderBy);
|
|
113
|
+
if (result.limit !== undefined)
|
|
114
|
+
q = q.limit(result.limit);
|
|
115
|
+
if (result.offset !== undefined)
|
|
116
|
+
q = q.offset(result.offset);
|
|
117
|
+
const rows = await q.all();
|
|
118
|
+
if (!withCount)
|
|
119
|
+
return rows;
|
|
120
|
+
// Count query: same WHERE, no limit/offset/orderBy.
|
|
121
|
+
let cq = db.select({ c: count() }).from(view);
|
|
122
|
+
if (combinedWhere)
|
|
123
|
+
cq = cq.where(combinedWhere);
|
|
124
|
+
const countRow = (await cq.all())[0];
|
|
125
|
+
const total = countRow?.c ?? 0;
|
|
126
|
+
return { rows, total };
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
if (err instanceof FilterParseError) {
|
|
130
|
+
reply.code(400).send({ error: err.code, message: err.message });
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
throw err;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
// ── Get by ID ─────────────────────────────────────────────────────────────
|
|
137
|
+
fastify.get(`${path}/:id`, async (req, reply) => {
|
|
138
|
+
const { id } = req.params;
|
|
139
|
+
if (useRawSql) {
|
|
140
|
+
const numericId = Number(id);
|
|
141
|
+
if (!Number.isFinite(numericId)) {
|
|
142
|
+
return reply.code(400).send({ error: "invalid_id" });
|
|
143
|
+
}
|
|
144
|
+
// biome-ignore lint/suspicious/noExplicitAny: dynamic raw result
|
|
145
|
+
const rows = await db.all(sql.raw(`SELECT * FROM "${viewName}" WHERE "${idCol}" = ${numericId} LIMIT 1`));
|
|
146
|
+
const row = rows[0] ? camelizeRow(rows[0]) : undefined;
|
|
147
|
+
return row ?? reply.code(404).send({ error: "not_found" });
|
|
148
|
+
}
|
|
149
|
+
// biome-ignore lint/suspicious/noExplicitAny: Drizzle table/view column ref
|
|
150
|
+
const colRef = view[idCol];
|
|
151
|
+
const row = await db.select().from(view).where(colRef !== undefined ? eq(colRef, Number(id)) : undefined).get();
|
|
152
|
+
return row ?? reply.code(404).send({ error: "not_found" });
|
|
153
|
+
});
|
|
154
|
+
// ── Mutations explicitly rejected (405) ───────────────────────────────────
|
|
155
|
+
fastify.post(path, REJECT_MUTATION);
|
|
156
|
+
fastify.patch(`${path}/:id`, REJECT_MUTATION);
|
|
157
|
+
fastify.delete(`${path}/:id`, REJECT_MUTATION);
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=mount-read-only.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mount-read-only.js","sourceRoot":"","sources":["../../src/drizzle-fastify/mount-read-only.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/drizzle-fastify/util.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAIhD"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for drizzle-fastify route helpers.
|
|
3
|
+
*/
|
|
4
|
+
// Accepts "1" or boolean true (the qs serialization of withCount: 1 from buildFilterQs).
|
|
5
|
+
export function isTruthyFlag(v) {
|
|
6
|
+
if (v === undefined || v === null)
|
|
7
|
+
return false;
|
|
8
|
+
if (typeof v === "boolean")
|
|
9
|
+
return v;
|
|
10
|
+
return String(v) === "1";
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/drizzle-fastify/util.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,yFAAyF;AACzF,MAAM,UAAU,YAAY,CAAC,CAAU;IACrC,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAChD,IAAI,OAAO,CAAC,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC;IACrC,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC;AAC3B,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export interface ValidationFailure {
|
|
2
|
+
field: string;
|
|
3
|
+
rule: string;
|
|
4
|
+
message: string;
|
|
5
|
+
expected?: unknown;
|
|
6
|
+
received?: unknown;
|
|
7
|
+
}
|
|
8
|
+
interface BaseOpts {
|
|
9
|
+
code?: string;
|
|
10
|
+
entity?: string;
|
|
11
|
+
cause?: unknown;
|
|
12
|
+
}
|
|
13
|
+
export declare class RuntimeError extends Error {
|
|
14
|
+
readonly code: string;
|
|
15
|
+
readonly entity?: string;
|
|
16
|
+
readonly cause?: unknown;
|
|
17
|
+
constructor(message: string, opts: BaseOpts & {
|
|
18
|
+
code: string;
|
|
19
|
+
});
|
|
20
|
+
toJSON(): Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
export declare class ValidationError extends RuntimeError {
|
|
23
|
+
readonly errors: ValidationFailure[];
|
|
24
|
+
constructor(message: string, opts: {
|
|
25
|
+
entity?: string;
|
|
26
|
+
errors: ValidationFailure[];
|
|
27
|
+
cause?: unknown;
|
|
28
|
+
});
|
|
29
|
+
toJSON(): Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
export declare class NotFoundError extends RuntimeError {
|
|
32
|
+
readonly id: unknown;
|
|
33
|
+
constructor(message: string, opts: {
|
|
34
|
+
entity: string;
|
|
35
|
+
id: unknown;
|
|
36
|
+
cause?: unknown;
|
|
37
|
+
});
|
|
38
|
+
toJSON(): Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
export declare class ConstraintViolationError extends RuntimeError {
|
|
41
|
+
readonly kind: "unique" | "foreign_key" | "not_null" | "check";
|
|
42
|
+
readonly table: string;
|
|
43
|
+
readonly field?: string;
|
|
44
|
+
readonly detail?: string;
|
|
45
|
+
constructor(message: string, opts: {
|
|
46
|
+
kind: "unique" | "foreign_key" | "not_null" | "check";
|
|
47
|
+
table: string;
|
|
48
|
+
field?: string;
|
|
49
|
+
detail?: string;
|
|
50
|
+
cause?: unknown;
|
|
51
|
+
});
|
|
52
|
+
toJSON(): Record<string, unknown>;
|
|
53
|
+
}
|
|
54
|
+
export declare class MetadataError extends RuntimeError {
|
|
55
|
+
constructor(message: string, opts?: {
|
|
56
|
+
entity?: string;
|
|
57
|
+
cause?: unknown;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
export declare class UnsafeNameError extends RuntimeError {
|
|
61
|
+
readonly value: string;
|
|
62
|
+
constructor(message: string, opts: {
|
|
63
|
+
value: string;
|
|
64
|
+
});
|
|
65
|
+
toJSON(): Record<string, unknown>;
|
|
66
|
+
}
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,UAAU,QAAQ;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,SAAkB,KAAK,CAAC,EAAE,OAAO,CAAC;gBAEtB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;IAQ9D,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAKlC;AAED,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,QAAQ,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC;gBAEzB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;IAM3F,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAG3C;AAED,qBAAa,aAAc,SAAQ,YAAY;IAC7C,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;gBAET,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;IAM1E,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAG3C;AAED,qBAAa,wBAAyB,SAAQ,YAAY;IACxD,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,aAAa,GAAG,UAAU,GAAG,OAAO,CAAC;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE;QACjC,IAAI,EAAE,QAAQ,GAAG,aAAa,GAAG,UAAU,GAAG,OAAO,CAAC;QACtD,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB;IASQ,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAM3C;AAED,qBAAa,aAAc,SAAQ,YAAY;gBACjC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO;CAI7E;AAED,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE;IAM3C,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAG3C"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// All errors extend RuntimeError and JSON-serialize via toJSON() — admin UIs and MCP tools
|
|
2
|
+
// surface them as structured JSON, not Error.message strings.
|
|
3
|
+
export class RuntimeError extends Error {
|
|
4
|
+
code;
|
|
5
|
+
entity;
|
|
6
|
+
cause;
|
|
7
|
+
constructor(message, opts) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "RuntimeError";
|
|
10
|
+
this.code = opts.code;
|
|
11
|
+
if (opts.entity !== undefined)
|
|
12
|
+
this.entity = opts.entity;
|
|
13
|
+
if (opts.cause !== undefined)
|
|
14
|
+
this.cause = opts.cause;
|
|
15
|
+
}
|
|
16
|
+
toJSON() {
|
|
17
|
+
const out = { name: this.name, code: this.code, message: this.message };
|
|
18
|
+
if (this.entity !== undefined)
|
|
19
|
+
out.entity = this.entity;
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export class ValidationError extends RuntimeError {
|
|
24
|
+
errors;
|
|
25
|
+
constructor(message, opts) {
|
|
26
|
+
super(message, { code: "validation_failed", ...(opts.entity !== undefined ? { entity: opts.entity } : {}), ...(opts.cause !== undefined ? { cause: opts.cause } : {}) });
|
|
27
|
+
this.name = "ValidationError";
|
|
28
|
+
this.errors = opts.errors;
|
|
29
|
+
}
|
|
30
|
+
toJSON() {
|
|
31
|
+
return { ...super.toJSON(), errors: this.errors };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export class NotFoundError extends RuntimeError {
|
|
35
|
+
id;
|
|
36
|
+
constructor(message, opts) {
|
|
37
|
+
super(message, { code: "not_found", entity: opts.entity, ...(opts.cause !== undefined ? { cause: opts.cause } : {}) });
|
|
38
|
+
this.name = "NotFoundError";
|
|
39
|
+
this.id = opts.id;
|
|
40
|
+
}
|
|
41
|
+
toJSON() {
|
|
42
|
+
return { ...super.toJSON(), id: this.id };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export class ConstraintViolationError extends RuntimeError {
|
|
46
|
+
kind;
|
|
47
|
+
table;
|
|
48
|
+
field;
|
|
49
|
+
detail;
|
|
50
|
+
constructor(message, opts) {
|
|
51
|
+
super(message, { code: "constraint_violation", ...(opts.cause !== undefined ? { cause: opts.cause } : {}) });
|
|
52
|
+
this.name = "ConstraintViolationError";
|
|
53
|
+
this.kind = opts.kind;
|
|
54
|
+
this.table = opts.table;
|
|
55
|
+
if (opts.field !== undefined)
|
|
56
|
+
this.field = opts.field;
|
|
57
|
+
if (opts.detail !== undefined)
|
|
58
|
+
this.detail = opts.detail;
|
|
59
|
+
}
|
|
60
|
+
toJSON() {
|
|
61
|
+
const out = { ...super.toJSON(), kind: this.kind, table: this.table };
|
|
62
|
+
if (this.field !== undefined)
|
|
63
|
+
out.field = this.field;
|
|
64
|
+
if (this.detail !== undefined)
|
|
65
|
+
out.detail = this.detail;
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export class MetadataError extends RuntimeError {
|
|
70
|
+
constructor(message, opts = {}) {
|
|
71
|
+
super(message, { code: "metadata_error", ...(opts.entity !== undefined ? { entity: opts.entity } : {}), ...(opts.cause !== undefined ? { cause: opts.cause } : {}) });
|
|
72
|
+
this.name = "MetadataError";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export class UnsafeNameError extends RuntimeError {
|
|
76
|
+
value;
|
|
77
|
+
constructor(message, opts) {
|
|
78
|
+
super(message, { code: "unsafe_name" });
|
|
79
|
+
this.name = "UnsafeNameError";
|
|
80
|
+
this.value = opts.value;
|
|
81
|
+
}
|
|
82
|
+
toJSON() {
|
|
83
|
+
return { ...super.toJSON(), value: this.value };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import type { ZodTypeAny } from "zod";
|
|
3
|
+
import type { ObjectManager } from "../object-manager.js";
|
|
4
|
+
import type { RouteShorthandOptions } from "fastify";
|
|
5
|
+
export type CrudVerb = "list" | "get" | "create" | "update" | "delete";
|
|
6
|
+
export interface CrudRoutesOptions {
|
|
7
|
+
fastify: FastifyInstance;
|
|
8
|
+
/** REST resource path, e.g. "/subscribers". */
|
|
9
|
+
path: string;
|
|
10
|
+
/** Entity name from metadata, e.g. "Subscriber". */
|
|
11
|
+
entity: string;
|
|
12
|
+
/** Zod schema for create payloads (typically `<Entity>InsertSchema`). */
|
|
13
|
+
insertSchema: ZodTypeAny;
|
|
14
|
+
/** Zod schema for update payloads (typically `<Entity>UpdateSchema`). */
|
|
15
|
+
updateSchema: ZodTypeAny;
|
|
16
|
+
/**
|
|
17
|
+
* ObjectManager accessor. A function rather than the instance so callers
|
|
18
|
+
* can keep their lazy-init pattern (load metadata once at startup).
|
|
19
|
+
*/
|
|
20
|
+
om: () => Promise<ObjectManager>;
|
|
21
|
+
/**
|
|
22
|
+
* Limit which verbs are mounted. Defaults to all five. Useful for
|
|
23
|
+
* read-only resources (`expose: ["list", "get"]`).
|
|
24
|
+
*/
|
|
25
|
+
expose?: readonly CrudVerb[];
|
|
26
|
+
/**
|
|
27
|
+
* Fastify route-level hooks applied to every mounted verb. The most common
|
|
28
|
+
* use is `preHandler` for auth/authz:
|
|
29
|
+
*
|
|
30
|
+
* routeOptions: { preHandler: requireAdminAuth }
|
|
31
|
+
*
|
|
32
|
+
* Anything Fastify accepts on a route options object is valid here
|
|
33
|
+
* (preHandler, onRequest, preValidation, preParsing, schema, etc.).
|
|
34
|
+
*/
|
|
35
|
+
routeOptions?: RouteShorthandOptions;
|
|
36
|
+
/**
|
|
37
|
+
* HTTP method for the update verb. Defaults to "patch" (semantic partial-
|
|
38
|
+
* update). Set to "put" to preserve a legacy API contract that already
|
|
39
|
+
* uses PUT for updates.
|
|
40
|
+
*/
|
|
41
|
+
updateMethod?: "patch" | "put";
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Mount the 5 standard REST endpoints for an entity:
|
|
45
|
+
*
|
|
46
|
+
* GET {path} → list, with ?limit & ?offset query params
|
|
47
|
+
* GET {path}/:id → findById
|
|
48
|
+
* POST {path} → create (validated via insertSchema)
|
|
49
|
+
* PATCH {path}/:id → partial update (validated via updateSchema)
|
|
50
|
+
* DELETE {path}/:id → delete
|
|
51
|
+
*/
|
|
52
|
+
export declare function mountCrudRoutes(opts: CrudRoutesOptions): void;
|
|
53
|
+
type SingleVerbOptions = Omit<CrudRoutesOptions, "expose">;
|
|
54
|
+
export declare function mountListRoute(opts: SingleVerbOptions): void;
|
|
55
|
+
export declare function mountGetRoute(opts: SingleVerbOptions): void;
|
|
56
|
+
export declare function mountCreateRoute(opts: SingleVerbOptions): void;
|
|
57
|
+
export declare function mountUpdateRoute(opts: SingleVerbOptions): void;
|
|
58
|
+
export declare function mountDeleteRoute(opts: SingleVerbOptions): void;
|
|
59
|
+
/**
|
|
60
|
+
* Coerce a `:id` path param. Numeric strings parse as Number (covers long/int
|
|
61
|
+
* PKs); anything else passes through as a string (UUIDs, string keys).
|
|
62
|
+
*/
|
|
63
|
+
export declare function parseId(raw: string): number | string;
|
|
64
|
+
export {};
|
|
65
|
+
//# sourceMappingURL=index.d.ts.map
|