@devicecloud.dev/dcd 5.0.0-beta.0 → 5.0.0-beta.2

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.
Files changed (104) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -0
  3. package/dist/commands/artifacts.d.ts +28 -28
  4. package/dist/commands/artifacts.js +20 -23
  5. package/dist/commands/cloud.d.ts +57 -57
  6. package/dist/commands/cloud.js +224 -192
  7. package/dist/commands/list.d.ts +22 -22
  8. package/dist/commands/list.js +43 -40
  9. package/dist/commands/live.js +134 -127
  10. package/dist/commands/login.d.ts +11 -11
  11. package/dist/commands/login.js +46 -44
  12. package/dist/commands/logout.js +16 -18
  13. package/dist/commands/status.d.ts +11 -11
  14. package/dist/commands/status.js +53 -44
  15. package/dist/commands/switch-org.d.ts +7 -7
  16. package/dist/commands/switch-org.js +19 -21
  17. package/dist/commands/upgrade.js +41 -33
  18. package/dist/commands/upload.d.ts +10 -10
  19. package/dist/commands/upload.js +42 -43
  20. package/dist/commands/whoami.js +17 -20
  21. package/dist/config/environments.js +6 -12
  22. package/dist/config/flags/api.flags.js +1 -4
  23. package/dist/config/flags/binary.flags.js +1 -4
  24. package/dist/config/flags/device.flags.js +6 -9
  25. package/dist/config/flags/environment.flags.js +1 -4
  26. package/dist/config/flags/execution.flags.js +1 -4
  27. package/dist/config/flags/github.flags.js +1 -4
  28. package/dist/config/flags/output.flags.js +1 -4
  29. package/dist/constants.js +15 -18
  30. package/dist/gateways/api-gateway.d.ts +31 -6
  31. package/dist/gateways/api-gateway.js +70 -16
  32. package/dist/gateways/cli-auth-gateway.d.ts +1 -1
  33. package/dist/gateways/cli-auth-gateway.js +3 -6
  34. package/dist/gateways/realtime-gateway.d.ts +32 -0
  35. package/dist/gateways/realtime-gateway.js +103 -0
  36. package/dist/gateways/supabase-gateway.d.ts +1 -1
  37. package/dist/gateways/supabase-gateway.js +10 -14
  38. package/dist/index.js +41 -38
  39. package/dist/mcp/context.d.ts +33 -0
  40. package/dist/mcp/context.js +33 -0
  41. package/dist/mcp/helpers.d.ts +16 -0
  42. package/dist/mcp/helpers.js +34 -0
  43. package/dist/mcp/index.d.ts +2 -0
  44. package/dist/mcp/index.js +24 -0
  45. package/dist/mcp/server.d.ts +7 -0
  46. package/dist/mcp/server.js +27 -0
  47. package/dist/mcp/tools/download-artifacts.d.ts +11 -0
  48. package/dist/mcp/tools/download-artifacts.js +84 -0
  49. package/dist/mcp/tools/get-status.d.ts +7 -0
  50. package/dist/mcp/tools/get-status.js +39 -0
  51. package/dist/mcp/tools/list-devices.d.ts +7 -0
  52. package/dist/mcp/tools/list-devices.js +27 -0
  53. package/dist/mcp/tools/list-runs.d.ts +3 -0
  54. package/dist/mcp/tools/list-runs.js +60 -0
  55. package/dist/mcp/tools/run-cloud-test.d.ts +14 -0
  56. package/dist/mcp/tools/run-cloud-test.js +233 -0
  57. package/dist/methods.d.ts +32 -1
  58. package/dist/methods.js +133 -79
  59. package/dist/services/device-validation.service.d.ts +1 -1
  60. package/dist/services/device-validation.service.js +1 -5
  61. package/dist/services/execution-plan.service.js +14 -17
  62. package/dist/services/execution-plan.utils.js +15 -23
  63. package/dist/services/flow-paths.d.ts +17 -0
  64. package/dist/services/flow-paths.js +52 -0
  65. package/dist/services/metadata-extractor.service.js +22 -25
  66. package/dist/services/moropo.service.js +18 -20
  67. package/dist/services/report-download.service.d.ts +1 -1
  68. package/dist/services/report-download.service.js +5 -9
  69. package/dist/services/results-polling.service.d.ts +18 -3
  70. package/dist/services/results-polling.service.js +211 -108
  71. package/dist/services/telemetry.service.d.ts +10 -1
  72. package/dist/services/telemetry.service.js +40 -18
  73. package/dist/services/test-submission.service.d.ts +21 -4
  74. package/dist/services/test-submission.service.js +51 -34
  75. package/dist/services/version.service.d.ts +30 -7
  76. package/dist/services/version.service.js +88 -32
  77. package/dist/types/domain/auth.types.d.ts +8 -0
  78. package/dist/types/domain/auth.types.js +1 -2
  79. package/dist/types/domain/device.types.js +8 -11
  80. package/dist/types/domain/live.types.js +1 -2
  81. package/dist/types/generated/schema.types.js +1 -2
  82. package/dist/types/index.d.ts +2 -2
  83. package/dist/types/index.js +2 -18
  84. package/dist/types.js +1 -2
  85. package/dist/utils/auth.d.ts +1 -1
  86. package/dist/utils/auth.js +27 -28
  87. package/dist/utils/ci.d.ts +12 -0
  88. package/dist/utils/ci.js +39 -0
  89. package/dist/utils/cli.d.ts +16 -2
  90. package/dist/utils/cli.js +57 -29
  91. package/dist/utils/compatibility.d.ts +1 -1
  92. package/dist/utils/compatibility.js +5 -7
  93. package/dist/utils/config-store.js +33 -43
  94. package/dist/utils/connectivity.js +1 -4
  95. package/dist/utils/expo.js +15 -21
  96. package/dist/utils/orgs.js +8 -12
  97. package/dist/utils/paths.js +2 -5
  98. package/dist/utils/progress.d.ts +3 -0
  99. package/dist/utils/progress.js +47 -8
  100. package/dist/utils/styling.d.ts +35 -37
  101. package/dist/utils/styling.js +52 -86
  102. package/dist/utils/ui.d.ts +41 -0
  103. package/dist/utils/ui.js +95 -0
  104. package/package.json +27 -24
@@ -1,27 +1,27 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TestSubmissionService = void 0;
4
- const node_crypto_1 = require("node:crypto");
5
- const path = require("node:path");
6
- const methods_1 = require("../methods");
7
- const paths_1 = require("../utils/paths");
1
+ import { createHash } from 'node:crypto';
2
+ import * as path from 'node:path';
3
+ import { compressFilesFromRelativePath } from '../methods.js';
4
+ import { toPortableRelativePath } from '../utils/paths.js';
8
5
  const mimeTypeLookupByExtension = {
9
6
  zip: 'application/zip',
10
7
  };
11
8
  /**
12
9
  * Service for building test submission form data
13
10
  */
14
- class TestSubmissionService {
11
+ export class TestSubmissionService {
15
12
  /**
16
- * Build FormData for test submission
13
+ * Build the test-submission payload: the compressed flow zip plus every
14
+ * non-`file` field, each encoded exactly as it is sent today. The same
15
+ * `fields` feed both the new JSON `submitFlowTest` body and the legacy
16
+ * multipart `buildFormData`, guaranteeing byte-identical field encoding
17
+ * across both paths.
17
18
  * @param config Test submission configuration
18
- * @returns FormData ready to be submitted to the API
19
+ * @returns The flow zip buffer, its SHA-256, and the string-encoded fields
19
20
  */
20
- async buildTestFormData(config) {
21
+ async buildTestPayload(config) {
21
22
  const { appBinaryId, flowFile, executionPlan, commonRoot, cliVersion, env = [], metadata = [], googlePlay = false, androidApiLevel, androidDevice, androidNoSnapshot, iOSVersion, iOSDevice, name, runnerType, maestroVersion, deviceLocale, orientation, mitmHost, mitmPath, retry, continueOnFailure = true, report, showCrosshairs, maestroChromeOnboarding, raw, disableAnimations, debug = false, logger, } = config;
22
23
  const { allExcludeTags, allIncludeTags, flowMetadata, flowOverrides, flowsToRun: testFileNames, referencedFiles, sequence, workspaceConfig, } = executionPlan;
23
24
  const { flows: sequentialFlows = [] } = sequence ?? {};
24
- const testFormData = new FormData();
25
25
  const envObject = this.parseKeyValuePairs(env);
26
26
  const metadataObject = this.parseKeyValuePairs(metadata);
27
27
  if (Object.keys(envObject).length > 0) {
@@ -42,7 +42,7 @@ class TestSubmissionService {
42
42
  }
43
43
  }
44
44
  this.logDebug(debug, logger, `[DEBUG] Compressing files from path: ${flowFile}`);
45
- const buffer = await (0, methods_1.compressFilesFromRelativePath)(flowFile?.endsWith('.yaml') || flowFile?.endsWith('.yml')
45
+ const buffer = await compressFilesFromRelativePath(flowFile?.endsWith('.yaml') || flowFile?.endsWith('.yml')
46
46
  ? path.dirname(flowFile)
47
47
  : flowFile, [
48
48
  ...new Set([
@@ -53,19 +53,18 @@ class TestSubmissionService {
53
53
  ], commonRoot);
54
54
  this.logDebug(debug, logger, `[DEBUG] Compressed file size: ${buffer.length} bytes`);
55
55
  // Calculate SHA-256 hash of the flow ZIP
56
- const sha = (0, node_crypto_1.createHash)('sha256').update(buffer).digest('hex');
56
+ const sha = createHash('sha256').update(buffer).digest('hex');
57
57
  this.logDebug(debug, logger, `[DEBUG] Flow ZIP SHA-256: ${sha}`);
58
- const blob = new Blob([buffer], {
59
- type: mimeTypeLookupByExtension.zip,
60
- });
61
- testFormData.set('file', blob, 'flowFile.zip');
62
- testFormData.set('sha', sha);
63
- testFormData.set('appBinaryId', appBinaryId);
64
- testFormData.set('testFileNames', JSON.stringify(this.normalizePaths(testFileNames, commonRoot)));
65
- testFormData.set('flowMetadata', JSON.stringify(this.normalizePathMap(flowMetadata, commonRoot)));
66
- testFormData.set('testFileOverrides', JSON.stringify(this.normalizePathMap(flowOverrides, commonRoot)));
67
- testFormData.set('sequentialFlows', JSON.stringify(this.normalizePaths(sequentialFlows, commonRoot)));
68
- testFormData.set('env', JSON.stringify(envObject));
58
+ // String-encoded fields, in the same order and with the same encoding as
59
+ // the legacy multipart FormData. Reused verbatim by both submission paths.
60
+ const fields = {};
61
+ fields.sha = sha;
62
+ fields.appBinaryId = appBinaryId;
63
+ fields.testFileNames = JSON.stringify(this.normalizePaths(testFileNames, commonRoot));
64
+ fields.flowMetadata = JSON.stringify(this.normalizePathMap(flowMetadata, commonRoot));
65
+ fields.testFileOverrides = JSON.stringify(this.normalizePathMap(flowOverrides, commonRoot));
66
+ fields.sequentialFlows = JSON.stringify(this.normalizePaths(sequentialFlows, commonRoot));
67
+ fields.env = JSON.stringify(envObject);
69
68
  // Note: googlePlay is now included in configPayload below instead of as a separate field
70
69
  // to work around a FormData parsing issue in the API
71
70
  const targetPlatform = iOSDevice || iOSVersion ? 'ios' : 'android';
@@ -92,13 +91,13 @@ class TestSubmissionService {
92
91
  disableAnimations: effectiveDisableAnimations,
93
92
  version: cliVersion,
94
93
  };
95
- testFormData.set('config', JSON.stringify(configPayload));
94
+ fields.config = JSON.stringify(configPayload);
96
95
  if (Object.keys(metadataObject).length > 0) {
97
96
  const metadataPayload = { userMetadata: metadataObject };
98
- testFormData.set('metadata', JSON.stringify(metadataPayload));
97
+ fields.metadata = JSON.stringify(metadataPayload);
99
98
  this.logDebug(debug, logger, `[DEBUG] Sending metadata to API: ${JSON.stringify(metadataPayload)}`);
100
99
  }
101
- this.setOptionalFields(testFormData, {
100
+ this.setOptionalFields(fields, {
102
101
  androidApiLevel,
103
102
  androidDevice,
104
103
  iOSDevice,
@@ -107,9 +106,28 @@ class TestSubmissionService {
107
106
  runnerType,
108
107
  });
109
108
  if (workspaceConfig) {
110
- testFormData.set('workspaceConfig', JSON.stringify(workspaceConfig));
109
+ fields.workspaceConfig = JSON.stringify(workspaceConfig);
110
+ }
111
+ return { buffer, fields, sha };
112
+ }
113
+ /**
114
+ * Wraps the payload fields and flow zip into multipart FormData for the
115
+ * legacy `POST /uploads/flow` fallback. `file` is set first to preserve the
116
+ * exact part ordering the old code produced.
117
+ * @param fields String-encoded fields from {@link buildTestPayload}
118
+ * @param buffer The compressed flow zip
119
+ * @returns FormData ready to be submitted to the multipart API
120
+ */
121
+ buildFormData(fields, buffer) {
122
+ const formData = new FormData();
123
+ const blob = new Blob([buffer], {
124
+ type: mimeTypeLookupByExtension.zip,
125
+ });
126
+ formData.set('file', blob, 'flowFile.zip');
127
+ for (const [key, value] of Object.entries(fields)) {
128
+ formData.set(key, value);
111
129
  }
112
- return testFormData;
130
+ return formData;
113
131
  }
114
132
  logDebug(debug, logger, message) {
115
133
  if (debug && logger) {
@@ -117,7 +135,7 @@ class TestSubmissionService {
117
135
  }
118
136
  }
119
137
  normalizeFilePath(filePath, commonRoot) {
120
- return (0, paths_1.toPortableRelativePath)(filePath, commonRoot);
138
+ return toPortableRelativePath(filePath, commonRoot);
121
139
  }
122
140
  normalizePathMap(map, commonRoot) {
123
141
  return Object.fromEntries(Object.entries(map).map(([key, value]) => [
@@ -137,12 +155,11 @@ class TestSubmissionService {
137
155
  return acc;
138
156
  }, {});
139
157
  }
140
- setOptionalFields(formData, fields) {
158
+ setOptionalFields(target, fields) {
141
159
  for (const [key, value] of Object.entries(fields)) {
142
160
  if (value) {
143
- formData.set(key, value.toString());
161
+ target[key] = value.toString();
144
162
  }
145
163
  }
146
164
  }
147
165
  }
148
- exports.TestSubmissionService = TestSubmissionService;
@@ -1,4 +1,19 @@
1
- import { CompatibilityData } from '../utils/compatibility';
1
+ import { CompatibilityData } from '../utils/compatibility.js';
2
+ export type ReleaseChannel = 'beta' | 'stable';
3
+ /**
4
+ * Outcome of a release-manifest lookup. `ok: true` means the manifest was
5
+ * reachable — `version` is the published version on the channel, or `null` when
6
+ * nothing is published there yet. `ok: false` means the lookup itself failed
7
+ * (network/timeout/non-2xx).
8
+ */
9
+ export type LatestVersionResult = {
10
+ ok: true;
11
+ channel: ReleaseChannel;
12
+ version: null | string;
13
+ } | {
14
+ ok: false;
15
+ error: string;
16
+ };
2
17
  /**
3
18
  * Service for handling version validation and checking
4
19
  */
@@ -6,14 +21,22 @@ export declare class VersionService {
6
21
  /**
7
22
  * Fetch the latest published CLI version from the release manifest.
8
23
  * Works for both npm- and binary-installed users (no `npm` shell-out).
9
- * Silently returns null on any failure — this check is informational only.
24
+ *
25
+ * The result is discriminated so callers can tell "reachable, but no release
26
+ * on this channel yet" (`ok: true, version: null`) apart from an actual
27
+ * network/manifest failure (`ok: false`) — the old single-`null` return
28
+ * conflated the two and produced a misleading "check your network" error
29
+ * during the beta. Prerelease installs (current version contains `-`) query
30
+ * the opt-in beta channel; everyone else gets the stable channel.
10
31
  */
11
- checkLatestCliVersion(): Promise<null | string>;
32
+ checkLatestCliVersion(currentVersion?: string): Promise<LatestVersionResult>;
12
33
  /**
13
- * Compare two semantic version strings
14
- * @param current - Current version
15
- * @param latest - Latest version
16
- * @returns true if current is older than latest
34
+ * Compare two semantic version strings (SemVer 2.0.0 precedence, including
35
+ * prerelease tags). Returns true if `current` is strictly older than `latest`.
36
+ *
37
+ * Prerelease handling matters here: a beta-to-beta bump such as
38
+ * "5.0.0-beta.0" -> "5.0.0-beta.1" shares the same major.minor.patch, so we
39
+ * must compare the prerelease identifiers to detect that an upgrade exists.
17
40
  */
18
41
  isOutdated(current: string, latest: string): boolean;
19
42
  /**
@@ -1,57 +1,114 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VersionService = void 0;
4
1
  const DEFAULT_MANIFEST_URL = 'https://get.devicecloud.dev/latest.json';
5
2
  const MANIFEST_TIMEOUT_MS = 3000;
3
+ /**
4
+ * Compare two semantic versions per SemVer 2.0.0 precedence rules.
5
+ * Returns a negative number if `a < b`, positive if `a > b`, and 0 if equal.
6
+ *
7
+ * Implements the prerelease rules that the previous naive comparator dropped:
8
+ * - A version WITH a prerelease has lower precedence than the same version
9
+ * without one ("1.0.0-beta" < "1.0.0").
10
+ * - Prerelease identifiers are compared dot-separated, left to right:
11
+ * numeric identifiers compare numerically, alphanumeric ones compare
12
+ * lexically (ASCII), and numeric always sorts below alphanumeric. A longer
13
+ * set of identifiers wins when all preceding ones are equal.
14
+ */
15
+ function compareSemver(a, b) {
16
+ const split = (v) => {
17
+ const [core, ...preParts] = v.trim().replace(/^v/, '').split('-');
18
+ const nums = core.split('.').map((n) => Number(n) || 0);
19
+ const pre = preParts.join('-');
20
+ return {
21
+ release: [nums[0] || 0, nums[1] || 0, nums[2] || 0],
22
+ pre: pre ? pre.split('.') : [],
23
+ };
24
+ };
25
+ const left = split(a);
26
+ const right = split(b);
27
+ for (let i = 0; i < 3; i++) {
28
+ if (left.release[i] !== right.release[i]) {
29
+ return left.release[i] - right.release[i];
30
+ }
31
+ }
32
+ // Equal release: a version with no prerelease outranks one that has it.
33
+ if (left.pre.length === 0 && right.pre.length === 0)
34
+ return 0;
35
+ if (left.pre.length === 0)
36
+ return 1;
37
+ if (right.pre.length === 0)
38
+ return -1;
39
+ const len = Math.min(left.pre.length, right.pre.length);
40
+ for (let i = 0; i < len; i++) {
41
+ const lp = left.pre[i];
42
+ const rp = right.pre[i];
43
+ if (lp === rp)
44
+ continue;
45
+ const ln = /^\d+$/.test(lp);
46
+ const rn = /^\d+$/.test(rp);
47
+ if (ln && rn)
48
+ return Number(lp) - Number(rp);
49
+ if (ln)
50
+ return -1; // numeric identifiers sort below alphanumeric
51
+ if (rn)
52
+ return 1;
53
+ return lp < rp ? -1 : 1;
54
+ }
55
+ return left.pre.length - right.pre.length;
56
+ }
6
57
  /**
7
58
  * Service for handling version validation and checking
8
59
  */
9
- class VersionService {
60
+ export class VersionService {
10
61
  /**
11
62
  * Fetch the latest published CLI version from the release manifest.
12
63
  * Works for both npm- and binary-installed users (no `npm` shell-out).
13
- * Silently returns null on any failure — this check is informational only.
64
+ *
65
+ * The result is discriminated so callers can tell "reachable, but no release
66
+ * on this channel yet" (`ok: true, version: null`) apart from an actual
67
+ * network/manifest failure (`ok: false`) — the old single-`null` return
68
+ * conflated the two and produced a misleading "check your network" error
69
+ * during the beta. Prerelease installs (current version contains `-`) query
70
+ * the opt-in beta channel; everyone else gets the stable channel.
14
71
  */
15
- async checkLatestCliVersion() {
16
- const url = process.env.DCD_MANIFEST_URL ?? DEFAULT_MANIFEST_URL;
72
+ async checkLatestCliVersion(currentVersion) {
73
+ const channel = currentVersion?.includes('-') ? 'beta' : 'stable';
74
+ const base = process.env.DCD_MANIFEST_URL ?? DEFAULT_MANIFEST_URL;
75
+ const url = channel === 'beta'
76
+ ? `${base}${base.includes('?') ? '&' : '?'}channel=beta`
77
+ : base;
17
78
  const controller = new AbortController();
18
79
  const timer = setTimeout(() => controller.abort(), MANIFEST_TIMEOUT_MS);
19
80
  try {
20
81
  const res = await fetch(url, { signal: controller.signal });
21
- if (!res.ok)
22
- return null;
82
+ if (!res.ok) {
83
+ return { ok: false, error: `manifest responded with HTTP ${res.status}` };
84
+ }
23
85
  const data = (await res.json());
24
- return typeof data.version === 'string' ? data.version : null;
86
+ return {
87
+ ok: true,
88
+ channel,
89
+ version: typeof data.version === 'string' ? data.version : null,
90
+ };
25
91
  }
26
- catch {
27
- return null;
92
+ catch (error) {
93
+ return {
94
+ ok: false,
95
+ error: error instanceof Error ? error.message : String(error),
96
+ };
28
97
  }
29
98
  finally {
30
99
  clearTimeout(timer);
31
100
  }
32
101
  }
33
102
  /**
34
- * Compare two semantic version strings
35
- * @param current - Current version
36
- * @param latest - Latest version
37
- * @returns true if current is older than latest
103
+ * Compare two semantic version strings (SemVer 2.0.0 precedence, including
104
+ * prerelease tags). Returns true if `current` is strictly older than `latest`.
105
+ *
106
+ * Prerelease handling matters here: a beta-to-beta bump such as
107
+ * "5.0.0-beta.0" -> "5.0.0-beta.1" shares the same major.minor.patch, so we
108
+ * must compare the prerelease identifiers to detect that an upgrade exists.
38
109
  */
39
110
  isOutdated(current, latest) {
40
- // Strip any prerelease suffix ("1.2.3-beta.1" -> "1.2.3") and default
41
- // missing segments to 0 so short/prerelease versions still compare.
42
- const parts = (version) => {
43
- const nums = version.split('-')[0].split('.').map(Number);
44
- return [nums[0] || 0, nums[1] || 0, nums[2] || 0];
45
- };
46
- const currentParts = parts(current);
47
- const latestParts = parts(latest);
48
- for (let i = 0; i < 3; i++) {
49
- if (currentParts[i] < latestParts[i])
50
- return true;
51
- if (currentParts[i] > latestParts[i])
52
- return false;
53
- }
54
- return false;
111
+ return compareSemver(current, latest) < 0;
55
112
  }
56
113
  /**
57
114
  * Resolve and validate Maestro version against API compatibility data
@@ -95,4 +152,3 @@ class VersionService {
95
152
  return resolvedVersion;
96
153
  }
97
154
  }
98
- exports.VersionService = VersionService;
@@ -1,3 +1,4 @@
1
+ import type { DcdEnvName } from '../../config/environments.js';
1
2
  /**
2
3
  * Auth context threaded through gateways and services. Callers build this once
3
4
  * (via resolveAuth) and the gateway spreads .headers into every fetch.
@@ -5,6 +6,13 @@
5
6
  export interface AuthContext {
6
7
  headers: Record<string, string>;
7
8
  mode: 'apiKey' | 'bearer';
9
+ /**
10
+ * Supabase JWT — present when mode === 'bearer'. Lets realtime subscriptions
11
+ * authenticate the socket (RLS) without re-reading the session from disk.
12
+ */
13
+ accessToken?: string;
14
+ /** Environment the session belongs to — present when mode === 'bearer'. */
15
+ env?: DcdEnvName;
8
16
  /** Present when mode === 'bearer'. */
9
17
  orgId?: string;
10
18
  /** Present when mode === 'bearer'. */
@@ -1,2 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ export {};
@@ -1,11 +1,8 @@
1
- "use strict";
2
1
  /**
3
2
  * Device type definitions - should be kept in sync with API
4
3
  * @see /Users/riglar/repos/dcd/api/src/common/types/device.types.ts
5
4
  */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.EAndroidApiLevels = exports.EiOSVersions = exports.EAndroidDevices = exports.EiOSDevices = void 0;
8
- var EiOSDevices;
5
+ export var EiOSDevices;
9
6
  (function (EiOSDevices) {
10
7
  EiOSDevices["ipad-pro-6th-gen"] = "ipad-pro-6th-gen";
11
8
  EiOSDevices["iphone-14"] = "iphone-14";
@@ -16,23 +13,23 @@ var EiOSDevices;
16
13
  EiOSDevices["iphone-16-plus"] = "iphone-16-plus";
17
14
  EiOSDevices["iphone-16-pro"] = "iphone-16-pro";
18
15
  EiOSDevices["iphone-16-pro-max"] = "iphone-16-pro-max";
19
- })(EiOSDevices || (exports.EiOSDevices = EiOSDevices = {}));
20
- var EAndroidDevices;
16
+ })(EiOSDevices || (EiOSDevices = {}));
17
+ export var EAndroidDevices;
21
18
  (function (EAndroidDevices) {
22
19
  EAndroidDevices["generic-tablet"] = "generic-tablet";
23
20
  EAndroidDevices["pixel-6"] = "pixel-6";
24
21
  EAndroidDevices["pixel-6-pro"] = "pixel-6-pro";
25
22
  EAndroidDevices["pixel-7"] = "pixel-7";
26
23
  EAndroidDevices["pixel-7-pro"] = "pixel-7-pro";
27
- })(EAndroidDevices || (exports.EAndroidDevices = EAndroidDevices = {}));
28
- var EiOSVersions;
24
+ })(EAndroidDevices || (EAndroidDevices = {}));
25
+ export var EiOSVersions;
29
26
  (function (EiOSVersions) {
30
27
  EiOSVersions["eighteen"] = "18";
31
28
  EiOSVersions["seventeen"] = "17";
32
29
  EiOSVersions["sixteen"] = "16";
33
30
  EiOSVersions["twentySix"] = "26";
34
- })(EiOSVersions || (exports.EiOSVersions = EiOSVersions = {}));
35
- var EAndroidApiLevels;
31
+ })(EiOSVersions || (EiOSVersions = {}));
32
+ export var EAndroidApiLevels;
36
33
  (function (EAndroidApiLevels) {
37
34
  EAndroidApiLevels["thirty"] = "30";
38
35
  EAndroidApiLevels["thirtyFive"] = "35";
@@ -42,4 +39,4 @@ var EAndroidApiLevels;
42
39
  EAndroidApiLevels["thirtyThree"] = "33";
43
40
  EAndroidApiLevels["thirtyTwo"] = "32";
44
41
  EAndroidApiLevels["twentyNine"] = "29";
45
- })(EAndroidApiLevels || (exports.EAndroidApiLevels = EAndroidApiLevels = {}));
42
+ })(EAndroidApiLevels || (EAndroidApiLevels = {}));
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  // Hand-defined: the swagger spec has no /live routes, so openapi-typescript
3
2
  // cannot generate these in schema.types.ts.
4
- Object.defineProperty(exports, "__esModule", { value: true });
3
+ export {};
@@ -1,3 +1,2 @@
1
- "use strict";
2
1
  /* eslint-disable prettier/prettier */
3
- Object.defineProperty(exports, "__esModule", { value: true });
2
+ export {};
@@ -2,5 +2,5 @@
2
2
  * Centralized type exports.
3
3
  * Types are organized by domain in subdirectories.
4
4
  */
5
- export * from './domain/device.types';
6
- export * from './generated/schema.types';
5
+ export * from './domain/device.types.js';
6
+ export * from './generated/schema.types.js';
@@ -1,24 +1,8 @@
1
- "use strict";
2
1
  /**
3
2
  * Centralized type exports.
4
3
  * Types are organized by domain in subdirectories.
5
4
  */
6
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
- if (k2 === undefined) k2 = k;
8
- var desc = Object.getOwnPropertyDescriptor(m, k);
9
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
- desc = { enumerable: true, get: function() { return m[k]; } };
11
- }
12
- Object.defineProperty(o, k2, desc);
13
- }) : (function(o, m, k, k2) {
14
- if (k2 === undefined) k2 = k;
15
- o[k2] = m[k];
16
- }));
17
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
- };
20
- Object.defineProperty(exports, "__esModule", { value: true });
21
5
  // Domain-specific types
22
- __exportStar(require("./domain/device.types"), exports);
6
+ export * from './domain/device.types.js';
23
7
  // Generated types from OpenAPI schema
24
- __exportStar(require("./generated/schema.types"), exports);
8
+ export * from './generated/schema.types.js';
package/dist/types.js CHANGED
@@ -1,2 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ export {};
@@ -1,4 +1,4 @@
1
- import type { AuthContext } from '../types/domain/auth.types';
1
+ import type { AuthContext } from '../types/domain/auth.types.js';
2
2
  export interface ResolveAuthOptions {
3
3
  apiKeyFlag: string | undefined;
4
4
  /** When true, bypass stored session entirely (for `dcd login` itself). */
@@ -1,6 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveAuth = resolveAuth;
4
1
  /**
5
2
  * Resolves which credential a command should use and returns the fetch headers
6
3
  * to send. Precedence: --api-key flag > DEVICE_CLOUD_API_KEY env > stored
@@ -10,19 +7,19 @@ exports.resolveAuth = resolveAuth;
10
7
  * use the returned AuthContext for the duration of the command — one resolve
11
8
  * per invocation, not per request.
12
9
  */
13
- const node_fs_1 = require("node:fs");
14
- const environments_1 = require("../config/environments");
15
- const cli_auth_gateway_1 = require("../gateways/cli-auth-gateway");
16
- const telemetry_service_1 = require("../services/telemetry.service");
17
- const cli_1 = require("./cli");
18
- const config_store_1 = require("./config-store");
10
+ import { closeSync, openSync, rmSync, statSync } from 'node:fs';
11
+ import { ENVIRONMENTS } from '../config/environments.js';
12
+ import { CliAuthGateway } from '../gateways/cli-auth-gateway.js';
13
+ import { telemetry } from '../services/telemetry.service.js';
14
+ import { CliError } from './cli.js';
15
+ import { getConfigPath, readConfig, writeConfig, } from './config-store.js';
19
16
  const REFRESH_SKEW_SECONDS = 60;
20
17
  // Refresh lock tuning: a refresh is a single HTTP round-trip, so anything
21
18
  // holding the lock longer than this is presumed dead.
22
19
  const LOCK_STALE_MS = 10_000;
23
20
  const LOCK_WAIT_MS = 10_000;
24
21
  const LOCK_POLL_MS = 250;
25
- async function resolveAuth(opts) {
22
+ export async function resolveAuth(opts) {
26
23
  if (!opts.sessionOnly) {
27
24
  const flag = opts.apiKeyFlag?.trim();
28
25
  if (flag) {
@@ -30,7 +27,7 @@ async function resolveAuth(opts) {
30
27
  mode: 'apiKey',
31
28
  headers: { 'x-app-api-key': flag },
32
29
  };
33
- telemetry_service_1.telemetry.configure({ auth });
30
+ telemetry.configure({ auth });
34
31
  return auth;
35
32
  }
36
33
  const env = process.env.DEVICE_CLOUD_API_KEY?.trim();
@@ -39,14 +36,14 @@ async function resolveAuth(opts) {
39
36
  mode: 'apiKey',
40
37
  headers: { 'x-app-api-key': env },
41
38
  };
42
- telemetry_service_1.telemetry.configure({ auth });
39
+ telemetry.configure({ auth });
43
40
  return auth;
44
41
  }
45
42
  }
46
43
  if (opts.skipSession) {
47
44
  throw missingCredentialsError();
48
45
  }
49
- let config = (0, config_store_1.readConfig)();
46
+ let config = readConfig();
50
47
  if (!config?.session) {
51
48
  throw missingCredentialsError();
52
49
  }
@@ -56,10 +53,12 @@ async function resolveAuth(opts) {
56
53
  ({ config, session } = await refreshSessionWithLock(config));
57
54
  }
58
55
  if (!config.current_org_id) {
59
- throw new cli_1.CliError('No active organization set. Run `dcd switch-org <slug>` to pick one.');
56
+ throw new CliError('No active organization set. Run `dcd switch-org <slug>` to pick one.');
60
57
  }
61
58
  const auth = {
62
59
  mode: 'bearer',
60
+ accessToken: session.access_token,
61
+ env: config.env,
63
62
  orgId: config.current_org_id,
64
63
  userEmail: session.user_email,
65
64
  headers: {
@@ -67,7 +66,7 @@ async function resolveAuth(opts) {
67
66
  'x-dcd-org': config.current_org_id,
68
67
  },
69
68
  };
70
- telemetry_service_1.telemetry.configure({ auth, apiUrl: config.api_url });
69
+ telemetry.configure({ auth, apiUrl: config.api_url });
71
70
  return auth;
72
71
  }
73
72
  /**
@@ -76,27 +75,27 @@ async function resolveAuth(opts) {
76
75
  * Supabase refresh token — token rotation would revoke the session family.
77
76
  */
78
77
  async function refreshSessionWithLock(initial) {
79
- const lockPath = `${(0, config_store_1.getConfigPath)()}.lock`;
78
+ const lockPath = `${getConfigPath()}.lock`;
80
79
  await acquireRefreshLock(lockPath);
81
80
  try {
82
81
  // Re-read: another process may have refreshed while we waited.
83
- const current = (0, config_store_1.readConfig)() ?? initial;
82
+ const current = readConfig() ?? initial;
84
83
  const session = current.session ?? initial.session;
85
84
  const now = Math.floor(Date.now() / 1000);
86
85
  if (session.expires_at > now + REFRESH_SKEW_SECONDS) {
87
86
  return { config: current, session };
88
87
  }
89
- const { anonKey } = environments_1.ENVIRONMENTS[current.env].supabase;
90
- const refreshed = await cli_auth_gateway_1.CliAuthGateway.refresh(current.supabase_url, anonKey, session);
88
+ const { anonKey } = ENVIRONMENTS[current.env].supabase;
89
+ const refreshed = await CliAuthGateway.refresh(current.supabase_url, anonKey, session);
91
90
  // Re-read again and merge only `session` so a concurrent `switch-org`
92
91
  // write (org fields) isn't reverted by our pre-refresh snapshot.
93
- const merged = { ...((0, config_store_1.readConfig)() ?? current), session: refreshed };
94
- (0, config_store_1.writeConfig)(merged);
92
+ const merged = { ...(readConfig() ?? current), session: refreshed };
93
+ writeConfig(merged);
95
94
  return { config: merged, session: refreshed };
96
95
  }
97
96
  finally {
98
97
  try {
99
- (0, node_fs_1.rmSync)(lockPath, { force: true });
98
+ rmSync(lockPath, { force: true });
100
99
  }
101
100
  catch { /* best effort */ }
102
101
  }
@@ -105,7 +104,7 @@ async function acquireRefreshLock(lockPath) {
105
104
  const deadline = Date.now() + LOCK_WAIT_MS;
106
105
  for (;;) {
107
106
  try {
108
- (0, node_fs_1.closeSync)((0, node_fs_1.openSync)(lockPath, 'wx'));
107
+ closeSync(openSync(lockPath, 'wx'));
109
108
  return;
110
109
  }
111
110
  catch {
@@ -113,19 +112,19 @@ async function acquireRefreshLock(lockPath) {
113
112
  // Don't hang the command forever: steal the lock if possible and
114
113
  // proceed regardless — worst case we race like the pre-lock code did.
115
114
  try {
116
- (0, node_fs_1.rmSync)(lockPath, { force: true });
115
+ rmSync(lockPath, { force: true });
117
116
  }
118
117
  catch { /* best effort */ }
119
118
  try {
120
- (0, node_fs_1.closeSync)((0, node_fs_1.openSync)(lockPath, 'wx'));
119
+ closeSync(openSync(lockPath, 'wx'));
121
120
  }
122
121
  catch { /* best effort */ }
123
122
  return;
124
123
  }
125
124
  try {
126
- if (Date.now() - (0, node_fs_1.statSync)(lockPath).mtimeMs > LOCK_STALE_MS) {
125
+ if (Date.now() - statSync(lockPath).mtimeMs > LOCK_STALE_MS) {
127
126
  // Holder presumed dead — take over.
128
- (0, node_fs_1.rmSync)(lockPath, { force: true });
127
+ rmSync(lockPath, { force: true });
129
128
  continue;
130
129
  }
131
130
  }
@@ -138,5 +137,5 @@ async function acquireRefreshLock(lockPath) {
138
137
  }
139
138
  }
140
139
  function missingCredentialsError() {
141
- return new cli_1.CliError('Not authenticated. Provide an API key via --api-key or the DEVICE_CLOUD_API_KEY environment variable, or run `dcd login`.');
140
+ return new CliError('Not authenticated. Provide an API key via --api-key or the DEVICE_CLOUD_API_KEY environment variable, or run `dcd login`.');
142
141
  }