@liquidmetal-ai/precip 1.0.0
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/.prettierrc +9 -0
- package/CHANGELOG.md +8 -0
- package/eslint.config.mjs +28 -0
- package/package.json +53 -0
- package/src/engine/agent.ts +478 -0
- package/src/engine/llm-provider.test.ts +275 -0
- package/src/engine/llm-provider.ts +330 -0
- package/src/engine/stream-parser.ts +170 -0
- package/src/index.ts +142 -0
- package/src/mounts/mount-manager.test.ts +516 -0
- package/src/mounts/mount-manager.ts +327 -0
- package/src/mounts/mount-registry.ts +196 -0
- package/src/mounts/zod-to-string.test.ts +154 -0
- package/src/mounts/zod-to-string.ts +213 -0
- package/src/presets/agent-tools.ts +57 -0
- package/src/presets/index.ts +5 -0
- package/src/sandbox/README.md +1321 -0
- package/src/sandbox/bridges/README.md +571 -0
- package/src/sandbox/bridges/actor.test.ts +229 -0
- package/src/sandbox/bridges/actor.ts +195 -0
- package/src/sandbox/bridges/bridge-fixes.test.ts +614 -0
- package/src/sandbox/bridges/bucket.test.ts +300 -0
- package/src/sandbox/bridges/cleanup-reproduction.test.ts +225 -0
- package/src/sandbox/bridges/console-multiple.test.ts +187 -0
- package/src/sandbox/bridges/console.test.ts +157 -0
- package/src/sandbox/bridges/console.ts +122 -0
- package/src/sandbox/bridges/fetch.ts +93 -0
- package/src/sandbox/bridges/index.ts +78 -0
- package/src/sandbox/bridges/readable-stream.ts +323 -0
- package/src/sandbox/bridges/response.test.ts +154 -0
- package/src/sandbox/bridges/response.ts +123 -0
- package/src/sandbox/bridges/review-fixes.test.ts +331 -0
- package/src/sandbox/bridges/search.test.ts +475 -0
- package/src/sandbox/bridges/search.ts +264 -0
- package/src/sandbox/bridges/shared/body-methods.ts +93 -0
- package/src/sandbox/bridges/shared/cleanup.ts +112 -0
- package/src/sandbox/bridges/shared/convert.ts +76 -0
- package/src/sandbox/bridges/shared/headers.ts +181 -0
- package/src/sandbox/bridges/shared/index.ts +36 -0
- package/src/sandbox/bridges/shared/json-helpers.ts +77 -0
- package/src/sandbox/bridges/shared/path-parser.ts +109 -0
- package/src/sandbox/bridges/shared/promise-helper.ts +108 -0
- package/src/sandbox/bridges/shared/registry-setup.ts +84 -0
- package/src/sandbox/bridges/shared/response-object.ts +280 -0
- package/src/sandbox/bridges/shared/result-builder.ts +130 -0
- package/src/sandbox/bridges/shared/scope-helpers.ts +44 -0
- package/src/sandbox/bridges/shared/stream-reader.ts +90 -0
- package/src/sandbox/bridges/storage-bridge.test.ts +893 -0
- package/src/sandbox/bridges/storage.ts +421 -0
- package/src/sandbox/bridges/text-decoder.ts +190 -0
- package/src/sandbox/bridges/text-encoder.ts +102 -0
- package/src/sandbox/bridges/types.ts +39 -0
- package/src/sandbox/bridges/utils.ts +123 -0
- package/src/sandbox/index.ts +6 -0
- package/src/sandbox/quickjs-wasm.d.ts +9 -0
- package/src/sandbox/sandbox.test.ts +191 -0
- package/src/sandbox/sandbox.ts +831 -0
- package/src/sandbox/test-helper.ts +43 -0
- package/src/sandbox/test-mocks.ts +154 -0
- package/src/sandbox/user-stream.test.ts +77 -0
- package/src/skills/frontmatter.test.ts +305 -0
- package/src/skills/frontmatter.ts +200 -0
- package/src/skills/index.ts +9 -0
- package/src/skills/skills-loader.test.ts +237 -0
- package/src/skills/skills-loader.ts +200 -0
- package/src/tools/actor-storage-tools.ts +250 -0
- package/src/tools/code-tools.test.ts +199 -0
- package/src/tools/code-tools.ts +444 -0
- package/src/tools/file-tools.ts +206 -0
- package/src/tools/registry.ts +125 -0
- package/src/tools/script-tools.ts +145 -0
- package/src/tools/smartbucket-tools.ts +203 -0
- package/src/tools/sql-tools.ts +213 -0
- package/src/tools/tool-factory.ts +119 -0
- package/src/types.ts +512 -0
- package/tsconfig.eslint.json +5 -0
- package/tsconfig.json +15 -0
- package/vitest.config.ts +33 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { executeWithAsyncHost } from '../test-helper.js';
|
|
3
|
+
|
|
4
|
+
describe('Console Bridge', () => {
|
|
5
|
+
it('should capture console.log output', async () => {
|
|
6
|
+
const code = `
|
|
7
|
+
console.log('Hello World');
|
|
8
|
+
return 'done'
|
|
9
|
+
`;
|
|
10
|
+
|
|
11
|
+
const result = await executeWithAsyncHost(code, {});
|
|
12
|
+
|
|
13
|
+
expect(result.success).toBe(true);
|
|
14
|
+
expect(result.consoleOutput).toBeDefined();
|
|
15
|
+
expect(result.consoleOutput).toContain('Hello World');
|
|
16
|
+
expect(result.result).toBe('done');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should capture multiple console.log calls', async () => {
|
|
20
|
+
const code = `
|
|
21
|
+
console.log('Line 1');
|
|
22
|
+
console.log('Line 2');
|
|
23
|
+
console.log('Line 3');
|
|
24
|
+
return 'complete'
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const result = await executeWithAsyncHost(code, {});
|
|
28
|
+
|
|
29
|
+
expect(result.success).toBe(true);
|
|
30
|
+
expect(result.consoleOutput).toHaveLength(3);
|
|
31
|
+
expect(result.consoleOutput).toContain('Line 1');
|
|
32
|
+
expect(result.consoleOutput).toContain('Line 2');
|
|
33
|
+
expect(result.consoleOutput).toContain('Line 3');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should handle console.log with multiple arguments', async () => {
|
|
37
|
+
const code = `
|
|
38
|
+
console.log('User:', { name: 'John', age: 30 }, 'status:', 'active');
|
|
39
|
+
return 'done'
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
const result = await executeWithAsyncHost(code, {});
|
|
43
|
+
|
|
44
|
+
expect(result.success).toBe(true);
|
|
45
|
+
expect(result.consoleOutput).toHaveLength(1);
|
|
46
|
+
expect(result.consoleOutput[0]).toContain('User:');
|
|
47
|
+
expect(result.consoleOutput[0]).toContain('name');
|
|
48
|
+
expect(result.consoleOutput[0]).toContain('John');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should capture console.error with prefix', async () => {
|
|
52
|
+
const code = `
|
|
53
|
+
console.error('Something went wrong');
|
|
54
|
+
return 'done'
|
|
55
|
+
`;
|
|
56
|
+
|
|
57
|
+
const result = await executeWithAsyncHost(code, {});
|
|
58
|
+
|
|
59
|
+
expect(result.success).toBe(true);
|
|
60
|
+
expect(result.consoleOutput).toHaveLength(1);
|
|
61
|
+
expect(result.consoleOutput[0]).toContain('[ERROR]');
|
|
62
|
+
expect(result.consoleOutput[0]).toContain('Something went wrong');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should capture console.warn with prefix', async () => {
|
|
66
|
+
const code = `
|
|
67
|
+
console.warn('This is a warning');
|
|
68
|
+
return 'done'
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
const result = await executeWithAsyncHost(code, {});
|
|
72
|
+
|
|
73
|
+
expect(result.success).toBe(true);
|
|
74
|
+
expect(result.consoleOutput).toHaveLength(1);
|
|
75
|
+
expect(result.consoleOutput[0]).toContain('[WARN]');
|
|
76
|
+
expect(result.consoleOutput[0]).toContain('This is a warning');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should handle console.log in async code', async () => {
|
|
80
|
+
const code = `
|
|
81
|
+
console.log('Before async');
|
|
82
|
+
await (async () => {
|
|
83
|
+
// Simulate async operation
|
|
84
|
+
await Promise.resolve();
|
|
85
|
+
})();
|
|
86
|
+
console.log('After async');
|
|
87
|
+
return 'done'
|
|
88
|
+
`;
|
|
89
|
+
|
|
90
|
+
const result = await executeWithAsyncHost(code, {});
|
|
91
|
+
|
|
92
|
+
expect(result.success).toBe(true);
|
|
93
|
+
expect(result.consoleOutput).toHaveLength(2);
|
|
94
|
+
expect(result.consoleOutput).toContain('Before async');
|
|
95
|
+
expect(result.consoleOutput).toContain('After async');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should work without any console output', async () => {
|
|
99
|
+
const code = `
|
|
100
|
+
return 'no console'
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
const result = await executeWithAsyncHost(code, {});
|
|
104
|
+
|
|
105
|
+
expect(result.success).toBe(true);
|
|
106
|
+
// consoleOutput will be an empty array if no logs
|
|
107
|
+
expect(result.consoleOutput || []).toHaveLength(0);
|
|
108
|
+
expect(result.result).toBe('no console');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should handle null and undefined in console.log', async () => {
|
|
112
|
+
const code = `
|
|
113
|
+
console.log(null, undefined);
|
|
114
|
+
return 'done'
|
|
115
|
+
`;
|
|
116
|
+
|
|
117
|
+
const result = await executeWithAsyncHost(code, {});
|
|
118
|
+
|
|
119
|
+
expect(result.success).toBe(true);
|
|
120
|
+
expect(result.consoleOutput).toHaveLength(1);
|
|
121
|
+
expect(result.consoleOutput[0]).toContain('null');
|
|
122
|
+
expect(result.consoleOutput[0]).toContain('undefined');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should handle numbers in console.log', async () => {
|
|
126
|
+
const code = `
|
|
127
|
+
console.log('Count:', 42, 'Ratio:', 3.14);
|
|
128
|
+
return 'done'
|
|
129
|
+
`;
|
|
130
|
+
|
|
131
|
+
const result = await executeWithAsyncHost(code, {});
|
|
132
|
+
|
|
133
|
+
expect(result.success).toBe(true);
|
|
134
|
+
expect(result.consoleOutput).toHaveLength(1);
|
|
135
|
+
expect(result.consoleOutput[0]).toContain('Count:');
|
|
136
|
+
expect(result.consoleOutput[0]).toContain('42');
|
|
137
|
+
expect(result.consoleOutput[0]).toContain('3.14');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should handle arrays in console.log', async () => {
|
|
141
|
+
const code = `
|
|
142
|
+
console.log('Items:', [1, 2, 3, 4, 5]);
|
|
143
|
+
return 'done'
|
|
144
|
+
`;
|
|
145
|
+
|
|
146
|
+
const result = await executeWithAsyncHost(code, {});
|
|
147
|
+
|
|
148
|
+
expect(result.success).toBe(true);
|
|
149
|
+
expect(result.consoleOutput).toHaveLength(1);
|
|
150
|
+
expect(result.consoleOutput[0]).toContain('Items:');
|
|
151
|
+
expect(result.consoleOutput[0]).toContain('1');
|
|
152
|
+
expect(result.consoleOutput[0]).toContain('2');
|
|
153
|
+
expect(result.consoleOutput[0]).toContain('3');
|
|
154
|
+
expect(result.consoleOutput[0]).toContain('4');
|
|
155
|
+
expect(result.consoleOutput[0]).toContain('5');
|
|
156
|
+
});
|
|
157
|
+
});
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Console API bridge - Provides console.log for sandbox
|
|
3
|
+
*
|
|
4
|
+
* Captures console.log output and returns it with the sandbox result
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { QuickJSContext, QuickJSHandle } from 'quickjs-emscripten-core';
|
|
8
|
+
import type { BridgeContext } from './types.js';
|
|
9
|
+
|
|
10
|
+
/** Module-level store for console output, keyed by context (avoids monkey-patching) */
|
|
11
|
+
const consoleOutputMap = new WeakMap<QuickJSContext, string[]>();
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Install console API in the sandbox
|
|
15
|
+
*/
|
|
16
|
+
export function installConsole(ctx: BridgeContext): void {
|
|
17
|
+
const { context, logger } = ctx;
|
|
18
|
+
|
|
19
|
+
// Array to store console output
|
|
20
|
+
const output: string[] = [];
|
|
21
|
+
consoleOutputMap.set(context, output);
|
|
22
|
+
|
|
23
|
+
// Helper function to format arguments
|
|
24
|
+
const formatArgs = (...args: any[]): string => {
|
|
25
|
+
return args
|
|
26
|
+
.map(arg => {
|
|
27
|
+
if (arg === null) return 'null';
|
|
28
|
+
if (arg === undefined) return 'undefined';
|
|
29
|
+
if (typeof arg === 'object') {
|
|
30
|
+
try {
|
|
31
|
+
return JSON.stringify(arg, null, 2);
|
|
32
|
+
} catch {
|
|
33
|
+
return '[object Object]';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return String(arg);
|
|
37
|
+
})
|
|
38
|
+
.join(' ');
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
{
|
|
42
|
+
using consoleHandle = context.newObject();
|
|
43
|
+
|
|
44
|
+
// Create log method (also used as info and debug per spec)
|
|
45
|
+
{
|
|
46
|
+
using logMethod = context.newFunction('log', (...args: QuickJSHandle[]) => {
|
|
47
|
+
const jsArgs = args.map(arg => {
|
|
48
|
+
try {
|
|
49
|
+
return context.dump(arg);
|
|
50
|
+
} catch {
|
|
51
|
+
return '[Error converting argument]';
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const outputLine = formatArgs(...jsArgs);
|
|
56
|
+
output.push(outputLine);
|
|
57
|
+
logger?.info?.('[Console]', outputLine);
|
|
58
|
+
|
|
59
|
+
return context.undefined;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
context.setProp(consoleHandle, 'log', logMethod);
|
|
63
|
+
// Alias info and debug to log (per spec, they behave like console.log)
|
|
64
|
+
context.setProp(consoleHandle, 'info', logMethod);
|
|
65
|
+
context.setProp(consoleHandle, 'debug', logMethod);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Create error method
|
|
69
|
+
{
|
|
70
|
+
using errorMethod = context.newFunction('error', (...args: QuickJSHandle[]) => {
|
|
71
|
+
const jsArgs = args.map(arg => {
|
|
72
|
+
try {
|
|
73
|
+
return context.dump(arg);
|
|
74
|
+
} catch {
|
|
75
|
+
return '[Error converting argument]';
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const outputLine = formatArgs(...jsArgs);
|
|
80
|
+
output.push('[ERROR] ' + outputLine);
|
|
81
|
+
logger?.error?.('[Console]', outputLine);
|
|
82
|
+
|
|
83
|
+
return context.undefined;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
context.setProp(consoleHandle, 'error', errorMethod);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Create warn method
|
|
90
|
+
{
|
|
91
|
+
using warnMethod = context.newFunction('warn', (...args: QuickJSHandle[]) => {
|
|
92
|
+
const jsArgs = args.map(arg => {
|
|
93
|
+
try {
|
|
94
|
+
return context.dump(arg);
|
|
95
|
+
} catch {
|
|
96
|
+
return '[Error converting argument]';
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const outputLine = formatArgs(...jsArgs);
|
|
101
|
+
output.push('[WARN] ' + outputLine);
|
|
102
|
+
logger?.warn?.('[Console]', outputLine);
|
|
103
|
+
|
|
104
|
+
return context.undefined;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
context.setProp(consoleHandle, 'warn', warnMethod);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Set console global
|
|
111
|
+
context.setProp(context.global, 'console', consoleHandle);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
logger?.info?.('[Bridge] console installed');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get console output from context
|
|
119
|
+
*/
|
|
120
|
+
export function getConsoleOutput(context: QuickJSContext): string[] {
|
|
121
|
+
return consoleOutputMap.get(context) || [];
|
|
122
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch API bridge - Bridges runtime's native fetch with Response, Headers, and Streams
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { QuickJSHandle } from 'quickjs-emscripten-core';
|
|
6
|
+
import type { BridgeContext } from './types.js';
|
|
7
|
+
import {
|
|
8
|
+
withTrackedPromiseResult,
|
|
9
|
+
createResponseObject,
|
|
10
|
+
setupStreamRegistry
|
|
11
|
+
} from './shared/index.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Install fetch API in the sandbox
|
|
15
|
+
* Bridges to runtime's native fetch
|
|
16
|
+
*/
|
|
17
|
+
export function installFetch(ctx: BridgeContext): void {
|
|
18
|
+
const { context, logger, cleanupHandlers } = ctx;
|
|
19
|
+
|
|
20
|
+
// Track AbortControllers for pending fetches so we can abort them on cleanup
|
|
21
|
+
const pendingAbortControllers = new Set<AbortController>();
|
|
22
|
+
|
|
23
|
+
// Flag to track if we're disposing - helps avoid operations on disposed context
|
|
24
|
+
let isDisposing = false;
|
|
25
|
+
|
|
26
|
+
// Create response object context for the factory (includes cleanup handler)
|
|
27
|
+
const objCtx = setupStreamRegistry(ctx, 'Fetch');
|
|
28
|
+
|
|
29
|
+
// Register cleanup handler to abort pending fetches (before stream cleanup)
|
|
30
|
+
cleanupHandlers.unshift(async () => {
|
|
31
|
+
isDisposing = true;
|
|
32
|
+
|
|
33
|
+
// Abort all pending fetches
|
|
34
|
+
logger?.info?.(`[Fetch] Aborting ${pendingAbortControllers.size} pending fetch(es)`);
|
|
35
|
+
for (const controller of pendingAbortControllers) {
|
|
36
|
+
try {
|
|
37
|
+
controller.abort('Sandbox cleanup');
|
|
38
|
+
} catch {
|
|
39
|
+
// Ignore abort errors
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
pendingAbortControllers.clear();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Install fetch function
|
|
46
|
+
{
|
|
47
|
+
using fetchHandle = context.newFunction('fetch', (...args: QuickJSHandle[]) => {
|
|
48
|
+
const jsArgs = args.map(arg => context.dump(arg));
|
|
49
|
+
|
|
50
|
+
// Create AbortController for this fetch
|
|
51
|
+
const abortController = new AbortController();
|
|
52
|
+
pendingAbortControllers.add(abortController);
|
|
53
|
+
|
|
54
|
+
// Merge user's signal with our abort controller if they provided one
|
|
55
|
+
const userInit = jsArgs[1] || {};
|
|
56
|
+
// Strip signal from userInit — sandbox signals deserialize as plain objects (not real
|
|
57
|
+
// AbortSignal instances), so AbortSignal.any() would throw TypeError. Custom abort
|
|
58
|
+
// signaling from sandbox code is not supported; use fetch timeout instead.
|
|
59
|
+
if (userInit.signal) {
|
|
60
|
+
logger?.warn?.('[Fetch] Stripping unsupported AbortSignal from sandbox fetch init. ' +
|
|
61
|
+
'Custom AbortSignal is not supported in sandbox code.');
|
|
62
|
+
}
|
|
63
|
+
const { signal: _ignoredSignal, ...safeInit } = userInit;
|
|
64
|
+
const fetchInit = {
|
|
65
|
+
...safeInit,
|
|
66
|
+
signal: abortController.signal
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
logger?.info?.('[Fetch] fetch() called');
|
|
70
|
+
|
|
71
|
+
return withTrackedPromiseResult(ctx, async () => {
|
|
72
|
+
// Skip if disposing - let cleanup handle it
|
|
73
|
+
if (isDisposing) {
|
|
74
|
+
throw new Error('Sandbox is disposing');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
// Use runtime's native fetch with abort support
|
|
79
|
+
const response = await (globalThis.fetch as any)(jsArgs[0], fetchInit);
|
|
80
|
+
|
|
81
|
+
// Create QuickJS Response object using the unified factory
|
|
82
|
+
return createResponseObject(ctx, { response }, objCtx);
|
|
83
|
+
} finally {
|
|
84
|
+
pendingAbortControllers.delete(abortController);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
context.setProp(context.global, 'fetch', fetchHandle);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
logger?.info?.('[Bridge] fetch installed');
|
|
93
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QuickJS Bridge System
|
|
3
|
+
*
|
|
4
|
+
* Modular bridge implementations for exposing runtime APIs to QuickJS sandbox.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export * from './types.js';
|
|
8
|
+
export * from './utils.js';
|
|
9
|
+
export * from './shared/index.js';
|
|
10
|
+
export { installTextEncoder } from './text-encoder.js';
|
|
11
|
+
export { installTextDecoder } from './text-decoder.js';
|
|
12
|
+
export { installFetch } from './fetch.js';
|
|
13
|
+
export { installResponse } from './response.js';
|
|
14
|
+
export { installConsole, getConsoleOutput } from './console.js';
|
|
15
|
+
export { createStreamObject, createReaderObject } from './readable-stream.js';
|
|
16
|
+
export { installStorage, type StorageMountInfo } from './storage.js';
|
|
17
|
+
export { installSearch, type SearchMountInfo } from './search.js';
|
|
18
|
+
export { installActor } from './actor.js';
|
|
19
|
+
|
|
20
|
+
import type { BridgeContext, PromiseTracker } from './types.js';
|
|
21
|
+
import type { Logger } from '../../types.js';
|
|
22
|
+
import { installTextEncoder } from './text-encoder.js';
|
|
23
|
+
import { installTextDecoder } from './text-decoder.js';
|
|
24
|
+
import { installFetch } from './fetch.js';
|
|
25
|
+
import { installResponse } from './response.js';
|
|
26
|
+
import { installConsole } from './console.js';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Install all standard bridges (fetch, Response, console, TextEncoder, TextDecoder)
|
|
30
|
+
*/
|
|
31
|
+
export function installAllBridges(ctx: BridgeContext): void {
|
|
32
|
+
installFetch(ctx);
|
|
33
|
+
installResponse(ctx);
|
|
34
|
+
installConsole(ctx);
|
|
35
|
+
installTextEncoder(ctx);
|
|
36
|
+
installTextDecoder(ctx);
|
|
37
|
+
|
|
38
|
+
ctx.logger?.info?.('[Bridge] All bridges installed');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Create a promise tracker for async operations
|
|
43
|
+
*/
|
|
44
|
+
export function createPromiseTracker(): PromiseTracker {
|
|
45
|
+
let notifyNewPromise: () => void;
|
|
46
|
+
const newPromiseSignal = new Promise<void>(resolve => {
|
|
47
|
+
notifyNewPromise = resolve;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
pendingPromises: new Set(),
|
|
52
|
+
notifyNewPromise: notifyNewPromise!,
|
|
53
|
+
newPromiseSignal,
|
|
54
|
+
deferredPromises: new Set()
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Run all cleanup handlers registered by bridges
|
|
60
|
+
*/
|
|
61
|
+
export async function runCleanupHandlers(
|
|
62
|
+
handlers: Array<() => void | Promise<void>>,
|
|
63
|
+
logger?: Logger
|
|
64
|
+
): Promise<void> {
|
|
65
|
+
logger?.info?.(`[Bridge] Running ${handlers.length} cleanup handlers`);
|
|
66
|
+
|
|
67
|
+
for (let i = 0; i < handlers.length; i++) {
|
|
68
|
+
try {
|
|
69
|
+
const handler = handlers[i]!;
|
|
70
|
+
await handler();
|
|
71
|
+
logger?.info?.(`[Bridge] Cleanup handler ${i + 1}/${handlers.length} completed`);
|
|
72
|
+
} catch (e) {
|
|
73
|
+
logger?.warn?.(`[Bridge] Cleanup handler ${i + 1}/${handlers.length} failed: ${e}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
logger?.info?.('[Bridge] All cleanup handlers completed');
|
|
78
|
+
}
|