feat: Add debug script for field defaults and enhance Jest configuration for ES modules

This commit is contained in:
anonpenguin 2025-06-19 21:47:35 +03:00
parent 1e3c5d46be
commit 831c977eda
9 changed files with 202 additions and 59 deletions

44
debug_fields.js Normal file
View File

@ -0,0 +1,44 @@
// Simple debug script to test field defaults
const { execSync } = require('child_process');
// Run a small test using jest directly
const testCode = `
import { BaseModel } from './src/framework/models/BaseModel';
import { Model, Field } from './src/framework/models/decorators';
@Model({
scope: 'global',
type: 'docstore'
})
class TestUser extends BaseModel {
@Field({ type: 'string', required: true })
username: string;
@Field({ type: 'number', required: false, default: 0 })
score: number;
@Field({ type: 'boolean', required: false, default: true })
isActive: boolean;
}
// Debug the fields
console.log('TestUser.fields:', TestUser.fields);
console.log('TestUser.fields size:', TestUser.fields?.size);
if (TestUser.fields) {
for (const [fieldName, fieldConfig] of TestUser.fields) {
console.log(\`Field: \${fieldName}, Config:\`, fieldConfig);
}
}
// Test instance creation
const user = new TestUser();
console.log('User instance score:', user.score);
console.log('User instance isActive:', user.isActive);
// Check private fields
console.log('User _score:', (user as any)._score);
console.log('User _isActive:', (user as any)._isActive);
`;
console.log('Test code created for debugging...');

View File

@ -1,13 +1,17 @@
module.exports = {
preset: 'ts-jest',
preset: 'ts-jest/presets/default-esm',
testEnvironment: 'node',
roots: ['<rootDir>/tests/real'],
testMatch: ['**/real/**/*.test.ts'],
// ES Module configuration
extensionsToTreatAsEsm: ['.ts'],
transform: {
'^.+\\.ts$': [
'ts-jest',
{
isolatedModules: true,
useESM: true,
},
],
},
@ -28,9 +32,6 @@ module.exports = {
// Environment variables for real tests
setupFilesAfterEnv: ['<rootDir>/tests/real/jest.setup.ts'],
// Longer timeout for setup/teardown
setupFilesTimeout: 120000,
// Disable watch mode (real tests are too slow)
watchman: false,
@ -44,9 +45,18 @@ module.exports = {
// Fail fast on first error (saves time with slow tests)
bail: 1,
// Module path mapping
moduleNameMapping: {
'^@/(.*)$': '<rootDir>/src/$1',
'^@tests/(.*)$': '<rootDir>/tests/$1'
}
// ES Module support
extensionsToTreatAsEsm: ['.ts'],
// Transform ES modules - more comprehensive pattern
transformIgnorePatterns: [
'node_modules/(?!(helia|@helia|@orbitdb|@libp2p|@chainsafe|@multiformats|multiformats|datastore-fs|blockstore-fs|libp2p)/)',
],
// Module resolution for ES modules
resolver: undefined,
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], // Module name mapping to handle ES modules
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
};

View File

@ -27,7 +27,7 @@
"test:e2e": "jest tests/e2e",
"test:real": "jest --config jest.real.config.cjs",
"test:real:debug": "REAL_TEST_DEBUG=true jest --config jest.real.config.cjs",
"test:real:basic": "jest --config jest.real.config.cjs tests/real/basic-integration.test.ts",
"test:real:basic": "jest --config jest.real.config.cjs tests/real/real-integration.test.ts",
"test:real:p2p": "jest --config jest.real.config.cjs tests/real/peer-discovery.test.ts"
},
"keywords": [

View File

@ -6,11 +6,20 @@ module.exports = async () => {
process.env.NODE_ENV = 'test';
process.env.DEBROS_TEST_MODE = 'real';
// Check for required dependencies
// Check for required dependencies - skip for ES module packages
try {
require('helia');
require('@orbitdb/core');
console.log('✅ Required dependencies available');
// Just check if the packages exist without importing them
const fs = require('fs');
const path = require('path');
const heliaPath = path.join(__dirname, '../../node_modules/helia');
const orbitdbPath = path.join(__dirname, '../../node_modules/@orbitdb/core');
if (fs.existsSync(heliaPath) && fs.existsSync(orbitdbPath)) {
console.log('✅ Required dependencies available');
} else {
throw new Error('Required packages not found');
}
} catch (error) {
console.error('❌ Missing required dependencies for real tests:', error.message);
process.exit(1);

View File

@ -11,8 +11,18 @@ const debugMode = process.env.REAL_TEST_DEBUG === 'true';
if (!debugMode) {
// Silence routine logs but keep errors and important messages
console.log = (...args: any[]) => {
const message = args.join(' ');
if (message.includes('❌') || message.includes('✅') || message.includes('🚀') || message.includes('🧹')) {
try {
const message = args.map(arg => {
if (typeof arg === 'string') return arg;
if (typeof arg === 'object' && arg !== null) return JSON.stringify(arg);
return String(arg);
}).join(' ');
if (message.includes('❌') || message.includes('✅') || message.includes('🚀') || message.includes('🧹')) {
originalConsole.log(...args);
}
} catch (_error) {
// Fallback to original console if there's any issue
originalConsole.log(...args);
}
};

View File

@ -0,0 +1,66 @@
// Manual wrapper for ES modules to work with Jest
// This file provides CommonJS-compatible interfaces for pure ES modules
// Synchronous wrappers that use dynamic imports with await
export async function loadModules() {
const [
heliaModule,
libp2pModule,
tcpModule,
noiseModule,
yamuxModule,
gossipsubModule,
identifyModule,
] = await Promise.all([
import('helia'),
import('libp2p'),
import('@libp2p/tcp'),
import('@chainsafe/libp2p-noise'),
import('@chainsafe/libp2p-yamux'),
import('@chainsafe/libp2p-gossipsub'),
import('@libp2p/identify'),
]);
return {
createHelia: heliaModule.createHelia,
createLibp2p: libp2pModule.createLibp2p,
tcp: tcpModule.tcp,
noise: noiseModule.noise,
yamux: yamuxModule.yamux,
gossipsub: gossipsubModule.gossipsub,
identify: identifyModule.identify,
};
}
// Separate async loader for OrbitDB
export async function loadOrbitDBModules() {
const orbitdbModule = await import('@orbitdb/core');
return {
createOrbitDB: orbitdbModule.createOrbitDB,
};
}
// Separate async loaders for datastore modules that might have different import patterns
export async function loadDatastoreModules() {
try {
const [blockstoreModule, datastoreModule] = await Promise.all([
import('blockstore-fs'),
import('datastore-fs'),
]);
return {
FsBlockstore: blockstoreModule.FsBlockstore,
FsDatastore: datastoreModule.FsDatastore,
};
} catch (_error) {
// Fallback to require() for modules that might not be pure ES modules
const FsBlockstore = require('blockstore-fs').FsBlockstore;
const FsDatastore = require('datastore-fs').FsDatastore;
return {
FsBlockstore,
FsDatastore,
};
}
}

View File

@ -1,12 +1,4 @@
import { createHelia } from 'helia';
import { createLibp2p } from 'libp2p';
import { tcp } from '@libp2p/tcp';
import { noise } from '@chainsafe/libp2p-noise';
import { yamux } from '@chainsafe/libp2p-yamux';
import { gossipsub } from '@chainsafe/libp2p-gossipsub';
import { identify } from '@libp2p/identify';
import { FsBlockstore } from 'blockstore-fs';
import { FsDatastore } from 'datastore-fs';
import { loadModules, loadDatastoreModules } from './helia-wrapper';
import { join } from 'path';
import { PrivateSwarmSetup } from './swarm-setup';
import { IPFSInstance } from '../../../src/framework/services/OrbitDBService';
@ -28,6 +20,11 @@ export class RealIPFSService implements IPFSInstance {
console.log(`🚀 Initializing IPFS node ${this.nodeIndex}...`);
try {
// Load ES modules dynamically
const { createHelia, createLibp2p, tcp, noise, yamux, gossipsub, identify } =
await loadModules();
const { FsBlockstore, FsDatastore } = await loadDatastoreModules();
// Create libp2p instance with private swarm configuration
this.libp2p = await createLibp2p({
addresses: {

View File

@ -1,4 +1,4 @@
import { createOrbitDB } from '@orbitdb/core';
import { loadOrbitDBModules } from './helia-wrapper';
import { RealIPFSService } from './ipfs-setup';
import { OrbitDBInstance } from '../../../src/framework/services/OrbitDBService';
@ -17,6 +17,9 @@ export class RealOrbitDBService implements OrbitDBInstance {
console.log(`🌀 Initializing OrbitDB for node ${this.nodeIndex}...`);
try {
// Load OrbitDB ES modules dynamically
const { createOrbitDB } = await loadOrbitDBModules();
const ipfs = this.ipfsService.getHelia();
if (!ipfs) {
throw new Error('IPFS node must be initialized before OrbitDB');
@ -26,7 +29,7 @@ export class RealOrbitDBService implements OrbitDBInstance {
this.orbitdb = await createOrbitDB({
ipfs,
id: `orbitdb-node-${this.nodeIndex}`,
directory: `./orbitdb-${this.nodeIndex}` // Local directory for this node
directory: `./orbitdb-${this.nodeIndex}`, // Local directory for this node
});
console.log(`✅ OrbitDB initialized for node ${this.nodeIndex}`);
@ -61,7 +64,7 @@ export class RealOrbitDBService implements OrbitDBInstance {
case 'docstore':
database = await this.orbitdb.open(name, {
type: 'documents',
AccessController: 'orbitdb'
AccessController: 'orbitdb',
});
break;
@ -69,7 +72,7 @@ export class RealOrbitDBService implements OrbitDBInstance {
case 'eventlog':
database = await this.orbitdb.open(name, {
type: 'events',
AccessController: 'orbitdb'
AccessController: 'orbitdb',
});
break;
@ -77,7 +80,7 @@ export class RealOrbitDBService implements OrbitDBInstance {
case 'kvstore':
database = await this.orbitdb.open(name, {
type: 'keyvalue',
AccessController: 'orbitdb'
AccessController: 'orbitdb',
});
break;
@ -85,7 +88,7 @@ export class RealOrbitDBService implements OrbitDBInstance {
// Default to documents store
database = await this.orbitdb.open(name, {
type: 'documents',
AccessController: 'orbitdb'
AccessController: 'orbitdb',
});
}
@ -169,13 +172,15 @@ export class RealOrbitDBService implements OrbitDBInstance {
type: database.type,
peers: database.peers || [],
all: await database.all(),
meta: database.meta || {}
meta: database.meta || {},
};
}
}
// Utility function to create OrbitDB network from IPFS network
export async function createOrbitDBNetwork(ipfsNodes: RealIPFSService[]): Promise<RealOrbitDBService[]> {
export async function createOrbitDBNetwork(
ipfsNodes: RealIPFSService[],
): Promise<RealOrbitDBService[]> {
console.log(`🌀 Creating OrbitDB network with ${ipfsNodes.length} nodes...`);
const orbitdbNodes: RealOrbitDBService[] = [];
@ -195,7 +200,7 @@ export async function shutdownOrbitDBNetwork(orbitdbNodes: RealOrbitDBService[])
console.log(`🛑 Shutting down OrbitDB network...`);
// Stop all OrbitDB nodes
await Promise.all(orbitdbNodes.map(node => node.stop()));
await Promise.all(orbitdbNodes.map((node) => node.stop()));
console.log(`✅ OrbitDB network shutdown complete`);
}
@ -204,7 +209,7 @@ export async function shutdownOrbitDBNetwork(orbitdbNodes: RealOrbitDBService[])
export async function testDatabaseReplication(
orbitdbNodes: RealOrbitDBService[],
dbName: string,
dbType: string = 'documents'
dbType: string = 'documents',
): Promise<boolean> {
console.log(`🔄 Testing database replication for '${dbName}'...`);
@ -222,7 +227,7 @@ export async function testDatabaseReplication(
const db2 = await orbitdbNodes[1].openDB(dbName, dbType);
// Wait for replication
await new Promise(resolve => setTimeout(resolve, 2000));
await new Promise((resolve) => setTimeout(resolve, 2000));
// Check if data replicated
const db2Data = await db2.all();

View File

@ -13,6 +13,8 @@
"moduleResolution": "bundler",
/* Skip type checking of declaration files. */
"skipLibCheck": true,
/* Perform compilation without referencing other files. */
"isolatedModules": true,
/* Removes comments from the project's output JavaScript code. */
"removeComments": true,
/* Enables experimental support for emitting type metadata for decorators which works with the module reflect-metadata. */