orange-orm 4.9.0 → 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 (42) hide show
  1. package/README.md +120 -0
  2. package/deno.lock +76 -0
  3. package/dist/index.browser.mjs +192 -56
  4. package/dist/index.mjs +292 -179
  5. package/docs/changelog.md +2 -0
  6. package/other.db +0 -0
  7. package/package.json +1 -1
  8. package/src/bunPg/newDatabase.js +3 -14
  9. package/src/bunPg/newTransaction.js +1 -0
  10. package/src/bunSqlite/newDatabase.js +22 -13
  11. package/src/bunSqlite/newTransaction.js +1 -0
  12. package/src/client/index.js +8 -1
  13. package/src/client/netAdapter.js +13 -2
  14. package/src/d1/newDatabase.js +3 -13
  15. package/src/d1/newTransaction.js +1 -0
  16. package/src/getTSDefinition.js +14 -1
  17. package/src/hostExpress.js +8 -3
  18. package/src/hostLocal.js +66 -6
  19. package/src/map2.d.ts +18 -1
  20. package/src/mssql/newDatabase.js +3 -13
  21. package/src/mssql/newTransaction.js +1 -0
  22. package/src/mySql/newDatabase.js +3 -13
  23. package/src/mySql/newTransaction.js +1 -0
  24. package/src/nodeSqlite/newDatabase.js +29 -18
  25. package/src/nodeSqlite/newTransaction.js +1 -0
  26. package/src/oracle/newDatabase.js +3 -13
  27. package/src/oracle/newTransaction.js +1 -0
  28. package/src/pg/newDatabase.js +4 -16
  29. package/src/pg/newTransaction.js +1 -0
  30. package/src/pglite/newDatabase.js +3 -14
  31. package/src/pglite/newTransaction.js +1 -0
  32. package/src/sap/newDatabase.js +3 -13
  33. package/src/sap/newTransaction.js +1 -0
  34. package/src/sqlite3/newDatabase.js +22 -13
  35. package/src/sqlite3/newTransaction.js +1 -0
  36. package/src/sqliteFunction.js +20 -0
  37. package/src/table/begin.js +0 -1
  38. package/src/table/commit.js +21 -1
  39. package/src/table/query/singleQuery/joinSql/newShallowJoinSqlCore.js +6 -4
  40. package/src/table/rollback.js +22 -2
  41. package/src/tedious/newDatabase.js +3 -13
  42. 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
+ }