postgrest-parser 0.1.0 → 0.1.2

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,226 @@
1
+ /**
2
+ * Type-safe TypeScript wrapper for PostgREST Parser
3
+ *
4
+ * This module provides a type-safe, idiomatic TypeScript API on top of
5
+ * the auto-generated WASM bindings, improving developer experience.
6
+ */
7
+ import type { HttpMethod, QueryResult, RequestHeaders, SelectOptions, InsertOptions, UpdateOptions, DeleteOptions, RpcOptions } from "./types.js";
8
+ export { default as init, initSchemaFromDb } from "./postgrest_parser.js";
9
+ /**
10
+ * Type-safe PostgREST Parser client
11
+ *
12
+ * Provides strongly-typed methods for generating PostgREST-compatible SQL queries.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const client = new PostgRESTParser();
17
+ *
18
+ * // SELECT query
19
+ * const getUsers = client.select("users", {
20
+ * filters: { "age": "gte.18", "status": "eq.active" },
21
+ * order: ["created_at.desc"],
22
+ * limit: 10
23
+ * });
24
+ *
25
+ * // INSERT query
26
+ * const createUser = client.insert("users", {
27
+ * name: "Alice",
28
+ * email: "alice@example.com"
29
+ * }, {
30
+ * returning: "*",
31
+ * prefer: { return: "representation" }
32
+ * });
33
+ *
34
+ * // Execute with your database client
35
+ * const rows = await db.query(getUsers.query, getUsers.params);
36
+ * ```
37
+ */
38
+ export declare class PostgRESTParser {
39
+ /**
40
+ * Parse a complete HTTP request and generate appropriate SQL
41
+ *
42
+ * This is the universal routing method that handles all HTTP methods.
43
+ *
44
+ * @param method - HTTP method: "GET", "POST", "PUT", "PATCH", "DELETE"
45
+ * @param path - Resource path (table name or "rpc/function_name")
46
+ * @param queryString - URL query string
47
+ * @param body - Request body (object or null)
48
+ * @param headers - Request headers (object or null)
49
+ * @returns Query result with SQL, params, and tables
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * const result = client.parseRequest("GET", "users", "age=gte.18", null, null);
54
+ * const rows = await db.query(result.query, result.params);
55
+ * ```
56
+ */
57
+ parseRequest(method: HttpMethod, path: string, queryString: string, body?: Record<string, unknown> | Record<string, unknown>[] | null, headers?: RequestHeaders | null): QueryResult;
58
+ /**
59
+ * Generate a SELECT query
60
+ *
61
+ * @param table - Table name to query
62
+ * @param options - Query options (filters, ordering, pagination)
63
+ * @returns Query result with SQL, params, and tables
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * const result = client.select("users", {
68
+ * filters: { "age": "gte.18", "status": "eq.active" },
69
+ * order: ["created_at.desc"],
70
+ * limit: 10,
71
+ * offset: 0
72
+ * });
73
+ * ```
74
+ */
75
+ select(table: string, options?: SelectOptions): QueryResult;
76
+ /**
77
+ * Generate an INSERT query
78
+ *
79
+ * @param table - Table name
80
+ * @param data - Data to insert (single object or array of objects)
81
+ * @param options - Insert options (returning, onConflict, prefer)
82
+ * @returns Query result with SQL, params, and tables
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * const result = client.insert("users", {
87
+ * name: "Alice",
88
+ * email: "alice@example.com"
89
+ * }, {
90
+ * returning: "*",
91
+ * prefer: { return: "representation" }
92
+ * });
93
+ * ```
94
+ */
95
+ insert(table: string, data: Record<string, unknown> | Record<string, unknown>[], options?: InsertOptions): QueryResult;
96
+ /**
97
+ * Generate an UPSERT query (INSERT with ON CONFLICT)
98
+ *
99
+ * PUT method auto-generates ON CONFLICT from filter columns.
100
+ *
101
+ * @param table - Table name
102
+ * @param data - Data to upsert
103
+ * @param conflictColumns - Columns to use for conflict detection
104
+ * @param options - Upsert options (returning, prefer)
105
+ * @returns Query result with SQL, params, and tables
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * const result = client.upsert("users", {
110
+ * email: "alice@example.com",
111
+ * name: "Alice Updated"
112
+ * }, ["email"], {
113
+ * returning: "*"
114
+ * });
115
+ * ```
116
+ */
117
+ upsert(table: string, data: Record<string, unknown>, conflictColumns: string[], options?: InsertOptions): QueryResult;
118
+ /**
119
+ * Generate an UPDATE query
120
+ *
121
+ * @param table - Table name
122
+ * @param data - Data to update
123
+ * @param filters - Filter conditions to match rows
124
+ * @param options - Update options (returning, prefer)
125
+ * @returns Query result with SQL, params, and tables
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * const result = client.update("users", {
130
+ * status: "active"
131
+ * }, {
132
+ * "id": "eq.123"
133
+ * }, {
134
+ * returning: "id,status"
135
+ * });
136
+ * ```
137
+ */
138
+ update(table: string, data: Record<string, unknown>, filters: Record<string, string>, options?: UpdateOptions): QueryResult;
139
+ /**
140
+ * Generate a DELETE query
141
+ *
142
+ * @param table - Table name
143
+ * @param filters - Filter conditions to match rows to delete
144
+ * @param options - Delete options (returning, prefer)
145
+ * @returns Query result with SQL, params, and tables
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * const result = client.delete("users", {
150
+ * "status": "eq.inactive",
151
+ * "last_login": "lt.2023-01-01"
152
+ * }, {
153
+ * returning: "id"
154
+ * });
155
+ * ```
156
+ */
157
+ delete(table: string, filters: Record<string, string>, options?: DeleteOptions): QueryResult;
158
+ /**
159
+ * Generate an RPC (stored procedure/function) call
160
+ *
161
+ * @param functionName - Function name (can include schema)
162
+ * @param args - Function arguments as object
163
+ * @param options - RPC options (select, filters, ordering)
164
+ * @returns Query result with SQL, params, and tables
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * const result = client.rpc("calculate_total", {
169
+ * order_id: 123,
170
+ * tax_rate: 0.08
171
+ * }, {
172
+ * select: ["total", "tax"],
173
+ * limit: 1
174
+ * });
175
+ * ```
176
+ */
177
+ rpc(functionName: string, args?: Record<string, unknown>, options?: RpcOptions): QueryResult;
178
+ /**
179
+ * Parse a query string without generating SQL
180
+ *
181
+ * Useful for inspecting the parsed structure before generating SQL.
182
+ *
183
+ * @param queryString - PostgREST query string
184
+ * @returns Parsed query parameters as object
185
+ *
186
+ * @example
187
+ * ```typescript
188
+ * const parsed = client.parseOnly("age=gte.18&status=eq.active&order=created_at.desc");
189
+ * console.log(parsed); // { filters: [...], order: [...] }
190
+ * ```
191
+ */
192
+ parseOnly(queryString: string): unknown;
193
+ /**
194
+ * Build a WHERE clause from filter conditions
195
+ *
196
+ * @param filters - Filter conditions as object
197
+ * @returns Object with clause (SQL string) and params (array of values)
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * const filters = [
202
+ * { column: "age", operator: "gte", value: "18" },
203
+ * { column: "status", operator: "eq", value: "active" }
204
+ * ];
205
+ * const result = client.buildFilterClause(filters);
206
+ * console.log(result.clause); // "age >= $1 AND status = $2"
207
+ * console.log(result.params); // ["18", "active"]
208
+ * ```
209
+ */
210
+ buildFilterClause(filters: unknown): unknown;
211
+ }
212
+ /**
213
+ * Create a new PostgREST Parser client instance
214
+ *
215
+ * @returns New PostgRESTParser instance
216
+ *
217
+ * @example
218
+ * ```typescript
219
+ * import { createClient } from './pkg/client.js';
220
+ *
221
+ * const client = createClient();
222
+ * const result = client.select("users", { limit: 10 });
223
+ * ```
224
+ */
225
+ export declare function createClient(): PostgRESTParser;
226
+ export type { HttpMethod, QueryResult, RequestHeaders, SelectOptions, InsertOptions, UpdateOptions, DeleteOptions, RpcOptions, PreferOptions, PostgRESTParserError, } from "./types.js";
package/pkg/client.js ADDED
@@ -0,0 +1,378 @@
1
+ /**
2
+ * Type-safe TypeScript wrapper for PostgREST Parser
3
+ *
4
+ * This module provides a type-safe, idiomatic TypeScript API on top of
5
+ * the auto-generated WASM bindings, improving developer experience.
6
+ */
7
+ import { parseRequest as wasmParseRequest, parseInsert as wasmParseInsert, parseUpdate as wasmParseUpdate, parseDelete as wasmParseDelete, parseRpc as wasmParseRpc, parseOnly as wasmParseOnly, buildFilterClause as wasmBuildFilterClause, } from "./postgrest_parser.js";
8
+ // Re-export WASM initialization functions
9
+ export { default as init, initSchemaFromDb } from "./postgrest_parser.js";
10
+ /**
11
+ * Convert WASM result to typed QueryResult
12
+ */
13
+ function toQueryResult(wasmResult) {
14
+ return {
15
+ query: wasmResult.query,
16
+ params: wasmResult.params,
17
+ tables: wasmResult.tables,
18
+ };
19
+ }
20
+ /**
21
+ * Convert headers object to JSON string
22
+ */
23
+ function headersToJson(headers) {
24
+ return headers ? JSON.stringify(headers) : undefined;
25
+ }
26
+ /**
27
+ * Convert PreferOptions to Prefer header value
28
+ */
29
+ function preferToHeader(prefer) {
30
+ if (!prefer)
31
+ return undefined;
32
+ const parts = [];
33
+ if (prefer.return)
34
+ parts.push(`return=${prefer.return}`);
35
+ if (prefer.resolution)
36
+ parts.push(`resolution=${prefer.resolution}`);
37
+ if (prefer.missing)
38
+ parts.push(`missing=${prefer.missing}`);
39
+ if (prefer.count)
40
+ parts.push(`count=${prefer.count}`);
41
+ return parts.length > 0 ? parts.join(",") : undefined;
42
+ }
43
+ /**
44
+ * Build query string from filters and options
45
+ */
46
+ function buildQueryString(filters, options) {
47
+ const parts = [];
48
+ // Add filters
49
+ if (filters) {
50
+ for (const [key, value] of Object.entries(filters)) {
51
+ parts.push(`${key}=${value}`);
52
+ }
53
+ }
54
+ // Add select
55
+ if (options?.select) {
56
+ const select = Array.isArray(options.select)
57
+ ? options.select.join(",")
58
+ : options.select;
59
+ parts.push(`select=${select}`);
60
+ }
61
+ // Add order
62
+ if (options?.order) {
63
+ const order = Array.isArray(options.order)
64
+ ? options.order.join(",")
65
+ : options.order;
66
+ parts.push(`order=${order}`);
67
+ }
68
+ // Add limit
69
+ if (options?.limit !== undefined) {
70
+ parts.push(`limit=${options.limit}`);
71
+ }
72
+ // Add offset
73
+ if (options?.offset !== undefined) {
74
+ parts.push(`offset=${options.offset}`);
75
+ }
76
+ // Add on_conflict
77
+ if (options?.onConflict) {
78
+ const onConflict = Array.isArray(options.onConflict)
79
+ ? options.onConflict.join(",")
80
+ : options.onConflict;
81
+ parts.push(`on_conflict=${onConflict}`);
82
+ }
83
+ // Add returning
84
+ if (options?.returning) {
85
+ const returning = Array.isArray(options.returning)
86
+ ? options.returning.join(",")
87
+ : options.returning;
88
+ parts.push(`returning=${returning}`);
89
+ }
90
+ return parts.join("&");
91
+ }
92
+ /**
93
+ * Type-safe PostgREST Parser client
94
+ *
95
+ * Provides strongly-typed methods for generating PostgREST-compatible SQL queries.
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * const client = new PostgRESTParser();
100
+ *
101
+ * // SELECT query
102
+ * const getUsers = client.select("users", {
103
+ * filters: { "age": "gte.18", "status": "eq.active" },
104
+ * order: ["created_at.desc"],
105
+ * limit: 10
106
+ * });
107
+ *
108
+ * // INSERT query
109
+ * const createUser = client.insert("users", {
110
+ * name: "Alice",
111
+ * email: "alice@example.com"
112
+ * }, {
113
+ * returning: "*",
114
+ * prefer: { return: "representation" }
115
+ * });
116
+ *
117
+ * // Execute with your database client
118
+ * const rows = await db.query(getUsers.query, getUsers.params);
119
+ * ```
120
+ */
121
+ export class PostgRESTParser {
122
+ /**
123
+ * Parse a complete HTTP request and generate appropriate SQL
124
+ *
125
+ * This is the universal routing method that handles all HTTP methods.
126
+ *
127
+ * @param method - HTTP method: "GET", "POST", "PUT", "PATCH", "DELETE"
128
+ * @param path - Resource path (table name or "rpc/function_name")
129
+ * @param queryString - URL query string
130
+ * @param body - Request body (object or null)
131
+ * @param headers - Request headers (object or null)
132
+ * @returns Query result with SQL, params, and tables
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * const result = client.parseRequest("GET", "users", "age=gte.18", null, null);
137
+ * const rows = await db.query(result.query, result.params);
138
+ * ```
139
+ */
140
+ parseRequest(method, path, queryString, body, headers) {
141
+ const bodyJson = body ? JSON.stringify(body) : undefined;
142
+ const headersJson = headers ? headersToJson(headers) : undefined;
143
+ const result = wasmParseRequest(method, path, queryString, bodyJson, headersJson);
144
+ return toQueryResult(result);
145
+ }
146
+ /**
147
+ * Generate a SELECT query
148
+ *
149
+ * @param table - Table name to query
150
+ * @param options - Query options (filters, ordering, pagination)
151
+ * @returns Query result with SQL, params, and tables
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * const result = client.select("users", {
156
+ * filters: { "age": "gte.18", "status": "eq.active" },
157
+ * order: ["created_at.desc"],
158
+ * limit: 10,
159
+ * offset: 0
160
+ * });
161
+ * ```
162
+ */
163
+ select(table, options = {}) {
164
+ const queryString = buildQueryString(options.filters, options);
165
+ const headers = options.count
166
+ ? { Prefer: `count=${options.count}` }
167
+ : undefined;
168
+ const result = wasmParseRequest("GET", table, queryString, undefined, headersToJson(headers));
169
+ return toQueryResult(result);
170
+ }
171
+ /**
172
+ * Generate an INSERT query
173
+ *
174
+ * @param table - Table name
175
+ * @param data - Data to insert (single object or array of objects)
176
+ * @param options - Insert options (returning, onConflict, prefer)
177
+ * @returns Query result with SQL, params, and tables
178
+ *
179
+ * @example
180
+ * ```typescript
181
+ * const result = client.insert("users", {
182
+ * name: "Alice",
183
+ * email: "alice@example.com"
184
+ * }, {
185
+ * returning: "*",
186
+ * prefer: { return: "representation" }
187
+ * });
188
+ * ```
189
+ */
190
+ insert(table, data, options = {}) {
191
+ const queryString = buildQueryString(undefined, {
192
+ onConflict: options.onConflict,
193
+ returning: options.returning,
194
+ });
195
+ const preferHeader = preferToHeader(options.prefer);
196
+ const headers = preferHeader
197
+ ? { Prefer: preferHeader }
198
+ : undefined;
199
+ const result = wasmParseInsert(table, JSON.stringify(data), queryString || undefined, headersToJson(headers));
200
+ return toQueryResult(result);
201
+ }
202
+ /**
203
+ * Generate an UPSERT query (INSERT with ON CONFLICT)
204
+ *
205
+ * PUT method auto-generates ON CONFLICT from filter columns.
206
+ *
207
+ * @param table - Table name
208
+ * @param data - Data to upsert
209
+ * @param conflictColumns - Columns to use for conflict detection
210
+ * @param options - Upsert options (returning, prefer)
211
+ * @returns Query result with SQL, params, and tables
212
+ *
213
+ * @example
214
+ * ```typescript
215
+ * const result = client.upsert("users", {
216
+ * email: "alice@example.com",
217
+ * name: "Alice Updated"
218
+ * }, ["email"], {
219
+ * returning: "*"
220
+ * });
221
+ * ```
222
+ */
223
+ upsert(table, data, conflictColumns, options = {}) {
224
+ // Build filters from conflict columns for PUT auto-conflict
225
+ const filters = {};
226
+ for (const col of conflictColumns) {
227
+ if (col in data) {
228
+ filters[col] = `eq.${data[col]}`;
229
+ }
230
+ }
231
+ const queryString = buildQueryString(filters, {
232
+ returning: options.returning,
233
+ });
234
+ const preferHeader = preferToHeader(options.prefer);
235
+ const headers = preferHeader
236
+ ? { Prefer: preferHeader }
237
+ : undefined;
238
+ const result = wasmParseRequest("PUT", table, queryString, JSON.stringify(data), headersToJson(headers));
239
+ return toQueryResult(result);
240
+ }
241
+ /**
242
+ * Generate an UPDATE query
243
+ *
244
+ * @param table - Table name
245
+ * @param data - Data to update
246
+ * @param filters - Filter conditions to match rows
247
+ * @param options - Update options (returning, prefer)
248
+ * @returns Query result with SQL, params, and tables
249
+ *
250
+ * @example
251
+ * ```typescript
252
+ * const result = client.update("users", {
253
+ * status: "active"
254
+ * }, {
255
+ * "id": "eq.123"
256
+ * }, {
257
+ * returning: "id,status"
258
+ * });
259
+ * ```
260
+ */
261
+ update(table, data, filters, options = {}) {
262
+ const queryString = buildQueryString(filters, {
263
+ returning: options.returning,
264
+ });
265
+ const preferHeader = preferToHeader(options.prefer);
266
+ const headers = preferHeader
267
+ ? { Prefer: preferHeader }
268
+ : undefined;
269
+ const result = wasmParseUpdate(table, JSON.stringify(data), queryString, headersToJson(headers));
270
+ return toQueryResult(result);
271
+ }
272
+ /**
273
+ * Generate a DELETE query
274
+ *
275
+ * @param table - Table name
276
+ * @param filters - Filter conditions to match rows to delete
277
+ * @param options - Delete options (returning, prefer)
278
+ * @returns Query result with SQL, params, and tables
279
+ *
280
+ * @example
281
+ * ```typescript
282
+ * const result = client.delete("users", {
283
+ * "status": "eq.inactive",
284
+ * "last_login": "lt.2023-01-01"
285
+ * }, {
286
+ * returning: "id"
287
+ * });
288
+ * ```
289
+ */
290
+ delete(table, filters, options = {}) {
291
+ const queryString = buildQueryString(filters, {
292
+ returning: options.returning,
293
+ });
294
+ const preferHeader = preferToHeader(options.prefer);
295
+ const headers = preferHeader
296
+ ? { Prefer: preferHeader }
297
+ : undefined;
298
+ const result = wasmParseDelete(table, queryString, headersToJson(headers));
299
+ return toQueryResult(result);
300
+ }
301
+ /**
302
+ * Generate an RPC (stored procedure/function) call
303
+ *
304
+ * @param functionName - Function name (can include schema)
305
+ * @param args - Function arguments as object
306
+ * @param options - RPC options (select, filters, ordering)
307
+ * @returns Query result with SQL, params, and tables
308
+ *
309
+ * @example
310
+ * ```typescript
311
+ * const result = client.rpc("calculate_total", {
312
+ * order_id: 123,
313
+ * tax_rate: 0.08
314
+ * }, {
315
+ * select: ["total", "tax"],
316
+ * limit: 1
317
+ * });
318
+ * ```
319
+ */
320
+ rpc(functionName, args = {}, options = {}) {
321
+ const queryString = buildQueryString(options.filters, options);
322
+ const result = wasmParseRpc(functionName, JSON.stringify(args), queryString || undefined, undefined);
323
+ return toQueryResult(result);
324
+ }
325
+ /**
326
+ * Parse a query string without generating SQL
327
+ *
328
+ * Useful for inspecting the parsed structure before generating SQL.
329
+ *
330
+ * @param queryString - PostgREST query string
331
+ * @returns Parsed query parameters as object
332
+ *
333
+ * @example
334
+ * ```typescript
335
+ * const parsed = client.parseOnly("age=gte.18&status=eq.active&order=created_at.desc");
336
+ * console.log(parsed); // { filters: [...], order: [...] }
337
+ * ```
338
+ */
339
+ parseOnly(queryString) {
340
+ return wasmParseOnly(queryString);
341
+ }
342
+ /**
343
+ * Build a WHERE clause from filter conditions
344
+ *
345
+ * @param filters - Filter conditions as object
346
+ * @returns Object with clause (SQL string) and params (array of values)
347
+ *
348
+ * @example
349
+ * ```typescript
350
+ * const filters = [
351
+ * { column: "age", operator: "gte", value: "18" },
352
+ * { column: "status", operator: "eq", value: "active" }
353
+ * ];
354
+ * const result = client.buildFilterClause(filters);
355
+ * console.log(result.clause); // "age >= $1 AND status = $2"
356
+ * console.log(result.params); // ["18", "active"]
357
+ * ```
358
+ */
359
+ buildFilterClause(filters) {
360
+ return wasmBuildFilterClause(filters);
361
+ }
362
+ }
363
+ /**
364
+ * Create a new PostgREST Parser client instance
365
+ *
366
+ * @returns New PostgRESTParser instance
367
+ *
368
+ * @example
369
+ * ```typescript
370
+ * import { createClient } from './pkg/client.js';
371
+ *
372
+ * const client = createClient();
373
+ * const result = client.select("users", { limit: 10 });
374
+ * ```
375
+ */
376
+ export function createClient() {
377
+ return new PostgRESTParser();
378
+ }