@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,516 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple unit test for MountManager.getMountsDescription()
|
|
3
|
+
* This isolates the mount description functionality from WASM dependencies
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect } from 'vitest';
|
|
7
|
+
import { MountManager } from './mount-manager.js';
|
|
8
|
+
|
|
9
|
+
// Shared mock factories — cast as `any` to satisfy interface types in tests
|
|
10
|
+
const mockBucketResource = () =>
|
|
11
|
+
({
|
|
12
|
+
get: () => Promise.resolve(null),
|
|
13
|
+
put: () => Promise.resolve(),
|
|
14
|
+
list: () => Promise.resolve({ objects: [], delimitedPrefixes: [], truncated: false }),
|
|
15
|
+
delete: () => Promise.resolve(),
|
|
16
|
+
head: () => Promise.resolve(null)
|
|
17
|
+
}) as any;
|
|
18
|
+
|
|
19
|
+
const mockSmartBucketResource = () =>
|
|
20
|
+
({
|
|
21
|
+
...mockBucketResource(),
|
|
22
|
+
search: () =>
|
|
23
|
+
Promise.resolve({
|
|
24
|
+
results: [],
|
|
25
|
+
pagination: { total: 0, page: 1, pageSize: 10, totalPages: 0, hasMore: false }
|
|
26
|
+
}),
|
|
27
|
+
chunkSearch: () => Promise.resolve({ results: [] }),
|
|
28
|
+
documentChat: () => Promise.resolve({ answer: '' }),
|
|
29
|
+
getPaginatedResults: () =>
|
|
30
|
+
Promise.resolve({
|
|
31
|
+
results: [],
|
|
32
|
+
pagination: { total: 0, page: 1, pageSize: 10, totalPages: 0, hasMore: false }
|
|
33
|
+
})
|
|
34
|
+
}) as any;
|
|
35
|
+
|
|
36
|
+
const mockSqlResource = () =>
|
|
37
|
+
({
|
|
38
|
+
prepare: () => ({
|
|
39
|
+
all: () => Promise.resolve({ success: true, results: [], meta: {} }),
|
|
40
|
+
first: () => Promise.resolve(null),
|
|
41
|
+
run: () => Promise.resolve({ success: true, results: [], meta: {} })
|
|
42
|
+
}),
|
|
43
|
+
exec: () => Promise.resolve({ count: 0, duration: 0 }),
|
|
44
|
+
batch: () => Promise.resolve([])
|
|
45
|
+
}) as any;
|
|
46
|
+
|
|
47
|
+
const mockKvResource = () =>
|
|
48
|
+
({
|
|
49
|
+
get: () => Promise.resolve(null),
|
|
50
|
+
put: () => Promise.resolve(),
|
|
51
|
+
list: () => Promise.resolve({ keys: [], list_complete: true, cacheStatus: null }),
|
|
52
|
+
delete: () => Promise.resolve(),
|
|
53
|
+
getWithMetadata: () => Promise.resolve({ value: null, metadata: null, cacheStatus: null }),
|
|
54
|
+
clear: () => Promise.resolve({ deleted: 0, total: 0 })
|
|
55
|
+
}) as any;
|
|
56
|
+
|
|
57
|
+
const mockActorNamespaceResource = () =>
|
|
58
|
+
({
|
|
59
|
+
idFromName: (_name: string) => ({ toString: () => _name, equals: () => false, name: _name }),
|
|
60
|
+
get: () => ({})
|
|
61
|
+
}) as any;
|
|
62
|
+
|
|
63
|
+
const mockActorStorageResource = () =>
|
|
64
|
+
({
|
|
65
|
+
get: () => Promise.resolve(undefined),
|
|
66
|
+
put: () => Promise.resolve(),
|
|
67
|
+
delete: () => Promise.resolve(false),
|
|
68
|
+
deleteAll: () => Promise.resolve(),
|
|
69
|
+
list: () => Promise.resolve(new Map()),
|
|
70
|
+
getAlarm: () => Promise.resolve(null),
|
|
71
|
+
setAlarm: () => Promise.resolve(),
|
|
72
|
+
deleteAlarm: () => Promise.resolve()
|
|
73
|
+
}) as any;
|
|
74
|
+
|
|
75
|
+
describe('MountManager - getMountsDescription', () => {
|
|
76
|
+
it('should generate empty description for no mounts', () => {
|
|
77
|
+
const manager = new MountManager({});
|
|
78
|
+
const desc = manager.getMountsDescription();
|
|
79
|
+
|
|
80
|
+
expect(desc).toBe('');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should generate description for Buckets', () => {
|
|
84
|
+
const manager = new MountManager({
|
|
85
|
+
'user-data': {
|
|
86
|
+
type: 'bucket',
|
|
87
|
+
resource: mockBucketResource()
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const desc = manager.getMountsDescription();
|
|
92
|
+
|
|
93
|
+
expect(desc).toContain('AVAILABLE STORAGE');
|
|
94
|
+
expect(desc).toContain('## File Storage');
|
|
95
|
+
expect(desc).toContain('/user-data/');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should generate description for SmartBuckets', () => {
|
|
99
|
+
const manager = new MountManager({
|
|
100
|
+
knowledge: {
|
|
101
|
+
type: 'smartbucket',
|
|
102
|
+
resource: mockSmartBucketResource()
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const desc = manager.getMountsDescription();
|
|
107
|
+
|
|
108
|
+
expect(desc).toContain('AVAILABLE STORAGE');
|
|
109
|
+
expect(desc).toContain('## File Storage');
|
|
110
|
+
expect(desc).toContain('/knowledge/');
|
|
111
|
+
expect(desc).toContain('[searchable]');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should generate description for SqlDatabase', () => {
|
|
115
|
+
const manager = new MountManager({
|
|
116
|
+
analytics: {
|
|
117
|
+
type: 'database',
|
|
118
|
+
resource: mockSqlResource()
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const desc = manager.getMountsDescription();
|
|
123
|
+
|
|
124
|
+
expect(desc).toContain('AVAILABLE STORAGE');
|
|
125
|
+
expect(desc).toContain('## Databases');
|
|
126
|
+
expect(desc).toContain('/analytics');
|
|
127
|
+
expect(desc).toContain('query()');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should generate description for KvCache', () => {
|
|
131
|
+
const manager = new MountManager({
|
|
132
|
+
cache: {
|
|
133
|
+
type: 'kv',
|
|
134
|
+
resource: mockKvResource()
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const desc = manager.getMountsDescription();
|
|
139
|
+
|
|
140
|
+
expect(desc).toContain('AVAILABLE STORAGE');
|
|
141
|
+
expect(desc).toContain('## Key-Value Cache');
|
|
142
|
+
expect(desc).toContain('/cache/<key>');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should generate description for mixed mount types including SmartBuckets', () => {
|
|
146
|
+
const manager = new MountManager({
|
|
147
|
+
files: {
|
|
148
|
+
type: 'bucket',
|
|
149
|
+
resource: mockBucketResource()
|
|
150
|
+
},
|
|
151
|
+
knowledge: {
|
|
152
|
+
type: 'smartbucket',
|
|
153
|
+
resource: mockSmartBucketResource()
|
|
154
|
+
},
|
|
155
|
+
db: {
|
|
156
|
+
type: 'database',
|
|
157
|
+
resource: mockSqlResource()
|
|
158
|
+
},
|
|
159
|
+
kv: {
|
|
160
|
+
type: 'kv',
|
|
161
|
+
resource: mockKvResource()
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const desc = manager.getMountsDescription();
|
|
166
|
+
|
|
167
|
+
expect(desc).toContain('## File Storage');
|
|
168
|
+
expect(desc).toContain('## Databases');
|
|
169
|
+
expect(desc).toContain('## Key-Value Cache');
|
|
170
|
+
expect(desc).toContain('/files/');
|
|
171
|
+
expect(desc).toContain('/knowledge/');
|
|
172
|
+
expect(desc).toContain('/db');
|
|
173
|
+
expect(desc).toContain('/kv/<key>');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should include correct mount references', () => {
|
|
177
|
+
const manager = new MountManager({
|
|
178
|
+
bucket1: {
|
|
179
|
+
type: 'bucket',
|
|
180
|
+
resource: mockBucketResource()
|
|
181
|
+
},
|
|
182
|
+
db1: {
|
|
183
|
+
type: 'database',
|
|
184
|
+
resource: mockSqlResource()
|
|
185
|
+
},
|
|
186
|
+
kv1: {
|
|
187
|
+
type: 'kv',
|
|
188
|
+
resource: mockKvResource()
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const desc = manager.getMountsDescription();
|
|
193
|
+
|
|
194
|
+
expect(desc).toContain('/bucket1/');
|
|
195
|
+
expect(desc).toContain('/db1');
|
|
196
|
+
expect(desc).toContain('/kv1/<key>');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should detect SmartBucket type correctly', () => {
|
|
200
|
+
const manager = new MountManager({
|
|
201
|
+
smartbucket: {
|
|
202
|
+
type: 'smartbucket',
|
|
203
|
+
resource: mockSmartBucketResource()
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const mount = manager.getMount('smartbucket');
|
|
208
|
+
expect(mount?.type).toBe('smartbucket');
|
|
209
|
+
|
|
210
|
+
const smartbucketMounts = manager.getMountsByType('smartbucket');
|
|
211
|
+
expect(smartbucketMounts.length).toBe(1);
|
|
212
|
+
expect(smartbucketMounts[0].name).toBe('smartbucket');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should format description with proper sections and spacing', () => {
|
|
216
|
+
const manager = new MountManager({
|
|
217
|
+
data: {
|
|
218
|
+
type: 'bucket',
|
|
219
|
+
resource: mockBucketResource()
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const desc = manager.getMountsDescription();
|
|
224
|
+
|
|
225
|
+
expect(desc).toMatch(/AVAILABLE STORAGE:/);
|
|
226
|
+
expect(desc).toContain('/data/');
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('should mark SmartBuckets as searchable', () => {
|
|
230
|
+
const manager = new MountManager({
|
|
231
|
+
knowledge: {
|
|
232
|
+
type: 'smartbucket',
|
|
233
|
+
resource: mockSmartBucketResource()
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const desc = manager.getMountsDescription();
|
|
238
|
+
|
|
239
|
+
expect(desc).toContain('[searchable]');
|
|
240
|
+
expect(desc).toContain('/knowledge/');
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
describe('MountManager - Mount Configs', () => {
|
|
245
|
+
it('should accept mount with explicit type', () => {
|
|
246
|
+
const manager = new MountManager({
|
|
247
|
+
data: {
|
|
248
|
+
type: 'bucket',
|
|
249
|
+
resource: mockBucketResource()
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const mount = manager.getMount('data');
|
|
254
|
+
expect(mount?.type).toBe('bucket');
|
|
255
|
+
expect(mount?.name).toBe('data');
|
|
256
|
+
expect(mount?.description).toBeUndefined();
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('should accept mount with description and mode', () => {
|
|
260
|
+
const manager = new MountManager({
|
|
261
|
+
knowledge: {
|
|
262
|
+
type: 'bucket',
|
|
263
|
+
resource: mockBucketResource(),
|
|
264
|
+
description: 'Product documentation and API references',
|
|
265
|
+
mode: 'rw'
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
const mount = manager.getMount('knowledge');
|
|
270
|
+
expect(mount?.type).toBe('bucket');
|
|
271
|
+
expect(mount?.name).toBe('knowledge');
|
|
272
|
+
expect(mount?.description).toBe('Product documentation and API references');
|
|
273
|
+
expect(mount?.mode).toBe('rw');
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('should include description and mode in getMountsDescription output', () => {
|
|
277
|
+
const manager = new MountManager({
|
|
278
|
+
knowledge: {
|
|
279
|
+
type: 'bucket',
|
|
280
|
+
resource: mockBucketResource(),
|
|
281
|
+
description: 'Product documentation and API references',
|
|
282
|
+
mode: 'rw'
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
const desc = manager.getMountsDescription();
|
|
287
|
+
expect(desc).toContain('Product documentation and API references');
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
describe('MountManager - Actor Mounts', () => {
|
|
292
|
+
it('should accept actor mount config', async () => {
|
|
293
|
+
const { z } = await import('zod');
|
|
294
|
+
|
|
295
|
+
const manager = new MountManager({
|
|
296
|
+
session: {
|
|
297
|
+
type: 'actor',
|
|
298
|
+
resource: mockActorNamespaceResource(),
|
|
299
|
+
description: 'Per-user session state',
|
|
300
|
+
methods: {
|
|
301
|
+
getState: {
|
|
302
|
+
description: 'Get current session state',
|
|
303
|
+
params: z.tuple([]),
|
|
304
|
+
returns: z.object({ loggedIn: z.boolean() })
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
const mount = manager.getMount('session');
|
|
311
|
+
expect(mount?.type).toBe('actor');
|
|
312
|
+
expect(mount?.name).toBe('session');
|
|
313
|
+
expect(mount?.description).toBe('Per-user session state');
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('should return actor mounts via getActorMounts', async () => {
|
|
317
|
+
const { z } = await import('zod');
|
|
318
|
+
|
|
319
|
+
const manager = new MountManager({
|
|
320
|
+
session: {
|
|
321
|
+
type: 'actor',
|
|
322
|
+
resource: mockActorNamespaceResource(),
|
|
323
|
+
description: 'Per-user session state',
|
|
324
|
+
methods: {
|
|
325
|
+
getState: {
|
|
326
|
+
description: 'Get current session state',
|
|
327
|
+
params: z.tuple([]),
|
|
328
|
+
returns: z.object({ loggedIn: z.boolean() })
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
data: {
|
|
333
|
+
type: 'bucket',
|
|
334
|
+
resource: mockBucketResource()
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
const actorMounts = manager.getActorMounts();
|
|
339
|
+
expect(actorMounts.size).toBe(1);
|
|
340
|
+
expect(actorMounts.has('session')).toBe(true);
|
|
341
|
+
|
|
342
|
+
const sessionMount = actorMounts.get('session');
|
|
343
|
+
expect(sessionMount?.methods).toBeDefined();
|
|
344
|
+
expect(sessionMount?.methods.getState).toBeDefined();
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('should generate actor documentation in getMountsDescription', async () => {
|
|
348
|
+
const { z } = await import('zod');
|
|
349
|
+
|
|
350
|
+
const manager = new MountManager({
|
|
351
|
+
session: {
|
|
352
|
+
type: 'actor',
|
|
353
|
+
resource: mockActorNamespaceResource(),
|
|
354
|
+
description: 'Per-user session state',
|
|
355
|
+
methods: {
|
|
356
|
+
getState: {
|
|
357
|
+
description: 'Get current session state',
|
|
358
|
+
params: z.tuple([]),
|
|
359
|
+
returns: z.object({ loggedIn: z.boolean() })
|
|
360
|
+
},
|
|
361
|
+
updatePrefs: {
|
|
362
|
+
description: 'Update user preferences',
|
|
363
|
+
params: z.tuple([z.object({ theme: z.enum(['light', 'dark']).optional() }).describe('prefs')]),
|
|
364
|
+
returns: z.object({ success: z.boolean() })
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
const desc = manager.getMountsDescription();
|
|
371
|
+
|
|
372
|
+
expect(desc).toContain('## Actors');
|
|
373
|
+
expect(desc).toContain('### /session');
|
|
374
|
+
expect(desc).toContain('Per-user session state');
|
|
375
|
+
expect(desc).toContain('getState');
|
|
376
|
+
expect(desc).toContain('Get current session state');
|
|
377
|
+
expect(desc).toContain('updatePrefs');
|
|
378
|
+
expect(desc).toContain('Update user preferences');
|
|
379
|
+
expect(desc).toContain('actor("/session"');
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
describe('MountManager - Actor Storage Mounts', () => {
|
|
384
|
+
it('should accept actor-storage mount config', () => {
|
|
385
|
+
const manager = new MountManager({
|
|
386
|
+
state: {
|
|
387
|
+
type: 'actor-storage',
|
|
388
|
+
resource: mockActorStorageResource(),
|
|
389
|
+
description: 'Actor local state',
|
|
390
|
+
mode: 'rw'
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
const mount = manager.getMount('state');
|
|
395
|
+
expect(mount?.type).toBe('actor-storage');
|
|
396
|
+
expect(mount?.name).toBe('state');
|
|
397
|
+
expect(mount?.description).toBe('Actor local state');
|
|
398
|
+
expect(mount?.mode).toBe('rw');
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it('should generate actor-storage description in getMountsDescription', () => {
|
|
402
|
+
const manager = new MountManager({
|
|
403
|
+
state: {
|
|
404
|
+
type: 'actor-storage',
|
|
405
|
+
resource: mockActorStorageResource(),
|
|
406
|
+
description: 'Session state',
|
|
407
|
+
mode: 'rw'
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
const desc = manager.getMountsDescription();
|
|
412
|
+
|
|
413
|
+
expect(desc).toContain('## Actor State');
|
|
414
|
+
expect(desc).toContain('Strongly consistent');
|
|
415
|
+
expect(desc).toContain('/state/<key>');
|
|
416
|
+
expect(desc).toContain('Session state');
|
|
417
|
+
expect(desc).toContain('read-write');
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('should generate KV cache description with semantic information', () => {
|
|
421
|
+
const manager = new MountManager({
|
|
422
|
+
cache: {
|
|
423
|
+
type: 'kv',
|
|
424
|
+
resource: mockKvResource(),
|
|
425
|
+
description: 'Session cache',
|
|
426
|
+
mode: 'rw'
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
const desc = manager.getMountsDescription();
|
|
431
|
+
|
|
432
|
+
expect(desc).toContain('## Key-Value Cache');
|
|
433
|
+
expect(desc).toContain('Ephemeral');
|
|
434
|
+
expect(desc).toContain('expiration');
|
|
435
|
+
expect(desc).toContain('/cache/<key>');
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
it('should generate file storage description with semantic information', () => {
|
|
439
|
+
const manager = new MountManager({
|
|
440
|
+
uploads: {
|
|
441
|
+
type: 'bucket',
|
|
442
|
+
resource: mockBucketResource(),
|
|
443
|
+
description: 'User files',
|
|
444
|
+
mode: 'rw'
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
const desc = manager.getMountsDescription();
|
|
449
|
+
|
|
450
|
+
expect(desc).toContain('## File Storage');
|
|
451
|
+
expect(desc).toContain('Durable object storage');
|
|
452
|
+
expect(desc).toContain('contentType');
|
|
453
|
+
expect(desc).toContain('/uploads/');
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('should include actor-storage in mixed mount descriptions', () => {
|
|
457
|
+
const manager = new MountManager({
|
|
458
|
+
files: {
|
|
459
|
+
type: 'bucket',
|
|
460
|
+
resource: mockBucketResource()
|
|
461
|
+
},
|
|
462
|
+
cache: {
|
|
463
|
+
type: 'kv',
|
|
464
|
+
resource: mockKvResource()
|
|
465
|
+
},
|
|
466
|
+
state: {
|
|
467
|
+
type: 'actor-storage',
|
|
468
|
+
resource: mockActorStorageResource(),
|
|
469
|
+
mode: 'rw'
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
const desc = manager.getMountsDescription();
|
|
474
|
+
|
|
475
|
+
expect(desc).toContain('## File Storage');
|
|
476
|
+
expect(desc).toContain('## Key-Value Cache');
|
|
477
|
+
expect(desc).toContain('## Actor State');
|
|
478
|
+
expect(desc).toContain('/files/');
|
|
479
|
+
expect(desc).toContain('/cache/<key>');
|
|
480
|
+
expect(desc).toContain('/state/<key>');
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
describe('MountManager - Path Parsing', () => {
|
|
485
|
+
it('should parse bucket path correctly', () => {
|
|
486
|
+
const manager = new MountManager({
|
|
487
|
+
data: {
|
|
488
|
+
type: 'bucket',
|
|
489
|
+
resource: mockBucketResource()
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
const parsed = manager.parsePath('/data/path/to/file.txt');
|
|
494
|
+
expect(parsed.mountName).toBe('data');
|
|
495
|
+
expect(parsed.path).toBe('path/to/file.txt');
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it('should parse path without trailing path', () => {
|
|
499
|
+
const manager = new MountManager({
|
|
500
|
+
data: {
|
|
501
|
+
type: 'bucket',
|
|
502
|
+
resource: mockBucketResource()
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
const parsed = manager.parsePath('/data');
|
|
507
|
+
expect(parsed.mountName).toBe('data');
|
|
508
|
+
expect(parsed.path).toBe('');
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it('should reject paths without leading slash', () => {
|
|
512
|
+
const manager = new MountManager({});
|
|
513
|
+
|
|
514
|
+
expect(() => manager.parsePath('data/file.txt')).toThrow('Paths must start with /');
|
|
515
|
+
});
|
|
516
|
+
});
|