Skip to content

Commit

Permalink
Support for Cloudflare D1
Browse files Browse the repository at this point in the history
  • Loading branch information
lroal committed Feb 16, 2025
1 parent 4771bc8 commit ea97bfa
Show file tree
Hide file tree
Showing 338 changed files with 16,753 additions and 10,685 deletions.
32 changes: 18 additions & 14 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# FROM node:18-alpine
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-20-bullseye
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:dev-22-bookworm



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

#alpine
# RUN apk update
# RUN apk add git
# RUN apk add libnsl
# RUN apk add gcompat
# RUN ln -s /usr/lib/libnsl.so.3 /usr/lib/libnsl.so.1
# RUN apk --no-cache add curl
# RUN apk --no-cache add unixodbc
# RUN apk add sudo
# odbc alpine
# https://docs.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver16
# RUN curl -O https://download.microsoft.com/download/b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486/msodbcsql18_18.1.1.1-1_amd64.apk
# 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
# RUN sudo apk add --allow-untrusted msodbcsql18_18.1.1.1-1_amd64.apk

# RUN sudo apk add --allow-untrusted mssql-tools18_18.1.1.1-1_amd64.apk

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

#alpine
# RUN apk update
# RUN apk --no-cache add curl
# RUN apk --no-cache add unixodbc
# RUN apk add sudo
# # - RUN apk add unzip
# RUN curl -O https://download.microsoft.com/download/b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486/msodbcsql18_18.1.1.1-1_amd64.apk
# 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
# RUN sudo apk add --allow-untrusted msodbcsql18_18.1.1.1-1_amd64.apk
# RUN sudo apk add --allow-untrusted mssql-tools18_18.1.1.1-1_amd64.apk
# RUN sudo apk add libnsl
# RUN sudo apk add gcompat
# RUN ln -s /usr/lib/libnsl.so.3 /usr/lib/libnsl.so.1


# RUN mkdir -p /usr/config
# WORKDIR /usr/config

Expand Down
27 changes: 8 additions & 19 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
container: ["node:18-alpine", "node:20-alpine", "node:22-alpine"]
container: ["node:18-bookworm", "node:20-bookworm", "node:22-bookworm"]
container:
image: ${{ matrix.container }}
services:
Expand Down Expand Up @@ -69,28 +69,17 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: apk update
- run: apk --no-cache add curl
- run: apk --no-cache add unixodbc
- run: apk add sudo
# - run: apk add unzip
- run: curl -O https://download.microsoft.com/download/b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486/msodbcsql18_18.1.1.1-1_amd64.apk
- 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
- run: sudo apk add --allow-untrusted msodbcsql18_18.1.1.1-1_amd64.apk
- run: sudo apk add --allow-untrusted mssql-tools18_18.1.1.1-1_amd64.apk
- run: sudo apk add libnsl
- run: sudo apk add gcompat
- run: ln -s /usr/lib/libnsl.so.3 /usr/lib/libnsl.so.1
# - run: npm install
# - 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
# - run: ldd /__w/rdb/rdb/tests/libsybdrvodb.so
- run: curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
- run: curl https://packages.microsoft.com/config/debian/11/prod.list > /etc/apt/sources.list.d/mssql-release.list
- run: apt-get update
- run: ACCEPT_EULA=Y apt-get install -y msodbcsql18
- run: apt-get install -y unixodbc-dev
- run: npm install
- run: npm run lint
- run: npm run tscheck
- run: npm run test
- run: npm run coverage
# Only run the coverage once
- if: ${{ matrix.container == 'node:16-alpine' && github.ref == 'refs/heads/master'}}
- run: npm run coverage # Only run the coverage once
- if: ${{ matrix.container == 'node:18-bookworm' && github.ref == 'refs/heads/master'}}
name: Get Coverage for badge
run: |
# var SUMMARY = [
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ package-lock.json
.vscode
demo.db
demo*.db*
coverage
coverage
.env
47 changes: 43 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ The ultimate Object Relational Mapper for Node.js and Typescript, offering seaml
✅ MySQL
✅ Oracle
✅ SAP ASE
✅ SQLite
✅ SQLite
✅ Cloudflare D1


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

const db = map.http('http://localhost:3000/orange');
```

__MySQL__
```bash
$ npm install mysql2
Expand Down Expand Up @@ -364,6 +366,38 @@ With schema
import map from './map';
const db = map.postgres('postgres://postgres:postgres@postgres/postgres?search_path=custom');
```
__Cloudflare D1__
<sub>📄 wrangler.toml</sub>
```toml
name = "d1-tutorial"
main = "src/index.ts"
compatibility_date = "2025-02-04"

# Bind a D1 database. D1 is Cloudflare’s native serverless SQL database.
# Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#d1-databases
[[d1_databases]]
binding = "DB"
database_name = "<your-name-for-the-database>"
database_id = "<your-guid-for-the-database>"
```

<sub>📄 src/index.ts</sub>
```javascript
import map from './map';

export interface Env {
// Must match the binding name in wrangler.toml
DB: D1Database;
}

export default {
async fetch(request, env): Promise<Response> {
const db = map.d1(env.DB);
const customers = await db.customer.getAll();
return Response.json(customers);
},
} satisfies ExportedHandler<Env>;
```
__Oracle__
```bash
npm install oracledb
Expand Down Expand Up @@ -468,7 +502,7 @@ Currently, there are three concurrency strategies:
- <strong>`overwrite`</strong> Overwrites the property, regardless of changes by others.
- <strong>`skipOnConflict`</strong> Silently avoids updating the property if another user has modified it in the interim.
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.
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.
```javascript
import map from './map';
Expand Down Expand Up @@ -885,7 +919,7 @@ Currently, there are three concurrency strategies:
- <strong>`overwrite`</strong> Overwrites the property, regardless of changes by others.
- <strong>`skipOnConflict`</strong> Silently avoids updating the property if another user has modified it in the interim.
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.
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.
```javascript
import map from './map';
Expand Down Expand Up @@ -1580,6 +1614,8 @@ async function getRows() {
Within the transaction, a customer is retrieved and its balance updated using the tx object to ensure operations are transactional.
An error is deliberately thrown to demonstrate a rollback, ensuring all previous changes within the transaction are reverted.
Always use the provided tx object for operations within the transaction to maintain data integrity.</p>
<p>(NOTE: Transactions are not supported for Cloudflare D1)</p>
```javascript
import map from './map';
Expand All @@ -1597,6 +1633,7 @@ async function execute() {
}

```
</details>
<details><summary><strong>Data types</strong></summary>
Expand Down Expand Up @@ -1966,7 +2003,7 @@ async function getRows() {
</details>
<details><summary><strong>Logging</strong></summary>
<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>
<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>
```javascript
import orange from 'orange-orm';
Expand Down Expand Up @@ -1996,8 +2033,10 @@ async function updateRow() {
output:
```bash
BEGIN
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
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
COMMIT
BEGIN
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
INSERT INTO orderLine (orderId,product,amount) VALUES (2,?,300)
Expand Down
2 changes: 2 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
## Changelog
__4.5.0__
Support for Cloudflare D1.
__4.4.2__
Support for schema in connection string. Postgrs only. [#116](https://github.com/alfateam/orange-orm/issues/118)
__4.4.1__
Expand Down
20 changes: 12 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "orange-orm",
"version": "4.4.2",
"version": "4.5.0",
"main": "./src/index.js",
"browser": "./src/client/index.mjs",
"bin": {
Expand Down Expand Up @@ -48,23 +48,21 @@
"lint": "eslint ./",
"fix": "eslint ./ --fix",
"owasp": "owasp-dependency-check --project \"MY_PROJECT\" --scan \"package-lock.json\" --exclude \"dependency-check-bin\" --out \"owasp\" --format HTML",
"beta": "publish --tag beta"
"beta": "npm publish --tag beta"
},
"dependencies": {
"@lroal/on-change": "^4.0.2",
"@tediousjs/connection-string": "^0.4.1",
"@types/express": "^4.17.13",
"@cloudflare/workers-types": "^4.20241106.0",
"@types/oracledb": "^6.0.4",
"@types/tedious": "^4.0.14",
"ajv": "^6.10.2",
"axios": "^1.6.2",
"deferred": "^0.7.5",
"fast-json-patch": "^3.1.1",
"findup-sync": "^5.0.0",
"glob": "^10.3.4",
"module-definition": "^4.0.0",
"node-cls": "^1.0.5",
"promise": "^8.0.3",
"rfdc": "^1.2.0",
"uuid": "^8.3.2"
},
Expand Down Expand Up @@ -99,10 +97,15 @@
},
"tedious": {
"optional": true
},
"oracledb": {
"optional": true
}
},
"devDependencies": {
"@rollup/plugin-commonjs": "^21.0.1",
"devDependencies": {
"@miniflare/d1": "^2.14.4",
"@rollup/plugin-commonjs": "^28.0.2",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^13.0.0",
"@typescript-eslint/eslint-plugin": "^6.x",
"@typescript-eslint/parser": "^6.x",
Expand All @@ -111,6 +114,7 @@
"eslint": "^8.57.0",
"eslint-plugin-jest": "^27.1.7",
"express": "^4.18.2",
"miniflare": "^3.20250129.0",
"msnodesqlv8": "^4.1.0",
"mysql2": "^3.9.4",
"oracledb": "^6.3.0",
Expand All @@ -121,7 +125,7 @@
"sqlite3": "^5.0.2",
"tedious": "^18.2.0",
"typescript": "^5.4.5",
"vitest": "^0.34.1"
"vitest": "^0.34.6"
},
"engines": {
"node": ">= 8.0.0"
Expand Down
17 changes: 12 additions & 5 deletions src/applyPatch.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
const fastjson = require('fast-json-patch');
let { inspect } = require('util');
let assert = require('assert');
let fromCompareObject = require('./fromCompareObject');
let toCompareObject = require('./toCompareObject');

Expand Down Expand Up @@ -43,12 +41,12 @@ function applyPatch({ options = {} }, dto, changes, column) {
assertDatesEqual(oldValue, expectedOldValue);
}
else
assert.deepEqual(oldValue, expectedOldValue);
assertDeepEqual(oldValue, expectedOldValue);
}
catch (e) {
if (concurrency === 'skipOnConflict')
return false;
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)}.`);
throw new Error(`The field ${change.path.replace('/', '')} was changed by another user. Expected ${inspect(fromCompareObject(expectedOldValue))}, but was ${inspect(fromCompareObject(oldValue))}.`);
}
}
return true;
Expand Down Expand Up @@ -99,7 +97,16 @@ function assertDatesEqual(date1, date2) {
date1 = `${parts1[0]}T${time1parts[0]}`;
date2 = `${parts2[0]}T${time2parts[0]}`;
}
assert.deepEqual(date1, date2);
assertDeepEqual(date1, date2);
}

function assertDeepEqual(a, b) {
if (JSON.stringify(a) !== JSON.stringify(b))
throw new Error('A, b are not equal');
}

function inspect(obj) {
return JSON.stringify(obj, null, 2);
}

module.exports = applyPatch;
2 changes: 2 additions & 0 deletions src/client/clientMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ function map(index, _fn) {
dbMap.sap = throwDb;
dbMap.oracle = throwDb;
dbMap.sqlite = throwDb;
dbMap.d1 = throwDb;

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

return new Proxy(onFinal, handler);
}
Expand Down
12 changes: 12 additions & 0 deletions src/client/createProviders.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ function createProviders(index) {
return createPool.bind(null, 'sqlite');
}
});
Object.defineProperty(dbMap, 'd1', {
get: function() {
return createPool.bind(null, 'd1');
}
});
Object.defineProperty(dbMap, 'http', {
get: function() {
return createPool.bind(null, 'http');
Expand Down Expand Up @@ -97,12 +102,19 @@ function negotiateCachedPool(fn, providers) {
get sqlite() {
return createPool.bind(null, 'sqlite');
},
get d1() {
return createPool.bind(null, 'd1');
},
get http() {
return createPool.bind(null, 'http');
}
};

function createPool(providerName, ...args) {
//todo
if (providerName === 'd1') {
return providers[providerName].apply(null, args);
}
const key = JSON.stringify(args);
if (!cache[providerName])
cache[providerName] = {};
Expand Down
Loading

0 comments on commit ea97bfa

Please sign in to comment.