diff --git a/packages/cli-kit/src/cli/api/graphql/admin/generated/theme_files_upsert.ts b/packages/cli-kit/src/cli/api/graphql/admin/generated/theme_files_upsert.ts new file mode 100644 index 0000000000..f7bd095c8f --- /dev/null +++ b/packages/cli-kit/src/cli/api/graphql/admin/generated/theme_files_upsert.ts @@ -0,0 +1,98 @@ +/* eslint-disable @typescript-eslint/consistent-type-definitions */ +import * as Types from './types.js' + +import {TypedDocumentNode as DocumentNode} from '@graphql-typed-document-node/core' + +export type ThemeFilesUpsertMutationVariables = Types.Exact<{ + files: Types.OnlineStoreThemeFilesUpsertFileInput[] | Types.OnlineStoreThemeFilesUpsertFileInput + themeId: Types.Scalars['ID']['input'] +}> + +export type ThemeFilesUpsertMutation = { + themeFilesUpsert?: { + upsertedThemeFiles?: {filename: string}[] | null + userErrors: {filename?: string | null; message: string}[] + } | null +} + +export const ThemeFilesUpsert = { + kind: 'Document', + definitions: [ + { + kind: 'OperationDefinition', + operation: 'mutation', + name: {kind: 'Name', value: 'themeFilesUpsert'}, + variableDefinitions: [ + { + kind: 'VariableDefinition', + variable: {kind: 'Variable', name: {kind: 'Name', value: 'files'}}, + type: { + kind: 'NonNullType', + type: { + kind: 'ListType', + type: { + kind: 'NonNullType', + type: {kind: 'NamedType', name: {kind: 'Name', value: 'OnlineStoreThemeFilesUpsertFileInput'}}, + }, + }, + }, + }, + { + kind: 'VariableDefinition', + variable: {kind: 'Variable', name: {kind: 'Name', value: 'themeId'}}, + type: {kind: 'NonNullType', type: {kind: 'NamedType', name: {kind: 'Name', value: 'ID'}}}, + }, + ], + selectionSet: { + kind: 'SelectionSet', + selections: [ + { + kind: 'Field', + name: {kind: 'Name', value: 'themeFilesUpsert'}, + arguments: [ + { + kind: 'Argument', + name: {kind: 'Name', value: 'files'}, + value: {kind: 'Variable', name: {kind: 'Name', value: 'files'}}, + }, + { + kind: 'Argument', + name: {kind: 'Name', value: 'themeId'}, + value: {kind: 'Variable', name: {kind: 'Name', value: 'themeId'}}, + }, + ], + selectionSet: { + kind: 'SelectionSet', + selections: [ + { + kind: 'Field', + name: {kind: 'Name', value: 'upsertedThemeFiles'}, + selectionSet: { + kind: 'SelectionSet', + selections: [ + {kind: 'Field', name: {kind: 'Name', value: 'filename'}}, + {kind: 'Field', name: {kind: 'Name', value: '__typename'}}, + ], + }, + }, + { + kind: 'Field', + name: {kind: 'Name', value: 'userErrors'}, + selectionSet: { + kind: 'SelectionSet', + selections: [ + {kind: 'Field', name: {kind: 'Name', value: 'filename'}}, + {kind: 'Field', name: {kind: 'Name', value: 'message'}}, + {kind: 'Field', name: {kind: 'Name', value: '__typename'}}, + ], + }, + }, + {kind: 'Field', name: {kind: 'Name', value: '__typename'}}, + ], + }, + }, + ], + }, + }, + ], +} as unknown as DocumentNode diff --git a/packages/cli-kit/src/cli/api/graphql/admin/generated/types.d.ts b/packages/cli-kit/src/cli/api/graphql/admin/generated/types.d.ts index 910af7f783..65f693bfcd 100644 --- a/packages/cli-kit/src/cli/api/graphql/admin/generated/types.d.ts +++ b/packages/cli-kit/src/cli/api/graphql/admin/generated/types.d.ts @@ -120,6 +120,23 @@ export type Scalars = { UtcOffset: {input: any; output: any} } +/** The input fields for the theme file body. */ +export type OnlineStoreThemeFileBodyInput = { + /** The input type of the theme file body. */ + type: OnlineStoreThemeFileBodyInputType + /** The body of the theme file. */ + value: Scalars['String']['input'] +} + +/** The input type for a theme file body. */ +export type OnlineStoreThemeFileBodyInputType = + /** The base64 encoded body of a theme file. */ + | 'BASE64' + /** The text body of the theme file. */ + | 'TEXT' + /** The url of the body of a theme file. */ + | 'URL' + /** Type of a theme file operation result. */ export type OnlineStoreThemeFileResultType = /** Operation was malformed or invalid. */ @@ -137,6 +154,14 @@ export type OnlineStoreThemeFileResultType = /** Operation could not be processed due to issues with input data. */ | 'UNPROCESSABLE_ENTITY' +/** The input fields for the file to create or update. */ +export type OnlineStoreThemeFilesUpsertFileInput = { + /** The body of the theme file. */ + body: OnlineStoreThemeFileBodyInput + /** The filename of the theme file. */ + filename: Scalars['String']['input'] +} + /** The input fields for Theme attributes to update. */ export type OnlineStoreThemeInput = { /** The new name of the theme. */ diff --git a/packages/cli-kit/src/cli/api/graphql/admin/mutations/theme_files_upsert.graphql b/packages/cli-kit/src/cli/api/graphql/admin/mutations/theme_files_upsert.graphql new file mode 100644 index 0000000000..1106e4addc --- /dev/null +++ b/packages/cli-kit/src/cli/api/graphql/admin/mutations/theme_files_upsert.graphql @@ -0,0 +1,11 @@ +mutation themeFilesUpsert($files: [OnlineStoreThemeFilesUpsertFileInput!]!, $themeId: ID!) { + themeFilesUpsert(files: $files, themeId: $themeId) { + upsertedThemeFiles { + filename + } + userErrors { + filename + message + } + } +} diff --git a/packages/cli-kit/src/private/node/api.ts b/packages/cli-kit/src/private/node/api.ts index 2327fcafb7..92f966d9eb 100644 --- a/packages/cli-kit/src/private/node/api.ts +++ b/packages/cli-kit/src/private/node/api.ts @@ -126,7 +126,7 @@ function errorsIncludeStatus429(error: ClientError): boolean { } // GraphQL returns a 401 with a string error message when auth fails - // Therefore error.response.errros can be a string or GraphQLError[] + // Therefore error.response.errors can be a string or GraphQLError[] if (typeof error.response.errors === 'string') { return false } diff --git a/packages/cli-kit/src/public/node/themes/api.test.ts b/packages/cli-kit/src/public/node/themes/api.test.ts index abc6da3192..6f545378b8 100644 --- a/packages/cli-kit/src/public/node/themes/api.test.ts +++ b/packages/cli-kit/src/public/node/themes/api.test.ts @@ -10,11 +10,13 @@ import { AssetParams, deleteThemeAsset, } from './api.js' -import {RemoteBulkUploadResponse} from './factories.js' +import {Operation} from './types.js' import {ThemeDelete} from '../../../cli/api/graphql/admin/generated/theme_delete.js' import {ThemeUpdate} from '../../../cli/api/graphql/admin/generated/theme_update.js' import {ThemePublish} from '../../../cli/api/graphql/admin/generated/theme_publish.js' import {GetThemeFileChecksums} from '../../../cli/api/graphql/admin/generated/get_theme_file_checksums.js' +import {ThemeFilesUpsert} from '../../../cli/api/graphql/admin/generated/theme_files_upsert.js' +import {OnlineStoreThemeFileBodyInputType} from '../../../cli/api/graphql/admin/generated/types.js' import {test, vi, expect, describe} from 'vitest' import {adminRequestDoc, restRequest, supportedApiVersions} from '@shopify/cli-kit/node/api/admin' import {AbortError} from '@shopify/cli-kit/node/error' @@ -112,19 +114,18 @@ describe('createTheme', () => { test('creates a theme', async () => { // Given - vi.mocked(restRequest) - .mockResolvedValueOnce({ - json: {theme: {id, name, role, processing}}, - status: 200, - headers: {}, - }) - .mockResolvedValueOnce({ - json: { - results: [], - }, - status: 207, - headers: {}, - }) + vi.mocked(restRequest).mockResolvedValueOnce({ + json: {theme: {id, name, role, processing}}, + status: 200, + headers: {}, + }) + + vi.mocked(adminRequestDoc).mockResolvedValue({ + themeFilesUpsert: { + upsertedThemeFiles: [], + userErrors: [], + }, + }) // When const theme = await createTheme(params, session) @@ -397,120 +398,131 @@ describe('request errors', () => { }) }) +// to fix describe('bulkUploadThemeAssets', async () => { - test('uploads multiple assets', async () => { + test('uploads multiple TEXT and BASE64 assets', async () => { const id = 123 const assets: AssetParams[] = [ {key: 'snippets/product-variant-picker.liquid', value: 'content'}, - {key: 'templates/404.json', value: 'to_be_replaced_with_hash'}, - ] - - const mockResults: RemoteBulkUploadResponse[] = [ - { - code: 200, - body: { - asset: { - key: 'assets/test.liquid', - checksum: '3f26c8569292ce6f1cc991c5fa7d3fcb', - attachment: '', - value: '', - }, - }, - }, { - code: 400, - body: { - errors: {asset: ['expected Hash to be a String']}, - }, + key: 'templates/404.json', + value: 'to_be_replaced_with_hash', }, ] - vi.mocked(restRequest).mockResolvedValue({ - json: {results: mockResults}, - status: 207, - headers: {}, + vi.mocked(adminRequestDoc).mockResolvedValue({ + themeFilesUpsert: { + upsertedThemeFiles: [{filename: 'snippets/product-variant-picker.liquid'}, {filename: 'templates/404.json'}], + userErrors: [], + }, }) // When const bulkUploadresults = await bulkUploadThemeAssets(id, assets, session) // Then - expect(restRequest).toHaveBeenCalledWith( - 'PUT', - `/themes/${id}/assets/bulk`, - session, + expect(adminRequestDoc).toHaveBeenCalledWith(ThemeFilesUpsert, session, { + themeId: `gid://shopify/OnlineStoreTheme/${id}`, + files: [ + { + filename: 'snippets/product-variant-picker.liquid', + body: {value: 'content', type: 'TEXT' as OnlineStoreThemeFileBodyInputType}, + }, + { + filename: 'templates/404.json', + body: {value: 'to_be_replaced_with_hash', type: 'TEXT' as OnlineStoreThemeFileBodyInputType}, + }, + ], + }) + + expect(bulkUploadresults).toEqual([ { - assets: [ - {key: 'snippets/product-variant-picker.liquid', value: 'content'}, - {key: 'templates/404.json', value: 'to_be_replaced_with_hash'}, - ], + key: 'snippets/product-variant-picker.liquid', + success: true, + operation: Operation.Upload, }, - {}, - ) - expect(bulkUploadresults).toHaveLength(2) - expect(bulkUploadresults[0]).toEqual({ - key: 'snippets/product-variant-picker.liquid', - success: true, - errors: {}, - operation: 'UPLOAD', - asset: { - attachment: '', - key: 'assets/test.liquid', - checksum: '3f26c8569292ce6f1cc991c5fa7d3fcb', - value: '', + { + key: 'templates/404.json', + success: true, + operation: Operation.Upload, }, - }) - expect(bulkUploadresults[1]).toEqual({ - key: 'templates/404.json', - operation: 'UPLOAD', - success: false, - errors: {asset: ['expected Hash to be a String']}, - asset: undefined, - }) + ]) }) - test('throws an error when the server responds with a 404', async () => { + test('throws an error when returns userErrors with filenames', async () => { const id = 123 const assets: AssetParams[] = [ {key: 'snippets/product-variant-picker.liquid', value: 'content'}, - {key: 'templates/404.json', value: 'to_be_replaced_with_hash'}, + { + key: 'templates/404.json', + value: 'to_be_replaced_with_hash', + }, ] - vi.mocked(restRequest).mockResolvedValue({ - json: {}, - status: 404, - headers: {}, + vi.mocked(adminRequestDoc).mockResolvedValue({ + themeFilesUpsert: { + upsertedThemeFiles: [], + userErrors: [ + {filename: 'snippets/product-variant-picker.liquid', message: 'Something went wrong'}, + {filename: 'templates/404.json', message: 'Something went wrong'}, + ], + }, }) // When - await expect(async () => { - return bulkUploadThemeAssets(id, assets, session) - // Then - }).rejects.toThrowError(AbortError) + const bulkUploadresults = await bulkUploadThemeAssets(id, assets, session) + + // Then + expect(adminRequestDoc).toHaveBeenCalledWith(ThemeFilesUpsert, session, { + themeId: `gid://shopify/OnlineStoreTheme/${id}`, + files: [ + { + filename: 'snippets/product-variant-picker.liquid', + body: {value: 'content', type: 'TEXT' as OnlineStoreThemeFileBodyInputType}, + }, + { + filename: 'templates/404.json', + body: {value: 'to_be_replaced_with_hash', type: 'TEXT' as OnlineStoreThemeFileBodyInputType}, + }, + ], + }) + + expect(bulkUploadresults).toEqual([ + { + key: 'snippets/product-variant-picker.liquid', + success: false, + operation: Operation.Upload, + errors: {asset: ['Something went wrong']}, + }, + { + key: 'templates/404.json', + success: false, + operation: Operation.Upload, + errors: {asset: ['Something went wrong']}, + }, + ]) }) - test('throws an error when the server responds with a 403', async () => { - // Given + test('throws an error when returns userErrors with no filename', async () => { const id = 123 const assets: AssetParams[] = [ {key: 'snippets/product-variant-picker.liquid', value: 'content'}, - {key: 'templates/404.json', value: 'to_be_replaced_with_hash'}, + { + key: 'templates/404.json', + value: 'to_be_replaced_with_hash', + }, ] - const message = `Cannot delete generated asset 'assets/bla.css'. Delete 'assets/bla.css.liquid' instead.` - vi.mocked(restRequest).mockResolvedValue({ - json: { - message, + vi.mocked(adminRequestDoc).mockResolvedValue({ + themeFilesUpsert: { + upsertedThemeFiles: [], + userErrors: [{message: 'Something went wrong'}], }, - status: 403, - headers: {}, }) - // When - await expect(async () => { - return bulkUploadThemeAssets(id, assets, session) - - // Then - }).rejects.toThrowError(new AbortError(message)) + await expect(bulkUploadThemeAssets(id, assets, session)).rejects.toThrow(AbortError) + await expect(bulkUploadThemeAssets(id, assets, session)).rejects.toThrow( + 'Error uploading theme files: Something went wrong', + ) }) }) diff --git a/packages/cli-kit/src/public/node/themes/api.ts b/packages/cli-kit/src/public/node/themes/api.ts index 61a6fc4d18..2396666781 100644 --- a/packages/cli-kit/src/public/node/themes/api.ts +++ b/packages/cli-kit/src/public/node/themes/api.ts @@ -6,11 +6,19 @@ import {ThemeDelete} from '../../../cli/api/graphql/admin/generated/theme_delete import {ThemePublish} from '../../../cli/api/graphql/admin/generated/theme_publish.js' import {GetThemeFileBodies} from '../../../cli/api/graphql/admin/generated/get_theme_file_bodies.js' import {GetThemeFileChecksums} from '../../../cli/api/graphql/admin/generated/get_theme_file_checksums.js' +import { + ThemeFilesUpsert, + ThemeFilesUpsertMutation, +} from '../../../cli/api/graphql/admin/generated/theme_files_upsert.js' +import { + OnlineStoreThemeFileBodyInputType, + OnlineStoreThemeFilesUpsertFileInput, +} from '../../../cli/api/graphql/admin/generated/types.js' import {restRequest, RestResponse, adminRequestDoc} from '@shopify/cli-kit/node/api/admin' import {AdminSession} from '@shopify/cli-kit/node/session' import {AbortError} from '@shopify/cli-kit/node/error' -import {buildBulkUploadResults, buildTheme} from '@shopify/cli-kit/node/themes/factories' -import {Result, Checksum, Key, Theme, ThemeAsset} from '@shopify/cli-kit/node/themes/types' +import {buildTheme} from '@shopify/cli-kit/node/themes/factories' +import {Result, Checksum, Key, Theme, ThemeAsset, Operation} from '@shopify/cli-kit/node/themes/types' import {outputDebug} from '@shopify/cli-kit/node/output' import {sleep} from '@shopify/cli-kit/node/system' @@ -59,7 +67,7 @@ export async function fetchThemeAssets(id: number, filenames: Key[], session: Ad if (!response.theme?.files?.nodes || !response.theme?.files?.pageInfo) { const userErrors = response.theme?.files?.userErrors.map((error) => error.filename).join(', ') - throw new AbortError(`Error fetching assets: ${userErrors}`) + unexpectedGraphQLError(`Error fetching assets: ${userErrors}`) } const {nodes, pageInfo} = response.theme.files @@ -98,11 +106,81 @@ export async function bulkUploadThemeAssets( assets: AssetParams[], session: AdminSession, ): Promise { - const response = await request('PUT', `/themes/${id}/assets/bulk`, session, {assets}) - if (response.status !== 207) { - throw new AbortError('Upload failed, could not reach the server') + const results: Result[] = [] + for (let i = 0; i < assets.length; i += 50) { + const chunk = assets.slice(i, i + 50) + const files = prepareFilesForUpload(chunk) + // eslint-disable-next-line no-await-in-loop + const uploadResults = await uploadFiles(id, files, session) + results.push(...processUploadResults(uploadResults)) + } + return results +} + +function prepareFilesForUpload(assets: AssetParams[]): OnlineStoreThemeFilesUpsertFileInput[] { + return assets.map((asset) => { + if (asset.value) { + return { + filename: asset.key, + body: { + type: 'TEXT' as const, + value: asset.value, + }, + } + } else if (asset.attachment) { + return { + filename: asset.key, + body: { + type: 'BASE64' as const, + value: asset.attachment, + }, + } + } else { + unexpectedGraphQLError('Asset must have a value or attachment') + } + }) +} + +async function uploadFiles( + themeId: number, + files: {filename: string; body: {type: OnlineStoreThemeFileBodyInputType; value: string}}[], + session: AdminSession, +): Promise { + return adminRequestDoc(ThemeFilesUpsert, session, {themeId: themeGid(themeId), files}) +} + +function processUploadResults(uploadResults: ThemeFilesUpsertMutation): Result[] { + const {themeFilesUpsert} = uploadResults + + if (!themeFilesUpsert) { + unexpectedGraphQLError('Failed to upload theme files') } - return buildBulkUploadResults(response.json.results, assets) + + const {upsertedThemeFiles, userErrors} = themeFilesUpsert + + const results: Result[] = [] + + upsertedThemeFiles?.forEach((file) => { + results.push({ + key: file.filename, + success: true, + operation: Operation.Upload, + }) + }) + + userErrors.forEach((error) => { + if (!error.filename) { + unexpectedGraphQLError(`Error uploading theme files: ${error.message}`) + } + results.push({ + key: error.filename, + success: false, + operation: Operation.Upload, + errors: {asset: [error.message]}, + }) + }) + + return results } export async function fetchChecksums(id: number, session: AdminSession): Promise { @@ -354,10 +432,8 @@ async function parseThemeFileContent(body: OnlineStoreThemeFileBody): Promise { - return { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - key: assets[index]!.key, - success: bulkUpload.code === 200, - errors: bulkUpload.body.errors || {}, - asset: bulkUpload.body.asset, - operation: Operation.Upload, - } - }) -} diff --git a/packages/theme/src/cli/utilities/theme-uploader.test.ts b/packages/theme/src/cli/utilities/theme-uploader.test.ts index 1d09f7f3db..e7938b58cc 100644 --- a/packages/theme/src/cli/utilities/theme-uploader.test.ts +++ b/packages/theme/src/cli/utilities/theme-uploader.test.ts @@ -61,7 +61,7 @@ describe('theme-uploader', () => { ) // When - const {renderThemeSyncProgress} = await uploadTheme(remoteTheme, adminSession, remote, local, uploadOptions) + const {renderThemeSyncProgress} = uploadTheme(remoteTheme, adminSession, remote, local, uploadOptions) await renderThemeSyncProgress() // Then @@ -82,7 +82,7 @@ describe('theme-uploader', () => { ) // When - const {renderThemeSyncProgress} = await uploadTheme(remoteTheme, adminSession, remote, local, uploadOptions) + const {renderThemeSyncProgress} = uploadTheme(remoteTheme, adminSession, remote, local, uploadOptions) await renderThemeSyncProgress() // Then @@ -103,7 +103,7 @@ describe('theme-uploader', () => { ) // When - const {renderThemeSyncProgress} = await uploadTheme(remoteTheme, adminSession, remote, local, { + const {renderThemeSyncProgress} = uploadTheme(remoteTheme, adminSession, remote, local, { ...uploadOptions, nodelete: true, }) @@ -121,7 +121,7 @@ describe('theme-uploader', () => { const themeFileSystem = fakeThemeFileSystem('tmp', new Map([])) // When - const {renderThemeSyncProgress} = await uploadTheme( + const {renderThemeSyncProgress} = uploadTheme( remoteTheme, adminSession, remoteChecksums, @@ -142,7 +142,7 @@ describe('theme-uploader', () => { const themeFileSystem = fakeThemeFileSystem('tmp', new Map([])) // When - const {renderThemeSyncProgress} = await uploadTheme( + const {renderThemeSyncProgress} = uploadTheme( remoteTheme, adminSession, remoteChecksums, @@ -160,7 +160,7 @@ describe('theme-uploader', () => { const themeFileSystem = fakeThemeFileSystem('tmp', new Map([])) // When - const {renderThemeSyncProgress} = await uploadTheme(remoteTheme, adminSession, [], themeFileSystem, uploadOptions) + const {renderThemeSyncProgress} = uploadTheme(remoteTheme, adminSession, [], themeFileSystem, uploadOptions) await renderThemeSyncProgress() // Then @@ -180,7 +180,7 @@ describe('theme-uploader', () => { ) // When - const {renderThemeSyncProgress} = await uploadTheme( + const {renderThemeSyncProgress} = uploadTheme( remoteTheme, adminSession, remoteChecksums, @@ -220,7 +220,7 @@ describe('theme-uploader', () => { ) // When - const {renderThemeSyncProgress} = await uploadTheme( + const {renderThemeSyncProgress} = uploadTheme( remoteTheme, adminSession, remoteChecksums, @@ -256,7 +256,7 @@ describe('theme-uploader', () => { const themeFileSystem = fakeThemeFileSystem('tmp', new Map([])) // When - const {renderThemeSyncProgress} = await uploadTheme( + const {renderThemeSyncProgress} = uploadTheme( remoteTheme, adminSession, remoteChecksums, @@ -300,7 +300,7 @@ describe('theme-uploader', () => { ) // When - const {renderThemeSyncProgress} = await uploadTheme( + const {renderThemeSyncProgress} = uploadTheme( remoteTheme, adminSession, remoteChecksums, @@ -401,7 +401,7 @@ describe('theme-uploader', () => { const themeFileSystem = fakeThemeFileSystem('tmp', files) // When - const {renderThemeSyncProgress} = await uploadTheme( + const {renderThemeSyncProgress} = uploadTheme( remoteTheme, adminSession, remoteChecksums, @@ -434,7 +434,7 @@ describe('theme-uploader', () => { ) // When - const {renderThemeSyncProgress} = await uploadTheme( + const {renderThemeSyncProgress} = uploadTheme( remoteTheme, adminSession, remoteChecksums, @@ -486,7 +486,7 @@ describe('theme-uploader', () => { ]) // When - const {renderThemeSyncProgress} = await uploadTheme( + const {renderThemeSyncProgress} = uploadTheme( remoteTheme, adminSession, remoteChecksums, @@ -543,7 +543,7 @@ describe('theme-uploader', () => { ) // When - const {renderThemeSyncProgress} = await uploadTheme(remoteTheme, adminSession, remote, local, uploadOptions) + const {renderThemeSyncProgress} = uploadTheme(remoteTheme, adminSession, remote, local, uploadOptions) await renderThemeSyncProgress() // Then @@ -576,7 +576,7 @@ describe('theme-uploader', () => { ) // When - const {renderThemeSyncProgress} = await uploadTheme(remoteTheme, adminSession, remote, localTheme, uploadOptions) + const {renderThemeSyncProgress} = uploadTheme(remoteTheme, adminSession, remote, localTheme, uploadOptions) await renderThemeSyncProgress() // Then diff --git a/packages/theme/src/cli/utilities/theme-uploader.ts b/packages/theme/src/cli/utilities/theme-uploader.ts index d720bd6f13..c5370c5d24 100644 --- a/packages/theme/src/cli/utilities/theme-uploader.ts +++ b/packages/theme/src/cli/utilities/theme-uploader.ts @@ -18,9 +18,9 @@ type ChecksumWithSize = Checksum & {size: number} type FileBatch = ChecksumWithSize[] // Limits for Bulk Requests -export const MAX_BATCH_FILE_COUNT = 10 -// 100KB -export const MAX_BATCH_BYTESIZE = 102400 +export const MAX_BATCH_FILE_COUNT = 50 +// 10MB +export const MAX_BATCH_BYTESIZE = 1024 * 1024 * 10 export const MAX_UPLOAD_RETRY_COUNT = 2 export function uploadTheme( @@ -176,8 +176,14 @@ function orderFilesToBeDeleted(files: Checksum[]): Checksum[] { export const MINIMUM_THEME_ASSETS = [ {key: 'config/settings_schema.json', value: '[]'}, - {key: 'layout/password.liquid', value: '{{ content_for_header }}{{ content_for_layout }}'}, - {key: 'layout/theme.liquid', value: '{{ content_for_header }}{{ content_for_layout }}'}, + { + key: 'layout/password.liquid', + value: '{{ content_for_header }}{{ content_for_layout }}', + }, + { + key: 'layout/theme.liquid', + value: '{{ content_for_header }}{{ content_for_layout }}', + }, ] as const /** * If there's no theme in the remote, we need to create it first so that @@ -235,7 +241,7 @@ function buildUploadJob( const independentFilesUploadPromise = Promise.resolve().then(() => uploadFileBatches(independentFiles.flat())) const promise = Promise.all([dependentFilesUploadPromise, independentFilesUploadPromise]).then(() => { - progress.current += progress.total + progress.current = progress.total }) return {progress, promise}