@devultur/core 1.0.3 → 1.1.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/dist/index.d.ts +197 -5
- package/dist/index.js +225 -20
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,65 @@ declare function timingSafeEqual(received: string, expected: string): boolean;
|
|
|
30
30
|
*/
|
|
31
31
|
declare function isBearerOf(authHeader: string | null | undefined, secret: string | null | undefined): boolean;
|
|
32
32
|
|
|
33
|
+
/** `compliance`: nobody shortens or removes it. `governance`: a privileged call can. */
|
|
34
|
+
type RetentionMode = "governance" | "compliance";
|
|
35
|
+
/** `inherit`: derived files (transcode outputs, captions) are retained too. `original-only`: only the upload. */
|
|
36
|
+
type RetentionDerivatives = "inherit" | "original-only";
|
|
37
|
+
interface Retention {
|
|
38
|
+
mode: RetentionMode;
|
|
39
|
+
/** Fixed expiry (ISO 8601 with a time zone). Mutually exclusive with `period`. */
|
|
40
|
+
until?: string;
|
|
41
|
+
/** Seconds from upload, or from hold release when a hold was set. Mutually exclusive with `until`. */
|
|
42
|
+
period?: number;
|
|
43
|
+
/** Default `inherit`. */
|
|
44
|
+
derivatives?: RetentionDerivatives;
|
|
45
|
+
}
|
|
46
|
+
interface ObjectLock {
|
|
47
|
+
retention?: Retention;
|
|
48
|
+
/** Retained indefinitely while true, whatever `retention` says. */
|
|
49
|
+
hold?: boolean;
|
|
50
|
+
}
|
|
51
|
+
type ResolvedRetention = Retention & {
|
|
52
|
+
derivatives: RetentionDerivatives;
|
|
53
|
+
};
|
|
54
|
+
interface ResolvedLock {
|
|
55
|
+
retention?: ResolvedRetention;
|
|
56
|
+
hold: boolean;
|
|
57
|
+
}
|
|
58
|
+
type LockValidation = {
|
|
59
|
+
ok: true;
|
|
60
|
+
lock: ObjectLock;
|
|
61
|
+
} | {
|
|
62
|
+
ok: false;
|
|
63
|
+
errors: string[];
|
|
64
|
+
};
|
|
65
|
+
type LockResolution = {
|
|
66
|
+
ok: true;
|
|
67
|
+
lock: ResolvedLock;
|
|
68
|
+
} | {
|
|
69
|
+
ok: false;
|
|
70
|
+
errors: string[];
|
|
71
|
+
};
|
|
72
|
+
declare const RETENTION_MODES: readonly RetentionMode[];
|
|
73
|
+
declare const RETENTION_DERIVATIVES: readonly RetentionDerivatives[];
|
|
74
|
+
declare const DEFAULT_RETENTION_DERIVATIVES: RetentionDerivatives;
|
|
75
|
+
/** Longest `period`: 100 years, in seconds. Beyond it an expiry can't be computed as a date. */
|
|
76
|
+
declare const MAX_RETENTION_PERIOD_SECONDS: number;
|
|
77
|
+
/** True for an ISO 8601 instant with a time zone (`2030-01-01T00:00:00Z`) that names a real date. */
|
|
78
|
+
declare function isIsoInstant(value: unknown): value is string;
|
|
79
|
+
/**
|
|
80
|
+
* Validates an untrusted lock (request body, upload token, config). A past `until` is valid: it is
|
|
81
|
+
* simply not locked any more, so an app default with a fixed date never breaks once it passes.
|
|
82
|
+
*/
|
|
83
|
+
declare function validateLock(input: unknown, path?: string): LockValidation;
|
|
84
|
+
/**
|
|
85
|
+
* Resolves the effective lock: resource override → app default → off. `hold` resolves on its own.
|
|
86
|
+
* `retention` resolves as a whole (its mode and expiry come from the same level, so `until` and
|
|
87
|
+
* `period` never mix), except `derivatives`, which falls back to the app default and then `inherit`.
|
|
88
|
+
* A resource can't drop an app default retention; it can only replace it.
|
|
89
|
+
*/
|
|
90
|
+
declare function resolveLock(resource?: ObjectLock, appDefault?: ObjectLock): LockResolution;
|
|
91
|
+
|
|
33
92
|
interface MediaUrlOptions {
|
|
34
93
|
/** Viewer token for authorized media. Added as `?token=`. */
|
|
35
94
|
token?: string;
|
|
@@ -100,6 +159,11 @@ interface DevulturMedia<V extends Visibility = Visibility> {
|
|
|
100
159
|
readonly visibility: V;
|
|
101
160
|
/** Original upload name (display / download). */
|
|
102
161
|
readonly filename?: string;
|
|
162
|
+
/**
|
|
163
|
+
* Key of the uploaded object (`{visibility}/{kind}/{id}/…`; a video's raw file), as the upload returned it. Optional
|
|
164
|
+
* and informational: delivery paths are still derived from the other fields (`mediaPath`), never from it.
|
|
165
|
+
*/
|
|
166
|
+
readonly key?: string;
|
|
103
167
|
/** Intrinsic width (px) — enables `<img>` with no CLS. */
|
|
104
168
|
readonly width?: number;
|
|
105
169
|
/** Intrinsic height (px). */
|
|
@@ -144,7 +208,12 @@ declare class DevulturApiError extends Error {
|
|
|
144
208
|
readonly status: number;
|
|
145
209
|
readonly code?: string;
|
|
146
210
|
readonly limit?: number;
|
|
147
|
-
|
|
211
|
+
/**
|
|
212
|
+
* The response body's fields other than `error` and `code`, e.g. `{ key, mode, until, hold }`
|
|
213
|
+
* on a `409 retention_active`. `undefined` when the body had none (or wasn't JSON).
|
|
214
|
+
*/
|
|
215
|
+
readonly details?: Readonly<Record<string, unknown>>;
|
|
216
|
+
constructor(message: string, status: number, code?: string, limit?: number, details?: Record<string, unknown>);
|
|
148
217
|
}
|
|
149
218
|
|
|
150
219
|
declare const TranscodePreset: {
|
|
@@ -277,6 +346,15 @@ interface AssetEntry {
|
|
|
277
346
|
uploaded: string;
|
|
278
347
|
cacheControl: string | null;
|
|
279
348
|
visibility: Visibility;
|
|
349
|
+
/**
|
|
350
|
+
* Past its `expiresAt` but still retained by a lock: viewers get `410`, the worker's credential can still read it,
|
|
351
|
+
* and it isn't deleted until its lock ends. Absent on a live entry (expired free entries aren't listed).
|
|
352
|
+
*/
|
|
353
|
+
expired?: true;
|
|
354
|
+
/** The effective expiry of the retention keeping an expired entry, when it has a date (ISO 8601). */
|
|
355
|
+
retainedUntil?: string;
|
|
356
|
+
/** An expired entry kept by a hold. */
|
|
357
|
+
hold?: true;
|
|
280
358
|
}
|
|
281
359
|
interface ListAssetsOptions {
|
|
282
360
|
/** Prefix within `asset/` (`site/`). Empty = the whole namespace the key can reach. */
|
|
@@ -312,6 +390,8 @@ interface CreateUploadTokenOptions {
|
|
|
312
390
|
fileExpiresIn?: number;
|
|
313
391
|
/** Visibility of the token's files (default `private`). */
|
|
314
392
|
visibility?: Visibility;
|
|
393
|
+
/** Minimum lock of every file uploaded with the token: an upload can make it stricter, never looser. */
|
|
394
|
+
lock?: ObjectLock;
|
|
315
395
|
}
|
|
316
396
|
interface UploadToken {
|
|
317
397
|
token: string;
|
|
@@ -340,6 +420,10 @@ interface UploadAssetOptions {
|
|
|
340
420
|
* config's per-prefix rule, or `public, max-age=60, stale-while-revalidate=86400`.
|
|
341
421
|
*/
|
|
342
422
|
cacheControl?: string;
|
|
423
|
+
/** Lock of the file (retention and/or hold), written with its bytes. */
|
|
424
|
+
lock?: ObjectLock;
|
|
425
|
+
/** Shorthand for `lock.hold`. */
|
|
426
|
+
hold?: boolean;
|
|
343
427
|
/** Parts in flight at once in a multipart upload (default 4). */
|
|
344
428
|
concurrency?: number;
|
|
345
429
|
onProgress?: (percent: number) => void;
|
|
@@ -395,10 +479,70 @@ interface FilesClient {
|
|
|
395
479
|
delete(key: string): Promise<void>;
|
|
396
480
|
}
|
|
397
481
|
|
|
482
|
+
/** The lock in force on an object. */
|
|
483
|
+
interface LockView {
|
|
484
|
+
key: string;
|
|
485
|
+
mode: RetentionMode | null;
|
|
486
|
+
/** Effective expiry (ISO): a fixed `until`, or a `period` from upload (or from the hold's release). */
|
|
487
|
+
until: string | null;
|
|
488
|
+
period: number | null;
|
|
489
|
+
hold: boolean;
|
|
490
|
+
derivatives: RetentionDerivatives | null;
|
|
491
|
+
/** `upload`: set when the file was uploaded. `sidecar`: changed since. `none`: not locked. */
|
|
492
|
+
source: "sidecar" | "upload" | "none";
|
|
493
|
+
/** Number of changes since upload (`null` while the upload lock is in force). */
|
|
494
|
+
revision: number | null;
|
|
495
|
+
}
|
|
496
|
+
/** A retention change. Fields merge over the current retention; `until` and `period` replace each other. */
|
|
497
|
+
interface SetLockOptions {
|
|
498
|
+
mode?: RetentionMode;
|
|
499
|
+
until?: string;
|
|
500
|
+
period?: number;
|
|
501
|
+
derivatives?: RetentionDerivatives;
|
|
502
|
+
/** Drop the retention (governance only, with `bypassGovernance`). */
|
|
503
|
+
remove?: true;
|
|
504
|
+
/** Shorten or remove a governance retention: the worker's `authorizeLockChange` hook must approve it. */
|
|
505
|
+
bypassGovernance?: boolean;
|
|
506
|
+
}
|
|
507
|
+
/** What a hold request names: a list of keys (at most {@link HOLD_PAGE_SIZE} per call), or every key under a prefix. */
|
|
508
|
+
type HoldTarget = string[] | {
|
|
509
|
+
prefix: string;
|
|
510
|
+
};
|
|
511
|
+
interface HoldSkip {
|
|
512
|
+
key: string;
|
|
513
|
+
/**
|
|
514
|
+
* `not_found`, `unchanged` (already in that state), `lock_conflict` (changed concurrently: retry it) or
|
|
515
|
+
* `lock_record_unreadable` (a release of a lock record the worker can't read: an operator must repair it).
|
|
516
|
+
*/
|
|
517
|
+
reason: "not_found" | "unchanged" | "lock_conflict" | "lock_record_unreadable";
|
|
518
|
+
}
|
|
519
|
+
interface HoldPage {
|
|
520
|
+
updated: string[];
|
|
521
|
+
skipped: HoldSkip[];
|
|
522
|
+
/** More objects under the prefix: pass it back to get the next page. */
|
|
523
|
+
cursor?: string;
|
|
524
|
+
}
|
|
525
|
+
/** Objects the worker changes per hold request (keys per call, and objects per prefix page). */
|
|
526
|
+
declare const HOLD_PAGE_SIZE = 50;
|
|
527
|
+
interface LockClient {
|
|
528
|
+
get(key: string): Promise<LockView>;
|
|
529
|
+
set(key: string, options: SetLockOptions): Promise<LockView>;
|
|
530
|
+
/** One page: up to {@link HOLD_PAGE_SIZE} keys, or one page of a prefix (`cursor` for the next). */
|
|
531
|
+
hold(target: HoldTarget, options?: {
|
|
532
|
+
cursor?: string;
|
|
533
|
+
}): Promise<HoldPage>;
|
|
534
|
+
release(target: HoldTarget, options?: {
|
|
535
|
+
cursor?: string;
|
|
536
|
+
}): Promise<HoldPage>;
|
|
537
|
+
/** Every page: a long key list in chunks, or a prefix following its cursor. */
|
|
538
|
+
holdAll(target: HoldTarget): AsyncGenerator<HoldPage>;
|
|
539
|
+
releaseAll(target: HoldTarget): AsyncGenerator<HoldPage>;
|
|
540
|
+
}
|
|
541
|
+
type JsonRequest$1 = <T>(path: string, init?: RequestInit) => Promise<T>;
|
|
542
|
+
declare function createLockClient(request: JsonRequest$1): LockClient;
|
|
543
|
+
|
|
398
544
|
interface WorkerClient {
|
|
399
|
-
uploadFile(file: File, options?:
|
|
400
|
-
metadata?: Record<string, string>;
|
|
401
|
-
}): Promise<UploadFileResult>;
|
|
545
|
+
uploadFile(file: File, options?: UploadFileOptions): Promise<UploadFileResult>;
|
|
402
546
|
createUploadUrl(options: CreateUploadUrlOptions): Promise<WorkerUploadResult>;
|
|
403
547
|
getMediaUrl(media: DevulturMedia, options?: MediaUrlOptions): string;
|
|
404
548
|
extractKey(url: string): string | null;
|
|
@@ -417,6 +561,11 @@ interface WorkerClient {
|
|
|
417
561
|
files: FilesClient;
|
|
418
562
|
/** Paginated listing of the `asset/` namespace. */
|
|
419
563
|
assets: AssetsClient;
|
|
564
|
+
/** Object lock: retention (read, set, extend) and holds. */
|
|
565
|
+
lock: LockClient;
|
|
566
|
+
}
|
|
567
|
+
interface UploadFileOptions extends Pick<CreateUploadUrlOptions, "lock" | "hold"> {
|
|
568
|
+
metadata?: Record<string, string>;
|
|
420
569
|
}
|
|
421
570
|
interface UploadFileResult {
|
|
422
571
|
key: string;
|
|
@@ -568,6 +717,10 @@ interface CreateUploadUrlOptions {
|
|
|
568
717
|
/** Intrinsic dimensions (captured client-side for images) → carried into the reference. */
|
|
569
718
|
width?: number;
|
|
570
719
|
height?: number;
|
|
720
|
+
/** Lock of the new file (retention and/or hold), written with its bytes. Needs a media worker. */
|
|
721
|
+
lock?: ObjectLock;
|
|
722
|
+
/** Shorthand for `lock.hold`: retain the file indefinitely from the moment it exists. */
|
|
723
|
+
hold?: boolean;
|
|
571
724
|
}
|
|
572
725
|
interface UploadUrlResult {
|
|
573
726
|
url: string;
|
|
@@ -888,6 +1041,45 @@ declare function sha256Hex(data: BufferSource): Promise<string>;
|
|
|
888
1041
|
/** `Repr-Digest` value (RFC 9530) for a hex sha256: `sha-256=:<base64>:`. */
|
|
889
1042
|
declare function reprDigest(sha256: string): string;
|
|
890
1043
|
|
|
1044
|
+
/** A point in time: a `Date`, an ISO 8601 string or epoch milliseconds. */
|
|
1045
|
+
type LockInstant = Date | string | number;
|
|
1046
|
+
interface LockTiming {
|
|
1047
|
+
/** When the object was uploaded: a `period` without a hold counts from here. */
|
|
1048
|
+
uploadedAt?: LockInstant;
|
|
1049
|
+
/** When the hold was released: a `period` counts from here instead when a hold was used. */
|
|
1050
|
+
holdReleasedAt?: LockInstant;
|
|
1051
|
+
}
|
|
1052
|
+
type LockChangeReason = "compliance_extend_only" | "lock_privilege_required";
|
|
1053
|
+
/** `bypass`: the change shortened or removed a governance retention, so it used the privilege (audit it). */
|
|
1054
|
+
type LockChange = {
|
|
1055
|
+
ok: true;
|
|
1056
|
+
bypass: boolean;
|
|
1057
|
+
} | {
|
|
1058
|
+
ok: false;
|
|
1059
|
+
reason: LockChangeReason;
|
|
1060
|
+
};
|
|
1061
|
+
interface LockChangeOptions {
|
|
1062
|
+
/** The caller passed the app's `authorizeLockChange` check. */
|
|
1063
|
+
privileged: boolean;
|
|
1064
|
+
now: LockInstant;
|
|
1065
|
+
/** Needed when either side has a `period` without a hold. */
|
|
1066
|
+
timing?: LockTiming;
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* The retention expiry as an ISO string. `undefined` when there is no retention, or when a period
|
|
1070
|
+
* is still waiting for its hold to be released (the object stays locked by the hold meanwhile).
|
|
1071
|
+
*/
|
|
1072
|
+
declare function effectiveUntil(lock: ObjectLock, timing?: LockTiming): string | undefined;
|
|
1073
|
+
/** True while a hold is set or the retention hasn't expired. Delete and overwrite are rejected meanwhile. */
|
|
1074
|
+
declare function isLocked(lock: ObjectLock, now: LockInstant, timing?: LockTiming): boolean;
|
|
1075
|
+
/**
|
|
1076
|
+
* Whether a lock may go from `before` to `after` (both already validated). Holds can always be set
|
|
1077
|
+
* and released, and releasing one starts its period now. While a retention is active: `compliance`
|
|
1078
|
+
* only extends (no earlier expiry, no switch to governance, no `original-only`); `governance` is
|
|
1079
|
+
* shortened, weakened or removed only with `privileged`. An expired retention can be replaced freely.
|
|
1080
|
+
*/
|
|
1081
|
+
declare function canChange(before: ObjectLock, after: ObjectLock, options: LockChangeOptions): LockChange;
|
|
1082
|
+
|
|
891
1083
|
declare const VIDEO_MIMES: string[];
|
|
892
1084
|
declare const IMAGE_MIMES: string[];
|
|
893
1085
|
declare const FILE_MIMES: string[];
|
|
@@ -1158,4 +1350,4 @@ declare function createHTTPWebhookHandler(options: {
|
|
|
1158
1350
|
}>;
|
|
1159
1351
|
};
|
|
1160
1352
|
|
|
1161
|
-
export { AES_BLOCK_BYTES, ASSET_NAMESPACE, type AssetEntry, type AssetListing, type AssetUploadResult, type AssetsClient, BYTES_PER_UNIT, ByteUnit, CACHE_CONTROL_PRESETS, CacheControlPreset, type CacheRule, type CapabilityUploadResult, CaptionLocale, CaptionLocaleLabel, CaptionStatus, type CaptionsJobResult, type CaptionsStatusResult, type CreateUploadTokenOptions, type CreateUploadUrlOptions, DEFAULT_ASSET_CACHE_CONTROL, DEFAULT_PRESIGN_EXPIRY_SECONDS, DEFAULT_SECURITY, DEFAULT_TOKEN_EXPIRY_SECONDS, DEFAULT_VISIBILITY, type DeleteVideoResult, type DeliveryConfig, DevulturApiError, type DevulturConfig, type DevulturKind, type DevulturMedia, type DirectStorageConfig, type DownloadLink, type DownloadLinkOptions, type DownloadUrlOptions, ENCRYPTION_ALG, ENCRYPTION_VERSION, EXPIRES_AT_HEADER, type EncryptionMeta, FILE_MIMES, type FailureReason, type FetchApi, type FileBuilderConfig, type FileHead, type FilesClient, GENERATED_PRIVATE_CACHE_CONTROL, GENERATED_PUBLIC_CACHE_CONTROL, IMAGE_MIMES, type ImageBuilderConfig, ImageFit, ImageFormat, type IssueTokenOptions, type JsonRequest, LEGACY_PRESET_SPECS, type ListAssetsOptions, MAIN_VARIANT, MAX_ASSET_PATH_DEPTH, MAX_ASSET_PATH_LENGTH, MAX_BITRATE_KBPS, MAX_CACHE_CONTROL_LENGTH, MAX_CACHE_RULES, MAX_VARIANTS, MIN_BITRATE_KBPS, MULTIPART_PART_SIZE_BYTES, MULTIPART_QUERY, MULTIPART_THRESHOLD_BYTES, type MediaEvent, MediaEventName, type MediaEventNameValue, type MediaEventType, type MediaRouter, type MediaRouterConfig, MediaStatus, type MediaStatusValue, MediaType, type MediaUrlOptions, type Mp4Output, MultipartAction, type MultipartPart, type MultipartSession, type MultipartUploadOptions, POSTER_FORMATS, type PartUploader, type PosterAt, type PosterFormat, type PosterUrls, type PresetResolution, type PresetSource, type ProcessingPhase, REPR_DIGEST_HEADER, SESSION_HEADER, SHA256_HEADER, SHA256_VERIFIED_HEADER, type ScopeCoverage, SecurityLevel, type SecurityPolicy, type SignOptions, type SpecValidation, type SpeechConnectorConfig, SpeechProvider, type StorageClient, type StorageConfig, type StorageConnectorConfig, StorageProvider, type StoredRendition, TRANSCODE_CROPS, TRANSCODE_RUNGS, TRANSCODE_TEMPLATES, type TokenResult, TokenScope, type TranscodeConnectorConfig, type TranscodeCrop, type TranscodeJobResult, type TranscodeOptions, type TranscodeOutputKeys, type TranscodeOutputUrls, type TranscodeOutputs, TranscodePreset, TranscodeProvider, type TranscodeRendition, type TranscodeRung, type TranscodeSettings, type TranscodeSpec, TranscodeStatus, type TranscodeStatusResult, TranscodeTemplate, type TranscodeVariant, type TranslationConnectorConfig, TranslationProvider, type UploadAssetOptions, type UploadFileResult, UploadRequestKind, type UploadToken, type UploadUrlResult, type UploadsClient, VIDEO_MIMES, type VideoBuilderConfig, type VideoModuleConfig, type VideoOutputRef, type Visibility, type WebhookVerifyOptions, type WorkerClient, type WorkerConfig, type WorkerTransport, type WorkerUploadResult, assetKey, assetRelativePath, buildMediaUrl, buildPosterUrls, buildStoryboardUrl, buildTranscodeOutputUrls, buildVideoMp4Url, bytesToHex, captionPath, classifyUploadRequest, computeFingerprint, contentDispositionAttachment, contentDispositionInline, counterForBlock, coversScope, createDecryptStream, createHTTPWebhookHandler, createMediaRouter, createStorageClient, createWorkerClient, createWorkerTransport, decodeEncryptionMeta, decryptChunk, edgeTtlSeconds, encodeEncryptionMeta, encryptBytes, exportEncryptionKey, extractId, extractKeyFromUrl, file, filePath, formatSize, generateEncryptionKey, generateId, generateIv, getDownloadUrl, handleWebhook, image, imagePath, importEncryptionKey, isAssetKey, isBearerOf, isPrefixWithin, isValidMime, isValidOutputName, isVideoRawKey, isWithinPathPrefix, isWorkerUploadUrl, matchCacheRule, mediaCaptionPath, mediaPath, mediaThumbnailPath, mediaUrl, missingScopes, multipartAbortUrl, multipartCompleteUrl, multipartCreateUrl, multipartPartCount, multipartPartRange, multipartPartUrl, normalizeAssetPath, normalizeAssetPrefix, normalizeCacheRules, normalizePathPrefix, normalizeSha256, parseSize, publicUrl, relativeMediaPath, reprDigest, resolveCacheControl, resolveSecurity, resolveTranscodePreset, resolveVisibility, sanitizeFilename, sha256Hex, shouldUseMultipart, signPayload, signUrl, timingSafeEqual, toPrivateCacheControl, uploadMultipart, uploadToCapabilityUrl, validateTranscodeSpec, verifySignature, verifySignedUrl, video, videoBasePath, videoHlsPath, videoHlsPlaylistPath, videoMp4Dir, videoMp4Path, videoPosterPath, videoRawPath, videoStoryboardDir, videoStoryboardPath, videoThumbnailPath, visibilityFromKey };
|
|
1353
|
+
export { AES_BLOCK_BYTES, ASSET_NAMESPACE, type AssetEntry, type AssetListing, type AssetUploadResult, type AssetsClient, BYTES_PER_UNIT, ByteUnit, CACHE_CONTROL_PRESETS, CacheControlPreset, type CacheRule, type CapabilityUploadResult, CaptionLocale, CaptionLocaleLabel, CaptionStatus, type CaptionsJobResult, type CaptionsStatusResult, type CreateUploadTokenOptions, type CreateUploadUrlOptions, DEFAULT_ASSET_CACHE_CONTROL, DEFAULT_PRESIGN_EXPIRY_SECONDS, DEFAULT_RETENTION_DERIVATIVES, DEFAULT_SECURITY, DEFAULT_TOKEN_EXPIRY_SECONDS, DEFAULT_VISIBILITY, type DeleteVideoResult, type DeliveryConfig, DevulturApiError, type DevulturConfig, type DevulturKind, type DevulturMedia, type DirectStorageConfig, type DownloadLink, type DownloadLinkOptions, type DownloadUrlOptions, ENCRYPTION_ALG, ENCRYPTION_VERSION, EXPIRES_AT_HEADER, type EncryptionMeta, FILE_MIMES, type FailureReason, type FetchApi, type FileBuilderConfig, type FileHead, type FilesClient, GENERATED_PRIVATE_CACHE_CONTROL, GENERATED_PUBLIC_CACHE_CONTROL, HOLD_PAGE_SIZE, type HoldPage, type HoldSkip, type HoldTarget, IMAGE_MIMES, type ImageBuilderConfig, ImageFit, ImageFormat, type IssueTokenOptions, type JsonRequest, LEGACY_PRESET_SPECS, type ListAssetsOptions, type LockChange, type LockChangeOptions, type LockChangeReason, type LockClient, type LockInstant, type LockResolution, type LockTiming, type LockValidation, type LockView, MAIN_VARIANT, MAX_ASSET_PATH_DEPTH, MAX_ASSET_PATH_LENGTH, MAX_BITRATE_KBPS, MAX_CACHE_CONTROL_LENGTH, MAX_CACHE_RULES, MAX_RETENTION_PERIOD_SECONDS, MAX_VARIANTS, MIN_BITRATE_KBPS, MULTIPART_PART_SIZE_BYTES, MULTIPART_QUERY, MULTIPART_THRESHOLD_BYTES, type MediaEvent, MediaEventName, type MediaEventNameValue, type MediaEventType, type MediaRouter, type MediaRouterConfig, MediaStatus, type MediaStatusValue, MediaType, type MediaUrlOptions, type Mp4Output, MultipartAction, type MultipartPart, type MultipartSession, type MultipartUploadOptions, type ObjectLock, POSTER_FORMATS, type PartUploader, type PosterAt, type PosterFormat, type PosterUrls, type PresetResolution, type PresetSource, type ProcessingPhase, REPR_DIGEST_HEADER, RETENTION_DERIVATIVES, RETENTION_MODES, type ResolvedLock, type ResolvedRetention, type Retention, type RetentionDerivatives, type RetentionMode, SESSION_HEADER, SHA256_HEADER, SHA256_VERIFIED_HEADER, type ScopeCoverage, SecurityLevel, type SecurityPolicy, type SetLockOptions, type SignOptions, type SpecValidation, type SpeechConnectorConfig, SpeechProvider, type StorageClient, type StorageConfig, type StorageConnectorConfig, StorageProvider, type StoredRendition, TRANSCODE_CROPS, TRANSCODE_RUNGS, TRANSCODE_TEMPLATES, type TokenResult, TokenScope, type TranscodeConnectorConfig, type TranscodeCrop, type TranscodeJobResult, type TranscodeOptions, type TranscodeOutputKeys, type TranscodeOutputUrls, type TranscodeOutputs, TranscodePreset, TranscodeProvider, type TranscodeRendition, type TranscodeRung, type TranscodeSettings, type TranscodeSpec, TranscodeStatus, type TranscodeStatusResult, TranscodeTemplate, type TranscodeVariant, type TranslationConnectorConfig, TranslationProvider, type UploadAssetOptions, type UploadFileOptions, type UploadFileResult, UploadRequestKind, type UploadToken, type UploadUrlResult, type UploadsClient, VIDEO_MIMES, type VideoBuilderConfig, type VideoModuleConfig, type VideoOutputRef, type Visibility, type WebhookVerifyOptions, type WorkerClient, type WorkerConfig, type WorkerTransport, type WorkerUploadResult, assetKey, assetRelativePath, buildMediaUrl, buildPosterUrls, buildStoryboardUrl, buildTranscodeOutputUrls, buildVideoMp4Url, bytesToHex, canChange, captionPath, classifyUploadRequest, computeFingerprint, contentDispositionAttachment, contentDispositionInline, counterForBlock, coversScope, createDecryptStream, createHTTPWebhookHandler, createLockClient, createMediaRouter, createStorageClient, createWorkerClient, createWorkerTransport, decodeEncryptionMeta, decryptChunk, edgeTtlSeconds, effectiveUntil, encodeEncryptionMeta, encryptBytes, exportEncryptionKey, extractId, extractKeyFromUrl, file, filePath, formatSize, generateEncryptionKey, generateId, generateIv, getDownloadUrl, handleWebhook, image, imagePath, importEncryptionKey, isAssetKey, isBearerOf, isIsoInstant, isLocked, isPrefixWithin, isValidMime, isValidOutputName, isVideoRawKey, isWithinPathPrefix, isWorkerUploadUrl, matchCacheRule, mediaCaptionPath, mediaPath, mediaThumbnailPath, mediaUrl, missingScopes, multipartAbortUrl, multipartCompleteUrl, multipartCreateUrl, multipartPartCount, multipartPartRange, multipartPartUrl, normalizeAssetPath, normalizeAssetPrefix, normalizeCacheRules, normalizePathPrefix, normalizeSha256, parseSize, publicUrl, relativeMediaPath, reprDigest, resolveCacheControl, resolveLock, resolveSecurity, resolveTranscodePreset, resolveVisibility, sanitizeFilename, sha256Hex, shouldUseMultipart, signPayload, signUrl, timingSafeEqual, toPrivateCacheControl, uploadMultipart, uploadToCapabilityUrl, validateLock, validateTranscodeSpec, verifySignature, verifySignedUrl, video, videoBasePath, videoHlsPath, videoHlsPlaylistPath, videoMp4Dir, videoMp4Path, videoPosterPath, videoRawPath, videoStoryboardDir, videoStoryboardPath, videoThumbnailPath, visibilityFromKey };
|
package/dist/index.js
CHANGED
|
@@ -445,6 +445,138 @@ function reprDigest(sha256) {
|
|
|
445
445
|
return `sha-256=:${btoa(binary)}:`;
|
|
446
446
|
}
|
|
447
447
|
|
|
448
|
+
// src/lock.ts
|
|
449
|
+
var RETENTION_MODES = ["governance", "compliance"];
|
|
450
|
+
var RETENTION_DERIVATIVES = ["inherit", "original-only"];
|
|
451
|
+
var DEFAULT_RETENTION_DERIVATIVES = "inherit";
|
|
452
|
+
var MAX_RETENTION_PERIOD_SECONDS = 100 * 366 * 24 * 60 * 60;
|
|
453
|
+
var LOCK_FIELDS = /* @__PURE__ */ new Set(["retention", "hold"]);
|
|
454
|
+
var RETENTION_FIELDS = /* @__PURE__ */ new Set(["mode", "until", "period", "derivatives"]);
|
|
455
|
+
var ISO_8601_INSTANT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?(Z|[+-]\d{2}:\d{2})$/;
|
|
456
|
+
var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
457
|
+
function isIsoInstant(value) {
|
|
458
|
+
return typeof value === "string" && ISO_8601_INSTANT.test(value) && !Number.isNaN(Date.parse(value));
|
|
459
|
+
}
|
|
460
|
+
function rejectUnknownKeys(input, allowed, path, errors) {
|
|
461
|
+
for (const key of Object.keys(input)) {
|
|
462
|
+
if (!allowed.has(key)) errors.push(`${path}.${key}: unknown field`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
function validateRetention(input, path, errors) {
|
|
466
|
+
if (!isObject(input)) {
|
|
467
|
+
errors.push(`${path}: must be an object`);
|
|
468
|
+
return void 0;
|
|
469
|
+
}
|
|
470
|
+
const errorCount = errors.length;
|
|
471
|
+
rejectUnknownKeys(input, RETENTION_FIELDS, path, errors);
|
|
472
|
+
const { mode, until, period, derivatives } = input;
|
|
473
|
+
if (!RETENTION_MODES.includes(mode)) {
|
|
474
|
+
errors.push(`${path}.mode: must be "governance" or "compliance"`);
|
|
475
|
+
}
|
|
476
|
+
if (until !== void 0 && !isIsoInstant(until)) {
|
|
477
|
+
errors.push(`${path}.until: must be an ISO 8601 date-time with a zone`);
|
|
478
|
+
}
|
|
479
|
+
const isPeriodValid = Number.isSafeInteger(period) && period > 0 && period <= MAX_RETENTION_PERIOD_SECONDS;
|
|
480
|
+
if (period !== void 0 && !isPeriodValid) {
|
|
481
|
+
errors.push(`${path}.period: must be a positive integer number of seconds, at most 100 years`);
|
|
482
|
+
}
|
|
483
|
+
if (until !== void 0 && period !== void 0) errors.push(`${path}: set either until or period, not both`);
|
|
484
|
+
if (until === void 0 && period === void 0) errors.push(`${path}: until or period is required`);
|
|
485
|
+
if (derivatives !== void 0 && !RETENTION_DERIVATIVES.includes(derivatives)) {
|
|
486
|
+
errors.push(`${path}.derivatives: must be "inherit" or "original-only"`);
|
|
487
|
+
}
|
|
488
|
+
if (errors.length > errorCount) return void 0;
|
|
489
|
+
const retention = { mode };
|
|
490
|
+
if (until !== void 0) retention.until = until;
|
|
491
|
+
if (period !== void 0) retention.period = period;
|
|
492
|
+
if (derivatives !== void 0) retention.derivatives = derivatives;
|
|
493
|
+
return retention;
|
|
494
|
+
}
|
|
495
|
+
function validateLock(input, path = "lock") {
|
|
496
|
+
if (!isObject(input)) return { ok: false, errors: [`${path}: must be an object`] };
|
|
497
|
+
const errors = [];
|
|
498
|
+
rejectUnknownKeys(input, LOCK_FIELDS, path, errors);
|
|
499
|
+
const lock = {};
|
|
500
|
+
if (input.retention !== void 0) lock.retention = validateRetention(input.retention, `${path}.retention`, errors);
|
|
501
|
+
if (input.hold !== void 0 && typeof input.hold !== "boolean") errors.push(`${path}.hold: must be a boolean`);
|
|
502
|
+
if (typeof input.hold === "boolean") lock.hold = input.hold;
|
|
503
|
+
if (errors.length > 0) return { ok: false, errors };
|
|
504
|
+
return { ok: true, lock };
|
|
505
|
+
}
|
|
506
|
+
function resolveLock(resource, appDefault) {
|
|
507
|
+
const errors = [];
|
|
508
|
+
const resourceLock = resource === void 0 ? {} : collect(validateLock(resource, "lock"), errors);
|
|
509
|
+
const defaultLock = appDefault === void 0 ? {} : collect(validateLock(appDefault, "default"), errors);
|
|
510
|
+
if (errors.length > 0) return { ok: false, errors };
|
|
511
|
+
const lock = { hold: resourceLock.hold ?? defaultLock.hold ?? false };
|
|
512
|
+
const retention = resourceLock.retention ?? defaultLock.retention;
|
|
513
|
+
if (!retention) return { ok: true, lock };
|
|
514
|
+
const derivatives = resourceLock.retention?.derivatives ?? defaultLock.retention?.derivatives ?? DEFAULT_RETENTION_DERIVATIVES;
|
|
515
|
+
lock.retention = { ...retention, derivatives };
|
|
516
|
+
return { ok: true, lock };
|
|
517
|
+
}
|
|
518
|
+
function collect(validation, errors) {
|
|
519
|
+
if (validation.ok) return validation.lock;
|
|
520
|
+
errors.push(...validation.errors);
|
|
521
|
+
return {};
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// src/lock-change.ts
|
|
525
|
+
var toMs = (instant) => instant instanceof Date ? instant.getTime() : new Date(instant).getTime();
|
|
526
|
+
function retentionEnd(lock, timing) {
|
|
527
|
+
const { retention } = lock;
|
|
528
|
+
if (!retention) return { kind: "none" };
|
|
529
|
+
if (retention.until !== void 0) return { kind: "at", ms: toMs(retention.until) };
|
|
530
|
+
const period = retention.period ?? 0;
|
|
531
|
+
if (lock.hold) return { kind: "pending", period };
|
|
532
|
+
const start = timing?.holdReleasedAt ?? timing?.uploadedAt;
|
|
533
|
+
if (start === void 0) throw new RangeError("lock: a retention period needs uploadedAt or holdReleasedAt");
|
|
534
|
+
return { kind: "at", ms: toMs(start) + period * 1e3 };
|
|
535
|
+
}
|
|
536
|
+
function effectiveUntil(lock, timing) {
|
|
537
|
+
const end = retentionEnd(lock, timing);
|
|
538
|
+
return end.kind === "at" ? new Date(end.ms).toISOString() : void 0;
|
|
539
|
+
}
|
|
540
|
+
function isLocked(lock, now, timing) {
|
|
541
|
+
if (lock.hold) return true;
|
|
542
|
+
const end = retentionEnd(lock, timing);
|
|
543
|
+
return end.kind === "at" && toMs(now) < end.ms;
|
|
544
|
+
}
|
|
545
|
+
function afterTiming(before, after, options) {
|
|
546
|
+
if (!before.hold || after.hold) return options.timing;
|
|
547
|
+
return { ...options.timing, holdReleasedAt: options.now };
|
|
548
|
+
}
|
|
549
|
+
function earliestEndMs(end, nowMs) {
|
|
550
|
+
if (end.kind === "none") return Number.NEGATIVE_INFINITY;
|
|
551
|
+
return end.kind === "at" ? end.ms : nowMs + end.period * 1e3;
|
|
552
|
+
}
|
|
553
|
+
function isShortened(beforeEnd, afterEnd, nowMs, isStillHeld) {
|
|
554
|
+
if (afterEnd.kind === "none") return true;
|
|
555
|
+
if (beforeEnd.kind === "pending" && afterEnd.kind === "at" && isStillHeld) return true;
|
|
556
|
+
return earliestEndMs(afterEnd, nowMs) < earliestEndMs(beforeEnd, nowMs);
|
|
557
|
+
}
|
|
558
|
+
function isWeakerSetting(before, after) {
|
|
559
|
+
if (!after) return true;
|
|
560
|
+
if (before.mode === "compliance" && after.mode === "governance") return true;
|
|
561
|
+
return (before.derivatives ?? "inherit") === "inherit" && after.derivatives === "original-only";
|
|
562
|
+
}
|
|
563
|
+
function isRetentionActive(end, nowMs) {
|
|
564
|
+
if (end.kind === "pending") return true;
|
|
565
|
+
return end.kind === "at" && nowMs < end.ms;
|
|
566
|
+
}
|
|
567
|
+
function canChange(before, after, options) {
|
|
568
|
+
const nowMs = toMs(options.now);
|
|
569
|
+
const beforeEnd = retentionEnd(before, options.timing);
|
|
570
|
+
if (!before.retention || !isRetentionActive(beforeEnd, nowMs)) return { ok: true, bypass: false };
|
|
571
|
+
const afterEnd = retentionEnd(after, afterTiming(before, after, options));
|
|
572
|
+
const isShortenedEnd = isShortened(beforeEnd, afterEnd, nowMs, after.hold === true);
|
|
573
|
+
const isWeakened = isShortenedEnd || isWeakerSetting(before.retention, after.retention);
|
|
574
|
+
if (!isWeakened) return { ok: true, bypass: false };
|
|
575
|
+
if (before.retention.mode === "compliance") return { ok: false, reason: "compliance_extend_only" };
|
|
576
|
+
if (!options.privileged) return { ok: false, reason: "lock_privilege_required" };
|
|
577
|
+
return { ok: true, bypass: true };
|
|
578
|
+
}
|
|
579
|
+
|
|
448
580
|
// src/paths.ts
|
|
449
581
|
function videoBasePath(id) {
|
|
450
582
|
return `video/${id}`;
|
|
@@ -571,28 +703,40 @@ var DevulturApiError = class extends Error {
|
|
|
571
703
|
status;
|
|
572
704
|
code;
|
|
573
705
|
limit;
|
|
574
|
-
|
|
706
|
+
/**
|
|
707
|
+
* The response body's fields other than `error` and `code`, e.g. `{ key, mode, until, hold }`
|
|
708
|
+
* on a `409 retention_active`. `undefined` when the body had none (or wasn't JSON).
|
|
709
|
+
*/
|
|
710
|
+
details;
|
|
711
|
+
constructor(message, status, code, limit, details) {
|
|
575
712
|
super(message);
|
|
576
713
|
this.name = "DevulturApiError";
|
|
577
714
|
this.status = status;
|
|
578
715
|
this.code = code;
|
|
579
716
|
this.limit = limit;
|
|
717
|
+
this.details = details;
|
|
580
718
|
}
|
|
581
719
|
};
|
|
720
|
+
function detailsFrom(body) {
|
|
721
|
+
const { error: _error, code: _code, ...rest } = body;
|
|
722
|
+
return Object.keys(rest).length > 0 ? rest : void 0;
|
|
723
|
+
}
|
|
582
724
|
async function errorFromResponse(response) {
|
|
583
725
|
let message = `API error: ${response.status}`;
|
|
584
726
|
let code;
|
|
585
727
|
let limit;
|
|
728
|
+
let details;
|
|
586
729
|
try {
|
|
587
730
|
const body = await response.json();
|
|
588
|
-
if (typeof body === "object" && body !== null) {
|
|
731
|
+
if (typeof body === "object" && body !== null && !Array.isArray(body)) {
|
|
589
732
|
if ("error" in body && typeof body.error === "string") message = body.error;
|
|
590
733
|
if ("code" in body && typeof body.code === "string") code = body.code;
|
|
591
734
|
if ("limit" in body && typeof body.limit === "number") limit = body.limit;
|
|
735
|
+
details = detailsFrom(body);
|
|
592
736
|
}
|
|
593
737
|
} catch {
|
|
594
738
|
}
|
|
595
|
-
return new DevulturApiError(message, response.status, code, limit);
|
|
739
|
+
return new DevulturApiError(message, response.status, code, limit, details);
|
|
596
740
|
}
|
|
597
741
|
|
|
598
742
|
// src/multipart.ts
|
|
@@ -1020,7 +1164,9 @@ function createUploadsClient(base, request) {
|
|
|
1020
1164
|
expiresIn: options.expiresIn,
|
|
1021
1165
|
overwrite: options.overwrite,
|
|
1022
1166
|
visibility: options.visibility,
|
|
1023
|
-
cacheControl: options.cacheControl
|
|
1167
|
+
cacheControl: options.cacheControl,
|
|
1168
|
+
lock: options.lock,
|
|
1169
|
+
hold: options.hold
|
|
1024
1170
|
})
|
|
1025
1171
|
});
|
|
1026
1172
|
const uploadUrl = `${base}${created.uploadUrl}`;
|
|
@@ -1064,6 +1210,46 @@ function createFilesClient(fetchApi, request) {
|
|
|
1064
1210
|
};
|
|
1065
1211
|
}
|
|
1066
1212
|
|
|
1213
|
+
// src/worker-lock.ts
|
|
1214
|
+
var HOLD_PAGE_SIZE = 50;
|
|
1215
|
+
var encodeKey2 = (key) => key.split("/").map(encodeURIComponent).join("/");
|
|
1216
|
+
function chunks(keys) {
|
|
1217
|
+
const pages = [];
|
|
1218
|
+
for (let start = 0; start < keys.length; start += HOLD_PAGE_SIZE)
|
|
1219
|
+
pages.push(keys.slice(start, start + HOLD_PAGE_SIZE));
|
|
1220
|
+
return pages;
|
|
1221
|
+
}
|
|
1222
|
+
function createLockClient(request) {
|
|
1223
|
+
const page = (hold, target, cursor) => request("/v1/lock/hold", {
|
|
1224
|
+
method: "POST",
|
|
1225
|
+
body: JSON.stringify({
|
|
1226
|
+
hold,
|
|
1227
|
+
...Array.isArray(target) ? { keys: target } : { prefix: target.prefix },
|
|
1228
|
+
...cursor ? { cursor } : {}
|
|
1229
|
+
})
|
|
1230
|
+
});
|
|
1231
|
+
async function* every(hold, target) {
|
|
1232
|
+
if (Array.isArray(target)) {
|
|
1233
|
+
for (const keys of chunks(target)) yield page(hold, keys);
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
let cursor;
|
|
1237
|
+
do {
|
|
1238
|
+
const result = await page(hold, target, cursor);
|
|
1239
|
+
yield result;
|
|
1240
|
+
cursor = result.cursor;
|
|
1241
|
+
} while (cursor);
|
|
1242
|
+
}
|
|
1243
|
+
return {
|
|
1244
|
+
get: (key) => request(`/v1/lock/${encodeKey2(key)}`),
|
|
1245
|
+
set: (key, options) => request(`/v1/lock/${encodeKey2(key)}`, { method: "PUT", body: JSON.stringify(options) }),
|
|
1246
|
+
hold: (target, options) => page(true, target, options?.cursor),
|
|
1247
|
+
release: (target, options) => page(false, target, options?.cursor),
|
|
1248
|
+
holdAll: (target) => every(true, target),
|
|
1249
|
+
releaseAll: (target) => every(false, target)
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1067
1253
|
// src/worker-client.ts
|
|
1068
1254
|
function createWorkerTransport(credential, baseUrl) {
|
|
1069
1255
|
async function fetchApi(path, options = {}) {
|
|
@@ -1098,13 +1284,16 @@ function createWorkerClient(credential, baseUrl) {
|
|
|
1098
1284
|
uploads: createUploadsClient(base, request),
|
|
1099
1285
|
files: createFilesClient(fetchApi, request),
|
|
1100
1286
|
assets: createAssetsClient(request),
|
|
1287
|
+
lock: createLockClient(request),
|
|
1101
1288
|
async uploadFile(file2, options) {
|
|
1102
1289
|
const result = await request("/v1/upload", {
|
|
1103
1290
|
method: "POST",
|
|
1104
1291
|
body: JSON.stringify({
|
|
1105
1292
|
filename: file2.name,
|
|
1106
1293
|
contentType: file2.type,
|
|
1107
|
-
metadata: options?.metadata
|
|
1294
|
+
metadata: options?.metadata,
|
|
1295
|
+
lock: options?.lock,
|
|
1296
|
+
hold: options?.hold
|
|
1108
1297
|
})
|
|
1109
1298
|
});
|
|
1110
1299
|
await uploadToCapabilityUrl(`${base}${result.uploadUrl}`, file2);
|
|
@@ -1122,7 +1311,9 @@ function createWorkerClient(credential, baseUrl) {
|
|
|
1122
1311
|
filename: options.filename,
|
|
1123
1312
|
contentType: options.contentType,
|
|
1124
1313
|
metadata: options.metadata,
|
|
1125
|
-
visibility: options.visibility
|
|
1314
|
+
visibility: options.visibility,
|
|
1315
|
+
lock: options.lock,
|
|
1316
|
+
hold: options.hold
|
|
1126
1317
|
})
|
|
1127
1318
|
});
|
|
1128
1319
|
return {
|
|
@@ -1230,6 +1421,8 @@ function createMediaRouter(config) {
|
|
|
1230
1421
|
if (!isDirectStorageConfig(config) || !storage) {
|
|
1231
1422
|
throw new Error("No storage client or worker client available.");
|
|
1232
1423
|
}
|
|
1424
|
+
if (options.lock || options.hold)
|
|
1425
|
+
throw new Error("Object lock needs a media worker; direct storage can't enforce it.");
|
|
1233
1426
|
const mediaType = resolveMediaType(options.contentType, config);
|
|
1234
1427
|
if (!mediaType) {
|
|
1235
1428
|
throw new Error(`Unsupported content type: "${options.contentType}"`);
|
|
@@ -1372,13 +1565,13 @@ var MIN_BITRATE_KBPS = 200;
|
|
|
1372
1565
|
var MAX_BITRATE_KBPS = 4e4;
|
|
1373
1566
|
var MAX_POSTER_SECONDS = 3600;
|
|
1374
1567
|
var NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
1375
|
-
var
|
|
1568
|
+
var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1376
1569
|
var isRung = (value) => TRANSCODE_RUNGS.includes(value);
|
|
1377
1570
|
var isCrop = (value) => TRANSCODE_CROPS.includes(value);
|
|
1378
1571
|
function isValidOutputName(name) {
|
|
1379
1572
|
return typeof name === "string" && NAME_PATTERN.test(name);
|
|
1380
1573
|
}
|
|
1381
|
-
function
|
|
1574
|
+
function rejectUnknownKeys2(raw, allowed, path, errors) {
|
|
1382
1575
|
for (const key of Object.keys(raw)) {
|
|
1383
1576
|
if (!allowed.includes(key)) errors.push(`${path}.${key}: unknown field`);
|
|
1384
1577
|
}
|
|
@@ -1396,7 +1589,7 @@ function readRungs(value, path, errors) {
|
|
|
1396
1589
|
return value;
|
|
1397
1590
|
}
|
|
1398
1591
|
function readBitrate(value, rungs, path, errors) {
|
|
1399
|
-
if (!
|
|
1592
|
+
if (!isObject2(value)) {
|
|
1400
1593
|
errors.push(`${path}: must be an object of rung \u2192 kbps`);
|
|
1401
1594
|
return void 0;
|
|
1402
1595
|
}
|
|
@@ -1408,22 +1601,22 @@ function readBitrate(value, rungs, path, errors) {
|
|
|
1408
1601
|
return value;
|
|
1409
1602
|
}
|
|
1410
1603
|
function readMp4(value, path, errors) {
|
|
1411
|
-
if (!
|
|
1604
|
+
if (!isObject2(value)) {
|
|
1412
1605
|
errors.push(`${path}: must be an object`);
|
|
1413
1606
|
return void 0;
|
|
1414
1607
|
}
|
|
1415
|
-
|
|
1608
|
+
rejectUnknownKeys2(value, ["rungs", "bitrate"], path, errors);
|
|
1416
1609
|
const rungs = readRungs(value.rungs, `${path}.rungs`, errors);
|
|
1417
1610
|
if (!rungs) return void 0;
|
|
1418
1611
|
if (value.bitrate === void 0) return { rungs };
|
|
1419
1612
|
return { rungs, bitrate: readBitrate(value.bitrate, rungs, `${path}.bitrate`, errors) };
|
|
1420
1613
|
}
|
|
1421
1614
|
function readHls(value, path, errors) {
|
|
1422
|
-
if (!
|
|
1615
|
+
if (!isObject2(value)) {
|
|
1423
1616
|
errors.push(`${path}: must be an object`);
|
|
1424
1617
|
return void 0;
|
|
1425
1618
|
}
|
|
1426
|
-
|
|
1619
|
+
rejectUnknownKeys2(value, ["rungs"], path, errors);
|
|
1427
1620
|
if (value.rungs === void 0) return {};
|
|
1428
1621
|
return { rungs: readRungs(value.rungs, `${path}.rungs`, errors) };
|
|
1429
1622
|
}
|
|
@@ -1439,11 +1632,11 @@ function readPosterAt(value, path, errors) {
|
|
|
1439
1632
|
return value;
|
|
1440
1633
|
}
|
|
1441
1634
|
function readPoster(value, path, errors) {
|
|
1442
|
-
if (!
|
|
1635
|
+
if (!isObject2(value)) {
|
|
1443
1636
|
errors.push(`${path}: must be an object`);
|
|
1444
1637
|
return void 0;
|
|
1445
1638
|
}
|
|
1446
|
-
|
|
1639
|
+
rejectUnknownKeys2(value, ["at", "formats"], path, errors);
|
|
1447
1640
|
const at = readPosterAt(value.at, `${path}.at`, errors);
|
|
1448
1641
|
if (value.formats === void 0) return { at };
|
|
1449
1642
|
const isFormatList = Array.isArray(value.formats) && value.formats.length > 0 && value.formats.every((format) => POSTER_FORMATS.includes(format));
|
|
@@ -1451,11 +1644,11 @@ function readPoster(value, path, errors) {
|
|
|
1451
1644
|
return { at, formats: value.formats };
|
|
1452
1645
|
}
|
|
1453
1646
|
function readVariant(value, path, errors) {
|
|
1454
|
-
if (!
|
|
1647
|
+
if (!isObject2(value)) {
|
|
1455
1648
|
errors.push(`${path}: must be an object`);
|
|
1456
1649
|
return void 0;
|
|
1457
1650
|
}
|
|
1458
|
-
|
|
1651
|
+
rejectUnknownKeys2(value, ["name", "crop", "mp4"], path, errors);
|
|
1459
1652
|
if (!isValidOutputName(value.name) || value.name === MAIN_VARIANT) {
|
|
1460
1653
|
errors.push(`${path}.name: must be a lowercase slug (a-z, 0-9, -; max 32) other than "${MAIN_VARIANT}"`);
|
|
1461
1654
|
}
|
|
@@ -1470,7 +1663,7 @@ function readVariants(value, errors) {
|
|
|
1470
1663
|
return void 0;
|
|
1471
1664
|
}
|
|
1472
1665
|
const variants = value.map((variant, index) => readVariant(variant, `variants[${index}]`, errors));
|
|
1473
|
-
const names = value.map((variant) =>
|
|
1666
|
+
const names = value.map((variant) => isObject2(variant) ? variant.name : void 0);
|
|
1474
1667
|
if (new Set(names).size !== names.length) errors.push("variants: names must be unique");
|
|
1475
1668
|
return variants.filter((variant) => variant !== void 0);
|
|
1476
1669
|
}
|
|
@@ -1488,9 +1681,9 @@ function readSwitches(input, spec, errors) {
|
|
|
1488
1681
|
}
|
|
1489
1682
|
}
|
|
1490
1683
|
function validateTranscodeSpec(input) {
|
|
1491
|
-
if (!
|
|
1684
|
+
if (!isObject2(input)) return { ok: false, errors: ["spec: must be an object"] };
|
|
1492
1685
|
const errors = [];
|
|
1493
|
-
|
|
1686
|
+
rejectUnknownKeys2(input, SPEC_FIELDS, "spec", errors);
|
|
1494
1687
|
const spec = {};
|
|
1495
1688
|
if (input.hls !== void 0) spec.hls = readHls(input.hls, "hls", errors);
|
|
1496
1689
|
if (input.mp4 !== void 0) spec.mp4 = readMp4(input.mp4, "mp4", errors);
|
|
@@ -1661,6 +1854,7 @@ export {
|
|
|
1661
1854
|
CaptionStatus,
|
|
1662
1855
|
DEFAULT_ASSET_CACHE_CONTROL,
|
|
1663
1856
|
DEFAULT_PRESIGN_EXPIRY_SECONDS,
|
|
1857
|
+
DEFAULT_RETENTION_DERIVATIVES,
|
|
1664
1858
|
DEFAULT_SECURITY,
|
|
1665
1859
|
DEFAULT_TOKEN_EXPIRY_SECONDS,
|
|
1666
1860
|
DEFAULT_VISIBILITY,
|
|
@@ -1671,6 +1865,7 @@ export {
|
|
|
1671
1865
|
FILE_MIMES,
|
|
1672
1866
|
GENERATED_PRIVATE_CACHE_CONTROL,
|
|
1673
1867
|
GENERATED_PUBLIC_CACHE_CONTROL,
|
|
1868
|
+
HOLD_PAGE_SIZE,
|
|
1674
1869
|
IMAGE_MIMES,
|
|
1675
1870
|
ImageFit,
|
|
1676
1871
|
ImageFormat,
|
|
@@ -1681,6 +1876,7 @@ export {
|
|
|
1681
1876
|
MAX_BITRATE_KBPS,
|
|
1682
1877
|
MAX_CACHE_CONTROL_LENGTH,
|
|
1683
1878
|
MAX_CACHE_RULES,
|
|
1879
|
+
MAX_RETENTION_PERIOD_SECONDS,
|
|
1684
1880
|
MAX_VARIANTS,
|
|
1685
1881
|
MIN_BITRATE_KBPS,
|
|
1686
1882
|
MULTIPART_PART_SIZE_BYTES,
|
|
@@ -1692,6 +1888,8 @@ export {
|
|
|
1692
1888
|
MultipartAction,
|
|
1693
1889
|
POSTER_FORMATS,
|
|
1694
1890
|
REPR_DIGEST_HEADER,
|
|
1891
|
+
RETENTION_DERIVATIVES,
|
|
1892
|
+
RETENTION_MODES,
|
|
1695
1893
|
SESSION_HEADER,
|
|
1696
1894
|
SHA256_HEADER,
|
|
1697
1895
|
SHA256_VERIFIED_HEADER,
|
|
@@ -1717,6 +1915,7 @@ export {
|
|
|
1717
1915
|
buildTranscodeOutputUrls,
|
|
1718
1916
|
buildVideoMp4Url,
|
|
1719
1917
|
bytesToHex,
|
|
1918
|
+
canChange,
|
|
1720
1919
|
captionPath,
|
|
1721
1920
|
classifyUploadRequest,
|
|
1722
1921
|
computeFingerprint,
|
|
@@ -1726,6 +1925,7 @@ export {
|
|
|
1726
1925
|
coversScope,
|
|
1727
1926
|
createDecryptStream,
|
|
1728
1927
|
createHTTPWebhookHandler,
|
|
1928
|
+
createLockClient,
|
|
1729
1929
|
createMediaRouter,
|
|
1730
1930
|
createStorageClient,
|
|
1731
1931
|
createWorkerClient,
|
|
@@ -1733,6 +1933,7 @@ export {
|
|
|
1733
1933
|
decodeEncryptionMeta,
|
|
1734
1934
|
decryptChunk,
|
|
1735
1935
|
edgeTtlSeconds,
|
|
1936
|
+
effectiveUntil,
|
|
1736
1937
|
encodeEncryptionMeta,
|
|
1737
1938
|
encryptBytes,
|
|
1738
1939
|
exportEncryptionKey,
|
|
@@ -1751,6 +1952,8 @@ export {
|
|
|
1751
1952
|
importEncryptionKey,
|
|
1752
1953
|
isAssetKey,
|
|
1753
1954
|
isBearerOf,
|
|
1955
|
+
isIsoInstant,
|
|
1956
|
+
isLocked,
|
|
1754
1957
|
isPrefixWithin,
|
|
1755
1958
|
isValidMime,
|
|
1756
1959
|
isValidOutputName,
|
|
@@ -1779,6 +1982,7 @@ export {
|
|
|
1779
1982
|
relativeMediaPath,
|
|
1780
1983
|
reprDigest,
|
|
1781
1984
|
resolveCacheControl,
|
|
1985
|
+
resolveLock,
|
|
1782
1986
|
resolveSecurity,
|
|
1783
1987
|
resolveTranscodePreset,
|
|
1784
1988
|
resolveVisibility,
|
|
@@ -1791,6 +1995,7 @@ export {
|
|
|
1791
1995
|
toPrivateCacheControl,
|
|
1792
1996
|
uploadMultipart,
|
|
1793
1997
|
uploadToCapabilityUrl,
|
|
1998
|
+
validateLock,
|
|
1794
1999
|
validateTranscodeSpec,
|
|
1795
2000
|
verifySignature,
|
|
1796
2001
|
verifySignedUrl,
|