@proseql/rest 0.1.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/LICENSE +21 -0
- package/dist/error-mapping.d.ts +60 -0
- package/dist/error-mapping.d.ts.map +1 -0
- package/dist/error-mapping.js +183 -0
- package/dist/error-mapping.js.map +1 -0
- package/dist/handlers.d.ts +112 -0
- package/dist/handlers.d.ts.map +1 -0
- package/dist/handlers.js +402 -0
- package/dist/handlers.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/query-params.d.ts +132 -0
- package/dist/query-params.d.ts.map +1 -0
- package/dist/query-params.js +380 -0
- package/dist/query-params.js.map +1 -0
- package/dist/relationship-routes.d.ts +82 -0
- package/dist/relationship-routes.d.ts.map +1 -0
- package/dist/relationship-routes.js +273 -0
- package/dist/relationship-routes.js.map +1 -0
- package/package.json +49 -0
package/dist/handlers.js
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REST Handler Generation for proseql databases.
|
|
3
|
+
*
|
|
4
|
+
* Generates framework-agnostic HTTP handlers for CRUD operations, queries,
|
|
5
|
+
* and aggregations from a DatabaseConfig and EffectDatabase instance.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { Cause, Chunk, Effect, Option, Runtime, Stream } from "effect";
|
|
10
|
+
import { parseAggregateParams, parseQueryParams } from "./query-params.js";
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Handler Factory
|
|
13
|
+
// ============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* Create REST handlers for all collections in a database.
|
|
16
|
+
*
|
|
17
|
+
* Generates framework-agnostic route descriptors that can be adapted to any
|
|
18
|
+
* HTTP framework (Express, Hono, Bun.serve, etc.).
|
|
19
|
+
*
|
|
20
|
+
* Generated routes per collection:
|
|
21
|
+
* - GET /:collection — Query with filters, sort, pagination
|
|
22
|
+
* - GET /:collection/:id — Find by ID
|
|
23
|
+
* - POST /:collection — Create entity
|
|
24
|
+
* - PUT /:collection/:id — Update entity
|
|
25
|
+
* - DELETE /:collection/:id — Delete entity
|
|
26
|
+
* - POST /:collection/batch — Create multiple entities
|
|
27
|
+
* - GET /:collection/aggregate — Aggregation queries
|
|
28
|
+
*
|
|
29
|
+
* @param config - The database configuration defining collections
|
|
30
|
+
* @param db - An EffectDatabase or EffectDatabaseWithPersistence instance
|
|
31
|
+
* @returns Array of route descriptors with method, path, and handler
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* import { createRestHandlers } from "@proseql/rest"
|
|
36
|
+
* import { createEffectDatabase } from "@proseql/core"
|
|
37
|
+
*
|
|
38
|
+
* const config = {
|
|
39
|
+
* books: { schema: BookSchema, relationships: {} },
|
|
40
|
+
* } as const
|
|
41
|
+
*
|
|
42
|
+
* const db = await Effect.runPromise(createEffectDatabase(config, initialData))
|
|
43
|
+
* const routes = createRestHandlers(config, db)
|
|
44
|
+
*
|
|
45
|
+
* // Adapt to your framework:
|
|
46
|
+
* for (const { method, path, handler } of routes) {
|
|
47
|
+
* app[method.toLowerCase()](path, async (req, res) => {
|
|
48
|
+
* const response = await handler({
|
|
49
|
+
* params: req.params,
|
|
50
|
+
* query: req.query,
|
|
51
|
+
* body: req.body,
|
|
52
|
+
* })
|
|
53
|
+
* res.status(response.status).json(response.body)
|
|
54
|
+
* })
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export const createRestHandlers = (config, db) => {
|
|
59
|
+
const routes = [];
|
|
60
|
+
// Generate routes for each collection
|
|
61
|
+
for (const collectionName of Object.keys(config)) {
|
|
62
|
+
// biome-ignore lint/suspicious/noExplicitAny: Collection type is dynamic based on config
|
|
63
|
+
const collection = db[collectionName];
|
|
64
|
+
// GET /:collection — Query with filters, sort, pagination
|
|
65
|
+
routes.push({
|
|
66
|
+
method: "GET",
|
|
67
|
+
path: `/${collectionName}`,
|
|
68
|
+
handler: createQueryHandler(collection),
|
|
69
|
+
});
|
|
70
|
+
// GET /:collection/aggregate — Aggregation queries
|
|
71
|
+
// Must be before /:collection/:id to avoid matching "aggregate" as an ID
|
|
72
|
+
routes.push({
|
|
73
|
+
method: "GET",
|
|
74
|
+
path: `/${collectionName}/aggregate`,
|
|
75
|
+
handler: createAggregateHandler(collection),
|
|
76
|
+
});
|
|
77
|
+
// GET /:collection/:id — Find by ID
|
|
78
|
+
routes.push({
|
|
79
|
+
method: "GET",
|
|
80
|
+
path: `/${collectionName}/:id`,
|
|
81
|
+
handler: createFindByIdHandler(collection),
|
|
82
|
+
});
|
|
83
|
+
// POST /:collection — Create entity
|
|
84
|
+
routes.push({
|
|
85
|
+
method: "POST",
|
|
86
|
+
path: `/${collectionName}`,
|
|
87
|
+
handler: createCreateHandler(collection),
|
|
88
|
+
});
|
|
89
|
+
// POST /:collection/batch — Create multiple entities
|
|
90
|
+
routes.push({
|
|
91
|
+
method: "POST",
|
|
92
|
+
path: `/${collectionName}/batch`,
|
|
93
|
+
handler: createBatchHandler(collection),
|
|
94
|
+
});
|
|
95
|
+
// PUT /:collection/:id — Update entity
|
|
96
|
+
routes.push({
|
|
97
|
+
method: "PUT",
|
|
98
|
+
path: `/${collectionName}/:id`,
|
|
99
|
+
handler: createUpdateHandler(collection),
|
|
100
|
+
});
|
|
101
|
+
// DELETE /:collection/:id — Delete entity
|
|
102
|
+
routes.push({
|
|
103
|
+
method: "DELETE",
|
|
104
|
+
path: `/${collectionName}/:id`,
|
|
105
|
+
handler: createDeleteHandler(collection),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return routes;
|
|
109
|
+
};
|
|
110
|
+
// ============================================================================
|
|
111
|
+
// Individual Handler Factories
|
|
112
|
+
// ============================================================================
|
|
113
|
+
/**
|
|
114
|
+
* Create a GET handler for querying a collection.
|
|
115
|
+
* Parses query parameters and delegates to the collection's query method.
|
|
116
|
+
*/
|
|
117
|
+
const createQueryHandler = (
|
|
118
|
+
// biome-ignore lint/suspicious/noExplicitAny: Collection type is dynamic
|
|
119
|
+
collection) => {
|
|
120
|
+
return async (req) => {
|
|
121
|
+
// Parse query params into proseql-compatible query config
|
|
122
|
+
const queryConfig = parseQueryParams(req.query);
|
|
123
|
+
const stream = collection.query(queryConfig);
|
|
124
|
+
const result = await Effect.runPromise(Stream.runCollect(stream).pipe(Effect.map(Chunk.toReadonlyArray)));
|
|
125
|
+
return { status: 200, body: result };
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* Create a GET handler for finding an entity by ID.
|
|
130
|
+
*/
|
|
131
|
+
const createFindByIdHandler = (
|
|
132
|
+
// biome-ignore lint/suspicious/noExplicitAny: Collection type is dynamic
|
|
133
|
+
collection) => {
|
|
134
|
+
return async (req) => {
|
|
135
|
+
const { id } = req.params;
|
|
136
|
+
try {
|
|
137
|
+
const findEffect = collection.findById(id);
|
|
138
|
+
const entity = await Effect.runPromise(findEffect);
|
|
139
|
+
return { status: 200, body: entity };
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
// TODO: Map errors via mapErrorToResponse (task 8.1)
|
|
143
|
+
if (isTaggedError(error, "NotFoundError")) {
|
|
144
|
+
return {
|
|
145
|
+
status: 404,
|
|
146
|
+
body: { error: "Not found", _tag: "NotFoundError" },
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return { status: 500, body: { error: "Internal server error" } };
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* Create a POST handler for creating an entity.
|
|
155
|
+
*/
|
|
156
|
+
const createCreateHandler = (
|
|
157
|
+
// biome-ignore lint/suspicious/noExplicitAny: Collection type is dynamic
|
|
158
|
+
collection) => {
|
|
159
|
+
return async (req) => {
|
|
160
|
+
try {
|
|
161
|
+
const createEffect = collection.create(req.body);
|
|
162
|
+
const entity = await Effect.runPromise(createEffect);
|
|
163
|
+
return { status: 201, body: entity };
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
// TODO: Map errors via mapErrorToResponse (task 8.1)
|
|
167
|
+
if (isTaggedError(error, "ValidationError")) {
|
|
168
|
+
return {
|
|
169
|
+
status: 400,
|
|
170
|
+
body: { error: "Validation error", _tag: "ValidationError" },
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (isTaggedError(error, "DuplicateKeyError")) {
|
|
174
|
+
return {
|
|
175
|
+
status: 409,
|
|
176
|
+
body: { error: "Duplicate key", _tag: "DuplicateKeyError" },
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
if (isTaggedError(error, "UniqueConstraintError")) {
|
|
180
|
+
return {
|
|
181
|
+
status: 409,
|
|
182
|
+
body: {
|
|
183
|
+
error: "Unique constraint violation",
|
|
184
|
+
_tag: "UniqueConstraintError",
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
if (isTaggedError(error, "ForeignKeyError")) {
|
|
189
|
+
return {
|
|
190
|
+
status: 422,
|
|
191
|
+
body: { error: "Foreign key violation", _tag: "ForeignKeyError" },
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
if (isTaggedError(error, "HookError")) {
|
|
195
|
+
return {
|
|
196
|
+
status: 422,
|
|
197
|
+
body: { error: "Hook error", _tag: "HookError" },
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
return { status: 500, body: { error: "Internal server error" } };
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Create a PUT handler for updating an entity.
|
|
206
|
+
*/
|
|
207
|
+
const createUpdateHandler = (
|
|
208
|
+
// biome-ignore lint/suspicious/noExplicitAny: Collection type is dynamic
|
|
209
|
+
collection) => {
|
|
210
|
+
return async (req) => {
|
|
211
|
+
const { id } = req.params;
|
|
212
|
+
try {
|
|
213
|
+
const updateEffect = collection.update(id, req.body);
|
|
214
|
+
const entity = await Effect.runPromise(updateEffect);
|
|
215
|
+
return { status: 200, body: entity };
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
// TODO: Map errors via mapErrorToResponse (task 8.1)
|
|
219
|
+
if (isTaggedError(error, "NotFoundError")) {
|
|
220
|
+
return {
|
|
221
|
+
status: 404,
|
|
222
|
+
body: { error: "Not found", _tag: "NotFoundError" },
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
if (isTaggedError(error, "ValidationError")) {
|
|
226
|
+
return {
|
|
227
|
+
status: 400,
|
|
228
|
+
body: { error: "Validation error", _tag: "ValidationError" },
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
if (isTaggedError(error, "UniqueConstraintError")) {
|
|
232
|
+
return {
|
|
233
|
+
status: 409,
|
|
234
|
+
body: {
|
|
235
|
+
error: "Unique constraint violation",
|
|
236
|
+
_tag: "UniqueConstraintError",
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
if (isTaggedError(error, "HookError")) {
|
|
241
|
+
return {
|
|
242
|
+
status: 422,
|
|
243
|
+
body: { error: "Hook error", _tag: "HookError" },
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
return { status: 500, body: { error: "Internal server error" } };
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
};
|
|
250
|
+
/**
|
|
251
|
+
* Create a DELETE handler for deleting an entity.
|
|
252
|
+
*/
|
|
253
|
+
const createDeleteHandler = (
|
|
254
|
+
// biome-ignore lint/suspicious/noExplicitAny: Collection type is dynamic
|
|
255
|
+
collection) => {
|
|
256
|
+
return async (req) => {
|
|
257
|
+
const { id } = req.params;
|
|
258
|
+
try {
|
|
259
|
+
const deleteEffect = collection.delete(id);
|
|
260
|
+
const entity = await Effect.runPromise(deleteEffect);
|
|
261
|
+
return { status: 200, body: entity };
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
// TODO: Map errors via mapErrorToResponse (task 8.1)
|
|
265
|
+
if (isTaggedError(error, "NotFoundError")) {
|
|
266
|
+
return {
|
|
267
|
+
status: 404,
|
|
268
|
+
body: { error: "Not found", _tag: "NotFoundError" },
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
if (isTaggedError(error, "HookError")) {
|
|
272
|
+
return {
|
|
273
|
+
status: 422,
|
|
274
|
+
body: { error: "Hook error", _tag: "HookError" },
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
return { status: 500, body: { error: "Internal server error" } };
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
};
|
|
281
|
+
/**
|
|
282
|
+
* Create a POST handler for batch creating entities.
|
|
283
|
+
*/
|
|
284
|
+
const createBatchHandler = (
|
|
285
|
+
// biome-ignore lint/suspicious/noExplicitAny: Collection type is dynamic
|
|
286
|
+
collection) => {
|
|
287
|
+
return async (req) => {
|
|
288
|
+
try {
|
|
289
|
+
const batchEffect = collection.createMany(req.body);
|
|
290
|
+
const result = await Effect.runPromise(batchEffect);
|
|
291
|
+
return { status: 201, body: result };
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
// TODO: Map errors via mapErrorToResponse (task 8.1)
|
|
295
|
+
if (isTaggedError(error, "ValidationError")) {
|
|
296
|
+
return {
|
|
297
|
+
status: 400,
|
|
298
|
+
body: { error: "Validation error", _tag: "ValidationError" },
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
if (isTaggedError(error, "DuplicateKeyError")) {
|
|
302
|
+
return {
|
|
303
|
+
status: 409,
|
|
304
|
+
body: { error: "Duplicate key", _tag: "DuplicateKeyError" },
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
if (isTaggedError(error, "UniqueConstraintError")) {
|
|
308
|
+
return {
|
|
309
|
+
status: 409,
|
|
310
|
+
body: {
|
|
311
|
+
error: "Unique constraint violation",
|
|
312
|
+
_tag: "UniqueConstraintError",
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
if (isTaggedError(error, "ForeignKeyError")) {
|
|
317
|
+
return {
|
|
318
|
+
status: 422,
|
|
319
|
+
body: { error: "Foreign key violation", _tag: "ForeignKeyError" },
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
if (isTaggedError(error, "HookError")) {
|
|
323
|
+
return {
|
|
324
|
+
status: 422,
|
|
325
|
+
body: { error: "Hook error", _tag: "HookError" },
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
return { status: 500, body: { error: "Internal server error" } };
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
};
|
|
332
|
+
/**
|
|
333
|
+
* Create a GET handler for aggregation queries.
|
|
334
|
+
* Parses aggregate query parameters and delegates to the collection's aggregate method.
|
|
335
|
+
*
|
|
336
|
+
* Supported query parameters:
|
|
337
|
+
* - count=true: Count entities
|
|
338
|
+
* - sum=field or sum=field1,field2: Sum numeric fields
|
|
339
|
+
* - avg=field: Calculate average of numeric fields
|
|
340
|
+
* - min=field: Find minimum value
|
|
341
|
+
* - max=field: Find maximum value
|
|
342
|
+
* - groupBy=field or groupBy=field1,field2: Group results by field(s)
|
|
343
|
+
* - Filter params: Same as query endpoint (e.g., genre=sci-fi, year[$gte]=1970)
|
|
344
|
+
*
|
|
345
|
+
* If no aggregation is specified, defaults to count=true.
|
|
346
|
+
*/
|
|
347
|
+
const createAggregateHandler = (
|
|
348
|
+
// biome-ignore lint/suspicious/noExplicitAny: Collection type is dynamic
|
|
349
|
+
collection) => {
|
|
350
|
+
return async (req) => {
|
|
351
|
+
try {
|
|
352
|
+
// Parse aggregate query params
|
|
353
|
+
const aggregateConfig = parseAggregateParams(req.query);
|
|
354
|
+
const aggregateEffect = collection.aggregate(aggregateConfig);
|
|
355
|
+
const result = await Effect.runPromise(aggregateEffect);
|
|
356
|
+
return { status: 200, body: result };
|
|
357
|
+
}
|
|
358
|
+
catch (_error) {
|
|
359
|
+
return { status: 500, body: { error: "Internal server error" } };
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
};
|
|
363
|
+
// ============================================================================
|
|
364
|
+
// Utility Functions
|
|
365
|
+
// ============================================================================
|
|
366
|
+
/**
|
|
367
|
+
* Extract a tagged error from an unknown error value.
|
|
368
|
+
*
|
|
369
|
+
* Effect.runPromise throws a FiberFailure when the Effect fails.
|
|
370
|
+
* This function extracts the underlying tagged error from the FiberFailure
|
|
371
|
+
* or returns the error directly if it's already a tagged error.
|
|
372
|
+
*/
|
|
373
|
+
const extractTaggedError = (error) => {
|
|
374
|
+
// Check if it's a FiberFailure (from Effect.runPromise)
|
|
375
|
+
if (Runtime.isFiberFailure(error)) {
|
|
376
|
+
// Get the cause from the FiberFailure using the well-known symbol
|
|
377
|
+
const causeSymbol = Symbol.for("effect/Runtime/FiberFailure/Cause");
|
|
378
|
+
const cause = error[causeSymbol];
|
|
379
|
+
// Extract the failure from the cause
|
|
380
|
+
const failure = Cause.failureOption(cause);
|
|
381
|
+
if (Option.isSome(failure)) {
|
|
382
|
+
const value = failure.value;
|
|
383
|
+
if (value !== null && typeof value === "object" && "_tag" in value) {
|
|
384
|
+
return value;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
// Check if it's already a tagged error
|
|
389
|
+
if (error !== null && typeof error === "object" && "_tag" in error) {
|
|
390
|
+
return error;
|
|
391
|
+
}
|
|
392
|
+
return null;
|
|
393
|
+
};
|
|
394
|
+
/**
|
|
395
|
+
* Type guard to check if an error has a specific _tag.
|
|
396
|
+
* Handles both direct tagged errors and FiberFailure wrappers.
|
|
397
|
+
*/
|
|
398
|
+
const isTaggedError = (error, tag) => {
|
|
399
|
+
const taggedError = extractTaggedError(error);
|
|
400
|
+
return taggedError !== null && taggedError._tag === tag;
|
|
401
|
+
};
|
|
402
|
+
//# sourceMappingURL=handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handlers.js","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* proseql-rest — Auto-generated REST API for proseql databases.
|
|
3
|
+
*
|
|
4
|
+
* Given a proseql DatabaseConfig, generates framework-agnostic HTTP handlers
|
|
5
|
+
* for CRUD operations, queries, and aggregations. Includes query parameter
|
|
6
|
+
* parsing for filters, sorting, pagination, and field selection.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { createRestHandlers } from "@proseql/rest"
|
|
11
|
+
* import { createEffectDatabase } from "@proseql/core"
|
|
12
|
+
*
|
|
13
|
+
* const handlers = createRestHandlers(config, db)
|
|
14
|
+
*
|
|
15
|
+
* // Framework-agnostic handler signature:
|
|
16
|
+
* // (req: { params, query, body }) => Promise<{ status, body }>
|
|
17
|
+
*
|
|
18
|
+
* // Generated routes:
|
|
19
|
+
* // GET /books — query with filters, sort, pagination
|
|
20
|
+
* // GET /books/:id — findById
|
|
21
|
+
* // POST /books — create
|
|
22
|
+
* // PUT /books/:id — update
|
|
23
|
+
* // DELETE /books/:id — delete
|
|
24
|
+
* // POST /books/batch — createMany
|
|
25
|
+
* // GET /books/aggregate — aggregation
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @module
|
|
29
|
+
*/
|
|
30
|
+
export { createRestHandlers, type HttpMethod, type RestHandler, type RestRequest, type RestResponse, type RouteDescriptor, } from "./handlers.js";
|
|
31
|
+
export { type ParsedAggregateConfig, type ParsedQueryConfig, parseAggregateParams, parseQueryParams, type QueryParams, } from "./query-params.js";
|
|
32
|
+
export { type ErrorResponse, mapErrorToResponse } from "./error-mapping.js";
|
|
33
|
+
export { createRelationshipRoutes, extractRelationships, } from "./relationship-routes.js";
|
|
34
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAMH,OAAO,EACN,kBAAkB,EAClB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,eAAe,GACpB,MAAM,eAAe,CAAC;AAMvB,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,WAAW,GAChB,MAAM,mBAAmB,CAAC;AAM3B,OAAO,EAAE,KAAK,aAAa,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAM5E,OAAO,EACN,wBAAwB,EACxB,oBAAoB,GACpB,MAAM,0BAA0B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* proseql-rest — Auto-generated REST API for proseql databases.
|
|
3
|
+
*
|
|
4
|
+
* Given a proseql DatabaseConfig, generates framework-agnostic HTTP handlers
|
|
5
|
+
* for CRUD operations, queries, and aggregations. Includes query parameter
|
|
6
|
+
* parsing for filters, sorting, pagination, and field selection.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { createRestHandlers } from "@proseql/rest"
|
|
11
|
+
* import { createEffectDatabase } from "@proseql/core"
|
|
12
|
+
*
|
|
13
|
+
* const handlers = createRestHandlers(config, db)
|
|
14
|
+
*
|
|
15
|
+
* // Framework-agnostic handler signature:
|
|
16
|
+
* // (req: { params, query, body }) => Promise<{ status, body }>
|
|
17
|
+
*
|
|
18
|
+
* // Generated routes:
|
|
19
|
+
* // GET /books — query with filters, sort, pagination
|
|
20
|
+
* // GET /books/:id — findById
|
|
21
|
+
* // POST /books — create
|
|
22
|
+
* // PUT /books/:id — update
|
|
23
|
+
* // DELETE /books/:id — delete
|
|
24
|
+
* // POST /books/batch — createMany
|
|
25
|
+
* // GET /books/aggregate — aggregation
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @module
|
|
29
|
+
*/
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Handler Generation
|
|
32
|
+
// ============================================================================
|
|
33
|
+
export { createRestHandlers, } from "./handlers.js";
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// Query Parameter Parsing
|
|
36
|
+
// ============================================================================
|
|
37
|
+
export { parseAggregateParams, parseQueryParams, } from "./query-params.js";
|
|
38
|
+
// ============================================================================
|
|
39
|
+
// Error Mapping
|
|
40
|
+
// ============================================================================
|
|
41
|
+
export { mapErrorToResponse } from "./error-mapping.js";
|
|
42
|
+
// ============================================================================
|
|
43
|
+
// Relationship Routes
|
|
44
|
+
// ============================================================================
|
|
45
|
+
export { createRelationshipRoutes, extractRelationships, } from "./relationship-routes.js";
|
|
46
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,+EAA+E;AAC/E,qBAAqB;AACrB,+EAA+E;AAE/E,OAAO,EACN,kBAAkB,GAMlB,MAAM,eAAe,CAAC;AAEvB,+EAA+E;AAC/E,0BAA0B;AAC1B,+EAA+E;AAE/E,OAAO,EAGN,oBAAoB,EACpB,gBAAgB,GAEhB,MAAM,mBAAmB,CAAC;AAE3B,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E,OAAO,EAAsB,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE5E,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E,OAAO,EACN,wBAAwB,EACxB,oBAAoB,GACpB,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REST Query Parameter Parsing for proseql databases.
|
|
3
|
+
*
|
|
4
|
+
* Converts URL query parameters into proseql-compatible query configurations.
|
|
5
|
+
* Supports simple equality, operator syntax, sorting, pagination, and field selection.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { AggregateConfig } from "@proseql/core";
|
|
10
|
+
/**
|
|
11
|
+
* Parsed query configuration compatible with proseql's query method.
|
|
12
|
+
* Contains where clauses, sort configuration, pagination, and field selection.
|
|
13
|
+
*/
|
|
14
|
+
export interface ParsedQueryConfig {
|
|
15
|
+
/** Where clause with field filters */
|
|
16
|
+
readonly where: Record<string, unknown>;
|
|
17
|
+
/** Sort configuration (field -> "asc" | "desc") */
|
|
18
|
+
readonly sort?: Record<string, "asc" | "desc">;
|
|
19
|
+
/** Maximum number of results to return */
|
|
20
|
+
readonly limit?: number;
|
|
21
|
+
/** Number of results to skip */
|
|
22
|
+
readonly offset?: number;
|
|
23
|
+
/** Fields to include in results */
|
|
24
|
+
readonly select?: ReadonlyArray<string>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Input query parameters from URL.
|
|
28
|
+
* Values can be strings or arrays of strings for repeated parameters.
|
|
29
|
+
*/
|
|
30
|
+
export type QueryParams = Record<string, string | ReadonlyArray<string>>;
|
|
31
|
+
/**
|
|
32
|
+
* Parse URL query parameters into a proseql-compatible query configuration.
|
|
33
|
+
*
|
|
34
|
+
* Supports the following syntax:
|
|
35
|
+
*
|
|
36
|
+
* - Simple equality: `?genre=sci-fi` becomes `where: { genre: "sci-fi" }`
|
|
37
|
+
* - Operator syntax: `?year[$gte]=1970&year[$lt]=2000` becomes `where: { year: { $gte: 1970, $lt: 2000 } }`
|
|
38
|
+
* - Sorting: `?sort=year:desc` or `?sort=year:desc,title:asc` becomes `sort: { year: "desc", title: "asc" }`
|
|
39
|
+
* - Pagination: `?limit=10&offset=20` becomes `limit: 10, offset: 20`
|
|
40
|
+
* - Field selection: `?select=title,year` becomes `select: ["title", "year"]`
|
|
41
|
+
*
|
|
42
|
+
* Type coercion is applied:
|
|
43
|
+
* - Numeric strings become numbers when used with numeric operators ($gt, $gte, $lt, $lte, $size)
|
|
44
|
+
* - "true" and "false" become booleans
|
|
45
|
+
* - Comma-separated values in $in/$nin become arrays
|
|
46
|
+
*
|
|
47
|
+
* @param query - URL query parameters as key-value pairs
|
|
48
|
+
* @returns Parsed query configuration for proseql's query method
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* // Simple equality
|
|
53
|
+
* parseQueryParams({ genre: "sci-fi" })
|
|
54
|
+
* // → { where: { genre: "sci-fi" } }
|
|
55
|
+
*
|
|
56
|
+
* // Operator syntax
|
|
57
|
+
* parseQueryParams({ "year[$gte]": "1970", "year[$lt]": "2000" })
|
|
58
|
+
* // → { where: { year: { $gte: 1970, $lt: 2000 } } }
|
|
59
|
+
*
|
|
60
|
+
* // Combined
|
|
61
|
+
* parseQueryParams({
|
|
62
|
+
* genre: "sci-fi",
|
|
63
|
+
* "year[$gte]": "1970",
|
|
64
|
+
* sort: "year:desc",
|
|
65
|
+
* limit: "10",
|
|
66
|
+
* select: "title,year"
|
|
67
|
+
* })
|
|
68
|
+
* // → {
|
|
69
|
+
* // where: { genre: "sci-fi", year: { $gte: 1970 } },
|
|
70
|
+
* // sort: { year: "desc" },
|
|
71
|
+
* // limit: 10,
|
|
72
|
+
* // select: ["title", "year"]
|
|
73
|
+
* // }
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare const parseQueryParams: (query: QueryParams) => ParsedQueryConfig;
|
|
77
|
+
/**
|
|
78
|
+
* Parsed aggregate configuration compatible with proseql's aggregate method.
|
|
79
|
+
* Contains aggregation options and optional where clause.
|
|
80
|
+
*/
|
|
81
|
+
export interface ParsedAggregateConfig {
|
|
82
|
+
/** Count entities */
|
|
83
|
+
readonly count?: true;
|
|
84
|
+
/** Field(s) to sum */
|
|
85
|
+
readonly sum?: string | ReadonlyArray<string>;
|
|
86
|
+
/** Field(s) to average */
|
|
87
|
+
readonly avg?: string | ReadonlyArray<string>;
|
|
88
|
+
/** Field(s) to find minimum */
|
|
89
|
+
readonly min?: string | ReadonlyArray<string>;
|
|
90
|
+
/** Field(s) to find maximum */
|
|
91
|
+
readonly max?: string | ReadonlyArray<string>;
|
|
92
|
+
/** Field(s) to group by */
|
|
93
|
+
readonly groupBy?: string | ReadonlyArray<string>;
|
|
94
|
+
/** Where clause with field filters */
|
|
95
|
+
readonly where?: Record<string, unknown>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Parse URL query parameters into a proseql-compatible aggregate configuration.
|
|
99
|
+
*
|
|
100
|
+
* Supports the following syntax:
|
|
101
|
+
*
|
|
102
|
+
* - Count: `?count=true` becomes `{ count: true }`
|
|
103
|
+
* - Sum: `?sum=pages` or `?sum=pages,price` becomes `{ sum: "pages" }` or `{ sum: ["pages", "price"] }`
|
|
104
|
+
* - Avg: `?avg=rating` becomes `{ avg: "rating" }`
|
|
105
|
+
* - Min/Max: `?min=year&max=year` becomes `{ min: "year", max: "year" }`
|
|
106
|
+
* - GroupBy: `?groupBy=genre` or `?groupBy=genre,year` becomes `{ groupBy: "genre" }` or `{ groupBy: ["genre", "year"] }`
|
|
107
|
+
* - Filters: Same as query params - `?genre=sci-fi` or `?year[$gte]=1970`
|
|
108
|
+
*
|
|
109
|
+
* @param query - URL query parameters as key-value pairs
|
|
110
|
+
* @returns Parsed aggregate configuration for proseql's aggregate method
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* // Simple count
|
|
115
|
+
* parseAggregateParams({ count: "true" })
|
|
116
|
+
* // → { count: true }
|
|
117
|
+
*
|
|
118
|
+
* // Count with filter
|
|
119
|
+
* parseAggregateParams({ count: "true", genre: "sci-fi" })
|
|
120
|
+
* // → { count: true, where: { genre: "sci-fi" } }
|
|
121
|
+
*
|
|
122
|
+
* // Grouped aggregate
|
|
123
|
+
* parseAggregateParams({ count: "true", groupBy: "genre" })
|
|
124
|
+
* // → { count: true, groupBy: "genre" }
|
|
125
|
+
*
|
|
126
|
+
* // Multiple aggregations
|
|
127
|
+
* parseAggregateParams({ count: "true", sum: "pages", avg: "rating", groupBy: "genre" })
|
|
128
|
+
* // → { count: true, sum: "pages", avg: "rating", groupBy: "genre" }
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export declare const parseAggregateParams: (query: QueryParams) => AggregateConfig;
|
|
132
|
+
//# sourceMappingURL=query-params.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-params.d.ts","sourceRoot":"","sources":["../src/query-params.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAMrD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IACjC,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAExC,mDAAmD;IACnD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;IAE/C,0CAA0C;IAC1C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAExB,gCAAgC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEzB,mCAAmC;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACxC;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;AAmCzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,WAAW,KAAG,iBAiErD,CAAC;AAkKF;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACrC,qBAAqB;IACrB,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC;IAEtB,sBAAsB;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAE9C,0BAA0B;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAE9C,+BAA+B;IAC/B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAE9C,+BAA+B;IAC/B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAE9C,2BAA2B;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAElD,sCAAsC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAcD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,WAAW,KAAG,eA8DzD,CAAC"}
|