@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.
@@ -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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"}
@@ -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"}