@devicecloud.dev/dcd 3.7.0 → 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,7 @@ const StreamZip = require("node-stream-zip");
12
12
  const constants_1 = require("../constants");
13
13
  const methods_1 = require("../methods");
14
14
  const plan_1 = require("../plan");
15
+ const ApiGateway_1 = require("../gateways/ApiGateway");
15
16
  exports.mimeTypeLookupByExtension = {
16
17
  apk: 'application/vnd.android.package-archive',
17
18
  yaml: 'application/x-yaml',
@@ -518,11 +519,7 @@ class Cloud extends core_1.Command {
518
519
  if (debug) {
519
520
  this.log(`DEBUG: Submitting flow upload request to ${apiUrl}/uploads/flow`);
520
521
  }
521
- const options = {
522
- body: testFormData,
523
- headers: { 'x-app-api-key': apiKey },
524
- };
525
- const { message, results } = await (0, methods_1.typeSafePost)(apiUrl, '/uploads/flow', options);
522
+ const { message, results } = await ApiGateway_1.ApiGateway.uploadFlow(apiUrl, apiKey, testFormData);
526
523
  if (debug) {
527
524
  this.log(`DEBUG: Flow upload response received`);
528
525
  this.log(`DEBUG: Message: ${message}`);
@@ -581,9 +578,7 @@ class Cloud extends core_1.Command {
581
578
  if (debug) {
582
579
  this.log(`DEBUG: Polling for results: ${results[0].test_upload_id}`);
583
580
  }
584
- const { results: updatedResults } = await (0, methods_1.typeSafeGet)(apiUrl, `/results/${results[0].test_upload_id}`, {
585
- headers: { 'x-app-api-key': apiKey },
586
- });
581
+ const { results: updatedResults } = await ApiGateway_1.ApiGateway.getResultsForUpload(apiUrl, apiKey, results[0].test_upload_id);
587
582
  if (!updatedResults) {
588
583
  throw new Error('no results');
589
584
  }
@@ -607,11 +602,24 @@ class Cloud extends core_1.Command {
607
602
  if (!json) {
608
603
  core_1.ux.action.stop('completed');
609
604
  this.log('\n');
605
+ const hasFailedTests = updatedResults.some((result) => result.status === 'FAILED');
610
606
  (0, cli_ux_1.table)(updatedResults, {
611
607
  status: { get: (row) => row.status },
612
608
  test: {
613
609
  get: (row) => `${row.test_file_name} ${row.retry_of ? '(retry)' : ''}`,
614
610
  },
611
+ duration: {
612
+ get: (row) => row.duration_seconds
613
+ ? (0, methods_1.formatDurationSeconds)(row.duration_seconds)
614
+ : '',
615
+ },
616
+ ...(hasFailedTests && {
617
+ fail_reason: {
618
+ get: (row) => row.status === 'FAILED' && row.fail_reason
619
+ ? row.fail_reason
620
+ : '',
621
+ },
622
+ }),
615
623
  }, { printLine: this.log.bind(this) });
616
624
  this.log('\n');
617
625
  this.log('Run completed, you can access the results at:');
@@ -624,13 +632,7 @@ class Cloud extends core_1.Command {
624
632
  if (debug) {
625
633
  this.log(`DEBUG: Downloading artifacts: ${downloadArtifacts}`);
626
634
  }
627
- await (0, methods_1.typeSafePostDownload)(apiUrl, `/results/${results[0].test_upload_id}/download`, {
628
- body: JSON.stringify({ results: downloadArtifacts }),
629
- headers: {
630
- 'content-type': 'application/json',
631
- 'x-app-api-key': apiKey,
632
- },
633
- }, artifactsPath);
635
+ await ApiGateway_1.ApiGateway.downloadArtifactsZip(apiUrl, apiKey, results[0].test_upload_id, downloadArtifacts, artifactsPath);
634
636
  this.log('\n');
635
637
  this.log(`Test artifacts have been downloaded to ${artifactsPath || './artifacts.zip'}`);
636
638
  }
@@ -657,6 +659,10 @@ class Cloud extends core_1.Command {
657
659
  tests: resultsWithoutEarlierTries.map((r) => ({
658
660
  name: r.test_file_name,
659
661
  status: r.status,
662
+ duration_seconds: r.duration_seconds,
663
+ fail_reason: r.status === 'FAILED'
664
+ ? r.fail_reason || 'No reason provided'
665
+ : undefined,
660
666
  })),
661
667
  };
662
668
  if (flags['json-file']) {
@@ -679,6 +685,10 @@ class Cloud extends core_1.Command {
679
685
  tests: resultsWithoutEarlierTries.map((r) => ({
680
686
  name: r.test_file_name,
681
687
  status: r.status,
688
+ duration_seconds: r.duration_seconds,
689
+ fail_reason: r.status === 'FAILED'
690
+ ? r.fail_reason || 'No reason provided'
691
+ : undefined,
682
692
  })),
683
693
  };
684
694
  if (flags['json-file']) {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const constants_1 = require("../constants");
5
+ const ApiGateway_1 = require("../gateways/ApiGateway");
5
6
  const methods_1 = require("../methods");
6
7
  class Status extends core_1.Command {
7
8
  static description = 'Get the status of an upload by name or upload ID';
@@ -52,7 +53,7 @@ class Status extends core_1.Command {
52
53
  return;
53
54
  }
54
55
  try {
55
- const status = (await (0, methods_1.getUploadStatus)(apiUrl, apiKey, {
56
+ const status = (await ApiGateway_1.ApiGateway.getUploadStatus(apiUrl, apiKey, {
56
57
  name,
57
58
  uploadId,
58
59
  }));
@@ -85,14 +86,7 @@ class Status extends core_1.Command {
85
86
  this.log(` Fail reason: ${item.failReason}`);
86
87
  }
87
88
  if (item.durationSeconds) {
88
- const minutes = Math.floor(item.durationSeconds / 60);
89
- const seconds = item.durationSeconds % 60;
90
- if (minutes > 0) {
91
- this.log(` Duration: ${minutes}m ${seconds}s`);
92
- }
93
- else {
94
- this.log(` Duration: ${item.durationSeconds}s`);
95
- }
89
+ this.log(` Duration: ${(0, methods_1.formatDurationSeconds)(item.durationSeconds)}`);
96
90
  }
97
91
  this.log('');
98
92
  });
@@ -0,0 +1,35 @@
1
+ import { TAppMetadata } from '../types';
2
+ export declare class ApiGateway {
3
+ static getResultsForUpload: (baseUrl: string, apiKey: string, uploadId: string) => Promise<{
4
+ statusCode?: number;
5
+ results?: import("../../../api/schema.types").components["schemas"]["TResultResponse"][];
6
+ }>;
7
+ static getUploadStatus: (baseUrl: string, apiKey: string, options: {
8
+ uploadId?: string;
9
+ name?: string;
10
+ }) => Promise<{
11
+ status: "PASSED" | "FAILED" | "CANCELLED" | "PENDING";
12
+ tests: Array<{
13
+ name: string;
14
+ status: "PASSED" | "FAILED" | "CANCELLED" | "PENDING";
15
+ durationSeconds?: number;
16
+ failReason?: string;
17
+ }>;
18
+ }>;
19
+ static downloadArtifactsZip: (baseUrl: string, apiKey: string, uploadId: string, results: "ALL" | "FAILED", artifactsPath?: string) => Promise<void>;
20
+ static uploadFlow: (baseUrl: string, apiKey: string, testFormData: FormData) => Promise<{
21
+ message?: string;
22
+ results?: import("../../../api/schema.types").components["schemas"]["IDBResult"][];
23
+ }>;
24
+ static finaliseUpload: (baseUrl: string, apiKey: string, id: string, metadata: TAppMetadata, path: string, sha: string) => Promise<Record<string, never>>;
25
+ static getBinaryUploadUrl: (baseUrl: string, apiKey: string, platform: "android" | "ios") => Promise<{
26
+ message: string;
27
+ path: string;
28
+ token: string;
29
+ id: string;
30
+ }>;
31
+ static checkForExistingUpload: (baseUrl: string, apiKey: string, sha: string) => Promise<{
32
+ appBinaryId: string;
33
+ exists: boolean;
34
+ }>;
35
+ }
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiGateway = void 0;
4
+ class ApiGateway {
5
+ static getResultsForUpload = async (baseUrl, apiKey, uploadId) => {
6
+ // todo: merge with getUploadStatus
7
+ const res = await fetch(`${baseUrl}/results/${uploadId}`, {
8
+ headers: { 'x-app-api-key': apiKey },
9
+ });
10
+ if (!res.ok) {
11
+ throw new Error(await res.text());
12
+ }
13
+ return res.json();
14
+ };
15
+ static getUploadStatus = async (baseUrl, apiKey, options) => {
16
+ const queryParams = new URLSearchParams();
17
+ if (options.uploadId) {
18
+ queryParams.append('uploadId', options.uploadId);
19
+ }
20
+ if (options.name) {
21
+ queryParams.append('name', options.name);
22
+ }
23
+ const response = await fetch(`${baseUrl}/uploads/status?${queryParams}`, {
24
+ headers: {
25
+ 'x-app-api-key': apiKey,
26
+ },
27
+ });
28
+ if (!response.ok) {
29
+ throw new Error(`Failed to fetch status: ${response.statusText}`);
30
+ }
31
+ return response.json();
32
+ };
33
+ static downloadArtifactsZip = async (baseUrl, apiKey, uploadId, results, artifactsPath = './artifacts.zip') => {
34
+ const res = await fetch(`${baseUrl}/results/${uploadId}/download`, {
35
+ method: 'POST',
36
+ headers: {
37
+ 'x-app-api-key': apiKey,
38
+ },
39
+ body: JSON.stringify({ results }),
40
+ });
41
+ if (!res.ok) {
42
+ throw new Error(await res.text());
43
+ }
44
+ // Handle tilde expansion for home directory
45
+ if (artifactsPath.startsWith('~/') || artifactsPath === '~') {
46
+ artifactsPath = artifactsPath.replace(/^~(?=$|\/|\\)/, require('os').homedir());
47
+ }
48
+ // Create directory structure if it doesn't exist
49
+ const { dirname } = require('node:path');
50
+ const { mkdirSync, createWriteStream } = require('node:fs');
51
+ const { finished } = require('node:stream/promises');
52
+ const { Readable } = require('node:stream');
53
+ const directory = dirname(artifactsPath);
54
+ if (directory !== '.') {
55
+ try {
56
+ mkdirSync(directory, { recursive: true });
57
+ }
58
+ catch (error) {
59
+ // Ignore if directory already exists
60
+ if (error.code !== 'EEXIST') {
61
+ throw error;
62
+ }
63
+ }
64
+ }
65
+ const fileStream = createWriteStream(artifactsPath, { flags: 'wx' });
66
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
67
+ await finished(Readable.fromWeb(res.body).pipe(fileStream));
68
+ };
69
+ static uploadFlow = async (baseUrl, apiKey, testFormData) => {
70
+ const res = await fetch(`${baseUrl}/uploads/flow`, {
71
+ method: 'POST',
72
+ body: testFormData,
73
+ headers: {
74
+ 'x-app-api-key': apiKey,
75
+ },
76
+ });
77
+ if (!res.ok) {
78
+ throw new Error(await res.text());
79
+ }
80
+ return res.json();
81
+ };
82
+ static finaliseUpload = async (baseUrl, apiKey, id, metadata, path, sha) => {
83
+ const res = await fetch(`${baseUrl}/uploads/finaliseUpload`, {
84
+ method: 'POST',
85
+ body: JSON.stringify({ id, metadata, path, sha }),
86
+ headers: {
87
+ 'content-type': 'application/json',
88
+ 'x-app-api-key': apiKey,
89
+ },
90
+ });
91
+ if (!res.ok) {
92
+ throw new Error(await res.text());
93
+ }
94
+ return res.json();
95
+ };
96
+ static getBinaryUploadUrl = async (baseUrl, apiKey, platform) => {
97
+ const res = await fetch(`${baseUrl}/uploads/getBinaryUploadUrl`, {
98
+ method: 'POST',
99
+ body: JSON.stringify({ platform }),
100
+ headers: {
101
+ 'content-type': 'application/json',
102
+ 'x-app-api-key': apiKey,
103
+ },
104
+ });
105
+ if (!res.ok) {
106
+ throw new Error(await res.text());
107
+ }
108
+ return res.json();
109
+ };
110
+ static checkForExistingUpload = async (baseUrl, apiKey, sha) => {
111
+ const res = await fetch(`${baseUrl}/uploads/checkForExistingUpload`, {
112
+ method: 'POST',
113
+ body: JSON.stringify({ sha }),
114
+ headers: {
115
+ 'content-type': 'application/json',
116
+ 'x-app-api-key': apiKey,
117
+ },
118
+ });
119
+ return res.json();
120
+ };
121
+ }
122
+ exports.ApiGateway = ApiGateway;
@@ -0,0 +1,11 @@
1
+ export declare class SupabaseGateway {
2
+ private static SB;
3
+ static getSupabaseKeys(env: 'prod' | 'dev'): {
4
+ SUPABASE_PUBLIC_KEY: string;
5
+ SUPABASE_URL: string;
6
+ } | {
7
+ SUPABASE_PUBLIC_KEY: string;
8
+ SUPABASE_URL: string;
9
+ };
10
+ static uploadToSignedUrl(env: 'prod' | 'dev', path: string, token: string, file: File): Promise<void>;
11
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SupabaseGateway = void 0;
4
+ const supabase_js_1 = require("@supabase/supabase-js");
5
+ class SupabaseGateway {
6
+ static SB = {
7
+ dev: {
8
+ SUPABASE_PUBLIC_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxibXNvd2VodGp3bnFsdXJwZW1iIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDkyMTg0ODcsImV4cCI6MjAyNDc5NDQ4N30.zeLTMAuZ_WwYvGdeP0kdvL_Zrs-RQee5APPyxmWq7qQ',
9
+ SUPABASE_URL: 'https://lbmsowehtjwnqlurpemb.supabase.co',
10
+ },
11
+ prod: {
12
+ SUPABASE_PUBLIC_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBneWRucGhiaW1ldGluc2dma2JvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDc1OTQzNDYsImV4cCI6MjAyMzE3MDM0Nn0.hAYOMFxxwX1exkQkY9xyQJGC_GhGnyogkj2N-kBkMI8',
13
+ SUPABASE_URL: 'https://pgydnphbimetinsgfkbo.supabase.co',
14
+ },
15
+ };
16
+ static getSupabaseKeys(env) {
17
+ return this.SB[env];
18
+ }
19
+ static async uploadToSignedUrl(env, path, token, file) {
20
+ const { SUPABASE_URL, SUPABASE_PUBLIC_KEY } = 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
+ throw new Error(uploadToUrl.error);
27
+ }
28
+ }
29
+ exports.SupabaseGateway = SupabaseGateway;
package/dist/methods.d.ts CHANGED
@@ -1,18 +1,5 @@
1
1
  import * as archiver from 'archiver';
2
- import { paths } from '../../api/schema.types';
3
2
  import { TAppMetadata } from './types';
4
- export declare const typeSafePost: <T extends keyof paths>(baseUrl: string, path: string, init?: {
5
- body?: BodyInit;
6
- headers?: HeadersInit;
7
- }) => Promise<paths[T]["post"]["responses"]["201"]["content"]["application/json"]>;
8
- export declare const typeSafePostDownload: (baseUrl: string, path: string, init?: {
9
- body?: BodyInit;
10
- headers?: HeadersInit;
11
- }, artifactsPath?: string) => Promise<void>;
12
- export declare const typeSafeGet: <T extends keyof paths>(baseUrl: string, path: string, init?: {
13
- body?: FormData;
14
- headers?: HeadersInit;
15
- }) => Promise<paths[T]["get"]["responses"]["200"]["content"]["application/json"]>;
16
3
  export declare const toBuffer: (archive: archiver.Archiver) => Promise<Buffer<ArrayBuffer>>;
17
4
  export declare const compressFolderToBlob: (sourceDir: string) => Promise<Blob>;
18
5
  export declare const compressFilesFromRelativePath: (path: string, files: string[], commonRoot: string) => Promise<Buffer<ArrayBuffer>>;
@@ -23,18 +10,6 @@ export declare const extractAppMetadataIos: (appFolderPath: string) => Promise<T
23
10
  export declare const uploadBinary: (filePath: string, apiUrl: string, apiKey: string, ignoreShaCheck?: boolean, log?: boolean) => Promise<string>;
24
11
  export declare const uploadBinaries: (finalAppFiles: string[], apiUrl: string, apiKey: string, ignoreShaCheck?: boolean, log?: boolean) => Promise<string[]>;
25
12
  export declare const verifyAdditionalAppFiles: (appFiles: string[] | undefined) => Promise<void>;
26
- export declare const getUploadStatus: (apiUrl: string, apiKey: string, options: {
27
- uploadId?: string;
28
- name?: string;
29
- }) => Promise<{
30
- status: "PASSED" | "FAILED" | "CANCELLED" | "PENDING";
31
- tests: Array<{
32
- name: string;
33
- status: "PASSED" | "FAILED" | "CANCELLED" | "PENDING";
34
- durationSeconds?: number;
35
- failReason?: string;
36
- }>;
37
- }>;
38
13
  /**
39
14
  * Writes JSON data to a file with error handling
40
15
  * @param filePath - Path to the output JSON file
@@ -46,3 +21,9 @@ export declare const writeJSONFile: (filePath: string, data: any, logger: {
46
21
  log: (message: string) => void;
47
22
  warn: (message: string) => void;
48
23
  }) => void;
24
+ /**
25
+ * Formats duration in seconds into a human readable string
26
+ * @param durationSeconds - Duration in seconds
27
+ * @returns Formatted duration string (e.g. "2m 30s" or "45s")
28
+ */
29
+ export declare const formatDurationSeconds: (durationSeconds: number) => string;
package/dist/methods.js CHANGED
@@ -1,8 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.writeJSONFile = exports.getUploadStatus = exports.verifyAdditionalAppFiles = exports.uploadBinaries = exports.uploadBinary = exports.extractAppMetadataIos = exports.extractAppMetadataIosZip = exports.extractAppMetadataAndroid = exports.verifyAppZip = exports.compressFilesFromRelativePath = exports.compressFolderToBlob = exports.toBuffer = exports.typeSafeGet = exports.typeSafePostDownload = exports.typeSafePost = void 0;
3
+ exports.formatDurationSeconds = exports.writeJSONFile = exports.verifyAdditionalAppFiles = exports.uploadBinaries = exports.uploadBinary = exports.extractAppMetadataIos = exports.extractAppMetadataIosZip = exports.extractAppMetadataAndroid = exports.verifyAppZip = exports.compressFilesFromRelativePath = exports.compressFolderToBlob = exports.toBuffer = void 0;
4
4
  const core_1 = require("@oclif/core");
5
- const supabase_js_1 = require("@supabase/supabase-js");
6
5
  // required polyfill for node 18
7
6
  const file_1 = require("@web-std/file");
8
7
  // @ts-ignore
@@ -13,64 +12,13 @@ const node_fs_1 = require("node:fs");
13
12
  const promises_1 = require("node:fs/promises");
14
13
  const nodePath = require("node:path");
15
14
  const node_stream_1 = require("node:stream");
16
- const promises_2 = require("node:stream/promises");
17
15
  const StreamZip = require("node-stream-zip");
18
16
  const plist_1 = require("plist");
19
17
  const node_crypto_1 = require("node:crypto");
20
18
  const path = require("path");
21
- const node_path_1 = require("node:path");
22
- const os = require("node:os");
23
19
  const cloud_1 = require("./commands/cloud");
24
- const typeSafePost = async (baseUrl, path, init) => {
25
- const res = await fetch(baseUrl + path, {
26
- ...init,
27
- method: 'POST',
28
- });
29
- if (!res.ok) {
30
- throw new Error(await res.text());
31
- }
32
- return res.json();
33
- };
34
- exports.typeSafePost = typeSafePost;
35
- const typeSafePostDownload = async (baseUrl, path, init, artifactsPath) => {
36
- const res = await fetch(baseUrl + path, {
37
- ...init,
38
- method: 'POST',
39
- });
40
- if (!res.ok) {
41
- throw new Error(await res.text());
42
- }
43
- let outputPath = artifactsPath || './artifacts.zip';
44
- // Handle tilde expansion for home directory
45
- if (outputPath.startsWith('~/') || outputPath === '~') {
46
- outputPath = outputPath.replace(/^~(?=$|\/|\\)/, os.homedir());
47
- }
48
- // Create directory structure if it doesn't exist
49
- const directory = (0, node_path_1.dirname)(outputPath);
50
- if (directory !== '.') {
51
- try {
52
- (0, node_fs_1.mkdirSync)(directory, { recursive: true });
53
- }
54
- catch (error) {
55
- // Ignore if directory already exists
56
- if (error.code !== 'EEXIST') {
57
- throw error;
58
- }
59
- }
60
- }
61
- const fileStream = (0, node_fs_1.createWriteStream)(outputPath, { flags: 'wx' });
62
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
- await (0, promises_2.finished)(node_stream_1.Readable.fromWeb(res.body).pipe(fileStream));
64
- };
65
- exports.typeSafePostDownload = typeSafePostDownload;
66
- const typeSafeGet = async (baseUrl, path, init) => {
67
- const res = await fetch(baseUrl + path, init);
68
- if (!res.ok) {
69
- throw new Error(await res.text());
70
- }
71
- return res.json();
72
- };
73
- exports.typeSafeGet = typeSafeGet;
20
+ const SupabaseGateway_1 = require("./gateways/SupabaseGateway");
21
+ const ApiGateway_1 = require("./gateways/ApiGateway");
74
22
  const toBuffer = async (archive) => {
75
23
  const chunks = [];
76
24
  const writable = new node_stream_1.Writable();
@@ -216,13 +164,7 @@ const uploadBinary = async (filePath, apiUrl, apiKey, ignoreShaCheck = false, lo
216
164
  }
217
165
  if (!ignoreShaCheck && sha) {
218
166
  try {
219
- const { appBinaryId, exists } = await (0, exports.typeSafePost)(apiUrl, '/uploads/checkForExistingUpload', {
220
- body: JSON.stringify({ sha }),
221
- headers: {
222
- 'content-type': 'application/json',
223
- 'x-app-api-key': apiKey,
224
- },
225
- });
167
+ const { appBinaryId, exists } = await ApiGateway_1.ApiGateway.checkForExistingUpload(apiUrl, apiKey, sha);
226
168
  if (exists) {
227
169
  if (log) {
228
170
  core_1.ux.info(`sha hash matches existing binary with id: ${appBinaryId}, skipping upload. Force upload with --ignore-sha-check`);
@@ -235,15 +177,7 @@ const uploadBinary = async (filePath, apiUrl, apiKey, ignoreShaCheck = false, lo
235
177
  // ignore error
236
178
  }
237
179
  }
238
- const { id, message, path, token } = await (0, exports.typeSafePost)(apiUrl, '/uploads/getBinaryUploadUrl', {
239
- body: JSON.stringify({
240
- platform: filePath?.endsWith('.apk') ? 'android' : 'ios',
241
- }),
242
- headers: {
243
- 'content-type': 'application/json',
244
- 'x-app-api-key': apiKey,
245
- },
246
- });
180
+ const { id, message, path, token } = await ApiGateway_1.ApiGateway.getBinaryUploadUrl(apiUrl, apiKey, filePath?.endsWith('.apk') ? 'android' : 'ios');
247
181
  if (!path)
248
182
  throw new Error(message);
249
183
  let metadata;
@@ -259,33 +193,9 @@ const uploadBinary = async (filePath, apiUrl, apiKey, ignoreShaCheck = false, lo
259
193
  core_1.ux.warn('Failed to extract app metadata, please share with support@devicecloud.dev so we can improve our parsing.');
260
194
  }
261
195
  }
262
- // this needs to made nicer by using envs or maybe fetching the keys from the getSignedURL call
263
- const SB = {
264
- dev: {
265
- SUPABASE_PUBLIC_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxibXNvd2VodGp3bnFsdXJwZW1iIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDkyMTg0ODcsImV4cCI6MjAyNDc5NDQ4N30.zeLTMAuZ_WwYvGdeP0kdvL_Zrs-RQee5APPyxmWq7qQ',
266
- SUPABASE_URL: 'https://lbmsowehtjwnqlurpemb.supabase.co',
267
- },
268
- prod: {
269
- SUPABASE_PUBLIC_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBneWRucGhiaW1ldGluc2dma2JvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDc1OTQzNDYsImV4cCI6MjAyMzE3MDM0Nn0.hAYOMFxxwX1exkQkY9xyQJGC_GhGnyogkj2N-kBkMI8',
270
- SUPABASE_URL: 'https://pgydnphbimetinsgfkbo.supabase.co',
271
- },
272
- };
273
- const { SUPABASE_PUBLIC_KEY, SUPABASE_URL } = SB[apiUrl === 'https://api.devicecloud.dev' ? 'prod' : 'dev'];
274
- const supabase = (0, supabase_js_1.createClient)(SUPABASE_URL, SUPABASE_PUBLIC_KEY);
275
- const uploadToUrl = await supabase.storage
276
- .from('organizations')
277
- .uploadToSignedUrl(path, token, file);
278
- if (uploadToUrl.error)
279
- throw new Error(uploadToUrl.error);
280
- const { error } = await (0, exports.typeSafePost)(apiUrl, '/uploads/finaliseUpload', {
281
- body: JSON.stringify({ id, metadata, path, sha }),
282
- headers: {
283
- 'content-type': 'application/json',
284
- 'x-app-api-key': apiKey,
285
- },
286
- });
287
- if (error)
288
- throw new Error(error);
196
+ const env = apiUrl === 'https://api.devicecloud.dev' ? 'prod' : 'dev';
197
+ await SupabaseGateway_1.SupabaseGateway.uploadToSignedUrl(env, path, token, file);
198
+ await ApiGateway_1.ApiGateway.finaliseUpload(apiUrl, apiKey, id, metadata, path, sha);
289
199
  if (log) {
290
200
  core_1.ux.action.stop(`\nBinary uploaded with id: ${id}`);
291
201
  }
@@ -329,25 +239,6 @@ async function getFileHashFromFile(file) {
329
239
  processChunks();
330
240
  });
331
241
  }
332
- const getUploadStatus = async (apiUrl, apiKey, options) => {
333
- const queryParams = new URLSearchParams();
334
- if (options.uploadId) {
335
- queryParams.append('uploadId', options.uploadId);
336
- }
337
- if (options.name) {
338
- queryParams.append('name', options.name);
339
- }
340
- const response = await fetch(`${apiUrl}/uploads/status?${queryParams}`, {
341
- headers: {
342
- 'x-app-api-key': apiKey,
343
- },
344
- });
345
- if (!response.ok) {
346
- throw new Error(`Failed to fetch status: ${response.statusText}`);
347
- }
348
- return response.json();
349
- };
350
- exports.getUploadStatus = getUploadStatus;
351
242
  /**
352
243
  * Writes JSON data to a file with error handling
353
244
  * @param filePath - Path to the output JSON file
@@ -367,3 +258,17 @@ const writeJSONFile = (filePath, data, logger) => {
367
258
  }
368
259
  };
369
260
  exports.writeJSONFile = writeJSONFile;
261
+ /**
262
+ * Formats duration in seconds into a human readable string
263
+ * @param durationSeconds - Duration in seconds
264
+ * @returns Formatted duration string (e.g. "2m 30s" or "45s")
265
+ */
266
+ const formatDurationSeconds = (durationSeconds) => {
267
+ const minutes = Math.floor(durationSeconds / 60);
268
+ const seconds = durationSeconds % 60;
269
+ if (minutes > 0) {
270
+ return `${minutes}m ${seconds}s`;
271
+ }
272
+ return `${durationSeconds}s`;
273
+ };
274
+ exports.formatDurationSeconds = formatDurationSeconds;
@@ -544,5 +544,5 @@
544
544
  ]
545
545
  }
546
546
  },
547
- "version": "3.7.0"
547
+ "version": "3.7.1"
548
548
  }
package/package.json CHANGED
@@ -79,7 +79,7 @@
79
79
  "prepare": "yarn build",
80
80
  "version": "oclif readme && git add README.md"
81
81
  },
82
- "version": "3.7.0",
82
+ "version": "3.7.1",
83
83
  "bugs": {
84
84
  "url": "https://discord.gg/gm3mJwcNw8"
85
85
  },