@soulcraft/brainy 3.0.1 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -4
- package/dist/brainy.d.ts +5 -1
- package/dist/brainy.js +74 -2
- package/dist/neural/embeddedPatterns.js +3 -1
- package/dist/storage/adapters/fileSystemStorage.js +21 -3
- package/dist/storage/baseStorage.js +10 -2
- package/dist/types/brainy.types.d.ts +2 -0
- package/dist/universal/crypto.d.ts +11 -1
- package/dist/universal/crypto.js +24 -93
- package/dist/universal/events.d.ts +3 -2
- package/dist/universal/events.js +6 -75
- package/dist/universal/fs.d.ts +2 -3
- package/dist/universal/fs.js +5 -211
- package/dist/universal/path.d.ts +3 -2
- package/dist/universal/path.js +22 -78
- package/dist/universal/uuid.d.ts +1 -1
- package/dist/universal/uuid.js +1 -1
- package/dist/utils/metadataIndex.js +50 -2
- package/package.json +1 -14
- package/dist/browserFramework.d.ts +0 -15
- package/dist/browserFramework.js +0 -34
- package/dist/browserFramework.minimal.d.ts +0 -14
- package/dist/browserFramework.minimal.js +0 -34
package/README.md
CHANGED
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
|
|
12
12
|
**🧠 Brainy 3.0 - Universal Knowledge Protocol™**
|
|
13
13
|
|
|
14
|
-
**World's first Triple Intelligence™ database** unifying vector similarity, graph relationships, and document filtering in one magical API.
|
|
14
|
+
**World's first Triple Intelligence™ database** unifying vector similarity, graph relationships, and document filtering in one magical API. **Framework-friendly design** works seamlessly with Next.js, React, Vue, Angular, and any modern JavaScript framework.
|
|
15
15
|
|
|
16
16
|
**Why Brainy Leads**: We're the first to solve the impossible—combining three different database paradigms (vector, graph, document) into one unified query interface. This breakthrough enables us to be the Universal Knowledge Protocol where all tools, augmentations, and AI models speak the same language.
|
|
17
17
|
|
|
18
|
-
**
|
|
18
|
+
**Framework-first design.** Built for modern web development with zero configuration and automatic framework compatibility. O(log n) performance, <10ms search latency, production-ready.
|
|
19
19
|
|
|
20
20
|
## 🎉 What's New in 3.0
|
|
21
21
|
|
|
@@ -98,6 +98,73 @@ const filtered = await brain.find({
|
|
|
98
98
|
})
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
+
## 🌐 Framework Integration
|
|
102
|
+
|
|
103
|
+
**Brainy 3.0 is framework-first!** Works seamlessly with any modern JavaScript framework:
|
|
104
|
+
|
|
105
|
+
### ⚛️ **React & Next.js**
|
|
106
|
+
```javascript
|
|
107
|
+
import { Brainy } from '@soulcraft/brainy'
|
|
108
|
+
|
|
109
|
+
function SearchComponent() {
|
|
110
|
+
const [brain] = useState(() => new Brainy())
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
brain.init()
|
|
114
|
+
}, [])
|
|
115
|
+
|
|
116
|
+
const handleSearch = async (query) => {
|
|
117
|
+
const results = await brain.find(query)
|
|
118
|
+
setResults(results)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 🟢 **Vue.js & Nuxt.js**
|
|
124
|
+
```javascript
|
|
125
|
+
import { Brainy } from '@soulcraft/brainy'
|
|
126
|
+
|
|
127
|
+
export default {
|
|
128
|
+
async mounted() {
|
|
129
|
+
this.brain = new Brainy()
|
|
130
|
+
await this.brain.init()
|
|
131
|
+
},
|
|
132
|
+
methods: {
|
|
133
|
+
async search(query) {
|
|
134
|
+
return await this.brain.find(query)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 🅰️ **Angular**
|
|
141
|
+
```typescript
|
|
142
|
+
import { Injectable } from '@angular/core'
|
|
143
|
+
import { Brainy } from '@soulcraft/brainy'
|
|
144
|
+
|
|
145
|
+
@Injectable({ providedIn: 'root' })
|
|
146
|
+
export class BrainyService {
|
|
147
|
+
private brain = new Brainy()
|
|
148
|
+
|
|
149
|
+
async init() {
|
|
150
|
+
await this.brain.init()
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async search(query: string) {
|
|
154
|
+
return await this.brain.find(query)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### 🔥 **Other Frameworks**
|
|
160
|
+
Brainy works with **any** framework that supports ES6 imports: Svelte, Solid.js, Qwik, Fresh, and more!
|
|
161
|
+
|
|
162
|
+
**Framework Compatibility:**
|
|
163
|
+
- ✅ All modern bundlers (Webpack, Vite, Rollup, Parcel)
|
|
164
|
+
- ✅ SSR/SSG (Next.js, Nuxt, SvelteKit, Astro)
|
|
165
|
+
- ✅ Edge runtimes (Vercel Edge, Cloudflare Workers)
|
|
166
|
+
- ✅ Browser and Node.js environments
|
|
167
|
+
|
|
101
168
|
## 📋 System Requirements
|
|
102
169
|
|
|
103
170
|
**Node.js Version:** 22 LTS or later (recommended)
|
|
@@ -436,9 +503,9 @@ const brain = new Brainy({
|
|
|
436
503
|
}
|
|
437
504
|
})
|
|
438
505
|
|
|
439
|
-
// Browser Storage (OPFS)
|
|
506
|
+
// Browser Storage (OPFS) - Works with frameworks
|
|
440
507
|
const brain = new Brainy({
|
|
441
|
-
storage: {type: 'opfs'}
|
|
508
|
+
storage: {type: 'opfs'} // Framework handles browser polyfills
|
|
442
509
|
})
|
|
443
510
|
|
|
444
511
|
// S3 Compatible (Production)
|
|
@@ -629,6 +696,12 @@ We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
|
629
696
|
|
|
630
697
|
## 📖 Documentation
|
|
631
698
|
|
|
699
|
+
### Framework Integration
|
|
700
|
+
- [Framework Integration Guide](docs/guides/framework-integration.md) - **NEW!** Complete framework setup guide
|
|
701
|
+
- [Next.js Integration](docs/guides/nextjs-integration.md) - **NEW!** React and Next.js examples
|
|
702
|
+
- [Vue.js Integration](docs/guides/vue-integration.md) - **NEW!** Vue and Nuxt examples
|
|
703
|
+
|
|
704
|
+
### Core Documentation
|
|
632
705
|
- [Getting Started Guide](docs/guides/getting-started.md)
|
|
633
706
|
- [API Reference](docs/api/README.md)
|
|
634
707
|
- [Architecture Overview](docs/architecture/overview.md)
|
package/dist/brainy.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { ImprovedNeuralAPI } from './neural/improvedNeuralAPI.js';
|
|
8
8
|
import { NaturalLanguageProcessor } from './neural/naturalLanguageProcessor.js';
|
|
9
9
|
import { TripleIntelligenceSystem } from './triple/TripleIntelligenceSystem.js';
|
|
10
|
-
import { Entity, Relation, Result, AddParams, UpdateParams, RelateParams, FindParams, SimilarParams, GetRelationsParams, AddManyParams, DeleteManyParams, BatchResult, BrainyConfig } from './types/brainy.types.js';
|
|
10
|
+
import { Entity, Relation, Result, AddParams, UpdateParams, RelateParams, FindParams, SimilarParams, GetRelationsParams, AddManyParams, DeleteManyParams, RelateManyParams, BatchResult, BrainyConfig } from './types/brainy.types.js';
|
|
11
11
|
import { NounType } from './types/graphTypes.js';
|
|
12
12
|
/**
|
|
13
13
|
* The main Brainy class - Clean, Beautiful, Powerful
|
|
@@ -98,6 +98,10 @@ export declare class Brainy<T = any> {
|
|
|
98
98
|
continueOnError?: boolean;
|
|
99
99
|
onProgress?: (completed: number, total: number) => void;
|
|
100
100
|
}): Promise<BatchResult<string>>;
|
|
101
|
+
/**
|
|
102
|
+
* Create multiple relationships with batch processing
|
|
103
|
+
*/
|
|
104
|
+
relateMany(params: RelateManyParams<T>): Promise<string[]>;
|
|
101
105
|
/**
|
|
102
106
|
* Clear all data from the database
|
|
103
107
|
*/
|
package/dist/brainy.js
CHANGED
|
@@ -16,6 +16,7 @@ import { NaturalLanguageProcessor } from './neural/naturalLanguageProcessor.js';
|
|
|
16
16
|
import { TripleIntelligenceSystem } from './triple/TripleIntelligenceSystem.js';
|
|
17
17
|
import { MetadataIndexManager } from './utils/metadataIndex.js';
|
|
18
18
|
import { GraphAdjacencyIndex } from './graph/graphAdjacencyIndex.js';
|
|
19
|
+
import { configureLogger, LogLevel } from './utils/logger.js';
|
|
19
20
|
import { NounType } from './types/graphTypes.js';
|
|
20
21
|
/**
|
|
21
22
|
* The main Brainy class - Clean, Beautiful, Powerful
|
|
@@ -50,13 +51,22 @@ export class Brainy {
|
|
|
50
51
|
storage: { ...this.config.storage, ...configOverrides.storage },
|
|
51
52
|
model: { ...this.config.model, ...configOverrides.model },
|
|
52
53
|
index: { ...this.config.index, ...configOverrides.index },
|
|
53
|
-
augmentations: { ...this.config.augmentations, ...configOverrides.augmentations }
|
|
54
|
+
augmentations: { ...this.config.augmentations, ...configOverrides.augmentations },
|
|
55
|
+
verbose: configOverrides.verbose ?? this.config.verbose,
|
|
56
|
+
silent: configOverrides.silent ?? this.config.silent
|
|
54
57
|
};
|
|
55
58
|
// Set dimensions if provided
|
|
56
59
|
if (dimensions) {
|
|
57
60
|
this.dimensions = dimensions;
|
|
58
61
|
}
|
|
59
62
|
}
|
|
63
|
+
// Configure logging based on config options
|
|
64
|
+
if (this.config.silent) {
|
|
65
|
+
configureLogger({ level: -1 }); // Suppress all logs
|
|
66
|
+
}
|
|
67
|
+
else if (this.config.verbose) {
|
|
68
|
+
configureLogger({ level: LogLevel.DEBUG }); // Enable verbose logging
|
|
69
|
+
}
|
|
60
70
|
try {
|
|
61
71
|
// Setup and initialize storage
|
|
62
72
|
this.storage = await this.setupStorage();
|
|
@@ -663,6 +673,66 @@ export class Brainy {
|
|
|
663
673
|
result.duration = Date.now() - startTime;
|
|
664
674
|
return result;
|
|
665
675
|
}
|
|
676
|
+
/**
|
|
677
|
+
* Create multiple relationships with batch processing
|
|
678
|
+
*/
|
|
679
|
+
async relateMany(params) {
|
|
680
|
+
await this.ensureInitialized();
|
|
681
|
+
const result = {
|
|
682
|
+
successful: [],
|
|
683
|
+
failed: [],
|
|
684
|
+
total: params.items.length,
|
|
685
|
+
duration: 0
|
|
686
|
+
};
|
|
687
|
+
const startTime = Date.now();
|
|
688
|
+
const chunkSize = params.chunkSize || 100;
|
|
689
|
+
for (let i = 0; i < params.items.length; i += chunkSize) {
|
|
690
|
+
const chunk = params.items.slice(i, i + chunkSize);
|
|
691
|
+
if (params.parallel) {
|
|
692
|
+
// Process chunk in parallel
|
|
693
|
+
const promises = chunk.map(async (item) => {
|
|
694
|
+
try {
|
|
695
|
+
const relationId = await this.relate(item);
|
|
696
|
+
result.successful.push(relationId);
|
|
697
|
+
}
|
|
698
|
+
catch (error) {
|
|
699
|
+
result.failed.push({
|
|
700
|
+
item,
|
|
701
|
+
error: error.message || 'Unknown error'
|
|
702
|
+
});
|
|
703
|
+
if (!params.continueOnError) {
|
|
704
|
+
throw error;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
await Promise.all(promises);
|
|
709
|
+
}
|
|
710
|
+
else {
|
|
711
|
+
// Process chunk sequentially
|
|
712
|
+
for (const item of chunk) {
|
|
713
|
+
try {
|
|
714
|
+
const relationId = await this.relate(item);
|
|
715
|
+
result.successful.push(relationId);
|
|
716
|
+
}
|
|
717
|
+
catch (error) {
|
|
718
|
+
result.failed.push({
|
|
719
|
+
item,
|
|
720
|
+
error: error.message || 'Unknown error'
|
|
721
|
+
});
|
|
722
|
+
if (!params.continueOnError) {
|
|
723
|
+
throw error;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
// Report progress
|
|
729
|
+
if (params.onProgress) {
|
|
730
|
+
params.onProgress(result.successful.length + result.failed.length, result.total);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
result.duration = Date.now() - startTime;
|
|
734
|
+
return result.successful;
|
|
735
|
+
}
|
|
666
736
|
/**
|
|
667
737
|
* Clear all data from the database
|
|
668
738
|
*/
|
|
@@ -1130,7 +1200,9 @@ export class Brainy {
|
|
|
1130
1200
|
warmup: config?.warmup ?? false,
|
|
1131
1201
|
realtime: config?.realtime ?? false,
|
|
1132
1202
|
multiTenancy: config?.multiTenancy ?? false,
|
|
1133
|
-
telemetry: config?.telemetry ?? false
|
|
1203
|
+
telemetry: config?.telemetry ?? false,
|
|
1204
|
+
verbose: config?.verbose ?? false,
|
|
1205
|
+
silent: config?.silent ?? false
|
|
1134
1206
|
};
|
|
1135
1207
|
}
|
|
1136
1208
|
/**
|
|
@@ -4040,5 +4040,7 @@ export const PATTERNS_METADATA = {
|
|
|
4040
4040
|
total: 403493
|
|
4041
4041
|
}
|
|
4042
4042
|
};
|
|
4043
|
-
|
|
4043
|
+
// Only log if not suppressed - controlled by logging configuration
|
|
4044
|
+
import { prodLog } from '../utils/logger.js';
|
|
4045
|
+
prodLog.info(`🧠 Brainy Pattern Library loaded: ${EMBEDDED_PATTERNS.length} patterns, ${(PATTERNS_METADATA.sizeBytes.total / 1024).toFixed(1)}KB total`);
|
|
4044
4046
|
//# sourceMappingURL=embeddedPatterns.js.map
|
|
@@ -503,8 +503,23 @@ export class FileSystemStorage extends BaseStorage {
|
|
|
503
503
|
}
|
|
504
504
|
// Get page of files
|
|
505
505
|
const pageFiles = nounFiles.slice(startIndex, startIndex + limit);
|
|
506
|
-
// Load nouns
|
|
506
|
+
// Load nouns - count actual successfully loaded items
|
|
507
507
|
const items = [];
|
|
508
|
+
let successfullyLoaded = 0;
|
|
509
|
+
let totalValidFiles = 0;
|
|
510
|
+
// First pass: count total valid files (for accurate totalCount)
|
|
511
|
+
// This is necessary to fix the pagination bug
|
|
512
|
+
for (const file of nounFiles) {
|
|
513
|
+
try {
|
|
514
|
+
// Just check if file exists and is readable
|
|
515
|
+
await fs.promises.access(path.join(this.nounsDir, file), fs.constants.R_OK);
|
|
516
|
+
totalValidFiles++;
|
|
517
|
+
}
|
|
518
|
+
catch {
|
|
519
|
+
// File not readable, skip
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
// Second pass: load the current page
|
|
508
523
|
for (const file of pageFiles) {
|
|
509
524
|
try {
|
|
510
525
|
const data = await fs.promises.readFile(path.join(this.nounsDir, file), 'utf-8');
|
|
@@ -523,18 +538,21 @@ export class FileSystemStorage extends BaseStorage {
|
|
|
523
538
|
continue;
|
|
524
539
|
}
|
|
525
540
|
items.push(noun);
|
|
541
|
+
successfullyLoaded++;
|
|
526
542
|
}
|
|
527
543
|
catch (error) {
|
|
528
544
|
console.warn(`Failed to read noun file ${file}:`, error);
|
|
529
545
|
}
|
|
530
546
|
}
|
|
531
|
-
|
|
547
|
+
// CRITICAL FIX: hasMore should be based on actual valid files, not just file count
|
|
548
|
+
// Also check if we actually loaded any items from this page
|
|
549
|
+
const hasMore = (startIndex + limit < totalValidFiles) && (successfullyLoaded > 0 || startIndex === 0);
|
|
532
550
|
const nextCursor = hasMore && pageFiles.length > 0
|
|
533
551
|
? pageFiles[pageFiles.length - 1].replace('.json', '')
|
|
534
552
|
: undefined;
|
|
535
553
|
return {
|
|
536
554
|
items,
|
|
537
|
-
totalCount:
|
|
555
|
+
totalCount: totalValidFiles, // Use actual valid file count, not all files
|
|
538
556
|
hasMore,
|
|
539
557
|
nextCursor
|
|
540
558
|
};
|
|
@@ -345,10 +345,14 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
345
345
|
});
|
|
346
346
|
// Apply offset if needed (some adapters might not support offset)
|
|
347
347
|
const items = result.items.slice(offset);
|
|
348
|
+
// CRITICAL SAFETY CHECK: Prevent infinite loops
|
|
349
|
+
// If we have no items but hasMore is true, force hasMore to false
|
|
350
|
+
// This prevents pagination bugs from causing infinite loops
|
|
351
|
+
const safeHasMore = items.length > 0 ? result.hasMore : false;
|
|
348
352
|
return {
|
|
349
353
|
items,
|
|
350
354
|
totalCount: result.totalCount || totalCount,
|
|
351
|
-
hasMore:
|
|
355
|
+
hasMore: safeHasMore,
|
|
352
356
|
nextCursor: result.nextCursor
|
|
353
357
|
};
|
|
354
358
|
}
|
|
@@ -490,10 +494,14 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
490
494
|
});
|
|
491
495
|
// Apply offset if needed (some adapters might not support offset)
|
|
492
496
|
const items = result.items.slice(offset);
|
|
497
|
+
// CRITICAL SAFETY CHECK: Prevent infinite loops
|
|
498
|
+
// If we have no items but hasMore is true, force hasMore to false
|
|
499
|
+
// This prevents pagination bugs from causing infinite loops
|
|
500
|
+
const safeHasMore = items.length > 0 ? result.hasMore : false;
|
|
493
501
|
return {
|
|
494
502
|
items,
|
|
495
503
|
totalCount: result.totalCount || totalCount,
|
|
496
|
-
hasMore:
|
|
504
|
+
hasMore: safeHasMore,
|
|
497
505
|
nextCursor: result.nextCursor
|
|
498
506
|
};
|
|
499
507
|
}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Universal Crypto implementation
|
|
3
|
-
*
|
|
3
|
+
* Framework-friendly: Trusts that frameworks provide crypto polyfills
|
|
4
|
+
* Works in all environments: Browser (via framework), Node.js, Serverless
|
|
4
5
|
*/
|
|
5
6
|
/**
|
|
6
7
|
* Generate random bytes
|
|
8
|
+
* Framework-friendly: Assumes crypto API is available via framework polyfills
|
|
7
9
|
*/
|
|
8
10
|
export declare function randomBytes(size: number): Uint8Array;
|
|
9
11
|
/**
|
|
10
12
|
* Generate random UUID
|
|
13
|
+
* Framework-friendly: Assumes crypto.randomUUID is available via framework polyfills
|
|
11
14
|
*/
|
|
12
15
|
export declare function randomUUID(): string;
|
|
13
16
|
/**
|
|
14
17
|
* Create hash (simplified interface)
|
|
18
|
+
* Framework-friendly: Relies on Node.js crypto or framework-provided implementations
|
|
15
19
|
*/
|
|
16
20
|
export declare function createHash(algorithm: string): {
|
|
17
21
|
update: (data: string | Uint8Array) => any;
|
|
@@ -19,6 +23,7 @@ export declare function createHash(algorithm: string): {
|
|
|
19
23
|
};
|
|
20
24
|
/**
|
|
21
25
|
* Create HMAC
|
|
26
|
+
* Framework-friendly: Relies on Node.js crypto or framework-provided implementations
|
|
22
27
|
*/
|
|
23
28
|
export declare function createHmac(algorithm: string, key: string | Uint8Array): {
|
|
24
29
|
update: (data: string | Uint8Array) => any;
|
|
@@ -26,14 +31,17 @@ export declare function createHmac(algorithm: string, key: string | Uint8Array):
|
|
|
26
31
|
};
|
|
27
32
|
/**
|
|
28
33
|
* PBKDF2 synchronous
|
|
34
|
+
* Framework-friendly: Relies on Node.js crypto or framework-provided implementations
|
|
29
35
|
*/
|
|
30
36
|
export declare function pbkdf2Sync(password: string | Uint8Array, salt: string | Uint8Array, iterations: number, keylen: number, digest: string): Uint8Array;
|
|
31
37
|
/**
|
|
32
38
|
* Scrypt synchronous
|
|
39
|
+
* Framework-friendly: Relies on Node.js crypto or framework-provided implementations
|
|
33
40
|
*/
|
|
34
41
|
export declare function scryptSync(password: string | Uint8Array, salt: string | Uint8Array, keylen: number, options?: any): Uint8Array;
|
|
35
42
|
/**
|
|
36
43
|
* Create cipher
|
|
44
|
+
* Framework-friendly: Relies on Node.js crypto or framework-provided implementations
|
|
37
45
|
*/
|
|
38
46
|
export declare function createCipheriv(algorithm: string, key: Uint8Array, iv: Uint8Array): {
|
|
39
47
|
update: (data: string, inputEncoding?: string, outputEncoding?: string) => string;
|
|
@@ -41,6 +49,7 @@ export declare function createCipheriv(algorithm: string, key: Uint8Array, iv: U
|
|
|
41
49
|
};
|
|
42
50
|
/**
|
|
43
51
|
* Create decipher
|
|
52
|
+
* Framework-friendly: Relies on Node.js crypto or framework-provided implementations
|
|
44
53
|
*/
|
|
45
54
|
export declare function createDecipheriv(algorithm: string, key: Uint8Array, iv: Uint8Array): {
|
|
46
55
|
update: (data: string, inputEncoding?: string, outputEncoding?: string) => string;
|
|
@@ -48,6 +57,7 @@ export declare function createDecipheriv(algorithm: string, key: Uint8Array, iv:
|
|
|
48
57
|
};
|
|
49
58
|
/**
|
|
50
59
|
* Timing safe equal
|
|
60
|
+
* Framework-friendly: Relies on Node.js crypto or framework-provided implementations
|
|
51
61
|
*/
|
|
52
62
|
export declare function timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean;
|
|
53
63
|
declare const _default: {
|
package/dist/universal/crypto.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Universal Crypto implementation
|
|
3
|
-
*
|
|
3
|
+
* Framework-friendly: Trusts that frameworks provide crypto polyfills
|
|
4
|
+
* Works in all environments: Browser (via framework), Node.js, Serverless
|
|
4
5
|
*/
|
|
5
|
-
import {
|
|
6
|
+
import { isNode } from '../utils/environment.js';
|
|
6
7
|
let nodeCrypto = null;
|
|
7
8
|
// Dynamic import for Node.js crypto (only in Node.js environment)
|
|
8
9
|
if (isNode()) {
|
|
@@ -15,29 +16,26 @@ if (isNode()) {
|
|
|
15
16
|
}
|
|
16
17
|
/**
|
|
17
18
|
* Generate random bytes
|
|
19
|
+
* Framework-friendly: Assumes crypto API is available via framework polyfills
|
|
18
20
|
*/
|
|
19
21
|
export function randomBytes(size) {
|
|
20
|
-
if (
|
|
21
|
-
// Use Web Crypto API (available in browsers and modern Node.js)
|
|
22
|
+
if (typeof crypto !== 'undefined') {
|
|
23
|
+
// Use Web Crypto API (available in browsers via framework polyfills and modern Node.js)
|
|
22
24
|
const array = new Uint8Array(size);
|
|
23
25
|
crypto.getRandomValues(array);
|
|
24
26
|
return array;
|
|
25
27
|
}
|
|
26
28
|
else if (nodeCrypto) {
|
|
27
|
-
// Use Node.js crypto
|
|
29
|
+
// Use Node.js crypto
|
|
28
30
|
return new Uint8Array(nodeCrypto.randomBytes(size));
|
|
29
31
|
}
|
|
30
32
|
else {
|
|
31
|
-
|
|
32
|
-
const array = new Uint8Array(size);
|
|
33
|
-
for (let i = 0; i < size; i++) {
|
|
34
|
-
array[i] = Math.floor(Math.random() * 256);
|
|
35
|
-
}
|
|
36
|
-
return array;
|
|
33
|
+
throw new Error('Crypto API not available. Framework bundlers should provide crypto polyfills.');
|
|
37
34
|
}
|
|
38
35
|
}
|
|
39
36
|
/**
|
|
40
37
|
* Generate random UUID
|
|
38
|
+
* Framework-friendly: Assumes crypto.randomUUID is available via framework polyfills
|
|
41
39
|
*/
|
|
42
40
|
export function randomUUID() {
|
|
43
41
|
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
@@ -47,158 +45,91 @@ export function randomUUID() {
|
|
|
47
45
|
return nodeCrypto.randomUUID();
|
|
48
46
|
}
|
|
49
47
|
else {
|
|
50
|
-
|
|
51
|
-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
52
|
-
const r = Math.random() * 16 | 0;
|
|
53
|
-
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
54
|
-
return v.toString(16);
|
|
55
|
-
});
|
|
48
|
+
throw new Error('crypto.randomUUID not available. Framework bundlers should provide crypto polyfills.');
|
|
56
49
|
}
|
|
57
50
|
}
|
|
58
51
|
/**
|
|
59
52
|
* Create hash (simplified interface)
|
|
53
|
+
* Framework-friendly: Relies on Node.js crypto or framework-provided implementations
|
|
60
54
|
*/
|
|
61
55
|
export function createHash(algorithm) {
|
|
62
56
|
if (nodeCrypto && nodeCrypto.createHash) {
|
|
63
57
|
return nodeCrypto.createHash(algorithm);
|
|
64
58
|
}
|
|
65
59
|
else {
|
|
66
|
-
|
|
67
|
-
let hash = 0;
|
|
68
|
-
const hashObj = {
|
|
69
|
-
update: (data) => {
|
|
70
|
-
const text = typeof data === 'string' ? data : new TextDecoder().decode(data);
|
|
71
|
-
for (let i = 0; i < text.length; i++) {
|
|
72
|
-
const char = text.charCodeAt(i);
|
|
73
|
-
hash = ((hash << 5) - hash) + char;
|
|
74
|
-
hash = hash & hash; // Convert to 32-bit integer
|
|
75
|
-
}
|
|
76
|
-
return hashObj;
|
|
77
|
-
},
|
|
78
|
-
digest: (encoding) => {
|
|
79
|
-
return Math.abs(hash).toString(16);
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
return hashObj;
|
|
60
|
+
throw new Error(`createHash not available. For browser environments, frameworks should provide crypto polyfills or use Web Crypto API directly.`);
|
|
83
61
|
}
|
|
84
62
|
}
|
|
85
63
|
/**
|
|
86
64
|
* Create HMAC
|
|
65
|
+
* Framework-friendly: Relies on Node.js crypto or framework-provided implementations
|
|
87
66
|
*/
|
|
88
67
|
export function createHmac(algorithm, key) {
|
|
89
68
|
if (nodeCrypto && nodeCrypto.createHmac) {
|
|
90
69
|
return nodeCrypto.createHmac(algorithm, key);
|
|
91
70
|
}
|
|
92
71
|
else {
|
|
93
|
-
|
|
94
|
-
return createHash(algorithm);
|
|
72
|
+
throw new Error(`createHmac not available. For browser environments, frameworks should provide crypto polyfills or use Web Crypto API directly.`);
|
|
95
73
|
}
|
|
96
74
|
}
|
|
97
75
|
/**
|
|
98
76
|
* PBKDF2 synchronous
|
|
77
|
+
* Framework-friendly: Relies on Node.js crypto or framework-provided implementations
|
|
99
78
|
*/
|
|
100
79
|
export function pbkdf2Sync(password, salt, iterations, keylen, digest) {
|
|
101
80
|
if (nodeCrypto && nodeCrypto.pbkdf2Sync) {
|
|
102
81
|
return new Uint8Array(nodeCrypto.pbkdf2Sync(password, salt, iterations, keylen, digest));
|
|
103
82
|
}
|
|
104
83
|
else {
|
|
105
|
-
|
|
106
|
-
const result = new Uint8Array(keylen);
|
|
107
|
-
const passwordStr = typeof password === 'string' ? password : new TextDecoder().decode(password);
|
|
108
|
-
const saltStr = typeof salt === 'string' ? salt : new TextDecoder().decode(salt);
|
|
109
|
-
let hash = 0;
|
|
110
|
-
const combined = passwordStr + saltStr;
|
|
111
|
-
for (let i = 0; i < combined.length; i++) {
|
|
112
|
-
hash = ((hash << 5) - hash) + combined.charCodeAt(i);
|
|
113
|
-
hash = hash & hash;
|
|
114
|
-
}
|
|
115
|
-
for (let i = 0; i < keylen; i++) {
|
|
116
|
-
result[i] = (Math.abs(hash + i) % 256);
|
|
117
|
-
}
|
|
118
|
-
return result;
|
|
84
|
+
throw new Error(`pbkdf2Sync not available. For browser environments, frameworks should provide crypto polyfills or use Web Crypto API directly.`);
|
|
119
85
|
}
|
|
120
86
|
}
|
|
121
87
|
/**
|
|
122
88
|
* Scrypt synchronous
|
|
89
|
+
* Framework-friendly: Relies on Node.js crypto or framework-provided implementations
|
|
123
90
|
*/
|
|
124
91
|
export function scryptSync(password, salt, keylen, options) {
|
|
125
92
|
if (nodeCrypto && nodeCrypto.scryptSync) {
|
|
126
93
|
return new Uint8Array(nodeCrypto.scryptSync(password, salt, keylen, options));
|
|
127
94
|
}
|
|
128
95
|
else {
|
|
129
|
-
|
|
130
|
-
return pbkdf2Sync(password, salt, 10000, keylen, 'sha256');
|
|
96
|
+
throw new Error(`scryptSync not available. For browser environments, frameworks should provide crypto polyfills or use Web Crypto API directly.`);
|
|
131
97
|
}
|
|
132
98
|
}
|
|
133
99
|
/**
|
|
134
100
|
* Create cipher
|
|
101
|
+
* Framework-friendly: Relies on Node.js crypto or framework-provided implementations
|
|
135
102
|
*/
|
|
136
103
|
export function createCipheriv(algorithm, key, iv) {
|
|
137
104
|
if (nodeCrypto && nodeCrypto.createCipheriv) {
|
|
138
105
|
return nodeCrypto.createCipheriv(algorithm, key, iv);
|
|
139
106
|
}
|
|
140
107
|
else {
|
|
141
|
-
|
|
142
|
-
let encrypted = '';
|
|
143
|
-
return {
|
|
144
|
-
update: (data, inputEncoding, outputEncoding) => {
|
|
145
|
-
for (let i = 0; i < data.length; i++) {
|
|
146
|
-
const char = data.charCodeAt(i);
|
|
147
|
-
const keyByte = key[i % key.length];
|
|
148
|
-
const ivByte = iv[i % iv.length];
|
|
149
|
-
encrypted += String.fromCharCode(char ^ keyByte ^ ivByte);
|
|
150
|
-
}
|
|
151
|
-
return outputEncoding === 'hex' ? Buffer.from(encrypted, 'binary').toString('hex') : encrypted;
|
|
152
|
-
},
|
|
153
|
-
final: (outputEncoding) => {
|
|
154
|
-
return outputEncoding === 'hex' ? '' : '';
|
|
155
|
-
}
|
|
156
|
-
};
|
|
108
|
+
throw new Error(`createCipheriv not available. For browser environments, frameworks should provide crypto polyfills or use Web Crypto API directly.`);
|
|
157
109
|
}
|
|
158
110
|
}
|
|
159
111
|
/**
|
|
160
112
|
* Create decipher
|
|
113
|
+
* Framework-friendly: Relies on Node.js crypto or framework-provided implementations
|
|
161
114
|
*/
|
|
162
115
|
export function createDecipheriv(algorithm, key, iv) {
|
|
163
116
|
if (nodeCrypto && nodeCrypto.createDecipheriv) {
|
|
164
117
|
return nodeCrypto.createDecipheriv(algorithm, key, iv);
|
|
165
118
|
}
|
|
166
119
|
else {
|
|
167
|
-
|
|
168
|
-
let decrypted = '';
|
|
169
|
-
return {
|
|
170
|
-
update: (data, inputEncoding, outputEncoding) => {
|
|
171
|
-
const input = inputEncoding === 'hex' ? Buffer.from(data, 'hex').toString('binary') : data;
|
|
172
|
-
for (let i = 0; i < input.length; i++) {
|
|
173
|
-
const char = input.charCodeAt(i);
|
|
174
|
-
const keyByte = key[i % key.length];
|
|
175
|
-
const ivByte = iv[i % iv.length];
|
|
176
|
-
decrypted += String.fromCharCode(char ^ keyByte ^ ivByte);
|
|
177
|
-
}
|
|
178
|
-
return decrypted;
|
|
179
|
-
},
|
|
180
|
-
final: (outputEncoding) => {
|
|
181
|
-
return '';
|
|
182
|
-
}
|
|
183
|
-
};
|
|
120
|
+
throw new Error(`createDecipheriv not available. For browser environments, frameworks should provide crypto polyfills or use Web Crypto API directly.`);
|
|
184
121
|
}
|
|
185
122
|
}
|
|
186
123
|
/**
|
|
187
124
|
* Timing safe equal
|
|
125
|
+
* Framework-friendly: Relies on Node.js crypto or framework-provided implementations
|
|
188
126
|
*/
|
|
189
127
|
export function timingSafeEqual(a, b) {
|
|
190
128
|
if (nodeCrypto && nodeCrypto.timingSafeEqual) {
|
|
191
129
|
return nodeCrypto.timingSafeEqual(a, b);
|
|
192
130
|
}
|
|
193
131
|
else {
|
|
194
|
-
|
|
195
|
-
if (a.length !== b.length)
|
|
196
|
-
return false;
|
|
197
|
-
let result = 0;
|
|
198
|
-
for (let i = 0; i < a.length; i++) {
|
|
199
|
-
result |= a[i] ^ b[i];
|
|
200
|
-
}
|
|
201
|
-
return result === 0;
|
|
132
|
+
throw new Error(`timingSafeEqual not available. For browser environments, frameworks should provide crypto polyfills or use Web Crypto API directly.`);
|
|
202
133
|
}
|
|
203
134
|
}
|
|
204
135
|
export default {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Universal Events implementation
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Framework-friendly: Trusts that frameworks provide events polyfills
|
|
4
|
+
* Works in all environments: Browser (via framework), Node.js, Serverless
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
7
|
* Universal EventEmitter interface
|
|
@@ -16,6 +16,7 @@ export interface UniversalEventEmitter {
|
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* Universal EventEmitter class
|
|
19
|
+
* Framework-friendly: Assumes events API is available via framework polyfills
|
|
19
20
|
*/
|
|
20
21
|
export declare class EventEmitter implements UniversalEventEmitter {
|
|
21
22
|
private emitter;
|