mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-11-29 12:14:59 +03:00
feat(client): add octobase-node to electron (#1672)
Co-authored-by: Himself65 <himself65@outlook.com>
This commit is contained in:
parent
449ffbc73f
commit
c2b1a9b118
48
.github/workflows/client-app.yml
vendored
48
.github/workflows/client-app.yml
vendored
@ -69,6 +69,14 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
|
|
||||||
|
- name: install Rust stable
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Rust cache
|
||||||
|
uses: swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: './packages/octobase-node -> target'
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: before-make-web-static
|
name: before-make-web-static
|
||||||
@ -79,6 +87,13 @@ jobs:
|
|||||||
name: before-make-electron-dist
|
name: before-make-electron-dist
|
||||||
path: apps/electron/dist
|
path: apps/electron/dist
|
||||||
|
|
||||||
|
- name: build octobase-node
|
||||||
|
run: yarn build:octobase-node
|
||||||
|
working-directory: apps/electron
|
||||||
|
|
||||||
|
- name: move octobase Binary
|
||||||
|
run: cp ./packages/octobase-node/octobase.*.node ./apps/electron/dist/layers/main/
|
||||||
|
|
||||||
- name: make build
|
- name: make build
|
||||||
run: yarn make-macos-x64
|
run: yarn make-macos-x64
|
||||||
working-directory: apps/electron
|
working-directory: apps/electron
|
||||||
@ -102,6 +117,17 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
|
|
||||||
|
- name: install Rust stable
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: add arm64 target
|
||||||
|
run: rustup target add aarch64-apple-darwin
|
||||||
|
|
||||||
|
- name: Rust cache
|
||||||
|
uses: swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: './packages/octobase-node -> target'
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: before-make-web-static
|
name: before-make-web-static
|
||||||
@ -112,6 +138,13 @@ jobs:
|
|||||||
name: before-make-electron-dist
|
name: before-make-electron-dist
|
||||||
path: apps/electron/dist
|
path: apps/electron/dist
|
||||||
|
|
||||||
|
- name: build octobase-node
|
||||||
|
run: yarn build:octobase-node
|
||||||
|
working-directory: apps/electron
|
||||||
|
|
||||||
|
- name: move octobase Binary
|
||||||
|
run: cp ./packages/octobase-node/octobase.*.node ./apps/electron/dist/layers/main/
|
||||||
|
|
||||||
- name: make build
|
- name: make build
|
||||||
run: yarn make-macos-arm64
|
run: yarn make-macos-arm64
|
||||||
working-directory: apps/electron
|
working-directory: apps/electron
|
||||||
@ -135,6 +168,9 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
|
|
||||||
|
- name: install Rust stable
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: before-make-web-static
|
name: before-make-web-static
|
||||||
@ -145,6 +181,18 @@ jobs:
|
|||||||
name: before-make-electron-dist
|
name: before-make-electron-dist
|
||||||
path: apps/electron/dist
|
path: apps/electron/dist
|
||||||
|
|
||||||
|
- name: move octobase Binary
|
||||||
|
run: cp ./packages/octobase-node/octobase.*.node ./apps/electron/dist/layers/main/
|
||||||
|
|
||||||
|
- name: Rust cache
|
||||||
|
uses: swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: './packages/octobase-node -> target'
|
||||||
|
|
||||||
|
- name: build octobase-node
|
||||||
|
run: yarn build:octobase-node
|
||||||
|
working-directory: apps/electron
|
||||||
|
|
||||||
- name: make build
|
- name: make build
|
||||||
run: yarn make-windows-x64
|
run: yarn make-windows-x64
|
||||||
working-directory: apps/electron
|
working-directory: apps/electron
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
yarnPath: .yarn/releases/yarn-3.4.1.cjs
|
|
||||||
nodeLinker: node-modules
|
|
@ -9,10 +9,13 @@ See https://github.com/electron/forge/issues/2633
|
|||||||
|
|
||||||
```
|
```
|
||||||
# in project root, start web app at :8080
|
# in project root, start web app at :8080
|
||||||
pnpm dev
|
yarn dev
|
||||||
|
|
||||||
|
# build octobase-node
|
||||||
|
yarn workspace @affine/octobase-node build
|
||||||
|
|
||||||
# in /apps/electron, start electron app
|
# in /apps/electron, start electron app
|
||||||
pnpm dev
|
yarn dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
21
apps/electron/layers/main/src/app-state/index.ts
Normal file
21
apps/electron/layers/main/src/app-state/index.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import * as os from 'node:os';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
import { Storage } from '@affine/octobase-node';
|
||||||
|
import { ipcMain } from 'electron';
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
|
||||||
|
const AFFINE_ROOT = path.join(os.homedir(), '.affine');
|
||||||
|
|
||||||
|
fs.ensureDirSync(AFFINE_ROOT);
|
||||||
|
|
||||||
|
// todo: rethink this
|
||||||
|
export const appState = {
|
||||||
|
storage: new Storage(path.join(AFFINE_ROOT, 'test.db')),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const registerHandlers = () => {
|
||||||
|
ipcMain.handle('workspaceSync', async (_, id) => {
|
||||||
|
return appState.storage.sync(id, '');
|
||||||
|
});
|
||||||
|
};
|
@ -2,6 +2,7 @@ import './security-restrictions';
|
|||||||
|
|
||||||
import { app } from 'electron';
|
import { app } from 'electron';
|
||||||
|
|
||||||
|
import { registerHandlers } from './app-state';
|
||||||
import { restoreOrCreateWindow } from './main-window';
|
import { restoreOrCreateWindow } from './main-window';
|
||||||
import { registerProtocol } from './protocol';
|
import { registerProtocol } from './protocol';
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ app.on('activate', restoreOrCreateWindow);
|
|||||||
app
|
app
|
||||||
.whenReady()
|
.whenReady()
|
||||||
.then(registerProtocol)
|
.then(registerProtocol)
|
||||||
|
.then(registerHandlers)
|
||||||
.then(restoreOrCreateWindow)
|
.then(restoreOrCreateWindow)
|
||||||
.catch(e => console.error('Failed create window:', e));
|
.catch(e => console.error('Failed create window:', e));
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* @module preload
|
* @module preload
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { contextBridge } from 'electron';
|
import { contextBridge, ipcRenderer } from 'electron';
|
||||||
|
|
||||||
import { sha256sum } from './sha256sum';
|
import { sha256sum } from './sha256sum';
|
||||||
|
|
||||||
@ -31,3 +31,11 @@ contextBridge.exposeInMainWorld('yerba', { version: 0.1 });
|
|||||||
* window.nodeCrypto('data')
|
* window.nodeCrypto('data')
|
||||||
*/
|
*/
|
||||||
contextBridge.exposeInMainWorld('nodeCrypto', { sha256sum });
|
contextBridge.exposeInMainWorld('nodeCrypto', { sha256sum });
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('apis', {
|
||||||
|
workspaceSync: (id: string) => ipcRenderer.invoke('workspaceSync', id),
|
||||||
|
});
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('appInfo', {
|
||||||
|
electron: 1,
|
||||||
|
});
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"make-macos-arm64": "electron-forge make --platform=darwin --arch=arm64",
|
"make-macos-arm64": "electron-forge make --platform=darwin --arch=arm64",
|
||||||
"make-macos-x64": "electron-forge make --platform=darwin --arch=x64",
|
"make-macos-x64": "electron-forge make --platform=darwin --arch=x64",
|
||||||
"make-windows-x64": "electron-forge make --platform=win32 --arch=x64",
|
"make-windows-x64": "electron-forge make --platform=win32 --arch=x64",
|
||||||
|
"build:octobase-node": "yarn workspace @affine/octobase-node build",
|
||||||
"postinstall": "ELECTRON_RUN_AS_NODE=1 electron scripts/update-electron-vendors.mjs"
|
"postinstall": "ELECTRON_RUN_AS_NODE=1 electron scripts/update-electron-vendors.mjs"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
@ -21,6 +22,7 @@
|
|||||||
},
|
},
|
||||||
"main": "./dist/layers/main/index.js",
|
"main": "./dist/layers/main/index.js",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@affine/octobase-node": "workspace:*",
|
||||||
"@electron-forge/cli": "^6.0.5",
|
"@electron-forge/cli": "^6.0.5",
|
||||||
"@electron-forge/core": "^6.0.5",
|
"@electron-forge/core": "^6.0.5",
|
||||||
"@electron-forge/core-utils": "^6.0.5",
|
"@electron-forge/core-utils": "^6.0.5",
|
||||||
@ -37,6 +39,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"electron-window-state": "^5.0.3"
|
"electron-window-state": "^5.0.3",
|
||||||
|
"fs-extra": "^11.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,18 +27,18 @@ console.log('build with following dir', {
|
|||||||
|
|
||||||
// step 0: clean up
|
// step 0: clean up
|
||||||
await cleanup();
|
await cleanup();
|
||||||
console.log('Clean up done');
|
echo('Clean up done');
|
||||||
|
|
||||||
// step 1: build web (nextjs) dist
|
// step 1: build web (nextjs) dist
|
||||||
cd(repoRootDir);
|
cd(repoRootDir);
|
||||||
await $`pnpm i -r`;
|
await $`yarn add`;
|
||||||
await $`pnpm build`;
|
await $`yarn build`;
|
||||||
await $`pnpm export`;
|
await $`yarn export`;
|
||||||
await fs.move(affineWebOutDir, publicAffineOutDir, { overwrite: true });
|
await fs.move(affineWebOutDir, publicAffineOutDir, { overwrite: true });
|
||||||
|
|
||||||
// step 2: build electron resources
|
// step 2: build electron resources
|
||||||
await buildLayers();
|
await buildLayers();
|
||||||
console.log('Build layers done');
|
echo('Build layers done');
|
||||||
|
|
||||||
/// --------
|
/// --------
|
||||||
/// --------
|
/// --------
|
||||||
|
197
packages/octobase-node/.gitignore
vendored
Normal file
197
packages/octobase-node/.gitignore
vendored
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/node
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=node
|
||||||
|
|
||||||
|
### Node ###
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# TypeScript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
.env.test
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/node
|
||||||
|
|
||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/macos
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=macos
|
||||||
|
|
||||||
|
### macOS ###
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two
|
||||||
|
Icon
|
||||||
|
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
### macOS Patch ###
|
||||||
|
# iCloud generated files
|
||||||
|
*.icloud
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/macos
|
||||||
|
|
||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/windows
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=windows
|
||||||
|
|
||||||
|
### Windows ###
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
Thumbs.db:encryptable
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/windows
|
||||||
|
|
||||||
|
#Added by cargo
|
||||||
|
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
|
|
||||||
|
.pnp.*
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/patches
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/sdks
|
||||||
|
!.yarn/versions
|
||||||
|
|
||||||
|
*.node
|
29
packages/octobase-node/Cargo.toml
Normal file
29
packages/octobase-node/Cargo.toml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
[package]
|
||||||
|
edition = "2021"
|
||||||
|
name = "affine_octobase"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
|
||||||
|
napi = { version = "2.11.1", default-features = false, features = ["napi4", "tokio_rt"] }
|
||||||
|
napi-derive = "2.11.0"
|
||||||
|
jwst = { git = "https://github.com/toeverything/OctoBase", rev = "b701935", package = "jwst" }
|
||||||
|
jwst-storage = { git = "https://github.com/toeverything/OctoBase", rev = "b701935", package = "jwst-storage", features = [ "sqlite"] }
|
||||||
|
cloud-database = { git = "https://github.com/toeverything/OctoBase", rev = "b701935", package = "cloud-database", features = [ "sqlite"] }
|
||||||
|
jwst-rpc = { git = "https://github.com/toeverything/OctoBase", rev = "b701935", package = "jwst-rpc" }
|
||||||
|
lib0 = "0.16.3"
|
||||||
|
tokio = "1.24.2"
|
||||||
|
yrs = "0.16.3"
|
||||||
|
bytes = "1.3.0"
|
||||||
|
futures = "^0.3.25"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
napi-build = "2.0.1"
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
rust-embed = { git = "https://github.com/pyrossh/rust-embed", rev = "7c0fc42" }
|
||||||
|
lib0 = { git = "https://github.com/toeverything/y-crdt", rev = "a3f7263" }
|
||||||
|
yrs = { git = "https://github.com/toeverything/y-crdt", rev = "a3f7263" }
|
5
packages/octobase-node/build.rs
Normal file
5
packages/octobase-node/build.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
extern crate napi_build;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
napi_build::setup();
|
||||||
|
}
|
20
packages/octobase-node/index.d.ts
vendored
Normal file
20
packages/octobase-node/index.d.ts
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
/* auto-generated by NAPI-RS */
|
||||||
|
|
||||||
|
export class Storage {
|
||||||
|
constructor(path: string);
|
||||||
|
error(): string | null;
|
||||||
|
getBlob(workspaceId: string | undefined | null, id: string): Promise<Buffer>;
|
||||||
|
connect(workspaceId: string, remote: string): Workspace | null;
|
||||||
|
sync(workspaceId: string, remote: string): Workspace;
|
||||||
|
}
|
||||||
|
export class Workspace {
|
||||||
|
constructor(id: string);
|
||||||
|
id(): string;
|
||||||
|
clientId(): number;
|
||||||
|
search(query: string): string;
|
||||||
|
getSearchIndex(): Array<string>;
|
||||||
|
setSearchIndex(fields: Array<string>): boolean;
|
||||||
|
}
|
269
packages/octobase-node/index.js
Normal file
269
packages/octobase-node/index.js
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
|
||||||
|
/* auto-generated by NAPI-RS */
|
||||||
|
|
||||||
|
const { existsSync, readFileSync } = require('fs')
|
||||||
|
const { join } = require('path');
|
||||||
|
|
||||||
|
const { platform, arch } = process;
|
||||||
|
|
||||||
|
let nativeBinding = null;
|
||||||
|
let localFileExisted = false;
|
||||||
|
let loadError = null;
|
||||||
|
|
||||||
|
function isMusl() {
|
||||||
|
// For Node 10
|
||||||
|
if (!process.report || typeof process.report.getReport !== 'function') {
|
||||||
|
try {
|
||||||
|
const lddPath = require('child_process')
|
||||||
|
.execSync('which ldd')
|
||||||
|
.toString()
|
||||||
|
.trim();
|
||||||
|
return readFileSync(lddPath, 'utf8').includes('musl');
|
||||||
|
} catch (e) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const { glibcVersionRuntime } = process.report.getReport().header;
|
||||||
|
return !glibcVersionRuntime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (platform) {
|
||||||
|
case 'android':
|
||||||
|
switch (arch) {
|
||||||
|
case 'arm64':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'octobase.android-arm64.node')
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./octobase.android-arm64.node');
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@affine/octobase-node-android-arm64');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'arm':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'octobase.android-arm-eabi.node')
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./octobase.android-arm-eabi.node');
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@affine/octobase-node-android-arm-eabi');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported architecture on Android ${arch}`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'win32':
|
||||||
|
switch (arch) {
|
||||||
|
case 'x64':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'octobase.win32-x64-msvc.node')
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./octobase.win32-x64-msvc.node');
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@affine/octobase-node-win32-x64-msvc');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'ia32':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'octobase.win32-ia32-msvc.node')
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./octobase.win32-ia32-msvc.node');
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@affine/octobase-node-win32-ia32-msvc');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'arm64':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'octobase.win32-arm64-msvc.node')
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./octobase.win32-arm64-msvc.node');
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@affine/octobase-node-win32-arm64-msvc');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported architecture on Windows: ${arch}`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'darwin':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'octobase.darwin-universal.node')
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./octobase.darwin-universal.node');
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@affine/octobase-node-darwin-universal');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} catch {}
|
||||||
|
switch (arch) {
|
||||||
|
case 'x64':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'octobase.darwin-x64.node')
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./octobase.darwin-x64.node');
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@affine/octobase-node-darwin-x64');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'arm64':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'octobase.darwin-arm64.node')
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./octobase.darwin-arm64.node');
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@affine/octobase-node-darwin-arm64');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported architecture on macOS: ${arch}`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'freebsd':
|
||||||
|
if (arch !== 'x64') {
|
||||||
|
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`);
|
||||||
|
}
|
||||||
|
localFileExisted = existsSync(join(__dirname, 'octobase.freebsd-x64.node'));
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./octobase.freebsd-x64.node');
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@affine/octobase-node-freebsd-x64');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'linux':
|
||||||
|
switch (arch) {
|
||||||
|
case 'x64':
|
||||||
|
if (isMusl()) {
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'octobase.linux-x64-musl.node')
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./octobase.linux-x64-musl.node');
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@affine/octobase-node-linux-x64-musl');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'octobase.linux-x64-gnu.node')
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./octobase.linux-x64-gnu.node');
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@affine/octobase-node-linux-x64-gnu');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'arm64':
|
||||||
|
if (isMusl()) {
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'octobase.linux-arm64-musl.node')
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./octobase.linux-arm64-musl.node');
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@affine/octobase-node-linux-arm64-musl');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'octobase.linux-arm64-gnu.node')
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./octobase.linux-arm64-gnu.node');
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@affine/octobase-node-linux-arm64-gnu');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'arm':
|
||||||
|
localFileExisted = existsSync(
|
||||||
|
join(__dirname, 'octobase.linux-arm-gnueabihf.node')
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
if (localFileExisted) {
|
||||||
|
nativeBinding = require('./octobase.linux-arm-gnueabihf.node');
|
||||||
|
} else {
|
||||||
|
nativeBinding = require('@affine/octobase-node-linux-arm-gnueabihf');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loadError = e;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported architecture on Linux: ${arch}`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nativeBinding) {
|
||||||
|
if (loadError) {
|
||||||
|
throw loadError;
|
||||||
|
}
|
||||||
|
throw new Error(`Failed to load native binding`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { Storage, Workspace } = nativeBinding;
|
||||||
|
|
||||||
|
module.exports.Storage = Storage;
|
||||||
|
module.exports.Workspace = Workspace;
|
29
packages/octobase-node/package.json
Normal file
29
packages/octobase-node/package.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "@affine/octobase-node",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"types": "index.d.ts",
|
||||||
|
"napi": {
|
||||||
|
"name": "octobase",
|
||||||
|
"triples": {
|
||||||
|
"additional": [
|
||||||
|
"aarch64-apple-darwin"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@napi-rs/cli": "^2.14.8",
|
||||||
|
"@types/node": "^18.15.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"artifacts": "napi artifacts",
|
||||||
|
"build": "napi build --platform --release",
|
||||||
|
"build:debug": "napi build --platform",
|
||||||
|
"universal": "napi universal",
|
||||||
|
"version": "napi version"
|
||||||
|
}
|
||||||
|
}
|
216
packages/octobase-node/src/block.rs
Normal file
216
packages/octobase-node/src/block.rs
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
use super::DynamicValue;
|
||||||
|
use jwst::{Block as JwstBlock, Workspace};
|
||||||
|
use lib0::any::Any;
|
||||||
|
|
||||||
|
#[napi()]
|
||||||
|
pub struct Block {
|
||||||
|
pub workspace: Workspace,
|
||||||
|
pub block: JwstBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi()]
|
||||||
|
impl Block {
|
||||||
|
#[napi(constructor)]
|
||||||
|
pub fn new(workspace: Workspace, block: JwstBlock) -> Self {
|
||||||
|
Self { workspace, block }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn get(&self, key: String) -> Option<DynamicValue> {
|
||||||
|
self.workspace
|
||||||
|
.with_trx(|trx| self.block.get(&trx.trx, &key).map(DynamicValue::new))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn children(&self) -> Vec<String> {
|
||||||
|
self.workspace.with_trx(|trx| self.block.children(&trx.trx))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn push_children(&self, block: &Block) {
|
||||||
|
self.workspace
|
||||||
|
.with_trx(|mut trx| self.block.push_children(&mut trx.trx, &block.block));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn insert_children_at(&self, block: &Block, pos: u32) {
|
||||||
|
self.workspace.with_trx(|mut trx| {
|
||||||
|
self.block
|
||||||
|
.insert_children_at(&mut trx.trx, &block.block, pos)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn insert_children_before(&self, block: &Block, reference: &str) {
|
||||||
|
self.workspace.with_trx(|mut trx| {
|
||||||
|
self.block
|
||||||
|
.insert_children_before(&mut trx.trx, &block.block, reference)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn insert_children_after(&self, block: &Block, reference: &str) {
|
||||||
|
self.workspace.with_trx(|mut trx| {
|
||||||
|
self.block
|
||||||
|
.insert_children_after(&mut trx.trx, &block.block, reference)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn remove_children(&self, block: &Block) {
|
||||||
|
self.workspace
|
||||||
|
.with_trx(|mut trx| self.block.remove_children(&mut trx.trx, &block.block));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn exists_children(&self, block_id: &str) -> i32 {
|
||||||
|
self.workspace
|
||||||
|
.with_trx(|trx| self.block.exists_children(&trx.trx, block_id))
|
||||||
|
.map(|i| i as i32)
|
||||||
|
.unwrap_or(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn parent(&self) -> String {
|
||||||
|
self.workspace
|
||||||
|
.with_trx(|trx| self.block.parent(&trx.trx).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn updated(&self) -> u64 {
|
||||||
|
self.workspace.with_trx(|trx| self.block.updated(&trx.trx))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn id(&self) -> String {
|
||||||
|
self.block.block_id()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn flavor(&self) -> String {
|
||||||
|
self.workspace.with_trx(|trx| self.block.flavor(&trx.trx))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn version(&self) -> String {
|
||||||
|
self.workspace.with_trx(|trx| {
|
||||||
|
let [major, minor] = self.block.version(&trx.trx);
|
||||||
|
format!("{major}.{minor}")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn created(&self) -> u64 {
|
||||||
|
self.workspace.with_trx(|trx| self.block.created(&trx.trx))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn set_bool(&self, key: String, value: bool) {
|
||||||
|
self.workspace
|
||||||
|
.with_trx(|mut trx| self.block.set(&mut trx.trx, &key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn set_string(&self, key: String, value: String) {
|
||||||
|
self.workspace
|
||||||
|
.with_trx(|mut trx| self.block.set(&mut trx.trx, &key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn set_float(&self, key: String, value: f64) {
|
||||||
|
self.workspace
|
||||||
|
.with_trx(|mut trx| self.block.set(&mut trx.trx, &key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn set_integer(&self, key: String, value: i64) {
|
||||||
|
self.workspace
|
||||||
|
.with_trx(|mut trx| self.block.set(&mut trx.trx, &key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn set_null(&self, key: String) {
|
||||||
|
self.workspace
|
||||||
|
.with_trx(|mut trx| self.block.set(&mut trx.trx, &key, Any::Null));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn is_bool(&self, key: String) -> bool {
|
||||||
|
self.workspace.with_trx(|trx| {
|
||||||
|
self.block
|
||||||
|
.get(&trx.trx, &key)
|
||||||
|
.map(|a| matches!(a, Any::Bool(_)))
|
||||||
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn is_string(&self, key: String) -> bool {
|
||||||
|
self.workspace.with_trx(|trx| {
|
||||||
|
self.block
|
||||||
|
.get(&trx.trx, &key)
|
||||||
|
.map(|a| matches!(a, Any::String(_)))
|
||||||
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn is_float(&self, key: String) -> bool {
|
||||||
|
self.workspace.with_trx(|trx| {
|
||||||
|
self.block
|
||||||
|
.get(&trx.trx, &key)
|
||||||
|
.map(|a| matches!(a, Any::Number(_)))
|
||||||
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn is_integer(&self, key: String) -> bool {
|
||||||
|
self.workspace.with_trx(|trx| {
|
||||||
|
self.block
|
||||||
|
.get(&trx.trx, &key)
|
||||||
|
.map(|a| matches!(a, Any::BigInt(_)))
|
||||||
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn get_bool(&self, key: String) -> Option<i64> {
|
||||||
|
self.workspace.with_trx(|trx| {
|
||||||
|
self.block.get(&trx.trx, &key).and_then(|a| match a {
|
||||||
|
Any::Bool(i) => Some(i.into()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn get_string(&self, key: String) -> Option<String> {
|
||||||
|
self.workspace.with_trx(|trx| {
|
||||||
|
self.block.get(&trx.trx, &key).and_then(|a| match a {
|
||||||
|
Any::String(i) => Some(i.into()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn get_float(&self, key: String) -> Option<f64> {
|
||||||
|
self.workspace.with_trx(|trx| {
|
||||||
|
self.block.get(&trx.trx, &key).and_then(|a| match a {
|
||||||
|
Any::Number(i) => Some(i),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn get_integer(&self, key: String) -> Option<i64> {
|
||||||
|
self.workspace.with_trx(|trx| {
|
||||||
|
self.block.get(&trx.trx, &key).and_then(|a| match a {
|
||||||
|
Any::BigInt(i) => Some(i),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
68
packages/octobase-node/src/dynamic_value.rs
Normal file
68
packages/octobase-node/src/dynamic_value.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
use lib0::any::Any;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub type DynamicValueMap = HashMap<String, DynamicValue>;
|
||||||
|
|
||||||
|
pub struct DynamicValue {
|
||||||
|
any: Any,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynamicValue {
|
||||||
|
pub fn new(any: Any) -> Self {
|
||||||
|
Self { any }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_bool(&self) -> Option<bool> {
|
||||||
|
match self.any {
|
||||||
|
Any::Bool(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_number(&self) -> Option<f64> {
|
||||||
|
match self.any {
|
||||||
|
Any::Number(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_int(&self) -> Option<i64> {
|
||||||
|
match self.any {
|
||||||
|
Any::BigInt(value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_string(&self) -> Option<String> {
|
||||||
|
match &self.any {
|
||||||
|
Any::String(value) => Some(value.to_string()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_buffer(&self) -> Option<Vec<u8>> {
|
||||||
|
match &self.any {
|
||||||
|
Any::Buffer(value) => Some(value.to_vec()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_array(&self) -> Option<Vec<DynamicValue>> {
|
||||||
|
match &self.any {
|
||||||
|
Any::Array(value) => Some(value.iter().map(|a| DynamicValue::new(a.clone())).collect()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_map(&self) -> Option<HashMap<String, DynamicValue>> {
|
||||||
|
match &self.any {
|
||||||
|
Any::Map(value) => Some(
|
||||||
|
value
|
||||||
|
.iter()
|
||||||
|
.map(|(key, value)| (key.clone(), DynamicValue::new(value.clone())))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
packages/octobase-node/src/lib.rs
Normal file
12
packages/octobase-node/src/lib.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// mod block;
|
||||||
|
mod dynamic_value;
|
||||||
|
mod storage;
|
||||||
|
mod workspace;
|
||||||
|
|
||||||
|
// pub use block::Block;
|
||||||
|
pub use dynamic_value::{DynamicValue, DynamicValueMap};
|
||||||
|
pub use storage::Storage;
|
||||||
|
pub use workspace::Workspace;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate napi_derive;
|
125
packages/octobase-node/src/storage.rs
Normal file
125
packages/octobase-node/src/storage.rs
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
use crate::Workspace;
|
||||||
|
use jwst::{error, info, BlobStorage, DocStorage};
|
||||||
|
use jwst_rpc::start_client;
|
||||||
|
use jwst_storage::JwstStorage as AutoStorage;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::{runtime::Runtime, sync::RwLock};
|
||||||
|
use napi::bindgen_prelude::*;
|
||||||
|
use napi::{Error, Result, Status};
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Storage {
|
||||||
|
pub(crate) storage: Option<Arc<RwLock<AutoStorage>>>,
|
||||||
|
pub(crate) error: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
impl Storage {
|
||||||
|
#[napi(constructor)]
|
||||||
|
pub fn new(path: String) -> Self {
|
||||||
|
let rt = Runtime::new().unwrap();
|
||||||
|
|
||||||
|
// FIXME: do not use block_on
|
||||||
|
match rt.block_on(AutoStorage::new(&format!("sqlite:{path}?mode=rwc"))) {
|
||||||
|
Ok(pool) => Self {
|
||||||
|
storage: Some(Arc::new(RwLock::new(pool))),
|
||||||
|
error: None,
|
||||||
|
},
|
||||||
|
Err(e) => Self {
|
||||||
|
storage: None,
|
||||||
|
error: Some(e.to_string()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn error(&self) -> Option<String> {
|
||||||
|
self.error.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub async fn get_blob(&self, workspace_id: Option<String>, id: String) -> Result<Buffer> {
|
||||||
|
if let Some(storage) = &self.storage {
|
||||||
|
let storage_handle = storage.read().await;
|
||||||
|
let blobs = storage_handle.blobs();
|
||||||
|
|
||||||
|
let blob = blobs.get_blob(workspace_id.clone(), id.clone(), None).await.map_err(|e| {
|
||||||
|
Error::new(
|
||||||
|
Status::GenericFailure,
|
||||||
|
format!(
|
||||||
|
"Failed to get blob file {}/{} from storage, error: {}",
|
||||||
|
workspace_id.clone().unwrap_or_default().to_string(),
|
||||||
|
id,
|
||||||
|
e
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(blob.into())
|
||||||
|
} else {
|
||||||
|
return Err(Error::new(
|
||||||
|
Status::GenericFailure,
|
||||||
|
"Storage is not connected",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn connect(&mut self, workspace_id: String, remote: String) -> Option<Workspace> {
|
||||||
|
match self.sync(workspace_id, remote) {
|
||||||
|
Ok(workspace) => Some(workspace),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to connect to workspace: {}", e);
|
||||||
|
self.error = Some(e.to_string());
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn sync(&self, workspace_id: String, remote: String) -> Result<Workspace> {
|
||||||
|
if let Some(storage) = &self.storage {
|
||||||
|
let rt = Runtime::new().unwrap();
|
||||||
|
|
||||||
|
// FIXME: do not use block_on
|
||||||
|
let mut workspace = rt
|
||||||
|
.block_on(async move {
|
||||||
|
let storage = storage.read().await;
|
||||||
|
|
||||||
|
start_client(&storage, workspace_id, remote).await
|
||||||
|
})
|
||||||
|
.map_err(|e| Error::new(Status::GenericFailure, e.to_string()))?;
|
||||||
|
|
||||||
|
let (sub, workspace) = {
|
||||||
|
let id = workspace.id();
|
||||||
|
let storage = self.storage.clone();
|
||||||
|
let sub = workspace.observe(move |_, e| {
|
||||||
|
let id = id.clone();
|
||||||
|
if let Some(storage) = storage.clone() {
|
||||||
|
let rt = Runtime::new().unwrap();
|
||||||
|
info!("update: {:?}", &e.update);
|
||||||
|
if let Err(e) = rt.block_on(async move {
|
||||||
|
let storage = storage.write().await;
|
||||||
|
storage.docs().write_update(id, &e.update).await
|
||||||
|
}) {
|
||||||
|
error!("Failed to write update to storage: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
(sub, workspace)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Workspace {
|
||||||
|
workspace,
|
||||||
|
_sub: sub,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(Error::new(
|
||||||
|
Status::GenericFailure,
|
||||||
|
"Storage is not connected",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
84
packages/octobase-node/src/workspace.rs
Normal file
84
packages/octobase-node/src/workspace.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// use super::Block;
|
||||||
|
use jwst::Workspace as JwstWorkspace;
|
||||||
|
use yrs::UpdateSubscription;
|
||||||
|
|
||||||
|
|
||||||
|
#[napi()]
|
||||||
|
pub struct Workspace {
|
||||||
|
pub(crate) workspace: JwstWorkspace,
|
||||||
|
pub(crate) _sub: Option<UpdateSubscription>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi()]
|
||||||
|
impl Workspace {
|
||||||
|
#[napi(constructor)]
|
||||||
|
pub fn new(id: String) -> Self {
|
||||||
|
Self {
|
||||||
|
workspace: JwstWorkspace::new(id),
|
||||||
|
_sub: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn id(&self) -> String {
|
||||||
|
self.workspace.id()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn client_id(&self) -> i64 {
|
||||||
|
self.workspace.client_id() as i64
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[napi]
|
||||||
|
// pub fn get(&self, block_id: String) -> Option<Block> {
|
||||||
|
// let workspace = self.workspace.clone();
|
||||||
|
// self.workspace.with_trx(|mut trx| {
|
||||||
|
// let block = trx
|
||||||
|
// .get_blocks()
|
||||||
|
// .get(&trx.trx, &block_id)
|
||||||
|
// .map(|b| Block::new(workspace, b));
|
||||||
|
// drop(trx);
|
||||||
|
// block
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[napi]
|
||||||
|
// pub fn create(&self, block_id: String, flavor: String) -> Block {
|
||||||
|
// let workspace = self.workspace.clone();
|
||||||
|
// self.workspace.with_trx(|mut trx| {
|
||||||
|
// let block = Block::new(
|
||||||
|
// workspace,
|
||||||
|
// trx.get_blocks().create(&mut trx.trx, block_id, flavor),
|
||||||
|
// );
|
||||||
|
// drop(trx);
|
||||||
|
// block
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn search(&self, query: String) -> String {
|
||||||
|
self.workspace.search_result(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[napi]
|
||||||
|
// pub fn get_blocks_by_flavour(&self, flavour: &str) -> Vec<Block> {
|
||||||
|
// self.workspace
|
||||||
|
// .with_trx(|mut trx| trx.get_blocks().get_blocks_by_flavour(&trx.trx, flavour))
|
||||||
|
// .iter()
|
||||||
|
// .map(|block| Block {
|
||||||
|
// workspace: self.workspace.clone(),
|
||||||
|
// block: block.clone(),
|
||||||
|
// })
|
||||||
|
// .collect()
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn get_search_index(&self) -> Vec<String> {
|
||||||
|
self.workspace.metadata().search_index
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn set_search_index(&self, fields: Vec<String>) -> bool {
|
||||||
|
self.workspace.set_search_index(fields)
|
||||||
|
}
|
||||||
|
}
|
22
yarn.lock
22
yarn.lock
@ -148,6 +148,7 @@ __metadata:
|
|||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@affine/electron@workspace:apps/electron"
|
resolution: "@affine/electron@workspace:apps/electron"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@affine/octobase-node": "workspace:*"
|
||||||
"@electron-forge/cli": ^6.0.5
|
"@electron-forge/cli": ^6.0.5
|
||||||
"@electron-forge/core": ^6.0.5
|
"@electron-forge/core": ^6.0.5
|
||||||
"@electron-forge/core-utils": ^6.0.5
|
"@electron-forge/core-utils": ^6.0.5
|
||||||
@ -162,6 +163,7 @@ __metadata:
|
|||||||
electron: 23.1.4
|
electron: 23.1.4
|
||||||
electron-window-state: ^5.0.3
|
electron-window-state: ^5.0.3
|
||||||
esbuild: ^0.17.12
|
esbuild: ^0.17.12
|
||||||
|
fs-extra: ^11.1.1
|
||||||
zx: ^7.2.1
|
zx: ^7.2.1
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
@ -197,6 +199,15 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
|
"@affine/octobase-node@workspace:*, @affine/octobase-node@workspace:packages/octobase-node":
|
||||||
|
version: 0.0.0-use.local
|
||||||
|
resolution: "@affine/octobase-node@workspace:packages/octobase-node"
|
||||||
|
dependencies:
|
||||||
|
"@napi-rs/cli": ^2.14.8
|
||||||
|
"@types/node": ^18.15.5
|
||||||
|
languageName: unknown
|
||||||
|
linkType: soft
|
||||||
|
|
||||||
"@affine/templates@workspace:*, @affine/templates@workspace:packages/templates":
|
"@affine/templates@workspace:*, @affine/templates@workspace:packages/templates":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@affine/templates@workspace:packages/templates"
|
resolution: "@affine/templates@workspace:packages/templates"
|
||||||
@ -4106,6 +4117,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@napi-rs/cli@npm:^2.14.8":
|
||||||
|
version: 2.15.2
|
||||||
|
resolution: "@napi-rs/cli@npm:2.15.2"
|
||||||
|
bin:
|
||||||
|
napi: scripts/index.js
|
||||||
|
checksum: dedcbd339f634e4a4c442febbfaee7c0fd907f3b8082c1f3ddba18b0e02ae2f0e4bff75688763669db5fb1a0f5ab26a7200cb6c01fe8825effd0a70a44bcbbc7
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@ndelangen/get-tarball@npm:^3.0.7":
|
"@ndelangen/get-tarball@npm:^3.0.7":
|
||||||
version: 3.0.7
|
version: 3.0.7
|
||||||
resolution: "@ndelangen/get-tarball@npm:3.0.7"
|
resolution: "@ndelangen/get-tarball@npm:3.0.7"
|
||||||
@ -12154,7 +12174,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"fs-extra@npm:^11.1.0":
|
"fs-extra@npm:^11.1.0, fs-extra@npm:^11.1.1":
|
||||||
version: 11.1.1
|
version: 11.1.1
|
||||||
resolution: "fs-extra@npm:11.1.1"
|
resolution: "fs-extra@npm:11.1.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
Loading…
Reference in New Issue
Block a user