@devicecloud.dev/dcd 4.1.7 → 4.2.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.
@@ -16,30 +16,30 @@ class TestSubmissionService {
16
16
  * @returns FormData ready to be submitted to the API
17
17
  */
18
18
  async buildTestFormData(config) {
19
- const { appBinaryId, flowFile, executionPlan, commonRoot, cliVersion, env = [], metadata = [], googlePlay = false, androidApiLevel, androidDevice, iOSVersion, iOSDevice, name, runnerType, maestroVersion, deviceLocale, orientation, mitmHost, mitmPath, retry, continueOnFailure = true, report, showCrosshairs, skipChromeOnboarding, raw, debug = false, logger, } = config;
19
+ const { appBinaryId, flowFile, executionPlan, commonRoot, cliVersion, env = [], metadata = [], googlePlay = false, androidApiLevel, androidDevice, iOSVersion, iOSDevice, name, runnerType, maestroVersion, deviceLocale, orientation, mitmHost, mitmPath, retry, continueOnFailure = true, report, showCrosshairs, raw, debug = false, logger, } = config;
20
20
  const { allExcludeTags, allIncludeTags, flowMetadata, flowOverrides, flowsToRun: testFileNames, referencedFiles, sequence, workspaceConfig, } = executionPlan;
21
21
  const { flows: sequentialFlows = [] } = sequence ?? {};
22
22
  const testFormData = new FormData();
23
23
  const envObject = this.parseKeyValuePairs(env);
24
24
  const metadataObject = this.parseKeyValuePairs(metadata);
25
25
  if (Object.keys(envObject).length > 0) {
26
- this.logDebug(debug, logger, `DEBUG: Environment variables: ${JSON.stringify(envObject)}`);
26
+ this.logDebug(debug, logger, `[DEBUG] Environment variables: ${JSON.stringify(envObject)}`);
27
27
  }
28
28
  if (Object.keys(metadataObject).length > 0) {
29
- this.logDebug(debug, logger, `DEBUG: User metadata: ${JSON.stringify(metadataObject)}`);
29
+ this.logDebug(debug, logger, `[DEBUG] User metadata: ${JSON.stringify(metadataObject)}`);
30
30
  }
31
31
  // Log non-YAML file assets being uploaded
32
32
  if (referencedFiles.length > 0) {
33
33
  const nonYamlFiles = referencedFiles.filter((file) => !file.endsWith('.yaml') && !file.endsWith('.yml'));
34
34
  if (nonYamlFiles.length > 0) {
35
- this.logDebug(debug, logger, `DEBUG: Uploading ${nonYamlFiles.length} non-YAML file asset(s):`);
35
+ this.logDebug(debug, logger, `[DEBUG] Uploading ${nonYamlFiles.length} non-YAML file asset(s):`);
36
36
  for (const file of nonYamlFiles) {
37
37
  const normalizedPath = this.normalizeFilePath(file, commonRoot);
38
- this.logDebug(debug, logger, `DEBUG: - ${normalizedPath}`);
38
+ this.logDebug(debug, logger, `[DEBUG] - ${normalizedPath}`);
39
39
  }
40
40
  }
41
41
  }
42
- this.logDebug(debug, logger, `DEBUG: Compressing files from path: ${flowFile}`);
42
+ this.logDebug(debug, logger, `[DEBUG] Compressing files from path: ${flowFile}`);
43
43
  const buffer = await (0, methods_1.compressFilesFromRelativePath)(flowFile?.endsWith('.yaml') || flowFile?.endsWith('.yml')
44
44
  ? path.dirname(flowFile)
45
45
  : flowFile, [
@@ -49,7 +49,7 @@ class TestSubmissionService {
49
49
  ...sequentialFlows,
50
50
  ]),
51
51
  ], commonRoot);
52
- this.logDebug(debug, logger, `DEBUG: Compressed file size: ${buffer.length} bytes`);
52
+ this.logDebug(debug, logger, `[DEBUG] Compressed file size: ${buffer.length} bytes`);
53
53
  const blob = new Blob([buffer], {
54
54
  type: mimeTypeLookupByExtension.zip,
55
55
  });
@@ -74,14 +74,13 @@ class TestSubmissionService {
74
74
  raw: JSON.stringify(raw),
75
75
  report,
76
76
  showCrosshairs,
77
- skipChromeOnboarding,
78
77
  version: cliVersion,
79
78
  };
80
79
  testFormData.set('config', JSON.stringify(configPayload));
81
80
  if (Object.keys(metadataObject).length > 0) {
82
81
  const metadataPayload = { userMetadata: metadataObject };
83
82
  testFormData.set('metadata', JSON.stringify(metadataPayload));
84
- this.logDebug(debug, logger, `DEBUG: Sending metadata to API: ${JSON.stringify(metadataPayload)}`);
83
+ this.logDebug(debug, logger, `[DEBUG] Sending metadata to API: ${JSON.stringify(metadataPayload)}`);
85
84
  }
86
85
  this.setOptionalFields(testFormData, {
87
86
  androidApiLevel,
@@ -59,13 +59,13 @@ class VersionService {
59
59
  if (requestedVersion === 'latest') {
60
60
  resolvedVersion = latestVersion;
61
61
  if (debug) {
62
- log(`DEBUG: Resolved "latest" to ${latestVersion}`);
62
+ log(`[DEBUG] Resolved "latest" to ${latestVersion}`);
63
63
  }
64
64
  }
65
65
  else if (!requestedVersion) {
66
66
  resolvedVersion = defaultVersion;
67
67
  if (debug) {
68
- log(`DEBUG: Using default Maestro version ${defaultVersion}`);
68
+ log(`[DEBUG] Using default Maestro version ${defaultVersion}`);
69
69
  }
70
70
  }
71
71
  // Validate Maestro version
@@ -73,8 +73,8 @@ class VersionService {
73
73
  throw new Error(`Maestro version ${resolvedVersion} is not supported. Supported versions: ${supportedVersions.join(', ')}`);
74
74
  }
75
75
  if (debug) {
76
- log(`DEBUG: Maestro version validated: ${resolvedVersion}`);
77
- log(`DEBUG: Supported Maestro versions: ${supportedVersions.join(', ')}`);
76
+ log(`[DEBUG] Maestro version validated: ${resolvedVersion}`);
77
+ log(`[DEBUG] Supported Maestro versions: ${supportedVersions.join(', ')}`);
78
78
  }
79
79
  return resolvedVersion;
80
80
  }
@@ -67,6 +67,22 @@ export interface paths {
67
67
  patch?: never;
68
68
  trace?: never;
69
69
  };
70
+ "/uploads/finishLargeFile": {
71
+ parameters: {
72
+ query?: never;
73
+ header?: never;
74
+ path?: never;
75
+ cookie?: never;
76
+ };
77
+ get?: never;
78
+ put?: never;
79
+ post: operations["UploadsController_finishLargeFile"];
80
+ delete?: never;
81
+ options?: never;
82
+ head?: never;
83
+ patch?: never;
84
+ trace?: never;
85
+ };
70
86
  "/uploads/flow": {
71
87
  parameters: {
72
88
  query?: never;
@@ -437,27 +453,38 @@ export interface components {
437
453
  };
438
454
  IGetBinaryUploadUrlArgs: {
439
455
  platform: Record<string, never>;
456
+ fileSize: number;
457
+ };
458
+ B2SimpleUpload: {
459
+ uploadUrl: string;
460
+ authorizationToken: string;
461
+ };
462
+ B2UploadPartUrl: {
463
+ uploadUrl: string;
464
+ authorizationToken: string;
465
+ };
466
+ B2LargeUpload: {
467
+ fileId: string;
468
+ fileName: string;
469
+ uploadPartUrls: components["schemas"]["B2UploadPartUrl"][];
470
+ };
471
+ B2UploadStrategy: {
472
+ /** @enum {string} */
473
+ strategy: "simple" | "large";
474
+ simple?: components["schemas"]["B2SimpleUpload"];
475
+ large?: components["schemas"]["B2LargeUpload"];
440
476
  };
441
477
  IGetBinaryUploadUrlResponse: {
442
- message: string;
478
+ /** @description Temporary upload path in uploads/ folder for TUS upload */
443
479
  path: string;
444
- token: string;
480
+ /** @description Temporary upload path (same as path) */
481
+ tempPath: string;
482
+ /** @description Final path where file will be moved after upload completes */
483
+ finalPath: string;
484
+ /** @description Upload ID */
445
485
  id: string;
446
- b2?: {
447
- strategy: 'simple' | 'large';
448
- simple?: {
449
- uploadUrl: string;
450
- authorizationToken: string;
451
- };
452
- large?: {
453
- fileId: string;
454
- fileName: string;
455
- uploadPartUrls: Array<{
456
- uploadUrl: string;
457
- authorizationToken: string;
458
- }>;
459
- };
460
- };
486
+ /** @description Backblaze upload strategy if configured */
487
+ b2?: components["schemas"]["B2UploadStrategy"];
461
488
  };
462
489
  ICheckForExistingUploadArgs: {
463
490
  sha: string;
@@ -467,12 +494,43 @@ export interface components {
467
494
  exists: boolean;
468
495
  };
469
496
  IFinaliseUploadArgs: {
497
+ /** @description Final path where file should be moved to (for TUS uploads) */
498
+ finalPath?: string;
499
+ /**
500
+ * @description Whether the Supabase upload was successful
501
+ * @default true
502
+ */
503
+ supabaseSuccess: boolean;
504
+ /**
505
+ * @description Whether the Backblaze upload was successful
506
+ * @default false
507
+ */
508
+ backblazeSuccess: boolean;
470
509
  id: string;
471
510
  path: string;
472
511
  metadata: Record<string, never>;
473
512
  sha: string;
474
513
  };
475
514
  IFinaliseUploadResponse: Record<string, never>;
515
+ IFinishLargeFileArgs: {
516
+ /**
517
+ * @description The Backblaze file ID from the large file upload
518
+ * @example abc123xyz
519
+ */
520
+ fileId: string;
521
+ /**
522
+ * @description Array of SHA1 hashes for each uploaded part
523
+ * @example [
524
+ * "sha1hash1",
525
+ * "sha1hash2"
526
+ * ]
527
+ */
528
+ partSha1Array: string[];
529
+ };
530
+ IFinishLargeFileResponse: {
531
+ success: boolean;
532
+ result: Record<string, never>;
533
+ };
476
534
  ICreateTestUploadArgs: {
477
535
  /**
478
536
  * Format: binary
@@ -522,6 +580,18 @@ export interface components {
522
580
  fail_reason?: string;
523
581
  duration_seconds?: number;
524
582
  };
583
+ UpdateOrgNameDto: {
584
+ /**
585
+ * @description Organization ID
586
+ * @example 123
587
+ */
588
+ orgId: number;
589
+ /**
590
+ * @description Organization name
591
+ * @example Acme Corporation
592
+ */
593
+ name: string;
594
+ };
525
595
  };
526
596
  responses: never;
527
597
  parameters: never;
@@ -647,6 +717,32 @@ export interface operations {
647
717
  };
648
718
  };
649
719
  };
720
+ UploadsController_finishLargeFile: {
721
+ parameters: {
722
+ query?: never;
723
+ header: {
724
+ "x-app-api-key": string;
725
+ };
726
+ path?: never;
727
+ cookie?: never;
728
+ };
729
+ requestBody: {
730
+ content: {
731
+ "application/json": components["schemas"]["IFinishLargeFileArgs"];
732
+ };
733
+ };
734
+ responses: {
735
+ /** @description The large file upload has been completed. */
736
+ 201: {
737
+ headers: {
738
+ [name: string]: unknown;
739
+ };
740
+ content: {
741
+ "application/json": components["schemas"]["IFinishLargeFileResponse"];
742
+ };
743
+ };
744
+ };
745
+ };
650
746
  UploadsController_createTest: {
651
747
  parameters: {
652
748
  query?: never;
@@ -1142,10 +1238,11 @@ export interface operations {
1142
1238
  * "1.41.0",
1143
1239
  * "2.0.2",
1144
1240
  * "2.0.3",
1145
- * "2.0.4"
1241
+ * "2.0.4",
1242
+ * "2.0.9"
1146
1243
  * ],
1147
1244
  * "defaultVersion": "1.41.0",
1148
- * "latestVersion": "2.0.4"
1245
+ * "latestVersion": "2.0.9"
1149
1246
  * }
1150
1247
  * }
1151
1248
  * }
@@ -1372,10 +1469,7 @@ export interface operations {
1372
1469
  };
1373
1470
  requestBody: {
1374
1471
  content: {
1375
- "application/json": {
1376
- orgId: string;
1377
- name: string;
1378
- };
1472
+ "application/json": components["schemas"]["UpdateOrgNameDto"];
1379
1473
  };
1380
1474
  };
1381
1475
  responses: {
@@ -67,6 +67,22 @@ export interface paths {
67
67
  patch?: never;
68
68
  trace?: never;
69
69
  };
70
+ "/uploads/finishLargeFile": {
71
+ parameters: {
72
+ query?: never;
73
+ header?: never;
74
+ path?: never;
75
+ cookie?: never;
76
+ };
77
+ get?: never;
78
+ put?: never;
79
+ post: operations["UploadsController_finishLargeFile"];
80
+ delete?: never;
81
+ options?: never;
82
+ head?: never;
83
+ patch?: never;
84
+ trace?: never;
85
+ };
70
86
  "/uploads/flow": {
71
87
  parameters: {
72
88
  query?: never;
@@ -436,13 +452,44 @@ export interface components {
436
452
  binaryId: string;
437
453
  };
438
454
  IGetBinaryUploadUrlArgs: {
455
+ /** @description File size in bytes (optional, for Backblaze upload strategy) */
456
+ fileSize?: number;
457
+ /** @description Whether client uses TUS resumable uploads (true for new clients, undefined/false for legacy) */
458
+ useTus?: boolean;
439
459
  platform: Record<string, never>;
440
460
  };
461
+ B2SimpleUpload: {
462
+ uploadUrl: string;
463
+ authorizationToken: string;
464
+ };
465
+ B2UploadPartUrl: {
466
+ uploadUrl: string;
467
+ authorizationToken: string;
468
+ };
469
+ B2LargeUpload: {
470
+ fileId: string;
471
+ fileName: string;
472
+ uploadPartUrls: components["schemas"]["B2UploadPartUrl"][];
473
+ };
474
+ B2UploadStrategy: {
475
+ /** @enum {string} */
476
+ strategy: "simple" | "large";
477
+ simple?: components["schemas"]["B2SimpleUpload"];
478
+ large?: components["schemas"]["B2LargeUpload"];
479
+ };
441
480
  IGetBinaryUploadUrlResponse: {
442
- message: string;
481
+ /** @description Temporary upload path in uploads/ folder for TUS upload */
443
482
  path: string;
444
- token: string;
483
+ /** @description Temporary upload path (same as path) */
484
+ tempPath: string;
485
+ /** @description Final path where file will be moved after upload completes */
486
+ finalPath: string;
487
+ /** @description Upload ID */
445
488
  id: string;
489
+ /** @description Backblaze upload strategy if configured */
490
+ b2?: components["schemas"]["B2UploadStrategy"];
491
+ /** @description Signed upload URL token for legacy clients (deprecated) */
492
+ token?: string;
446
493
  };
447
494
  ICheckForExistingUploadArgs: {
448
495
  sha: string;
@@ -452,12 +499,45 @@ export interface components {
452
499
  exists: boolean;
453
500
  };
454
501
  IFinaliseUploadArgs: {
502
+ /** @description File metadata (bundle ID, package name, platform) - required for new clients */
503
+ metadata?: Record<string, never>;
504
+ /** @description SHA-256 hash of the file - required for new clients */
505
+ sha?: string;
506
+ /**
507
+ * @description Whether the Supabase upload was successful
508
+ * @default true
509
+ */
510
+ supabaseSuccess: boolean;
511
+ /**
512
+ * @description Whether the Backblaze upload was successful
513
+ * @default false
514
+ */
515
+ backblazeSuccess: boolean;
516
+ /** @description Whether client uses TUS resumable uploads (true for new clients, undefined/false for legacy) */
517
+ useTus?: boolean;
455
518
  id: string;
456
519
  path: string;
457
- metadata: Record<string, never>;
458
- sha: string;
459
520
  };
460
521
  IFinaliseUploadResponse: Record<string, never>;
522
+ IFinishLargeFileArgs: {
523
+ /**
524
+ * @description The Backblaze file ID from the large file upload
525
+ * @example abc123xyz
526
+ */
527
+ fileId: string;
528
+ /**
529
+ * @description Array of SHA1 hashes for each uploaded part
530
+ * @example [
531
+ * "sha1hash1",
532
+ * "sha1hash2"
533
+ * ]
534
+ */
535
+ partSha1Array: string[];
536
+ };
537
+ IFinishLargeFileResponse: {
538
+ success: boolean;
539
+ result: Record<string, never>;
540
+ };
461
541
  ICreateTestUploadArgs: {
462
542
  /**
463
543
  * Format: binary
@@ -644,6 +724,32 @@ export interface operations {
644
724
  };
645
725
  };
646
726
  };
727
+ UploadsController_finishLargeFile: {
728
+ parameters: {
729
+ query?: never;
730
+ header: {
731
+ "x-app-api-key": string;
732
+ };
733
+ path?: never;
734
+ cookie?: never;
735
+ };
736
+ requestBody: {
737
+ content: {
738
+ "application/json": components["schemas"]["IFinishLargeFileArgs"];
739
+ };
740
+ };
741
+ responses: {
742
+ /** @description The large file upload has been completed. */
743
+ 201: {
744
+ headers: {
745
+ [name: string]: unknown;
746
+ };
747
+ content: {
748
+ "application/json": components["schemas"]["IFinishLargeFileResponse"];
749
+ };
750
+ };
751
+ };
752
+ };
647
753
  UploadsController_createTest: {
648
754
  parameters: {
649
755
  query?: never;
@@ -1139,10 +1245,11 @@ export interface operations {
1139
1245
  * "1.41.0",
1140
1246
  * "2.0.2",
1141
1247
  * "2.0.3",
1142
- * "2.0.4"
1248
+ * "2.0.4",
1249
+ * "2.0.9"
1143
1250
  * ],
1144
1251
  * "defaultVersion": "1.41.0",
1145
- * "latestVersion": "2.0.4"
1252
+ * "latestVersion": "2.0.9"
1146
1253
  * }
1147
1254
  * }
1148
1255
  * }
@@ -168,12 +168,6 @@
168
168
  "allowNo": false,
169
169
  "type": "boolean"
170
170
  },
171
- "skip-chrome-onboarding": {
172
- "description": "[Android only] Skip Chrome browser onboarding screens when running tests",
173
- "name": "skip-chrome-onboarding",
174
- "allowNo": false,
175
- "type": "boolean"
176
- },
177
171
  "env": {
178
172
  "char": "e",
179
173
  "description": "One or more environment variables to inject into your flows",
@@ -572,5 +566,5 @@
572
566
  ]
573
567
  }
574
568
  },
575
- "version": "4.1.7"
569
+ "version": "4.2.0"
576
570
  }
package/package.json CHANGED
@@ -7,6 +7,7 @@
7
7
  "@oclif/core": "^3.27.0",
8
8
  "@oclif/plugin-help": "^6.2.36",
9
9
  "@supabase/supabase-js": "^2.81.1",
10
+ "@types/tus-js-client": "^2.1.0",
10
11
  "app-info-parser": "^1.1.6",
11
12
  "archiver": "^7.0.1",
12
13
  "bplist-parser": "^0.3.2",
@@ -14,7 +15,8 @@
14
15
  "glob": "^11.1.0",
15
16
  "js-yaml": "^4.1.1",
16
17
  "node-stream-zip": "^1.15.0",
17
- "plist": "^3.1.0"
18
+ "plist": "^3.1.0",
19
+ "tus-js-client": "^4.3.1"
18
20
  },
19
21
  "description": "Better cloud maestro testing",
20
22
  "devDependencies": {
@@ -66,7 +68,7 @@
66
68
  "type": "git",
67
69
  "url": "https://devicecloud.dev"
68
70
  },
69
- "version": "4.1.7",
71
+ "version": "4.2.0",
70
72
  "bugs": {
71
73
  "url": "https://discord.gg/gm3mJwcNw8"
72
74
  },