Skip to content

Commit ea97bfa

Browse files
committed
Support for Cloudflare D1
1 parent 4771bc8 commit ea97bfa

File tree

338 files changed

+16753
-10685
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

338 files changed

+16753
-10685
lines changed

.devcontainer/Dockerfile

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# FROM node:18-alpine
2-
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-20-bullseye
2+
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:dev-22-bookworm
3+
34

45

56
# [Optional] Uncomment this section to install additional OS packages.
@@ -11,19 +12,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-20-bullseye
1112
# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"
1213

1314
#alpine
14-
# RUN apk update
15-
# RUN apk add git
16-
# RUN apk add libnsl
17-
# RUN apk add gcompat
18-
# RUN ln -s /usr/lib/libnsl.so.3 /usr/lib/libnsl.so.1
19-
# RUN apk --no-cache add curl
20-
# RUN apk --no-cache add unixodbc
21-
# RUN apk add sudo
22-
# odbc alpine
23-
# https://docs.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver16
24-
# RUN curl -O https://download.microsoft.com/download/b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486/msodbcsql18_18.1.1.1-1_amd64.apk
25-
# RUN curl -O https://download.microsoft.com/download/b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486/mssql-tools18_18.1.1.1-1_amd64.apk
26-
# RUN sudo apk add --allow-untrusted msodbcsql18_18.1.1.1-1_amd64.apk
15+
2716
# RUN sudo apk add --allow-untrusted mssql-tools18_18.1.1.1-1_amd64.apk
2817

2918
#debian
@@ -35,6 +24,21 @@ RUN sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18
3524
#RUN ~/.bashrc
3625
RUN apt-get install -y unixodbc-dev
3726

27+
#alpine
28+
# RUN apk update
29+
# RUN apk --no-cache add curl
30+
# RUN apk --no-cache add unixodbc
31+
# RUN apk add sudo
32+
# # - RUN apk add unzip
33+
# RUN curl -O https://download.microsoft.com/download/b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486/msodbcsql18_18.1.1.1-1_amd64.apk
34+
# RUN curl -O https://download.microsoft.com/download/b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486/mssql-tools18_18.1.1.1-1_amd64.apk
35+
# RUN sudo apk add --allow-untrusted msodbcsql18_18.1.1.1-1_amd64.apk
36+
# RUN sudo apk add --allow-untrusted mssql-tools18_18.1.1.1-1_amd64.apk
37+
# RUN sudo apk add libnsl
38+
# RUN sudo apk add gcompat
39+
# RUN ln -s /usr/lib/libnsl.so.3 /usr/lib/libnsl.so.1
40+
41+
3842
# RUN mkdir -p /usr/config
3943
# WORKDIR /usr/config
4044

.github/workflows/ci.yml

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
runs-on: ubuntu-latest
1515
strategy:
1616
matrix:
17-
container: ["node:18-alpine", "node:20-alpine", "node:22-alpine"]
17+
container: ["node:18-bookworm", "node:20-bookworm", "node:22-bookworm"]
1818
container:
1919
image: ${{ matrix.container }}
2020
services:
@@ -69,28 +69,17 @@ jobs:
6969
uses: actions/setup-node@v4
7070
with:
7171
node-version: ${{ matrix.node-version }}
72-
- run: apk update
73-
- run: apk --no-cache add curl
74-
- run: apk --no-cache add unixodbc
75-
- run: apk add sudo
76-
# - run: apk add unzip
77-
- run: curl -O https://download.microsoft.com/download/b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486/msodbcsql18_18.1.1.1-1_amd64.apk
78-
- run: curl -O https://download.microsoft.com/download/b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486/mssql-tools18_18.1.1.1-1_amd64.apk
79-
- run: sudo apk add --allow-untrusted msodbcsql18_18.1.1.1-1_amd64.apk
80-
- run: sudo apk add --allow-untrusted mssql-tools18_18.1.1.1-1_amd64.apk
81-
- run: sudo apk add libnsl
82-
- run: sudo apk add gcompat
83-
- run: ln -s /usr/lib/libnsl.so.3 /usr/lib/libnsl.so.1
84-
# - run: npm install
85-
# - run: wget https://download.oracle.com/otn_software/linux/instantclient/193000/instantclient-basic-linux.x64-19.3.0.0.0dbru.zip && unzip instantclient-basic-linux.x64-19.3.0.0.0dbru.zip && cp -r instantclient_19_3/* /lib && rm -rf instantclient-basic-linux.x64-19.3.0.0.0dbru.zip && apk --no-cache add libaio && cd /lib && ln -s libnsl.so.2 /usr/lib/libnsl.so.1 && ln -s libc.so /usr/lib/libresolv.so.2
86-
# - run: ldd /__w/rdb/rdb/tests/libsybdrvodb.so
72+
- run: curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
73+
- run: curl https://packages.microsoft.com/config/debian/11/prod.list > /etc/apt/sources.list.d/mssql-release.list
74+
- run: apt-get update
75+
- run: ACCEPT_EULA=Y apt-get install -y msodbcsql18
76+
- run: apt-get install -y unixodbc-dev
8777
- run: npm install
8878
- run: npm run lint
8979
- run: npm run tscheck
9080
- run: npm run test
91-
- run: npm run coverage
92-
# Only run the coverage once
93-
- if: ${{ matrix.container == 'node:16-alpine' && github.ref == 'refs/heads/master'}}
81+
- run: npm run coverage # Only run the coverage once
82+
- if: ${{ matrix.container == 'node:18-bookworm' && github.ref == 'refs/heads/master'}}
9483
name: Get Coverage for badge
9584
run: |
9685
# var SUMMARY = [

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ package-lock.json
77
.vscode
88
demo.db
99
demo*.db*
10-
coverage
10+
coverage
11+
.env

README.md

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ The ultimate Object Relational Mapper for Node.js and Typescript, offering seaml
2929
✅ MySQL
3030
✅ Oracle
3131
✅ SAP ASE
32-
✅ SQLite
32+
✅ SQLite
33+
✅ Cloudflare D1
3334

3435

3536
This is the _Modern Typescript Documentation_. Are you looking for the [_Classic Documentation_](https://github.com/alfateam/orange-orm/blob/master/docs/docs.md) ?
@@ -319,6 +320,7 @@ import map from './map';
319320

320321
const db = map.http('http://localhost:3000/orange');
321322
```
323+
322324
__MySQL__
323325
```bash
324326
$ npm install mysql2
@@ -364,6 +366,38 @@ With schema
364366
import map from './map';
365367
const db = map.postgres('postgres://postgres:postgres@postgres/postgres?search_path=custom');
366368
```
369+
__Cloudflare D1__
370+
<sub>📄 wrangler.toml</sub>
371+
```toml
372+
name = "d1-tutorial"
373+
main = "src/index.ts"
374+
compatibility_date = "2025-02-04"
375+
376+
# Bind a D1 database. D1 is Cloudflare’s native serverless SQL database.
377+
# Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#d1-databases
378+
[[d1_databases]]
379+
binding = "DB"
380+
database_name = "<your-name-for-the-database>"
381+
database_id = "<your-guid-for-the-database>"
382+
```
383+
384+
<sub>📄 src/index.ts</sub>
385+
```javascript
386+
import map from './map';
387+
388+
export interface Env {
389+
// Must match the binding name in wrangler.toml
390+
DB: D1Database;
391+
}
392+
393+
export default {
394+
async fetch(request, env): Promise<Response> {
395+
const db = map.d1(env.DB);
396+
const customers = await db.customer.getAll();
397+
return Response.json(customers);
398+
},
399+
} satisfies ExportedHandler<Env>;
400+
```
367401
__Oracle__
368402
```bash
369403
npm install oracledb
@@ -468,7 +502,7 @@ Currently, there are three concurrency strategies:
468502
- <strong>`overwrite`</strong> Overwrites the property, regardless of changes by others.
469503
- <strong>`skipOnConflict`</strong> Silently avoids updating the property if another user has modified it in the interim.
470504
471-
The <strong>concurrency</strong> option can be set either for the whole table or individually for each column. In the example below, we are using the <strong>overwrite</strong> strategy on the <strong>vendor</strong> table except on the column <strong>balance</strong> which uses the <strong>skipOnConflict</strong> strategy. In this particular case, a row with <strong>id: 1</strong> already exists, the <strong>name</strong> and <strong>isActive</strong> fields will be overwritten, but the balance will remain the same as in the original record, demonstrating the effectiveness of combining multiple <strong>concurrency</strong> strategies.
505+
The <strong>concurrency</strong> option can be set either for the whole table or individually for each column. In the example below, we've set the concurrency strategy on <strong>vendor</strong> table to <strong>overwrite</strong> except for the column <strong>balance</strong> which uses the <strong>skipOnConflict</strong> strategy. In this particular case, a row with <strong>id: 1</strong> already exists, the <strong>name</strong> and <strong>isActive</strong> fields will be overwritten, but the balance will remain the same as in the original record, demonstrating the effectiveness of combining multiple <strong>concurrency</strong> strategies.
472506
473507
```javascript
474508
import map from './map';
@@ -885,7 +919,7 @@ Currently, there are three concurrency strategies:
885919
- <strong>`overwrite`</strong> Overwrites the property, regardless of changes by others.
886920
- <strong>`skipOnConflict`</strong> Silently avoids updating the property if another user has modified it in the interim.
887921
888-
The <strong>concurrency</strong> option can be set either for the whole table or individually for each column. In the example below, we are using the <strong>overwrite</strong> strategy on the table <strong>vendor</strong> except on the column <strong>balance</strong> which uses the <strong>skipOnConflict</strong> strategy. In this particular case, a row with <strong>id: 1</strong> already exists, the <strong>name</strong> and <strong>isActive</strong> fields will be overwritten, but the balance will remain the same as in the original record, demonstrating the effectiveness of combining multiple <strong>concurrency</strong> strategies.
922+
The <strong>concurrency</strong> option can be set either for the whole table or individually for each column. In the example below, we've set the concurrency strategy on <strong>vendor</strong> table to <strong>overwrite</strong> except for the column <strong>balance</strong> which uses the <strong>skipOnConflict</strong> strategy. In this particular case, a row with <strong>id: 1</strong> already exists, the <strong>name</strong> and <strong>isActive</strong> fields will be overwritten, but the balance will remain the same as in the original record, demonstrating the effectiveness of combining multiple <strong>concurrency</strong> strategies.
889923
890924
```javascript
891925
import map from './map';
@@ -1580,6 +1614,8 @@ async function getRows() {
15801614
Within the transaction, a customer is retrieved and its balance updated using the tx object to ensure operations are transactional.
15811615
An error is deliberately thrown to demonstrate a rollback, ensuring all previous changes within the transaction are reverted.
15821616
Always use the provided tx object for operations within the transaction to maintain data integrity.</p>
1617+
<p>(NOTE: Transactions are not supported for Cloudflare D1)</p>
1618+
15831619
15841620
```javascript
15851621
import map from './map';
@@ -1597,6 +1633,7 @@ async function execute() {
15971633
}
15981634

15991635
```
1636+
16001637
</details>
16011638
16021639
<details><summary><strong>Data types</strong></summary>
@@ -1966,7 +2003,7 @@ async function getRows() {
19662003
</details>
19672004
19682005
<details><summary><strong>Logging</strong></summary>
1969-
<p>You enable logging by listening to the query event on the `orange` object. During this event, both the SQL statement and any associated parameters are logged.</p>
2006+
<p>You enable logging by listening to the query event on the `orange` object. During this event, both the SQL statement and any associated parameters are logged. The logged output reveals the sequence of SQL commands executed, offering developers a transparent view into database operations, which aids in debugging and ensures data integrity.</p>
19702007
19712008
```javascript
19722009
import orange from 'orange-orm';
@@ -1996,8 +2033,10 @@ async function updateRow() {
19962033
19972034
output:
19982035
```bash
2036+
BEGIN
19992037
select _order.id as s_order0,_order.orderDate as s_order1,_order.customerId as s_order2 from _order _order where _order.id=2 order by _order.id limit 1
20002038
select orderLine.id as sorderLine0,orderLine.orderId as sorderLine1,orderLine.product as sorderLine2,orderLine.amount as sorderLine3 from orderLine orderLine where orderLine.orderId in (2) order by orderLine.id
2039+
COMMIT
20012040
BEGIN
20022041
select _order.id as s_order0,_order.orderDate as s_order1,_order.customerId as s_order2 from _order _order where _order.id=2 order by _order.id limit 1
20032042
INSERT INTO orderLine (orderId,product,amount) VALUES (2,?,300)

docs/changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
## Changelog
2+
__4.5.0__
3+
Support for Cloudflare D1.
24
__4.4.2__
35
Support for schema in connection string. Postgrs only. [#116](https://github.com/alfateam/orange-orm/issues/118)
46
__4.4.1__

package.json

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "orange-orm",
3-
"version": "4.4.2",
3+
"version": "4.5.0",
44
"main": "./src/index.js",
55
"browser": "./src/client/index.mjs",
66
"bin": {
@@ -48,23 +48,21 @@
4848
"lint": "eslint ./",
4949
"fix": "eslint ./ --fix",
5050
"owasp": "owasp-dependency-check --project \"MY_PROJECT\" --scan \"package-lock.json\" --exclude \"dependency-check-bin\" --out \"owasp\" --format HTML",
51-
"beta": "publish --tag beta"
51+
"beta": "npm publish --tag beta"
5252
},
5353
"dependencies": {
5454
"@lroal/on-change": "^4.0.2",
5555
"@tediousjs/connection-string": "^0.4.1",
5656
"@types/express": "^4.17.13",
57+
"@cloudflare/workers-types": "^4.20241106.0",
5758
"@types/oracledb": "^6.0.4",
5859
"@types/tedious": "^4.0.14",
5960
"ajv": "^6.10.2",
6061
"axios": "^1.6.2",
61-
"deferred": "^0.7.5",
6262
"fast-json-patch": "^3.1.1",
6363
"findup-sync": "^5.0.0",
6464
"glob": "^10.3.4",
6565
"module-definition": "^4.0.0",
66-
"node-cls": "^1.0.5",
67-
"promise": "^8.0.3",
6866
"rfdc": "^1.2.0",
6967
"uuid": "^8.3.2"
7068
},
@@ -99,10 +97,15 @@
9997
},
10098
"tedious": {
10199
"optional": true
100+
},
101+
"oracledb": {
102+
"optional": true
102103
}
103104
},
104-
"devDependencies": {
105-
"@rollup/plugin-commonjs": "^21.0.1",
105+
"devDependencies": {
106+
"@miniflare/d1": "^2.14.4",
107+
"@rollup/plugin-commonjs": "^28.0.2",
108+
"@rollup/plugin-json": "^6.1.0",
106109
"@rollup/plugin-node-resolve": "^13.0.0",
107110
"@typescript-eslint/eslint-plugin": "^6.x",
108111
"@typescript-eslint/parser": "^6.x",
@@ -111,6 +114,7 @@
111114
"eslint": "^8.57.0",
112115
"eslint-plugin-jest": "^27.1.7",
113116
"express": "^4.18.2",
117+
"miniflare": "^3.20250129.0",
114118
"msnodesqlv8": "^4.1.0",
115119
"mysql2": "^3.9.4",
116120
"oracledb": "^6.3.0",
@@ -121,7 +125,7 @@
121125
"sqlite3": "^5.0.2",
122126
"tedious": "^18.2.0",
123127
"typescript": "^5.4.5",
124-
"vitest": "^0.34.1"
128+
"vitest": "^0.34.6"
125129
},
126130
"engines": {
127131
"node": ">= 8.0.0"

src/applyPatch.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
const fastjson = require('fast-json-patch');
2-
let { inspect } = require('util');
3-
let assert = require('assert');
42
let fromCompareObject = require('./fromCompareObject');
53
let toCompareObject = require('./toCompareObject');
64

@@ -43,12 +41,12 @@ function applyPatch({ options = {} }, dto, changes, column) {
4341
assertDatesEqual(oldValue, expectedOldValue);
4442
}
4543
else
46-
assert.deepEqual(oldValue, expectedOldValue);
44+
assertDeepEqual(oldValue, expectedOldValue);
4745
}
4846
catch (e) {
4947
if (concurrency === 'skipOnConflict')
5048
return false;
51-
throw new Error(`The field ${change.path.replace('/', '')} was changed by another user. Expected ${inspect(fromCompareObject(expectedOldValue), false, 10)}, but was ${inspect(fromCompareObject(oldValue), false, 10)}.`);
49+
throw new Error(`The field ${change.path.replace('/', '')} was changed by another user. Expected ${inspect(fromCompareObject(expectedOldValue))}, but was ${inspect(fromCompareObject(oldValue))}.`);
5250
}
5351
}
5452
return true;
@@ -99,7 +97,16 @@ function assertDatesEqual(date1, date2) {
9997
date1 = `${parts1[0]}T${time1parts[0]}`;
10098
date2 = `${parts2[0]}T${time2parts[0]}`;
10199
}
102-
assert.deepEqual(date1, date2);
100+
assertDeepEqual(date1, date2);
101+
}
102+
103+
function assertDeepEqual(a, b) {
104+
if (JSON.stringify(a) !== JSON.stringify(b))
105+
throw new Error('A, b are not equal');
106+
}
107+
108+
function inspect(obj) {
109+
return JSON.stringify(obj, null, 2);
103110
}
104111

105112
module.exports = applyPatch;

src/client/clientMap.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ function map(index, _fn) {
3939
dbMap.sap = throwDb;
4040
dbMap.oracle = throwDb;
4141
dbMap.sqlite = throwDb;
42+
dbMap.d1 = throwDb;
4243

4344
function throwDb() {
4445
throw new Error('Cannot create pool for database outside node');
@@ -65,6 +66,7 @@ function map(index, _fn) {
6566
onFinal.sap = () => index({ db: throwDb, providers: dbMap });
6667
onFinal.oracle = () => index({ db: throwDb, providers: dbMap });
6768
onFinal.sqlite = () => index({ db: throwDb, providers: dbMap });
69+
onFinal.d1 = () => index({ db: throwDb, providers: dbMap });
6870

6971
return new Proxy(onFinal, handler);
7072
}

src/client/createProviders.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ function createProviders(index) {
4848
return createPool.bind(null, 'sqlite');
4949
}
5050
});
51+
Object.defineProperty(dbMap, 'd1', {
52+
get: function() {
53+
return createPool.bind(null, 'd1');
54+
}
55+
});
5156
Object.defineProperty(dbMap, 'http', {
5257
get: function() {
5358
return createPool.bind(null, 'http');
@@ -97,12 +102,19 @@ function negotiateCachedPool(fn, providers) {
97102
get sqlite() {
98103
return createPool.bind(null, 'sqlite');
99104
},
105+
get d1() {
106+
return createPool.bind(null, 'd1');
107+
},
100108
get http() {
101109
return createPool.bind(null, 'http');
102110
}
103111
};
104112

105113
function createPool(providerName, ...args) {
114+
//todo
115+
if (providerName === 'd1') {
116+
return providers[providerName].apply(null, args);
117+
}
106118
const key = JSON.stringify(args);
107119
if (!cache[providerName])
108120
cache[providerName] = {};

0 commit comments

Comments
 (0)