@finueva/drive 0.1.0 → 0.1.1

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/dist/client.js CHANGED
@@ -1,483 +1,256 @@
1
- //#region ../../node_modules/.pnpm/openapi-fetch@0.17.0/node_modules/openapi-fetch/dist/index.mjs
2
- const PATH_PARAM_RE = /\{[^{}]+\}/g;
3
- const supportsRequestInitExt = () => {
4
- return typeof process === "object" && Number.parseInt(process?.versions?.node?.substring(0, 2)) >= 18 && process.versions.undici;
1
+ //#region src/core.ts
2
+ const DRIVE_API_ERROR_CODES = Object.freeze([
3
+ "authentication_required",
4
+ "identity_authority_unavailable",
5
+ "permission_denied",
6
+ "resource_not_found",
7
+ "invalid_request",
8
+ "invalid_pagination_cursor",
9
+ "invalid_idempotency_key",
10
+ "precondition_failed",
11
+ "idempotency_conflict",
12
+ "quota_exceeded",
13
+ "service_unavailable",
14
+ "internal_error"
15
+ ]);
16
+ const DRIVE_API_ERROR_MESSAGES = Object.freeze({
17
+ authentication_required: "Authentication is required.",
18
+ identity_authority_unavailable: "Authentication is temporarily unavailable.",
19
+ permission_denied: "The requested action is not permitted.",
20
+ resource_not_found: "The requested resource was not found.",
21
+ invalid_request: "The request is invalid.",
22
+ invalid_pagination_cursor: "The pagination cursor is invalid.",
23
+ invalid_idempotency_key: "The idempotency key is invalid.",
24
+ precondition_failed: "The request precondition failed.",
25
+ idempotency_conflict: "The idempotency key conflicts with an earlier request.",
26
+ quota_exceeded: "The workspace storage quota would be exceeded.",
27
+ service_unavailable: "The service is temporarily unavailable.",
28
+ internal_error: "The request could not be completed."
29
+ });
30
+ const SDK_MESSAGES = Object.freeze({
31
+ invalid_input: "The Drive SDK input is invalid.",
32
+ credential_unavailable: "A Drive credential could not be obtained.",
33
+ network_error: "The Drive request could not reach the service.",
34
+ timeout: "The Drive request timed out.",
35
+ aborted: "The Drive request was aborted.",
36
+ invalid_response: "Drive returned an invalid response."
37
+ });
38
+ var DriveError = class extends Error {
39
+ code;
40
+ status;
41
+ requestId;
42
+ details;
43
+ constructor(code, options = {}) {
44
+ super(code in DRIVE_API_ERROR_MESSAGES ? DRIVE_API_ERROR_MESSAGES[code] : SDK_MESSAGES[code]);
45
+ this.name = "DriveError";
46
+ this.code = code;
47
+ this.status = options.status ?? null;
48
+ this.requestId = options.requestId ?? null;
49
+ this.details = Object.freeze({});
50
+ }
5
51
  };
6
- function randomID() {
7
- return Math.random().toString(36).slice(2, 11);
52
+ //#endregion
53
+ //#region src/internal/client/response.ts
54
+ const ERROR_BODY_LIMIT = 16 * 1024;
55
+ const SUCCESS_BODY_LIMIT = 256 * 1024;
56
+ const RESPONSE_CHUNK_LIMIT = 256;
57
+ const RESPONSE_DEADLINE_MILLISECONDS = 5e3;
58
+ const REQUEST_ID = /^[A-Za-z0-9_-]{16,128}$/;
59
+ const COLLECTION_ETAG = /^"drive\.permission-collection\.v1\.(0|[1-9][0-9]*)"$/;
60
+ const GRANT_ETAG$1 = /^"drive\.permission-grant\.v1\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)"$/;
61
+ const LIST_ETAG$1 = /^"drive\.permission-list\.v1\.[0-9a-f]{64}"$/;
62
+ async function parseResponse(response, operationSignal, preconditionStatus, successStatuses, guard, allowsQuotaExceeded) {
63
+ const requestId = safeRequestId(response.headers.get("x-request-id"));
64
+ if (requestId === null) {
65
+ cancelResponse(response);
66
+ throw new DriveError("invalid_response", { status: response.status });
67
+ }
68
+ if (!successStatuses.includes(response.status)) throw await normalizeError(response, requestId, operationSignal, preconditionStatus, allowsQuotaExceeded);
69
+ const { value } = await parseBoundedJson(response, SUCCESS_BODY_LIMIT, response.status, requestId, operationSignal);
70
+ const status = response.status;
71
+ if (!guard(value, status, response.headers)) throw new DriveError("invalid_response", {
72
+ status,
73
+ requestId
74
+ });
75
+ return Object.freeze({
76
+ data: value,
77
+ status,
78
+ requestId,
79
+ validators: readValidators(response.headers)
80
+ });
8
81
  }
9
- function createClient(clientOptions) {
10
- let { baseUrl = "", Request: CustomRequest = globalThis.Request, fetch: baseFetch = globalThis.fetch, querySerializer: globalQuerySerializer, bodySerializer: globalBodySerializer, pathSerializer: globalPathSerializer, headers: baseHeaders, requestInitExt = void 0, ...baseOptions } = { ...clientOptions };
11
- requestInitExt = supportsRequestInitExt() ? requestInitExt : void 0;
12
- baseUrl = removeTrailingSlash(baseUrl);
13
- const globalMiddlewares = [];
14
- async function coreFetch(schemaPath, fetchOptions) {
15
- const { baseUrl: localBaseUrl, fetch = baseFetch, Request = CustomRequest, headers, params = {}, parseAs = "json", querySerializer: requestQuerySerializer, bodySerializer = globalBodySerializer ?? defaultBodySerializer, pathSerializer: requestPathSerializer, body, middleware: requestMiddlewares = [], ...init } = fetchOptions || {};
16
- let finalBaseUrl = baseUrl;
17
- if (localBaseUrl) finalBaseUrl = removeTrailingSlash(localBaseUrl) ?? baseUrl;
18
- let querySerializer = typeof globalQuerySerializer === "function" ? globalQuerySerializer : createQuerySerializer(globalQuerySerializer);
19
- if (requestQuerySerializer) querySerializer = typeof requestQuerySerializer === "function" ? requestQuerySerializer : createQuerySerializer({
20
- ...typeof globalQuerySerializer === "object" ? globalQuerySerializer : {},
21
- ...requestQuerySerializer
82
+ function parseCollectionEtag(value) {
83
+ const match = COLLECTION_ETAG.exec(value);
84
+ if (!match) return void 0;
85
+ const revision = Number(match[1]);
86
+ return Number.isSafeInteger(revision) ? revision : void 0;
87
+ }
88
+ function parseGrantEtag(value) {
89
+ const match = GRANT_ETAG$1.exec(value);
90
+ if (!match) return void 0;
91
+ const collectionRevision = Number(match[1]);
92
+ const grantRevision = Number(match[2]);
93
+ return Number.isSafeInteger(collectionRevision) && Number.isSafeInteger(grantRevision) ? [collectionRevision, grantRevision] : void 0;
94
+ }
95
+ async function normalizeError(response, headerRequestId, signal, preconditionStatus, allowsQuotaExceeded) {
96
+ let parsed;
97
+ try {
98
+ parsed = await parseBoundedJson(response, ERROR_BODY_LIMIT, response.status, headerRequestId, signal);
99
+ } catch (error) {
100
+ if (error instanceof DriveError && error.code === "aborted") return error;
101
+ return new DriveError("invalid_response", {
102
+ status: response.status,
103
+ requestId: headerRequestId
22
104
  });
23
- const pathSerializer = requestPathSerializer || globalPathSerializer || defaultPathSerializer;
24
- const serializedBody = body === void 0 ? void 0 : bodySerializer(body, mergeHeaders(baseHeaders, headers, params.header));
25
- const finalHeaders = mergeHeaders(serializedBody === void 0 || serializedBody instanceof FormData ? {} : { "Content-Type": "application/json" }, baseHeaders, headers, params.header);
26
- const finalMiddlewares = [...globalMiddlewares, ...requestMiddlewares];
27
- const requestInit = {
28
- redirect: "follow",
29
- ...baseOptions,
30
- ...init,
31
- body: serializedBody,
32
- headers: finalHeaders
33
- };
34
- let id;
35
- let options;
36
- let request = new Request(createFinalURL(schemaPath, {
37
- baseUrl: finalBaseUrl,
38
- params,
39
- querySerializer,
40
- pathSerializer
41
- }), requestInit);
42
- let response;
43
- for (const key in init) if (!(key in request)) request[key] = init[key];
44
- if (finalMiddlewares.length) {
45
- id = randomID();
46
- options = Object.freeze({
47
- baseUrl: finalBaseUrl,
48
- fetch,
49
- parseAs,
50
- querySerializer,
51
- bodySerializer,
52
- pathSerializer
53
- });
54
- for (const m of finalMiddlewares) if (m && typeof m === "object" && typeof m.onRequest === "function") {
55
- const result = await m.onRequest({
56
- request,
57
- schemaPath,
58
- params,
59
- options,
60
- id
61
- });
62
- if (result) if (result instanceof Request) request = result;
63
- else if (result instanceof Response) {
64
- response = result;
65
- break;
66
- } else throw new Error("onRequest: must return new Request() or Response() when modifying the request");
67
- }
68
- }
69
- if (!response) {
70
- try {
71
- response = await fetch(request, requestInitExt);
72
- } catch (error2) {
73
- let errorAfterMiddleware = error2;
74
- if (finalMiddlewares.length) for (let i = finalMiddlewares.length - 1; i >= 0; i--) {
75
- const m = finalMiddlewares[i];
76
- if (m && typeof m === "object" && typeof m.onError === "function") {
77
- const result = await m.onError({
78
- request,
79
- error: errorAfterMiddleware,
80
- schemaPath,
81
- params,
82
- options,
83
- id
84
- });
85
- if (result) {
86
- if (result instanceof Response) {
87
- errorAfterMiddleware = void 0;
88
- response = result;
89
- break;
90
- }
91
- if (result instanceof Error) {
92
- errorAfterMiddleware = result;
93
- continue;
94
- }
95
- throw new Error("onError: must return new Response() or instance of Error");
96
- }
97
- }
98
- }
99
- if (errorAfterMiddleware) throw errorAfterMiddleware;
100
- }
101
- if (finalMiddlewares.length) for (let i = finalMiddlewares.length - 1; i >= 0; i--) {
102
- const m = finalMiddlewares[i];
103
- if (m && typeof m === "object" && typeof m.onResponse === "function") {
104
- const result = await m.onResponse({
105
- request,
106
- response,
107
- schemaPath,
108
- params,
109
- options,
110
- id
111
- });
112
- if (result) {
113
- if (!(result instanceof Response)) throw new Error("onResponse: must return new Response() when modifying the response");
114
- response = result;
115
- }
116
- }
117
- }
118
- }
119
- const contentLength = response.headers.get("Content-Length");
120
- if (response.status === 204 || request.method === "HEAD" || contentLength === "0" && !response.headers.get("Transfer-Encoding")?.includes("chunked")) return response.ok ? {
121
- data: void 0,
122
- response
123
- } : {
124
- error: void 0,
125
- response
126
- };
127
- if (response.ok) {
128
- const getResponseData = async () => {
129
- if (parseAs === "stream") return response.body;
130
- if (parseAs === "json" && !contentLength) {
131
- const raw = await response.text();
132
- return raw ? JSON.parse(raw) : void 0;
133
- }
134
- return await response[parseAs]();
135
- };
136
- return {
137
- data: await getResponseData(),
138
- response
139
- };
140
- }
141
- let error = await response.text();
142
- try {
143
- error = JSON.parse(error);
144
- } catch {}
145
- return {
146
- error,
147
- response
148
- };
149
105
  }
106
+ const value = parsed.value;
107
+ if (!isExactRecord$1(value, ["error"]) || !isExactRecord$1(value.error, [
108
+ "code",
109
+ "message",
110
+ "requestId",
111
+ "details"
112
+ ])) return new DriveError("invalid_response", {
113
+ status: response.status,
114
+ requestId: headerRequestId
115
+ });
116
+ const envelope = value.error;
117
+ if (typeof envelope.code !== "string" || !DRIVE_API_ERROR_CODES.includes(envelope.code) || envelope.message !== DRIVE_API_ERROR_MESSAGES[envelope.code] || !REQUEST_ID.test(String(envelope.requestId)) || envelope.requestId !== headerRequestId || !isExactRecord$1(envelope.details, []) || !errorStatusMatches(envelope.code, response.status, preconditionStatus, allowsQuotaExceeded) || parsed.text !== JSON.stringify(value)) return new DriveError("invalid_response", {
118
+ status: response.status,
119
+ requestId: headerRequestId
120
+ });
121
+ return new DriveError(envelope.code, {
122
+ status: response.status,
123
+ requestId: envelope.requestId
124
+ });
125
+ }
126
+ function errorStatusMatches(code, status, preconditionStatus, allowsQuotaExceeded) {
127
+ if (code === "quota_exceeded" && !allowsQuotaExceeded) return false;
150
128
  return {
151
- request(method, url, init) {
152
- return coreFetch(url, {
153
- ...init,
154
- method: method.toUpperCase()
155
- });
156
- },
157
- GET(url, init) {
158
- return coreFetch(url, {
159
- ...init,
160
- method: "GET"
161
- });
162
- },
163
- PUT(url, init) {
164
- return coreFetch(url, {
165
- ...init,
166
- method: "PUT"
167
- });
168
- },
169
- POST(url, init) {
170
- return coreFetch(url, {
171
- ...init,
172
- method: "POST"
173
- });
174
- },
175
- DELETE(url, init) {
176
- return coreFetch(url, {
177
- ...init,
178
- method: "DELETE"
179
- });
180
- },
181
- OPTIONS(url, init) {
182
- return coreFetch(url, {
183
- ...init,
184
- method: "OPTIONS"
185
- });
186
- },
187
- HEAD(url, init) {
188
- return coreFetch(url, {
189
- ...init,
190
- method: "HEAD"
191
- });
192
- },
193
- PATCH(url, init) {
194
- return coreFetch(url, {
195
- ...init,
196
- method: "PATCH"
197
- });
198
- },
199
- TRACE(url, init) {
200
- return coreFetch(url, {
201
- ...init,
202
- method: "TRACE"
129
+ authentication_required: [401],
130
+ identity_authority_unavailable: [503],
131
+ permission_denied: [403],
132
+ resource_not_found: [404],
133
+ invalid_request: [422],
134
+ invalid_pagination_cursor: [422],
135
+ invalid_idempotency_key: [422],
136
+ precondition_failed: [preconditionStatus],
137
+ idempotency_conflict: [409],
138
+ quota_exceeded: [409],
139
+ service_unavailable: [503],
140
+ internal_error: [500]
141
+ }[code].includes(status);
142
+ }
143
+ async function parseBoundedJson(response, maximumBytes, status, requestId, signal) {
144
+ if (!response.body) throw new DriveError("invalid_response", {
145
+ status,
146
+ requestId
147
+ });
148
+ const reader = response.body.getReader();
149
+ const decoder = new TextDecoder("utf-8", { fatal: true });
150
+ let size = 0;
151
+ let chunks = 0;
152
+ let text = "";
153
+ const contentLength = response.headers.get("content-length");
154
+ if (contentLength !== null && (!/^(?:0|[1-9][0-9]*)$/.test(contentLength) || Number(contentLength) > maximumBytes)) {
155
+ reader.cancel().catch(() => {});
156
+ throw new DriveError("invalid_response", {
157
+ status,
158
+ requestId
159
+ });
160
+ }
161
+ const deadline = new AbortController();
162
+ const timeout = setTimeout(() => deadline.abort(), RESPONSE_DEADLINE_MILLISECONDS);
163
+ try {
164
+ while (true) {
165
+ if (signal?.aborted) throw new DriveError("aborted");
166
+ const { done, value } = await readResponseChunk(reader, signal, deadline.signal);
167
+ if (done) break;
168
+ chunks += 1;
169
+ size += value.byteLength;
170
+ if (chunks > RESPONSE_CHUNK_LIMIT || size > maximumBytes) throw new DriveError("invalid_response", {
171
+ status,
172
+ requestId
203
173
  });
204
- },
205
- use(...middleware) {
206
- for (const m of middleware) {
207
- if (!m) continue;
208
- if (typeof m !== "object" || !("onRequest" in m || "onResponse" in m || "onError" in m)) throw new Error("Middleware must be an object with one of `onRequest()`, `onResponse() or `onError()`");
209
- globalMiddlewares.push(m);
210
- }
211
- },
212
- eject(...middleware) {
213
- for (const m of middleware) {
214
- const i = globalMiddlewares.indexOf(m);
215
- if (i !== -1) globalMiddlewares.splice(i, 1);
216
- }
174
+ text += decoder.decode(value, { stream: true });
217
175
  }
218
- };
219
- }
220
- function serializePrimitiveParam(name, value, options) {
221
- if (value === void 0 || value === null) return "";
222
- if (typeof value === "object") throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");
223
- return `${name}=${options?.allowReserved === true ? value : encodeURIComponent(value)}`;
224
- }
225
- function serializeObjectParam(name, value, options) {
226
- if (!value || typeof value !== "object") return "";
227
- const values = [];
228
- const joiner = {
229
- simple: ",",
230
- label: ".",
231
- matrix: ";"
232
- }[options.style] || "&";
233
- if (options.style !== "deepObject" && options.explode === false) {
234
- for (const k in value) values.push(k, options.allowReserved === true ? value[k] : encodeURIComponent(value[k]));
235
- const final2 = values.join(",");
236
- switch (options.style) {
237
- case "form": return `${name}=${final2}`;
238
- case "label": return `.${final2}`;
239
- case "matrix": return `;${name}=${final2}`;
240
- default: return final2;
176
+ text += decoder.decode();
177
+ return {
178
+ value: JSON.parse(text),
179
+ text
180
+ };
181
+ } catch (error) {
182
+ if (error instanceof DriveError) {
183
+ if (error.code === "aborted" || signal?.aborted) throw new DriveError("aborted");
184
+ throw error;
241
185
  }
186
+ throw new DriveError("invalid_response", {
187
+ status,
188
+ requestId
189
+ });
190
+ } finally {
191
+ clearTimeout(timeout);
192
+ reader.cancel().catch(() => {});
242
193
  }
243
- for (const k in value) {
244
- const finalName = options.style === "deepObject" ? `${name}[${k}]` : k;
245
- values.push(serializePrimitiveParam(finalName, value[k], options));
246
- }
247
- const final = values.join(joiner);
248
- return options.style === "label" || options.style === "matrix" ? `${joiner}${final}` : final;
249
194
  }
250
- function serializeArrayParam(name, value, options) {
251
- if (!Array.isArray(value)) return "";
252
- if (options.explode === false) {
253
- const joiner2 = {
254
- form: ",",
255
- spaceDelimited: "%20",
256
- pipeDelimited: "|"
257
- }[options.style] || ",";
258
- const final = (options.allowReserved === true ? value : value.map((v) => encodeURIComponent(v))).join(joiner2);
259
- switch (options.style) {
260
- case "simple": return final;
261
- case "label": return `.${final}`;
262
- case "matrix": return `;${name}=${final}`;
263
- default: return `${name}=${final}`;
264
- }
195
+ async function readResponseChunk(reader, operationSignal, deadlineSignal) {
196
+ if (operationSignal?.aborted) throw new DriveError("aborted");
197
+ if (deadlineSignal.aborted) throw new DriveError("invalid_response");
198
+ let removeOperationAbort = () => {};
199
+ let removeDeadlineAbort = () => {};
200
+ const operationAbort = new Promise((_, reject) => {
201
+ if (!operationSignal) return;
202
+ const onAbort = () => reject(new DriveError("aborted"));
203
+ operationSignal.addEventListener("abort", onAbort, { once: true });
204
+ removeOperationAbort = () => operationSignal.removeEventListener("abort", onAbort);
205
+ });
206
+ const deadlineAbort = new Promise((_, reject) => {
207
+ const onAbort = () => reject(new DriveError("invalid_response"));
208
+ deadlineSignal.addEventListener("abort", onAbort, { once: true });
209
+ removeDeadlineAbort = () => deadlineSignal.removeEventListener("abort", onAbort);
210
+ });
211
+ try {
212
+ return await Promise.race([
213
+ reader.read(),
214
+ operationAbort,
215
+ deadlineAbort
216
+ ]);
217
+ } finally {
218
+ removeOperationAbort();
219
+ removeDeadlineAbort();
265
220
  }
266
- const joiner = {
267
- simple: ",",
268
- label: ".",
269
- matrix: ";"
270
- }[options.style] || "&";
271
- const values = [];
272
- for (const v of value) if (options.style === "simple" || options.style === "label") values.push(options.allowReserved === true ? v : encodeURIComponent(v));
273
- else values.push(serializePrimitiveParam(name, v, options));
274
- return options.style === "label" || options.style === "matrix" ? `${joiner}${values.join(joiner)}` : values.join(joiner);
275
221
  }
276
- function createQuerySerializer(options) {
277
- return function querySerializer(queryParams) {
278
- const search = [];
279
- if (queryParams && typeof queryParams === "object") for (const name in queryParams) {
280
- const value = queryParams[name];
281
- if (value === void 0 || value === null) continue;
282
- if (Array.isArray(value)) {
283
- if (value.length === 0) continue;
284
- search.push(serializeArrayParam(name, value, {
285
- style: "form",
286
- explode: true,
287
- ...options?.array,
288
- allowReserved: options?.allowReserved || false
289
- }));
290
- continue;
291
- }
292
- if (typeof value === "object") {
293
- search.push(serializeObjectParam(name, value, {
294
- style: "deepObject",
295
- explode: true,
296
- ...options?.object,
297
- allowReserved: options?.allowReserved || false
298
- }));
299
- continue;
300
- }
301
- search.push(serializePrimitiveParam(name, value, options));
302
- }
303
- return search.join("&");
304
- };
222
+ function cancelResponse(response) {
223
+ response.body?.cancel().catch(() => {});
305
224
  }
306
- function defaultPathSerializer(pathname, pathParams) {
307
- let nextURL = pathname;
308
- for (const match of pathname.match(PATH_PARAM_RE) ?? []) {
309
- let name = match.substring(1, match.length - 1);
310
- let explode = false;
311
- let style = "simple";
312
- if (name.endsWith("*")) {
313
- explode = true;
314
- name = name.substring(0, name.length - 1);
315
- }
316
- if (name.startsWith(".")) {
317
- style = "label";
318
- name = name.substring(1);
319
- } else if (name.startsWith(";")) {
320
- style = "matrix";
321
- name = name.substring(1);
322
- }
323
- if (!pathParams || pathParams[name] === void 0 || pathParams[name] === null) continue;
324
- const value = pathParams[name];
325
- if (Array.isArray(value)) {
326
- nextURL = nextURL.replace(match, serializeArrayParam(name, value, {
327
- style,
328
- explode
329
- }));
330
- continue;
331
- }
332
- if (typeof value === "object") {
333
- nextURL = nextURL.replace(match, serializeObjectParam(name, value, {
334
- style,
335
- explode
336
- }));
337
- continue;
338
- }
339
- if (style === "matrix") {
340
- nextURL = nextURL.replace(match, `;${serializePrimitiveParam(name, value)}`);
341
- continue;
342
- }
343
- nextURL = nextURL.replace(match, style === "label" ? `.${encodeURIComponent(value)}` : encodeURIComponent(value));
344
- }
345
- return nextURL;
346
- }
347
- function defaultBodySerializer(body, headers) {
348
- if (body instanceof FormData) return body;
349
- if (headers) {
350
- if ((headers.get instanceof Function ? headers.get("Content-Type") ?? headers.get("content-type") : headers["Content-Type"] ?? headers["content-type"]) === "application/x-www-form-urlencoded") return new URLSearchParams(body).toString();
351
- }
352
- return JSON.stringify(body);
353
- }
354
- function createFinalURL(pathname, options) {
355
- let finalURL = `${options.baseUrl}${pathname}`;
356
- if (options.params?.path) finalURL = options.pathSerializer(finalURL, options.params.path);
357
- let search = options.querySerializer(options.params.query ?? {});
358
- if (search.startsWith("?")) search = search.substring(1);
359
- if (search) finalURL += `?${search}`;
360
- return finalURL;
361
- }
362
- function mergeHeaders(...allHeaders) {
363
- const finalHeaders = new Headers();
364
- for (const h of allHeaders) {
365
- if (!h || typeof h !== "object") continue;
366
- const iterator = h instanceof Headers ? h.entries() : Object.entries(h);
367
- for (const [k, v] of iterator) if (v === null) finalHeaders.delete(k);
368
- else if (Array.isArray(v)) for (const v2 of v) finalHeaders.append(k, v2);
369
- else if (v !== void 0) finalHeaders.set(k, v);
370
- }
371
- return finalHeaders;
372
- }
373
- function removeTrailingSlash(url) {
374
- if (url.endsWith("/")) return url.substring(0, url.length - 1);
375
- return url;
376
- }
377
- String.fromCharCode;
378
- const TRAILING_SLASH_RE = /\/$|\/\?|\/#/;
379
- const JOIN_LEADING_SLASH_RE = /^\.?\//;
380
- function hasTrailingSlash(input = "", respectQueryAndFragment) {
381
- if (!respectQueryAndFragment) return input.endsWith("/");
382
- return TRAILING_SLASH_RE.test(input);
383
- }
384
- function withTrailingSlash(input = "", respectQueryAndFragment) {
385
- if (!respectQueryAndFragment) return input.endsWith("/") ? input : input + "/";
386
- if (hasTrailingSlash(input, true)) return input || "/";
387
- let path = input;
388
- let fragment = "";
389
- const fragmentIndex = input.indexOf("#");
390
- if (fragmentIndex !== -1) {
391
- path = input.slice(0, fragmentIndex);
392
- fragment = input.slice(fragmentIndex);
393
- if (!path) return fragment;
394
- }
395
- const [s0, ...s] = path.split("?");
396
- return s0 + "/" + (s.length > 0 ? `?${s.join("?")}` : "") + fragment;
225
+ function safeRequestId(value) {
226
+ return value !== null && REQUEST_ID.test(value) ? value : null;
397
227
  }
398
- function isNonEmptyURL(url) {
399
- return url && url !== "/";
228
+ function readValidators(headers) {
229
+ const etag = headers.get("etag");
230
+ const permissionCollectionEtag = headers.get("drive-permission-collection-etag");
231
+ const permissionGrantEtag = headers.get("drive-permission-grant-etag");
232
+ return Object.freeze({
233
+ ...etag !== null && LIST_ETAG$1.test(etag) ? { etag } : {},
234
+ ...permissionCollectionEtag !== null && parseCollectionEtag(permissionCollectionEtag) !== void 0 ? { permissionCollectionEtag } : {},
235
+ ...permissionGrantEtag !== null && parseGrantEtag(permissionGrantEtag) !== void 0 ? { permissionGrantEtag } : {}
236
+ });
400
237
  }
401
- function joinURL(base, ...input) {
402
- let url = base || "";
403
- for (const segment of input.filter((url2) => isNonEmptyURL(url2))) if (url) {
404
- const _segment = segment.replace(JOIN_LEADING_SLASH_RE, "");
405
- url = withTrailingSlash(url) + _segment;
406
- } else url = segment;
407
- return url;
238
+ function isExactRecord$1(value, keys) {
239
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
240
+ return Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
408
241
  }
409
242
  //#endregion
410
- //#region src/core.ts
411
- const DRIVE_API_ERROR_CODES = Object.freeze([
412
- "authentication_required",
413
- "identity_authority_unavailable",
414
- "permission_denied",
415
- "resource_not_found",
416
- "invalid_request",
417
- "invalid_pagination_cursor",
418
- "invalid_idempotency_key",
419
- "precondition_failed",
420
- "idempotency_conflict",
421
- "quota_exceeded",
422
- "service_unavailable",
423
- "internal_error"
424
- ]);
425
- const DRIVE_API_ERROR_MESSAGES = Object.freeze({
426
- authentication_required: "Authentication is required.",
427
- identity_authority_unavailable: "Authentication is temporarily unavailable.",
428
- permission_denied: "The requested action is not permitted.",
429
- resource_not_found: "The requested resource was not found.",
430
- invalid_request: "The request is invalid.",
431
- invalid_pagination_cursor: "The pagination cursor is invalid.",
432
- invalid_idempotency_key: "The idempotency key is invalid.",
433
- precondition_failed: "The request precondition failed.",
434
- idempotency_conflict: "The idempotency key conflicts with an earlier request.",
435
- quota_exceeded: "The workspace storage quota would be exceeded.",
436
- service_unavailable: "The service is temporarily unavailable.",
437
- internal_error: "The request could not be completed."
438
- });
439
- const SDK_MESSAGES = Object.freeze({
440
- invalid_input: "The Drive SDK input is invalid.",
441
- credential_unavailable: "A Drive credential could not be obtained.",
442
- network_error: "The Drive request could not reach the service.",
443
- timeout: "The Drive request timed out.",
444
- aborted: "The Drive request was aborted.",
445
- invalid_response: "Drive returned an invalid response."
446
- });
447
- var DriveError = class extends Error {
448
- code;
449
- status;
450
- requestId;
451
- details;
452
- constructor(code, options = {}) {
453
- super(code in DRIVE_API_ERROR_MESSAGES ? DRIVE_API_ERROR_MESSAGES[code] : SDK_MESSAGES[code]);
454
- this.name = "DriveError";
455
- this.code = code;
456
- this.status = options.status ?? null;
457
- this.requestId = options.requestId ?? null;
458
- this.details = Object.freeze({});
459
- }
460
- };
461
- //#endregion
462
- //#region src/internal/client.ts
463
- const ERROR_BODY_LIMIT = 16 * 1024;
464
- const SUCCESS_BODY_LIMIT = 256 * 1024;
465
- const RESPONSE_CHUNK_LIMIT = 256;
466
- const RESPONSE_DEADLINE_MILLISECONDS = 5e3;
467
- const DEFAULT_REQUEST_TIMEOUT_MILLISECONDS = 3e4;
468
- const MAX_REQUEST_TIMEOUT_MILLISECONDS = 12e4;
469
- const REQUEST_ID = /^[A-Za-z0-9_-]{16,128}$/;
470
- const COLLECTION_ETAG = /^"drive\.permission-collection\.v1\.(0|[1-9][0-9]*)"$/;
243
+ //#region src/internal/client/guards.ts
244
+ const ITEM_ID$1 = /^drive\.item\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
245
+ const WORKSPACE_ID$1 = /^drive\.workspace\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
246
+ const VOLUME_ID = /^drive\.volume\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
247
+ const GRANT_ID$1 = /^drive\.grant\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
248
+ const UPLOAD_ID$1 = /^drive\.upload\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
249
+ const CURSOR$1 = /^drive\.cursor\.v1\.[A-Za-z0-9_-]+$/;
250
+ const AUTH_ID$1 = /^[A-Za-z0-9_-]{1,128}$/;
471
251
  const GRANT_ETAG = /^"drive\.permission-grant\.v1\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)"$/;
472
252
  const LIST_ETAG = /^"drive\.permission-list\.v1\.[0-9a-f]{64}"$/;
473
- const ITEM_ID = /^drive\.item\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
474
- const WORKSPACE_ID = /^drive\.workspace\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
475
- const VOLUME_ID = /^drive\.volume\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
476
- const GRANT_ID = /^drive\.grant\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
477
- const UPLOAD_ID = /^drive\.upload\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
478
- const CURSOR = /^drive\.cursor\.v1\.[A-Za-z0-9_-]+$/;
479
- const AUTH_ID = /^[A-Za-z0-9_-]{1,128}$/;
480
- const PERMISSION_ACTIONS = Object.freeze([
253
+ const PERMISSION_ACTIONS$1 = Object.freeze([
481
254
  "items:content:read",
482
255
  "items:create",
483
256
  "items:delete",
@@ -494,281 +267,186 @@ const EFFECTIVE_ACTIONS = Object.freeze([
494
267
  "items:permissions:read",
495
268
  "items:update"
496
269
  ]);
497
- const IDEMPOTENCY_KEY = /^[A-Za-z0-9_-]{16,128}$/;
498
- const MAX_UPLOAD_BYTE_LENGTH = 33554432e4;
270
+ const MAX_UPLOAD_BYTE_LENGTH$1 = 33554432e4;
499
271
  const UPLOAD_QUOTA_EXPIRY_MILLISECONDS = 864e5;
500
- function createDriveClientInternal(config) {
501
- let runtime;
502
- try {
503
- runtime = Object.freeze({
504
- baseUrl: parseBaseUrl(config.baseUrl),
505
- fetchImplementation: config.fetch ?? globalThis.fetch,
506
- getCredential: config.getCredential,
507
- timeoutMilliseconds: parseTimeoutMilliseconds(config.timeoutMilliseconds)
508
- });
509
- } catch {
510
- throw new DriveError("invalid_input");
511
- }
512
- if (typeof runtime.fetchImplementation !== "function" || typeof runtime.getCredential !== "function") throw new DriveError("invalid_input");
513
- return Object.freeze({
514
- listFolderChildren: (input) => listFolderChildren(runtime, input),
515
- createFolder: (input) => createFolder(runtime, input),
516
- listItemPermissions: (input) => listItemPermissions(runtime, input),
517
- readEffectiveItemPermissions: (input) => readEffectiveItemPermissions(runtime, input),
518
- createItemPermission: (input) => createItemPermission(runtime, input),
519
- updateItemPermission: (input) => updateItemPermission(runtime, input),
520
- revokeItemPermission: (input) => revokeItemPermission(runtime, input),
521
- createUpload: (input) => createUpload(runtime, input),
522
- abortUpload: (input) => abortUpload(runtime, input)
523
- });
272
+ function isFolderPage(value, workspaceId, parentItemId) {
273
+ if (!(isExactRecord(value, ["items", "nextCursor"]) && Array.isArray(value.items) && value.items.length <= 50 && value.items.every(isFolderItem) && (value.nextCursor === null || typeof value.nextCursor === "string" && value.nextCursor.length <= 699 && CURSOR$1.test(value.nextCursor)))) return false;
274
+ return value.items.every((item) => item.workspaceId === workspaceId && item.parentItemId === parentItemId) && isStrictlyOrdered(value.items, (item) => item.itemId);
524
275
  }
525
- async function listFolderChildren(runtime, input) {
526
- const validated = validatePageInput(input);
527
- return execute(runtime, validated.signal, 409, (api, requestOptions) => api.GET("/api/v1/workspaces/{workspaceId}/items/{itemId}/children", {
528
- ...operationOptions("listFolderChildren", { params: {
529
- path: resourcePath(validated),
530
- query: pageQuery(validated)
531
- } }),
532
- ...requestOptions
533
- }), [200], (value) => isFolderPage(value, validated.workspaceId, validated.itemId));
276
+ function isFolderItem(value) {
277
+ return isExactRecord(value, [
278
+ "workspaceId",
279
+ "volumeId",
280
+ "itemId",
281
+ "parentItemId",
282
+ "kind",
283
+ "name",
284
+ "visibility",
285
+ "revision"
286
+ ]) && typeof value.workspaceId === "string" && WORKSPACE_ID$1.test(value.workspaceId) && typeof value.volumeId === "string" && VOLUME_ID.test(value.volumeId) && typeof value.itemId === "string" && ITEM_ID$1.test(value.itemId) && typeof value.parentItemId === "string" && ITEM_ID$1.test(value.parentItemId) && (value.kind === "file" || value.kind === "folder") && isSafeFilename$1(value.name) && (value.visibility === "workspace" || value.visibility === "restricted") && isSafeInteger$1(value.revision);
534
287
  }
535
- async function createFolder(runtime, input) {
536
- const validated = validateCreateFolderInput(input);
537
- return execute(runtime, validated.signal, 409, (api, requestOptions) => api.POST("/api/v1/workspaces/{workspaceId}/items/{itemId}/children", {
538
- ...operationOptions("createFolder", {
539
- params: {
540
- path: resourcePath(validated),
541
- header: { "Idempotency-Key": validated.idempotencyKey }
542
- },
543
- body: { name: validated.name }
544
- }),
545
- ...requestOptions
546
- }), [200, 201], (value) => isFolderItem(value) && value.workspaceId === validated.workspaceId && value.parentItemId === validated.itemId && value.kind === "folder" && value.name === validated.name);
288
+ function isPermissionPage(value, headers, targetItemId) {
289
+ if (!(isExactRecord(value, [
290
+ "items",
291
+ "nextCursor",
292
+ "collectionRevision",
293
+ "collectionEtag"
294
+ ]) && Array.isArray(value.items) && value.items.length <= 50 && value.items.every(isPermissionGrant) && (value.nextCursor === null || typeof value.nextCursor === "string" && value.nextCursor.length <= 699 && CURSOR$1.test(value.nextCursor)) && isSafeInteger$1(value.collectionRevision) && typeof value.collectionEtag === "string" && parseCollectionEtag(value.collectionEtag) === value.collectionRevision)) return false;
295
+ const collectionHeader = headers.get("drive-permission-collection-etag");
296
+ const representationEtag = headers.get("etag");
297
+ const collectionRevision = value.collectionRevision;
298
+ if (!isSafeInteger$1(collectionRevision)) return false;
299
+ return collectionHeader === value.collectionEtag && representationEtag !== null && LIST_ETAG.test(representationEtag) && value.items.every((grant) => (grant.source === "direct" ? grant.sourceItemId === targetItemId : grant.sourceItemId !== targetItemId) && grant.etag === permissionGrantEtag(collectionRevision, grant.revision)) && isStrictlyOrdered(value.items, (grant) => `${grant.sourceItemId}\0${grant.grantId}`);
547
300
  }
548
- async function listItemPermissions(runtime, input) {
549
- const validated = validatePageInput(input);
550
- return execute(runtime, validated.signal, 412, (api, requestOptions) => api.GET("/api/v1/workspaces/{workspaceId}/items/{itemId}/permissions", {
551
- ...operationOptions("listItemPermissions", { params: {
552
- path: resourcePath(validated),
553
- query: pageQuery(validated)
554
- } }),
555
- ...requestOptions
556
- }), [200], (value, _status, headers) => isPermissionPage(value, headers, validated.itemId));
301
+ function isPermissionMutation(value, targetItemId, expectedGrantId) {
302
+ if (!isExactRecord(value, [
303
+ "outcome",
304
+ "grant",
305
+ "collectionRevision"
306
+ ]) || value.outcome !== "committed" && value.outcome !== "replayed" || !isSafeInteger$1(value.collectionRevision)) return false;
307
+ const grant = value.grant;
308
+ return isExactRecord(grant, [
309
+ "source",
310
+ "sourceItemId",
311
+ "grantId",
312
+ "grantee",
313
+ "actions",
314
+ "availability",
315
+ "expiresAtEpochMilliseconds",
316
+ "revision"
317
+ ]) && grant.source === "direct" && typeof grant.sourceItemId === "string" && grant.sourceItemId === targetItemId && typeof grant.grantId === "string" && GRANT_ID$1.test(grant.grantId) && (expectedGrantId === void 0 || grant.grantId === expectedGrantId) && isGrantee(grant.grantee) && isPermissionActions(grant.actions) && [
318
+ "active",
319
+ "expired",
320
+ "revoked"
321
+ ].includes(String(grant.availability)) && (grant.expiresAtEpochMilliseconds === null || isSafeInteger$1(grant.expiresAtEpochMilliseconds)) && isSafeInteger$1(grant.revision);
557
322
  }
558
- async function readEffectiveItemPermissions(runtime, input) {
559
- const validated = validateResourceInput(input);
560
- return execute(runtime, validated.signal, 412, (api, requestOptions) => api.GET("/api/v1/workspaces/{workspaceId}/items/{itemId}/permissions/effective", {
561
- ...operationOptions("readEffectiveItemPermissions", { params: { path: resourcePath(validated) } }),
562
- ...requestOptions
563
- }), [200], (value) => isEffectivePermissionResponse(value, validated.itemId));
564
- }
565
- async function createItemPermission(runtime, input) {
566
- const validated = validateCreatePermissionInput(input);
567
- return execute(runtime, validated.signal, 412, (api, requestOptions) => api.POST("/api/v1/workspaces/{workspaceId}/items/{itemId}/permissions", {
568
- ...operationOptions("createItemPermission", {
569
- params: {
570
- path: resourcePath(validated),
571
- header: {
572
- "Idempotency-Key": validated.idempotencyKey,
573
- "If-Match": validated.ifMatch
574
- }
575
- },
576
- body: {
577
- grantee: validated.grantee,
578
- actions: validated.actions,
579
- expiresAtEpochMilliseconds: validated.expiresAtEpochMilliseconds
580
- }
581
- }),
582
- ...requestOptions
583
- }), [200, 201], (value, status, headers) => isPermissionMutation(value, validated.itemId) && (status === 201 && value.outcome === "committed" || status === 200 && value.outcome === "replayed") && (status === 200 || value.grant.availability !== "revoked") && mutationValidatorsAreCoherent(headers));
323
+ function isEffectivePermissionResponse(value, targetItemId) {
324
+ if (!isExactRecord(value, ["actions"]) || !Array.isArray(value.actions) || value.actions.length !== 7) return false;
325
+ for (let index = 0; index < value.actions.length; index += 1) {
326
+ const action = value.actions[index];
327
+ if (action === void 0 || !isExactRecord(action, [
328
+ "action",
329
+ "allowed",
330
+ "basis",
331
+ "sourceItemId",
332
+ "grantId"
333
+ ]) || action.action !== EFFECTIVE_ACTIONS[index] || typeof action.allowed !== "boolean" || !(action.basis === null || [
334
+ "workspace_visibility",
335
+ "restricted_administrator_bypass",
336
+ "direct_grant",
337
+ "inherited_grant"
338
+ ].includes(String(action.basis))) || !(action.sourceItemId === null || typeof action.sourceItemId === "string" && ITEM_ID$1.test(action.sourceItemId)) || !(action.grantId === null || typeof action.grantId === "string" && GRANT_ID$1.test(action.grantId)) || !effectiveSourceIsCoherent(action, targetItemId)) return false;
339
+ }
340
+ return true;
584
341
  }
585
- async function updateItemPermission(runtime, input) {
586
- const validated = validateUpdatePermissionInput(input);
587
- const body = {};
588
- if (validated.actions !== void 0) body.actions = validated.actions;
589
- if (validated.expiresAtEpochMilliseconds !== void 0) body.expiresAtEpochMilliseconds = validated.expiresAtEpochMilliseconds;
590
- return execute(runtime, validated.signal, 412, (api, requestOptions) => api.PATCH("/api/v1/workspaces/{workspaceId}/items/{itemId}/permissions/{grantId}", {
591
- ...operationOptions("updateItemPermission", {
592
- params: {
593
- path: {
594
- ...resourcePath(validated),
595
- grantId: validated.grantId
596
- },
597
- header: {
598
- "Idempotency-Key": validated.idempotencyKey,
599
- "If-Match": validated.ifMatch
600
- }
601
- },
602
- body
603
- }),
604
- ...requestOptions
605
- }), [200], (value, _status, headers) => isPermissionMutation(value, validated.itemId, validated.grantId) && value.grant.availability !== "revoked" && mutationValidatorsAreCoherent(headers));
342
+ function isUploadQuotaReservation(value) {
343
+ return isExactRecord(value, [
344
+ "workspaceId",
345
+ "uploadId",
346
+ "parentItemId",
347
+ "expectedByteLength",
348
+ "state",
349
+ "terminalReason",
350
+ "createdAtEpochMilliseconds",
351
+ "expiresAtEpochMilliseconds",
352
+ "terminalAtEpochMilliseconds"
353
+ ]) && typeof value.workspaceId === "string" && WORKSPACE_ID$1.test(value.workspaceId) && typeof value.uploadId === "string" && UPLOAD_ID$1.test(value.uploadId) && typeof value.parentItemId === "string" && ITEM_ID$1.test(value.parentItemId) && typeof value.expectedByteLength === "number" && Number.isSafeInteger(value.expectedByteLength) && value.expectedByteLength >= 0 && value.expectedByteLength <= MAX_UPLOAD_BYTE_LENGTH$1 && (value.state === "reserved" || value.state === "converted" || value.state === "released") && (value.terminalReason === null || value.terminalReason === "completed" || value.terminalReason === "aborted" || value.terminalReason === "failed" || value.terminalReason === "expired") && isSafeInteger$1(value.createdAtEpochMilliseconds) && isSafeInteger$1(value.expiresAtEpochMilliseconds) && value.expiresAtEpochMilliseconds - value.createdAtEpochMilliseconds === UPLOAD_QUOTA_EXPIRY_MILLISECONDS && (value.terminalAtEpochMilliseconds === null || isSafeInteger$1(value.terminalAtEpochMilliseconds) && value.terminalAtEpochMilliseconds >= value.createdAtEpochMilliseconds);
606
354
  }
607
- async function revokeItemPermission(runtime, input) {
608
- const validated = validateGrantMutationInput(input);
609
- return execute(runtime, validated.signal, 412, (api, requestOptions) => api.DELETE("/api/v1/workspaces/{workspaceId}/items/{itemId}/permissions/{grantId}", {
610
- ...operationOptions("revokeItemPermission", { params: {
611
- path: {
612
- ...resourcePath(validated),
613
- grantId: validated.grantId
614
- },
615
- header: {
616
- "Idempotency-Key": validated.idempotencyKey,
617
- "If-Match": validated.ifMatch
618
- }
619
- } }),
620
- ...requestOptions
621
- }), [200], (value, _status, headers) => isPermissionMutation(value, validated.itemId, validated.grantId) && value.grant.availability === "revoked" && parseCollectionEtag(headers.get("drive-permission-collection-etag") ?? "") !== void 0 && headers.get("drive-permission-grant-etag") === null);
355
+ function mutationValidatorsAreCoherent(headers) {
356
+ const collection = headers.get("drive-permission-collection-etag");
357
+ const grant = headers.get("drive-permission-grant-etag");
358
+ const collectionRevision = collection === null ? void 0 : parseCollectionEtag(collection);
359
+ if (collectionRevision === void 0) return false;
360
+ if (grant === null) return true;
361
+ const currentGrant = parseGrantEtag(grant);
362
+ return currentGrant !== void 0 && currentGrant[0] === collectionRevision;
622
363
  }
623
- async function createUpload(runtime, input) {
624
- const validated = validateCreateUploadInput(input);
625
- return execute(runtime, validated.signal, 409, (api, requestOptions) => api.POST("/api/v1/workspaces/{workspaceId}/uploads", {
626
- ...operationOptions("createUpload", {
627
- params: {
628
- path: { workspaceId: validated.workspaceId },
629
- header: { "Idempotency-Key": validated.idempotencyKey }
630
- },
631
- body: {
632
- parentItemId: validated.parentItemId,
633
- expectedByteLength: validated.expectedByteLength
634
- }
635
- }),
636
- ...requestOptions
637
- }), [200, 201], (value) => isUploadQuotaReservation(value) && value.workspaceId === validated.workspaceId && value.parentItemId === validated.parentItemId && value.expectedByteLength === validated.expectedByteLength && value.state === "reserved" && value.terminalReason === null && value.terminalAtEpochMilliseconds === null, true);
364
+ function isPermissionGrant(value) {
365
+ return isExactRecord(value, [
366
+ "source",
367
+ "sourceItemId",
368
+ "grantId",
369
+ "grantee",
370
+ "actions",
371
+ "availability",
372
+ "expiresAtEpochMilliseconds",
373
+ "revision",
374
+ "etag"
375
+ ]) && (value.source === "direct" || value.source === "inherited") && typeof value.sourceItemId === "string" && ITEM_ID$1.test(value.sourceItemId) && typeof value.grantId === "string" && GRANT_ID$1.test(value.grantId) && isGrantee(value.grantee) && isPermissionActions(value.actions) && [
376
+ "active",
377
+ "expired",
378
+ "revoked"
379
+ ].includes(String(value.availability)) && (value.expiresAtEpochMilliseconds === null || isSafeInteger$1(value.expiresAtEpochMilliseconds)) && isSafeInteger$1(value.revision) && typeof value.etag === "string" && GRANT_ETAG.test(value.etag);
638
380
  }
639
- async function abortUpload(runtime, input) {
640
- const validated = validateAbortUploadInput(input);
641
- return execute(runtime, validated.signal, 409, (api, requestOptions) => api.DELETE("/api/v1/workspaces/{workspaceId}/uploads/{uploadId}", {
642
- ...operationOptions("abortUpload", { params: { path: {
643
- workspaceId: validated.workspaceId,
644
- uploadId: validated.uploadId
645
- } } }),
646
- ...requestOptions
647
- }), [200], (value) => isUploadQuotaReservation(value) && value.workspaceId === validated.workspaceId && value.uploadId === validated.uploadId && value.state === "released" && value.terminalReason === "aborted" && value.terminalAtEpochMilliseconds !== null);
381
+ function isGrantee(value) {
382
+ return isExactRecord(value, ["type", "userId"]) && value.type === "user" && typeof value.userId === "string" && AUTH_ID$1.test(value.userId) || isExactRecord(value, ["type", "teamId"]) && value.type === "team" && typeof value.teamId === "string" && AUTH_ID$1.test(value.teamId);
648
383
  }
649
- async function execute(runtime, signal, preconditionStatus, send, successStatuses, guard, allowsQuotaExceeded = false) {
650
- const operation = createOperationSignal(signal, runtime.timeoutMilliseconds);
651
- try {
652
- const operationSignal = operation.signal;
653
- throwIfAborted(operationSignal);
654
- const credential = await obtainCredential(runtime.getCredential, operationSignal);
655
- let capturedResponse;
656
- await send(createClient({
657
- baseUrl: runtime.baseUrl,
658
- fetch: async (request) => {
659
- try {
660
- const response = await raceWithAbort(runtime.fetchImplementation(request), operationSignal);
661
- if (!(response instanceof Response)) throw new DriveError("invalid_response");
662
- capturedResponse = response;
663
- } catch (error) {
664
- if (error instanceof DriveError) throw error;
665
- throw operationSignal.aborted ? new DriveError("aborted") : new DriveError("network_error");
666
- }
667
- return new Response(null, { status: 204 });
668
- }
669
- }), {
670
- cache: "no-store",
671
- credentials: "omit",
672
- headers: new Headers({ authorization: `Bearer ${credential}` }),
673
- signal: operationSignal
674
- });
675
- if (!capturedResponse) throw new DriveError("network_error");
676
- const response = capturedResponse;
677
- const requestId = safeRequestId(response.headers.get("x-request-id"));
678
- if (requestId === null) {
679
- cancelResponse(response);
680
- throw new DriveError("invalid_response", { status: response.status });
681
- }
682
- if (!successStatuses.includes(response.status)) throw await normalizeError(response, requestId, operationSignal, preconditionStatus, allowsQuotaExceeded);
683
- const { value } = await parseBoundedJson(response, SUCCESS_BODY_LIMIT, response.status, requestId, operationSignal);
684
- const status = response.status;
685
- if (!guard(value, status, response.headers)) throw new DriveError("invalid_response", {
686
- status,
687
- requestId
688
- });
689
- return Object.freeze({
690
- data: value,
691
- status,
692
- requestId,
693
- validators: readValidators(response.headers)
694
- });
695
- } catch (error) {
696
- if (operation.didTimeout()) throw new DriveError("timeout");
697
- if (error instanceof DriveError) throw error;
698
- throw operation.signal.aborted ? new DriveError("aborted") : new DriveError("network_error");
699
- } finally {
700
- operation.dispose();
701
- }
384
+ function isPermissionActions(value) {
385
+ return Array.isArray(value) && value.length >= 1 && value.length <= 6 && new Set(value).size === value.length && value.every((action) => PERMISSION_ACTIONS$1.includes(action)) && value.every((action, index) => index === 0 || PERMISSION_ACTIONS$1.indexOf(action) > PERMISSION_ACTIONS$1.indexOf(value[index - 1]));
702
386
  }
703
- function parseTimeoutMilliseconds(value) {
704
- if (value === void 0) return DEFAULT_REQUEST_TIMEOUT_MILLISECONDS;
705
- if (!Number.isSafeInteger(value) || value < 1 || value > MAX_REQUEST_TIMEOUT_MILLISECONDS) throw new DriveError("invalid_input");
706
- return value;
387
+ function effectiveSourceIsCoherent(action, targetItemId) {
388
+ if (!action.allowed) return action.basis === null && action.sourceItemId === null && action.grantId === null;
389
+ if (action.basis === "workspace_visibility" || action.basis === "restricted_administrator_bypass") return action.sourceItemId === null && action.grantId === null;
390
+ if (action.basis === "direct_grant") return action.sourceItemId === targetItemId && typeof action.grantId === "string";
391
+ if (action.basis === "inherited_grant") return typeof action.sourceItemId === "string" && action.sourceItemId !== targetItemId && typeof action.grantId === "string";
392
+ return false;
707
393
  }
708
- function createOperationSignal(callerSignal, timeoutMilliseconds) {
709
- const controller = new AbortController();
710
- let timedOut = false;
711
- const onAbort = () => controller.abort();
712
- if (callerSignal?.aborted) controller.abort();
713
- else callerSignal?.addEventListener("abort", onAbort, { once: true });
714
- const timeout = setTimeout(() => {
715
- timedOut = true;
716
- controller.abort();
717
- }, timeoutMilliseconds);
718
- return {
719
- signal: controller.signal,
720
- didTimeout: () => timedOut,
721
- dispose: () => {
722
- clearTimeout(timeout);
723
- callerSignal?.removeEventListener("abort", onAbort);
724
- }
725
- };
394
+ function permissionGrantEtag(collectionRevision, grantRevision) {
395
+ return `"drive.permission-grant.v1.${collectionRevision}.${grantRevision}"`;
726
396
  }
727
- function parseBaseUrl(value) {
728
- if (typeof value !== "string") throw new TypeError("Drive base URL is invalid");
729
- let url;
730
- try {
731
- url = new URL(value);
732
- } catch {
733
- throw new TypeError("Drive base URL is invalid");
397
+ function isSafeFilename$1(value) {
398
+ if (typeof value !== "string" || value.length === 0 || value.length > 510 || value.normalize("NFC") !== value || value.endsWith(".") || value.endsWith(" ") || filenameIsReserved$1(value)) return false;
399
+ let codePoints = 0;
400
+ let bytes = 0;
401
+ for (const character of value) {
402
+ const point = character.codePointAt(0);
403
+ if (point === void 0 || point === 47 || point === 92 || point <= 31 || point >= 127 && point <= 159 || point === 8232 || point === 8233 || point >= 55296 && point <= 57343) return false;
404
+ codePoints += 1;
405
+ bytes += point <= 127 ? 1 : point <= 2047 ? 2 : point <= 65535 ? 3 : 4;
406
+ if (codePoints > 255 || bytes > 255) return false;
734
407
  }
735
- const localHttp = url.protocol === "http:" && [
736
- "localhost",
737
- "127.0.0.1",
738
- "[::1]"
739
- ].includes(url.hostname);
740
- if (url.protocol !== "https:" && !localHttp || url.username || url.password || url.search || url.hash || !url.host) throw new TypeError("Drive base URL is invalid");
741
- return joinURL(url.origin, url.pathname);
408
+ return true;
742
409
  }
743
- async function obtainCredential(provider, signal) {
744
- throwIfAborted(signal);
745
- let credential;
746
- try {
747
- credential = await raceWithAbort(provider({ signal }), signal);
748
- } catch (error) {
749
- if (error instanceof DriveError && error.code === "aborted") throw error;
750
- throw signal.aborted ? new DriveError("aborted") : new DriveError("credential_unavailable");
751
- }
752
- if (typeof credential !== "string" || credential.length < 1 || credential.length > 16384 || !/^[\x21-\x7e]+$/.test(credential)) throw new DriveError("credential_unavailable");
753
- return credential;
410
+ function filenameIsReserved$1(value) {
411
+ const base = value.split(".", 1)[0].toUpperCase();
412
+ return value === "." || value === ".." || [
413
+ "CON",
414
+ "PRN",
415
+ "AUX",
416
+ "NUL",
417
+ "CLOCK$",
418
+ "CONIN$",
419
+ "CONOUT$"
420
+ ].includes(base) || /^COM[1-9]$/.test(base) || /^LPT[1-9]$/.test(base);
754
421
  }
755
- function throwIfAborted(signal) {
756
- if (signal.aborted) throw new DriveError("aborted");
422
+ function isStrictlyOrdered(values, key) {
423
+ return values.every((value, index) => index === 0 || key(values[index - 1]) < key(value));
757
424
  }
758
- async function raceWithAbort(promise, signal) {
759
- throwIfAborted(signal);
760
- let removeAbort = () => {};
761
- const aborted = new Promise((_, reject) => {
762
- const onAbort = () => reject(new DriveError("aborted"));
763
- signal.addEventListener("abort", onAbort, { once: true });
764
- removeAbort = () => signal.removeEventListener("abort", onAbort);
765
- });
766
- try {
767
- return await Promise.race([promise, aborted]);
768
- } finally {
769
- removeAbort();
770
- }
425
+ function isSafeInteger$1(value) {
426
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
427
+ }
428
+ function isExactRecord(value, keys) {
429
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
430
+ return Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
771
431
  }
432
+ //#endregion
433
+ //#region src/internal/client/request.ts
434
+ const ITEM_ID = /^drive\.item\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
435
+ const WORKSPACE_ID = /^drive\.workspace\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
436
+ const GRANT_ID = /^drive\.grant\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
437
+ const UPLOAD_ID = /^drive\.upload\.[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
438
+ const CURSOR = /^drive\.cursor\.v1\.[A-Za-z0-9_-]+$/;
439
+ const AUTH_ID = /^[A-Za-z0-9_-]{1,128}$/;
440
+ const PERMISSION_ACTIONS = Object.freeze([
441
+ "items:content:read",
442
+ "items:create",
443
+ "items:delete",
444
+ "items:metadata:read",
445
+ "items:permissions:read",
446
+ "items:update"
447
+ ]);
448
+ const IDEMPOTENCY_KEY = /^[A-Za-z0-9_-]{16,128}$/;
449
+ const MAX_UPLOAD_BYTE_LENGTH = 33554432e4;
772
450
  function validateResourceInput(value) {
773
451
  const input = captureRecord(value, ["workspaceId", "itemId"], ["signal"]);
774
452
  const workspaceId = input.workspaceId;
@@ -915,418 +593,803 @@ function validateCreateUploadInput(value) {
915
593
  ...signal === void 0 ? {} : { signal }
916
594
  };
917
595
  }
918
- function validateAbortUploadInput(value) {
919
- const input = captureRecord(value, ["workspaceId", "uploadId"], ["signal"]);
920
- const signal = validateSignal(input.signal);
921
- if (typeof input.workspaceId !== "string" || !WORKSPACE_ID.test(input.workspaceId) || typeof input.uploadId !== "string" || !UPLOAD_ID.test(input.uploadId)) throw new DriveError("invalid_input");
922
- return {
923
- workspaceId: input.workspaceId,
924
- uploadId: input.uploadId,
925
- ...signal === void 0 ? {} : { signal }
926
- };
596
+ function validateAbortUploadInput(value) {
597
+ const input = captureRecord(value, ["workspaceId", "uploadId"], ["signal"]);
598
+ const signal = validateSignal(input.signal);
599
+ if (typeof input.workspaceId !== "string" || !WORKSPACE_ID.test(input.workspaceId) || typeof input.uploadId !== "string" || !UPLOAD_ID.test(input.uploadId)) throw new DriveError("invalid_input");
600
+ return {
601
+ workspaceId: input.workspaceId,
602
+ uploadId: input.uploadId,
603
+ ...signal === void 0 ? {} : { signal }
604
+ };
605
+ }
606
+ function resourcePath(input) {
607
+ return {
608
+ workspaceId: input.workspaceId,
609
+ itemId: input.itemId
610
+ };
611
+ }
612
+ function pageQuery(input) {
613
+ return {
614
+ ...input.limit === void 0 ? {} : { limit: input.limit },
615
+ ...input.cursor === void 0 ? {} : { cursor: input.cursor }
616
+ };
617
+ }
618
+ function operationOptions(_operationId, options) {
619
+ return options;
620
+ }
621
+ function captureRecord(value, required, optional) {
622
+ try {
623
+ if (typeof value !== "object" && typeof value !== "function" || value === null || Array.isArray(value)) throw new DriveError("invalid_input");
624
+ const descriptors = Object.getOwnPropertyDescriptors(value);
625
+ const keys = Reflect.ownKeys(descriptors);
626
+ const allowed = new Set([...required, ...optional]);
627
+ if (keys.some((key) => typeof key !== "string" || !allowed.has(key)) || required.some((key) => !Object.hasOwn(descriptors, key))) throw new DriveError("invalid_input");
628
+ const result = {};
629
+ for (const key of keys) {
630
+ if (typeof key !== "string") throw new DriveError("invalid_input");
631
+ const descriptor = descriptors[key];
632
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) throw new DriveError("invalid_input");
633
+ result[key] = descriptor.value;
634
+ }
635
+ return result;
636
+ } catch (error) {
637
+ if (error instanceof DriveError) throw error;
638
+ throw new DriveError("invalid_input");
639
+ }
640
+ }
641
+ function validateSignal(value) {
642
+ if (value === void 0) return void 0;
643
+ if (!(value instanceof AbortSignal)) throw new DriveError("invalid_input");
644
+ return value;
645
+ }
646
+ function validateGrantee(value) {
647
+ const type = captureRecord(value, ["type"], ["userId", "teamId"]);
648
+ if (type.type === "user") {
649
+ const user = captureRecord(value, ["type", "userId"], []);
650
+ if (typeof user.userId === "string" && AUTH_ID.test(user.userId)) return {
651
+ type: "user",
652
+ userId: user.userId
653
+ };
654
+ }
655
+ if (type.type === "team") {
656
+ const team = captureRecord(value, ["type", "teamId"], []);
657
+ if (typeof team.teamId === "string" && AUTH_ID.test(team.teamId)) return {
658
+ type: "team",
659
+ teamId: team.teamId
660
+ };
661
+ }
662
+ throw new DriveError("invalid_input");
663
+ }
664
+ function validateInputActions(value) {
665
+ const actions = captureArray(value);
666
+ if (actions.length < 1 || actions.length > PERMISSION_ACTIONS.length || new Set(actions).size !== actions.length || !actions.every((action) => PERMISSION_ACTIONS.includes(action))) throw new DriveError("invalid_input");
667
+ return actions;
668
+ }
669
+ function captureArray(value) {
670
+ try {
671
+ if (!Array.isArray(value)) throw new DriveError("invalid_input");
672
+ const descriptors = Object.getOwnPropertyDescriptors(value);
673
+ if (Reflect.ownKeys(descriptors).some((key) => typeof key !== "string")) throw new DriveError("invalid_input");
674
+ const result = [];
675
+ for (let index = 0; index < value.length; index += 1) {
676
+ const descriptor = descriptors[String(index)];
677
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) throw new DriveError("invalid_input");
678
+ result.push(descriptor.value);
679
+ }
680
+ if (Reflect.ownKeys(descriptors).length !== value.length + 1) throw new DriveError("invalid_input");
681
+ return result;
682
+ } catch (error) {
683
+ if (error instanceof DriveError) throw error;
684
+ throw new DriveError("invalid_input");
685
+ }
686
+ }
687
+ function isIdempotencyKey(value) {
688
+ return typeof value === "string" && IDEMPOTENCY_KEY.test(value);
689
+ }
690
+ function isExpiry(value) {
691
+ return value === null || isSafeInteger(value);
692
+ }
693
+ function isSafeFilename(value) {
694
+ if (typeof value !== "string" || value.length === 0 || value.length > 510 || value.normalize("NFC") !== value || value.endsWith(".") || value.endsWith(" ") || filenameIsReserved(value)) return false;
695
+ let codePoints = 0;
696
+ let bytes = 0;
697
+ for (const character of value) {
698
+ const point = character.codePointAt(0);
699
+ if (point === void 0 || point === 47 || point === 92 || point <= 31 || point >= 127 && point <= 159 || point === 8232 || point === 8233 || point >= 55296 && point <= 57343) return false;
700
+ codePoints += 1;
701
+ bytes += point <= 127 ? 1 : point <= 2047 ? 2 : point <= 65535 ? 3 : 4;
702
+ if (codePoints > 255 || bytes > 255) return false;
703
+ }
704
+ return true;
705
+ }
706
+ function filenameIsReserved(value) {
707
+ const base = value.split(".", 1)[0].toUpperCase();
708
+ return value === "." || value === ".." || [
709
+ "CON",
710
+ "PRN",
711
+ "AUX",
712
+ "NUL",
713
+ "CLOCK$",
714
+ "CONIN$",
715
+ "CONOUT$"
716
+ ].includes(base) || /^COM[1-9]$/.test(base) || /^LPT[1-9]$/.test(base);
717
+ }
718
+ function isSafeInteger(value) {
719
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
720
+ }
721
+ //#endregion
722
+ //#region ../../node_modules/.pnpm/openapi-fetch@0.17.0/node_modules/openapi-fetch/dist/index.mjs
723
+ const PATH_PARAM_RE = /\{[^{}]+\}/g;
724
+ const supportsRequestInitExt = () => {
725
+ return typeof process === "object" && Number.parseInt(process?.versions?.node?.substring(0, 2)) >= 18 && process.versions.undici;
726
+ };
727
+ function randomID() {
728
+ return Math.random().toString(36).slice(2, 11);
729
+ }
730
+ function createClient(clientOptions) {
731
+ let { baseUrl = "", Request: CustomRequest = globalThis.Request, fetch: baseFetch = globalThis.fetch, querySerializer: globalQuerySerializer, bodySerializer: globalBodySerializer, pathSerializer: globalPathSerializer, headers: baseHeaders, requestInitExt = void 0, ...baseOptions } = { ...clientOptions };
732
+ requestInitExt = supportsRequestInitExt() ? requestInitExt : void 0;
733
+ baseUrl = removeTrailingSlash(baseUrl);
734
+ const globalMiddlewares = [];
735
+ async function coreFetch(schemaPath, fetchOptions) {
736
+ const { baseUrl: localBaseUrl, fetch = baseFetch, Request = CustomRequest, headers, params = {}, parseAs = "json", querySerializer: requestQuerySerializer, bodySerializer = globalBodySerializer ?? defaultBodySerializer, pathSerializer: requestPathSerializer, body, middleware: requestMiddlewares = [], ...init } = fetchOptions || {};
737
+ let finalBaseUrl = baseUrl;
738
+ if (localBaseUrl) finalBaseUrl = removeTrailingSlash(localBaseUrl) ?? baseUrl;
739
+ let querySerializer = typeof globalQuerySerializer === "function" ? globalQuerySerializer : createQuerySerializer(globalQuerySerializer);
740
+ if (requestQuerySerializer) querySerializer = typeof requestQuerySerializer === "function" ? requestQuerySerializer : createQuerySerializer({
741
+ ...typeof globalQuerySerializer === "object" ? globalQuerySerializer : {},
742
+ ...requestQuerySerializer
743
+ });
744
+ const pathSerializer = requestPathSerializer || globalPathSerializer || defaultPathSerializer;
745
+ const serializedBody = body === void 0 ? void 0 : bodySerializer(body, mergeHeaders(baseHeaders, headers, params.header));
746
+ const finalHeaders = mergeHeaders(serializedBody === void 0 || serializedBody instanceof FormData ? {} : { "Content-Type": "application/json" }, baseHeaders, headers, params.header);
747
+ const finalMiddlewares = [...globalMiddlewares, ...requestMiddlewares];
748
+ const requestInit = {
749
+ redirect: "follow",
750
+ ...baseOptions,
751
+ ...init,
752
+ body: serializedBody,
753
+ headers: finalHeaders
754
+ };
755
+ let id;
756
+ let options;
757
+ let request = new Request(createFinalURL(schemaPath, {
758
+ baseUrl: finalBaseUrl,
759
+ params,
760
+ querySerializer,
761
+ pathSerializer
762
+ }), requestInit);
763
+ let response;
764
+ for (const key in init) if (!(key in request)) request[key] = init[key];
765
+ if (finalMiddlewares.length) {
766
+ id = randomID();
767
+ options = Object.freeze({
768
+ baseUrl: finalBaseUrl,
769
+ fetch,
770
+ parseAs,
771
+ querySerializer,
772
+ bodySerializer,
773
+ pathSerializer
774
+ });
775
+ for (const m of finalMiddlewares) if (m && typeof m === "object" && typeof m.onRequest === "function") {
776
+ const result = await m.onRequest({
777
+ request,
778
+ schemaPath,
779
+ params,
780
+ options,
781
+ id
782
+ });
783
+ if (result) if (result instanceof Request) request = result;
784
+ else if (result instanceof Response) {
785
+ response = result;
786
+ break;
787
+ } else throw new Error("onRequest: must return new Request() or Response() when modifying the request");
788
+ }
789
+ }
790
+ if (!response) {
791
+ try {
792
+ response = await fetch(request, requestInitExt);
793
+ } catch (error2) {
794
+ let errorAfterMiddleware = error2;
795
+ if (finalMiddlewares.length) for (let i = finalMiddlewares.length - 1; i >= 0; i--) {
796
+ const m = finalMiddlewares[i];
797
+ if (m && typeof m === "object" && typeof m.onError === "function") {
798
+ const result = await m.onError({
799
+ request,
800
+ error: errorAfterMiddleware,
801
+ schemaPath,
802
+ params,
803
+ options,
804
+ id
805
+ });
806
+ if (result) {
807
+ if (result instanceof Response) {
808
+ errorAfterMiddleware = void 0;
809
+ response = result;
810
+ break;
811
+ }
812
+ if (result instanceof Error) {
813
+ errorAfterMiddleware = result;
814
+ continue;
815
+ }
816
+ throw new Error("onError: must return new Response() or instance of Error");
817
+ }
818
+ }
819
+ }
820
+ if (errorAfterMiddleware) throw errorAfterMiddleware;
821
+ }
822
+ if (finalMiddlewares.length) for (let i = finalMiddlewares.length - 1; i >= 0; i--) {
823
+ const m = finalMiddlewares[i];
824
+ if (m && typeof m === "object" && typeof m.onResponse === "function") {
825
+ const result = await m.onResponse({
826
+ request,
827
+ response,
828
+ schemaPath,
829
+ params,
830
+ options,
831
+ id
832
+ });
833
+ if (result) {
834
+ if (!(result instanceof Response)) throw new Error("onResponse: must return new Response() when modifying the response");
835
+ response = result;
836
+ }
837
+ }
838
+ }
839
+ }
840
+ const contentLength = response.headers.get("Content-Length");
841
+ if (response.status === 204 || request.method === "HEAD" || contentLength === "0" && !response.headers.get("Transfer-Encoding")?.includes("chunked")) return response.ok ? {
842
+ data: void 0,
843
+ response
844
+ } : {
845
+ error: void 0,
846
+ response
847
+ };
848
+ if (response.ok) {
849
+ const getResponseData = async () => {
850
+ if (parseAs === "stream") return response.body;
851
+ if (parseAs === "json" && !contentLength) {
852
+ const raw = await response.text();
853
+ return raw ? JSON.parse(raw) : void 0;
854
+ }
855
+ return await response[parseAs]();
856
+ };
857
+ return {
858
+ data: await getResponseData(),
859
+ response
860
+ };
861
+ }
862
+ let error = await response.text();
863
+ try {
864
+ error = JSON.parse(error);
865
+ } catch {}
866
+ return {
867
+ error,
868
+ response
869
+ };
870
+ }
871
+ return {
872
+ request(method, url, init) {
873
+ return coreFetch(url, {
874
+ ...init,
875
+ method: method.toUpperCase()
876
+ });
877
+ },
878
+ GET(url, init) {
879
+ return coreFetch(url, {
880
+ ...init,
881
+ method: "GET"
882
+ });
883
+ },
884
+ PUT(url, init) {
885
+ return coreFetch(url, {
886
+ ...init,
887
+ method: "PUT"
888
+ });
889
+ },
890
+ POST(url, init) {
891
+ return coreFetch(url, {
892
+ ...init,
893
+ method: "POST"
894
+ });
895
+ },
896
+ DELETE(url, init) {
897
+ return coreFetch(url, {
898
+ ...init,
899
+ method: "DELETE"
900
+ });
901
+ },
902
+ OPTIONS(url, init) {
903
+ return coreFetch(url, {
904
+ ...init,
905
+ method: "OPTIONS"
906
+ });
907
+ },
908
+ HEAD(url, init) {
909
+ return coreFetch(url, {
910
+ ...init,
911
+ method: "HEAD"
912
+ });
913
+ },
914
+ PATCH(url, init) {
915
+ return coreFetch(url, {
916
+ ...init,
917
+ method: "PATCH"
918
+ });
919
+ },
920
+ TRACE(url, init) {
921
+ return coreFetch(url, {
922
+ ...init,
923
+ method: "TRACE"
924
+ });
925
+ },
926
+ use(...middleware) {
927
+ for (const m of middleware) {
928
+ if (!m) continue;
929
+ if (typeof m !== "object" || !("onRequest" in m || "onResponse" in m || "onError" in m)) throw new Error("Middleware must be an object with one of `onRequest()`, `onResponse() or `onError()`");
930
+ globalMiddlewares.push(m);
931
+ }
932
+ },
933
+ eject(...middleware) {
934
+ for (const m of middleware) {
935
+ const i = globalMiddlewares.indexOf(m);
936
+ if (i !== -1) globalMiddlewares.splice(i, 1);
937
+ }
938
+ }
939
+ };
940
+ }
941
+ function serializePrimitiveParam(name, value, options) {
942
+ if (value === void 0 || value === null) return "";
943
+ if (typeof value === "object") throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");
944
+ return `${name}=${options?.allowReserved === true ? value : encodeURIComponent(value)}`;
927
945
  }
928
- function captureRecord(value, required, optional) {
929
- try {
930
- if (typeof value !== "object" && typeof value !== "function" || value === null || Array.isArray(value)) throw new DriveError("invalid_input");
931
- const descriptors = Object.getOwnPropertyDescriptors(value);
932
- const keys = Reflect.ownKeys(descriptors);
933
- const allowed = new Set([...required, ...optional]);
934
- if (keys.some((key) => typeof key !== "string" || !allowed.has(key)) || required.some((key) => !Object.hasOwn(descriptors, key))) throw new DriveError("invalid_input");
935
- const result = {};
936
- for (const key of keys) {
937
- if (typeof key !== "string") throw new DriveError("invalid_input");
938
- const descriptor = descriptors[key];
939
- if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) throw new DriveError("invalid_input");
940
- result[key] = descriptor.value;
946
+ function serializeObjectParam(name, value, options) {
947
+ if (!value || typeof value !== "object") return "";
948
+ const values = [];
949
+ const joiner = {
950
+ simple: ",",
951
+ label: ".",
952
+ matrix: ";"
953
+ }[options.style] || "&";
954
+ if (options.style !== "deepObject" && options.explode === false) {
955
+ for (const k in value) values.push(k, options.allowReserved === true ? value[k] : encodeURIComponent(value[k]));
956
+ const final2 = values.join(",");
957
+ switch (options.style) {
958
+ case "form": return `${name}=${final2}`;
959
+ case "label": return `.${final2}`;
960
+ case "matrix": return `;${name}=${final2}`;
961
+ default: return final2;
941
962
  }
942
- return result;
943
- } catch (error) {
944
- if (error instanceof DriveError) throw error;
945
- throw new DriveError("invalid_input");
946
- }
947
- }
948
- function validateSignal(value) {
949
- if (value === void 0) return void 0;
950
- if (!(value instanceof AbortSignal)) throw new DriveError("invalid_input");
951
- return value;
952
- }
953
- function validateGrantee(value) {
954
- const type = captureRecord(value, ["type"], ["userId", "teamId"]);
955
- if (type.type === "user") {
956
- const user = captureRecord(value, ["type", "userId"], []);
957
- if (typeof user.userId === "string" && AUTH_ID.test(user.userId)) return {
958
- type: "user",
959
- userId: user.userId
960
- };
961
963
  }
962
- if (type.type === "team") {
963
- const team = captureRecord(value, ["type", "teamId"], []);
964
- if (typeof team.teamId === "string" && AUTH_ID.test(team.teamId)) return {
965
- type: "team",
966
- teamId: team.teamId
967
- };
964
+ for (const k in value) {
965
+ const finalName = options.style === "deepObject" ? `${name}[${k}]` : k;
966
+ values.push(serializePrimitiveParam(finalName, value[k], options));
968
967
  }
969
- throw new DriveError("invalid_input");
970
- }
971
- function validateInputActions(value) {
972
- const actions = captureArray(value);
973
- if (actions.length < 1 || actions.length > PERMISSION_ACTIONS.length || new Set(actions).size !== actions.length || !actions.every((action) => PERMISSION_ACTIONS.includes(action))) throw new DriveError("invalid_input");
974
- return actions;
968
+ const final = values.join(joiner);
969
+ return options.style === "label" || options.style === "matrix" ? `${joiner}${final}` : final;
975
970
  }
976
- function captureArray(value) {
977
- try {
978
- if (!Array.isArray(value)) throw new DriveError("invalid_input");
979
- const descriptors = Object.getOwnPropertyDescriptors(value);
980
- if (Reflect.ownKeys(descriptors).some((key) => typeof key !== "string")) throw new DriveError("invalid_input");
981
- const result = [];
982
- for (let index = 0; index < value.length; index += 1) {
983
- const descriptor = descriptors[String(index)];
984
- if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) throw new DriveError("invalid_input");
985
- result.push(descriptor.value);
971
+ function serializeArrayParam(name, value, options) {
972
+ if (!Array.isArray(value)) return "";
973
+ if (options.explode === false) {
974
+ const joiner2 = {
975
+ form: ",",
976
+ spaceDelimited: "%20",
977
+ pipeDelimited: "|"
978
+ }[options.style] || ",";
979
+ const final = (options.allowReserved === true ? value : value.map((v) => encodeURIComponent(v))).join(joiner2);
980
+ switch (options.style) {
981
+ case "simple": return final;
982
+ case "label": return `.${final}`;
983
+ case "matrix": return `;${name}=${final}`;
984
+ default: return `${name}=${final}`;
986
985
  }
987
- if (Reflect.ownKeys(descriptors).length !== value.length + 1) throw new DriveError("invalid_input");
988
- return result;
989
- } catch (error) {
990
- if (error instanceof DriveError) throw error;
991
- throw new DriveError("invalid_input");
992
986
  }
987
+ const joiner = {
988
+ simple: ",",
989
+ label: ".",
990
+ matrix: ";"
991
+ }[options.style] || "&";
992
+ const values = [];
993
+ for (const v of value) if (options.style === "simple" || options.style === "label") values.push(options.allowReserved === true ? v : encodeURIComponent(v));
994
+ else values.push(serializePrimitiveParam(name, v, options));
995
+ return options.style === "label" || options.style === "matrix" ? `${joiner}${values.join(joiner)}` : values.join(joiner);
993
996
  }
994
- function isIdempotencyKey(value) {
995
- return typeof value === "string" && IDEMPOTENCY_KEY.test(value);
996
- }
997
- function isExpiry(value) {
998
- return value === null || isSafeInteger(value);
999
- }
1000
- function resourcePath(input) {
1001
- return {
1002
- workspaceId: input.workspaceId,
1003
- itemId: input.itemId
1004
- };
1005
- }
1006
- function pageQuery(input) {
1007
- return {
1008
- ...input.limit === void 0 ? {} : { limit: input.limit },
1009
- ...input.cursor === void 0 ? {} : { cursor: input.cursor }
997
+ function createQuerySerializer(options) {
998
+ return function querySerializer(queryParams) {
999
+ const search = [];
1000
+ if (queryParams && typeof queryParams === "object") for (const name in queryParams) {
1001
+ const value = queryParams[name];
1002
+ if (value === void 0 || value === null) continue;
1003
+ if (Array.isArray(value)) {
1004
+ if (value.length === 0) continue;
1005
+ search.push(serializeArrayParam(name, value, {
1006
+ style: "form",
1007
+ explode: true,
1008
+ ...options?.array,
1009
+ allowReserved: options?.allowReserved || false
1010
+ }));
1011
+ continue;
1012
+ }
1013
+ if (typeof value === "object") {
1014
+ search.push(serializeObjectParam(name, value, {
1015
+ style: "deepObject",
1016
+ explode: true,
1017
+ ...options?.object,
1018
+ allowReserved: options?.allowReserved || false
1019
+ }));
1020
+ continue;
1021
+ }
1022
+ search.push(serializePrimitiveParam(name, value, options));
1023
+ }
1024
+ return search.join("&");
1010
1025
  };
1011
1026
  }
1012
- function operationOptions(_operationId, options) {
1013
- return options;
1014
- }
1015
- async function normalizeError(response, headerRequestId, signal, preconditionStatus, allowsQuotaExceeded) {
1016
- let parsed;
1017
- try {
1018
- parsed = await parseBoundedJson(response, ERROR_BODY_LIMIT, response.status, headerRequestId, signal);
1019
- } catch (error) {
1020
- if (error instanceof DriveError && error.code === "aborted") return error;
1021
- return new DriveError("invalid_response", {
1022
- status: response.status,
1023
- requestId: headerRequestId
1024
- });
1025
- }
1026
- const value = parsed.value;
1027
- if (!isExactRecord(value, ["error"]) || !isExactRecord(value.error, [
1028
- "code",
1029
- "message",
1030
- "requestId",
1031
- "details"
1032
- ])) return new DriveError("invalid_response", {
1033
- status: response.status,
1034
- requestId: headerRequestId
1035
- });
1036
- const envelope = value.error;
1037
- if (typeof envelope.code !== "string" || !DRIVE_API_ERROR_CODES.includes(envelope.code) || envelope.message !== DRIVE_API_ERROR_MESSAGES[envelope.code] || !REQUEST_ID.test(String(envelope.requestId)) || envelope.requestId !== headerRequestId || !isExactRecord(envelope.details, []) || !errorStatusMatches(envelope.code, response.status, preconditionStatus, allowsQuotaExceeded) || parsed.text !== JSON.stringify(value)) return new DriveError("invalid_response", {
1038
- status: response.status,
1039
- requestId: headerRequestId
1040
- });
1041
- return new DriveError(envelope.code, {
1042
- status: response.status,
1043
- requestId: envelope.requestId
1044
- });
1045
- }
1046
- function errorStatusMatches(code, status, preconditionStatus, allowsQuotaExceeded) {
1047
- if (code === "quota_exceeded" && !allowsQuotaExceeded) return false;
1048
- return {
1049
- authentication_required: [401],
1050
- identity_authority_unavailable: [503],
1051
- permission_denied: [403],
1052
- resource_not_found: [404],
1053
- invalid_request: [422],
1054
- invalid_pagination_cursor: [422],
1055
- invalid_idempotency_key: [422],
1056
- precondition_failed: [preconditionStatus],
1057
- idempotency_conflict: [409],
1058
- quota_exceeded: [409],
1059
- service_unavailable: [503],
1060
- internal_error: [500]
1061
- }[code].includes(status);
1062
- }
1063
- async function parseBoundedJson(response, maximumBytes, status, requestId, signal) {
1064
- if (!response.body) throw new DriveError("invalid_response", {
1065
- status,
1066
- requestId
1067
- });
1068
- const reader = response.body.getReader();
1069
- const decoder = new TextDecoder("utf-8", { fatal: true });
1070
- let size = 0;
1071
- let chunks = 0;
1072
- let text = "";
1073
- const contentLength = response.headers.get("content-length");
1074
- if (contentLength !== null && (!/^(?:0|[1-9][0-9]*)$/.test(contentLength) || Number(contentLength) > maximumBytes)) {
1075
- reader.cancel().catch(() => {});
1076
- throw new DriveError("invalid_response", {
1077
- status,
1078
- requestId
1079
- });
1080
- }
1081
- const deadline = new AbortController();
1082
- const timeout = setTimeout(() => deadline.abort(), RESPONSE_DEADLINE_MILLISECONDS);
1083
- try {
1084
- while (true) {
1085
- if (signal?.aborted) throw new DriveError("aborted");
1086
- const { done, value } = await readResponseChunk(reader, signal, deadline.signal);
1087
- if (done) break;
1088
- chunks += 1;
1089
- size += value.byteLength;
1090
- if (chunks > RESPONSE_CHUNK_LIMIT || size > maximumBytes) throw new DriveError("invalid_response", {
1091
- status,
1092
- requestId
1093
- });
1094
- text += decoder.decode(value, { stream: true });
1027
+ function defaultPathSerializer(pathname, pathParams) {
1028
+ let nextURL = pathname;
1029
+ for (const match of pathname.match(PATH_PARAM_RE) ?? []) {
1030
+ let name = match.substring(1, match.length - 1);
1031
+ let explode = false;
1032
+ let style = "simple";
1033
+ if (name.endsWith("*")) {
1034
+ explode = true;
1035
+ name = name.substring(0, name.length - 1);
1036
+ }
1037
+ if (name.startsWith(".")) {
1038
+ style = "label";
1039
+ name = name.substring(1);
1040
+ } else if (name.startsWith(";")) {
1041
+ style = "matrix";
1042
+ name = name.substring(1);
1095
1043
  }
1096
- text += decoder.decode();
1097
- return {
1098
- value: JSON.parse(text),
1099
- text
1100
- };
1101
- } catch (error) {
1102
- if (error instanceof DriveError) {
1103
- if (error.code === "aborted" || signal?.aborted) throw new DriveError("aborted");
1104
- throw error;
1044
+ if (!pathParams || pathParams[name] === void 0 || pathParams[name] === null) continue;
1045
+ const value = pathParams[name];
1046
+ if (Array.isArray(value)) {
1047
+ nextURL = nextURL.replace(match, serializeArrayParam(name, value, {
1048
+ style,
1049
+ explode
1050
+ }));
1051
+ continue;
1105
1052
  }
1106
- throw new DriveError("invalid_response", {
1107
- status,
1108
- requestId
1109
- });
1110
- } finally {
1111
- clearTimeout(timeout);
1112
- reader.cancel().catch(() => {});
1053
+ if (typeof value === "object") {
1054
+ nextURL = nextURL.replace(match, serializeObjectParam(name, value, {
1055
+ style,
1056
+ explode
1057
+ }));
1058
+ continue;
1059
+ }
1060
+ if (style === "matrix") {
1061
+ nextURL = nextURL.replace(match, `;${serializePrimitiveParam(name, value)}`);
1062
+ continue;
1063
+ }
1064
+ nextURL = nextURL.replace(match, style === "label" ? `.${encodeURIComponent(value)}` : encodeURIComponent(value));
1113
1065
  }
1066
+ return nextURL;
1114
1067
  }
1115
- async function readResponseChunk(reader, operationSignal, deadlineSignal) {
1116
- if (operationSignal?.aborted) throw new DriveError("aborted");
1117
- if (deadlineSignal.aborted) throw new DriveError("invalid_response");
1118
- let removeOperationAbort = () => {};
1119
- let removeDeadlineAbort = () => {};
1120
- const operationAbort = new Promise((_, reject) => {
1121
- if (!operationSignal) return;
1122
- const onAbort = () => reject(new DriveError("aborted"));
1123
- operationSignal.addEventListener("abort", onAbort, { once: true });
1124
- removeOperationAbort = () => operationSignal.removeEventListener("abort", onAbort);
1125
- });
1126
- const deadlineAbort = new Promise((_, reject) => {
1127
- const onAbort = () => reject(new DriveError("invalid_response"));
1128
- deadlineSignal.addEventListener("abort", onAbort, { once: true });
1129
- removeDeadlineAbort = () => deadlineSignal.removeEventListener("abort", onAbort);
1130
- });
1131
- try {
1132
- return await Promise.race([
1133
- reader.read(),
1134
- operationAbort,
1135
- deadlineAbort
1136
- ]);
1137
- } finally {
1138
- removeOperationAbort();
1139
- removeDeadlineAbort();
1068
+ function defaultBodySerializer(body, headers) {
1069
+ if (body instanceof FormData) return body;
1070
+ if (headers) {
1071
+ if ((headers.get instanceof Function ? headers.get("Content-Type") ?? headers.get("content-type") : headers["Content-Type"] ?? headers["content-type"]) === "application/x-www-form-urlencoded") return new URLSearchParams(body).toString();
1140
1072
  }
1073
+ return JSON.stringify(body);
1141
1074
  }
1142
- function cancelResponse(response) {
1143
- response.body?.cancel().catch(() => {});
1075
+ function createFinalURL(pathname, options) {
1076
+ let finalURL = `${options.baseUrl}${pathname}`;
1077
+ if (options.params?.path) finalURL = options.pathSerializer(finalURL, options.params.path);
1078
+ let search = options.querySerializer(options.params.query ?? {});
1079
+ if (search.startsWith("?")) search = search.substring(1);
1080
+ if (search) finalURL += `?${search}`;
1081
+ return finalURL;
1144
1082
  }
1145
- function safeRequestId(value) {
1146
- return value !== null && REQUEST_ID.test(value) ? value : null;
1083
+ function mergeHeaders(...allHeaders) {
1084
+ const finalHeaders = new Headers();
1085
+ for (const h of allHeaders) {
1086
+ if (!h || typeof h !== "object") continue;
1087
+ const iterator = h instanceof Headers ? h.entries() : Object.entries(h);
1088
+ for (const [k, v] of iterator) if (v === null) finalHeaders.delete(k);
1089
+ else if (Array.isArray(v)) for (const v2 of v) finalHeaders.append(k, v2);
1090
+ else if (v !== void 0) finalHeaders.set(k, v);
1091
+ }
1092
+ return finalHeaders;
1147
1093
  }
1148
- function readValidators(headers) {
1149
- const etag = headers.get("etag");
1150
- const permissionCollectionEtag = headers.get("drive-permission-collection-etag");
1151
- const permissionGrantEtag = headers.get("drive-permission-grant-etag");
1152
- return Object.freeze({
1153
- ...etag !== null && LIST_ETAG.test(etag) ? { etag } : {},
1154
- ...permissionCollectionEtag !== null && parseCollectionEtag(permissionCollectionEtag) !== void 0 ? { permissionCollectionEtag } : {},
1155
- ...permissionGrantEtag !== null && parseGrantEtag(permissionGrantEtag) !== void 0 ? { permissionGrantEtag } : {}
1156
- });
1094
+ function removeTrailingSlash(url) {
1095
+ if (url.endsWith("/")) return url.substring(0, url.length - 1);
1096
+ return url;
1157
1097
  }
1158
- function isFolderPage(value, workspaceId, parentItemId) {
1159
- if (!(isExactRecord(value, ["items", "nextCursor"]) && Array.isArray(value.items) && value.items.length <= 50 && value.items.every(isFolderItem) && (value.nextCursor === null || typeof value.nextCursor === "string" && value.nextCursor.length <= 699 && CURSOR.test(value.nextCursor)))) return false;
1160
- return value.items.every((item) => item.workspaceId === workspaceId && item.parentItemId === parentItemId) && isStrictlyOrdered(value.items, (item) => item.itemId);
1098
+ String.fromCharCode;
1099
+ const TRAILING_SLASH_RE = /\/$|\/\?|\/#/;
1100
+ const JOIN_LEADING_SLASH_RE = /^\.?\//;
1101
+ function hasTrailingSlash(input = "", respectQueryAndFragment) {
1102
+ if (!respectQueryAndFragment) return input.endsWith("/");
1103
+ return TRAILING_SLASH_RE.test(input);
1161
1104
  }
1162
- function isFolderItem(value) {
1163
- return isExactRecord(value, [
1164
- "workspaceId",
1165
- "volumeId",
1166
- "itemId",
1167
- "parentItemId",
1168
- "kind",
1169
- "name",
1170
- "visibility",
1171
- "revision"
1172
- ]) && typeof value.workspaceId === "string" && WORKSPACE_ID.test(value.workspaceId) && typeof value.volumeId === "string" && VOLUME_ID.test(value.volumeId) && typeof value.itemId === "string" && ITEM_ID.test(value.itemId) && typeof value.parentItemId === "string" && ITEM_ID.test(value.parentItemId) && (value.kind === "file" || value.kind === "folder") && isSafeFilename(value.name) && (value.visibility === "workspace" || value.visibility === "restricted") && isSafeInteger(value.revision);
1105
+ function withTrailingSlash(input = "", respectQueryAndFragment) {
1106
+ if (!respectQueryAndFragment) return input.endsWith("/") ? input : input + "/";
1107
+ if (hasTrailingSlash(input, true)) return input || "/";
1108
+ let path = input;
1109
+ let fragment = "";
1110
+ const fragmentIndex = input.indexOf("#");
1111
+ if (fragmentIndex !== -1) {
1112
+ path = input.slice(0, fragmentIndex);
1113
+ fragment = input.slice(fragmentIndex);
1114
+ if (!path) return fragment;
1115
+ }
1116
+ const [s0, ...s] = path.split("?");
1117
+ return s0 + "/" + (s.length > 0 ? `?${s.join("?")}` : "") + fragment;
1173
1118
  }
1174
- function isPermissionPage(value, headers, targetItemId) {
1175
- if (!(isExactRecord(value, [
1176
- "items",
1177
- "nextCursor",
1178
- "collectionRevision",
1179
- "collectionEtag"
1180
- ]) && Array.isArray(value.items) && value.items.length <= 50 && value.items.every(isPermissionGrant) && (value.nextCursor === null || typeof value.nextCursor === "string" && value.nextCursor.length <= 699 && CURSOR.test(value.nextCursor)) && isSafeInteger(value.collectionRevision) && typeof value.collectionEtag === "string" && parseCollectionEtag(value.collectionEtag) === value.collectionRevision)) return false;
1181
- const collectionHeader = headers.get("drive-permission-collection-etag");
1182
- const representationEtag = headers.get("etag");
1183
- const collectionRevision = value.collectionRevision;
1184
- if (!isSafeInteger(collectionRevision)) return false;
1185
- return collectionHeader === value.collectionEtag && representationEtag !== null && LIST_ETAG.test(representationEtag) && value.items.every((grant) => (grant.source === "direct" ? grant.sourceItemId === targetItemId : grant.sourceItemId !== targetItemId) && grant.etag === permissionGrantEtag(collectionRevision, grant.revision)) && isStrictlyOrdered(value.items, (grant) => `${grant.sourceItemId}\0${grant.grantId}`);
1119
+ function isNonEmptyURL(url) {
1120
+ return url && url !== "/";
1186
1121
  }
1187
- function isPermissionGrant(value) {
1188
- return isExactRecord(value, [
1189
- "source",
1190
- "sourceItemId",
1191
- "grantId",
1192
- "grantee",
1193
- "actions",
1194
- "availability",
1195
- "expiresAtEpochMilliseconds",
1196
- "revision",
1197
- "etag"
1198
- ]) && (value.source === "direct" || value.source === "inherited") && typeof value.sourceItemId === "string" && ITEM_ID.test(value.sourceItemId) && typeof value.grantId === "string" && GRANT_ID.test(value.grantId) && isGrantee(value.grantee) && isPermissionActions(value.actions) && [
1199
- "active",
1200
- "expired",
1201
- "revoked"
1202
- ].includes(String(value.availability)) && (value.expiresAtEpochMilliseconds === null || isSafeInteger(value.expiresAtEpochMilliseconds)) && isSafeInteger(value.revision) && typeof value.etag === "string" && GRANT_ETAG.test(value.etag);
1122
+ function joinURL(base, ...input) {
1123
+ let url = base || "";
1124
+ for (const segment of input.filter((url2) => isNonEmptyURL(url2))) if (url) {
1125
+ const _segment = segment.replace(JOIN_LEADING_SLASH_RE, "");
1126
+ url = withTrailingSlash(url) + _segment;
1127
+ } else url = segment;
1128
+ return url;
1203
1129
  }
1204
- function isGrantee(value) {
1205
- return isExactRecord(value, ["type", "userId"]) && value.type === "user" && typeof value.userId === "string" && AUTH_ID.test(value.userId) || isExactRecord(value, ["type", "teamId"]) && value.type === "team" && typeof value.teamId === "string" && AUTH_ID.test(value.teamId);
1130
+ //#endregion
1131
+ //#region src/internal/client/transport.ts
1132
+ const DEFAULT_REQUEST_TIMEOUT_MILLISECONDS = 3e4;
1133
+ const MAX_REQUEST_TIMEOUT_MILLISECONDS = 12e4;
1134
+ function createClientRuntime(config) {
1135
+ let runtime;
1136
+ try {
1137
+ runtime = Object.freeze({
1138
+ baseUrl: parseBaseUrl(config.baseUrl),
1139
+ fetchImplementation: config.fetch ?? globalThis.fetch,
1140
+ getCredential: config.getCredential,
1141
+ timeoutMilliseconds: parseTimeoutMilliseconds(config.timeoutMilliseconds)
1142
+ });
1143
+ } catch {
1144
+ throw new DriveError("invalid_input");
1145
+ }
1146
+ if (typeof runtime.fetchImplementation !== "function" || typeof runtime.getCredential !== "function") throw new DriveError("invalid_input");
1147
+ return runtime;
1148
+ }
1149
+ async function execute(runtime, signal, preconditionStatus, send, successStatuses, guard, allowsQuotaExceeded = false) {
1150
+ const operation = createOperationSignal(signal, runtime.timeoutMilliseconds);
1151
+ try {
1152
+ const operationSignal = operation.signal;
1153
+ throwIfAborted(operationSignal);
1154
+ const credential = await obtainCredential(runtime.getCredential, operationSignal);
1155
+ let capturedResponse;
1156
+ await send(createClient({
1157
+ baseUrl: runtime.baseUrl,
1158
+ fetch: async (request) => {
1159
+ try {
1160
+ const response = await raceWithAbort(runtime.fetchImplementation(request), operationSignal);
1161
+ if (!(response instanceof Response)) throw new DriveError("invalid_response");
1162
+ capturedResponse = response;
1163
+ } catch (error) {
1164
+ if (error instanceof DriveError) throw error;
1165
+ throw operationSignal.aborted ? new DriveError("aborted") : new DriveError("network_error");
1166
+ }
1167
+ return new Response(null, { status: 204 });
1168
+ }
1169
+ }), {
1170
+ cache: "no-store",
1171
+ credentials: "omit",
1172
+ headers: new Headers({ authorization: `Bearer ${credential}` }),
1173
+ signal: operationSignal
1174
+ });
1175
+ if (!capturedResponse) throw new DriveError("network_error");
1176
+ return await parseResponse(capturedResponse, operationSignal, preconditionStatus, successStatuses, guard, allowsQuotaExceeded);
1177
+ } catch (error) {
1178
+ if (operation.didTimeout()) throw new DriveError("timeout");
1179
+ if (error instanceof DriveError) throw error;
1180
+ throw operation.signal.aborted ? new DriveError("aborted") : new DriveError("network_error");
1181
+ } finally {
1182
+ operation.dispose();
1183
+ }
1184
+ }
1185
+ function parseTimeoutMilliseconds(value) {
1186
+ if (value === void 0) return DEFAULT_REQUEST_TIMEOUT_MILLISECONDS;
1187
+ if (!Number.isSafeInteger(value) || value < 1 || value > MAX_REQUEST_TIMEOUT_MILLISECONDS) throw new DriveError("invalid_input");
1188
+ return value;
1189
+ }
1190
+ function createOperationSignal(callerSignal, timeoutMilliseconds) {
1191
+ const controller = new AbortController();
1192
+ let timedOut = false;
1193
+ const onAbort = () => controller.abort();
1194
+ if (callerSignal?.aborted) controller.abort();
1195
+ else callerSignal?.addEventListener("abort", onAbort, { once: true });
1196
+ const timeout = setTimeout(() => {
1197
+ timedOut = true;
1198
+ controller.abort();
1199
+ }, timeoutMilliseconds);
1200
+ return {
1201
+ signal: controller.signal,
1202
+ didTimeout: () => timedOut,
1203
+ dispose: () => {
1204
+ clearTimeout(timeout);
1205
+ callerSignal?.removeEventListener("abort", onAbort);
1206
+ }
1207
+ };
1208
+ }
1209
+ function parseBaseUrl(value) {
1210
+ if (typeof value !== "string") throw new TypeError("Drive base URL is invalid");
1211
+ let url;
1212
+ try {
1213
+ url = new URL(value);
1214
+ } catch {
1215
+ throw new TypeError("Drive base URL is invalid");
1216
+ }
1217
+ const localHttp = url.protocol === "http:" && [
1218
+ "localhost",
1219
+ "127.0.0.1",
1220
+ "[::1]"
1221
+ ].includes(url.hostname);
1222
+ if (url.protocol !== "https:" && !localHttp || url.username || url.password || url.search || url.hash || !url.host) throw new TypeError("Drive base URL is invalid");
1223
+ return joinURL(url.origin, url.pathname);
1206
1224
  }
1207
- function isPermissionActions(value) {
1208
- return Array.isArray(value) && value.length >= 1 && value.length <= 6 && new Set(value).size === value.length && value.every((action) => PERMISSION_ACTIONS.includes(action)) && value.every((action, index) => index === 0 || PERMISSION_ACTIONS.indexOf(action) > PERMISSION_ACTIONS.indexOf(value[index - 1]));
1225
+ async function obtainCredential(provider, signal) {
1226
+ throwIfAborted(signal);
1227
+ let credential;
1228
+ try {
1229
+ credential = await raceWithAbort(provider({ signal }), signal);
1230
+ } catch (error) {
1231
+ if (error instanceof DriveError && error.code === "aborted") throw error;
1232
+ throw signal.aborted ? new DriveError("aborted") : new DriveError("credential_unavailable");
1233
+ }
1234
+ if (typeof credential !== "string" || credential.length < 1 || credential.length > 16384 || !/^[\x21-\x7e]+$/.test(credential)) throw new DriveError("credential_unavailable");
1235
+ return credential;
1209
1236
  }
1210
- function isPermissionMutation(value, targetItemId, expectedGrantId) {
1211
- if (!isExactRecord(value, [
1212
- "outcome",
1213
- "grant",
1214
- "collectionRevision"
1215
- ]) || value.outcome !== "committed" && value.outcome !== "replayed" || !isSafeInteger(value.collectionRevision)) return false;
1216
- const grant = value.grant;
1217
- return isExactRecord(grant, [
1218
- "source",
1219
- "sourceItemId",
1220
- "grantId",
1221
- "grantee",
1222
- "actions",
1223
- "availability",
1224
- "expiresAtEpochMilliseconds",
1225
- "revision"
1226
- ]) && grant.source === "direct" && typeof grant.sourceItemId === "string" && grant.sourceItemId === targetItemId && typeof grant.grantId === "string" && GRANT_ID.test(grant.grantId) && (expectedGrantId === void 0 || grant.grantId === expectedGrantId) && isGrantee(grant.grantee) && isPermissionActions(grant.actions) && [
1227
- "active",
1228
- "expired",
1229
- "revoked"
1230
- ].includes(String(grant.availability)) && (grant.expiresAtEpochMilliseconds === null || isSafeInteger(grant.expiresAtEpochMilliseconds)) && isSafeInteger(grant.revision);
1237
+ function throwIfAborted(signal) {
1238
+ if (signal.aborted) throw new DriveError("aborted");
1231
1239
  }
1232
- function isEffectivePermissionResponse(value, targetItemId) {
1233
- if (!isExactRecord(value, ["actions"]) || !Array.isArray(value.actions) || value.actions.length !== 7) return false;
1234
- for (let index = 0; index < value.actions.length; index += 1) {
1235
- const action = value.actions[index];
1236
- if (action === void 0 || !isExactRecord(action, [
1237
- "action",
1238
- "allowed",
1239
- "basis",
1240
- "sourceItemId",
1241
- "grantId"
1242
- ]) || action.action !== EFFECTIVE_ACTIONS[index] || typeof action.allowed !== "boolean" || !(action.basis === null || [
1243
- "workspace_visibility",
1244
- "restricted_administrator_bypass",
1245
- "direct_grant",
1246
- "inherited_grant"
1247
- ].includes(String(action.basis))) || !(action.sourceItemId === null || typeof action.sourceItemId === "string" && ITEM_ID.test(action.sourceItemId)) || !(action.grantId === null || typeof action.grantId === "string" && GRANT_ID.test(action.grantId)) || !effectiveSourceIsCoherent(action, targetItemId)) return false;
1240
+ async function raceWithAbort(promise, signal) {
1241
+ throwIfAborted(signal);
1242
+ let removeAbort = () => {};
1243
+ const aborted = new Promise((_, reject) => {
1244
+ const onAbort = () => reject(new DriveError("aborted"));
1245
+ signal.addEventListener("abort", onAbort, { once: true });
1246
+ removeAbort = () => signal.removeEventListener("abort", onAbort);
1247
+ });
1248
+ try {
1249
+ return await Promise.race([promise, aborted]);
1250
+ } finally {
1251
+ removeAbort();
1248
1252
  }
1249
- return true;
1250
- }
1251
- function isUploadQuotaReservation(value) {
1252
- return isExactRecord(value, [
1253
- "workspaceId",
1254
- "uploadId",
1255
- "parentItemId",
1256
- "expectedByteLength",
1257
- "state",
1258
- "terminalReason",
1259
- "createdAtEpochMilliseconds",
1260
- "expiresAtEpochMilliseconds",
1261
- "terminalAtEpochMilliseconds"
1262
- ]) && typeof value.workspaceId === "string" && WORKSPACE_ID.test(value.workspaceId) && typeof value.uploadId === "string" && UPLOAD_ID.test(value.uploadId) && typeof value.parentItemId === "string" && ITEM_ID.test(value.parentItemId) && typeof value.expectedByteLength === "number" && Number.isSafeInteger(value.expectedByteLength) && value.expectedByteLength >= 0 && value.expectedByteLength <= MAX_UPLOAD_BYTE_LENGTH && (value.state === "reserved" || value.state === "converted" || value.state === "released") && (value.terminalReason === null || value.terminalReason === "completed" || value.terminalReason === "aborted" || value.terminalReason === "failed" || value.terminalReason === "expired") && isSafeInteger(value.createdAtEpochMilliseconds) && isSafeInteger(value.expiresAtEpochMilliseconds) && value.expiresAtEpochMilliseconds - value.createdAtEpochMilliseconds === UPLOAD_QUOTA_EXPIRY_MILLISECONDS && (value.terminalAtEpochMilliseconds === null || isSafeInteger(value.terminalAtEpochMilliseconds) && value.terminalAtEpochMilliseconds >= value.createdAtEpochMilliseconds);
1263
1253
  }
1264
- function effectiveSourceIsCoherent(action, targetItemId) {
1265
- if (!action.allowed) return action.basis === null && action.sourceItemId === null && action.grantId === null;
1266
- if (action.basis === "workspace_visibility" || action.basis === "restricted_administrator_bypass") return action.sourceItemId === null && action.grantId === null;
1267
- if (action.basis === "direct_grant") return action.sourceItemId === targetItemId && typeof action.grantId === "string";
1268
- if (action.basis === "inherited_grant") return typeof action.sourceItemId === "string" && action.sourceItemId !== targetItemId && typeof action.grantId === "string";
1269
- return false;
1254
+ //#endregion
1255
+ //#region src/internal/client/operations.ts
1256
+ function createDriveClientInternal(config) {
1257
+ const runtime = createClientRuntime(config);
1258
+ return Object.freeze({
1259
+ listFolderChildren: (input) => listFolderChildren(runtime, input),
1260
+ createFolder: (input) => createFolder(runtime, input),
1261
+ listItemPermissions: (input) => listItemPermissions(runtime, input),
1262
+ readEffectiveItemPermissions: (input) => readEffectiveItemPermissions(runtime, input),
1263
+ createItemPermission: (input) => createItemPermission(runtime, input),
1264
+ updateItemPermission: (input) => updateItemPermission(runtime, input),
1265
+ revokeItemPermission: (input) => revokeItemPermission(runtime, input),
1266
+ createUpload: (input) => createUpload(runtime, input),
1267
+ abortUpload: (input) => abortUpload(runtime, input)
1268
+ });
1270
1269
  }
1271
- function mutationValidatorsAreCoherent(headers) {
1272
- const collection = headers.get("drive-permission-collection-etag");
1273
- const grant = headers.get("drive-permission-grant-etag");
1274
- const collectionRevision = collection === null ? void 0 : parseCollectionEtag(collection);
1275
- if (collectionRevision === void 0) return false;
1276
- if (grant === null) return true;
1277
- const currentGrant = parseGrantEtag(grant);
1278
- return currentGrant !== void 0 && currentGrant[0] === collectionRevision;
1270
+ async function listFolderChildren(runtime, input) {
1271
+ const validated = validatePageInput(input);
1272
+ return execute(runtime, validated.signal, 409, (api, requestOptions) => api.GET("/api/v1/workspaces/{workspaceId}/items/{itemId}/children", {
1273
+ ...operationOptions("listFolderChildren", { params: {
1274
+ path: resourcePath(validated),
1275
+ query: pageQuery(validated)
1276
+ } }),
1277
+ ...requestOptions
1278
+ }), [200], (value) => isFolderPage(value, validated.workspaceId, validated.itemId));
1279
1279
  }
1280
- function parseCollectionEtag(value) {
1281
- const match = COLLECTION_ETAG.exec(value);
1282
- if (!match) return void 0;
1283
- const revision = Number(match[1]);
1284
- return Number.isSafeInteger(revision) ? revision : void 0;
1280
+ async function createFolder(runtime, input) {
1281
+ const validated = validateCreateFolderInput(input);
1282
+ return execute(runtime, validated.signal, 409, (api, requestOptions) => api.POST("/api/v1/workspaces/{workspaceId}/items/{itemId}/children", {
1283
+ ...operationOptions("createFolder", {
1284
+ params: {
1285
+ path: resourcePath(validated),
1286
+ header: { "Idempotency-Key": validated.idempotencyKey }
1287
+ },
1288
+ body: { name: validated.name }
1289
+ }),
1290
+ ...requestOptions
1291
+ }), [200, 201], (value) => isFolderItem(value) && value.workspaceId === validated.workspaceId && value.parentItemId === validated.itemId && value.kind === "folder" && value.name === validated.name);
1285
1292
  }
1286
- function parseGrantEtag(value) {
1287
- const match = GRANT_ETAG.exec(value);
1288
- if (!match) return void 0;
1289
- const collectionRevision = Number(match[1]);
1290
- const grantRevision = Number(match[2]);
1291
- return Number.isSafeInteger(collectionRevision) && Number.isSafeInteger(grantRevision) ? [collectionRevision, grantRevision] : void 0;
1293
+ async function listItemPermissions(runtime, input) {
1294
+ const validated = validatePageInput(input);
1295
+ return execute(runtime, validated.signal, 412, (api, requestOptions) => api.GET("/api/v1/workspaces/{workspaceId}/items/{itemId}/permissions", {
1296
+ ...operationOptions("listItemPermissions", { params: {
1297
+ path: resourcePath(validated),
1298
+ query: pageQuery(validated)
1299
+ } }),
1300
+ ...requestOptions
1301
+ }), [200], (value, _status, headers) => isPermissionPage(value, headers, validated.itemId));
1292
1302
  }
1293
- function permissionGrantEtag(collectionRevision, grantRevision) {
1294
- return `"drive.permission-grant.v1.${collectionRevision}.${grantRevision}"`;
1303
+ async function readEffectiveItemPermissions(runtime, input) {
1304
+ const validated = validateResourceInput(input);
1305
+ return execute(runtime, validated.signal, 412, (api, requestOptions) => api.GET("/api/v1/workspaces/{workspaceId}/items/{itemId}/permissions/effective", {
1306
+ ...operationOptions("readEffectiveItemPermissions", { params: { path: resourcePath(validated) } }),
1307
+ ...requestOptions
1308
+ }), [200], (value) => isEffectivePermissionResponse(value, validated.itemId));
1295
1309
  }
1296
- function isSafeFilename(value) {
1297
- if (typeof value !== "string" || value.length === 0 || value.length > 510 || value.normalize("NFC") !== value || value.endsWith(".") || value.endsWith(" ") || filenameIsReserved(value)) return false;
1298
- let codePoints = 0;
1299
- let bytes = 0;
1300
- for (const character of value) {
1301
- const point = character.codePointAt(0);
1302
- if (point === void 0 || point === 47 || point === 92 || point <= 31 || point >= 127 && point <= 159 || point === 8232 || point === 8233 || point >= 55296 && point <= 57343) return false;
1303
- codePoints += 1;
1304
- bytes += point <= 127 ? 1 : point <= 2047 ? 2 : point <= 65535 ? 3 : 4;
1305
- if (codePoints > 255 || bytes > 255) return false;
1306
- }
1307
- return true;
1310
+ async function createItemPermission(runtime, input) {
1311
+ const validated = validateCreatePermissionInput(input);
1312
+ return execute(runtime, validated.signal, 412, (api, requestOptions) => api.POST("/api/v1/workspaces/{workspaceId}/items/{itemId}/permissions", {
1313
+ ...operationOptions("createItemPermission", {
1314
+ params: {
1315
+ path: resourcePath(validated),
1316
+ header: {
1317
+ "Idempotency-Key": validated.idempotencyKey,
1318
+ "If-Match": validated.ifMatch
1319
+ }
1320
+ },
1321
+ body: {
1322
+ grantee: validated.grantee,
1323
+ actions: validated.actions,
1324
+ expiresAtEpochMilliseconds: validated.expiresAtEpochMilliseconds
1325
+ }
1326
+ }),
1327
+ ...requestOptions
1328
+ }), [200, 201], (value, status, headers) => isPermissionMutation(value, validated.itemId) && (status === 201 && value.outcome === "committed" || status === 200 && value.outcome === "replayed") && (status === 200 || value.grant.availability !== "revoked") && mutationValidatorsAreCoherent(headers));
1308
1329
  }
1309
- function filenameIsReserved(value) {
1310
- const base = value.split(".", 1)[0].toUpperCase();
1311
- return value === "." || value === ".." || [
1312
- "CON",
1313
- "PRN",
1314
- "AUX",
1315
- "NUL",
1316
- "CLOCK$",
1317
- "CONIN$",
1318
- "CONOUT$"
1319
- ].includes(base) || /^COM[1-9]$/.test(base) || /^LPT[1-9]$/.test(base);
1330
+ async function updateItemPermission(runtime, input) {
1331
+ const validated = validateUpdatePermissionInput(input);
1332
+ const body = {};
1333
+ if (validated.actions !== void 0) body.actions = validated.actions;
1334
+ if (validated.expiresAtEpochMilliseconds !== void 0) body.expiresAtEpochMilliseconds = validated.expiresAtEpochMilliseconds;
1335
+ return execute(runtime, validated.signal, 412, (api, requestOptions) => api.PATCH("/api/v1/workspaces/{workspaceId}/items/{itemId}/permissions/{grantId}", {
1336
+ ...operationOptions("updateItemPermission", {
1337
+ params: {
1338
+ path: {
1339
+ ...resourcePath(validated),
1340
+ grantId: validated.grantId
1341
+ },
1342
+ header: {
1343
+ "Idempotency-Key": validated.idempotencyKey,
1344
+ "If-Match": validated.ifMatch
1345
+ }
1346
+ },
1347
+ body
1348
+ }),
1349
+ ...requestOptions
1350
+ }), [200], (value, _status, headers) => isPermissionMutation(value, validated.itemId, validated.grantId) && value.grant.availability !== "revoked" && mutationValidatorsAreCoherent(headers));
1320
1351
  }
1321
- function isStrictlyOrdered(values, key) {
1322
- return values.every((value, index) => index === 0 || key(values[index - 1]) < key(value));
1352
+ async function revokeItemPermission(runtime, input) {
1353
+ const validated = validateGrantMutationInput(input);
1354
+ return execute(runtime, validated.signal, 412, (api, requestOptions) => api.DELETE("/api/v1/workspaces/{workspaceId}/items/{itemId}/permissions/{grantId}", {
1355
+ ...operationOptions("revokeItemPermission", { params: {
1356
+ path: {
1357
+ ...resourcePath(validated),
1358
+ grantId: validated.grantId
1359
+ },
1360
+ header: {
1361
+ "Idempotency-Key": validated.idempotencyKey,
1362
+ "If-Match": validated.ifMatch
1363
+ }
1364
+ } }),
1365
+ ...requestOptions
1366
+ }), [200], (value, _status, headers) => isPermissionMutation(value, validated.itemId, validated.grantId) && value.grant.availability === "revoked" && parseCollectionEtag(headers.get("drive-permission-collection-etag") ?? "") !== void 0 && headers.get("drive-permission-grant-etag") === null);
1323
1367
  }
1324
- function isSafeInteger(value) {
1325
- return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
1368
+ async function createUpload(runtime, input) {
1369
+ const validated = validateCreateUploadInput(input);
1370
+ return execute(runtime, validated.signal, 409, (api, requestOptions) => api.POST("/api/v1/workspaces/{workspaceId}/uploads", {
1371
+ ...operationOptions("createUpload", {
1372
+ params: {
1373
+ path: { workspaceId: validated.workspaceId },
1374
+ header: { "Idempotency-Key": validated.idempotencyKey }
1375
+ },
1376
+ body: {
1377
+ parentItemId: validated.parentItemId,
1378
+ expectedByteLength: validated.expectedByteLength
1379
+ }
1380
+ }),
1381
+ ...requestOptions
1382
+ }), [200, 201], (value) => isUploadQuotaReservation(value) && value.workspaceId === validated.workspaceId && value.parentItemId === validated.parentItemId && value.expectedByteLength === validated.expectedByteLength && value.state === "reserved" && value.terminalReason === null && value.terminalAtEpochMilliseconds === null, true);
1326
1383
  }
1327
- function isExactRecord(value, keys) {
1328
- if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
1329
- return Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
1384
+ async function abortUpload(runtime, input) {
1385
+ const validated = validateAbortUploadInput(input);
1386
+ return execute(runtime, validated.signal, 409, (api, requestOptions) => api.DELETE("/api/v1/workspaces/{workspaceId}/uploads/{uploadId}", {
1387
+ ...operationOptions("abortUpload", { params: { path: {
1388
+ workspaceId: validated.workspaceId,
1389
+ uploadId: validated.uploadId
1390
+ } } }),
1391
+ ...requestOptions
1392
+ }), [200], (value) => isUploadQuotaReservation(value) && value.workspaceId === validated.workspaceId && value.uploadId === validated.uploadId && value.state === "released" && value.terminalReason === "aborted" && value.terminalAtEpochMilliseconds !== null);
1330
1393
  }
1331
1394
  //#endregion
1332
1395
  //#region src/client.ts