@onreza/sqlx-js 0.1.0 → 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.
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync, statSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
- import { PgClient, parseDatabaseUrl } from "./pg/wire.js";
3
+ import { PgClient, parseDatabaseUrl, PgError } from "./pg/wire.js";
4
4
  import { applyPending, acquireMigrateLock, releaseMigrateLock, DEFAULT_MIGRATE_LOCK_KEY } from "./commands/migrate.js";
5
5
  const SUFFIX = /[!?]$/;
6
6
  function renameRows(rows) {
@@ -148,6 +148,63 @@ export class TooManyRowsError extends Error {
148
148
  this.actual = actual;
149
149
  }
150
150
  }
151
+ // SQLSTATE is exactly five characters from [0-9A-Z]; lowercase or other shapes
152
+ // are never valid, so transport codes like "EPIPE" must not match on shape alone.
153
+ const SQLSTATE = /^[0-9A-Z]{5}$/;
154
+ function firstString(...candidates) {
155
+ for (const value of candidates) {
156
+ if (typeof value === "string" && value.length > 0)
157
+ return value;
158
+ }
159
+ return undefined;
160
+ }
161
+ export function toPgError(e) {
162
+ if (e instanceof PgError)
163
+ return e;
164
+ if (e === null || typeof e !== "object")
165
+ return null;
166
+ const o = e;
167
+ const code = typeof o.code === "string" ? o.code : undefined;
168
+ // `_name` variants are postgres.js; bare forms are node-postgres. Bare
169
+ // `column`/`schema`/`table` can also collide with runtime-added Error
170
+ // properties, so we read namespaced variants first and only accept strings.
171
+ const severity = firstString(o.severity, o.severity_local);
172
+ // A genuine database error is identified by the driver's branded name
173
+ // (Postgres.js) or by a SQLSTATE-shaped code paired with a severity. Transport
174
+ // and system errors (EPIPE, ECONNREFUSED, CONNECTION_ENDED) carry neither, so
175
+ // they pass through untouched instead of masquerading as a PgError.
176
+ const isDatabaseError = o.name === "PostgresError" ||
177
+ (code !== undefined && SQLSTATE.test(code) && severity !== undefined);
178
+ if (!isDatabaseError)
179
+ return null;
180
+ const fields = {};
181
+ if (typeof o.message === "string" && o.message.length > 0)
182
+ fields.M = o.message;
183
+ if (code)
184
+ fields.C = code;
185
+ if (typeof o.detail === "string" && o.detail.length > 0)
186
+ fields.D = o.detail;
187
+ if (typeof o.hint === "string" && o.hint.length > 0)
188
+ fields.H = o.hint;
189
+ const position = firstString(o.position) ?? (typeof o.position === "number" && Number.isFinite(o.position) ? String(o.position) : undefined);
190
+ if (position)
191
+ fields.P = position;
192
+ if (severity)
193
+ fields.S = severity;
194
+ const table = firstString(o.table_name, o.table);
195
+ if (table)
196
+ fields.t = table;
197
+ const column = firstString(o.column_name, o.column);
198
+ if (column)
199
+ fields.c = column;
200
+ const constraint = firstString(o.constraint_name, o.constraint);
201
+ if (constraint)
202
+ fields.n = constraint;
203
+ const schema = firstString(o.schema_name, o.schema);
204
+ if (schema)
205
+ fields.s = schema;
206
+ return new PgError(fields, { cause: e });
207
+ }
151
208
  export const _internal = {
152
209
  renameRows,
153
210
  encodeParam,
@@ -156,13 +213,32 @@ export const _internal = {
156
213
  loadSqlFile,
157
214
  buildSetTransaction,
158
215
  clearIdentifierCache,
216
+ toPgError,
159
217
  };
160
218
  async function runQuery(client, query, params) {
161
219
  const encoded = params.length === 0
162
220
  ? params
163
221
  : params.map((p) => client.transformParam ? client.transformParam(p) : encodeParam(p));
164
- const rows = await client.query(query, encoded);
165
- return renameRows(rows);
222
+ const onQuery = client.onQuery;
223
+ if (!onQuery) {
224
+ try {
225
+ return renameRows(await client.query(query, encoded));
226
+ }
227
+ catch (e) {
228
+ throw toPgError(e) ?? e;
229
+ }
230
+ }
231
+ const start = performance.now();
232
+ try {
233
+ const rows = await client.query(query, encoded);
234
+ onQuery({ query, params, durationMs: performance.now() - start, rowCount: rows.length });
235
+ return renameRows(rows);
236
+ }
237
+ catch (e) {
238
+ const error = toPgError(e) ?? e;
239
+ onQuery({ query, params, durationMs: performance.now() - start, error });
240
+ throw error;
241
+ }
166
242
  }
167
243
  async function runOne(client, query, params) {
168
244
  const rows = await runQuery(client, query, params);
@@ -422,6 +498,10 @@ export async function migrate(opts = {}) {
422
498
  log(`migrate: applied ${String(e.version).padStart(4, "0")}_${e.name}`);
423
499
  appliedAny = true;
424
500
  }
501
+ else if (e.kind === "adopted") {
502
+ log(`migrate: adopted ${String(e.version).padStart(4, "0")}_${e.name} (${e.replaced} replaced)`);
503
+ appliedAny = true;
504
+ }
425
505
  else if (e.kind === "tampered") {
426
506
  throw new Error(`sqlx-js.migrate: ${e.version}_${e.name} hash mismatch (applied ${e.applied.slice(0, 16)}… vs current ${e.current.slice(0, 16)}…)`);
427
507
  }
@@ -2,7 +2,7 @@ import ts from "typescript";
2
2
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
3
  import { dirname, join, relative, resolve } from "node:path";
4
4
  const EXCLUDE_DIRS = new Set(["node_modules", ".git", ".sqlx-js", "dist", "build", ".next"]);
5
- const SQLX_MODULES = new Set(["@onreza/sqlx-js", "@onreza/sqlx-js/bun"]);
5
+ const SQLX_MODULES = new Set(["@onreza/sqlx-js"]);
6
6
  const EXT = /\.(ts|tsx|mts|cts)$/;
7
7
  function walk(dir, out) {
8
8
  for (const name of readdirSync(dir)) {
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@onreza/sqlx-js",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Compile-time-checked raw SQL for TypeScript + PostgreSQL. Inspired by sqlx.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
+ "engines": {
8
+ "node": ">=18"
9
+ },
7
10
  "sideEffects": false,
8
11
  "main": "./dist/src/index.js",
9
12
  "types": "./dist/src/index.d.ts",
@@ -12,12 +15,6 @@
12
15
  "types": "./dist/src/index.d.ts",
13
16
  "import": "./dist/src/index.js",
14
17
  "default": "./dist/src/index.js"
15
- },
16
- "./bun": {
17
- "types": "./dist/src/bun.d.ts",
18
- "bun": "./dist/src/bun.js",
19
- "import": "./dist/src/bun.js",
20
- "default": "./dist/src/bun.js"
21
18
  }
22
19
  },
23
20
  "bin": {
@@ -52,7 +49,7 @@
52
49
  "migrations"
53
50
  ],
54
51
  "scripts": {
55
- "prepare": "[ -d .git ] && lefthook install >/dev/null 2>&1 || true",
52
+ "prepare": "(test -f dist/bin/sqlx-js.js || bun run build) && ([ ! -d .git ] || lefthook install >/dev/null 2>&1 || true)",
56
53
  "test": "bun test tests",
57
54
  "test:typecheck": "tsc --noEmit",
58
55
  "test:example": "bunx tsc -p example --noEmit",
@@ -1,7 +0,0 @@
1
- import { SQL } from "bun";
2
- export type BunClient = SQL;
3
- export declare function getClient(): BunClient;
4
- export declare function setClient(client: BunClient): void;
5
- export declare function close(): Promise<void>;
6
- export declare const sql: import("./runtime.js").SqlRoot;
7
- export declare const unsafe: (query: string, ...params: unknown[]) => Promise<Record<string, unknown>[]>;
@@ -1,45 +0,0 @@
1
- import { SQL } from "bun";
2
- import { createSqlRuntime } from "./runtime.js";
3
- class BunRuntimeClient {
4
- client;
5
- constructor(client) {
6
- this.client = client;
7
- }
8
- async query(query, params) {
9
- return await this.client.unsafe(query, params);
10
- }
11
- async transaction(fn) {
12
- return await this.client.begin(async (tx) => {
13
- return await fn(new BunRuntimeClient(tx));
14
- });
15
- }
16
- async close() {
17
- await this.client.close();
18
- }
19
- }
20
- let defaultClient = null;
21
- function createDefaultClient() {
22
- const url = process.env.DATABASE_URL;
23
- if (!url)
24
- throw new Error("sqlx-js: DATABASE_URL is not set");
25
- return new BunRuntimeClient(new SQL({ url, bigint: true }));
26
- }
27
- function getRuntimeClient() {
28
- defaultClient ??= createDefaultClient();
29
- return defaultClient;
30
- }
31
- export function getClient() {
32
- return getRuntimeClient().client;
33
- }
34
- export function setClient(client) {
35
- defaultClient = new BunRuntimeClient(client);
36
- }
37
- export async function close() {
38
- if (defaultClient) {
39
- await defaultClient.close();
40
- defaultClient = null;
41
- }
42
- }
43
- const runtime = createSqlRuntime(getRuntimeClient);
44
- export const sql = runtime.sql;
45
- export const unsafe = runtime.unsafe;
package/dist/src/bun.d.ts DELETED
@@ -1,22 +0,0 @@
1
- import * as rt from "./bun-runtime.js";
2
- import type { Typed as TypedFor, TypedFile as TypedFileFor, TypedSql as TypedSqlFor } from "./typed.js";
3
- export interface KnownQueries {
4
- }
5
- export interface KnownFileQueries {
6
- }
7
- export type { SqlxJsConfig } from "./config.js";
8
- export type { SslMode, ConnConfig } from "./pg/wire.js";
9
- export { PgError, ConnectionLostError } from "./pg/wire.js";
10
- export { NoRowsError, TooManyRowsError } from "./runtime.js";
11
- export type { TransactionOptions, MigrateOptions } from "./runtime.js";
12
- export type { BunClient } from "./bun-runtime.js";
13
- export type TypedFile = TypedFileFor<KnownFileQueries>;
14
- export type TypedSql = TypedSqlFor<KnownQueries, KnownFileQueries>;
15
- export type Typed = TypedFor<KnownQueries, KnownFileQueries, import("./runtime.js").TransactionOptions>;
16
- export declare const sql: Typed;
17
- export type Unsafe = (query: string, ...params: unknown[]) => Promise<Record<string, unknown>[]>;
18
- export declare const unsafe: Unsafe;
19
- export declare const getClient: typeof rt.getClient;
20
- export declare const setClient: typeof rt.setClient;
21
- export declare const close: typeof rt.close;
22
- export { migrate, clearSqlFileCache, encodePgArrayLiteral, id } from "./runtime.js";
package/dist/src/bun.js DELETED
@@ -1,9 +0,0 @@
1
- import * as rt from "./bun-runtime.js";
2
- export { PgError, ConnectionLostError } from "./pg/wire.js";
3
- export { NoRowsError, TooManyRowsError } from "./runtime.js";
4
- export const sql = rt.sql;
5
- export const unsafe = rt.unsafe;
6
- export const getClient = rt.getClient;
7
- export const setClient = rt.setClient;
8
- export const close = rt.close;
9
- export { migrate, clearSqlFileCache, encodePgArrayLiteral, id } from "./runtime.js";