orange-orm 5.0.0-beta.1 β 5.0.0-beta.2
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/README.md +86 -242
- package/dist/index.browser.mjs +1 -31
- package/dist/index.mjs +2 -32
- package/package.json +1 -1
- package/src/map.d.ts +4 -46
- package/src/map2.d.ts +9 -19
- package/src/table/column.js +2 -32
- package/src/tedious/outputInsertedSql.js +2 -2
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ Watch the [tutorial video on YouTube](https://youtu.be/1IwwjPr2lMs)
|
|
|
51
51
|

|
|
52
52
|
|
|
53
53
|
<sub>π map.ts</sub>
|
|
54
|
-
```
|
|
54
|
+
```javascript
|
|
55
55
|
import orange from 'orange-orm';
|
|
56
56
|
|
|
57
57
|
const map = orange.map(x => ({
|
|
@@ -88,7 +88,7 @@ const map = orange.map(x => ({
|
|
|
88
88
|
street: column('street').string(),
|
|
89
89
|
postalCode: column('postalCode').string(),
|
|
90
90
|
postalPlace: column('postalPlace').string(),
|
|
91
|
-
countryCode: column('countryCode').string()
|
|
91
|
+
countryCode: column('countryCode').string(),
|
|
92
92
|
}))
|
|
93
93
|
|
|
94
94
|
})).map(x => ({
|
|
@@ -106,7 +106,7 @@ export default map;
|
|
|
106
106
|
```
|
|
107
107
|
<sub>π update.ts</sub>
|
|
108
108
|
|
|
109
|
-
```
|
|
109
|
+
```javascript
|
|
110
110
|
import map from './map';
|
|
111
111
|
const db = map.sqlite('demo.db');
|
|
112
112
|
|
|
@@ -127,14 +127,14 @@ async function updateRow() {
|
|
|
127
127
|
```
|
|
128
128
|
<sub>π filter.ts</sub>
|
|
129
129
|
|
|
130
|
-
```
|
|
130
|
+
```javascript
|
|
131
131
|
import map from './map';
|
|
132
132
|
const db = map.sqlite('demo.db');
|
|
133
133
|
|
|
134
134
|
getRows();
|
|
135
135
|
|
|
136
136
|
async function getRows() {
|
|
137
|
-
const orders = await db.order.
|
|
137
|
+
const orders = await db.order.getAll({
|
|
138
138
|
where: x => x.lines.any(line => line.product.contains('broomstick'))
|
|
139
139
|
.and(x.customer.name.startsWith('Harry')),
|
|
140
140
|
lines: {
|
|
@@ -157,8 +157,7 @@ Each column within your database table is designated by using the <strong><i>col
|
|
|
157
157
|
Relationships between tables can also be outlined. By using methods like <strong><i>hasOne</i></strong>, <strong><i>hasMany</i></strong>, and <strong><i>references</i></strong>, you can establish connections that reflect the relationships in your data schema. In the example below, an 'order' is linked to a 'customer' reference, a 'deliveryAddress', and multiple 'lines'. The hasMany and hasOne relations represents ownership - the tables 'deliveryAddress' and 'orderLine' are owned by the 'order' table, and therefore, they contain the 'orderId' column referring to their parent table, which is 'order'. The similar relationship exists between orderLine and package - hence the packages are owned by the orderLine. Conversely, the customer table is independent and can exist without any knowledge of the 'order' table. Therefore we say that the order table <i>references</i> the customer table - necessitating the existence of a 'customerId' column in the 'order' table.</p>
|
|
158
158
|
|
|
159
159
|
<sub>π map.ts</sub>
|
|
160
|
-
|
|
161
|
-
```ts
|
|
160
|
+
```javascript
|
|
162
161
|
import orange from 'orange-orm';
|
|
163
162
|
|
|
164
163
|
const map = orange.map(x => ({
|
|
@@ -194,7 +193,7 @@ const map = orange.map(x => ({
|
|
|
194
193
|
street: column('street').string(),
|
|
195
194
|
postalCode: column('postalCode').string(),
|
|
196
195
|
postalPlace: column('postalPlace').string(),
|
|
197
|
-
countryCode: column('countryCode').string()
|
|
196
|
+
countryCode: column('countryCode').string(),
|
|
198
197
|
}))
|
|
199
198
|
|
|
200
199
|
})).map(x => ({
|
|
@@ -219,8 +218,7 @@ Then, we define a SQL string. This string outlines the structure of our SQLite d
|
|
|
219
218
|
Because of a peculiarity in SQLite, which only allows one statement execution at a time, we split this SQL string into separate statements. We do this using the split() method, which breaks up the string at every semicolon.
|
|
220
219
|
|
|
221
220
|
<sub>π init.ts</sub>
|
|
222
|
-
|
|
223
|
-
```ts
|
|
221
|
+
```javascript
|
|
224
222
|
import map from './map';
|
|
225
223
|
const db = map.sqlite('demo.db');
|
|
226
224
|
|
|
@@ -312,13 +310,34 @@ __Why close ?__
|
|
|
312
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.
|
|
313
311
|
|
|
314
312
|
__SQLite user-defined functions__
|
|
315
|
-
You can register custom SQLite functions using `db.function(name, fn)`.
|
|
316
|
-
|
|
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
|
+
```
|
|
317
336
|
|
|
318
337
|
__From the browser__
|
|
319
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)
|
|
320
339
|
<sub>π server.ts</sub>
|
|
321
|
-
```
|
|
340
|
+
```javascript
|
|
322
341
|
import map from './map';
|
|
323
342
|
import { json } from 'body-parser';
|
|
324
343
|
import express from 'express';
|
|
@@ -335,7 +354,7 @@ express().disable('x-powered-by')
|
|
|
335
354
|
```
|
|
336
355
|
|
|
337
356
|
<sub>π browser.ts</sub>
|
|
338
|
-
```
|
|
357
|
+
```javascript
|
|
339
358
|
import map from './map';
|
|
340
359
|
|
|
341
360
|
const db = map.http('http://localhost:3000/orange');
|
|
@@ -413,7 +432,7 @@ database_id = "<your-guid-for-the-database>"
|
|
|
413
432
|
```
|
|
414
433
|
|
|
415
434
|
<sub>π src/index.ts</sub>
|
|
416
|
-
```
|
|
435
|
+
```javascript
|
|
417
436
|
import map from './map';
|
|
418
437
|
|
|
419
438
|
export interface Env {
|
|
@@ -424,7 +443,7 @@ export interface Env {
|
|
|
424
443
|
export default {
|
|
425
444
|
async fetch(request, env): Promise<Response> {
|
|
426
445
|
const db = map.d1(env.DB);
|
|
427
|
-
const customers = await db.customer.
|
|
446
|
+
const customers = await db.customer.getAll();
|
|
428
447
|
return Response.json(customers);
|
|
429
448
|
},
|
|
430
449
|
} satisfies ExportedHandler<Env>;
|
|
@@ -592,7 +611,7 @@ const db = map.sqlite('demo.db');
|
|
|
592
611
|
getRows();
|
|
593
612
|
|
|
594
613
|
async function getRows() {
|
|
595
|
-
const orders = await db.order.
|
|
614
|
+
const orders = await db.order.getAll({
|
|
596
615
|
customer: true,
|
|
597
616
|
deliveryAddress: true,
|
|
598
617
|
lines: {
|
|
@@ -611,7 +630,7 @@ const db = map.sqlite('demo.db');
|
|
|
611
630
|
getRows();
|
|
612
631
|
|
|
613
632
|
async function getRows() {
|
|
614
|
-
const orders = await db.order.
|
|
633
|
+
const orders = await db.order.getAll({
|
|
615
634
|
offset: 1,
|
|
616
635
|
orderBy: ['orderDate desc', 'id'],
|
|
617
636
|
limit: 10,
|
|
@@ -643,7 +662,7 @@ const db = map.sqlite('demo.db');
|
|
|
643
662
|
getRows();
|
|
644
663
|
|
|
645
664
|
async function getRows() {
|
|
646
|
-
const orders = await db.order.
|
|
665
|
+
const orders = await db.order.getAll({
|
|
647
666
|
numberOfLines: x => x.count(x => x.lines.id),
|
|
648
667
|
totalAmount: x => x.sum(x => lines.amount),
|
|
649
668
|
balance: x => x.customer.balance
|
|
@@ -660,7 +679,7 @@ const db = map.sqlite('demo.db');
|
|
|
660
679
|
getRows();
|
|
661
680
|
|
|
662
681
|
async function getRows() {
|
|
663
|
-
const orders = await db.order.
|
|
682
|
+
const orders = await db.order.getAll({
|
|
664
683
|
where: x => x.lines.any(line => line.product.contains('i'))
|
|
665
684
|
.and(x.customer.balance.greaterThan(180)),
|
|
666
685
|
customer: true,
|
|
@@ -669,13 +688,14 @@ async function getRows() {
|
|
|
669
688
|
});
|
|
670
689
|
}
|
|
671
690
|
```
|
|
672
|
-
You can also use the alternative syntax for the `where
|
|
691
|
+
You can also use the alternative syntax for the `where-filter`. This way, the filter can be constructed independently from the fetching strategy. Keep in mind that you must use the `getMany` method instead of the `getAll` method.
|
|
692
|
+
It is also possible to combine `where-filter` with the independent filter when using the `getMany` method.
|
|
673
693
|
```javascript
|
|
674
694
|
async function getRows() {
|
|
675
695
|
const filter = db.order.lines.any(line => line.product.contains('i'))
|
|
676
696
|
.and(db.order.customer.balance.greaterThan(180));
|
|
677
|
-
const orders = await db.order.getMany({
|
|
678
|
-
where:
|
|
697
|
+
const orders = await db.order.getMany(filter, {
|
|
698
|
+
//where: x => ... can be combined as well
|
|
679
699
|
customer: true,
|
|
680
700
|
deliveryAddress: true,
|
|
681
701
|
lines: true
|
|
@@ -790,7 +810,7 @@ const db = map.sqlite('demo.db');
|
|
|
790
810
|
update();
|
|
791
811
|
|
|
792
812
|
async function update() {
|
|
793
|
-
let orders = await db.order.
|
|
813
|
+
let orders = await db.order.getAll({
|
|
794
814
|
orderBy: 'id',
|
|
795
815
|
lines: true,
|
|
796
816
|
deliveryAddress: true,
|
|
@@ -1023,7 +1043,7 @@ const db = map.sqlite('demo.db');
|
|
|
1023
1043
|
updateInsertDelete();
|
|
1024
1044
|
|
|
1025
1045
|
async function updateInsertDelete() {
|
|
1026
|
-
const orders = await db.order.
|
|
1046
|
+
const orders = await db.order.getAll({
|
|
1027
1047
|
customer: true,
|
|
1028
1048
|
deliveryAddress: true,
|
|
1029
1049
|
lines: true
|
|
@@ -1070,7 +1090,7 @@ const db = map.sqlite('demo.db');
|
|
|
1070
1090
|
deleteRows();
|
|
1071
1091
|
|
|
1072
1092
|
async function deleteRows() {
|
|
1073
|
-
let orders = await db.order.
|
|
1093
|
+
let orders = await db.order.getAll({
|
|
1074
1094
|
where: x => x.customer.name.eq('George')
|
|
1075
1095
|
});
|
|
1076
1096
|
|
|
@@ -1086,7 +1106,7 @@ const db = map.sqlite('demo.db');
|
|
|
1086
1106
|
deleteRows();
|
|
1087
1107
|
|
|
1088
1108
|
async function deleteRows() {
|
|
1089
|
-
let orders = await db.order.
|
|
1109
|
+
let orders = await db.order.getAll({
|
|
1090
1110
|
where: x => x.deliveryAddress.name.eq('George'),
|
|
1091
1111
|
customer: true,
|
|
1092
1112
|
deliveryAddress: true,
|
|
@@ -1154,7 +1174,7 @@ Raw sql queries, raw sql filters and transactions are disabled at the http clien
|
|
|
1154
1174
|
|
|
1155
1175
|
<sub>π server.ts</sub>
|
|
1156
1176
|
|
|
1157
|
-
```
|
|
1177
|
+
```javascript
|
|
1158
1178
|
import map from './map';
|
|
1159
1179
|
import { json } from 'body-parser';
|
|
1160
1180
|
import express from 'express';
|
|
@@ -1171,7 +1191,7 @@ express().disable('x-powered-by')
|
|
|
1171
1191
|
```
|
|
1172
1192
|
|
|
1173
1193
|
<sub>π browser.ts</sub>
|
|
1174
|
-
```
|
|
1194
|
+
```javascript
|
|
1175
1195
|
import map from './map';
|
|
1176
1196
|
|
|
1177
1197
|
const db = map.http('http://localhost:3000/orange');
|
|
@@ -1202,7 +1222,7 @@ One notable side effect compared to the previous example, is that only the order
|
|
|
1202
1222
|
|
|
1203
1223
|
<sub>π server.ts</sub>
|
|
1204
1224
|
|
|
1205
|
-
```
|
|
1225
|
+
```javascript
|
|
1206
1226
|
import map from './map';
|
|
1207
1227
|
import { json } from 'body-parser';
|
|
1208
1228
|
import express from 'express';
|
|
@@ -1237,7 +1257,7 @@ function validateToken(req, res, next) {
|
|
|
1237
1257
|
|
|
1238
1258
|
<sub>π browser.ts</sub>
|
|
1239
1259
|
|
|
1240
|
-
```
|
|
1260
|
+
```javascript
|
|
1241
1261
|
import map from './map';
|
|
1242
1262
|
|
|
1243
1263
|
const db = map.http('http://localhost:3000/orange');
|
|
@@ -1281,8 +1301,9 @@ async function updateRows() {
|
|
|
1281
1301
|
|
|
1282
1302
|
```
|
|
1283
1303
|
|
|
1284
|
-
__Row Level
|
|
1304
|
+
__Row Level Security (Postgres)__
|
|
1285
1305
|
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.
|
|
1306
|
+
Available transaction hooks are `beforeBegin`, `afterBegin`, `beforeCommit`, `afterCommit`, and `afterRollback`.
|
|
1286
1307
|
|
|
1287
1308
|
<sub>π setup.sql</sub>
|
|
1288
1309
|
|
|
@@ -1309,7 +1330,7 @@ insert into tenant_data (tenant_id, value) values
|
|
|
1309
1330
|
|
|
1310
1331
|
<sub>π server.ts</sub>
|
|
1311
1332
|
|
|
1312
|
-
```
|
|
1333
|
+
```javascript
|
|
1313
1334
|
import map from './map';
|
|
1314
1335
|
import { json } from 'body-parser';
|
|
1315
1336
|
import express from 'express';
|
|
@@ -1324,7 +1345,6 @@ express().disable('x-powered-by')
|
|
|
1324
1345
|
.use('/orange', db.express({
|
|
1325
1346
|
hooks: {
|
|
1326
1347
|
transaction: {
|
|
1327
|
-
//beforeBegin: async (db, req) => ...,
|
|
1328
1348
|
afterBegin: async (db, req) => {
|
|
1329
1349
|
const tenantId = Number.parseInt(String(req.user?.tenantId ?? ''), 10);
|
|
1330
1350
|
if (!Number.isFinite(tenantId)) throw new Error('Missing tenant id');
|
|
@@ -1333,12 +1353,7 @@ express().disable('x-powered-by')
|
|
|
1333
1353
|
sql: 'select set_config(\'app.tenant_id\', ?, true)',
|
|
1334
1354
|
parameters: [String(tenantId)]
|
|
1335
1355
|
});
|
|
1336
|
-
}
|
|
1337
|
-
//beforeCommit: async (db, req) => ...,
|
|
1338
|
-
//afterCommit: async (db, req) => ...,
|
|
1339
|
-
// afterRollback: async (db, req, error) => {
|
|
1340
|
-
// console.dir(error);
|
|
1341
|
-
// }
|
|
1356
|
+
}
|
|
1342
1357
|
}
|
|
1343
1358
|
}
|
|
1344
1359
|
}))
|
|
@@ -1367,7 +1382,7 @@ function decodeFakeJwt(token) {
|
|
|
1367
1382
|
|
|
1368
1383
|
<sub>π browser.ts</sub>
|
|
1369
1384
|
|
|
1370
|
-
```
|
|
1385
|
+
```javascript
|
|
1371
1386
|
import map from './map';
|
|
1372
1387
|
|
|
1373
1388
|
const db = map.http('http://localhost:3000/orange');
|
|
@@ -1397,7 +1412,7 @@ const db = map.sqlite('demo.db');
|
|
|
1397
1412
|
getRows();
|
|
1398
1413
|
|
|
1399
1414
|
async function getRows() {
|
|
1400
|
-
const rows = await db.order.
|
|
1415
|
+
const rows = await db.order.getAll({
|
|
1401
1416
|
deliveryAddress: true
|
|
1402
1417
|
});
|
|
1403
1418
|
}
|
|
@@ -1413,7 +1428,7 @@ const db = map.sqlite('demo.db');
|
|
|
1413
1428
|
getRows();
|
|
1414
1429
|
|
|
1415
1430
|
async function getRows() {
|
|
1416
|
-
const rows = await db.order.
|
|
1431
|
+
const rows = await db.order.getAll({
|
|
1417
1432
|
orderDate: false,
|
|
1418
1433
|
deliveryAddress: {
|
|
1419
1434
|
countryCode: true,
|
|
@@ -1437,7 +1452,7 @@ const db = map.sqlite('demo.db');
|
|
|
1437
1452
|
getRows();
|
|
1438
1453
|
|
|
1439
1454
|
async function getRows() {
|
|
1440
|
-
const rows = await db.customer.
|
|
1455
|
+
const rows = await db.customer.getAll({
|
|
1441
1456
|
where x => x.name.equal('Harry')
|
|
1442
1457
|
});
|
|
1443
1458
|
}
|
|
@@ -1450,7 +1465,7 @@ const db = map.sqlite('demo.db');
|
|
|
1450
1465
|
getRows();
|
|
1451
1466
|
|
|
1452
1467
|
async function getRows() {
|
|
1453
|
-
const rows = await db.customer.
|
|
1468
|
+
const rows = await db.customer.getAll({
|
|
1454
1469
|
where x => x.name.notEqual('Harry')
|
|
1455
1470
|
});
|
|
1456
1471
|
}
|
|
@@ -1463,7 +1478,7 @@ const db = map.sqlite('demo.db');
|
|
|
1463
1478
|
getRows();
|
|
1464
1479
|
|
|
1465
1480
|
async function getRows() {
|
|
1466
|
-
const rows = await db.customer.
|
|
1481
|
+
const rows = await db.customer.getAll({
|
|
1467
1482
|
where: x => x.name.contains('arr')
|
|
1468
1483
|
});
|
|
1469
1484
|
}
|
|
@@ -1478,7 +1493,7 @@ getRows();
|
|
|
1478
1493
|
async function getRows() {
|
|
1479
1494
|
const filter = db.customer.name.startsWith('Harr');
|
|
1480
1495
|
|
|
1481
|
-
const rows = await db.customer.
|
|
1496
|
+
const rows = await db.customer.getAll({
|
|
1482
1497
|
where: x => x.name.startsWith('Harr')
|
|
1483
1498
|
});
|
|
1484
1499
|
}
|
|
@@ -1491,7 +1506,7 @@ const db = map.sqlite('demo.db');
|
|
|
1491
1506
|
getRows();
|
|
1492
1507
|
|
|
1493
1508
|
async function getRows() {
|
|
1494
|
-
const rows = await db.customer.
|
|
1509
|
+
const rows = await db.customer.getAll({
|
|
1495
1510
|
where: x => x.name.endsWith('arry')
|
|
1496
1511
|
});
|
|
1497
1512
|
}
|
|
@@ -1504,7 +1519,7 @@ const db = map.sqlite('demo.db');
|
|
|
1504
1519
|
getRows();
|
|
1505
1520
|
|
|
1506
1521
|
async function getRows() {
|
|
1507
|
-
const rows = await db.order.
|
|
1522
|
+
const rows = await db.order.getAll({
|
|
1508
1523
|
where: x => x.orderDate.greaterThan('2023-07-14T12:00:00')
|
|
1509
1524
|
});
|
|
1510
1525
|
}
|
|
@@ -1517,7 +1532,7 @@ const db = map.sqlite('demo.db');
|
|
|
1517
1532
|
getRows();
|
|
1518
1533
|
|
|
1519
1534
|
async function getRows() {
|
|
1520
|
-
const rows = await db.order.
|
|
1535
|
+
const rows = await db.order.getAll({
|
|
1521
1536
|
where: x => x.orderDate.greaterThanOrEqual('2023-07-14T12:00:00')
|
|
1522
1537
|
});
|
|
1523
1538
|
}
|
|
@@ -1530,7 +1545,7 @@ const db = map.sqlite('demo.db');
|
|
|
1530
1545
|
getRows();
|
|
1531
1546
|
|
|
1532
1547
|
async function getRows() {
|
|
1533
|
-
const rows = await db.order.
|
|
1548
|
+
const rows = await db.order.getAll({
|
|
1534
1549
|
where: x => x.orderDate.lessThan('2023-07-14T12:00:00')
|
|
1535
1550
|
});
|
|
1536
1551
|
}
|
|
@@ -1543,7 +1558,7 @@ const db = map.sqlite('demo.db');
|
|
|
1543
1558
|
getRows();
|
|
1544
1559
|
|
|
1545
1560
|
async function getRows() {
|
|
1546
|
-
const rows = await db.order.
|
|
1561
|
+
const rows = await db.order.getAll({
|
|
1547
1562
|
where: x => x.orderDate.lessThanOrEqual('2023-07-14T12:00:00')
|
|
1548
1563
|
});
|
|
1549
1564
|
}
|
|
@@ -1556,7 +1571,7 @@ const db = map.sqlite('demo.db');
|
|
|
1556
1571
|
getRows();
|
|
1557
1572
|
|
|
1558
1573
|
async function getRows() {
|
|
1559
|
-
const rows = await db.order.
|
|
1574
|
+
const rows = await db.order.getAll({
|
|
1560
1575
|
where: x => x.orderDate.between('2023-07-14T12:00:00', '2024-07-14T12:00:00')
|
|
1561
1576
|
});
|
|
1562
1577
|
}
|
|
@@ -1569,7 +1584,7 @@ const db = map.sqlite('demo.db');
|
|
|
1569
1584
|
getRows();
|
|
1570
1585
|
|
|
1571
1586
|
async function getRows() {
|
|
1572
|
-
const rows = await db.order.
|
|
1587
|
+
const rows = await db.order.getAll({
|
|
1573
1588
|
where: x => x.customer.name.in('George', 'Harry')
|
|
1574
1589
|
});
|
|
1575
1590
|
|
|
@@ -1591,11 +1606,11 @@ async function getRows() {
|
|
|
1591
1606
|
parameters: ['%arry']
|
|
1592
1607
|
};
|
|
1593
1608
|
|
|
1594
|
-
const rowsWithRaw = await db.customer.
|
|
1609
|
+
const rowsWithRaw = await db.customer.getAll({
|
|
1595
1610
|
where: () => rawFilter
|
|
1596
1611
|
});
|
|
1597
1612
|
|
|
1598
|
-
const rowsWithCombined = await db.customer.
|
|
1613
|
+
const rowsWithCombined = await db.customer.getAll({
|
|
1599
1614
|
where: x => x.balance.greaterThan(100).and(rawFilter)
|
|
1600
1615
|
});
|
|
1601
1616
|
}
|
|
@@ -1613,7 +1628,7 @@ const db = map.sqlite('demo.db');
|
|
|
1613
1628
|
getRows();
|
|
1614
1629
|
|
|
1615
1630
|
async function getRows() {
|
|
1616
|
-
const orders = await db.order.
|
|
1631
|
+
const orders = await db.order.getAll({
|
|
1617
1632
|
lines: {
|
|
1618
1633
|
where: x => x.product.contains('broomstick')
|
|
1619
1634
|
},
|
|
@@ -1635,7 +1650,7 @@ const db = map.sqlite('demo.db');
|
|
|
1635
1650
|
getRows();
|
|
1636
1651
|
|
|
1637
1652
|
async function getRows() {
|
|
1638
|
-
const rows = await db.order.
|
|
1653
|
+
const rows = await db.order.getAll({
|
|
1639
1654
|
where: x => x.customer.name.equal('Harry')
|
|
1640
1655
|
.and(x.orderDate.greaterThan('2023-07-14T12:00:00'))
|
|
1641
1656
|
});
|
|
@@ -1650,7 +1665,7 @@ getRows();
|
|
|
1650
1665
|
|
|
1651
1666
|
async function getRows() {
|
|
1652
1667
|
|
|
1653
|
-
const rows = await db.order.
|
|
1668
|
+
const rows = await db.order.getAll({
|
|
1654
1669
|
where: y => y.customer( x => x.name.equal('George')
|
|
1655
1670
|
.or(x.name.equal('Harry')))
|
|
1656
1671
|
});
|
|
@@ -1665,7 +1680,7 @@ getRows();
|
|
|
1665
1680
|
|
|
1666
1681
|
async function getRows() {
|
|
1667
1682
|
//Neither George nor Harry
|
|
1668
|
-
const rows = await db.order.
|
|
1683
|
+
const rows = await db.order.getAll({
|
|
1669
1684
|
where: y => y.customer(x => x.name.equal('George')
|
|
1670
1685
|
.or(x.name.equal('Harry')))
|
|
1671
1686
|
.not()
|
|
@@ -1680,7 +1695,7 @@ const db = map.sqlite('demo.db');
|
|
|
1680
1695
|
getRows();
|
|
1681
1696
|
|
|
1682
1697
|
async function getRows() {
|
|
1683
|
-
const rows = await db.order.
|
|
1698
|
+
const rows = await db.order.getAll({
|
|
1684
1699
|
where: x => x.deliveryAddress.exists()
|
|
1685
1700
|
});
|
|
1686
1701
|
}
|
|
@@ -1701,7 +1716,7 @@ const db = map.sqlite('demo.db');
|
|
|
1701
1716
|
getRows();
|
|
1702
1717
|
|
|
1703
1718
|
async function getRows() {
|
|
1704
|
-
const rows = await db.order.
|
|
1719
|
+
const rows = await db.order.getAll({
|
|
1705
1720
|
where: y => y.lines.any(x => x.product.contains('guitar'))
|
|
1706
1721
|
//equivalent syntax:
|
|
1707
1722
|
//where: x => x.lines.product.contains('guitar')
|
|
@@ -1717,7 +1732,7 @@ const db = map.sqlite('demo.db');
|
|
|
1717
1732
|
getRows();
|
|
1718
1733
|
|
|
1719
1734
|
async function getRows() {
|
|
1720
|
-
const rows = await db.order.
|
|
1735
|
+
const rows = await db.order.getAll({
|
|
1721
1736
|
where: y => y.lines.all(x => x.product.contains('a'))
|
|
1722
1737
|
});
|
|
1723
1738
|
}
|
|
@@ -1731,7 +1746,7 @@ const db = map.sqlite('demo.db');
|
|
|
1731
1746
|
getRows();
|
|
1732
1747
|
|
|
1733
1748
|
async function getRows() {
|
|
1734
|
-
const rows = await db.order.
|
|
1749
|
+
const rows = await db.order.getAll({
|
|
1735
1750
|
where: y => y.lines.none(x => x.product.equal('Magic wand'))
|
|
1736
1751
|
});
|
|
1737
1752
|
}
|
|
@@ -1780,8 +1795,7 @@ async function execute() {
|
|
|
1780
1795
|
- **`json`** and **`jsonOf<T>`** are represented as an object or array in javascript and maps to JSON, JSONB, NVARCHAR(max) or TEXT (sqlite) in sql.
|
|
1781
1796
|
|
|
1782
1797
|
<sub>π map.ts</sub>
|
|
1783
|
-
|
|
1784
|
-
```ts
|
|
1798
|
+
```javascript
|
|
1785
1799
|
import orange from 'orange-orm';
|
|
1786
1800
|
|
|
1787
1801
|
interface Pet {
|
|
@@ -1804,8 +1818,7 @@ const map = orange.map(x => ({
|
|
|
1804
1818
|
}));
|
|
1805
1819
|
```
|
|
1806
1820
|
<sub>π map.js</sub>
|
|
1807
|
-
|
|
1808
|
-
```js
|
|
1821
|
+
```javascript
|
|
1809
1822
|
import orange from 'orange-orm';
|
|
1810
1823
|
|
|
1811
1824
|
/**
|
|
@@ -1832,138 +1845,6 @@ const map = orange.map(x => ({
|
|
|
1832
1845
|
```
|
|
1833
1846
|
</details>
|
|
1834
1847
|
|
|
1835
|
-
<details id="enums"><summary><strong>Enums</strong></summary>
|
|
1836
|
-
<p>Enums can be defined using object literals, arrays, or TypeScript enums. The <strong><i>enum(...)</i></strong> method uses literal types when possible, so prefer patterns that keep literals (inline objects, <code>as const</code>, or <code>Object.freeze</code> in JS).</p>
|
|
1837
|
-
|
|
1838
|
-
<sub>π map.ts (TypeScript enum)</sub>
|
|
1839
|
-
|
|
1840
|
-
```ts
|
|
1841
|
-
enum CountryCode {
|
|
1842
|
-
NORWAY = 'NO',
|
|
1843
|
-
SWEDEN = 'SE',
|
|
1844
|
-
DENMARK = 'DK',
|
|
1845
|
-
FINLAND = 'FI',
|
|
1846
|
-
ICELAND = 'IS',
|
|
1847
|
-
GERMANY = 'DE',
|
|
1848
|
-
FRANCE = 'FR',
|
|
1849
|
-
NETHERLANDS = 'NL',
|
|
1850
|
-
SPAIN = 'ES',
|
|
1851
|
-
ITALY = 'IT',
|
|
1852
|
-
}
|
|
1853
|
-
|
|
1854
|
-
const map = orange.map(x => ({
|
|
1855
|
-
deliveryAddress: x.table('deliveryAddress').map(({ column }) => ({
|
|
1856
|
-
id: column('id').numeric().primary(),
|
|
1857
|
-
name: column('name').string(),
|
|
1858
|
-
street: column('street').string(),
|
|
1859
|
-
postalCode: column('postalCode').string(),
|
|
1860
|
-
postalPlace: column('postalPlace').string(),
|
|
1861
|
-
countryCode: column('countryCode').string().enum(CountryCode),
|
|
1862
|
-
}))
|
|
1863
|
-
}));
|
|
1864
|
-
```
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
<sub>π map.ts (as const)</sub>
|
|
1868
|
-
|
|
1869
|
-
```ts
|
|
1870
|
-
const Countries = {
|
|
1871
|
-
NORWAY: 'NO',
|
|
1872
|
-
SWEDEN: 'SE',
|
|
1873
|
-
DENMARK: 'DK',
|
|
1874
|
-
FINLAND: 'FI',
|
|
1875
|
-
ICELAND: 'IS',
|
|
1876
|
-
GERMANY: 'DE',
|
|
1877
|
-
FRANCE: 'FR',
|
|
1878
|
-
NETHERLANDS: 'NL',
|
|
1879
|
-
SPAIN: 'ES',
|
|
1880
|
-
ITALY: 'IT',
|
|
1881
|
-
} as const;
|
|
1882
|
-
|
|
1883
|
-
const map = orange.map(x => ({
|
|
1884
|
-
deliveryAddress: x.table('deliveryAddress').map(({ column }) => ({
|
|
1885
|
-
id: column('id').numeric().primary(),
|
|
1886
|
-
name: column('name').string(),
|
|
1887
|
-
street: column('street').string(),
|
|
1888
|
-
postalCode: column('postalCode').string(),
|
|
1889
|
-
postalPlace: column('postalPlace').string(),
|
|
1890
|
-
countryCode: column('countryCode').string().enum(Countries),
|
|
1891
|
-
}))
|
|
1892
|
-
}));
|
|
1893
|
-
```
|
|
1894
|
-
|
|
1895
|
-
<sub>π map.ts (array)</sub>
|
|
1896
|
-
|
|
1897
|
-
```ts
|
|
1898
|
-
const map = orange.map(x => ({
|
|
1899
|
-
deliveryAddress: x.table('deliveryAddress').map(({ column }) => ({
|
|
1900
|
-
id: column('id').numeric().primary(),
|
|
1901
|
-
name: column('name').string(),
|
|
1902
|
-
street: column('street').string(),
|
|
1903
|
-
postalCode: column('postalCode').string(),
|
|
1904
|
-
postalPlace: column('postalPlace').string(),
|
|
1905
|
-
countryCode: column('countryCode').string().enum(
|
|
1906
|
-
['NO', 'SE', 'DK', 'FI', 'IS', 'DE', 'FR', 'NL', 'ES', 'IT']
|
|
1907
|
-
),
|
|
1908
|
-
}))
|
|
1909
|
-
}));
|
|
1910
|
-
```
|
|
1911
|
-
|
|
1912
|
-
<sub>π map.ts (literal object)</sub>
|
|
1913
|
-
|
|
1914
|
-
```ts
|
|
1915
|
-
const map = orange.map(x => ({
|
|
1916
|
-
deliveryAddress: x.table('deliveryAddress').map(({ column }) => ({
|
|
1917
|
-
id: column('id').numeric().primary(),
|
|
1918
|
-
name: column('name').string(),
|
|
1919
|
-
street: column('street').string(),
|
|
1920
|
-
postalCode: column('postalCode').string(),
|
|
1921
|
-
postalPlace: column('postalPlace').string(),
|
|
1922
|
-
countryCode: column('countryCode').string().enum({
|
|
1923
|
-
NORWAY: 'NO',
|
|
1924
|
-
SWEDEN: 'SE',
|
|
1925
|
-
DENMARK: 'DK',
|
|
1926
|
-
FINLAND: 'FI',
|
|
1927
|
-
ICELAND: 'IS',
|
|
1928
|
-
GERMANY: 'DE',
|
|
1929
|
-
FRANCE: 'FR',
|
|
1930
|
-
NETHERLANDS: 'NL',
|
|
1931
|
-
SPAIN: 'ES',
|
|
1932
|
-
ITALY: 'IT',
|
|
1933
|
-
}),
|
|
1934
|
-
}))
|
|
1935
|
-
}));
|
|
1936
|
-
```
|
|
1937
|
-
|
|
1938
|
-
<sub>π map.js (Object.freeze)</sub>
|
|
1939
|
-
|
|
1940
|
-
```js
|
|
1941
|
-
const Countries = Object.freeze({
|
|
1942
|
-
NORWAY: 'NO',
|
|
1943
|
-
SWEDEN: 'SE',
|
|
1944
|
-
DENMARK: 'DK',
|
|
1945
|
-
FINLAND: 'FI',
|
|
1946
|
-
ICELAND: 'IS',
|
|
1947
|
-
GERMANY: 'DE',
|
|
1948
|
-
FRANCE: 'FR',
|
|
1949
|
-
NETHERLANDS: 'NL',
|
|
1950
|
-
SPAIN: 'ES',
|
|
1951
|
-
ITALY: 'IT',
|
|
1952
|
-
});
|
|
1953
|
-
|
|
1954
|
-
const map = orange.map(x => ({
|
|
1955
|
-
deliveryAddress: x.table('deliveryAddress').map(({ column }) => ({
|
|
1956
|
-
id: column('id').numeric().primary(),
|
|
1957
|
-
name: column('name').string(),
|
|
1958
|
-
street: column('street').string(),
|
|
1959
|
-
postalCode: column('postalCode').string(),
|
|
1960
|
-
postalPlace: column('postalPlace').string(),
|
|
1961
|
-
countryCode: column('countryCode').string().enum(Countries),
|
|
1962
|
-
}))
|
|
1963
|
-
}));
|
|
1964
|
-
```
|
|
1965
|
-
</details>
|
|
1966
|
-
|
|
1967
1848
|
<details id="default-values"><summary><strong>Default values</strong></summary>
|
|
1968
1849
|
<p>Utilizing default values can be especially useful for automatically populating these fields when the underlying database doesn't offer native support for default value generation.
|
|
1969
1850
|
|
|
@@ -1990,7 +1871,7 @@ export default map;
|
|
|
1990
1871
|
<p>In the previous sections you have already seen the <strong><i>notNull()</i></strong> validator being used on some columns. This will not only generate correct typescript mapping, but also throw an error if value is set to null or undefined. However, sometimes we do not want the notNull-validator to be run on inserts. Typically, when we have an autoincremental key or server generated uuid, it does not make sense to check for null on insert. This is where <strong><i>notNullExceptInsert()</strong></i> comes to rescue. You can also create your own custom validator as shown below. The last kind of validator, is the <a href="https://ajv.js.org/json-schema.html">ajv JSON schema validator</a>. This can be used on json columns as well as any other column type.</p>
|
|
1991
1872
|
|
|
1992
1873
|
<sub>π map.ts</sub>
|
|
1993
|
-
```
|
|
1874
|
+
```javascript
|
|
1994
1875
|
import orange from 'orange-orm';
|
|
1995
1876
|
|
|
1996
1877
|
interface Pet {
|
|
@@ -2021,7 +1902,7 @@ const map = orange.map(x => ({
|
|
|
2021
1902
|
export default map;
|
|
2022
1903
|
```
|
|
2023
1904
|
<sub>π map.js</sub>
|
|
2024
|
-
```
|
|
1905
|
+
```javascript
|
|
2025
1906
|
import orange from 'orange-orm';
|
|
2026
1907
|
|
|
2027
1908
|
/**
|
|
@@ -2159,43 +2040,6 @@ async function getRows() {
|
|
|
2159
2040
|
```
|
|
2160
2041
|
</details>
|
|
2161
2042
|
|
|
2162
|
-
<details id="sqlite-user-defined-functions"><summary><strong>SQLite user-defined functions</strong></summary>
|
|
2163
|
-
|
|
2164
|
-
You can register custom SQLite functions on the connection using `db.function(name, fn)`.
|
|
2165
|
-
|
|
2166
|
-
The `fn` argument is your user-defined callback:
|
|
2167
|
-
- It is invoked by SQLite every time the SQL function is called.
|
|
2168
|
-
- Callback arguments are positional and match the SQL call (for example, `my_fn(a, b)` maps to `(a, b)`).
|
|
2169
|
-
- Return a SQLite-compatible scalar value (for example text, number, or `null`).
|
|
2170
|
-
- Throwing inside the callback fails the SQL statement.
|
|
2171
|
-
|
|
2172
|
-
`db.function(...)` is sync-only in Node and Deno, but can be async or sync in Bun.
|
|
2173
|
-
|
|
2174
|
-
```javascript
|
|
2175
|
-
import map from './map';
|
|
2176
|
-
const db = map.sqlite('demo.db');
|
|
2177
|
-
|
|
2178
|
-
await db.function('add_prefix', (text, prefix) => `${prefix}${text}`);
|
|
2179
|
-
|
|
2180
|
-
const rows = await db.query(
|
|
2181
|
-
"select id, name, add_prefix(name, '[VIP] ') as prefixedName from customer"
|
|
2182
|
-
);
|
|
2183
|
-
```
|
|
2184
|
-
|
|
2185
|
-
If you need the function inside a transaction, register it within the transaction callback to ensure it is available on that connection.
|
|
2186
|
-
|
|
2187
|
-
```javascript
|
|
2188
|
-
await db.transaction(async (db) => {
|
|
2189
|
-
await db.function('add_prefix', (text, prefix) => `${prefix}${text}`);
|
|
2190
|
-
return db.query(
|
|
2191
|
-
"select id, name, add_prefix(name, '[VIP] ') as prefixedName from customer"
|
|
2192
|
-
);
|
|
2193
|
-
});
|
|
2194
|
-
```
|
|
2195
|
-
|
|
2196
|
-
`db.function(...)` is available on direct SQLite connections (for example `map.sqlite(...)`) and not through `map.http(...)`.
|
|
2197
|
-
</details>
|
|
2198
|
-
|
|
2199
2043
|
<details id="aggregates"><summary><strong>Aggregate functions</strong></summary>
|
|
2200
2044
|
|
|
2201
2045
|
You can count records and aggregate numerical columns. This can either be done across rows or separately for each row.
|
|
@@ -2217,7 +2061,7 @@ const db = map.sqlite('demo.db');
|
|
|
2217
2061
|
getRows();
|
|
2218
2062
|
|
|
2219
2063
|
async function getRows() {
|
|
2220
|
-
const orders = await db.order.
|
|
2064
|
+
const orders = await db.order.getAll({
|
|
2221
2065
|
numberOfLines: x => x.count(x => x.lines.id),
|
|
2222
2066
|
totalAmount: x => x.sum(x => lines.amount),
|
|
2223
2067
|
balance: x => x.customer.balance
|
|
@@ -2268,7 +2112,7 @@ async function getCount() {
|
|
|
2268
2112
|
|
|
2269
2113
|
<sub>π map.ts</sub>
|
|
2270
2114
|
|
|
2271
|
-
```
|
|
2115
|
+
```javascript
|
|
2272
2116
|
import orange from 'orange-orm';
|
|
2273
2117
|
|
|
2274
2118
|
const map = orange.map(x => ({
|
|
@@ -2284,7 +2128,7 @@ export default map;
|
|
|
2284
2128
|
```
|
|
2285
2129
|
<sub>π sensitive.ts</sub>
|
|
2286
2130
|
|
|
2287
|
-
```
|
|
2131
|
+
```javascript
|
|
2288
2132
|
import map from './map';
|
|
2289
2133
|
const db = map.sqlite('demo.db');
|
|
2290
2134
|
|
package/dist/index.browser.mjs
CHANGED
|
@@ -5886,37 +5886,6 @@ function requireColumn () {
|
|
|
5886
5886
|
return c;
|
|
5887
5887
|
};
|
|
5888
5888
|
|
|
5889
|
-
c.enum = function(values) {
|
|
5890
|
-
let list = values;
|
|
5891
|
-
if (Array.isArray(values))
|
|
5892
|
-
list = values;
|
|
5893
|
-
else if (values && typeof values === 'object') {
|
|
5894
|
-
const keys = Object.keys(values);
|
|
5895
|
-
const nonNumericKeys = keys.filter((key) => !/^-?\d+$/.test(key));
|
|
5896
|
-
list = (nonNumericKeys.length ? nonNumericKeys : keys).map((key) => values[key]);
|
|
5897
|
-
}
|
|
5898
|
-
else
|
|
5899
|
-
throw new Error('enum values must be an array');
|
|
5900
|
-
const allowed = new Set(list);
|
|
5901
|
-
column.enum = list;
|
|
5902
|
-
function validate(value) {
|
|
5903
|
-
if (value === undefined || value === null)
|
|
5904
|
-
return;
|
|
5905
|
-
if (!allowed.has(value)) {
|
|
5906
|
-
const formatted = list.map((v) => JSON.stringify(v)).join(', ');
|
|
5907
|
-
throw new Error(`Column ${column.alias} must be one of: ${formatted}`);
|
|
5908
|
-
}
|
|
5909
|
-
}
|
|
5910
|
-
return c.validate(validate);
|
|
5911
|
-
};
|
|
5912
|
-
|
|
5913
|
-
c.enum2 = function(...values) {
|
|
5914
|
-
const list = values.length === 1 && Array.isArray(values[0])
|
|
5915
|
-
? values[0]
|
|
5916
|
-
: values;
|
|
5917
|
-
return c.enum(list);
|
|
5918
|
-
};
|
|
5919
|
-
|
|
5920
5889
|
c.default = function(value) {
|
|
5921
5890
|
column.default = value;
|
|
5922
5891
|
return c;
|
|
@@ -5932,6 +5901,7 @@ function requireColumn () {
|
|
|
5932
5901
|
var oldAlias = column.alias;
|
|
5933
5902
|
table._aliases.delete(oldAlias);
|
|
5934
5903
|
table._aliases.add(alias);
|
|
5904
|
+
delete table[oldAlias];
|
|
5935
5905
|
table[alias] = column;
|
|
5936
5906
|
column.alias = alias;
|
|
5937
5907
|
return c;
|
package/dist/index.mjs
CHANGED
|
@@ -5887,37 +5887,6 @@ function requireColumn () {
|
|
|
5887
5887
|
return c;
|
|
5888
5888
|
};
|
|
5889
5889
|
|
|
5890
|
-
c.enum = function(values) {
|
|
5891
|
-
let list = values;
|
|
5892
|
-
if (Array.isArray(values))
|
|
5893
|
-
list = values;
|
|
5894
|
-
else if (values && typeof values === 'object') {
|
|
5895
|
-
const keys = Object.keys(values);
|
|
5896
|
-
const nonNumericKeys = keys.filter((key) => !/^-?\d+$/.test(key));
|
|
5897
|
-
list = (nonNumericKeys.length ? nonNumericKeys : keys).map((key) => values[key]);
|
|
5898
|
-
}
|
|
5899
|
-
else
|
|
5900
|
-
throw new Error('enum values must be an array');
|
|
5901
|
-
const allowed = new Set(list);
|
|
5902
|
-
column.enum = list;
|
|
5903
|
-
function validate(value) {
|
|
5904
|
-
if (value === undefined || value === null)
|
|
5905
|
-
return;
|
|
5906
|
-
if (!allowed.has(value)) {
|
|
5907
|
-
const formatted = list.map((v) => JSON.stringify(v)).join(', ');
|
|
5908
|
-
throw new Error(`Column ${column.alias} must be one of: ${formatted}`);
|
|
5909
|
-
}
|
|
5910
|
-
}
|
|
5911
|
-
return c.validate(validate);
|
|
5912
|
-
};
|
|
5913
|
-
|
|
5914
|
-
c.enum2 = function(...values) {
|
|
5915
|
-
const list = values.length === 1 && Array.isArray(values[0])
|
|
5916
|
-
? values[0]
|
|
5917
|
-
: values;
|
|
5918
|
-
return c.enum(list);
|
|
5919
|
-
};
|
|
5920
|
-
|
|
5921
5890
|
c.default = function(value) {
|
|
5922
5891
|
column.default = value;
|
|
5923
5892
|
return c;
|
|
@@ -5933,6 +5902,7 @@ function requireColumn () {
|
|
|
5933
5902
|
var oldAlias = column.alias;
|
|
5934
5903
|
table._aliases.delete(oldAlias);
|
|
5935
5904
|
table._aliases.add(alias);
|
|
5905
|
+
delete table[oldAlias];
|
|
5936
5906
|
table[alias] = column;
|
|
5937
5907
|
column.alias = alias;
|
|
5938
5908
|
return c;
|
|
@@ -19655,7 +19625,7 @@ function requireOutputInsertedSql () {
|
|
|
19655
19625
|
|
|
19656
19626
|
function formatColumn(column) {
|
|
19657
19627
|
if (column.formatOut)
|
|
19658
|
-
return column.formatOut(context, 'INSERTED')
|
|
19628
|
+
return `${column.formatOut(context, 'INSERTED')} AS [${column._dbName}]`;
|
|
19659
19629
|
else
|
|
19660
19630
|
return `INSERTED.[${column._dbName}]`;
|
|
19661
19631
|
}
|
package/package.json
CHANGED
package/src/map.d.ts
CHANGED
|
@@ -96,9 +96,7 @@ type ReturnArrayOrObj<W, V1, V2> =
|
|
|
96
96
|
V1;
|
|
97
97
|
|
|
98
98
|
|
|
99
|
-
type ColumnToType<T> = T extends
|
|
100
|
-
? E
|
|
101
|
-
: T extends UuidColumnSymbol
|
|
99
|
+
type ColumnToType<T> = T extends UuidColumnSymbol
|
|
102
100
|
? string
|
|
103
101
|
: T extends StringColumnSymbol
|
|
104
102
|
? string
|
|
@@ -627,10 +625,6 @@ type NotNullExceptInsert = {
|
|
|
627
625
|
[' notNullExceptInsert']: boolean;
|
|
628
626
|
};
|
|
629
627
|
|
|
630
|
-
type EnumOf<T> = {
|
|
631
|
-
[' enum']: T;
|
|
632
|
-
};
|
|
633
|
-
|
|
634
628
|
type JsonOf<T> = {
|
|
635
629
|
[' isjsonOf']: boolean;
|
|
636
630
|
type: T;
|
|
@@ -740,11 +734,6 @@ type DateWithTimeZoneValidator<M> = M extends NotNull
|
|
|
740
734
|
};
|
|
741
735
|
|
|
742
736
|
type StringColumnTypeDef<M> = StringValidator<M> & {
|
|
743
|
-
enum<const V extends readonly string[]>(values: V): StringColumnTypeDef<M & EnumOf<V[number]>> & EnumOf<V[number]>;
|
|
744
|
-
enum<const V extends Record<string, string>>(values: V): StringColumnTypeDef<M & EnumOf<V[keyof V]>> & EnumOf<V[keyof V]>;
|
|
745
|
-
enum<TEnum>(values: Record<string, TEnum>): StringColumnTypeDef<M & EnumOf<TEnum>> & EnumOf<TEnum>;
|
|
746
|
-
enum<E extends string>(values: readonly E[]): StringColumnTypeDef<M & EnumOf<E>> & EnumOf<E>;
|
|
747
|
-
enum<E, V extends Record<string, E>>(values: V): StringColumnTypeDef<M & EnumOf<V[keyof V]>> & EnumOf<V[keyof V]>;
|
|
748
737
|
primary(): StringColumnTypeDef<M & IsPrimary> & IsPrimary;
|
|
749
738
|
notNull(): StringColumnTypeDef<M & NotNull> & NotNull;
|
|
750
739
|
notNullExceptInsert(): StringColumnTypeDef<M & NotNull & NotNullExceptInsert> & NotNull & NotNullExceptInsert;
|
|
@@ -756,10 +745,6 @@ type StringColumnTypeDef<M> = StringValidator<M> & {
|
|
|
756
745
|
M;
|
|
757
746
|
|
|
758
747
|
type NumericColumnTypeDef<M> = NumericValidator<M> & {
|
|
759
|
-
enum<const V extends Record<string, number>>(values: V): NumericColumnTypeDef<M & EnumOf<V[keyof V]>> & EnumOf<V[keyof V]>;
|
|
760
|
-
enum<TEnum>(values: Record<string, TEnum>): NumericColumnTypeDef<M & EnumOf<TEnum>> & EnumOf<TEnum>;
|
|
761
|
-
enum<E extends number>(values: readonly E[]): NumericColumnTypeDef<M & EnumOf<E>> & EnumOf<E>;
|
|
762
|
-
enum<E, V extends Record<string, E>>(values: V): NumericColumnTypeDef<M & EnumOf<V[keyof V]>> & EnumOf<V[keyof V]>;
|
|
763
748
|
primary(): NumericColumnTypeDef<M & IsPrimary> & IsPrimary;
|
|
764
749
|
notNull(): NumericColumnTypeDef<M & NotNull> & NotNull;
|
|
765
750
|
notNullExceptInsert(): NumericColumnTypeDef<M & NotNull & NotNullExceptInsert> & NotNull & NotNullExceptInsert;
|
|
@@ -771,10 +756,6 @@ type NumericColumnTypeDef<M> = NumericValidator<M> & {
|
|
|
771
756
|
M;
|
|
772
757
|
|
|
773
758
|
type BigIntColumnTypeDef<M> = BigIntValidator<M> & {
|
|
774
|
-
enum<const V extends Record<string, bigint>>(values: V): BigIntColumnTypeDef<M & EnumOf<V[keyof V]>> & EnumOf<V[keyof V]>;
|
|
775
|
-
enum<TEnum>(values: Record<string, TEnum>): BigIntColumnTypeDef<M & EnumOf<TEnum>> & EnumOf<TEnum>;
|
|
776
|
-
enum<E extends bigint>(values: readonly E[]): BigIntColumnTypeDef<M & EnumOf<E>> & EnumOf<E>;
|
|
777
|
-
enum<E, V extends Record<string, E>>(values: V): BigIntColumnTypeDef<M & EnumOf<V[keyof V]>> & EnumOf<V[keyof V]>;
|
|
778
759
|
primary(): BigIntColumnTypeDef<M & IsPrimary> & IsPrimary;
|
|
779
760
|
notNull(): BigIntColumnTypeDef<M & NotNull> & NotNull;
|
|
780
761
|
notNullExceptInsert(): BigIntColumnTypeDef<M & NotNull & NotNullExceptInsert> & NotNull & NotNullExceptInsert;
|
|
@@ -786,10 +767,6 @@ type BigIntColumnTypeDef<M> = BigIntValidator<M> & {
|
|
|
786
767
|
M;
|
|
787
768
|
|
|
788
769
|
type UuidColumnTypeDef<M> = UuidValidator<M> & {
|
|
789
|
-
enum<const V extends Record<string, string>>(values: V): UuidColumnTypeDef<M & EnumOf<V[keyof V]>> & EnumOf<V[keyof V]>;
|
|
790
|
-
enum<TEnum>(values: Record<string, TEnum>): UuidColumnTypeDef<M & EnumOf<TEnum>> & EnumOf<TEnum>;
|
|
791
|
-
enum<E extends string>(values: readonly E[]): UuidColumnTypeDef<M & EnumOf<E>> & EnumOf<E>;
|
|
792
|
-
enum<E, V extends Record<string, E>>(values: V): UuidColumnTypeDef<M & EnumOf<V[keyof V]>> & EnumOf<V[keyof V]>;
|
|
793
770
|
primary(): UuidColumnTypeDef<M & IsPrimary> & IsPrimary;
|
|
794
771
|
notNull(): UuidColumnTypeDef<M & NotNull> & NotNull;
|
|
795
772
|
notNullExceptInsert(): UuidColumnTypeDef<M & NotNull & NotNullExceptInsert> & NotNull & NotNullExceptInsert;
|
|
@@ -812,10 +789,6 @@ type JSONColumnTypeDef<M> = JSONValidator<M> & {
|
|
|
812
789
|
M;
|
|
813
790
|
|
|
814
791
|
type BinaryColumnTypeDef<M> = BinaryValidator<M> & {
|
|
815
|
-
enum<const V extends Record<string, string>>(values: V): BinaryColumnTypeDef<M & EnumOf<V[keyof V]>> & EnumOf<V[keyof V]>;
|
|
816
|
-
enum<TEnum>(values: Record<string, TEnum>): BinaryColumnTypeDef<M & EnumOf<TEnum>> & EnumOf<TEnum>;
|
|
817
|
-
enum<E extends string>(values: readonly E[]): BinaryColumnTypeDef<M & EnumOf<E>> & EnumOf<E>;
|
|
818
|
-
enum<E, V extends Record<string, E>>(values: V): BinaryColumnTypeDef<M & EnumOf<V[keyof V]>> & EnumOf<V[keyof V]>;
|
|
819
792
|
primary(): BinaryColumnTypeDef<M & IsPrimary> & IsPrimary;
|
|
820
793
|
notNull(): BinaryColumnTypeDef<M & NotNull> & NotNull;
|
|
821
794
|
notNullExceptInsert(): BinaryColumnTypeDef<M & NotNull & NotNullExceptInsert> & NotNull & NotNullExceptInsert;
|
|
@@ -827,10 +800,6 @@ type BinaryColumnTypeDef<M> = BinaryValidator<M> & {
|
|
|
827
800
|
M;
|
|
828
801
|
|
|
829
802
|
type BooleanColumnTypeDef<M> = BooleanValidator<M> & {
|
|
830
|
-
enum<const V extends Record<string, boolean>>(values: V): BooleanColumnTypeDef<M & EnumOf<V[keyof V]>> & EnumOf<V[keyof V]>;
|
|
831
|
-
enum<TEnum>(values: Record<string, TEnum>): BooleanColumnTypeDef<M & EnumOf<TEnum>> & EnumOf<TEnum>;
|
|
832
|
-
enum<E extends boolean>(values: readonly E[]): BooleanColumnTypeDef<M & EnumOf<E>> & EnumOf<E>;
|
|
833
|
-
enum<E, V extends Record<string, E>>(values: V): BooleanColumnTypeDef<M & EnumOf<V[keyof V]>> & EnumOf<V[keyof V]>;
|
|
834
803
|
primary(): BooleanColumnTypeDef<M & IsPrimary> & IsPrimary;
|
|
835
804
|
notNull(): BooleanColumnTypeDef<M & NotNull> & NotNull;
|
|
836
805
|
notNullExceptInsert(): BooleanColumnTypeDef<M & NotNull & NotNullExceptInsert> & NotNull & NotNullExceptInsert;
|
|
@@ -842,10 +811,6 @@ type BooleanColumnTypeDef<M> = BooleanValidator<M> & {
|
|
|
842
811
|
M;
|
|
843
812
|
|
|
844
813
|
type DateColumnTypeDef<M> = DateValidator<M> & {
|
|
845
|
-
enum<const V extends Record<string, string | Date>>(values: V): DateColumnTypeDef<M & EnumOf<V[keyof V]>> & EnumOf<V[keyof V]>;
|
|
846
|
-
enum<TEnum>(values: Record<string, TEnum>): DateColumnTypeDef<M & EnumOf<TEnum>> & EnumOf<TEnum>;
|
|
847
|
-
enum<E extends string | Date>(values: readonly E[]): DateColumnTypeDef<M & EnumOf<E>> & EnumOf<E>;
|
|
848
|
-
enum<E, V extends Record<string, E>>(values: V): DateColumnTypeDef<M & EnumOf<V[keyof V]>> & EnumOf<V[keyof V]>;
|
|
849
814
|
primary(): DateColumnTypeDef<M & IsPrimary> & IsPrimary;
|
|
850
815
|
notNull(): DateColumnTypeDef<M & NotNull> & NotNull;
|
|
851
816
|
notNullExceptInsert(): DateColumnTypeDef<M & NotNull & NotNullExceptInsert> & NotNull & NotNullExceptInsert;
|
|
@@ -857,10 +822,6 @@ type DateColumnTypeDef<M> = DateValidator<M> & {
|
|
|
857
822
|
M;
|
|
858
823
|
|
|
859
824
|
type DateWithTimeZoneColumnTypeDef<M> = DateValidator<M> & {
|
|
860
|
-
enum<const V extends Record<string, string | Date>>(values: V): DateWithTimeZoneColumnTypeDef<M & EnumOf<V[keyof V]>> & EnumOf<V[keyof V]>;
|
|
861
|
-
enum<TEnum>(values: Record<string, TEnum>): DateWithTimeZoneColumnTypeDef<M & EnumOf<TEnum>> & EnumOf<TEnum>;
|
|
862
|
-
enum<E extends string | Date>(values: readonly E[]): DateWithTimeZoneColumnTypeDef<M & EnumOf<E>> & EnumOf<E>;
|
|
863
|
-
enum<E, V extends Record<string, E>>(values: V): DateWithTimeZoneColumnTypeDef<M & EnumOf<V[keyof V]>> & EnumOf<V[keyof V]>;
|
|
864
825
|
primary(): DateWithTimeZoneColumnTypeDef<M & IsPrimary> & IsPrimary;
|
|
865
826
|
notNull(): DateWithTimeZoneColumnTypeDef<M & NotNull> & NotNull;
|
|
866
827
|
notNullExceptInsert(): DateWithTimeZoneColumnTypeDef<M & NotNull & NotNullExceptInsert> & NotNull & NotNullExceptInsert;
|
|
@@ -1039,9 +1000,7 @@ type ExtractPrimaryKeyNames<T> =
|
|
|
1039
1000
|
type RelationTarget<T> =
|
|
1040
1001
|
T extends { __tableAlias: infer S } ? Extract<S, string> : string;
|
|
1041
1002
|
|
|
1042
|
-
type
|
|
1043
|
-
|
|
1044
|
-
type ColumnToSchemaType<T> = (
|
|
1003
|
+
type ColumnToSchemaType<T> =
|
|
1045
1004
|
T extends JsonOf<infer U>
|
|
1046
1005
|
? { ' type': 'json'; ' tsType': U }
|
|
1047
1006
|
& (T extends NotNullExceptInsert ? { ' notNull': true; ' notNullExceptInsert': true }
|
|
@@ -1087,8 +1046,7 @@ type ColumnToSchemaType<T> = (
|
|
|
1087
1046
|
& (T extends NotNullExceptInsert ? { ' notNull': true; ' notNullExceptInsert': true }
|
|
1088
1047
|
: T extends NotNull ? { ' notNull': true }
|
|
1089
1048
|
: {}) :
|
|
1090
|
-
never
|
|
1091
|
-
) & EnumSchema<T>;
|
|
1049
|
+
never;
|
|
1092
1050
|
|
|
1093
1051
|
export type MappedDbDef<T> = {
|
|
1094
1052
|
map<V extends AllowedDbMap<V>>(
|
|
@@ -1101,4 +1059,4 @@ export type MappedDbDef<T> = {
|
|
|
1101
1059
|
* Usage: type Schema = ReturnType<typeof db.toSchema>
|
|
1102
1060
|
*/
|
|
1103
1061
|
toSchema: <U = T>() => SchemaFromMappedDb<U>;
|
|
1104
|
-
} & T & DbConnectable<T>;
|
|
1062
|
+
} & T & DbConnectable<T>;
|
package/src/map2.d.ts
CHANGED
|
@@ -10,7 +10,6 @@ export type ORMColumnType = 'string' | 'bigint' | 'uuid' | 'date' | 'numeric' |
|
|
|
10
10
|
// Base column definition with space-prefixed required properties
|
|
11
11
|
export type ORMColumnDefinition = {
|
|
12
12
|
' type': ORMColumnType;
|
|
13
|
-
' enum'?: readonly (string | number | boolean | Date)[] | Record<string, string | number | boolean | Date>;
|
|
14
13
|
' notNull'?: boolean;
|
|
15
14
|
' notNullExceptInsert'?: boolean;
|
|
16
15
|
};
|
|
@@ -25,7 +24,7 @@ export type ORMJsonColumnDefinition<T = any> = {
|
|
|
25
24
|
|
|
26
25
|
type NormalizeColumn<T> =
|
|
27
26
|
T extends ORMColumnType
|
|
28
|
-
? { ' type': T; '
|
|
27
|
+
? { ' type': T; ' notNull'?: boolean; ' notNullExceptInsert'?: boolean }
|
|
29
28
|
: T extends { ' type': ORMColumnType }
|
|
30
29
|
? { ' notNull'?: boolean; ' notNullExceptInsert'?: boolean } & T
|
|
31
30
|
: T extends { ' type': 'json'; ' tsType': any }
|
|
@@ -42,8 +41,6 @@ type IsRequiredInsert<CT> =
|
|
|
42
41
|
: false; // Otherwise, it's optional
|
|
43
42
|
|
|
44
43
|
type ColumnTypeToTS<CT> =
|
|
45
|
-
NormalizeColumn<CT> extends { ' enum': readonly (infer E)[] } ? E :
|
|
46
|
-
NormalizeColumn<CT> extends { ' enum': Record<string, infer E> } ? E :
|
|
47
44
|
NormalizeColumn<CT>[' type'] extends 'numeric' ? number :
|
|
48
45
|
NormalizeColumn<CT>[' type'] extends 'boolean' ? boolean :
|
|
49
46
|
NormalizeColumn<CT>[' type'] extends 'json'
|
|
@@ -166,7 +163,7 @@ type BaseFetchStrategy<M extends Record<string, TableDefinition<M>>, K extends k
|
|
|
166
163
|
orderBy?: OrderBy<M, K>;
|
|
167
164
|
limit?: number;
|
|
168
165
|
offset?: number;
|
|
169
|
-
where?:
|
|
166
|
+
where?: WhereFunc<M, K>;
|
|
170
167
|
};
|
|
171
168
|
|
|
172
169
|
export type PrimaryKeyObject<M extends Record<string, TableDefinition<M>>, K extends keyof M> =
|
|
@@ -344,25 +341,18 @@ export type AggregateStrategy<M extends Record<string, TableDefinition<M>>, K ex
|
|
|
344
341
|
BaseAggregateStrategy<M, K>
|
|
345
342
|
| CustomAggregateSelectors<M, K>;
|
|
346
343
|
|
|
347
|
-
type
|
|
348
|
-
RawFilter | Array<PrimaryKeyObject<M, K>>;
|
|
349
|
-
|
|
350
|
-
type WhereFunc<M extends Record<string, TableDefinition<M>>, K extends keyof M> =
|
|
351
|
-
(row: RootTableRefs<M, K>) => WhereFilter<M, K>;
|
|
352
|
-
|
|
353
|
-
type WhereClause<M extends Record<string, TableDefinition<M>>, K extends keyof M> =
|
|
354
|
-
WhereFilter<M, K> | WhereFunc<M, K> | (() => WhereFilter<M, K>);
|
|
344
|
+
type WhereFunc<M extends Record<string, TableDefinition<M>>, K extends keyof M> = (row: RootTableRefs<M, K>) => RawFilter | Array<PrimaryKeyObject<M, K>>;
|
|
355
345
|
|
|
356
346
|
type BaseAggregateStrategy<M extends Record<string, TableDefinition<M>>, K extends keyof M> = {
|
|
357
347
|
limit?: number;
|
|
358
348
|
offset?: number;
|
|
359
|
-
where?:
|
|
349
|
+
where?: WhereFunc<M, K>;
|
|
360
350
|
};
|
|
361
351
|
|
|
362
352
|
type CustomAggregateSelectors<M extends Record<string, TableDefinition<M>>, K extends keyof M> = {
|
|
363
353
|
[key: string]: (row: RootSelectionRefs<M, K>) => ValidSelectorReturnTypes<M, K>;
|
|
364
354
|
} & {
|
|
365
|
-
where?:
|
|
355
|
+
where?: WhereFunc<M, K>;
|
|
366
356
|
} & {
|
|
367
357
|
// Explicitly prevent limit/offset in selectors
|
|
368
358
|
limit?: never;
|
|
@@ -684,13 +674,13 @@ type AggregateCustomSelectorProperties<M extends Record<string, TableDefinition<
|
|
|
684
674
|
|
|
685
675
|
export type TableClient<M extends Record<string, TableDefinition<M>>, K extends keyof M> = {
|
|
686
676
|
// Array methods - return arrays with array-level active record methods, but individual items are plain
|
|
687
|
-
getMany<strategy extends FetchStrategy<M, K
|
|
677
|
+
getMany<strategy extends FetchStrategy<M, K> = {}>(strategy: strategy): Promise<WithArrayActiveRecord<Array<DeepExpand<Selection<M, K, strategy>>>, M, K>>;
|
|
688
678
|
|
|
689
679
|
// Aggregate methods - return plain objects (no active record methods)
|
|
690
680
|
aggregate<strategy extends AggregateStrategy<M, K>>(strategy: strategy): Promise<Array<DeepExpand<AggregateCustomSelectorProperties<M, K, strategy>>>>;
|
|
691
681
|
|
|
692
682
|
// Single item methods - return individual objects with individual active record methods
|
|
693
|
-
getOne<strategy extends FetchStrategy<M, K
|
|
683
|
+
getOne<strategy extends FetchStrategy<M, K> = {}>(
|
|
694
684
|
strategy?: strategy
|
|
695
685
|
): Promise<WithActiveRecord<DeepExpand<Selection<M, K, strategy>>, M, K> | undefined>;
|
|
696
686
|
|
|
@@ -705,12 +695,12 @@ export type TableClient<M extends Record<string, TableDefinition<M>>, K extends
|
|
|
705
695
|
// UPDATED: Bulk update methods with relations support
|
|
706
696
|
update(
|
|
707
697
|
row: UpdateRowWithRelations<M, K>,
|
|
708
|
-
opts: { where:
|
|
698
|
+
opts: { where: (row: RootTableRefs<M, K>) => RawFilter | Array<PrimaryKeyObject<M, K>> }
|
|
709
699
|
): Promise<void>;
|
|
710
700
|
|
|
711
701
|
update<strategy extends FetchStrategy<M, K>>(
|
|
712
702
|
row: UpdateRowWithRelations<M, K>,
|
|
713
|
-
opts: { where:
|
|
703
|
+
opts: { where: (row: RootTableRefs<M, K>) => RawFilter | Array<PrimaryKeyObject<M, K>> },
|
|
714
704
|
strategy: strategy
|
|
715
705
|
): Promise<WithArrayActiveRecord<Array<DeepExpand<Selection<M, K, strategy>>>, M, K>>;
|
|
716
706
|
|
package/src/table/column.js
CHANGED
|
@@ -54,37 +54,6 @@ function defineColumn(column, table) {
|
|
|
54
54
|
return c;
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
-
c.enum = function(values) {
|
|
58
|
-
let list = values;
|
|
59
|
-
if (Array.isArray(values))
|
|
60
|
-
list = values;
|
|
61
|
-
else if (values && typeof values === 'object') {
|
|
62
|
-
const keys = Object.keys(values);
|
|
63
|
-
const nonNumericKeys = keys.filter((key) => !/^-?\d+$/.test(key));
|
|
64
|
-
list = (nonNumericKeys.length ? nonNumericKeys : keys).map((key) => values[key]);
|
|
65
|
-
}
|
|
66
|
-
else
|
|
67
|
-
throw new Error('enum values must be an array');
|
|
68
|
-
const allowed = new Set(list);
|
|
69
|
-
column.enum = list;
|
|
70
|
-
function validate(value) {
|
|
71
|
-
if (value === undefined || value === null)
|
|
72
|
-
return;
|
|
73
|
-
if (!allowed.has(value)) {
|
|
74
|
-
const formatted = list.map((v) => JSON.stringify(v)).join(', ');
|
|
75
|
-
throw new Error(`Column ${column.alias} must be one of: ${formatted}`);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return c.validate(validate);
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
c.enum2 = function(...values) {
|
|
82
|
-
const list = values.length === 1 && Array.isArray(values[0])
|
|
83
|
-
? values[0]
|
|
84
|
-
: values;
|
|
85
|
-
return c.enum(list);
|
|
86
|
-
};
|
|
87
|
-
|
|
88
57
|
c.default = function(value) {
|
|
89
58
|
column.default = value;
|
|
90
59
|
return c;
|
|
@@ -100,6 +69,7 @@ function defineColumn(column, table) {
|
|
|
100
69
|
var oldAlias = column.alias;
|
|
101
70
|
table._aliases.delete(oldAlias);
|
|
102
71
|
table._aliases.add(alias);
|
|
72
|
+
delete table[oldAlias];
|
|
103
73
|
table[alias] = column;
|
|
104
74
|
column.alias = alias;
|
|
105
75
|
return c;
|
|
@@ -190,4 +160,4 @@ function inspect(obj) {
|
|
|
190
160
|
}
|
|
191
161
|
|
|
192
162
|
|
|
193
|
-
module.exports = defineColumn;
|
|
163
|
+
module.exports = defineColumn;
|
|
@@ -9,7 +9,7 @@ function outputInsertedSql(context, table) {
|
|
|
9
9
|
|
|
10
10
|
function formatColumn(column) {
|
|
11
11
|
if (column.formatOut)
|
|
12
|
-
return column.formatOut(context, 'INSERTED')
|
|
12
|
+
return `${column.formatOut(context, 'INSERTED')} AS [${column._dbName}]`;
|
|
13
13
|
else
|
|
14
14
|
return `INSERTED.[${column._dbName}]`;
|
|
15
15
|
}
|
|
@@ -17,4 +17,4 @@ function outputInsertedSql(context, table) {
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
module.exports = outputInsertedSql;
|
|
20
|
+
module.exports = outputInsertedSql;
|