orange-orm 4.9.1 → 4.10.0-beta.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 (40) hide show
  1. package/README.md +120 -0
  2. package/deno.lock +76 -0
  3. package/dist/index.browser.mjs +187 -53
  4. package/dist/index.mjs +287 -176
  5. package/other.db +0 -0
  6. package/package.json +1 -1
  7. package/src/bunPg/newDatabase.js +3 -14
  8. package/src/bunPg/newTransaction.js +1 -0
  9. package/src/bunSqlite/newDatabase.js +22 -13
  10. package/src/bunSqlite/newTransaction.js +1 -0
  11. package/src/client/index.js +8 -1
  12. package/src/client/netAdapter.js +13 -2
  13. package/src/d1/newDatabase.js +3 -13
  14. package/src/d1/newTransaction.js +1 -0
  15. package/src/getTSDefinition.js +14 -1
  16. package/src/hostExpress.js +8 -3
  17. package/src/hostLocal.js +66 -6
  18. package/src/map2.d.ts +18 -1
  19. package/src/mssql/newDatabase.js +3 -13
  20. package/src/mssql/newTransaction.js +1 -0
  21. package/src/mySql/newDatabase.js +3 -13
  22. package/src/mySql/newTransaction.js +1 -0
  23. package/src/nodeSqlite/newDatabase.js +29 -18
  24. package/src/nodeSqlite/newTransaction.js +1 -0
  25. package/src/oracle/newDatabase.js +3 -13
  26. package/src/oracle/newTransaction.js +1 -0
  27. package/src/pg/newDatabase.js +4 -16
  28. package/src/pg/newTransaction.js +1 -0
  29. package/src/pglite/newDatabase.js +3 -14
  30. package/src/pglite/newTransaction.js +1 -0
  31. package/src/sap/newDatabase.js +3 -13
  32. package/src/sap/newTransaction.js +1 -0
  33. package/src/sqlite3/newDatabase.js +22 -13
  34. package/src/sqlite3/newTransaction.js +1 -0
  35. package/src/sqliteFunction.js +20 -0
  36. package/src/table/begin.js +0 -1
  37. package/src/table/commit.js +21 -1
  38. package/src/table/rollback.js +22 -2
  39. package/src/tedious/newDatabase.js +3 -13
  40. package/src/tedious/newTransaction.js +1 -0
package/README.md CHANGED
@@ -309,6 +309,31 @@ await pool.close(); // closes all pooled connections
309
309
  __Why close ?__
310
310
  In serverless environments (e.g. AWS Lambda, Vercel, Cloudflare Workers) execution contexts are frequently frozen and resumed. Explicitly closing the client or pool ensures that file handles are released promptly and prevents “database locked” errors between invocations.
311
311
 
312
+ __SQLite user-defined functions__
313
+ You can register custom SQLite functions on the connection using `db.function(name, fn)`.
314
+
315
+ ```javascript
316
+ import map from './map';
317
+ const db = map.sqlite('demo.db');
318
+
319
+ db.function('add_prefix', (text, prefix) => `${prefix}${text}`);
320
+
321
+ const rows = await db.query(
322
+ "select id, name, add_prefix(name, '[VIP] ') as prefixedName from customer"
323
+ );
324
+ ```
325
+
326
+ If you need the function inside a transaction, register it within the transaction callback to ensure it is available on that connection.
327
+
328
+ ```javascript
329
+ await db.transaction(async (db) => {
330
+ db.function('add_prefix', (text, prefix) => `${prefix}${text}`);
331
+ return db.query(
332
+ "select id, name, add_prefix(name, '[VIP] ') as prefixedName from customer"
333
+ );
334
+ });
335
+ ```
336
+
312
337
  __From the browser__
313
338
  You can securely use Orange from the browser by utilizing the Express plugin, which serves to safeguard sensitive database credentials from exposure at the client level. This technique bypasses the need to transmit raw SQL queries directly from the client to the server. Instead, it logs method calls initiated by the client, which are later replayed and authenticated on the server. This not only reinforces security by preventing the disclosure of raw SQL queries on the client side but also facilitates a smoother operation. Essentially, this method mirrors a traditional REST API, augmented with advanced TypeScript tooling for enhanced functionality. You can read more about it in the section called [In the browser](#user-content-in-the-browser)
314
339
  <sub>📄 server.ts</sub>
@@ -1273,6 +1298,101 @@ async function updateRows() {
1273
1298
 
1274
1299
  }
1275
1300
 
1301
+ ```
1302
+
1303
+ __Row Level Security (Postgres)__
1304
+ You can enforce tenant isolation at the database level by combining Postgres RLS with Express hooks. The example below mirrors the “Interceptors and base filter” style by putting the tenant id in a (fake) token on the client, then extracting it on the server and setting it inside the transaction. This is convenient for a demo because we can seed data and prove rows are filtered. In a real application you must validate signatures and derive tenant id from a trusted identity source, not from arbitrary client input.
1305
+
1306
+ <sub>📄 setup.sql</sub>
1307
+
1308
+ ```sql
1309
+ create role rls_app_user nologin;
1310
+
1311
+ create table tenant_data (
1312
+ id serial primary key,
1313
+ tenant_id int not null,
1314
+ value text not null
1315
+ );
1316
+
1317
+ alter table tenant_data enable row level security;
1318
+ create policy tenant_data_tenant on tenant_data
1319
+ using (tenant_id = current_setting('app.tenant_id', true)::int);
1320
+
1321
+ grant select, insert, update, delete on tenant_data to rls_app_user;
1322
+
1323
+ insert into tenant_data (tenant_id, value) values
1324
+ (1, 'alpha'),
1325
+ (1, 'beta'),
1326
+ (2, 'gamma');
1327
+ ```
1328
+
1329
+ <sub>📄 server.ts</sub>
1330
+
1331
+ ```javascript
1332
+ import map from './map';
1333
+ import { json } from 'body-parser';
1334
+ import express from 'express';
1335
+ import cors from 'cors';
1336
+
1337
+ const db = map.postgres('postgres://postgres:postgres@localhost/postgres');
1338
+
1339
+ express().disable('x-powered-by')
1340
+ .use(json({ limit: '100mb' }))
1341
+ .use(cors())
1342
+ .use('/orange', validateToken)
1343
+ .use('/orange', db.express({
1344
+ hooks: {
1345
+ transaction: {
1346
+ afterBegin: async (db, req) => {
1347
+ const tenantId = Number.parseInt(String(req.user?.tenantId ?? ''), 10);
1348
+ if (!Number.isFinite(tenantId)) throw new Error('Missing tenant id');
1349
+ await db.query('set local role rls_app_user');
1350
+ await db.query({
1351
+ sql: 'select set_config(\'app.tenant_id\', ?, true)',
1352
+ parameters: [String(tenantId)]
1353
+ });
1354
+ }
1355
+ }
1356
+ }
1357
+ }))
1358
+ .listen(3000, () => console.log('Example app listening on port 3000!'));
1359
+
1360
+ function validateToken(req, res, next) {
1361
+ const authHeader = req.headers.authorization;
1362
+ if (!authHeader) return res.status(401).json({ error: 'Authorization header missing' });
1363
+ try {
1364
+ const token = authHeader.replace(/^Bearer\s+/i, '');
1365
+ const payload = decodeFakeJwt(token); // demo-only, do not use in production
1366
+ req.user = { tenantId: String(payload.tenantId) };
1367
+ return next();
1368
+ } catch (error) {
1369
+ return res.status(401).json({ error: 'Invalid token' });
1370
+ }
1371
+ }
1372
+
1373
+ function decodeFakeJwt(token) {
1374
+ // Demo-only format: "tenant:<id>"
1375
+ const match = /^tenant:(\d+)$/.exec(token);
1376
+ if (!match) throw new Error('Invalid demo token');
1377
+ return { tenantId: Number(match[1]) };
1378
+ }
1379
+ ```
1380
+
1381
+ <sub>📄 browser.ts</sub>
1382
+
1383
+ ```javascript
1384
+ import map from './map';
1385
+
1386
+ const db = map.http('http://localhost:3000/orange');
1387
+
1388
+ db.interceptors.request.use((config) => {
1389
+ // Demo-only token: payload carries the tenant id so we can verify filtering
1390
+ config.headers.Authorization = 'Bearer tenant:1';
1391
+ return config;
1392
+ });
1393
+
1394
+ const rows = await db.tenant_data.getMany();
1395
+ // rows => [{ id: 1, tenant_id: 1, value: 'alpha' }, { id: 2, tenant_id: 1, value: 'beta' }]
1276
1396
  ```
1277
1397
  </details>
1278
1398
 
package/deno.lock ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "version": "5",
3
+ "specifiers": {
4
+ "jsr:@std/assert@*": "1.0.17",
5
+ "jsr:@std/assert@^1.0.17": "1.0.17",
6
+ "jsr:@std/internal@^1.0.12": "1.0.12",
7
+ "jsr:@std/path@*": "1.1.4",
8
+ "jsr:@std/testing@*": "1.0.17"
9
+ },
10
+ "jsr": {
11
+ "@std/assert@1.0.17": {
12
+ "integrity": "df5ebfffe77c03b3fa1401e11c762cc8f603d51021c56c4d15a8c7ab45e90dbe",
13
+ "dependencies": [
14
+ "jsr:@std/internal"
15
+ ]
16
+ },
17
+ "@std/internal@1.0.12": {
18
+ "integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027"
19
+ },
20
+ "@std/path@1.1.4": {
21
+ "integrity": "1d2d43f39efb1b42f0b1882a25486647cb851481862dc7313390b2bb044314b5",
22
+ "dependencies": [
23
+ "jsr:@std/internal"
24
+ ]
25
+ },
26
+ "@std/testing@1.0.17": {
27
+ "integrity": "87bdc2700fa98249d48a17cd72413352d3d3680dcfbdb64947fd0982d6bbf681",
28
+ "dependencies": [
29
+ "jsr:@std/assert@^1.0.17",
30
+ "jsr:@std/internal"
31
+ ]
32
+ }
33
+ },
34
+ "workspace": {
35
+ "packageJson": {
36
+ "dependencies": [
37
+ "npm:@cloudflare/workers-types@^4.20241106.0",
38
+ "npm:@electric-sql/pglite@0.3",
39
+ "npm:@lroal/on-change@^4.0.2",
40
+ "npm:@rollup/plugin-commonjs@^28.0.2",
41
+ "npm:@rollup/plugin-json@^6.1.0",
42
+ "npm:@rollup/plugin-node-resolve@13",
43
+ "npm:@tediousjs/connection-string@~0.4.1",
44
+ "npm:@types/express@^4.17.13",
45
+ "npm:@types/oracledb@^6.0.4",
46
+ "npm:@types/tedious@^4.0.14",
47
+ "npm:@typescript-eslint/eslint-plugin@6",
48
+ "npm:@typescript-eslint/parser@6",
49
+ "npm:@vitest/coverage-v8@^3.2.4",
50
+ "npm:ajv@^8.17.1",
51
+ "npm:axios@^1.6.2",
52
+ "npm:better-sqlite3@^11.8.1",
53
+ "npm:cors@^2.8.5",
54
+ "npm:eslint-plugin-jest@^27.1.7",
55
+ "npm:eslint@^8.57.0",
56
+ "npm:express@^4.18.2",
57
+ "npm:fast-json-patch@^3.1.1",
58
+ "npm:findup-sync@5",
59
+ "npm:glob@^10.3.4 || ^11.0.2",
60
+ "npm:module-definition@4 || 5 || * || 6",
61
+ "npm:msnodesqlv8@^4.1.0",
62
+ "npm:mysql2@^3.9.4",
63
+ "npm:oracledb@^6.3.0",
64
+ "npm:owasp-dependency-check@^0.0.21",
65
+ "npm:pg-query-stream@^3.3.2",
66
+ "npm:pg@^8.5.1",
67
+ "npm:rfdc@^1.2.0",
68
+ "npm:rollup@^2.52.7",
69
+ "npm:tedious@19",
70
+ "npm:typescript@^5.4.5",
71
+ "npm:uuid@^8.3.2 || 9 || 10 || ^11.1.0",
72
+ "npm:vitest@^3.2.4"
73
+ ]
74
+ }
75
+ }
76
+ }
@@ -465,6 +465,7 @@ export interface ExpressConfig {
465
465
  concurrency?: Concurrency;
466
466
  readonly?: boolean;
467
467
  disableBulkDeletes?: boolean;
468
+ hooks?: ExpressHooks;
468
469
  }
469
470
 
470
471
  export interface ExpressContext {
@@ -473,6 +474,18 @@ export interface ExpressContext {
473
474
  client: RdbClient;
474
475
  }
475
476
 
477
+ export interface ExpressTransactionHooks {
478
+ beforeBegin?: (db: Pool, request: import('express').Request, response: import('express').Response) => void | Promise<void>;
479
+ afterBegin?: (db: Pool, request: import('express').Request, response: import('express').Response) => void | Promise<void>;
480
+ beforeCommit?: (db: Pool, request: import('express').Request, response: import('express').Response) => void | Promise<void>;
481
+ afterCommit?: (db: Pool, request: import('express').Request, response: import('express').Response) => void | Promise<void>;
482
+ afterRollback?: (db: Pool, request: import('express').Request, response: import('express').Response, error?: unknown) => void | Promise<void>;
483
+ }
484
+
485
+ export interface ExpressHooks extends ExpressTransactionHooks {
486
+ transaction?: ExpressTransactionHooks;
487
+ }
488
+
476
489
  export interface ExpressTables {${getExpressTables()}
477
490
  }
478
491
  `;
@@ -610,13 +623,18 @@ function requireHostExpress () {
610
623
  const dbOptions = { db: options.db || client.db };
611
624
  let c = {};
612
625
  const readonly = { readonly: options.readonly};
626
+ const sharedHooks = options.hooks;
613
627
  for (let tableName in client.tables) {
628
+ const tableOptions = options[tableName] || {};
629
+ const hooks = tableOptions.hooks || sharedHooks;
614
630
  c[tableName] = hostLocal({
615
631
  ...dbOptions,
616
632
  ...readonly,
617
- ...options[tableName],
633
+ ...tableOptions,
618
634
  table: client.tables[tableName],
619
- isHttp: true, client
635
+ isHttp: true,
636
+ client,
637
+ hooks
620
638
 
621
639
  });
622
640
  }
@@ -2200,6 +2218,35 @@ function requireQuery () {
2200
2218
  return query;
2201
2219
  }
2202
2220
 
2221
+ var sqliteFunction;
2222
+ var hasRequiredSqliteFunction;
2223
+
2224
+ function requireSqliteFunction () {
2225
+ if (hasRequiredSqliteFunction) return sqliteFunction;
2226
+ hasRequiredSqliteFunction = 1;
2227
+ const executeChanges = requireExecuteChanges();
2228
+ const popChanges = requirePopChanges();
2229
+ const getSessionSingleton = requireGetSessionSingleton();
2230
+
2231
+ function executeQueries(context, ...rest) {
2232
+ var changes = popChanges(context);
2233
+
2234
+ return executeChanges(context, changes).then(onDoneChanges);
2235
+
2236
+ function onDoneChanges() {
2237
+ var client = getSessionSingleton(context, 'dbClient');
2238
+ if (client && typeof client.function === 'function')
2239
+ return client.function.apply(client, rest);
2240
+ if (client && typeof client.createFunction === 'function')
2241
+ return client.createFunction.apply(client, rest);
2242
+ throw new Error('SQLite client does not support user-defined functions');
2243
+ }
2244
+ }
2245
+
2246
+ sqliteFunction = executeQueries;
2247
+ return sqliteFunction;
2248
+ }
2249
+
2203
2250
  var hostLocal_1;
2204
2251
  var hasRequiredHostLocal;
2205
2252
 
@@ -2210,6 +2257,7 @@ function requireHostLocal () {
2210
2257
  let getMeta = requireGetMeta();
2211
2258
  let setSessionSingleton = requireSetSessionSingleton();
2212
2259
  let executeQuery = requireQuery();
2260
+ let executeSqliteFunction = requireSqliteFunction();
2213
2261
  let hostExpress = requireHostExpress();
2214
2262
  const readonlyOps = ['getManyDto', 'getMany', 'aggregate', 'count'];
2215
2263
  // { db, table, defaultConcurrency,
@@ -2220,9 +2268,12 @@ function requireHostLocal () {
2220
2268
  // disableBulkDeletes, isBrowser }
2221
2269
  function hostLocal() {
2222
2270
  const _options = arguments[0];
2223
- let { table, transaction, db, isHttp } = _options;
2271
+ let { table, transaction, db, isHttp, hooks, client } = _options;
2272
+ const transactionHooks = hooks && hooks.transaction;
2273
+ const getTransactionHook = (name) =>
2274
+ (transactionHooks && transactionHooks[name]) || (hooks && hooks[name]);
2224
2275
 
2225
- let c = { get, post, patch, query, express };
2276
+ let c = { get, post, patch, query, sqliteFunction, express };
2226
2277
 
2227
2278
  function get() {
2228
2279
  return getMeta(table);
@@ -2273,10 +2324,41 @@ function requireHostLocal () {
2273
2324
  else
2274
2325
  db = dbPromise;
2275
2326
  }
2276
- if (readonlyOps.includes(body.path))
2327
+ const beforeBegin = getTransactionHook('beforeBegin');
2328
+ const afterBegin = getTransactionHook('afterBegin');
2329
+ const beforeCommit = getTransactionHook('beforeCommit');
2330
+ const afterCommit = getTransactionHook('afterCommit');
2331
+ const afterRollback = getTransactionHook('afterRollback');
2332
+ const hasTransactionHooks = !!(beforeBegin
2333
+ || afterBegin
2334
+ || beforeCommit
2335
+ || afterCommit
2336
+ || afterRollback);
2337
+ if (!hasTransactionHooks && readonlyOps.includes(body.path))
2277
2338
  await db.transaction({ readonly: true }, fn);
2278
- else
2279
- await db.transaction(fn);
2339
+ else {
2340
+ await db.transaction(async (context) => {
2341
+ const hookDb = typeof client === 'function'
2342
+ ? client({ transaction: (fn) => fn(context) })
2343
+ : (client || db);
2344
+ if (afterCommit)
2345
+ setSessionSingleton(context, 'afterCommitHook', () =>
2346
+ afterCommit(hookDb, request, response)
2347
+ );
2348
+ if (afterRollback)
2349
+ setSessionSingleton(context, 'afterRollbackHook', (error) =>
2350
+ afterRollback(hookDb, request, response, error)
2351
+ );
2352
+ if (beforeBegin)
2353
+ await beforeBegin(hookDb, request, response);
2354
+ if (afterBegin)
2355
+ await afterBegin(hookDb, request, response);
2356
+ await fn(context);
2357
+ if (beforeCommit)
2358
+ await beforeCommit(hookDb, request, response);
2359
+ });
2360
+ }
2361
+
2280
2362
  }
2281
2363
  return result;
2282
2364
 
@@ -2311,6 +2393,31 @@ function requireHostLocal () {
2311
2393
 
2312
2394
  }
2313
2395
 
2396
+ async function sqliteFunction() {
2397
+ let args = arguments;
2398
+ let result;
2399
+
2400
+ if (transaction)
2401
+ await transaction(fn);
2402
+ else {
2403
+ if (typeof db === 'function') {
2404
+ let dbPromise = db();
2405
+ if (dbPromise.then)
2406
+ db = await dbPromise;
2407
+ else
2408
+ db = dbPromise;
2409
+ }
2410
+ result = await db.sqliteFunction.apply(null, arguments);
2411
+ }
2412
+
2413
+ return result;
2414
+
2415
+ async function fn(...args1) {
2416
+ result = await executeSqliteFunction.apply(null, [...args1, ...args]);
2417
+ }
2418
+
2419
+ }
2420
+
2314
2421
  function express(client, options) {
2315
2422
  return hostExpress(hostLocal, client, options);
2316
2423
  }
@@ -2401,6 +2508,7 @@ function requireNetAdapter () {
2401
2508
  post,
2402
2509
  patch,
2403
2510
  query,
2511
+ sqliteFunction,
2404
2512
  express
2405
2513
  };
2406
2514
 
@@ -2456,6 +2564,10 @@ function requireNetAdapter () {
2456
2564
  throw new Error('Queries are not supported through http');
2457
2565
  }
2458
2566
 
2567
+ function sqliteFunction() {
2568
+ throw new Error('Sqlite Function is not supported through http');
2569
+ }
2570
+
2459
2571
  function express() {
2460
2572
  throw new Error('Hosting in express is not supported on the client side');
2461
2573
  }
@@ -2469,7 +2581,8 @@ function requireNetAdapter () {
2469
2581
  get,
2470
2582
  post,
2471
2583
  patch,
2472
- query
2584
+ query,
2585
+ sqliteFunction
2473
2586
  };
2474
2587
 
2475
2588
  return c;
@@ -2494,6 +2607,11 @@ function requireNetAdapter () {
2494
2607
  return adapter.query.apply(null, arguments);
2495
2608
  }
2496
2609
 
2610
+ async function sqliteFunction() {
2611
+ const adapter = await getInnerAdapter();
2612
+ return adapter.sqliteFunction.apply(null, arguments);
2613
+ }
2614
+
2497
2615
  async function getInnerAdapter() {
2498
2616
  const db = await getDb();
2499
2617
  if (typeof db === 'string') {
@@ -2782,6 +2900,7 @@ function requireClient () {
2782
2900
  }
2783
2901
  };
2784
2902
  client.query = query;
2903
+ client.function = sqliteFunction;
2785
2904
  client.transaction = runInTransaction;
2786
2905
  client.db = baseUrl;
2787
2906
  client.mssql = onProvider.bind(null, 'mssql');
@@ -2870,6 +2989,11 @@ function requireClient () {
2870
2989
  return adapter.query.apply(null, arguments);
2871
2990
  }
2872
2991
 
2992
+ async function sqliteFunction() {
2993
+ const adapter = netAdapter(baseUrl, undefined, { tableOptions: { db: baseUrl, transaction } });
2994
+ return adapter.sqliteFunction.apply(null, arguments);
2995
+ }
2996
+
2873
2997
  function express(arg) {
2874
2998
  if (providers.express) {
2875
2999
  return providers.express(client, { ...options, ...arg });
@@ -3553,6 +3677,7 @@ function requireClient () {
3553
3677
  return;
3554
3678
 
3555
3679
  let body = stringify({ patch, options: { ...tableOptions, ...concurrencyOptions, strategy, deduceStrategy } });
3680
+
3556
3681
  let adapter = netAdapter(url, tableName, { axios: axiosInterceptor, tableOptions });
3557
3682
  let { changed, strategy: newStrategy } = await adapter.patch(body);
3558
3683
  copyInto(changed, [row]);
@@ -12938,14 +13063,34 @@ function requireCommit () {
12938
13063
  const getSessionSingleton = requireGetSessionSingleton();
12939
13064
 
12940
13065
  function _commit(context, result) {
13066
+ let hookError;
12941
13067
  return popAndPushChanges()
13068
+ .then(callAfterCommit)
12942
13069
  .then(releaseDbClient.bind(null, context))
12943
- .then(onReleased);
13070
+ .then(onReleased)
13071
+ .then(throwHookErrorIfAny);
12944
13072
 
12945
13073
  function onReleased() {
12946
13074
  return result;
12947
13075
  }
12948
13076
 
13077
+ function throwHookErrorIfAny(res) {
13078
+ if (hookError)
13079
+ throw hookError;
13080
+ return res;
13081
+ }
13082
+
13083
+ function callAfterCommit() {
13084
+ const hook = getSessionSingleton(context, 'afterCommitHook');
13085
+ if (!hook)
13086
+ return Promise.resolve();
13087
+ return Promise.resolve()
13088
+ .then(() => hook())
13089
+ .catch((e) => {
13090
+ hookError = e;
13091
+ });
13092
+ }
13093
+
12949
13094
  async function popAndPushChanges() {
12950
13095
  let changes = popChanges(context);
12951
13096
  while (changes.length > 0) {
@@ -13046,10 +13191,13 @@ function requireRollback () {
13046
13191
  const getSessionSingleton = requireGetSessionSingleton();
13047
13192
 
13048
13193
  function _rollback(context, e) {
13194
+ let hookError;
13049
13195
  var chain = resultToPromise()
13050
13196
  .then(() => popChanges(context))
13051
13197
  .then(executeRollback)
13052
- .then(() => releaseDbClient(context));
13198
+ .then(callAfterRollback)
13199
+ .then(() => releaseDbClient(context))
13200
+ .then(throwHookErrorIfAny);
13053
13201
 
13054
13202
 
13055
13203
  function executeRollback() {
@@ -13059,6 +13207,23 @@ function requireRollback () {
13059
13207
  return executeQuery(context, rollbackCommand);
13060
13208
  }
13061
13209
 
13210
+ function callAfterRollback() {
13211
+ const hook = getSessionSingleton(context, 'afterRollbackHook');
13212
+ if (!hook)
13213
+ return Promise.resolve();
13214
+ return Promise.resolve()
13215
+ .then(() => hook(e))
13216
+ .catch((err) => {
13217
+ hookError = err;
13218
+ });
13219
+ }
13220
+
13221
+ function throwHookErrorIfAny(res) {
13222
+ if (hookError)
13223
+ throw hookError;
13224
+ return res;
13225
+ }
13226
+
13062
13227
  if (e) {
13063
13228
  if (e.message?.indexOf('ORA-01476: divisor is equal to zero') > -1)
13064
13229
  return newThrow(context, new Error('Conflict when updating a column'), chain);
@@ -13940,6 +14105,7 @@ function requireNewTransaction$2 () {
13940
14105
  rdb.aggregateCount = 0;
13941
14106
  rdb.quote = (name) => `"${name}"`;
13942
14107
  rdb.cache = {};
14108
+ rdb.changes = [];
13943
14109
 
13944
14110
  if (readonly) {
13945
14111
  rdb.dbClient = {
@@ -14043,7 +14209,6 @@ function requireBegin () {
14043
14209
  let setSessionSingleton = requireSetSessionSingleton();
14044
14210
 
14045
14211
  function begin(context, transactionLess) {
14046
- setSessionSingleton(context, 'changes', []);
14047
14212
  if (transactionLess) {
14048
14213
  setSessionSingleton(context, 'transactionLess', true);
14049
14214
  return Promise.resolve();
@@ -14840,7 +15005,6 @@ function requireNewDatabase$2 () {
14840
15005
  let hostLocal = requireHostLocal();
14841
15006
  let doQuery = requireQuery();
14842
15007
  let releaseDbClient = requireReleaseDbClient();
14843
- let setSessionSingleton = requireSetSessionSingleton();
14844
15008
 
14845
15009
  function newDatabase(d1Database, poolOptions) {
14846
15010
  if (!d1Database)
@@ -14857,10 +15021,9 @@ function requireNewDatabase$2 () {
14857
15021
  }
14858
15022
  let domain = createDomain();
14859
15023
 
14860
- if (fn)
14861
- return domain.run(runInTransaction);
14862
- else
14863
- return domain.run(run);
15024
+ if (!fn)
15025
+ throw new Error('transaction requires a function');
15026
+ return domain.run(runInTransaction);
14864
15027
 
14865
15028
  async function runInTransaction() {
14866
15029
  let result;
@@ -14879,13 +15042,6 @@ function requireNewDatabase$2 () {
14879
15042
  return _begin(domain, transactionLess);
14880
15043
  }
14881
15044
 
14882
- function run() {
14883
- let p;
14884
- let transaction = newTransaction(domain, pool, options);
14885
- p = new Promise(transaction);
14886
-
14887
- return p.then(begin);
14888
- }
14889
15045
 
14890
15046
  };
14891
15047
 
@@ -14913,7 +15069,6 @@ function requireNewDatabase$2 () {
14913
15069
  let domain = createDomain();
14914
15070
  let transaction = newTransaction(domain, pool);
14915
15071
  let p = domain.run(() => new Promise(transaction)
14916
- .then(() => setSessionSingleton(domain, 'changes', []))
14917
15072
  .then(() => doQuery(domain, query).then(onResult, onError)));
14918
15073
  return p;
14919
15074
 
@@ -15358,6 +15513,7 @@ function requireNewTransaction$1 () {
15358
15513
  rdb.aggregateCount = 0;
15359
15514
  rdb.quote = quote;
15360
15515
  rdb.cache = {};
15516
+ rdb.changes = [];
15361
15517
 
15362
15518
  if (readonly) {
15363
15519
  rdb.dbClient = {
@@ -15600,7 +15756,6 @@ function requireNewDatabase$1 () {
15600
15756
  let hostLocal = requireHostLocal();
15601
15757
  let doQuery = requireQuery();
15602
15758
  let releaseDbClient = requireReleaseDbClient();
15603
- let setSessionSingleton = requireSetSessionSingleton();
15604
15759
 
15605
15760
  function newDatabase(connectionString, poolOptions) {
15606
15761
  poolOptions = poolOptions || { min: 1 };
@@ -15615,10 +15770,9 @@ function requireNewDatabase$1 () {
15615
15770
  }
15616
15771
  let domain = createDomain();
15617
15772
 
15618
- if (fn)
15619
- return domain.run(runInTransaction);
15620
- else
15621
- return domain.run(run);
15773
+ if (!fn)
15774
+ throw new Error('transaction requires a function');
15775
+ return domain.run(runInTransaction);
15622
15776
 
15623
15777
  async function runInTransaction() {
15624
15778
  let result;
@@ -15637,14 +15791,6 @@ function requireNewDatabase$1 () {
15637
15791
  return _begin(domain, options);
15638
15792
  }
15639
15793
 
15640
- function run() {
15641
- let p;
15642
- let transaction = newTransaction(domain, pool, options);
15643
- p = new Promise(transaction);
15644
-
15645
- return p.then(begin)
15646
- .then(negotiateSchema);
15647
- }
15648
15794
 
15649
15795
  function negotiateSchema(previous) {
15650
15796
  let schema = options && options.schema;
@@ -15685,7 +15831,6 @@ function requireNewDatabase$1 () {
15685
15831
  let domain = createDomain();
15686
15832
  let transaction = newTransaction(domain, pool);
15687
15833
  let p = domain.run(() => new Promise(transaction)
15688
- .then(() => setSessionSingleton(domain, 'changes', []))
15689
15834
  .then(() => doQuery(domain, query).then(onResult, onError)));
15690
15835
  return p;
15691
15836
 
@@ -15887,6 +16032,7 @@ function requireNewTransaction () {
15887
16032
  rdb.aggregateCount = 0;
15888
16033
  rdb.quote = quote;
15889
16034
  rdb.cache = {};
16035
+ rdb.changes = [];
15890
16036
 
15891
16037
  if (readonly) {
15892
16038
  rdb.dbClient = {
@@ -16152,7 +16298,6 @@ function requireNewDatabase () {
16152
16298
  let hostLocal = requireHostLocal();
16153
16299
  let doQuery = requireQuery();
16154
16300
  let releaseDbClient = requireReleaseDbClient();
16155
- let setSessionSingleton = requireSetSessionSingleton();
16156
16301
 
16157
16302
  function newDatabase(connectionString, poolOptions) {
16158
16303
  if (!connectionString)
@@ -16169,10 +16314,9 @@ function requireNewDatabase () {
16169
16314
  }
16170
16315
  let domain = createDomain();
16171
16316
 
16172
- if (fn)
16173
- return domain.run(runInTransaction);
16174
- else
16175
- return domain.run(run);
16317
+ if (!fn)
16318
+ throw new Error('transaction requires a function');
16319
+ return domain.run(runInTransaction);
16176
16320
 
16177
16321
  async function runInTransaction() {
16178
16322
  let result;
@@ -16191,15 +16335,6 @@ function requireNewDatabase () {
16191
16335
  return _begin(domain, options);
16192
16336
  }
16193
16337
 
16194
- function run() {
16195
- let p;
16196
- let transaction = newTransaction(domain, pool, options);
16197
- p = new Promise(transaction);
16198
-
16199
- return p.then(begin)
16200
- .then(negotiateSchema);
16201
- }
16202
-
16203
16338
  function negotiateSchema(previous) {
16204
16339
  let schema = options && options.schema;
16205
16340
  if (!schema)
@@ -16239,7 +16374,6 @@ function requireNewDatabase () {
16239
16374
  let domain = createDomain();
16240
16375
  let transaction = newTransaction(domain, pool);
16241
16376
  let p = domain.run(() => new Promise(transaction)
16242
- .then(() => setSessionSingleton(domain, 'changes', []))
16243
16377
  .then(() => doQuery(domain, query).then(onResult, onError)));
16244
16378
  return p;
16245
16379