@transloadit/utils 4.7.1 → 4.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- export declare const COMPILE_ASSEMBLY_INSTRUCTIONS_DEFAULT_MODEL = "openai/gpt-5.6-sol";
1
+ export declare const COMPILE_ASSEMBLY_INSTRUCTIONS_DEFAULT_MODEL = "openai/gpt-6-astra";
2
2
  export declare const COMPILE_ASSEMBLY_INSTRUCTIONS_DEFAULT_REASONING_EFFORT = "medium";
3
3
  export declare const COMPILE_ASSEMBLY_INSTRUCTIONS_DEFAULT_MAX_ATTEMPTS = 3;
4
4
  export declare const COMPILE_ASSEMBLY_INSTRUCTIONS_MCP_ALLOWED_TOOLS: readonly ["transloadit_get_robot_help", "transloadit_lint_assembly_instructions", "transloadit_list_robots"];
@@ -1,4 +1,4 @@
1
- export const COMPILE_ASSEMBLY_INSTRUCTIONS_DEFAULT_MODEL = 'openai/gpt-5.6-sol';
1
+ export const COMPILE_ASSEMBLY_INSTRUCTIONS_DEFAULT_MODEL = 'openai/gpt-6-astra';
2
2
  export const COMPILE_ASSEMBLY_INSTRUCTIONS_DEFAULT_REASONING_EFFORT = 'medium';
3
3
  export const COMPILE_ASSEMBLY_INSTRUCTIONS_DEFAULT_MAX_ATTEMPTS = 3;
4
4
  export const COMPILE_ASSEMBLY_INSTRUCTIONS_MCP_ALLOWED_TOOLS = [
package/dist/index.d.ts CHANGED
@@ -17,4 +17,6 @@ export declare const verifyWebhookSignature: (options: VerifyWebhookSignatureOpt
17
17
  * Produces the same URL as the synchronous `getSignedSmartCdnUrl` from `@transloadit/utils/node`.
18
18
  */
19
19
  export declare const getSignedSmartCdnUrl: (opts: SmartCdnUrlOptions) => Promise<string>;
20
+ export type { StorageGrantClaims, StorageGrantScope } from './storageGrant.ts';
21
+ export { decodeStorageGrant, normalizeStorageGrantPrefix, parseStorageGrantClaims, STORAGE_GRANT_SCOPES, } from './storageGrant.ts';
20
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAIvD,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAExE,YAAY,EACV,iBAAiB,EACjB,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EACvB,mBAAmB,EACnB,0BAA0B,EAC1B,oBAAoB,EACpB,0BAA0B,EAC1B,wBAAwB,EACxB,mBAAmB,EACnB,6BAA6B,GAC9B,MAAM,oBAAoB,CAAA;AAE3B,cAAc,mCAAmC,CAAA;AACjD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACnF,OAAO,EACL,6BAA6B,EAC7B,2BAA2B,EAC3B,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,oBAAoB,CAAA;AAqD3B,eAAO,MAAM,UAAU,GACrB,cAAc,MAAM,EACpB,YAAY,MAAM,EAClB,YAAW,kBAA6B,KACvC,OAAO,CAAC,MAAM,CAOhB,CAAA;AAED,MAAM,MAAM,6BAA6B,GAAG;IAC1C,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,eAAO,MAAM,sBAAsB,GACjC,SAAS,6BAA6B,KACrC,OAAO,CAAC,OAAO,CAkBjB,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAU,MAAM,kBAAkB,KAAG,OAAO,CAAC,MAAM,CAInF,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAIvD,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAExE,YAAY,EACV,iBAAiB,EACjB,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EACvB,mBAAmB,EACnB,0BAA0B,EAC1B,oBAAoB,EACpB,0BAA0B,EAC1B,wBAAwB,EACxB,mBAAmB,EACnB,6BAA6B,GAC9B,MAAM,oBAAoB,CAAA;AAE3B,cAAc,mCAAmC,CAAA;AACjD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACnF,OAAO,EACL,6BAA6B,EAC7B,2BAA2B,EAC3B,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,oBAAoB,CAAA;AAqD3B,eAAO,MAAM,UAAU,GACrB,cAAc,MAAM,EACpB,YAAY,MAAM,EAClB,YAAW,kBAA6B,KACvC,OAAO,CAAC,MAAM,CAOhB,CAAA;AAED,MAAM,MAAM,6BAA6B,GAAG;IAC1C,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,eAAO,MAAM,sBAAsB,GACjC,SAAS,6BAA6B,KACrC,OAAO,CAAC,OAAO,CAkBjB,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAU,MAAM,kBAAkB,KAAG,OAAO,CAAC,MAAM,CAInF,CAAA;AAED,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAE9E,OAAO,EACL,kBAAkB,EAClB,2BAA2B,EAC3B,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,mBAAmB,CAAA"}
package/dist/index.js CHANGED
@@ -69,3 +69,4 @@ export const getSignedSmartCdnUrl = async (opts) => {
69
69
  const signature = await hmacHex('sha256', opts.authSecret, prepared.stringToSign);
70
70
  return finishSmartCdnUrl(prepared, signature);
71
71
  };
72
+ export { decodeStorageGrant, normalizeStorageGrantPrefix, parseStorageGrantClaims, STORAGE_GRANT_SCOPES, } from "./storageGrant.js";
package/dist/node.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { SignatureAlgorithm } from './index.ts';
2
2
  import type { SmartCdnUrlOptions } from './smartCdn.ts';
3
3
  import type { SmartCdnImageCandidates, SmartCdnImagePolicyOptions } from './smartCdnImage.ts';
4
+ import type { StorageGrantClaims, StorageGrantScope } from './storageGrant.ts';
4
5
  export type { SignatureAlgorithm } from './index.ts';
5
6
  export type { ParsedSmartCdnUrl, ParseSmartCdnUrlOptions, SmartCdnUnsignedUrlOptions, SmartCdnUrlOptions, SmartCdnUrlParams, } from './smartCdn.ts';
6
7
  export type { SignSmartCdnImageRequest, SmartCdnImageCandidate, SmartCdnImageCandidates, SmartCdnImageFormat, SmartCdnImageFormatQuality, SmartCdnImageFormats, SmartCdnImagePolicyOptions, SmartCdnImageSignRequest, SmartCdnImageSource, SmartCdnImageSourceDimensions, } from './smartCdnImage.ts';
@@ -26,4 +27,35 @@ export declare const getSignedSmartCdnUrl: (opts: SmartCdnUrlOptions) => string;
26
27
  * rendition above the backend's width or derived-height limits.
27
28
  */
28
29
  export declare function getSignedSmartCdnImageCandidates(opts: SmartCdnImageCandidatesOptions): SmartCdnImageCandidates;
30
+ export type { StorageGrantClaims, StorageGrantScope } from './storageGrant.ts';
31
+ export { decodeStorageGrant, normalizeStorageGrantPrefix, parseStorageGrantClaims, STORAGE_GRANT_SCOPES, } from './storageGrant.ts';
32
+ export type SignStorageGrantOptions = {
33
+ /** The workspace slug ("bucket" in S3 terms). */
34
+ bucket: string;
35
+ /** Key prefix to confine the session to. Default: the whole workspace. */
36
+ prefix?: string;
37
+ /** Default: read-only. */
38
+ scopes?: StorageGrantScope[];
39
+ /** Who the grant is minted for (informational). */
40
+ sub?: string;
41
+ /** Grant lifetime. Default: 900 (15 minutes). */
42
+ expiresInSeconds?: number;
43
+ /** Clock override for tests. */
44
+ nowMs?: number;
45
+ };
46
+ /**
47
+ * Mints a storage grant: an HS256 JWT with the v1 claim set, byte-compatible
48
+ * with api2's `StorageGrantManager` (same header, claim order and encoding).
49
+ */
50
+ export declare const signStorageGrant: (options: SignStorageGrantOptions, secret: string) => {
51
+ grant: string;
52
+ claims: StorageGrantClaims;
53
+ };
54
+ /**
55
+ * Verifies a storage grant: HS256 signature (timing-safe), strict v1 claim
56
+ * shape, and expiry. Throws with a stable message on any failure.
57
+ */
58
+ export declare const verifyStorageGrant: (token: string, secret: string, { nowMs }?: {
59
+ nowMs?: number;
60
+ }) => StorageGrantClaims;
29
61
  //# sourceMappingURL=node.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AACpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AACvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAA;AAO7F,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AACpD,YAAY,EACV,iBAAiB,EACjB,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EACvB,mBAAmB,EACnB,0BAA0B,EAC1B,oBAAoB,EACpB,0BAA0B,EAC1B,wBAAwB,EACxB,mBAAmB,EACnB,6BAA6B,GAC9B,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACnF,OAAO,EACL,2BAA2B,EAC3B,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,oBAAoB,CAAA;AAE3B,MAAM,MAAM,uBAAuB,GAAG,kBAAkB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;AAExE,8EAA8E;AAC9E,MAAM,WAAW,8BAA+B,SAAQ,0BAA0B;IAChF,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAA;IACf,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAA;IAClB,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,eAAO,MAAM,cAAc,GACzB,cAAc,MAAM,EACpB,YAAY,MAAM,EAClB,YAAW,uBAAkC,KAC5C,MAKF,CAAA;AAED,4FAA4F;AAC5F,eAAO,MAAM,oBAAoB,GAAI,MAAM,kBAAkB,KAAG,MAM/D,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAC9C,IAAI,EAAE,8BAA8B,GACnC,uBAAuB,CAsBzB"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AACpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AACvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAA;AAC7F,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAS9E,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AACpD,YAAY,EACV,iBAAiB,EACjB,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EACvB,mBAAmB,EACnB,0BAA0B,EAC1B,oBAAoB,EACpB,0BAA0B,EAC1B,wBAAwB,EACxB,mBAAmB,EACnB,6BAA6B,GAC9B,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACnF,OAAO,EACL,2BAA2B,EAC3B,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,oBAAoB,CAAA;AAE3B,MAAM,MAAM,uBAAuB,GAAG,kBAAkB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;AAExE,8EAA8E;AAC9E,MAAM,WAAW,8BAA+B,SAAQ,0BAA0B;IAChF,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAA;IACf,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAA;IAClB,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,eAAO,MAAM,cAAc,GACzB,cAAc,MAAM,EACpB,YAAY,MAAM,EAClB,YAAW,uBAAkC,KAC5C,MAKF,CAAA;AAED,4FAA4F;AAC5F,eAAO,MAAM,oBAAoB,GAAI,MAAM,kBAAkB,KAAG,MAM/D,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAC9C,IAAI,EAAE,8BAA8B,GACnC,uBAAuB,CAsBzB;AAID,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAE9E,OAAO,EACL,kBAAkB,EAClB,2BAA2B,EAC3B,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,mBAAmB,CAAA;AAK1B,MAAM,MAAM,uBAAuB,GAAG;IACpC,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAA;IACd,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0BAA0B;IAC1B,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAA;IAC5B,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,iDAAiD;IACjD,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAC3B,SAAS,uBAAuB,EAChC,QAAQ,MAAM,KACb;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAA;CAiB7C,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,YAAwB;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,KAC9C,kBA6BF,CAAA"}
package/dist/node.js CHANGED
@@ -1,6 +1,8 @@
1
- import { createHmac } from 'node:crypto';
1
+ import { Buffer } from 'node:buffer';
2
+ import { createHmac, timingSafeEqual } from 'node:crypto';
2
3
  import { finishSmartCdnUrl, prepareSmartCdnUrl } from "./smartCdn.js";
3
4
  import { createSmartCdnImageCandidates } from "./smartCdnImage.js";
5
+ import { parseStorageGrantClaims } from "./storageGrant.js";
4
6
  export { getSmartCdnUrl, parseSmartCdnUrl, stripSmartCdnAuth } from "./smartCdn.js";
5
7
  export { resolveSmartCdnImageFormats, resolveSmartCdnImageWidths, smartCdnImageMaxDimension, } from "./smartCdnImage.js";
6
8
  export const signParamsSync = (paramsString, authSecret, algorithm = 'sha384') => {
@@ -43,3 +45,60 @@ export function getSignedSmartCdnImageCandidates(opts) {
43
45
  workspace,
44
46
  }));
45
47
  }
48
+ export { decodeStorageGrant, normalizeStorageGrantPrefix, parseStorageGrantClaims, STORAGE_GRANT_SCOPES, } from "./storageGrant.js";
49
+ const base64url = (value) => (typeof value === 'string' ? Buffer.from(value) : value).toString('base64url');
50
+ /**
51
+ * Mints a storage grant: an HS256 JWT with the v1 claim set, byte-compatible
52
+ * with api2's `StorageGrantManager` (same header, claim order and encoding).
53
+ */
54
+ export const signStorageGrant = (options, secret) => {
55
+ const { bucket, prefix = '', scopes = ['read'], sub, expiresInSeconds = 900 } = options;
56
+ const iat = Math.floor((options.nowMs ?? Date.now()) / 1000);
57
+ const claims = {
58
+ v: 1,
59
+ bucket,
60
+ prefix,
61
+ scopes: [...new Set(scopes)],
62
+ ...(sub === undefined ? {} : { sub }),
63
+ iat,
64
+ exp: iat + expiresInSeconds,
65
+ };
66
+ const signingInput = `${base64url(JSON.stringify({ alg: 'HS256', typ: 'JWT' }))}.${base64url(JSON.stringify(claims))}`;
67
+ const signature = base64url(createHmac('sha256', secret).update(signingInput).digest());
68
+ return { grant: `${signingInput}.${signature}`, claims };
69
+ };
70
+ /**
71
+ * Verifies a storage grant: HS256 signature (timing-safe), strict v1 claim
72
+ * shape, and expiry. Throws with a stable message on any failure.
73
+ */
74
+ export const verifyStorageGrant = (token, secret, { nowMs = Date.now() } = {}) => {
75
+ const [headerPart, payloadPart, signaturePart, ...rest] = token.split('.');
76
+ if (!headerPart || !payloadPart || !signaturePart || rest.length > 0) {
77
+ throw new Error('Invalid storage grant');
78
+ }
79
+ const expected = createHmac('sha256', secret).update(`${headerPart}.${payloadPart}`).digest();
80
+ const actual = Buffer.from(signaturePart, 'base64url');
81
+ if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) {
82
+ throw new Error('Invalid storage grant signature');
83
+ }
84
+ let header;
85
+ let payload;
86
+ try {
87
+ header = JSON.parse(Buffer.from(headerPart, 'base64url').toString('utf8'));
88
+ payload = JSON.parse(Buffer.from(payloadPart, 'base64url').toString('utf8'));
89
+ }
90
+ catch {
91
+ throw new Error('Invalid storage grant');
92
+ }
93
+ if (typeof header !== 'object' ||
94
+ header === null ||
95
+ header.alg !== 'HS256') {
96
+ throw new Error('Invalid storage grant algorithm');
97
+ }
98
+ const claims = parseStorageGrantClaims(payload);
99
+ if (claims === null)
100
+ throw new Error('Invalid storage grant claims');
101
+ if (claims.exp <= Math.floor(nowMs / 1000))
102
+ throw new Error('The storage grant has expired');
103
+ return claims;
104
+ };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The Transloadit Storage grant: a short-lived HS256 JWT an integrator's
3
+ * server mints (or api2's `POST /storage/grants` mints for it) and
4
+ * Companion's S3 provider verifies. This module is the one wire contract —
5
+ * claim shape, decoding, and (in `@transloadit/utils/node`) minting and
6
+ * verification — so the implementations cannot drift apart.
7
+ *
8
+ * Everything in this file is browser-safe: the client may *read* a grant's
9
+ * claims to decide what UI to show, but verification is the server's job.
10
+ */
11
+ export declare const STORAGE_GRANT_SCOPES: readonly ["read", "write"];
12
+ export type StorageGrantScope = (typeof STORAGE_GRANT_SCOPES)[number];
13
+ export type StorageGrantClaims = {
14
+ v: 1;
15
+ /** The workspace slug ("bucket" in S3 terms). */
16
+ bucket: string;
17
+ /** Key prefix the session is confined to; may be empty. */
18
+ prefix: string;
19
+ scopes: StorageGrantScope[];
20
+ /** Who the grant was minted for (informational). */
21
+ sub?: string;
22
+ /** Unix seconds. */
23
+ iat?: number;
24
+ /** Unix seconds. */
25
+ exp: number;
26
+ };
27
+ /**
28
+ * Validates a decoded JWT payload against the grant contract. Returns the
29
+ * claims (scopes deduplicated) or null when the shape is not a v1 grant.
30
+ */
31
+ export declare const parseStorageGrantClaims: (payload: unknown) => StorageGrantClaims | null;
32
+ /** Companion's prefix policy: no leading slashes, a trailing slash unless empty. */
33
+ export declare const normalizeStorageGrantPrefix: (prefix: string) => string;
34
+ /**
35
+ * Reads a grant's payload without verifying the signature — for clients that
36
+ * only need to know what the session will be allowed to do. Returns null for
37
+ * anything that does not decode to a v1 grant.
38
+ */
39
+ export declare const decodeStorageGrant: (token: string) => StorageGrantClaims | null;
40
+ //# sourceMappingURL=storageGrant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storageGrant.d.ts","sourceRoot":"","sources":["../src/storageGrant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,eAAO,MAAM,oBAAoB,4BAA6B,CAAA;AAC9D,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAA;AAErE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,CAAC,EAAE,CAAC,CAAA;IACJ,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAA;IACd,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,iBAAiB,EAAE,CAAA;IAC3B,oDAAoD;IACpD,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,oBAAoB;IACpB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,oBAAoB;IACpB,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAKD;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAAI,SAAS,OAAO,KAAG,kBAAkB,GAAG,IAyB/E,CAAA;AAED,oFAAoF;AACpF,eAAO,MAAM,2BAA2B,GAAI,QAAQ,MAAM,KAAG,MAG5D,CAAA;AASD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,KAAG,kBAAkB,GAAG,IAQvE,CAAA"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The Transloadit Storage grant: a short-lived HS256 JWT an integrator's
3
+ * server mints (or api2's `POST /storage/grants` mints for it) and
4
+ * Companion's S3 provider verifies. This module is the one wire contract —
5
+ * claim shape, decoding, and (in `@transloadit/utils/node`) minting and
6
+ * verification — so the implementations cannot drift apart.
7
+ *
8
+ * Everything in this file is browser-safe: the client may *read* a grant's
9
+ * claims to decide what UI to show, but verification is the server's job.
10
+ */
11
+ export const STORAGE_GRANT_SCOPES = ['read', 'write'];
12
+ const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
13
+ /**
14
+ * Validates a decoded JWT payload against the grant contract. Returns the
15
+ * claims (scopes deduplicated) or null when the shape is not a v1 grant.
16
+ */
17
+ export const parseStorageGrantClaims = (payload) => {
18
+ if (!isRecord(payload) ||
19
+ payload.v !== 1 ||
20
+ typeof payload.bucket !== 'string' ||
21
+ payload.bucket.length === 0 ||
22
+ typeof payload.prefix !== 'string' ||
23
+ !Array.isArray(payload.scopes) ||
24
+ !payload.scopes.every((scope) => typeof scope === 'string' && STORAGE_GRANT_SCOPES.includes(scope)) ||
25
+ typeof payload.exp !== 'number') {
26
+ return null;
27
+ }
28
+ return {
29
+ v: 1,
30
+ bucket: payload.bucket,
31
+ prefix: payload.prefix,
32
+ scopes: [...new Set(payload.scopes)],
33
+ ...(typeof payload.sub === 'string' && { sub: payload.sub }),
34
+ ...(typeof payload.iat === 'number' && { iat: payload.iat }),
35
+ exp: payload.exp,
36
+ };
37
+ };
38
+ /** Companion's prefix policy: no leading slashes, a trailing slash unless empty. */
39
+ export const normalizeStorageGrantPrefix = (prefix) => {
40
+ const cleaned = prefix.replace(/^\/+/, '');
41
+ return cleaned.length === 0 || cleaned.endsWith('/') ? cleaned : `${cleaned}/`;
42
+ };
43
+ const decodeBase64UrlToUtf8 = (input) => {
44
+ const base64 = input.replace(/-/g, '+').replace(/_/g, '/');
45
+ const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);
46
+ const binary = atob(padded);
47
+ return new TextDecoder().decode(Uint8Array.from(binary, (char) => char.charCodeAt(0)));
48
+ };
49
+ /**
50
+ * Reads a grant's payload without verifying the signature — for clients that
51
+ * only need to know what the session will be allowed to do. Returns null for
52
+ * anything that does not decode to a v1 grant.
53
+ */
54
+ export const decodeStorageGrant = (token) => {
55
+ try {
56
+ const payload = token.split('.')[1];
57
+ if (payload === undefined || payload.length === 0)
58
+ return null;
59
+ return parseStorageGrantClaims(JSON.parse(decodeBase64UrlToUtf8(payload)));
60
+ }
61
+ catch {
62
+ return null;
63
+ }
64
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@transloadit/utils",
3
- "version": "4.7.1",
3
+ "version": "4.8.1",
4
4
  "description": "Transloadit shared utilities",
5
5
  "type": "module",
6
6
  "license": "MIT",