@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/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;AAE7C,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,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,QAAQ,IAAI,CAAC,EAAE,EAAE,CAAC;QACjD,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"}
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"}
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAgBA,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAOjD"}
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 '""';
@@ -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,SAAS,iBAAiB,CAAC,CAAU;IACnC,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,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,YAAY,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,cAAc,CAAC,CAAS,EAAE,OAAe;IAChD,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC1B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC;AAC9D,CAAC"}
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 : never;
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 {
@@ -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,GACN,KAAK,CAAC;AAGZ,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"}
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.1",
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": ["dist", "src", "README.md"],
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 "./types.js";
2
- import { QueryBuilder, type ExecContext } from "./query.js";
3
- import { InsertBuilder, UpdateBuilder, DeleteBuilder } from "./mutation.js";
4
- import { RpcCaller } from "./rpc.js";
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 — v0 covers the 80% of queries: equality, comparison, set membership,
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 to URLSearchParams as `column=op.value`.
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 "./serialize.js";
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
- export function fEq(column: string, value: unknown): FilterEntry {
16
- return { column, expr: `eq.${serializeValue(value)}` };
17
- }
18
- export function fNeq(column: string, value: unknown): FilterEntry {
19
- return { column, expr: `neq.${serializeValue(value)}` };
20
- }
21
- export function fGt(column: string, value: unknown): FilterEntry {
22
- return { column, expr: `gt.${serializeValue(value)}` };
23
- }
24
- export function fGte(column: string, value: unknown): FilterEntry {
25
- return { column, expr: `gte.${serializeValue(value)}` };
26
- }
27
- export function fLt(column: string, value: unknown): FilterEntry {
28
- return { column, expr: `lt.${serializeValue(value)}` };
29
- }
30
- export function fLte(column: string, value: unknown): FilterEntry {
31
- return { column, expr: `lte.${serializeValue(value)}` };
32
- }
33
- export function fLike(column: string, pattern: string): FilterEntry {
34
- return { column, expr: `like.${serializeValue(pattern)}` };
35
- }
36
- export function fIlike(column: string, pattern: string): FilterEntry {
37
- return { column, expr: `ilike.${serializeValue(pattern)}` };
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
- export function fIn(column: string, values: readonly unknown[]): FilterEntry {
40
- return { column, expr: `in.${serializeValue(values)}` };
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
- // `is` is the correct way to check NULL / boolean — `eq.null` is a no-op in SQL.
43
- export function fIs(column: string, value: IsValue): FilterEntry {
44
- return { column, expr: `is.${value}` };
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
- // Negate any single-op filter: PostgREST prefixes the operator with `not.`
47
- // e.g. `column=not.is.null`, `column=not.eq.5`.
48
- export function fNot(entry: FilterEntry): FilterEntry {
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 "./client.js";
2
- export { CloudRestError } from "./errors.js";
3
- export { QueryBuilder } from "./query.js";
4
- export { InsertBuilder, UpdateBuilder, DeleteBuilder } from "./mutation.js";
5
- export { RpcCaller } from "./rpc.js";
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 "./types.js";
13
- export type { OrderOptions } from "./query.js";
14
- export type { IsValue } from "./filter.js";
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;