@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/dist/universal/events.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
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
|
-
import {
|
|
6
|
+
import { isNode } from '../utils/environment.js';
|
|
7
7
|
let nodeEvents = null;
|
|
8
8
|
// Dynamic import for Node.js events (only in Node.js environment)
|
|
9
9
|
if (isNode()) {
|
|
@@ -14,73 +14,6 @@ if (isNode()) {
|
|
|
14
14
|
// Ignore import errors in non-Node environments
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
/**
|
|
18
|
-
* Browser implementation using EventTarget
|
|
19
|
-
*/
|
|
20
|
-
class BrowserEventEmitter extends EventTarget {
|
|
21
|
-
constructor() {
|
|
22
|
-
super(...arguments);
|
|
23
|
-
this.listeners = new Map();
|
|
24
|
-
}
|
|
25
|
-
on(event, listener) {
|
|
26
|
-
if (!this.listeners.has(event)) {
|
|
27
|
-
this.listeners.set(event, new Set());
|
|
28
|
-
}
|
|
29
|
-
this.listeners.get(event).add(listener);
|
|
30
|
-
const handler = (e) => {
|
|
31
|
-
const customEvent = e;
|
|
32
|
-
listener(...(customEvent.detail || []));
|
|
33
|
-
};
|
|
34
|
-
listener.__handler = handler;
|
|
35
|
-
this.addEventListener(event, handler);
|
|
36
|
-
return this;
|
|
37
|
-
}
|
|
38
|
-
off(event, listener) {
|
|
39
|
-
const eventListeners = this.listeners.get(event);
|
|
40
|
-
if (eventListeners) {
|
|
41
|
-
eventListeners.delete(listener);
|
|
42
|
-
const handler = listener.__handler;
|
|
43
|
-
if (handler) {
|
|
44
|
-
this.removeEventListener(event, handler);
|
|
45
|
-
delete listener.__handler;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return this;
|
|
49
|
-
}
|
|
50
|
-
emit(event, ...args) {
|
|
51
|
-
const customEvent = new CustomEvent(event, { detail: args });
|
|
52
|
-
this.dispatchEvent(customEvent);
|
|
53
|
-
const eventListeners = this.listeners.get(event);
|
|
54
|
-
return eventListeners ? eventListeners.size > 0 : false;
|
|
55
|
-
}
|
|
56
|
-
once(event, listener) {
|
|
57
|
-
const onceListener = (...args) => {
|
|
58
|
-
this.off(event, onceListener);
|
|
59
|
-
listener(...args);
|
|
60
|
-
};
|
|
61
|
-
return this.on(event, onceListener);
|
|
62
|
-
}
|
|
63
|
-
removeAllListeners(event) {
|
|
64
|
-
if (event) {
|
|
65
|
-
const eventListeners = this.listeners.get(event);
|
|
66
|
-
if (eventListeners) {
|
|
67
|
-
for (const listener of eventListeners) {
|
|
68
|
-
this.off(event, listener);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
for (const [eventName] of this.listeners) {
|
|
74
|
-
this.removeAllListeners(eventName);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return this;
|
|
78
|
-
}
|
|
79
|
-
listenerCount(event) {
|
|
80
|
-
const eventListeners = this.listeners.get(event);
|
|
81
|
-
return eventListeners ? eventListeners.size : 0;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
17
|
/**
|
|
85
18
|
* Node.js implementation using events.EventEmitter
|
|
86
19
|
*/
|
|
@@ -113,17 +46,15 @@ class NodeEventEmitter {
|
|
|
113
46
|
}
|
|
114
47
|
/**
|
|
115
48
|
* Universal EventEmitter class
|
|
49
|
+
* Framework-friendly: Assumes events API is available via framework polyfills
|
|
116
50
|
*/
|
|
117
51
|
export class EventEmitter {
|
|
118
52
|
constructor() {
|
|
119
|
-
if (
|
|
120
|
-
this.emitter = new BrowserEventEmitter();
|
|
121
|
-
}
|
|
122
|
-
else if (isNode() && nodeEvents) {
|
|
53
|
+
if (isNode() && nodeEvents) {
|
|
123
54
|
this.emitter = new NodeEventEmitter();
|
|
124
55
|
}
|
|
125
56
|
else {
|
|
126
|
-
|
|
57
|
+
throw new Error('Events operations not available. Framework bundlers should provide events polyfills.');
|
|
127
58
|
}
|
|
128
59
|
}
|
|
129
60
|
on(event, listener) {
|
package/dist/universal/fs.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Universal File System implementation
|
|
3
|
-
*
|
|
4
|
-
* Node.js
|
|
5
|
-
* Serverless: Uses memory-based fallback
|
|
3
|
+
* Framework-friendly: Trusts that frameworks provide fs polyfills
|
|
4
|
+
* Works in all environments: Browser (via framework), Node.js, Serverless
|
|
6
5
|
*/
|
|
7
6
|
/**
|
|
8
7
|
* Universal file operations interface
|
package/dist/universal/fs.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Universal File System implementation
|
|
3
|
-
*
|
|
4
|
-
* Node.js
|
|
5
|
-
* Serverless: Uses memory-based fallback
|
|
3
|
+
* Framework-friendly: Trusts that frameworks provide fs polyfills
|
|
4
|
+
* Works in all environments: Browser (via framework), Node.js, Serverless
|
|
6
5
|
*/
|
|
7
|
-
import {
|
|
6
|
+
import { isNode } from '../utils/environment.js';
|
|
8
7
|
let nodeFs = null;
|
|
9
8
|
// Dynamic import for Node.js fs (only in Node.js environment)
|
|
10
9
|
if (isNode()) {
|
|
@@ -15,125 +14,6 @@ if (isNode()) {
|
|
|
15
14
|
// Ignore import errors in non-Node environments
|
|
16
15
|
}
|
|
17
16
|
}
|
|
18
|
-
/**
|
|
19
|
-
* Browser implementation using OPFS
|
|
20
|
-
*/
|
|
21
|
-
class BrowserFS {
|
|
22
|
-
async getRoot() {
|
|
23
|
-
if ('storage' in navigator && 'getDirectory' in navigator.storage) {
|
|
24
|
-
return await navigator.storage.getDirectory();
|
|
25
|
-
}
|
|
26
|
-
throw new Error('OPFS not supported in this browser');
|
|
27
|
-
}
|
|
28
|
-
async getFileHandle(path, create = false) {
|
|
29
|
-
const root = await this.getRoot();
|
|
30
|
-
const parts = path.split('/').filter(p => p);
|
|
31
|
-
let dir = root;
|
|
32
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
33
|
-
dir = await dir.getDirectoryHandle(parts[i], { create });
|
|
34
|
-
}
|
|
35
|
-
const fileName = parts[parts.length - 1];
|
|
36
|
-
return await dir.getFileHandle(fileName, { create });
|
|
37
|
-
}
|
|
38
|
-
async getDirHandle(path, create = false) {
|
|
39
|
-
const root = await this.getRoot();
|
|
40
|
-
const parts = path.split('/').filter(p => p);
|
|
41
|
-
let dir = root;
|
|
42
|
-
for (const part of parts) {
|
|
43
|
-
dir = await dir.getDirectoryHandle(part, { create });
|
|
44
|
-
}
|
|
45
|
-
return dir;
|
|
46
|
-
}
|
|
47
|
-
async readFile(path, encoding) {
|
|
48
|
-
try {
|
|
49
|
-
const fileHandle = await this.getFileHandle(path);
|
|
50
|
-
const file = await fileHandle.getFile();
|
|
51
|
-
return await file.text();
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
throw new Error(`File not found: ${path}`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
async writeFile(path, data, encoding) {
|
|
58
|
-
const fileHandle = await this.getFileHandle(path, true);
|
|
59
|
-
const writable = await fileHandle.createWritable();
|
|
60
|
-
await writable.write(data);
|
|
61
|
-
await writable.close();
|
|
62
|
-
}
|
|
63
|
-
async mkdir(path, options = { recursive: true }) {
|
|
64
|
-
await this.getDirHandle(path, true);
|
|
65
|
-
}
|
|
66
|
-
async exists(path) {
|
|
67
|
-
try {
|
|
68
|
-
await this.getFileHandle(path);
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
72
|
-
try {
|
|
73
|
-
await this.getDirHandle(path);
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
async readdir(path, options) {
|
|
82
|
-
const dir = await this.getDirHandle(path);
|
|
83
|
-
if (options?.withFileTypes) {
|
|
84
|
-
const entries = [];
|
|
85
|
-
for await (const [name, handle] of dir.entries()) {
|
|
86
|
-
entries.push({
|
|
87
|
-
name,
|
|
88
|
-
isDirectory: () => handle.kind === 'directory',
|
|
89
|
-
isFile: () => handle.kind === 'file'
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
return entries;
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
const entries = [];
|
|
96
|
-
for await (const [name] of dir.entries()) {
|
|
97
|
-
entries.push(name);
|
|
98
|
-
}
|
|
99
|
-
return entries;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
async unlink(path) {
|
|
103
|
-
const parts = path.split('/').filter(p => p);
|
|
104
|
-
const fileName = parts.pop();
|
|
105
|
-
const dirPath = parts.join('/');
|
|
106
|
-
if (dirPath) {
|
|
107
|
-
const dir = await this.getDirHandle(dirPath);
|
|
108
|
-
await dir.removeEntry(fileName);
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
const root = await this.getRoot();
|
|
112
|
-
await root.removeEntry(fileName);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
async stat(path) {
|
|
116
|
-
try {
|
|
117
|
-
await this.getFileHandle(path);
|
|
118
|
-
return { isFile: () => true, isDirectory: () => false };
|
|
119
|
-
}
|
|
120
|
-
catch {
|
|
121
|
-
try {
|
|
122
|
-
await this.getDirHandle(path);
|
|
123
|
-
return { isFile: () => false, isDirectory: () => true };
|
|
124
|
-
}
|
|
125
|
-
catch {
|
|
126
|
-
throw new Error(`Path not found: ${path}`);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
async access(path, mode) {
|
|
131
|
-
const exists = await this.exists(path);
|
|
132
|
-
if (!exists) {
|
|
133
|
-
throw new Error(`ENOENT: no such file or directory, access '${path}'`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
17
|
/**
|
|
138
18
|
* Node.js implementation using fs/promises
|
|
139
19
|
*/
|
|
@@ -176,99 +56,13 @@ class NodeFS {
|
|
|
176
56
|
await nodeFs.access(path, mode);
|
|
177
57
|
}
|
|
178
58
|
}
|
|
179
|
-
/**
|
|
180
|
-
* Memory-based fallback for serverless/edge environments
|
|
181
|
-
*/
|
|
182
|
-
class MemoryFS {
|
|
183
|
-
constructor() {
|
|
184
|
-
this.files = new Map();
|
|
185
|
-
this.dirs = new Set();
|
|
186
|
-
}
|
|
187
|
-
async readFile(path, encoding) {
|
|
188
|
-
const content = this.files.get(path);
|
|
189
|
-
if (content === undefined) {
|
|
190
|
-
throw new Error(`File not found: ${path}`);
|
|
191
|
-
}
|
|
192
|
-
return content;
|
|
193
|
-
}
|
|
194
|
-
async writeFile(path, data, encoding) {
|
|
195
|
-
this.files.set(path, data);
|
|
196
|
-
// Ensure parent directories exist
|
|
197
|
-
const parts = path.split('/').slice(0, -1);
|
|
198
|
-
for (let i = 1; i <= parts.length; i++) {
|
|
199
|
-
this.dirs.add(parts.slice(0, i).join('/'));
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
async mkdir(path, options = { recursive: true }) {
|
|
203
|
-
this.dirs.add(path);
|
|
204
|
-
if (options.recursive) {
|
|
205
|
-
const parts = path.split('/');
|
|
206
|
-
for (let i = 1; i <= parts.length; i++) {
|
|
207
|
-
this.dirs.add(parts.slice(0, i).join('/'));
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
async exists(path) {
|
|
212
|
-
return this.files.has(path) || this.dirs.has(path);
|
|
213
|
-
}
|
|
214
|
-
async readdir(path, options) {
|
|
215
|
-
const entries = new Set();
|
|
216
|
-
const pathPrefix = path + '/';
|
|
217
|
-
for (const filePath of this.files.keys()) {
|
|
218
|
-
if (filePath.startsWith(pathPrefix)) {
|
|
219
|
-
const relativePath = filePath.slice(pathPrefix.length);
|
|
220
|
-
const firstSegment = relativePath.split('/')[0];
|
|
221
|
-
entries.add(firstSegment);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
for (const dirPath of this.dirs) {
|
|
225
|
-
if (dirPath.startsWith(pathPrefix)) {
|
|
226
|
-
const relativePath = dirPath.slice(pathPrefix.length);
|
|
227
|
-
const firstSegment = relativePath.split('/')[0];
|
|
228
|
-
if (firstSegment)
|
|
229
|
-
entries.add(firstSegment);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
if (options?.withFileTypes) {
|
|
233
|
-
return Array.from(entries).map(name => ({
|
|
234
|
-
name,
|
|
235
|
-
isDirectory: () => this.dirs.has(path + '/' + name),
|
|
236
|
-
isFile: () => this.files.has(path + '/' + name)
|
|
237
|
-
}));
|
|
238
|
-
}
|
|
239
|
-
return Array.from(entries);
|
|
240
|
-
}
|
|
241
|
-
async unlink(path) {
|
|
242
|
-
this.files.delete(path);
|
|
243
|
-
}
|
|
244
|
-
async stat(path) {
|
|
245
|
-
const isFile = this.files.has(path);
|
|
246
|
-
const isDir = this.dirs.has(path);
|
|
247
|
-
if (!isFile && !isDir) {
|
|
248
|
-
throw new Error(`Path not found: ${path}`);
|
|
249
|
-
}
|
|
250
|
-
return {
|
|
251
|
-
isFile: () => isFile,
|
|
252
|
-
isDirectory: () => isDir
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
async access(path, mode) {
|
|
256
|
-
const exists = await this.exists(path);
|
|
257
|
-
if (!exists) {
|
|
258
|
-
throw new Error(`ENOENT: no such file or directory, access '${path}'`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
59
|
// Create the appropriate filesystem implementation
|
|
263
60
|
let fsImpl;
|
|
264
|
-
if (
|
|
265
|
-
fsImpl = new BrowserFS();
|
|
266
|
-
}
|
|
267
|
-
else if (isNode() && nodeFs) {
|
|
61
|
+
if (isNode() && nodeFs) {
|
|
268
62
|
fsImpl = new NodeFS();
|
|
269
63
|
}
|
|
270
64
|
else {
|
|
271
|
-
|
|
65
|
+
throw new Error('File system operations not available. Framework bundlers should provide fs polyfills or use storage adapters like OPFS, Memory, or S3.');
|
|
272
66
|
}
|
|
273
67
|
// Export the filesystem operations
|
|
274
68
|
export const readFile = fsImpl.readFile.bind(fsImpl);
|
package/dist/universal/path.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Universal Path implementation
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Framework-friendly: Trusts that frameworks provide path polyfills
|
|
4
|
+
* Works in all environments: Browser (via framework), Node.js, Serverless
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
7
|
* Universal path operations
|
|
8
|
+
* Framework-friendly: Assumes path API is available via framework polyfills
|
|
8
9
|
*/
|
|
9
10
|
export declare function join(...paths: string[]): string;
|
|
10
11
|
export declare function dirname(path: string): string;
|
package/dist/universal/path.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Universal Path implementation
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Framework-friendly: Trusts that frameworks provide path polyfills
|
|
4
|
+
* Works in all environments: Browser (via framework), Node.js, Serverless
|
|
5
5
|
*/
|
|
6
6
|
import { isNode } from '../utils/environment.js';
|
|
7
7
|
let nodePath = null;
|
|
@@ -16,119 +16,63 @@ if (isNode()) {
|
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* Universal path operations
|
|
19
|
+
* Framework-friendly: Assumes path API is available via framework polyfills
|
|
19
20
|
*/
|
|
20
21
|
export function join(...paths) {
|
|
21
22
|
if (nodePath) {
|
|
22
23
|
return nodePath.join(...paths);
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
for (const path of paths) {
|
|
27
|
-
if (path) {
|
|
28
|
-
parts.push(...path.split('/').filter(p => p));
|
|
29
|
-
}
|
|
25
|
+
else {
|
|
26
|
+
throw new Error('Path operations not available. Framework bundlers should provide path polyfills.');
|
|
30
27
|
}
|
|
31
|
-
return parts.join('/');
|
|
32
28
|
}
|
|
33
29
|
export function dirname(path) {
|
|
34
30
|
if (nodePath) {
|
|
35
31
|
return nodePath.dirname(path);
|
|
36
32
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return '.';
|
|
41
|
-
return parts.slice(0, -1).join('/');
|
|
33
|
+
else {
|
|
34
|
+
throw new Error('Path operations not available. Framework bundlers should provide path polyfills.');
|
|
35
|
+
}
|
|
42
36
|
}
|
|
43
37
|
export function basename(path, ext) {
|
|
44
38
|
if (nodePath) {
|
|
45
39
|
return nodePath.basename(path, ext);
|
|
46
40
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
let name = parts[parts.length - 1];
|
|
50
|
-
if (ext && name.endsWith(ext)) {
|
|
51
|
-
name = name.slice(0, -ext.length);
|
|
41
|
+
else {
|
|
42
|
+
throw new Error('Path operations not available. Framework bundlers should provide path polyfills.');
|
|
52
43
|
}
|
|
53
|
-
return name;
|
|
54
44
|
}
|
|
55
45
|
export function extname(path) {
|
|
56
46
|
if (nodePath) {
|
|
57
47
|
return nodePath.extname(path);
|
|
58
48
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return lastDot === -1 ? '' : name.slice(lastDot);
|
|
49
|
+
else {
|
|
50
|
+
throw new Error('Path operations not available. Framework bundlers should provide path polyfills.');
|
|
51
|
+
}
|
|
63
52
|
}
|
|
64
53
|
export function resolve(...paths) {
|
|
65
54
|
if (nodePath) {
|
|
66
55
|
return nodePath.resolve(...paths);
|
|
67
56
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
for (let i = paths.length - 1; i >= -1 && !resolvedAbsolute; i--) {
|
|
72
|
-
const path = i >= 0 ? paths[i] : '/';
|
|
73
|
-
if (!path)
|
|
74
|
-
continue;
|
|
75
|
-
resolved = path + '/' + resolved;
|
|
76
|
-
resolvedAbsolute = path.charAt(0) === '/';
|
|
77
|
-
}
|
|
78
|
-
// Normalize the path
|
|
79
|
-
resolved = normalizeArray(resolved.split('/').filter(p => p), !resolvedAbsolute).join('/');
|
|
80
|
-
return (resolvedAbsolute ? '/' : '') + resolved;
|
|
57
|
+
else {
|
|
58
|
+
throw new Error('Path operations not available. Framework bundlers should provide path polyfills.');
|
|
59
|
+
}
|
|
81
60
|
}
|
|
82
61
|
export function relative(from, to) {
|
|
83
62
|
if (nodePath) {
|
|
84
63
|
return nodePath.relative(from, to);
|
|
85
64
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
let commonLength = 0;
|
|
90
|
-
for (let i = 0; i < Math.min(fromParts.length, toParts.length); i++) {
|
|
91
|
-
if (fromParts[i] === toParts[i]) {
|
|
92
|
-
commonLength++;
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
const upCount = fromParts.length - commonLength;
|
|
99
|
-
const upParts = new Array(upCount).fill('..');
|
|
100
|
-
const downParts = toParts.slice(commonLength);
|
|
101
|
-
return [...upParts, ...downParts].join('/');
|
|
65
|
+
else {
|
|
66
|
+
throw new Error('Path operations not available. Framework bundlers should provide path polyfills.');
|
|
67
|
+
}
|
|
102
68
|
}
|
|
103
69
|
export function isAbsolute(path) {
|
|
104
70
|
if (nodePath) {
|
|
105
71
|
return nodePath.isAbsolute(path);
|
|
106
72
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Normalize array helper function
|
|
112
|
-
*/
|
|
113
|
-
function normalizeArray(parts, allowAboveRoot) {
|
|
114
|
-
const res = [];
|
|
115
|
-
for (let i = 0; i < parts.length; i++) {
|
|
116
|
-
const p = parts[i];
|
|
117
|
-
if (!p || p === '.')
|
|
118
|
-
continue;
|
|
119
|
-
if (p === '..') {
|
|
120
|
-
if (res.length && res[res.length - 1] !== '..') {
|
|
121
|
-
res.pop();
|
|
122
|
-
}
|
|
123
|
-
else if (allowAboveRoot) {
|
|
124
|
-
res.push('..');
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
res.push(p);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return res;
|
|
73
|
+
else {
|
|
74
|
+
throw new Error('Path operations not available. Framework bundlers should provide path polyfills.');
|
|
75
|
+
}
|
|
132
76
|
}
|
|
133
77
|
// Path separator (always use forward slash for consistency)
|
|
134
78
|
export const sep = '/';
|
package/dist/universal/uuid.d.ts
CHANGED
package/dist/universal/uuid.js
CHANGED
|
@@ -1250,10 +1250,31 @@ export class MetadataIndexManager {
|
|
|
1250
1250
|
const nounLimit = 25; // Even smaller batches during initialization to prevent socket exhaustion
|
|
1251
1251
|
let hasMoreNouns = true;
|
|
1252
1252
|
let totalNounsProcessed = 0;
|
|
1253
|
-
|
|
1253
|
+
let consecutiveEmptyBatches = 0;
|
|
1254
|
+
const MAX_ITERATIONS = 10000; // Safety limit to prevent infinite loops
|
|
1255
|
+
let iterations = 0;
|
|
1256
|
+
while (hasMoreNouns && iterations < MAX_ITERATIONS) {
|
|
1257
|
+
iterations++;
|
|
1254
1258
|
const result = await this.storage.getNouns({
|
|
1255
1259
|
pagination: { offset: nounOffset, limit: nounLimit }
|
|
1256
1260
|
});
|
|
1261
|
+
// CRITICAL SAFETY CHECK: Prevent infinite loop on empty results
|
|
1262
|
+
if (result.items.length === 0) {
|
|
1263
|
+
consecutiveEmptyBatches++;
|
|
1264
|
+
if (consecutiveEmptyBatches >= 3) {
|
|
1265
|
+
prodLog.warn('⚠️ Breaking metadata rebuild loop: received 3 consecutive empty batches');
|
|
1266
|
+
break;
|
|
1267
|
+
}
|
|
1268
|
+
// If hasMore is true but items are empty, it's likely a bug
|
|
1269
|
+
if (result.hasMore) {
|
|
1270
|
+
prodLog.warn(`⚠️ Storage returned empty items but hasMore=true at offset ${nounOffset}`);
|
|
1271
|
+
hasMoreNouns = false; // Force exit
|
|
1272
|
+
break;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
else {
|
|
1276
|
+
consecutiveEmptyBatches = 0; // Reset counter on non-empty batch
|
|
1277
|
+
}
|
|
1257
1278
|
// CRITICAL FIX: Use batch metadata reading to prevent socket exhaustion
|
|
1258
1279
|
const nounIds = result.items.map(noun => noun.id);
|
|
1259
1280
|
let metadataBatch;
|
|
@@ -1315,10 +1336,30 @@ export class MetadataIndexManager {
|
|
|
1315
1336
|
const verbLimit = 25; // Even smaller batches during initialization to prevent socket exhaustion
|
|
1316
1337
|
let hasMoreVerbs = true;
|
|
1317
1338
|
let totalVerbsProcessed = 0;
|
|
1318
|
-
|
|
1339
|
+
let consecutiveEmptyVerbBatches = 0;
|
|
1340
|
+
let verbIterations = 0;
|
|
1341
|
+
while (hasMoreVerbs && verbIterations < MAX_ITERATIONS) {
|
|
1342
|
+
verbIterations++;
|
|
1319
1343
|
const result = await this.storage.getVerbs({
|
|
1320
1344
|
pagination: { offset: verbOffset, limit: verbLimit }
|
|
1321
1345
|
});
|
|
1346
|
+
// CRITICAL SAFETY CHECK: Prevent infinite loop on empty results
|
|
1347
|
+
if (result.items.length === 0) {
|
|
1348
|
+
consecutiveEmptyVerbBatches++;
|
|
1349
|
+
if (consecutiveEmptyVerbBatches >= 3) {
|
|
1350
|
+
prodLog.warn('⚠️ Breaking verb metadata rebuild loop: received 3 consecutive empty batches');
|
|
1351
|
+
break;
|
|
1352
|
+
}
|
|
1353
|
+
// If hasMore is true but items are empty, it's likely a bug
|
|
1354
|
+
if (result.hasMore) {
|
|
1355
|
+
prodLog.warn(`⚠️ Storage returned empty verb items but hasMore=true at offset ${verbOffset}`);
|
|
1356
|
+
hasMoreVerbs = false; // Force exit
|
|
1357
|
+
break;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
else {
|
|
1361
|
+
consecutiveEmptyVerbBatches = 0; // Reset counter on non-empty batch
|
|
1362
|
+
}
|
|
1322
1363
|
// CRITICAL FIX: Use batch verb metadata reading to prevent socket exhaustion
|
|
1323
1364
|
const verbIds = result.items.map(verb => verb.id);
|
|
1324
1365
|
let verbMetadataBatch;
|
|
@@ -1372,6 +1413,13 @@ export class MetadataIndexManager {
|
|
|
1372
1413
|
}
|
|
1373
1414
|
await this.yieldToEventLoop();
|
|
1374
1415
|
}
|
|
1416
|
+
// Check if we hit iteration limits
|
|
1417
|
+
if (iterations >= MAX_ITERATIONS) {
|
|
1418
|
+
prodLog.error(`❌ Metadata noun rebuild hit maximum iteration limit (${MAX_ITERATIONS}). This indicates a bug in storage pagination.`);
|
|
1419
|
+
}
|
|
1420
|
+
if (verbIterations >= MAX_ITERATIONS) {
|
|
1421
|
+
prodLog.error(`❌ Metadata verb rebuild hit maximum iteration limit (${MAX_ITERATIONS}). This indicates a bug in storage pagination.`);
|
|
1422
|
+
}
|
|
1375
1423
|
// Flush to storage with final yield
|
|
1376
1424
|
prodLog.debug('💾 Flushing metadata index to storage...');
|
|
1377
1425
|
await this.flush();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soulcraft/brainy",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Universal Knowledge Protocol™ - World's first Triple Intelligence database unifying vector, graph, and document search in one API. 31 nouns × 40 verbs for infinite expressiveness.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -15,17 +15,8 @@
|
|
|
15
15
|
"./src/setup.ts",
|
|
16
16
|
"./src/utils/textEncoding.ts"
|
|
17
17
|
],
|
|
18
|
-
"browser": {
|
|
19
|
-
"fs": false,
|
|
20
|
-
"fs/promises": false,
|
|
21
|
-
"path": "path-browserify",
|
|
22
|
-
"crypto": "crypto-browserify",
|
|
23
|
-
"./dist/cortex/cortex.js": "./dist/browserFramework.js"
|
|
24
|
-
},
|
|
25
18
|
"exports": {
|
|
26
19
|
".": {
|
|
27
|
-
"browser": "./dist/browserFramework.js",
|
|
28
|
-
"node": "./dist/index.js",
|
|
29
20
|
"import": "./dist/index.js",
|
|
30
21
|
"types": "./dist/index.d.ts"
|
|
31
22
|
},
|
|
@@ -45,10 +36,6 @@
|
|
|
45
36
|
"import": "./dist/utils/textEncoding.js",
|
|
46
37
|
"types": "./dist/utils/textEncoding.d.ts"
|
|
47
38
|
},
|
|
48
|
-
"./browserFramework": {
|
|
49
|
-
"import": "./dist/browserFramework.js",
|
|
50
|
-
"types": "./dist/browserFramework.d.ts"
|
|
51
|
-
},
|
|
52
39
|
"./universal": {
|
|
53
40
|
"import": "./dist/universal/index.js",
|
|
54
41
|
"types": "./dist/universal/index.d.ts"
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Browser Framework Entry Point for Brainy
|
|
3
|
-
* Optimized for modern frameworks like Angular, React, Vue, etc.
|
|
4
|
-
* Auto-detects environment and uses optimal storage (OPFS in browsers)
|
|
5
|
-
*/
|
|
6
|
-
import { Brainy, BrainyConfig } from './brainy.js';
|
|
7
|
-
import { VerbType, NounType } from './types/graphTypes.js';
|
|
8
|
-
/**
|
|
9
|
-
* Create a Brainy instance optimized for browser frameworks
|
|
10
|
-
* Auto-detects environment and selects optimal storage and settings
|
|
11
|
-
*/
|
|
12
|
-
export declare function createBrowserBrainy(config?: Partial<BrainyConfig>): Promise<Brainy>;
|
|
13
|
-
export { VerbType, NounType, Brainy };
|
|
14
|
-
export type { BrainyConfig };
|
|
15
|
-
export default createBrowserBrainy;
|