@rool-dev/sdk 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.
Files changed (56) hide show
  1. package/README.md +465 -1005
  2. package/dist/channel.d.ts +93 -248
  3. package/dist/channel.d.ts.map +1 -1
  4. package/dist/channel.js +410 -577
  5. package/dist/channel.js.map +1 -1
  6. package/dist/client.d.ts +14 -40
  7. package/dist/client.d.ts.map +1 -1
  8. package/dist/client.js +31 -116
  9. package/dist/client.js.map +1 -1
  10. package/dist/graphql.d.ts +11 -35
  11. package/dist/graphql.d.ts.map +1 -1
  12. package/dist/graphql.js +72 -298
  13. package/dist/graphql.js.map +1 -1
  14. package/dist/index.d.ts +4 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +4 -4
  17. package/dist/index.js.map +1 -1
  18. package/dist/path.d.ts +6 -0
  19. package/dist/path.d.ts.map +1 -0
  20. package/dist/path.js +47 -0
  21. package/dist/path.js.map +1 -0
  22. package/dist/reroute.d.ts +22 -0
  23. package/dist/reroute.d.ts.map +1 -0
  24. package/dist/reroute.js +61 -0
  25. package/dist/reroute.js.map +1 -0
  26. package/dist/rest.d.ts +27 -0
  27. package/dist/rest.d.ts.map +1 -0
  28. package/dist/rest.js +78 -0
  29. package/dist/rest.js.map +1 -0
  30. package/dist/router.d.ts.map +1 -1
  31. package/dist/router.js +25 -10
  32. package/dist/router.js.map +1 -1
  33. package/dist/space.d.ts +23 -16
  34. package/dist/space.d.ts.map +1 -1
  35. package/dist/space.js +115 -67
  36. package/dist/space.js.map +1 -1
  37. package/dist/subscription.d.ts.map +1 -1
  38. package/dist/subscription.js +47 -36
  39. package/dist/subscription.js.map +1 -1
  40. package/dist/types.d.ts +86 -203
  41. package/dist/types.d.ts.map +1 -1
  42. package/dist/types.js +0 -4
  43. package/dist/types.js.map +1 -1
  44. package/dist/webdav.d.ts +176 -0
  45. package/dist/webdav.d.ts.map +1 -0
  46. package/dist/webdav.js +495 -0
  47. package/dist/webdav.js.map +1 -0
  48. package/package.json +2 -1
  49. package/dist/apps.d.ts +0 -30
  50. package/dist/apps.d.ts.map +0 -1
  51. package/dist/apps.js +0 -81
  52. package/dist/apps.js.map +0 -1
  53. package/dist/media.d.ts +0 -76
  54. package/dist/media.d.ts.map +0 -1
  55. package/dist/media.js +0 -249
  56. package/dist/media.js.map +0 -1
@@ -0,0 +1,176 @@
1
+ import type { AuthManager } from './auth.js';
2
+ export type WebDAVDepth = '0' | '1' | 'infinity';
3
+ export type WebDAVSyncLevel = '1' | 'infinite';
4
+ export type WebDAVLockDepth = '0' | 'infinity';
5
+ export type WebDAVPropName = 'creationdate' | 'displayname' | 'getcontentlength' | 'getcontenttype' | 'getetag' | 'getlastmodified' | 'lockdiscovery' | 'quota-available-bytes' | 'quota-used-bytes' | 'resourcetype' | 'supportedlock' | 'current-user-privilege-set' | 'supported-report-set' | 'sync-token' | (string & {});
6
+ export interface WebDAVConfig {
7
+ webdavUrl: string;
8
+ spaceId: string;
9
+ authManager: AuthManager;
10
+ /** Called on shard refusal/drain (421/503). Return the new WebDAV base URL. */
11
+ onRefused?: () => Promise<string>;
12
+ }
13
+ export interface SpaceFileStorageUsage {
14
+ /** Bytes currently used by files in this space. */
15
+ usedBytes: number;
16
+ /** Bytes still available, or null when the plan has no storage limit. */
17
+ availableBytes: number | null;
18
+ /** Total storage limit, or null when the plan has no storage limit. */
19
+ limitBytes: number | null;
20
+ }
21
+ export interface WebDAVRequestInit extends RequestInit {
22
+ /** Treat the path as a collection URL, i.e. include the trailing slash. */
23
+ collection?: boolean;
24
+ }
25
+ export interface WebDAVWriteResult {
26
+ status: 200 | 201 | 204;
27
+ etag: string | null;
28
+ location: string | null;
29
+ }
30
+ export interface WebDAVLockResult {
31
+ status: 200 | 201;
32
+ token: string;
33
+ timeoutSeconds: number | null;
34
+ locks: WebDAVActiveLock[];
35
+ xml: string;
36
+ }
37
+ export interface WebDAVMultiStatus {
38
+ status: 207;
39
+ xml: string;
40
+ responses: WebDAVResponse[];
41
+ }
42
+ export interface WebDAVSyncCollectionResult extends WebDAVMultiStatus {
43
+ token: string;
44
+ }
45
+ export interface WebDAVResponse {
46
+ href: string;
47
+ path: string;
48
+ isCollection: boolean;
49
+ status: number | null;
50
+ props: WebDAVProps;
51
+ propstats: WebDAVPropstat[];
52
+ }
53
+ export interface WebDAVPropstat {
54
+ status: number;
55
+ props: Record<string, unknown>;
56
+ }
57
+ export interface WebDAVProps {
58
+ creationdate?: string;
59
+ displayname?: string;
60
+ getcontentlength?: number;
61
+ getcontenttype?: string;
62
+ getetag?: string;
63
+ getlastmodified?: string;
64
+ resourcetype?: 'collection' | '';
65
+ quotaUsedBytes?: number;
66
+ quotaAvailableBytes?: number | null;
67
+ canWrite?: boolean;
68
+ locks?: WebDAVActiveLock[];
69
+ syncToken?: string;
70
+ supportedReports?: string[];
71
+ [key: string]: unknown;
72
+ }
73
+ export interface WebDAVActiveLock {
74
+ token: string | null;
75
+ owner: string | null;
76
+ depth: WebDAVLockDepth | null;
77
+ timeoutSeconds: number | null;
78
+ root: string | null;
79
+ scope: 'exclusive' | 'shared' | null;
80
+ type: 'write' | null;
81
+ }
82
+ export declare class WebDAVError extends Error {
83
+ status: number;
84
+ statusText: string;
85
+ body: string;
86
+ constructor(response: Response, body: string);
87
+ }
88
+ /** WebDAV client for a space's authenticated file storage. */
89
+ export declare class RoolWebDAV {
90
+ private webdavUrl;
91
+ private spaceId;
92
+ private authManager;
93
+ private onRefused?;
94
+ constructor(config: WebDAVConfig);
95
+ /** Update the WebDAV base URL (used after shard rerouting). */
96
+ setWebDAVUrl(webdavUrl: string): void;
97
+ /** Return the WebDAV href for a machine path. */
98
+ href(path?: string, options?: {
99
+ collection?: boolean;
100
+ }): string;
101
+ /** Return the absolute WebDAV URL for a machine path. */
102
+ url(path?: string, options?: {
103
+ collection?: boolean;
104
+ }): string;
105
+ /** Low-level WebDAV request for a machine path. Adds Rool auth and returns the raw Response. */
106
+ request(method: string, path?: string, init?: WebDAVRequestInit): Promise<Response>;
107
+ options(path?: string): Promise<Response>;
108
+ propfind(path: string, options: {
109
+ depth: WebDAVDepth;
110
+ props?: 'allprop' | 'propname' | WebDAVPropName[];
111
+ signal?: AbortSignal;
112
+ }): Promise<WebDAVMultiStatus>;
113
+ syncCollection(path: string, options: {
114
+ token?: string | null;
115
+ level: WebDAVSyncLevel;
116
+ props?: 'allprop' | WebDAVPropName[];
117
+ limit?: number;
118
+ signal?: AbortSignal;
119
+ }): Promise<WebDAVSyncCollectionResult>;
120
+ /** Return WebDAV quota usage for this space. */
121
+ getStorageUsage(): Promise<SpaceFileStorageUsage>;
122
+ get(path: string, options?: {
123
+ range?: string | {
124
+ start: number;
125
+ end?: number;
126
+ };
127
+ signal?: AbortSignal;
128
+ }): Promise<Response>;
129
+ head(path: string): Promise<Response>;
130
+ put(path: string, body: BodyInit, options?: {
131
+ contentType?: string;
132
+ ifMatch?: string;
133
+ ifNoneMatch?: string;
134
+ lockToken?: string;
135
+ signal?: AbortSignal;
136
+ headers?: HeadersInit;
137
+ }): Promise<WebDAVWriteResult>;
138
+ delete(path: string, options?: {
139
+ collection?: boolean;
140
+ ifMatch?: string;
141
+ lockToken?: string;
142
+ headers?: HeadersInit;
143
+ }): Promise<void>;
144
+ mkcol(path: string, options?: {
145
+ lockToken?: string;
146
+ headers?: HeadersInit;
147
+ }): Promise<void>;
148
+ copy(source: string, destination: string, options?: {
149
+ depth?: '0' | 'infinity';
150
+ overwrite?: boolean;
151
+ lockToken?: string;
152
+ headers?: HeadersInit;
153
+ }): Promise<WebDAVWriteResult>;
154
+ move(source: string, destination: string, options?: {
155
+ overwrite?: boolean;
156
+ lockToken?: string;
157
+ headers?: HeadersInit;
158
+ }): Promise<WebDAVWriteResult>;
159
+ lock(path: string, options: {
160
+ collection?: boolean;
161
+ depth: WebDAVLockDepth;
162
+ owner?: string;
163
+ timeoutSeconds?: number;
164
+ signal?: AbortSignal;
165
+ }): Promise<WebDAVLockResult>;
166
+ refreshLock(path: string, token: string, options?: {
167
+ collection?: boolean;
168
+ timeoutSeconds?: number;
169
+ signal?: AbortSignal;
170
+ }): Promise<WebDAVLockResult>;
171
+ unlock(token: string): Promise<void>;
172
+ private moveOrCopy;
173
+ private authenticatedFetch;
174
+ private pathUrl;
175
+ }
176
+ //# sourceMappingURL=webdav.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webdav.d.ts","sourceRoot":"","sources":["../src/webdav.ts"],"names":[],"mappings":"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"}
package/dist/webdav.js ADDED
@@ -0,0 +1,495 @@
1
+ import { machinePath } from './path.js';
2
+ import { fetchWithReroute, isThrowRetryable } from './reroute.js';
3
+ const XML_HEADER = '<?xml version="1.0" encoding="utf-8"?>';
4
+ function getTimezone() {
5
+ try {
6
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
7
+ }
8
+ catch {
9
+ return undefined;
10
+ }
11
+ }
12
+ const KNOWN_PROPS = [
13
+ 'creationdate',
14
+ 'displayname',
15
+ 'getcontentlength',
16
+ 'getcontenttype',
17
+ 'getetag',
18
+ 'getlastmodified',
19
+ 'lockdiscovery',
20
+ 'quota-available-bytes',
21
+ 'quota-used-bytes',
22
+ 'resourcetype',
23
+ 'supportedlock',
24
+ 'current-user-privilege-set',
25
+ 'supported-report-set',
26
+ 'sync-token',
27
+ ];
28
+ export class WebDAVError extends Error {
29
+ status;
30
+ statusText;
31
+ body;
32
+ constructor(response, body) {
33
+ super(`WebDAV request failed: ${response.status} ${response.statusText}`);
34
+ this.name = 'WebDAVError';
35
+ this.status = response.status;
36
+ this.statusText = response.statusText;
37
+ this.body = body;
38
+ }
39
+ }
40
+ /** WebDAV client for a space's authenticated file storage. */
41
+ export class RoolWebDAV {
42
+ webdavUrl;
43
+ spaceId;
44
+ authManager;
45
+ onRefused;
46
+ constructor(config) {
47
+ this.webdavUrl = normalizeWebDAVBaseUrl(config.webdavUrl);
48
+ this.spaceId = config.spaceId;
49
+ this.authManager = config.authManager;
50
+ this.onRefused = config.onRefused;
51
+ }
52
+ /** Update the WebDAV base URL (used after shard rerouting). */
53
+ setWebDAVUrl(webdavUrl) {
54
+ this.webdavUrl = normalizeWebDAVBaseUrl(webdavUrl);
55
+ }
56
+ /** Return the WebDAV href for a machine path. */
57
+ href(path = '/', options) {
58
+ return this.pathUrl(path, options).href;
59
+ }
60
+ /** Return the absolute WebDAV URL for a machine path. */
61
+ url(path = '/', options) {
62
+ const davPath = this.pathUrl(path, options);
63
+ return `${this.webdavUrl}${davPath.href}`;
64
+ }
65
+ /** Low-level WebDAV request for a machine path. Adds Rool auth and returns the raw Response. */
66
+ async request(method, path = '/', init = {}) {
67
+ const { collection, ...fetchInit } = init;
68
+ const requestInit = { ...fetchInit, method };
69
+ // 421 (wrong shard) and 503 (draining) reject before executing server-side;
70
+ // a node that rolled fully away rejects the fetch opaquely (no CORS on the
71
+ // LB's 5xx). Both re-resolve the owner and retry. A one-shot ReadableStream
72
+ // body can't be re-sent, so it disables retries.
73
+ const onRefused = this.onRefused;
74
+ const bodyIsStream = typeof ReadableStream !== 'undefined' && requestInit.body instanceof ReadableStream;
75
+ return fetchWithReroute({
76
+ send: () => this.authenticatedFetch(this.url(path, { collection }), requestInit),
77
+ reroute: onRefused ? async () => this.setWebDAVUrl(await onRefused()) : undefined,
78
+ retryOnThrow: isThrowRetryable(method) && !bodyIsStream,
79
+ });
80
+ }
81
+ async options(path = '/') {
82
+ return this.request('OPTIONS', path);
83
+ }
84
+ async propfind(path, options) {
85
+ const response = await this.request('PROPFIND', path, {
86
+ collection: true,
87
+ signal: options.signal,
88
+ headers: {
89
+ Depth: options.depth,
90
+ 'Content-Type': 'application/xml; charset=utf-8',
91
+ },
92
+ body: propfindXml(options.props),
93
+ });
94
+ await assertStatus(response, 207);
95
+ const xml = await response.text();
96
+ return parseMultiStatus(xml, this.spaceId);
97
+ }
98
+ async syncCollection(path, options) {
99
+ const response = await this.request('REPORT', path, {
100
+ collection: true,
101
+ signal: options.signal,
102
+ headers: { 'Content-Type': 'application/xml; charset=utf-8' },
103
+ body: syncCollectionXml(options),
104
+ });
105
+ await assertStatus(response, 207);
106
+ const xml = await response.text();
107
+ const multistatus = parseMultiStatus(xml, this.spaceId);
108
+ const token = textOf(xml, 'sync-token');
109
+ if (!token)
110
+ throw new Error('sync-collection response missing sync-token');
111
+ return { ...multistatus, token };
112
+ }
113
+ /** Return WebDAV quota usage for this space. */
114
+ async getStorageUsage() {
115
+ const result = await this.propfind('', {
116
+ depth: '0',
117
+ props: ['quota-used-bytes', 'quota-available-bytes'],
118
+ });
119
+ const props = result.responses[0]?.props;
120
+ const usedBytes = props?.quotaUsedBytes;
121
+ if (typeof usedBytes !== 'number') {
122
+ throw new Error('Storage usage response missing quota-used-bytes');
123
+ }
124
+ const availableBytes = typeof props?.quotaAvailableBytes === 'number'
125
+ ? props.quotaAvailableBytes
126
+ : null;
127
+ return {
128
+ usedBytes,
129
+ availableBytes,
130
+ limitBytes: availableBytes === null ? null : usedBytes + availableBytes,
131
+ };
132
+ }
133
+ async get(path, options = {}) {
134
+ const headers = new Headers();
135
+ if (options.range)
136
+ headers.set('Range', rangeHeader(options.range));
137
+ const response = await this.request('GET', path, {
138
+ signal: options.signal,
139
+ headers,
140
+ });
141
+ await assertOk(response);
142
+ return response;
143
+ }
144
+ async head(path) {
145
+ const response = await this.request('HEAD', path);
146
+ await assertOk(response);
147
+ return response;
148
+ }
149
+ async put(path, body, options = {}) {
150
+ const headers = writeHeaders(options);
151
+ if (options.contentType)
152
+ headers.set('Content-Type', options.contentType);
153
+ const response = await this.request('PUT', path, {
154
+ signal: options.signal,
155
+ headers,
156
+ body,
157
+ });
158
+ await assertStatus(response, 200, 201, 204);
159
+ return writeResult(response);
160
+ }
161
+ async delete(path, options = {}) {
162
+ const response = await this.request('DELETE', path, {
163
+ collection: options.collection,
164
+ headers: writeHeaders(options),
165
+ });
166
+ await assertStatus(response, 204);
167
+ }
168
+ async mkcol(path, options = {}) {
169
+ const response = await this.request('MKCOL', path, {
170
+ collection: true,
171
+ headers: writeHeaders(options),
172
+ });
173
+ await assertStatus(response, 201);
174
+ }
175
+ async copy(source, destination, options = {}) {
176
+ const response = await this.moveOrCopy('COPY', source, destination, options);
177
+ return writeResult(response);
178
+ }
179
+ async move(source, destination, options = {}) {
180
+ const response = await this.moveOrCopy('MOVE', source, destination, options);
181
+ return writeResult(response);
182
+ }
183
+ async lock(path, options) {
184
+ const headers = new Headers({
185
+ Depth: options.depth,
186
+ 'Content-Type': 'application/xml; charset=utf-8',
187
+ });
188
+ if (options.timeoutSeconds)
189
+ headers.set('Timeout', `Second-${options.timeoutSeconds}`);
190
+ const response = await this.request('LOCK', path, {
191
+ collection: options.collection,
192
+ signal: options.signal,
193
+ headers,
194
+ body: lockXml(options.owner ?? ''),
195
+ });
196
+ await assertStatus(response, 200, 201);
197
+ return lockResult(response, await response.text());
198
+ }
199
+ async refreshLock(path, token, options = {}) {
200
+ const headers = new Headers({ If: `(<${token}>)` });
201
+ if (options.timeoutSeconds)
202
+ headers.set('Timeout', `Second-${options.timeoutSeconds}`);
203
+ const response = await this.request('LOCK', path, {
204
+ collection: options.collection,
205
+ signal: options.signal,
206
+ headers,
207
+ });
208
+ await assertStatus(response, 200);
209
+ return lockResult(response, await response.text());
210
+ }
211
+ async unlock(token) {
212
+ const response = await this.request('UNLOCK', '/', {
213
+ collection: true,
214
+ headers: { 'Lock-Token': `<${token}>` },
215
+ });
216
+ await assertStatus(response, 204);
217
+ }
218
+ async moveOrCopy(method, source, destination, options) {
219
+ const headers = writeHeaders(options);
220
+ headers.set('Destination', this.url(destination));
221
+ if (options.overwrite !== undefined)
222
+ headers.set('Overwrite', options.overwrite ? 'T' : 'F');
223
+ if (method === 'COPY' && options.depth)
224
+ headers.set('Depth', options.depth);
225
+ const response = await this.request(method, source, {
226
+ headers,
227
+ });
228
+ await assertStatus(response, 201, 204);
229
+ return response;
230
+ }
231
+ async authenticatedFetch(url, init) {
232
+ const tokens = await this.authManager.getTokens();
233
+ if (!tokens)
234
+ throw new Error('Not authenticated');
235
+ const headers = new Headers(init.headers);
236
+ headers.set('Authorization', `Bearer ${tokens.accessToken}`);
237
+ headers.set('X-Rool-Token', tokens.roolToken);
238
+ const timezone = getTimezone();
239
+ if (timezone)
240
+ headers.set('X-Timezone', timezone);
241
+ const requestInit = { ...init, headers };
242
+ if (typeof ReadableStream !== 'undefined' && init.body instanceof ReadableStream) {
243
+ requestInit.duplex = 'half';
244
+ }
245
+ return fetch(url, requestInit);
246
+ }
247
+ pathUrl(path, options) {
248
+ const parsed = machinePath(path, { spaceId: this.spaceId });
249
+ if (parsed !== '/' && parsed !== '/space' && !parsed.startsWith('/space/') && parsed !== '/rool-drive' && !parsed.startsWith('/rool-drive/')) {
250
+ throw new Error('WebDAV paths must be machine paths under /space or /rool-drive');
251
+ }
252
+ const isCollection = options?.collection ?? parsed === '/';
253
+ const encodedSpace = encodeURIComponent(this.spaceId);
254
+ const encodedPath = parsed === '/'
255
+ ? ''
256
+ : parsed.slice(1).split('/').map(encodeURIComponent).join('/');
257
+ const suffix = encodedPath ? `/${encodedPath}` : '';
258
+ return {
259
+ href: `/space/${encodedSpace}${suffix}${isCollection ? '/' : ''}`,
260
+ path: parsed,
261
+ isCollection,
262
+ };
263
+ }
264
+ }
265
+ function normalizeWebDAVBaseUrl(url) {
266
+ return url.replace(/\/+$/, '').replace(/\/dav$/, '');
267
+ }
268
+ function rangeHeader(range) {
269
+ if (typeof range === 'string')
270
+ return range;
271
+ if (!Number.isSafeInteger(range.start) || range.start < 0)
272
+ throw new Error('Invalid range start');
273
+ if (range.end === undefined)
274
+ return `bytes=${range.start}-`;
275
+ if (!Number.isSafeInteger(range.end) || range.end < range.start)
276
+ throw new Error('Invalid range end');
277
+ return `bytes=${range.start}-${range.end}`;
278
+ }
279
+ function writeHeaders(options) {
280
+ const headers = new Headers(options.headers);
281
+ if (options.ifMatch)
282
+ headers.set('If-Match', options.ifMatch);
283
+ if (options.ifNoneMatch)
284
+ headers.set('If-None-Match', options.ifNoneMatch);
285
+ if (options.lockToken)
286
+ headers.set('If', `(<${options.lockToken}>)`);
287
+ return headers;
288
+ }
289
+ function propfindXml(props) {
290
+ if (!props || props === 'allprop') {
291
+ return `${XML_HEADER}<d:propfind xmlns:d="DAV:"><d:allprop/></d:propfind>`;
292
+ }
293
+ if (props === 'propname') {
294
+ return `${XML_HEADER}<d:propfind xmlns:d="DAV:"><d:propname/></d:propfind>`;
295
+ }
296
+ const names = props.map((name) => `<d:${name}/>`).join('');
297
+ return `${XML_HEADER}<d:propfind xmlns:d="DAV:"><d:prop>${names}</d:prop></d:propfind>`;
298
+ }
299
+ function syncCollectionXml(options) {
300
+ if (options.limit !== undefined && (!Number.isSafeInteger(options.limit) || options.limit <= 0)) {
301
+ throw new Error('Invalid sync-collection limit');
302
+ }
303
+ const token = `<d:sync-token>${xmlEscape(options.token ?? '')}</d:sync-token>`;
304
+ const level = `<d:sync-level>${options.level}</d:sync-level>`;
305
+ const props = !options.props || options.props === 'allprop'
306
+ ? '<d:prop><d:displayname/><d:getcontentlength/><d:getcontenttype/><d:getetag/><d:getlastmodified/><d:resourcetype/></d:prop>'
307
+ : `<d:prop>${options.props.map((name) => `<d:${name}/>`).join('')}</d:prop>`;
308
+ const limit = options.limit ? `<d:limit><d:nresults>${options.limit}</d:nresults></d:limit>` : '';
309
+ return `${XML_HEADER}<d:sync-collection xmlns:d="DAV:">${token}${level}${props}${limit}</d:sync-collection>`;
310
+ }
311
+ function lockXml(owner) {
312
+ return `${XML_HEADER}<d:lockinfo xmlns:d="DAV:"><d:lockscope><d:exclusive/></d:lockscope><d:locktype><d:write/></d:locktype><d:owner>${xmlEscape(owner)}</d:owner></d:lockinfo>`;
313
+ }
314
+ function writeResult(response) {
315
+ return {
316
+ status: response.status,
317
+ etag: response.headers.get('ETag'),
318
+ location: response.headers.get('Location'),
319
+ };
320
+ }
321
+ async function assertOk(response) {
322
+ if (response.ok)
323
+ return;
324
+ throw new WebDAVError(response, await response.text());
325
+ }
326
+ async function assertStatus(response, ...statuses) {
327
+ if (statuses.includes(response.status))
328
+ return;
329
+ throw new WebDAVError(response, await response.text());
330
+ }
331
+ function lockResult(response, xml) {
332
+ const locks = parseActiveLocks(xml);
333
+ const headerToken = response.headers.get('Lock-Token')?.match(/<([^>]+)>/)?.[1] ?? null;
334
+ const token = locks[0]?.token ?? headerToken;
335
+ if (!token)
336
+ throw new Error('LOCK response missing lock token');
337
+ return {
338
+ status: response.status,
339
+ token,
340
+ timeoutSeconds: locks[0]?.timeoutSeconds ?? parseTimeout(response.headers.get('Timeout')),
341
+ locks,
342
+ xml,
343
+ };
344
+ }
345
+ function parseMultiStatus(xml, spaceId) {
346
+ const responses = blocks(xml, 'response').map((responseXml) => parseResponse(responseXml, spaceId));
347
+ return { status: 207, xml, responses };
348
+ }
349
+ function parseResponse(xml, spaceId) {
350
+ const href = textOf(xml, 'href') ?? '';
351
+ const propstats = blocks(xml, 'propstat').map(parsePropstat);
352
+ const okProps = propstats.find((propstat) => propstat.status === 200)?.props ?? {};
353
+ const props = toWebDAVProps(okProps);
354
+ const isCollection = props.resourcetype === 'collection' || href.endsWith('/');
355
+ return {
356
+ href,
357
+ path: pathFromHref(href, spaceId),
358
+ isCollection,
359
+ status: statusCode(textOf(xml, 'status')),
360
+ props,
361
+ propstats,
362
+ };
363
+ }
364
+ function parsePropstat(xml) {
365
+ const propXml = block(xml, 'prop') ?? '';
366
+ const props = {};
367
+ for (const name of KNOWN_PROPS) {
368
+ const raw = block(propXml, name) ?? selfClosing(propXml, name);
369
+ if (raw !== null)
370
+ props[name] = parsePropValue(name, raw);
371
+ }
372
+ return {
373
+ status: statusCode(textOf(xml, 'status')) ?? 0,
374
+ props,
375
+ };
376
+ }
377
+ function parsePropValue(name, raw) {
378
+ if (name === 'getcontentlength' || name === 'quota-used-bytes' || name === 'quota-available-bytes') {
379
+ const value = Number(stripTags(raw));
380
+ return Number.isFinite(value) ? value : null;
381
+ }
382
+ if (name === 'resourcetype')
383
+ return hasTag(raw, 'collection') ? 'collection' : '';
384
+ if (name === 'lockdiscovery')
385
+ return parseActiveLocks(raw);
386
+ if (name === 'current-user-privilege-set')
387
+ return { canWrite: hasTag(raw, 'write') || hasTag(raw, 'bind') };
388
+ if (name === 'supportedlock')
389
+ return { write: hasTag(raw, 'write') };
390
+ if (name === 'supported-report-set')
391
+ return { reports: hasTag(raw, 'sync-collection') ? ['sync-collection'] : [] };
392
+ return xmlUnescape(stripTags(raw));
393
+ }
394
+ function toWebDAVProps(raw) {
395
+ const props = { ...raw };
396
+ if (typeof raw['quota-used-bytes'] === 'number')
397
+ props.quotaUsedBytes = raw['quota-used-bytes'];
398
+ if (typeof raw['quota-available-bytes'] === 'number' || raw['quota-available-bytes'] === null) {
399
+ props.quotaAvailableBytes = raw['quota-available-bytes'];
400
+ }
401
+ if (Array.isArray(raw.lockdiscovery))
402
+ props.locks = raw.lockdiscovery;
403
+ if (typeof raw['sync-token'] === 'string')
404
+ props.syncToken = raw['sync-token'];
405
+ const supportedReports = raw['supported-report-set'];
406
+ if (supportedReports?.reports)
407
+ props.supportedReports = supportedReports.reports;
408
+ const privileges = raw['current-user-privilege-set'];
409
+ if (privileges)
410
+ props.canWrite = !!privileges.canWrite;
411
+ return props;
412
+ }
413
+ function parseActiveLocks(xml) {
414
+ return blocks(xml, 'activelock').map((activeLock) => ({
415
+ token: block(activeLock, 'locktoken') ? textOf(block(activeLock, 'locktoken') ?? '', 'href') : null,
416
+ owner: textOf(activeLock, 'owner'),
417
+ depth: parseLockDepth(stripTags(block(activeLock, 'depth') ?? '')),
418
+ timeoutSeconds: parseTimeout(stripTags(block(activeLock, 'timeout') ?? '')),
419
+ root: block(activeLock, 'lockroot') ? textOf(block(activeLock, 'lockroot') ?? '', 'href') : null,
420
+ scope: hasTag(block(activeLock, 'lockscope') ?? '', 'exclusive') ? 'exclusive' : hasTag(block(activeLock, 'lockscope') ?? '', 'shared') ? 'shared' : null,
421
+ type: hasTag(block(activeLock, 'locktype') ?? '', 'write') ? 'write' : null,
422
+ }));
423
+ }
424
+ function parseLockDepth(value) {
425
+ const normalized = value.trim().toLowerCase();
426
+ if (normalized === '0' || normalized === 'infinity')
427
+ return normalized;
428
+ return null;
429
+ }
430
+ function parseTimeout(value) {
431
+ const match = value?.match(/Second-(\d+)/i);
432
+ if (!match)
433
+ return null;
434
+ const seconds = Number(match[1]);
435
+ return Number.isFinite(seconds) ? seconds : null;
436
+ }
437
+ function pathFromHref(href, spaceId) {
438
+ try {
439
+ return machinePath(href, { spaceId });
440
+ }
441
+ catch {
442
+ return href;
443
+ }
444
+ }
445
+ function blocks(xml, name) {
446
+ const re = tagRe(name, 'g');
447
+ const matches = [];
448
+ for (const match of xml.matchAll(re))
449
+ matches.push(match[1]);
450
+ return matches;
451
+ }
452
+ function block(xml, name) {
453
+ return tagRe(name).exec(xml)?.[1] ?? null;
454
+ }
455
+ function selfClosing(xml, name) {
456
+ return selfClosingRe(name).test(xml) ? '' : null;
457
+ }
458
+ function textOf(xml, name) {
459
+ const value = block(xml, name);
460
+ return value === null ? null : xmlUnescape(stripTags(value));
461
+ }
462
+ function hasTag(xml, name) {
463
+ return tagRe(name).test(xml) || selfClosingRe(name).test(xml);
464
+ }
465
+ function tagRe(name, flags = '') {
466
+ return new RegExp(`<(?:[A-Za-z0-9_]+:)?${escapeRe(name)}\\b[^>]*>([\\s\\S]*?)<\\/(?:[A-Za-z0-9_]+:)?${escapeRe(name)}>`, flags);
467
+ }
468
+ function selfClosingRe(name) {
469
+ return new RegExp(`<(?:[A-Za-z0-9_]+:)?${escapeRe(name)}\\b[^>]*/>`);
470
+ }
471
+ function statusCode(status) {
472
+ const match = status?.match(/HTTP\/\d(?:\.\d)?\s+(\d{3})/i);
473
+ return match ? Number(match[1]) : null;
474
+ }
475
+ function stripTags(value) {
476
+ return value.replace(/<[^>]*>/g, '').trim();
477
+ }
478
+ function xmlEscape(value) {
479
+ return value
480
+ .replace(/&/g, '&amp;')
481
+ .replace(/</g, '&lt;')
482
+ .replace(/>/g, '&gt;')
483
+ .replace(/"/g, '&quot;');
484
+ }
485
+ function xmlUnescape(value) {
486
+ return value
487
+ .replace(/&quot;/g, '"')
488
+ .replace(/&gt;/g, '>')
489
+ .replace(/&lt;/g, '<')
490
+ .replace(/&amp;/g, '&');
491
+ }
492
+ function escapeRe(value) {
493
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
494
+ }
495
+ //# sourceMappingURL=webdav.js.map