@rool-dev/svelte 0.10.1-dev.cdc8b46 → 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.
@@ -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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"}
@@ -0,0 +1,399 @@
1
+ import { isObjectPath, machinePath, } from '@rool-dev/sdk';
2
+ const ROOT = '/';
3
+ const DEFAULT_PROPS = [
4
+ 'displayname',
5
+ 'getcontentlength',
6
+ 'getcontenttype',
7
+ 'getetag',
8
+ 'getlastmodified',
9
+ 'resourcetype',
10
+ ];
11
+ function normalizePath(path) {
12
+ return machinePath(path);
13
+ }
14
+ function parentPath(path) {
15
+ if (path === ROOT)
16
+ return null;
17
+ const parts = path.split('/').filter(Boolean);
18
+ parts.pop();
19
+ return parts.length === 0 ? ROOT : `/${parts.join('/')}`;
20
+ }
21
+ function leafName(path) {
22
+ if (path === ROOT)
23
+ return 'Space';
24
+ const leaf = path.split('/').filter(Boolean).pop() ?? '';
25
+ try {
26
+ return decodeURIComponent(leaf);
27
+ }
28
+ catch {
29
+ return leaf;
30
+ }
31
+ }
32
+ function rootOf(path) {
33
+ if (path === ROOT)
34
+ return '';
35
+ if (path === '/space' || path.startsWith('/space/'))
36
+ return 'space';
37
+ if (path === '/rool-drive' || path.startsWith('/rool-drive/'))
38
+ return 'rool-drive';
39
+ return '';
40
+ }
41
+ function modifiedAt(props) {
42
+ const raw = props.getlastmodified;
43
+ if (typeof raw !== 'string')
44
+ return null;
45
+ const parsed = Date.parse(raw);
46
+ return Number.isNaN(parsed) ? null : parsed;
47
+ }
48
+ function nodeFromResponse(response) {
49
+ const path = normalizePath(response.path);
50
+ return {
51
+ id: path,
52
+ path,
53
+ parent: parentPath(path),
54
+ name: typeof response.props.displayname === 'string' && response.props.displayname
55
+ ? response.props.displayname
56
+ : leafName(path),
57
+ root: rootOf(path),
58
+ isCollection: response.isCollection,
59
+ size: typeof response.props.getcontentlength === 'number' ? response.props.getcontentlength : null,
60
+ contentType: typeof response.props.getcontenttype === 'string' ? response.props.getcontenttype : null,
61
+ etag: typeof response.props.getetag === 'string' ? response.props.getetag : null,
62
+ modifiedAt: modifiedAt(response.props),
63
+ href: response.href || null,
64
+ };
65
+ }
66
+ function isDeletedResponse(response) {
67
+ if (response.status === 404 || response.status === 410)
68
+ return true;
69
+ return response.propstats.length > 0 && response.propstats.every((p) => p.status === 404 || p.status === 410);
70
+ }
71
+ function sameNode(a, b) {
72
+ return a.id === b.id
73
+ && a.path === b.path
74
+ && a.parent === b.parent
75
+ && a.name === b.name
76
+ && a.root === b.root
77
+ && a.isCollection === b.isCollection
78
+ && a.size === b.size
79
+ && a.contentType === b.contentType
80
+ && a.etag === b.etag
81
+ && a.modifiedAt === b.modifiedAt
82
+ && a.href === b.href;
83
+ }
84
+ function sortNodes(a, b) {
85
+ return Number(b.isCollection) - Number(a.isCollection) || a.name.localeCompare(b.name);
86
+ }
87
+ function emptyEvent(token, reset = false) {
88
+ return { reset, changedPaths: new Set(), deletedPaths: new Set(), token };
89
+ }
90
+ /**
91
+ * Canonical Svelte-owned tree for the whole per-space WebDAV filesystem.
92
+ *
93
+ * It watches the SDK's coarse `filesChanged` / `filesReset` events and
94
+ * reconciles with WebDAV `sync-collection`. Consumers that care about both
95
+ * object files (`/space/...`) and user files (`/rool-drive/...`) should depend
96
+ * on this tree.
97
+ */
98
+ export class ReactiveFileTree {
99
+ #space;
100
+ #nodes = new Map();
101
+ #children = new Map();
102
+ #listeners = new Set();
103
+ #unsubscribers = [];
104
+ #syncing = null;
105
+ #initialLoad = null;
106
+ #syncAgain = false;
107
+ #closed = false;
108
+ nodes = $state([]);
109
+ byPath = $state({});
110
+ token = $state(null);
111
+ version = $state(0);
112
+ loading = $state(true);
113
+ syncing = $state(false);
114
+ error = $state(null);
115
+ constructor(space) {
116
+ this.#space = space;
117
+ this.#setNode(rootNode());
118
+ this.#publishState();
119
+ this.#setupSpaceListeners();
120
+ this.#initialLoad = this.loadSnapshot().then(() => undefined, () => undefined);
121
+ }
122
+ get isClosed() { return this.#closed; }
123
+ get root() { return ROOT; }
124
+ subscribe(listener) {
125
+ this.#listeners.add(listener);
126
+ return () => this.#listeners.delete(listener);
127
+ }
128
+ ready() {
129
+ return this.#initialLoad ?? Promise.resolve();
130
+ }
131
+ get(path) {
132
+ return this.#nodes.get(normalizePath(path));
133
+ }
134
+ has(path) {
135
+ return this.#nodes.has(normalizePath(path));
136
+ }
137
+ childrenOf(path) {
138
+ return (this.#children.get(normalizePath(path)) ?? [])
139
+ .map((child) => this.#nodes.get(child))
140
+ .filter((node) => !!node);
141
+ }
142
+ descendantsOf(path) {
143
+ const root = normalizePath(path);
144
+ return this.nodes.filter((node) => node.path !== root && isDescendant(node.path, root));
145
+ }
146
+ /** Object file paths sorted by modified time descending. */
147
+ objectPaths(options = {}) {
148
+ const paths = this.nodes
149
+ .filter((node) => !node.isCollection && isObjectPath(node.path))
150
+ .filter((node) => !options.collection || safeCollection(node.path) === options.collection)
151
+ .sort((a, b) => (b.modifiedAt ?? 0) - (a.modifiedAt ?? 0))
152
+ .map((node) => node.path);
153
+ if (options.order === 'asc')
154
+ paths.reverse();
155
+ return options.limit === undefined ? paths : paths.slice(0, options.limit);
156
+ }
157
+ collections() {
158
+ return this.childrenOf('/space')
159
+ .filter((node) => node.isCollection)
160
+ .map((node) => node.name)
161
+ .sort((a, b) => a.localeCompare(b));
162
+ }
163
+ async loadSnapshot() {
164
+ if (this.#closed)
165
+ return { changed: false, ...emptyEvent(this.token, true) };
166
+ this.loading = true;
167
+ this.error = null;
168
+ try {
169
+ let snapshot;
170
+ try {
171
+ snapshot = await this.#space.webdav.syncCollection('/', { token: null, level: 'infinite', props: [...DEFAULT_PROPS] });
172
+ }
173
+ catch {
174
+ const [listing, tokenListing] = await Promise.all([
175
+ this.#space.webdav.propfind('/', { depth: 'infinity', props: [...DEFAULT_PROPS] }),
176
+ this.#space.webdav.propfind('/', { depth: '0', props: ['sync-token'] }),
177
+ ]);
178
+ snapshot = {
179
+ token: tokenListing.responses[0]?.props.syncToken ?? null,
180
+ responses: listing.responses,
181
+ };
182
+ }
183
+ this.token = snapshot.token;
184
+ const result = this.#replaceSnapshot(snapshot.responses);
185
+ const event = { reset: true, changedPaths: result.changedPaths, deletedPaths: result.deletedPaths, token: this.token };
186
+ this.#emit(event);
187
+ return { changed: result.changed, ...event };
188
+ }
189
+ catch (error) {
190
+ this.error = error instanceof Error ? error : new Error(String(error));
191
+ throw error;
192
+ }
193
+ finally {
194
+ this.loading = false;
195
+ }
196
+ }
197
+ async sync() {
198
+ if (this.#closed)
199
+ return { changed: false, ...emptyEvent(this.token) };
200
+ if (this.#syncing) {
201
+ this.#syncAgain = true;
202
+ return this.#syncing;
203
+ }
204
+ this.syncing = true;
205
+ this.error = null;
206
+ this.#syncing = this.#syncOnce();
207
+ try {
208
+ const result = await this.#syncing;
209
+ return result;
210
+ }
211
+ finally {
212
+ this.#syncing = null;
213
+ this.syncing = false;
214
+ if (this.#syncAgain && !this.#closed) {
215
+ this.#syncAgain = false;
216
+ void this.sync();
217
+ }
218
+ }
219
+ }
220
+ refresh() {
221
+ return this.loadSnapshot();
222
+ }
223
+ close() {
224
+ if (this.#closed)
225
+ return;
226
+ this.#closed = true;
227
+ for (const unsub of this.#unsubscribers)
228
+ unsub();
229
+ this.#unsubscribers.length = 0;
230
+ this.#listeners.clear();
231
+ }
232
+ #setupSpaceListeners() {
233
+ const onFilesChanged = () => { void this.sync(); };
234
+ const onFilesReset = () => { void this.loadSnapshot(); };
235
+ const onConnectionStateChanged = (state) => {
236
+ if (state === 'connected' && !this.loading)
237
+ void this.sync();
238
+ };
239
+ this.#space.on('filesChanged', onFilesChanged);
240
+ this.#space.on('filesReset', onFilesReset);
241
+ this.#space.on('connectionStateChanged', onConnectionStateChanged);
242
+ this.#unsubscribers.push(() => this.#space.off('filesChanged', onFilesChanged));
243
+ this.#unsubscribers.push(() => this.#space.off('filesReset', onFilesReset));
244
+ this.#unsubscribers.push(() => this.#space.off('connectionStateChanged', onConnectionStateChanged));
245
+ }
246
+ async #syncOnce() {
247
+ try {
248
+ const delta = await this.#space.webdav.syncCollection('/', { token: this.token, level: 'infinite', props: [...DEFAULT_PROPS] });
249
+ this.token = delta.token;
250
+ const result = this.#applyResponses(delta.responses);
251
+ const event = { reset: false, changedPaths: result.changedPaths, deletedPaths: result.deletedPaths, token: this.token };
252
+ if (result.changed || event.deletedPaths.size > 0)
253
+ this.#emit(event);
254
+ return { changed: result.changed, ...event };
255
+ }
256
+ catch {
257
+ return this.loadSnapshot();
258
+ }
259
+ }
260
+ #replaceSnapshot(responses) {
261
+ const next = new Map();
262
+ next.set(ROOT, rootNode());
263
+ for (const response of responses) {
264
+ if (isDeletedResponse(response))
265
+ continue;
266
+ const node = nodeFromResponse(response);
267
+ next.set(node.path, node);
268
+ }
269
+ // Ensure aggregate roots exist even if a server/proxy omits them.
270
+ if (!next.has('/space'))
271
+ next.set('/space', syntheticRootChild('/space', 'space'));
272
+ if (!next.has('/rool-drive'))
273
+ next.set('/rool-drive', syntheticRootChild('/rool-drive', 'rool-drive'));
274
+ const changedPaths = new Set();
275
+ const deletedPaths = new Set();
276
+ for (const [path, node] of next) {
277
+ const old = this.#nodes.get(path);
278
+ if (!old || !sameNode(old, node))
279
+ changedPaths.add(path);
280
+ }
281
+ for (const path of this.#nodes.keys()) {
282
+ if (!next.has(path))
283
+ deletedPaths.add(path);
284
+ }
285
+ this.#nodes = next;
286
+ this.#rebuildChildren();
287
+ const changed = changedPaths.size > 0 || deletedPaths.size > 0;
288
+ if (changed)
289
+ this.#publishState();
290
+ return { changed, changedPaths, deletedPaths };
291
+ }
292
+ #applyResponses(responses) {
293
+ let changed = false;
294
+ const changedPaths = new Set();
295
+ const deletedPaths = new Set();
296
+ for (const response of responses) {
297
+ const path = normalizePath(response.path);
298
+ if (path === ROOT)
299
+ continue;
300
+ if (isDeletedResponse(response)) {
301
+ if (this.#deleteSubtree(path, deletedPaths))
302
+ changed = true;
303
+ else
304
+ deletedPaths.add(path);
305
+ continue;
306
+ }
307
+ const node = nodeFromResponse(response);
308
+ const old = this.#nodes.get(node.path);
309
+ if (!old || !sameNode(old, node)) {
310
+ this.#setNode(node);
311
+ changed = true;
312
+ changedPaths.add(node.path);
313
+ }
314
+ }
315
+ if (changed) {
316
+ this.#rebuildChildren();
317
+ this.#publishState();
318
+ }
319
+ return { changed, changedPaths, deletedPaths };
320
+ }
321
+ #setNode(node) {
322
+ this.#nodes.set(node.path, node);
323
+ }
324
+ #deleteSubtree(path, deletedPaths) {
325
+ let deleted = false;
326
+ for (const nodePath of [...this.#nodes.keys()].sort((a, b) => b.length - a.length)) {
327
+ if (nodePath === path || isDescendant(nodePath, path)) {
328
+ this.#nodes.delete(nodePath);
329
+ deletedPaths.add(nodePath);
330
+ deleted = true;
331
+ }
332
+ }
333
+ return deleted;
334
+ }
335
+ #rebuildChildren() {
336
+ this.#children = new Map();
337
+ for (const node of this.#nodes.values()) {
338
+ if (!node.parent)
339
+ continue;
340
+ const bucket = this.#children.get(node.parent) ?? [];
341
+ bucket.push(node.path);
342
+ this.#children.set(node.parent, bucket);
343
+ }
344
+ for (const [parent, children] of this.#children) {
345
+ children.sort((a, b) => sortNodes(this.#nodes.get(a), this.#nodes.get(b)));
346
+ this.#children.set(parent, children);
347
+ }
348
+ }
349
+ #publishState() {
350
+ const nodes = [...this.#nodes.values()].sort((a, b) => a.path === ROOT ? -1 : b.path === ROOT ? 1 : a.path.localeCompare(b.path));
351
+ this.nodes = nodes;
352
+ this.byPath = Object.fromEntries(nodes.map((node) => [node.path, node]));
353
+ this.version += 1;
354
+ }
355
+ #emit(event) {
356
+ for (const listener of this.#listeners)
357
+ listener(event);
358
+ }
359
+ }
360
+ function rootNode() {
361
+ return {
362
+ id: ROOT,
363
+ path: ROOT,
364
+ parent: null,
365
+ name: 'Space',
366
+ root: '',
367
+ isCollection: true,
368
+ size: null,
369
+ contentType: null,
370
+ etag: null,
371
+ modifiedAt: null,
372
+ href: null,
373
+ };
374
+ }
375
+ function syntheticRootChild(path, name) {
376
+ return {
377
+ id: path,
378
+ path,
379
+ parent: ROOT,
380
+ name,
381
+ root: name,
382
+ isCollection: true,
383
+ size: null,
384
+ contentType: null,
385
+ etag: null,
386
+ modifiedAt: null,
387
+ href: null,
388
+ };
389
+ }
390
+ function isDescendant(path, ancestor) {
391
+ if (ancestor === ROOT)
392
+ return path !== ROOT;
393
+ return path.startsWith(`${ancestor}/`);
394
+ }
395
+ function safeCollection(path) {
396
+ if (!isObjectPath(path))
397
+ return undefined;
398
+ return path.split('/')[2];
399
+ }
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  export { createRool, generateId } from './rool.svelte.js';
2
- export { loc, parseLocation, normalizeLocation, isLocation, generateBasename, resolveMachineResource, } from '@rool-dev/sdk';
3
- export type { ParsedLocation, MachineResource } from '@rool-dev/sdk';
2
+ export { isObjectPath, machinePath, machineUri } from '@rool-dev/sdk';
4
3
  export { wrapChannel } from './channel.svelte.js';
5
4
  export { wrapSpace } from './space.svelte.js';
5
+ export { ReactiveFileTree } from './file-tree.svelte.js';
6
6
  export type { Rool } from './rool.svelte.js';
7
7
  export type { ReactiveChannel, ReactiveConversationHandle, ReactiveObject, ReactiveWatch, ReactiveChannelList, WatchOptions } from './channel.svelte.js';
8
8
  export type { ReactiveSpace } from './space.svelte.js';
9
- export type { RoolClientConfig, RoolChannel, RoolSpace, RoolSpaceInfo, RoolObject, RoolObjectStat, RoolUserRole, ConnectionState, ChannelInfo, Conversation, ConversationInfo, CurrentUser, Interaction, FindObjectsOptions, PromptOptions, CreateObjectOptions, UpdateObjectOptions, MoveObjectOptions, FieldType, FieldDef, CollectionDef, SpaceSchema, SpaceMember, UserResult, RoolClient, ExtensionInfo, PublishedExtensionInfo, UploadExtensionOptions, ExtensionManifest, FindExtensionsOptions, RoolSpaceEvents, ProbeRequestEvent, OpenExtensionEvent, } from '@rool-dev/sdk';
9
+ export type { ReactiveFileNode, ReactiveFilePath, ReactiveFileRoot, ReactiveFileTreeEvent, ReactiveFileTreeSyncResult } from './file-tree.svelte.js';
10
+ export type { RoolClientConfig, RoolChannel, RoolSpace, RoolSpaceInfo, RoolObject, GetObjectsResult, RoolObjectStat, RoolUserRole, ConnectionState, ChannelInfo, Conversation, ConversationInfo, CurrentUser, Interaction, PromptOptions, PromptAttachment, UpdateObjectOptions, MoveObjectOptions, CollectionOptions, FieldType, FieldDef, CollectionDef, SpaceSchema, SpaceMember, UserResult, RoolClient, RoolSpaceEvents, SpaceFileStorageUsage, WebDAVDepth, WebDAVSyncLevel, WebDAVPropName, WebDAVResponse, WebDAVProps, } from '@rool-dev/sdk';
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG1D,OAAO,EACL,GAAG,EACH,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGrE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAG9C,YAAY,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,YAAY,EAAE,eAAe,EAAE,0BAA0B,EAAE,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACzJ,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,YAAY,EACV,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,aAAa,EACb,UAAU,EACV,cAAc,EACd,YAAY,EACZ,eAAe,EACf,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,SAAS,EACT,QAAQ,EACR,aAAa,EACb,WAAW,EACX,WAAW,EACX,UAAU,EACV,UAAU,EACV,aAAa,EACb,sBAAsB,EACtB,sBAAsB,EACtB,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG1D,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGtE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGzD,YAAY,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,YAAY,EAAE,eAAe,EAAE,0BAA0B,EAAE,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACzJ,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAGrJ,YAAY,EACV,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,SAAS,EACT,QAAQ,EACR,aAAa,EACb,WAAW,EACX,WAAW,EACX,UAAU,EACV,UAAU,EACV,eAAe,EACf,qBAAqB,EACrB,WAAW,EACX,eAAe,EACf,cAAc,EACd,cAAc,EACd,WAAW,GACZ,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  // Main export
2
2
  export { createRool, generateId } from './rool.svelte.js';
3
- // Location and machine-resource helpers — re-exported from the SDK for convenience
4
- export { loc, parseLocation, normalizeLocation, isLocation, generateBasename, resolveMachineResource, } from '@rool-dev/sdk';
3
+ // Machine path helpers — re-exported from the SDK for convenience
4
+ export { isObjectPath, machinePath, machineUri } from '@rool-dev/sdk';
5
5
  // Reactive wrappers
6
6
  export { wrapChannel } from './channel.svelte.js';
7
7
  export { wrapSpace } from './space.svelte.js';
8
+ export { ReactiveFileTree } from './file-tree.svelte.js';
@@ -1,4 +1,4 @@
1
- import { RoolClient, type RoolSpaceInfo, type ConnectionState, type RoolClientConfig, type CurrentUser, type FindExtensionsOptions, type ExtensionInfo, type PublishedExtensionInfo, type UploadExtensionOptions } from '@rool-dev/sdk';
1
+ import { RoolClient, type RoolSpaceInfo, type ConnectionState, type RoolClientConfig, type CurrentUser } from '@rool-dev/sdk';
2
2
  import { type ReactiveChannelList } from './channel.svelte.js';
3
3
  import { type ReactiveSpace } from './space.svelte.js';
4
4
  /**
@@ -58,6 +58,10 @@ declare class RoolImpl {
58
58
  * Create a new space. Returns a ReactiveSpace.
59
59
  */
60
60
  createSpace(name: string): Promise<ReactiveSpace>;
61
+ /**
62
+ * Duplicate an existing space. Returns a ReactiveSpace.
63
+ */
64
+ duplicateSpace(sourceSpaceId: string, name: string): Promise<ReactiveSpace>;
61
65
  /**
62
66
  * Manually refresh the spaces list.
63
67
  */
@@ -66,6 +70,22 @@ declare class RoolImpl {
66
70
  * Delete a space.
67
71
  */
68
72
  deleteSpace(spaceId: string): Promise<void>;
73
+ /**
74
+ * Mark the current user for deletion, then log out locally.
75
+ */
76
+ deleteCurrentUser(): Promise<void>;
77
+ /**
78
+ * Set or change the current user's password.
79
+ */
80
+ setPassword(password: string): Promise<void>;
81
+ /**
82
+ * Get a value from user storage.
83
+ */
84
+ getUserStorage<T = unknown>(key: string): T | undefined;
85
+ /**
86
+ * Get all user storage data.
87
+ */
88
+ getAllUserStorage(): Record<string, unknown>;
69
89
  /**
70
90
  * Set a value in user storage.
71
91
  * Updates reactive state immediately, then syncs to server.
@@ -98,28 +118,8 @@ declare class RoolImpl {
98
118
  updateCurrentUser(input: {
99
119
  name?: string;
100
120
  slug?: string;
121
+ marketingOptIn?: boolean;
101
122
  }): Promise<CurrentUser>;
102
- /**
103
- * Install an extension into a space.
104
- * Creates/updates a channel with the extension's manifest settings.
105
- * Returns the channel ID.
106
- */
107
- /** Upload or update a user extension bundle. */
108
- uploadExtension(extensionId: string, options: UploadExtensionOptions): Promise<ExtensionInfo>;
109
- /** Delete a user extension permanently. */
110
- deleteExtension(extensionId: string): Promise<void>;
111
- /** List the current user's extensions. */
112
- listExtensions(): Promise<ExtensionInfo[]>;
113
- /** Get info for a specific user extension. */
114
- getExtensionInfo(extensionId: string): Promise<ExtensionInfo | null>;
115
- /** Search published extensions. */
116
- findExtensions(options?: FindExtensionsOptions): Promise<PublishedExtensionInfo[]>;
117
- /** Respond to a server-initiated probe with a method-specific result or error. */
118
- probeResponse(requestId: string, result?: unknown, error?: string): Promise<boolean>;
119
- /** Publish a user extension (make it publicly discoverable). */
120
- publishToPublic(extensionId: string): Promise<void>;
121
- /** Unpublish an extension (remove from public listing). */
122
- unpublishFromPublic(extensionId: string): Promise<void>;
123
123
  /**
124
124
  * Create a reactive channel list for a space.
125
125
  * Auto-updates when channels are created, updated, or deleted.
@@ -1 +1 @@
1
- {"version":3,"file":"rool.svelte.d.ts","sourceRoot":"","sources":["../src/rool.svelte.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rool.svelte.d.ts","sourceRoot":"","sources":["../src/rool.svelte.ts"],"names":[],"mappings":"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"}