From 44d19ad628e569c0f3a92a0555fbc1229a8818a2 Mon Sep 17 00:00:00 2001 From: 12inchpenguin Date: Mon, 7 Apr 2025 15:49:13 +0300 Subject: [PATCH] updates types for orbit and bug fixes, and sanitiza data on document prepare --- examples/integration-with-app.ts | 126 ------------------------------- orbitdb.d.ts | 2 +- package.json | 7 +- pnpm-lock.yaml | 37 +++++++-- src/db/stores/baseStore.ts | 106 ++++++++++++++------------ src/orbit/orbitDBService.ts | 2 +- 6 files changed, 93 insertions(+), 187 deletions(-) delete mode 100644 examples/integration-with-app.ts diff --git a/examples/integration-with-app.ts b/examples/integration-with-app.ts deleted file mode 100644 index e507efa..0000000 --- a/examples/integration-with-app.ts +++ /dev/null @@ -1,126 +0,0 @@ -// Example of how to integrate the new @debros/node-core package into an application - -import express from 'express'; -import { initIpfs, initOrbitDB, createServiceLogger, getConnectedPeers } from '@debros/node-core'; // This would be the import path when installed from npm - -// Create service-specific loggers -const apiLogger = createServiceLogger('API'); -const networkLogger = createServiceLogger('NETWORK'); - -// Initialize Express app -const app = express(); -app.use(express.json()); - -// Network state -let ipfsNode: any; -let orbitInstance: any; -let messageDB: any; - -// Initialize network components -async function initializeNetwork() { - try { - // Initialize IPFS - networkLogger.info('Initializing IPFS node...'); - ipfsNode = await initIpfs(); - - // Initialize OrbitDB - networkLogger.info('Initializing OrbitDB...'); - orbitInstance = await initOrbitDB({ - getHelia: () => ipfsNode, - }); - - // Open message database - messageDB = await orbitInstance.open('messages', { - type: 'feed', - }); - - networkLogger.info('Network components initialized successfully'); - - // Log connected peers every minute - setInterval(() => { - const peers = getConnectedPeers(); - networkLogger.info(`Connected to ${peers.size} peers`); - }, 60000); - - return true; - } catch (error) { - networkLogger.error('Failed to initialize network:', error); - return false; - } -} - -// API routes -app.get('/api/peers', (req, res) => { - const peers = getConnectedPeers(); - const peerList: any[] = []; - - peers.forEach((data, peerId) => { - peerList.push({ - id: peerId, - load: data.load, - address: data.publicAddress, - lastSeen: data.lastSeen, - }); - }); - - res.json({ peers: peerList }); -}); - -app.get('/api/messages', async (req, res) => { - try { - const messages = messageDB.iterator({ limit: 100 }).collect(); - res.json({ messages }); - } catch (error) { - apiLogger.error('Error fetching messages:', error); - res.status(500).json({ error: 'Failed to fetch messages' }); - } -}); - -app.post('/api/messages', async (req, res) => { - try { - const { content } = req.body; - if (!content) { - return res.status(400).json({ error: 'Content is required' }); - } - - const entry = await messageDB.add({ - content, - timestamp: Date.now(), - }); - - res.status(201).json({ id: entry }); - } catch (error) { - apiLogger.error('Error creating message:', error); - res.status(500).json({ error: 'Failed to create message' }); - } -}); - -// Start the application -async function startApp() { - const networkInitialized = await initializeNetwork(); - - if (networkInitialized) { - const port = config.env.port; - app.listen(port, () => { - apiLogger.info(`Server listening on port ${port}`); - }); - } else { - apiLogger.error('Cannot start application: Network initialization failed'); - process.exit(1); - } -} - -// Shutdown handler -process.on('SIGINT', async () => { - networkLogger.info('Application shutting down...'); - if (orbitInstance) { - await orbitInstance.stop(); - } - if (ipfsNode) { - await initIpfs.stop(); - } - process.exit(0); -}); - -// Start the application -startApp(); diff --git a/orbitdb.d.ts b/orbitdb.d.ts index 6fa4fe0..7d72996 100644 --- a/orbitdb.d.ts +++ b/orbitdb.d.ts @@ -1,7 +1,7 @@ // custom.d.ts declare module '@orbitdb/core' { // Import the types from @constl/orbit-db-types - import { OrbitDBTypes } from '@constl/orbit-db-types'; + import { OrbitDBTypes } from '@orbitdb/core-types'; // Assuming @orbitdb/core exports an interface or type you want to override // Replace 'OrbitDB' with the actual export name from @orbitdb/core you want to type diff --git a/package.json b/package.json index 90f83f3..0db68df 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@debros/network", - "version": "0.0.16-alpha", + "version": "0.0.17-alpha", "description": "Debros network core functionality for IPFS, libp2p and OrbitDB", "type": "module", "main": "dist/index.js", @@ -59,7 +59,6 @@ "typescript": ">=5.0.0" }, "devDependencies": { - "@constl/orbit-db-types": "^2.0.6", "@eslint/js": "^9.24.0", "@orbitdb/core-types": "^1.0.14", "@types/express": "^5.0.1", @@ -70,12 +69,14 @@ "eslint": "^9.24.0", "eslint-config-prettier": "^10.1.1", "eslint-plugin-prettier": "^5.2.6", + "globals": "^16.0.0", "husky": "^8.0.3", "lint-staged": "^15.5.0", "prettier": "^3.5.3", "rimraf": "^5.0.5", "tsc-esm-fix": "^3.1.2", - "typescript": "^5.8.2" + "typescript": "^5.8.2", + "typescript-eslint": "^8.29.0" }, "compilerOptions": { "typeRoots": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5d8bc62..5e2dddd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -78,9 +78,6 @@ importers: specifier: ^3.17.0 version: 3.17.0 devDependencies: - '@constl/orbit-db-types': - specifier: ^2.0.6 - version: 2.0.6 '@eslint/js': specifier: ^9.24.0 version: 9.24.0 @@ -111,6 +108,9 @@ importers: eslint-plugin-prettier: specifier: ^5.2.6 version: 5.2.6(eslint-config-prettier@10.1.1(eslint@9.24.0))(eslint@9.24.0)(prettier@3.5.3) + globals: + specifier: ^16.0.0 + version: 16.0.0 husky: specifier: ^8.0.3 version: 8.0.3 @@ -129,6 +129,9 @@ importers: typescript: specifier: ^5.8.2 version: 5.8.2 + typescript-eslint: + specifier: ^8.29.0 + version: 8.29.0(eslint@9.24.0)(typescript@5.8.2) packages: @@ -827,9 +830,6 @@ packages: resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} - '@constl/orbit-db-types@2.0.6': - resolution: {integrity: sha512-p23e3tEr9izE9zskwqdN66VhFWYrbhxTvJNalhWqKkXnSV/58gbtc+yFobLPClKEDNKtYAhYhO6spLiI+VpZEg==} - '@dabh/diagnostics@2.0.3': resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} @@ -2251,6 +2251,10 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} + globals@16.0.0: + resolution: {integrity: sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==} + engines: {node: '>=18'} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -3750,6 +3754,13 @@ packages: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} + typescript-eslint@8.29.0: + resolution: {integrity: sha512-ep9rVd9B4kQsZ7ZnWCVxUE/xDLUUUsRzE0poAeNu+4CkFErLfuvPt/qtm2EpnSyfvsR0S6QzDFSrPCFBwf64fg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + typescript@5.8.2: resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} engines: {node: '>=14.17'} @@ -4862,8 +4873,6 @@ snapshots: '@colors/colors@1.6.0': {} - '@constl/orbit-db-types@2.0.6': {} - '@dabh/diagnostics@2.0.3': dependencies: colorspace: 1.1.4 @@ -7077,6 +7086,8 @@ snapshots: globals@14.0.0: {} + globals@16.0.0: {} + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -8846,6 +8857,16 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.1 + typescript-eslint@8.29.0(eslint@9.24.0)(typescript@5.8.2): + dependencies: + '@typescript-eslint/eslint-plugin': 8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.24.0)(typescript@5.8.2))(eslint@9.24.0)(typescript@5.8.2) + '@typescript-eslint/parser': 8.29.0(eslint@9.24.0)(typescript@5.8.2) + '@typescript-eslint/utils': 8.29.0(eslint@9.24.0)(typescript@5.8.2) + eslint: 9.24.0 + typescript: 5.8.2 + transitivePeerDependencies: + - supports-color + typescript@5.8.2: {} uint8-varint@2.0.4: diff --git a/src/db/stores/baseStore.ts b/src/db/stores/baseStore.ts index 52648a4..131c4c6 100644 --- a/src/db/stores/baseStore.ts +++ b/src/db/stores/baseStore.ts @@ -1,11 +1,17 @@ import { createServiceLogger } from '../../utils/logger'; import { openDB } from '../../orbit/orbitDBService'; import { getConnection } from '../core/connection'; -import * as cache from '../cache/cacheService'; -import * as events from '../events/eventService'; import { validateDocument } from '../schema/validator'; -import { measurePerformance } from '../metrics/metricsService'; -import { ErrorCode, StoreType, StoreOptions, CreateResult, UpdateResult, PaginatedResult, QueryOptions, ListOptions } from '../types'; +import { + ErrorCode, + StoreType, + StoreOptions, + CreateResult, + UpdateResult, + PaginatedResult, + QueryOptions, + ListOptions, +} from '../types'; import { DBError } from '../core/error'; const logger = createServiceLogger('DB_STORE'); @@ -18,65 +24,57 @@ export interface BaseStore { * Create a new document */ create>( - collection: string, - id: string, - data: Omit, - options?: StoreOptions + collection: string, + id: string, + data: Omit, + options?: StoreOptions, ): Promise; - + /** * Get a document by ID */ get>( - collection: string, - id: string, - options?: StoreOptions & { skipCache?: boolean } + collection: string, + id: string, + options?: StoreOptions & { skipCache?: boolean }, ): Promise; - + /** * Update a document */ update>( - collection: string, - id: string, - data: Partial>, - options?: StoreOptions & { upsert?: boolean } + collection: string, + id: string, + data: Partial>, + options?: StoreOptions & { upsert?: boolean }, ): Promise; - + /** * Delete a document */ - remove( - collection: string, - id: string, - options?: StoreOptions - ): Promise; - + remove(collection: string, id: string, options?: StoreOptions): Promise; + /** * List all documents in a collection with pagination */ list>( - collection: string, - options?: ListOptions + collection: string, + options?: ListOptions, ): Promise>; - + /** * Query documents in a collection with filtering and pagination */ query>( - collection: string, + collection: string, filter: (doc: T) => boolean, - options?: QueryOptions + options?: QueryOptions, ): Promise>; - + /** * Create an index for a collection to speed up queries */ - createIndex( - collection: string, - field: string, - options?: StoreOptions - ): Promise; + createIndex(collection: string, field: string, options?: StoreOptions): Promise; } /** @@ -85,14 +83,21 @@ export interface BaseStore { export async function openStore( collection: string, storeType: StoreType, - options?: StoreOptions + options?: StoreOptions, ): Promise { try { const connection = getConnection(options?.connectionId); - return await openDB(collection, storeType); + logger.info(`Connection for ${collection}:`, connection); + return await openDB(collection, storeType).catch((err) => { + throw new Error(`OrbitDB openDB failed: ${err.message}`); + }); } catch (error) { logger.error(`Error opening ${storeType} store for collection ${collection}:`, error); - throw new DBError(ErrorCode.OPERATION_FAILED, `Failed to open ${storeType} store for collection ${collection}`, error); + throw new DBError( + ErrorCode.OPERATION_FAILED, + `Failed to open ${storeType} store for collection ${collection}`, + error, + ); } } @@ -102,31 +107,36 @@ export async function openStore( export function prepareDocument>( collection: string, data: Omit, - existingDoc?: T | null + existingDoc?: T | null, ): T { const timestamp = Date.now(); - + + // Sanitize the input data by replacing undefined with null + const sanitizedData = Object.fromEntries( + Object.entries(data).map(([key, value]) => [key, value === undefined ? null : value]), + ) as Omit; + // If it's an update to an existing document if (existingDoc) { const doc = { ...existingDoc, - ...data, - updatedAt: timestamp + ...sanitizedData, + updatedAt: timestamp, } as T; - + // Validate the document against its schema validateDocument(collection, doc); return doc; } - + // Otherwise it's a new document const doc = { - ...data, + ...sanitizedData, createdAt: timestamp, - updatedAt: timestamp + updatedAt: timestamp, } as unknown as T; - + // Validate the document against its schema validateDocument(collection, doc); return doc; -} \ No newline at end of file +} diff --git a/src/orbit/orbitDBService.ts b/src/orbit/orbitDBService.ts index eff514d..8b0b647 100644 --- a/src/orbit/orbitDBService.ts +++ b/src/orbit/orbitDBService.ts @@ -74,7 +74,7 @@ export const openDB = async (name: string, type: string) => { const dbOptions = { type, overwrite: false, - AccessController: IPFSAccessController({ write: ['*'], storage: getHelia() }), + AccessController: IPFSAccessController({ write: ['*'] }), }; if (existingAddress) {