shipthis 0.1.47 → 0.1.48

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 (91) hide show
  1. package/dist/{AppleBundleIdDetails-DogjUozj.js → AppleBundleIdDetails-4WNaBSGY.js} +7 -7
  2. package/dist/{Command-CWKSGrtu.js → Command-BdiF-ris.js} +4 -4
  3. package/dist/{CommandGame-B7EQbgMq.js → CommandGame-BExi5Od4.js} +2 -2
  4. package/dist/{Create-DVnctE1E.js → Create-DUl1dfhZ.js} +5 -5
  5. package/dist/{GameStatus-DbwQmv4w.js → GameStatus-NSIKI7Zi.js} +5 -4
  6. package/dist/{Import-OqdHUTRB.js → Import-B_2jztJs.js} +6 -6
  7. package/dist/{JobLogTail-D-IhoJDu.js → JobLogTail-DrUqKwiv.js} +5 -5
  8. package/dist/JobProgress-CFwOCp-e.js +394 -0
  9. package/dist/{JobStatusTable-BrfiBJ2p.js → JobStatusTable-Csfw4ag1.js} +6 -4
  10. package/dist/{ProjectCredentialsTable-DyUiZFwz.js → ProjectCredentialsTable-BXWs0Ler.js} +5 -5
  11. package/dist/{UserCredentialsTable-DBasvwqB.js → UserCredentialsTable-Cr2NT9gg.js} +5 -5
  12. package/dist/{baseAppleCommand-Dfzhp492.js → baseAppleCommand-CMBV0P9z.js} +1 -1
  13. package/dist/{baseCommand-B7pUDBYl.js → baseCommand-C7OdounZ.js} +20 -6
  14. package/dist/{baseGameAndroidCommand-BhridFzb.js → baseGameAndroidCommand-BE6hsL-c.js} +2 -2
  15. package/dist/{baseGameCommand-BTC5XD8W.js → baseGameCommand-Bap2bDqP.js} +5 -5
  16. package/dist/commands/apiKey/create.js +5 -5
  17. package/dist/commands/apiKey/list.js +6 -6
  18. package/dist/commands/apiKey/revoke.js +6 -6
  19. package/dist/commands/apple/apiKey/create.js +9 -9
  20. package/dist/commands/apple/apiKey/delete.js +7 -7
  21. package/dist/commands/apple/apiKey/export.js +8 -8
  22. package/dist/commands/apple/apiKey/import.js +8 -8
  23. package/dist/commands/apple/apiKey/status.js +7 -7
  24. package/dist/commands/apple/certificate/create.js +9 -9
  25. package/dist/commands/apple/certificate/delete.js +7 -7
  26. package/dist/commands/apple/certificate/export.js +8 -8
  27. package/dist/commands/apple/certificate/import.js +8 -8
  28. package/dist/commands/apple/certificate/status.js +7 -7
  29. package/dist/commands/apple/login.js +5 -5
  30. package/dist/commands/apple/status.js +6 -6
  31. package/dist/commands/dashboard.js +5 -5
  32. package/dist/commands/game/android/apiKey/connect.js +11 -11
  33. package/dist/commands/game/android/apiKey/create.js +13 -13
  34. package/dist/commands/game/android/apiKey/delete.js +7 -7
  35. package/dist/commands/game/android/apiKey/export.js +9 -9
  36. package/dist/commands/game/android/apiKey/import.js +9 -9
  37. package/dist/commands/game/android/apiKey/invite.js +6 -6
  38. package/dist/commands/game/android/apiKey/policy.js +6 -6
  39. package/dist/commands/game/android/apiKey/status.js +9 -9
  40. package/dist/commands/game/android/keyStore/create.js +10 -10
  41. package/dist/commands/game/android/keyStore/delete.js +7 -7
  42. package/dist/commands/game/android/keyStore/export.js +8 -8
  43. package/dist/commands/game/android/keyStore/import.js +11 -11
  44. package/dist/commands/game/android/keyStore/status.js +8 -8
  45. package/dist/commands/game/android/status.js +5 -5
  46. package/dist/commands/game/build/download.js +6 -6
  47. package/dist/commands/game/build/list.js +7 -7
  48. package/dist/commands/game/create.js +7 -8
  49. package/dist/commands/game/details.js +6 -6
  50. package/dist/commands/game/export.js +29 -14
  51. package/dist/commands/game/ios/app/addTester.js +7 -7
  52. package/dist/commands/game/ios/app/create.js +6 -6
  53. package/dist/commands/game/ios/app/status.js +9 -9
  54. package/dist/commands/game/ios/app/sync.js +7 -7
  55. package/dist/commands/game/ios/profile/create.js +9 -9
  56. package/dist/commands/game/ios/profile/delete.js +7 -7
  57. package/dist/commands/game/ios/profile/export.js +8 -8
  58. package/dist/commands/game/ios/profile/import.js +8 -8
  59. package/dist/commands/game/ios/profile/status.js +9 -9
  60. package/dist/commands/game/ios/status.js +11 -11
  61. package/dist/commands/game/ios/wizard.js +6 -6
  62. package/dist/commands/game/job/list.js +6 -6
  63. package/dist/commands/game/job/status.js +9 -9
  64. package/dist/commands/game/list.js +6 -6
  65. package/dist/commands/game/ship.js +22 -15
  66. package/dist/commands/game/status.js +8 -8
  67. package/dist/commands/game/wizard.js +21 -20
  68. package/dist/commands/internal/fastlane.js +5 -5
  69. package/dist/commands/internal/readme.js +5 -5
  70. package/dist/commands/login.js +5 -5
  71. package/dist/commands/status.js +6 -6
  72. package/dist/commands/util/android-build-method.js +5 -5
  73. package/dist/commands/util/glass.js +4 -4
  74. package/dist/{export-7GgsEM8k.js → export-gdKqwR7u.js} +1 -1
  75. package/dist/{import-DKibfocC.js → import-CICoTBgo.js} +1 -1
  76. package/dist/{index-BeGDjWvi.js → index-B_z5RCr4.js} +7 -7
  77. package/dist/{index-3FFnBAwx.js → index-BaxMfQnE.js} +8 -8
  78. package/dist/{index-B_dXK-Zt.js → index-YX-OJ2AA.js} +1 -1
  79. package/dist/{upload-DGY-glMy.js → upload-Drre-qsL.js} +1 -1
  80. package/dist/{useAppleApp-DRJf8q6R.js → useAppleApp-ChI2baWJ.js} +1 -1
  81. package/dist/{useAppleBundleId-B-5a-soi.js → useAppleBundleId-CUTbojvt.js} +1 -1
  82. package/dist/{useAppleProfiles-BXXI2MAc.js → useAppleProfiles-omIx9KVc.js} +4 -4
  83. package/dist/{useGoogleStatus-CETX9Rbw.js → useGoogleStatus-v86ZyFiz.js} +2 -2
  84. package/dist/{useProjectCredentials-AV_OlZEu.js → useProjectCredentials-5Ycm5B4x.js} +3 -3
  85. package/dist/{useWebSocket-DgYLiSrd.js → useWebSocket-AjcqLcJX.js} +1 -1
  86. package/docs/game/export.md +13 -2
  87. package/docs/game/ship.md +1 -1
  88. package/npm-shrinkwrap.json +53 -53
  89. package/oclif.manifest.json +19 -4
  90. package/package.json +2 -3
  91. package/dist/JobProgress-C24YiJOA.js +0 -252
@@ -11,28 +11,28 @@ import 'readline-sync';
11
11
  import 'luxon';
12
12
  import 'axios';
13
13
  import 'isomorphic-git';
14
- import './baseCommand-B7pUDBYl.js';
14
+ import './baseCommand-C7OdounZ.js';
15
15
  import '@oclif/core';
16
16
  import '@tanstack/react-query';
17
17
  import 'react';
18
- import { u as useAppleApp } from './useAppleApp-DRJf8q6R.js';
19
- import 'fast-glob';
18
+ import { u as useAppleApp } from './useAppleApp-ChI2baWJ.js';
20
19
  import 'uuid';
21
- import 'socket.io-client';
22
- import 'fullscreen-ink';
20
+ import 'fast-glob';
23
21
  import 'stream';
24
22
  import 'yazl';
23
+ import 'socket.io-client';
24
+ import 'fullscreen-ink';
25
25
  import 'string-length';
26
26
  import 'strip-ansi';
27
27
  import { T as Table } from './Table-FaNgpyeq.js';
28
28
  import { T as Title } from './Title-BCQtayg6.js';
29
29
  import 'open';
30
- import './baseGameCommand-BTC5XD8W.js';
30
+ import './baseGameCommand-Bap2bDqP.js';
31
31
  import 'godot-export-presets';
32
32
  import 'marked';
33
33
  import 'marked-terminal';
34
34
  import 'qrcode';
35
- import { u as useAppleBundleId } from './useAppleBundleId-B-5a-soi.js';
35
+ import { u as useAppleBundleId } from './useAppleBundleId-CUTbojvt.js';
36
36
 
37
37
  const AppleAppDetails = (props) => {
38
38
  const { data, isLoading } = useAppleApp(props);
@@ -2,7 +2,7 @@ import { jsx } from 'react/jsx-runtime';
2
2
  import { QueryClientProvider } from '@tanstack/react-query';
3
3
  import { useScreenSize } from 'fullscreen-ink';
4
4
  import { Box } from 'ink';
5
- import { I as queryClient } from './baseCommand-B7pUDBYl.js';
5
+ import { H as queryClient } from './baseCommand-C7OdounZ.js';
6
6
  import 'axios';
7
7
  import 'node:fs';
8
8
  import 'luxon';
@@ -15,11 +15,11 @@ import 'isomorphic-git';
15
15
  import '@oclif/core';
16
16
  import 'react';
17
17
  import 'socket.io-client';
18
+ import 'uuid';
19
+ import 'fast-glob';
18
20
  import 'stream';
19
21
  import 'yazl';
20
- import 'fast-glob';
21
- import 'uuid';
22
- import { C as CommandProvider } from './baseGameCommand-BTC5XD8W.js';
22
+ import { C as CommandProvider } from './baseGameCommand-Bap2bDqP.js';
23
23
 
24
24
  const Command = ({ children, command }) => {
25
25
  const { width } = useScreenSize();
@@ -1,6 +1,6 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { G as GameProvider } from './baseGameCommand-BTC5XD8W.js';
3
- import { C as Command } from './Command-CWKSGrtu.js';
2
+ import { G as GameProvider } from './baseGameCommand-Bap2bDqP.js';
3
+ import { C as Command } from './Command-BdiF-ris.js';
4
4
 
5
5
  const CommandGame = ({ children, command }) => /* @__PURE__ */ jsx(Command, { command, children: /* @__PURE__ */ jsx(GameProvider, { children }) });
6
6
 
@@ -3,7 +3,7 @@ import { useQueryClient } from '@tanstack/react-query';
3
3
  import axios from 'axios';
4
4
  import { Box } from 'ink';
5
5
  import { useContext } from 'react';
6
- import { q as getAuthedHeaders, p as API_URL } from './baseCommand-B7pUDBYl.js';
6
+ import { q as getAuthedHeaders, p as API_URL } from './baseCommand-C7OdounZ.js';
7
7
  import 'ink-spinner';
8
8
  import 'node:crypto';
9
9
  import 'node:fs';
@@ -14,13 +14,13 @@ import 'readline-sync';
14
14
  import 'luxon';
15
15
  import 'isomorphic-git';
16
16
  import '@oclif/core';
17
- import { i as GameContext, k as cacheKeys } from './baseGameCommand-BTC5XD8W.js';
18
- import 'fast-glob';
17
+ import { j as GameContext, i as cacheKeys } from './baseGameCommand-Bap2bDqP.js';
19
18
  import 'uuid';
20
- import 'socket.io-client';
21
- import 'fullscreen-ink';
19
+ import 'fast-glob';
22
20
  import 'stream';
23
21
  import 'yazl';
22
+ import 'socket.io-client';
23
+ import 'fullscreen-ink';
24
24
  import 'string-length';
25
25
  import 'strip-ansi';
26
26
  import 'open';
@@ -1,15 +1,17 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { Text, Box } from 'ink';
3
3
  import { useState, useEffect, useContext } from 'react';
4
- import { c as getShortDate, P as Platform, l as getProject, M as getProjectPlatformProgress } from './baseCommand-B7pUDBYl.js';
4
+ import { c as getShortDate, P as Platform, l as getProject, L as getProjectPlatformProgress } from './baseCommand-C7OdounZ.js';
5
5
  import 'ink-spinner';
6
- import { g as getShortUUID, m as makeHumanReadable, i as GameContext, j as CommandContext } from './baseGameCommand-BTC5XD8W.js';
6
+ import { g as getShortUUID, m as makeHumanReadable, j as GameContext, k as CommandContext } from './baseGameCommand-Bap2bDqP.js';
7
7
  import '@tanstack/react-query';
8
8
  import 'axios';
9
9
  import 'luxon';
10
10
  import 'node:fs';
11
- import 'fast-glob';
12
11
  import 'uuid';
12
+ import 'fast-glob';
13
+ import 'stream';
14
+ import 'yazl';
13
15
  import 'socket.io-client';
14
16
  import 'fullscreen-ink';
15
17
  import 'string-length';
@@ -23,7 +25,6 @@ import 'marked-terminal';
23
25
  import { N as NextSteps } from './NextSteps-DbJHmscQ.js';
24
26
  import 'qrcode';
25
27
  import 'godot-export-presets';
26
- import 'yazl';
27
28
 
28
29
  function isPlatformConfigured(platform, progress) {
29
30
  if (!progress) return false;
@@ -11,25 +11,25 @@ import 'readline-sync';
11
11
  import 'luxon';
12
12
  import 'axios';
13
13
  import 'isomorphic-git';
14
- import { I as queryClient, P as Platform, C as CredentialsType } from './baseCommand-B7pUDBYl.js';
14
+ import { H as queryClient, P as Platform, C as CredentialsType } from './baseCommand-C7OdounZ.js';
15
15
  import '@oclif/core';
16
16
  import { useMutation } from '@tanstack/react-query';
17
- import 'fast-glob';
18
17
  import { v4 } from 'uuid';
19
- import 'socket.io-client';
20
- import 'fullscreen-ink';
18
+ import 'fast-glob';
21
19
  import 'stream';
22
20
  import { ZipFile } from 'yazl';
21
+ import 'socket.io-client';
22
+ import 'fullscreen-ink';
23
23
  import 'string-length';
24
24
  import 'strip-ansi';
25
25
  import 'open';
26
26
  import '@inkjs/ui';
27
- import { k as cacheKeys, i as GameContext } from './baseGameCommand-BTC5XD8W.js';
27
+ import { i as cacheKeys, j as GameContext } from './baseGameCommand-Bap2bDqP.js';
28
28
  import 'marked';
29
29
  import 'marked-terminal';
30
30
  import 'qrcode';
31
31
  import 'godot-export-presets';
32
- import { i as importCredential } from './import-DKibfocC.js';
32
+ import { i as importCredential } from './import-CICoTBgo.js';
33
33
 
34
34
  async function importKeystore({ log = () => {
35
35
  }, ...opt }) {
@@ -5,7 +5,7 @@ import { useInfiniteQuery } from '@tanstack/react-query';
5
5
  import axios from 'axios';
6
6
  import { useState, useRef, useEffect } from 'react';
7
7
  import 'node:fs';
8
- import { q as getAuthedHeaders, p as API_URL, H as castArrayObjectDates, J as JobStatus, a8 as castJobDates, a6 as castObjectDates, a9 as getShortTime } from './baseCommand-B7pUDBYl.js';
8
+ import { q as getAuthedHeaders, p as API_URL, F as castArrayObjectDates, J as JobStatus, a5 as castJobDates, a3 as castObjectDates, a6 as getShortTime } from './baseCommand-C7OdounZ.js';
9
9
  import 'luxon';
10
10
  import 'node:crypto';
11
11
  import 'node:path';
@@ -14,14 +14,14 @@ import 'node:url';
14
14
  import 'readline-sync';
15
15
  import 'isomorphic-git';
16
16
  import '@oclif/core';
17
+ import 'uuid';
18
+ import 'fast-glob';
17
19
  import 'stream';
18
20
  import 'yazl';
19
- import 'fast-glob';
20
- import 'uuid';
21
21
  import 'socket.io-client';
22
- import { k as cacheKeys, y as useJob, h as getStageColor, x as getMessageColor } from './baseGameCommand-BTC5XD8W.js';
22
+ import { i as cacheKeys, v as useJob, h as getStageColor, w as getMessageColor } from './baseGameCommand-Bap2bDqP.js';
23
23
  import 'fullscreen-ink';
24
- import { u as useWebSocket } from './useWebSocket-DgYLiSrd.js';
24
+ import { u as useWebSocket } from './useWebSocket-AjcqLcJX.js';
25
25
  import stringLength from 'string-length';
26
26
  import stripAnsi from 'strip-ansi';
27
27
  import { T as Title } from './Title-BCQtayg6.js';
@@ -0,0 +1,394 @@
1
+ import { useMutation } from '@tanstack/react-query';
2
+ import { a7 as LEGACY_DEFAULT_IGNORED_FILES_GLOBS, a8 as LEGACY_DEFAULT_SHIPPED_FILES_GLOBS, x as DEFAULT_PLATFORM_GLOBS, P as Platform, l as getProject, a9 as getNewUploadTicket, aa as startJobsFromUpload, H as queryClient, a1 as LogLevel } from './baseCommand-C7OdounZ.js';
3
+ import 'node:crypto';
4
+ import fs__default from 'node:fs';
5
+ import 'node:path';
6
+ import 'node:readline';
7
+ import 'node:url';
8
+ import 'readline-sync';
9
+ import 'luxon';
10
+ import 'axios';
11
+ import 'isomorphic-git';
12
+ import '@oclif/core';
13
+ import { useState } from 'react';
14
+ import { x as getFileHash, i as cacheKeys, y as getPlatformName, w as getMessageColor } from './baseGameCommand-Bap2bDqP.js';
15
+ import { v4 } from 'uuid';
16
+ import fg from 'fast-glob';
17
+ import { Readable, Transform } from 'stream';
18
+ import { q as queryProjectCredentials } from './useProjectCredentials-5Ycm5B4x.js';
19
+ import { ZipFile } from 'yazl';
20
+ import 'socket.io-client';
21
+ import 'fullscreen-ink';
22
+ import { Box, Text } from 'ink';
23
+ import { g as getCWDGitInfo } from './git-BpsfNFZ_.js';
24
+ import { jsxs, jsx } from 'react/jsx-runtime';
25
+ import 'ink-spinner';
26
+ import { u as useJobWatching, a as JobLogLine } from './JobLogTail-DrUqKwiv.js';
27
+ import 'open';
28
+ import '@inkjs/ui';
29
+ import 'marked';
30
+ import 'marked-terminal';
31
+ import { P as ProgressSpinner } from './ProgressSpinner-Um6ARKlk.js';
32
+ import 'qrcode';
33
+ import 'string-length';
34
+ import 'strip-ansi';
35
+ import 'godot-export-presets';
36
+
37
+ const WARN_LEARN_MORE = "Learn more: https://shipth.is/docs/guides/controlling-uploaded-files";
38
+ const WARN_LEGACY_DEFAULT = "Using legacy default globs - you should upgrade to the new globs. " + WARN_LEARN_MORE;
39
+ const WARN_MISSING_GLOBS = "No file globs configured in shipthis.json; using defaults. " + WARN_LEARN_MORE;
40
+ const WARN_UPGRADE_GLOBS = "Using legacy custom file selection globs - you should upgrade to the new globs. " + WARN_LEARN_MORE;
41
+ function normalize(value) {
42
+ if (!value) return void 0;
43
+ return value.map((v) => v.trim()).filter(Boolean);
44
+ }
45
+ function arraysEqual(a, b) {
46
+ if (a.length !== b.length) return false;
47
+ for (let i = 0; i < a.length; i++) {
48
+ if (a[i] !== b[i]) return false;
49
+ }
50
+ return true;
51
+ }
52
+ function isLegacyIgnoredDefault(ignoredFilesGlobs) {
53
+ const normalized = normalize(ignoredFilesGlobs);
54
+ if (!normalized) return false;
55
+ const knownLegacyIgnoredDefaults = [
56
+ LEGACY_DEFAULT_IGNORED_FILES_GLOBS,
57
+ [".git", ".gitignore", "shipthis.json", "shipthis-*.zip"]
58
+ ];
59
+ return knownLegacyIgnoredDefaults.some((snapshot) => arraysEqual(snapshot, normalized));
60
+ }
61
+ function isLegacyShippedDefault(shippedFilesGlobs) {
62
+ const normalized = normalize(shippedFilesGlobs);
63
+ if (!normalized) return false;
64
+ return arraysEqual(LEGACY_DEFAULT_SHIPPED_FILES_GLOBS, normalized);
65
+ }
66
+ function getSafeGlobsConfig(pc) {
67
+ return {
68
+ android: {
69
+ exclude: pc.globs?.android?.exclude ?? DEFAULT_PLATFORM_GLOBS.android.exclude,
70
+ include: pc.globs?.android?.include ?? DEFAULT_PLATFORM_GLOBS.android.include
71
+ },
72
+ base: {
73
+ exclude: pc.globs?.base?.exclude ?? DEFAULT_PLATFORM_GLOBS.base.exclude,
74
+ include: pc.globs?.base?.include ?? DEFAULT_PLATFORM_GLOBS.base.include
75
+ },
76
+ ios: {
77
+ exclude: pc.globs?.ios?.exclude ?? DEFAULT_PLATFORM_GLOBS.ios.exclude,
78
+ include: pc.globs?.ios?.include ?? DEFAULT_PLATFORM_GLOBS.ios.include
79
+ }
80
+ };
81
+ }
82
+ function getRulesetForPlatform(safe, platforms) {
83
+ if (platforms.length !== 1) return { exclude: [], include: [] };
84
+ if (platforms[0] === Platform.IOS) return safe.ios;
85
+ if (platforms[0] === Platform.ANDROID) return safe.android;
86
+ return { exclude: [], include: [] };
87
+ }
88
+ function getFinalRuleset(projectConfig, platforms) {
89
+ const { shippedFilesGlobs, ignoredFilesGlobs, globs } = projectConfig;
90
+ const legacyShippedProvided = Array.isArray(shippedFilesGlobs);
91
+ const legacyIgnoredProvided = Array.isArray(ignoredFilesGlobs);
92
+ const hasLegacy = legacyShippedProvided || legacyIgnoredProvided;
93
+ const hasCompleteLegacy = legacyShippedProvided && legacyIgnoredProvided;
94
+ const hasGlobs = Boolean(globs);
95
+ const returnNewOrDefaults = (warning) => {
96
+ const safe = getSafeGlobsConfig(projectConfig);
97
+ const platformRuleset = getRulesetForPlatform(safe, platforms);
98
+ return {
99
+ warning,
100
+ include: [...safe.base.include, ...platformRuleset.include],
101
+ exclude: [...safe.base.exclude, ...platformRuleset.exclude]
102
+ };
103
+ };
104
+ if (!hasLegacy) {
105
+ return returnNewOrDefaults(hasGlobs ? void 0 : WARN_MISSING_GLOBS);
106
+ }
107
+ const legacyIsAllDefaults = hasCompleteLegacy && Boolean(shippedFilesGlobs) && Boolean(ignoredFilesGlobs) && isLegacyShippedDefault(shippedFilesGlobs) && isLegacyIgnoredDefault(ignoredFilesGlobs);
108
+ if (!legacyIsAllDefaults) {
109
+ return {
110
+ warning: WARN_UPGRADE_GLOBS,
111
+ include: shippedFilesGlobs ?? LEGACY_DEFAULT_SHIPPED_FILES_GLOBS,
112
+ exclude: ignoredFilesGlobs ?? LEGACY_DEFAULT_IGNORED_FILES_GLOBS
113
+ };
114
+ }
115
+ return returnNewOrDefaults(WARN_LEGACY_DEFAULT);
116
+ }
117
+ async function getFilesToShip(projectConfig, platforms, log, warnLog) {
118
+ const { include, exclude, warning } = getFinalRuleset(projectConfig, platforms);
119
+ if (warning) warnLog(warning);
120
+ log("Retrieving file globs...");
121
+ log("Finding files to include in zip...");
122
+ const files = await fg(include, { dot: true, ignore: exclude });
123
+ log(`Found ${files.length} files, adding to zip...`);
124
+ return files;
125
+ }
126
+
127
+ const ON_PROGRESS_THROTTLE_MS = 2e3;
128
+ function createProgressStream(totalSize, onProgress, throttleMs) {
129
+ let sent = 0;
130
+ let lastCallTime = 0;
131
+ return new Transform({
132
+ transform(chunk, encoding, callback) {
133
+ sent += chunk.length;
134
+ const now = Date.now();
135
+ if (now - lastCallTime >= throttleMs) {
136
+ onProgress(sent, totalSize);
137
+ lastCallTime = now;
138
+ }
139
+ callback(null, chunk);
140
+ }
141
+ });
142
+ }
143
+ function uploadZip({ url, zipStream, zipSize, onProgress }) {
144
+ const startTime = Date.now();
145
+ const progressStream = createProgressStream(zipSize, (sent, total) => {
146
+ const elapsedSeconds = (Date.now() - startTime) / 1e3;
147
+ const speedMBps = elapsedSeconds < 1e-3 ? 0 : sent / elapsedSeconds / 1024 / 1024;
148
+ onProgress({
149
+ progress: total ? sent / total : 0,
150
+ loadedBytes: sent,
151
+ totalBytes: total,
152
+ speedMBps,
153
+ elapsedSeconds
154
+ });
155
+ }, ON_PROGRESS_THROTTLE_MS);
156
+ const streamWithProgress = zipStream.pipe(progressStream);
157
+ const webStream = Readable.toWeb(streamWithProgress);
158
+ const response = fetch(url, {
159
+ method: "PUT",
160
+ headers: {
161
+ "Content-Type": "application/zip",
162
+ "Content-Length": zipSize.toString()
163
+ },
164
+ body: webStream,
165
+ duplex: "half"
166
+ });
167
+ return response;
168
+ }
169
+
170
+ function formatProgressLog(label, data, bytesKey, totalKey, isEstimated = false) {
171
+ const elapsed = data.elapsedSeconds.toFixed(1);
172
+ const transferredMB = (data[bytesKey] / 1024 / 1024).toFixed(2);
173
+ const totalMB = (data[totalKey] / 1024 / 1024).toFixed(2);
174
+ const progressPercent = Math.round(data.progress * 100);
175
+ const speed = data.speedMBps.toFixed(2);
176
+ const totalPrefix = isEstimated ? "~" : "";
177
+ return `${label}: ${progressPercent}% (${transferredMB}MB / ${totalPrefix}${totalMB}MB) - ${elapsed}s - ${speed}MB/s`;
178
+ }
179
+ async function getPlatforms(project, flags, log) {
180
+ log("Determining platforms to ship...");
181
+ const specifiedPlatform = flags.platform ? flags.platform.toUpperCase() : null;
182
+ if (specifiedPlatform) {
183
+ log(`Platform specified: ${specifiedPlatform}`);
184
+ return [specifiedPlatform];
185
+ }
186
+ log("No platform specified, fetching project credentials...");
187
+ const useDemoCredentials = Boolean(project.details?.useDemoCredentials);
188
+ if (useDemoCredentials) return [Platform.ANDROID, Platform.IOS];
189
+ log("Fetching project credentials...");
190
+ const response = await queryProjectCredentials({
191
+ projectId: project.id,
192
+ pageNumber: 0,
193
+ pageSize: 100
194
+ });
195
+ log(`Found ${response.data.length} project credentials, filtering to active ones...`);
196
+ const credentialPlatforms = [...new Set(response.data.filter((cred) => cred.isActive).map((cred) => cred.platform))];
197
+ log(`Active platforms: ${credentialPlatforms.join(", ")}`);
198
+ return credentialPlatforms;
199
+ }
200
+
201
+ const COMPRESSION_RATIO = 0.65;
202
+ async function createZip({ files, outputPath, onProgress }) {
203
+ const startTime = Date.now();
204
+ const statPromises = files.map(async (file) => {
205
+ try {
206
+ return await fs__default.promises.stat(file);
207
+ } catch {
208
+ return null;
209
+ }
210
+ });
211
+ const statsResults = await Promise.all(statPromises);
212
+ let totalSourceSize = 0;
213
+ for (const stats of statsResults) {
214
+ if (stats) {
215
+ totalSourceSize += stats.size;
216
+ }
217
+ }
218
+ const estimatedZipSize = Math.max(Math.round(totalSourceSize * COMPRESSION_RATIO), 1);
219
+ const zipFile = new ZipFile();
220
+ for (const file of files) {
221
+ zipFile.addFile(file, file);
222
+ }
223
+ return new Promise((resolve, reject) => {
224
+ let settled = false;
225
+ const handleError = (error) => {
226
+ if (settled) return;
227
+ settled = true;
228
+ progressStream.destroy();
229
+ outputStream.destroy();
230
+ reject(error);
231
+ };
232
+ const handleSuccess = () => {
233
+ if (settled) return;
234
+ settled = true;
235
+ resolve();
236
+ };
237
+ const outputStream = fs__default.createWriteStream(outputPath);
238
+ const progressStream = createProgressStream(estimatedZipSize, (written, total) => {
239
+ const elapsedSeconds = (Date.now() - startTime) / 1e3;
240
+ const speedMBps = elapsedSeconds < 1e-3 ? 0 : written / elapsedSeconds / 1024 / 1024;
241
+ onProgress({
242
+ progress: total ? Math.min(1, written / total) : 0,
243
+ writtenBytes: written,
244
+ estimatedTotalBytes: total,
245
+ sourceTotalBytes: totalSourceSize,
246
+ elapsedSeconds,
247
+ speedMBps
248
+ });
249
+ }, ON_PROGRESS_THROTTLE_MS);
250
+ zipFile.outputStream.on("error", handleError);
251
+ progressStream.on("error", handleError);
252
+ outputStream.on("error", handleError);
253
+ outputStream.on("close", handleSuccess);
254
+ zipFile.outputStream.pipe(progressStream).pipe(outputStream);
255
+ zipFile.end();
256
+ });
257
+ }
258
+
259
+ const ERR_NOT_CONFIGURED = "No Android or iOS configuration found. Please run `shipthis game wizard android` or `shipthis game wizard ios` to configure your game.";
260
+ async function ship({ command, log, warnLog, shipFlags }) {
261
+ const commandFlags = command.getFlags();
262
+ const finalFlags = shipFlags || commandFlags;
263
+ const { useDemoCredentials } = finalFlags;
264
+ const verbose = !!finalFlags.verbose || finalFlags.dryRun;
265
+ const vlog = verbose ? log : () => {
266
+ };
267
+ if (finalFlags.dryRun) {
268
+ log("Dry run - listing files that would be shipped and applying verbose logging...");
269
+ }
270
+ vlog("Fetching game config...");
271
+ const projectConfig = await command.getProjectConfig();
272
+ if (!projectConfig.project) throw new Error("No project found in project config");
273
+ const project = await getProject(projectConfig.project.id);
274
+ const projectUsesDemoCredentials = Boolean(project.details?.useDemoCredentials);
275
+ const isUsingDemoCredentials = useDemoCredentials ?? projectUsesDemoCredentials ?? false;
276
+ const hasConfiguredIos = Boolean(project.details?.iosBundleId);
277
+ const hasConfiguredAndroid = Boolean(project.details?.androidPackageName);
278
+ if (!isUsingDemoCredentials && !hasConfiguredAndroid && !hasConfiguredIos) {
279
+ throw new Error(ERR_NOT_CONFIGURED);
280
+ }
281
+ const platforms = await getPlatforms(project, finalFlags, vlog);
282
+ const files = await getFilesToShip(projectConfig, platforms, vlog, warnLog);
283
+ if (finalFlags.dryRun) {
284
+ log(`Dry run - would ship ${files.length} files:`);
285
+ for (const file of files) {
286
+ log(` ${file}`);
287
+ }
288
+ process.exit(0);
289
+ return [];
290
+ }
291
+ const tmpZipFileName = `shipthis-${v4()}.zip`;
292
+ const tmpZipFile = `${process.cwd()}/${tmpZipFileName}`;
293
+ log(`Creating zip file: ${tmpZipFileName}`);
294
+ await createZip({
295
+ files,
296
+ outputPath: tmpZipFile,
297
+ onProgress: (data) => {
298
+ log(formatProgressLog("Zipping", data, "writtenBytes", "estimatedTotalBytes", true));
299
+ }
300
+ });
301
+ let response;
302
+ let zipFileMd5 = "";
303
+ let uploadTicket = null;
304
+ try {
305
+ const { size } = fs__default.statSync(tmpZipFile);
306
+ vlog("Requesting upload ticket...");
307
+ uploadTicket = await getNewUploadTicket(projectConfig.project.id);
308
+ log("Uploading zip file...");
309
+ const zipStream = fs__default.createReadStream(tmpZipFile);
310
+ response = await uploadZip({
311
+ url: uploadTicket.url,
312
+ zipStream,
313
+ zipSize: size,
314
+ onProgress: (data) => {
315
+ log(formatProgressLog("Uploading", data, "loadedBytes", "totalBytes", false));
316
+ }
317
+ });
318
+ vlog("Computing zip file hash...");
319
+ zipFileMd5 = await getFileHash(tmpZipFile);
320
+ } finally {
321
+ if (fs__default.existsSync(tmpZipFile)) {
322
+ try {
323
+ vlog("Cleaning up temporary zip file...");
324
+ fs__default.unlinkSync(tmpZipFile);
325
+ } catch (err) {
326
+ if (warnLog) {
327
+ warnLog(`Failed to remove temporary zip file: ${String(err)}`);
328
+ }
329
+ }
330
+ }
331
+ }
332
+ if (!response.ok) {
333
+ throw new Error(`Upload failed: ${response.status} ${response.statusText}`);
334
+ }
335
+ log(`Upload complete`);
336
+ vlog("Fetching Git info...");
337
+ const gitInfo = await getCWDGitInfo();
338
+ const uploadDetails = {
339
+ ...gitInfo,
340
+ zipFileMd5
341
+ };
342
+ vlog("Starting jobs from upload...");
343
+ const startJobsOptions = {
344
+ ...uploadDetails,
345
+ platform: finalFlags.platform?.toUpperCase(),
346
+ skipPublish: finalFlags.skipPublish,
347
+ verbose: finalFlags.verbose,
348
+ useDemoCredentials: isUsingDemoCredentials,
349
+ gameEngineVersion: finalFlags.gameEngineVersion
350
+ };
351
+ const jobs = await startJobsFromUpload(uploadTicket.id, startJobsOptions);
352
+ vlog("Job submission complete.");
353
+ if (jobs.length === 0) {
354
+ throw new Error("No jobs were created. Please check your game configuration and try again.");
355
+ }
356
+ if (finalFlags?.follow) {
357
+ log("Waiting for job to start...");
358
+ }
359
+ return jobs;
360
+ }
361
+
362
+ const useShip = () => useMutation({
363
+ mutationFn: ship,
364
+ async onSuccess(data) {
365
+ if (!data.length) return;
366
+ const projectId = data[0].project.id;
367
+ const queryKey = cacheKeys.jobs({ pageNumber: 0, projectId });
368
+ queryClient.invalidateQueries({ queryKey });
369
+ }
370
+ });
371
+
372
+ const JobProgress = (props) => {
373
+ const [lastWarningLog, setLastWarningLog] = useState(null);
374
+ const { progress } = useJobWatching({
375
+ isWatching: true,
376
+ jobId: props.job.id,
377
+ onComplete: props.onComplete,
378
+ onFailure: props.onFailure,
379
+ projectId: props.job.project.id,
380
+ onNewLogEntry: (logEntry) => {
381
+ if (logEntry.level == LogLevel.WARN) setLastWarningLog(logEntry);
382
+ }
383
+ });
384
+ const label = `${getPlatformName(props.job.type)} build progress...`;
385
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", gap: 0, children: [
386
+ /* @__PURE__ */ jsx(ProgressSpinner, { label, progress, spinnerType: "dots" }),
387
+ lastWarningLog && /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, marginLeft: 2, children: [
388
+ /* @__PURE__ */ jsx(Text, { color: getMessageColor(lastWarningLog.level), children: "WARNING" }),
389
+ /* @__PURE__ */ jsx(JobLogLine, { log: lastWarningLog, showTimestamp: false, showStage: false })
390
+ ] })
391
+ ] });
392
+ };
393
+
394
+ export { JobProgress as J, useShip as u };
@@ -3,15 +3,17 @@ import { Box, Text } from 'ink';
3
3
  import Spinner from 'ink-spinner';
4
4
  import { DateTime } from 'luxon';
5
5
  import { useState, useEffect } from 'react';
6
- import { J as JobStatus } from './baseCommand-B7pUDBYl.js';
6
+ import { J as JobStatus } from './baseCommand-C7OdounZ.js';
7
7
  import '@tanstack/react-query';
8
8
  import 'axios';
9
9
  import 'node:fs';
10
- import { e as getJobSummary, h as getStageColor, f as getJobStatusColor } from './baseGameCommand-BTC5XD8W.js';
11
- import 'fast-glob';
10
+ import { e as getJobSummary, h as getStageColor, f as getJobStatusColor } from './baseGameCommand-Bap2bDqP.js';
12
11
  import 'uuid';
12
+ import 'fast-glob';
13
+ import 'stream';
14
+ import 'yazl';
13
15
  import 'socket.io-client';
14
- import { u as useJobWatching } from './JobLogTail-D-IhoJDu.js';
16
+ import { u as useJobWatching } from './JobLogTail-DrUqKwiv.js';
15
17
  import 'fullscreen-ink';
16
18
  import { a as StatusRow, b as StatusRowLabel } from './StatusTable-DzRWcMr4.js';
17
19
  import { T as Title } from './Title-BCQtayg6.js';
@@ -10,17 +10,17 @@ import 'readline-sync';
10
10
  import 'luxon';
11
11
  import 'axios';
12
12
  import 'isomorphic-git';
13
- import './baseCommand-B7pUDBYl.js';
13
+ import './baseCommand-C7OdounZ.js';
14
14
  import '@oclif/core';
15
15
  import '@tanstack/react-query';
16
16
  import 'react';
17
- import { u as useProjectCredentials, g as getProjectCredentialSummary } from './useProjectCredentials-AV_OlZEu.js';
18
- import 'fast-glob';
17
+ import { u as useProjectCredentials, g as getProjectCredentialSummary } from './useProjectCredentials-5Ycm5B4x.js';
19
18
  import 'uuid';
20
- import 'socket.io-client';
21
- import 'fullscreen-ink';
19
+ import 'fast-glob';
22
20
  import 'stream';
23
21
  import 'yazl';
22
+ import 'socket.io-client';
23
+ import 'fullscreen-ink';
24
24
  import { T as Table } from './Table-FaNgpyeq.js';
25
25
  import { T as Title } from './Title-BCQtayg6.js';
26
26
 
@@ -10,17 +10,17 @@ import 'readline-sync';
10
10
  import 'luxon';
11
11
  import axios from 'axios';
12
12
  import 'isomorphic-git';
13
- import { c as getShortDate, q as getAuthedHeaders, p as API_URL, H as castArrayObjectDates } from './baseCommand-B7pUDBYl.js';
13
+ import { c as getShortDate, q as getAuthedHeaders, p as API_URL, F as castArrayObjectDates } from './baseCommand-C7OdounZ.js';
14
14
  import '@oclif/core';
15
15
  import { useQuery } from '@tanstack/react-query';
16
16
  import 'react';
17
- import 'fast-glob';
18
17
  import 'uuid';
19
- import { k as cacheKeys, g as getShortUUID } from './baseGameCommand-BTC5XD8W.js';
20
- import 'socket.io-client';
21
- import 'fullscreen-ink';
18
+ import 'fast-glob';
22
19
  import 'stream';
23
20
  import 'yazl';
21
+ import { i as cacheKeys, g as getShortUUID } from './baseGameCommand-Bap2bDqP.js';
22
+ import 'socket.io-client';
23
+ import 'fullscreen-ink';
24
24
  import { T as Table } from './Table-FaNgpyeq.js';
25
25
  import { T as Title } from './Title-BCQtayg6.js';
26
26
 
@@ -1,4 +1,4 @@
1
- import { B as BaseAuthenticatedCommand } from './baseGameCommand-BTC5XD8W.js';
1
+ import { B as BaseAuthenticatedCommand } from './baseGameCommand-Bap2bDqP.js';
2
2
 
3
3
  class BaseAppleCommand extends BaseAuthenticatedCommand {
4
4
  async init() {