@typesql/core 0.0.4 → 0.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @typesql/core
2
2
 
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Docs, package descriptions and error messages spell the project name TypeSQL.
8
+
9
+ ## 0.1.0
10
+
11
+ ### Minor Changes
12
+
13
+ - Adapters build their `sql` with `createTransactionalSql(client, driver)`, typed `TransactionalSql` (core's `Sql` plus `transaction(fn)`), and `runTransaction` sends BEGIN/COMMIT/ROLLBACK or savepoints; an `Executor` may have a `count(text, values)` method, which `executeCount` runs instead.
14
+
3
15
  ## 0.0.4
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -70,7 +70,9 @@ checked against their context.
70
70
  your migrations.
71
71
  - **Running queries**: with `createSql(executor)` (or an adapter's `createSql(client)`), queries
72
72
  have `execute()`, `executeTakeFirst()`, `executeTakeFirstOrThrow()` and `executeCount()` (the
73
- affected row count). An `Executor` resolves to the rows or to `{ rows, rowCount }`.
73
+ affected row count). An `Executor` resolves to the rows or to `{ rows, rowCount }`. An
74
+ adapter's `sql` also has `sql.transaction(fn)`, which runs `fn` with a `sql` bound to one
75
+ transaction (`TransactionalSql`).
74
76
  - **camelCase rows**: `createSql(executor, { rowKeys: "camelCase" })` renames the rows'
75
77
  top-level keys (`created_at` → `createdAt`, as Kysely's `CamelCasePlugin` does) in the types
76
78
  and at runtime, leaving JSON values alone.
@@ -187,7 +187,7 @@ type GetRel<DB, Ref extends TableRef> = IsTop<Ref["schema"]> extends true ? Ref[
187
187
  type SchemaExists<DB, S> = IsTop<S> extends true ? true : S extends string ? IsSchemaKey<DB, S> : false;
188
188
  type NoSchema<S> = SqlError<`schema "${S & string}" does not exist`, null>;
189
189
  /** A new top-level relation would overwrite a schema (schemas and public relations share `DB`'s keys). */
190
- type SchemaClash<DB, Ref extends TableRef> = IsTop<Ref["schema"]> extends true ? Ref["name"] extends keyof DB ? SqlError<`cannot create relation "${Ref["name"]}": a schema with that name exists (typesql keeps schemas and public relations in one object)`, null> : null : null;
190
+ type SchemaClash<DB, Ref extends TableRef> = IsTop<Ref["schema"]> extends true ? Ref["name"] extends keyof DB ? SqlError<`cannot create relation "${Ref["name"]}": a schema with that name exists (TypeSQL keeps schemas and public relations in one object)`, null> : null : null;
191
191
  /** Checks before adding a relation at `Ref`: `null` when it can be added. */
192
192
  type CanCreate<DB, Ref extends TableRef> = SchemaExists<DB, Ref["schema"]> extends false ? NoSchema<Ref["schema"]> : SchemaClash<DB, Ref>;
193
193
  type PutRel<DB, Ref extends TableRef, V> = IsTop<Ref["schema"]> extends true ? SetKey<DB, Ref["name"], V> : Ref["schema"] extends keyof DB ? SetKey<DB, Ref["schema"] & string, SetKey<DB[Ref["schema"]], Ref["name"], V>> : NoSchema<Ref["schema"]>;
@@ -281,7 +281,7 @@ type NameCols<C extends Cols, Names, Acc extends Cols, TooMany extends string> =
281
281
  } ? NameCols<R, NR, [...Acc, {
282
282
  readonly n: N;
283
283
  readonly t: I;
284
- }], TooMany> : SqlError<"a column name list cannot rename the columns of a * projection (typesql limitation): list the columns explicitly", null> : SqlError<TooMany, null> : [...Acc, ...C];
284
+ }], TooMany> : SqlError<"a column name list cannot rename the columns of a * projection (TypeSQL limitation): list the columns explicitly", null> : SqlError<TooMany, null> : [...Acc, ...C];
285
285
  /** First duplicate output name, or null. */
286
286
  type DupCol<C extends Cols, Seen> = C extends readonly [infer H, ...infer R extends Cols] ? H extends {
287
287
  readonly star: infer M;
@@ -438,7 +438,7 @@ type DropTypeNs<NS, N extends string> = Rebuild<{
438
438
  type StripCols<Tbl, N extends string> = Rebuild<{
439
439
  [K in keyof Tbl as ColUses<Tbl[K], N> extends true ? never : K]: Tbl[K];
440
440
  }>;
441
- type ApplyCreateSchema<DB, Ine, Name extends string> = Name extends "public" ? (Ine extends true ? DB : SqlError<`schema "public" already exists`, null>) : Name extends keyof DB ? IsSchemaKey<DB, Name> extends true ? (Ine extends true ? DB : SqlError<`schema "${Name}" already exists`, null>) : SqlError<`cannot create schema "${Name}": a relation with that name exists (typesql keeps schemas and public relations in one object)`, null> : SetKey<DB, Name, {}>;
441
+ type ApplyCreateSchema<DB, Ine, Name extends string> = Name extends "public" ? (Ine extends true ? DB : SqlError<`schema "public" already exists`, null>) : Name extends keyof DB ? IsSchemaKey<DB, Name> extends true ? (Ine extends true ? DB : SqlError<`schema "${Name}" already exists`, null>) : SqlError<`cannot create schema "${Name}": a relation with that name exists (TypeSQL keeps schemas and public relations in one object)`, null> : SetKey<DB, Name, {}>;
442
442
  type ApplyDropSchemas<DB, Names, Ife, Casc> = Names extends readonly [infer N extends string, ...infer Rest] ? N extends "public" ? SqlError<"DROP SCHEMA public is not supported", null> : IsSchemaKey<DB, N> extends true ? [keyof DB[N & keyof DB]] extends [never] ? ApplyDropSchemas<DelKey<DB, N>, Rest, Ife, Casc> : Casc extends true ? ApplyDropSchemas<DelKey<DB, N>, Rest, Ife, Casc> : SqlError<`cannot drop schema ${N} because other objects depend on it`, null> : Ife extends true ? ApplyDropSchemas<DB, Rest, Ife, Casc> : SqlError<`schema "${N}" does not exist`, null> : DB;
443
443
  type ApplyRenameSchema<DB, F extends string, To extends string> = IsSchemaKey<DB, F> extends true ? To extends keyof DB | "public" ? SqlError<`schema "${To}" already exists`, null> : RenameKey<DB, F, To> : SqlError<`schema "${F}" does not exist`, null>;
444
444
  export {};
package/dist/index.d.ts CHANGED
@@ -18,5 +18,6 @@ export type { Tok, Toks, WordTok, IdTok, NumTok, StrTok, ParamTok, OpTok } from
18
18
  export type { ParseProgram, ParseStatement } from "./engine/parser/stmt.js";
19
19
  export type * as Ast from "./engine/ast.js";
20
20
  export type { ColInfo, ColMap, Cols, Scope, ScopeItem, ScopeLevel, Ctx, CteMap } from "./engine/types.js";
21
+ export { createTransactionalSql, runTransaction, type TransactionalSql, type SqlDriver } from "./transaction.js";
21
22
  export { Migrations, migrations, runMigrations, type MigrationClient, type SchemaOf, type ValidateDdl } from "./migrations.js";
22
23
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -4,5 +4,6 @@ export { makeQuery, makeFragment, mapRows, rowKeysOf, NoResultError } from "./qu
4
4
  export { param, valueOrDefault } from "./runtime/inline.js";
5
5
  export { json, JsonParam } from "./runtime/json.js";
6
6
  export { splice, renumberParams, maxParam, quoteIdent, joinFragments, hasSlots, rewriteNamed } from "./runtime/splice.js";
7
+ export { createTransactionalSql, runTransaction } from "./transaction.js";
7
8
  export { Migrations, migrations, runMigrations } from "./migrations.js";
8
9
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAyF,MAAM,iBAAiB,CAAC;AACnI,OAAO,EAAE,SAAS,EAAoF,MAAM,eAAe,CAAC;AAC5H,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAA+J,MAAM,YAAY,CAAC;AACrP,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,EAAoC,MAAM,qBAAqB,CAAC;AAiB5J,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAyD,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAyF,MAAM,iBAAiB,CAAC;AACnI,OAAO,EAAE,SAAS,EAAoF,MAAM,eAAe,CAAC;AAC5H,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAA+J,MAAM,YAAY,CAAC;AACrP,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,EAAoC,MAAM,qBAAqB,CAAC;AAiB5J,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAyC,MAAM,kBAAkB,CAAC;AACjH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAyD,MAAM,iBAAiB,CAAC"}
package/dist/query.d.ts CHANGED
@@ -21,9 +21,12 @@ export interface ExecutorResult {
21
21
  * Runs a statement and resolves to its rows, or to `{ rows, rowCount }`. Adapters build one from a
22
22
  * driver client (`executor(pool)` in `@typesql/pg`, which reports `rowCount`); pass it to
23
23
  * `createSql(executor)` or to `q.execute(executor)`. An executor that resolves to the rows alone
24
- * works for everything except `executeCount`.
24
+ * works for everything except `executeCount`, unless it also has a `count` method.
25
25
  */
26
- export type Executor = (text: string, values: readonly unknown[]) => PromiseLike<readonly unknown[] | ExecutorResult>;
26
+ export type Executor = ((text: string, values: readonly unknown[]) => PromiseLike<readonly unknown[] | ExecutorResult>) & {
27
+ /** Runs the statement for its row count; `executeCount` uses it when present (Prisma's `$executeRaw`). */
28
+ readonly count?: (text: string, values: readonly unknown[]) => PromiseLike<number>;
29
+ };
27
30
  /** Thrown by `executeTakeFirstOrThrow` when the query returns no rows. */
28
31
  export declare class NoResultError extends Error {
29
32
  readonly name = "NoResultError";
@@ -71,7 +74,7 @@ export type TextType<Q> = Q extends {
71
74
  /**
72
75
  * Turn rows a driver returned for `q` into the rows `q`'s type describes: a fresh array, with
73
76
  * the keys renamed when `q` comes from `createSql(executor, { rowKeys: "camelCase" })`. For
74
- * helpers that run `q.text` themselves, such as the adapters' `query(client, q)`.
77
+ * helpers that run `q.text` themselves instead of through an `Executor`.
75
78
  */
76
79
  export declare function mapRows<Q extends {
77
80
  readonly text: string;
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAE3D,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,EAAE,OAAO,MAAM,CAAC;AACxC,MAAM,CAAC,OAAO,CAAC,MAAM,MAAM,EAAE,OAAO,MAAM,CAAC;AAC3C,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,OAAO,MAAM,CAAC;AACzC,MAAM,CAAC,OAAO,CAAC,MAAM,OAAO,EAAE,OAAO,MAAM,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,OAAO,EAAE,KAAK,WAAW,CAAC,SAAS,OAAO,EAAE,GAAG,cAAc,CAAC,CAAC;AAEtH,0EAA0E;AAC1E,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAkB,IAAI,mBAAmB;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,YAAY,IAAI,EAAE,MAAM,EAGvB;CACF;AAED,MAAM,WAAW,KAAK,CAAC,GAAG,EAAE,MAAM,SAAS,SAAS,OAAO,EAAE,GAAG,SAAS,OAAO,EAAE,EAAE,IAAI,SAAS,MAAM,GAAG,MAAM;IAC9G,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;IACpC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC;IACvB,gHAAgH;IAChH,OAAO,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7C,yDAAyD;IACzD,gBAAgB,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,GAAG,SAAS,CAAC,CAAC;IAChE,iEAAiE;IACjE,uBAAuB,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3D;;;;OAIG;IACH,YAAY,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACpD;AAED;;;;GAIG;AACH,MAAM,WAAW,QAAQ,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,EAAE,MAAM,SAAS,SAAS,OAAO,EAAE,GAAG,SAAS,OAAO,EAAE,EAAE,OAAO,SAAS,SAAS,OAAO,EAAE,GAAG,SAAS,OAAO,EAAE;IACrK,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;IACpC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAC1F,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,SAAS;IAAE,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,CAAC,GAAG,KAAK,CAAC;AAClF,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS;IAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,SAAS,MAAM,CAAA;CAAE,GAAG,CAAC,GAAG,MAAM,CAAC;AAK9F;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,CAEzG;AAED,+FAA+F;AAC/F,wBAAgB,SAAS,CAAC,CAAC,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAE/D;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,SAAS,SAAS,OAAO,EAAE,EAAE,IAAI,SAAS,MAAM,GAAG,MAAM,EAC5F,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,SAAS,OAAO,EAAE,EAC1B,QAAQ,CAAC,EAAE,QAAQ,EACnB,OAAO,GAAE,OAAoB,GAC5B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CA+B1B;AAMD,wBAAgB,YAAY,CAAC,IAAI,SAAS,MAAM,EAAE,MAAM,SAAS,SAAS,OAAO,EAAE,EAAE,OAAO,SAAS,SAAS,OAAO,EAAE,GAAG,SAAS,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAEvN"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
package/dist/query.js CHANGED
@@ -17,7 +17,7 @@ const ROW_KEYS = Symbol.for("typesql.rowKeys");
17
17
  /**
18
18
  * Turn rows a driver returned for `q` into the rows `q`'s type describes: a fresh array, with
19
19
  * the keys renamed when `q` comes from `createSql(executor, { rowKeys: "camelCase" })`. For
20
- * helpers that run `q.text` themselves, such as the adapters' `query(client, q)`.
20
+ * helpers that run `q.text` themselves instead of through an `Executor`.
21
21
  */
22
22
  export function mapRows(q, rows) {
23
23
  return applyRowKeys(rows, rowKeysOf(q));
@@ -31,14 +31,14 @@ export function rowKeysOf(q) {
31
31
  * `{ text, values }` (strictly, too). `rowKeys` renames the keys of the rows `execute*` return.
32
32
  */
33
33
  export function makeQuery(text, values, executor, rowKeys = "preserve") {
34
- const run = async (given) => {
34
+ const pick = (given) => {
35
35
  const exec = given ?? executor;
36
36
  if (!exec)
37
37
  throw new Error("typesql: no executor: pass one to createSql(executor) or to execute(executor)");
38
- return exec(text, values);
38
+ return exec;
39
39
  };
40
40
  const execute = async (given) => {
41
- const result = await run(given);
41
+ const result = await pick(given)(text, values);
42
42
  return applyRowKeys(isRows(result) ? result : result.rows, rowKeys);
43
43
  };
44
44
  return Object.defineProperties({ text, values }, {
@@ -46,7 +46,10 @@ export function makeQuery(text, values, executor, rowKeys = "preserve") {
46
46
  execute: { value: execute },
47
47
  executeCount: {
48
48
  value: async (given) => {
49
- const result = await run(given);
49
+ const exec = pick(given);
50
+ if (exec.count)
51
+ return exec.count(text, values);
52
+ const result = await exec(text, values);
50
53
  if (isRows(result) || result.rowCount === undefined) {
51
54
  throw new Error("typesql: the executor did not report a row count: resolve to { rows, rowCount } (the adapters' executor() do)");
52
55
  }
package/dist/query.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import type { Col, Nullable, View } from "./column.js";
2
2
  /**
3
3
  * Relations of `pg_catalog` in the {@link PgCatalog} slice. Catalog tables are declared as
4
- * views too: typesql treats the whole slice as read-only.
4
+ * views too: TypeSQL treats the whole slice as read-only.
5
5
  */
6
6
  export interface PgCatalogNamespace {
7
7
  /** Table `pg_catalog.pg_am`: Access methods. */
@@ -0,0 +1,43 @@
1
+ /**
2
+ * What the adapters' `createSql(client)` returns: a `sql` whose queries run on `client`, and
3
+ * `sql.transaction(fn)`, which runs `fn` with a `sql` bound to one transaction.
4
+ */
5
+ import { type Sql } from "./create-sql.js";
6
+ import type { Executor } from "./query.js";
7
+ import type { RowKeys, SqlOptions } from "./row-keys.js";
8
+ /**
9
+ * Core's `Sql` plus `transaction(fn)`: `fn` gets `tx`, the same `sql` bound to one transaction.
10
+ * The transaction commits when `fn` resolves and rolls back when it throws (the error is
11
+ * rethrown). `tx.transaction(fn)` nests with a savepoint.
12
+ */
13
+ export interface TransactionalSql<DB, TM, Keys extends RowKeys = "preserve"> extends Sql<DB, TM, Keys> {
14
+ transaction<T>(fn: (tx: TransactionalSql<DB, TM, Keys>) => Promise<T>): Promise<T>;
15
+ }
16
+ /** How an adapter runs statements and transactions on its driver's client `C`. */
17
+ export interface SqlDriver<C> {
18
+ executor(client: C): Executor;
19
+ /**
20
+ * Run `fn` inside a transaction on `client` and resolve to its result: commit when `fn`
21
+ * resolves, roll back and rethrow when it throws. `fn` gets the client bound to the
22
+ * transaction. `depth` is how many `transaction` calls enclose this one (0 at the top), so a
23
+ * client that cannot tell can use a savepoint when `depth > 0`.
24
+ */
25
+ transaction<T>(client: C, depth: number, fn: (tx: C) => Promise<T>): Promise<T>;
26
+ }
27
+ /**
28
+ * For adapters: core's `createSql` bound to `driver.executor(client)`, with `transaction(fn)`
29
+ * running on `driver.transaction`. Without a `client`, `execute*` and `transaction` reject.
30
+ */
31
+ export declare function createTransactionalSql<DB, TM, C>(client: C | undefined, driver: SqlDriver<C>, options?: SqlOptions, depth?: number): TransactionalSql<DB, TM, RowKeys>;
32
+ /**
33
+ * For drivers without a transaction API: run `fn` between `BEGIN` and `COMMIT` sent with `run`
34
+ * on one connection, or between `SAVEPOINT name` and `RELEASE SAVEPOINT name` when `savepoint`
35
+ * is given (inside a transaction). When `fn` or the commit throws, it rolls back (`ROLLBACK`,
36
+ * `ROLLBACK TO SAVEPOINT name`) and rethrows that error; if the rollback fails too, its error is
37
+ * passed to `onRollbackError` and the original is still thrown. A failing `BEGIN` is thrown as is.
38
+ */
39
+ export declare function runTransaction<T>(run: (statement: string) => PromiseLike<unknown>, fn: () => Promise<T>, options?: {
40
+ readonly savepoint?: string;
41
+ readonly onRollbackError?: (err: unknown) => void;
42
+ }): Promise<T>;
43
+ //# sourceMappingURL=transaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAa,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEzD;;;;GAIG;AACH,MAAM,WAAW,gBAAgB,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,SAAS,OAAO,GAAG,UAAU,CAAE,SAAQ,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC;IACpG,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,gBAAgB,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACpF;AAED,kFAAkF;AAClF,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,QAAQ,CAAC;IAC9B;;;;;OAKG;IACH,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACjF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,UAAU,EAAE,KAAK,SAAI,GAAG,gBAAgB,CAAC,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,CAQjK;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,CAAC,EACpC,GAAG,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,WAAW,CAAC,OAAO,CAAC,EAChD,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,GAAE;IAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CAAO,GAC/F,OAAO,CAAC,CAAC,CAAC,CAeZ"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * What the adapters' `createSql(client)` returns: a `sql` whose queries run on `client`, and
3
+ * `sql.transaction(fn)`, which runs `fn` with a `sql` bound to one transaction.
4
+ */
5
+ import { createSql } from "./create-sql.js";
6
+ /**
7
+ * For adapters: core's `createSql` bound to `driver.executor(client)`, with `transaction(fn)`
8
+ * running on `driver.transaction`. Without a `client`, `execute*` and `transaction` reject.
9
+ */
10
+ export function createTransactionalSql(client, driver, options, depth = 0) {
11
+ const sql = createSql(client === undefined ? undefined : driver.executor(client), options);
12
+ return Object.assign(sql, {
13
+ transaction: async (fn) => {
14
+ if (client === undefined)
15
+ throw new Error("typesql: no client: pass one to createSql(client) to use sql.transaction()");
16
+ return driver.transaction(client, depth, (tx) => fn(createTransactionalSql(tx, driver, options, depth + 1)));
17
+ },
18
+ });
19
+ }
20
+ /**
21
+ * For drivers without a transaction API: run `fn` between `BEGIN` and `COMMIT` sent with `run`
22
+ * on one connection, or between `SAVEPOINT name` and `RELEASE SAVEPOINT name` when `savepoint`
23
+ * is given (inside a transaction). When `fn` or the commit throws, it rolls back (`ROLLBACK`,
24
+ * `ROLLBACK TO SAVEPOINT name`) and rethrows that error; if the rollback fails too, its error is
25
+ * passed to `onRollbackError` and the original is still thrown. A failing `BEGIN` is thrown as is.
26
+ */
27
+ export async function runTransaction(run, fn, options = {}) {
28
+ const sp = options.savepoint;
29
+ await run(sp ? `SAVEPOINT ${sp}` : "BEGIN");
30
+ try {
31
+ const result = await fn();
32
+ await run(sp ? `RELEASE SAVEPOINT ${sp}` : "COMMIT");
33
+ return result;
34
+ }
35
+ catch (err) {
36
+ try {
37
+ await run(sp ? `ROLLBACK TO SAVEPOINT ${sp}` : "ROLLBACK");
38
+ }
39
+ catch (rollbackErr) {
40
+ options.onRollbackError?.(rollbackErr);
41
+ }
42
+ throw err;
43
+ }
44
+ }
45
+ //# sourceMappingURL=transaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction.js","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,SAAS,EAAY,MAAM,iBAAiB,CAAC;AAyBtD;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAY,MAAqB,EAAE,MAAoB,EAAE,OAAoB,EAAE,KAAK,GAAG,CAAC;IAC5H,MAAM,GAAG,GAAG,SAAS,CAAS,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAA4C,CAAC,CAAC;IACxI,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE;QACxB,WAAW,EAAE,KAAK,EAAK,EAAyD,EAAc,EAAE;YAC9F,IAAI,MAAM,KAAK,SAAS;gBAAE,MAAM,IAAI,KAAK,CAAC,4EAA4E,CAAC,CAAC;YACxH,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,sBAAsB,CAAY,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1H,CAAC;KACF,CAAiD,CAAC;AACrD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAgD,EAChD,EAAoB,EACpB,OAAO,GAAuF,EAAE;IAEhG,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC;IAC7B,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC5C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,EAAE,EAAE,CAAC;QAC1B,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACrD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,yBAAyB,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QAC7D,CAAC;QAAC,OAAO,WAAW,EAAE,CAAC;YACrB,OAAO,CAAC,eAAe,EAAE,CAAC,WAAW,CAAC,CAAC;QACzC,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typesql/core",
3
- "version": "0.0.4",
3
+ "version": "0.1.1",
4
4
  "description": "Type-level Postgres SQL inference for TypeScript. Zero runtime dependencies.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -177,7 +177,7 @@ type NoSchema<S> = SqlError<`schema "${S & string}" does not exist`, null>;
177
177
  /** A new top-level relation would overwrite a schema (schemas and public relations share `DB`'s keys). */
178
178
  type SchemaClash<DB, Ref extends TableRef> =
179
179
  IsTop<Ref["schema"]> extends true
180
- ? Ref["name"] extends keyof DB ? SqlError<`cannot create relation "${Ref["name"]}": a schema with that name exists (typesql keeps schemas and public relations in one object)`, null> : null
180
+ ? Ref["name"] extends keyof DB ? SqlError<`cannot create relation "${Ref["name"]}": a schema with that name exists (TypeSQL keeps schemas and public relations in one object)`, null> : null
181
181
  : null;
182
182
  /** Checks before adding a relation at `Ref`: `null` when it can be added. */
183
183
  type CanCreate<DB, Ref extends TableRef> =
@@ -326,7 +326,7 @@ type NameCols<C extends Cols, Names, Acc extends Cols, TooMany extends string> =
326
326
  Names extends readonly [infer N extends string, ...infer NR]
327
327
  ? C extends readonly [infer H, ...infer R extends Cols]
328
328
  ? H extends { readonly n: string; readonly t: infer I extends ColInfo } ? NameCols<R, NR, [...Acc, { readonly n: N; readonly t: I }], TooMany>
329
- : SqlError<"a column name list cannot rename the columns of a * projection (typesql limitation): list the columns explicitly", null>
329
+ : SqlError<"a column name list cannot rename the columns of a * projection (TypeSQL limitation): list the columns explicitly", null>
330
330
  : SqlError<TooMany, null>
331
331
  : [...Acc, ...C];
332
332
 
@@ -585,7 +585,7 @@ type ApplyCreateSchema<DB, Ine, Name extends string> =
585
585
  Name extends "public" ? (Ine extends true ? DB : SqlError<`schema "public" already exists`, null>)
586
586
  : Name extends keyof DB
587
587
  ? IsSchemaKey<DB, Name> extends true ? (Ine extends true ? DB : SqlError<`schema "${Name}" already exists`, null>)
588
- : SqlError<`cannot create schema "${Name}": a relation with that name exists (typesql keeps schemas and public relations in one object)`, null>
588
+ : SqlError<`cannot create schema "${Name}": a relation with that name exists (TypeSQL keeps schemas and public relations in one object)`, null>
589
589
  : SetKey<DB, Name, {}>;
590
590
 
591
591
  type ApplyDropSchemas<DB, Names, Ife, Casc> =
package/src/index.ts CHANGED
@@ -21,4 +21,5 @@ export type { Tok, Toks, WordTok, IdTok, NumTok, StrTok, ParamTok, OpTok } from
21
21
  export type { ParseProgram, ParseStatement } from "./engine/parser/stmt.js";
22
22
  export type * as Ast from "./engine/ast.js";
23
23
  export type { ColInfo, ColMap, Cols, Scope, ScopeItem, ScopeLevel, Ctx, CteMap } from "./engine/types.js";
24
+ export { createTransactionalSql, runTransaction, type TransactionalSql, type SqlDriver } from "./transaction.js";
24
25
  export { Migrations, migrations, runMigrations, type MigrationClient, type SchemaOf, type ValidateDdl } from "./migrations.js";
package/src/query.ts CHANGED
@@ -24,9 +24,12 @@ export interface ExecutorResult {
24
24
  * Runs a statement and resolves to its rows, or to `{ rows, rowCount }`. Adapters build one from a
25
25
  * driver client (`executor(pool)` in `@typesql/pg`, which reports `rowCount`); pass it to
26
26
  * `createSql(executor)` or to `q.execute(executor)`. An executor that resolves to the rows alone
27
- * works for everything except `executeCount`.
27
+ * works for everything except `executeCount`, unless it also has a `count` method.
28
28
  */
29
- export type Executor = (text: string, values: readonly unknown[]) => PromiseLike<readonly unknown[] | ExecutorResult>;
29
+ export type Executor = ((text: string, values: readonly unknown[]) => PromiseLike<readonly unknown[] | ExecutorResult>) & {
30
+ /** Runs the statement for its row count; `executeCount` uses it when present (Prisma's `$executeRaw`). */
31
+ readonly count?: (text: string, values: readonly unknown[]) => PromiseLike<number>;
32
+ };
30
33
 
31
34
  /** Thrown by `executeTakeFirstOrThrow` when the query returns no rows. */
32
35
  export class NoResultError extends Error {
@@ -81,7 +84,7 @@ const ROW_KEYS = Symbol.for("typesql.rowKeys");
81
84
  /**
82
85
  * Turn rows a driver returned for `q` into the rows `q`'s type describes: a fresh array, with
83
86
  * the keys renamed when `q` comes from `createSql(executor, { rowKeys: "camelCase" })`. For
84
- * helpers that run `q.text` themselves, such as the adapters' `query(client, q)`.
87
+ * helpers that run `q.text` themselves instead of through an `Executor`.
85
88
  */
86
89
  export function mapRows<Q extends { readonly text: string }>(q: Q, rows: readonly unknown[]): RowType<Q>[] {
87
90
  return applyRowKeys(rows, rowKeysOf(q)) as RowType<Q>[];
@@ -102,13 +105,13 @@ export function makeQuery<Row, Params extends readonly unknown[], Text extends s
102
105
  executor?: Executor,
103
106
  rowKeys: RowKeys = "preserve",
104
107
  ): Query<Row, Params, Text> {
105
- const run = async (given?: Executor): Promise<readonly unknown[] | ExecutorResult> => {
108
+ const pick = (given?: Executor): Executor => {
106
109
  const exec = given ?? executor;
107
110
  if (!exec) throw new Error("typesql: no executor: pass one to createSql(executor) or to execute(executor)");
108
- return exec(text, values);
111
+ return exec;
109
112
  };
110
113
  const execute = async (given?: Executor): Promise<unknown[]> => {
111
- const result = await run(given);
114
+ const result = await pick(given)(text, values);
112
115
  return applyRowKeys(isRows(result) ? result : result.rows, rowKeys);
113
116
  };
114
117
  return Object.defineProperties({ text, values }, {
@@ -116,7 +119,9 @@ export function makeQuery<Row, Params extends readonly unknown[], Text extends s
116
119
  execute: { value: execute },
117
120
  executeCount: {
118
121
  value: async (given?: Executor) => {
119
- const result = await run(given);
122
+ const exec = pick(given);
123
+ if (exec.count) return exec.count(text, values);
124
+ const result = await exec(text, values);
120
125
  if (isRows(result) || result.rowCount === undefined) {
121
126
  throw new Error("typesql: the executor did not report a row count: resolve to { rows, rowCount } (the adapters' executor() do)");
122
127
  }
@@ -3,7 +3,7 @@ import type { Col, Nullable, View } from "./column.js";
3
3
 
4
4
  /**
5
5
  * Relations of `pg_catalog` in the {@link PgCatalog} slice. Catalog tables are declared as
6
- * views too: typesql treats the whole slice as read-only.
6
+ * views too: TypeSQL treats the whole slice as read-only.
7
7
  */
8
8
  export interface PgCatalogNamespace {
9
9
  /** Table `pg_catalog.pg_am`: Access methods. */
@@ -0,0 +1,70 @@
1
+ /**
2
+ * What the adapters' `createSql(client)` returns: a `sql` whose queries run on `client`, and
3
+ * `sql.transaction(fn)`, which runs `fn` with a `sql` bound to one transaction.
4
+ */
5
+ import { createSql, type Sql } from "./create-sql.js";
6
+ import type { Executor } from "./query.js";
7
+ import type { RowKeys, SqlOptions } from "./row-keys.js";
8
+
9
+ /**
10
+ * Core's `Sql` plus `transaction(fn)`: `fn` gets `tx`, the same `sql` bound to one transaction.
11
+ * The transaction commits when `fn` resolves and rolls back when it throws (the error is
12
+ * rethrown). `tx.transaction(fn)` nests with a savepoint.
13
+ */
14
+ export interface TransactionalSql<DB, TM, Keys extends RowKeys = "preserve"> extends Sql<DB, TM, Keys> {
15
+ transaction<T>(fn: (tx: TransactionalSql<DB, TM, Keys>) => Promise<T>): Promise<T>;
16
+ }
17
+
18
+ /** How an adapter runs statements and transactions on its driver's client `C`. */
19
+ export interface SqlDriver<C> {
20
+ executor(client: C): Executor;
21
+ /**
22
+ * Run `fn` inside a transaction on `client` and resolve to its result: commit when `fn`
23
+ * resolves, roll back and rethrow when it throws. `fn` gets the client bound to the
24
+ * transaction. `depth` is how many `transaction` calls enclose this one (0 at the top), so a
25
+ * client that cannot tell can use a savepoint when `depth > 0`.
26
+ */
27
+ transaction<T>(client: C, depth: number, fn: (tx: C) => Promise<T>): Promise<T>;
28
+ }
29
+
30
+ /**
31
+ * For adapters: core's `createSql` bound to `driver.executor(client)`, with `transaction(fn)`
32
+ * running on `driver.transaction`. Without a `client`, `execute*` and `transaction` reject.
33
+ */
34
+ export function createTransactionalSql<DB, TM, C>(client: C | undefined, driver: SqlDriver<C>, options?: SqlOptions, depth = 0): TransactionalSql<DB, TM, RowKeys> {
35
+ const sql = createSql<DB, TM>(client === undefined ? undefined : driver.executor(client), options as { readonly rowKeys: "camelCase" });
36
+ return Object.assign(sql, {
37
+ transaction: async <T>(fn: (tx: TransactionalSql<DB, TM, RowKeys>) => Promise<T>): Promise<T> => {
38
+ if (client === undefined) throw new Error("typesql: no client: pass one to createSql(client) to use sql.transaction()");
39
+ return driver.transaction(client, depth, (tx) => fn(createTransactionalSql<DB, TM, C>(tx, driver, options, depth + 1)));
40
+ },
41
+ }) as unknown as TransactionalSql<DB, TM, RowKeys>;
42
+ }
43
+
44
+ /**
45
+ * For drivers without a transaction API: run `fn` between `BEGIN` and `COMMIT` sent with `run`
46
+ * on one connection, or between `SAVEPOINT name` and `RELEASE SAVEPOINT name` when `savepoint`
47
+ * is given (inside a transaction). When `fn` or the commit throws, it rolls back (`ROLLBACK`,
48
+ * `ROLLBACK TO SAVEPOINT name`) and rethrows that error; if the rollback fails too, its error is
49
+ * passed to `onRollbackError` and the original is still thrown. A failing `BEGIN` is thrown as is.
50
+ */
51
+ export async function runTransaction<T>(
52
+ run: (statement: string) => PromiseLike<unknown>,
53
+ fn: () => Promise<T>,
54
+ options: { readonly savepoint?: string; readonly onRollbackError?: (err: unknown) => void } = {},
55
+ ): Promise<T> {
56
+ const sp = options.savepoint;
57
+ await run(sp ? `SAVEPOINT ${sp}` : "BEGIN");
58
+ try {
59
+ const result = await fn();
60
+ await run(sp ? `RELEASE SAVEPOINT ${sp}` : "COMMIT");
61
+ return result;
62
+ } catch (err) {
63
+ try {
64
+ await run(sp ? `ROLLBACK TO SAVEPOINT ${sp}` : "ROLLBACK");
65
+ } catch (rollbackErr) {
66
+ options.onRollbackError?.(rollbackErr);
67
+ }
68
+ throw err;
69
+ }
70
+ }