@onreza/sqlx-js 0.0.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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +496 -0
  3. package/ROADMAP.md +28 -0
  4. package/dist/bin/sqlx-js.d.ts +2 -0
  5. package/dist/bin/sqlx-js.js +180 -0
  6. package/dist/src/bun-runtime.d.ts +7 -0
  7. package/dist/src/bun-runtime.js +45 -0
  8. package/dist/src/bun.d.ts +22 -0
  9. package/dist/src/bun.js +9 -0
  10. package/dist/src/cache.d.ts +36 -0
  11. package/dist/src/cache.js +104 -0
  12. package/dist/src/codegen.d.ts +2 -0
  13. package/dist/src/codegen.js +74 -0
  14. package/dist/src/commands/migrate.d.ts +63 -0
  15. package/dist/src/commands/migrate.js +283 -0
  16. package/dist/src/commands/prepare.d.ts +23 -0
  17. package/dist/src/commands/prepare.js +314 -0
  18. package/dist/src/commands/schema.d.ts +14 -0
  19. package/dist/src/commands/schema.js +72 -0
  20. package/dist/src/commands/watch.d.ts +21 -0
  21. package/dist/src/commands/watch.js +94 -0
  22. package/dist/src/config.d.ts +6 -0
  23. package/dist/src/config.js +23 -0
  24. package/dist/src/index.d.ts +23 -0
  25. package/dist/src/index.js +10 -0
  26. package/dist/src/pg/analyze.d.ts +13 -0
  27. package/dist/src/pg/analyze.js +479 -0
  28. package/dist/src/pg/extensions.d.ts +2 -0
  29. package/dist/src/pg/extensions.js +15 -0
  30. package/dist/src/pg/narrow.d.ts +3 -0
  31. package/dist/src/pg/narrow.js +146 -0
  32. package/dist/src/pg/oids.d.ts +10 -0
  33. package/dist/src/pg/oids.js +137 -0
  34. package/dist/src/pg/param-map.d.ts +12 -0
  35. package/dist/src/pg/param-map.js +180 -0
  36. package/dist/src/pg/schema.d.ts +61 -0
  37. package/dist/src/pg/schema.js +225 -0
  38. package/dist/src/pg/wire.d.ts +134 -0
  39. package/dist/src/pg/wire.js +705 -0
  40. package/dist/src/postgres-runtime.d.ts +11 -0
  41. package/dist/src/postgres-runtime.js +150 -0
  42. package/dist/src/runtime.d.ts +69 -0
  43. package/dist/src/runtime.js +446 -0
  44. package/dist/src/scan/scanner.d.ts +12 -0
  45. package/dist/src/scan/scanner.js +283 -0
  46. package/dist/src/schema-snapshot.d.ts +104 -0
  47. package/dist/src/schema-snapshot.js +473 -0
  48. package/dist/src/typed.d.ts +25 -0
  49. package/dist/src/typed.js +1 -0
  50. package/package.json +78 -0
@@ -0,0 +1,72 @@
1
+ import { introspectDatabase, readSchemaSnapshot, schemaSnapshotEqual, schemaSnapshotExists, writeSchemaManifest, writeSchemaSnapshot } from "../schema-snapshot.js";
2
+ import { PgClient, parseDatabaseUrl } from "../pg/wire.js";
3
+ import { acquireMigrateLock, applyPending, DEFAULT_MIGRATE_LOCK_KEY, releaseMigrateLock } from "./migrate.js";
4
+ export async function applyShadowMigrations(databaseUrl, migrationsDir, log = console.log) {
5
+ const client = new PgClient(parseDatabaseUrl(databaseUrl));
6
+ await client.connect();
7
+ let locked = false;
8
+ let applied = 0;
9
+ try {
10
+ await acquireMigrateLock(client, DEFAULT_MIGRATE_LOCK_KEY);
11
+ locked = true;
12
+ const result = await applyPending(client, migrationsDir, (e) => {
13
+ if (e.kind === "applied") {
14
+ applied++;
15
+ log(`shadow: applied ${String(e.version).padStart(4, "0")}_${e.name}`);
16
+ }
17
+ else if (e.kind === "tampered") {
18
+ throw new Error(`sqlx-js shadow: ${e.version}_${e.name} hash mismatch (applied ${e.applied.slice(0, 16)} vs current ${e.current.slice(0, 16)})`);
19
+ }
20
+ else {
21
+ throw new Error(`sqlx-js shadow: ${e.version}_${e.name} failed — ${e.error}`);
22
+ }
23
+ });
24
+ if (applied === 0 && result.tampered === 0 && result.failed === 0)
25
+ log("shadow: migrations up-to-date");
26
+ return { applied };
27
+ }
28
+ finally {
29
+ if (locked) {
30
+ try {
31
+ await releaseMigrateLock(client, DEFAULT_MIGRATE_LOCK_KEY);
32
+ }
33
+ catch (e) {
34
+ log(`shadow: failed to release advisory lock: ${e.message}`);
35
+ }
36
+ }
37
+ await client.end();
38
+ }
39
+ }
40
+ function effectiveDatabaseUrl(opts) {
41
+ return opts.shadowUrl ?? opts.databaseUrl;
42
+ }
43
+ async function prepareShadowIfNeeded(opts) {
44
+ if (opts.shadowUrl)
45
+ await applyShadowMigrations(opts.shadowUrl, opts.migrationsDir);
46
+ }
47
+ export async function runSchemaDump(opts) {
48
+ await prepareShadowIfNeeded(opts);
49
+ const snapshot = await introspectDatabase(effectiveDatabaseUrl(opts));
50
+ writeSchemaSnapshot(opts.snapshotPath, snapshot);
51
+ if (opts.writeManifest)
52
+ writeSchemaManifest(opts.manifestPath, snapshot);
53
+ console.log(`schema: wrote ${opts.snapshotPath}`);
54
+ if (opts.writeManifest)
55
+ console.log(`schema: wrote ${opts.manifestPath}`);
56
+ }
57
+ export async function runSchemaCheck(opts) {
58
+ if (!schemaSnapshotExists(opts.snapshotPath)) {
59
+ console.error(`schema: missing snapshot ${opts.snapshotPath}`);
60
+ console.error("schema: run `sqlx-js schema dump` against a live database");
61
+ process.exit(1);
62
+ }
63
+ await prepareShadowIfNeeded(opts);
64
+ const expected = readSchemaSnapshot(opts.snapshotPath);
65
+ const actual = await introspectDatabase(effectiveDatabaseUrl(opts));
66
+ if (!schemaSnapshotEqual(expected, actual)) {
67
+ console.error(`schema: snapshot is stale: ${opts.snapshotPath}`);
68
+ console.error("schema: run `sqlx-js schema dump` and commit the updated snapshot");
69
+ process.exit(1);
70
+ }
71
+ console.log(`schema: ok — ${actual.relations.length} relation(s), ${actual.types.length} type(s), ${actual.functions.length} function(s)`);
72
+ }
@@ -0,0 +1,21 @@
1
+ import { openSession, prepareOnce, type PrepareOptions, type PrepareSession } from "./prepare.js";
2
+ export type WatchPrepareHookResult = {
3
+ resetSession?: boolean;
4
+ };
5
+ export type WatchOptions = PrepareOptions & {
6
+ beforePrepare?: () => Promise<WatchPrepareHookResult | void>;
7
+ };
8
+ export type WatchState = {
9
+ session: PrepareSession | null;
10
+ };
11
+ type WatchDeps = {
12
+ openSession: typeof openSession;
13
+ prepareOnce: typeof prepareOnce;
14
+ };
15
+ export declare function prepareWatchedOnce(opts: WatchOptions, state: WatchState, log: (msg: string) => void, err: (msg: string) => void, deps?: WatchDeps): Promise<{
16
+ entries: number;
17
+ failures: number;
18
+ pruned: number;
19
+ }>;
20
+ export declare function runWatch(opts: WatchOptions): Promise<void>;
21
+ export {};
@@ -0,0 +1,94 @@
1
+ import { watch as fsWatch } from "node:fs";
2
+ import { basename } from "node:path";
3
+ import { openSession, prepareOnce } from "./prepare.js";
4
+ const EXT_RE = /\.(ts|tsx|mts|cts|sql)$/;
5
+ const SKIP_DIRS = ["node_modules", ".git", ".sqlx-js", "dist", "build", ".next"];
6
+ const DEBOUNCE_MS = 150;
7
+ const DEFAULT_DEPS = { openSession, prepareOnce };
8
+ async function closeSession(session) {
9
+ if (!session)
10
+ return;
11
+ try {
12
+ await session.client.end();
13
+ }
14
+ catch { }
15
+ }
16
+ export async function prepareWatchedOnce(opts, state, log, err, deps = DEFAULT_DEPS) {
17
+ const hookResult = await opts.beforePrepare?.();
18
+ if (hookResult?.resetSession === true) {
19
+ await closeSession(state.session);
20
+ state.session = null;
21
+ }
22
+ if (!state.session)
23
+ state.session = await deps.openSession(opts);
24
+ return await deps.prepareOnce(opts, state.session, log, err);
25
+ }
26
+ export async function runWatch(opts) {
27
+ const stamp = () => new Date().toTimeString().slice(0, 8);
28
+ const log = (m) => console.log(`[${stamp()}] ${m}`);
29
+ const err = (m) => console.error(`[${stamp()}] ${m}`);
30
+ const state = { session: null };
31
+ log("watch: initial prepare");
32
+ try {
33
+ const r = await prepareWatchedOnce(opts, state, log, err);
34
+ log(`watch: ready — ${r.entries} queries, ${r.failures} failures`);
35
+ }
36
+ catch (e) {
37
+ err(`watch: initial prepare failed — ${e.message}`);
38
+ }
39
+ log(`watch: monitoring ${opts.root}`);
40
+ let pending = false;
41
+ let running = null;
42
+ let timer = null;
43
+ const trigger = () => {
44
+ if (timer)
45
+ clearTimeout(timer);
46
+ timer = setTimeout(async () => {
47
+ timer = null;
48
+ if (running) {
49
+ pending = true;
50
+ return;
51
+ }
52
+ const start = Date.now();
53
+ running = (async () => {
54
+ try {
55
+ const r = await prepareWatchedOnce(opts, state, log, err);
56
+ log(`watch: re-prepared in ${Date.now() - start}ms (${r.entries} queries, ${r.failures} failures)`);
57
+ }
58
+ catch (e) {
59
+ err(`watch: prepare error — ${e.message}`);
60
+ }
61
+ })();
62
+ await running;
63
+ running = null;
64
+ if (pending) {
65
+ pending = false;
66
+ trigger();
67
+ }
68
+ }, DEBOUNCE_MS);
69
+ };
70
+ const watcher = fsWatch(opts.root, { recursive: true }, (_event, filename) => {
71
+ if (!filename)
72
+ return;
73
+ const raw = filename.toString();
74
+ const f = raw.replace(/\\/g, "/");
75
+ if (SKIP_DIRS.some((d) => f === d || f.startsWith(`${d}/`) || f.includes(`/${d}/`)))
76
+ return;
77
+ const base = basename(f);
78
+ if (base === "sqlx-js-env.d.ts" || base === "sqlx-js.d.ts")
79
+ return;
80
+ if (!EXT_RE.test(f))
81
+ return;
82
+ trigger();
83
+ });
84
+ const shutdown = async () => {
85
+ console.log();
86
+ log("watch: stopping");
87
+ watcher.close();
88
+ await closeSession(state.session);
89
+ process.exit(0);
90
+ };
91
+ process.on("SIGINT", shutdown);
92
+ process.on("SIGTERM", shutdown);
93
+ await new Promise(() => { });
94
+ }
@@ -0,0 +1,6 @@
1
+ export type SqlxJsConfig = {
2
+ jsonbTypes?: Record<string, string>;
3
+ customTypes?: Record<string, string>;
4
+ };
5
+ export declare function loadConfig(root: string): Promise<SqlxJsConfig>;
6
+ export declare function lookupJsonbType(cfg: SqlxJsConfig, schema: string, table: string, column: string): string | undefined;
@@ -0,0 +1,23 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ export async function loadConfig(root) {
4
+ for (const name of ["sqlx-js.config.ts", "sqlx-js.config.js", "sqlx-js.config.mjs"]) {
5
+ const p = join(root, name);
6
+ if (!existsSync(p))
7
+ continue;
8
+ const mod = await import(p);
9
+ const cfg = (mod.default ?? mod);
10
+ if (typeof cfg !== "object" || cfg === null) {
11
+ throw new Error(`sqlx-js: ${name} must default-export a config object`);
12
+ }
13
+ return cfg;
14
+ }
15
+ return {};
16
+ }
17
+ export function lookupJsonbType(cfg, schema, table, column) {
18
+ const types = cfg.jsonbTypes;
19
+ if (!types)
20
+ return undefined;
21
+ return (types[`${schema}.${table}.${column}`] ??
22
+ types[`${table}.${column}`]);
23
+ }
@@ -0,0 +1,23 @@
1
+ import * as rt from "./postgres-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 { PostgresClient, PostgresOptions } from "./postgres-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 createClient: typeof rt.createClient;
22
+ export declare const close: typeof rt.close;
23
+ export { migrate, clearSqlFileCache, encodePgArrayLiteral, id } from "./runtime.js";
@@ -0,0 +1,10 @@
1
+ import * as rt from "./postgres-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 createClient = rt.createClient;
9
+ export const close = rt.close;
10
+ export { migrate, clearSqlFileCache, encodePgArrayLiteral, id } from "./runtime.js";
@@ -0,0 +1,13 @@
1
+ import type { FieldDescription } from "./wire.js";
2
+ import type { SchemaCache } from "./schema.js";
3
+ export type AnalysisResult = {
4
+ perColumnNullable: boolean[];
5
+ referencedTables: {
6
+ schema?: string;
7
+ name: string;
8
+ }[];
9
+ degraded?: {
10
+ reason: string;
11
+ };
12
+ };
13
+ export declare function analyzeQuery(sql: string, rowDesc: FieldDescription[], schema: SchemaCache): Promise<AnalysisResult>;