@deterministic-code/datasources-sqlite 0.0.0-stage → 0.1.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.
package/package.json CHANGED
@@ -1,6 +1,22 @@
1
1
  {
2
2
  "name": "@deterministic-code/datasources-sqlite",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
7
+ "exports": {
8
+ ".": "./src/index.ts"
9
+ },
10
+ "files": [
11
+ "src"
12
+ ],
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "license": "MIT",
17
+ "dependencies": {
18
+ "@deterministic-code/datasources": "^0.1.0",
19
+ "@types/better-sqlite3": "^7.6.13",
20
+ "better-sqlite3": "^12.10.0"
21
+ }
22
+ }
@@ -0,0 +1,24 @@
1
+ import { CreateTableStatement, CreateTableStatementTextRenderer, PrimaryKey, type Column } from "@deterministic-code/datasources";
2
+ import { columnTypes } from "./columnTypes.ts";
3
+
4
+ export class SqliteCreateTableStatementRenderer extends CreateTableStatementTextRenderer {
5
+ override render(statement: CreateTableStatement): string {
6
+ const identity = new Set(statement.columns.filter((column) => column.identity).map((column) => column.name));
7
+ const constraints = statement.constraints.filter(
8
+ (constraint) => !(constraint instanceof PrimaryKey) || !constraint.columns.every((column) => identity.has(column)),
9
+ );
10
+ return super.render(
11
+ constraints.length === statement.constraints.length
12
+ ? statement
13
+ : new CreateTableStatement(statement.table, statement.columns, constraints),
14
+ );
15
+ }
16
+
17
+ protected typeName(column: Column): string {
18
+ return columnTypes[column.type] ?? column.type;
19
+ }
20
+
21
+ protected identity(column: Column): string {
22
+ return column.identity ? " PRIMARY KEY AUTOINCREMENT" : "";
23
+ }
24
+ }
@@ -0,0 +1,53 @@
1
+ import {
2
+ DatasourceError,
3
+ StatementTextRenderer,
4
+ type Cell,
5
+ type DatasourceOptions,
6
+ type ICommand,
7
+ type IConnection,
8
+ type IDriver,
9
+ type Information,
10
+ type ITransaction,
11
+ type IsolationLevel,
12
+ type TypeConverter,
13
+ } from "@deterministic-code/datasources";
14
+ import { SqliteConnection } from "./connection.ts";
15
+ import { SqliteCommand } from "./command.ts";
16
+ import { SqliteTransaction } from "./transaction.ts";
17
+ import { SqliteRowTypeConverters } from "./converters/SqliteRowTypeConverters.ts";
18
+ import { SqliteStatementRenderer } from "./SqliteStatementRenderer.ts";
19
+ import { columnTypes } from "./columnTypes.ts";
20
+
21
+ export { columnTypes };
22
+
23
+ const statements = new StatementTextRenderer(t => new SqliteStatementRenderer(t));
24
+
25
+ export class SqliteDriver implements IDriver {
26
+ readonly statementRenderer = statements;
27
+ readonly information: Information = {
28
+ descriptions: [
29
+ "Embedded SQL database for local development and tests.",
30
+ "Full connection, command, transaction, and repository support.",
31
+ ],
32
+ overrides: [
33
+ "Overrides SelectInsertedIdStatement to SELECT last_insert_rowid() AS {column}",
34
+ "Overrides CreateTableStatement type mapping (int→INTEGER, bool→BOOLEAN, uuid/string/datetime→TEXT, decimal→NUMERIC, float→REAL, binary→BLOB)",
35
+ ],
36
+ changeLogs: [],
37
+ };
38
+ rowTypeConverters: Record<string, TypeConverter<Cell, Cell>> = SqliteRowTypeConverters;
39
+ columnType = (name: string): string =>
40
+ columnTypes[name] ??
41
+ (() => {
42
+ throw new DatasourceError(`Unknown sqlite type "${name}"`);
43
+ })();
44
+ connection = (options?: DatasourceOptions): IConnection =>
45
+ SqliteConnection.from(options);
46
+ command = (connection: IConnection): ICommand =>
47
+ new SqliteCommand(connection as SqliteConnection, statements);
48
+ transaction = (
49
+ connection: IConnection,
50
+ isolation: IsolationLevel = "readCommitted",
51
+ ): ITransaction =>
52
+ new SqliteTransaction(connection as SqliteConnection, isolation);
53
+ }
@@ -0,0 +1,28 @@
1
+ import {
2
+ CreateTableStatement,
3
+ SelectInsertedIdStatement,
4
+ SelectInsertedIdStatementTextRenderer,
5
+ StatementRenderer,
6
+ type StatementTextRenderer,
7
+ } from "@deterministic-code/datasources";
8
+ import { SqliteCreateTableStatementRenderer } from "./SqliteCreateTableStatementRenderer.ts";
9
+
10
+ class SqliteSelectInsertedIdStatementTextRenderer extends SelectInsertedIdStatementTextRenderer {
11
+ override render(statement: SelectInsertedIdStatement): string {
12
+ return `SELECT last_insert_rowid() AS ${statement.column}`;
13
+ }
14
+ }
15
+
16
+ export class SqliteStatementRenderer extends StatementRenderer {
17
+ constructor(text: StatementTextRenderer) {
18
+ super(text);
19
+ }
20
+
21
+ protected override visitSelectInsertedId(inserted: SelectInsertedIdStatement): string {
22
+ return new SqliteSelectInsertedIdStatementTextRenderer().render(inserted);
23
+ }
24
+
25
+ protected override visitCreateTable(create: CreateTableStatement): string {
26
+ return new SqliteCreateTableStatementRenderer().render(create);
27
+ }
28
+ }
@@ -0,0 +1,10 @@
1
+ export const columnTypes: Record<string, string> = {
2
+ int: "INTEGER",
3
+ uuid: "TEXT",
4
+ string: "TEXT",
5
+ bool: "BOOLEAN",
6
+ decimal: "NUMERIC",
7
+ float: "REAL",
8
+ datetime: "TEXT",
9
+ binary: "BLOB",
10
+ };
package/src/command.ts ADDED
@@ -0,0 +1,231 @@
1
+ import {
2
+ ArrayReader,
3
+ CommandMiddlewareHost,
4
+ FirstResultReader,
5
+ DatasourceError,
6
+ Native,
7
+ ReturnStatement,
8
+ SelectStatement,
9
+ StatementTextRenderer,
10
+ parametersToRecord,
11
+ Response,
12
+ splitStatements,
13
+ statementParameters,
14
+ StreamingReader,
15
+ type Cell,
16
+ type Cells,
17
+ type Scalar,
18
+ type ICommand,
19
+ type IReader,
20
+ type IStatement,
21
+ type Parameter,
22
+ } from "@deterministic-code/datasources";
23
+ import type { SqliteConnection } from "./connection.ts";
24
+ import type { SqliteStatement } from "./i-sqlite-driver.ts";
25
+ import { reverse, SqliteRowTypeConverters } from "./converters/SqliteRowTypeConverters.ts";
26
+ import { SqliteStatementRenderer } from "./SqliteStatementRenderer.ts";
27
+
28
+ const bound = (parameters?: Parameter[]): Record<string, Scalar> => {
29
+ const record = parametersToRecord(parameters);
30
+ return Object.fromEntries(
31
+ Object.entries(record).map(([name, value]) => [name, reverse(value)]),
32
+ ) as Record<string, Scalar>;
33
+ };
34
+
35
+ const toSqlite = (sql: string, params: Record<string, Scalar>): { text: string; values: Scalar[] } => {
36
+ const values: Scalar[] = [];
37
+ const text = sql.replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (_match, name: string) => {
38
+ values.push(params[name] ?? null);
39
+ return "?";
40
+ });
41
+ return { text, values };
42
+ };
43
+
44
+ export class SqliteCommand extends CommandMiddlewareHost implements ICommand {
45
+ private readonly statements: StatementTextRenderer;
46
+
47
+ constructor(readonly connection: SqliteConnection, statements?: StatementTextRenderer) {
48
+ super();
49
+ this.statements = statements ?? new StatementTextRenderer(t => new SqliteStatementRenderer(t));
50
+ }
51
+
52
+ async executeReaderNative(
53
+ text: Native,
54
+ parameters?: Parameter[],
55
+ ): Promise<IReader<Cells>> {
56
+ return this.run(text.text, parameters, async (query, params) => {
57
+ if (!query.trim()) return { value: new ArrayReader([]), rows: [] };
58
+ this.connection.ensureOpen();
59
+ const statements = splitStatements(query);
60
+ if (statements.length === 0) return { value: new ArrayReader([]), rows: [] };
61
+ const paramsBound = bound(params);
62
+ let index = 0;
63
+ let iter = this.iterate(statements[0] as string, paramsBound);
64
+ return FirstResultReader.capture(
65
+ new StreamingReader(
66
+ async () => {
67
+ const step = iter.next();
68
+ return step.done ? undefined : step.value;
69
+ },
70
+ async () => {
71
+ iter.return();
72
+ index += 1;
73
+ const sql = statements[index];
74
+ if (!sql) return false;
75
+ iter = this.iterate(sql, paramsBound);
76
+ return true;
77
+ },
78
+ async () => {
79
+ iter.return();
80
+ },
81
+ ),
82
+ );
83
+ });
84
+ }
85
+
86
+ async executeReaderStatement(
87
+ statement: IStatement,
88
+ parameters?: Parameter[],
89
+ ): Promise<IReader<Cells>> {
90
+ return this.executeReaderNative(new Native(this.statements.render(statement)), parameters);
91
+ }
92
+
93
+ async executeStatement(statement: IStatement, parameters?: Parameter[]): Promise<Response> {
94
+ return statement instanceof SelectStatement || statement instanceof ReturnStatement
95
+ ? this.firstCell(statement, parameters)
96
+ : this.executeNative(new Native(this.statements.render(statement)), parameters);
97
+ }
98
+
99
+ private async firstCell(statement: IStatement, parameters?: Parameter[]): Promise<Response> {
100
+ const reader = await this.executeReaderStatement(statement, parameters);
101
+ try {
102
+ if (!(await reader.next())) return new Response(null, 0, parameters);
103
+ const row = reader.current;
104
+ /* v8 ignore next */
105
+ if (!row) throw new DatasourceError("Reader current must be a row");
106
+ const values = Object.values(row);
107
+ return new Response(values.length === 0 ? null : values[0], 1, parameters);
108
+ } finally {
109
+ await reader.dispose();
110
+ }
111
+ }
112
+
113
+ async executeNative(text: Native, parameters?: Parameter[]): Promise<Response> {
114
+ return this.run(text.text, parameters, async (query, params) => {
115
+ if (!query.trim()) return { value: new Response(undefined, 0, params), rows: [] };
116
+ this.connection.ensureOpen();
117
+ const db = this.connection.require();
118
+ const named = bound(params);
119
+ let changes = 0;
120
+ for (const sql of splitStatements(query)) {
121
+ const names = new Set(statementParameters(sql));
122
+ const used = Object.fromEntries(
123
+ Object.entries(named).filter(([name]) => names.has(name.replace(/^[$:@]/, ""))),
124
+ );
125
+ const { text, values } = toSqlite(sql, used);
126
+ changes += db.prepare(text).run(...values).changes;
127
+ }
128
+ return { value: new Response(null, changes, params), rows: [] };
129
+ });
130
+ }
131
+
132
+ private iterate(sql: string, params: Record<string, Scalar>): IterableIterator<Cells> & {
133
+ return(): IteratorResult<Cells>;
134
+ } {
135
+ const statement = this.connection.require().prepare(sql) as ColumnStatement;
136
+ const columns = statement.columns?.() ?? [];
137
+ const names = qualified(columns);
138
+ const declared = columns.map((column) => column.type ?? null);
139
+ const raw = names.some((name, index) => name !== columns[index]?.name)
140
+ ? statement.raw?.(true).iterate(params)
141
+ : undefined;
142
+ const rows = raw ? namedRows(raw, names, declared) : closable(statement.iterate(params), columns);
143
+ return rows[Symbol.iterator]();
144
+ }
145
+ }
146
+
147
+ type SqliteColumn = { name: string; column: string | null; table: string | null; type?: string | null };
148
+
149
+ const booleanDeclared = (declared?: string | null): boolean =>
150
+ declared?.toUpperCase() === "BOOLEAN";
151
+
152
+ const cell = (value: Cell | null | undefined, declared?: string | null): Cell | null =>
153
+ value == null ? null : booleanDeclared(declared) ? SqliteRowTypeConverters.bool.convert(value) : value;
154
+
155
+ type ColumnStatement = SqliteStatement & {
156
+ columns?: () => SqliteColumn[];
157
+ raw?: (enabled: boolean) => {
158
+ iterate(params?: Record<string, Scalar>): IterableIterator<Cell[]>;
159
+ };
160
+ };
161
+
162
+ const closable = (
163
+ iter: IterableIterator<Cells>,
164
+ columns: SqliteColumn[],
165
+ ): IterableIterator<Cells> & { return(): IteratorResult<Cells> } => {
166
+ const stop = iter.return ? iter.return.bind(iter) : undefined;
167
+ const rows: IterableIterator<Cells> & { return(): IteratorResult<Cells> } = {
168
+ next: () => {
169
+ const step = iter.next();
170
+ if (step.done || !step.value) return step;
171
+ const value = Object.fromEntries(
172
+ Object.entries(step.value).map(([name, item]) => [
173
+ name,
174
+ cell(item, columns.find((column) => column.name === name)?.type),
175
+ ]),
176
+ ) as Cells;
177
+ return { done: false, value };
178
+ },
179
+ return: () => {
180
+ if (stop) stop();
181
+ return { done: true, value: undefined };
182
+ },
183
+ [Symbol.iterator]() {
184
+ return rows;
185
+ },
186
+ };
187
+ return rows;
188
+ };
189
+
190
+ const namedRows = (
191
+ iter: IterableIterator<Cell[]>,
192
+ names: string[],
193
+ declared: (string | null)[],
194
+ ): IterableIterator<Cells> & { return(): IteratorResult<Cells> } => {
195
+ /* v8 ignore next */
196
+ const stop = iter.return ? iter.return.bind(iter) : undefined;
197
+ const rows: IterableIterator<Cells> & { return(): IteratorResult<Cells> } = {
198
+ next: () => {
199
+ const step = iter.next();
200
+ return step.done
201
+ ? { done: true, value: undefined }
202
+ : {
203
+ done: false,
204
+ value: Object.fromEntries(
205
+ names.map((name, index) => [name, cell(step.value[index], declared[index])]),
206
+ ),
207
+ };
208
+ },
209
+ return: () => {
210
+ /* v8 ignore next */
211
+ if (stop) stop();
212
+ return { done: true, value: undefined };
213
+ },
214
+ [Symbol.iterator]() {
215
+ return rows;
216
+ },
217
+ };
218
+ return rows;
219
+ };
220
+
221
+ const qualified = (columns: SqliteColumn[]): string[] => {
222
+ const names = columns.map((column) => column.name);
223
+ const duplicated = new Set(names.filter((name, index) => names.indexOf(name) !== index));
224
+ return duplicated.size === 0
225
+ ? names
226
+ : columns.map((column, index) =>
227
+ duplicated.has(names[index]!) && column.table && column.column
228
+ ? `${column.table}.${column.column}`
229
+ : names[index]!
230
+ );
231
+ };
@@ -0,0 +1,7 @@
1
+ import type { SqliteDatabaseCtor } from "./i-sqlite-driver.ts";
2
+
3
+ export interface SqliteConnectionOptions {
4
+ filename?: string;
5
+ url?: string;
6
+ Database?: SqliteDatabaseCtor;
7
+ }
@@ -0,0 +1,83 @@
1
+ import {
2
+ DatasourceError,
3
+ IsolationLevel,
4
+ StatementTextRenderer,
5
+ type DatasourceOptions,
6
+ type ICommand,
7
+ type IConnection,
8
+ type IsolationLevelName,
9
+ type ITransaction,
10
+ } from "@deterministic-code/datasources";
11
+ import { createRequire } from "node:module";
12
+ import { SqliteCommand } from "./command.ts";
13
+ import type { SqliteConnectionOptions } from "./connection-options.ts";
14
+ import type { SqliteDatabase, SqliteDatabaseCtor } from "./i-sqlite-driver.ts";
15
+ import { filenameOf } from "./sqlitePath.ts";
16
+ import { SqliteStatementRenderer } from "./SqliteStatementRenderer.ts";
17
+ import { SqliteTransaction } from "./transaction.ts";
18
+
19
+ const require = createRequire(import.meta.url);
20
+
21
+ const pragma = (isolation: IsolationLevelName): string =>
22
+ isolation === IsolationLevel.ReadUncommitted
23
+ ? "PRAGMA read_uncommitted = 1"
24
+ : "PRAGMA read_uncommitted = 0";
25
+
26
+ export class SqliteConnection implements IConnection {
27
+ readonly provider = "sqlite";
28
+ filename: string;
29
+ isOpen = false;
30
+ private db?: SqliteDatabase;
31
+ private readonly Database?: SqliteDatabaseCtor;
32
+
33
+ static from = (options?: DatasourceOptions): SqliteConnection =>
34
+ new SqliteConnection({
35
+ url: options?.DATASOURCE_URL,
36
+ Database: require("better-sqlite3") as SqliteDatabaseCtor,
37
+ });
38
+
39
+ constructor(options: SqliteConnectionOptions = {}) {
40
+ this.filename = filenameOf(options);
41
+ this.Database = options.Database;
42
+ }
43
+
44
+ createCommand = (): ICommand =>
45
+ new SqliteCommand(this, new StatementTextRenderer(t => new SqliteStatementRenderer(t)));
46
+
47
+ async open(): Promise<void> {
48
+ if (this.isOpen) throw new DatasourceError("Connection is already open");
49
+ if (!this.Database) throw new DatasourceError("SQLite driver is required");
50
+ this.db = new this.Database(this.filename, { timeout: 0 });
51
+ this.isOpen = true;
52
+ }
53
+
54
+ async close(): Promise<void> {
55
+ this.db?.close();
56
+ this.db = undefined;
57
+ this.isOpen = false;
58
+ }
59
+
60
+ [Symbol.asyncDispose] = (): Promise<void> => this.close();
61
+
62
+ async startTransaction(
63
+ isolation: IsolationLevelName = IsolationLevel.ReadCommitted,
64
+ ): Promise<ITransaction> {
65
+ this.ensureOpen();
66
+ this.exec(pragma(isolation));
67
+ this.exec("BEGIN");
68
+ return new SqliteTransaction(this, isolation);
69
+ }
70
+
71
+ ensureOpen(): void {
72
+ if (!this.isOpen) throw new DatasourceError("Connection is not open");
73
+ }
74
+
75
+ require(): SqliteDatabase {
76
+ if (!this.db) throw new DatasourceError("Connection is not open");
77
+ return this.db;
78
+ }
79
+
80
+ exec(sql: string): void {
81
+ this.require().exec(sql);
82
+ }
83
+ }
@@ -0,0 +1,6 @@
1
+ import { TypeConverter, type Cell } from "@deterministic-code/datasources";
2
+
3
+ export class BinaryConverter extends TypeConverter<Cell, Cell> {
4
+ convert = (from: Cell): Cell => from;
5
+ reverseConvert = (to: Cell): Cell => to;
6
+ }
@@ -0,0 +1,6 @@
1
+ import { TypeConverter, type Cell } from "@deterministic-code/datasources";
2
+
3
+ export class BoolConverter extends TypeConverter<Cell, Cell> {
4
+ convert = (from: Cell): Cell => (typeof from === "number" ? from !== 0 : from);
5
+ reverseConvert = (to: Cell): Cell => (typeof to === "boolean" ? (to ? 1 : 0) : to);
6
+ }
@@ -0,0 +1,8 @@
1
+ import { TypeConverter, type Cell } from "@deterministic-code/datasources";
2
+
3
+ export class DateTimeConverter extends TypeConverter<Cell, Cell> {
4
+ convert = (from: Cell): Cell =>
5
+ typeof from === "string" ? new Date(from) : from;
6
+ reverseConvert = (to: Cell): Cell =>
7
+ to instanceof Date ? to.toISOString() : to;
8
+ }
@@ -0,0 +1,7 @@
1
+ import { TypeConverter, type Cell } from "@deterministic-code/datasources";
2
+
3
+ export class DecimalConverter extends TypeConverter<Cell, Cell> {
4
+ convert = (from: Cell): Cell => from;
5
+ reverseConvert = (to: Cell): Cell =>
6
+ typeof to === "number" ? String(to) : to;
7
+ }
@@ -0,0 +1,24 @@
1
+ import { type Cell, type TypeConverter } from "@deterministic-code/datasources";
2
+ import { BinaryConverter } from "./BinaryConverter.ts";
3
+ import { BoolConverter } from "./BoolConverter.ts";
4
+ import { DateTimeConverter } from "./DateTimeConverter.ts";
5
+ import { DecimalConverter } from "./DecimalConverter.ts";
6
+ import { UuidConverter } from "./UuidConverter.ts";
7
+
8
+ export const SqliteRowTypeConverters: Record<string, TypeConverter<Cell, Cell>> = {
9
+ bool: new BoolConverter(),
10
+ datetime: new DateTimeConverter(),
11
+ uuid: new UuidConverter(),
12
+ decimal: new DecimalConverter(),
13
+ binary: new BinaryConverter(),
14
+ };
15
+
16
+ export const reverse = (value: Cell): Cell =>
17
+ typeof value === "boolean"
18
+ ? SqliteRowTypeConverters.bool.reverseConvert(value)
19
+ : value instanceof Date
20
+ ? SqliteRowTypeConverters.datetime.reverseConvert(value)
21
+ : value instanceof Uint8Array
22
+ ? SqliteRowTypeConverters.binary.reverseConvert(value)
23
+ : value;
24
+
@@ -0,0 +1,6 @@
1
+ import { TypeConverter, type Cell } from "@deterministic-code/datasources";
2
+
3
+ export class UuidConverter extends TypeConverter<Cell, Cell> {
4
+ convert = (from: Cell): Cell => from;
5
+ reverseConvert = (to: Cell): Cell => to;
6
+ }
@@ -0,0 +1,21 @@
1
+ import type { Cell, Cells, Scalar } from "@deterministic-code/datasources";
2
+
3
+ export interface SqliteStatement {
4
+ run(params?: Record<string, Scalar>): {
5
+ changes: number;
6
+ lastInsertRowid?: Cell;
7
+ };
8
+ all(params?: Record<string, Scalar>): Cells[];
9
+ iterate(params?: Record<string, Scalar>): IterableIterator<Cells>;
10
+ }
11
+
12
+ export interface SqliteDatabase {
13
+ prepare(sql: string): SqliteStatement;
14
+ exec(sql: string): void;
15
+ close(): void;
16
+ }
17
+
18
+ export type SqliteDatabaseCtor = new (
19
+ filename: string,
20
+ options?: { timeout?: number },
21
+ ) => SqliteDatabase;
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { SqliteDriver } from "./SqliteDriver.ts";
2
+ export { SqliteStatementRenderer } from "./SqliteStatementRenderer.ts";
@@ -0,0 +1,8 @@
1
+ export const sqliteFilename = (url: string): string | undefined => {
2
+ if (!/^sqlite:/i.test(url)) return undefined;
3
+ const path = url.replace(/^sqlite:(?:\/\/\/|\/\/)?/i, "");
4
+ return path === "" || path === ":memory" || path === ":memory:" ? ":memory:" : path;
5
+ };
6
+
7
+ export const filenameOf = (options: { filename?: string; url?: string }): string =>
8
+ options.filename ?? (options.url ? sqliteFilename(options.url) ?? ":memory:" : ":memory:");
@@ -0,0 +1,21 @@
1
+ import {
2
+ IsolationLevel,
3
+ type ITransaction,
4
+ type IsolationLevelName,
5
+ } from "@deterministic-code/datasources";
6
+ import type { SqliteConnection } from "./connection.ts";
7
+
8
+ export class SqliteTransaction implements ITransaction {
9
+ constructor(
10
+ readonly connection: SqliteConnection,
11
+ readonly isolation: IsolationLevelName = IsolationLevel.ReadCommitted,
12
+ ) {}
13
+
14
+ async commit(): Promise<void> {
15
+ this.connection.exec("COMMIT");
16
+ }
17
+
18
+ async abort(): Promise<void> {
19
+ this.connection.exec("ROLLBACK");
20
+ }
21
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.