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