@devicecloud.dev/dcd 3.7.11 → 4.0.0

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.
@@ -1,55 +1,44 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
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}`, {
4
+ exports.ApiGateway = {
5
+ async checkForExistingUpload(baseUrl, apiKey, sha) {
6
+ const res = await fetch(`${baseUrl}/uploads/checkForExistingUpload`, {
7
+ body: JSON.stringify({ sha }),
24
8
  headers: {
9
+ 'content-type': 'application/json',
25
10
  'x-app-api-key': apiKey,
26
11
  },
12
+ method: 'POST',
27
13
  });
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') => {
14
+ return res.json();
15
+ },
16
+ async downloadArtifactsZip(baseUrl, apiKey, uploadId, results, artifactsPath = './artifacts.zip') {
34
17
  const res = await fetch(`${baseUrl}/results/${uploadId}/download`, {
35
- method: 'POST',
18
+ body: JSON.stringify({ results }),
36
19
  headers: {
37
- 'x-app-api-key': apiKey,
38
20
  'content-type': 'application/json',
21
+ 'x-app-api-key': apiKey,
39
22
  },
40
- body: JSON.stringify({ results }),
23
+ method: 'POST',
41
24
  });
42
25
  if (!res.ok) {
43
26
  throw new Error(await res.text());
44
27
  }
45
28
  // Handle tilde expansion for home directory
46
29
  if (artifactsPath.startsWith('~/') || artifactsPath === '~') {
47
- artifactsPath = artifactsPath.replace(/^~(?=$|\/|\\)/, require('os').homedir());
30
+ artifactsPath = artifactsPath.replace(/^~(?=$|\/|\\)/,
31
+ // eslint-disable-next-line unicorn/prefer-module
32
+ require('node:os').homedir());
48
33
  }
49
34
  // Create directory structure if it doesn't exist
35
+ // eslint-disable-next-line unicorn/prefer-module
50
36
  const { dirname } = require('node:path');
51
- const { mkdirSync, createWriteStream } = require('node:fs');
37
+ // eslint-disable-next-line unicorn/prefer-module
38
+ const { createWriteStream, mkdirSync } = require('node:fs');
39
+ // eslint-disable-next-line unicorn/prefer-module
52
40
  const { finished } = require('node:stream/promises');
41
+ // eslint-disable-next-line unicorn/prefer-module
53
42
  const { Readable } = require('node:stream');
54
43
  const directory = dirname(artifactsPath);
55
44
  if (directory !== '.') {
@@ -64,60 +53,76 @@ class ApiGateway {
64
53
  }
65
54
  }
66
55
  const fileStream = createWriteStream(artifactsPath, { flags: 'wx' });
67
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
56
  await finished(Readable.fromWeb(res.body).pipe(fileStream));
69
- };
70
- static uploadFlow = async (baseUrl, apiKey, testFormData) => {
71
- const res = await fetch(`${baseUrl}/uploads/flow`, {
72
- method: 'POST',
73
- body: testFormData,
74
- headers: {
75
- 'x-app-api-key': apiKey,
76
- },
77
- });
78
- if (!res.ok) {
79
- throw new Error(await res.text());
80
- }
81
- return res.json();
82
- };
83
- static finaliseUpload = async (baseUrl, apiKey, id, metadata, path, sha) => {
57
+ },
58
+ // eslint-disable-next-line max-params
59
+ async finaliseUpload(baseUrl, apiKey, id, metadata, path, sha) {
84
60
  const res = await fetch(`${baseUrl}/uploads/finaliseUpload`, {
85
- method: 'POST',
86
61
  body: JSON.stringify({ id, metadata, path, sha }),
87
62
  headers: {
88
63
  'content-type': 'application/json',
89
64
  'x-app-api-key': apiKey,
90
65
  },
66
+ method: 'POST',
91
67
  });
92
68
  if (!res.ok) {
93
69
  throw new Error(await res.text());
94
70
  }
95
71
  return res.json();
96
- };
97
- static getBinaryUploadUrl = async (baseUrl, apiKey, platform) => {
72
+ },
73
+ async getBinaryUploadUrl(baseUrl, apiKey, platform) {
98
74
  const res = await fetch(`${baseUrl}/uploads/getBinaryUploadUrl`, {
99
- method: 'POST',
100
75
  body: JSON.stringify({ platform }),
101
76
  headers: {
102
77
  'content-type': 'application/json',
103
78
  'x-app-api-key': apiKey,
104
79
  },
80
+ method: 'POST',
105
81
  });
106
82
  if (!res.ok) {
107
83
  throw new Error(await res.text());
108
84
  }
109
85
  return res.json();
110
- };
111
- static checkForExistingUpload = async (baseUrl, apiKey, sha) => {
112
- const res = await fetch(`${baseUrl}/uploads/checkForExistingUpload`, {
113
- method: 'POST',
114
- body: JSON.stringify({ sha }),
86
+ },
87
+ async getResultsForUpload(baseUrl, apiKey, uploadId) {
88
+ // TODO: merge with getUploadStatus
89
+ const res = await fetch(`${baseUrl}/results/${uploadId}`, {
90
+ headers: { 'x-app-api-key': apiKey },
91
+ });
92
+ if (!res.ok) {
93
+ throw new Error(await res.text());
94
+ }
95
+ return res.json();
96
+ },
97
+ async getUploadStatus(baseUrl, apiKey, options) {
98
+ const queryParams = new URLSearchParams();
99
+ if (options.uploadId) {
100
+ queryParams.append('uploadId', options.uploadId);
101
+ }
102
+ if (options.name) {
103
+ queryParams.append('name', options.name);
104
+ }
105
+ const response = await fetch(`${baseUrl}/uploads/status?${queryParams}`, {
115
106
  headers: {
116
- 'content-type': 'application/json',
117
107
  'x-app-api-key': apiKey,
118
108
  },
119
109
  });
110
+ if (!response.ok) {
111
+ throw new Error(`Failed to fetch status: ${response.statusText}`);
112
+ }
113
+ return response.json();
114
+ },
115
+ async uploadFlow(baseUrl, apiKey, testFormData) {
116
+ const res = await fetch(`${baseUrl}/uploads/flow`, {
117
+ body: testFormData,
118
+ headers: {
119
+ 'x-app-api-key': apiKey,
120
+ },
121
+ method: 'POST',
122
+ });
123
+ if (!res.ok) {
124
+ throw new Error(await res.text());
125
+ }
120
126
  return res.json();
121
- };
122
- }
123
- exports.ApiGateway = ApiGateway;
127
+ },
128
+ };
@@ -1,11 +1,11 @@
1
1
  export declare class SupabaseGateway {
2
2
  private static SB;
3
- static getSupabaseKeys(env: 'prod' | 'dev'): {
3
+ static getSupabaseKeys(env: 'dev' | 'prod'): {
4
4
  SUPABASE_PUBLIC_KEY: string;
5
5
  SUPABASE_URL: string;
6
6
  } | {
7
7
  SUPABASE_PUBLIC_KEY: string;
8
8
  SUPABASE_URL: string;
9
9
  };
10
- static uploadToSignedUrl(env: 'prod' | 'dev', path: string, token: string, file: File): Promise<void>;
10
+ static uploadToSignedUrl(env: 'dev' | 'prod', path: string, token: string, file: File): Promise<void>;
11
11
  }
@@ -10,20 +10,23 @@ class SupabaseGateway {
10
10
  },
11
11
  prod: {
12
12
  SUPABASE_PUBLIC_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBneWRucGhiaW1ldGluc2dma2JvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDc1OTQzNDYsImV4cCI6MjAyMzE3MDM0Nn0.hAYOMFxxwX1exkQkY9xyQJGC_GhGnyogkj2N-kBkMI8',
13
- SUPABASE_URL: 'https://pgydnphbimetinsgfkbo.supabase.co',
13
+ SUPABASE_URL: 'https://cloud.devicecloud.dev',
14
14
  },
15
15
  };
16
16
  static getSupabaseKeys(env) {
17
17
  return this.SB[env];
18
18
  }
19
19
  static async uploadToSignedUrl(env, path, token, file) {
20
- const { SUPABASE_URL, SUPABASE_PUBLIC_KEY } = this.getSupabaseKeys(env);
20
+ const { SUPABASE_PUBLIC_KEY, SUPABASE_URL } = this.getSupabaseKeys(env);
21
21
  const supabase = (0, supabase_js_1.createClient)(SUPABASE_URL, SUPABASE_PUBLIC_KEY);
22
22
  const uploadToUrl = await supabase.storage
23
23
  .from('organizations')
24
24
  .uploadToSignedUrl(path, token, file);
25
- if (uploadToUrl.error)
26
- throw new Error(uploadToUrl.error);
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);
29
+ }
27
30
  }
28
31
  }
29
32
  exports.SupabaseGateway = SupabaseGateway;
package/dist/methods.d.ts CHANGED
@@ -2,7 +2,7 @@ import * as archiver from 'archiver';
2
2
  import { TAppMetadata } from './types';
3
3
  export declare const toBuffer: (archive: archiver.Archiver) => Promise<Buffer<ArrayBuffer>>;
4
4
  export declare const compressFolderToBlob: (sourceDir: string) => Promise<Blob>;
5
- export declare const compressFilesFromRelativePath: (path: string, files: string[], commonRoot: string) => Promise<Buffer<ArrayBuffer>>;
5
+ export declare const compressFilesFromRelativePath: (basePath: string, files: string[], commonRoot: string) => Promise<Buffer<ArrayBuffer>>;
6
6
  export declare const verifyAppZip: (zipPath: string) => Promise<void>;
7
7
  export declare const extractAppMetadataAndroid: (appFilePath: string) => Promise<TAppMetadata>;
8
8
  export declare const extractAppMetadataIosZip: (appFilePath: string) => Promise<TAppMetadata>;
@@ -17,7 +17,7 @@ export declare const verifyAdditionalAppFiles: (appFiles: string[] | undefined)
17
17
  * @param logger - Logger object with log and warn methods
18
18
  * @returns true if successful, false if an error occurred
19
19
  */
20
- export declare const writeJSONFile: (filePath: string, data: any, logger: {
20
+ export declare const writeJSONFile: (filePath: string, data: unknown, logger: {
21
21
  log: (message: string) => void;
22
22
  warn: (message: string) => void;
23
23
  }) => void;
package/dist/methods.js CHANGED
@@ -4,21 +4,19 @@ exports.formatDurationSeconds = exports.writeJSONFile = exports.verifyAdditional
4
4
  const core_1 = require("@oclif/core");
5
5
  // required polyfill for node 18
6
6
  const file_1 = require("@web-std/file");
7
- // @ts-ignore
8
7
  const AppInfoParser = require("app-info-parser");
9
8
  const archiver = require("archiver");
10
9
  const bplist_parser_1 = require("bplist-parser");
10
+ const node_crypto_1 = require("node:crypto");
11
11
  const node_fs_1 = require("node:fs");
12
12
  const promises_1 = require("node:fs/promises");
13
- const nodePath = require("node:path");
13
+ const path = require("node:path");
14
14
  const node_stream_1 = require("node:stream");
15
15
  const StreamZip = require("node-stream-zip");
16
16
  const plist_1 = require("plist");
17
- const node_crypto_1 = require("node:crypto");
18
- const path = require("path");
19
17
  const cloud_1 = require("./commands/cloud");
20
- const SupabaseGateway_1 = require("./gateways/SupabaseGateway");
21
- const ApiGateway_1 = require("./gateways/ApiGateway");
18
+ const api_gateway_1 = require("./gateways/api-gateway");
19
+ const supabase_gateway_1 = require("./gateways/supabase-gateway");
22
20
  const toBuffer = async (archive) => {
23
21
  const chunks = [];
24
22
  const writable = new node_stream_1.Writable();
@@ -46,7 +44,7 @@ const compressFolderToBlob = async (sourceDir) => {
46
44
  return new Blob([buffer], { type: 'application/zip' });
47
45
  };
48
46
  exports.compressFolderToBlob = compressFolderToBlob;
49
- const compressFilesFromRelativePath = async (path, files, commonRoot) => {
47
+ const compressFilesFromRelativePath = async (basePath, files, commonRoot) => {
50
48
  const archive = archiver('zip', {
51
49
  zlib: { level: 9 },
52
50
  });
@@ -54,7 +52,7 @@ const compressFilesFromRelativePath = async (path, files, commonRoot) => {
54
52
  throw err;
55
53
  });
56
54
  for (const file of files) {
57
- archive.file(nodePath.resolve(path, file), {
55
+ archive.file(path.resolve(basePath, file), {
58
56
  name: file.replace(commonRoot, ''),
59
57
  });
60
58
  }
@@ -148,23 +146,24 @@ const uploadBinary = async (filePath, apiUrl, apiKey, ignoreShaCheck = false, lo
148
146
  file = new file_1.File([zippedAppBlob], filePath + '.zip');
149
147
  }
150
148
  else {
151
- const binaryBlob = new Blob([await (0, promises_1.readFile)(filePath)], {
149
+ const fileBuffer = await (0, promises_1.readFile)(filePath);
150
+ const binaryBlob = new Blob([new Uint8Array(fileBuffer)], {
152
151
  type: cloud_1.mimeTypeLookupByExtension[filePath.split('.').pop()],
153
152
  });
154
153
  file = new file_1.File([binaryBlob], filePath);
155
154
  }
156
- let sha = undefined;
155
+ let sha;
157
156
  try {
158
157
  sha = await getFileHashFromFile(file);
159
158
  }
160
- catch (e) {
159
+ catch (error) {
161
160
  if (log) {
162
- console.warn('Warning: Failed to get file hash', e);
161
+ console.warn('Warning: Failed to get file hash', error);
163
162
  }
164
163
  }
165
164
  if (!ignoreShaCheck && sha) {
166
165
  try {
167
- const { appBinaryId, exists } = await ApiGateway_1.ApiGateway.checkForExistingUpload(apiUrl, apiKey, sha);
166
+ const { appBinaryId, exists } = await api_gateway_1.ApiGateway.checkForExistingUpload(apiUrl, apiKey, sha);
168
167
  if (exists) {
169
168
  if (log) {
170
169
  core_1.ux.info(`sha hash matches existing binary with id: ${appBinaryId}, skipping upload. Force upload with --ignore-sha-check`);
@@ -177,7 +176,7 @@ const uploadBinary = async (filePath, apiUrl, apiKey, ignoreShaCheck = false, lo
177
176
  // ignore error
178
177
  }
179
178
  }
180
- const { id, message, path, token } = await ApiGateway_1.ApiGateway.getBinaryUploadUrl(apiUrl, apiKey, filePath?.endsWith('.apk') ? 'android' : 'ios');
179
+ const { id, message, path, token } = await api_gateway_1.ApiGateway.getBinaryUploadUrl(apiUrl, apiKey, filePath?.endsWith('.apk') ? 'android' : 'ios');
181
180
  if (!path)
182
181
  throw new Error(message);
183
182
  let metadata;
@@ -194,8 +193,8 @@ const uploadBinary = async (filePath, apiUrl, apiKey, ignoreShaCheck = false, lo
194
193
  }
195
194
  }
196
195
  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);
196
+ await supabase_gateway_1.SupabaseGateway.uploadToSignedUrl(env, path, token, file);
197
+ await api_gateway_1.ApiGateway.finaliseUpload(apiUrl, apiKey, id, metadata, path, sha);
199
198
  if (log) {
200
199
  core_1.ux.action.stop(`\nBinary uploaded with id: ${id}`);
201
200
  }
@@ -224,16 +223,16 @@ async function getFileHashFromFile(file) {
224
223
  const reader = stream.getReader();
225
224
  const processChunks = async () => {
226
225
  try {
227
- while (true) {
228
- const { done, value } = await reader.read();
229
- if (done)
230
- break;
226
+ let readerResult = await reader.read();
227
+ while (!readerResult.done) {
228
+ const { value } = readerResult;
231
229
  hash.update(value);
230
+ readerResult = await reader.read();
232
231
  }
233
232
  resolve(hash.digest('hex'));
234
233
  }
235
- catch (err) {
236
- reject(err);
234
+ catch (error) {
235
+ reject(error);
237
236
  }
238
237
  };
239
238
  processChunks();
package/dist/plan.d.ts CHANGED
@@ -15,9 +15,9 @@ interface IExecutionOrder {
15
15
  interface IExecutionPlan {
16
16
  allExcludeTags?: null | string[];
17
17
  allIncludeTags?: null | string[];
18
+ flowMetadata: Record<string, Record<string, unknown>>;
18
19
  flowsToRun: string[];
19
20
  referencedFiles: string[];
20
- flowMetadata: Record<string, Record<string, unknown>>;
21
21
  sequence?: IFlowSequence | null;
22
22
  totalFlowFiles: number;
23
23
  workspaceConfig?: IWorkspaceConfig;
package/dist/plan.js CHANGED
@@ -42,11 +42,11 @@ function filterFlowFiles(unfilteredFlowFiles, excludeFlows) {
42
42
  return unfilteredFlowFiles;
43
43
  }
44
44
  function getWorkspaceConfig(input, unfilteredFlowFiles) {
45
- const possibleConfigPaths = [
45
+ const possibleConfigPaths = new Set([
46
46
  path.join(input, 'config.yaml'),
47
47
  path.join(input, 'config.yml'),
48
- ].map((p) => path.normalize(p));
49
- const configFilePath = unfilteredFlowFiles.find((file) => possibleConfigPaths.includes(path.normalize(file)));
48
+ ].map((p) => path.normalize(p)));
49
+ const configFilePath = unfilteredFlowFiles.find((file) => possibleConfigPaths.has(path.normalize(file)));
50
50
  const config = configFilePath
51
51
  ? (0, planMethods_1.readYamlFileAsJson)(configFilePath)
52
52
  : {};
@@ -69,9 +69,9 @@ async function plan(input, includeTags, excludeTags, excludeFlows, configFile) {
69
69
  }
70
70
  const checkedDependancies = await checkDependencies(normalizedInput);
71
71
  return {
72
+ flowMetadata,
72
73
  flowsToRun: [normalizedInput],
73
74
  referencedFiles: [...new Set(checkedDependancies)],
74
- flowMetadata,
75
75
  totalFlowFiles: 1,
76
76
  };
77
77
  }
@@ -184,9 +184,9 @@ async function plan(input, includeTags, excludeTags, excludeFlows, configFile) {
184
184
  return {
185
185
  allExcludeTags,
186
186
  allIncludeTags,
187
+ flowMetadata,
187
188
  flowsToRun: normalFlows,
188
189
  referencedFiles: [...new Set(allFiles)],
189
- flowMetadata,
190
190
  sequence: {
191
191
  continueOnFailure: workspaceConfig.executionOrder?.continueOnFailure,
192
192
  flows: flowsToRunInSequence,
@@ -6,9 +6,6 @@ export declare const readYamlFileAsJson: (filePath: string) => unknown;
6
6
  export declare const readTestYamlFileAsJson: (filePath: string) => {
7
7
  config: Record<string, unknown>;
8
8
  testSteps: Record<string, unknown>[];
9
- } | {
10
- config: null;
11
- testSteps: Record<string, unknown>[];
12
9
  };
13
10
  export declare function readDirectory(dir: string, filterFunction?: (filePath: string) => boolean): Promise<string[]>;
14
11
  export declare const checkIfFilesExistInWorkspace: (commandName: string, command: Record<string, string> | string | string[], absoluteFilePath: string) => {
@@ -69,7 +69,7 @@ const readTestYamlFileAsJson = (filePath) => {
69
69
  const yamlText = fs.readFileSync(normalizedPath, 'utf8');
70
70
  const normalizedText = yamlText
71
71
  .replaceAll('\r\n', '\n')
72
- .replaceAll(/\n---[ \t]*\n/g, '\n---\n');
72
+ .replaceAll(/\n---[\t ]*\n/g, '\n---\n');
73
73
  if (normalizedText.includes('\n---\n')) {
74
74
  const yamlTexts = normalizedText.split('\n---\n');
75
75
  const config = yaml.load(yamlTexts[0]);
@@ -21,16 +21,16 @@ export declare enum EAndroidDevices {
21
21
  'pixel-7-pro' = "pixel-7-pro"
22
22
  }
23
23
  export declare enum EiOSVersions {
24
- 'sixteen' = "16",
24
+ 'eighteen' = "18",
25
25
  'seventeen' = "17",
26
- 'eighteen' = "18"
26
+ 'sixteen' = "16"
27
27
  }
28
28
  export declare enum EAndroidApiLevels {
29
- 'twentyNine' = "29",
30
29
  'thirty' = "30",
30
+ 'thirtyFive' = "35",
31
+ 'thirtyFour' = "34",
31
32
  'thirtyOne' = "31",
32
- 'thirtyTwo' = "32",
33
33
  'thirtyThree' = "33",
34
- 'thirtyFour' = "34",
35
- 'thirtyFive' = "35"
34
+ 'thirtyTwo' = "32",
35
+ 'twentyNine' = "29"
36
36
  }
@@ -27,17 +27,17 @@ var EAndroidDevices;
27
27
  })(EAndroidDevices || (exports.EAndroidDevices = EAndroidDevices = {}));
28
28
  var EiOSVersions;
29
29
  (function (EiOSVersions) {
30
- EiOSVersions["sixteen"] = "16";
31
- EiOSVersions["seventeen"] = "17";
32
30
  EiOSVersions["eighteen"] = "18";
31
+ EiOSVersions["seventeen"] = "17";
32
+ EiOSVersions["sixteen"] = "16";
33
33
  })(EiOSVersions || (exports.EiOSVersions = EiOSVersions = {}));
34
34
  var EAndroidApiLevels;
35
35
  (function (EAndroidApiLevels) {
36
- EAndroidApiLevels["twentyNine"] = "29";
37
36
  EAndroidApiLevels["thirty"] = "30";
37
+ EAndroidApiLevels["thirtyFive"] = "35";
38
+ EAndroidApiLevels["thirtyFour"] = "34";
38
39
  EAndroidApiLevels["thirtyOne"] = "31";
39
- EAndroidApiLevels["thirtyTwo"] = "32";
40
40
  EAndroidApiLevels["thirtyThree"] = "33";
41
- EAndroidApiLevels["thirtyFour"] = "34";
42
- EAndroidApiLevels["thirtyFive"] = "35";
41
+ EAndroidApiLevels["thirtyTwo"] = "32";
42
+ EAndroidApiLevels["twentyNine"] = "29";
43
43
  })(EAndroidApiLevels || (exports.EAndroidApiLevels = EAndroidApiLevels = {}));