@putdotio/sdk 11.5.0 → 12.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -51
- package/dist/{forms-Ch5rwD58.js → forms-Bd1tWyx0.js} +13 -2
- package/dist/forms-Bd1tWyx0.js.map +1 -0
- package/dist/{index-BTZhke9H.d.ts → index-BdlCuqLB.d.ts} +22 -25
- package/dist/index.d.ts +1 -1
- package/dist/index.js +35 -55
- package/dist/index.js.map +1 -1
- package/dist/utilities.d.ts +53 -47
- package/dist/utilities.js +38 -27
- package/dist/utilities.js.map +1 -1
- package/package.json +3 -3
- package/dist/forms-Ch5rwD58.js.map +0 -1
package/dist/utilities.d.ts
CHANGED
|
@@ -1,53 +1,60 @@
|
|
|
1
|
-
import { Am as PutioErrorEnvelope, Dm as PutioApiError, Nm as PutioKnownOperationErrorContract, Om as PutioAuthError, Pm as PutioOperationError, Rm as PutioRateLimitError, zo as FileBroad } from "./index-
|
|
1
|
+
import { Am as PutioErrorEnvelope, Dm as PutioApiError, Nm as PutioKnownOperationErrorContract, Om as PutioAuthError, Pm as PutioOperationError, Rm as PutioRateLimitError, zo as FileBroad } from "./index-BdlCuqLB.js";
|
|
2
2
|
//#region src/utilities/date.d.ts
|
|
3
|
-
declare const ensureUTC: (date?: unknown) => string;
|
|
4
|
-
declare const toTimeAgo: (date?: unknown) => string;
|
|
5
|
-
declare const formatDate: (date?: unknown, format?: "LL" | Intl.DateTimeFormatOptions) => string;
|
|
6
|
-
declare const daysDiff: (date1: unknown, date2: unknown) => number;
|
|
7
|
-
declare const daysDiffFromNow: (date?: unknown) => number;
|
|
8
|
-
declare const getUnixTimestamp: (date: unknown) => number;
|
|
3
|
+
export declare const ensureUTC: (date?: unknown) => string;
|
|
4
|
+
export declare const toTimeAgo: (date?: unknown) => string;
|
|
5
|
+
export declare const formatDate: (date?: unknown, format?: "LL" | Intl.DateTimeFormatOptions) => string;
|
|
6
|
+
export declare const daysDiff: (date1: unknown, date2: unknown) => number;
|
|
7
|
+
export declare const daysDiffFromNow: (date?: unknown) => number;
|
|
8
|
+
export declare const getUnixTimestamp: (date: unknown) => number;
|
|
9
9
|
//#endregion
|
|
10
10
|
//#region src/utilities/duration.d.ts
|
|
11
|
-
declare const secondsToDuration: (seconds: unknown) => string;
|
|
12
|
-
declare const secondsToReadableDuration: (seconds: unknown) => string;
|
|
11
|
+
export declare const secondsToDuration: (seconds: unknown) => string;
|
|
12
|
+
export declare const secondsToReadableDuration: (seconds: unknown) => string;
|
|
13
13
|
//#endregion
|
|
14
14
|
//#region src/utilities/file-render-type.d.ts
|
|
15
|
-
type FileRenderType = "archive" | "audio" | "epub" | "folder" | "image" | "other" | "pdf" | "text" | "text/markdown" | "video";
|
|
16
|
-
type FileRenderTypeInput = Pick<FileBroad, "content_type" | "extension" | "file_type">;
|
|
17
|
-
declare const getFileRenderType: (file: FileRenderTypeInput) => FileRenderType;
|
|
15
|
+
export type FileRenderType = "archive" | "audio" | "epub" | "folder" | "image" | "other" | "pdf" | "text" | "text/markdown" | "video";
|
|
16
|
+
export type FileRenderTypeInput = Pick<FileBroad, "content_type" | "extension" | "file_type">;
|
|
17
|
+
export declare const getFileRenderType: (file: FileRenderTypeInput) => FileRenderType;
|
|
18
18
|
//#endregion
|
|
19
19
|
//#region src/utilities/file-size.d.ts
|
|
20
|
-
interface HumanFileSizeOptions {
|
|
20
|
+
export interface HumanFileSizeOptions {
|
|
21
21
|
readonly decimals?: number;
|
|
22
22
|
readonly unitSeparator?: string;
|
|
23
23
|
}
|
|
24
|
-
declare const toHumanFileSize: (fileSizeInBytes: string | number, options?: HumanFileSizeOptions) => string;
|
|
24
|
+
export declare const toHumanFileSize: (fileSizeInBytes: string | number, options?: HumanFileSizeOptions) => string;
|
|
25
25
|
//#endregion
|
|
26
26
|
//#region src/utilities/file-url-provider.d.ts
|
|
27
|
-
type FileUrlProviderInput = Pick<FileBroad, "content_type" | "extension" | "file_type" | "id" | "is_mp4_available">;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
readonly
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
export type FileUrlProviderInput = Pick<FileBroad, "content_type" | "extension" | "file_type" | "id" | "is_mp4_available">;
|
|
28
|
+
export type FileUrlProviderOptions = {
|
|
29
|
+
/** The put.io API origin, with or without the trailing `/v2`. */
|
|
30
|
+
readonly baseUrl: string;
|
|
31
|
+
/** The account download token from `getAccountInfo({ download_token: 1 })`, not the OAuth token. */
|
|
32
|
+
readonly downloadToken: string;
|
|
33
|
+
};
|
|
34
|
+
export declare class FileUrlProvider {
|
|
35
|
+
readonly apiUrl: string;
|
|
36
|
+
/** The account download token from `getAccountInfo({ download_token: 1 })`, not the OAuth token. */
|
|
37
|
+
readonly downloadToken: string;
|
|
38
|
+
readonly baseUrl: string;
|
|
39
|
+
constructor(options: FileUrlProviderOptions);
|
|
40
|
+
getDownloadUrl(fileOrFileId: FileUrlProviderInput | number): string | null;
|
|
41
|
+
getHlsStreamUrl(file: FileUrlProviderInput, params?: {
|
|
35
42
|
readonly maxSubtitleCount?: number;
|
|
36
43
|
readonly playOriginal?: boolean;
|
|
37
44
|
readonly subtitleLanguages?: ReadonlyArray<string>;
|
|
38
45
|
}): string | null;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
getMp4DownloadUrl(file: FileUrlProviderInput): string | null;
|
|
47
|
+
getMp4StreamUrl(file: FileUrlProviderInput): string | null;
|
|
48
|
+
getStreamUrl(file: FileUrlProviderInput): string | null;
|
|
49
|
+
getXspfUrl(file: FileUrlProviderInput): string | null;
|
|
43
50
|
}
|
|
44
51
|
//#endregion
|
|
45
52
|
//#region src/utilities/localized-error.d.ts
|
|
46
|
-
type LocalizedErrorRecoverySuggestionTypeInstruction = {
|
|
53
|
+
export type LocalizedErrorRecoverySuggestionTypeInstruction = {
|
|
47
54
|
readonly type: "instruction";
|
|
48
55
|
readonly description: string;
|
|
49
56
|
};
|
|
50
|
-
type LocalizedErrorRecoverySuggestionTypeAction = {
|
|
57
|
+
export type LocalizedErrorRecoverySuggestionTypeAction = {
|
|
51
58
|
readonly type: "action";
|
|
52
59
|
readonly description: string;
|
|
53
60
|
readonly trigger: {
|
|
@@ -55,66 +62,65 @@ type LocalizedErrorRecoverySuggestionTypeAction = {
|
|
|
55
62
|
readonly callback: () => void;
|
|
56
63
|
};
|
|
57
64
|
};
|
|
58
|
-
type LocalizedErrorRecoverySuggestionTypeCaptcha = {
|
|
65
|
+
export type LocalizedErrorRecoverySuggestionTypeCaptcha = {
|
|
59
66
|
readonly type: "captcha";
|
|
60
67
|
readonly description: string;
|
|
61
68
|
};
|
|
62
|
-
type LocalizedErrorRecoverySuggestion = LocalizedErrorRecoverySuggestionTypeInstruction | LocalizedErrorRecoverySuggestionTypeAction | LocalizedErrorRecoverySuggestionTypeCaptcha;
|
|
63
|
-
interface LocalizedErrorParams {
|
|
69
|
+
export type LocalizedErrorRecoverySuggestion = LocalizedErrorRecoverySuggestionTypeInstruction | LocalizedErrorRecoverySuggestionTypeAction | LocalizedErrorRecoverySuggestionTypeCaptcha;
|
|
70
|
+
export interface LocalizedErrorParams {
|
|
64
71
|
readonly message: string;
|
|
65
72
|
readonly recoverySuggestion: LocalizedErrorRecoverySuggestion;
|
|
66
73
|
readonly underlyingError: unknown;
|
|
67
74
|
readonly meta?: Record<string, unknown>;
|
|
68
75
|
}
|
|
69
|
-
declare class LocalizedError extends Error {
|
|
76
|
+
export declare class LocalizedError extends Error {
|
|
70
77
|
readonly recoverySuggestion: LocalizedErrorRecoverySuggestion;
|
|
71
78
|
readonly underlyingError: unknown;
|
|
72
79
|
readonly meta: Record<string, unknown>;
|
|
73
80
|
constructor(params: LocalizedErrorParams);
|
|
74
81
|
}
|
|
75
82
|
type StatusAndBodyError = PutioApiError | PutioAuthError | PutioRateLimitError | PutioOperationError<string, string, PutioKnownOperationErrorContract>;
|
|
76
|
-
type PutioLocalizableError = {
|
|
83
|
+
export type PutioLocalizableError = {
|
|
77
84
|
readonly _tag?: string;
|
|
78
85
|
readonly body: PutioErrorEnvelope;
|
|
79
86
|
readonly status?: number;
|
|
80
87
|
};
|
|
81
|
-
type PutioErrorWithBody = StatusAndBodyError;
|
|
82
|
-
type LocalizeErrorFn<E> = (error: E) => Pick<LocalizedErrorParams, "message" | "recoverySuggestion" | "meta">;
|
|
83
|
-
type APIErrorByStatusCodeLocalizer = {
|
|
88
|
+
export type PutioErrorWithBody = StatusAndBodyError;
|
|
89
|
+
export type LocalizeErrorFn<E> = (error: E) => Pick<LocalizedErrorParams, "message" | "recoverySuggestion" | "meta">;
|
|
90
|
+
export type APIErrorByStatusCodeLocalizer = {
|
|
84
91
|
readonly kind: "api_status_code";
|
|
85
92
|
readonly status_code: number;
|
|
86
93
|
readonly localize: LocalizeErrorFn<PutioLocalizableError>;
|
|
87
94
|
};
|
|
88
|
-
type APIErrorByErrorTypeLocalizer = {
|
|
95
|
+
export type APIErrorByErrorTypeLocalizer = {
|
|
89
96
|
readonly kind: "api_error_type";
|
|
90
97
|
readonly error_type: string;
|
|
91
98
|
readonly localize: LocalizeErrorFn<PutioLocalizableError>;
|
|
92
99
|
};
|
|
93
|
-
type MatchConditionLocalizer<E> = {
|
|
100
|
+
export type MatchConditionLocalizer<E> = {
|
|
94
101
|
readonly kind: "match_condition";
|
|
95
102
|
readonly match: (error: E) => boolean;
|
|
96
103
|
readonly localize: LocalizeErrorFn<E>;
|
|
97
104
|
};
|
|
98
|
-
type GenericErrorLocalizer = {
|
|
105
|
+
export type GenericErrorLocalizer = {
|
|
99
106
|
readonly kind: "generic";
|
|
100
107
|
readonly localize: LocalizeErrorFn<unknown>;
|
|
101
108
|
};
|
|
102
|
-
type ErrorLocalizer<E> = APIErrorByStatusCodeLocalizer | APIErrorByErrorTypeLocalizer | MatchConditionLocalizer<E> | GenericErrorLocalizer;
|
|
103
|
-
type ErrorLocalizerFn = (error: unknown) => LocalizedError;
|
|
104
|
-
declare const isErrorLocalizer: (fn: unknown) => fn is ErrorLocalizerFn;
|
|
105
|
-
declare const createLocalizeError: <GlobalError>(globalLocalizers: ReadonlyArray<ErrorLocalizer<GlobalError>>) => <ScopedError extends GlobalError>(error: ScopedError, scopedLocalizers?: ReadonlyArray<ErrorLocalizer<ScopedError>>) => LocalizedError;
|
|
109
|
+
export type ErrorLocalizer<E> = APIErrorByStatusCodeLocalizer | APIErrorByErrorTypeLocalizer | MatchConditionLocalizer<E> | GenericErrorLocalizer;
|
|
110
|
+
export type ErrorLocalizerFn = (error: unknown) => LocalizedError;
|
|
111
|
+
export declare const isErrorLocalizer: (fn: unknown) => fn is ErrorLocalizerFn;
|
|
112
|
+
export declare const createLocalizeError: <GlobalError>(globalLocalizers: ReadonlyArray<ErrorLocalizer<GlobalError>>) => <ScopedError extends GlobalError>(error: ScopedError, scopedLocalizers?: ReadonlyArray<ErrorLocalizer<ScopedError>>) => LocalizedError;
|
|
106
113
|
//#endregion
|
|
107
114
|
//#region src/utilities/string.d.ts
|
|
108
|
-
declare const dotsToSpaces: (input: string) => string;
|
|
109
|
-
declare const truncate: (input: string, options: {
|
|
115
|
+
export declare const dotsToSpaces: (input: string) => string;
|
|
116
|
+
export declare const truncate: (input: string, options: {
|
|
110
117
|
readonly length: number;
|
|
111
118
|
readonly ellipsis?: string;
|
|
112
119
|
}) => string;
|
|
113
|
-
declare const truncateMiddle: (input: string, options: {
|
|
120
|
+
export declare const truncateMiddle: (input: string, options: {
|
|
114
121
|
readonly frontLength: number;
|
|
115
122
|
readonly backLength: number;
|
|
116
123
|
readonly ellipsis?: string;
|
|
117
124
|
}) => string;
|
|
118
125
|
//#endregion
|
|
119
|
-
export { APIErrorByErrorTypeLocalizer, APIErrorByStatusCodeLocalizer, ErrorLocalizer, ErrorLocalizerFn, FileRenderType, FileRenderTypeInput, FileURLProvider, FileUrlProviderInput, GenericErrorLocalizer, HumanFileSizeOptions, LocalizeErrorFn, LocalizedError, LocalizedErrorParams, LocalizedErrorRecoverySuggestion, LocalizedErrorRecoverySuggestionTypeAction, LocalizedErrorRecoverySuggestionTypeCaptcha, LocalizedErrorRecoverySuggestionTypeInstruction, MatchConditionLocalizer, PutioErrorWithBody, PutioLocalizableError, createLocalizeError, daysDiff, daysDiffFromNow, dotsToSpaces, ensureUTC, formatDate, getFileRenderType, getUnixTimestamp, isErrorLocalizer, secondsToDuration, secondsToReadableDuration, toHumanFileSize, toTimeAgo, truncate, truncateMiddle };
|
|
120
126
|
//# sourceMappingURL=utilities.d.ts.map
|
package/dist/utilities.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Predicate } from "effect";
|
|
1
|
+
import { W as mapDecodeErrorToValidationError, d as encodePathSegment, r as NonEmptyStringSchema, t as joinCsv, u as buildPutioUrl, z as isPutioErrorEnvelope } from "./forms-Bd1tWyx0.js";
|
|
2
|
+
import { Predicate, Result, Schema } from "effect";
|
|
3
3
|
//#region src/utilities/date.ts
|
|
4
4
|
const LONG_DATE_FORMATTER = new Intl.DateTimeFormat("en-US", {
|
|
5
5
|
dateStyle: "long",
|
|
@@ -152,49 +152,60 @@ const toHumanFileSize = (fileSizeInBytes, options = {}) => {
|
|
|
152
152
|
};
|
|
153
153
|
//#endregion
|
|
154
154
|
//#region src/utilities/file-url-provider.ts
|
|
155
|
-
const
|
|
155
|
+
const FileUrlProviderOptionsSchema = Schema.Struct({
|
|
156
|
+
baseUrl: NonEmptyStringSchema,
|
|
157
|
+
downloadToken: NonEmptyStringSchema
|
|
158
|
+
});
|
|
159
|
+
const decodeOptions = (options) => {
|
|
160
|
+
const decoded = Schema.decodeUnknownResult(FileUrlProviderOptionsSchema, { onExcessProperty: "error" })(options);
|
|
161
|
+
if (Result.isFailure(decoded)) throw mapDecodeErrorToValidationError(decoded.failure);
|
|
162
|
+
return decoded.success;
|
|
163
|
+
};
|
|
164
|
+
const normalizeApiBaseUrl = (apiUrl) => apiUrl.endsWith("/v2") ? apiUrl.slice(0, -3) : apiUrl;
|
|
156
165
|
const isVideoFile = (file) => getFileRenderType(file) === "video";
|
|
157
|
-
var
|
|
158
|
-
|
|
159
|
-
token
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
this.
|
|
166
|
+
var FileUrlProvider = class {
|
|
167
|
+
apiUrl;
|
|
168
|
+
/** The account download token from `getAccountInfo({ download_token: 1 })`, not the OAuth token. */
|
|
169
|
+
downloadToken;
|
|
170
|
+
baseUrl;
|
|
171
|
+
constructor(options) {
|
|
172
|
+
const { baseUrl, downloadToken } = decodeOptions(options);
|
|
173
|
+
this.baseUrl = normalizeApiBaseUrl(baseUrl);
|
|
174
|
+
this.apiUrl = `${this.baseUrl}/v2`;
|
|
175
|
+
this.downloadToken = downloadToken;
|
|
165
176
|
}
|
|
166
|
-
|
|
167
|
-
if (typeof fileOrFileId === "number") return buildPutioUrl(this.
|
|
177
|
+
getDownloadUrl(fileOrFileId) {
|
|
178
|
+
if (typeof fileOrFileId === "number") return buildPutioUrl(this.baseUrl, `/v2/files/${encodePathSegment(fileOrFileId)}/download`, { oauth_token: this.downloadToken });
|
|
168
179
|
if (fileOrFileId.file_type === "FOLDER") return null;
|
|
169
|
-
return buildPutioUrl(this.
|
|
180
|
+
return buildPutioUrl(this.baseUrl, `/v2/files/${encodePathSegment(fileOrFileId.id)}/download`, { oauth_token: this.downloadToken });
|
|
170
181
|
}
|
|
171
|
-
|
|
182
|
+
getHlsStreamUrl(file, params = {}) {
|
|
172
183
|
if (!isVideoFile(file)) return null;
|
|
173
|
-
return buildPutioUrl(this.
|
|
184
|
+
return buildPutioUrl(this.baseUrl, `/v2/files/${encodePathSegment(file.id)}/hls/media.m3u8`, {
|
|
174
185
|
max_subtitle_count: params.maxSubtitleCount,
|
|
175
|
-
oauth_token: this.
|
|
186
|
+
oauth_token: this.downloadToken,
|
|
176
187
|
original: params.playOriginal ? 1 : void 0,
|
|
177
188
|
subtitle_languages: joinCsv(params.subtitleLanguages)
|
|
178
189
|
});
|
|
179
190
|
}
|
|
180
|
-
|
|
191
|
+
getMp4DownloadUrl(file) {
|
|
181
192
|
if (!isVideoFile(file) || !file.is_mp4_available) return null;
|
|
182
|
-
return buildPutioUrl(this.
|
|
193
|
+
return buildPutioUrl(this.baseUrl, `/v2/files/${encodePathSegment(file.id)}/mp4/download`, { oauth_token: this.downloadToken });
|
|
183
194
|
}
|
|
184
|
-
|
|
195
|
+
getMp4StreamUrl(file) {
|
|
185
196
|
if (!isVideoFile(file) || !file.is_mp4_available) return null;
|
|
186
|
-
return buildPutioUrl(this.
|
|
197
|
+
return buildPutioUrl(this.baseUrl, `/v2/files/${encodePathSegment(file.id)}/mp4/stream`, { oauth_token: this.downloadToken });
|
|
187
198
|
}
|
|
188
|
-
|
|
199
|
+
getStreamUrl(file) {
|
|
189
200
|
switch (getFileRenderType(file)) {
|
|
190
|
-
case "audio": return buildPutioUrl(this.
|
|
191
|
-
case "video": return buildPutioUrl(this.
|
|
201
|
+
case "audio": return buildPutioUrl(this.baseUrl, `/v2/files/${encodePathSegment(file.id)}/stream.mp3`, { oauth_token: this.downloadToken });
|
|
202
|
+
case "video": return buildPutioUrl(this.baseUrl, `/v2/files/${encodePathSegment(file.id)}/stream`, { oauth_token: this.downloadToken });
|
|
192
203
|
default: return null;
|
|
193
204
|
}
|
|
194
205
|
}
|
|
195
|
-
|
|
206
|
+
getXspfUrl(file) {
|
|
196
207
|
if (!isVideoFile(file)) return null;
|
|
197
|
-
return buildPutioUrl(this.
|
|
208
|
+
return buildPutioUrl(this.baseUrl, `/v2/files/${encodePathSegment(file.id)}/xspf`, { oauth_token: this.downloadToken });
|
|
198
209
|
}
|
|
199
210
|
};
|
|
200
211
|
//#endregion
|
|
@@ -275,6 +286,6 @@ const truncateMiddle = (input, options) => {
|
|
|
275
286
|
return `${input.slice(0, frontLength)}${ellipsis}${input.slice(input.length - backLength)}`;
|
|
276
287
|
};
|
|
277
288
|
//#endregion
|
|
278
|
-
export {
|
|
289
|
+
export { FileUrlProvider, LocalizedError, createLocalizeError, daysDiff, daysDiffFromNow, dotsToSpaces, ensureUTC, formatDate, getFileRenderType, getUnixTimestamp, isErrorLocalizer, secondsToDuration, secondsToReadableDuration, toHumanFileSize, toTimeAgo, truncate, truncateMiddle };
|
|
279
290
|
|
|
280
291
|
//# sourceMappingURL=utilities.js.map
|
package/dist/utilities.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utilities.js","names":[],"sources":["../src/utilities/date.ts","../src/utilities/duration.ts","../src/utilities/file-render-type.ts","../src/utilities/file-size.ts","../src/utilities/file-url-provider.ts","../src/utilities/localized-error.ts","../src/utilities/string.ts"],"sourcesContent":["const LONG_DATE_FORMATTER = new Intl.DateTimeFormat(\"en-US\", {\n dateStyle: \"long\",\n timeZone: \"UTC\",\n});\n\nconst RELATIVE_TIME_FORMATTER = new Intl.RelativeTimeFormat(\"en\", {\n numeric: \"always\",\n});\n\nconst ensureDate = (value: unknown): Date => {\n if (value instanceof Date) {\n return value;\n }\n\n if (typeof value === \"string\") {\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n return new Date(`${value}T00:00:00.000Z`);\n }\n\n return new Date(value.endsWith(\"Z\") ? value : `${value}Z`);\n }\n\n return new Date();\n};\n\nconst toRelativeUnit = (secondsDelta: number) => {\n const absoluteSeconds = Math.abs(secondsDelta);\n\n if (absoluteSeconds >= 60 * 60 * 24 * 365) {\n return { unit: \"year\" as const, value: Math.round(secondsDelta / (60 * 60 * 24 * 365)) };\n }\n\n if (absoluteSeconds >= 60 * 60 * 24 * 30) {\n return { unit: \"month\" as const, value: Math.round(secondsDelta / (60 * 60 * 24 * 30)) };\n }\n\n if (absoluteSeconds >= 60 * 60 * 24) {\n return { unit: \"day\" as const, value: Math.round(secondsDelta / (60 * 60 * 24)) };\n }\n\n if (absoluteSeconds >= 60 * 60) {\n return { unit: \"hour\" as const, value: Math.round(secondsDelta / (60 * 60)) };\n }\n\n if (absoluteSeconds >= 60) {\n return { unit: \"minute\" as const, value: Math.round(secondsDelta / 60) };\n }\n\n return { unit: \"second\" as const, value: Math.round(secondsDelta) };\n};\n\nexport const ensureUTC = (date: unknown = null): string => ensureDate(date).toISOString();\n\nexport const toTimeAgo = (date: unknown = null): string => {\n if (!date) {\n return \"N/A\";\n }\n\n const target = ensureDate(date);\n const secondsDelta = Math.round((target.getTime() - Date.now()) / 1000);\n const { unit, value } = toRelativeUnit(secondsDelta);\n return RELATIVE_TIME_FORMATTER.format(value, unit);\n};\n\nexport const formatDate = (\n date: unknown = null,\n format: \"LL\" | Intl.DateTimeFormatOptions = \"LL\",\n): string => {\n if (!date) {\n return \"N/A\";\n }\n\n const normalizedDate = ensureDate(date);\n\n if (format === \"LL\") {\n return LONG_DATE_FORMATTER.format(normalizedDate);\n }\n\n return new Intl.DateTimeFormat(\"en-US\", {\n timeZone: \"UTC\",\n ...format,\n }).format(normalizedDate);\n};\n\nexport const daysDiff = (date1: unknown, date2: unknown): number => {\n const day1 = ensureDate(date1);\n const day2 = ensureDate(date2);\n\n const utcDay1 = Date.UTC(day1.getUTCFullYear(), day1.getUTCMonth(), day1.getUTCDate());\n const utcDay2 = Date.UTC(day2.getUTCFullYear(), day2.getUTCMonth(), day2.getUTCDate());\n\n return Math.abs(Math.round((utcDay2 - utcDay1) / (1000 * 60 * 60 * 24)));\n};\n\nexport const daysDiffFromNow = (date: unknown = null): number => {\n if (!date) {\n return 0;\n }\n\n return daysDiff(date, new Date());\n};\n\nexport const getUnixTimestamp = (date: unknown): number =>\n Math.floor(ensureDate(date).getTime() / 1000);\n","const normalizeNumber = (value: unknown): number => (typeof value === \"number\" ? value : 0);\n\nconst formatPadded = (value: number): string => String(value).padStart(2, \"0\");\n\nexport const secondsToDuration = (seconds: unknown): string => {\n const normalizedSeconds = Math.floor(normalizeNumber(seconds));\n\n if (normalizedSeconds <= 0) {\n return \"00:00\";\n }\n\n const days = Math.floor(normalizedSeconds / 86_400);\n const hours = Math.floor((normalizedSeconds % 86_400) / 3_600);\n const minutes = Math.floor((normalizedSeconds % 3_600) / 60);\n const remainingSeconds = normalizedSeconds % 60;\n\n if (days > 0) {\n return [days, hours, minutes, remainingSeconds].map(formatPadded).join(\":\");\n }\n\n if (normalizedSeconds >= 3_600) {\n return [Math.floor(normalizedSeconds / 3_600), minutes, remainingSeconds]\n .map(formatPadded)\n .join(\":\");\n }\n\n return [minutes, remainingSeconds].map(formatPadded).join(\":\");\n};\n\nexport const secondsToReadableDuration = (seconds: unknown): string => {\n const normalizedSeconds = Math.floor(normalizeNumber(seconds));\n\n if (normalizedSeconds <= 0) {\n return \"N/A\";\n }\n\n if (normalizedSeconds < 60) {\n return `${normalizedSeconds} s`;\n }\n\n if (normalizedSeconds >= 86_400) {\n const days = Math.floor(normalizedSeconds / 86_400);\n const roundedHours = Math.round((normalizedSeconds - days * 86_400) / 3_600);\n\n if (roundedHours === 24) {\n return `${days + 1}d`;\n }\n\n return roundedHours > 0 ? `${days}d ${roundedHours}h` : `${days}d`;\n }\n\n if (normalizedSeconds >= 3_600) {\n const hours = Math.floor(normalizedSeconds / 3_600);\n const roundedMinutes = Math.round((normalizedSeconds - hours * 3_600) / 60);\n return roundedMinutes > 0 ? `${hours}h ${roundedMinutes}m` : `${hours}h`;\n }\n\n const roundedMinutes = Math.round(normalizedSeconds / 60);\n const remainingSeconds = normalizedSeconds - roundedMinutes * 60;\n return remainingSeconds > 0 ? `${roundedMinutes}m ${remainingSeconds}s` : `${roundedMinutes}m`;\n};\n","import type { FileBroad } from \"../domains/files.js\";\n\nexport type FileRenderType =\n | \"archive\"\n | \"audio\"\n | \"epub\"\n | \"folder\"\n | \"image\"\n | \"other\"\n | \"pdf\"\n | \"text\"\n | \"text/markdown\"\n | \"video\";\n\nexport type FileRenderTypeInput = Pick<FileBroad, \"content_type\" | \"extension\" | \"file_type\">;\n\nexport const getFileRenderType = (file: FileRenderTypeInput): FileRenderType => {\n const { content_type, extension, file_type } = file;\n\n if (typeof content_type !== \"string\") {\n return \"other\";\n }\n\n if (content_type === \"application/x-directory\") {\n return \"folder\";\n }\n\n if (content_type.startsWith(\"audio\") || file_type === \"AUDIO\") {\n return \"audio\";\n }\n\n if (content_type.startsWith(\"video\")) {\n return \"video\";\n }\n\n if (content_type.startsWith(\"image\")) {\n return \"image\";\n }\n\n if (content_type === \"application/pdf\") {\n return \"pdf\";\n }\n\n if (content_type === \"application/epub+zip\") {\n return \"epub\";\n }\n\n if (content_type === \"application/zip\" || content_type === \"application/x-rar\") {\n return \"archive\";\n }\n\n if (\n content_type.startsWith(\"text\") ||\n content_type.startsWith(\"application/x-subrip\") ||\n file_type === \"TEXT\"\n ) {\n if (content_type.endsWith(\"/markdown\") || extension === \"md\") {\n return \"text/markdown\";\n }\n\n return \"text\";\n }\n\n return \"other\";\n};\n","export interface HumanFileSizeOptions {\n readonly decimals?: number;\n readonly unitSeparator?: string;\n}\n\nconst FILE_SIZE_UNITS = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\"] as const;\n\nexport const toHumanFileSize = (\n fileSizeInBytes: string | number,\n options: HumanFileSizeOptions = {},\n): string => {\n const { decimals = 1, unitSeparator = \" \" } = options;\n const parsed =\n typeof fileSizeInBytes === \"string\" ? Number.parseInt(fileSizeInBytes, 10) : fileSizeInBytes;\n\n if (!Number.isFinite(parsed) || parsed <= 0) {\n return `0${unitSeparator}B`;\n }\n\n const exponent = Math.min(\n Math.floor(Math.log(parsed) / Math.log(1024)),\n FILE_SIZE_UNITS.length - 1,\n );\n const normalized = parsed / 1024 ** exponent;\n const fractionDigits = normalized % 1 === 0 ? 0 : decimals;\n\n return `${normalized.toFixed(fractionDigits)}${unitSeparator}${FILE_SIZE_UNITS[exponent]}`;\n};\n","import { joinCsv } from \"../core/forms.js\";\nimport { buildPutioUrl, encodePathSegment } from \"../core/http.js\";\nimport type { FileBroad } from \"../domains/files.js\";\nimport { getFileRenderType, type FileRenderTypeInput } from \"./file-render-type.js\";\n\nexport type FileUrlProviderInput = Pick<\n FileBroad,\n \"content_type\" | \"extension\" | \"file_type\" | \"id\" | \"is_mp4_available\"\n>;\n\nconst normalizeApiBaseUrl = (apiURL: string): string =>\n apiURL.endsWith(\"/v2\") ? apiURL.slice(0, -3) : apiURL;\n\nconst isVideoFile = (file: FileRenderTypeInput): boolean => getFileRenderType(file) === \"video\";\n\nexport class FileURLProvider {\n readonly apiURL: string;\n\n readonly token: string;\n\n readonly baseURL: string;\n\n constructor(apiURL: string, token: string) {\n this.baseURL = normalizeApiBaseUrl(apiURL);\n this.apiURL = `${this.baseURL}/v2`;\n this.token = token;\n }\n\n getDownloadURL(fileOrFileId: FileUrlProviderInput | number): string | null {\n if (typeof fileOrFileId === \"number\") {\n return buildPutioUrl(this.baseURL, `/v2/files/${encodePathSegment(fileOrFileId)}/download`, {\n oauth_token: this.token,\n });\n }\n\n if (fileOrFileId.file_type === \"FOLDER\") {\n return null;\n }\n\n return buildPutioUrl(this.baseURL, `/v2/files/${encodePathSegment(fileOrFileId.id)}/download`, {\n oauth_token: this.token,\n });\n }\n\n getHLSStreamURL(\n file: FileUrlProviderInput,\n params: {\n readonly maxSubtitleCount?: number;\n readonly playOriginal?: boolean;\n readonly subtitleLanguages?: ReadonlyArray<string>;\n } = {},\n ): string | null {\n if (!isVideoFile(file)) {\n return null;\n }\n\n return buildPutioUrl(this.baseURL, `/v2/files/${encodePathSegment(file.id)}/hls/media.m3u8`, {\n max_subtitle_count: params.maxSubtitleCount,\n oauth_token: this.token,\n original: params.playOriginal ? 1 : undefined,\n subtitle_languages: joinCsv(params.subtitleLanguages),\n });\n }\n\n getMP4DownloadURL(file: FileUrlProviderInput): string | null {\n if (!isVideoFile(file) || !file.is_mp4_available) {\n return null;\n }\n\n return buildPutioUrl(this.baseURL, `/v2/files/${encodePathSegment(file.id)}/mp4/download`, {\n oauth_token: this.token,\n });\n }\n\n getMP4StreamURL(file: FileUrlProviderInput): string | null {\n if (!isVideoFile(file) || !file.is_mp4_available) {\n return null;\n }\n\n return buildPutioUrl(this.baseURL, `/v2/files/${encodePathSegment(file.id)}/mp4/stream`, {\n oauth_token: this.token,\n });\n }\n\n getStreamURL(file: FileUrlProviderInput): string | null {\n switch (getFileRenderType(file)) {\n case \"audio\":\n return buildPutioUrl(this.baseURL, `/v2/files/${encodePathSegment(file.id)}/stream.mp3`, {\n oauth_token: this.token,\n });\n case \"video\":\n return buildPutioUrl(this.baseURL, `/v2/files/${encodePathSegment(file.id)}/stream`, {\n oauth_token: this.token,\n });\n default:\n return null;\n }\n }\n\n getXSPFURL(file: FileUrlProviderInput): string | null {\n if (!isVideoFile(file)) {\n return null;\n }\n\n return buildPutioUrl(this.baseURL, `/v2/files/${encodePathSegment(file.id)}/xspf`, {\n oauth_token: this.token,\n });\n }\n}\n","import { Predicate } from \"effect\";\n\nimport {\n PutioApiError,\n PutioAuthError,\n type PutioErrorEnvelope,\n isPutioErrorEnvelope,\n PutioOperationError,\n PutioRateLimitError,\n type PutioKnownOperationErrorContract,\n} from \"../core/errors.js\";\n\nexport type LocalizedErrorRecoverySuggestionTypeInstruction = {\n readonly type: \"instruction\";\n readonly description: string;\n};\n\nexport type LocalizedErrorRecoverySuggestionTypeAction = {\n readonly type: \"action\";\n readonly description: string;\n readonly trigger: {\n readonly label: string;\n readonly callback: () => void;\n };\n};\n\nexport type LocalizedErrorRecoverySuggestionTypeCaptcha = {\n readonly type: \"captcha\";\n readonly description: string;\n};\n\nexport type LocalizedErrorRecoverySuggestion =\n | LocalizedErrorRecoverySuggestionTypeInstruction\n | LocalizedErrorRecoverySuggestionTypeAction\n | LocalizedErrorRecoverySuggestionTypeCaptcha;\n\nexport interface LocalizedErrorParams {\n readonly message: string;\n readonly recoverySuggestion: LocalizedErrorRecoverySuggestion;\n readonly underlyingError: unknown;\n readonly meta?: Record<string, unknown>;\n}\n\nexport class LocalizedError extends Error {\n readonly recoverySuggestion: LocalizedErrorRecoverySuggestion;\n\n readonly underlyingError: unknown;\n\n readonly meta: Record<string, unknown>;\n\n constructor(params: LocalizedErrorParams) {\n super(params.message);\n this.name = \"LocalizedError\";\n this.recoverySuggestion = params.recoverySuggestion;\n this.underlyingError = params.underlyingError;\n this.meta = params.meta ?? {};\n }\n}\n\ntype StatusAndBodyError =\n | PutioApiError\n | PutioAuthError\n | PutioRateLimitError\n | PutioOperationError<string, string, PutioKnownOperationErrorContract>;\n\nexport type PutioLocalizableError = {\n readonly _tag?: string;\n readonly body: PutioErrorEnvelope;\n readonly status?: number;\n};\n\nexport type PutioErrorWithBody = StatusAndBodyError;\n\nconst isLocalizablePutioErrorEnvelope = (value: unknown): value is PutioErrorEnvelope =>\n Predicate.isObject(value) &&\n (\"status_code\" in value || \"error_type\" in value || \"error_message\" in value) &&\n isPutioErrorEnvelope(value);\n\nconst isPutioErrorWithBody = (value: unknown): value is PutioLocalizableError =>\n Predicate.isObject(value) && \"body\" in value && isPutioErrorEnvelope(value.body);\n\nconst normalizePutioError = (\n error: PutioErrorEnvelope | PutioLocalizableError,\n): PutioLocalizableError => {\n if (isLocalizablePutioErrorEnvelope(error)) {\n return {\n _tag: \"PutioErrorEnvelope\",\n body: error,\n status: error.status_code,\n };\n }\n\n return error;\n};\n\nexport type LocalizeErrorFn<E> = (\n error: E,\n) => Pick<LocalizedErrorParams, \"message\" | \"recoverySuggestion\" | \"meta\">;\n\nexport type APIErrorByStatusCodeLocalizer = {\n readonly kind: \"api_status_code\";\n readonly status_code: number;\n readonly localize: LocalizeErrorFn<PutioLocalizableError>;\n};\n\nexport type APIErrorByErrorTypeLocalizer = {\n readonly kind: \"api_error_type\";\n readonly error_type: string;\n readonly localize: LocalizeErrorFn<PutioLocalizableError>;\n};\n\nexport type MatchConditionLocalizer<E> = {\n readonly kind: \"match_condition\";\n readonly match: (error: E) => boolean;\n readonly localize: LocalizeErrorFn<E>;\n};\n\nexport type GenericErrorLocalizer = {\n readonly kind: \"generic\";\n readonly localize: LocalizeErrorFn<unknown>;\n};\n\nexport type ErrorLocalizer<E> =\n | APIErrorByStatusCodeLocalizer\n | APIErrorByErrorTypeLocalizer\n | MatchConditionLocalizer<E>\n | GenericErrorLocalizer;\n\nexport type ErrorLocalizerFn = (error: unknown) => LocalizedError;\n\nexport const isErrorLocalizer = (fn: unknown): fn is ErrorLocalizerFn => {\n try {\n return typeof fn === \"function\" && fn(new Error(\"test\")) instanceof LocalizedError;\n } catch {\n return false;\n }\n};\n\nexport const createLocalizeError =\n <GlobalError>(globalLocalizers: ReadonlyArray<ErrorLocalizer<GlobalError>>) =>\n <ScopedError extends GlobalError>(\n error: ScopedError,\n scopedLocalizers: ReadonlyArray<ErrorLocalizer<ScopedError>> = [],\n ): LocalizedError => {\n const localizers = [...scopedLocalizers, ...globalLocalizers];\n\n if (isPutioErrorWithBody(error) || isLocalizablePutioErrorEnvelope(error)) {\n const apiError = normalizePutioError(error);\n\n const byErrorType = localizers.find(\n (localizer): localizer is APIErrorByErrorTypeLocalizer =>\n localizer.kind === \"api_error_type\" && localizer.error_type === apiError.body.error_type,\n );\n\n if (byErrorType) {\n return new LocalizedError({\n underlyingError: apiError,\n ...byErrorType.localize(apiError),\n });\n }\n\n const byMatchCondition = localizers.find(\n (localizer): localizer is MatchConditionLocalizer<ScopedError> =>\n localizer.kind === \"match_condition\" && localizer.match(error),\n );\n\n if (byMatchCondition) {\n return new LocalizedError({\n underlyingError: error,\n ...byMatchCondition.localize(error),\n });\n }\n\n const byStatusCode = localizers.find(\n (localizer): localizer is APIErrorByStatusCodeLocalizer =>\n localizer.kind === \"api_status_code\" &&\n localizer.status_code === apiError.body.status_code,\n );\n\n if (byStatusCode) {\n return new LocalizedError({\n underlyingError: apiError,\n ...byStatusCode.localize(apiError),\n });\n }\n }\n\n const byMatchCondition = localizers.find(\n (localizer): localizer is MatchConditionLocalizer<ScopedError> =>\n localizer.kind === \"match_condition\" && localizer.match(error),\n );\n\n if (byMatchCondition) {\n return new LocalizedError({\n underlyingError: error,\n ...byMatchCondition.localize(error),\n });\n }\n\n const genericLocalizer = localizers.find(\n (localizer): localizer is GenericErrorLocalizer => localizer.kind === \"generic\",\n );\n\n if (genericLocalizer) {\n return new LocalizedError({\n underlyingError: error,\n ...genericLocalizer.localize(error),\n });\n }\n\n throw new Error(`No localizer found for error: ${String(error)}`);\n };\n","export const dotsToSpaces = (input: string): string => input.split(\".\").join(\" \");\n\nexport const truncate = (\n input: string,\n options: {\n readonly length: number;\n readonly ellipsis?: string;\n },\n): string => {\n const { ellipsis = \"...\", length } = options;\n\n if (input.length <= length) {\n return input;\n }\n\n return `${input.slice(0, length)}${ellipsis}`;\n};\n\nexport const truncateMiddle = (\n input: string,\n options: {\n readonly frontLength: number;\n readonly backLength: number;\n readonly ellipsis?: string;\n },\n): string => {\n const { backLength, ellipsis = \"...\", frontLength } = options;\n const preservedLength = frontLength + backLength;\n\n if (input.length <= preservedLength) {\n return input;\n }\n\n return `${input.slice(0, frontLength)}${ellipsis}${input.slice(input.length - backLength)}`;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utilities.js","names":[],"sources":["../src/utilities/date.ts","../src/utilities/duration.ts","../src/utilities/file-render-type.ts","../src/utilities/file-size.ts","../src/utilities/file-url-provider.ts","../src/utilities/localized-error.ts","../src/utilities/string.ts"],"sourcesContent":["const LONG_DATE_FORMATTER = new Intl.DateTimeFormat(\"en-US\", {\n dateStyle: \"long\",\n timeZone: \"UTC\",\n});\n\nconst RELATIVE_TIME_FORMATTER = new Intl.RelativeTimeFormat(\"en\", {\n numeric: \"always\",\n});\n\nconst ensureDate = (value: unknown): Date => {\n if (value instanceof Date) {\n return value;\n }\n\n if (typeof value === \"string\") {\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n return new Date(`${value}T00:00:00.000Z`);\n }\n\n return new Date(value.endsWith(\"Z\") ? value : `${value}Z`);\n }\n\n return new Date();\n};\n\nconst toRelativeUnit = (secondsDelta: number) => {\n const absoluteSeconds = Math.abs(secondsDelta);\n\n if (absoluteSeconds >= 60 * 60 * 24 * 365) {\n return { unit: \"year\" as const, value: Math.round(secondsDelta / (60 * 60 * 24 * 365)) };\n }\n\n if (absoluteSeconds >= 60 * 60 * 24 * 30) {\n return { unit: \"month\" as const, value: Math.round(secondsDelta / (60 * 60 * 24 * 30)) };\n }\n\n if (absoluteSeconds >= 60 * 60 * 24) {\n return { unit: \"day\" as const, value: Math.round(secondsDelta / (60 * 60 * 24)) };\n }\n\n if (absoluteSeconds >= 60 * 60) {\n return { unit: \"hour\" as const, value: Math.round(secondsDelta / (60 * 60)) };\n }\n\n if (absoluteSeconds >= 60) {\n return { unit: \"minute\" as const, value: Math.round(secondsDelta / 60) };\n }\n\n return { unit: \"second\" as const, value: Math.round(secondsDelta) };\n};\n\nexport const ensureUTC = (date: unknown = null): string => ensureDate(date).toISOString();\n\nexport const toTimeAgo = (date: unknown = null): string => {\n if (!date) {\n return \"N/A\";\n }\n\n const target = ensureDate(date);\n const secondsDelta = Math.round((target.getTime() - Date.now()) / 1000);\n const { unit, value } = toRelativeUnit(secondsDelta);\n return RELATIVE_TIME_FORMATTER.format(value, unit);\n};\n\nexport const formatDate = (\n date: unknown = null,\n format: \"LL\" | Intl.DateTimeFormatOptions = \"LL\",\n): string => {\n if (!date) {\n return \"N/A\";\n }\n\n const normalizedDate = ensureDate(date);\n\n if (format === \"LL\") {\n return LONG_DATE_FORMATTER.format(normalizedDate);\n }\n\n return new Intl.DateTimeFormat(\"en-US\", {\n timeZone: \"UTC\",\n ...format,\n }).format(normalizedDate);\n};\n\nexport const daysDiff = (date1: unknown, date2: unknown): number => {\n const day1 = ensureDate(date1);\n const day2 = ensureDate(date2);\n\n const utcDay1 = Date.UTC(day1.getUTCFullYear(), day1.getUTCMonth(), day1.getUTCDate());\n const utcDay2 = Date.UTC(day2.getUTCFullYear(), day2.getUTCMonth(), day2.getUTCDate());\n\n return Math.abs(Math.round((utcDay2 - utcDay1) / (1000 * 60 * 60 * 24)));\n};\n\nexport const daysDiffFromNow = (date: unknown = null): number => {\n if (!date) {\n return 0;\n }\n\n return daysDiff(date, new Date());\n};\n\nexport const getUnixTimestamp = (date: unknown): number =>\n Math.floor(ensureDate(date).getTime() / 1000);\n","const normalizeNumber = (value: unknown): number => (typeof value === \"number\" ? value : 0);\n\nconst formatPadded = (value: number): string => String(value).padStart(2, \"0\");\n\nexport const secondsToDuration = (seconds: unknown): string => {\n const normalizedSeconds = Math.floor(normalizeNumber(seconds));\n\n if (normalizedSeconds <= 0) {\n return \"00:00\";\n }\n\n const days = Math.floor(normalizedSeconds / 86_400);\n const hours = Math.floor((normalizedSeconds % 86_400) / 3_600);\n const minutes = Math.floor((normalizedSeconds % 3_600) / 60);\n const remainingSeconds = normalizedSeconds % 60;\n\n if (days > 0) {\n return [days, hours, minutes, remainingSeconds].map(formatPadded).join(\":\");\n }\n\n if (normalizedSeconds >= 3_600) {\n return [Math.floor(normalizedSeconds / 3_600), minutes, remainingSeconds]\n .map(formatPadded)\n .join(\":\");\n }\n\n return [minutes, remainingSeconds].map(formatPadded).join(\":\");\n};\n\nexport const secondsToReadableDuration = (seconds: unknown): string => {\n const normalizedSeconds = Math.floor(normalizeNumber(seconds));\n\n if (normalizedSeconds <= 0) {\n return \"N/A\";\n }\n\n if (normalizedSeconds < 60) {\n return `${normalizedSeconds} s`;\n }\n\n if (normalizedSeconds >= 86_400) {\n const days = Math.floor(normalizedSeconds / 86_400);\n const roundedHours = Math.round((normalizedSeconds - days * 86_400) / 3_600);\n\n if (roundedHours === 24) {\n return `${days + 1}d`;\n }\n\n return roundedHours > 0 ? `${days}d ${roundedHours}h` : `${days}d`;\n }\n\n if (normalizedSeconds >= 3_600) {\n const hours = Math.floor(normalizedSeconds / 3_600);\n const roundedMinutes = Math.round((normalizedSeconds - hours * 3_600) / 60);\n return roundedMinutes > 0 ? `${hours}h ${roundedMinutes}m` : `${hours}h`;\n }\n\n const roundedMinutes = Math.round(normalizedSeconds / 60);\n const remainingSeconds = normalizedSeconds - roundedMinutes * 60;\n return remainingSeconds > 0 ? `${roundedMinutes}m ${remainingSeconds}s` : `${roundedMinutes}m`;\n};\n","import type { FileBroad } from \"../domains/files.js\";\n\nexport type FileRenderType =\n | \"archive\"\n | \"audio\"\n | \"epub\"\n | \"folder\"\n | \"image\"\n | \"other\"\n | \"pdf\"\n | \"text\"\n | \"text/markdown\"\n | \"video\";\n\nexport type FileRenderTypeInput = Pick<FileBroad, \"content_type\" | \"extension\" | \"file_type\">;\n\nexport const getFileRenderType = (file: FileRenderTypeInput): FileRenderType => {\n const { content_type, extension, file_type } = file;\n\n if (typeof content_type !== \"string\") {\n return \"other\";\n }\n\n if (content_type === \"application/x-directory\") {\n return \"folder\";\n }\n\n if (content_type.startsWith(\"audio\") || file_type === \"AUDIO\") {\n return \"audio\";\n }\n\n if (content_type.startsWith(\"video\")) {\n return \"video\";\n }\n\n if (content_type.startsWith(\"image\")) {\n return \"image\";\n }\n\n if (content_type === \"application/pdf\") {\n return \"pdf\";\n }\n\n if (content_type === \"application/epub+zip\") {\n return \"epub\";\n }\n\n if (content_type === \"application/zip\" || content_type === \"application/x-rar\") {\n return \"archive\";\n }\n\n if (\n content_type.startsWith(\"text\") ||\n content_type.startsWith(\"application/x-subrip\") ||\n file_type === \"TEXT\"\n ) {\n if (content_type.endsWith(\"/markdown\") || extension === \"md\") {\n return \"text/markdown\";\n }\n\n return \"text\";\n }\n\n return \"other\";\n};\n","export interface HumanFileSizeOptions {\n readonly decimals?: number;\n readonly unitSeparator?: string;\n}\n\nconst FILE_SIZE_UNITS = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\"] as const;\n\nexport const toHumanFileSize = (\n fileSizeInBytes: string | number,\n options: HumanFileSizeOptions = {},\n): string => {\n const { decimals = 1, unitSeparator = \" \" } = options;\n const parsed =\n typeof fileSizeInBytes === \"string\" ? Number.parseInt(fileSizeInBytes, 10) : fileSizeInBytes;\n\n if (!Number.isFinite(parsed) || parsed <= 0) {\n return `0${unitSeparator}B`;\n }\n\n const exponent = Math.min(\n Math.floor(Math.log(parsed) / Math.log(1024)),\n FILE_SIZE_UNITS.length - 1,\n );\n const normalized = parsed / 1024 ** exponent;\n const fractionDigits = normalized % 1 === 0 ? 0 : decimals;\n\n return `${normalized.toFixed(fractionDigits)}${unitSeparator}${FILE_SIZE_UNITS[exponent]}`;\n};\n","import { Result, Schema } from \"effect\";\n\nimport { mapDecodeErrorToValidationError } from \"../core/errors.js\";\nimport { joinCsv } from \"../core/forms.js\";\nimport { buildPutioUrl, encodePathSegment } from \"../core/http.js\";\nimport type { FileBroad } from \"../domains/files.js\";\nimport { NonEmptyStringSchema } from \"../core/validation.js\";\nimport { getFileRenderType, type FileRenderTypeInput } from \"./file-render-type.js\";\n\nexport type FileUrlProviderInput = Pick<\n FileBroad,\n \"content_type\" | \"extension\" | \"file_type\" | \"id\" | \"is_mp4_available\"\n>;\n\nconst FileUrlProviderOptionsSchema = Schema.Struct({\n baseUrl: NonEmptyStringSchema,\n downloadToken: NonEmptyStringSchema,\n});\n\nexport type FileUrlProviderOptions = {\n /** The put.io API origin, with or without the trailing `/v2`. */\n readonly baseUrl: string;\n /** The account download token from `getAccountInfo({ download_token: 1 })`, not the OAuth token. */\n readonly downloadToken: string;\n};\n\nconst decodeOptions = (options: FileUrlProviderOptions): FileUrlProviderOptions => {\n const decoded = Schema.decodeUnknownResult(FileUrlProviderOptionsSchema, {\n onExcessProperty: \"error\",\n })(options);\n if (Result.isFailure(decoded)) {\n throw mapDecodeErrorToValidationError(decoded.failure);\n }\n return decoded.success;\n};\n\nconst normalizeApiBaseUrl = (apiUrl: string): string =>\n apiUrl.endsWith(\"/v2\") ? apiUrl.slice(0, -3) : apiUrl;\n\nconst isVideoFile = (file: FileRenderTypeInput): boolean => getFileRenderType(file) === \"video\";\n\nexport class FileUrlProvider {\n readonly apiUrl: string;\n\n /** The account download token from `getAccountInfo({ download_token: 1 })`, not the OAuth token. */\n readonly downloadToken: string;\n\n readonly baseUrl: string;\n\n constructor(options: FileUrlProviderOptions) {\n const { baseUrl, downloadToken } = decodeOptions(options);\n this.baseUrl = normalizeApiBaseUrl(baseUrl);\n this.apiUrl = `${this.baseUrl}/v2`;\n this.downloadToken = downloadToken;\n }\n\n getDownloadUrl(fileOrFileId: FileUrlProviderInput | number): string | null {\n if (typeof fileOrFileId === \"number\") {\n return buildPutioUrl(this.baseUrl, `/v2/files/${encodePathSegment(fileOrFileId)}/download`, {\n oauth_token: this.downloadToken,\n });\n }\n\n if (fileOrFileId.file_type === \"FOLDER\") {\n return null;\n }\n\n return buildPutioUrl(this.baseUrl, `/v2/files/${encodePathSegment(fileOrFileId.id)}/download`, {\n oauth_token: this.downloadToken,\n });\n }\n\n getHlsStreamUrl(\n file: FileUrlProviderInput,\n params: {\n readonly maxSubtitleCount?: number;\n readonly playOriginal?: boolean;\n readonly subtitleLanguages?: ReadonlyArray<string>;\n } = {},\n ): string | null {\n if (!isVideoFile(file)) {\n return null;\n }\n\n return buildPutioUrl(this.baseUrl, `/v2/files/${encodePathSegment(file.id)}/hls/media.m3u8`, {\n max_subtitle_count: params.maxSubtitleCount,\n oauth_token: this.downloadToken,\n original: params.playOriginal ? 1 : undefined,\n subtitle_languages: joinCsv(params.subtitleLanguages),\n });\n }\n\n getMp4DownloadUrl(file: FileUrlProviderInput): string | null {\n if (!isVideoFile(file) || !file.is_mp4_available) {\n return null;\n }\n\n return buildPutioUrl(this.baseUrl, `/v2/files/${encodePathSegment(file.id)}/mp4/download`, {\n oauth_token: this.downloadToken,\n });\n }\n\n getMp4StreamUrl(file: FileUrlProviderInput): string | null {\n if (!isVideoFile(file) || !file.is_mp4_available) {\n return null;\n }\n\n return buildPutioUrl(this.baseUrl, `/v2/files/${encodePathSegment(file.id)}/mp4/stream`, {\n oauth_token: this.downloadToken,\n });\n }\n\n getStreamUrl(file: FileUrlProviderInput): string | null {\n switch (getFileRenderType(file)) {\n case \"audio\":\n return buildPutioUrl(this.baseUrl, `/v2/files/${encodePathSegment(file.id)}/stream.mp3`, {\n oauth_token: this.downloadToken,\n });\n case \"video\":\n return buildPutioUrl(this.baseUrl, `/v2/files/${encodePathSegment(file.id)}/stream`, {\n oauth_token: this.downloadToken,\n });\n default:\n return null;\n }\n }\n\n getXspfUrl(file: FileUrlProviderInput): string | null {\n if (!isVideoFile(file)) {\n return null;\n }\n\n return buildPutioUrl(this.baseUrl, `/v2/files/${encodePathSegment(file.id)}/xspf`, {\n oauth_token: this.downloadToken,\n });\n }\n}\n","import { Predicate } from \"effect\";\n\nimport {\n PutioApiError,\n PutioAuthError,\n type PutioErrorEnvelope,\n isPutioErrorEnvelope,\n PutioOperationError,\n PutioRateLimitError,\n type PutioKnownOperationErrorContract,\n} from \"../core/errors.js\";\n\nexport type LocalizedErrorRecoverySuggestionTypeInstruction = {\n readonly type: \"instruction\";\n readonly description: string;\n};\n\nexport type LocalizedErrorRecoverySuggestionTypeAction = {\n readonly type: \"action\";\n readonly description: string;\n readonly trigger: {\n readonly label: string;\n readonly callback: () => void;\n };\n};\n\nexport type LocalizedErrorRecoverySuggestionTypeCaptcha = {\n readonly type: \"captcha\";\n readonly description: string;\n};\n\nexport type LocalizedErrorRecoverySuggestion =\n | LocalizedErrorRecoverySuggestionTypeInstruction\n | LocalizedErrorRecoverySuggestionTypeAction\n | LocalizedErrorRecoverySuggestionTypeCaptcha;\n\nexport interface LocalizedErrorParams {\n readonly message: string;\n readonly recoverySuggestion: LocalizedErrorRecoverySuggestion;\n readonly underlyingError: unknown;\n readonly meta?: Record<string, unknown>;\n}\n\nexport class LocalizedError extends Error {\n readonly recoverySuggestion: LocalizedErrorRecoverySuggestion;\n\n readonly underlyingError: unknown;\n\n readonly meta: Record<string, unknown>;\n\n constructor(params: LocalizedErrorParams) {\n super(params.message);\n this.name = \"LocalizedError\";\n this.recoverySuggestion = params.recoverySuggestion;\n this.underlyingError = params.underlyingError;\n this.meta = params.meta ?? {};\n }\n}\n\ntype StatusAndBodyError =\n | PutioApiError\n | PutioAuthError\n | PutioRateLimitError\n | PutioOperationError<string, string, PutioKnownOperationErrorContract>;\n\nexport type PutioLocalizableError = {\n readonly _tag?: string;\n readonly body: PutioErrorEnvelope;\n readonly status?: number;\n};\n\nexport type PutioErrorWithBody = StatusAndBodyError;\n\nconst isLocalizablePutioErrorEnvelope = (value: unknown): value is PutioErrorEnvelope =>\n Predicate.isObject(value) &&\n (\"status_code\" in value || \"error_type\" in value || \"error_message\" in value) &&\n isPutioErrorEnvelope(value);\n\nconst isPutioErrorWithBody = (value: unknown): value is PutioLocalizableError =>\n Predicate.isObject(value) && \"body\" in value && isPutioErrorEnvelope(value.body);\n\nconst normalizePutioError = (\n error: PutioErrorEnvelope | PutioLocalizableError,\n): PutioLocalizableError => {\n if (isLocalizablePutioErrorEnvelope(error)) {\n return {\n _tag: \"PutioErrorEnvelope\",\n body: error,\n status: error.status_code,\n };\n }\n\n return error;\n};\n\nexport type LocalizeErrorFn<E> = (\n error: E,\n) => Pick<LocalizedErrorParams, \"message\" | \"recoverySuggestion\" | \"meta\">;\n\nexport type APIErrorByStatusCodeLocalizer = {\n readonly kind: \"api_status_code\";\n readonly status_code: number;\n readonly localize: LocalizeErrorFn<PutioLocalizableError>;\n};\n\nexport type APIErrorByErrorTypeLocalizer = {\n readonly kind: \"api_error_type\";\n readonly error_type: string;\n readonly localize: LocalizeErrorFn<PutioLocalizableError>;\n};\n\nexport type MatchConditionLocalizer<E> = {\n readonly kind: \"match_condition\";\n readonly match: (error: E) => boolean;\n readonly localize: LocalizeErrorFn<E>;\n};\n\nexport type GenericErrorLocalizer = {\n readonly kind: \"generic\";\n readonly localize: LocalizeErrorFn<unknown>;\n};\n\nexport type ErrorLocalizer<E> =\n | APIErrorByStatusCodeLocalizer\n | APIErrorByErrorTypeLocalizer\n | MatchConditionLocalizer<E>\n | GenericErrorLocalizer;\n\nexport type ErrorLocalizerFn = (error: unknown) => LocalizedError;\n\nexport const isErrorLocalizer = (fn: unknown): fn is ErrorLocalizerFn => {\n try {\n return typeof fn === \"function\" && fn(new Error(\"test\")) instanceof LocalizedError;\n } catch {\n return false;\n }\n};\n\nexport const createLocalizeError =\n <GlobalError>(globalLocalizers: ReadonlyArray<ErrorLocalizer<GlobalError>>) =>\n <ScopedError extends GlobalError>(\n error: ScopedError,\n scopedLocalizers: ReadonlyArray<ErrorLocalizer<ScopedError>> = [],\n ): LocalizedError => {\n const localizers = [...scopedLocalizers, ...globalLocalizers];\n\n if (isPutioErrorWithBody(error) || isLocalizablePutioErrorEnvelope(error)) {\n const apiError = normalizePutioError(error);\n\n const byErrorType = localizers.find(\n (localizer): localizer is APIErrorByErrorTypeLocalizer =>\n localizer.kind === \"api_error_type\" && localizer.error_type === apiError.body.error_type,\n );\n\n if (byErrorType) {\n return new LocalizedError({\n underlyingError: apiError,\n ...byErrorType.localize(apiError),\n });\n }\n\n const byMatchCondition = localizers.find(\n (localizer): localizer is MatchConditionLocalizer<ScopedError> =>\n localizer.kind === \"match_condition\" && localizer.match(error),\n );\n\n if (byMatchCondition) {\n return new LocalizedError({\n underlyingError: error,\n ...byMatchCondition.localize(error),\n });\n }\n\n const byStatusCode = localizers.find(\n (localizer): localizer is APIErrorByStatusCodeLocalizer =>\n localizer.kind === \"api_status_code\" &&\n localizer.status_code === apiError.body.status_code,\n );\n\n if (byStatusCode) {\n return new LocalizedError({\n underlyingError: apiError,\n ...byStatusCode.localize(apiError),\n });\n }\n }\n\n const byMatchCondition = localizers.find(\n (localizer): localizer is MatchConditionLocalizer<ScopedError> =>\n localizer.kind === \"match_condition\" && localizer.match(error),\n );\n\n if (byMatchCondition) {\n return new LocalizedError({\n underlyingError: error,\n ...byMatchCondition.localize(error),\n });\n }\n\n const genericLocalizer = localizers.find(\n (localizer): localizer is GenericErrorLocalizer => localizer.kind === \"generic\",\n );\n\n if (genericLocalizer) {\n return new LocalizedError({\n underlyingError: error,\n ...genericLocalizer.localize(error),\n });\n }\n\n throw new Error(`No localizer found for error: ${String(error)}`);\n };\n","export const dotsToSpaces = (input: string): string => input.split(\".\").join(\" \");\n\nexport const truncate = (\n input: string,\n options: {\n readonly length: number;\n readonly ellipsis?: string;\n },\n): string => {\n const { ellipsis = \"...\", length } = options;\n\n if (input.length <= length) {\n return input;\n }\n\n return `${input.slice(0, length)}${ellipsis}`;\n};\n\nexport const truncateMiddle = (\n input: string,\n options: {\n readonly frontLength: number;\n readonly backLength: number;\n readonly ellipsis?: string;\n },\n): string => {\n const { backLength, ellipsis = \"...\", frontLength } = options;\n const preservedLength = frontLength + backLength;\n\n if (input.length <= preservedLength) {\n return input;\n }\n\n return `${input.slice(0, frontLength)}${ellipsis}${input.slice(input.length - backLength)}`;\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@putdotio/sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "12.0.0",
|
|
4
4
|
"description": "Effect-first TypeScript SDK for the put.io API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"effect",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"verify": "vp check . && vp run lint:package && knip && knip --production --no-gitignore && vp run validate:routes:packed && vp test run --coverage --passWithNoTests"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"effect": "4.0.0
|
|
65
|
+
"effect": "4.0.0"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
@@ -79,5 +79,5 @@
|
|
|
79
79
|
"engines": {
|
|
80
80
|
"node": ">=24.19.0"
|
|
81
81
|
},
|
|
82
|
-
"packageManager": "pnpm@12.
|
|
82
|
+
"packageManager": "pnpm@12.4.2"
|
|
83
83
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"forms-Ch5rwD58.js","names":[],"sources":["../src/core/errors.ts","../src/core/defaults.ts","../src/core/http.ts","../src/core/forms.ts"],"sourcesContent":["import { Data, Effect, Predicate, Schema } from \"effect\";\n\nexport const PutioErrorEnvelopeSchema = Schema.Struct({\n status: Schema.optional(Schema.String),\n error: Schema.optional(Schema.String),\n error_message: Schema.optional(Schema.String),\n error_type: Schema.optional(Schema.String),\n error_uri: Schema.optional(Schema.String),\n error_id: Schema.optional(Schema.NullOr(Schema.String)),\n extra: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),\n status_code: Schema.optional(Schema.Int),\n details: Schema.optional(Schema.Unknown),\n});\n\nexport type PutioErrorEnvelope = Schema.Schema.Type<typeof PutioErrorEnvelopeSchema>;\n\nexport interface PutioKnownErrorContract<\n TType extends string = string,\n TStatus extends number = number,\n TBody extends PutioErrorEnvelope = PutioErrorEnvelope,\n> {\n readonly bodySchema?: Schema.Schema<TBody> | undefined;\n readonly errorType?: TType | undefined;\n readonly statusCode?: TStatus | undefined;\n}\n\ntype ContractErrorType<TContract extends PutioKnownErrorContract> =\n TContract extends PutioKnownErrorContract<infer TType, number> ? TType : never;\n\ntype ContractStatusCode<TContract extends PutioKnownErrorContract> =\n TContract extends PutioKnownErrorContract<string, infer TStatus> ? TStatus : never;\n\ntype ContractBody<TContract extends PutioKnownErrorContract> = TContract extends {\n readonly bodySchema: Schema.Schema<infer TBody extends PutioErrorEnvelope>;\n}\n ? TBody\n : PutioErrorEnvelope;\n\nexport type PutioTypedErrorEnvelope<TContract extends PutioKnownErrorContract> =\n TContract extends PutioKnownErrorContract\n ? Omit<ContractBody<TContract>, \"error_message\" | \"error_type\" | \"status_code\"> & {\n readonly error_message?: string;\n readonly error_type?: [ContractErrorType<TContract>] extends [never]\n ? string\n : ContractErrorType<TContract>;\n readonly status_code?: [ContractStatusCode<TContract>] extends [never]\n ? number\n : ContractStatusCode<TContract>;\n }\n : never;\n\nexport interface PutioOperationErrorSpec<\n TDomain extends string = string,\n TOperation extends string = string,\n TContracts extends ReadonlyArray<PutioKnownErrorContract> =\n ReadonlyArray<PutioKnownErrorContract>,\n> {\n readonly domain: TDomain;\n readonly operation: TOperation;\n readonly knownErrors: TContracts;\n}\n\ntype OperationContractOf<TSpec extends PutioOperationErrorSpec> = Extract<\n TSpec[\"knownErrors\"][number],\n PutioKnownOperationErrorContract\n>;\n\ntype OperationContractOfParts<\n TDomain extends string,\n TOperation extends string,\n TContracts extends ReadonlyArray<PutioKnownErrorContract>,\n> = OperationContractOf<PutioOperationErrorSpec<TDomain, TOperation, TContracts>>;\n\ntype PutioOperationErrorReason<TContract extends PutioKnownErrorContract> = TContract extends {\n readonly errorType: infer TType extends string;\n}\n ? {\n readonly kind: \"error_type\";\n readonly errorType: TType;\n }\n : TContract extends { readonly statusCode: infer TStatus extends number }\n ? {\n readonly kind: \"status_code\";\n readonly statusCode: TStatus;\n }\n : never;\n\nexport type PutioKnownOperationErrorContract =\n | {\n readonly errorType: string;\n readonly statusCode?: number;\n }\n | {\n readonly errorType?: undefined;\n readonly statusCode: number;\n };\n\nconst isKnownContractMatch = <TContract extends PutioKnownErrorContract>(\n contract: TContract,\n error: PutioApiError | PutioAuthError,\n): contract is Extract<TContract, PutioKnownOperationErrorContract> => {\n if (contract.bodySchema !== undefined && !Schema.is(contract.bodySchema)(error.body)) {\n return false;\n }\n\n if (contract.errorType !== undefined) {\n if (error.body.error_type !== contract.errorType) {\n return false;\n }\n\n return contract.statusCode === undefined || error.status === contract.statusCode;\n }\n\n if (contract.statusCode !== undefined) {\n return error.status === contract.statusCode;\n }\n\n return false;\n};\n\nconst isMatchableApiError = (error: PutioSdkError): error is PutioApiError | PutioAuthError =>\n error._tag === \"PutioApiError\" || error._tag === \"PutioAuthError\";\n\nconst hasOptionalString = (record: Readonly<Record<string, unknown>>, key: string): boolean =>\n !(key in record) || record[key] === undefined || typeof record[key] === \"string\";\n\nconst hasOptionalInteger = (record: Readonly<Record<string, unknown>>, key: string): boolean =>\n !(key in record) ||\n record[key] === undefined ||\n (typeof record[key] === \"number\" && Number.isInteger(record[key]));\n\nconst hasOptionalNullableString = (\n record: Readonly<Record<string, unknown>>,\n key: string,\n): boolean =>\n !(key in record) ||\n record[key] === undefined ||\n record[key] === null ||\n typeof record[key] === \"string\";\n\nconst hasOptionalRecord = (record: Readonly<Record<string, unknown>>, key: string): boolean =>\n !(key in record) || record[key] === undefined || Predicate.isObject(record[key]);\n\nconst isKnownOperationErrorContract = (value: unknown): value is PutioKnownOperationErrorContract =>\n Predicate.isObject(value) &&\n (typeof value.errorType === \"string\" || typeof value.statusCode === \"number\") &&\n hasOptionalString(value, \"errorType\") &&\n hasOptionalInteger(value, \"statusCode\");\n\nconst isOperationReason = (\n value: unknown,\n): value is PutioOperationErrorReason<PutioKnownOperationErrorContract> => {\n if (!Predicate.isObject(value)) {\n return false;\n }\n\n if (value.kind === \"error_type\") {\n return typeof value.errorType === \"string\";\n }\n\n if (value.kind === \"status_code\") {\n return typeof value.statusCode === \"number\";\n }\n\n return false;\n};\n\nfunction toOperationReason<TContract extends { readonly errorType: string }>(\n contract: TContract,\n): PutioOperationErrorReason<TContract>;\nfunction toOperationReason<TContract extends { readonly statusCode: number }>(\n contract: TContract,\n): PutioOperationErrorReason<TContract>;\nfunction toOperationReason(\n contract: PutioKnownOperationErrorContract,\n): PutioOperationErrorReason<PutioKnownOperationErrorContract> {\n if (contract.errorType !== undefined) {\n return {\n kind: \"error_type\",\n errorType: contract.errorType,\n };\n }\n\n return {\n kind: \"status_code\",\n statusCode: contract.statusCode,\n };\n}\n\nexport class PutioTransportError extends Data.TaggedError(\"PutioTransportError\")<{\n readonly cause: unknown;\n}> {}\n\nexport class PutioConfigurationError extends Data.TaggedError(\"PutioConfigurationError\")<{\n readonly cause: unknown;\n}> {}\n\nexport class PutioValidationError extends Data.TaggedError(\"PutioValidationError\")<{\n readonly cause: unknown;\n}> {}\n\nconst PutioResponseErrorFields = {\n status: Schema.Int,\n body: PutioErrorEnvelopeSchema,\n cause: Schema.optional(Schema.Defect()),\n};\n\nexport class PutioApiError extends Schema.TaggedError<PutioApiError>()(\"PutioApiError\", {\n ...PutioResponseErrorFields,\n}) {}\n\nexport class PutioAuthError extends Schema.TaggedError<PutioAuthError>()(\"PutioAuthError\", {\n ...PutioResponseErrorFields,\n}) {}\n\nexport class PutioRateLimitError extends Schema.TaggedError<PutioRateLimitError>()(\n \"PutioRateLimitError\",\n {\n ...PutioResponseErrorFields,\n action: Schema.optional(Schema.String),\n id: Schema.optional(Schema.String),\n limit: Schema.optional(Schema.String),\n remaining: Schema.optional(Schema.String),\n retryAfter: Schema.optional(Schema.String),\n reset: Schema.optional(Schema.String),\n },\n) {}\n\nexport class PutioOperationError<\n TDomain extends string,\n TOperation extends string,\n TContract extends PutioKnownOperationErrorContract,\n> extends Data.TaggedError(\"PutioOperationError\")<{\n readonly body: PutioTypedErrorEnvelope<TContract>;\n readonly contract: TContract;\n readonly domain: TDomain;\n readonly operation: TOperation;\n readonly reason: PutioOperationErrorReason<TContract>;\n readonly status: number;\n}> {}\n\nexport type PutioSdkError =\n | PutioConfigurationError\n | PutioTransportError\n | PutioValidationError\n | PutioApiError\n | PutioAuthError\n | PutioRateLimitError;\n\nexport type PutioUnhandledSdkError =\n | PutioConfigurationError\n | PutioTransportError\n | PutioValidationError\n | PutioApiError\n | PutioAuthError\n | PutioRateLimitError;\n\nexport type PutioOperationErrorFromSpec<TSpec extends PutioOperationErrorSpec> =\n PutioOperationError<TSpec[\"domain\"], TSpec[\"operation\"], OperationContractOf<TSpec>>;\n\nexport type PutioOperationFailure<TSpec extends PutioOperationErrorSpec> =\n | PutioUnhandledSdkError\n | PutioOperationErrorFromSpec<TSpec>;\n\nexport type AnyPutioOperationError = PutioOperationError<\n string,\n string,\n PutioKnownOperationErrorContract\n>;\n\nexport const definePutioOperationErrorSpec = <\n const TDomain extends string,\n const TOperation extends string,\n const TContracts extends ReadonlyArray<PutioKnownErrorContract>,\n>(\n spec: PutioOperationErrorSpec<TDomain, TOperation, TContracts>,\n) => spec;\n\nexport const decodePutioErrorEnvelope = Schema.decodeUnknownEffect(PutioErrorEnvelopeSchema);\n\nexport const isPutioErrorEnvelope = (value: unknown): value is PutioErrorEnvelope =>\n Predicate.isObject(value) &&\n hasOptionalString(value, \"status\") &&\n hasOptionalString(value, \"error\") &&\n hasOptionalString(value, \"error_message\") &&\n hasOptionalString(value, \"error_type\") &&\n hasOptionalString(value, \"error_uri\") &&\n hasOptionalNullableString(value, \"error_id\") &&\n hasOptionalRecord(value, \"extra\") &&\n hasOptionalInteger(value, \"status_code\");\n\nconst hasStatusAndBody = <TTag extends string>(\n value: unknown,\n tag: TTag,\n): value is {\n readonly _tag: TTag;\n readonly body: PutioErrorEnvelope;\n readonly status: number;\n} =>\n Predicate.isObject(value) &&\n value._tag === tag &&\n typeof value.status === \"number\" &&\n isPutioErrorEnvelope(value.body);\n\nexport const isPutioApiError = (value: unknown): value is PutioApiError =>\n hasStatusAndBody(value, \"PutioApiError\");\n\nexport const isPutioAuthError = (value: unknown): value is PutioAuthError =>\n hasStatusAndBody(value, \"PutioAuthError\");\n\nexport const isPutioRateLimitError = (value: unknown): value is PutioRateLimitError =>\n hasStatusAndBody(value, \"PutioRateLimitError\") &&\n Predicate.isObject(value) &&\n hasOptionalString(value, \"action\") &&\n hasOptionalString(value, \"id\") &&\n hasOptionalString(value, \"limit\") &&\n hasOptionalString(value, \"remaining\") &&\n hasOptionalString(value, \"retryAfter\") &&\n hasOptionalString(value, \"reset\");\n\nexport const isPutioOperationError = (value: unknown): value is AnyPutioOperationError =>\n Predicate.isObject(value) &&\n value._tag === \"PutioOperationError\" &&\n isPutioErrorEnvelope(value.body) &&\n isKnownOperationErrorContract(value.contract) &&\n typeof value.domain === \"string\" &&\n typeof value.operation === \"string\" &&\n isOperationReason(value.reason) &&\n typeof value.status === \"number\";\n\nexport const fallbackPutioErrorEnvelope = (status: number): PutioErrorEnvelope => ({\n error_message: `put.io API request failed with status ${status}`,\n status_code: status,\n});\n\nconst getHeader = (headers: Headers, name: string): string | undefined =>\n headers.get(name) ?? undefined;\n\nexport const responseRateLimitHeaders = (headers: Headers) => ({\n action: getHeader(headers, \"x-ratelimit-action\"),\n id: getHeader(headers, \"x-ratelimit-id\"),\n limit: getHeader(headers, \"x-ratelimit-limit\"),\n remaining: getHeader(headers, \"x-ratelimit-remaining\"),\n retryAfter: getHeader(headers, \"retry-after\"),\n reset: getHeader(headers, \"x-ratelimit-reset\"),\n});\n\nexport const mapDecodeErrorToValidationError = (cause: unknown) =>\n new PutioValidationError({ cause });\n\nexport const mapConfigurationError = (cause: unknown) => new PutioConfigurationError({ cause });\n\nexport const mapTransportError = (cause: unknown) => new PutioTransportError({ cause });\n\nexport const makeResponseError = (\n status: number,\n headers: Headers,\n body: PutioErrorEnvelope,\n cause?: unknown,\n): PutioSdkError => {\n if (status === 429) {\n return new PutioRateLimitError({\n status,\n body,\n ...responseRateLimitHeaders(headers),\n cause,\n });\n }\n\n if (status === 401 || status === 403) {\n return new PutioAuthError({ status, body, cause });\n }\n\n return new PutioApiError({ status, body, cause });\n};\n\nexport const parseErrorBody = (status: number, json: unknown) =>\n decodePutioErrorEnvelope(json).pipe(\n Effect.orElseSucceed(() => fallbackPutioErrorEnvelope(status)),\n );\n\nexport function withOperationErrors<\n A,\n E extends PutioSdkError,\n R,\n const TDomain extends string,\n const TOperation extends string,\n const TContracts extends ReadonlyArray<PutioKnownErrorContract>,\n>(\n effect: Effect.Effect<A, E, R>,\n spec: PutioOperationErrorSpec<TDomain, TOperation, TContracts>,\n): Effect.Effect<\n A,\n | E\n | PutioOperationError<\n TDomain,\n TOperation,\n OperationContractOfParts<TDomain, TOperation, TContracts>\n >,\n R\n>;\nexport function withOperationErrors<\n const TDomain extends string,\n const TOperation extends string,\n const TContracts extends ReadonlyArray<PutioKnownErrorContract>,\n>(\n spec: PutioOperationErrorSpec<TDomain, TOperation, TContracts>,\n): <A, E extends PutioSdkError, R>(\n effect: Effect.Effect<A, E, R>,\n) => Effect.Effect<\n A,\n | E\n | PutioOperationError<\n TDomain,\n TOperation,\n OperationContractOfParts<TDomain, TOperation, TContracts>\n >,\n R\n>;\nexport function withOperationErrors<\n A,\n E extends PutioSdkError,\n R,\n const TDomain extends string,\n const TOperation extends string,\n const TContracts extends ReadonlyArray<PutioKnownErrorContract>,\n>(\n effectOrSpec: Effect.Effect<A, E, R> | PutioOperationErrorSpec<TDomain, TOperation, TContracts>,\n maybeSpec?: PutioOperationErrorSpec<TDomain, TOperation, TContracts>,\n):\n | Effect.Effect<\n A,\n | E\n | PutioOperationError<\n TDomain,\n TOperation,\n OperationContractOfParts<TDomain, TOperation, TContracts>\n >,\n R\n >\n | (<A2, E2 extends PutioSdkError, R2>(\n effect: Effect.Effect<A2, E2, R2>,\n ) => Effect.Effect<\n A2,\n | E2\n | PutioOperationError<\n TDomain,\n TOperation,\n OperationContractOfParts<TDomain, TOperation, TContracts>\n >,\n R2\n >) {\n if (maybeSpec === undefined) {\n const spec = effectOrSpec as PutioOperationErrorSpec<TDomain, TOperation, TContracts>;\n\n return <A2, E2 extends PutioSdkError, R2>(effect: Effect.Effect<A2, E2, R2>) =>\n withOperationErrors(effect, spec);\n }\n\n const effect = effectOrSpec as Effect.Effect<A, E, R>;\n const spec = maybeSpec;\n\n return effect.pipe(\n Effect.mapError((error) => {\n if (!isMatchableApiError(error)) {\n return error;\n }\n\n const contract = spec.knownErrors.find((candidate) => isKnownContractMatch(candidate, error));\n\n if (!contract) {\n return error;\n }\n\n return new PutioOperationError({\n body: error.body as PutioTypedErrorEnvelope<\n OperationContractOfParts<TDomain, TOperation, TContracts>\n >,\n contract,\n domain: spec.domain,\n operation: spec.operation,\n reason: toOperationReason(contract),\n status: error.status,\n });\n }),\n );\n}\n","export const DEFAULT_PUTIO_API_BASE_URL = \"https://api.put.io\";\nexport const DEFAULT_PUTIO_UPLOAD_BASE_URL = \"https://upload.put.io\";\nexport const DEFAULT_PUTIO_WEB_APP_URL = \"https://app.put.io\";\n","import { Context, Effect, Layer, Schema } from \"effect\";\n\nimport {\n DEFAULT_PUTIO_API_BASE_URL,\n DEFAULT_PUTIO_UPLOAD_BASE_URL,\n DEFAULT_PUTIO_WEB_APP_URL,\n} from \"./defaults.js\";\nimport {\n fallbackPutioErrorEnvelope,\n makeResponseError,\n mapConfigurationError,\n mapDecodeErrorToValidationError,\n mapTransportError,\n parseErrorBody,\n PutioConfigurationError,\n type PutioSdkError,\n} from \"./errors.js\";\n\nexport type PutioQueryValue = string | number | boolean | null | undefined;\n\nexport type PutioQuery = Readonly<Record<string, PutioQueryValue>>;\n\nexport type PutioPathSegment = string | number | boolean;\n\nexport interface PutioSdkConfigShape {\n readonly accessToken?: string;\n readonly baseUrl?: string | URL;\n readonly uploadBaseUrl?: string | URL;\n readonly webAppUrl?: string | URL;\n}\n\nexport class PutioSdkConfig extends Context.Service<PutioSdkConfig, PutioSdkConfigShape>()(\n \"PutioSdkConfig\",\n) {}\n\nexport interface PutioHttpRequest {\n readonly body?: BodyInit;\n readonly headers: Headers;\n readonly method: PutioRequestMethod;\n readonly url: string;\n}\n\nexport interface PutioHttpResponse {\n readonly arrayBuffer: Effect.Effect<ArrayBuffer, PutioSdkError>;\n readonly headers: Headers;\n readonly json: Effect.Effect<unknown, PutioSdkError>;\n readonly status: number;\n}\n\nexport interface PutioHttpClientShape {\n readonly execute: (request: PutioHttpRequest) => Effect.Effect<PutioHttpResponse, PutioSdkError>;\n}\n\nexport class PutioHttpClient extends Context.Service<PutioHttpClient, PutioHttpClientShape>()(\n \"PutioHttpClient\",\n) {}\n\nexport type PutioSdkContext = PutioSdkConfig | PutioHttpClient;\n\nexport const makePutioSdkConfig = (config: PutioSdkConfigShape): PutioSdkConfigShape => ({\n accessToken: config.accessToken,\n baseUrl: config.baseUrl ?? DEFAULT_PUTIO_API_BASE_URL,\n uploadBaseUrl: config.uploadBaseUrl ?? DEFAULT_PUTIO_UPLOAD_BASE_URL,\n webAppUrl: config.webAppUrl ?? DEFAULT_PUTIO_WEB_APP_URL,\n});\n\nexport const makePutioSdkLayer = (config: PutioSdkConfigShape) =>\n Layer.succeed(PutioSdkConfig, makePutioSdkConfig(config));\n\nexport const makePutioSdkLiveLayer = (config: PutioSdkConfigShape) =>\n Layer.mergeAll(makePutioSdkLayer(config), makePutioFetchLayer());\n\nexport const makePutioFetchLayer = (\n fetchImplementation: typeof globalThis.fetch = globalThis.fetch,\n) => Layer.succeed(PutioHttpClient, makePutioFetchClient(fetchImplementation));\n\nexport const makePutioFetchClient = (\n fetchImplementation: typeof globalThis.fetch = globalThis.fetch,\n): PutioHttpClientShape => ({\n execute: (request) =>\n Effect.suspend(() => {\n const controller = new AbortController();\n const read = <A>(body: () => Promise<A>) =>\n Effect.tryPromise({ try: body, catch: mapTransportError }).pipe(\n Effect.onInterrupt(() => Effect.sync(() => controller.abort())),\n );\n\n return read(async () => {\n const response = await fetchImplementation(request.url, {\n body: request.body,\n headers: request.headers,\n method: request.method,\n signal: controller.signal,\n });\n\n return {\n arrayBuffer: read(() => response.arrayBuffer()),\n headers: response.headers,\n json: isSuccessStatus(response.status)\n ? read(() => response.json())\n : read(() => response.text()).pipe(\n Effect.flatMap((text) =>\n Schema.decodeUnknownEffect(Schema.UnknownFromJsonString)(text).pipe(\n Effect.mapError(() =>\n makeResponseError(\n response.status,\n response.headers,\n fallbackPutioErrorEnvelope(response.status),\n // Native JSON errors can quote response contents in their message.\n new SyntaxError(\"The HTTP error response body is not valid JSON\"),\n ),\n ),\n ),\n ),\n ),\n status: response.status,\n };\n });\n }),\n});\n\nexport const encodePathSegment = (value: PutioPathSegment): string =>\n encodeURIComponent(String(value));\n\nconst absoluteUrlPattern = /^(?:[a-z][a-z\\d+.-]*:|\\/\\/)/i;\n\nconst normalizeApiPath = (path: string): string => {\n if (absoluteUrlPattern.test(path)) {\n throw mapConfigurationError(\"SDK API request paths must be relative to the configured baseUrl\");\n }\n\n return path.startsWith(\"/\") ? path : `/${path}`;\n};\n\nexport const buildPutioUrl = (baseUrl: string | URL, path: string, query?: PutioQuery): string => {\n const url = new URL(normalizeApiPath(path), baseUrl);\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n\n return url.toString();\n};\n\nconst mapUrlBuildError = (cause: unknown) =>\n cause instanceof PutioConfigurationError ? cause : mapConfigurationError(cause);\n\nexport const OkResponseSchema = Schema.Struct({\n status: Schema.Literal(\"OK\"),\n});\n\nexport type PutioAuth =\n | { readonly type: \"config-token\" }\n | { readonly type: \"token\"; readonly token: string }\n | { readonly type: \"basic\"; readonly username: string; readonly password: string }\n | { readonly type: \"none\" };\n\ntype PutioRequestMethod = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\ntype PutioRequestBody =\n | { readonly type: \"none\" }\n | { readonly type: \"json\"; readonly value: unknown }\n | { readonly type: \"form\"; readonly value: Record<string, unknown> }\n | { readonly type: \"form-data\"; readonly value: FormData };\n\ninterface PutioRequestOptions {\n readonly method: PutioRequestMethod;\n readonly path: string;\n readonly baseUrl?: string | URL;\n readonly query?: PutioQuery;\n readonly headers?: HeadersInit;\n readonly auth?: PutioAuth;\n readonly body?: PutioRequestBody;\n}\n\nconst isSuccessStatus = (status: number) => status >= 200 && status < 300;\n\nconst decodeSuccessJson = <S extends Schema.Top>(schema: S, response: PutioHttpResponse) =>\n response.json.pipe(\n Effect.flatMap((json) =>\n Schema.decodeUnknownEffect(schema)(json).pipe(\n Effect.mapError(mapDecodeErrorToValidationError),\n ),\n ),\n );\n\nconst decodeFailure = (response: PutioHttpResponse, headers: Headers) =>\n response.json.pipe(\n Effect.flatMap((json) => parseErrorBody(response.status, json)),\n Effect.map((body) => makeResponseError(response.status, headers, body)),\n );\n\nconst toBasicAuthorization = (username: string, password: string) => {\n if (typeof globalThis.btoa === \"function\") {\n return `Basic ${globalThis.btoa(`${username}:${password}`)}`;\n }\n\n throw mapConfigurationError(\n \"This runtime does not provide btoa(), so Basic auth cannot be encoded safely\",\n );\n};\n\nconst resolveAuthorization = (\n config: PutioSdkConfigShape,\n auth: PutioAuth | undefined,\n): Effect.Effect<string | undefined, PutioSdkError> => {\n const resolvedAuth = auth ?? { type: \"config-token\" as const };\n\n switch (resolvedAuth.type) {\n case \"none\":\n return Effect.succeed(undefined);\n case \"token\":\n return Effect.succeed(`Token ${resolvedAuth.token}`);\n case \"basic\":\n return Effect.try({\n try: () => toBasicAuthorization(resolvedAuth.username, resolvedAuth.password),\n catch: mapConfigurationError,\n });\n case \"config-token\":\n if (!config.accessToken) {\n return Effect.fail(\n mapConfigurationError(\n \"This endpoint requires an access token, but PutioSdkConfig.accessToken is missing\",\n ),\n );\n }\n\n return Effect.succeed(`Token ${config.accessToken}`);\n }\n};\n\nconst normalizeFormValue = (value: unknown): string => {\n if (value === null || value === undefined) {\n return \"\";\n }\n\n if (typeof value === \"string\") {\n return value;\n }\n\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n\n return JSON.stringify(value);\n};\n\nconst withBody = (body: PutioRequestBody): Effect.Effect<BodyInit | undefined, PutioSdkError> => {\n switch (body.type) {\n case \"none\":\n return Effect.succeed(undefined);\n case \"json\":\n return Effect.try({\n try: () => JSON.stringify(body.value),\n catch: mapDecodeErrorToValidationError,\n });\n case \"form\":\n return Effect.succeed(\n new URLSearchParams(\n Object.entries(body.value).flatMap(([key, value]) =>\n value === undefined ? [] : [[key, normalizeFormValue(value)]],\n ),\n ),\n );\n case \"form-data\":\n return Effect.succeed(body.value);\n }\n};\n\nconst makeRequest = (url: string, options: PutioRequestOptions, authorization?: string) =>\n Effect.gen(function* () {\n const headers = new Headers(options.headers);\n\n const body = options.body ?? { type: \"none\" as const };\n if (!headers.has(\"accept\")) {\n headers.set(\"accept\", \"application/json\");\n }\n\n if (authorization) {\n headers.set(\"authorization\", authorization);\n }\n\n if (body.type === \"json\" && !headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/json\");\n }\n\n return {\n body: yield* withBody(body),\n headers,\n method: options.method,\n url,\n };\n });\n\nconst executeRequest = (options: PutioRequestOptions) =>\n Effect.gen(function* () {\n const config = yield* PutioSdkConfig;\n const httpClient = yield* PutioHttpClient;\n const authorization = yield* resolveAuthorization(config, options.auth);\n const url = yield* Effect.try({\n try: () =>\n buildPutioUrl(\n options.baseUrl ?? config.baseUrl ?? DEFAULT_PUTIO_API_BASE_URL,\n options.path,\n options.query,\n ),\n catch: mapUrlBuildError,\n });\n const request = yield* makeRequest(url, options, authorization);\n\n return yield* httpClient.execute(request);\n });\n\nexport const requestJson = <S extends Schema.Top>(\n schema: S,\n options: PutioRequestOptions,\n): Effect.Effect<S[\"Type\"], PutioSdkError, PutioSdkContext | S[\"DecodingServices\"]> =>\n executeRequest(options).pipe(\n Effect.flatMap((response) =>\n isSuccessStatus(response.status)\n ? decodeSuccessJson(schema, response)\n : decodeFailure(response, response.headers).pipe(Effect.flatMap(Effect.fail)),\n ),\n );\n\nexport const selectJsonField =\n <K extends string>(field: K) =>\n <A extends { readonly [P in K]: unknown }, E, R>(\n effect: Effect.Effect<A, E, R>,\n ): Effect.Effect<A[K], E, R> =>\n effect.pipe(Effect.map((value) => value[field]));\n\nexport const selectJsonFields =\n <const K extends readonly string[]>(...fields: K) =>\n <A extends { readonly [P in K[number]]: unknown }, E, R>(\n effect: Effect.Effect<A, E, R>,\n ): Effect.Effect<Pick<A, K[number]>, E, R> =>\n effect.pipe(\n Effect.map(\n (value) =>\n Object.fromEntries(fields.map((field) => [field, value[field as K[number]]])) as Pick<\n A,\n K[number]\n >,\n ),\n );\n\nexport const requestArrayBuffer = (\n options: PutioRequestOptions,\n): Effect.Effect<Uint8Array, PutioSdkError, PutioSdkContext> =>\n executeRequest(options).pipe(\n Effect.flatMap((response) =>\n isSuccessStatus(response.status)\n ? response.arrayBuffer.pipe(Effect.map((buffer) => new Uint8Array(buffer)))\n : decodeFailure(response, response.headers).pipe(Effect.flatMap(Effect.fail)),\n ),\n );\n\nexport const requestText = (\n options: PutioRequestOptions,\n): Effect.Effect<string, PutioSdkError, PutioSdkContext> =>\n requestArrayBuffer(options).pipe(Effect.map((bytes) => new TextDecoder().decode(bytes)));\n\nexport const requestVoid = (\n options: PutioRequestOptions,\n): Effect.Effect<void, PutioSdkError, PutioSdkContext> =>\n executeRequest(options).pipe(\n Effect.flatMap((response) =>\n isSuccessStatus(response.status)\n ? Effect.void\n : decodeFailure(response, response.headers).pipe(Effect.flatMap(Effect.fail)),\n ),\n );\n","type CsvValue = string | number;\n\nexport interface CursorSelectionInput<TValue extends CsvValue = number> {\n readonly cursor?: string;\n readonly excludeIds?: ReadonlyArray<TValue>;\n readonly ids?: ReadonlyArray<TValue>;\n}\n\nexport const joinCsv = <TValue extends CsvValue>(\n values: ReadonlyArray<TValue> | undefined,\n): string | undefined => (values && values.length > 0 ? values.join(\",\") : undefined);\n\nexport const toCursorSelectionForm = <\n TValue extends CsvValue,\n const TIdsFieldName extends string = \"file_ids\",\n>(\n selection: CursorSelectionInput<TValue>,\n idsFieldName?: TIdsFieldName,\n): {\n readonly cursor?: string;\n readonly exclude_ids?: string;\n} & Record<TIdsFieldName, string | undefined> => {\n const resolvedIdsFieldName = (idsFieldName ?? \"file_ids\") as TIdsFieldName;\n\n return {\n cursor: selection.cursor,\n exclude_ids: joinCsv(selection.excludeIds),\n [resolvedIdsFieldName]: joinCsv(selection.ids),\n } as {\n readonly cursor?: string;\n readonly exclude_ids?: string;\n } & Record<TIdsFieldName, string | undefined>;\n};\n"],"mappings":"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"}
|