mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-14 17:50:32 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3263ecfa36 | ||
|
|
1da5a21f42 | ||
|
|
64b9b14d09 |
No files matched your search
@@ -2,6 +2,12 @@
|
||||
|
||||
This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
## [27.4.1](https://github.com/expo/eas-cli/releases/tag/v27.4.1) - 2026-11-04
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- [simulator-launcher][build-tools] Wait for a starting warm Simulator launcher before claiming its session, instead of falling back to a cold start. ([#4810](https://github.com/expo/eas-cli/pull/4810) by [@gwdp](https://github.com/gwdp))
|
||||
|
||||
## [27.4.0](https://github.com/expo/eas-cli/releases/tag/v27.4.0) - 2026-11-03
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"STAGING": "27.3.0",
|
||||
"PRODUCTION": "27.3.0"
|
||||
"STAGING": "27.4.0",
|
||||
"PRODUCTION": "27.4.0"
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"version": "27.4.0",
|
||||
"version": "27.4.1",
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
# @expo/build-tools
|
||||
|
||||
## 27.4.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Wait for a starting warm Simulator launcher before claiming its session, instead of falling back to a cold start. ([#4810](https://github.com/expo/eas-cli/pull/4810) by [@gwdp](https://github.com/gwdp))
|
||||
|
||||
## 27.4.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/build-tools",
|
||||
"version": "27.4.0",
|
||||
"version": "27.4.1",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"license": "BUSL-1.1",
|
||||
"author": "Expo <[email protected]>",
|
||||
@@ -42,17 +42,17 @@
|
||||
"dependencies": {
|
||||
"@expo/config": "55.0.10",
|
||||
"@expo/config-plugins": "55.0.7",
|
||||
"@expo/downloader": "27.4.0",
|
||||
"@expo/eas-build-job": "27.4.0",
|
||||
"@expo/downloader": "27.4.1",
|
||||
"@expo/eas-build-job": "27.4.1",
|
||||
"@expo/env": "^0.4.0",
|
||||
"@expo/logger": "27.4.0",
|
||||
"@expo/logger": "27.4.1",
|
||||
"@expo/package-manager": "1.9.10",
|
||||
"@expo/plist": "^0.3.5",
|
||||
"@expo/results": "^1.0.0",
|
||||
"@expo/spawn-async": "1.7.2",
|
||||
"@expo/steps": "27.4.0",
|
||||
"@expo/template-file": "27.4.0",
|
||||
"@expo/turtle-spawn": "27.4.0",
|
||||
"@expo/steps": "27.4.1",
|
||||
"@expo/template-file": "27.4.1",
|
||||
"@expo/turtle-spawn": "27.4.1",
|
||||
"@expo/xcpretty": "^4.4.7",
|
||||
"@ngrok/ngrok": "1.7.0",
|
||||
"@sentry/node": "7.77.0",
|
||||
|
||||
@@ -37,7 +37,11 @@ it('does not claim when the job does not use the warm Simulator', async () => {
|
||||
expect(stopWarmSimulatorAsync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('claims the requested device and returns the launcher record', async () => {
|
||||
it('logs the wait before claiming, without logging the private launcher record', async () => {
|
||||
jest.mocked(runSimulatorLauncherAsync).mockImplementationOnce(async () => {
|
||||
expect(logger.info).toHaveBeenCalledWith('Waiting for the warm Simulator launcher.');
|
||||
return JSON.stringify(claim);
|
||||
});
|
||||
const session = await tryClaimWarmIosSessionAsync({
|
||||
engine: 'serve-sim',
|
||||
requestedDevice: 'iPhone 17',
|
||||
@@ -48,6 +52,10 @@ it('claims the requested device and returns the launcher record', async () => {
|
||||
env,
|
||||
signal: undefined,
|
||||
});
|
||||
expect(jest.mocked(logger.info).mock.calls).toEqual([
|
||||
['Waiting for the warm Simulator launcher.'],
|
||||
['Claimed the warm Simulator iPhone 17 (UDID).'],
|
||||
]);
|
||||
expect(session).toMatchObject({
|
||||
device: claim.device,
|
||||
server: { pid: 10, port: 3200, token: 'serve-token' },
|
||||
|
||||
@@ -66,6 +66,7 @@ export async function tryClaimWarmIosSessionAsync({
|
||||
if (await readLocalEgressHandoffAsync()) {
|
||||
throw new Error('A local egress session needs a cold start.');
|
||||
}
|
||||
logger.info('Waiting for the warm Simulator launcher.');
|
||||
const claimArgs = ['claim', engine, ...(requestedDevice ? [requestedDevice] : [])];
|
||||
const claim = ClaimSchema.parse(
|
||||
JSON.parse(await runSimulatorLauncherAsync(claimArgs, { env, signal }))
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# create-eas-build-function
|
||||
|
||||
## 27.4.1
|
||||
|
||||
No changes in this release.
|
||||
|
||||
## 27.4.0
|
||||
|
||||
No changes in this release.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-eas-build-function",
|
||||
"version": "27.4.0",
|
||||
"version": "27.4.1",
|
||||
"description": "Create functions for use in EAS Build custom builds.",
|
||||
"keywords": [
|
||||
"expo",
|
||||
@@ -35,7 +35,7 @@
|
||||
"clean": "rimraf ./build/ \"*.tsbuildinfo\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/steps": "27.4.0"
|
||||
"@expo/steps": "27.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@expo/package-manager": "1.9.10",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @expo/downloader
|
||||
|
||||
## 27.4.1
|
||||
|
||||
No changes in this release.
|
||||
|
||||
## 27.4.0
|
||||
|
||||
No changes in this release.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/downloader",
|
||||
"version": "27.4.0",
|
||||
"version": "27.4.1",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"license": "BUSL-1.1",
|
||||
"author": "Expo <[email protected]>",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# eas-build-cache-provider
|
||||
|
||||
## 27.4.1
|
||||
|
||||
No changes in this release.
|
||||
|
||||
## 27.4.0
|
||||
|
||||
No changes in this release.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eas-build-cache-provider",
|
||||
"version": "27.4.0",
|
||||
"version": "27.4.1",
|
||||
"description": "A build cache provider plugin for the Expo CLI",
|
||||
"homepage": "https://github.com/expo/eas-cli",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @expo/eas-build-job
|
||||
|
||||
## 27.4.1
|
||||
|
||||
No changes in this release.
|
||||
|
||||
## 27.4.0
|
||||
|
||||
No changes in this release.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/eas-build-job",
|
||||
"version": "27.4.0",
|
||||
"version": "27.4.1",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"license": "MIT",
|
||||
"author": "Expo <[email protected]>",
|
||||
@@ -25,9 +25,9 @@
|
||||
"clean": "rimraf node_modules dist coverage \"*.tsbuildinfo\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/logger": "27.4.0",
|
||||
"@expo/logger": "27.4.1",
|
||||
"@expo/results": "1.0.0",
|
||||
"@expo/turtle-spawn": "27.4.0",
|
||||
"@expo/turtle-spawn": "27.4.1",
|
||||
"joi": "^17.13.1",
|
||||
"semver": "^7.6.2",
|
||||
"zod": "^4.3.5"
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# eas-cli
|
||||
|
||||
## 27.4.1
|
||||
|
||||
No changes in this release.
|
||||
|
||||
## 27.4.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
+146
-146
File diff suppressed because it is too large.
Load diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eas-cli",
|
||||
"version": "27.4.0",
|
||||
"version": "27.4.1",
|
||||
"description": "EAS command line tool",
|
||||
"keywords": [
|
||||
"cli",
|
||||
@@ -48,12 +48,12 @@
|
||||
"@expo/code-signing-certificates": "0.0.5",
|
||||
"@expo/config": "55.0.10",
|
||||
"@expo/config-plugins": "55.0.7",
|
||||
"@expo/eas-build-job": "27.4.0",
|
||||
"@expo/eas-json": "27.4.0",
|
||||
"@expo/eas-build-job": "27.4.1",
|
||||
"@expo/eas-json": "27.4.1",
|
||||
"@expo/env": "^1.0.0",
|
||||
"@expo/experimentation": "0.0.5",
|
||||
"@expo/json-file": "8.3.3",
|
||||
"@expo/logger": "27.4.0",
|
||||
"@expo/logger": "27.4.1",
|
||||
"@expo/multipart-body-parser": "2.0.0",
|
||||
"@expo/osascript": "2.1.4",
|
||||
"@expo/package-manager": "1.9.10",
|
||||
@@ -65,7 +65,7 @@
|
||||
"@expo/results": "1.0.0",
|
||||
"@expo/rudder-sdk-node": "1.1.1",
|
||||
"@expo/spawn-async": "1.7.2",
|
||||
"@expo/steps": "27.4.0",
|
||||
"@expo/steps": "27.4.1",
|
||||
"@expo/timeago.js": "1.0.0",
|
||||
"@oclif/core": "^4.8.3",
|
||||
"@oclif/plugin-autocomplete": "^3.2.40",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @expo/eas-json
|
||||
|
||||
## 27.4.1
|
||||
|
||||
No changes in this release.
|
||||
|
||||
## 27.4.0
|
||||
|
||||
No changes in this release.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/eas-json",
|
||||
"version": "27.4.0",
|
||||
"version": "27.4.1",
|
||||
"description": "A library for interacting with eas.json",
|
||||
"homepage": "https://github.com/expo/eas-cli",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
@@ -32,7 +32,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "7.23.5",
|
||||
"@expo/eas-build-job": "27.4.0",
|
||||
"@expo/eas-build-job": "27.4.1",
|
||||
"chalk": "4.1.2",
|
||||
"env-string": "1.0.1",
|
||||
"fs-extra": "11.2.0",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# eas-cli-local-build-plugin
|
||||
|
||||
## 27.4.1
|
||||
|
||||
No changes in this release.
|
||||
|
||||
## 27.4.0
|
||||
|
||||
No changes in this release.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eas-cli-local-build-plugin",
|
||||
"version": "27.4.0",
|
||||
"version": "27.4.1",
|
||||
"description": "Tool for running EAS compatible builds on a local machine.",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"license": "BUSL-1.1",
|
||||
@@ -26,12 +26,12 @@
|
||||
"clean": "rimraf node_modules dist coverage \"*.tsbuildinfo\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/build-tools": "27.4.0",
|
||||
"@expo/build-tools": "27.4.1",
|
||||
"@expo/bunyan": "^4.0.1",
|
||||
"@expo/eas-build-job": "27.4.0",
|
||||
"@expo/logger": "27.4.0",
|
||||
"@expo/eas-build-job": "27.4.1",
|
||||
"@expo/logger": "27.4.1",
|
||||
"@expo/spawn-async": "^1.7.2",
|
||||
"@expo/turtle-spawn": "27.4.0",
|
||||
"@expo/turtle-spawn": "27.4.1",
|
||||
"chalk": "4.1.2",
|
||||
"env-paths": "^2.2.1",
|
||||
"fs-extra": "^11.2.0",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @expo/logger
|
||||
|
||||
## 27.4.1
|
||||
|
||||
No changes in this release.
|
||||
|
||||
## 27.4.0
|
||||
|
||||
No changes in this release.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/logger",
|
||||
"version": "27.4.0",
|
||||
"version": "27.4.1",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"license": "BUSL-1.1",
|
||||
"author": "Expo <[email protected]>",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @expo/simulator-launcher
|
||||
|
||||
## 27.4.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Wait for a starting warm Simulator launcher before claiming its session, instead of falling back to a cold start. ([#4810](https://github.com/expo/eas-cli/pull/4810) by [@gwdp](https://github.com/gwdp))
|
||||
|
||||
## 27.4.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/simulator-launcher",
|
||||
"version": "27.4.0",
|
||||
"version": "27.4.1",
|
||||
"description": "Launch and claim Simulator sessions on EAS workers",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"license": "BUSL-1.1",
|
||||
|
||||
@@ -4,13 +4,14 @@ import path from 'node:path';
|
||||
|
||||
import { claimAsync } from '../commands/claim';
|
||||
import { stopAsync } from '../commands/stop';
|
||||
import * as launcherState from '../utils/state';
|
||||
import {
|
||||
LAUNCHER_STATE_FILE,
|
||||
readLauncherStateAsync,
|
||||
updateLauncherStateAsync,
|
||||
waitForLauncherStateAsync,
|
||||
} from '../utils/state';
|
||||
import { deferred } from './helpers';
|
||||
import { deferred, until } from './helpers';
|
||||
|
||||
const boot = 'boot-one';
|
||||
let root: string;
|
||||
@@ -30,6 +31,7 @@ const info = {
|
||||
argentEventLogPath: '/argent/events',
|
||||
};
|
||||
const directory = () => path.join(root, 'control');
|
||||
const pollLauncherStateAsync = waitForLauncherStateAsync;
|
||||
|
||||
const nativeFs = {
|
||||
readFile: fs.readFile.bind(fs),
|
||||
@@ -46,6 +48,7 @@ const remap = (file: unknown): unknown =>
|
||||
: file;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.spyOn(process.stderr, 'write').mockReturnValue(true);
|
||||
root = await fs.mkdtemp(path.join(os.tmpdir(), 'launcher-state-'));
|
||||
jest.spyOn(os, 'homedir').mockReturnValue(root);
|
||||
await fs.mkdir(path.join(root, 'control'));
|
||||
@@ -192,6 +195,7 @@ test('two job claims acquire only one ready session and the result includes its
|
||||
await updateLauncherStateAsync(boot, state => ({ ...state!, status: 'claimed' }));
|
||||
await Promise.all(claims);
|
||||
expect(results.filter(value => value === 0)).toHaveLength(1);
|
||||
expect(process.stderr.write).not.toHaveBeenCalled();
|
||||
expect(JSON.parse(String(output.mock.calls[0][0]))).toMatchObject({
|
||||
...info,
|
||||
status: 'claimed',
|
||||
@@ -200,6 +204,252 @@ test('two job claims acquire only one ready session and the result includes its
|
||||
await expect(claimAsync(boot, 'serve-sim')).rejects.toThrow('available');
|
||||
});
|
||||
|
||||
async function beginStartingClaimAsync(engine = 'serve-sim') {
|
||||
let observedStarting = false;
|
||||
jest
|
||||
.spyOn(launcherState, 'waitForLauncherStateAsync')
|
||||
.mockImplementationOnce((bootUuid, isComplete, timeoutMs) =>
|
||||
pollLauncherStateAsync(
|
||||
bootUuid,
|
||||
state => {
|
||||
const complete = isComplete(state);
|
||||
observedStarting ||= state?.status === 'starting';
|
||||
return complete;
|
||||
},
|
||||
timeoutMs
|
||||
)
|
||||
);
|
||||
const result = claimAsync(boot, engine, 'iPhone 17').then(
|
||||
value => value,
|
||||
error => error as Error
|
||||
);
|
||||
await until(() => observedStarting);
|
||||
return { result };
|
||||
}
|
||||
|
||||
test('a claim waits for the minimal starting record to become ready without changing it', async () => {
|
||||
const starting = {
|
||||
bootUuid: boot,
|
||||
status: 'starting' as const,
|
||||
controllerPid: info.controllerPid,
|
||||
launcherVersion: '27.4.0',
|
||||
};
|
||||
await updateLauncherStateAsync(boot, () => starting);
|
||||
const output = jest.spyOn(process.stdout, 'write').mockReturnValue(true);
|
||||
const { result } = await beginStartingClaimAsync();
|
||||
expect(await readLauncherStateAsync(boot)).toEqual(starting);
|
||||
expect(await fs.readdir(directory())).toEqual([path.basename(LAUNCHER_STATE_FILE)]);
|
||||
expect(output).not.toHaveBeenCalled();
|
||||
|
||||
await updateLauncherStateAsync(boot, state => ({ ...state!, ...info, status: 'ready' }));
|
||||
await waitForLauncherStateAsync(boot, state => state?.status === 'claiming', 1_000);
|
||||
await updateLauncherStateAsync(boot, state => ({ ...state!, status: 'claimed' }));
|
||||
expect(await result).toBe(0);
|
||||
expect(JSON.parse(String(output.mock.calls[0][0]))).toMatchObject({
|
||||
...info,
|
||||
status: 'claimed',
|
||||
engine: 'serve-sim',
|
||||
requestedDevice: 'iPhone 17',
|
||||
});
|
||||
});
|
||||
|
||||
test('two claims waiting for startup still acquire only one session', async () => {
|
||||
await updateLauncherStateAsync(boot, () => ({
|
||||
bootUuid: boot,
|
||||
status: 'starting',
|
||||
controllerPid: info.controllerPid,
|
||||
}));
|
||||
jest.spyOn(process.stdout, 'write').mockReturnValue(true);
|
||||
const first = await beginStartingClaimAsync();
|
||||
const second = await beginStartingClaimAsync('argent');
|
||||
await updateLauncherStateAsync(boot, state => ({ ...state!, ...info, status: 'ready' }));
|
||||
await waitForLauncherStateAsync(boot, state => state?.status === 'claiming', 1_000);
|
||||
await updateLauncherStateAsync(boot, state => ({ ...state!, status: 'claimed' }));
|
||||
const results = await Promise.all([first.result, second.result]);
|
||||
expect(results.filter(value => value === 0)).toHaveLength(1);
|
||||
expect(results.filter(value => value instanceof Error)).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('startup wait progress is throttled and stdout remains the claim JSON', async () => {
|
||||
let now = 0;
|
||||
jest.spyOn(Date, 'now').mockImplementation(() => now);
|
||||
const output = jest.spyOn(process.stdout, 'write').mockReturnValue(true);
|
||||
const progress = jest.mocked(process.stderr.write);
|
||||
await updateLauncherStateAsync(boot, () => ({
|
||||
bootUuid: boot,
|
||||
status: 'starting',
|
||||
controllerPid: info.controllerPid,
|
||||
}));
|
||||
const { result } = await beginStartingClaimAsync();
|
||||
expect(progress.mock.calls).toEqual([
|
||||
['Warm Simulator is still starting; waiting (0s elapsed, 420s limit).\n'],
|
||||
]);
|
||||
const reads = jest.mocked(fs.readFile).mock.calls.length;
|
||||
now = 10_000;
|
||||
await until(() => jest.mocked(fs.readFile).mock.calls.length >= reads + 2);
|
||||
expect(progress).toHaveBeenCalledTimes(1);
|
||||
expect(output).not.toHaveBeenCalled();
|
||||
now = 30_000;
|
||||
await until(() => progress.mock.calls.length === 2);
|
||||
expect(progress.mock.calls[1]).toEqual([
|
||||
'Warm Simulator is still starting; waiting (30s elapsed, 420s limit).\n',
|
||||
]);
|
||||
now = 31_000;
|
||||
await updateLauncherStateAsync(boot, state => ({ ...state!, ...info, status: 'ready' }));
|
||||
await waitForLauncherStateAsync(boot, state => state?.status === 'claiming', 1_000);
|
||||
await updateLauncherStateAsync(boot, state => ({ ...state!, status: 'claimed' }));
|
||||
expect(await result).toBe(0);
|
||||
expect(progress.mock.calls).toHaveLength(3);
|
||||
expect(progress.mock.calls[2]).toEqual(['Warm Simulator became ready after waiting 31s.\n']);
|
||||
expect(output).toHaveBeenCalledTimes(1);
|
||||
expect(JSON.parse(String(output.mock.calls[0][0]))).toMatchObject({
|
||||
status: 'claimed',
|
||||
server: { token: info.server.token },
|
||||
});
|
||||
expect(progress.mock.calls.flat().join('')).not.toContain(info.server.token);
|
||||
});
|
||||
|
||||
test.each(['stopping', 'disabled'] as const)(
|
||||
'a waiting claim preserves a startup failure in %s state',
|
||||
async status => {
|
||||
await updateLauncherStateAsync(boot, () => ({
|
||||
bootUuid: boot,
|
||||
status: 'starting',
|
||||
controllerPid: info.controllerPid,
|
||||
}));
|
||||
const { result } = await beginStartingClaimAsync();
|
||||
await updateLauncherStateAsync(boot, state => ({
|
||||
...state!,
|
||||
status,
|
||||
error: 'serve-sim exited',
|
||||
}));
|
||||
expect(await result).toEqual(
|
||||
new Error(
|
||||
'Simulator preparation was interrupted; cleanup may still be running. serve-sim exited'
|
||||
)
|
||||
);
|
||||
expect((await readLauncherStateAsync(boot))?.status).toBe(status);
|
||||
}
|
||||
);
|
||||
|
||||
test.each(['missing', 'old boot', 'different controller', 'different start time'] as const)(
|
||||
'a waiting claim cannot acquire %s state',
|
||||
async replacement => {
|
||||
await updateLauncherStateAsync(boot, () => ({
|
||||
bootUuid: boot,
|
||||
status: 'starting',
|
||||
controllerPid: info.controllerPid,
|
||||
controllerStartTime: info.controllerStartTime,
|
||||
}));
|
||||
const { result } = await beginStartingClaimAsync();
|
||||
if (replacement === 'missing') {
|
||||
await fs.rm(LAUNCHER_STATE_FILE);
|
||||
} else if (replacement === 'old boot') {
|
||||
await updateLauncherStateAsync('other-boot', () => ({
|
||||
bootUuid: 'other-boot',
|
||||
status: 'ready',
|
||||
...info,
|
||||
}));
|
||||
} else {
|
||||
await updateLauncherStateAsync(boot, state => ({
|
||||
...state!,
|
||||
...info,
|
||||
status: 'ready',
|
||||
...(replacement === 'different controller'
|
||||
? { controllerPid: info.controllerPid + 1 }
|
||||
: { controllerStartTime: 'replacement-start' }),
|
||||
}));
|
||||
}
|
||||
expect(await result).toBeInstanceOf(Error);
|
||||
expect((await readLauncherStateAsync(boot))?.status).not.toBe('claiming');
|
||||
}
|
||||
);
|
||||
|
||||
test('a claim rechecks controller identity under the state lock', async () => {
|
||||
await updateLauncherStateAsync(boot, () => ({ bootUuid: boot, status: 'ready', ...info }));
|
||||
jest.spyOn(fs, 'mkdir').mockImplementationOnce(async file => {
|
||||
await nativeFs.writeFile(
|
||||
remap(LAUNCHER_STATE_FILE) as string,
|
||||
JSON.stringify({ bootUuid: boot, status: 'ready', ...info, controllerPid: 9002 })
|
||||
);
|
||||
await nativeFs.mkdir(remap(file) as string);
|
||||
return undefined;
|
||||
});
|
||||
await expect(claimAsync(boot, 'serve-sim')).rejects.toThrow('different session');
|
||||
expect(await readLauncherStateAsync(boot)).toMatchObject({
|
||||
status: 'ready',
|
||||
controllerPid: 9002,
|
||||
});
|
||||
});
|
||||
|
||||
test('startup and claim acknowledgement share one timeout', async () => {
|
||||
let now = 0;
|
||||
jest.spyOn(Date, 'now').mockImplementation(() => now);
|
||||
await updateLauncherStateAsync(boot, () => ({
|
||||
bootUuid: boot,
|
||||
status: 'starting',
|
||||
controllerPid: info.controllerPid,
|
||||
}));
|
||||
const { result } = await beginStartingClaimAsync();
|
||||
now = 419_000;
|
||||
await updateLauncherStateAsync(boot, state => ({ ...state!, ...info, status: 'ready' }));
|
||||
await waitForLauncherStateAsync(boot, state => state?.status === 'claiming', 1_000);
|
||||
now = 420_001;
|
||||
let settled = false;
|
||||
void result.then(() => {
|
||||
settled = true;
|
||||
});
|
||||
try {
|
||||
await until(() => settled);
|
||||
expect(await result).toEqual(
|
||||
new Error('Timed out waiting for the Simulator launcher; cleanup may still be running.')
|
||||
);
|
||||
} finally {
|
||||
now = 1_000_000;
|
||||
await result;
|
||||
}
|
||||
});
|
||||
|
||||
test('startup times out without acquiring or changing the starting record', async () => {
|
||||
let now = 0;
|
||||
jest.spyOn(Date, 'now').mockImplementation(() => now);
|
||||
await updateLauncherStateAsync(boot, () => ({
|
||||
bootUuid: boot,
|
||||
status: 'starting',
|
||||
controllerPid: info.controllerPid,
|
||||
}));
|
||||
const { result } = await beginStartingClaimAsync();
|
||||
now = 420_001;
|
||||
expect(await result).toEqual(
|
||||
new Error('Timed out waiting for the Simulator launcher; cleanup may still be running.')
|
||||
);
|
||||
expect((await readLauncherStateAsync(boot))?.status).toBe('starting');
|
||||
});
|
||||
|
||||
test('a claim cannot acquire after the deadline expires while taking the state lock', async () => {
|
||||
let now = 0;
|
||||
jest.spyOn(Date, 'now').mockImplementation(() => now);
|
||||
await updateLauncherStateAsync(boot, () => ({ bootUuid: boot, status: 'ready', ...info }));
|
||||
jest.spyOn(fs, 'mkdir').mockImplementationOnce(async file => {
|
||||
now = 420_001;
|
||||
await nativeFs.mkdir(remap(file) as string);
|
||||
return undefined;
|
||||
});
|
||||
await expect(claimAsync(boot, 'serve-sim')).rejects.toThrow('Timed out');
|
||||
expect((await readLauncherStateAsync(boot))?.status).toBe('ready');
|
||||
});
|
||||
|
||||
test.each(['missing', 'old boot', 'starting without a controller'] as const)(
|
||||
'a claim immediately rejects %s state',
|
||||
async state => {
|
||||
if (state !== 'missing') {
|
||||
const bootUuid = state === 'old boot' ? 'old-boot' : boot;
|
||||
await updateLauncherStateAsync(bootUuid, () => ({ bootUuid, status: 'starting' }));
|
||||
}
|
||||
await expect(claimAsync(boot, 'serve-sim')).rejects.toThrow('available');
|
||||
}
|
||||
);
|
||||
|
||||
test('a claim fails when the launcher hands over a different device than requested', async () => {
|
||||
await updateLauncherStateAsync(boot, () => ({ bootUuid: boot, status: 'ready', ...info }));
|
||||
const claim = expect(claimAsync(boot, 'serve-sim', 'iPad Pro')).rejects.toThrow(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
ClaimInputSchema,
|
||||
type LauncherState,
|
||||
WarmSessionInfoSchema,
|
||||
updateLauncherStateAsync,
|
||||
waitForLauncherStateAsync,
|
||||
@@ -16,13 +17,72 @@ export async function claimAsync(
|
||||
`Invalid Simulator session request. Choose ${ClaimInputSchema.shape.engine.options.join(', ')} and an optional device name or UDID.`
|
||||
);
|
||||
}
|
||||
// Startup and handoff share the worker's existing claim timeout.
|
||||
const startedAt = Date.now();
|
||||
const deadline = startedAt + 420_000;
|
||||
let lastWaitLogAt: number | undefined;
|
||||
let controller: LauncherState | undefined;
|
||||
const ready = await waitForLauncherStateAsync(
|
||||
bootUuid,
|
||||
state => {
|
||||
if (state?.status === 'stopping' || state?.status === 'disabled' || state?.error) {
|
||||
throw new Error(
|
||||
`Simulator preparation was interrupted; cleanup may still be running.${state.error ? ` ${state.error}` : ''}`
|
||||
);
|
||||
}
|
||||
if (
|
||||
!state ||
|
||||
(state.status !== 'starting' && state.status !== 'ready') ||
|
||||
state.controllerPid === undefined
|
||||
) {
|
||||
throw new Error('No warm Simulator session is available to claim on this VM.');
|
||||
}
|
||||
if (
|
||||
controller &&
|
||||
(state.controllerPid !== controller.controllerPid ||
|
||||
(controller.controllerStartTime !== undefined &&
|
||||
state.controllerStartTime !== controller.controllerStartTime))
|
||||
) {
|
||||
throw new Error('The Simulator launcher returned a different session or engine.');
|
||||
}
|
||||
controller = state;
|
||||
if (state.status === 'starting') {
|
||||
const now = Date.now();
|
||||
if (lastWaitLogAt === undefined || now - lastWaitLogAt >= 30_000) {
|
||||
process.stderr.write(
|
||||
`Warm Simulator is still starting; waiting (${Math.floor((now - startedAt) / 1_000)}s elapsed, 420s limit).\n`
|
||||
);
|
||||
lastWaitLogAt = now;
|
||||
}
|
||||
}
|
||||
return state.status === 'ready';
|
||||
},
|
||||
deadline - Date.now()
|
||||
);
|
||||
const prepared = WarmSessionInfoSchema.parse(ready);
|
||||
const acquired = await updateLauncherStateAsync(bootUuid, state => {
|
||||
if (state?.status !== 'ready') {
|
||||
throw new Error('No warm Simulator session is available to claim on this VM.');
|
||||
}
|
||||
if (
|
||||
state.controllerPid !== prepared.controllerPid ||
|
||||
state.controllerStartTime !== prepared.controllerStartTime
|
||||
) {
|
||||
throw new Error('The Simulator launcher returned a different session or engine.');
|
||||
}
|
||||
if (Date.now() >= deadline) {
|
||||
throw new Error(
|
||||
'Timed out waiting for the Simulator launcher; cleanup may still be running.'
|
||||
);
|
||||
}
|
||||
WarmSessionInfoSchema.parse(state);
|
||||
return { ...state, ...input.data, status: 'claiming' };
|
||||
});
|
||||
if (lastWaitLogAt !== undefined) {
|
||||
process.stderr.write(
|
||||
`Warm Simulator became ready after waiting ${Math.floor((Date.now() - startedAt) / 1_000)}s.\n`
|
||||
);
|
||||
}
|
||||
const claimed = await waitForLauncherStateAsync(
|
||||
bootUuid,
|
||||
state => {
|
||||
@@ -40,7 +100,7 @@ export async function claimAsync(
|
||||
}
|
||||
return state.status === 'claimed';
|
||||
},
|
||||
420_000
|
||||
Math.max(0, deadline - Date.now())
|
||||
);
|
||||
const { device } = WarmSessionInfoSchema.parse(claimed);
|
||||
if (
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @expo/steps
|
||||
|
||||
## 27.4.1
|
||||
|
||||
No changes in this release.
|
||||
|
||||
## 27.4.0
|
||||
|
||||
No changes in this release.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/steps",
|
||||
"version": "27.4.0",
|
||||
"version": "27.4.1",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"license": "BUSL-1.1",
|
||||
"author": "Expo <[email protected]>",
|
||||
@@ -27,8 +27,8 @@
|
||||
"clean": "rimraf node_modules dist coverage \"*.tsbuildinfo\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/eas-build-job": "27.4.0",
|
||||
"@expo/logger": "27.4.0",
|
||||
"@expo/eas-build-job": "27.4.1",
|
||||
"@expo/logger": "27.4.1",
|
||||
"@expo/spawn-async": "^1.7.2",
|
||||
"arg": "^5.0.2",
|
||||
"fast-glob": "^3.3.2",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @expo/template-file
|
||||
|
||||
## 27.4.1
|
||||
|
||||
No changes in this release.
|
||||
|
||||
## 27.4.0
|
||||
|
||||
No changes in this release.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/template-file",
|
||||
"version": "27.4.0",
|
||||
"version": "27.4.1",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"license": "BUSL-1.1",
|
||||
"author": "Expo <[email protected]>",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @expo/turtle-spawn
|
||||
|
||||
## 27.4.1
|
||||
|
||||
No changes in this release.
|
||||
|
||||
## 27.4.0
|
||||
|
||||
No changes in this release.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/turtle-spawn",
|
||||
"version": "27.4.0",
|
||||
"version": "27.4.1",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"license": "BUSL-1.1",
|
||||
"author": "Expo <[email protected]>",
|
||||
@@ -25,7 +25,7 @@
|
||||
"clean": "rimraf node_modules dist coverage \"*.tsbuildinfo\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/logger": "27.4.0",
|
||||
"@expo/logger": "27.4.1",
|
||||
"@expo/spawn-async": "^1.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @expo/vm-tools
|
||||
|
||||
## 27.4.1
|
||||
|
||||
No changes in this release.
|
||||
|
||||
## 27.4.0
|
||||
|
||||
No changes in this release.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/vm-tools",
|
||||
"version": "27.4.0",
|
||||
"version": "27.4.1",
|
||||
"description": "VM startup script loader",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"license": "BUSL-1.1",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @expo/worker
|
||||
|
||||
## 27.4.1
|
||||
|
||||
No changes in this release.
|
||||
|
||||
## 27.4.0
|
||||
|
||||
No changes in this release.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/worker",
|
||||
"version": "27.4.0",
|
||||
"version": "27.4.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "yarn watch",
|
||||
@@ -19,17 +19,17 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "7.24.7",
|
||||
"@expo/build-tools": "27.4.0",
|
||||
"@expo/build-tools": "27.4.1",
|
||||
"@expo/config-plugins": "55.0.7",
|
||||
"@expo/config-types": "55.0.5",
|
||||
"@expo/eas-build-job": "27.4.0",
|
||||
"@expo/logger": "27.4.0",
|
||||
"@expo/eas-build-job": "27.4.1",
|
||||
"@expo/logger": "27.4.1",
|
||||
"@expo/results": "1.0.0",
|
||||
"@expo/rudder-sdk-node": "1.1.1",
|
||||
"@expo/spawn-async": "1.7.2",
|
||||
"@expo/steps": "27.4.0",
|
||||
"@expo/template-file": "27.4.0",
|
||||
"@expo/turtle-spawn": "27.4.0",
|
||||
"@expo/steps": "27.4.1",
|
||||
"@expo/template-file": "27.4.1",
|
||||
"@expo/turtle-spawn": "27.4.1",
|
||||
"@hapi/boom": "10.0.1",
|
||||
"check-disk-space": "3.4.0",
|
||||
"fast-glob": "3.3.2",
|
||||
|
||||
@@ -2515,24 +2515,24 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@expo/build-tools@npm:27.4.0, @expo/build-tools@workspace:packages/build-tools":
|
||||
"@expo/build-tools@npm:27.4.1, @expo/build-tools@workspace:packages/build-tools":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@expo/build-tools@workspace:packages/build-tools"
|
||||
dependencies:
|
||||
"@expo/config": "npm:55.0.10"
|
||||
"@expo/config-plugins": "npm:55.0.7"
|
||||
"@expo/downloader": "npm:27.4.0"
|
||||
"@expo/eas-build-job": "npm:27.4.0"
|
||||
"@expo/downloader": "npm:27.4.1"
|
||||
"@expo/eas-build-job": "npm:27.4.1"
|
||||
"@expo/env": "npm:^0.4.0"
|
||||
"@expo/logger": "npm:27.4.0"
|
||||
"@expo/logger": "npm:27.4.1"
|
||||
"@expo/package-manager": "npm:1.9.10"
|
||||
"@expo/plist": "npm:^0.3.5"
|
||||
"@expo/repack-app": "npm:~0.9.0"
|
||||
"@expo/results": "npm:^1.0.0"
|
||||
"@expo/spawn-async": "npm:1.7.2"
|
||||
"@expo/steps": "npm:27.4.0"
|
||||
"@expo/template-file": "npm:27.4.0"
|
||||
"@expo/turtle-spawn": "npm:27.4.0"
|
||||
"@expo/steps": "npm:27.4.1"
|
||||
"@expo/template-file": "npm:27.4.1"
|
||||
"@expo/turtle-spawn": "npm:27.4.1"
|
||||
"@expo/xcpretty": "npm:^4.4.7"
|
||||
"@ngrok/ngrok": "npm:1.7.0"
|
||||
"@sentry/node": "npm:7.77.0"
|
||||
@@ -2768,7 +2768,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@expo/downloader@npm:27.4.0, @expo/downloader@workspace:packages/downloader":
|
||||
"@expo/downloader@npm:27.4.1, @expo/downloader@workspace:packages/downloader":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@expo/downloader@workspace:packages/downloader"
|
||||
dependencies:
|
||||
@@ -2784,13 +2784,13 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@expo/eas-build-job@npm:27.4.0, @expo/eas-build-job@workspace:packages/eas-build-job":
|
||||
"@expo/eas-build-job@npm:27.4.1, @expo/eas-build-job@workspace:packages/eas-build-job":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@expo/eas-build-job@workspace:packages/eas-build-job"
|
||||
dependencies:
|
||||
"@expo/logger": "npm:27.4.0"
|
||||
"@expo/logger": "npm:27.4.1"
|
||||
"@expo/results": "npm:1.0.0"
|
||||
"@expo/turtle-spawn": "npm:27.4.0"
|
||||
"@expo/turtle-spawn": "npm:27.4.1"
|
||||
"@types/jest": "npm:^29.5.12"
|
||||
"@types/node": "npm:22.20.5"
|
||||
jest: "npm:^29.7.0"
|
||||
@@ -2803,12 +2803,12 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@expo/eas-json@npm:27.4.0, @expo/eas-json@workspace:packages/eas-json":
|
||||
"@expo/eas-json@npm:27.4.1, @expo/eas-json@workspace:packages/eas-json":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@expo/eas-json@workspace:packages/eas-json"
|
||||
dependencies:
|
||||
"@babel/code-frame": "npm:7.23.5"
|
||||
"@expo/eas-build-job": "npm:27.4.0"
|
||||
"@expo/eas-build-job": "npm:27.4.1"
|
||||
"@tsconfig/node22": "npm:22.0.6"
|
||||
"@types/babel__code-frame": "npm:7.0.3"
|
||||
"@types/fs-extra": "npm:11.0.4"
|
||||
@@ -2931,7 +2931,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@expo/logger@npm:27.4.0, @expo/logger@workspace:packages/logger":
|
||||
"@expo/logger@npm:27.4.1, @expo/logger@workspace:packages/logger":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@expo/logger@workspace:packages/logger"
|
||||
dependencies:
|
||||
@@ -3147,12 +3147,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@expo/steps@npm:27.4.0, @expo/steps@workspace:packages/steps":
|
||||
"@expo/steps@npm:27.4.1, @expo/steps@workspace:packages/steps":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@expo/steps@workspace:packages/steps"
|
||||
dependencies:
|
||||
"@expo/eas-build-job": "npm:27.4.0"
|
||||
"@expo/logger": "npm:27.4.0"
|
||||
"@expo/eas-build-job": "npm:27.4.1"
|
||||
"@expo/logger": "npm:27.4.1"
|
||||
"@expo/spawn-async": "npm:^1.7.2"
|
||||
"@jest/globals": "npm:^29.7.0"
|
||||
"@types/jest": "npm:^29.5.12"
|
||||
@@ -3178,7 +3178,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@expo/template-file@npm:27.4.0, @expo/template-file@workspace:packages/template-file":
|
||||
"@expo/template-file@npm:27.4.1, @expo/template-file@workspace:packages/template-file":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@expo/template-file@workspace:packages/template-file"
|
||||
dependencies:
|
||||
@@ -3201,11 +3201,11 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@expo/turtle-spawn@npm:27.4.0, @expo/turtle-spawn@workspace:packages/turtle-spawn":
|
||||
"@expo/turtle-spawn@npm:27.4.1, @expo/turtle-spawn@workspace:packages/turtle-spawn":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@expo/turtle-spawn@workspace:packages/turtle-spawn"
|
||||
dependencies:
|
||||
"@expo/logger": "npm:27.4.0"
|
||||
"@expo/logger": "npm:27.4.1"
|
||||
"@expo/spawn-async": "npm:^1.7.2"
|
||||
"@types/jest": "npm:^29.5.12"
|
||||
"@types/node": "npm:22.20.5"
|
||||
@@ -3236,17 +3236,17 @@ __metadata:
|
||||
resolution: "@expo/worker@workspace:packages/worker"
|
||||
dependencies:
|
||||
"@babel/core": "npm:7.24.7"
|
||||
"@expo/build-tools": "npm:27.4.0"
|
||||
"@expo/build-tools": "npm:27.4.1"
|
||||
"@expo/config-plugins": "npm:55.0.7"
|
||||
"@expo/config-types": "npm:55.0.5"
|
||||
"@expo/eas-build-job": "npm:27.4.0"
|
||||
"@expo/logger": "npm:27.4.0"
|
||||
"@expo/eas-build-job": "npm:27.4.1"
|
||||
"@expo/logger": "npm:27.4.1"
|
||||
"@expo/results": "npm:1.0.0"
|
||||
"@expo/rudder-sdk-node": "npm:1.1.1"
|
||||
"@expo/spawn-async": "npm:1.7.2"
|
||||
"@expo/steps": "npm:27.4.0"
|
||||
"@expo/template-file": "npm:27.4.0"
|
||||
"@expo/turtle-spawn": "npm:27.4.0"
|
||||
"@expo/steps": "npm:27.4.1"
|
||||
"@expo/template-file": "npm:27.4.1"
|
||||
"@expo/turtle-spawn": "npm:27.4.1"
|
||||
"@hapi/boom": "npm:10.0.1"
|
||||
"@types/babel__core": "npm:7.20.5"
|
||||
"@types/fs-extra": "npm:11.0.4"
|
||||
@@ -10130,7 +10130,7 @@ __metadata:
|
||||
resolution: "create-eas-build-function@workspace:packages/create-eas-build-function"
|
||||
dependencies:
|
||||
"@expo/package-manager": "npm:1.9.10"
|
||||
"@expo/steps": "npm:27.4.0"
|
||||
"@expo/steps": "npm:27.4.1"
|
||||
"@jest/globals": "npm:^29.7.0"
|
||||
"@types/jest": "npm:^29.5.12"
|
||||
"@types/node": "npm:22.20.5"
|
||||
@@ -10714,12 +10714,12 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "eas-cli-local-build-plugin@workspace:packages/local-build-plugin"
|
||||
dependencies:
|
||||
"@expo/build-tools": "npm:27.4.0"
|
||||
"@expo/build-tools": "npm:27.4.1"
|
||||
"@expo/bunyan": "npm:^4.0.1"
|
||||
"@expo/eas-build-job": "npm:27.4.0"
|
||||
"@expo/logger": "npm:27.4.0"
|
||||
"@expo/eas-build-job": "npm:27.4.1"
|
||||
"@expo/logger": "npm:27.4.1"
|
||||
"@expo/spawn-async": "npm:^1.7.2"
|
||||
"@expo/turtle-spawn": "npm:27.4.0"
|
||||
"@expo/turtle-spawn": "npm:27.4.1"
|
||||
"@types/fs-extra": "npm:^11.0.4"
|
||||
"@types/hapi__joi": "npm:^17.1.14"
|
||||
"@types/lodash": "npm:^4.17.4"
|
||||
@@ -10773,12 +10773,12 @@ __metadata:
|
||||
"@expo/code-signing-certificates": "npm:0.0.5"
|
||||
"@expo/config": "npm:55.0.10"
|
||||
"@expo/config-plugins": "npm:55.0.7"
|
||||
"@expo/eas-build-job": "npm:27.4.0"
|
||||
"@expo/eas-json": "npm:27.4.0"
|
||||
"@expo/eas-build-job": "npm:27.4.1"
|
||||
"@expo/eas-json": "npm:27.4.1"
|
||||
"@expo/env": "npm:^1.0.0"
|
||||
"@expo/experimentation": "npm:0.0.5"
|
||||
"@expo/json-file": "npm:8.3.3"
|
||||
"@expo/logger": "npm:27.4.0"
|
||||
"@expo/logger": "npm:27.4.1"
|
||||
"@expo/multipart-body-parser": "npm:2.0.0"
|
||||
"@expo/osascript": "npm:2.1.4"
|
||||
"@expo/package-manager": "npm:1.9.10"
|
||||
@@ -10790,7 +10790,7 @@ __metadata:
|
||||
"@expo/results": "npm:1.0.0"
|
||||
"@expo/rudder-sdk-node": "npm:1.1.1"
|
||||
"@expo/spawn-async": "npm:1.7.2"
|
||||
"@expo/steps": "npm:27.4.0"
|
||||
"@expo/steps": "npm:27.4.1"
|
||||
"@expo/timeago.js": "npm:1.0.0"
|
||||
"@graphql-codegen/cli": "npm:6.3.1"
|
||||
"@graphql-codegen/introspection": "npm:5.0.2"
|
||||
|
||||
Reference in new issue
Block a user