@devultur/core 1.0.3 → 1.2.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 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
- constructor(message: string, status: number, code?: string, limit?: number);
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
- constructor(message, status, code, limit) {
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 isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
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 rejectUnknownKeys(raw, allowed, path, errors) {
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 (!isObject(value)) {
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 (!isObject(value)) {
1604
+ if (!isObject2(value)) {
1412
1605
  errors.push(`${path}: must be an object`);
1413
1606
  return void 0;
1414
1607
  }
1415
- rejectUnknownKeys(value, ["rungs", "bitrate"], path, errors);
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 (!isObject(value)) {
1615
+ if (!isObject2(value)) {
1423
1616
  errors.push(`${path}: must be an object`);
1424
1617
  return void 0;
1425
1618
  }
1426
- rejectUnknownKeys(value, ["rungs"], path, errors);
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 (!isObject(value)) {
1635
+ if (!isObject2(value)) {
1443
1636
  errors.push(`${path}: must be an object`);
1444
1637
  return void 0;
1445
1638
  }
1446
- rejectUnknownKeys(value, ["at", "formats"], path, errors);
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 (!isObject(value)) {
1647
+ if (!isObject2(value)) {
1455
1648
  errors.push(`${path}: must be an object`);
1456
1649
  return void 0;
1457
1650
  }
1458
- rejectUnknownKeys(value, ["name", "crop", "mp4"], path, errors);
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) => isObject(variant) ? variant.name : void 0);
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 (!isObject(input)) return { ok: false, errors: ["spec: must be an object"] };
1684
+ if (!isObject2(input)) return { ok: false, errors: ["spec: must be an object"] };
1492
1685
  const errors = [];
1493
- rejectUnknownKeys(input, SPEC_FIELDS, "spec", errors);
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devultur/core",
3
- "version": "1.0.3",
3
+ "version": "1.2.0",
4
4
  "description": "Shared config, types, storage path conventions, media references and URL signing for the Devultur media SDKs",
5
5
  "keywords": [
6
6
  "media",