@limrun/api 0.46.0 → 0.46.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 (73) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/client.d.mts +2 -3
  3. package/client.d.mts.map +1 -1
  4. package/client.d.ts +2 -3
  5. package/client.d.ts.map +1 -1
  6. package/client.js +2 -2
  7. package/client.js.map +1 -1
  8. package/client.mjs +1 -1
  9. package/client.mjs.map +1 -1
  10. package/exec-client.d.mts +12 -0
  11. package/exec-client.d.mts.map +1 -1
  12. package/exec-client.d.ts +12 -0
  13. package/exec-client.d.ts.map +1 -1
  14. package/exec-client.js.map +1 -1
  15. package/exec-client.mjs.map +1 -1
  16. package/index.d.mts +2 -1
  17. package/index.d.mts.map +1 -1
  18. package/index.d.ts +2 -1
  19. package/index.d.ts.map +1 -1
  20. package/index.js +10 -1
  21. package/index.js.map +1 -1
  22. package/index.mjs +1 -0
  23. package/index.mjs.map +1 -1
  24. package/package.json +11 -1
  25. package/resources/gradle-instances-helpers.d.mts +9 -7
  26. package/resources/gradle-instances-helpers.d.mts.map +1 -1
  27. package/resources/gradle-instances-helpers.d.ts +9 -7
  28. package/resources/gradle-instances-helpers.d.ts.map +1 -1
  29. package/resources/gradle-instances-helpers.js +7 -3
  30. package/resources/gradle-instances-helpers.js.map +1 -1
  31. package/resources/gradle-instances-helpers.mjs +8 -2
  32. package/resources/gradle-instances-helpers.mjs.map +1 -1
  33. package/resources/gradle-instances.d.mts +9 -0
  34. package/resources/gradle-instances.d.mts.map +1 -1
  35. package/resources/gradle-instances.d.ts +9 -0
  36. package/resources/gradle-instances.d.ts.map +1 -1
  37. package/resources/index.d.mts +4 -3
  38. package/resources/index.d.mts.map +1 -1
  39. package/resources/index.d.ts +4 -3
  40. package/resources/index.d.ts.map +1 -1
  41. package/resources/index.js +7 -3
  42. package/resources/index.js.map +1 -1
  43. package/resources/index.mjs +5 -1
  44. package/resources/index.mjs.map +1 -1
  45. package/resources/xcode-instances-helpers.d.mts +45 -2
  46. package/resources/xcode-instances-helpers.d.mts.map +1 -1
  47. package/resources/xcode-instances-helpers.d.ts +45 -2
  48. package/resources/xcode-instances-helpers.d.ts.map +1 -1
  49. package/resources/xcode-instances-helpers.js +39 -0
  50. package/resources/xcode-instances-helpers.js.map +1 -1
  51. package/resources/xcode-instances-helpers.mjs +40 -1
  52. package/resources/xcode-instances-helpers.mjs.map +1 -1
  53. package/src/client.ts +7 -19
  54. package/src/exec-client.ts +12 -0
  55. package/src/index.ts +23 -0
  56. package/src/resources/gradle-instances-helpers.ts +21 -4
  57. package/src/resources/gradle-instances.ts +10 -0
  58. package/src/resources/index.ts +14 -8
  59. package/src/resources/xcode-instances-helpers.ts +79 -2
  60. package/src/version.ts +1 -1
  61. package/src/xcode-cache.ts +327 -0
  62. package/version.d.mts +1 -1
  63. package/version.d.ts +1 -1
  64. package/version.js +1 -1
  65. package/version.mjs +1 -1
  66. package/xcode-cache.d.mts +142 -0
  67. package/xcode-cache.d.mts.map +1 -0
  68. package/xcode-cache.d.ts +142 -0
  69. package/xcode-cache.d.ts.map +1 -0
  70. package/xcode-cache.js +175 -0
  71. package/xcode-cache.js.map +1 -0
  72. package/xcode-cache.mjs +165 -0
  73. package/xcode-cache.mjs.map +1 -0
@@ -24,6 +24,13 @@ export {
24
24
  type AssetGetParams,
25
25
  type AssetGetOrCreateParams,
26
26
  } from './assets';
27
+ export {
28
+ GradleInstances,
29
+ type GradleInstance,
30
+ type GradleInstanceCreateParams,
31
+ type GradleInstanceListParams,
32
+ type GradleInstancesItems,
33
+ } from './gradle-instances';
27
34
  export {
28
35
  IosInstances,
29
36
  type IosInstance,
@@ -38,13 +45,6 @@ export {
38
45
  type XcodeInstanceListParams,
39
46
  type XcodeInstancesItems,
40
47
  } from './xcode-instances';
41
- export {
42
- type GradleInstance,
43
- type GradleInstanceCreateParams,
44
- type GradleInstanceListParams,
45
- type GradleInstancesItems,
46
- } from './gradle-instances';
47
-
48
48
  export { Assets, AssetGetOrUploadParams, AssetGetOrUploadResponse } from './assets-helpers';
49
49
  export {
50
50
  XcodeInstances,
@@ -57,6 +57,8 @@ export {
57
57
  type XcodeRunOptions,
58
58
  type XcodeGenConfig,
59
59
  type XcodeSigningConfig,
60
+ type XcodeCloudSigningConfig,
61
+ type XcodeCloudSigningMethod,
60
62
  type ReactNativeBuildConfig,
61
63
  type SimulatorAttachResult,
62
64
  type SimulatorStatus,
@@ -68,9 +70,13 @@ export {
68
70
  type XcodeBuildLog,
69
71
  type BazelBuildLog,
70
72
  type WebhookConfig,
73
+ type XcodeInstanceCreateParamsWithCache,
71
74
  } from './xcode-instances-helpers';
75
+ // Side-effect import: grafts the createClient/listBuildLogs helpers onto the
76
+ // generated GradleInstances class. The type-only re-export below is erased at
77
+ // runtime, so it would not load the module on its own.
78
+ import './gradle-instances-helpers';
72
79
  export {
73
- GradleInstances,
74
80
  type GradleCreateClientParams,
75
81
  type GradleClient,
76
82
  type GradleSyncOptions,
@@ -1,8 +1,19 @@
1
- import { XcodeInstances as GeneratedXcodeInstances, type XcodeInstance } from './xcode-instances';
1
+ import {
2
+ XcodeInstances as GeneratedXcodeInstances,
3
+ type XcodeInstance,
4
+ type XcodeInstanceCreateParams,
5
+ } from './xcode-instances';
2
6
  import { type IosInstance } from './ios-instances';
3
7
  import { APIPromise } from '../core/api-promise';
4
8
  import { RequestOptions } from '../internal/request-options';
5
9
  import { path } from '../internal/utils/path';
10
+ import {
11
+ followXcodeCache,
12
+ type XcodeCacheConfig,
13
+ type XcodeCacheFollowOptions,
14
+ type XcodeCacheFollowResult,
15
+ type XcodeInstanceCache,
16
+ } from '../xcode-cache';
6
17
  import {
7
18
  exec,
8
19
  type AppStoreUploadConfig,
@@ -130,6 +141,21 @@ export type XcodeSigningConfig = {
130
141
  provisioningProfilesBase64?: string[];
131
142
  };
132
143
 
144
+ export type XcodeCloudSigningMethod = 'app-store-connect' | 'release-testing' | 'debugging';
145
+
146
+ /**
147
+ * Apple-managed signing credentials used while exporting an unsigned device
148
+ * archive. Apple retains the signing certificate; callers provide only the
149
+ * App Store Connect team key.
150
+ */
151
+ export type XcodeCloudSigningConfig = {
152
+ method: XcodeCloudSigningMethod;
153
+ teamId: string;
154
+ apiKeyId: string;
155
+ apiIssuerId: string;
156
+ apiPrivateKeyBase64: string;
157
+ };
158
+
133
159
  export type ReactNativeBuildConfig = {
134
160
  /**
135
161
  * Relative path from the synced workspace root to the Expo app directory.
@@ -151,9 +177,10 @@ export type ReactNativeBuildConfig = {
151
177
  export type XcodeBuildOptions = {
152
178
  upload?: { assetName: string } | { signedUploadUrl: string };
153
179
  signing?: XcodeSigningConfig;
180
+ cloudSigning?: XcodeCloudSigningConfig;
154
181
  /**
155
182
  * Upload the signed device IPA to App Store Connect after the build.
156
- * Requires signing and sdk=iphoneos. Check ExecResult.appstore on
183
+ * Requires manual or cloud signing and sdk=iphoneos. Check ExecResult.appstore on
157
184
  * completion; it is absent when the instance's limbuild predates the
158
185
  * feature (old servers silently ignore this option).
159
186
  */
@@ -442,6 +469,11 @@ export type XcodeCreateClientParams =
442
469
  | { instance: XcodeInstance; logLevel?: LogLevel }
443
470
  | { apiUrl: string; token: string; logLevel?: LogLevel };
444
471
 
472
+ /** Generated create params plus the build cache the generator does not know about yet. */
473
+ export type XcodeInstanceCreateParamsWithCache = Omit<XcodeInstanceCreateParams, 'spec'> & {
474
+ spec?: XcodeInstanceCreateParams['spec'] & { cache?: XcodeCacheConfig };
475
+ };
476
+
445
477
  function normalizeWorkspaceRelativePath(remotePath: string): string {
446
478
  if (
447
479
  remotePath === '' ||
@@ -584,6 +616,50 @@ export class XcodeInstances extends GeneratedXcodeInstances {
584
616
  return this._client.get(path`/v1/xcode_instances/${id}/bazel_build_logs`, options);
585
617
  }
586
618
 
619
+ /**
620
+ * Create an Xcode instance, optionally with a build cache.
621
+ *
622
+ * Widens the generated signature with `spec.cache`; drop this override once the generator
623
+ * knows about the field. The request body passes through untouched either way.
624
+ */
625
+ override create(
626
+ params: XcodeInstanceCreateParamsWithCache,
627
+ options?: RequestOptions,
628
+ ): APIPromise<XcodeInstance> {
629
+ return super.create(params as XcodeInstanceCreateParams, options);
630
+ }
631
+
632
+ /** Current cache configuration and status of an instance. */
633
+ getCache(id: string, options?: RequestOptions): APIPromise<XcodeInstanceCache> {
634
+ return this._client.get<XcodeInstanceCache>(path`/v1/xcode_instances/${id}/cache`, options);
635
+ }
636
+
637
+ /**
638
+ * Bind the destination key this instance publishes under at termination. Set once: binding
639
+ * the same key again is accepted, a different one is rejected.
640
+ *
641
+ * The instance needs a stable directory to publish from, which only a create that asked for
642
+ * caching allocates, so bind on an instance created with cache paths or a cache key.
643
+ */
644
+ bindCacheKey(id: string, key: string, options?: RequestOptions): APIPromise<XcodeInstanceCache> {
645
+ return this._client.put<XcodeInstanceCache>(path`/v1/xcode_instances/${id}/cache`, {
646
+ body: { key },
647
+ ...options,
648
+ });
649
+ }
650
+
651
+ /**
652
+ * Follow an instance's cache status until the chosen side reaches a terminal phase. Reports
653
+ * every phase change through `onUpdate`, so a caller can show a restore as it happens.
654
+ */
655
+ followCache(id: string, options?: XcodeCacheFollowOptions): Promise<XcodeCacheFollowResult> {
656
+ const apiKey = this._client.apiKey;
657
+ if (!apiKey) {
658
+ throw new Error('Following cache status needs an API key on the client');
659
+ }
660
+ return followXcodeCache({ baseURL: this._client.baseURL, apiKey, instanceId: id }, options);
661
+ }
662
+
587
663
  async createClient(params: XcodeCreateClientParams): Promise<XcodeClient> {
588
664
  let apiUrl: string;
589
665
  let token: string;
@@ -720,6 +796,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
720
796
  ...(options?.xcodegen && { xcodegen: options.xcodegen }),
721
797
  ...(options?.reactNative && { reactNative: options.reactNative }),
722
798
  ...(options?.signing && { signing: options.signing }),
799
+ ...(options?.cloudSigning && { cloudSigning: options.cloudSigning }),
723
800
  // The SDK calls this App Store; limbuild's existing wire field remains
724
801
  // testflight until the exec API is revised separately.
725
802
  ...(options?.appstore && { testflight: options.appstore }),
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.46.0'; // x-release-please-version
1
+ export const VERSION = '0.46.2'; // x-release-please-version
@@ -0,0 +1,327 @@
1
+ import { createEventSource, type EventSourceMessage } from 'eventsource-client';
2
+
3
+ import { nodeProxyTransport } from './internal/proxy-transport';
4
+
5
+ /**
6
+ * Build cache configuration for an Xcode instance. Omitted means an ordinary uncached
7
+ * instance, which resolves nothing, adopts no stable directory, and publishes nothing.
8
+ */
9
+ export type XcodeCacheConfig = {
10
+ /**
11
+ * Where this instance's workspace is published at termination. Organization-scoped and set
12
+ * once, either here or later through `bindCacheKey`. Reusing a key is normal operation:
13
+ * termination replaces its archive.
14
+ *
15
+ * With no restoreKeys, a key given here also serves as the single restore key.
16
+ */
17
+ key?: string;
18
+ /**
19
+ * Keys to restore from, tried in exactly this order: exact match first, then literal prefix,
20
+ * newest archive first within a prefix. Immutable after create.
21
+ */
22
+ restoreKeys?: string[];
23
+ /**
24
+ * Project-root-relative paths to store. Create-only, and it controls what is STORED: the
25
+ * first publication under a key fixes its path set and restores materialize that set.
26
+ * Omitted means the whole deterministic workspace.
27
+ */
28
+ paths?: string[];
29
+ };
30
+
31
+ /**
32
+ * How far a restore got. `disabled`, `unsupported` and `skipped` are not failures: they
33
+ * describe an ordinary cold instance, which is perfectly usable. Only `failed` means the
34
+ * workspace was supposed to be warm and something actually broke.
35
+ */
36
+ export type XcodeCacheRestorePhase =
37
+ | 'disabled'
38
+ | 'unsupported'
39
+ | 'placing'
40
+ | 'downloading'
41
+ | 'materializing'
42
+ | 'restored'
43
+ | 'skipped'
44
+ | 'failed';
45
+
46
+ /** How far publication got. */
47
+ export type XcodeCacheSavePhase =
48
+ | 'disabled'
49
+ | 'idle'
50
+ | 'quiescing'
51
+ | 'archiving'
52
+ | 'uploading'
53
+ | 'verifying'
54
+ | 'published'
55
+ | 'skipped'
56
+ | 'failed'
57
+ | 'timed_out';
58
+
59
+ /** A restore key resolution passed over, so a miss is explainable rather than just a miss. */
60
+ export type XcodeCacheSkippedKey = { key: string; reason: string };
61
+
62
+ export type XcodeCacheRestoreStatus = {
63
+ phase: XcodeCacheRestorePhase;
64
+ reason?: string;
65
+ message?: string;
66
+ /** Restore key whose archive was used. */
67
+ matchedKey?: string;
68
+ matchKind?: 'exact_hit' | 'prefix_hit';
69
+ skippedKeys?: XcodeCacheSkippedKey[];
70
+ /** Path set actually materialized, which is the set the archive stores. */
71
+ paths?: string[];
72
+ /** Whether the bytes came through the regional accelerator or direct from object storage. */
73
+ source?: 'tag' | 'tigris';
74
+ bytes?: number;
75
+ uncompressedBytes?: number;
76
+ startedAt?: string;
77
+ finishedAt?: string;
78
+ };
79
+
80
+ export type XcodeCacheSaveStatus = {
81
+ phase: XcodeCacheSavePhase;
82
+ reason?: string;
83
+ message?: string;
84
+ /** Destination key the archive is published under. */
85
+ cacheKey?: string;
86
+ paths?: string[];
87
+ bytes?: number;
88
+ uncompressedBytes?: number;
89
+ startedAt?: string;
90
+ finishedAt?: string;
91
+ };
92
+
93
+ export type XcodeInstanceCache = {
94
+ config?: XcodeCacheConfig;
95
+ restore: XcodeCacheRestoreStatus;
96
+ save: XcodeCacheSaveStatus;
97
+ };
98
+
99
+ /** Which half of the cache lifecycle a follower waits on. */
100
+ export type XcodeCacheSide = 'restore' | 'save';
101
+
102
+ export type XcodeCacheFollowOptions = {
103
+ /** Defaults to 'restore'. */
104
+ side?: XcodeCacheSide;
105
+ /**
106
+ * Called once per distinct state, including the snapshot the stream opens with. Phases that
107
+ * repeat, which a reconnect produces, are not reported again.
108
+ */
109
+ onUpdate?: (cache: XcodeInstanceCache) => void;
110
+ /**
111
+ * Called when the stream is open, and again after each reconnect. A caller that is about to
112
+ * cause the very transitions it wants to watch waits for this first, so it cannot race its
113
+ * own subscription.
114
+ */
115
+ onOpen?: () => void;
116
+ /**
117
+ * How long to wait for a terminal phase. Restores move multi-gigabyte archives and
118
+ * publication is capped server-side at ten minutes, so these are generous by design.
119
+ */
120
+ timeoutMs?: number;
121
+ signal?: AbortSignal;
122
+ };
123
+
124
+ export type XcodeCacheFollowResult = {
125
+ /** The last state seen, terminal unless the stream ended first. */
126
+ cache: XcodeInstanceCache;
127
+ /**
128
+ * True when the instance was collected before the phase went terminal. The last snapshot is
129
+ * still the summary: the control plane writes the terminal phase and deletes moments later.
130
+ */
131
+ gone: boolean;
132
+ };
133
+
134
+ /**
135
+ * The instance was gone before it said anything about its cache, so there is no last state to
136
+ * summarise. Distinct from a follow that ends with `gone: true`, which at least saw one.
137
+ */
138
+ export class XcodeCacheGoneError extends Error {
139
+ constructor(readonly instanceId: string) {
140
+ super(`Instance ${instanceId} was gone before it reported any cache status`);
141
+ this.name = 'XcodeCacheGoneError';
142
+ }
143
+ }
144
+
145
+ export class XcodeCacheTimeoutError extends Error {
146
+ constructor(
147
+ readonly side: XcodeCacheSide,
148
+ readonly timeoutMs: number,
149
+ readonly cache: XcodeInstanceCache | undefined,
150
+ ) {
151
+ super(
152
+ `Timed out after ${Math.round(timeoutMs / 1000)}s waiting for the cache ${side} to finish` +
153
+ (cache ? ` (last phase: ${cache[side].phase})` : ''),
154
+ );
155
+ this.name = 'XcodeCacheTimeoutError';
156
+ }
157
+ }
158
+
159
+ const DEFAULT_TIMEOUT_MS: Record<XcodeCacheSide, number> = {
160
+ restore: 20 * 60 * 1000,
161
+ save: 12 * 60 * 1000,
162
+ };
163
+
164
+ /** Phases past which nothing more will happen. */
165
+ export function isRestoreTerminal(phase: XcodeCacheRestorePhase): boolean {
166
+ return (
167
+ phase === 'disabled' ||
168
+ phase === 'unsupported' ||
169
+ phase === 'restored' ||
170
+ phase === 'skipped' ||
171
+ phase === 'failed'
172
+ );
173
+ }
174
+
175
+ /**
176
+ * Separates the one outcome worth reacting to from the terminal phases that just mean "cold,
177
+ * carry on". A caller that created an instance for a warm workspace deletes it on a failure,
178
+ * but a fallback to a cold build leaves a perfectly usable instance behind.
179
+ */
180
+ export function isRestoreFailure(phase: XcodeCacheRestorePhase): boolean {
181
+ return phase === 'failed';
182
+ }
183
+
184
+ export function isSaveTerminal(phase: XcodeCacheSavePhase): boolean {
185
+ return (
186
+ phase === 'disabled' ||
187
+ phase === 'published' ||
188
+ phase === 'skipped' ||
189
+ phase === 'failed' ||
190
+ phase === 'timed_out'
191
+ );
192
+ }
193
+
194
+ export function isCacheTerminal(cache: XcodeInstanceCache, side: XcodeCacheSide): boolean {
195
+ return side === 'restore' ? isRestoreTerminal(cache.restore.phase) : isSaveTerminal(cache.save.phase);
196
+ }
197
+
198
+ export type XcodeCacheFollowTarget = {
199
+ baseURL: string;
200
+ apiKey: string;
201
+ instanceId: string;
202
+ };
203
+
204
+ /**
205
+ * Follows an instance's cache status until the chosen side reaches a terminal phase, the
206
+ * instance is collected, or the wait times out.
207
+ *
208
+ * The transport is the same endpoint that serves a JSON snapshot, asked for as an event
209
+ * stream. The server emits on phase change only, so every callback is a real transition.
210
+ */
211
+ export function followXcodeCache(
212
+ target: XcodeCacheFollowTarget,
213
+ options: XcodeCacheFollowOptions = {},
214
+ ): Promise<XcodeCacheFollowResult> {
215
+ const side = options.side ?? 'restore';
216
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS[side];
217
+ let base = target.baseURL;
218
+ while (base.endsWith('/')) base = base.slice(0, -1);
219
+ const url = `${base}/v1/xcode_instances/${encodeURIComponent(target.instanceId)}/cache`;
220
+
221
+ return new Promise<XcodeCacheFollowResult>((resolve, reject) => {
222
+ let last: XcodeInstanceCache | undefined;
223
+ let lastKey = '';
224
+ let settled = false;
225
+
226
+ const finish = (fn: () => void) => {
227
+ if (settled) return;
228
+ settled = true;
229
+ clearTimeout(timer);
230
+ options.signal?.removeEventListener('abort', onAbort);
231
+ try {
232
+ source.close();
233
+ } catch {
234
+ // A stream that never opened has nothing to close.
235
+ }
236
+ fn();
237
+ };
238
+
239
+ const timer = setTimeout(
240
+ () => finish(() => reject(new XcodeCacheTimeoutError(side, timeoutMs, last))),
241
+ timeoutMs,
242
+ );
243
+ const onAbort = () => finish(() => reject(options.signal?.reason ?? new Error('aborted')));
244
+
245
+ let confirming = false;
246
+ const confirmGone = () => {
247
+ if (confirming || settled) return;
248
+ confirming = true;
249
+ nodeProxyTransport
250
+ .fetch(url, {
251
+ headers: { Authorization: `Bearer ${target.apiKey}`, Accept: 'application/json' },
252
+ })
253
+ .then(async (res) => {
254
+ confirming = false;
255
+ if (res.status !== 404) {
256
+ await res.text().catch(() => undefined);
257
+ return;
258
+ }
259
+ finish(() =>
260
+ last ? resolve({ cache: last, gone: true }) : reject(new XcodeCacheGoneError(target.instanceId)),
261
+ );
262
+ })
263
+ .catch(() => {
264
+ confirming = false;
265
+ });
266
+ };
267
+
268
+ // The endpoint answers from the region, which keeps the instance while it terminates and
269
+ // drops it once it is collected, so a 404 is the instance being over rather than a fault.
270
+ // Without this the client treats it as a connection to retry and follows a gone instance
271
+ // until the timeout, which is minutes of a command that looks hung and says nothing.
272
+ const fetchOrGone: typeof nodeProxyTransport.fetch = async (input, init) => {
273
+ const res = await nodeProxyTransport.fetch(input, init);
274
+ if (res.status === 404) {
275
+ finish(() =>
276
+ last ? resolve({ cache: last, gone: true }) : reject(new XcodeCacheGoneError(target.instanceId)),
277
+ );
278
+ }
279
+ return res;
280
+ };
281
+
282
+ const source = createEventSource({
283
+ url,
284
+ fetch: fetchOrGone,
285
+ onConnect: () => options.onOpen?.(),
286
+ headers: {
287
+ Authorization: `Bearer ${target.apiKey}`,
288
+ Accept: 'text/event-stream',
289
+ },
290
+ onMessage: (message: EventSourceMessage) => {
291
+ if (message.event !== 'cache' && message.event !== 'gone') return;
292
+ let cache: XcodeInstanceCache;
293
+ try {
294
+ cache = JSON.parse(typeof message.data === 'string' ? message.data : String(message.data));
295
+ } catch (err) {
296
+ finish(() => reject(new Error(`Cache stream sent unreadable data: ${err}`)));
297
+ return;
298
+ }
299
+ last = cache;
300
+ if (message.event === 'gone') {
301
+ // The server also ends the stream this way when its own watch is cut short, which
302
+ // Kubernetes does routinely on a long one, so a snapshot decides which it was. The
303
+ // client library reconnects on its own when the instance is in fact still there.
304
+ confirmGone();
305
+ return;
306
+ }
307
+ // A reconnect replays the current state, which is not a transition.
308
+ const key = `${cache.restore.phase}/${cache.restore.reason ?? ''}|${cache.save.phase}/${
309
+ cache.save.reason ?? ''
310
+ }`;
311
+ if (key !== lastKey) {
312
+ lastKey = key;
313
+ options.onUpdate?.(cache);
314
+ }
315
+ if (isCacheTerminal(cache, side)) {
316
+ finish(() => resolve({ cache, gone: false }));
317
+ }
318
+ },
319
+ });
320
+
321
+ if (options.signal?.aborted) {
322
+ onAbort();
323
+ return;
324
+ }
325
+ options.signal?.addEventListener('abort', onAbort, { once: true });
326
+ });
327
+ }
package/version.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.46.0";
1
+ export declare const VERSION = "0.46.2";
2
2
  //# sourceMappingURL=version.d.mts.map
package/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.46.0";
1
+ export declare const VERSION = "0.46.2";
2
2
  //# sourceMappingURL=version.d.ts.map
package/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
- exports.VERSION = '0.46.0'; // x-release-please-version
4
+ exports.VERSION = '0.46.2'; // x-release-please-version
5
5
  //# sourceMappingURL=version.js.map
package/version.mjs CHANGED
@@ -1,2 +1,2 @@
1
- export const VERSION = '0.46.0'; // x-release-please-version
1
+ export const VERSION = '0.46.2'; // x-release-please-version
2
2
  //# sourceMappingURL=version.mjs.map
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Build cache configuration for an Xcode instance. Omitted means an ordinary uncached
3
+ * instance, which resolves nothing, adopts no stable directory, and publishes nothing.
4
+ */
5
+ export type XcodeCacheConfig = {
6
+ /**
7
+ * Where this instance's workspace is published at termination. Organization-scoped and set
8
+ * once, either here or later through `bindCacheKey`. Reusing a key is normal operation:
9
+ * termination replaces its archive.
10
+ *
11
+ * With no restoreKeys, a key given here also serves as the single restore key.
12
+ */
13
+ key?: string;
14
+ /**
15
+ * Keys to restore from, tried in exactly this order: exact match first, then literal prefix,
16
+ * newest archive first within a prefix. Immutable after create.
17
+ */
18
+ restoreKeys?: string[];
19
+ /**
20
+ * Project-root-relative paths to store. Create-only, and it controls what is STORED: the
21
+ * first publication under a key fixes its path set and restores materialize that set.
22
+ * Omitted means the whole deterministic workspace.
23
+ */
24
+ paths?: string[];
25
+ };
26
+ /**
27
+ * How far a restore got. `disabled`, `unsupported` and `skipped` are not failures: they
28
+ * describe an ordinary cold instance, which is perfectly usable. Only `failed` means the
29
+ * workspace was supposed to be warm and something actually broke.
30
+ */
31
+ export type XcodeCacheRestorePhase = 'disabled' | 'unsupported' | 'placing' | 'downloading' | 'materializing' | 'restored' | 'skipped' | 'failed';
32
+ /** How far publication got. */
33
+ export type XcodeCacheSavePhase = 'disabled' | 'idle' | 'quiescing' | 'archiving' | 'uploading' | 'verifying' | 'published' | 'skipped' | 'failed' | 'timed_out';
34
+ /** A restore key resolution passed over, so a miss is explainable rather than just a miss. */
35
+ export type XcodeCacheSkippedKey = {
36
+ key: string;
37
+ reason: string;
38
+ };
39
+ export type XcodeCacheRestoreStatus = {
40
+ phase: XcodeCacheRestorePhase;
41
+ reason?: string;
42
+ message?: string;
43
+ /** Restore key whose archive was used. */
44
+ matchedKey?: string;
45
+ matchKind?: 'exact_hit' | 'prefix_hit';
46
+ skippedKeys?: XcodeCacheSkippedKey[];
47
+ /** Path set actually materialized, which is the set the archive stores. */
48
+ paths?: string[];
49
+ /** Whether the bytes came through the regional accelerator or direct from object storage. */
50
+ source?: 'tag' | 'tigris';
51
+ bytes?: number;
52
+ uncompressedBytes?: number;
53
+ startedAt?: string;
54
+ finishedAt?: string;
55
+ };
56
+ export type XcodeCacheSaveStatus = {
57
+ phase: XcodeCacheSavePhase;
58
+ reason?: string;
59
+ message?: string;
60
+ /** Destination key the archive is published under. */
61
+ cacheKey?: string;
62
+ paths?: string[];
63
+ bytes?: number;
64
+ uncompressedBytes?: number;
65
+ startedAt?: string;
66
+ finishedAt?: string;
67
+ };
68
+ export type XcodeInstanceCache = {
69
+ config?: XcodeCacheConfig;
70
+ restore: XcodeCacheRestoreStatus;
71
+ save: XcodeCacheSaveStatus;
72
+ };
73
+ /** Which half of the cache lifecycle a follower waits on. */
74
+ export type XcodeCacheSide = 'restore' | 'save';
75
+ export type XcodeCacheFollowOptions = {
76
+ /** Defaults to 'restore'. */
77
+ side?: XcodeCacheSide;
78
+ /**
79
+ * Called once per distinct state, including the snapshot the stream opens with. Phases that
80
+ * repeat, which a reconnect produces, are not reported again.
81
+ */
82
+ onUpdate?: (cache: XcodeInstanceCache) => void;
83
+ /**
84
+ * Called when the stream is open, and again after each reconnect. A caller that is about to
85
+ * cause the very transitions it wants to watch waits for this first, so it cannot race its
86
+ * own subscription.
87
+ */
88
+ onOpen?: () => void;
89
+ /**
90
+ * How long to wait for a terminal phase. Restores move multi-gigabyte archives and
91
+ * publication is capped server-side at ten minutes, so these are generous by design.
92
+ */
93
+ timeoutMs?: number;
94
+ signal?: AbortSignal;
95
+ };
96
+ export type XcodeCacheFollowResult = {
97
+ /** The last state seen, terminal unless the stream ended first. */
98
+ cache: XcodeInstanceCache;
99
+ /**
100
+ * True when the instance was collected before the phase went terminal. The last snapshot is
101
+ * still the summary: the control plane writes the terminal phase and deletes moments later.
102
+ */
103
+ gone: boolean;
104
+ };
105
+ /**
106
+ * The instance was gone before it said anything about its cache, so there is no last state to
107
+ * summarise. Distinct from a follow that ends with `gone: true`, which at least saw one.
108
+ */
109
+ export declare class XcodeCacheGoneError extends Error {
110
+ readonly instanceId: string;
111
+ constructor(instanceId: string);
112
+ }
113
+ export declare class XcodeCacheTimeoutError extends Error {
114
+ readonly side: XcodeCacheSide;
115
+ readonly timeoutMs: number;
116
+ readonly cache: XcodeInstanceCache | undefined;
117
+ constructor(side: XcodeCacheSide, timeoutMs: number, cache: XcodeInstanceCache | undefined);
118
+ }
119
+ /** Phases past which nothing more will happen. */
120
+ export declare function isRestoreTerminal(phase: XcodeCacheRestorePhase): boolean;
121
+ /**
122
+ * Separates the one outcome worth reacting to from the terminal phases that just mean "cold,
123
+ * carry on". A caller that created an instance for a warm workspace deletes it on a failure,
124
+ * but a fallback to a cold build leaves a perfectly usable instance behind.
125
+ */
126
+ export declare function isRestoreFailure(phase: XcodeCacheRestorePhase): boolean;
127
+ export declare function isSaveTerminal(phase: XcodeCacheSavePhase): boolean;
128
+ export declare function isCacheTerminal(cache: XcodeInstanceCache, side: XcodeCacheSide): boolean;
129
+ export type XcodeCacheFollowTarget = {
130
+ baseURL: string;
131
+ apiKey: string;
132
+ instanceId: string;
133
+ };
134
+ /**
135
+ * Follows an instance's cache status until the chosen side reaches a terminal phase, the
136
+ * instance is collected, or the wait times out.
137
+ *
138
+ * The transport is the same endpoint that serves a JSON snapshot, asked for as an event
139
+ * stream. The server emits on phase change only, so every callback is a real transition.
140
+ */
141
+ export declare function followXcodeCache(target: XcodeCacheFollowTarget, options?: XcodeCacheFollowOptions): Promise<XcodeCacheFollowResult>;
142
+ //# sourceMappingURL=xcode-cache.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xcode-cache.d.mts","sourceRoot":"","sources":["src/xcode-cache.ts"],"names":[],"mappings":"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"}