@ponder/client 0.9.0-next.1 → 0.9.0-next.11

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/index.d.ts CHANGED
@@ -1,22 +1,51 @@
1
+ import { SQLWrapper, QueryWithTypings } from 'drizzle-orm';
2
+ export { and, asc, avg, avgDistinct, between, count, countDistinct, desc, eq, exists, gt, gte, ilike, inArray, isNotNull, isNull, like, lt, lte, max, min, ne, not, notBetween, notExists, notIlike, notInArray, notLike, or, relations, sql, sum, sumDistinct } from 'drizzle-orm';
1
3
  import { PgRemoteDatabase } from 'drizzle-orm/pg-proxy';
2
- export { and, asc, avg, avgDistinct, between, count, countDistinct, desc, eq, exists, gt, gte, inArray, isNotNull, isNull, like, lt, lte, max, min, ne, not, notBetween, notExists, notIlike, notInArray, or, relations, sql, sum, sumDistinct } from 'drizzle-orm';
3
4
  export { alias, except, exceptAll, intersect, intersectAll, union, unionAll } from 'drizzle-orm/pg-core';
4
5
 
5
- declare const setDatabaseSchema: <T extends {
6
- [name: string]: unknown;
7
- }>(schema: T, schemaName: string) => T;
8
-
9
6
  type Schema = {
10
7
  [name: string]: unknown;
11
8
  };
12
9
  type Prettify<T> = {
13
10
  [K in keyof T]: T[K];
14
11
  } & {};
12
+ type Status = {
13
+ [network: string]: {
14
+ block: {
15
+ number: number;
16
+ timestamp: number;
17
+ } | null;
18
+ ready: boolean;
19
+ };
20
+ };
21
+ type ClientDb<schema extends Schema = Schema> = Prettify<Omit<PgRemoteDatabase<schema>, "insert" | "update" | "delete" | "transaction" | "refreshMaterializedView" | "_">>;
15
22
  type Client<schema extends Schema = Schema> = {
16
- db: Prettify<Omit<PgRemoteDatabase<schema>, "insert" | "update" | "delete" | "transaction" | "refreshMaterializedView" | "_">>;
23
+ /** Query the database. */
24
+ db: ClientDb<schema>;
25
+ /** Subscribe to live updates. */
26
+ live: <result>(queryFn: (db: ClientDb<schema>) => Promise<result>, onData: (result: result) => void, onError?: (error: Error) => void) => {
27
+ unsubscribe: () => void;
28
+ };
29
+ /** Get the status of all chains. */
30
+ getStatus: () => Promise<Status>;
17
31
  };
18
- declare const createClient: <schema extends Schema>(url: string, { schema }: {
32
+ declare const compileQuery: (query: SQLWrapper) => QueryWithTypings;
33
+ /**
34
+ * Create a client for querying Ponder apps.
35
+ *
36
+ * @param baseUrl - The URL of the Ponder app.
37
+ * @param schema - The schema of the Ponder app.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * import { createClient } from "@ponder/client";
42
+ * import * as schema from "../ponder.schema";
43
+ *
44
+ * const client = createClient("https://.../sql", { schema });
45
+ * ```
46
+ */
47
+ declare const createClient: <schema extends Schema>(baseUrl: string, { schema }: {
19
48
  schema: schema;
20
49
  }) => Client<schema>;
21
50
 
22
- export { type Client, createClient, setDatabaseSchema };
51
+ export { type Client, type Status, compileQuery, createClient };
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/index.ts
2
2
  import { drizzle } from "drizzle-orm/pg-proxy";
3
+ import superjson from "superjson";
3
4
  import {
4
5
  sql,
5
6
  eq,
@@ -17,6 +18,8 @@ import {
17
18
  between,
18
19
  notBetween,
19
20
  like,
21
+ notLike,
22
+ ilike,
20
23
  notIlike,
21
24
  not,
22
25
  asc,
@@ -42,32 +45,89 @@ import {
42
45
  except,
43
46
  exceptAll
44
47
  } from "drizzle-orm/pg-core";
45
-
46
- // src/setDatabaseSchema.ts
47
- import { Table, is } from "drizzle-orm";
48
- var Schema = Symbol.for("drizzle:Schema");
49
- var setDatabaseSchema = (schema, schemaName) => {
50
- for (const table of Object.values(schema)) {
51
- if (is(table, Table)) {
52
- table[Schema] = schemaName;
53
- }
48
+ var getEventSource = async () => {
49
+ let SSE;
50
+ if (typeof window === "undefined") {
51
+ const undici = await import(
52
+ /* webpackIgnore: true */
53
+ "undici"
54
+ );
55
+ SSE = undici.EventSource;
56
+ } else {
57
+ SSE = EventSource;
54
58
  }
55
- return schema;
59
+ return SSE;
56
60
  };
57
-
58
- // src/index.ts
59
- var createClient = (url, { schema }) => {
60
- const db = drizzle(
61
- async (sql2, params, method, typings) => {
62
- const result = await fetch(`${url}/client`, {
63
- method: "POST",
64
- body: JSON.stringify({ sql: sql2, params, method, typings })
65
- });
66
- return await result.json();
61
+ var getUrl = (baseUrl, method, query) => {
62
+ const url = new URL(`${baseUrl}/${method}`);
63
+ if (query) {
64
+ url.searchParams.set("sql", superjson.stringify(query));
65
+ }
66
+ return url;
67
+ };
68
+ var noopDatabase = drizzle(() => Promise.resolve({ rows: [] }), {
69
+ casing: "snake_case"
70
+ });
71
+ var dialect = noopDatabase.dialect;
72
+ var compileQuery = (query) => {
73
+ return dialect.sqlToQuery(query.getSQL());
74
+ };
75
+ var createClient = (baseUrl, { schema }) => {
76
+ let sse;
77
+ let liveCount = 0;
78
+ const client = {
79
+ db: drizzle(
80
+ async (sql2, params, _, typings) => {
81
+ const builtQuery = { sql: sql2, params, typings };
82
+ const response = await fetch(getUrl(baseUrl, "db", builtQuery), {
83
+ method: "POST"
84
+ });
85
+ if (response.ok === false) {
86
+ const error = new Error(await response.text());
87
+ error.stack = void 0;
88
+ throw error;
89
+ }
90
+ const result = await response.json();
91
+ return {
92
+ ...result,
93
+ rows: result.rows.map((row) => Object.values(row))
94
+ };
95
+ },
96
+ { schema, casing: "snake_case" }
97
+ ),
98
+ live: (queryFn, onData, onError) => {
99
+ const addEventListeners = () => {
100
+ sse.addEventListener("message", () => {
101
+ queryFn(client.db).then(onData).catch(onError);
102
+ });
103
+ sse.addEventListener("error", () => {
104
+ onError?.(new Error("server disconnected"));
105
+ });
106
+ };
107
+ liveCount++;
108
+ if (sse === void 0) {
109
+ getEventSource().then((SSE) => {
110
+ sse = new SSE(getUrl(baseUrl, "live"));
111
+ addEventListeners();
112
+ });
113
+ } else {
114
+ addEventListeners();
115
+ }
116
+ return {
117
+ unsubscribe: () => {
118
+ if (--liveCount === 0)
119
+ sse?.close();
120
+ }
121
+ };
67
122
  },
68
- { schema, casing: "snake_case" }
69
- );
70
- return { db };
123
+ getStatus: async () => {
124
+ const response = await fetch(getUrl(baseUrl, "status"), {
125
+ method: "POST"
126
+ });
127
+ return response.json();
128
+ }
129
+ };
130
+ return client;
71
131
  };
72
132
  export {
73
133
  alias,
@@ -76,6 +136,7 @@ export {
76
136
  avg,
77
137
  avgDistinct,
78
138
  between,
139
+ compileQuery,
79
140
  count,
80
141
  countDistinct,
81
142
  createClient,
@@ -86,6 +147,7 @@ export {
86
147
  exists,
87
148
  gt,
88
149
  gte,
150
+ ilike,
89
151
  inArray,
90
152
  intersect,
91
153
  intersectAll,
@@ -102,9 +164,9 @@ export {
102
164
  notExists,
103
165
  notIlike,
104
166
  notInArray,
167
+ notLike,
105
168
  or,
106
169
  relations,
107
- setDatabaseSchema,
108
170
  sql,
109
171
  sum,
110
172
  sumDistinct,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/setDatabaseSchema.ts"],"sourcesContent":["import { type PgRemoteDatabase, drizzle } from \"drizzle-orm/pg-proxy\";\n\ntype Schema = { [name: string]: unknown };\n\ntype Prettify<T> = {\n [K in keyof T]: T[K];\n} & {};\n\nexport type Client<schema extends Schema = Schema> = {\n db: Prettify<\n Omit<\n PgRemoteDatabase<schema>,\n | \"insert\"\n | \"update\"\n | \"delete\"\n | \"transaction\"\n | \"refreshMaterializedView\"\n | \"_\"\n >\n >;\n};\n\nexport const createClient = <schema extends Schema>(\n url: string,\n { schema }: { schema: schema },\n): Client<schema> => {\n const db = drizzle(\n async (sql, params, method, typings) => {\n const result = await fetch(`${url}/client`, {\n method: \"POST\",\n body: JSON.stringify({ sql, params, method, typings }),\n });\n\n return await result.json();\n },\n { schema, casing: \"snake_case\" },\n );\n\n return { db };\n};\n\nexport {\n sql,\n eq,\n gt,\n gte,\n lt,\n lte,\n ne,\n isNull,\n isNotNull,\n inArray,\n notInArray,\n exists,\n notExists,\n between,\n notBetween,\n like,\n notIlike,\n not,\n asc,\n desc,\n and,\n or,\n count,\n countDistinct,\n avg,\n avgDistinct,\n sum,\n sumDistinct,\n max,\n min,\n relations,\n} from \"drizzle-orm\";\n\nexport {\n alias,\n union,\n unionAll,\n intersect,\n intersectAll,\n except,\n exceptAll,\n} from \"drizzle-orm/pg-core\";\n\nexport { setDatabaseSchema } from \"./setDatabaseSchema.js\";\n","import { Table, is } from \"drizzle-orm\";\n\nconst Schema = Symbol.for(\"drizzle:Schema\");\n\nexport const setDatabaseSchema = <T extends { [name: string]: unknown }>(\n schema: T,\n schemaName: string,\n): T => {\n for (const table of Object.values(schema)) {\n if (is(table, Table)) {\n // @ts-ignore\n table[Schema] = schemaName;\n }\n }\n return schema;\n};\n"],"mappings":";AAAA,SAAgC,eAAe;AAyC/C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACnFP,SAAS,OAAO,UAAU;AAE1B,IAAM,SAAS,OAAO,IAAI,gBAAgB;AAEnC,IAAM,oBAAoB,CAC/B,QACA,eACM;AACN,aAAW,SAAS,OAAO,OAAO,MAAM,GAAG;AACzC,QAAI,GAAG,OAAO,KAAK,GAAG;AAEpB,YAAM,MAAM,IAAI;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AACT;;;ADOO,IAAM,eAAe,CAC1B,KACA,EAAE,OAAO,MACU;AACnB,QAAM,KAAK;AAAA,IACT,OAAOA,MAAK,QAAQ,QAAQ,YAAY;AACtC,YAAM,SAAS,MAAM,MAAM,GAAG,GAAG,WAAW;AAAA,QAC1C,QAAQ;AAAA,QACR,MAAM,KAAK,UAAU,EAAE,KAAAA,MAAK,QAAQ,QAAQ,QAAQ,CAAC;AAAA,MACvD,CAAC;AAED,aAAO,MAAM,OAAO,KAAK;AAAA,IAC3B;AAAA,IACA,EAAE,QAAQ,QAAQ,aAAa;AAAA,EACjC;AAEA,SAAO,EAAE,GAAG;AACd;","names":["sql"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { QueryWithTypings, SQLWrapper } from \"drizzle-orm\";\nimport type { PgDialect } from \"drizzle-orm/pg-core\";\nimport { type PgRemoteDatabase, drizzle } from \"drizzle-orm/pg-proxy\";\nimport superjson from \"superjson\";\n\nconst getEventSource = async () => {\n let SSE: typeof EventSource;\n if (typeof window === \"undefined\") {\n const undici = await import(/* webpackIgnore: true */ \"undici\");\n // @ts-ignore\n SSE = undici.EventSource;\n } else {\n SSE = EventSource;\n }\n\n return SSE;\n};\n\ntype Schema = { [name: string]: unknown };\n\ntype Prettify<T> = {\n [K in keyof T]: T[K];\n} & {};\n\nexport type Status = {\n [network: string]: {\n block: { number: number; timestamp: number } | null;\n ready: boolean;\n };\n};\n\ntype ClientDb<schema extends Schema = Schema> = Prettify<\n Omit<\n PgRemoteDatabase<schema>,\n | \"insert\"\n | \"update\"\n | \"delete\"\n | \"transaction\"\n | \"refreshMaterializedView\"\n | \"_\"\n >\n>;\n\nexport type Client<schema extends Schema = Schema> = {\n /** Query the database. */\n db: ClientDb<schema>;\n /** Subscribe to live updates. */\n live: <result>(\n queryFn: (db: ClientDb<schema>) => Promise<result>,\n onData: (result: result) => void,\n onError?: (error: Error) => void,\n ) => {\n unsubscribe: () => void;\n };\n /** Get the status of all chains. */\n getStatus: () => Promise<Status>;\n};\n\nconst getUrl = (\n baseUrl: string,\n method: \"live\" | \"db\" | \"status\",\n query?: QueryWithTypings,\n) => {\n const url = new URL(`${baseUrl}/${method}`);\n if (query) {\n url.searchParams.set(\"sql\", superjson.stringify(query));\n }\n return url;\n};\n\nconst noopDatabase = drizzle(() => Promise.resolve({ rows: [] }), {\n casing: \"snake_case\",\n});\n\n// @ts-ignore\nconst dialect: PgDialect = noopDatabase.dialect;\n\nexport const compileQuery = (query: SQLWrapper) => {\n return dialect.sqlToQuery(query.getSQL());\n};\n\n/**\n * Create a client for querying Ponder apps.\n *\n * @param baseUrl - The URL of the Ponder app.\n * @param schema - The schema of the Ponder app.\n *\n * @example\n * ```ts\n * import { createClient } from \"@ponder/client\";\n * import * as schema from \"../ponder.schema\";\n *\n * const client = createClient(\"https://.../sql\", { schema });\n * ```\n */\nexport const createClient = <schema extends Schema>(\n baseUrl: string,\n { schema }: { schema: schema },\n): Client<schema> => {\n let sse: EventSource | undefined;\n let liveCount = 0;\n\n const client: Client<schema> = {\n db: drizzle(\n async (sql, params, _, typings) => {\n const builtQuery = { sql, params, typings };\n const response = await fetch(getUrl(baseUrl, \"db\", builtQuery), {\n method: \"POST\",\n });\n\n if (response.ok === false) {\n const error = new Error(await response.text());\n error.stack = undefined;\n throw error;\n }\n\n const result = await response.json();\n\n return {\n ...result,\n rows: result.rows.map((row: object) => Object.values(row)),\n };\n },\n { schema, casing: \"snake_case\" },\n ),\n live: (queryFn, onData, onError) => {\n const addEventListeners = () => {\n sse!.addEventListener(\"message\", () => {\n queryFn(client.db).then(onData).catch(onError);\n });\n\n sse!.addEventListener(\"error\", () => {\n onError?.(new Error(\"server disconnected\"));\n });\n };\n\n liveCount++;\n if (sse === undefined) {\n getEventSource().then((SSE) => {\n sse = new SSE(getUrl(baseUrl, \"live\"));\n addEventListeners();\n });\n } else {\n addEventListeners();\n }\n\n return {\n unsubscribe: () => {\n if (--liveCount === 0) sse?.close();\n },\n };\n },\n getStatus: async () => {\n const response = await fetch(getUrl(baseUrl, \"status\"), {\n method: \"POST\",\n });\n\n return response.json();\n },\n };\n\n return client;\n};\n\nexport {\n sql,\n eq,\n gt,\n gte,\n lt,\n lte,\n ne,\n isNull,\n isNotNull,\n inArray,\n notInArray,\n exists,\n notExists,\n between,\n notBetween,\n like,\n notLike,\n ilike,\n notIlike,\n not,\n asc,\n desc,\n and,\n or,\n count,\n countDistinct,\n avg,\n avgDistinct,\n sum,\n sumDistinct,\n max,\n min,\n relations,\n} from \"drizzle-orm\";\n\nexport {\n alias,\n union,\n unionAll,\n intersect,\n intersectAll,\n except,\n exceptAll,\n} from \"drizzle-orm/pg-core\";\n"],"mappings":"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","names":["sql"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ponder/client",
3
- "version": "0.9.0-next.1",
3
+ "version": "0.9.0-next.11",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -26,6 +26,9 @@
26
26
  "import": "./dist/index.js"
27
27
  }
28
28
  },
29
+ "browser": {
30
+ "undici": false
31
+ },
29
32
  "peerDependencies": {
30
33
  "typescript": ">=5.0.4"
31
34
  },
@@ -35,7 +38,9 @@
35
38
  }
36
39
  },
37
40
  "dependencies": {
38
- "drizzle-orm": "0.36.4"
41
+ "drizzle-orm": "0.36.4",
42
+ "superjson": "^2.2.2",
43
+ "undici": "^7.2.0"
39
44
  },
40
45
  "devDependencies": {
41
46
  "tsup": "^8.0.1"
package/src/index.ts CHANGED
@@ -1,4 +1,20 @@
1
+ import type { QueryWithTypings, SQLWrapper } from "drizzle-orm";
2
+ import type { PgDialect } from "drizzle-orm/pg-core";
1
3
  import { type PgRemoteDatabase, drizzle } from "drizzle-orm/pg-proxy";
4
+ import superjson from "superjson";
5
+
6
+ const getEventSource = async () => {
7
+ let SSE: typeof EventSource;
8
+ if (typeof window === "undefined") {
9
+ const undici = await import(/* webpackIgnore: true */ "undici");
10
+ // @ts-ignore
11
+ SSE = undici.EventSource;
12
+ } else {
13
+ SSE = EventSource;
14
+ }
15
+
16
+ return SSE;
17
+ };
2
18
 
3
19
  type Schema = { [name: string]: unknown };
4
20
 
@@ -6,37 +22,144 @@ type Prettify<T> = {
6
22
  [K in keyof T]: T[K];
7
23
  } & {};
8
24
 
25
+ export type Status = {
26
+ [network: string]: {
27
+ block: { number: number; timestamp: number } | null;
28
+ ready: boolean;
29
+ };
30
+ };
31
+
32
+ type ClientDb<schema extends Schema = Schema> = Prettify<
33
+ Omit<
34
+ PgRemoteDatabase<schema>,
35
+ | "insert"
36
+ | "update"
37
+ | "delete"
38
+ | "transaction"
39
+ | "refreshMaterializedView"
40
+ | "_"
41
+ >
42
+ >;
43
+
9
44
  export type Client<schema extends Schema = Schema> = {
10
- db: Prettify<
11
- Omit<
12
- PgRemoteDatabase<schema>,
13
- | "insert"
14
- | "update"
15
- | "delete"
16
- | "transaction"
17
- | "refreshMaterializedView"
18
- | "_"
19
- >
20
- >;
45
+ /** Query the database. */
46
+ db: ClientDb<schema>;
47
+ /** Subscribe to live updates. */
48
+ live: <result>(
49
+ queryFn: (db: ClientDb<schema>) => Promise<result>,
50
+ onData: (result: result) => void,
51
+ onError?: (error: Error) => void,
52
+ ) => {
53
+ unsubscribe: () => void;
54
+ };
55
+ /** Get the status of all chains. */
56
+ getStatus: () => Promise<Status>;
21
57
  };
22
58
 
59
+ const getUrl = (
60
+ baseUrl: string,
61
+ method: "live" | "db" | "status",
62
+ query?: QueryWithTypings,
63
+ ) => {
64
+ const url = new URL(`${baseUrl}/${method}`);
65
+ if (query) {
66
+ url.searchParams.set("sql", superjson.stringify(query));
67
+ }
68
+ return url;
69
+ };
70
+
71
+ const noopDatabase = drizzle(() => Promise.resolve({ rows: [] }), {
72
+ casing: "snake_case",
73
+ });
74
+
75
+ // @ts-ignore
76
+ const dialect: PgDialect = noopDatabase.dialect;
77
+
78
+ export const compileQuery = (query: SQLWrapper) => {
79
+ return dialect.sqlToQuery(query.getSQL());
80
+ };
81
+
82
+ /**
83
+ * Create a client for querying Ponder apps.
84
+ *
85
+ * @param baseUrl - The URL of the Ponder app.
86
+ * @param schema - The schema of the Ponder app.
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * import { createClient } from "@ponder/client";
91
+ * import * as schema from "../ponder.schema";
92
+ *
93
+ * const client = createClient("https://.../sql", { schema });
94
+ * ```
95
+ */
23
96
  export const createClient = <schema extends Schema>(
24
- url: string,
97
+ baseUrl: string,
25
98
  { schema }: { schema: schema },
26
99
  ): Client<schema> => {
27
- const db = drizzle(
28
- async (sql, params, method, typings) => {
29
- const result = await fetch(`${url}/client`, {
100
+ let sse: EventSource | undefined;
101
+ let liveCount = 0;
102
+
103
+ const client: Client<schema> = {
104
+ db: drizzle(
105
+ async (sql, params, _, typings) => {
106
+ const builtQuery = { sql, params, typings };
107
+ const response = await fetch(getUrl(baseUrl, "db", builtQuery), {
108
+ method: "POST",
109
+ });
110
+
111
+ if (response.ok === false) {
112
+ const error = new Error(await response.text());
113
+ error.stack = undefined;
114
+ throw error;
115
+ }
116
+
117
+ const result = await response.json();
118
+
119
+ return {
120
+ ...result,
121
+ rows: result.rows.map((row: object) => Object.values(row)),
122
+ };
123
+ },
124
+ { schema, casing: "snake_case" },
125
+ ),
126
+ live: (queryFn, onData, onError) => {
127
+ const addEventListeners = () => {
128
+ sse!.addEventListener("message", () => {
129
+ queryFn(client.db).then(onData).catch(onError);
130
+ });
131
+
132
+ sse!.addEventListener("error", () => {
133
+ onError?.(new Error("server disconnected"));
134
+ });
135
+ };
136
+
137
+ liveCount++;
138
+ if (sse === undefined) {
139
+ getEventSource().then((SSE) => {
140
+ sse = new SSE(getUrl(baseUrl, "live"));
141
+ addEventListeners();
142
+ });
143
+ } else {
144
+ addEventListeners();
145
+ }
146
+
147
+ return {
148
+ unsubscribe: () => {
149
+ if (--liveCount === 0) sse?.close();
150
+ },
151
+ };
152
+ },
153
+ getStatus: async () => {
154
+ const response = await fetch(getUrl(baseUrl, "status"), {
30
155
  method: "POST",
31
- body: JSON.stringify({ sql, params, method, typings }),
32
156
  });
33
157
 
34
- return await result.json();
158
+ return response.json();
35
159
  },
36
- { schema, casing: "snake_case" },
37
- );
160
+ };
38
161
 
39
- return { db };
162
+ return client;
40
163
  };
41
164
 
42
165
  export {
@@ -56,6 +179,8 @@ export {
56
179
  between,
57
180
  notBetween,
58
181
  like,
182
+ notLike,
183
+ ilike,
59
184
  notIlike,
60
185
  not,
61
186
  asc,
@@ -82,5 +207,3 @@ export {
82
207
  except,
83
208
  exceptAll,
84
209
  } from "drizzle-orm/pg-core";
85
-
86
- export { setDatabaseSchema } from "./setDatabaseSchema.js";
@@ -1,16 +0,0 @@
1
- import { Table, is } from "drizzle-orm";
2
-
3
- const Schema = Symbol.for("drizzle:Schema");
4
-
5
- export const setDatabaseSchema = <T extends { [name: string]: unknown }>(
6
- schema: T,
7
- schemaName: string,
8
- ): T => {
9
- for (const table of Object.values(schema)) {
10
- if (is(table, Table)) {
11
- // @ts-ignore
12
- table[Schema] = schemaName;
13
- }
14
- }
15
- return schema;
16
- };