🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@limrun/api

Package Overview
Dependencies
Maintainers
2
Versions
100
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@limrun/api - npm Package Compare versions

Comparing version
0.16.0
to
0.17.0-rc.1
+207
ios-client.d.mts
import { EventEmitter } from 'events';
/**
* Connection state of the instance client
*/
export type ConnectionState = 'connecting' | 'connected' | 'disconnected' | 'reconnecting';
/**
* Callback function for connection state changes
*/
export type ConnectionStateCallback = (state: ConnectionState) => void;
/**
* Events emitted by a simctl execution
*/
export interface SimctlExecutionEvents {
stdout: (data: Buffer) => void;
stderr: (data: Buffer) => void;
'line-stdout': (line: string) => void;
'line-stderr': (line: string) => void;
exit: (code: number) => void;
error: (error: Error) => void;
}
/**
* A client for interacting with a Limrun iOS instance
*/
export type InstanceClient = {
/**
* Take a screenshot of the current screen
* @returns A promise that resolves to the screenshot data
*/
screenshot: () => Promise<ScreenshotData>;
/**
* Disconnect from the Limrun instance
*/
disconnect: () => void;
/**
* Get current connection state
*/
getConnectionState: () => ConnectionState;
/**
* Register callback for connection state changes
* @returns A function to unregister the callback
*/
onConnectionStateChange: (callback: ConnectionStateCallback) => () => void;
/**
* Run `simctl` command targeting the instance with given arguments.
* Returns an EventEmitter that streams stdout, stderr, and exit events.
*
* @param args Arguments to pass to simctl
* @returns A SimctlExecution handle for listening to command output
*
* @example
* ```typescript
* const execution = client.simctl(['boot', 'device-id']);
*
* // Listen to raw data
* execution.on('stdout', (data) => {
* console.log('stdout:', data.toString());
* });
*
* // Or listen line-by-line
* execution.on('line-stdout', (line) => {
* console.log('Line:', line);
* });
*
* execution.on('line-stderr', (line) => {
* console.error('Error:', line);
* });
*
* execution.on('exit', (code) => {
* console.log('Process exited with code:', code);
* });
*
* // Or wait for completion
* const result = await execution.wait();
* console.log('Exit code:', result.code);
* console.log('Full stdout:', result.stdout.toString());
* ```
*/
simctl: (args: string[]) => SimctlExecution;
/**
* Copy a file to the sandbox of the simulator. Returns the path of the file that can be used in simctl commands.
* @param name The name of the file in the sandbox of the simulator.
* @param path The path of the file to copy to the sandbox of the simulator.
* @returns A promise that resolves to the path of the file that can be used in simctl commands.
*/
cp: (name: string, path: string) => Promise<string>;
};
/**
* Controls the verbosity of logging in the client
*/
export type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
/**
* Configuration options for creating an iOS client
*/
export type InstanceClientOptions = {
/**
* The API URL for the instance.
*/
apiUrl: string;
/**
* The token to use for authentication.
*/
token: string;
/**
* Controls logging verbosity
* @default 'info'
*/
logLevel?: LogLevel;
/**
* Maximum number of reconnection attempts
* @default 6
*/
maxReconnectAttempts?: number;
/**
* Initial reconnection delay in milliseconds
* @default 1000
*/
reconnectDelay?: number;
/**
* Maximum reconnection delay in milliseconds
* @default 30000
*/
maxReconnectDelay?: number;
};
type ScreenshotData = {
dataUri: string;
};
/**
* Handle for a running simctl command execution.
*
* This class extends EventEmitter and provides streaming access to command output.
* Methods starting with underscore (_) are internal and should not be called directly.
*
* @example
* ```typescript
* const execution = client.simctl(['boot', 'device-id']);
*
* // Listen to raw output
* execution.on('stdout', (data) => console.log(data.toString()));
*
* // Or listen line-by-line (more convenient for most use cases)
* execution.on('line-stdout', (line) => console.log('Output:', line));
* execution.on('line-stderr', (line) => console.error('Error:', line));
*
* execution.on('exit', (code) => console.log('Exit code:', code));
* ```
*/
export declare class SimctlExecution extends EventEmitter {
private stdoutChunks;
private stderrChunks;
private stdoutLineBuffer;
private stderrLineBuffer;
private exitCodeValue;
private completed;
private waitPromise;
private stopCallback;
get isRunning(): boolean;
constructor(stopCallback: () => void);
/**
* Register an event listener for stdout, stderr, line-stdout, line-stderr, exit, or error events.
* @param event The event name
* @param listener The callback function for this event
*/
on<E extends keyof SimctlExecutionEvents>(event: E, listener: SimctlExecutionEvents[E]): this;
/**
* Register a one-time event listener that will be removed after firing once.
* @param event The event name
* @param listener The callback function for this event
*/
once<E extends keyof SimctlExecutionEvents>(event: E, listener: SimctlExecutionEvents[E]): this;
/**
* Remove an event listener.
* @param event The event name
* @param listener The callback function to remove
*/
off<E extends keyof SimctlExecutionEvents>(event: E, listener: SimctlExecutionEvents[E]): this;
/**
* Wait for the command to complete and get the full result.
* This accumulates all stdout/stderr chunks in memory.
* @returns Promise that resolves with exit code and complete output
*/
wait(): Promise<{
code: number;
stdout: Buffer;
stderr: Buffer;
}>;
/**
* Stop the running simctl command (if supported by server).
* This cleans up the execution tracking.
*/
stop(): void;
/** @internal - Handle stdout data from server */
_handleStdout(data: Buffer): void;
/** @internal - Handle stderr data from server */
_handleStderr(data: Buffer): void;
/** @internal - Handle exit code from server */
_handleExit(code: number): void;
/** @internal - Handle errors from server or connection */
_handleError(error: Error): void;
}
/**
* Creates a client for interacting with a Limrun iOS instance
* @param options Configuration options including webrtcUrl, token and log level
* @returns An InstanceClient for controlling the instance
*/
export declare function createInstanceClient(options: InstanceClientOptions): Promise<InstanceClient>;
export {};
//# sourceMappingURL=ios-client.d.mts.map
{"version":3,"file":"ios-client.d.mts","sourceRoot":"","sources":["src/ios-client.ts"],"names":[],"mappings":"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"}
import { EventEmitter } from 'events';
/**
* Connection state of the instance client
*/
export type ConnectionState = 'connecting' | 'connected' | 'disconnected' | 'reconnecting';
/**
* Callback function for connection state changes
*/
export type ConnectionStateCallback = (state: ConnectionState) => void;
/**
* Events emitted by a simctl execution
*/
export interface SimctlExecutionEvents {
stdout: (data: Buffer) => void;
stderr: (data: Buffer) => void;
'line-stdout': (line: string) => void;
'line-stderr': (line: string) => void;
exit: (code: number) => void;
error: (error: Error) => void;
}
/**
* A client for interacting with a Limrun iOS instance
*/
export type InstanceClient = {
/**
* Take a screenshot of the current screen
* @returns A promise that resolves to the screenshot data
*/
screenshot: () => Promise<ScreenshotData>;
/**
* Disconnect from the Limrun instance
*/
disconnect: () => void;
/**
* Get current connection state
*/
getConnectionState: () => ConnectionState;
/**
* Register callback for connection state changes
* @returns A function to unregister the callback
*/
onConnectionStateChange: (callback: ConnectionStateCallback) => () => void;
/**
* Run `simctl` command targeting the instance with given arguments.
* Returns an EventEmitter that streams stdout, stderr, and exit events.
*
* @param args Arguments to pass to simctl
* @returns A SimctlExecution handle for listening to command output
*
* @example
* ```typescript
* const execution = client.simctl(['boot', 'device-id']);
*
* // Listen to raw data
* execution.on('stdout', (data) => {
* console.log('stdout:', data.toString());
* });
*
* // Or listen line-by-line
* execution.on('line-stdout', (line) => {
* console.log('Line:', line);
* });
*
* execution.on('line-stderr', (line) => {
* console.error('Error:', line);
* });
*
* execution.on('exit', (code) => {
* console.log('Process exited with code:', code);
* });
*
* // Or wait for completion
* const result = await execution.wait();
* console.log('Exit code:', result.code);
* console.log('Full stdout:', result.stdout.toString());
* ```
*/
simctl: (args: string[]) => SimctlExecution;
/**
* Copy a file to the sandbox of the simulator. Returns the path of the file that can be used in simctl commands.
* @param name The name of the file in the sandbox of the simulator.
* @param path The path of the file to copy to the sandbox of the simulator.
* @returns A promise that resolves to the path of the file that can be used in simctl commands.
*/
cp: (name: string, path: string) => Promise<string>;
};
/**
* Controls the verbosity of logging in the client
*/
export type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
/**
* Configuration options for creating an iOS client
*/
export type InstanceClientOptions = {
/**
* The API URL for the instance.
*/
apiUrl: string;
/**
* The token to use for authentication.
*/
token: string;
/**
* Controls logging verbosity
* @default 'info'
*/
logLevel?: LogLevel;
/**
* Maximum number of reconnection attempts
* @default 6
*/
maxReconnectAttempts?: number;
/**
* Initial reconnection delay in milliseconds
* @default 1000
*/
reconnectDelay?: number;
/**
* Maximum reconnection delay in milliseconds
* @default 30000
*/
maxReconnectDelay?: number;
};
type ScreenshotData = {
dataUri: string;
};
/**
* Handle for a running simctl command execution.
*
* This class extends EventEmitter and provides streaming access to command output.
* Methods starting with underscore (_) are internal and should not be called directly.
*
* @example
* ```typescript
* const execution = client.simctl(['boot', 'device-id']);
*
* // Listen to raw output
* execution.on('stdout', (data) => console.log(data.toString()));
*
* // Or listen line-by-line (more convenient for most use cases)
* execution.on('line-stdout', (line) => console.log('Output:', line));
* execution.on('line-stderr', (line) => console.error('Error:', line));
*
* execution.on('exit', (code) => console.log('Exit code:', code));
* ```
*/
export declare class SimctlExecution extends EventEmitter {
private stdoutChunks;
private stderrChunks;
private stdoutLineBuffer;
private stderrLineBuffer;
private exitCodeValue;
private completed;
private waitPromise;
private stopCallback;
get isRunning(): boolean;
constructor(stopCallback: () => void);
/**
* Register an event listener for stdout, stderr, line-stdout, line-stderr, exit, or error events.
* @param event The event name
* @param listener The callback function for this event
*/
on<E extends keyof SimctlExecutionEvents>(event: E, listener: SimctlExecutionEvents[E]): this;
/**
* Register a one-time event listener that will be removed after firing once.
* @param event The event name
* @param listener The callback function for this event
*/
once<E extends keyof SimctlExecutionEvents>(event: E, listener: SimctlExecutionEvents[E]): this;
/**
* Remove an event listener.
* @param event The event name
* @param listener The callback function to remove
*/
off<E extends keyof SimctlExecutionEvents>(event: E, listener: SimctlExecutionEvents[E]): this;
/**
* Wait for the command to complete and get the full result.
* This accumulates all stdout/stderr chunks in memory.
* @returns Promise that resolves with exit code and complete output
*/
wait(): Promise<{
code: number;
stdout: Buffer;
stderr: Buffer;
}>;
/**
* Stop the running simctl command (if supported by server).
* This cleans up the execution tracking.
*/
stop(): void;
/** @internal - Handle stdout data from server */
_handleStdout(data: Buffer): void;
/** @internal - Handle stderr data from server */
_handleStderr(data: Buffer): void;
/** @internal - Handle exit code from server */
_handleExit(code: number): void;
/** @internal - Handle errors from server or connection */
_handleError(error: Error): void;
}
/**
* Creates a client for interacting with a Limrun iOS instance
* @param options Configuration options including webrtcUrl, token and log level
* @returns An InstanceClient for controlling the instance
*/
export declare function createInstanceClient(options: InstanceClientOptions): Promise<InstanceClient>;
export {};
//# sourceMappingURL=ios-client.d.ts.map
{"version":3,"file":"ios-client.d.ts","sourceRoot":"","sources":["src/ios-client.ts"],"names":[],"mappings":"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"}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SimctlExecution = void 0;
exports.createInstanceClient = createInstanceClient;
const tslib_1 = require("./internal/tslib.js");
const ws_1 = require("ws");
const fs_1 = tslib_1.__importDefault(require("fs"));
const events_1 = require("events");
/**
* Handle for a running simctl command execution.
*
* This class extends EventEmitter and provides streaming access to command output.
* Methods starting with underscore (_) are internal and should not be called directly.
*
* @example
* ```typescript
* const execution = client.simctl(['boot', 'device-id']);
*
* // Listen to raw output
* execution.on('stdout', (data) => console.log(data.toString()));
*
* // Or listen line-by-line (more convenient for most use cases)
* execution.on('line-stdout', (line) => console.log('Output:', line));
* execution.on('line-stderr', (line) => console.error('Error:', line));
*
* execution.on('exit', (code) => console.log('Exit code:', code));
* ```
*/
class SimctlExecution extends events_1.EventEmitter {
get isRunning() {
return !this.completed;
}
constructor(stopCallback) {
super();
this.stdoutChunks = [];
this.stderrChunks = [];
this.stdoutLineBuffer = '';
this.stderrLineBuffer = '';
this.exitCodeValue = null;
this.completed = false;
this.waitPromise = null;
this.stopCallback = null;
this.stopCallback = stopCallback;
}
/**
* Register an event listener for stdout, stderr, line-stdout, line-stderr, exit, or error events.
* @param event The event name
* @param listener The callback function for this event
*/
on(event, listener) {
return super.on(event, listener);
}
/**
* Register a one-time event listener that will be removed after firing once.
* @param event The event name
* @param listener The callback function for this event
*/
once(event, listener) {
return super.once(event, listener);
}
/**
* Remove an event listener.
* @param event The event name
* @param listener The callback function to remove
*/
off(event, listener) {
return super.off(event, listener);
}
/**
* Wait for the command to complete and get the full result.
* This accumulates all stdout/stderr chunks in memory.
* @returns Promise that resolves with exit code and complete output
*/
wait() {
if (this.waitPromise) {
return this.waitPromise;
}
this.waitPromise = new Promise((resolve, reject) => {
if (this.completed) {
resolve({
code: this.exitCodeValue,
stdout: Buffer.concat(this.stdoutChunks),
stderr: Buffer.concat(this.stderrChunks),
});
return;
}
this.once('exit', (code) => {
resolve({
code,
stdout: Buffer.concat(this.stdoutChunks),
stderr: Buffer.concat(this.stderrChunks),
});
});
this.once('error', (error) => {
reject(error);
});
});
return this.waitPromise;
}
/**
* Stop the running simctl command (if supported by server).
* This cleans up the execution tracking.
*/
stop() {
if (this.stopCallback) {
this.stopCallback();
}
}
/** @internal - Handle stdout data from server */
_handleStdout(data) {
this.stdoutChunks.push(data);
this.emit('stdout', data);
// Process line-by-line
this.stdoutLineBuffer += data.toString('utf-8');
const lines = this.stdoutLineBuffer.split('\n');
// Keep the last incomplete line in the buffer
this.stdoutLineBuffer = lines.pop() || '';
// Emit complete lines
for (const line of lines) {
this.emit('line-stdout', line);
}
}
/** @internal - Handle stderr data from server */
_handleStderr(data) {
this.stderrChunks.push(data);
this.emit('stderr', data);
// Process line-by-line
this.stderrLineBuffer += data.toString('utf-8');
const lines = this.stderrLineBuffer.split('\n');
// Keep the last incomplete line in the buffer
this.stderrLineBuffer = lines.pop() || '';
// Emit complete lines
for (const line of lines) {
this.emit('line-stderr', line);
}
}
/** @internal - Handle exit code from server */
_handleExit(code) {
// Emit any remaining partial lines before exit
if (this.stdoutLineBuffer) {
this.emit('line-stdout', this.stdoutLineBuffer);
this.stdoutLineBuffer = '';
}
if (this.stderrLineBuffer) {
this.emit('line-stderr', this.stderrLineBuffer);
this.stderrLineBuffer = '';
}
this.exitCodeValue = code;
this.completed = true;
this.emit('exit', code);
}
/** @internal - Handle errors from server or connection */
_handleError(error) {
this.completed = true;
this.emit('error', error);
}
}
exports.SimctlExecution = SimctlExecution;
/**
* Creates a client for interacting with a Limrun iOS instance
* @param options Configuration options including webrtcUrl, token and log level
* @returns An InstanceClient for controlling the instance
*/
async function createInstanceClient(options) {
const endpointWebSocketUrl = `${options.apiUrl}/endpointWebSocket?token=${options.token}`;
const logLevel = options.logLevel ?? 'info';
const maxReconnectAttempts = options.maxReconnectAttempts ?? 6;
const reconnectDelay = options.reconnectDelay ?? 1000;
const maxReconnectDelay = options.maxReconnectDelay ?? 30000;
let ws = undefined;
let connectionState = 'connecting';
let reconnectAttempts = 0;
let reconnectTimeout;
let intentionalDisconnect = false;
const screenshotRequests = new Map();
const simctlExecutions = new Map();
const stateChangeCallbacks = new Set();
// Logger functions
const logger = {
debug: (...args) => {
if (logLevel === 'debug')
console.log(...args);
},
info: (...args) => {
if (logLevel === 'info' || logLevel === 'debug')
console.log(...args);
},
warn: (...args) => {
if (logLevel === 'warn' || logLevel === 'info' || logLevel === 'debug')
console.warn(...args);
},
error: (...args) => {
if (logLevel !== 'none')
console.error(...args);
},
};
const updateConnectionState = (newState) => {
if (connectionState !== newState) {
connectionState = newState;
logger.debug(`Connection state changed to: ${newState}`);
stateChangeCallbacks.forEach((callback) => {
try {
callback(newState);
}
catch (err) {
logger.error('Error in connection state callback:', err);
}
});
}
};
const failPendingRequests = (reason) => {
screenshotRequests.forEach((request) => request.rejecter(new Error(reason)));
screenshotRequests.clear();
simctlExecutions.forEach((execution) => execution._handleError(new Error(reason)));
simctlExecutions.clear();
};
const cleanup = () => {
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
reconnectTimeout = undefined;
}
if (pingInterval) {
clearInterval(pingInterval);
pingInterval = undefined;
}
if (ws) {
ws.removeAllListeners();
if (ws.readyState === ws_1.WebSocket.OPEN || ws.readyState === ws_1.WebSocket.CONNECTING) {
ws.close();
}
ws = undefined;
}
};
let pingInterval;
return new Promise((resolveConnection, rejectConnection) => {
let hasResolved = false;
// Reconnection logic with exponential backoff
const scheduleReconnect = () => {
if (intentionalDisconnect) {
logger.debug('Skipping reconnection (intentional disconnect)');
return;
}
if (reconnectAttempts >= maxReconnectAttempts) {
logger.error(`Max reconnection attempts (${maxReconnectAttempts}) reached. Giving up.`);
updateConnectionState('disconnected');
return;
}
const currentDelay = Math.min(reconnectDelay * Math.pow(2, reconnectAttempts), maxReconnectDelay);
reconnectAttempts++;
logger.debug(`Scheduling reconnection attempt ${reconnectAttempts} in ${currentDelay}ms...`);
updateConnectionState('reconnecting');
reconnectTimeout = setTimeout(() => {
logger.debug(`Attempting to reconnect (attempt ${reconnectAttempts})...`);
setupWebSocket();
}, currentDelay);
};
const setupWebSocket = () => {
cleanup();
updateConnectionState('connecting');
ws = new ws_1.WebSocket(endpointWebSocketUrl);
ws.on('message', (data) => {
let message;
try {
message = JSON.parse(data.toString());
}
catch (e) {
logger.error({ data, error: e }, 'Failed to parse JSON message');
return;
}
switch (message.type) {
case 'screenshot': {
if (!('dataUri' in message) || typeof message.dataUri !== 'string' || !('id' in message)) {
logger.warn('Received invalid screenshot message:', message);
break;
}
const screenshotMessage = message;
const request = screenshotRequests.get(screenshotMessage.id);
if (!request) {
logger.warn(`Received screenshot data for unknown or already handled session: ${screenshotMessage.id}`);
break;
}
logger.debug(`Received screenshot data URI for session ${screenshotMessage.id}.`);
request.resolver({ dataUri: screenshotMessage.dataUri });
screenshotRequests.delete(screenshotMessage.id);
break;
}
case 'screenshotError': {
if (!('message' in message) || !('id' in message)) {
logger.warn('Received invalid screenshot error message:', message);
break;
}
const errorMessage = message;
const request = screenshotRequests.get(errorMessage.id);
if (!request) {
logger.warn(`Received screenshot error for unknown or already handled session: ${errorMessage.id}`);
break;
}
logger.error(`Server reported an error capturing screenshot for session ${errorMessage.id}:`, errorMessage.message);
request.rejecter(new Error(errorMessage.message));
screenshotRequests.delete(errorMessage.id);
break;
}
case 'simctlStream': {
if (!('id' in message)) {
logger.warn('Received invalid simctl stream message:', message);
break;
}
const streamMessage = message;
const execution = simctlExecutions.get(streamMessage.id);
if (!execution) {
logger.warn(`Received simctl stream for unknown or already completed execution: ${streamMessage.id}`);
break;
}
// Handle stdout if present
if (streamMessage.stdout) {
try {
const stdoutBuffer = Buffer.from(streamMessage.stdout, 'base64');
execution._handleStdout(stdoutBuffer);
}
catch (err) {
logger.error('Failed to decode stdout data:', err);
}
}
// Handle stderr if present
if (streamMessage.stderr) {
try {
const stderrBuffer = Buffer.from(streamMessage.stderr, 'base64');
execution._handleStderr(stderrBuffer);
}
catch (err) {
logger.error('Failed to decode stderr data:', err);
}
}
// Handle exit code if present (final message)
if (streamMessage.exitCode !== undefined) {
logger.debug(`Simctl execution ${streamMessage.id} completed with exit code ${streamMessage.exitCode}`);
execution._handleExit(streamMessage.exitCode);
simctlExecutions.delete(streamMessage.id);
}
break;
}
case 'simctlError': {
if (!('message' in message) || !('id' in message)) {
logger.warn('Received invalid simctl error message:', message);
break;
}
const errorMessage = message;
const execution = simctlExecutions.get(errorMessage.id);
if (!execution) {
logger.warn(`Received simctl error for unknown or already handled execution: ${errorMessage.id}`);
break;
}
logger.error(`Server reported an error for simctl execution ${errorMessage.id}:`, errorMessage.message);
execution._handleError(new Error(errorMessage.message));
simctlExecutions.delete(errorMessage.id);
break;
}
default:
logger.warn('Received unexpected message type:', message);
break;
}
});
ws.on('error', (err) => {
logger.error('WebSocket error:', err.message);
if (!hasResolved && (ws?.readyState === ws_1.WebSocket.CONNECTING || ws?.readyState === ws_1.WebSocket.OPEN)) {
rejectConnection(err);
}
});
ws.on('close', () => {
if (pingInterval) {
clearInterval(pingInterval);
pingInterval = undefined;
}
const shouldReconnect = !intentionalDisconnect && connectionState !== 'disconnected';
updateConnectionState('disconnected');
logger.debug('Disconnected from server.');
failPendingRequests('Connection closed');
if (shouldReconnect) {
scheduleReconnect();
}
});
ws.on('open', () => {
logger.debug(`Connected to ${endpointWebSocketUrl}`);
reconnectAttempts = 0;
updateConnectionState('connected');
pingInterval = setInterval(() => {
if (ws && ws.readyState === ws_1.WebSocket.OPEN) {
ws.ping();
}
}, 30000);
if (!hasResolved) {
hasResolved = true;
resolveConnection({
screenshot,
disconnect,
getConnectionState,
onConnectionStateChange,
simctl,
cp,
});
}
});
};
const screenshot = async () => {
if (!ws || ws.readyState !== ws_1.WebSocket.OPEN) {
return Promise.reject(new Error('WebSocket is not connected or connection is not open.'));
}
const id = 'ts-client-' + Date.now();
const screenshotRequest = {
type: 'screenshot',
id,
};
return new Promise((resolve, reject) => {
logger.debug('Sending screenshot request:', screenshotRequest);
ws.send(JSON.stringify(screenshotRequest), (err) => {
if (err) {
logger.error('Failed to send screenshot request:', err);
reject(err);
}
});
const timeout = setTimeout(() => {
if (screenshotRequests.has(id)) {
logger.error(`Screenshot request timed out for session ${id}`);
screenshotRequests.get(id)?.rejecter(new Error('Screenshot request timed out'));
screenshotRequests.delete(id);
}
}, 30000);
screenshotRequests.set(id, {
resolver: (value) => {
clearTimeout(timeout);
resolve(value);
screenshotRequests.delete(id);
},
rejecter: (reason) => {
clearTimeout(timeout);
reject(reason);
screenshotRequests.delete(id);
},
});
});
};
const simctl = (args) => {
const id = 'ts-simctl-' + Date.now() + '-' + Math.random().toString(36).substring(7);
const cancelCallback = () => {
// Clean up execution tracking
simctlExecutions.delete(id);
logger.debug(`Simctl execution ${id} cancelled`);
};
const execution = new SimctlExecution(cancelCallback);
simctlExecutions.set(id, execution);
// Send request asynchronously
if (!ws || ws.readyState !== ws_1.WebSocket.OPEN) {
// Defer error to next tick to allow caller to attach listeners
process.nextTick(() => {
execution._handleError(new Error('WebSocket is not connected or connection is not open.'));
simctlExecutions.delete(id);
});
return execution;
}
const simctlRequest = {
type: 'simctl',
id,
args,
};
logger.debug('Sending simctl request:', simctlRequest);
ws.send(JSON.stringify(simctlRequest), (err) => {
if (err) {
logger.error('Failed to send simctl request:', err);
execution._handleError(err);
simctlExecutions.delete(id);
}
});
return execution;
};
const cp = async (name, filePath) => {
const fileStream = fs_1.default.createReadStream(filePath);
const uploadUrl = `${options.apiUrl}/files?name=${encodeURIComponent(name)}`;
try {
const response = await fetch(uploadUrl, {
method: 'PUT',
headers: {
'Content-Type': 'application/octet-stream',
'Content-Length': fs_1.default.statSync(filePath).size.toString(),
Authorization: `Bearer ${options.token}`,
},
body: fileStream,
duplex: 'half',
});
if (!response.ok) {
const errorBody = await response.text();
logger.debug(`Upload failed: ${response.status} ${errorBody}`);
throw new Error(`Upload failed: ${response.status} ${errorBody}`);
}
const result = (await response.json());
return result.path;
}
catch (err) {
logger.debug(`Failed to upload file ${filePath}:`, err);
throw err;
}
};
const disconnect = () => {
intentionalDisconnect = true;
cleanup();
updateConnectionState('disconnected');
failPendingRequests('Intentional disconnect');
logger.debug('Intentionally disconnected from WebSocket.');
};
const getConnectionState = () => {
return connectionState;
};
const onConnectionStateChange = (callback) => {
stateChangeCallbacks.add(callback);
return () => {
stateChangeCallbacks.delete(callback);
};
};
setupWebSocket();
});
}
//# sourceMappingURL=ios-client.js.map
{"version":3,"file":"ios-client.js","sourceRoot":"","sources":["src/ios-client.ts"],"names":[],"mappings":"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"}
import { WebSocket } from 'ws';
import fs from 'fs';
import { EventEmitter } from 'events';
/**
* Handle for a running simctl command execution.
*
* This class extends EventEmitter and provides streaming access to command output.
* Methods starting with underscore (_) are internal and should not be called directly.
*
* @example
* ```typescript
* const execution = client.simctl(['boot', 'device-id']);
*
* // Listen to raw output
* execution.on('stdout', (data) => console.log(data.toString()));
*
* // Or listen line-by-line (more convenient for most use cases)
* execution.on('line-stdout', (line) => console.log('Output:', line));
* execution.on('line-stderr', (line) => console.error('Error:', line));
*
* execution.on('exit', (code) => console.log('Exit code:', code));
* ```
*/
export class SimctlExecution extends EventEmitter {
get isRunning() {
return !this.completed;
}
constructor(stopCallback) {
super();
this.stdoutChunks = [];
this.stderrChunks = [];
this.stdoutLineBuffer = '';
this.stderrLineBuffer = '';
this.exitCodeValue = null;
this.completed = false;
this.waitPromise = null;
this.stopCallback = null;
this.stopCallback = stopCallback;
}
/**
* Register an event listener for stdout, stderr, line-stdout, line-stderr, exit, or error events.
* @param event The event name
* @param listener The callback function for this event
*/
on(event, listener) {
return super.on(event, listener);
}
/**
* Register a one-time event listener that will be removed after firing once.
* @param event The event name
* @param listener The callback function for this event
*/
once(event, listener) {
return super.once(event, listener);
}
/**
* Remove an event listener.
* @param event The event name
* @param listener The callback function to remove
*/
off(event, listener) {
return super.off(event, listener);
}
/**
* Wait for the command to complete and get the full result.
* This accumulates all stdout/stderr chunks in memory.
* @returns Promise that resolves with exit code and complete output
*/
wait() {
if (this.waitPromise) {
return this.waitPromise;
}
this.waitPromise = new Promise((resolve, reject) => {
if (this.completed) {
resolve({
code: this.exitCodeValue,
stdout: Buffer.concat(this.stdoutChunks),
stderr: Buffer.concat(this.stderrChunks),
});
return;
}
this.once('exit', (code) => {
resolve({
code,
stdout: Buffer.concat(this.stdoutChunks),
stderr: Buffer.concat(this.stderrChunks),
});
});
this.once('error', (error) => {
reject(error);
});
});
return this.waitPromise;
}
/**
* Stop the running simctl command (if supported by server).
* This cleans up the execution tracking.
*/
stop() {
if (this.stopCallback) {
this.stopCallback();
}
}
/** @internal - Handle stdout data from server */
_handleStdout(data) {
this.stdoutChunks.push(data);
this.emit('stdout', data);
// Process line-by-line
this.stdoutLineBuffer += data.toString('utf-8');
const lines = this.stdoutLineBuffer.split('\n');
// Keep the last incomplete line in the buffer
this.stdoutLineBuffer = lines.pop() || '';
// Emit complete lines
for (const line of lines) {
this.emit('line-stdout', line);
}
}
/** @internal - Handle stderr data from server */
_handleStderr(data) {
this.stderrChunks.push(data);
this.emit('stderr', data);
// Process line-by-line
this.stderrLineBuffer += data.toString('utf-8');
const lines = this.stderrLineBuffer.split('\n');
// Keep the last incomplete line in the buffer
this.stderrLineBuffer = lines.pop() || '';
// Emit complete lines
for (const line of lines) {
this.emit('line-stderr', line);
}
}
/** @internal - Handle exit code from server */
_handleExit(code) {
// Emit any remaining partial lines before exit
if (this.stdoutLineBuffer) {
this.emit('line-stdout', this.stdoutLineBuffer);
this.stdoutLineBuffer = '';
}
if (this.stderrLineBuffer) {
this.emit('line-stderr', this.stderrLineBuffer);
this.stderrLineBuffer = '';
}
this.exitCodeValue = code;
this.completed = true;
this.emit('exit', code);
}
/** @internal - Handle errors from server or connection */
_handleError(error) {
this.completed = true;
this.emit('error', error);
}
}
/**
* Creates a client for interacting with a Limrun iOS instance
* @param options Configuration options including webrtcUrl, token and log level
* @returns An InstanceClient for controlling the instance
*/
export async function createInstanceClient(options) {
const endpointWebSocketUrl = `${options.apiUrl}/endpointWebSocket?token=${options.token}`;
const logLevel = options.logLevel ?? 'info';
const maxReconnectAttempts = options.maxReconnectAttempts ?? 6;
const reconnectDelay = options.reconnectDelay ?? 1000;
const maxReconnectDelay = options.maxReconnectDelay ?? 30000;
let ws = undefined;
let connectionState = 'connecting';
let reconnectAttempts = 0;
let reconnectTimeout;
let intentionalDisconnect = false;
const screenshotRequests = new Map();
const simctlExecutions = new Map();
const stateChangeCallbacks = new Set();
// Logger functions
const logger = {
debug: (...args) => {
if (logLevel === 'debug')
console.log(...args);
},
info: (...args) => {
if (logLevel === 'info' || logLevel === 'debug')
console.log(...args);
},
warn: (...args) => {
if (logLevel === 'warn' || logLevel === 'info' || logLevel === 'debug')
console.warn(...args);
},
error: (...args) => {
if (logLevel !== 'none')
console.error(...args);
},
};
const updateConnectionState = (newState) => {
if (connectionState !== newState) {
connectionState = newState;
logger.debug(`Connection state changed to: ${newState}`);
stateChangeCallbacks.forEach((callback) => {
try {
callback(newState);
}
catch (err) {
logger.error('Error in connection state callback:', err);
}
});
}
};
const failPendingRequests = (reason) => {
screenshotRequests.forEach((request) => request.rejecter(new Error(reason)));
screenshotRequests.clear();
simctlExecutions.forEach((execution) => execution._handleError(new Error(reason)));
simctlExecutions.clear();
};
const cleanup = () => {
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
reconnectTimeout = undefined;
}
if (pingInterval) {
clearInterval(pingInterval);
pingInterval = undefined;
}
if (ws) {
ws.removeAllListeners();
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
ws.close();
}
ws = undefined;
}
};
let pingInterval;
return new Promise((resolveConnection, rejectConnection) => {
let hasResolved = false;
// Reconnection logic with exponential backoff
const scheduleReconnect = () => {
if (intentionalDisconnect) {
logger.debug('Skipping reconnection (intentional disconnect)');
return;
}
if (reconnectAttempts >= maxReconnectAttempts) {
logger.error(`Max reconnection attempts (${maxReconnectAttempts}) reached. Giving up.`);
updateConnectionState('disconnected');
return;
}
const currentDelay = Math.min(reconnectDelay * Math.pow(2, reconnectAttempts), maxReconnectDelay);
reconnectAttempts++;
logger.debug(`Scheduling reconnection attempt ${reconnectAttempts} in ${currentDelay}ms...`);
updateConnectionState('reconnecting');
reconnectTimeout = setTimeout(() => {
logger.debug(`Attempting to reconnect (attempt ${reconnectAttempts})...`);
setupWebSocket();
}, currentDelay);
};
const setupWebSocket = () => {
cleanup();
updateConnectionState('connecting');
ws = new WebSocket(endpointWebSocketUrl);
ws.on('message', (data) => {
let message;
try {
message = JSON.parse(data.toString());
}
catch (e) {
logger.error({ data, error: e }, 'Failed to parse JSON message');
return;
}
switch (message.type) {
case 'screenshot': {
if (!('dataUri' in message) || typeof message.dataUri !== 'string' || !('id' in message)) {
logger.warn('Received invalid screenshot message:', message);
break;
}
const screenshotMessage = message;
const request = screenshotRequests.get(screenshotMessage.id);
if (!request) {
logger.warn(`Received screenshot data for unknown or already handled session: ${screenshotMessage.id}`);
break;
}
logger.debug(`Received screenshot data URI for session ${screenshotMessage.id}.`);
request.resolver({ dataUri: screenshotMessage.dataUri });
screenshotRequests.delete(screenshotMessage.id);
break;
}
case 'screenshotError': {
if (!('message' in message) || !('id' in message)) {
logger.warn('Received invalid screenshot error message:', message);
break;
}
const errorMessage = message;
const request = screenshotRequests.get(errorMessage.id);
if (!request) {
logger.warn(`Received screenshot error for unknown or already handled session: ${errorMessage.id}`);
break;
}
logger.error(`Server reported an error capturing screenshot for session ${errorMessage.id}:`, errorMessage.message);
request.rejecter(new Error(errorMessage.message));
screenshotRequests.delete(errorMessage.id);
break;
}
case 'simctlStream': {
if (!('id' in message)) {
logger.warn('Received invalid simctl stream message:', message);
break;
}
const streamMessage = message;
const execution = simctlExecutions.get(streamMessage.id);
if (!execution) {
logger.warn(`Received simctl stream for unknown or already completed execution: ${streamMessage.id}`);
break;
}
// Handle stdout if present
if (streamMessage.stdout) {
try {
const stdoutBuffer = Buffer.from(streamMessage.stdout, 'base64');
execution._handleStdout(stdoutBuffer);
}
catch (err) {
logger.error('Failed to decode stdout data:', err);
}
}
// Handle stderr if present
if (streamMessage.stderr) {
try {
const stderrBuffer = Buffer.from(streamMessage.stderr, 'base64');
execution._handleStderr(stderrBuffer);
}
catch (err) {
logger.error('Failed to decode stderr data:', err);
}
}
// Handle exit code if present (final message)
if (streamMessage.exitCode !== undefined) {
logger.debug(`Simctl execution ${streamMessage.id} completed with exit code ${streamMessage.exitCode}`);
execution._handleExit(streamMessage.exitCode);
simctlExecutions.delete(streamMessage.id);
}
break;
}
case 'simctlError': {
if (!('message' in message) || !('id' in message)) {
logger.warn('Received invalid simctl error message:', message);
break;
}
const errorMessage = message;
const execution = simctlExecutions.get(errorMessage.id);
if (!execution) {
logger.warn(`Received simctl error for unknown or already handled execution: ${errorMessage.id}`);
break;
}
logger.error(`Server reported an error for simctl execution ${errorMessage.id}:`, errorMessage.message);
execution._handleError(new Error(errorMessage.message));
simctlExecutions.delete(errorMessage.id);
break;
}
default:
logger.warn('Received unexpected message type:', message);
break;
}
});
ws.on('error', (err) => {
logger.error('WebSocket error:', err.message);
if (!hasResolved && (ws?.readyState === WebSocket.CONNECTING || ws?.readyState === WebSocket.OPEN)) {
rejectConnection(err);
}
});
ws.on('close', () => {
if (pingInterval) {
clearInterval(pingInterval);
pingInterval = undefined;
}
const shouldReconnect = !intentionalDisconnect && connectionState !== 'disconnected';
updateConnectionState('disconnected');
logger.debug('Disconnected from server.');
failPendingRequests('Connection closed');
if (shouldReconnect) {
scheduleReconnect();
}
});
ws.on('open', () => {
logger.debug(`Connected to ${endpointWebSocketUrl}`);
reconnectAttempts = 0;
updateConnectionState('connected');
pingInterval = setInterval(() => {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.ping();
}
}, 30000);
if (!hasResolved) {
hasResolved = true;
resolveConnection({
screenshot,
disconnect,
getConnectionState,
onConnectionStateChange,
simctl,
cp,
});
}
});
};
const screenshot = async () => {
if (!ws || ws.readyState !== WebSocket.OPEN) {
return Promise.reject(new Error('WebSocket is not connected or connection is not open.'));
}
const id = 'ts-client-' + Date.now();
const screenshotRequest = {
type: 'screenshot',
id,
};
return new Promise((resolve, reject) => {
logger.debug('Sending screenshot request:', screenshotRequest);
ws.send(JSON.stringify(screenshotRequest), (err) => {
if (err) {
logger.error('Failed to send screenshot request:', err);
reject(err);
}
});
const timeout = setTimeout(() => {
if (screenshotRequests.has(id)) {
logger.error(`Screenshot request timed out for session ${id}`);
screenshotRequests.get(id)?.rejecter(new Error('Screenshot request timed out'));
screenshotRequests.delete(id);
}
}, 30000);
screenshotRequests.set(id, {
resolver: (value) => {
clearTimeout(timeout);
resolve(value);
screenshotRequests.delete(id);
},
rejecter: (reason) => {
clearTimeout(timeout);
reject(reason);
screenshotRequests.delete(id);
},
});
});
};
const simctl = (args) => {
const id = 'ts-simctl-' + Date.now() + '-' + Math.random().toString(36).substring(7);
const cancelCallback = () => {
// Clean up execution tracking
simctlExecutions.delete(id);
logger.debug(`Simctl execution ${id} cancelled`);
};
const execution = new SimctlExecution(cancelCallback);
simctlExecutions.set(id, execution);
// Send request asynchronously
if (!ws || ws.readyState !== WebSocket.OPEN) {
// Defer error to next tick to allow caller to attach listeners
process.nextTick(() => {
execution._handleError(new Error('WebSocket is not connected or connection is not open.'));
simctlExecutions.delete(id);
});
return execution;
}
const simctlRequest = {
type: 'simctl',
id,
args,
};
logger.debug('Sending simctl request:', simctlRequest);
ws.send(JSON.stringify(simctlRequest), (err) => {
if (err) {
logger.error('Failed to send simctl request:', err);
execution._handleError(err);
simctlExecutions.delete(id);
}
});
return execution;
};
const cp = async (name, filePath) => {
const fileStream = fs.createReadStream(filePath);
const uploadUrl = `${options.apiUrl}/files?name=${encodeURIComponent(name)}`;
try {
const response = await fetch(uploadUrl, {
method: 'PUT',
headers: {
'Content-Type': 'application/octet-stream',
'Content-Length': fs.statSync(filePath).size.toString(),
Authorization: `Bearer ${options.token}`,
},
body: fileStream,
duplex: 'half',
});
if (!response.ok) {
const errorBody = await response.text();
logger.debug(`Upload failed: ${response.status} ${errorBody}`);
throw new Error(`Upload failed: ${response.status} ${errorBody}`);
}
const result = (await response.json());
return result.path;
}
catch (err) {
logger.debug(`Failed to upload file ${filePath}:`, err);
throw err;
}
};
const disconnect = () => {
intentionalDisconnect = true;
cleanup();
updateConnectionState('disconnected');
failPendingRequests('Intentional disconnect');
logger.debug('Intentionally disconnected from WebSocket.');
};
const getConnectionState = () => {
return connectionState;
};
const onConnectionStateChange = (callback) => {
stateChangeCallbacks.add(callback);
return () => {
stateChangeCallbacks.delete(callback);
};
};
setupWebSocket();
});
}
//# sourceMappingURL=ios-client.mjs.map
{"version":3,"file":"ios-client.mjs","sourceRoot":"","sources":["src/ios-client.ts"],"names":[],"mappings":"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"}
import { WebSocket, Data } from 'ws';
import fs from 'fs';
import { EventEmitter } from 'events';
/**
* Connection state of the instance client
*/
export type ConnectionState = 'connecting' | 'connected' | 'disconnected' | 'reconnecting';
/**
* Callback function for connection state changes
*/
export type ConnectionStateCallback = (state: ConnectionState) => void;
/**
* Events emitted by a simctl execution
*/
export interface SimctlExecutionEvents {
stdout: (data: Buffer) => void;
stderr: (data: Buffer) => void;
'line-stdout': (line: string) => void;
'line-stderr': (line: string) => void;
exit: (code: number) => void;
error: (error: Error) => void;
}
/**
* A client for interacting with a Limrun iOS instance
*/
export type InstanceClient = {
/**
* Take a screenshot of the current screen
* @returns A promise that resolves to the screenshot data
*/
screenshot: () => Promise<ScreenshotData>;
/**
* Disconnect from the Limrun instance
*/
disconnect: () => void;
/**
* Get current connection state
*/
getConnectionState: () => ConnectionState;
/**
* Register callback for connection state changes
* @returns A function to unregister the callback
*/
onConnectionStateChange: (callback: ConnectionStateCallback) => () => void;
/**
* Run `simctl` command targeting the instance with given arguments.
* Returns an EventEmitter that streams stdout, stderr, and exit events.
*
* @param args Arguments to pass to simctl
* @returns A SimctlExecution handle for listening to command output
*
* @example
* ```typescript
* const execution = client.simctl(['boot', 'device-id']);
*
* // Listen to raw data
* execution.on('stdout', (data) => {
* console.log('stdout:', data.toString());
* });
*
* // Or listen line-by-line
* execution.on('line-stdout', (line) => {
* console.log('Line:', line);
* });
*
* execution.on('line-stderr', (line) => {
* console.error('Error:', line);
* });
*
* execution.on('exit', (code) => {
* console.log('Process exited with code:', code);
* });
*
* // Or wait for completion
* const result = await execution.wait();
* console.log('Exit code:', result.code);
* console.log('Full stdout:', result.stdout.toString());
* ```
*/
simctl: (args: string[]) => SimctlExecution;
/**
* Copy a file to the sandbox of the simulator. Returns the path of the file that can be used in simctl commands.
* @param name The name of the file in the sandbox of the simulator.
* @param path The path of the file to copy to the sandbox of the simulator.
* @returns A promise that resolves to the path of the file that can be used in simctl commands.
*/
cp: (name: string, path: string) => Promise<string>;
};
/**
* Controls the verbosity of logging in the client
*/
export type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
/**
* Configuration options for creating an iOS client
*/
export type InstanceClientOptions = {
/**
* The API URL for the instance.
*/
apiUrl: string;
/**
* The token to use for authentication.
*/
token: string;
/**
* Controls logging verbosity
* @default 'info'
*/
logLevel?: LogLevel;
/**
* Maximum number of reconnection attempts
* @default 6
*/
maxReconnectAttempts?: number;
/**
* Initial reconnection delay in milliseconds
* @default 1000
*/
reconnectDelay?: number;
/**
* Maximum reconnection delay in milliseconds
* @default 30000
*/
maxReconnectDelay?: number;
};
type ScreenshotRequest = {
type: 'screenshot';
id: string;
};
type ScreenshotResponse = {
type: 'screenshot';
dataUri: string;
id: string;
};
type ScreenshotData = {
dataUri: string;
};
type ScreenshotErrorResponse = {
type: 'screenshotError';
message: string;
id: string;
};
type SimctlRequest = {
type: 'simctl';
id: string;
args: string[];
};
type SimctlStreamResponse = {
type: 'simctlStream';
id: string;
stdout?: string; // base64 encoded
stderr?: string; // base64 encoded
exitCode?: number;
};
type SimctlErrorResponse = {
type: 'simctlError';
id: string;
message: string;
};
type ServerMessage =
| ScreenshotResponse
| ScreenshotErrorResponse
| SimctlStreamResponse
| SimctlErrorResponse
| { type: string; [key: string]: unknown };
/**
* Handle for a running simctl command execution.
*
* This class extends EventEmitter and provides streaming access to command output.
* Methods starting with underscore (_) are internal and should not be called directly.
*
* @example
* ```typescript
* const execution = client.simctl(['boot', 'device-id']);
*
* // Listen to raw output
* execution.on('stdout', (data) => console.log(data.toString()));
*
* // Or listen line-by-line (more convenient for most use cases)
* execution.on('line-stdout', (line) => console.log('Output:', line));
* execution.on('line-stderr', (line) => console.error('Error:', line));
*
* execution.on('exit', (code) => console.log('Exit code:', code));
* ```
*/
export class SimctlExecution extends EventEmitter {
private stdoutChunks: Buffer[] = [];
private stderrChunks: Buffer[] = [];
private stdoutLineBuffer = '';
private stderrLineBuffer = '';
private exitCodeValue: number | null = null;
private completed = false;
private waitPromise: Promise<{ code: number; stdout: Buffer; stderr: Buffer }> | null = null;
private stopCallback: (() => void) | null = null;
public get isRunning(): boolean {
return !this.completed;
}
constructor(stopCallback: () => void) {
super();
this.stopCallback = stopCallback;
}
/**
* Register an event listener for stdout, stderr, line-stdout, line-stderr, exit, or error events.
* @param event The event name
* @param listener The callback function for this event
*/
override on<E extends keyof SimctlExecutionEvents>(event: E, listener: SimctlExecutionEvents[E]): this {
return super.on(event, listener as any);
}
/**
* Register a one-time event listener that will be removed after firing once.
* @param event The event name
* @param listener The callback function for this event
*/
override once<E extends keyof SimctlExecutionEvents>(event: E, listener: SimctlExecutionEvents[E]): this {
return super.once(event, listener as any);
}
/**
* Remove an event listener.
* @param event The event name
* @param listener The callback function to remove
*/
override off<E extends keyof SimctlExecutionEvents>(event: E, listener: SimctlExecutionEvents[E]): this {
return super.off(event, listener as any);
}
/**
* Wait for the command to complete and get the full result.
* This accumulates all stdout/stderr chunks in memory.
* @returns Promise that resolves with exit code and complete output
*/
wait(): Promise<{ code: number; stdout: Buffer; stderr: Buffer }> {
if (this.waitPromise) {
return this.waitPromise;
}
this.waitPromise = new Promise((resolve, reject) => {
if (this.completed) {
resolve({
code: this.exitCodeValue!,
stdout: Buffer.concat(this.stdoutChunks),
stderr: Buffer.concat(this.stderrChunks),
});
return;
}
this.once('exit', (code) => {
resolve({
code,
stdout: Buffer.concat(this.stdoutChunks),
stderr: Buffer.concat(this.stderrChunks),
});
});
this.once('error', (error) => {
reject(error);
});
});
return this.waitPromise;
}
/**
* Stop the running simctl command (if supported by server).
* This cleans up the execution tracking.
*/
stop(): void {
if (this.stopCallback) {
this.stopCallback();
}
}
/** @internal - Handle stdout data from server */
_handleStdout(data: Buffer): void {
this.stdoutChunks.push(data);
this.emit('stdout', data);
// Process line-by-line
this.stdoutLineBuffer += data.toString('utf-8');
const lines = this.stdoutLineBuffer.split('\n');
// Keep the last incomplete line in the buffer
this.stdoutLineBuffer = lines.pop() || '';
// Emit complete lines
for (const line of lines) {
this.emit('line-stdout', line);
}
}
/** @internal - Handle stderr data from server */
_handleStderr(data: Buffer): void {
this.stderrChunks.push(data);
this.emit('stderr', data);
// Process line-by-line
this.stderrLineBuffer += data.toString('utf-8');
const lines = this.stderrLineBuffer.split('\n');
// Keep the last incomplete line in the buffer
this.stderrLineBuffer = lines.pop() || '';
// Emit complete lines
for (const line of lines) {
this.emit('line-stderr', line);
}
}
/** @internal - Handle exit code from server */
_handleExit(code: number): void {
// Emit any remaining partial lines before exit
if (this.stdoutLineBuffer) {
this.emit('line-stdout', this.stdoutLineBuffer);
this.stdoutLineBuffer = '';
}
if (this.stderrLineBuffer) {
this.emit('line-stderr', this.stderrLineBuffer);
this.stderrLineBuffer = '';
}
this.exitCodeValue = code;
this.completed = true;
this.emit('exit', code);
}
/** @internal - Handle errors from server or connection */
_handleError(error: Error): void {
this.completed = true;
this.emit('error', error);
}
}
/**
* Creates a client for interacting with a Limrun iOS instance
* @param options Configuration options including webrtcUrl, token and log level
* @returns An InstanceClient for controlling the instance
*/
export async function createInstanceClient(options: InstanceClientOptions): Promise<InstanceClient> {
const endpointWebSocketUrl = `${options.apiUrl}/endpointWebSocket?token=${options.token}`;
const logLevel = options.logLevel ?? 'info';
const maxReconnectAttempts = options.maxReconnectAttempts ?? 6;
const reconnectDelay = options.reconnectDelay ?? 1000;
const maxReconnectDelay = options.maxReconnectDelay ?? 30000;
let ws: WebSocket | undefined = undefined;
let connectionState: ConnectionState = 'connecting';
let reconnectAttempts = 0;
let reconnectTimeout: NodeJS.Timeout | undefined;
let intentionalDisconnect = false;
const screenshotRequests: Map<
string,
{
resolver: (value: ScreenshotData | PromiseLike<ScreenshotData>) => void;
rejecter: (reason?: any) => void;
}
> = new Map();
const simctlExecutions: Map<string, SimctlExecution> = new Map();
const stateChangeCallbacks: Set<ConnectionStateCallback> = new Set();
// Logger functions
const logger = {
debug: (...args: any[]) => {
if (logLevel === 'debug') console.log(...args);
},
info: (...args: any[]) => {
if (logLevel === 'info' || logLevel === 'debug') console.log(...args);
},
warn: (...args: any[]) => {
if (logLevel === 'warn' || logLevel === 'info' || logLevel === 'debug') console.warn(...args);
},
error: (...args: any[]) => {
if (logLevel !== 'none') console.error(...args);
},
};
const updateConnectionState = (newState: ConnectionState): void => {
if (connectionState !== newState) {
connectionState = newState;
logger.debug(`Connection state changed to: ${newState}`);
stateChangeCallbacks.forEach((callback) => {
try {
callback(newState);
} catch (err) {
logger.error('Error in connection state callback:', err);
}
});
}
};
const failPendingRequests = (reason: string): void => {
screenshotRequests.forEach((request) => request.rejecter(new Error(reason)));
screenshotRequests.clear();
simctlExecutions.forEach((execution) => execution._handleError(new Error(reason)));
simctlExecutions.clear();
};
const cleanup = (): void => {
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
reconnectTimeout = undefined;
}
if (pingInterval) {
clearInterval(pingInterval);
pingInterval = undefined;
}
if (ws) {
ws.removeAllListeners();
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
ws.close();
}
ws = undefined;
}
};
let pingInterval: NodeJS.Timeout | undefined;
return new Promise<InstanceClient>((resolveConnection, rejectConnection) => {
let hasResolved = false;
// Reconnection logic with exponential backoff
const scheduleReconnect = (): void => {
if (intentionalDisconnect) {
logger.debug('Skipping reconnection (intentional disconnect)');
return;
}
if (reconnectAttempts >= maxReconnectAttempts) {
logger.error(`Max reconnection attempts (${maxReconnectAttempts}) reached. Giving up.`);
updateConnectionState('disconnected');
return;
}
const currentDelay = Math.min(reconnectDelay * Math.pow(2, reconnectAttempts), maxReconnectDelay);
reconnectAttempts++;
logger.debug(`Scheduling reconnection attempt ${reconnectAttempts} in ${currentDelay}ms...`);
updateConnectionState('reconnecting');
reconnectTimeout = setTimeout(() => {
logger.debug(`Attempting to reconnect (attempt ${reconnectAttempts})...`);
setupWebSocket();
}, currentDelay);
};
const setupWebSocket = (): void => {
cleanup();
updateConnectionState('connecting');
ws = new WebSocket(endpointWebSocketUrl);
ws.on('message', (data: Data) => {
let message: ServerMessage;
try {
message = JSON.parse(data.toString());
} catch (e) {
logger.error({ data, error: e }, 'Failed to parse JSON message');
return;
}
switch (message.type) {
case 'screenshot': {
if (!('dataUri' in message) || typeof message.dataUri !== 'string' || !('id' in message)) {
logger.warn('Received invalid screenshot message:', message);
break;
}
const screenshotMessage = message as ScreenshotResponse;
const request = screenshotRequests.get(screenshotMessage.id);
if (!request) {
logger.warn(
`Received screenshot data for unknown or already handled session: ${screenshotMessage.id}`,
);
break;
}
logger.debug(`Received screenshot data URI for session ${screenshotMessage.id}.`);
request.resolver({ dataUri: screenshotMessage.dataUri });
screenshotRequests.delete(screenshotMessage.id);
break;
}
case 'screenshotError': {
if (!('message' in message) || !('id' in message)) {
logger.warn('Received invalid screenshot error message:', message);
break;
}
const errorMessage = message as ScreenshotErrorResponse;
const request = screenshotRequests.get(errorMessage.id);
if (!request) {
logger.warn(
`Received screenshot error for unknown or already handled session: ${errorMessage.id}`,
);
break;
}
logger.error(
`Server reported an error capturing screenshot for session ${errorMessage.id}:`,
errorMessage.message,
);
request.rejecter(new Error(errorMessage.message));
screenshotRequests.delete(errorMessage.id);
break;
}
case 'simctlStream': {
if (!('id' in message)) {
logger.warn('Received invalid simctl stream message:', message);
break;
}
const streamMessage = message as SimctlStreamResponse;
const execution = simctlExecutions.get(streamMessage.id);
if (!execution) {
logger.warn(
`Received simctl stream for unknown or already completed execution: ${streamMessage.id}`,
);
break;
}
// Handle stdout if present
if (streamMessage.stdout) {
try {
const stdoutBuffer = Buffer.from(streamMessage.stdout, 'base64');
execution._handleStdout(stdoutBuffer);
} catch (err) {
logger.error('Failed to decode stdout data:', err);
}
}
// Handle stderr if present
if (streamMessage.stderr) {
try {
const stderrBuffer = Buffer.from(streamMessage.stderr, 'base64');
execution._handleStderr(stderrBuffer);
} catch (err) {
logger.error('Failed to decode stderr data:', err);
}
}
// Handle exit code if present (final message)
if (streamMessage.exitCode !== undefined) {
logger.debug(
`Simctl execution ${streamMessage.id} completed with exit code ${streamMessage.exitCode}`,
);
execution._handleExit(streamMessage.exitCode);
simctlExecutions.delete(streamMessage.id);
}
break;
}
case 'simctlError': {
if (!('message' in message) || !('id' in message)) {
logger.warn('Received invalid simctl error message:', message);
break;
}
const errorMessage = message as SimctlErrorResponse;
const execution = simctlExecutions.get(errorMessage.id);
if (!execution) {
logger.warn(
`Received simctl error for unknown or already handled execution: ${errorMessage.id}`,
);
break;
}
logger.error(
`Server reported an error for simctl execution ${errorMessage.id}:`,
errorMessage.message,
);
execution._handleError(new Error(errorMessage.message));
simctlExecutions.delete(errorMessage.id);
break;
}
default:
logger.warn('Received unexpected message type:', message);
break;
}
});
ws.on('error', (err: Error) => {
logger.error('WebSocket error:', err.message);
if (!hasResolved && (ws?.readyState === WebSocket.CONNECTING || ws?.readyState === WebSocket.OPEN)) {
rejectConnection(err);
}
});
ws.on('close', () => {
if (pingInterval) {
clearInterval(pingInterval);
pingInterval = undefined;
}
const shouldReconnect = !intentionalDisconnect && connectionState !== 'disconnected';
updateConnectionState('disconnected');
logger.debug('Disconnected from server.');
failPendingRequests('Connection closed');
if (shouldReconnect) {
scheduleReconnect();
}
});
ws.on('open', () => {
logger.debug(`Connected to ${endpointWebSocketUrl}`);
reconnectAttempts = 0;
updateConnectionState('connected');
pingInterval = setInterval(() => {
if (ws && ws.readyState === WebSocket.OPEN) {
(ws as any).ping();
}
}, 30_000);
if (!hasResolved) {
hasResolved = true;
resolveConnection({
screenshot,
disconnect,
getConnectionState,
onConnectionStateChange,
simctl,
cp,
});
}
});
};
const screenshot = async (): Promise<ScreenshotData> => {
if (!ws || ws.readyState !== WebSocket.OPEN) {
return Promise.reject(new Error('WebSocket is not connected or connection is not open.'));
}
const id = 'ts-client-' + Date.now();
const screenshotRequest: ScreenshotRequest = {
type: 'screenshot',
id,
};
return new Promise<ScreenshotData>((resolve, reject) => {
logger.debug('Sending screenshot request:', screenshotRequest);
ws!.send(JSON.stringify(screenshotRequest), (err?: Error) => {
if (err) {
logger.error('Failed to send screenshot request:', err);
reject(err);
}
});
const timeout = setTimeout(() => {
if (screenshotRequests.has(id)) {
logger.error(`Screenshot request timed out for session ${id}`);
screenshotRequests.get(id)?.rejecter(new Error('Screenshot request timed out'));
screenshotRequests.delete(id);
}
}, 30000);
screenshotRequests.set(id, {
resolver: (value: ScreenshotData | PromiseLike<ScreenshotData>) => {
clearTimeout(timeout);
resolve(value);
screenshotRequests.delete(id);
},
rejecter: (reason?: any) => {
clearTimeout(timeout);
reject(reason);
screenshotRequests.delete(id);
},
});
});
};
const simctl = (args: string[]): SimctlExecution => {
const id = 'ts-simctl-' + Date.now() + '-' + Math.random().toString(36).substring(7);
const cancelCallback = () => {
// Clean up execution tracking
simctlExecutions.delete(id);
logger.debug(`Simctl execution ${id} cancelled`);
};
const execution = new SimctlExecution(cancelCallback);
simctlExecutions.set(id, execution);
// Send request asynchronously
if (!ws || ws.readyState !== WebSocket.OPEN) {
// Defer error to next tick to allow caller to attach listeners
process.nextTick(() => {
execution._handleError(new Error('WebSocket is not connected or connection is not open.'));
simctlExecutions.delete(id);
});
return execution;
}
const simctlRequest: SimctlRequest = {
type: 'simctl',
id,
args,
};
logger.debug('Sending simctl request:', simctlRequest);
ws.send(JSON.stringify(simctlRequest), (err?: Error) => {
if (err) {
logger.error('Failed to send simctl request:', err);
execution._handleError(err);
simctlExecutions.delete(id);
}
});
return execution;
};
const cp = async (name: string, filePath: string): Promise<string> => {
const fileStream = fs.createReadStream(filePath);
const uploadUrl = `${options.apiUrl}/files?name=${encodeURIComponent(name)}`;
try {
const response = await fetch(uploadUrl, {
method: 'PUT',
headers: {
'Content-Type': 'application/octet-stream',
'Content-Length': fs.statSync(filePath).size.toString(),
Authorization: `Bearer ${options.token}`,
},
body: fileStream,
duplex: 'half',
});
if (!response.ok) {
const errorBody = await response.text();
logger.debug(`Upload failed: ${response.status} ${errorBody}`);
throw new Error(`Upload failed: ${response.status} ${errorBody}`);
}
const result = (await response.json()) as { path: string };
return result.path;
} catch (err) {
logger.debug(`Failed to upload file ${filePath}:`, err);
throw err;
}
};
const disconnect = (): void => {
intentionalDisconnect = true;
cleanup();
updateConnectionState('disconnected');
failPendingRequests('Intentional disconnect');
logger.debug('Intentionally disconnected from WebSocket.');
};
const getConnectionState = (): ConnectionState => {
return connectionState;
};
const onConnectionStateChange = (callback: ConnectionStateCallback): (() => void) => {
stateChangeCallbacks.add(callback);
return () => {
stateChangeCallbacks.delete(callback);
};
};
setupWebSocket();
});
}
+1
-0

@@ -7,3 +7,4 @@ export { Limrun as default } from "./client.mjs";

export * from "./instance-client.mjs";
export * as Ios from "./ios-client.mjs";
export { LimrunError, APIError, APIConnectionError, APIConnectionTimeoutError, APIUserAbortError, NotFoundError, ConflictError, RateLimitError, BadRequestError, AuthenticationError, InternalServerError, PermissionDeniedError, UnprocessableEntityError, } from "./core/error.mjs";
//# sourceMappingURL=index.d.mts.map
+1
-1

@@ -1,1 +0,1 @@

{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"OAEO,EAAE,MAAM,IAAI,OAAO,EAAE;OAErB,EAAE,KAAK,UAAU,EAAE,MAAM,EAAE;OAC3B,EAAE,UAAU,EAAE;OACd,EAAE,MAAM,EAAE,KAAK,aAAa,EAAE;OAC9B,EAAE,WAAW,EAAE;;OAEf,EACL,WAAW,EACX,QAAQ,EACR,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,wBAAwB,GACzB"}
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"OAEO,EAAE,MAAM,IAAI,OAAO,EAAE;OAErB,EAAE,KAAK,UAAU,EAAE,MAAM,EAAE;OAC3B,EAAE,UAAU,EAAE;OACd,EAAE,MAAM,EAAE,KAAK,aAAa,EAAE;OAC9B,EAAE,WAAW,EAAE;;OAEf,KAAK,GAAG;OACR,EACL,WAAW,EACX,QAAQ,EACR,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,wBAAwB,GACzB"}

@@ -7,3 +7,4 @@ export { Limrun as default } from "./client.js";

export * from "./instance-client.js";
export * as Ios from "./ios-client.js";
export { LimrunError, APIError, APIConnectionError, APIConnectionTimeoutError, APIUserAbortError, NotFoundError, ConflictError, RateLimitError, BadRequestError, AuthenticationError, InternalServerError, PermissionDeniedError, UnprocessableEntityError, } from "./core/error.js";
//# sourceMappingURL=index.d.ts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"OAEO,EAAE,MAAM,IAAI,OAAO,EAAE;OAErB,EAAE,KAAK,UAAU,EAAE,MAAM,EAAE;OAC3B,EAAE,UAAU,EAAE;OACd,EAAE,MAAM,EAAE,KAAK,aAAa,EAAE;OAC9B,EAAE,WAAW,EAAE;;OAEf,EACL,WAAW,EACX,QAAQ,EACR,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,wBAAwB,GACzB"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"OAEO,EAAE,MAAM,IAAI,OAAO,EAAE;OAErB,EAAE,KAAK,UAAU,EAAE,MAAM,EAAE;OAC3B,EAAE,UAAU,EAAE;OACd,EAAE,MAAM,EAAE,KAAK,aAAa,EAAE;OAC9B,EAAE,WAAW,EAAE;;OAEf,KAAK,GAAG;OACR,EACL,WAAW,EACX,QAAQ,EACR,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,wBAAwB,GACzB"}

@@ -7,3 +7,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.UnprocessableEntityError = exports.PermissionDeniedError = exports.InternalServerError = exports.AuthenticationError = exports.BadRequestError = exports.RateLimitError = exports.ConflictError = exports.NotFoundError = exports.APIUserAbortError = exports.APIConnectionTimeoutError = exports.APIConnectionError = exports.APIError = exports.LimrunError = exports.PagePromise = exports.Limrun = exports.APIPromise = exports.toFile = exports.default = void 0;
exports.UnprocessableEntityError = exports.PermissionDeniedError = exports.InternalServerError = exports.AuthenticationError = exports.BadRequestError = exports.RateLimitError = exports.ConflictError = exports.NotFoundError = exports.APIUserAbortError = exports.APIConnectionTimeoutError = exports.APIConnectionError = exports.APIError = exports.LimrunError = exports.Ios = exports.PagePromise = exports.Limrun = exports.APIPromise = exports.toFile = exports.default = void 0;
const tslib_1 = require("./internal/tslib.js");

@@ -21,2 +21,3 @@ var client_1 = require("./client.js");

tslib_1.__exportStar(require("./instance-client.js"), exports);
exports.Ios = tslib_1.__importStar(require("./ios-client.js"));
var error_1 = require("./core/error.js");

@@ -23,0 +24,0 @@ Object.defineProperty(exports, "LimrunError", { enumerable: true, get: function () { return error_1.LimrunError; } });

@@ -1,1 +0,1 @@

{"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":";AAAA,sFAAsF;;;;AAEtF,sCAA6C;AAApC,iGAAA,MAAM,OAAW;AAE1B,6CAAyD;AAA/B,iGAAA,MAAM,OAAA;AAChC,qDAAgD;AAAvC,yGAAA,UAAU,OAAA;AACnB,sCAAsD;AAA7C,gGAAA,MAAM,OAAA;AACf,mDAAgD;AAAvC,yGAAA,WAAW,OAAA;AACpB,+DAAkC;AAClC,yCAcsB;AAbpB,oGAAA,WAAW,OAAA;AACX,iGAAA,QAAQ,OAAA;AACR,2GAAA,kBAAkB,OAAA;AAClB,kHAAA,yBAAyB,OAAA;AACzB,0GAAA,iBAAiB,OAAA;AACjB,sGAAA,aAAa,OAAA;AACb,sGAAA,aAAa,OAAA;AACb,uGAAA,cAAc,OAAA;AACd,wGAAA,eAAe,OAAA;AACf,4GAAA,mBAAmB,OAAA;AACnB,4GAAA,mBAAmB,OAAA;AACnB,8GAAA,qBAAqB,OAAA;AACrB,iHAAA,wBAAwB,OAAA"}
{"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":";AAAA,sFAAsF;;;;AAEtF,sCAA6C;AAApC,iGAAA,MAAM,OAAW;AAE1B,6CAAyD;AAA/B,iGAAA,MAAM,OAAA;AAChC,qDAAgD;AAAvC,yGAAA,UAAU,OAAA;AACnB,sCAAsD;AAA7C,gGAAA,MAAM,OAAA;AACf,mDAAgD;AAAvC,yGAAA,WAAW,OAAA;AACpB,+DAAkC;AAClC,+DAAoC;AACpC,yCAcsB;AAbpB,oGAAA,WAAW,OAAA;AACX,iGAAA,QAAQ,OAAA;AACR,2GAAA,kBAAkB,OAAA;AAClB,kHAAA,yBAAyB,OAAA;AACzB,0GAAA,iBAAiB,OAAA;AACjB,sGAAA,aAAa,OAAA;AACb,sGAAA,aAAa,OAAA;AACb,uGAAA,cAAc,OAAA;AACd,wGAAA,eAAe,OAAA;AACf,4GAAA,mBAAmB,OAAA;AACnB,4GAAA,mBAAmB,OAAA;AACnB,8GAAA,qBAAqB,OAAA;AACrB,iHAAA,wBAAwB,OAAA"}

@@ -8,3 +8,4 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

export * from "./instance-client.mjs";
export * as Ios from "./ios-client.mjs";
export { LimrunError, APIError, APIConnectionError, APIConnectionTimeoutError, APIUserAbortError, NotFoundError, ConflictError, RateLimitError, BadRequestError, AuthenticationError, InternalServerError, PermissionDeniedError, UnprocessableEntityError, } from "./core/error.mjs";
//# sourceMappingURL=index.mjs.map

@@ -1,1 +0,1 @@

{"version":3,"file":"index.mjs","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA,sFAAsF;OAE/E,EAAE,MAAM,IAAI,OAAO,EAAE;OAErB,EAAmB,MAAM,EAAE;OAC3B,EAAE,UAAU,EAAE;OACd,EAAE,MAAM,EAAsB;OAC9B,EAAE,WAAW,EAAE;;OAEf,EACL,WAAW,EACX,QAAQ,EACR,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,wBAAwB,GACzB"}
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA,sFAAsF;OAE/E,EAAE,MAAM,IAAI,OAAO,EAAE;OAErB,EAAmB,MAAM,EAAE;OAC3B,EAAE,UAAU,EAAE;OACd,EAAE,MAAM,EAAsB;OAC9B,EAAE,WAAW,EAAE;;OAEf,KAAK,GAAG;OACR,EACL,WAAW,EACX,QAAQ,EACR,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,wBAAwB,GACzB"}

@@ -8,2 +8,3 @@ "use strict";

exports.__exportStar = __exportStar;
exports.__importDefault = __importDefault;
function __classPrivateFieldSet(receiver, state, value, kind, f) {

@@ -83,1 +84,4 @@ if (kind === "m")

}
function __importDefault(mod) {
return mod && mod.__esModule ? mod : { default: mod };
}
{
"name": "@limrun/api",
"version": "0.16.0",
"version": "0.17.0-rc.1",
"description": "The official TypeScript library for the Limrun API",

@@ -99,2 +99,12 @@ "author": "Limrun <contact@limrun.com>",

},
"./ios-client": {
"import": "./ios-client.mjs",
"require": "./ios-client.js"
},
"./ios-client.js": {
"default": "./ios-client.js"
},
"./ios-client.mjs": {
"default": "./ios-client.mjs"
},
"./pagination": {

@@ -101,0 +111,0 @@ "import": "./pagination.mjs",

@@ -10,2 +10,3 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

export * from './instance-client';
export * as Ios from './ios-client';
export {

@@ -12,0 +13,0 @@ LimrunError,

@@ -1,1 +0,1 @@

export const VERSION = '0.16.0'; // x-release-please-version
export const VERSION = '0.17.0-rc.1'; // x-release-please-version

@@ -1,2 +0,2 @@

export declare const VERSION = "0.16.0";
export declare const VERSION = "0.17.0-rc.1";
//# sourceMappingURL=version.d.mts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"version.d.mts","sourceRoot":"","sources":["src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,WAAW,CAAC"}
{"version":3,"file":"version.d.mts","sourceRoot":"","sources":["src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,gBAAgB,CAAC"}

@@ -1,2 +0,2 @@

export declare const VERSION = "0.16.0";
export declare const VERSION = "0.17.0-rc.1";
//# sourceMappingURL=version.d.ts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,WAAW,CAAC"}
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,gBAAgB,CAAC"}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.VERSION = void 0;
exports.VERSION = '0.16.0'; // x-release-please-version
exports.VERSION = '0.17.0-rc.1'; // x-release-please-version
//# sourceMappingURL=version.js.map

@@ -1,1 +0,1 @@

{"version":3,"file":"version.js","sourceRoot":"","sources":["src/version.ts"],"names":[],"mappings":";;;AAAa,QAAA,OAAO,GAAG,QAAQ,CAAC,CAAC,2BAA2B"}
{"version":3,"file":"version.js","sourceRoot":"","sources":["src/version.ts"],"names":[],"mappings":";;;AAAa,QAAA,OAAO,GAAG,aAAa,CAAC,CAAC,2BAA2B"}

@@ -1,2 +0,2 @@

export const VERSION = '0.16.0'; // x-release-please-version
export const VERSION = '0.17.0-rc.1'; // x-release-please-version
//# sourceMappingURL=version.mjs.map

@@ -1,1 +0,1 @@

{"version":3,"file":"version.mjs","sourceRoot":"","sources":["src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,2BAA2B"}
{"version":3,"file":"version.mjs","sourceRoot":"","sources":["src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC,CAAC,2BAA2B"}