@limrun/api 0.23.2 → 0.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/client.d.mts +4 -0
  3. package/client.d.mts.map +1 -1
  4. package/client.d.ts +4 -0
  5. package/client.d.ts.map +1 -1
  6. package/client.js +3 -0
  7. package/client.js.map +1 -1
  8. package/client.mjs +3 -0
  9. package/client.mjs.map +1 -1
  10. package/exec-client.d.mts +3 -2
  11. package/exec-client.d.mts.map +1 -1
  12. package/exec-client.d.ts +3 -2
  13. package/exec-client.d.ts.map +1 -1
  14. package/exec-client.js +10 -3
  15. package/exec-client.js.map +1 -1
  16. package/exec-client.mjs +10 -3
  17. package/exec-client.mjs.map +1 -1
  18. package/folder-sync.d.mts.map +1 -1
  19. package/folder-sync.d.ts.map +1 -1
  20. package/folder-sync.js +5 -2
  21. package/folder-sync.js.map +1 -1
  22. package/folder-sync.mjs +5 -2
  23. package/folder-sync.mjs.map +1 -1
  24. package/index.d.mts +1 -1
  25. package/index.d.mts.map +1 -1
  26. package/index.d.ts +1 -1
  27. package/index.d.ts.map +1 -1
  28. package/index.js +1 -3
  29. package/index.js.map +1 -1
  30. package/index.mjs +0 -1
  31. package/index.mjs.map +1 -1
  32. package/instance-client.d.mts +31 -4
  33. package/instance-client.d.mts.map +1 -1
  34. package/instance-client.d.ts +31 -4
  35. package/instance-client.d.ts.map +1 -1
  36. package/instance-client.js +61 -2
  37. package/instance-client.js.map +1 -1
  38. package/instance-client.mjs +60 -2
  39. package/instance-client.mjs.map +1 -1
  40. package/internal/download-file.d.mts +2 -0
  41. package/internal/download-file.d.mts.map +1 -0
  42. package/internal/download-file.d.ts +2 -0
  43. package/internal/download-file.d.ts.map +1 -0
  44. package/internal/download-file.js +35 -0
  45. package/internal/download-file.js.map +1 -0
  46. package/internal/download-file.mjs +31 -0
  47. package/internal/download-file.mjs.map +1 -0
  48. package/internal/proxy-transport.d.mts +14 -0
  49. package/internal/proxy-transport.d.mts.map +1 -0
  50. package/internal/proxy-transport.d.ts +14 -0
  51. package/internal/proxy-transport.d.ts.map +1 -0
  52. package/internal/proxy-transport.js +59 -0
  53. package/internal/proxy-transport.js.map +1 -0
  54. package/internal/proxy-transport.mjs +56 -0
  55. package/internal/proxy-transport.mjs.map +1 -0
  56. package/internal/shims.d.mts.map +1 -1
  57. package/internal/shims.d.ts.map +1 -1
  58. package/internal/shims.js +2 -1
  59. package/internal/shims.js.map +1 -1
  60. package/internal/shims.mjs +2 -1
  61. package/internal/shims.mjs.map +1 -1
  62. package/internal/tslib.js +4 -4
  63. package/internal/utils/env.js +2 -2
  64. package/internal/utils/env.js.map +1 -1
  65. package/internal/utils/env.mjs +2 -2
  66. package/internal/utils/env.mjs.map +1 -1
  67. package/ios-client.d.mts +13 -1
  68. package/ios-client.d.mts.map +1 -1
  69. package/ios-client.d.ts +13 -1
  70. package/ios-client.d.ts.map +1 -1
  71. package/ios-client.js +44 -65
  72. package/ios-client.js.map +1 -1
  73. package/ios-client.mjs +42 -63
  74. package/ios-client.mjs.map +1 -1
  75. package/package.json +4 -11
  76. package/resources/android-instances.d.mts +5 -4
  77. package/resources/android-instances.d.mts.map +1 -1
  78. package/resources/android-instances.d.ts +5 -4
  79. package/resources/android-instances.d.ts.map +1 -1
  80. package/resources/assets-helpers.d.mts.map +1 -1
  81. package/resources/assets-helpers.d.ts.map +1 -1
  82. package/resources/assets-helpers.js +2 -1
  83. package/resources/assets-helpers.js.map +1 -1
  84. package/resources/assets-helpers.mjs +2 -1
  85. package/resources/assets-helpers.mjs.map +1 -1
  86. package/resources/index.d.mts +2 -0
  87. package/resources/index.d.mts.map +1 -1
  88. package/resources/index.d.ts +2 -0
  89. package/resources/index.d.ts.map +1 -1
  90. package/resources/index.js +3 -1
  91. package/resources/index.js.map +1 -1
  92. package/resources/index.mjs +1 -0
  93. package/resources/index.mjs.map +1 -1
  94. package/resources/ios-instances.d.mts +4 -4
  95. package/resources/ios-instances.d.ts +4 -4
  96. package/resources/xcode-instances-helpers.d.mts +76 -0
  97. package/resources/xcode-instances-helpers.d.mts.map +1 -0
  98. package/resources/xcode-instances-helpers.d.ts +76 -0
  99. package/resources/xcode-instances-helpers.d.ts.map +1 -0
  100. package/resources/xcode-instances-helpers.js +150 -0
  101. package/resources/xcode-instances-helpers.js.map +1 -0
  102. package/resources/xcode-instances-helpers.mjs +145 -0
  103. package/resources/xcode-instances-helpers.mjs.map +1 -0
  104. package/resources/xcode-instances.d.mts +122 -0
  105. package/resources/xcode-instances.d.mts.map +1 -0
  106. package/resources/xcode-instances.d.ts +122 -0
  107. package/resources/xcode-instances.d.ts.map +1 -0
  108. package/resources/xcode-instances.js +40 -0
  109. package/resources/xcode-instances.js.map +1 -0
  110. package/resources/xcode-instances.mjs +36 -0
  111. package/resources/xcode-instances.mjs.map +1 -0
  112. package/src/client.ts +27 -0
  113. package/src/exec-client.ts +12 -5
  114. package/src/folder-sync.ts +15 -12
  115. package/src/index.ts +6 -9
  116. package/src/instance-client.ts +107 -8
  117. package/src/internal/download-file.ts +33 -0
  118. package/src/internal/proxy-transport.ts +69 -0
  119. package/src/internal/shims.ts +2 -1
  120. package/src/internal/utils/env.ts +2 -2
  121. package/src/ios-client.ts +45 -67
  122. package/src/resources/android-instances.ts +6 -4
  123. package/src/resources/assets-helpers.ts +2 -1
  124. package/src/resources/index.ts +13 -0
  125. package/src/resources/ios-instances.ts +4 -4
  126. package/src/resources/xcode-instances-helpers.ts +228 -0
  127. package/src/resources/xcode-instances.ts +177 -0
  128. package/src/tunnel.ts +5 -0
  129. package/src/version.ts +1 -1
  130. package/tunnel.d.mts.map +1 -1
  131. package/tunnel.d.ts.map +1 -1
  132. package/tunnel.js +5 -0
  133. package/tunnel.js.map +1 -1
  134. package/tunnel.mjs +5 -0
  135. package/tunnel.mjs.map +1 -1
  136. package/version.d.mts +1 -1
  137. package/version.d.ts +1 -1
  138. package/version.js +1 -1
  139. package/version.mjs +1 -1
  140. package/sandbox-client.d.mts +0 -129
  141. package/sandbox-client.d.mts.map +0 -1
  142. package/sandbox-client.d.ts +0 -129
  143. package/sandbox-client.d.ts.map +0 -1
  144. package/sandbox-client.js +0 -158
  145. package/sandbox-client.js.map +0 -1
  146. package/sandbox-client.mjs +0 -154
  147. package/sandbox-client.mjs.map +0 -1
  148. package/src/sandbox-client.ts +0 -277
@@ -1,154 +0,0 @@
1
- import os from 'os';
2
- import path from 'path';
3
- import { syncFolder as syncFolderImpl } from "./folder-sync.mjs";
4
- import { exec } from "./exec-client.mjs";
5
- import { createIgnoreFn } from "./folder-sync-ignore.mjs";
6
- import crypto from 'crypto';
7
- /**
8
- * Creates a client for interacting with a sandboxed Xcode build service.
9
- *
10
- * @example
11
- * // When using an iOS instance (simulator already configured):
12
- * const client = await createXCodeSandboxClient({
13
- * apiUrl: instance.status.sandbox.xcode.url,
14
- * token: apiKey,
15
- * });
16
- *
17
- * // When using a standalone sandbox (need to configure simulator):
18
- * const client = await createXCodeSandboxClient({
19
- * apiUrl: 'https://sandbox.example.com',
20
- * token: 'xxx',
21
- * simulator: {
22
- * apiUrl: 'https://limulator.example.com',
23
- * token: 'yyy', // optional, defaults to sandbox token
24
- * },
25
- * });
26
- *
27
- * // Sync code and build
28
- * await client.sync('./my-ios-app', { watch: true });
29
- * const build = client.xcodebuild();
30
- * build.stdout.on('data', (line) => console.log('[build]', line));
31
- * const { exitCode } = await build;
32
- */
33
- export async function createXCodeSandboxClient(options) {
34
- const logLevel = options.logLevel ?? 'info';
35
- const logger = {
36
- debug: (...args) => {
37
- if (logLevel === 'debug')
38
- console.log('[XCodeSandbox]', ...args);
39
- },
40
- info: (...args) => {
41
- if (logLevel === 'info' || logLevel === 'debug')
42
- console.log('[XCodeSandbox]', ...args);
43
- },
44
- warn: (...args) => {
45
- if (logLevel === 'warn' || logLevel === 'info' || logLevel === 'debug')
46
- console.warn('[XCodeSandbox]', ...args);
47
- },
48
- error: (...args) => {
49
- if (logLevel !== 'none')
50
- console.error('[XCodeSandbox]', ...args);
51
- },
52
- };
53
- const log = (level, msg) => {
54
- switch (level) {
55
- case 'debug':
56
- logger.debug(msg);
57
- break;
58
- case 'info':
59
- logger.info(msg);
60
- break;
61
- case 'warn':
62
- logger.warn(msg);
63
- break;
64
- case 'error':
65
- logger.error(msg);
66
- break;
67
- default:
68
- logger.info(msg);
69
- break;
70
- }
71
- };
72
- // Configure the simulator connection if provided
73
- if (options.simulator) {
74
- const cfg = {
75
- simulatorApiUrl: options.simulator.apiUrl,
76
- simulatorToken: options.simulator.token ?? options.token,
77
- };
78
- const res = await fetch(`${options.apiUrl}/simulator`, {
79
- method: 'POST',
80
- headers: {
81
- 'Content-Type': 'application/json',
82
- Authorization: `Bearer ${options.token}`,
83
- },
84
- body: JSON.stringify(cfg),
85
- });
86
- const text = await res.text();
87
- if (!res.ok) {
88
- throw new Error(`POST /simulator failed: ${res.status} ${text}`);
89
- }
90
- }
91
- return {
92
- async sync(localCodePath, opts) {
93
- // Use folder name and hash of absolute path to scope basisCacheDir uniquely for each sync root
94
- const resolvedPath = path.resolve(localCodePath);
95
- const folderName = path.basename(resolvedPath);
96
- const hash = crypto.createHash('sha1').update(resolvedPath).digest('hex').slice(0, 8);
97
- const cacheKey = `limsync-cache-${folderName}-${hash}`;
98
- const basisCacheDir = opts?.basisCacheDir ?? path.join(os.tmpdir(), cacheKey);
99
- const codeSyncOpts = {
100
- apiUrl: options.apiUrl,
101
- token: options.token,
102
- udid: cacheKey,
103
- install: opts?.install ?? true,
104
- ignoreFn: await createIgnoreFn(localCodePath, {
105
- basisCacheDir,
106
- additional: (relativePath) => {
107
- if (relativePath.startsWith('build/') ||
108
- relativePath.startsWith('.build/') ||
109
- relativePath.startsWith('DerivedData/') ||
110
- relativePath.startsWith('Index.noindex/') ||
111
- relativePath.startsWith('ModuleCache.noindex/') ||
112
- relativePath.startsWith('.index-build/')) {
113
- return true;
114
- }
115
- if (relativePath.startsWith('.swiftpm/') ||
116
- relativePath.startsWith('Pods/') ||
117
- relativePath.startsWith('Carthage/Build/')) {
118
- return true;
119
- }
120
- if (relativePath.includes('/xcuserdata/')) {
121
- return true;
122
- }
123
- if (relativePath.includes('.dSYM/')) {
124
- return true;
125
- }
126
- // User-provided ignores
127
- if (opts?.ignore?.(relativePath)) {
128
- return true;
129
- }
130
- return false;
131
- },
132
- }),
133
- basisCacheDir,
134
- watch: opts?.watch ?? true,
135
- maxPatchBytes: opts?.maxPatchBytes ?? 4 * 1024 * 1024,
136
- launchMode: 'ForegroundIfRunning',
137
- log,
138
- };
139
- const result = await syncFolderImpl(localCodePath, codeSyncOpts);
140
- if (result.stopWatching) {
141
- return { stopWatching: result.stopWatching };
142
- }
143
- return {};
144
- },
145
- xcodebuild(opts) {
146
- return exec({ command: 'xcodebuild', ...(opts && { xcodebuild: opts }) }, {
147
- apiUrl: options.apiUrl,
148
- token: options.token,
149
- log,
150
- });
151
- },
152
- };
153
- }
154
- //# sourceMappingURL=sandbox-client.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sandbox-client.mjs","sourceRoot":"","sources":["src/sandbox-client.ts"],"names":[],"mappings":"OAAO,EAAE,MAAM,IAAI;OACZ,IAAI,MAAM,MAAM;OAChB,EAAE,UAAU,IAAI,cAAc,EAA0B;OACxD,EAAE,IAAI,EAAoB;OAC1B,EAAE,cAAc,EAAE;OAClB,MAAM,MAAM,QAAQ;AA8G3B;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,OAAwC;IAExC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC;IAC5C,MAAM,MAAM,GAAG;QACb,KAAK,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE;YAC5B,IAAI,QAAQ,KAAK,OAAO;gBAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,GAAG,IAAI,CAAC,CAAC;QACnE,CAAC;QACD,IAAI,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE;YAC3B,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,OAAO;gBAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,GAAG,IAAI,CAAC,CAAC;QAC1F,CAAC;QACD,IAAI,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE;YAC3B,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,OAAO;gBACpE,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,GAAG,IAAI,CAAC,CAAC;QAC5C,CAAC;QACD,KAAK,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE;YAC5B,IAAI,QAAQ,KAAK,MAAM;gBAAE,OAAO,CAAC,KAAK,CAAC,gBAAgB,EAAE,GAAG,IAAI,CAAC,CAAC;QACpE,CAAC;KACF,CAAC;IAEF,MAAM,GAAG,GAAG,CAAC,KAA0C,EAAE,GAAW,EAAE,EAAE;QACtE,QAAQ,KAAK,EAAE,CAAC;YACd,KAAK,OAAO;gBACV,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAClB,MAAM;YACR,KAAK,MAAM;gBACT,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACjB,MAAM;YACR,KAAK,MAAM;gBACT,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACjB,MAAM;YACR,KAAK,OAAO;gBACV,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAClB,MAAM;YACR;gBACE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACjB,MAAM;QACV,CAAC;IACH,CAAC,CAAC;IAEF,iDAAiD;IACjD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,MAAM,GAAG,GAGL;YACF,eAAe,EAAE,OAAO,CAAC,SAAS,CAAC,MAAM;YACzC,cAAc,EAAE,OAAO,CAAC,SAAS,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK;SACzD,CAAC;QAEF,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,YAAY,EAAE;YACrD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,OAAO,CAAC,KAAK,EAAE;aACzC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;SAC1B,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,aAAqB,EAAE,IAAkB;YAClD,+FAA+F;YAC/F,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;YACjD,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACtF,MAAM,QAAQ,GAAG,iBAAiB,UAAU,IAAI,IAAI,EAAE,CAAC;YACvD,MAAM,aAAa,GAAG,IAAI,EAAE,aAAa,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,QAAQ,CAAC,CAAC;YAC9E,MAAM,YAAY,GAAsB;gBACtC,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,IAAI;gBAC9B,QAAQ,EAAE,MAAM,cAAc,CAAC,aAAa,EAAE;oBAC5C,aAAa;oBACb,UAAU,EAAE,CAAC,YAAoB,EAAE,EAAE;wBACnC,IACE,YAAY,CAAC,UAAU,CAAC,QAAQ,CAAC;4BACjC,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC;4BAClC,YAAY,CAAC,UAAU,CAAC,cAAc,CAAC;4BACvC,YAAY,CAAC,UAAU,CAAC,gBAAgB,CAAC;4BACzC,YAAY,CAAC,UAAU,CAAC,sBAAsB,CAAC;4BAC/C,YAAY,CAAC,UAAU,CAAC,eAAe,CAAC,EACxC,CAAC;4BACD,OAAO,IAAI,CAAC;wBACd,CAAC;wBACD,IACE,YAAY,CAAC,UAAU,CAAC,WAAW,CAAC;4BACpC,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC;4BAChC,YAAY,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAC1C,CAAC;4BACD,OAAO,IAAI,CAAC;wBACd,CAAC;wBACD,IAAI,YAAY,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;4BAC1C,OAAO,IAAI,CAAC;wBACd,CAAC;wBACD,IAAI,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;4BACpC,OAAO,IAAI,CAAC;wBACd,CAAC;wBACD,wBAAwB;wBACxB,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC;4BACjC,OAAO,IAAI,CAAC;wBACd,CAAC;wBACD,OAAO,KAAK,CAAC;oBACf,CAAC;iBACF,CAAC;gBACF,aAAa;gBACb,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,IAAI;gBAC1B,aAAa,EAAE,IAAI,EAAE,aAAa,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI;gBACrD,UAAU,EAAE,qBAAqB;gBACjC,GAAG;aACJ,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;YACjE,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;gBACxB,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC;YAC/C,CAAC;YACD,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,UAAU,CAAC,IAAuB;YAChC,OAAO,IAAI,CACT,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,EAAE,EAC5D;gBACE,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,GAAG;aACJ,CACF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1,277 +0,0 @@
1
- import os from 'os';
2
- import path from 'path';
3
- import { syncFolder as syncFolderImpl, type FolderSyncOptions } from './folder-sync';
4
- import { exec, ExecChildProcess } from './exec-client';
5
- import { createIgnoreFn } from './folder-sync-ignore';
6
- import crypto from 'crypto';
7
-
8
- export type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
9
-
10
- /**
11
- * Build configuration for xcodebuild command.
12
- */
13
- export type XcodeBuildConfig = {
14
- workspace?: string;
15
- project?: string;
16
- scheme?: string;
17
- };
18
-
19
- /**
20
- * Simulator connection details for configuring the sandbox.
21
- */
22
- export type SimulatorConfig = {
23
- /** The API URL of the simulator (limulator) */
24
- apiUrl: string;
25
- /** Auth token for the simulator. If not provided, uses the sandbox token. */
26
- token?: string;
27
- };
28
-
29
- /**
30
- * Options for syncing source code to the sandbox.
31
- */
32
- export type SyncOptions = {
33
- /**
34
- * If true, watch the folder and re-sync on any changes. Defaults to true.
35
- */
36
- watch?: boolean;
37
- /**
38
- * Directory for the client-side folder-sync cache.
39
- * Used to store the last-synced “basis” copies of files (and related sync metadata) so we can compute xdelta patches
40
- * on subsequent syncs without re-downloading server state.
41
- *
42
- * Defaults to a temporary directory under the OS temp directory.
43
- */
44
- basisCacheDir?: string;
45
- /** Max patch size (bytes) to send as delta before falling back to full upload. */
46
- maxPatchBytes?: number;
47
- /** If true, install the app after syncing. Defaults to true. */
48
- install?: boolean;
49
- /**
50
- * Optional predicate for ignoring files and directories during sync.
51
- * Applied in addition to built-in sync and Xcode-specific ignore rules.
52
- * Called with the relative path from the sync root (using forward slashes).
53
- * For directories, the path ends with '/'.
54
- * Return true to ignore, false to keep.
55
- *
56
- * @example
57
- * // Ignore build folder
58
- * ignore: (path) => path.startsWith('build/')
59
- *
60
- * @example
61
- * // Ignore anything outside src/ and JSON files
62
- * ignore: (path) => !(path.startsWith('src/') || path.endsWith('.json'))
63
- */
64
- ignore?: (relativePath: string) => boolean;
65
- };
66
-
67
- /**
68
- * Result of a sync operation.
69
- */
70
- export type SyncResult = {
71
- /** Present only when watch=true; call to stop watching */
72
- stopWatching?: () => void;
73
- };
74
-
75
- /**
76
- * Client for interacting with a sandboxed Xcode build service.
77
- */
78
- export type XCodeSandboxClient = {
79
- /**
80
- * Sync source code to the sandbox. In watch mode, keeps syncing on changes.
81
- * Does NOT trigger builds - call xcodebuild() when ready.
82
- */
83
- sync: (localCodePath: string, opts?: SyncOptions) => Promise<SyncResult>;
84
-
85
- /**
86
- * Trigger xcodebuild on the synced source code.
87
- * Returns a ChildProcess-like object for streaming output.
88
- *
89
- * @example
90
- * // Stream build output
91
- * const build = client.xcodebuild();
92
- * build.stdout.on('data', (line) => console.log(line));
93
- * const { exitCode } = await build;
94
- */
95
- xcodebuild: (opts?: XcodeBuildConfig) => ExecChildProcess;
96
- };
97
-
98
- export type CreateXCodeSandboxClientOptions = {
99
- /** The API URL of the Xcode sandbox server */
100
- apiUrl: string;
101
- /** Auth token for the sandbox */
102
- token: string;
103
- /**
104
- * Simulator (limulator) connection details. Only needed if the sandbox is not
105
- * already configured (e.g., when created outside of an iOS instance).
106
- * When provided, the client will call POST /simulator to set up the connection.
107
- */
108
- simulator?: SimulatorConfig;
109
- /**
110
- * Controls logging verbosity
111
- * @default 'info'
112
- */
113
- logLevel?: LogLevel;
114
- };
115
-
116
- /**
117
- * Creates a client for interacting with a sandboxed Xcode build service.
118
- *
119
- * @example
120
- * // When using an iOS instance (simulator already configured):
121
- * const client = await createXCodeSandboxClient({
122
- * apiUrl: instance.status.sandbox.xcode.url,
123
- * token: apiKey,
124
- * });
125
- *
126
- * // When using a standalone sandbox (need to configure simulator):
127
- * const client = await createXCodeSandboxClient({
128
- * apiUrl: 'https://sandbox.example.com',
129
- * token: 'xxx',
130
- * simulator: {
131
- * apiUrl: 'https://limulator.example.com',
132
- * token: 'yyy', // optional, defaults to sandbox token
133
- * },
134
- * });
135
- *
136
- * // Sync code and build
137
- * await client.sync('./my-ios-app', { watch: true });
138
- * const build = client.xcodebuild();
139
- * build.stdout.on('data', (line) => console.log('[build]', line));
140
- * const { exitCode } = await build;
141
- */
142
- export async function createXCodeSandboxClient(
143
- options: CreateXCodeSandboxClientOptions,
144
- ): Promise<XCodeSandboxClient> {
145
- const logLevel = options.logLevel ?? 'info';
146
- const logger = {
147
- debug: (...args: unknown[]) => {
148
- if (logLevel === 'debug') console.log('[XCodeSandbox]', ...args);
149
- },
150
- info: (...args: unknown[]) => {
151
- if (logLevel === 'info' || logLevel === 'debug') console.log('[XCodeSandbox]', ...args);
152
- },
153
- warn: (...args: unknown[]) => {
154
- if (logLevel === 'warn' || logLevel === 'info' || logLevel === 'debug')
155
- console.warn('[XCodeSandbox]', ...args);
156
- },
157
- error: (...args: unknown[]) => {
158
- if (logLevel !== 'none') console.error('[XCodeSandbox]', ...args);
159
- },
160
- };
161
-
162
- const log = (level: 'debug' | 'info' | 'warn' | 'error', msg: string) => {
163
- switch (level) {
164
- case 'debug':
165
- logger.debug(msg);
166
- break;
167
- case 'info':
168
- logger.info(msg);
169
- break;
170
- case 'warn':
171
- logger.warn(msg);
172
- break;
173
- case 'error':
174
- logger.error(msg);
175
- break;
176
- default:
177
- logger.info(msg);
178
- break;
179
- }
180
- };
181
-
182
- // Configure the simulator connection if provided
183
- if (options.simulator) {
184
- const cfg: {
185
- simulatorApiUrl?: string;
186
- simulatorToken?: string;
187
- } = {
188
- simulatorApiUrl: options.simulator.apiUrl,
189
- simulatorToken: options.simulator.token ?? options.token,
190
- };
191
-
192
- const res = await fetch(`${options.apiUrl}/simulator`, {
193
- method: 'POST',
194
- headers: {
195
- 'Content-Type': 'application/json',
196
- Authorization: `Bearer ${options.token}`,
197
- },
198
- body: JSON.stringify(cfg),
199
- });
200
- const text = await res.text();
201
- if (!res.ok) {
202
- throw new Error(`POST /simulator failed: ${res.status} ${text}`);
203
- }
204
- }
205
-
206
- return {
207
- async sync(localCodePath: string, opts?: SyncOptions): Promise<SyncResult> {
208
- // Use folder name and hash of absolute path to scope basisCacheDir uniquely for each sync root
209
- const resolvedPath = path.resolve(localCodePath);
210
- const folderName = path.basename(resolvedPath);
211
- const hash = crypto.createHash('sha1').update(resolvedPath).digest('hex').slice(0, 8);
212
- const cacheKey = `limsync-cache-${folderName}-${hash}`;
213
- const basisCacheDir = opts?.basisCacheDir ?? path.join(os.tmpdir(), cacheKey);
214
- const codeSyncOpts: FolderSyncOptions = {
215
- apiUrl: options.apiUrl,
216
- token: options.token,
217
- udid: cacheKey,
218
- install: opts?.install ?? true,
219
- ignoreFn: await createIgnoreFn(localCodePath, {
220
- basisCacheDir,
221
- additional: (relativePath: string) => {
222
- if (
223
- relativePath.startsWith('build/') ||
224
- relativePath.startsWith('.build/') ||
225
- relativePath.startsWith('DerivedData/') ||
226
- relativePath.startsWith('Index.noindex/') ||
227
- relativePath.startsWith('ModuleCache.noindex/') ||
228
- relativePath.startsWith('.index-build/')
229
- ) {
230
- return true;
231
- }
232
- if (
233
- relativePath.startsWith('.swiftpm/') ||
234
- relativePath.startsWith('Pods/') ||
235
- relativePath.startsWith('Carthage/Build/')
236
- ) {
237
- return true;
238
- }
239
- if (relativePath.includes('/xcuserdata/')) {
240
- return true;
241
- }
242
- if (relativePath.includes('.dSYM/')) {
243
- return true;
244
- }
245
- // User-provided ignores
246
- if (opts?.ignore?.(relativePath)) {
247
- return true;
248
- }
249
- return false;
250
- },
251
- }),
252
- basisCacheDir,
253
- watch: opts?.watch ?? true,
254
- maxPatchBytes: opts?.maxPatchBytes ?? 4 * 1024 * 1024,
255
- launchMode: 'ForegroundIfRunning',
256
- log,
257
- };
258
-
259
- const result = await syncFolderImpl(localCodePath, codeSyncOpts);
260
- if (result.stopWatching) {
261
- return { stopWatching: result.stopWatching };
262
- }
263
- return {};
264
- },
265
-
266
- xcodebuild(opts?: XcodeBuildConfig): ExecChildProcess {
267
- return exec(
268
- { command: 'xcodebuild', ...(opts && { xcodebuild: opts }) },
269
- {
270
- apiUrl: options.apiUrl,
271
- token: options.token,
272
- log,
273
- },
274
- );
275
- },
276
- };
277
- }