@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.
- package/dist/commands/cloud.js +48 -44
- package/dist/commands/status.d.ts +0 -1
- package/dist/commands/status.js +10 -33
- package/dist/commands/upload.d.ts +1 -0
- package/dist/commands/upload.js +11 -9
- package/dist/config/flags/execution.flags.js +1 -1
- package/dist/gateways/api-gateway.d.ts +18 -2
- package/dist/gateways/api-gateway.js +18 -4
- package/dist/gateways/supabase-gateway.d.ts +1 -1
- package/dist/gateways/supabase-gateway.js +68 -9
- package/dist/methods.d.ts +1 -1
- package/dist/methods.js +467 -27
- package/dist/services/results-polling.service.d.ts +30 -0
- package/dist/services/results-polling.service.js +167 -71
- package/dist/services/test-submission.service.js +11 -0
- package/dist/types/generated/schema.types.d.ts +15 -0
- package/dist/types/schema.types.d.ts +1523 -0
- package/dist/types/schema.types.js +3 -0
- package/dist/utils/styling.d.ts +106 -0
- package/dist/utils/styling.js +166 -0
- package/oclif.manifest.json +8 -2
- package/package.json +16 -15
package/dist/commands/cloud.js
CHANGED
|
@@ -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
|
-
|
|
77
|
-
|
|
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
|
|
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
|
|
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
|
|
297
|
+
overridesLog += `\n ${styling_1.colors.dim('→')} ${relativePath}:`;
|
|
298
298
|
for (const [key, value] of Object.entries(overrides)) {
|
|
299
|
-
overridesLog += `\n
|
|
299
|
+
overridesLog += `\n ${styling_1.colors.dim(key + ':')} ${styling_1.colors.highlight(value)}`;
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
}
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
this.log(`
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
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('
|
|
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(
|
|
324
|
+
this.log((0, styling_1.listItem)(test));
|
|
322
325
|
}
|
|
323
326
|
if (sequentialFlows.length > 0) {
|
|
324
|
-
this.log('
|
|
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(
|
|
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
|
-
|
|
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(', ')
|
|
394
|
-
this.log(
|
|
395
|
-
const url =
|
|
396
|
-
this.log(
|
|
397
|
-
this.log(
|
|
398
|
-
this.log(
|
|
399
|
-
this.log(
|
|
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 {};
|
package/dist/commands/status.js
CHANGED
|
@@ -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('
|
|
102
|
-
this.log('═'.repeat(80));
|
|
102
|
+
this.log((0, styling_1.sectionHeader)('Upload Status'));
|
|
103
103
|
// Display overall status
|
|
104
|
-
|
|
105
|
-
this.log(`${overallSymbol} Status: ${status.status}`);
|
|
104
|
+
this.log(` ${(0, styling_1.formatStatus)(status.status)}`);
|
|
106
105
|
if (status.uploadId) {
|
|
107
|
-
this.log(
|
|
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(
|
|
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(
|
|
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('
|
|
117
|
-
this.log('─'.repeat(80));
|
|
115
|
+
this.log((0, styling_1.sectionHeader)('Test Results'));
|
|
118
116
|
for (const item of status.tests) {
|
|
119
|
-
|
|
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(`
|
|
119
|
+
this.log(` ${styling_1.colors.error('Fail reason:')} ${item.failReason}`);
|
|
124
120
|
}
|
|
125
121
|
if (item.durationSeconds) {
|
|
126
|
-
this.log(`
|
|
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<{
|
package/dist/commands/upload.js
CHANGED
|
@@ -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
|
-
|
|
42
|
-
|
|
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(`\
|
|
49
|
-
this.log(`
|
|
50
|
-
this.log(
|
|
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.
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
.
|
|
24
|
-
.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|