@rool-dev/svelte 0.8.1 → 0.8.2-dev.d82ea25
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 +137 -83
- package/dist/channel.svelte.d.ts +28 -56
- package/dist/channel.svelte.d.ts.map +1 -1
- package/dist/channel.svelte.js +108 -193
- 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 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- 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 +5 -0
- package/dist/space.svelte.d.ts.map +1 -1
- package/dist/space.svelte.js +10 -1
- package/package.json +3 -3
|
@@ -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,118 +1,86 @@
|
|
|
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
|
-
#currentIds = 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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
};
|
|
26
|
-
this.#channel.on('objectCreated', onObjectCreated);
|
|
27
|
-
this.#unsubscribers.push(() => this.#channel.off('objectCreated', onObjectCreated));
|
|
28
|
-
const onObjectUpdated = ({ objectId, object }) => {
|
|
29
|
-
const wasInCollection = this.#currentIds.has(objectId);
|
|
30
|
-
const nowMatches = this.#matches(object);
|
|
31
|
-
if (wasInCollection && nowMatches) {
|
|
32
|
-
// Update in place (merge to preserve fields from partial optimistic updates)
|
|
33
|
-
const index = this.objects.findIndex((o) => o.id === objectId);
|
|
34
|
-
if (index !== -1) {
|
|
35
|
-
this.objects[index] = { ...this.objects[index], ...object };
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
else if (wasInCollection && !nowMatches) {
|
|
39
|
-
// Check if the mismatch is due to missing keys (partial optimistic update)
|
|
40
|
-
// vs. genuinely changed values that no longer satisfy the filter.
|
|
41
|
-
const where = this.#options.where;
|
|
42
|
-
const isPartialUpdate = where && Object.keys(where).some((key) => !(key in object));
|
|
43
|
-
if (isPartialUpdate) {
|
|
44
|
-
// Partial update — merge onto existing object instead of removing
|
|
45
|
-
const index = this.objects.findIndex((o) => o.id === objectId);
|
|
46
|
-
if (index !== -1) {
|
|
47
|
-
this.objects[index] = { ...this.objects[index], ...object };
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
// Genuine mismatch — remove from collection
|
|
52
|
-
this.objects = this.objects.filter((o) => o.id !== objectId);
|
|
53
|
-
this.#currentIds.delete(objectId);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
else if (!wasInCollection && nowMatches) {
|
|
57
|
-
// Add to collection (re-fetch to respect limit/order)
|
|
58
|
-
this.refresh();
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
this.#channel.on('objectUpdated', onObjectUpdated);
|
|
62
|
-
this.#unsubscribers.push(() => this.#channel.off('objectUpdated', onObjectUpdated));
|
|
63
|
-
const onObjectDeleted = ({ objectId }) => {
|
|
64
|
-
if (this.#currentIds.has(objectId)) {
|
|
65
|
-
this.objects = this.objects.filter((o) => o.id !== objectId);
|
|
66
|
-
this.#currentIds.delete(objectId);
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
this.#channel.on('objectDeleted', onObjectDeleted);
|
|
70
|
-
this.#unsubscribers.push(() => this.#channel.off('objectDeleted', onObjectDeleted));
|
|
71
|
-
// Handle full resets
|
|
72
|
-
const onReset = () => this.refresh();
|
|
73
|
-
this.#channel.on('reset', onReset);
|
|
74
|
-
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);
|
|
75
73
|
}
|
|
76
|
-
/**
|
|
77
|
-
* Check if an object matches the `where` filter.
|
|
78
|
-
*/
|
|
79
|
-
#matches(object) {
|
|
80
|
-
// Collection filter matches by `type` field
|
|
81
|
-
if (this.#options.collection && object.type !== this.#options.collection)
|
|
82
|
-
return false;
|
|
83
|
-
const where = this.#options.where;
|
|
84
|
-
if (!where)
|
|
85
|
-
return true;
|
|
86
|
-
for (const [key, value] of Object.entries(where)) {
|
|
87
|
-
if (object[key] !== value)
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
|
-
return true;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Re-fetch the watched objects from the channel.
|
|
94
|
-
*/
|
|
74
|
+
/** Re-fetch matching objects using the canonical file tree for paths. */
|
|
95
75
|
async refresh() {
|
|
96
76
|
this.loading = true;
|
|
97
77
|
try {
|
|
98
|
-
|
|
99
|
-
where: this.#options.where,
|
|
100
|
-
collection: this.#options.collection,
|
|
101
|
-
limit: this.#options.limit,
|
|
102
|
-
order: this.#options.order,
|
|
103
|
-
ephemeral: true, // Don't pollute interaction history
|
|
104
|
-
};
|
|
105
|
-
const { objects } = await this.#channel.findObjects(findOptions);
|
|
106
|
-
this.objects = objects;
|
|
107
|
-
this.#currentIds = new Set(objects.map((o) => o.id));
|
|
78
|
+
this.objects = await watchObjectsFromTree(this.#channel, this.#fileTree, this.#options);
|
|
108
79
|
}
|
|
109
80
|
finally {
|
|
110
81
|
this.loading = false;
|
|
111
82
|
}
|
|
112
83
|
}
|
|
113
|
-
/**
|
|
114
|
-
* Stop listening for updates and clean up.
|
|
115
|
-
*/
|
|
116
84
|
close() {
|
|
117
85
|
for (const unsub of this.#unsubscribers)
|
|
118
86
|
unsub();
|
|
@@ -124,63 +92,39 @@ class ReactiveWatchImpl {
|
|
|
124
92
|
*/
|
|
125
93
|
class ReactiveObjectImpl {
|
|
126
94
|
#channel;
|
|
127
|
-
#
|
|
95
|
+
#fileTree;
|
|
96
|
+
#path;
|
|
128
97
|
#unsubscribers = [];
|
|
129
98
|
// Reactive state
|
|
130
99
|
data = $state(undefined);
|
|
131
100
|
loading = $state(true);
|
|
132
|
-
constructor(channel,
|
|
101
|
+
constructor(channel, fileTree, path) {
|
|
133
102
|
this.#channel = channel;
|
|
134
|
-
this.#
|
|
103
|
+
this.#fileTree = fileTree;
|
|
104
|
+
this.#path = machinePath(path);
|
|
135
105
|
this.#setup();
|
|
136
106
|
}
|
|
137
107
|
#setup() {
|
|
138
|
-
// Initial fetch
|
|
139
108
|
this.refresh();
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (objectId === this.#objectId) {
|
|
143
|
-
this.data = object;
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
this.#channel.on('objectUpdated', onObjectUpdated);
|
|
147
|
-
this.#unsubscribers.push(() => this.#channel.off('objectUpdated', onObjectUpdated));
|
|
148
|
-
// Listen for creation (in case object didn't exist initially)
|
|
149
|
-
const onObjectCreated = ({ object }) => {
|
|
150
|
-
if (object.id === this.#objectId) {
|
|
151
|
-
this.data = object;
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
this.#channel.on('objectCreated', onObjectCreated);
|
|
155
|
-
this.#unsubscribers.push(() => this.#channel.off('objectCreated', onObjectCreated));
|
|
156
|
-
// Listen for deletion
|
|
157
|
-
const onObjectDeleted = ({ objectId }) => {
|
|
158
|
-
if (objectId === this.#objectId) {
|
|
109
|
+
const unsubscribe = this.#fileTree.subscribe((event) => {
|
|
110
|
+
if (event.deletedPaths.has(this.#path)) {
|
|
159
111
|
this.data = undefined;
|
|
112
|
+
return;
|
|
160
113
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const onReset = () => this.refresh();
|
|
166
|
-
this.#channel.on('reset', onReset);
|
|
167
|
-
this.#unsubscribers.push(() => this.#channel.off('reset', onReset));
|
|
114
|
+
if (eventTouchesObject(event, this.#path))
|
|
115
|
+
void this.refresh();
|
|
116
|
+
});
|
|
117
|
+
this.#unsubscribers.push(unsubscribe);
|
|
168
118
|
}
|
|
169
|
-
/**
|
|
170
|
-
* Re-fetch the object from the channel.
|
|
171
|
-
*/
|
|
172
119
|
async refresh() {
|
|
173
120
|
this.loading = true;
|
|
174
121
|
try {
|
|
175
|
-
this.data = await this.#channel.getObject(this.#
|
|
122
|
+
this.data = await this.#channel.getObject(this.#path);
|
|
176
123
|
}
|
|
177
124
|
finally {
|
|
178
125
|
this.loading = false;
|
|
179
126
|
}
|
|
180
127
|
}
|
|
181
|
-
/**
|
|
182
|
-
* Stop listening for updates and clean up.
|
|
183
|
-
*/
|
|
184
128
|
close() {
|
|
185
129
|
for (const unsub of this.#unsubscribers)
|
|
186
130
|
unsub();
|
|
@@ -190,12 +134,6 @@ class ReactiveObjectImpl {
|
|
|
190
134
|
// ---------------------------------------------------------------------------
|
|
191
135
|
// ReactiveConversationHandle
|
|
192
136
|
// ---------------------------------------------------------------------------
|
|
193
|
-
/**
|
|
194
|
-
* A reactive conversation handle that auto-updates interactions when the
|
|
195
|
-
* conversation changes. Wraps the SDK's ConversationHandle with $state.
|
|
196
|
-
*
|
|
197
|
-
* Call `close()` when done to stop listening for updates.
|
|
198
|
-
*/
|
|
199
137
|
class ReactiveConversationHandleImpl {
|
|
200
138
|
#handle;
|
|
201
139
|
#conversationId;
|
|
@@ -205,9 +143,7 @@ class ReactiveConversationHandleImpl {
|
|
|
205
143
|
constructor(channel, conversationId) {
|
|
206
144
|
this.#conversationId = conversationId;
|
|
207
145
|
this.#handle = channel.conversation(conversationId);
|
|
208
|
-
// Initial load
|
|
209
146
|
this.interactions = this.#handle.getInteractions();
|
|
210
|
-
// Listen for updates to this conversation
|
|
211
147
|
const onConversationUpdated = (event) => {
|
|
212
148
|
if (event.conversationId === this.#conversationId) {
|
|
213
149
|
this.interactions = this.#handle.getInteractions();
|
|
@@ -215,7 +151,6 @@ class ReactiveConversationHandleImpl {
|
|
|
215
151
|
};
|
|
216
152
|
channel.on('conversationUpdated', onConversationUpdated);
|
|
217
153
|
this.#unsubscribers.push(() => channel.off('conversationUpdated', onConversationUpdated));
|
|
218
|
-
// Handle full resets
|
|
219
154
|
const onReset = () => {
|
|
220
155
|
this.interactions = this.#handle.getInteractions();
|
|
221
156
|
};
|
|
@@ -232,21 +167,21 @@ 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
|
-
|
|
170
|
+
putObject(...args) { return this.#handle.putObject(...args); }
|
|
171
|
+
patchObject(...args) { return this.#handle.patchObject(...args); }
|
|
172
|
+
moveObject(...args) { return this.#handle.moveObject(...args); }
|
|
238
173
|
deleteObjects(...args) { return this.#handle.deleteObjects(...args); }
|
|
174
|
+
/** @deprecated Use deleteObjects instead. */
|
|
175
|
+
deletePaths(...args) { return this.#handle.deletePaths(...args); }
|
|
239
176
|
// AI
|
|
240
177
|
prompt(...args) { return this.#handle.prompt(...args); }
|
|
178
|
+
stop() { return this.#handle.stop(); }
|
|
241
179
|
// Schema
|
|
242
180
|
createCollection(...args) { return this.#handle.createCollection(...args); }
|
|
243
181
|
alterCollection(...args) { return this.#handle.alterCollection(...args); }
|
|
244
182
|
dropCollection(...args) { return this.#handle.dropCollection(...args); }
|
|
245
183
|
// Metadata
|
|
246
184
|
setMetadata(...args) { return this.#handle.setMetadata(...args); }
|
|
247
|
-
/**
|
|
248
|
-
* Stop listening for updates and clean up.
|
|
249
|
-
*/
|
|
250
185
|
close() {
|
|
251
186
|
for (const unsub of this.#unsubscribers)
|
|
252
187
|
unsub();
|
|
@@ -259,49 +194,44 @@ class ReactiveConversationHandleImpl {
|
|
|
259
194
|
*/
|
|
260
195
|
class ReactiveChannelImpl {
|
|
261
196
|
#channel;
|
|
197
|
+
#fileTree;
|
|
262
198
|
#unsubscribers = [];
|
|
263
199
|
#closed = false;
|
|
264
200
|
// Reactive state
|
|
265
201
|
interactions = $state([]);
|
|
266
|
-
|
|
202
|
+
objectPaths = $state([]);
|
|
267
203
|
collections = $state([]);
|
|
268
204
|
conversations = $state([]);
|
|
269
|
-
constructor(channel) {
|
|
205
|
+
constructor(channel, fileTree) {
|
|
270
206
|
this.#channel = channel;
|
|
207
|
+
this.#fileTree = fileTree;
|
|
271
208
|
this.interactions = channel.getInteractions();
|
|
272
|
-
this.
|
|
273
|
-
this.collections =
|
|
209
|
+
this.objectPaths = fileTree.objectPaths();
|
|
210
|
+
this.collections = fileTree.collections();
|
|
274
211
|
this.conversations = channel.getConversations();
|
|
275
|
-
// Subscribe to channel updates → refresh interactions
|
|
276
212
|
const onChannelUpdated = () => {
|
|
277
213
|
this.interactions = channel.getInteractions();
|
|
214
|
+
this.conversations = channel.getConversations();
|
|
278
215
|
};
|
|
279
216
|
channel.on('channelUpdated', onChannelUpdated);
|
|
280
217
|
this.#unsubscribers.push(() => channel.off('channelUpdated', onChannelUpdated));
|
|
281
|
-
// Subscribe to conversation updates → refresh conversations
|
|
282
218
|
const onConversationUpdated = () => {
|
|
283
219
|
this.conversations = channel.getConversations();
|
|
284
220
|
};
|
|
285
221
|
channel.on('conversationUpdated', onConversationUpdated);
|
|
286
222
|
this.#unsubscribers.push(() => channel.off('conversationUpdated', onConversationUpdated));
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
this.
|
|
223
|
+
const refreshFromFileTree = () => {
|
|
224
|
+
this.objectPaths = fileTree.objectPaths();
|
|
225
|
+
this.collections = fileTree.collections();
|
|
290
226
|
};
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const onSchemaUpdated = () => {
|
|
297
|
-
this.collections = Object.keys(channel.getSchema());
|
|
298
|
-
};
|
|
299
|
-
channel.on('schemaUpdated', onSchemaUpdated);
|
|
300
|
-
this.#unsubscribers.push(() => channel.off('schemaUpdated', onSchemaUpdated));
|
|
227
|
+
this.#unsubscribers.push(fileTree.subscribe((event) => {
|
|
228
|
+
if (event.reset || eventTouchesObject(event) || [...event.changedPaths, ...event.deletedPaths].some((path) => path === '/space' || path.startsWith('/space/'))) {
|
|
229
|
+
refreshFromFileTree();
|
|
230
|
+
}
|
|
231
|
+
}));
|
|
301
232
|
const onReset = () => {
|
|
302
233
|
this.interactions = channel.getInteractions();
|
|
303
|
-
|
|
304
|
-
this.collections = Object.keys(channel.getSchema());
|
|
234
|
+
refreshFromFileTree();
|
|
305
235
|
this.conversations = channel.getConversations();
|
|
306
236
|
};
|
|
307
237
|
channel.on('reset', onReset);
|
|
@@ -316,10 +246,7 @@ class ReactiveChannelImpl {
|
|
|
316
246
|
get channelName() { return this.#channel.channelName; }
|
|
317
247
|
get isReadOnly() { return this.#channel.isReadOnly; }
|
|
318
248
|
get linkAccess() { return this.#channel.linkAccess; }
|
|
319
|
-
get extensionUrl() { return this.#channel.extensionUrl; }
|
|
320
|
-
get manifest() { return this.#channel.manifest; }
|
|
321
249
|
get isClosed() { return this.#closed; }
|
|
322
|
-
// Proxy all methods
|
|
323
250
|
close() {
|
|
324
251
|
if (this.#closed)
|
|
325
252
|
return;
|
|
@@ -331,14 +258,18 @@ class ReactiveChannelImpl {
|
|
|
331
258
|
}
|
|
332
259
|
// Object operations
|
|
333
260
|
getObject(...args) { return this.#channel.getObject(...args); }
|
|
261
|
+
getObjects(...args) { return this.#channel.getObjects(...args); }
|
|
334
262
|
stat(...args) { return this.#channel.stat(...args); }
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
updateObject(...args) { return this.#channel.updateObject(...args); }
|
|
263
|
+
putObject(...args) { return this.#channel.putObject(...args); }
|
|
264
|
+
patchObject(...args) { return this.#channel.patchObject(...args); }
|
|
265
|
+
moveObject(...args) { return this.#channel.moveObject(...args); }
|
|
339
266
|
deleteObjects(...args) { return this.#channel.deleteObjects(...args); }
|
|
267
|
+
/** @deprecated Use deleteObjects instead. */
|
|
268
|
+
deletePaths(...args) { return this.#channel.deletePaths(...args); }
|
|
340
269
|
// AI
|
|
341
270
|
prompt(...args) { return this.#channel.prompt(...args); }
|
|
271
|
+
stop() { return this.#channel.stop(); }
|
|
272
|
+
stopInteraction(...args) { return this.#channel.stopInteraction(...args); }
|
|
342
273
|
// Undo/redo
|
|
343
274
|
checkpoint(...args) { return this.#channel.checkpoint(...args); }
|
|
344
275
|
canUndo() { return this.#channel.canUndo(); }
|
|
@@ -365,11 +296,6 @@ class ReactiveChannelImpl {
|
|
|
365
296
|
createCollection(...args) { return this.#channel.createCollection(...args); }
|
|
366
297
|
alterCollection(...args) { return this.#channel.alterCollection(...args); }
|
|
367
298
|
dropCollection(...args) { return this.#channel.dropCollection(...args); }
|
|
368
|
-
// Media
|
|
369
|
-
uploadMedia(...args) { return this.#channel.uploadMedia(...args); }
|
|
370
|
-
fetchMedia(...args) { return this.#channel.fetchMedia(...args); }
|
|
371
|
-
deleteMedia(...args) { return this.#channel.deleteMedia(...args); }
|
|
372
|
-
listMedia() { return this.#channel.listMedia(); }
|
|
373
299
|
// Proxied fetch
|
|
374
300
|
fetch(...args) { return this.#channel.fetch(...args); }
|
|
375
301
|
// Channel admin
|
|
@@ -385,31 +311,27 @@ class ReactiveChannelImpl {
|
|
|
385
311
|
off(...args) { return this.#channel.off(...args); }
|
|
386
312
|
// Reactive primitives
|
|
387
313
|
/**
|
|
388
|
-
* Create a reactive object that auto-updates when the object changes.
|
|
389
|
-
* Throws if the channel has been closed.
|
|
314
|
+
* Create a reactive object that auto-updates when the object at this path changes.
|
|
390
315
|
*/
|
|
391
|
-
object(
|
|
316
|
+
object(path) {
|
|
392
317
|
if (this.#closed)
|
|
393
318
|
throw new Error('Cannot create reactive object: channel is closed');
|
|
394
|
-
return new ReactiveObjectImpl(this.#channel,
|
|
319
|
+
return new ReactiveObjectImpl(this.#channel, this.#fileTree, path);
|
|
395
320
|
}
|
|
396
321
|
/**
|
|
397
322
|
* Create a reactive watch that auto-updates when matching objects change.
|
|
398
|
-
* Throws if the channel has been closed.
|
|
399
323
|
*/
|
|
400
324
|
watch(options) {
|
|
401
325
|
if (this.#closed)
|
|
402
326
|
throw new Error('Cannot create reactive watch: channel is closed');
|
|
403
|
-
return new ReactiveWatchImpl(this.#channel, options);
|
|
327
|
+
return new ReactiveWatchImpl(this.#channel, this.#fileTree, options);
|
|
404
328
|
}
|
|
405
329
|
}
|
|
406
|
-
export function wrapChannel(channel) {
|
|
407
|
-
return new ReactiveChannelImpl(channel);
|
|
330
|
+
export function wrapChannel(channel, fileTree) {
|
|
331
|
+
return new ReactiveChannelImpl(channel, fileTree);
|
|
408
332
|
}
|
|
409
333
|
/**
|
|
410
334
|
* A reactive list of channels for a space that auto-updates via SSE events.
|
|
411
|
-
* Can be constructed with a RoolSpace directly or a Promise<RoolSpace>
|
|
412
|
-
* (starts with an empty list and loading=true until the space resolves).
|
|
413
335
|
*/
|
|
414
336
|
class ReactiveChannelListImpl {
|
|
415
337
|
#space = null;
|
|
@@ -431,7 +353,6 @@ class ReactiveChannelListImpl {
|
|
|
431
353
|
this.#space = space;
|
|
432
354
|
this.list = space.channels;
|
|
433
355
|
this.loading = false;
|
|
434
|
-
// Listen for channel lifecycle events on the space
|
|
435
356
|
const onChannelCreated = (channel) => {
|
|
436
357
|
this.list = [...this.list, channel];
|
|
437
358
|
};
|
|
@@ -448,9 +369,6 @@ class ReactiveChannelListImpl {
|
|
|
448
369
|
space.on('channelDeleted', onChannelDeleted);
|
|
449
370
|
this.#unsubscribers.push(() => space.off('channelDeleted', onChannelDeleted));
|
|
450
371
|
}
|
|
451
|
-
/**
|
|
452
|
-
* Refresh the channel list from the space.
|
|
453
|
-
*/
|
|
454
372
|
async refresh() {
|
|
455
373
|
if (!this.#space)
|
|
456
374
|
return;
|
|
@@ -463,9 +381,6 @@ class ReactiveChannelListImpl {
|
|
|
463
381
|
this.loading = false;
|
|
464
382
|
}
|
|
465
383
|
}
|
|
466
|
-
/**
|
|
467
|
-
* Stop listening for updates and clean up.
|
|
468
|
-
*/
|
|
469
384
|
close() {
|
|
470
385
|
for (const unsub of this.#unsubscribers)
|
|
471
386
|
unsub();
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { type RoolSpace, type WebDAVDepth, type WebDAVPropName, type WebDAVResponse, type WebDAVSyncLevel } from '@rool-dev/sdk';
|
|
2
|
+
export type ReactiveFilePath = string;
|
|
3
|
+
export type ReactiveFileRoot = '' | 'space' | 'rool-drive';
|
|
4
|
+
export interface ReactiveFileNode {
|
|
5
|
+
/** Stable node id. Same as `path`. */
|
|
6
|
+
id: ReactiveFilePath;
|
|
7
|
+
/** Machine/WebDAV path (`/`, `/space/...`, `/rool-drive/...`). */
|
|
8
|
+
path: ReactiveFilePath;
|
|
9
|
+
/** Parent path, or `null` for `/`. */
|
|
10
|
+
parent: ReactiveFilePath | null;
|
|
11
|
+
/** Last path segment, decoded by the server when available. */
|
|
12
|
+
name: string;
|
|
13
|
+
/** Which top-level filesystem this node belongs to. `/` has `root: ''`. */
|
|
14
|
+
root: ReactiveFileRoot;
|
|
15
|
+
isCollection: boolean;
|
|
16
|
+
size: number | null;
|
|
17
|
+
contentType: string | null;
|
|
18
|
+
etag: string | null;
|
|
19
|
+
modifiedAt: number | null;
|
|
20
|
+
href: string | null;
|
|
21
|
+
}
|
|
22
|
+
export interface ReactiveFileTreeEvent {
|
|
23
|
+
/** `true` when the tree was replaced from a full snapshot. */
|
|
24
|
+
reset: boolean;
|
|
25
|
+
changedPaths: Set<ReactiveFilePath>;
|
|
26
|
+
deletedPaths: Set<ReactiveFilePath>;
|
|
27
|
+
token: string | null;
|
|
28
|
+
}
|
|
29
|
+
export interface ReactiveFileTreeSyncResult extends ReactiveFileTreeEvent {
|
|
30
|
+
changed: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface ReactiveFileTreeTransport {
|
|
33
|
+
propfind(path: string, options: {
|
|
34
|
+
depth: WebDAVDepth;
|
|
35
|
+
props?: WebDAVPropName[];
|
|
36
|
+
signal?: AbortSignal;
|
|
37
|
+
}): Promise<{
|
|
38
|
+
responses: WebDAVResponse[];
|
|
39
|
+
}>;
|
|
40
|
+
syncCollection(path: string, options: {
|
|
41
|
+
token?: string | null;
|
|
42
|
+
level: WebDAVSyncLevel;
|
|
43
|
+
props?: WebDAVPropName[];
|
|
44
|
+
limit?: number;
|
|
45
|
+
signal?: AbortSignal;
|
|
46
|
+
}): Promise<{
|
|
47
|
+
token: string;
|
|
48
|
+
responses: WebDAVResponse[];
|
|
49
|
+
}>;
|
|
50
|
+
}
|
|
51
|
+
type Listener = (event: ReactiveFileTreeEvent) => void;
|
|
52
|
+
/**
|
|
53
|
+
* Canonical Svelte-owned tree for the whole per-space WebDAV filesystem.
|
|
54
|
+
*
|
|
55
|
+
* It watches the SDK's coarse `filesChanged` / `filesReset` events and
|
|
56
|
+
* reconciles with WebDAV `sync-collection`. Consumers that care about both
|
|
57
|
+
* object files (`/space/...`) and user files (`/rool-drive/...`) should depend
|
|
58
|
+
* on this tree.
|
|
59
|
+
*/
|
|
60
|
+
export declare class ReactiveFileTree {
|
|
61
|
+
#private;
|
|
62
|
+
nodes: ReactiveFileNode[];
|
|
63
|
+
byPath: Record<string, ReactiveFileNode>;
|
|
64
|
+
token: string | null;
|
|
65
|
+
version: number;
|
|
66
|
+
loading: boolean;
|
|
67
|
+
syncing: boolean;
|
|
68
|
+
error: Error | null;
|
|
69
|
+
constructor(space: RoolSpace);
|
|
70
|
+
get isClosed(): boolean;
|
|
71
|
+
get root(): ReactiveFilePath;
|
|
72
|
+
subscribe(listener: Listener): () => void;
|
|
73
|
+
ready(): Promise<void>;
|
|
74
|
+
get(path: string): ReactiveFileNode | undefined;
|
|
75
|
+
has(path: string): boolean;
|
|
76
|
+
childrenOf(path: string): ReactiveFileNode[];
|
|
77
|
+
descendantsOf(path: string): ReactiveFileNode[];
|
|
78
|
+
/** Object file paths sorted by modified time descending. */
|
|
79
|
+
objectPaths(options?: {
|
|
80
|
+
collection?: string;
|
|
81
|
+
order?: 'asc' | 'desc';
|
|
82
|
+
limit?: number;
|
|
83
|
+
}): string[];
|
|
84
|
+
collections(): string[];
|
|
85
|
+
loadSnapshot(): Promise<ReactiveFileTreeSyncResult>;
|
|
86
|
+
sync(): Promise<ReactiveFileTreeSyncResult>;
|
|
87
|
+
refresh(): Promise<ReactiveFileTreeSyncResult>;
|
|
88
|
+
close(): void;
|
|
89
|
+
}
|
|
90
|
+
export {};
|
|
91
|
+
//# sourceMappingURL=file-tree.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-tree.svelte.d.ts","sourceRoot":"","sources":["../src/file-tree.svelte.ts"],"names":[],"mappings":"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"}
|