@drawbridge/drawbridge-utils 0.0.99 → 0.0.101

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/ai.cjs CHANGED
@@ -168,6 +168,16 @@ var cost = {
168
168
  input: 150,
169
169
  output: 900
170
170
  },
171
+ // gemini-3.5-flash-lite — verified against Google's pricing page 2026-08-20:
172
+ // $0.03 cached / $0.30 input / $2.50 output per 1M tokens (thinking billed at
173
+ // output). A fifth of flash on input, ~a quarter on output. Growth's assistant
174
+ // ranks the feed on this tier — one call per page — so its rows were the
175
+ // unpriced ones until now.
176
+ "gemini-3.5-flash-lite": {
177
+ cached: 3,
178
+ input: 30,
179
+ output: 250
180
+ },
171
181
  "gemini-2.5-flash": {
172
182
  cached: 3,
173
183
  input: 30,
package/dist/ai.js CHANGED
@@ -134,6 +134,16 @@ var cost = {
134
134
  input: 150,
135
135
  output: 900
136
136
  },
137
+ // gemini-3.5-flash-lite — verified against Google's pricing page 2026-08-20:
138
+ // $0.03 cached / $0.30 input / $2.50 output per 1M tokens (thinking billed at
139
+ // output). A fifth of flash on input, ~a quarter on output. Growth's assistant
140
+ // ranks the feed on this tier — one call per page — so its rows were the
141
+ // unpriced ones until now.
142
+ "gemini-3.5-flash-lite": {
143
+ cached: 3,
144
+ input: 30,
145
+ output: 250
146
+ },
137
147
  "gemini-2.5-flash": {
138
148
  cached: 3,
139
149
  input: 30,
package/dist/billing.cjs CHANGED
@@ -168,6 +168,16 @@ var cost = {
168
168
  input: 150,
169
169
  output: 900
170
170
  },
171
+ // gemini-3.5-flash-lite — verified against Google's pricing page 2026-08-20:
172
+ // $0.03 cached / $0.30 input / $2.50 output per 1M tokens (thinking billed at
173
+ // output). A fifth of flash on input, ~a quarter on output. Growth's assistant
174
+ // ranks the feed on this tier — one call per page — so its rows were the
175
+ // unpriced ones until now.
176
+ "gemini-3.5-flash-lite": {
177
+ cached: 3,
178
+ input: 30,
179
+ output: 250
180
+ },
171
181
  "gemini-2.5-flash": {
172
182
  cached: 3,
173
183
  input: 30,
@@ -39,6 +39,16 @@ const cost = {
39
39
  input : 150,
40
40
  output : 900
41
41
  },
42
+ // gemini-3.5-flash-lite — verified against Google's pricing page 2026-08-20:
43
+ // $0.03 cached / $0.30 input / $2.50 output per 1M tokens (thinking billed at
44
+ // output). A fifth of flash on input, ~a quarter on output. Growth's assistant
45
+ // ranks the feed on this tier — one call per page — so its rows were the
46
+ // unpriced ones until now.
47
+ 'gemini-3.5-flash-lite' : {
48
+ cached : 3,
49
+ input : 30,
50
+ output : 250
51
+ },
42
52
  'gemini-2.5-flash' : {
43
53
  cached : 3,
44
54
  input : 30,
package/dist/billing.d.ts CHANGED
@@ -39,6 +39,16 @@ const cost = {
39
39
  input : 150,
40
40
  output : 900
41
41
  },
42
+ // gemini-3.5-flash-lite — verified against Google's pricing page 2026-08-20:
43
+ // $0.03 cached / $0.30 input / $2.50 output per 1M tokens (thinking billed at
44
+ // output). A fifth of flash on input, ~a quarter on output. Growth's assistant
45
+ // ranks the feed on this tier — one call per page — so its rows were the
46
+ // unpriced ones until now.
47
+ 'gemini-3.5-flash-lite' : {
48
+ cached : 3,
49
+ input : 30,
50
+ output : 250
51
+ },
42
52
  'gemini-2.5-flash' : {
43
53
  cached : 3,
44
54
  input : 30,
package/dist/billing.js CHANGED
@@ -138,6 +138,16 @@ var cost = {
138
138
  input: 150,
139
139
  output: 900
140
140
  },
141
+ // gemini-3.5-flash-lite — verified against Google's pricing page 2026-08-20:
142
+ // $0.03 cached / $0.30 input / $2.50 output per 1M tokens (thinking billed at
143
+ // output). A fifth of flash on input, ~a quarter on output. Growth's assistant
144
+ // ranks the feed on this tier — one call per page — so its rows were the
145
+ // unpriced ones until now.
146
+ "gemini-3.5-flash-lite": {
147
+ cached: 3,
148
+ input: 30,
149
+ output: 250
150
+ },
141
151
  "gemini-2.5-flash": {
142
152
  cached: 3,
143
153
  input: 30,
package/dist/features.cjs CHANGED
@@ -68,7 +68,7 @@ var field = {
68
68
  feature: "Campaign name field"
69
69
  },
70
70
  number: {
71
- key: "campaign:field:name",
71
+ key: "campaign:field:number",
72
72
  error: "Plan does not include number field",
73
73
  feature: "Campaign number field"
74
74
  },
@@ -41,7 +41,7 @@ const field = {
41
41
  feature : 'Campaign name field',
42
42
  },
43
43
  number : {
44
- key : 'campaign:field:name',
44
+ key : 'campaign:field:number',
45
45
  error : 'Plan does not include number field',
46
46
  feature : 'Campaign number field',
47
47
  },
@@ -41,7 +41,7 @@ const field = {
41
41
  feature : 'Campaign name field',
42
42
  },
43
43
  number : {
44
- key : 'campaign:field:name',
44
+ key : 'campaign:field:number',
45
45
  error : 'Plan does not include number field',
46
46
  feature : 'Campaign number field',
47
47
  },
package/dist/features.js CHANGED
@@ -40,7 +40,7 @@ var field = {
40
40
  feature: "Campaign name field"
41
41
  },
42
42
  number: {
43
- key: "campaign:field:name",
43
+ key: "campaign:field:number",
44
44
  error: "Plan does not include number field",
45
45
  feature: "Campaign number field"
46
46
  },
package/dist/fetch.cjs CHANGED
@@ -32,8 +32,7 @@ __export(fetch_exports, {
32
32
  isTransientError: () => isTransientError,
33
33
  queryString: () => queryString,
34
34
  request: () => request,
35
- setMemoryToken: () => setMemoryToken,
36
- upload: () => upload
35
+ setMemoryToken: () => setMemoryToken
37
36
  });
38
37
  module.exports = __toCommonJS(fetch_exports);
39
38
  var import_qs = __toESM(require("qs"), 1);
@@ -123,59 +122,6 @@ var request = async ({
123
122
  }
124
123
  return res;
125
124
  };
126
- var upload = ({
127
- body,
128
- endpoint,
129
- method = "POST",
130
- onProgress,
131
- signal,
132
- token,
133
- url = process.env.NEXT_PUBLIC_API_URI
134
- }) => {
135
- const authToken = token || (typeof window !== "undefined" ? memoryToken : null);
136
- const csrf = readCsrf();
137
- return new Promise((resolve, reject) => {
138
- const xhr = new XMLHttpRequest();
139
- xhr.open(method, url + endpoint, true);
140
- xhr.setRequestHeader("accept", "application/json");
141
- if (authToken) {
142
- xhr.setRequestHeader("authorization", "Bearer " + authToken);
143
- }
144
- if (csrf) {
145
- xhr.setRequestHeader("x-drawbridge-csrf", csrf);
146
- }
147
- xhr.upload.addEventListener("progress", (event) => {
148
- if (!event.lengthComputable || !onProgress) return;
149
- onProgress({
150
- bytesTotal: event.total,
151
- bytesUploaded: event.loaded
152
- });
153
- });
154
- xhr.addEventListener("load", () => {
155
- let data = null;
156
- try {
157
- data = JSON.parse(xhr.responseText);
158
- } catch {
159
- data = null;
160
- }
161
- if (xhr.status >= 200 && xhr.status < 300) return resolve(data);
162
- const error = new Error((data == null ? void 0 : data.message) || xhr.statusText);
163
- error.status = xhr.status;
164
- error.data = data;
165
- reject(error);
166
- });
167
- xhr.addEventListener("abort", () => {
168
- const error = new Error("Upload cancelled");
169
- error.name = "AbortError";
170
- reject(error);
171
- });
172
- xhr.addEventListener("error", () => reject(new Error("Network error")));
173
- if (signal) {
174
- signal.addEventListener("abort", () => xhr.abort(), { once: true });
175
- }
176
- xhr.send(body);
177
- });
178
- };
179
125
  var isTransientError = (error) => {
180
126
  const status = error == null ? void 0 : error.status;
181
127
  return !status || status === 429 || status >= 500;
@@ -185,6 +131,5 @@ var isTransientError = (error) => {
185
131
  isTransientError,
186
132
  queryString,
187
133
  request,
188
- setMemoryToken,
189
- upload
134
+ setMemoryToken
190
135
  });
package/dist/fetch.d.cts CHANGED
@@ -124,118 +124,6 @@ const request = async ({
124
124
 
125
125
  };
126
126
 
127
- // fetch() has no upload-progress event and never will, which is the reason every
128
- // upload in the dashboard could only ever show a spinner. XHR does, so anything
129
- // that wants a real progress bar posts through here instead.
130
- //
131
- // It lives beside request() rather than in the dashboard because `memoryToken`
132
- // above is module-private on purpose — a copy in app-web could only read it if
133
- // the token were exported, which is the one thing setMemoryToken exists to
134
- // prevent. The csrf cookie read is here too.
135
- //
136
- // The error shape is deliberately identical to request()'s — an Error carrying
137
- // .status and .data — so isTransientError() and every existing catch block work
138
- // against either transport without knowing which ran.
139
- const upload = ({
140
- body,
141
- endpoint,
142
- method = 'POST',
143
- onProgress,
144
- signal,
145
- token,
146
- url = process.env.NEXT_PUBLIC_API_URI
147
- }) => {
148
-
149
- const authToken = token || ( typeof window !== 'undefined' ? memoryToken : null );
150
- const csrf = readCsrf();
151
-
152
- return new Promise( ( resolve, reject ) => {
153
-
154
- const xhr = new XMLHttpRequest();
155
-
156
- xhr.open( method, url + endpoint, true );
157
-
158
- // No content-type, ever. The browser has to write the multipart boundary
159
- // itself and setting the header by hand drops it, which busboy then
160
- // rejects. Nor origin: it is a forbidden header name, so the browser
161
- // ignores setRequestHeader for it ( request() only sets it because its
162
- // server-side callers run where fetch allows it ).
163
- xhr.setRequestHeader( 'accept', 'application/json' );
164
-
165
- if( authToken ){
166
-
167
- xhr.setRequestHeader( 'authorization', 'Bearer ' + authToken );
168
-
169
- }
170
-
171
- if( csrf ){
172
-
173
- xhr.setRequestHeader( 'x-drawbridge-csrf', csrf );
174
-
175
- }
176
-
177
- xhr.upload.addEventListener( 'progress', ( event ) => {
178
-
179
- if( ! event.lengthComputable || ! onProgress ) return;
180
-
181
- onProgress({
182
- bytesTotal : event.total,
183
- bytesUploaded : event.loaded
184
- });
185
-
186
- });
187
-
188
- xhr.addEventListener( 'load', () => {
189
-
190
- let data = null;
191
-
192
- try {
193
-
194
- data = JSON.parse( xhr.responseText );
195
-
196
- } catch {
197
-
198
- data = null;
199
-
200
- }
201
-
202
- if( xhr.status >= 200 && xhr.status < 300 ) return resolve( data );
203
-
204
- const error = new Error( data?.message || xhr.statusText );
205
-
206
- error.status = xhr.status;
207
- error.data = data;
208
-
209
- reject( error );
210
-
211
- });
212
-
213
- xhr.addEventListener( 'abort', () => {
214
-
215
- const error = new Error( 'Upload cancelled' );
216
-
217
- error.name = 'AbortError';
218
-
219
- reject( error );
220
-
221
- });
222
-
223
- // No status on a network failure, which is exactly how isTransientError
224
- // classifies it — worth a retry rather than a sign-in bounce.
225
- xhr.addEventListener( 'error', () => reject( new Error( 'Network error' ) ) );
226
-
227
- if( signal ){
228
-
229
- signal.addEventListener( 'abort', () => xhr.abort(), { once : true } );
230
-
231
- }
232
-
233
- xhr.send( body );
234
-
235
- });
236
-
237
- };
238
-
239
127
  // Classifies an error thrown by request() — which carries `.status` from the
240
128
  // HTTP response, or no status for a network / parse failure. Transient errors
241
129
  // ( rate limits, server errors, connection blips ) may succeed on retry, so
@@ -251,4 +139,4 @@ const isTransientError = ( error ) => {
251
139
 
252
140
  };
253
141
 
254
- export { isTransientError, queryString, request, setMemoryToken, upload };
142
+ export { isTransientError, queryString, request, setMemoryToken };
package/dist/fetch.d.ts CHANGED
@@ -124,118 +124,6 @@ const request = async ({
124
124
 
125
125
  };
126
126
 
127
- // fetch() has no upload-progress event and never will, which is the reason every
128
- // upload in the dashboard could only ever show a spinner. XHR does, so anything
129
- // that wants a real progress bar posts through here instead.
130
- //
131
- // It lives beside request() rather than in the dashboard because `memoryToken`
132
- // above is module-private on purpose — a copy in app-web could only read it if
133
- // the token were exported, which is the one thing setMemoryToken exists to
134
- // prevent. The csrf cookie read is here too.
135
- //
136
- // The error shape is deliberately identical to request()'s — an Error carrying
137
- // .status and .data — so isTransientError() and every existing catch block work
138
- // against either transport without knowing which ran.
139
- const upload = ({
140
- body,
141
- endpoint,
142
- method = 'POST',
143
- onProgress,
144
- signal,
145
- token,
146
- url = process.env.NEXT_PUBLIC_API_URI
147
- }) => {
148
-
149
- const authToken = token || ( typeof window !== 'undefined' ? memoryToken : null );
150
- const csrf = readCsrf();
151
-
152
- return new Promise( ( resolve, reject ) => {
153
-
154
- const xhr = new XMLHttpRequest();
155
-
156
- xhr.open( method, url + endpoint, true );
157
-
158
- // No content-type, ever. The browser has to write the multipart boundary
159
- // itself and setting the header by hand drops it, which busboy then
160
- // rejects. Nor origin: it is a forbidden header name, so the browser
161
- // ignores setRequestHeader for it ( request() only sets it because its
162
- // server-side callers run where fetch allows it ).
163
- xhr.setRequestHeader( 'accept', 'application/json' );
164
-
165
- if( authToken ){
166
-
167
- xhr.setRequestHeader( 'authorization', 'Bearer ' + authToken );
168
-
169
- }
170
-
171
- if( csrf ){
172
-
173
- xhr.setRequestHeader( 'x-drawbridge-csrf', csrf );
174
-
175
- }
176
-
177
- xhr.upload.addEventListener( 'progress', ( event ) => {
178
-
179
- if( ! event.lengthComputable || ! onProgress ) return;
180
-
181
- onProgress({
182
- bytesTotal : event.total,
183
- bytesUploaded : event.loaded
184
- });
185
-
186
- });
187
-
188
- xhr.addEventListener( 'load', () => {
189
-
190
- let data = null;
191
-
192
- try {
193
-
194
- data = JSON.parse( xhr.responseText );
195
-
196
- } catch {
197
-
198
- data = null;
199
-
200
- }
201
-
202
- if( xhr.status >= 200 && xhr.status < 300 ) return resolve( data );
203
-
204
- const error = new Error( data?.message || xhr.statusText );
205
-
206
- error.status = xhr.status;
207
- error.data = data;
208
-
209
- reject( error );
210
-
211
- });
212
-
213
- xhr.addEventListener( 'abort', () => {
214
-
215
- const error = new Error( 'Upload cancelled' );
216
-
217
- error.name = 'AbortError';
218
-
219
- reject( error );
220
-
221
- });
222
-
223
- // No status on a network failure, which is exactly how isTransientError
224
- // classifies it — worth a retry rather than a sign-in bounce.
225
- xhr.addEventListener( 'error', () => reject( new Error( 'Network error' ) ) );
226
-
227
- if( signal ){
228
-
229
- signal.addEventListener( 'abort', () => xhr.abort(), { once : true } );
230
-
231
- }
232
-
233
- xhr.send( body );
234
-
235
- });
236
-
237
- };
238
-
239
127
  // Classifies an error thrown by request() — which carries `.status` from the
240
128
  // HTTP response, or no status for a network / parse failure. Transient errors
241
129
  // ( rate limits, server errors, connection blips ) may succeed on retry, so
@@ -251,4 +139,4 @@ const isTransientError = ( error ) => {
251
139
 
252
140
  };
253
141
 
254
- export { isTransientError, queryString, request, setMemoryToken, upload };
142
+ export { isTransientError, queryString, request, setMemoryToken };
package/dist/fetch.js CHANGED
@@ -86,59 +86,6 @@ var request = async ({
86
86
  }
87
87
  return res;
88
88
  };
89
- var upload = ({
90
- body,
91
- endpoint,
92
- method = "POST",
93
- onProgress,
94
- signal,
95
- token,
96
- url = process.env.NEXT_PUBLIC_API_URI
97
- }) => {
98
- const authToken = token || (typeof window !== "undefined" ? memoryToken : null);
99
- const csrf = readCsrf();
100
- return new Promise((resolve, reject) => {
101
- const xhr = new XMLHttpRequest();
102
- xhr.open(method, url + endpoint, true);
103
- xhr.setRequestHeader("accept", "application/json");
104
- if (authToken) {
105
- xhr.setRequestHeader("authorization", "Bearer " + authToken);
106
- }
107
- if (csrf) {
108
- xhr.setRequestHeader("x-drawbridge-csrf", csrf);
109
- }
110
- xhr.upload.addEventListener("progress", (event) => {
111
- if (!event.lengthComputable || !onProgress) return;
112
- onProgress({
113
- bytesTotal: event.total,
114
- bytesUploaded: event.loaded
115
- });
116
- });
117
- xhr.addEventListener("load", () => {
118
- let data = null;
119
- try {
120
- data = JSON.parse(xhr.responseText);
121
- } catch {
122
- data = null;
123
- }
124
- if (xhr.status >= 200 && xhr.status < 300) return resolve(data);
125
- const error = new Error((data == null ? void 0 : data.message) || xhr.statusText);
126
- error.status = xhr.status;
127
- error.data = data;
128
- reject(error);
129
- });
130
- xhr.addEventListener("abort", () => {
131
- const error = new Error("Upload cancelled");
132
- error.name = "AbortError";
133
- reject(error);
134
- });
135
- xhr.addEventListener("error", () => reject(new Error("Network error")));
136
- if (signal) {
137
- signal.addEventListener("abort", () => xhr.abort(), { once: true });
138
- }
139
- xhr.send(body);
140
- });
141
- };
142
89
  var isTransientError = (error) => {
143
90
  const status = error == null ? void 0 : error.status;
144
91
  return !status || status === 429 || status >= 500;
@@ -147,6 +94,5 @@ export {
147
94
  isTransientError,
148
95
  queryString,
149
96
  request,
150
- setMemoryToken,
151
- upload
97
+ setMemoryToken
152
98
  };
package/dist/plans.cjs CHANGED
@@ -78,7 +78,7 @@ var field = {
78
78
  feature: "Campaign name field"
79
79
  },
80
80
  number: {
81
- key: "campaign:field:name",
81
+ key: "campaign:field:number",
82
82
  error: "Plan does not include number field",
83
83
  feature: "Campaign number field"
84
84
  },
package/dist/plans.js CHANGED
@@ -40,7 +40,7 @@ var field = {
40
40
  feature: "Campaign name field"
41
41
  },
42
42
  number: {
43
- key: "campaign:field:name",
43
+ key: "campaign:field:number",
44
44
  error: "Plan does not include number field",
45
45
  feature: "Campaign number field"
46
46
  },
package/dist/upload.cjs CHANGED
@@ -22,10 +22,7 @@ __export(upload_exports, {
22
22
  allowedMimes: () => allowedMimes,
23
23
  allowedUploadTypes: () => allowedUploadTypes,
24
24
  isAllowedMime: () => isAllowedMime,
25
- maximumUploadBytes: () => maximumUploadBytes,
26
- resolveUploadType: () => resolveUploadType,
27
- uploadPartCount: () => uploadPartCount,
28
- uploadPartSize: () => uploadPartSize
25
+ resolveUploadType: () => resolveUploadType
29
26
  });
30
27
  module.exports = __toCommonJS(upload_exports);
31
28
  var allowedUploadTypes = {
@@ -39,11 +36,6 @@ var allowedUploadTypes = {
39
36
  webp: "image/webp"
40
37
  },
41
38
  video: {
42
- // iPhones record .mov (HEVC) by default, so this is the single most
43
- // likely video a merchant uploads. sync's storage.js already computes
44
- // needsReencode for hevc/h265 and re-encodes to h264, so allowing it
45
- // here is the only thing the pipeline was waiting on.
46
- mov: "video/quicktime",
47
39
  mp4: "video/mp4"
48
40
  }
49
41
  };
@@ -64,27 +56,10 @@ var resolveUploadType = (type, extension) => {
64
56
  return mime;
65
57
  };
66
58
  var isAllowedMime = (mime) => allowedMimes.has(mime);
67
- var megabyte = 1024 * 1024;
68
- var maximumUploadBytes = 2048 * megabyte;
69
- var maximumParts = 9e3;
70
- var minimumPartSize = 10 * megabyte;
71
- var uploadPartSize = (bytes) => {
72
- const size = Number(bytes) || 0;
73
- const required = Math.ceil(size / maximumParts);
74
- const rounded = Math.ceil(required / megabyte) * megabyte;
75
- return Math.max(minimumPartSize, rounded);
76
- };
77
- var uploadPartCount = (bytes) => {
78
- const size = Number(bytes) || 0;
79
- return Math.max(1, Math.ceil(size / uploadPartSize(size)));
80
- };
81
59
  // Annotate the CommonJS export names for ESM import in node:
82
60
  0 && (module.exports = {
83
61
  allowedMimes,
84
62
  allowedUploadTypes,
85
63
  isAllowedMime,
86
- maximumUploadBytes,
87
- resolveUploadType,
88
- uploadPartCount,
89
- uploadPartSize
64
+ resolveUploadType
90
65
  });
package/dist/upload.d.cts CHANGED
@@ -15,12 +15,6 @@
15
15
  //
16
16
  // Why both helpers and not just one: api routes know (type, extension) but not
17
17
  // the canonical mime; sync workers have the mime but not the original type tuple.
18
- //
19
- // A third consumer joined with presigned multipart uploads: the dashboard reads
20
- // this same object to build its file picker's accept attribute and the uploader's
21
- // client-side restrictions, so a wrong type is refused before two gigabytes move
22
- // rather than after. That is the whole reason the list is not copied into
23
- // app-web — four views of one list can't drift.
24
18
 
25
19
  const allowedUploadTypes = {
26
20
  application : {
@@ -33,11 +27,6 @@ const allowedUploadTypes = {
33
27
  webp : 'image/webp'
34
28
  },
35
29
  video : {
36
- // iPhones record .mov (HEVC) by default, so this is the single most
37
- // likely video a merchant uploads. sync's storage.js already computes
38
- // needsReencode for hevc/h265 and re-encodes to h264, so allowing it
39
- // here is the only thing the pipeline was waiting on.
40
- mov : 'video/quicktime',
41
30
  mp4 : 'video/mp4'
42
31
  }
43
32
  };
@@ -66,51 +55,4 @@ const resolveUploadType = ( type, extension ) => {
66
55
 
67
56
  const isAllowedMime = ( mime ) => allowedMimes.has( mime );
68
57
 
69
- const megabyte = ( 1024 * 1024 );
70
-
71
- // The ceiling for a presigned multipart upload. Video streams browser-to-bucket
72
- // on that path rather than through the api, so this is a product decision
73
- // rather than the fleet-protection one that keeps the single-request busboy
74
- // route at 50MB.
75
- //
76
- // It lives here because both sides have to agree on it exactly: the dashboard
77
- // rejects before transferring and the api rejects authoritatively, and if the
78
- // two numbers drift the client blocks files the api would happily accept.
79
- const maximumUploadBytes = ( 2048 * megabyte );
80
-
81
- // S3 caps a multipart upload at 10,000 parts and requires every part except the
82
- // last to be at least 5MB. A fixed part size therefore puts a silent ceiling on
83
- // the maximum file, so the size is derived from the declared length with
84
- // headroom against the hard limit.
85
- //
86
- // The 10MB floor is a request-volume decision, not an S3 one: a 2GB upload at
87
- // 5MB parts is 410 signing calls, at 10MB it is 205.
88
- //
89
- // Both the api and the dashboard call this, and the dashboard *must* — Uppy
90
- // builds its chunk list in the MultipartUploader constructor, which runs before
91
- // createMultipartUpload is ever called, so a part size returned from the create
92
- // endpoint arrives too late to chunk with. One function, two callers, no
93
- // possibility of disagreeing about where the part boundaries fall.
94
- const maximumParts = 9000;
95
- const minimumPartSize = ( 10 * megabyte );
96
-
97
- const uploadPartSize = ( bytes ) => {
98
-
99
- const size = Number( bytes ) || 0;
100
- const required = Math.ceil( size / maximumParts );
101
- const rounded = Math.ceil( required / megabyte ) * megabyte;
102
-
103
- return Math.max( minimumPartSize, rounded );
104
-
105
- };
106
-
107
- // An empty upload is still one part — S3 has no zero-part upload.
108
- const uploadPartCount = ( bytes ) => {
109
-
110
- const size = Number( bytes ) || 0;
111
-
112
- return Math.max( 1, Math.ceil( size / uploadPartSize( size ) ) );
113
-
114
- };
115
-
116
- export { allowedMimes, allowedUploadTypes, isAllowedMime, maximumUploadBytes, resolveUploadType, uploadPartCount, uploadPartSize };
58
+ export { allowedMimes, allowedUploadTypes, isAllowedMime, resolveUploadType };
package/dist/upload.d.ts CHANGED
@@ -15,12 +15,6 @@
15
15
  //
16
16
  // Why both helpers and not just one: api routes know (type, extension) but not
17
17
  // the canonical mime; sync workers have the mime but not the original type tuple.
18
- //
19
- // A third consumer joined with presigned multipart uploads: the dashboard reads
20
- // this same object to build its file picker's accept attribute and the uploader's
21
- // client-side restrictions, so a wrong type is refused before two gigabytes move
22
- // rather than after. That is the whole reason the list is not copied into
23
- // app-web — four views of one list can't drift.
24
18
 
25
19
  const allowedUploadTypes = {
26
20
  application : {
@@ -33,11 +27,6 @@ const allowedUploadTypes = {
33
27
  webp : 'image/webp'
34
28
  },
35
29
  video : {
36
- // iPhones record .mov (HEVC) by default, so this is the single most
37
- // likely video a merchant uploads. sync's storage.js already computes
38
- // needsReencode for hevc/h265 and re-encodes to h264, so allowing it
39
- // here is the only thing the pipeline was waiting on.
40
- mov : 'video/quicktime',
41
30
  mp4 : 'video/mp4'
42
31
  }
43
32
  };
@@ -66,51 +55,4 @@ const resolveUploadType = ( type, extension ) => {
66
55
 
67
56
  const isAllowedMime = ( mime ) => allowedMimes.has( mime );
68
57
 
69
- const megabyte = ( 1024 * 1024 );
70
-
71
- // The ceiling for a presigned multipart upload. Video streams browser-to-bucket
72
- // on that path rather than through the api, so this is a product decision
73
- // rather than the fleet-protection one that keeps the single-request busboy
74
- // route at 50MB.
75
- //
76
- // It lives here because both sides have to agree on it exactly: the dashboard
77
- // rejects before transferring and the api rejects authoritatively, and if the
78
- // two numbers drift the client blocks files the api would happily accept.
79
- const maximumUploadBytes = ( 2048 * megabyte );
80
-
81
- // S3 caps a multipart upload at 10,000 parts and requires every part except the
82
- // last to be at least 5MB. A fixed part size therefore puts a silent ceiling on
83
- // the maximum file, so the size is derived from the declared length with
84
- // headroom against the hard limit.
85
- //
86
- // The 10MB floor is a request-volume decision, not an S3 one: a 2GB upload at
87
- // 5MB parts is 410 signing calls, at 10MB it is 205.
88
- //
89
- // Both the api and the dashboard call this, and the dashboard *must* — Uppy
90
- // builds its chunk list in the MultipartUploader constructor, which runs before
91
- // createMultipartUpload is ever called, so a part size returned from the create
92
- // endpoint arrives too late to chunk with. One function, two callers, no
93
- // possibility of disagreeing about where the part boundaries fall.
94
- const maximumParts = 9000;
95
- const minimumPartSize = ( 10 * megabyte );
96
-
97
- const uploadPartSize = ( bytes ) => {
98
-
99
- const size = Number( bytes ) || 0;
100
- const required = Math.ceil( size / maximumParts );
101
- const rounded = Math.ceil( required / megabyte ) * megabyte;
102
-
103
- return Math.max( minimumPartSize, rounded );
104
-
105
- };
106
-
107
- // An empty upload is still one part — S3 has no zero-part upload.
108
- const uploadPartCount = ( bytes ) => {
109
-
110
- const size = Number( bytes ) || 0;
111
-
112
- return Math.max( 1, Math.ceil( size / uploadPartSize( size ) ) );
113
-
114
- };
115
-
116
- export { allowedMimes, allowedUploadTypes, isAllowedMime, maximumUploadBytes, resolveUploadType, uploadPartCount, uploadPartSize };
58
+ export { allowedMimes, allowedUploadTypes, isAllowedMime, resolveUploadType };
package/dist/upload.js CHANGED
@@ -10,11 +10,6 @@ var allowedUploadTypes = {
10
10
  webp: "image/webp"
11
11
  },
12
12
  video: {
13
- // iPhones record .mov (HEVC) by default, so this is the single most
14
- // likely video a merchant uploads. sync's storage.js already computes
15
- // needsReencode for hevc/h265 and re-encodes to h264, so allowing it
16
- // here is the only thing the pipeline was waiting on.
17
- mov: "video/quicktime",
18
13
  mp4: "video/mp4"
19
14
  }
20
15
  };
@@ -35,26 +30,9 @@ var resolveUploadType = (type, extension) => {
35
30
  return mime;
36
31
  };
37
32
  var isAllowedMime = (mime) => allowedMimes.has(mime);
38
- var megabyte = 1024 * 1024;
39
- var maximumUploadBytes = 2048 * megabyte;
40
- var maximumParts = 9e3;
41
- var minimumPartSize = 10 * megabyte;
42
- var uploadPartSize = (bytes) => {
43
- const size = Number(bytes) || 0;
44
- const required = Math.ceil(size / maximumParts);
45
- const rounded = Math.ceil(required / megabyte) * megabyte;
46
- return Math.max(minimumPartSize, rounded);
47
- };
48
- var uploadPartCount = (bytes) => {
49
- const size = Number(bytes) || 0;
50
- return Math.max(1, Math.ceil(size / uploadPartSize(size)));
51
- };
52
33
  export {
53
34
  allowedMimes,
54
35
  allowedUploadTypes,
55
36
  isAllowedMime,
56
- maximumUploadBytes,
57
- resolveUploadType,
58
- uploadPartCount,
59
- uploadPartSize
37
+ resolveUploadType
60
38
  };
package/package.json CHANGED
@@ -179,5 +179,5 @@
179
179
  "test": ". \"$HOME/.nvm/nvm.sh\" && nvm use && node --test"
180
180
  },
181
181
  "types": "dist/index.d.ts",
182
- "version": "0.0.99"
182
+ "version": "0.0.101"
183
183
  }