postgrest-parser 0.1.1 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postgrest-parser",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "PostgREST URL-to-SQL parser with WASM bindings for TypeScript/JavaScript",
5
5
  "main": "pkg/postgrest_parser.js",
6
6
  "types": "postgrest_parser.d.ts",
package/pkg/client.d.ts CHANGED
@@ -5,16 +5,21 @@
5
5
  * the auto-generated WASM bindings, improving developer experience.
6
6
  */
7
7
  import type { HttpMethod, QueryResult, RequestHeaders, SelectOptions, InsertOptions, UpdateOptions, DeleteOptions, RpcOptions } from "./types.js";
8
- export { default as init, initSchemaFromDb } from "./postgrest_parser.js";
8
+ export { default as init, initSchemaFromDb, clearSchema, clearAllSchemas } from "./postgrest_parser.js";
9
9
  /**
10
10
  * Type-safe PostgREST Parser client
11
11
  *
12
12
  * Provides strongly-typed methods for generating PostgREST-compatible SQL queries.
13
+ * Optionally bound to a schema ID for per-tenant schema resolution.
13
14
  *
14
15
  * @example
15
16
  * ```typescript
17
+ * // Default client (no schema binding)
16
18
  * const client = new PostgRESTParser();
17
19
  *
20
+ * // Per-tenant client with schema binding
21
+ * const tenantClient = new PostgRESTParser("tenant-123");
22
+ *
18
23
  * // SELECT query
19
24
  * const getUsers = client.select("users", {
20
25
  * filters: { "age": "gte.18", "status": "eq.active" },
@@ -22,205 +27,74 @@ export { default as init, initSchemaFromDb } from "./postgrest_parser.js";
22
27
  * limit: 10
23
28
  * });
24
29
  *
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
30
  * // Execute with your database client
35
31
  * const rows = await db.query(getUsers.query, getUsers.params);
36
32
  * ```
37
33
  */
38
34
  export declare class PostgRESTParser {
39
35
  /**
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
36
+ * Optional schema ID for per-tenant schema resolution.
37
+ */
38
+ readonly schemaId?: string;
39
+
40
+ /**
41
+ * Create a new PostgRESTParser instance.
50
42
  *
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
- * ```
43
+ * @param schemaId - Optional schema cache key for per-tenant schema resolution.
44
+ * Must match a key previously passed to `initSchemaFromDb()`.
45
+ */
46
+ constructor(schemaId?: string);
47
+
48
+ /**
49
+ * Parse a complete HTTP request and generate appropriate SQL
56
50
  */
57
51
  parseRequest(method: HttpMethod, path: string, queryString: string, body?: Record<string, unknown> | Record<string, unknown>[] | null, headers?: RequestHeaders | null): QueryResult;
52
+
58
53
  /**
59
54
  * 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
55
  */
75
56
  select(table: string, options?: SelectOptions): QueryResult;
57
+
76
58
  /**
77
59
  * 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
60
  */
95
61
  insert(table: string, data: Record<string, unknown> | Record<string, unknown>[], options?: InsertOptions): QueryResult;
62
+
96
63
  /**
97
64
  * 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
65
  */
117
66
  upsert(table: string, data: Record<string, unknown>, conflictColumns: string[], options?: InsertOptions): QueryResult;
67
+
118
68
  /**
119
69
  * 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
70
  */
138
71
  update(table: string, data: Record<string, unknown>, filters: Record<string, string>, options?: UpdateOptions): QueryResult;
72
+
139
73
  /**
140
74
  * 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
75
  */
157
76
  delete(table: string, filters: Record<string, string>, options?: DeleteOptions): QueryResult;
77
+
158
78
  /**
159
79
  * 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
80
  */
177
81
  rpc(functionName: string, args?: Record<string, unknown>, options?: RpcOptions): QueryResult;
82
+
178
83
  /**
179
84
  * 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
85
  */
192
86
  parseOnly(queryString: string): unknown;
87
+
193
88
  /**
194
89
  * 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
90
  */
210
91
  buildFilterClause(filters: unknown): unknown;
211
92
  }
212
93
  /**
213
94
  * Create a new PostgREST Parser client instance
214
95
  *
96
+ * @param schemaId - Optional schema cache key for per-tenant schema resolution.
215
97
  * @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
98
  */
225
- export declare function createClient(): PostgRESTParser;
99
+ export declare function createClient(schemaId?: string): PostgRESTParser;
226
100
  export type { HttpMethod, QueryResult, RequestHeaders, SelectOptions, InsertOptions, UpdateOptions, DeleteOptions, RpcOptions, PreferOptions, PostgRESTParserError, } from "./types.js";
package/pkg/client.js CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
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
8
  // Re-export WASM initialization functions
9
- export { default as init, initSchemaFromDb } from "./postgrest_parser.js";
9
+ export { default as init, initSchemaFromDb, clearSchema, clearAllSchemas } from "./postgrest_parser.js";
10
10
  /**
11
11
  * Convert WASM result to typed QueryResult
12
12
  */
@@ -93,11 +93,16 @@ function buildQueryString(filters, options) {
93
93
  * Type-safe PostgREST Parser client
94
94
  *
95
95
  * Provides strongly-typed methods for generating PostgREST-compatible SQL queries.
96
+ * Optionally bound to a schema ID for per-tenant schema resolution.
96
97
  *
97
98
  * @example
98
99
  * ```typescript
100
+ * // Default client (no schema binding)
99
101
  * const client = new PostgRESTParser();
100
102
  *
103
+ * // Per-tenant client with schema binding
104
+ * const tenantClient = new PostgRESTParser("tenant-123");
105
+ *
101
106
  * // SELECT query
102
107
  * const getUsers = client.select("users", {
103
108
  * filters: { "age": "gte.18", "status": "eq.active" },
@@ -119,6 +124,23 @@ function buildQueryString(filters, options) {
119
124
  * ```
120
125
  */
121
126
  export class PostgRESTParser {
127
+ /**
128
+ * Optional schema ID for per-tenant schema resolution.
129
+ * When set, all parse calls will use this schema's cached
130
+ * foreign key information for relation resolution.
131
+ */
132
+ schemaId;
133
+
134
+ /**
135
+ * Create a new PostgRESTParser instance.
136
+ *
137
+ * @param schemaId - Optional schema cache key for per-tenant schema resolution.
138
+ * Must match a key previously passed to `initSchemaFromDb()`.
139
+ */
140
+ constructor(schemaId) {
141
+ this.schemaId = schemaId;
142
+ }
143
+
122
144
  /**
123
145
  * Parse a complete HTTP request and generate appropriate SQL
124
146
  *
@@ -130,17 +152,11 @@ export class PostgRESTParser {
130
152
  * @param body - Request body (object or null)
131
153
  * @param headers - Request headers (object or null)
132
154
  * @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
155
  */
140
156
  parseRequest(method, path, queryString, body, headers) {
141
157
  const bodyJson = body ? JSON.stringify(body) : undefined;
142
158
  const headersJson = headers ? headersToJson(headers) : undefined;
143
- const result = wasmParseRequest(method, path, queryString, bodyJson, headersJson);
159
+ const result = wasmParseRequest(method, path, queryString, bodyJson, headersJson, this.schemaId);
144
160
  return toQueryResult(result);
145
161
  }
146
162
  /**
@@ -149,23 +165,13 @@ export class PostgRESTParser {
149
165
  * @param table - Table name to query
150
166
  * @param options - Query options (filters, ordering, pagination)
151
167
  * @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
168
  */
163
169
  select(table, options = {}) {
164
170
  const queryString = buildQueryString(options.filters, options);
165
171
  const headers = options.count
166
172
  ? { Prefer: `count=${options.count}` }
167
173
  : undefined;
168
- const result = wasmParseRequest("GET", table, queryString, undefined, headersToJson(headers));
174
+ const result = wasmParseRequest("GET", table, queryString, undefined, headersToJson(headers), this.schemaId);
169
175
  return toQueryResult(result);
170
176
  }
171
177
  /**
@@ -175,17 +181,6 @@ export class PostgRESTParser {
175
181
  * @param data - Data to insert (single object or array of objects)
176
182
  * @param options - Insert options (returning, onConflict, prefer)
177
183
  * @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
184
  */
190
185
  insert(table, data, options = {}) {
191
186
  const queryString = buildQueryString(undefined, {
@@ -196,7 +191,7 @@ export class PostgRESTParser {
196
191
  const headers = preferHeader
197
192
  ? { Prefer: preferHeader }
198
193
  : undefined;
199
- const result = wasmParseInsert(table, JSON.stringify(data), queryString || undefined, headersToJson(headers));
194
+ const result = wasmParseInsert(table, JSON.stringify(data), queryString || undefined, headersToJson(headers), this.schemaId);
200
195
  return toQueryResult(result);
201
196
  }
202
197
  /**
@@ -209,16 +204,6 @@ export class PostgRESTParser {
209
204
  * @param conflictColumns - Columns to use for conflict detection
210
205
  * @param options - Upsert options (returning, prefer)
211
206
  * @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
207
  */
223
208
  upsert(table, data, conflictColumns, options = {}) {
224
209
  // Build filters from conflict columns for PUT auto-conflict
@@ -235,7 +220,7 @@ export class PostgRESTParser {
235
220
  const headers = preferHeader
236
221
  ? { Prefer: preferHeader }
237
222
  : undefined;
238
- const result = wasmParseRequest("PUT", table, queryString, JSON.stringify(data), headersToJson(headers));
223
+ const result = wasmParseRequest("PUT", table, queryString, JSON.stringify(data), headersToJson(headers), this.schemaId);
239
224
  return toQueryResult(result);
240
225
  }
241
226
  /**
@@ -246,17 +231,6 @@ export class PostgRESTParser {
246
231
  * @param filters - Filter conditions to match rows
247
232
  * @param options - Update options (returning, prefer)
248
233
  * @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
234
  */
261
235
  update(table, data, filters, options = {}) {
262
236
  const queryString = buildQueryString(filters, {
@@ -266,7 +240,7 @@ export class PostgRESTParser {
266
240
  const headers = preferHeader
267
241
  ? { Prefer: preferHeader }
268
242
  : undefined;
269
- const result = wasmParseUpdate(table, JSON.stringify(data), queryString, headersToJson(headers));
243
+ const result = wasmParseUpdate(table, JSON.stringify(data), queryString, headersToJson(headers), this.schemaId);
270
244
  return toQueryResult(result);
271
245
  }
272
246
  /**
@@ -276,16 +250,6 @@ export class PostgRESTParser {
276
250
  * @param filters - Filter conditions to match rows to delete
277
251
  * @param options - Delete options (returning, prefer)
278
252
  * @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
253
  */
290
254
  delete(table, filters, options = {}) {
291
255
  const queryString = buildQueryString(filters, {
@@ -295,7 +259,7 @@ export class PostgRESTParser {
295
259
  const headers = preferHeader
296
260
  ? { Prefer: preferHeader }
297
261
  : undefined;
298
- const result = wasmParseDelete(table, queryString, headersToJson(headers));
262
+ const result = wasmParseDelete(table, queryString, headersToJson(headers), this.schemaId);
299
263
  return toQueryResult(result);
300
264
  }
301
265
  /**
@@ -305,21 +269,10 @@ export class PostgRESTParser {
305
269
  * @param args - Function arguments as object
306
270
  * @param options - RPC options (select, filters, ordering)
307
271
  * @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
272
  */
320
273
  rpc(functionName, args = {}, options = {}) {
321
274
  const queryString = buildQueryString(options.filters, options);
322
- const result = wasmParseRpc(functionName, JSON.stringify(args), queryString || undefined, undefined);
275
+ const result = wasmParseRpc(functionName, JSON.stringify(args), queryString || undefined, undefined, this.schemaId);
323
276
  return toQueryResult(result);
324
277
  }
325
278
  /**
@@ -329,12 +282,6 @@ export class PostgRESTParser {
329
282
  *
330
283
  * @param queryString - PostgREST query string
331
284
  * @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
285
  */
339
286
  parseOnly(queryString) {
340
287
  return wasmParseOnly(queryString);
@@ -344,17 +291,6 @@ export class PostgRESTParser {
344
291
  *
345
292
  * @param filters - Filter conditions as object
346
293
  * @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
294
  */
359
295
  buildFilterClause(filters) {
360
296
  return wasmBuildFilterClause(filters);
@@ -363,16 +299,23 @@ export class PostgRESTParser {
363
299
  /**
364
300
  * Create a new PostgREST Parser client instance
365
301
  *
302
+ * @param schemaId - Optional schema cache key for per-tenant schema resolution.
303
+ * Must match a key previously passed to `initSchemaFromDb()`.
366
304
  * @returns New PostgRESTParser instance
367
305
  *
368
306
  * @example
369
307
  * ```typescript
370
308
  * import { createClient } from './pkg/client.js';
371
309
  *
310
+ * // Default client
372
311
  * const client = createClient();
312
+ *
313
+ * // Per-tenant client
314
+ * const tenantClient = createClient("tenant-123");
315
+ *
373
316
  * const result = client.select("users", { limit: 10 });
374
317
  * ```
375
318
  */
376
- export function createClient() {
377
- return new PostgRESTParser();
319
+ export function createClient(schemaId) {
320
+ return new PostgRESTParser(schemaId);
378
321
  }
package/pkg/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "Your Name"
6
6
  ],
7
7
  "description": "PostgREST URL-to-SQL parser for Rust and WASM",
8
- "version": "0.1.1",
8
+ "version": "0.2.0",
9
9
  "license": "MIT",
10
10
  "repository": {
11
11
  "type": "git",