@rool-dev/svelte 0.10.1 → 0.10.2-dev.0bf8edb
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 +24 -19
- package/dist/channel.svelte.d.ts +20 -27
- package/dist/channel.svelte.d.ts.map +1 -1
- package/dist/channel.svelte.js +103 -171
- package/dist/file-tree.svelte.d.ts +91 -0
- package/dist/file-tree.svelte.d.ts.map +1 -0
- package/dist/file-tree.svelte.js +399 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/rool.svelte.d.ts +22 -22
- package/dist/rool.svelte.d.ts.map +1 -1
- package/dist/rool.svelte.js +33 -39
- package/dist/space.svelte.d.ts +3 -2
- package/dist/space.svelte.d.ts.map +1 -1
- package/dist/space.svelte.js +7 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Svelte 5 bindings for Rool Spaces. Adds reactive state to the SDK using `$state` runes.
|
|
4
4
|
|
|
5
|
-
> **Building a new Rool extension?** Start with [`@rool-dev/extension`](/extension/) — it includes a reactive channel and handles hosting for you. This package is for integrating Rool into an existing Svelte application that manages its own auth, routing, and build setup.
|
|
6
|
-
|
|
7
5
|
**Requires Svelte 5.** For core concepts (objects, references, AI placeholders, undo/redo), see the [SDK documentation](../sdk/README.md).
|
|
8
6
|
|
|
9
7
|
## Installation
|
|
@@ -55,9 +53,10 @@ The Svelte wrapper adds reactive state on top of the SDK:
|
|
|
55
53
|
| `rool.spacesError` | Error from loading spaces |
|
|
56
54
|
| `rool.connectionState` | SSE connection state |
|
|
57
55
|
| `rool.userStorage` | User storage (cross-device preferences) |
|
|
56
|
+
| `space.fileTree` | Canonical reactive WebDAV tree for `/`, including `/space` objects and `/rool-drive` user files |
|
|
58
57
|
| `channel.interactions` | Channel interactions (auto-updates) |
|
|
59
|
-
| `channel.objectLocations` | All object locations
|
|
60
|
-
| `channel.collections` | Collection
|
|
58
|
+
| `channel.objectLocations` | All object locations derived from `space.fileTree` |
|
|
59
|
+
| `channel.collections` | Collection directories derived from `space.fileTree` |
|
|
61
60
|
| `channel.conversations` | Conversations in this channel (auto-updates on create/delete/rename) |
|
|
62
61
|
| `thread.interactions` | Interactions for a specific conversation (auto-updates) |
|
|
63
62
|
| `watch.objects` | Objects matching a filter (auto-updates) |
|
|
@@ -65,6 +64,22 @@ The Svelte wrapper adds reactive state on top of the SDK:
|
|
|
65
64
|
|
|
66
65
|
Everything else passes through to the SDK directly. See the [SDK documentation](../sdk/README.md) for full API details.
|
|
67
66
|
|
|
67
|
+
### Reactive File Tree
|
|
68
|
+
|
|
69
|
+
Every `ReactiveSpace` owns a canonical reactive WebDAV tree. It is kept current with server `filesChanged`/`filesReset` events and WebDAV `sync-collection`, so it covers both object files and user files without polling.
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
const space = await rool.openSpace(spaceId);
|
|
73
|
+
|
|
74
|
+
space.fileTree.nodes; // ReactiveFileNode[]
|
|
75
|
+
space.fileTree.byPath['/space']; // lookup by machine/WebDAV path
|
|
76
|
+
space.fileTree.childrenOf('/'); // /space and /rool-drive
|
|
77
|
+
space.fileTree.childrenOf('/rool-drive');
|
|
78
|
+
space.fileTree.objectLocations(); // object locations from /space/**/*.json
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Use this tree when UI needs to react to both files and objects. Object helpers like `channel.object()`, `channel.watch()`, `channel.objectLocations`, and `channel.collections` are backed by this tree.
|
|
82
|
+
|
|
68
83
|
## API
|
|
69
84
|
|
|
70
85
|
### Lifecycle
|
|
@@ -161,7 +176,7 @@ space.close();
|
|
|
161
176
|
|
|
162
177
|
### ReactiveChannel
|
|
163
178
|
|
|
164
|
-
`space.openChannel()` returns a `ReactiveChannel` — the SDK's `RoolChannel` with reactive `interactions` and
|
|
179
|
+
`space.openChannel()` returns a `ReactiveChannel` — the SDK's `RoolChannel` with reactive `interactions` and file-tree-backed object helpers:
|
|
165
180
|
|
|
166
181
|
```svelte
|
|
167
182
|
<script>
|
|
@@ -258,12 +273,12 @@ Create auto-updating watches of objects filtered by field values:
|
|
|
258
273
|
{/if}
|
|
259
274
|
```
|
|
260
275
|
|
|
261
|
-
Watches automatically re-fetch when
|
|
276
|
+
Watches automatically re-fetch when matching object files change in `space.fileTree`.
|
|
262
277
|
|
|
263
278
|
**Lifecycle:** Watches are tied to their channel. Closing the channel stops all updates — existing watches will retain their last data but no longer refresh. Calling `channel.watch()` after `close()` throws.
|
|
264
279
|
|
|
265
280
|
```typescript
|
|
266
|
-
// Watch options
|
|
281
|
+
// Watch options
|
|
267
282
|
const articles = channel.watch({
|
|
268
283
|
collection: 'article',
|
|
269
284
|
where: { status: 'published' },
|
|
@@ -376,7 +391,6 @@ await channel.createObject('note', { text: 'Hello' }, { basename: 'welcome' })
|
|
|
376
391
|
await channel.updateObject(location, { data: { text: 'Updated' } })
|
|
377
392
|
await channel.moveObject(from, to)
|
|
378
393
|
await channel.deleteObjects([location])
|
|
379
|
-
await channel.findObjects({ collection: 'note' })
|
|
380
394
|
|
|
381
395
|
// AI
|
|
382
396
|
await channel.prompt('Summarize everything')
|
|
@@ -417,13 +431,10 @@ See the [SDK documentation](../sdk/README.md) for complete API details.
|
|
|
417
431
|
### Utilities
|
|
418
432
|
|
|
419
433
|
```typescript
|
|
420
|
-
import {
|
|
421
|
-
|
|
422
|
-
// 6-character alphanumeric basename
|
|
423
|
-
const basename = generateBasename();
|
|
434
|
+
import { loc, parseLocation, normalizeLocation } from '@rool-dev/svelte';
|
|
424
435
|
|
|
425
436
|
// Build / parse location strings
|
|
426
|
-
const location = loc('article',
|
|
437
|
+
const location = loc('article', 'welcome'); // '/space/article/welcome.json'
|
|
427
438
|
const parts = parseLocation(location); // { collection, basename }
|
|
428
439
|
const canonical = normalizeLocation('article/welcome'); // '/space/article/welcome.json'
|
|
429
440
|
```
|
|
@@ -450,7 +461,6 @@ import type {
|
|
|
450
461
|
ConversationInfo,
|
|
451
462
|
CurrentUser,
|
|
452
463
|
Interaction,
|
|
453
|
-
FindObjectsOptions,
|
|
454
464
|
PromptOptions,
|
|
455
465
|
CreateObjectOptions,
|
|
456
466
|
UpdateObjectOptions,
|
|
@@ -461,11 +471,6 @@ import type {
|
|
|
461
471
|
SpaceSchema,
|
|
462
472
|
SpaceMember,
|
|
463
473
|
UserResult,
|
|
464
|
-
PublishedExtensionInfo,
|
|
465
|
-
PublishExtensionOptions,
|
|
466
|
-
ExtensionManifest,
|
|
467
|
-
FindExtensionsOptions,
|
|
468
|
-
|
|
469
474
|
} from '@rool-dev/svelte';
|
|
470
475
|
```
|
|
471
476
|
|
package/dist/channel.svelte.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { RoolChannel, RoolSpace, Interaction, RoolObject, ChannelInfo, ConversationInfo, ConversationHandle } from '@rool-dev/sdk';
|
|
2
|
+
import { ReactiveFileTree } from './file-tree.svelte.js';
|
|
2
3
|
/**
|
|
3
4
|
* Options for creating a reactive watch.
|
|
4
|
-
*
|
|
5
|
+
* Structured object filter for reactive updates.
|
|
5
6
|
*/
|
|
6
7
|
export interface WatchOptions {
|
|
7
8
|
/** Field requirements for exact matching */
|
|
@@ -14,16 +15,14 @@ export interface WatchOptions {
|
|
|
14
15
|
order?: 'asc' | 'desc';
|
|
15
16
|
}
|
|
16
17
|
/**
|
|
17
|
-
* A reactive watch of objects that auto-updates when matching
|
|
18
|
+
* A reactive watch of objects that auto-updates when matching object files change.
|
|
18
19
|
*/
|
|
19
20
|
declare class ReactiveWatchImpl {
|
|
20
21
|
#private;
|
|
21
22
|
objects: RoolObject[];
|
|
22
23
|
loading: boolean;
|
|
23
|
-
constructor(channel: RoolChannel, options: WatchOptions);
|
|
24
|
-
/**
|
|
25
|
-
* Re-fetch the watched objects from the channel.
|
|
26
|
-
*/
|
|
24
|
+
constructor(channel: RoolChannel, fileTree: ReactiveFileTree, options: WatchOptions);
|
|
25
|
+
/** Re-fetch matching objects using the canonical file tree for paths. */
|
|
27
26
|
refresh(): Promise<void>;
|
|
28
27
|
close(): void;
|
|
29
28
|
}
|
|
@@ -35,7 +34,7 @@ declare class ReactiveObjectImpl {
|
|
|
35
34
|
#private;
|
|
36
35
|
data: RoolObject | undefined;
|
|
37
36
|
loading: boolean;
|
|
38
|
-
constructor(channel: RoolChannel,
|
|
37
|
+
constructor(channel: RoolChannel, fileTree: ReactiveFileTree, path: string);
|
|
39
38
|
refresh(): Promise<void>;
|
|
40
39
|
close(): void;
|
|
41
40
|
}
|
|
@@ -52,15 +51,11 @@ declare class ReactiveConversationHandleImpl {
|
|
|
52
51
|
getSystemInstruction(): string | undefined;
|
|
53
52
|
setSystemInstruction(...args: Parameters<ConversationHandle['setSystemInstruction']>): Promise<void>;
|
|
54
53
|
rename(...args: Parameters<ConversationHandle['rename']>): Promise<void>;
|
|
55
|
-
|
|
56
|
-
objects: RoolObject[];
|
|
57
|
-
message: string;
|
|
58
|
-
}>;
|
|
59
|
-
createObject(...args: Parameters<ConversationHandle['createObject']>): Promise<{
|
|
54
|
+
putObject(...args: Parameters<ConversationHandle['putObject']>): Promise<{
|
|
60
55
|
object: RoolObject;
|
|
61
56
|
message: string;
|
|
62
57
|
}>;
|
|
63
|
-
|
|
58
|
+
patchObject(...args: Parameters<ConversationHandle['patchObject']>): Promise<{
|
|
64
59
|
object: RoolObject;
|
|
65
60
|
message: string;
|
|
66
61
|
}>;
|
|
@@ -69,6 +64,8 @@ declare class ReactiveConversationHandleImpl {
|
|
|
69
64
|
message: string;
|
|
70
65
|
}>;
|
|
71
66
|
deleteObjects(...args: Parameters<ConversationHandle['deleteObjects']>): Promise<void>;
|
|
67
|
+
/** @deprecated Use deleteObjects instead. */
|
|
68
|
+
deletePaths(...args: Parameters<ConversationHandle['deletePaths']>): Promise<void>;
|
|
72
69
|
prompt(...args: Parameters<ConversationHandle['prompt']>): Promise<{
|
|
73
70
|
message: string;
|
|
74
71
|
objects: RoolObject[];
|
|
@@ -87,10 +84,10 @@ export type ReactiveConversationHandle = ReactiveConversationHandleImpl;
|
|
|
87
84
|
declare class ReactiveChannelImpl {
|
|
88
85
|
#private;
|
|
89
86
|
interactions: Interaction[];
|
|
90
|
-
|
|
87
|
+
objectPaths: string[];
|
|
91
88
|
collections: string[];
|
|
92
89
|
conversations: ConversationInfo[];
|
|
93
|
-
constructor(channel: RoolChannel);
|
|
90
|
+
constructor(channel: RoolChannel, fileTree: ReactiveFileTree);
|
|
94
91
|
get id(): string;
|
|
95
92
|
get name(): string;
|
|
96
93
|
get role(): import("@rool-dev/sdk").RoolUserRole;
|
|
@@ -99,22 +96,16 @@ declare class ReactiveChannelImpl {
|
|
|
99
96
|
get channelName(): string | null;
|
|
100
97
|
get isReadOnly(): boolean;
|
|
101
98
|
get linkAccess(): import("@rool-dev/sdk").LinkAccess;
|
|
102
|
-
get extensionUrl(): string | null;
|
|
103
|
-
get manifest(): import("@rool-dev/sdk").ExtensionManifest | null;
|
|
104
99
|
get isClosed(): boolean;
|
|
105
100
|
close(): void;
|
|
106
101
|
getObject(...args: Parameters<RoolChannel['getObject']>): Promise<RoolObject | undefined>;
|
|
102
|
+
getObjects(...args: Parameters<RoolChannel['getObjects']>): Promise<import("@rool-dev/sdk").GetObjectsResult>;
|
|
107
103
|
stat(...args: Parameters<RoolChannel['stat']>): import("@rool-dev/sdk").RoolObjectStat | undefined;
|
|
108
|
-
|
|
109
|
-
objects: RoolObject[];
|
|
110
|
-
message: string;
|
|
111
|
-
}>;
|
|
112
|
-
getObjectLocations(...args: Parameters<RoolChannel['getObjectLocations']>): string[];
|
|
113
|
-
createObject(...args: Parameters<RoolChannel['createObject']>): Promise<{
|
|
104
|
+
putObject(...args: Parameters<RoolChannel['putObject']>): Promise<{
|
|
114
105
|
object: RoolObject;
|
|
115
106
|
message: string;
|
|
116
107
|
}>;
|
|
117
|
-
|
|
108
|
+
patchObject(...args: Parameters<RoolChannel['patchObject']>): Promise<{
|
|
118
109
|
object: RoolObject;
|
|
119
110
|
message: string;
|
|
120
111
|
}>;
|
|
@@ -123,6 +114,8 @@ declare class ReactiveChannelImpl {
|
|
|
123
114
|
message: string;
|
|
124
115
|
}>;
|
|
125
116
|
deleteObjects(...args: Parameters<RoolChannel['deleteObjects']>): Promise<void>;
|
|
117
|
+
/** @deprecated Use deleteObjects instead. */
|
|
118
|
+
deletePaths(...args: Parameters<RoolChannel['deletePaths']>): Promise<void>;
|
|
126
119
|
prompt(...args: Parameters<RoolChannel['prompt']>): Promise<{
|
|
127
120
|
message: string;
|
|
128
121
|
objects: RoolObject[];
|
|
@@ -155,15 +148,15 @@ declare class ReactiveChannelImpl {
|
|
|
155
148
|
on(...args: Parameters<RoolChannel['on']>): () => void;
|
|
156
149
|
off(...args: Parameters<RoolChannel['off']>): void;
|
|
157
150
|
/**
|
|
158
|
-
* Create a reactive object that auto-updates when the object at this
|
|
151
|
+
* Create a reactive object that auto-updates when the object at this path changes.
|
|
159
152
|
*/
|
|
160
|
-
object(
|
|
153
|
+
object(path: string): ReactiveObject;
|
|
161
154
|
/**
|
|
162
155
|
* Create a reactive watch that auto-updates when matching objects change.
|
|
163
156
|
*/
|
|
164
157
|
watch(options: WatchOptions): ReactiveWatch;
|
|
165
158
|
}
|
|
166
|
-
export declare function wrapChannel(channel: RoolChannel): ReactiveChannel;
|
|
159
|
+
export declare function wrapChannel(channel: RoolChannel, fileTree: ReactiveFileTree): ReactiveChannel;
|
|
167
160
|
export type ReactiveChannel = ReactiveChannelImpl;
|
|
168
161
|
/**
|
|
169
162
|
* A reactive list of channels for a space that auto-updates via SSE events.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel.svelte.d.ts","sourceRoot":"","sources":["../src/channel.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"channel.svelte.d.ts","sourceRoot":"","sources":["../src/channel.svelte.ts"],"names":[],"mappings":"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"}
|
package/dist/channel.svelte.js
CHANGED
|
@@ -1,122 +1,81 @@
|
|
|
1
|
+
import { isObjectPath, machinePath } from '@rool-dev/sdk';
|
|
2
|
+
function objectCollection(path) {
|
|
3
|
+
if (!isObjectPath(path))
|
|
4
|
+
return undefined;
|
|
5
|
+
return path.split('/')[2];
|
|
6
|
+
}
|
|
7
|
+
function eventTouchesObject(event, objectPath, collection) {
|
|
8
|
+
if (event.reset)
|
|
9
|
+
return true;
|
|
10
|
+
for (const path of [...event.changedPaths, ...event.deletedPaths]) {
|
|
11
|
+
if (objectPath && path === objectPath)
|
|
12
|
+
return true;
|
|
13
|
+
if (!objectPath && isObjectPath(path)) {
|
|
14
|
+
if (!collection || objectCollection(path) === collection)
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
function sameJsonValue(a, b) {
|
|
21
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
22
|
+
}
|
|
23
|
+
async function watchObjectsFromTree(channel, fileTree, options) {
|
|
24
|
+
if (fileTree.loading)
|
|
25
|
+
await fileTree.ready();
|
|
26
|
+
const paths = fileTree.objectPaths({ collection: options.collection, order: options.order });
|
|
27
|
+
const objects = [];
|
|
28
|
+
for (const path of paths) {
|
|
29
|
+
const object = await channel.getObject(path);
|
|
30
|
+
if (!object)
|
|
31
|
+
continue;
|
|
32
|
+
if (options.where) {
|
|
33
|
+
let matches = true;
|
|
34
|
+
for (const [key, value] of Object.entries(options.where)) {
|
|
35
|
+
if (!sameJsonValue(object.body[key], value)) {
|
|
36
|
+
matches = false;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (!matches)
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
objects.push(object);
|
|
44
|
+
if (options.limit !== undefined && objects.length >= options.limit)
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
return objects;
|
|
48
|
+
}
|
|
1
49
|
/**
|
|
2
|
-
* A reactive watch of objects that auto-updates when matching
|
|
50
|
+
* A reactive watch of objects that auto-updates when matching object files change.
|
|
3
51
|
*/
|
|
4
52
|
class ReactiveWatchImpl {
|
|
5
53
|
#channel;
|
|
54
|
+
#fileTree;
|
|
6
55
|
#options;
|
|
7
56
|
#unsubscribers = [];
|
|
8
|
-
#currentLocations = new Set();
|
|
9
57
|
// Reactive state
|
|
10
58
|
objects = $state([]);
|
|
11
59
|
loading = $state(true);
|
|
12
|
-
constructor(channel, options) {
|
|
60
|
+
constructor(channel, fileTree, options) {
|
|
13
61
|
this.#channel = channel;
|
|
62
|
+
this.#fileTree = fileTree;
|
|
14
63
|
this.#options = options;
|
|
15
64
|
this.#setup();
|
|
16
65
|
}
|
|
17
66
|
#setup() {
|
|
18
|
-
// Initial fetch
|
|
19
67
|
this.refresh();
|
|
20
|
-
const
|
|
21
|
-
if (this.#
|
|
22
|
-
this.refresh();
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
this.#channel.on('objectCreated', onObjectCreated);
|
|
26
|
-
this.#unsubscribers.push(() => this.#channel.off('objectCreated', onObjectCreated));
|
|
27
|
-
const onObjectUpdated = ({ location, object }) => {
|
|
28
|
-
const wasInCollection = this.#currentLocations.has(location);
|
|
29
|
-
const nowMatches = this.#matches(object);
|
|
30
|
-
if (wasInCollection && nowMatches) {
|
|
31
|
-
// Update in place (merge to preserve fields from partial optimistic updates)
|
|
32
|
-
const index = this.objects.findIndex((o) => o.location === location);
|
|
33
|
-
if (index !== -1) {
|
|
34
|
-
this.objects[index] = {
|
|
35
|
-
...this.objects[index],
|
|
36
|
-
...object,
|
|
37
|
-
body: { ...this.objects[index].body, ...object.body },
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
else if (wasInCollection && !nowMatches) {
|
|
42
|
-
// Check if the mismatch is due to missing keys in body (partial optimistic update)
|
|
43
|
-
const where = this.#options.where;
|
|
44
|
-
const isPartialUpdate = where && Object.keys(where).some((key) => !(key in object.body));
|
|
45
|
-
if (isPartialUpdate) {
|
|
46
|
-
const index = this.objects.findIndex((o) => o.location === location);
|
|
47
|
-
if (index !== -1) {
|
|
48
|
-
this.objects[index] = {
|
|
49
|
-
...this.objects[index],
|
|
50
|
-
...object,
|
|
51
|
-
body: { ...this.objects[index].body, ...object.body },
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
// Genuine mismatch — remove from collection
|
|
57
|
-
this.objects = this.objects.filter((o) => o.location !== location);
|
|
58
|
-
this.#currentLocations.delete(location);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
else if (!wasInCollection && nowMatches) {
|
|
62
|
-
// Add to collection (re-fetch to respect limit/order)
|
|
63
|
-
this.refresh();
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
this.#channel.on('objectUpdated', onObjectUpdated);
|
|
67
|
-
this.#unsubscribers.push(() => this.#channel.off('objectUpdated', onObjectUpdated));
|
|
68
|
-
const onObjectDeleted = ({ location }) => {
|
|
69
|
-
if (this.#currentLocations.has(location)) {
|
|
70
|
-
this.objects = this.objects.filter((o) => o.location !== location);
|
|
71
|
-
this.#currentLocations.delete(location);
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
this.#channel.on('objectDeleted', onObjectDeleted);
|
|
75
|
-
this.#unsubscribers.push(() => this.#channel.off('objectDeleted', onObjectDeleted));
|
|
76
|
-
const onObjectMoved = ({ from, object }) => {
|
|
77
|
-
const wasInCollection = this.#currentLocations.has(from);
|
|
78
|
-
const nowMatches = this.#matches(object);
|
|
79
|
-
if (wasInCollection || nowMatches) {
|
|
80
|
-
this.refresh();
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
this.#channel.on('objectMoved', onObjectMoved);
|
|
84
|
-
this.#unsubscribers.push(() => this.#channel.off('objectMoved', onObjectMoved));
|
|
85
|
-
const onReset = () => this.refresh();
|
|
86
|
-
this.#channel.on('reset', onReset);
|
|
87
|
-
this.#unsubscribers.push(() => this.#channel.off('reset', onReset));
|
|
68
|
+
const unsubscribe = this.#fileTree.subscribe((event) => {
|
|
69
|
+
if (eventTouchesObject(event, undefined, this.#options.collection))
|
|
70
|
+
void this.refresh();
|
|
71
|
+
});
|
|
72
|
+
this.#unsubscribers.push(unsubscribe);
|
|
88
73
|
}
|
|
89
|
-
/**
|
|
90
|
-
* Check if an object matches the filter (collection + where on body).
|
|
91
|
-
*/
|
|
92
|
-
#matches(object) {
|
|
93
|
-
if (this.#options.collection && object.collection !== this.#options.collection)
|
|
94
|
-
return false;
|
|
95
|
-
const where = this.#options.where;
|
|
96
|
-
if (!where)
|
|
97
|
-
return true;
|
|
98
|
-
for (const [key, value] of Object.entries(where)) {
|
|
99
|
-
if (object.body[key] !== value)
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Re-fetch the watched objects from the channel.
|
|
106
|
-
*/
|
|
74
|
+
/** Re-fetch matching objects using the canonical file tree for paths. */
|
|
107
75
|
async refresh() {
|
|
108
76
|
this.loading = true;
|
|
109
77
|
try {
|
|
110
|
-
|
|
111
|
-
where: this.#options.where,
|
|
112
|
-
collection: this.#options.collection,
|
|
113
|
-
limit: this.#options.limit,
|
|
114
|
-
order: this.#options.order,
|
|
115
|
-
ephemeral: true,
|
|
116
|
-
};
|
|
117
|
-
const { objects } = await this.#channel.findObjects(findOptions);
|
|
118
|
-
this.objects = objects;
|
|
119
|
-
this.#currentLocations = new Set(objects.map((o) => o.location));
|
|
78
|
+
this.objects = await watchObjectsFromTree(this.#channel, this.#fileTree, this.#options);
|
|
120
79
|
}
|
|
121
80
|
finally {
|
|
122
81
|
this.loading = false;
|
|
@@ -133,58 +92,34 @@ class ReactiveWatchImpl {
|
|
|
133
92
|
*/
|
|
134
93
|
class ReactiveObjectImpl {
|
|
135
94
|
#channel;
|
|
136
|
-
#
|
|
95
|
+
#fileTree;
|
|
96
|
+
#path;
|
|
137
97
|
#unsubscribers = [];
|
|
138
98
|
// Reactive state
|
|
139
99
|
data = $state(undefined);
|
|
140
100
|
loading = $state(true);
|
|
141
|
-
constructor(channel,
|
|
101
|
+
constructor(channel, fileTree, path) {
|
|
142
102
|
this.#channel = channel;
|
|
143
|
-
this.#
|
|
103
|
+
this.#fileTree = fileTree;
|
|
104
|
+
this.#path = machinePath(path);
|
|
144
105
|
this.#setup();
|
|
145
106
|
}
|
|
146
107
|
#setup() {
|
|
147
108
|
this.refresh();
|
|
148
|
-
const
|
|
149
|
-
if (
|
|
150
|
-
this.data = object;
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
this.#channel.on('objectUpdated', onObjectUpdated);
|
|
154
|
-
this.#unsubscribers.push(() => this.#channel.off('objectUpdated', onObjectUpdated));
|
|
155
|
-
const onObjectCreated = ({ location, object }) => {
|
|
156
|
-
if (location === this.#location) {
|
|
157
|
-
this.data = object;
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
this.#channel.on('objectCreated', onObjectCreated);
|
|
161
|
-
this.#unsubscribers.push(() => this.#channel.off('objectCreated', onObjectCreated));
|
|
162
|
-
const onObjectDeleted = ({ location }) => {
|
|
163
|
-
if (location === this.#location) {
|
|
164
|
-
this.data = undefined;
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
this.#channel.on('objectDeleted', onObjectDeleted);
|
|
168
|
-
this.#unsubscribers.push(() => this.#channel.off('objectDeleted', onObjectDeleted));
|
|
169
|
-
const onObjectMoved = ({ from, to, object }) => {
|
|
170
|
-
if (from === this.#location) {
|
|
171
|
-
// Object moved away from this location; data is gone.
|
|
109
|
+
const unsubscribe = this.#fileTree.subscribe((event) => {
|
|
110
|
+
if (event.deletedPaths.has(this.#path)) {
|
|
172
111
|
this.data = undefined;
|
|
112
|
+
return;
|
|
173
113
|
}
|
|
174
|
-
|
|
175
|
-
this.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
this.#channel.on('objectMoved', onObjectMoved);
|
|
179
|
-
this.#unsubscribers.push(() => this.#channel.off('objectMoved', onObjectMoved));
|
|
180
|
-
const onReset = () => this.refresh();
|
|
181
|
-
this.#channel.on('reset', onReset);
|
|
182
|
-
this.#unsubscribers.push(() => this.#channel.off('reset', onReset));
|
|
114
|
+
if (eventTouchesObject(event, this.#path))
|
|
115
|
+
void this.refresh();
|
|
116
|
+
});
|
|
117
|
+
this.#unsubscribers.push(unsubscribe);
|
|
183
118
|
}
|
|
184
119
|
async refresh() {
|
|
185
120
|
this.loading = true;
|
|
186
121
|
try {
|
|
187
|
-
this.data = await this.#channel.getObject(this.#
|
|
122
|
+
this.data = await this.#channel.getObject(this.#path);
|
|
188
123
|
}
|
|
189
124
|
finally {
|
|
190
125
|
this.loading = false;
|
|
@@ -232,11 +167,12 @@ class ReactiveConversationHandleImpl {
|
|
|
232
167
|
setSystemInstruction(...args) { return this.#handle.setSystemInstruction(...args); }
|
|
233
168
|
rename(...args) { return this.#handle.rename(...args); }
|
|
234
169
|
// Object operations
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
updateObject(...args) { return this.#handle.updateObject(...args); }
|
|
170
|
+
putObject(...args) { return this.#handle.putObject(...args); }
|
|
171
|
+
patchObject(...args) { return this.#handle.patchObject(...args); }
|
|
238
172
|
moveObject(...args) { return this.#handle.moveObject(...args); }
|
|
239
173
|
deleteObjects(...args) { return this.#handle.deleteObjects(...args); }
|
|
174
|
+
/** @deprecated Use deleteObjects instead. */
|
|
175
|
+
deletePaths(...args) { return this.#handle.deletePaths(...args); }
|
|
240
176
|
// AI
|
|
241
177
|
prompt(...args) { return this.#handle.prompt(...args); }
|
|
242
178
|
// Schema
|
|
@@ -257,21 +193,24 @@ class ReactiveConversationHandleImpl {
|
|
|
257
193
|
*/
|
|
258
194
|
class ReactiveChannelImpl {
|
|
259
195
|
#channel;
|
|
196
|
+
#fileTree;
|
|
260
197
|
#unsubscribers = [];
|
|
261
198
|
#closed = false;
|
|
262
199
|
// Reactive state
|
|
263
200
|
interactions = $state([]);
|
|
264
|
-
|
|
201
|
+
objectPaths = $state([]);
|
|
265
202
|
collections = $state([]);
|
|
266
203
|
conversations = $state([]);
|
|
267
|
-
constructor(channel) {
|
|
204
|
+
constructor(channel, fileTree) {
|
|
268
205
|
this.#channel = channel;
|
|
206
|
+
this.#fileTree = fileTree;
|
|
269
207
|
this.interactions = channel.getInteractions();
|
|
270
|
-
this.
|
|
271
|
-
this.collections =
|
|
208
|
+
this.objectPaths = fileTree.objectPaths();
|
|
209
|
+
this.collections = fileTree.collections();
|
|
272
210
|
this.conversations = channel.getConversations();
|
|
273
211
|
const onChannelUpdated = () => {
|
|
274
212
|
this.interactions = channel.getInteractions();
|
|
213
|
+
this.conversations = channel.getConversations();
|
|
275
214
|
};
|
|
276
215
|
channel.on('channelUpdated', onChannelUpdated);
|
|
277
216
|
this.#unsubscribers.push(() => channel.off('channelUpdated', onChannelUpdated));
|
|
@@ -280,24 +219,18 @@ class ReactiveChannelImpl {
|
|
|
280
219
|
};
|
|
281
220
|
channel.on('conversationUpdated', onConversationUpdated);
|
|
282
221
|
this.#unsubscribers.push(() => channel.off('conversationUpdated', onConversationUpdated));
|
|
283
|
-
const
|
|
284
|
-
this.
|
|
222
|
+
const refreshFromFileTree = () => {
|
|
223
|
+
this.objectPaths = fileTree.objectPaths();
|
|
224
|
+
this.collections = fileTree.collections();
|
|
285
225
|
};
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
this.#unsubscribers.push(() => channel.off('objectMoved', refreshObjectLocations));
|
|
292
|
-
const onSchemaUpdated = () => {
|
|
293
|
-
this.collections = Object.keys(channel.getSchema());
|
|
294
|
-
};
|
|
295
|
-
channel.on('schemaUpdated', onSchemaUpdated);
|
|
296
|
-
this.#unsubscribers.push(() => channel.off('schemaUpdated', onSchemaUpdated));
|
|
226
|
+
this.#unsubscribers.push(fileTree.subscribe((event) => {
|
|
227
|
+
if (event.reset || eventTouchesObject(event) || [...event.changedPaths, ...event.deletedPaths].some((path) => path === '/space' || path.startsWith('/space/'))) {
|
|
228
|
+
refreshFromFileTree();
|
|
229
|
+
}
|
|
230
|
+
}));
|
|
297
231
|
const onReset = () => {
|
|
298
232
|
this.interactions = channel.getInteractions();
|
|
299
|
-
|
|
300
|
-
this.collections = Object.keys(channel.getSchema());
|
|
233
|
+
refreshFromFileTree();
|
|
301
234
|
this.conversations = channel.getConversations();
|
|
302
235
|
};
|
|
303
236
|
channel.on('reset', onReset);
|
|
@@ -312,8 +245,6 @@ class ReactiveChannelImpl {
|
|
|
312
245
|
get channelName() { return this.#channel.channelName; }
|
|
313
246
|
get isReadOnly() { return this.#channel.isReadOnly; }
|
|
314
247
|
get linkAccess() { return this.#channel.linkAccess; }
|
|
315
|
-
get extensionUrl() { return this.#channel.extensionUrl; }
|
|
316
|
-
get manifest() { return this.#channel.manifest; }
|
|
317
248
|
get isClosed() { return this.#closed; }
|
|
318
249
|
close() {
|
|
319
250
|
if (this.#closed)
|
|
@@ -326,13 +257,14 @@ class ReactiveChannelImpl {
|
|
|
326
257
|
}
|
|
327
258
|
// Object operations
|
|
328
259
|
getObject(...args) { return this.#channel.getObject(...args); }
|
|
260
|
+
getObjects(...args) { return this.#channel.getObjects(...args); }
|
|
329
261
|
stat(...args) { return this.#channel.stat(...args); }
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
createObject(...args) { return this.#channel.createObject(...args); }
|
|
333
|
-
updateObject(...args) { return this.#channel.updateObject(...args); }
|
|
262
|
+
putObject(...args) { return this.#channel.putObject(...args); }
|
|
263
|
+
patchObject(...args) { return this.#channel.patchObject(...args); }
|
|
334
264
|
moveObject(...args) { return this.#channel.moveObject(...args); }
|
|
335
265
|
deleteObjects(...args) { return this.#channel.deleteObjects(...args); }
|
|
266
|
+
/** @deprecated Use deleteObjects instead. */
|
|
267
|
+
deletePaths(...args) { return this.#channel.deletePaths(...args); }
|
|
336
268
|
// AI
|
|
337
269
|
prompt(...args) { return this.#channel.prompt(...args); }
|
|
338
270
|
// Undo/redo
|
|
@@ -376,12 +308,12 @@ class ReactiveChannelImpl {
|
|
|
376
308
|
off(...args) { return this.#channel.off(...args); }
|
|
377
309
|
// Reactive primitives
|
|
378
310
|
/**
|
|
379
|
-
* Create a reactive object that auto-updates when the object at this
|
|
311
|
+
* Create a reactive object that auto-updates when the object at this path changes.
|
|
380
312
|
*/
|
|
381
|
-
object(
|
|
313
|
+
object(path) {
|
|
382
314
|
if (this.#closed)
|
|
383
315
|
throw new Error('Cannot create reactive object: channel is closed');
|
|
384
|
-
return new ReactiveObjectImpl(this.#channel,
|
|
316
|
+
return new ReactiveObjectImpl(this.#channel, this.#fileTree, path);
|
|
385
317
|
}
|
|
386
318
|
/**
|
|
387
319
|
* Create a reactive watch that auto-updates when matching objects change.
|
|
@@ -389,11 +321,11 @@ class ReactiveChannelImpl {
|
|
|
389
321
|
watch(options) {
|
|
390
322
|
if (this.#closed)
|
|
391
323
|
throw new Error('Cannot create reactive watch: channel is closed');
|
|
392
|
-
return new ReactiveWatchImpl(this.#channel, options);
|
|
324
|
+
return new ReactiveWatchImpl(this.#channel, this.#fileTree, options);
|
|
393
325
|
}
|
|
394
326
|
}
|
|
395
|
-
export function wrapChannel(channel) {
|
|
396
|
-
return new ReactiveChannelImpl(channel);
|
|
327
|
+
export function wrapChannel(channel, fileTree) {
|
|
328
|
+
return new ReactiveChannelImpl(channel, fileTree);
|
|
397
329
|
}
|
|
398
330
|
/**
|
|
399
331
|
* A reactive list of channels for a space that auto-updates via SSE events.
|