@typesql/pg 0.1.5 → 0.2.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/src/index.ts CHANGED
@@ -1,21 +1,25 @@
1
- import { createTransactionalSql, type RowKeys, type SqlOptions, type TransactionalSql } from "@typesql/core";
1
+ import type { RowKeys, SqlOptions, TransactionalSql } from "@typesql/core";
2
+ import { createTransactionalSql } from "@typesql/core/adapter";
2
3
  import { driver, type PgQueryable } from "./client.js";
3
4
  import type { PgTypeMap } from "./type-map.js";
4
5
 
5
- export { NoResultError, type Executor, type TransactionalSql } from "@typesql/core";
6
- export type { PgInterval, PgParamTypeMap, PgTypeMap } from "./type-map.js";
7
6
  export {
8
- executor,
9
- migrate,
10
- type MigrationSource,
11
- type PgQueryable,
12
- type PgResult,
13
- type PgTransactionClient,
14
- type PgPoolLike,
7
+ executor,
8
+ type MigrationSource,
9
+ migrate,
10
+ type PgPoolLike,
11
+ type PgQueryable,
12
+ type PgResult,
13
+ type PgTransactionClient,
15
14
  } from "./client.js";
15
+ export type { PgInterval, PgParamTypeMap, PgTypeMap } from "./type-map.js";
16
16
 
17
17
  /** What `createSql<DB>(client)` returns: core's `Sql` under `PgTypeMap`, with `transaction(fn)`. */
18
- export type PgSql<DB, Keys extends RowKeys = "preserve"> = TransactionalSql<DB, PgTypeMap, Keys>;
18
+ export type PgSql<DB, Keys extends RowKeys = "preserve"> = TransactionalSql<
19
+ DB,
20
+ PgTypeMap,
21
+ Keys
22
+ >;
19
23
 
20
24
  /**
21
25
  * `createSql<DB>()` from core, pre-bound to `PgTypeMap` (`count(*)` is `string`, `int8` is `string`, dates are `Date`).
@@ -23,8 +27,21 @@ export type PgSql<DB, Keys extends RowKeys = "preserve"> = TransactionalSql<DB,
23
27
  * `sql.transaction(fn)` runs `fn` with a `sql` bound to one transaction.
24
28
  * `{ rowKeys: "camelCase" }` camelCases the rows' keys, in the types and at runtime.
25
29
  */
26
- export function createSql<DB>(client: PgQueryable | undefined, options: { readonly rowKeys: "camelCase" }): PgSql<DB, "camelCase">;
27
- export function createSql<DB>(client?: PgQueryable, options?: { readonly rowKeys?: "preserve" }): PgSql<DB>;
28
- export function createSql<DB>(client?: PgQueryable, options?: SqlOptions): PgSql<DB, RowKeys> {
29
- return createTransactionalSql<DB, PgTypeMap, PgQueryable>(client, driver, options);
30
+ export function createSql<DB>(
31
+ client: PgQueryable | undefined,
32
+ options: { readonly rowKeys: "camelCase" },
33
+ ): PgSql<DB, "camelCase">;
34
+ export function createSql<DB>(
35
+ client?: PgQueryable,
36
+ options?: { readonly rowKeys?: "preserve" },
37
+ ): PgSql<DB>;
38
+ export function createSql<DB>(
39
+ client?: PgQueryable,
40
+ options?: SqlOptions,
41
+ ): PgSql<DB, RowKeys> {
42
+ return createTransactionalSql<DB, PgTypeMap, PgQueryable>(
43
+ client,
44
+ driver,
45
+ options,
46
+ );
30
47
  }
@@ -3,15 +3,15 @@ import { dirname, resolve } from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
4
  import { parseArgs } from "node:util";
5
5
  import type { PgQueryable } from "../client.js";
6
- import { generateSource, type GenerateOptions } from "./emit.js";
7
- import { introspect, type IntrospectOptions } from "./introspect.js";
6
+ import { type GenerateOptions, generateSource } from "./emit.js";
7
+ import { type IntrospectOptions, introspect } from "./introspect.js";
8
8
 
9
9
  /** What a `--config` module exports as its default: CLI settings plus every generator option. */
10
10
  export interface IntrospectConfig extends GenerateOptions, IntrospectOptions {
11
- /** Connection string. Default: `--url`, then `$DATABASE_URL`. */
12
- readonly url?: string;
13
- /** Output file, relative to the working directory. Default: stdout. */
14
- readonly out?: string;
11
+ /** Connection string. Default: `--url`, then `$DATABASE_URL`. */
12
+ readonly url?: string;
13
+ /** Output file, relative to the working directory. Default: stdout. */
14
+ readonly out?: string;
15
15
  }
16
16
 
17
17
  const USAGE = `Usage: typesql introspect [options]
@@ -32,107 +32,131 @@ Options:
32
32
  `;
33
33
 
34
34
  interface Io {
35
- readonly stdout: (s: string) => void;
36
- readonly stderr: (s: string) => void;
37
- readonly env: Readonly<Record<string, string | undefined>>;
38
- readonly cwd: string;
39
- /** Opens a client for a connection string; default: a `pg.Client`. */
40
- readonly connect?: (url: string) => Promise<PgQueryable & { end(): Promise<void> }>;
35
+ readonly stdout: (s: string) => void;
36
+ readonly stderr: (s: string) => void;
37
+ readonly env: Readonly<Record<string, string | undefined>>;
38
+ readonly cwd: string;
39
+ /** Opens a client for a connection string; default: a `pg.Client`. */
40
+ readonly connect?: (
41
+ url: string,
42
+ ) => Promise<PgQueryable & { end(): Promise<void> }>;
41
43
  }
42
44
 
43
45
  const defaultIo = (): Io => ({
44
- stdout: (s) => process.stdout.write(s),
45
- stderr: (s) => process.stderr.write(s),
46
- env: process.env,
47
- cwd: process.cwd(),
46
+ stdout: (s) => process.stdout.write(s),
47
+ stderr: (s) => process.stderr.write(s),
48
+ env: process.env,
49
+ cwd: process.cwd(),
48
50
  });
49
51
 
50
- async function pgConnect(url: string): Promise<PgQueryable & { end(): Promise<void> }> {
51
- let Client: typeof import("pg").Client;
52
- try {
53
- ({ Client } = (await import("pg")).default);
54
- } catch {
55
- throw new Error('typesql introspect needs the "pg" package: npm install pg');
56
- }
57
- const client = new Client({ connectionString: url });
58
- await client.connect();
59
- return client;
52
+ async function pgConnect(
53
+ url: string,
54
+ ): Promise<PgQueryable & { end(): Promise<void> }> {
55
+ let Client: typeof import("pg").Client;
56
+ try {
57
+ ({ Client } = (await import("pg")).default);
58
+ } catch {
59
+ throw new Error(
60
+ 'typesql introspect needs the "pg" package: npm install pg',
61
+ );
62
+ }
63
+ const client = new Client({ connectionString: url });
64
+ await client.connect();
65
+ return client;
60
66
  }
61
67
 
68
+ const parse = (argv: readonly string[]) =>
69
+ parseArgs({
70
+ args: [...argv],
71
+ allowPositionals: true,
72
+ options: {
73
+ url: { type: "string" },
74
+ out: { type: "string" },
75
+ schema: { type: "string", multiple: true },
76
+ exclude: { type: "string", multiple: true },
77
+ "type-name": { type: "string" },
78
+ "import-from": { type: "string" },
79
+ config: { type: "string" },
80
+ "no-comments": { type: "boolean" },
81
+ help: { type: "boolean", short: "h" },
82
+ },
83
+ });
84
+
62
85
  /** Runs the `typesql` CLI with `argv` (without `node` and the script). Returns the exit code. */
63
- export async function main(argv: readonly string[], io: Io = defaultIo()): Promise<number> {
64
- let parsed;
65
- try {
66
- parsed = parseArgs({
67
- args: [...argv],
68
- allowPositionals: true,
69
- options: {
70
- url: { type: "string" },
71
- out: { type: "string" },
72
- schema: { type: "string", multiple: true },
73
- exclude: { type: "string", multiple: true },
74
- "type-name": { type: "string" },
75
- "import-from": { type: "string" },
76
- config: { type: "string" },
77
- "no-comments": { type: "boolean" },
78
- help: { type: "boolean", short: "h" },
79
- },
80
- });
81
- } catch (err) {
82
- io.stderr(`${(err as Error).message}\n\n${USAGE}`);
83
- return 2;
84
- }
85
- const { values, positionals } = parsed;
86
- if (values.help || positionals.length === 0) {
87
- (values.help ? io.stdout : io.stderr)(USAGE);
88
- return values.help ? 0 : 2;
89
- }
90
- if (positionals[0] !== "introspect" || positionals.length > 1) {
91
- io.stderr(`unknown command: ${positionals.join(" ")}\n\n${USAGE}`);
92
- return 2;
93
- }
86
+ export async function main(
87
+ argv: readonly string[],
88
+ io: Io = defaultIo(),
89
+ ): Promise<number> {
90
+ let parsed: ReturnType<typeof parse>;
91
+ try {
92
+ parsed = parse(argv);
93
+ } catch (err) {
94
+ io.stderr(`${(err as Error).message}\n\n${USAGE}`);
95
+ return 2;
96
+ }
97
+ const { values, positionals } = parsed;
98
+ if (values.help || positionals.length === 0) {
99
+ (values.help ? io.stdout : io.stderr)(USAGE);
100
+ return values.help ? 0 : 2;
101
+ }
102
+ if (positionals[0] !== "introspect" || positionals.length > 1) {
103
+ io.stderr(`unknown command: ${positionals.join(" ")}\n\n${USAGE}`);
104
+ return 2;
105
+ }
94
106
 
95
- try {
96
- let config: IntrospectConfig = {};
97
- if (values.config) {
98
- const mod = (await import(pathToFileURL(resolve(io.cwd, values.config)).href)) as { default?: IntrospectConfig };
99
- config = mod.default ?? {};
100
- }
101
- const url = values.url ?? config.url ?? io.env["DATABASE_URL"];
102
- if (!url) {
103
- io.stderr("typesql introspect: pass --url or set DATABASE_URL\n");
104
- return 2;
105
- }
106
- const list = (v: string[] | undefined) => v?.flatMap((s) => s.split(",").map((x) => x.trim()).filter(Boolean));
107
- const schemas = list(values.schema) ?? config.schemas;
108
- const exclude = list(values.exclude) ?? config.exclude;
109
- const options: IntrospectConfig = {
110
- ...config,
111
- ...(schemas !== undefined && { schemas }),
112
- ...(exclude !== undefined && { exclude }),
113
- ...(values["type-name"] !== undefined && { typeName: values["type-name"] }),
114
- ...(values["import-from"] !== undefined && { importFrom: values["import-from"] }),
115
- ...(values["no-comments"] && { comments: false }),
116
- };
117
- const client = await (io.connect ?? pgConnect)(url);
118
- let source: string;
119
- try {
120
- source = generateSource(await introspect(client, options), options);
121
- } finally {
122
- await client.end();
123
- }
124
- const out = values.out ?? config.out;
125
- if (out === undefined) {
126
- io.stdout(source);
127
- } else {
128
- const file = resolve(io.cwd, out);
129
- await mkdir(dirname(file), { recursive: true });
130
- await writeFile(file, source);
131
- io.stderr(`typesql introspect: wrote ${out}\n`);
132
- }
133
- return 0;
134
- } catch (err) {
135
- io.stderr(`typesql introspect: ${(err as Error).message}\n`);
136
- return 1;
137
- }
107
+ try {
108
+ let config: IntrospectConfig = {};
109
+ if (values.config) {
110
+ const mod = (await import(
111
+ pathToFileURL(resolve(io.cwd, values.config)).href
112
+ )) as { default?: IntrospectConfig };
113
+ config = mod.default ?? {};
114
+ }
115
+ const url = values.url ?? config.url ?? io.env["DATABASE_URL"];
116
+ if (!url) {
117
+ io.stderr("typesql introspect: pass --url or set DATABASE_URL\n");
118
+ return 2;
119
+ }
120
+ const list = (v: string[] | undefined) =>
121
+ v?.flatMap((s) =>
122
+ s
123
+ .split(",")
124
+ .map((x) => x.trim())
125
+ .filter(Boolean),
126
+ );
127
+ const schemas = list(values.schema) ?? config.schemas;
128
+ const exclude = list(values.exclude) ?? config.exclude;
129
+ const options: IntrospectConfig = {
130
+ ...config,
131
+ ...(schemas !== undefined && { schemas }),
132
+ ...(exclude !== undefined && { exclude }),
133
+ ...(values["type-name"] !== undefined && {
134
+ typeName: values["type-name"],
135
+ }),
136
+ ...(values["import-from"] !== undefined && {
137
+ importFrom: values["import-from"],
138
+ }),
139
+ ...(values["no-comments"] && { comments: false }),
140
+ };
141
+ const client = await (io.connect ?? pgConnect)(url);
142
+ let source: string;
143
+ try {
144
+ source = generateSource(await introspect(client, options), options);
145
+ } finally {
146
+ await client.end();
147
+ }
148
+ const out = values.out ?? config.out;
149
+ if (out === undefined) {
150
+ io.stdout(source);
151
+ } else {
152
+ const file = resolve(io.cwd, out);
153
+ await mkdir(dirname(file), { recursive: true });
154
+ await writeFile(file, source);
155
+ io.stderr(`typesql introspect: wrote ${out}\n`);
156
+ }
157
+ return 0;
158
+ } catch (err) {
159
+ io.stderr(`typesql introspect: ${(err as Error).message}\n`);
160
+ return 1;
161
+ }
138
162
  }