@ponder/client 0.14.13 → 0.15.1
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.js +115 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +142 -18
package/dist/index.js
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import {
|
|
3
|
+
Column,
|
|
4
|
+
SQL,
|
|
3
5
|
Table,
|
|
4
|
-
|
|
6
|
+
is,
|
|
7
|
+
isTable,
|
|
8
|
+
mapRelationalRow
|
|
5
9
|
} from "drizzle-orm";
|
|
6
10
|
import { isPgEnum } from "drizzle-orm/pg-core";
|
|
11
|
+
import { PgCountBuilder } from "drizzle-orm/pg-core/query-builders/count";
|
|
12
|
+
import { PgRelationalQuery } from "drizzle-orm/pg-core/query-builders/query";
|
|
13
|
+
import { PgRaw } from "drizzle-orm/pg-core/query-builders/raw";
|
|
7
14
|
import { drizzle } from "drizzle-orm/pg-proxy";
|
|
15
|
+
import { TypedQueryBuilder } from "drizzle-orm/query-builders/query-builder";
|
|
8
16
|
import { EventSource } from "eventsource";
|
|
9
17
|
import superjson from "superjson";
|
|
10
18
|
import {
|
|
@@ -41,7 +49,7 @@ import {
|
|
|
41
49
|
max,
|
|
42
50
|
min,
|
|
43
51
|
relations,
|
|
44
|
-
SQL
|
|
52
|
+
SQL as SQL2
|
|
45
53
|
} from "drizzle-orm";
|
|
46
54
|
import {
|
|
47
55
|
alias,
|
|
@@ -68,14 +76,12 @@ var compileQuery = (query) => {
|
|
|
68
76
|
return dialect.sqlToQuery(query.getSQL());
|
|
69
77
|
};
|
|
70
78
|
var createClient = (baseUrl, params = {}) => {
|
|
71
|
-
let sse;
|
|
72
|
-
let liveCount = 0;
|
|
73
79
|
const client = {
|
|
74
80
|
db: drizzle(
|
|
75
81
|
async (sql2, params2, method, typings) => {
|
|
76
82
|
const builtQuery = { sql: sql2, params: params2, typings };
|
|
77
83
|
const response = await fetch(getUrl(baseUrl, "db", builtQuery), {
|
|
78
|
-
method: "
|
|
84
|
+
method: "GET"
|
|
79
85
|
});
|
|
80
86
|
if (response.ok === false) {
|
|
81
87
|
const error = new Error(await response.text());
|
|
@@ -94,27 +100,93 @@ var createClient = (baseUrl, params = {}) => {
|
|
|
94
100
|
{ schema: params.schema, casing: "snake_case" }
|
|
95
101
|
),
|
|
96
102
|
live: (queryFn, onData, onError) => {
|
|
97
|
-
|
|
98
|
-
|
|
103
|
+
const noopDatabase2 = drizzle(() => Promise.resolve({ rows: [] }), {
|
|
104
|
+
schema: params.schema,
|
|
105
|
+
casing: "snake_case"
|
|
106
|
+
});
|
|
107
|
+
const queryPromise = queryFn(noopDatabase2);
|
|
108
|
+
if ("getSQL" in queryPromise === false) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
'"queryFn" must return SQL. You may have to remove `.execute()` from your query.'
|
|
111
|
+
);
|
|
99
112
|
}
|
|
100
|
-
const
|
|
101
|
-
|
|
113
|
+
const queryBuilder = queryPromise;
|
|
114
|
+
const query = compileQuery(queryBuilder);
|
|
115
|
+
const sse = new EventSource(getUrl(baseUrl, "live", query));
|
|
116
|
+
const onDataListener = async (event) => {
|
|
117
|
+
try {
|
|
118
|
+
const result = JSON.parse(event.data);
|
|
119
|
+
const drizzleShim = drizzle(
|
|
120
|
+
(_, __, method) => {
|
|
121
|
+
if (method === "all") {
|
|
122
|
+
return Promise.resolve({
|
|
123
|
+
...result,
|
|
124
|
+
rows: result.rows.map((row) => Object.values(row))
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return Promise.resolve(result);
|
|
128
|
+
},
|
|
129
|
+
{ schema: params.schema }
|
|
130
|
+
);
|
|
131
|
+
let data;
|
|
132
|
+
if (queryBuilder instanceof TypedQueryBuilder) {
|
|
133
|
+
const fields = queryBuilder._.selectedFields;
|
|
134
|
+
const orderedFields = orderSelectedFields(fields);
|
|
135
|
+
data = await drizzleShim._.session.prepareQuery(
|
|
136
|
+
query,
|
|
137
|
+
// @ts-ignore
|
|
138
|
+
orderedFields,
|
|
139
|
+
void 0,
|
|
140
|
+
false
|
|
141
|
+
).execute();
|
|
142
|
+
} else if (queryBuilder instanceof PgRelationalQuery) {
|
|
143
|
+
const selection = queryBuilder._toSQL().query.selection;
|
|
144
|
+
data = await drizzleShim._.session.prepareQuery(
|
|
145
|
+
query,
|
|
146
|
+
void 0,
|
|
147
|
+
void 0,
|
|
148
|
+
true,
|
|
149
|
+
(rawRows, mapColumnValue) => {
|
|
150
|
+
const rows = rawRows.map(
|
|
151
|
+
(row) => mapRelationalRow(
|
|
152
|
+
// @ts-ignore
|
|
153
|
+
queryBuilder.schema,
|
|
154
|
+
// @ts-ignore
|
|
155
|
+
queryBuilder.tableConfig,
|
|
156
|
+
// @ts-ignore
|
|
157
|
+
row,
|
|
158
|
+
selection,
|
|
159
|
+
mapColumnValue
|
|
160
|
+
)
|
|
161
|
+
);
|
|
162
|
+
if (queryBuilder.mode === "first") {
|
|
163
|
+
return rows[0];
|
|
164
|
+
}
|
|
165
|
+
return rows;
|
|
166
|
+
}
|
|
167
|
+
).execute();
|
|
168
|
+
} else if (queryBuilder instanceof PgRaw) {
|
|
169
|
+
data = await drizzleShim._.session.prepareQuery(query, void 0, void 0, false).execute();
|
|
170
|
+
} else if (queryBuilder instanceof PgCountBuilder) {
|
|
171
|
+
data = await drizzleShim._.session.count(queryBuilder.getSQL());
|
|
172
|
+
} else {
|
|
173
|
+
throw new Error("Unsupported query builder");
|
|
174
|
+
}
|
|
175
|
+
onData(data);
|
|
176
|
+
} catch (error) {
|
|
177
|
+
onError?.(error);
|
|
178
|
+
}
|
|
102
179
|
};
|
|
103
180
|
const onErrorListener = (_event) => {
|
|
104
181
|
onError?.(new Error("server disconnected"));
|
|
105
182
|
};
|
|
106
|
-
sse
|
|
107
|
-
sse
|
|
108
|
-
liveCount = liveCount + 1;
|
|
183
|
+
sse.addEventListener("message", onDataListener);
|
|
184
|
+
sse.addEventListener("error", onErrorListener);
|
|
109
185
|
return {
|
|
110
186
|
unsubscribe: () => {
|
|
111
|
-
sse
|
|
112
|
-
sse
|
|
113
|
-
|
|
114
|
-
if (liveCount === 0) {
|
|
115
|
-
sse?.close();
|
|
116
|
-
sse = void 0;
|
|
117
|
-
}
|
|
187
|
+
sse.removeEventListener("message", onDataListener);
|
|
188
|
+
sse.removeEventListener("error", onErrorListener);
|
|
189
|
+
sse.close();
|
|
118
190
|
}
|
|
119
191
|
};
|
|
120
192
|
},
|
|
@@ -134,8 +206,31 @@ var setDatabaseSchema = (schema, schemaName) => {
|
|
|
134
206
|
}
|
|
135
207
|
}
|
|
136
208
|
};
|
|
209
|
+
function orderSelectedFields(fields, pathPrefix) {
|
|
210
|
+
return Object.entries(fields).reduce(
|
|
211
|
+
(result, [name, field]) => {
|
|
212
|
+
if (typeof name !== "string") {
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
215
|
+
const newPath = pathPrefix ? [...pathPrefix, name] : [name];
|
|
216
|
+
if (is(field, Column) || is(field, SQL) || is(field, SQL.Aliased)) {
|
|
217
|
+
result.push({ path: newPath, field });
|
|
218
|
+
} else if (is(field, Table)) {
|
|
219
|
+
result.push(
|
|
220
|
+
...orderSelectedFields(field[Table.Symbol.Columns], newPath)
|
|
221
|
+
);
|
|
222
|
+
} else {
|
|
223
|
+
result.push(
|
|
224
|
+
...orderSelectedFields(field, newPath)
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
return result;
|
|
228
|
+
},
|
|
229
|
+
[]
|
|
230
|
+
);
|
|
231
|
+
}
|
|
137
232
|
export {
|
|
138
|
-
SQL,
|
|
233
|
+
SQL2 as SQL,
|
|
139
234
|
alias,
|
|
140
235
|
and,
|
|
141
236
|
asc,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {\n type QueryWithTypings,\n type SQLWrapper,\n Table,\n isTable,\n} from \"drizzle-orm\";\nimport { type PgDialect, isPgEnum } from \"drizzle-orm/pg-core\";\nimport { type PgRemoteDatabase, drizzle } from \"drizzle-orm/pg-proxy\";\nimport { EventSource } from \"eventsource\";\nimport superjson from \"superjson\";\n\ntype Schema = { [name: string]: unknown };\n\ntype Prettify<T> = {\n [K in keyof T]: T[K];\n} & {};\n\nexport type Status = {\n [chainName: string]: {\n id: number;\n block: { number: number; timestamp: number };\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 /**\n * Subscribe to live updates.\n *\n * @param queryFn - The query to subscribe to.\n * @param onData - The callback to call with each new query result\n * @param onError - The callback to call when an error occurs.\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 * client.live(\n * (db) => db.select().from(schema.account),\n * (result) => console.log(result),\n * (error) => console.error(error),\n * );\n * ```\n */\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\",\n query?: QueryWithTypings,\n) => {\n const url = new URL(baseUrl);\n url.pathname = `${url.pathname}/${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 params: { 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, method, 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 if (method === \"all\") {\n return {\n ...result,\n rows: result.rows.map((row: object) => Object.values(row)),\n };\n }\n\n return result;\n },\n { schema: params.schema, casing: \"snake_case\" },\n ),\n live: (queryFn, onData, onError) => {\n if (sse === undefined) {\n sse = new EventSource(getUrl(baseUrl, \"live\"));\n }\n\n const onDataListener = (_event: MessageEvent) => {\n queryFn(client.db).then(onData).catch(onError);\n };\n\n const onErrorListener = (_event: MessageEvent) => {\n onError?.(new Error(\"server disconnected\"));\n };\n\n sse?.addEventListener(\"message\", onDataListener);\n sse?.addEventListener(\"error\", onErrorListener);\n liveCount = liveCount + 1;\n\n return {\n unsubscribe: () => {\n sse?.removeEventListener(\"message\", onDataListener);\n sse?.removeEventListener(\"error\", onErrorListener);\n liveCount = liveCount - 1;\n if (liveCount === 0) {\n sse?.close();\n sse = undefined;\n }\n },\n };\n },\n getStatus: async () => {\n const response = await fetch(`${new URL(baseUrl).origin}/status`);\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 SQL,\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 const setDatabaseSchema = <T extends { [name: string]: unknown }>(\n schema: T,\n schemaName: string,\n) => {\n for (const table of Object.values(schema)) {\n if (isTable(table)) {\n // @ts-ignore\n table[Table.Symbol.Schema] = schemaName;\n } else if (isPgEnum(table)) {\n // @ts-ignore\n table.schema = schemaName;\n }\n }\n};\n"],"mappings":"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","names":["sql","params"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {\n type AnyColumn,\n Column,\n type QueryWithTypings,\n SQL,\n type SQLWrapper,\n type SelectedFieldsOrdered,\n Table,\n is,\n isTable,\n mapRelationalRow,\n} from \"drizzle-orm\";\nimport { type PgDialect, isPgEnum } from \"drizzle-orm/pg-core\";\nimport { PgCountBuilder } from \"drizzle-orm/pg-core/query-builders/count\";\nimport { PgRelationalQuery } from \"drizzle-orm/pg-core/query-builders/query\";\nimport { PgRaw } from \"drizzle-orm/pg-core/query-builders/raw\";\nimport { type PgRemoteDatabase, drizzle } from \"drizzle-orm/pg-proxy\";\nimport { TypedQueryBuilder } from \"drizzle-orm/query-builders/query-builder\";\nimport { EventSource } from \"eventsource\";\nimport superjson from \"superjson\";\n\ntype Schema = { [name: string]: unknown };\n\ntype Prettify<T> = {\n [K in keyof T]: T[K];\n} & {};\n\nexport type Status = {\n [chainName: string]: {\n id: number;\n block: { number: number; timestamp: number };\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 /**\n * Subscribe to live updates.\n *\n * @param queryFn - The query to subscribe to.\n * @param onData - The callback to call with each new query result\n * @param onError - The callback to call when an error occurs.\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 * client.live(\n * (db) => db.select().from(schema.account),\n * (result) => console.log(result),\n * (error) => console.error(error),\n * );\n * ```\n */\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\",\n query?: QueryWithTypings,\n) => {\n const url = new URL(baseUrl);\n url.pathname = `${url.pathname}/${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 params: { schema?: schema } = {},\n): Client<schema> => {\n const client: Client<schema> = {\n db: drizzle(\n async (sql, params, method, typings) => {\n const builtQuery = { sql, params, typings };\n const response = await fetch(getUrl(baseUrl, \"db\", builtQuery), {\n method: \"GET\",\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 if (method === \"all\") {\n return {\n ...result,\n rows: result.rows.map((row: object) => Object.values(row)),\n };\n }\n\n return result;\n },\n { schema: params.schema, casing: \"snake_case\" },\n ),\n live: (queryFn, onData, onError) => {\n const noopDatabase = drizzle(() => Promise.resolve({ rows: [] }), {\n schema: params.schema,\n casing: \"snake_case\",\n });\n\n const queryPromise = queryFn(noopDatabase);\n\n if (\"getSQL\" in queryPromise === false) {\n throw new Error(\n '\"queryFn\" must return SQL. You may have to remove `.execute()` from your query.',\n );\n }\n const queryBuilder = queryPromise as unknown as SQLWrapper;\n const query = compileQuery(queryBuilder);\n\n const sse = new EventSource(getUrl(baseUrl, \"live\", query));\n\n const onDataListener = async (event: MessageEvent) => {\n try {\n // @ts-ignore\n const result = JSON.parse(event.data);\n\n const drizzleShim = drizzle(\n (_, __, method) => {\n if (method === \"all\") {\n return Promise.resolve({\n ...result,\n rows: result.rows.map((row: object) => Object.values(row)),\n });\n }\n\n return Promise.resolve(result);\n },\n { schema: params.schema },\n );\n\n let data: unknown;\n\n if (queryBuilder instanceof TypedQueryBuilder) {\n const fields = queryBuilder._.selectedFields as Record<\n string,\n unknown\n >;\n const orderedFields = orderSelectedFields(fields);\n\n data = await drizzleShim._.session\n .prepareQuery(\n query,\n // @ts-ignore\n orderedFields,\n undefined,\n false,\n )\n .execute();\n } else if (queryBuilder instanceof PgRelationalQuery) {\n // @ts-ignore\n const selection = queryBuilder._toSQL().query.selection;\n data = await drizzleShim._.session\n .prepareQuery(\n query,\n undefined,\n undefined,\n true,\n (rawRows, mapColumnValue) => {\n const rows = rawRows.map((row) =>\n mapRelationalRow(\n // @ts-ignore\n queryBuilder.schema,\n // @ts-ignore\n queryBuilder.tableConfig,\n // @ts-ignore\n row,\n selection,\n mapColumnValue,\n ),\n );\n // @ts-ignore\n if (queryBuilder.mode === \"first\") {\n return rows[0];\n }\n return rows;\n },\n )\n .execute();\n } else if (queryBuilder instanceof PgRaw) {\n data = await drizzleShim._.session\n .prepareQuery(query, undefined, undefined, false)\n .execute();\n } else if (queryBuilder instanceof PgCountBuilder) {\n data = await drizzleShim._.session.count(queryBuilder.getSQL());\n } else {\n throw new Error(\"Unsupported query builder\");\n }\n\n // @ts-ignore\n onData(data);\n } catch (error) {\n onError?.(error as Error);\n }\n };\n\n const onErrorListener = (_event: MessageEvent) => {\n onError?.(new Error(\"server disconnected\"));\n };\n\n sse.addEventListener(\"message\", onDataListener);\n sse.addEventListener(\"error\", onErrorListener);\n\n return {\n unsubscribe: () => {\n sse.removeEventListener(\"message\", onDataListener);\n sse.removeEventListener(\"error\", onErrorListener);\n sse.close();\n },\n };\n },\n getStatus: async () => {\n const response = await fetch(`${new URL(baseUrl).origin}/status`);\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 SQL,\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 const setDatabaseSchema = <T extends { [name: string]: unknown }>(\n schema: T,\n schemaName: string,\n) => {\n for (const table of Object.values(schema)) {\n if (isTable(table)) {\n // @ts-ignore\n table[Table.Symbol.Schema] = schemaName;\n } else if (isPgEnum(table)) {\n // @ts-ignore\n table.schema = schemaName;\n }\n }\n};\n\nfunction orderSelectedFields<TColumn extends AnyColumn>(\n fields: Record<string, unknown>,\n pathPrefix?: string[],\n): SelectedFieldsOrdered<TColumn> {\n return Object.entries(fields).reduce<SelectedFieldsOrdered<AnyColumn>>(\n (result, [name, field]) => {\n if (typeof name !== \"string\") {\n return result;\n }\n\n const newPath = pathPrefix ? [...pathPrefix, name] : [name];\n if (is(field, Column) || is(field, SQL) || is(field, SQL.Aliased)) {\n result.push({ path: newPath, field });\n } else if (is(field, Table)) {\n result.push(\n // @ts-ignore\n ...orderSelectedFields(field[Table.Symbol.Columns], newPath),\n );\n } else {\n result.push(\n ...orderSelectedFields(field as Record<string, unknown>, newPath),\n );\n }\n return result;\n },\n [],\n ) as SelectedFieldsOrdered<TColumn>;\n}\n"],"mappings":"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","names":["SQL","sql","params","noopDatabase"]}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type AnyColumn,
|
|
3
|
+
Column,
|
|
2
4
|
type QueryWithTypings,
|
|
5
|
+
SQL,
|
|
3
6
|
type SQLWrapper,
|
|
7
|
+
type SelectedFieldsOrdered,
|
|
4
8
|
Table,
|
|
9
|
+
is,
|
|
5
10
|
isTable,
|
|
11
|
+
mapRelationalRow,
|
|
6
12
|
} from "drizzle-orm";
|
|
7
13
|
import { type PgDialect, isPgEnum } from "drizzle-orm/pg-core";
|
|
14
|
+
import { PgCountBuilder } from "drizzle-orm/pg-core/query-builders/count";
|
|
15
|
+
import { PgRelationalQuery } from "drizzle-orm/pg-core/query-builders/query";
|
|
16
|
+
import { PgRaw } from "drizzle-orm/pg-core/query-builders/raw";
|
|
8
17
|
import { type PgRemoteDatabase, drizzle } from "drizzle-orm/pg-proxy";
|
|
18
|
+
import { TypedQueryBuilder } from "drizzle-orm/query-builders/query-builder";
|
|
9
19
|
import { EventSource } from "eventsource";
|
|
10
20
|
import superjson from "superjson";
|
|
11
21
|
|
|
@@ -111,15 +121,12 @@ export const createClient = <schema extends Schema>(
|
|
|
111
121
|
baseUrl: string,
|
|
112
122
|
params: { schema?: schema } = {},
|
|
113
123
|
): Client<schema> => {
|
|
114
|
-
let sse: EventSource | undefined;
|
|
115
|
-
let liveCount = 0;
|
|
116
|
-
|
|
117
124
|
const client: Client<schema> = {
|
|
118
125
|
db: drizzle(
|
|
119
126
|
async (sql, params, method, typings) => {
|
|
120
127
|
const builtQuery = { sql, params, typings };
|
|
121
128
|
const response = await fetch(getUrl(baseUrl, "db", builtQuery), {
|
|
122
|
-
method: "
|
|
129
|
+
method: "GET",
|
|
123
130
|
});
|
|
124
131
|
|
|
125
132
|
if (response.ok === false) {
|
|
@@ -142,31 +149,119 @@ export const createClient = <schema extends Schema>(
|
|
|
142
149
|
{ schema: params.schema, casing: "snake_case" },
|
|
143
150
|
),
|
|
144
151
|
live: (queryFn, onData, onError) => {
|
|
145
|
-
|
|
146
|
-
|
|
152
|
+
const noopDatabase = drizzle(() => Promise.resolve({ rows: [] }), {
|
|
153
|
+
schema: params.schema,
|
|
154
|
+
casing: "snake_case",
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const queryPromise = queryFn(noopDatabase);
|
|
158
|
+
|
|
159
|
+
if ("getSQL" in queryPromise === false) {
|
|
160
|
+
throw new Error(
|
|
161
|
+
'"queryFn" must return SQL. You may have to remove `.execute()` from your query.',
|
|
162
|
+
);
|
|
147
163
|
}
|
|
164
|
+
const queryBuilder = queryPromise as unknown as SQLWrapper;
|
|
165
|
+
const query = compileQuery(queryBuilder);
|
|
166
|
+
|
|
167
|
+
const sse = new EventSource(getUrl(baseUrl, "live", query));
|
|
168
|
+
|
|
169
|
+
const onDataListener = async (event: MessageEvent) => {
|
|
170
|
+
try {
|
|
171
|
+
// @ts-ignore
|
|
172
|
+
const result = JSON.parse(event.data);
|
|
173
|
+
|
|
174
|
+
const drizzleShim = drizzle(
|
|
175
|
+
(_, __, method) => {
|
|
176
|
+
if (method === "all") {
|
|
177
|
+
return Promise.resolve({
|
|
178
|
+
...result,
|
|
179
|
+
rows: result.rows.map((row: object) => Object.values(row)),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return Promise.resolve(result);
|
|
184
|
+
},
|
|
185
|
+
{ schema: params.schema },
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
let data: unknown;
|
|
189
|
+
|
|
190
|
+
if (queryBuilder instanceof TypedQueryBuilder) {
|
|
191
|
+
const fields = queryBuilder._.selectedFields as Record<
|
|
192
|
+
string,
|
|
193
|
+
unknown
|
|
194
|
+
>;
|
|
195
|
+
const orderedFields = orderSelectedFields(fields);
|
|
148
196
|
|
|
149
|
-
|
|
150
|
-
|
|
197
|
+
data = await drizzleShim._.session
|
|
198
|
+
.prepareQuery(
|
|
199
|
+
query,
|
|
200
|
+
// @ts-ignore
|
|
201
|
+
orderedFields,
|
|
202
|
+
undefined,
|
|
203
|
+
false,
|
|
204
|
+
)
|
|
205
|
+
.execute();
|
|
206
|
+
} else if (queryBuilder instanceof PgRelationalQuery) {
|
|
207
|
+
// @ts-ignore
|
|
208
|
+
const selection = queryBuilder._toSQL().query.selection;
|
|
209
|
+
data = await drizzleShim._.session
|
|
210
|
+
.prepareQuery(
|
|
211
|
+
query,
|
|
212
|
+
undefined,
|
|
213
|
+
undefined,
|
|
214
|
+
true,
|
|
215
|
+
(rawRows, mapColumnValue) => {
|
|
216
|
+
const rows = rawRows.map((row) =>
|
|
217
|
+
mapRelationalRow(
|
|
218
|
+
// @ts-ignore
|
|
219
|
+
queryBuilder.schema,
|
|
220
|
+
// @ts-ignore
|
|
221
|
+
queryBuilder.tableConfig,
|
|
222
|
+
// @ts-ignore
|
|
223
|
+
row,
|
|
224
|
+
selection,
|
|
225
|
+
mapColumnValue,
|
|
226
|
+
),
|
|
227
|
+
);
|
|
228
|
+
// @ts-ignore
|
|
229
|
+
if (queryBuilder.mode === "first") {
|
|
230
|
+
return rows[0];
|
|
231
|
+
}
|
|
232
|
+
return rows;
|
|
233
|
+
},
|
|
234
|
+
)
|
|
235
|
+
.execute();
|
|
236
|
+
} else if (queryBuilder instanceof PgRaw) {
|
|
237
|
+
data = await drizzleShim._.session
|
|
238
|
+
.prepareQuery(query, undefined, undefined, false)
|
|
239
|
+
.execute();
|
|
240
|
+
} else if (queryBuilder instanceof PgCountBuilder) {
|
|
241
|
+
data = await drizzleShim._.session.count(queryBuilder.getSQL());
|
|
242
|
+
} else {
|
|
243
|
+
throw new Error("Unsupported query builder");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// @ts-ignore
|
|
247
|
+
onData(data);
|
|
248
|
+
} catch (error) {
|
|
249
|
+
onError?.(error as Error);
|
|
250
|
+
}
|
|
151
251
|
};
|
|
152
252
|
|
|
153
253
|
const onErrorListener = (_event: MessageEvent) => {
|
|
154
254
|
onError?.(new Error("server disconnected"));
|
|
155
255
|
};
|
|
156
256
|
|
|
157
|
-
sse
|
|
158
|
-
sse
|
|
159
|
-
liveCount = liveCount + 1;
|
|
257
|
+
sse.addEventListener("message", onDataListener);
|
|
258
|
+
sse.addEventListener("error", onErrorListener);
|
|
160
259
|
|
|
161
260
|
return {
|
|
162
261
|
unsubscribe: () => {
|
|
163
|
-
sse
|
|
164
|
-
sse
|
|
165
|
-
|
|
166
|
-
if (liveCount === 0) {
|
|
167
|
-
sse?.close();
|
|
168
|
-
sse = undefined;
|
|
169
|
-
}
|
|
262
|
+
sse.removeEventListener("message", onDataListener);
|
|
263
|
+
sse.removeEventListener("error", onErrorListener);
|
|
264
|
+
sse.close();
|
|
170
265
|
},
|
|
171
266
|
};
|
|
172
267
|
},
|
|
@@ -241,3 +336,32 @@ export const setDatabaseSchema = <T extends { [name: string]: unknown }>(
|
|
|
241
336
|
}
|
|
242
337
|
}
|
|
243
338
|
};
|
|
339
|
+
|
|
340
|
+
function orderSelectedFields<TColumn extends AnyColumn>(
|
|
341
|
+
fields: Record<string, unknown>,
|
|
342
|
+
pathPrefix?: string[],
|
|
343
|
+
): SelectedFieldsOrdered<TColumn> {
|
|
344
|
+
return Object.entries(fields).reduce<SelectedFieldsOrdered<AnyColumn>>(
|
|
345
|
+
(result, [name, field]) => {
|
|
346
|
+
if (typeof name !== "string") {
|
|
347
|
+
return result;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const newPath = pathPrefix ? [...pathPrefix, name] : [name];
|
|
351
|
+
if (is(field, Column) || is(field, SQL) || is(field, SQL.Aliased)) {
|
|
352
|
+
result.push({ path: newPath, field });
|
|
353
|
+
} else if (is(field, Table)) {
|
|
354
|
+
result.push(
|
|
355
|
+
// @ts-ignore
|
|
356
|
+
...orderSelectedFields(field[Table.Symbol.Columns], newPath),
|
|
357
|
+
);
|
|
358
|
+
} else {
|
|
359
|
+
result.push(
|
|
360
|
+
...orderSelectedFields(field as Record<string, unknown>, newPath),
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
return result;
|
|
364
|
+
},
|
|
365
|
+
[],
|
|
366
|
+
) as SelectedFieldsOrdered<TColumn>;
|
|
367
|
+
}
|