@ikeboy003/cloudrest-client 0.0.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/dist/client.d.ts +17 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +30 -0
- package/dist/client.js.map +1 -1
- package/dist/filter.d.ts +51 -10
- package/dist/filter.d.ts.map +1 -1
- package/dist/filter.js +71 -36
- package/dist/filter.js.map +1 -1
- package/dist/index.d.ts +36 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/mutation.d.ts +82 -20
- package/dist/mutation.d.ts.map +1 -1
- package/dist/mutation.js +160 -49
- package/dist/mutation.js.map +1 -1
- package/dist/query.d.ts +42 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +84 -17
- package/dist/query.js.map +1 -1
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +2 -1
- package/dist/rpc.js.map +1 -1
- package/dist/serialize.d.ts +4 -0
- package/dist/serialize.d.ts.map +1 -1
- package/dist/serialize.js +21 -1
- package/dist/serialize.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +10 -4
- package/src/client.ts +49 -4
- package/src/filter.ts +86 -36
- package/src/index.ts +26 -8
- package/src/mutation.ts +163 -55
- package/src/query.ts +95 -24
- package/src/rpc.ts +4 -3
- package/src/serialize.ts +24 -1
- package/src/types.ts +3 -1
package/dist/rpc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../src/rpc.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,0EAA0E;AAC1E,EAAE;AACF,sFAAsF;AACtF,EAAE;AACF,4BAA4B;AAC5B,qDAAqD;AACrD,qEAAqE;AAGrE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../src/rpc.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,0EAA0E;AAC1E,EAAE;AACF,sFAAsF;AACtF,EAAE;AACF,4BAA4B;AAC5B,qDAAqD;AACrD,qEAAqE;AAGrE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,MAAM,OAAO,SAAS;IAED;IACA;IACA;IAHnB,YACmB,GAAgB,EAChB,EAAU,EACV,OAAgC,EAAE;QAFlC,QAAG,GAAH,GAAG,CAAa;QAChB,OAAE,GAAF,EAAE,CAAQ;QACV,SAAI,GAAJ,IAAI,CAA8B;IAClD,CAAC;IAEI,KAAK,CAAC,IAAI;QAChB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QACxD,MAAM,OAAO,GAA2B;YACtC,MAAM,EAAE,kBAAkB;YAC1B,cAAc,EAAE,kBAAkB;YAClC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO;SACpB,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC;QAC1C,IAAI,KAAK;YAAE,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,KAAK,EAAE,CAAC;QAExD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE;YACpC,MAAM,EAAE,MAAM;YACd,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;SAChC,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9C,MAAM,IAAI,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAC7D,CAAC;QAED,sEAAsE;QACtE,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,SAAmB,CAAC;QACnD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,IAAI,CAAC,IAAI;YAAE,OAAO,SAAmB,CAAC;QACtC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAW,CAAC;IACpC,CAAC;IAED,IAAI,CACF,WAA8D,EAC9D,UAA+D;QAE/D,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,UAAmB,CAAC,CAAC;IAC5D,CAAC;CACF"}
|
package/dist/serialize.d.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
|
+
export declare function joinUrl(base: string, path: string): string;
|
|
1
2
|
export declare function serializeValue(v: unknown): string;
|
|
3
|
+
export declare function serializeListItem(v: unknown): string;
|
|
4
|
+
export declare function serializeArray(v: readonly unknown[]): string;
|
|
5
|
+
export declare function serializeRange(v: unknown): string;
|
|
2
6
|
//# sourceMappingURL=serialize.d.ts.map
|
package/dist/serialize.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAmBA,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAOjD;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAMpD;AAID,wBAAgB,cAAc,CAAC,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAE5D;AAKD,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAKjD"}
|
package/dist/serialize.js
CHANGED
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
const SCALAR_SPECIAL = /[,"]/;
|
|
13
13
|
// List items are inside `(a,b,c)`, so `(`, `)`, and `,` all need quoting.
|
|
14
14
|
const LIST_SPECIAL = /[(),."]/;
|
|
15
|
+
// Join the client baseUrl with a table/route, tolerant of trailing/leading
|
|
16
|
+
// slashes on either side, so `baseUrl="http://h:3005"` + `table="catalog_public"`
|
|
17
|
+
// (or "/catalog_public") both yield exactly one separating slash.
|
|
18
|
+
export function joinUrl(base, path) {
|
|
19
|
+
return `${base.replace(/\/+$/, "")}/${String(path).replace(/^\/+/, "")}`;
|
|
20
|
+
}
|
|
15
21
|
export function serializeValue(v) {
|
|
16
22
|
if (v === null || v === undefined)
|
|
17
23
|
return "null";
|
|
@@ -25,7 +31,7 @@ export function serializeValue(v) {
|
|
|
25
31
|
return String(v);
|
|
26
32
|
return escapeIfNeeded(String(v), SCALAR_SPECIAL);
|
|
27
33
|
}
|
|
28
|
-
function serializeListItem(v) {
|
|
34
|
+
export function serializeListItem(v) {
|
|
29
35
|
if (v === null || v === undefined)
|
|
30
36
|
return "null";
|
|
31
37
|
if (v instanceof Date)
|
|
@@ -36,6 +42,20 @@ function serializeListItem(v) {
|
|
|
36
42
|
return String(v);
|
|
37
43
|
return escapeIfNeeded(String(v), LIST_SPECIAL);
|
|
38
44
|
}
|
|
45
|
+
// PostgreSQL array literal `{a,b,c}` — used by the array operators cs/cd/ov
|
|
46
|
+
// (`tags=cs.{a,b}`). Distinct from the `(a,b)` list form used by `in`.
|
|
47
|
+
export function serializeArray(v) {
|
|
48
|
+
return `{${v.map(serializeListItem).join(",")}}`;
|
|
49
|
+
}
|
|
50
|
+
// Range/value argument for the range operators (sl/sr/nxr/nxl/adj). Accepts a
|
|
51
|
+
// ready-made literal (`"(1,10)"`, `"[2020-01-01,2020-12-31]"`) or a `[lo, hi]`
|
|
52
|
+
// tuple → `(lo,hi)`.
|
|
53
|
+
export function serializeRange(v) {
|
|
54
|
+
if (Array.isArray(v)) {
|
|
55
|
+
return `(${serializeListItem(v[0])},${serializeListItem(v[1])})`;
|
|
56
|
+
}
|
|
57
|
+
return String(v);
|
|
58
|
+
}
|
|
39
59
|
function escapeIfNeeded(s, special) {
|
|
40
60
|
if (s === "")
|
|
41
61
|
return '""';
|
package/dist/serialize.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize.js","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,+DAA+D;AAC/D,EAAE;AACF,SAAS;AACT,0DAA0D;AAC1D,uBAAuB;AACvB,4EAA4E;AAC5E,0EAA0E;AAE1E,uEAAuE;AACvE,sEAAsE;AACtE,+CAA+C;AAC/C,MAAM,cAAc,GAAG,MAAM,CAAC;AAC9B,0EAA0E;AAC1E,MAAM,YAAY,GAAG,SAAS,CAAC;AAE/B,MAAM,UAAU,cAAc,CAAC,CAAU;IACvC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACjD,IAAI,CAAC,YAAY,IAAI;QAAE,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9C,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IACvE,IAAI,OAAO,CAAC,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IACxD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IACrE,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;AACnD,CAAC;AAED,
|
|
1
|
+
{"version":3,"file":"serialize.js","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type RowOf<P extends AnyPaths, T extends keyof P> = P[T] extends {
|
|
|
9
9
|
};
|
|
10
10
|
};
|
|
11
11
|
};
|
|
12
|
-
} ? Body extends ReadonlyArray<infer Item> ? Item : Body :
|
|
12
|
+
} ? Body extends ReadonlyArray<infer Item> ? Item : Body : Record<string, unknown>;
|
|
13
13
|
export type TableName<P extends AnyPaths> = Extract<keyof P, string>;
|
|
14
14
|
export type TokenGetter = () => string | null | Promise<string | null>;
|
|
15
15
|
export interface ClientOptions {
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAK3C,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,MAAM,CAAC,IACrD,CAAC,CAAC,CAAC,CAAC,SAAS;IAAE,GAAG,EAAE;QAAE,SAAS,EAAE;YAAE,GAAG,EAAE;gBAAE,OAAO,EAAE;oBAAE,kBAAkB,EAAE,MAAM,IAAI,CAAA;iBAAE,CAAA;aAAE,CAAA;SAAE,CAAA;KAAE,CAAA;CAAE,GACzF,IAAI,SAAS,aAAa,CAAC,MAAM,IAAI,CAAC,GACpC,IAAI,GACJ,IAAI,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAK3C,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,MAAM,CAAC,IACrD,CAAC,CAAC,CAAC,CAAC,SAAS;IAAE,GAAG,EAAE;QAAE,SAAS,EAAE;YAAE,GAAG,EAAE;gBAAE,OAAO,EAAE;oBAAE,kBAAkB,EAAE,MAAM,IAAI,CAAA;iBAAE,CAAA;aAAE,CAAA;SAAE,CAAA;KAAE,CAAA;CAAE,GACzF,IAAI,SAAS,aAAa,CAAC,MAAM,IAAI,CAAC,GACpC,IAAI,GACJ,IAAI,GAGN,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAG9B,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;AAGrE,MAAM,MAAM,WAAW,GAAG,MAAM,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAEvE,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ikeboy003/cloudrest-client",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Typed query builder for cloudrest. PostgREST-grammar compatible.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -11,15 +11,21 @@
|
|
|
11
11
|
"import": "./dist/index.js"
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
|
-
"files": [
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"src",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
15
19
|
"scripts": {
|
|
16
|
-
"build": "tsc -p tsconfig.json",
|
|
17
|
-
"dev": "tsc -p tsconfig.json --watch"
|
|
20
|
+
"build": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
|
|
21
|
+
"dev": "tsc -p tsconfig.json --watch",
|
|
22
|
+
"test": "node --test test/*.test.mjs"
|
|
18
23
|
},
|
|
19
24
|
"publishConfig": {
|
|
20
25
|
"access": "public"
|
|
21
26
|
},
|
|
22
27
|
"devDependencies": {
|
|
28
|
+
"tsc-alias": "^1.8.17",
|
|
23
29
|
"typescript": "^5.5.0"
|
|
24
30
|
}
|
|
25
31
|
}
|
package/src/client.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import type { AnyPaths, ClientOptions, RowOf, TableName } from "
|
|
2
|
-
import { QueryBuilder, type ExecContext } from "
|
|
3
|
-
import { InsertBuilder, UpdateBuilder, DeleteBuilder } from "
|
|
4
|
-
import { RpcCaller } from "
|
|
1
|
+
import type { AnyPaths, ClientOptions, RowOf, TableName } from "@/types.js";
|
|
2
|
+
import { QueryBuilder, type ExecContext } from "@/query.js";
|
|
3
|
+
import { InsertBuilder, UpdateBuilder, DeleteBuilder } from "@/mutation.js";
|
|
4
|
+
import { RpcCaller } from "@/rpc.js";
|
|
5
|
+
|
|
6
|
+
export interface CopyOptions {
|
|
7
|
+
/** Target columns, in body order. Required (maps to `?columns=`). */
|
|
8
|
+
columns: string[];
|
|
9
|
+
/** Schema the table lives in (default "public"). */
|
|
10
|
+
schema?: string;
|
|
11
|
+
/** Wire format of `body` (default "csv"). NDJSON is transcoded server-side. */
|
|
12
|
+
format?: "csv" | "ndjson";
|
|
13
|
+
}
|
|
5
14
|
|
|
6
15
|
export class CloudRestClient<P extends AnyPaths> {
|
|
7
16
|
private readonly ctx: ExecContext;
|
|
@@ -50,6 +59,42 @@ export class CloudRestClient<P extends AnyPaths> {
|
|
|
50
59
|
): RpcCaller<Result> {
|
|
51
60
|
return new RpcCaller<Result>(this.ctx, fn, args);
|
|
52
61
|
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Bulk-ingest via the server's COPY endpoint
|
|
65
|
+
* (`POST /_copy/{schema}/{table}?columns=`). `body` is CSV (with a header row)
|
|
66
|
+
* or NDJSON. Streams straight into `COPY` — far faster than row-by-row
|
|
67
|
+
* inserts for large loads. Resolves to `{ inserted: N }`.
|
|
68
|
+
*/
|
|
69
|
+
async copy(
|
|
70
|
+
table: TableName<P>,
|
|
71
|
+
body: string | Uint8Array,
|
|
72
|
+
opts: CopyOptions,
|
|
73
|
+
): Promise<{ inserted: number }> {
|
|
74
|
+
const schema = opts.schema ?? "public";
|
|
75
|
+
const cols = opts.columns.map(encodeURIComponent).join(",");
|
|
76
|
+
const url = `${this.ctx.baseUrl}/_copy/${schema}/${String(table)}?columns=${cols}`;
|
|
77
|
+
const headers: Record<string, string> = {
|
|
78
|
+
Accept: "application/json",
|
|
79
|
+
"Content-Type":
|
|
80
|
+
opts.format === "ndjson" ? "application/x-ndjson" : "text/csv",
|
|
81
|
+
...this.ctx.headers,
|
|
82
|
+
};
|
|
83
|
+
const token = await this.ctx.getToken?.();
|
|
84
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
85
|
+
|
|
86
|
+
const res = await this.ctx.fetch(url, {
|
|
87
|
+
method: "POST",
|
|
88
|
+
headers,
|
|
89
|
+
body: body as BodyInit,
|
|
90
|
+
});
|
|
91
|
+
if (!res.ok) {
|
|
92
|
+
const errBody = await res.json().catch(() => null);
|
|
93
|
+
const { CloudRestError } = await import("@/errors.js");
|
|
94
|
+
throw new CloudRestError(res.status, res.statusText, errBody);
|
|
95
|
+
}
|
|
96
|
+
return (await res.json()) as { inserted: number };
|
|
97
|
+
}
|
|
53
98
|
}
|
|
54
99
|
|
|
55
100
|
export function createClient<P extends AnyPaths>(opts: ClientOptions): CloudRestClient<P> {
|
package/src/filter.ts
CHANGED
|
@@ -1,50 +1,100 @@
|
|
|
1
|
-
// Filter ops —
|
|
2
|
-
// null checks, pattern match. Composite ops (or, and, fts, jsonpath) come later.
|
|
1
|
+
// Filter ops — the full PostgREST operator set + nestable and/or/not logic.
|
|
3
2
|
//
|
|
4
|
-
// Each filter contributes one entry
|
|
3
|
+
// Each filter contributes one entry. A leaf renders as `column=op.value`; a
|
|
4
|
+
// logic group renders as `and=(child,child)` / `or=(...)`. As a CHILD inside a
|
|
5
|
+
// group, a leaf re-spells to `column.op.value` and a group to `and(...)`/`or(...)`,
|
|
6
|
+
// which is how PostgREST nests them.
|
|
5
7
|
|
|
6
|
-
import { serializeValue } from "
|
|
8
|
+
import { serializeValue, serializeArray, serializeRange } from "@/serialize.js";
|
|
7
9
|
|
|
8
10
|
export type IsValue = "null" | "true" | "false" | "unknown";
|
|
9
11
|
|
|
10
12
|
export interface FilterEntry {
|
|
13
|
+
/** Leaf: the column. Group: "and" | "or". */
|
|
11
14
|
column: string;
|
|
15
|
+
/** Leaf: "op.value". Group: "(child,child,...)". */
|
|
12
16
|
expr: string;
|
|
17
|
+
/** True for a logic group (and/or), so child-rendering omits the dot. */
|
|
18
|
+
group?: boolean;
|
|
13
19
|
}
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
export
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
export
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
export
|
|
37
|
-
|
|
21
|
+
const leaf = (column: string, expr: string): FilterEntry => ({ column, expr });
|
|
22
|
+
|
|
23
|
+
// ── comparison / equality ───────────────────────────────────────────────────
|
|
24
|
+
export const fEq = (c: string, v: unknown): FilterEntry => leaf(c, `eq.${serializeValue(v)}`);
|
|
25
|
+
export const fNeq = (c: string, v: unknown): FilterEntry => leaf(c, `neq.${serializeValue(v)}`);
|
|
26
|
+
export const fGt = (c: string, v: unknown): FilterEntry => leaf(c, `gt.${serializeValue(v)}`);
|
|
27
|
+
export const fGte = (c: string, v: unknown): FilterEntry => leaf(c, `gte.${serializeValue(v)}`);
|
|
28
|
+
export const fLt = (c: string, v: unknown): FilterEntry => leaf(c, `lt.${serializeValue(v)}`);
|
|
29
|
+
export const fLte = (c: string, v: unknown): FilterEntry => leaf(c, `lte.${serializeValue(v)}`);
|
|
30
|
+
|
|
31
|
+
// ── pattern match ───────────────────────────────────────────────────────────
|
|
32
|
+
export const fLike = (c: string, p: string): FilterEntry => leaf(c, `like.${serializeValue(p)}`);
|
|
33
|
+
export const fIlike = (c: string, p: string): FilterEntry => leaf(c, `ilike.${serializeValue(p)}`);
|
|
34
|
+
/** POSIX regex (`~`). */
|
|
35
|
+
export const fMatch = (c: string, p: string): FilterEntry => leaf(c, `match.${serializeValue(p)}`);
|
|
36
|
+
/** Case-insensitive POSIX regex (`~*`). */
|
|
37
|
+
export const fImatch = (c: string, p: string): FilterEntry => leaf(c, `imatch.${serializeValue(p)}`);
|
|
38
|
+
|
|
39
|
+
// ── set membership / null / distinct ────────────────────────────────────────
|
|
40
|
+
export const fIn = (c: string, vals: readonly unknown[]): FilterEntry => leaf(c, `in.${serializeValue(vals)}`);
|
|
41
|
+
// `is` is the correct NULL / boolean check — `eq.null` is a SQL no-op.
|
|
42
|
+
export const fIs = (c: string, v: IsValue): FilterEntry => leaf(c, `is.${v}`);
|
|
43
|
+
/** `IS DISTINCT FROM` — null-safe inequality. */
|
|
44
|
+
export const fIsDistinct = (c: string, v: unknown): FilterEntry => leaf(c, `isdistinct.${serializeValue(v)}`);
|
|
45
|
+
|
|
46
|
+
// ── full-text search ────────────────────────────────────────────────────────
|
|
47
|
+
// Optional text-search config (e.g. "english") rides in parens: `fts(english).query`.
|
|
48
|
+
const ftsOp = (op: string) => (c: string, query: string, config?: string): FilterEntry =>
|
|
49
|
+
leaf(c, config ? `${op}(${config}).${serializeValue(query)}` : `${op}.${serializeValue(query)}`);
|
|
50
|
+
/** `to_tsquery` full-text search. */
|
|
51
|
+
export const fFts = ftsOp("fts");
|
|
52
|
+
/** `plainto_tsquery`. */
|
|
53
|
+
export const fPlfts = ftsOp("plfts");
|
|
54
|
+
/** `phraseto_tsquery`. */
|
|
55
|
+
export const fPhfts = ftsOp("phfts");
|
|
56
|
+
/** `websearch_to_tsquery`. */
|
|
57
|
+
export const fWfts = ftsOp("wfts");
|
|
58
|
+
|
|
59
|
+
// ── array / range containment + adjacency ───────────────────────────────────
|
|
60
|
+
/** contains `@>` (array/range/jsonb). */
|
|
61
|
+
export const fCs = (c: string, v: readonly unknown[]): FilterEntry => leaf(c, `cs.${serializeArray(v)}`);
|
|
62
|
+
/** contained-in `<@`. */
|
|
63
|
+
export const fCd = (c: string, v: readonly unknown[]): FilterEntry => leaf(c, `cd.${serializeArray(v)}`);
|
|
64
|
+
/** overlap `&&` (array or range). */
|
|
65
|
+
export const fOv = (c: string, v: readonly unknown[] | string): FilterEntry =>
|
|
66
|
+
leaf(c, `ov.${Array.isArray(v) ? serializeArray(v) : serializeRange(v)}`);
|
|
67
|
+
/** strictly-left-of `<<`. */
|
|
68
|
+
export const fSl = (c: string, range: unknown): FilterEntry => leaf(c, `sl.${serializeRange(range)}`);
|
|
69
|
+
/** strictly-right-of `>>`. */
|
|
70
|
+
export const fSr = (c: string, range: unknown): FilterEntry => leaf(c, `sr.${serializeRange(range)}`);
|
|
71
|
+
/** does-not-extend-to-the-right-of `&<`. */
|
|
72
|
+
export const fNxr = (c: string, range: unknown): FilterEntry => leaf(c, `nxr.${serializeRange(range)}`);
|
|
73
|
+
/** does-not-extend-to-the-left-of `&>`. */
|
|
74
|
+
export const fNxl = (c: string, range: unknown): FilterEntry => leaf(c, `nxl.${serializeRange(range)}`);
|
|
75
|
+
/** adjacent `-|-`. */
|
|
76
|
+
export const fAdj = (c: string, range: unknown): FilterEntry => leaf(c, `adj.${serializeRange(range)}`);
|
|
77
|
+
|
|
78
|
+
// ── generic escape hatch ────────────────────────────────────────────────────
|
|
79
|
+
/** Any operator + already-serialized value, e.g. `fFilter("data->>k", "eq", "x")`. */
|
|
80
|
+
export const fFilter = (c: string, op: string, value: string): FilterEntry => leaf(c, `${op}.${value}`);
|
|
81
|
+
|
|
82
|
+
// ── negation + logic groups (nestable) ──────────────────────────────────────
|
|
83
|
+
/** Negate any single-op filter: `column=not.is.null`, `column=not.eq.5`. */
|
|
84
|
+
export function fNot(entry: FilterEntry): FilterEntry {
|
|
85
|
+
return { column: entry.column, expr: `not.${entry.expr}` };
|
|
38
86
|
}
|
|
39
|
-
|
|
40
|
-
|
|
87
|
+
|
|
88
|
+
/** Render an entry as a CHILD inside a logic group. */
|
|
89
|
+
function renderChild(e: FilterEntry): string {
|
|
90
|
+
return e.group ? `${e.column}${e.expr}` : `${e.column}.${e.expr}`;
|
|
41
91
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
92
|
+
|
|
93
|
+
/** Composite AND — `and=(a.eq.1,b.gt.2)`. Children may be leaves or groups (nests). */
|
|
94
|
+
export function fAnd(entries: readonly FilterEntry[]): FilterEntry {
|
|
95
|
+
return { column: "and", expr: `(${entries.map(renderChild).join(",")})`, group: true };
|
|
45
96
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return { column: entry.column, expr: `not.${entry.expr}` };
|
|
97
|
+
/** Composite OR — `or=(a.eq.1,b.gt.2)`. Children may be leaves or groups (nests). */
|
|
98
|
+
export function fOr(entries: readonly FilterEntry[]): FilterEntry {
|
|
99
|
+
return { column: "or", expr: `(${entries.map(renderChild).join(",")})`, group: true };
|
|
50
100
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
|
-
export { createClient, CloudRestClient } from "
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
1
|
+
export { createClient, CloudRestClient } from "@/client.js";
|
|
2
|
+
export type { CopyOptions } from "@/client.js";
|
|
3
|
+
export { CloudRestError } from "@/errors.js";
|
|
4
|
+
export { QueryBuilder } from "@/query.js";
|
|
5
|
+
export type { OrderOptions, CountStrategy, SingleQuery, PagedQuery } from "@/query.js";
|
|
6
|
+
export { InsertBuilder, UpdateBuilder, DeleteBuilder } from "@/mutation.js";
|
|
7
|
+
export { RpcCaller } from "@/rpc.js";
|
|
6
8
|
export type {
|
|
7
9
|
AnyPaths,
|
|
8
10
|
ClientOptions,
|
|
9
11
|
RowOf,
|
|
10
12
|
TableName,
|
|
11
13
|
TokenGetter,
|
|
12
|
-
} from "
|
|
13
|
-
export type {
|
|
14
|
-
|
|
14
|
+
} from "@/types.js";
|
|
15
|
+
export type { IsValue, FilterEntry } from "@/filter.js";
|
|
16
|
+
|
|
17
|
+
// Bare condition builders for composing `.and(...)` / `.or(...)` (nestable).
|
|
18
|
+
// db.from("books").or(cond.ilike("title","*x*"), cond.ilike("author","*x*"))
|
|
19
|
+
// db.from("books").and(cond.gte("year",2000), cond.or(cond.eq("a",1), cond.eq("b",2)))
|
|
20
|
+
import * as F from "@/filter.js";
|
|
21
|
+
import type { FilterEntry } from "@/filter.js";
|
|
22
|
+
export const cond = {
|
|
23
|
+
eq: F.fEq, neq: F.fNeq, gt: F.fGt, gte: F.fGte, lt: F.fLt, lte: F.fLte,
|
|
24
|
+
like: F.fLike, ilike: F.fIlike, match: F.fMatch, imatch: F.fImatch,
|
|
25
|
+
in: F.fIn, is: F.fIs, isDistinct: F.fIsDistinct,
|
|
26
|
+
fts: F.fFts, plfts: F.fPlfts, phfts: F.fPhfts, wfts: F.fWfts,
|
|
27
|
+
cs: F.fCs, cd: F.fCd, ov: F.fOv, sl: F.fSl, sr: F.fSr, nxr: F.fNxr, nxl: F.fNxl, adj: F.fAdj,
|
|
28
|
+
filter: F.fFilter, not: F.fNot,
|
|
29
|
+
// Variadic so they compose ergonomically + nest: cond.and(cond.eq(..), cond.or(..)).
|
|
30
|
+
and: (...e: FilterEntry[]): FilterEntry => F.fAnd(e),
|
|
31
|
+
or: (...e: FilterEntry[]): FilterEntry => F.fOr(e),
|
|
32
|
+
} as const;
|