@unrdf/knowledge-engine 5.0.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/LICENSE +21 -0
- package/README.md +84 -0
- package/package.json +64 -0
- package/src/browser-shims.mjs +343 -0
- package/src/browser.mjs +910 -0
- package/src/canonicalize.mjs +414 -0
- package/src/condition-cache.mjs +109 -0
- package/src/condition-evaluator.mjs +722 -0
- package/src/dark-matter-core.mjs +742 -0
- package/src/define-hook.mjs +213 -0
- package/src/effect-sandbox-browser.mjs +283 -0
- package/src/effect-sandbox-worker.mjs +170 -0
- package/src/effect-sandbox.mjs +517 -0
- package/src/engines/index.mjs +11 -0
- package/src/engines/rdf-engine.mjs +299 -0
- package/src/file-resolver.mjs +387 -0
- package/src/hook-executor-batching.mjs +277 -0
- package/src/hook-executor.mjs +870 -0
- package/src/hook-management.mjs +150 -0
- package/src/index.mjs +93 -0
- package/src/ken-parliment.mjs +119 -0
- package/src/ken.mjs +149 -0
- package/src/knowledge-engine/builtin-rules.mjs +190 -0
- package/src/knowledge-engine/inference-engine.mjs +418 -0
- package/src/knowledge-engine/knowledge-engine.mjs +317 -0
- package/src/knowledge-engine/pattern-dsl.mjs +142 -0
- package/src/knowledge-engine/pattern-matcher.mjs +215 -0
- package/src/knowledge-engine/rules.mjs +184 -0
- package/src/knowledge-engine.mjs +319 -0
- package/src/knowledge-hook-engine.mjs +360 -0
- package/src/knowledge-hook-manager.mjs +469 -0
- package/src/knowledge-substrate-core.mjs +927 -0
- package/src/lite.mjs +222 -0
- package/src/lockchain-writer-browser.mjs +414 -0
- package/src/lockchain-writer.mjs +602 -0
- package/src/monitoring/andon-signals.mjs +775 -0
- package/src/observability.mjs +531 -0
- package/src/parse.mjs +290 -0
- package/src/performance-optimizer.mjs +678 -0
- package/src/policy-pack.mjs +572 -0
- package/src/query-cache.mjs +116 -0
- package/src/query-optimizer.mjs +1051 -0
- package/src/query.mjs +306 -0
- package/src/reason.mjs +350 -0
- package/src/resolution-layer.mjs +506 -0
- package/src/schemas.mjs +1063 -0
- package/src/security/error-sanitizer.mjs +257 -0
- package/src/security/path-validator.mjs +194 -0
- package/src/security/sandbox-restrictions.mjs +331 -0
- package/src/security-validator.mjs +389 -0
- package/src/store-cache.mjs +137 -0
- package/src/telemetry.mjs +167 -0
- package/src/transaction.mjs +810 -0
- package/src/utils/adaptive-monitor.mjs +746 -0
- package/src/utils/circuit-breaker.mjs +513 -0
- package/src/utils/edge-case-handler.mjs +503 -0
- package/src/utils/memory-manager.mjs +498 -0
- package/src/utils/ring-buffer.mjs +282 -0
- package/src/validate.mjs +319 -0
- package/src/validators/index.mjs +338 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# @unrdf/knowledge-engine
|
|
2
|
+
|
|
3
|
+
 
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
**Rule Engine, Inference, and Pattern Matching** *(Optional Extension)*
|
|
7
|
+
|
|
8
|
+
Apply business rules and inference to RDF data. Includes reasoning engine and pattern matching.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm add @unrdf/knowledge-engine
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 📚 Examples
|
|
17
|
+
|
|
18
|
+
See these examples that demonstrate @unrdf/knowledge-engine:
|
|
19
|
+
|
|
20
|
+
- **[knowledge-engine-example.mjs](../../examples/knowledge-engine-example.mjs)** - Complete AI semantic analysis demo (1 hour)
|
|
21
|
+
- **[ai-semantic-example.mjs](../../examples/ai-semantic-example.mjs)** - Embedding-based semantic search
|
|
22
|
+
- **[comprehensive-feature-test.mjs](../../examples/comprehensive-feature-test.mjs)** - Knowledge engine integration
|
|
23
|
+
|
|
24
|
+
**Want AI-powered queries?** Start with [knowledge-engine-example.mjs](../../examples/knowledge-engine-example.mjs).
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
import { inferPatterns, createRuleSet } from '@unrdf/knowledge-engine'
|
|
30
|
+
|
|
31
|
+
// Define rules
|
|
32
|
+
const rules = createRuleSet([
|
|
33
|
+
{
|
|
34
|
+
name: 'infer-person',
|
|
35
|
+
pattern: '?x rdf:type foaf:Person',
|
|
36
|
+
then: '?x rdfs:label ?name'
|
|
37
|
+
}
|
|
38
|
+
])
|
|
39
|
+
|
|
40
|
+
// Apply inference
|
|
41
|
+
const inferred = await inferPatterns(store, rules)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- ✅ Forward chaining inference
|
|
47
|
+
- ✅ Rule definition and execution
|
|
48
|
+
- ✅ Pattern matching on RDF data
|
|
49
|
+
- ✅ Built-in ontology reasoning (RDF, RDFS, OWL)
|
|
50
|
+
- ✅ Custom rule sets
|
|
51
|
+
- ✅ Performance optimization
|
|
52
|
+
|
|
53
|
+
## Use Cases
|
|
54
|
+
|
|
55
|
+
- **Business rules**: Apply domain rules to RDF
|
|
56
|
+
- **Data enrichment**: Infer new facts from existing data
|
|
57
|
+
- **Ontology reasoning**: RDFS and OWL inference
|
|
58
|
+
- **Data quality**: Detect and fix inconsistencies
|
|
59
|
+
- **Knowledge extraction**: Mine patterns from data
|
|
60
|
+
|
|
61
|
+
## Documentation
|
|
62
|
+
|
|
63
|
+
- **[API Reference](./docs/API.md)** - Complete API documentation
|
|
64
|
+
- **[User Guide](./docs/GUIDE.md)** - Rule definition and usage
|
|
65
|
+
- **[Examples](./examples/)** - Code examples
|
|
66
|
+
- **[Contributing](./docs/CONTRIBUTING.md)** - How to contribute
|
|
67
|
+
|
|
68
|
+
## Status
|
|
69
|
+
|
|
70
|
+
**Optional Extension** - Not required for core UNRDF functionality.
|
|
71
|
+
Use only if you need rule-based inference.
|
|
72
|
+
|
|
73
|
+
## Depends On
|
|
74
|
+
|
|
75
|
+
- `@unrdf/core` - RDF substrate
|
|
76
|
+
- `@unrdf/streaming` - Change subscriptions
|
|
77
|
+
|
|
78
|
+
## VOC Usage
|
|
79
|
+
|
|
80
|
+
- VOC-3: ML Agent (pattern matching)
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@unrdf/knowledge-engine",
|
|
3
|
+
"version": "5.0.1",
|
|
4
|
+
"description": "UNRDF Knowledge Engine - Rule Engine, Inference, and Pattern Matching (Optional Extension)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.mjs",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.mjs",
|
|
9
|
+
"./query": "./src/query.mjs",
|
|
10
|
+
"./canonicalize": "./src/canonicalize.mjs",
|
|
11
|
+
"./parse": "./src/parse.mjs"
|
|
12
|
+
},
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"files": [
|
|
15
|
+
"src/",
|
|
16
|
+
"dist/",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"rdf",
|
|
22
|
+
"knowledge-engine",
|
|
23
|
+
"inference",
|
|
24
|
+
"reasoning",
|
|
25
|
+
"rules"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"eyereasoner": "^18.23.0",
|
|
29
|
+
"@unrdf/core": "5.0.1",
|
|
30
|
+
"@unrdf/streaming": "5.0.1"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^24.10.1",
|
|
34
|
+
"vitest": "^4.0.15"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/unrdf/unrdf.git",
|
|
42
|
+
"directory": "packages/knowledge-engine"
|
|
43
|
+
},
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/unrdf/unrdf/issues"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/unrdf/unrdf#readme",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"test": "vitest run --coverage",
|
|
54
|
+
"test:fast": "vitest run --coverage",
|
|
55
|
+
"test:watch": "vitest --coverage",
|
|
56
|
+
"build": "node build.config.mjs",
|
|
57
|
+
"lint": "eslint src/ test/ --max-warnings=0",
|
|
58
|
+
"lint:fix": "eslint src/ test/ --fix",
|
|
59
|
+
"format": "prettier --write src/ test/",
|
|
60
|
+
"format:check": "prettier --check src/ test/",
|
|
61
|
+
"clean": "rm -rf dist/ .nyc_output/ coverage/",
|
|
62
|
+
"dev": "echo 'Development mode for @unrdf/knowledge-engine'"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Browser compatibility shims for Node.js APIs
|
|
3
|
+
* @module browser-shims
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* Provides browser-compatible polyfills for Node.js APIs used in the knowledge engine.
|
|
7
|
+
* This allows the same codebase to work in both Node.js and browser environments.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Check if we're running in a browser environment
|
|
12
|
+
*/
|
|
13
|
+
export const isBrowser =
|
|
14
|
+
typeof globalThis?.window !== 'undefined' && typeof globalThis?.window?.document !== 'undefined';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Check if we're running in Node.js environment
|
|
18
|
+
*/
|
|
19
|
+
export const isNode = (() => {
|
|
20
|
+
try {
|
|
21
|
+
return typeof process !== 'undefined' && !!process?.versions?.node;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
})();
|
|
26
|
+
|
|
27
|
+
// Random UUID generation - use crypto.randomUUID if available, otherwise fallback
|
|
28
|
+
/**
|
|
29
|
+
*
|
|
30
|
+
*/
|
|
31
|
+
export function randomUUID() {
|
|
32
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
33
|
+
return crypto.randomUUID();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Fallback UUID generation
|
|
37
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
38
|
+
const r = (Math.random() * 16) | 0;
|
|
39
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
40
|
+
return v.toString(16);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Path utilities - browser-compatible version
|
|
45
|
+
export const path = {
|
|
46
|
+
join: (...args) => args.filter(Boolean).join('/').replace(/\/+/g, '/'),
|
|
47
|
+
resolve: (...args) => args.filter(Boolean).join('/').replace(/\/+/g, '/'),
|
|
48
|
+
dirname: path => path.replace(/\/$/, '').split('/').slice(0, -1).join('/') || '.',
|
|
49
|
+
basename: path => path.split('/').pop() || '',
|
|
50
|
+
extname: path => {
|
|
51
|
+
const ext = path.split('.').pop();
|
|
52
|
+
return ext && ext !== path ? '.' + ext : '';
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Process utilities
|
|
57
|
+
export const process = {
|
|
58
|
+
cwd: () => '/',
|
|
59
|
+
env: isBrowser
|
|
60
|
+
? {}
|
|
61
|
+
: (() => {
|
|
62
|
+
try {
|
|
63
|
+
return process.env;
|
|
64
|
+
} catch {
|
|
65
|
+
return {};
|
|
66
|
+
}
|
|
67
|
+
})(),
|
|
68
|
+
versions: isBrowser
|
|
69
|
+
? {}
|
|
70
|
+
: (() => {
|
|
71
|
+
try {
|
|
72
|
+
return process.versions;
|
|
73
|
+
} catch {
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
76
|
+
})(),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// File system operations - browser-compatible (no-ops or memory-based)
|
|
80
|
+
class BrowserFileSystem {
|
|
81
|
+
#files = new Map();
|
|
82
|
+
#directories = new Set(['/']);
|
|
83
|
+
|
|
84
|
+
constructor() {
|
|
85
|
+
// Create some default directories
|
|
86
|
+
this.#directories.add('/src');
|
|
87
|
+
this.#directories.add('/dist');
|
|
88
|
+
this.#directories.add('/examples');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
clear() {
|
|
92
|
+
this.#files.clear();
|
|
93
|
+
this.#directories.clear();
|
|
94
|
+
this.#directories.add('/');
|
|
95
|
+
this.#directories.add('/src');
|
|
96
|
+
this.#directories.add('/dist');
|
|
97
|
+
this.#directories.add('/examples');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
existsSync(path) {
|
|
101
|
+
return this.#files.has(path) || this.#directories.has(path);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
readFileSync(path, encoding = 'utf8') {
|
|
105
|
+
const content = this.#files.get(path);
|
|
106
|
+
if (!content) {
|
|
107
|
+
throw new Error(`ENOENT: no such file or directory, open '${path}'`);
|
|
108
|
+
}
|
|
109
|
+
return encoding === 'utf8' ? content : Buffer.from(content, 'utf8');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
writeFileSync(path, data, encoding = 'utf8') {
|
|
113
|
+
const content = encoding === 'utf8' ? data : data.toString();
|
|
114
|
+
this.#files.set(path, content);
|
|
115
|
+
|
|
116
|
+
// Ensure parent directories exist
|
|
117
|
+
const parent = path.dirname(path);
|
|
118
|
+
if (!this.#directories.has(parent) && parent !== path) {
|
|
119
|
+
this.#directories.add(parent);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
mkdirSync(path, _options = {}) {
|
|
124
|
+
if (this.#directories.has(path)) {
|
|
125
|
+
throw new Error(`EEXIST: file already exists, mkdir '${path}'`);
|
|
126
|
+
}
|
|
127
|
+
this.#directories.add(path);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
readdirSync(path) {
|
|
131
|
+
return Array.from(this.#files.keys()).filter(file => file.startsWith(path + '/'));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Async versions
|
|
135
|
+
async readFile(path, encoding = 'utf8') {
|
|
136
|
+
return Promise.resolve(this.readFileSync(path, encoding));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async writeFile(path, data, encoding = 'utf8') {
|
|
140
|
+
this.writeFileSync(path, data, encoding);
|
|
141
|
+
return Promise.resolve();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async mkdir(path, options = {}) {
|
|
145
|
+
this.mkdirSync(path, options);
|
|
146
|
+
return Promise.resolve();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const fs = isBrowser
|
|
151
|
+
? new BrowserFileSystem()
|
|
152
|
+
: await import('node:fs').then(m => m.default);
|
|
153
|
+
export const fsPromises = isBrowser
|
|
154
|
+
? new BrowserFileSystem()
|
|
155
|
+
: await import('node:fs/promises').then(m => m.default);
|
|
156
|
+
|
|
157
|
+
// Worker thread polyfill for browser
|
|
158
|
+
/**
|
|
159
|
+
*
|
|
160
|
+
*/
|
|
161
|
+
export class BrowserWorker {
|
|
162
|
+
/**
|
|
163
|
+
*
|
|
164
|
+
*/
|
|
165
|
+
constructor(source, options = {}) {
|
|
166
|
+
if (typeof source === 'string' && isBrowser) {
|
|
167
|
+
// Convert string to blob URL (browser only)
|
|
168
|
+
const blob = new Blob([source], { type: 'application/javascript' });
|
|
169
|
+
this.worker = new Worker(URL.createObjectURL(blob), options);
|
|
170
|
+
} else if (typeof source === 'string' && isNode) {
|
|
171
|
+
// In Node.js, can't create Worker from inline code - need a file
|
|
172
|
+
// Create a mock worker for testing purposes
|
|
173
|
+
this.worker = null;
|
|
174
|
+
this.mockMode = true;
|
|
175
|
+
} else {
|
|
176
|
+
// Assume it's a file path
|
|
177
|
+
this.worker = new Worker(source, options);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
this.messageHandlers = [];
|
|
181
|
+
this.errorHandlers = [];
|
|
182
|
+
this.exitHandlers = [];
|
|
183
|
+
this.terminated = false;
|
|
184
|
+
|
|
185
|
+
if (this.worker) {
|
|
186
|
+
this.worker.onmessage = event => {
|
|
187
|
+
this.messageHandlers.forEach(handler => handler(event.data));
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
this.worker.onerror = error => {
|
|
191
|
+
this.errorHandlers.forEach(handler => handler(error));
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
*
|
|
198
|
+
*/
|
|
199
|
+
postMessage(data) {
|
|
200
|
+
if (this.terminated) return;
|
|
201
|
+
if (this.worker) {
|
|
202
|
+
this.worker.postMessage(data);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
*
|
|
208
|
+
*/
|
|
209
|
+
terminate() {
|
|
210
|
+
this.terminated = true;
|
|
211
|
+
if (this.worker) {
|
|
212
|
+
this.worker.terminate();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
*
|
|
218
|
+
*/
|
|
219
|
+
on(event, handler) {
|
|
220
|
+
switch (event) {
|
|
221
|
+
case 'message':
|
|
222
|
+
this.messageHandlers.push(handler);
|
|
223
|
+
break;
|
|
224
|
+
case 'error':
|
|
225
|
+
this.errorHandlers.push(handler);
|
|
226
|
+
break;
|
|
227
|
+
case 'exit':
|
|
228
|
+
this.exitHandlers.push(handler);
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
*
|
|
235
|
+
*/
|
|
236
|
+
once(event, handler) {
|
|
237
|
+
const wrappedHandler = data => {
|
|
238
|
+
handler(data);
|
|
239
|
+
this.removeListener(event, wrappedHandler);
|
|
240
|
+
};
|
|
241
|
+
this.on(event, wrappedHandler);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
*
|
|
246
|
+
*/
|
|
247
|
+
removeListener(event, handler) {
|
|
248
|
+
switch (event) {
|
|
249
|
+
case 'message':
|
|
250
|
+
this.messageHandlers = this.messageHandlers.filter(h => h !== handler);
|
|
251
|
+
break;
|
|
252
|
+
case 'error':
|
|
253
|
+
this.errorHandlers = this.errorHandlers.filter(h => h !== handler);
|
|
254
|
+
break;
|
|
255
|
+
case 'exit':
|
|
256
|
+
this.exitHandlers = this.exitHandlers.filter(h => h !== handler);
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export const Worker = isBrowser
|
|
263
|
+
? BrowserWorker
|
|
264
|
+
: await import('node:worker_threads').then(m => m.Worker);
|
|
265
|
+
|
|
266
|
+
// Mock child_process.execSync - browser-compatible
|
|
267
|
+
/**
|
|
268
|
+
*
|
|
269
|
+
*/
|
|
270
|
+
export async function execSync(command, options = {}) {
|
|
271
|
+
if (isBrowser) {
|
|
272
|
+
console.warn(`[Browser] execSync not available in browser: ${command}`);
|
|
273
|
+
return ''; // Return empty string as mock
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const { execSync: nodeExecSync } = await import('child_process');
|
|
277
|
+
const result = nodeExecSync(command, { encoding: 'utf8', ...options });
|
|
278
|
+
// Ensure we return a string, not a Buffer
|
|
279
|
+
return typeof result === 'string' ? result : result.toString('utf8');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Hash utilities - use Web Crypto API in browser
|
|
283
|
+
/**
|
|
284
|
+
*
|
|
285
|
+
*/
|
|
286
|
+
export class BrowserHash {
|
|
287
|
+
/**
|
|
288
|
+
*
|
|
289
|
+
*/
|
|
290
|
+
constructor(algorithm = 'SHA-256') {
|
|
291
|
+
this.algorithm = algorithm.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
*
|
|
296
|
+
*/
|
|
297
|
+
update(data) {
|
|
298
|
+
this.data = typeof data === 'string' ? new TextEncoder().encode(data) : new Uint8Array(data);
|
|
299
|
+
return this;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
*
|
|
304
|
+
*/
|
|
305
|
+
async digest(encoding = 'hex') {
|
|
306
|
+
const hashBuffer = await crypto.subtle.digest(this.algorithm, this.data);
|
|
307
|
+
const hashArray = new Uint8Array(hashBuffer);
|
|
308
|
+
|
|
309
|
+
if (encoding === 'hex') {
|
|
310
|
+
return Array.from(hashArray)
|
|
311
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
312
|
+
.join('');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return hashArray;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
*
|
|
321
|
+
*/
|
|
322
|
+
export async function createHash(algorithm) {
|
|
323
|
+
if (isBrowser) {
|
|
324
|
+
return new BrowserHash(algorithm);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const { createHash: nodeCreateHash } = await import('node:crypto');
|
|
328
|
+
return nodeCreateHash(algorithm);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Export all shims as a unified object
|
|
332
|
+
export default {
|
|
333
|
+
isBrowser,
|
|
334
|
+
isNode,
|
|
335
|
+
randomUUID,
|
|
336
|
+
path,
|
|
337
|
+
process,
|
|
338
|
+
fs,
|
|
339
|
+
fsPromises,
|
|
340
|
+
Worker: BrowserWorker,
|
|
341
|
+
execSync,
|
|
342
|
+
createHash,
|
|
343
|
+
};
|