Set up monorepo workspace

This commit is contained in:
Andras Schmelczer 2025-02-18 22:36:53 +00:00
parent ae3acb9e1e
commit eb88d35c2e
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
10 changed files with 207 additions and 26 deletions

4
.gitignore vendored
View file

@ -7,8 +7,8 @@ node_modules
# Rust build folder
backend/target
# Obsidian plugin build folder
plugin/dist
obsidian-plugin/dist
sync-client/dist
backend/db.sqlite3*
backend/config.yml

View file

@ -4,10 +4,16 @@ import unusedImports from "eslint-plugin-unused-imports";
export default tseslint.config({
plugins: {
"unused-imports": unusedImports,
"unused-imports": unusedImports
},
extends: [eslint.configs.recommended, tseslint.configs.all],
ignores: ["**/types.ts", "**/*.test.ts"],
ignores: [
"**/types.ts",
"**/*.test.ts",
"**/dist/**/*",
"**/*.mjs",
"**/*.js"
],
rules: {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "off",
@ -20,8 +26,8 @@ export default tseslint.config({
"@typescript-eslint/max-params": [
"error",
{
max: 5,
},
max: 5
}
],
"unused-imports/no-unused-imports": "error",
"@typescript-eslint/no-magic-numbers": "off",
@ -33,14 +39,14 @@ export default tseslint.config({
vars: "all",
varsIgnorePattern: "^_",
args: "after-used",
argsIgnorePattern: "^_",
},
],
argsIgnorePattern: "^_"
}
]
},
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
tsconfigRootDir: import.meta.dirname
}
}
});

View file

@ -0,0 +1,10 @@
{
"id": "vault-link",
"name": "VaultLink",
"version": "0.0.30",
"minAppVersion": "0.0.0",
"description": "Self-hosted synchronization and collaboration for your Vault.",
"author": "Andras Schmelczer",
"authorUrl": "https://schmelczer.dev",
"isDesktopOnly": false
}

26
package.json Normal file
View file

@ -0,0 +1,26 @@
{
"name": "my-workspace",
"private": true,
"workspaces": [
"sync-client",
"obsidian-plugin"
],
"prettier": {
"trailingComma": "none",
"tabWidth": 4,
"useTabs": true,
"endOfLine": "lf"
},
"scripts": {
"build": "npm run build --workspaces",
"dev": "npm run dev --workspaces",
"test": "npm run test --workspaces",
"lint": "eslint --fix sync-client obsidian-plugin; prettier --write \"sync-client/**/*.(ts|scss|json|html)\" \"obsidian-plugin/**/*.(ts|scss|json|html)\""
},
"devDependencies": {
"prettier": "^3.4.2",
"eslint": "9.17.0",
"typescript-eslint": "8.18.0",
"eslint-plugin-unused-imports": "^4.1.4"
}
}

View file

@ -1,8 +0,0 @@
import type { TAbstractFile } from "obsidian";
export interface FileEventHandler {
onCreate: (path: TAbstractFile) => Promise<void>;
onDelete: (path: TAbstractFile) => Promise<void>;
onRename: (path: TAbstractFile, oldPath: string) => Promise<void>;
onModify: (path: TAbstractFile) => Promise<void>;
}

28
sync-client/package.json Normal file
View file

@ -0,0 +1,28 @@
{
"name": "sync-client",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/types/src/index.d.ts",
"scripts": {
"dev": "webpack watch --mode development",
"build": "webpack --mode production",
"test": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest"
},
"devDependencies": {
"tslib": "2.4.0",
"typescript": "5.7.2",
"sync_lib": "file:../backend/sync_lib/pkg",
"@types/jest": "^29.5.14",
"@types/node": "^16.11.6",
"jest": "^29.7.0",
"ts-jest": "^29.2.5",
"p-queue": "^8.0.1",
"fetch-retry": "^6.0.0",
"byte-base64": "^1.1.0",
"openapi-fetch": "0.13.3",
"openapi-typescript": "7.4.4",
"ts-loader": "^9.5.1",
"webpack": "^5.97.1",
"webpack-cli": "^6.0.1"
}
}

48
sync-client/src/index.ts Normal file
View file

@ -0,0 +1,48 @@
export { applyRemoteChangesLocally } from "./sync-operations/apply-remote-changes-locally";
export {
type RelativePath,
type DocumentId,
type VaultUpdateId,
type DocumentMetadata
} from "./database/document-metadata";
export { Database } from "./database/database";
export {
SyncService,
type CheckConnectionResult
} from "./services/sync-service";
export { Syncer } from "./sync-operations/syncer";
export {
SyncHistory,
SyncType,
SyncSource,
SyncStatus,
type HistoryStats,
type HistoryEntry
} from "./tracing/sync-history";
export { Logger, LogLevel } from "./tracing/logger";
export { type FileOperations } from "./file-operations";
import init from "sync_lib";
import wasmBin from "sync_lib/sync_lib_bg.wasm";
export const initialize = async (): Promise<void> => {
await init(
// eslint-disable-next-line
(wasmBin as any).default // it is loaded as a base64 string by webpack
);
};
export {
isFileTypeMergable,
mergeText,
bytesToBase64,
base64ToBytes,
merge,
isBinary
} from "sync_lib";

View file

@ -1,9 +1,9 @@
import type { Database } from "src/database/database";
import type { Database } from "../database/database";
import type {
DocumentMetadata,
RelativePath
} from "src/database/document-metadata";
import type { FileOperations } from "src/file-operations/file-operations";
import type { FileOperations } from "src/file-operations";
import type { SyncService } from "src/services/sync-service";
import { Logger } from "src/tracing/logger";
import type { SyncHistory } from "src/tracing/sync-history";
@ -102,7 +102,7 @@ export class Syncer {
try {
const allLocalFiles = await this.operations.listAllFiles();
const locallyDeletedFiles = [
let locallyDeletedFiles = [
...this.database.getDocuments().entries()
].filter(([path, _]) => !allLocalFiles.includes(path));
@ -126,7 +126,10 @@ export class Syncer {
);
if (originalFile !== undefined) {
// `originalFile` hasn't been deleted but it got moved instead
locallyDeletedFiles.remove(originalFile);
locallyDeletedFiles =
locallyDeletedFiles.filter(
(item) => item != originalFile
);
Logger.getInstance().debug(
`Document ${relativePath} was not found under its current path in the database but was found under a different path ${originalFile[0]}, scheduling sync to move it`
@ -231,7 +234,9 @@ export class Syncer {
this.history.addHistoryEntry({
status: SyncStatus.ERROR,
relativePath,
message: `File size exceeds the maximum file size limit of ${this.database.getSettings().maxFileSizeMB}MB`,
message: `File size exceeds the maximum file size limit of ${
this.database.getSettings().maxFileSizeMB
}MB`,
type: SyncType.CREATE
});
return;
@ -332,7 +337,9 @@ export class Syncer {
this.history.addHistoryEntry({
status: SyncStatus.ERROR,
relativePath,
message: `File size exceeds the maximum file size limit of ${this.database.getSettings().maxFileSizeMB}MB`,
message: `File size exceeds the maximum file size limit of ${
this.database.getSettings().maxFileSizeMB
}MB`,
type: SyncType.CREATE
});
return;

15
sync-client/tsconfig.json Normal file
View file

@ -0,0 +1,15 @@
{
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"target": "ES2022",
"noImplicitAny": true,
"moduleResolution": "node",
"strictNullChecks": true,
"esModuleInterop": true,
"lib": [
"DOM",
"ESNext"
]
}
}

View file

@ -0,0 +1,49 @@
const path = require("path");
module.exports = (_env, _argv) => ({
entry: "./src/index.ts",
devtool: "source-map",
module: {
rules: [
{
test: /\.ts$/,
use: [
{
loader: "ts-loader",
options: {
compilerOptions: {
declaration: true,
declarationDir: "./dist/types"
},
transpileOnly: false
}
}
]
},
{
test: /\.wasm$/,
type: "asset/inline"
}
]
},
optimization: {
minimize: false
},
resolve: {
extensions: [".ts", ".js"],
alias: {
root: __dirname,
src: path.resolve(__dirname, "src")
}
},
output: {
clean: true,
filename: "index.js",
library: {
name: "SyncClient",
type: "umd"
},
globalObject: "this",
path: path.resolve(__dirname, "dist")
}
});