@devicecloud.dev/dcd 4.1.5 → 4.1.7

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.
@@ -15,6 +15,7 @@ const results_polling_service_1 = require("../services/results-polling.service")
15
15
  const test_submission_service_1 = require("../services/test-submission.service");
16
16
  const version_service_1 = require("../services/version.service");
17
17
  const compatibility_1 = require("../utils/compatibility");
18
+ const styling_1 = require("../utils/styling");
18
19
  // Suppress punycode deprecation warning (caused by whatwg, supabase dependancy)
19
20
  process.removeAllListeners('warning');
20
21
  process.on('warning', (warning) => {
@@ -71,12 +72,11 @@ class Cloud extends core_1.Command {
71
72
  const latestVersion = await this.versionService.checkLatestCliVersion();
72
73
  if (latestVersion &&
73
74
  this.versionService.isOutdated(this.config.version, latestVersion)) {
74
- this.log(`
75
- -------------------
76
- A new version of the devicecloud.dev CLI is available: ${latestVersion}
77
- Run 'npm install -g @devicecloud.dev/dcd@latest' to update to the latest version
78
- -------------------
79
- `);
75
+ this.log(`\n${styling_1.dividers.light}\n` +
76
+ `${styling_1.colors.warning('⚠')} ${styling_1.colors.bold('Update Available')}\n` +
77
+ styling_1.colors.dim(` A new version of the DeviceCloud CLI is available: `) + styling_1.colors.highlight(latestVersion) + `\n` +
78
+ styling_1.colors.dim(` Run: `) + styling_1.colors.info(`npm install -g @devicecloud.dev/dcd@latest`) + `\n` +
79
+ `${styling_1.dividers.light}\n`);
80
80
  }
81
81
  };
82
82
  /** Service for CLI version checking */
@@ -165,18 +165,18 @@ class Cloud extends core_1.Command {
165
165
  logger: this.log.bind(this),
166
166
  });
167
167
  if (retry && retry > 2) {
168
- this.log("Retries are now free of charge but limited to 2. If you're test is still failing after 2 retries, please ask for help on Discord.");
168
+ this.log(styling_1.colors.warning('⚠') + ' ' + styling_1.colors.dim("Retries are now free of charge but limited to 2. If your test is still failing after 2 retries, please ask for help on Discord."));
169
169
  flags.retry = 2;
170
170
  retry = 2;
171
171
  }
172
172
  if (runnerType === 'm4') {
173
- this.log('Note: runnerType m4 is experimental and currently supports iOS only, Android will revert to default.');
173
+ this.log(styling_1.colors.info('ℹ') + ' ' + styling_1.colors.dim('Note: runnerType m4 is experimental and currently supports iOS only, Android will revert to default.'));
174
174
  }
175
175
  if (runnerType === 'm1') {
176
- this.log('Note: runnerType m1 is experimental and currently supports Android (Pixel 7, API Level 34) only.');
176
+ this.log(styling_1.colors.info('ℹ') + ' ' + styling_1.colors.dim('Note: runnerType m1 is experimental and currently supports Android (Pixel 7, API Level 34) only.'));
177
177
  }
178
178
  if (runnerType === 'gpu1') {
179
- this.log('Note: runnerType gpu1 is Android-only and requires contacting support to enable. Without support enablement, your runner type will revert to default.');
179
+ this.log(styling_1.colors.info('ℹ') + ' ' + styling_1.colors.dim('Note: runnerType gpu1 is Android-only and requires contacting support to enable. Without support enablement, your runner type will revert to default.'));
180
180
  }
181
181
  const { firstFile, secondFile } = args;
182
182
  let finalBinaryId = appBinaryId;
@@ -290,41 +290,44 @@ class Cloud extends core_1.Command {
290
290
  const hasOverrides = overridesEntries.some(([, overrides]) => Object.keys(overrides).length > 0);
291
291
  let overridesLog = '';
292
292
  if (hasOverrides) {
293
- overridesLog = '\n With overrides';
293
+ overridesLog = '\n\n ' + styling_1.colors.bold('With overrides');
294
294
  for (const [flowPath, overrides] of overridesEntries) {
295
295
  if (Object.keys(overrides).length > 0) {
296
296
  const relativePath = flowPath.replace(process.cwd(), '.');
297
- overridesLog += `\n → ${relativePath}:`;
297
+ overridesLog += `\n ${styling_1.colors.dim('')} ${relativePath}:`;
298
298
  for (const [key, value] of Object.entries(overrides)) {
299
- overridesLog += `\n ${key}: ${value}`;
299
+ overridesLog += `\n ${styling_1.colors.dim(key + ':')} ${styling_1.colors.highlight(value)}`;
300
300
  }
301
301
  }
302
302
  }
303
- overridesLog += '\n';
304
- }
305
- this.log(`
306
-
307
- Submitting new job
308
- → Flow(s): ${flowFile}
309
- App: ${appBinaryId || finalAppFile}
310
-
311
- With options
312
- → ${flagLogs.join(`
313
- → `)}${overridesLog}
314
-
315
- `);
303
+ }
304
+ this.log(`\n${(0, styling_1.sectionHeader)('Submitting new job')}`);
305
+ this.log(` ${styling_1.colors.dim('→ Flow(s):')} ${styling_1.colors.highlight(flowFile)}`);
306
+ this.log(` ${styling_1.colors.dim('→ App:')} ${styling_1.colors.highlight(appBinaryId || finalAppFile)}`);
307
+ if (flagLogs.length > 0) {
308
+ this.log(`\n ${styling_1.colors.bold('With options')}`);
309
+ for (const flagLog of flagLogs) {
310
+ const [key, ...valueParts] = flagLog.split(': ');
311
+ const value = valueParts.join(': ');
312
+ this.log(` ${styling_1.colors.dim('' + key + ':')} ${styling_1.colors.highlight(value)}`);
313
+ }
314
+ }
315
+ if (hasOverrides) {
316
+ this.log(overridesLog);
317
+ }
318
+ this.log('');
316
319
  if (dryRun) {
317
- this.log('\nDry run mode - no tests were actually triggered\n');
318
- this.log('The following tests would have been run:');
319
- this.log('─────────────────────────────────────────');
320
+ this.log(`\n${styling_1.colors.warning('⚠')} ${styling_1.colors.bold('Dry run mode')} ${styling_1.colors.dim('- no tests were actually triggered')}\n`);
321
+ this.log(styling_1.colors.bold('The following tests would have been run:'));
322
+ this.log(styling_1.dividers.light);
320
323
  for (const test of testFileNames) {
321
- this.log(`• ${test}`);
324
+ this.log((0, styling_1.listItem)(test));
322
325
  }
323
326
  if (sequentialFlows.length > 0) {
324
- this.log('\nSequential flows:');
325
- this.log('────────────────');
327
+ this.log(`\n${styling_1.colors.bold('Sequential flows:')}`);
328
+ this.log(styling_1.dividers.short);
326
329
  for (const flow of sequentialFlows) {
327
- this.log(`• ${flow}`);
330
+ this.log((0, styling_1.listItem)(flow));
328
331
  }
329
332
  }
330
333
  this.log('\n');
@@ -336,7 +339,7 @@ class Cloud extends core_1.Command {
336
339
  if (debug) {
337
340
  this.log(`DEBUG: Uploading binary file: ${finalAppFile}`);
338
341
  }
339
- const binaryId = await (0, methods_1.uploadBinary)(finalAppFile, apiUrl, apiKey, ignoreShaCheck, !json);
342
+ const binaryId = await (0, methods_1.uploadBinary)(finalAppFile, apiUrl, apiKey, ignoreShaCheck, !json, debug);
340
343
  finalBinaryId = binaryId;
341
344
  if (debug) {
342
345
  this.log(`DEBUG: Binary uploaded with ID: ${binaryId}`);
@@ -386,17 +389,18 @@ class Cloud extends core_1.Command {
386
389
  }
387
390
  if (!results?.length)
388
391
  (0, errors_1.error)('No tests created: ' + message);
389
- this.log(message);
390
- this.log(`\nCreated ${results.length} tests: ${results
392
+ this.log(styling_1.colors.success('✓') + ' ' + styling_1.colors.dim(message));
393
+ const testNames = results
391
394
  .map((r) => r.test_file_name)
392
395
  .sort((a, b) => a.localeCompare(b))
393
- .join(', ')}\n`);
394
- this.log('Run triggered, you can access the results at:');
395
- const url = `https://console.devicecloud.dev/results?upload=${results[0].test_upload_id}&result=${results[0].id}`;
396
- this.log(url);
397
- this.log(`\n`);
398
- this.log(`Your upload ID is: ${results[0].test_upload_id}`);
399
- this.log(`Poll upload status using: dcd status --api-key ... --upload-id ${results[0].test_upload_id}`);
396
+ .join(styling_1.colors.dim(', '));
397
+ this.log(`\n${styling_1.colors.bold(`Created ${results.length} test${results.length === 1 ? '' : 's'}:`)} ${testNames}\n`);
398
+ const url = (0, styling_1.getConsoleUrl)(apiUrl, results[0].test_upload_id, results[0].id);
399
+ this.log(styling_1.colors.bold('Run triggered') + styling_1.colors.dim(', you can access the results at:'));
400
+ this.log((0, styling_1.formatUrl)(url));
401
+ this.log(``);
402
+ this.log(styling_1.colors.dim('Your upload ID is: ') + (0, styling_1.formatId)(results[0].test_upload_id));
403
+ this.log(styling_1.colors.dim('Poll upload status using: ') + styling_1.colors.info(`dcd status --api-key ... --upload-id ${results[0].test_upload_id}`));
400
404
  if (async) {
401
405
  if (debug) {
402
406
  this.log(`DEBUG: Async flag is set, not waiting for results`);
@@ -417,7 +421,7 @@ class Cloud extends core_1.Command {
417
421
  if (json) {
418
422
  return jsonOutput;
419
423
  }
420
- this.log('Not waiting for results as async flag is set to true');
424
+ this.log(`\n${styling_1.colors.info('ℹ')} ${styling_1.colors.dim('Not waiting for results as async flag is set to true')}\n`);
421
425
  return;
422
426
  }
423
427
  // Poll for results until completion
@@ -31,6 +31,5 @@ export default class Status extends Command {
31
31
  'upload-id': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
32
32
  };
33
33
  run(): Promise<StatusResponse | void>;
34
- private getStatusSymbol;
35
34
  }
36
35
  export {};
@@ -5,6 +5,7 @@ const constants_1 = require("../constants");
5
5
  const api_gateway_1 = require("../gateways/api-gateway");
6
6
  const methods_1 = require("../methods");
7
7
  const connectivity_1 = require("../utils/connectivity");
8
+ const styling_1 = require("../utils/styling");
8
9
  class Status extends core_1.Command {
9
10
  static description = 'Get the status of an upload by name or upload ID';
10
11
  static enableJsonFlag = true;
@@ -98,32 +99,27 @@ class Status extends core_1.Command {
98
99
  if (json) {
99
100
  return status;
100
101
  }
101
- this.log('\n📊 Upload Status');
102
- this.log('═'.repeat(80));
102
+ this.log((0, styling_1.sectionHeader)('Upload Status'));
103
103
  // Display overall status
104
- const overallSymbol = this.getStatusSymbol(status.status);
105
- this.log(`${overallSymbol} Status: ${status.status}`);
104
+ this.log(` ${(0, styling_1.formatStatus)(status.status)}`);
106
105
  if (status.uploadId) {
107
- this.log(`🆔 Upload ID: ${status.uploadId}`);
106
+ this.log(` ${styling_1.colors.dim('Upload ID:')} ${(0, styling_1.formatId)(status.uploadId)}`);
108
107
  }
109
108
  if (status.appBinaryId) {
110
- this.log(`📱 Binary ID: ${status.appBinaryId}`);
109
+ this.log(` ${styling_1.colors.dim('Binary ID:')} ${(0, styling_1.formatId)(status.appBinaryId)}`);
111
110
  }
112
111
  if (status.consoleUrl) {
113
- this.log(`🔗 Console: ${status.consoleUrl}`);
112
+ this.log(` ${styling_1.colors.dim('Console:')} ${(0, styling_1.formatUrl)(status.consoleUrl)}`);
114
113
  }
115
114
  if (status.tests.length > 0) {
116
- this.log('\n📋 Test Results');
117
- this.log('─'.repeat(80));
115
+ this.log((0, styling_1.sectionHeader)('Test Results'));
118
116
  for (const item of status.tests) {
119
- const symbol = this.getStatusSymbol(item.status);
120
- this.log(`${symbol} ${item.name}`);
121
- this.log(` Status: ${item.status}`);
117
+ this.log(` ${(0, styling_1.formatStatus)(item.status)} ${styling_1.colors.bold(item.name)}`);
122
118
  if (item.status === 'FAILED' && item.failReason) {
123
- this.log(` Fail reason: ${item.failReason}`);
119
+ this.log(` ${styling_1.colors.error('Fail reason:')} ${item.failReason}`);
124
120
  }
125
121
  if (item.durationSeconds) {
126
- this.log(` Duration: ${(0, methods_1.formatDurationSeconds)(item.durationSeconds)}`);
122
+ this.log(` ${styling_1.colors.dim('Duration:')} ${(0, methods_1.formatDurationSeconds)(item.durationSeconds)}`);
127
123
  }
128
124
  this.log('');
129
125
  }
@@ -133,24 +129,5 @@ class Status extends core_1.Command {
133
129
  this.error(`Failed to get status: ${error.message}`);
134
130
  }
135
131
  }
136
- getStatusSymbol(status) {
137
- switch (status) {
138
- case 'PASSED': {
139
- return '✓';
140
- }
141
- case 'FAILED': {
142
- return '✗';
143
- }
144
- case 'CANCELLED': {
145
- return '⊘';
146
- }
147
- case 'PENDING': {
148
- return '⋯';
149
- }
150
- default: {
151
- return '?';
152
- }
153
- }
154
- }
155
132
  }
156
133
  exports.default = Status;
@@ -9,6 +9,7 @@ export default class Upload extends Command {
9
9
  static flags: {
10
10
  apiKey: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
11
11
  apiUrl: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
12
+ debug: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
13
  'ignore-sha-check': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
13
14
  };
14
15
  run(): Promise<{
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const constants_1 = require("../constants");
5
5
  const methods_1 = require("../methods");
6
+ const styling_1 = require("../utils/styling");
6
7
  class Upload extends core_1.Command {
7
8
  static args = {
8
9
  appFile: core_1.Args.string({
@@ -20,13 +21,14 @@ class Upload extends core_1.Command {
20
21
  static flags = {
21
22
  apiKey: constants_1.flags.apiKey,
22
23
  apiUrl: constants_1.flags.apiUrl,
24
+ debug: constants_1.flags.debug,
23
25
  'ignore-sha-check': constants_1.flags['ignore-sha-check'],
24
26
  };
25
27
  async run() {
26
28
  try {
27
29
  const { args, flags } = await this.parse(Upload);
28
30
  const { appFile } = args;
29
- const { apiKey: apiKeyFlag, apiUrl, 'ignore-sha-check': ignoreShaCheck, json, } = flags;
31
+ const { apiKey: apiKeyFlag, apiUrl, debug, 'ignore-sha-check': ignoreShaCheck, json, } = flags;
30
32
  const apiKey = apiKeyFlag || process.env.DEVICE_CLOUD_API_KEY;
31
33
  if (!apiKey) {
32
34
  throw new Error('You must provide an API key via --api-key flag or DEVICE_CLOUD_API_KEY environment variable');
@@ -37,17 +39,17 @@ class Upload extends core_1.Command {
37
39
  if (appFile.endsWith('.zip')) {
38
40
  await (0, methods_1.verifyAppZip)(appFile);
39
41
  }
40
- this.log(`
41
- Uploading app binary
42
- → File: ${appFile}
43
- `);
44
- const appBinaryId = await (0, methods_1.uploadBinary)(appFile, apiUrl, apiKey, ignoreShaCheck, !json);
42
+ this.log((0, styling_1.sectionHeader)('Uploading app binary'));
43
+ this.log(` ${styling_1.colors.dim('→ File:')} ${styling_1.colors.highlight(appFile)}`);
44
+ this.log('');
45
+ const appBinaryId = await (0, methods_1.uploadBinary)(appFile, apiUrl, apiKey, ignoreShaCheck, !json, debug);
45
46
  if (json) {
46
47
  return { appBinaryId };
47
48
  }
48
- this.log(`\nUpload complete. Binary ID: ${appBinaryId}\n`);
49
- this.log(`You can use this Binary ID in subsequent test runs with:`);
50
- this.log(`dcd cloud --app-binary-id ${appBinaryId} path/to/flow.yaml\n`);
49
+ this.log(`\n${styling_1.colors.success('✓')} ${styling_1.colors.bold('Upload complete')}`);
50
+ this.log(` ${styling_1.colors.dim('Binary ID:')} ${(0, styling_1.formatId)(appBinaryId)}\n`);
51
+ this.log(styling_1.colors.dim('You can use this Binary ID in subsequent test runs with:'));
52
+ this.log(styling_1.colors.info(`dcd cloud --app-binary-id ${appBinaryId} path/to/flow.yaml\n`));
51
53
  }
52
54
  catch (error) {
53
55
  this.error(error, { exit: 1 });
@@ -44,7 +44,7 @@ exports.executionFlags = {
44
44
  }),
45
45
  'runner-type': core_1.Flags.string({
46
46
  default: 'default',
47
- description: '[experimental] The type of runner to use - note: anything other than default will incur premium pricing tiers, see https://docs.devicecloud.dev/reference/runner-type for more information. gpu1 and cpu1 are Android-only and require contacting support to enable, otherwise reverts to default.',
47
+ description: '[experimental] The type of runner to use - note: anything other than default or cpu1 will incur premium pricing tiers, see https://docs.devicecloud.dev/reference/runner-type for more information.',
48
48
  options: ['default', 'm4', 'm1', 'gpu1', 'cpu1'],
49
49
  }),
50
50
  };
@@ -12,13 +12,29 @@ export declare const ApiGateway: {
12
12
  exists: boolean;
13
13
  }>;
14
14
  downloadArtifactsZip(baseUrl: string, apiKey: string, uploadId: string, results: "ALL" | "FAILED", artifactsPath?: string): Promise<void>;
15
- finaliseUpload(baseUrl: string, apiKey: string, id: string, metadata: TAppMetadata, path: string, sha: string): Promise<Record<string, never>>;
16
- getBinaryUploadUrl(baseUrl: string, apiKey: string, platform: "android" | "ios"): Promise<{
15
+ finaliseUpload(baseUrl: string, apiKey: string, id: string, metadata: TAppMetadata, path: string, sha: string, supabaseSuccess: boolean, backblazeSuccess: boolean): Promise<Record<string, never>>;
16
+ getBinaryUploadUrl(baseUrl: string, apiKey: string, platform: "android" | "ios", fileSize: number): Promise<{
17
17
  message: string;
18
18
  path: string;
19
19
  token: string;
20
20
  id: string;
21
+ b2?: {
22
+ strategy: "simple" | "large";
23
+ simple?: {
24
+ uploadUrl: string;
25
+ authorizationToken: string;
26
+ };
27
+ large?: {
28
+ fileId: string;
29
+ fileName: string;
30
+ uploadPartUrls: Array<{
31
+ uploadUrl: string;
32
+ authorizationToken: string;
33
+ }>;
34
+ };
35
+ };
21
36
  }>;
37
+ finishLargeFile(baseUrl: string, apiKey: string, fileId: string, partSha1Array: string[]): Promise<any>;
22
38
  getResultsForUpload(baseUrl: string, apiKey: string, uploadId: string): Promise<{
23
39
  statusCode?: number;
24
40
  results?: import("../types/generated/schema.types").components["schemas"]["TResultResponse"][];
@@ -99,9 +99,9 @@ exports.ApiGateway = {
99
99
  await finished(Readable.fromWeb(res.body).pipe(fileStream));
100
100
  },
101
101
  // eslint-disable-next-line max-params
102
- async finaliseUpload(baseUrl, apiKey, id, metadata, path, sha) {
102
+ async finaliseUpload(baseUrl, apiKey, id, metadata, path, sha, supabaseSuccess, backblazeSuccess) {
103
103
  const res = await fetch(`${baseUrl}/uploads/finaliseUpload`, {
104
- body: JSON.stringify({ id, metadata, path, sha }),
104
+ body: JSON.stringify({ id, metadata, path, sha, supabaseSuccess, backblazeSuccess }),
105
105
  headers: {
106
106
  'content-type': 'application/json',
107
107
  'x-app-api-key': apiKey,
@@ -113,9 +113,9 @@ exports.ApiGateway = {
113
113
  }
114
114
  return res.json();
115
115
  },
116
- async getBinaryUploadUrl(baseUrl, apiKey, platform) {
116
+ async getBinaryUploadUrl(baseUrl, apiKey, platform, fileSize) {
117
117
  const res = await fetch(`${baseUrl}/uploads/getBinaryUploadUrl`, {
118
- body: JSON.stringify({ platform }),
118
+ body: JSON.stringify({ platform, fileSize }),
119
119
  headers: {
120
120
  'content-type': 'application/json',
121
121
  'x-app-api-key': apiKey,
@@ -127,6 +127,20 @@ exports.ApiGateway = {
127
127
  }
128
128
  return res.json();
129
129
  },
130
+ async finishLargeFile(baseUrl, apiKey, fileId, partSha1Array) {
131
+ const res = await fetch(`${baseUrl}/uploads/finishLargeFile`, {
132
+ body: JSON.stringify({ fileId, partSha1Array }),
133
+ headers: {
134
+ 'content-type': 'application/json',
135
+ 'x-app-api-key': apiKey,
136
+ },
137
+ method: 'POST',
138
+ });
139
+ if (!res.ok) {
140
+ await this.handleApiError(res, 'Failed to finish large file');
141
+ }
142
+ return res.json();
143
+ },
130
144
  async getResultsForUpload(baseUrl, apiKey, uploadId) {
131
145
  // TODO: merge with getUploadStatus
132
146
  const res = await fetch(`${baseUrl}/results/${uploadId}`, {
@@ -7,5 +7,5 @@ export declare class SupabaseGateway {
7
7
  SUPABASE_PUBLIC_KEY: string;
8
8
  SUPABASE_URL: string;
9
9
  };
10
- static uploadToSignedUrl(env: 'dev' | 'prod', path: string, token: string, file: File): Promise<void>;
10
+ static uploadToSignedUrl(env: 'dev' | 'prod', path: string, token: string, file: File, debug?: boolean): Promise<void>;
11
11
  }
@@ -16,16 +16,75 @@ class SupabaseGateway {
16
16
  static getSupabaseKeys(env) {
17
17
  return this.SB[env];
18
18
  }
19
- static async uploadToSignedUrl(env, path, token, file) {
19
+ static async uploadToSignedUrl(env, path, token, file, debug = false) {
20
20
  const { SUPABASE_PUBLIC_KEY, SUPABASE_URL } = this.getSupabaseKeys(env);
21
- const supabase = (0, supabase_js_1.createClient)(SUPABASE_URL, SUPABASE_PUBLIC_KEY);
22
- const uploadToUrl = await supabase.storage
23
- .from('organizations')
24
- .uploadToSignedUrl(path, token, file);
25
- if (uploadToUrl.error) {
26
- const error = uploadToUrl.error;
27
- const errorMessage = typeof error === 'string' ? error : error?.message || 'Upload failed';
28
- throw new Error(errorMessage);
21
+ if (debug) {
22
+ console.log(`[DEBUG] Supabase upload starting...`);
23
+ console.log(`[DEBUG] Supabase URL: ${SUPABASE_URL}`);
24
+ console.log(`[DEBUG] Upload path: ${path}`);
25
+ console.log(`[DEBUG] File name: ${file.name}`);
26
+ console.log(`[DEBUG] File type: ${file.type}`);
27
+ }
28
+ try {
29
+ const supabase = (0, supabase_js_1.createClient)(SUPABASE_URL, SUPABASE_PUBLIC_KEY);
30
+ const uploadToUrl = await supabase.storage
31
+ .from('organizations')
32
+ .uploadToSignedUrl(path, token, file);
33
+ if (uploadToUrl.error) {
34
+ const error = uploadToUrl.error;
35
+ const errorMessage = typeof error === 'string' ? error : error?.message || 'Upload failed';
36
+ if (debug) {
37
+ console.error(`[DEBUG] Supabase upload error:`, error);
38
+ if (typeof error === 'object' && 'statusCode' in error) {
39
+ console.error(`[DEBUG] HTTP Status Code: ${error.statusCode}`);
40
+ }
41
+ }
42
+ throw new Error(errorMessage);
43
+ }
44
+ if (debug) {
45
+ console.log(`[DEBUG] Supabase upload successful`);
46
+ console.log(`[DEBUG] Upload result path: ${uploadToUrl.data?.path || 'N/A'}`);
47
+ }
48
+ }
49
+ catch (error) {
50
+ if (debug) {
51
+ console.error(`[DEBUG] === SUPABASE UPLOAD EXCEPTION ===`);
52
+ console.error(`[DEBUG] Exception caught:`, error);
53
+ console.error(`[DEBUG] Error type: ${error instanceof Error ? error.name : typeof error}`);
54
+ console.error(`[DEBUG] Error message: ${error instanceof Error ? error.message : String(error)}`);
55
+ if (error instanceof Error && error.stack) {
56
+ console.error(`[DEBUG] Error stack:\n${error.stack}`);
57
+ }
58
+ // Log additional context
59
+ console.error(`[DEBUG] Upload context:`);
60
+ console.error(`[DEBUG] - Environment: ${env}`);
61
+ console.error(`[DEBUG] - Supabase URL: ${SUPABASE_URL}`);
62
+ console.error(`[DEBUG] - Upload path: ${path}`);
63
+ console.error(`[DEBUG] - File name: ${file.name}`);
64
+ console.error(`[DEBUG] - File size: ${file.size} bytes`);
65
+ // Check for common network errors
66
+ if (error instanceof Error) {
67
+ const errorMsg = error.message.toLowerCase();
68
+ if (errorMsg.includes('econnrefused') || errorMsg.includes('connection refused')) {
69
+ console.error(`[DEBUG] Network error: Connection refused - check internet connectivity`);
70
+ }
71
+ else if (errorMsg.includes('etimedout') || errorMsg.includes('timeout')) {
72
+ console.error(`[DEBUG] Network error: Connection timeout - check internet connectivity or try again`);
73
+ }
74
+ else if (errorMsg.includes('enotfound') || errorMsg.includes('not found')) {
75
+ console.error(`[DEBUG] Network error: DNS lookup failed - check internet connectivity`);
76
+ }
77
+ else if (errorMsg.includes('econnreset') || errorMsg.includes('connection reset') || errorMsg.includes('connection lost')) {
78
+ console.error(`[DEBUG] Network error: Connection reset/lost - network unstable or interrupted`);
79
+ }
80
+ else if (errorMsg.includes('network')) {
81
+ console.error(`[DEBUG] Network-related error detected - check internet connectivity`);
82
+ }
83
+ }
84
+ }
85
+ // Re-throw with additional context
86
+ const errorMsg = error instanceof Error ? error.message : String(error);
87
+ throw new Error(`Supabase upload error: ${errorMsg}`);
29
88
  }
30
89
  }
31
90
  }
package/dist/methods.d.ts CHANGED
@@ -3,7 +3,7 @@ export declare const toBuffer: (archive: archiver.Archiver) => Promise<Buffer<Ar
3
3
  export declare const compressFolderToBlob: (sourceDir: string) => Promise<Blob>;
4
4
  export declare const compressFilesFromRelativePath: (basePath: string, files: string[], commonRoot: string) => Promise<Buffer<ArrayBuffer>>;
5
5
  export declare const verifyAppZip: (zipPath: string) => Promise<void>;
6
- export declare const uploadBinary: (filePath: string, apiUrl: string, apiKey: string, ignoreShaCheck?: boolean, log?: boolean) => Promise<string>;
6
+ export declare const uploadBinary: (filePath: string, apiUrl: string, apiKey: string, ignoreShaCheck?: boolean, log?: boolean, debug?: boolean) => Promise<string>;
7
7
  /**
8
8
  * Writes JSON data to a file with error handling
9
9
  * @param filePath - Path to the output JSON file