prest-js-sdk 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/.changeset/config.json +11 -0
- package/.github/workflows/release.yml +42 -0
- package/CHANGELOG.md +9 -0
- package/CLAUDE.md +203 -0
- package/README.md +219 -0
- package/dist/index.d.ts +207 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +366 -0
- package/dist/index.js.map +1 -0
- package/examples/filters.mjs +131 -0
- package/package.json +20 -0
- package/src/index.ts +543 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* prest-js-sdk
|
|
3
|
+
*
|
|
4
|
+
* TypeScript SDK for pREST (PostgreSQL REST API). Provides route-shaped methods
|
|
5
|
+
* for catalog/CRUD/stored-queries plus a typed filter DSL that serializes to
|
|
6
|
+
* pREST's `?field=op.value` URL syntax.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* import { PrestClient } from "prest-js-sdk";
|
|
10
|
+
*
|
|
11
|
+
* const client = new PrestClient("http://localhost:3000");
|
|
12
|
+
*
|
|
13
|
+
* // Catalog
|
|
14
|
+
* const dbs = await client.databases();
|
|
15
|
+
*
|
|
16
|
+
* // Query with typed filter
|
|
17
|
+
* const rows = await client.select<Balance>("yarsew", "public", "billing_balances", {
|
|
18
|
+
* where: { actor_id: { eq: 42 }, status: "active" },
|
|
19
|
+
* select: ["id", "balance"],
|
|
20
|
+
* order: ["balance:desc"],
|
|
21
|
+
* limit: 10,
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // Insert
|
|
25
|
+
* const [row] = await client.insert<Balance>("yarsew", "public", "billing_balances", {
|
|
26
|
+
* actor_id: 42, balance: 100.50, status: "active",
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // Update (filter + new values)
|
|
30
|
+
* const updated = await client.update<Balance>(
|
|
31
|
+
* "yarsew", "public", "billing_balances",
|
|
32
|
+
* { actor_id: 42 },
|
|
33
|
+
* { status: "frozen" },
|
|
34
|
+
* );
|
|
35
|
+
*
|
|
36
|
+
* // Delete
|
|
37
|
+
* await client.delete("yarsew", "public", "billing_balances", { actor_id: 42 });
|
|
38
|
+
*
|
|
39
|
+
* // Stored SQL (prest/etc/queries/reports/top_balances.sql)
|
|
40
|
+
* const top = await client.query<Balance>("reports", "top_balances", { min: 1000 });
|
|
41
|
+
*
|
|
42
|
+
* // Forward-compat Kratos auth (works once [auth.kratos] is wired into prest)
|
|
43
|
+
* const authed = await PrestClient.fromKratosSession(
|
|
44
|
+
* "http://localhost:4433",
|
|
45
|
+
* "http://localhost:3000",
|
|
46
|
+
* );
|
|
47
|
+
*/
|
|
48
|
+
export class PrestApiError extends Error {
|
|
49
|
+
status;
|
|
50
|
+
body;
|
|
51
|
+
constructor(status, message, body) {
|
|
52
|
+
super(`[${status}] ${message}`);
|
|
53
|
+
this.status = status;
|
|
54
|
+
this.body = body;
|
|
55
|
+
this.name = "PrestApiError";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// ============================================================
|
|
59
|
+
// Filter serializer (the heart of the DSL)
|
|
60
|
+
// ============================================================
|
|
61
|
+
const VALUELESS_OPS = new Set([
|
|
62
|
+
"null", "notnull", "true", "nottrue", "false", "notfalse",
|
|
63
|
+
]);
|
|
64
|
+
const ARRAY_OPS = new Set(["in", "nin", "any", "some", "all"]);
|
|
65
|
+
function serializeOpValue(op, val) {
|
|
66
|
+
if (VALUELESS_OPS.has(op)) {
|
|
67
|
+
return op; // ?field=null
|
|
68
|
+
}
|
|
69
|
+
if (ARRAY_OPS.has(op)) {
|
|
70
|
+
const arr = Array.isArray(val) ? val : val === undefined ? [] : [val];
|
|
71
|
+
return `${op}.(${arr.join(",")})`;
|
|
72
|
+
}
|
|
73
|
+
const v = val === undefined ? "" : typeof val === "string" ? val : String(val);
|
|
74
|
+
return `${op}.${v}`;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Serialize a Filter into pREST URL query params.
|
|
78
|
+
*
|
|
79
|
+
* Each field becomes one or more entries: `field=op.value`. Multiple ops on the
|
|
80
|
+
* same field are emitted as repeated query params (pREST AND-s them).
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* serializeFilter({ actor_id: 42 })
|
|
84
|
+
* // → "actor_id=eq.42"
|
|
85
|
+
* serializeFilter({ age: { gt: 30 }, name: { like: "foo%" } })
|
|
86
|
+
* // → "age=gt.30&name=like.foo%25"
|
|
87
|
+
* serializeFilter({ id: { in: [1, 2, 3] }, status: null })
|
|
88
|
+
* // → "id=in.(1,2,3)&status=null"
|
|
89
|
+
*/
|
|
90
|
+
export function serializeFilter(filter) {
|
|
91
|
+
const params = new URLSearchParams();
|
|
92
|
+
for (const [field, condition] of Object.entries(filter)) {
|
|
93
|
+
if (condition === null || condition === undefined) {
|
|
94
|
+
params.append(field, "null");
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (typeof condition === "object" && !Array.isArray(condition)) {
|
|
98
|
+
for (const [op, val] of Object.entries(condition)) {
|
|
99
|
+
params.append(field, serializeOpValue(op, val));
|
|
100
|
+
}
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
// Primitive shorthand
|
|
104
|
+
if (typeof condition === "boolean") {
|
|
105
|
+
params.append(field, condition ? "true" : "false");
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
params.append(field, `eq.${condition}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return params;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Serialize full SelectOpts into URLSearchParams (filter + modifiers).
|
|
115
|
+
*/
|
|
116
|
+
export function serializeSelectOpts(opts) {
|
|
117
|
+
const params = opts.where ? serializeFilter(opts.where) : new URLSearchParams();
|
|
118
|
+
if (opts.select?.length)
|
|
119
|
+
params.set("_select", opts.select.join(","));
|
|
120
|
+
if (opts.order?.length)
|
|
121
|
+
params.set("_order", opts.order.join(","));
|
|
122
|
+
if (opts.distinct)
|
|
123
|
+
params.set("_distinct", "true");
|
|
124
|
+
if (opts.count)
|
|
125
|
+
params.set("_count", "true");
|
|
126
|
+
if (opts.countFirst)
|
|
127
|
+
params.set("_count_first", "true");
|
|
128
|
+
if (opts.groupBy?.length)
|
|
129
|
+
params.set("_groupby", opts.groupBy.join(","));
|
|
130
|
+
if (opts.returning?.length)
|
|
131
|
+
params.set("_returning", opts.returning.join(","));
|
|
132
|
+
if (opts.or?.length) {
|
|
133
|
+
// pREST format: _or=field=op.value,field=op.value
|
|
134
|
+
const parts = [];
|
|
135
|
+
for (const f of opts.or) {
|
|
136
|
+
const fp = serializeFilter(f);
|
|
137
|
+
fp.forEach((v, k) => parts.push(`${k}=${v}`));
|
|
138
|
+
}
|
|
139
|
+
params.set("_or", parts.join(","));
|
|
140
|
+
}
|
|
141
|
+
if (opts.page !== undefined)
|
|
142
|
+
params.set("_page", String(opts.page));
|
|
143
|
+
if (opts.size !== undefined)
|
|
144
|
+
params.set("_size", String(opts.size));
|
|
145
|
+
if (opts.limit !== undefined)
|
|
146
|
+
params.set("_limit", String(opts.limit));
|
|
147
|
+
if (opts.offset !== undefined)
|
|
148
|
+
params.set("_offset", String(opts.offset));
|
|
149
|
+
return params;
|
|
150
|
+
}
|
|
151
|
+
// ============================================================
|
|
152
|
+
// PrestClient
|
|
153
|
+
// ============================================================
|
|
154
|
+
export class PrestClient {
|
|
155
|
+
prestUrl;
|
|
156
|
+
authHeader;
|
|
157
|
+
extraHeaders;
|
|
158
|
+
constructor(config) {
|
|
159
|
+
if (typeof config === "string") {
|
|
160
|
+
// Shortcut: new PrestClient(url, authToken?)
|
|
161
|
+
this.prestUrl = config.replace(/\/+$/, "");
|
|
162
|
+
const token = arguments[1];
|
|
163
|
+
this.authHeader = token ? `Bearer ${token}` : undefined;
|
|
164
|
+
this.extraHeaders = {};
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
this.prestUrl = config.prestUrl.replace(/\/+$/, "");
|
|
168
|
+
this.authHeader =
|
|
169
|
+
config.rawAuthHeader ??
|
|
170
|
+
(config.authToken ? `Bearer ${config.authToken}` : undefined);
|
|
171
|
+
this.extraHeaders = { ...(config.headers ?? {}) };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Forward-compat factory: validate a Kratos session and build a PrestClient.
|
|
176
|
+
*
|
|
177
|
+
* The vanilla pREST submodule does not yet wire Kratos (the planned
|
|
178
|
+
* `[auth.kratos]` middleware is documented in the parent project's CLAUDE.md
|
|
179
|
+
* but not merged into the submodule). This helper validates the session
|
|
180
|
+
* against Kratos and stores it as a Bearer header, so it will Just Work
|
|
181
|
+
* once the integration lands. Today it's useful if you're running a patched
|
|
182
|
+
* prest build with Kratos auth.
|
|
183
|
+
*
|
|
184
|
+
* @param kratosUrl - Kratos public URL, e.g. http://localhost:4433
|
|
185
|
+
* @param prestUrl - pREST base URL, defaults to http://localhost:3000
|
|
186
|
+
* @param sessionToken - Optional token. Reads `ory_kratos_session` cookie if omitted (browser).
|
|
187
|
+
* @returns PrestClient, or null if no valid session was found.
|
|
188
|
+
*/
|
|
189
|
+
static async fromKratosSession(kratosUrl, prestUrl = "http://localhost:3000", sessionToken) {
|
|
190
|
+
const token = sessionToken ??
|
|
191
|
+
(typeof document !== "undefined" ? readCookie("ory_kratos_session") : undefined);
|
|
192
|
+
if (!token)
|
|
193
|
+
return null;
|
|
194
|
+
const valid = await validateKratosSession(kratosUrl, token);
|
|
195
|
+
if (!valid)
|
|
196
|
+
return null;
|
|
197
|
+
return new PrestClient({ prestUrl, authToken: token });
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Set or replace the auth token after construction.
|
|
201
|
+
* Useful after calling `login()`. Returns `this` for chaining.
|
|
202
|
+
*/
|
|
203
|
+
setAuthToken(token) {
|
|
204
|
+
this.authHeader = `Bearer ${token}`;
|
|
205
|
+
return this;
|
|
206
|
+
}
|
|
207
|
+
// ─── Low-level request helper ────────────────────────────────────────────
|
|
208
|
+
/**
|
|
209
|
+
* Escape hatch for endpoints not covered by the typed methods.
|
|
210
|
+
* Throws PrestApiError on non-2xx.
|
|
211
|
+
*/
|
|
212
|
+
async request(method, path, init) {
|
|
213
|
+
const url = new URL(`${path.startsWith("/") ? "" : "/"}${path}`, this.prestUrl);
|
|
214
|
+
if (init?.params) {
|
|
215
|
+
init.params.forEach((v, k) => url.searchParams.append(k, v));
|
|
216
|
+
}
|
|
217
|
+
const res = await fetch(url.toString(), {
|
|
218
|
+
method,
|
|
219
|
+
headers: {
|
|
220
|
+
Accept: "application/json",
|
|
221
|
+
...(this.authHeader ? { Authorization: this.authHeader } : {}),
|
|
222
|
+
...this.extraHeaders,
|
|
223
|
+
...(init?.body && !(init.body instanceof FormData)
|
|
224
|
+
? { "Content-Type": "application/json" }
|
|
225
|
+
: {}),
|
|
226
|
+
...init?.headers,
|
|
227
|
+
},
|
|
228
|
+
body: init?.body ?? null,
|
|
229
|
+
redirect: "manual",
|
|
230
|
+
});
|
|
231
|
+
const text = await res.text();
|
|
232
|
+
let body = null;
|
|
233
|
+
if (text) {
|
|
234
|
+
try {
|
|
235
|
+
body = JSON.parse(text);
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
body = text;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (!res.ok) {
|
|
242
|
+
const msg = (body && typeof body === "object" && "error" in body
|
|
243
|
+
? String(body.error)
|
|
244
|
+
: res.statusText) || `HTTP ${res.status}`;
|
|
245
|
+
throw new PrestApiError(res.status, msg, body);
|
|
246
|
+
}
|
|
247
|
+
return body;
|
|
248
|
+
}
|
|
249
|
+
// ─── Catalog ─────────────────────────────────────────────────────────────
|
|
250
|
+
/** `GET /databases` — list database names. */
|
|
251
|
+
databases() {
|
|
252
|
+
return this.request("GET", "/databases");
|
|
253
|
+
}
|
|
254
|
+
/** `GET /schemas` — list schema names. */
|
|
255
|
+
schemas() {
|
|
256
|
+
return this.request("GET", "/schemas");
|
|
257
|
+
}
|
|
258
|
+
/** `GET /tables` — list table names. */
|
|
259
|
+
tables() {
|
|
260
|
+
return this.request("GET", "/tables");
|
|
261
|
+
}
|
|
262
|
+
/** `GET /{database}/{schema}` — list tables in a specific database/schema. */
|
|
263
|
+
tablesIn(database, schema) {
|
|
264
|
+
return this.request("GET", `/${encodeURIComponent(database)}/${encodeURIComponent(schema)}`);
|
|
265
|
+
}
|
|
266
|
+
/** `GET /show/{database}/{schema}/{table}` — table description / columns. */
|
|
267
|
+
showTable(database, schema, table) {
|
|
268
|
+
return this.request("GET", `/show/${encodeURIComponent(database)}/${encodeURIComponent(schema)}/${encodeURIComponent(table)}`);
|
|
269
|
+
}
|
|
270
|
+
// ─── CRUD ────────────────────────────────────────────────────────────────
|
|
271
|
+
/** `GET /{db}/{schema}/{table}` — SELECT with typed filter DSL. */
|
|
272
|
+
select(database, schema, table, opts = {}) {
|
|
273
|
+
const params = serializeSelectOpts(opts);
|
|
274
|
+
return this.request("GET", `/${encodeURIComponent(database)}/${encodeURIComponent(schema)}/${encodeURIComponent(table)}`, { params });
|
|
275
|
+
}
|
|
276
|
+
/** `POST /{db}/{schema}/{table}` — insert a single row. */
|
|
277
|
+
insert(database, schema, table, data) {
|
|
278
|
+
return this.request("POST", `/${encodeURIComponent(database)}/${encodeURIComponent(schema)}/${encodeURIComponent(table)}`, { body: JSON.stringify(data) });
|
|
279
|
+
}
|
|
280
|
+
/** `POST /batch/{db}/{schema}/{table}` — insert multiple rows in one call. */
|
|
281
|
+
insertBatch(database, schema, table, rows) {
|
|
282
|
+
return this.request("POST", `/batch/${encodeURIComponent(database)}/${encodeURIComponent(schema)}/${encodeURIComponent(table)}`, { body: JSON.stringify(rows) });
|
|
283
|
+
}
|
|
284
|
+
/** `PUT /{db}/{schema}/{table}?filter` — update rows matching filter. */
|
|
285
|
+
update(database, schema, table, where, data) {
|
|
286
|
+
const params = serializeFilter(where);
|
|
287
|
+
return this.request("PUT", `/${encodeURIComponent(database)}/${encodeURIComponent(schema)}/${encodeURIComponent(table)}`, { body: JSON.stringify(data), params });
|
|
288
|
+
}
|
|
289
|
+
/** `DELETE /{db}/{schema}/{table}?filter` — delete rows matching filter. */
|
|
290
|
+
delete(database, schema, table, where) {
|
|
291
|
+
const params = serializeFilter(where);
|
|
292
|
+
return this.request("DELETE", `/${encodeURIComponent(database)}/${encodeURIComponent(schema)}/${encodeURIComponent(table)}`, { params });
|
|
293
|
+
}
|
|
294
|
+
// ─── Stored queries ──────────────────────────────────────────────────────
|
|
295
|
+
/**
|
|
296
|
+
* Execute a stored SQL script: `GET /_QUERIES/{location}/{script}`.
|
|
297
|
+
*
|
|
298
|
+
* Scripts live in `<prest queries dir>/<location>/<script>.sql` and can
|
|
299
|
+
* reference params as `{{ sqlVal "key" }}` / `{{ sqlList "key" }}` in
|
|
300
|
+
* pREST's template syntax.
|
|
301
|
+
*
|
|
302
|
+
* @param location - Folder name under prest's `queries` directory.
|
|
303
|
+
* @param script - `.sql` filename without the extension.
|
|
304
|
+
* @param params - Rendered as URL query params; accessible in the SQL template.
|
|
305
|
+
*/
|
|
306
|
+
query(location, script, params = {}) {
|
|
307
|
+
const qs = new URLSearchParams();
|
|
308
|
+
for (const [k, v] of Object.entries(params))
|
|
309
|
+
qs.set(k, String(v));
|
|
310
|
+
return this.request("GET", `/_QUERIES/${encodeURIComponent(location)}/${encodeURIComponent(script)}`, { params: qs });
|
|
311
|
+
}
|
|
312
|
+
// ─── Health & auth ───────────────────────────────────────────────────────
|
|
313
|
+
/** `GET /_health` — returns true on 2xx, false otherwise. */
|
|
314
|
+
async health() {
|
|
315
|
+
try {
|
|
316
|
+
await this.request("GET", "/_health");
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* `POST /auth` — exchange username/password for a JWT.
|
|
325
|
+
* Stores the returned token and attaches it as `Authorization: Bearer <token>`
|
|
326
|
+
* on subsequent requests.
|
|
327
|
+
*
|
|
328
|
+
* Only works when pREST's `[auth]` block is enabled in `prest.toml`.
|
|
329
|
+
* Returns the token string.
|
|
330
|
+
*/
|
|
331
|
+
async login(username, password) {
|
|
332
|
+
const res = await this.request("POST", "/auth", {
|
|
333
|
+
body: JSON.stringify({ username, password }),
|
|
334
|
+
});
|
|
335
|
+
const token = res.token;
|
|
336
|
+
if (!token) {
|
|
337
|
+
throw new PrestApiError(200, "auth response missing 'token' field", res);
|
|
338
|
+
}
|
|
339
|
+
this.setAuthToken(token);
|
|
340
|
+
return token;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
// ============================================================
|
|
344
|
+
// Helpers (Kratos session validation — mirrors alist-kratos-sdk)
|
|
345
|
+
// ============================================================
|
|
346
|
+
function readCookie(name) {
|
|
347
|
+
if (typeof document === "undefined")
|
|
348
|
+
return undefined;
|
|
349
|
+
const match = document.cookie.match(new RegExp("(?:^|; )" + name + "=([^;]*)"));
|
|
350
|
+
return match ? decodeURIComponent(match[1]) : undefined;
|
|
351
|
+
}
|
|
352
|
+
async function validateKratosSession(kratosUrl, token) {
|
|
353
|
+
try {
|
|
354
|
+
const res = await fetch(`${kratosUrl.replace(/\/+$/, "")}/sessions/whoami`, {
|
|
355
|
+
headers: { "X-Session-Token": token, Accept: "application/json" },
|
|
356
|
+
});
|
|
357
|
+
if (!res.ok)
|
|
358
|
+
return false;
|
|
359
|
+
const session = (await res.json());
|
|
360
|
+
return session.active === true;
|
|
361
|
+
}
|
|
362
|
+
catch {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// Verifies that serializeFilter and serializeSelectOpts produce the exact URL
|
|
2
|
+
// param strings pREST's `WhereByRequest` parser expects.
|
|
3
|
+
//
|
|
4
|
+
// Run: npm run build && node examples/filters.mjs
|
|
5
|
+
// Exits non-zero on first mismatch.
|
|
6
|
+
//
|
|
7
|
+
// Note: the test imports the compiled JS from dist/, so build first.
|
|
8
|
+
|
|
9
|
+
import assert from "node:assert";
|
|
10
|
+
import { serializeFilter, serializeSelectOpts } from "../dist/index.js";
|
|
11
|
+
|
|
12
|
+
let passed = 0;
|
|
13
|
+
function check(name, got, want) {
|
|
14
|
+
assert.strictEqual(got, want, `${name}\n got: ${got}\n want: ${want}`);
|
|
15
|
+
passed++;
|
|
16
|
+
console.log(` ✓ ${name}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
console.log("serializeFilter");
|
|
20
|
+
|
|
21
|
+
// Shorthand scalar → eq.
|
|
22
|
+
check(
|
|
23
|
+
"scalar number shorthand",
|
|
24
|
+
serializeFilter({ actor_id: 42 }).toString(),
|
|
25
|
+
"actor_id=eq.42",
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
check(
|
|
29
|
+
"scalar string shorthand",
|
|
30
|
+
serializeFilter({ name: "alice" }).toString(),
|
|
31
|
+
"name=eq.alice",
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// Explicit operators
|
|
35
|
+
check(
|
|
36
|
+
"gt operator",
|
|
37
|
+
serializeFilter({ age: { gt: 30 } }).toString(),
|
|
38
|
+
"age=gt.30",
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
check(
|
|
42
|
+
"gte operator",
|
|
43
|
+
serializeFilter({ age: { gte: 30 } }).toString(),
|
|
44
|
+
"age=gte.30",
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
check(
|
|
48
|
+
"like operator (URL-encoded %)",
|
|
49
|
+
serializeFilter({ name: { like: "foo%" } }).toString(),
|
|
50
|
+
"name=like.foo%25",
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Array operator
|
|
54
|
+
check(
|
|
55
|
+
"in operator (parens, comma-separated)",
|
|
56
|
+
serializeFilter({ id: { in: [1, 2, 3] } }).toString(),
|
|
57
|
+
"id=in.%281%2C2%2C3%29", // URLSearchParams encodes ( ) ,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// Null / boolean shortcuts
|
|
61
|
+
check(
|
|
62
|
+
"null shorthand → IS NULL",
|
|
63
|
+
serializeFilter({ deleted_at: null }).toString(),
|
|
64
|
+
"deleted_at=null",
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
check(
|
|
68
|
+
"boolean true → IS TRUE",
|
|
69
|
+
serializeFilter({ is_active: true }).toString(),
|
|
70
|
+
"is_active=true",
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
check(
|
|
74
|
+
"boolean false → IS FALSE",
|
|
75
|
+
serializeFilter({ is_active: false }).toString(),
|
|
76
|
+
"is_active=false",
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
// Multiple fields → AND-ed (separate params)
|
|
80
|
+
check(
|
|
81
|
+
"multi-field AND",
|
|
82
|
+
serializeFilter({ actor_id: 42, status: { eq: "active" } }).toString(),
|
|
83
|
+
"actor_id=eq.42&status=eq.active",
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
console.log();
|
|
87
|
+
console.log("serializeSelectOpts");
|
|
88
|
+
|
|
89
|
+
check(
|
|
90
|
+
"select + order + pagination",
|
|
91
|
+
serializeSelectOpts({
|
|
92
|
+
where: { actor_id: 42 },
|
|
93
|
+
select: ["id", "amount"],
|
|
94
|
+
order: ["amount:desc"],
|
|
95
|
+
page: 1,
|
|
96
|
+
size: 20,
|
|
97
|
+
}).toString(),
|
|
98
|
+
"actor_id=eq.42&_select=id%2Camount&_order=amount%3Adesc&_page=1&_size=20",
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
check(
|
|
102
|
+
"distinct + count",
|
|
103
|
+
serializeSelectOpts({ distinct: true, count: true }).toString(),
|
|
104
|
+
"_distinct=true&_count=true",
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
check(
|
|
108
|
+
"limit/offset pagination",
|
|
109
|
+
serializeSelectOpts({ limit: 10, offset: 30 }).toString(),
|
|
110
|
+
"_limit=10&_offset=30",
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
check(
|
|
114
|
+
"groupBy + returning",
|
|
115
|
+
serializeSelectOpts({
|
|
116
|
+
groupBy: ["actor_id"],
|
|
117
|
+
returning: ["id", "amount"],
|
|
118
|
+
}).toString(),
|
|
119
|
+
"_groupby=actor_id&_returning=id%2Camount",
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
check(
|
|
123
|
+
"or clauses",
|
|
124
|
+
serializeSelectOpts({
|
|
125
|
+
or: [{ status: "active" }, { amount: { gt: 1000 } }],
|
|
126
|
+
}).toString(),
|
|
127
|
+
"_or=status%3Deq.active%2Camount%3Dgt.1000",
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
console.log();
|
|
131
|
+
console.log(`All ${passed} checks passed.`);
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "prest-js-sdk",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "TypeScript SDK for pREST (PostgreSQL REST API) with typed filter DSL and optional Kratos auth",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"dev": "tsc --watch",
|
|
11
|
+
"prepublishOnly": "npm run build",
|
|
12
|
+
"changeset": "changeset",
|
|
13
|
+
"version-packages": "changeset version",
|
|
14
|
+
"release": "npm run build && changeset publish"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@changesets/cli": "^2.27.10",
|
|
18
|
+
"typescript": "^5.7.2"
|
|
19
|
+
}
|
|
20
|
+
}
|