feat(client): subscribe to status (#76)
* feat(client): subscribe to status * fix(client): timeout id type
This commit is contained in:
parent
903af74da1
commit
d9ea6c7dd3
@ -1,3 +1,3 @@
|
|||||||
import { route } from '@fal-ai/serverless-proxy/nextjs';
|
import { route } from '@fal-ai/serverless-proxy/nextjs';
|
||||||
|
|
||||||
export const { GET, POST } = route;
|
export const { GET, POST, PUT } = route;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@fal-ai/serverless-client",
|
"name": "@fal-ai/serverless-client",
|
||||||
"description": "The fal serverless JS/TS client",
|
"description": "The fal serverless JS/TS client",
|
||||||
"version": "0.14.0-alpha.2",
|
"version": "0.14.0-alpha.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@ -2,7 +2,12 @@ import { getTemporaryAuthToken } from './auth';
|
|||||||
import { dispatchRequest } from './request';
|
import { dispatchRequest } from './request';
|
||||||
import { storageImpl } from './storage';
|
import { storageImpl } from './storage';
|
||||||
import { FalStream } from './streaming';
|
import { FalStream } from './streaming';
|
||||||
import { EnqueueResult, QueueStatus, RequestLog } from './types';
|
import {
|
||||||
|
CompletedQueueStatus,
|
||||||
|
EnqueueResult,
|
||||||
|
QueueStatus,
|
||||||
|
RequestLog,
|
||||||
|
} from './types';
|
||||||
import { ensureAppIdFormat, isUUIDv4, isValidUrl, parseAppId } from './utils';
|
import { ensureAppIdFormat, isUUIDv4, isValidUrl, parseAppId } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,6 +115,9 @@ export async function send<Input, Output>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type QueueStatusSubscriptionOptions = QueueStatusOptions &
|
||||||
|
Omit<QueueSubscribeOptions, 'onEnqueue' | 'webhookUrl'>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs a fal serverless function identified by its `id`.
|
* Runs a fal serverless function identified by its `id`.
|
||||||
*
|
*
|
||||||
@ -123,93 +131,10 @@ export async function run<Input, Output>(
|
|||||||
return send(id, options);
|
return send(id, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
type TimeoutId = ReturnType<typeof setTimeout>;
|
type TimeoutId = ReturnType<typeof setTimeout> | undefined;
|
||||||
|
|
||||||
const DEFAULT_POLL_INTERVAL = 500;
|
const DEFAULT_POLL_INTERVAL = 500;
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscribes to updates for a specific request in the queue.
|
|
||||||
*
|
|
||||||
* @param id - The ID or URL of the function web endpoint.
|
|
||||||
* @param options - Options to configure how the request is run and how updates are received.
|
|
||||||
* @returns A promise that resolves to the result of the request once it's completed.
|
|
||||||
*/
|
|
||||||
export async function subscribe<Input, Output>(
|
|
||||||
id: string,
|
|
||||||
options: RunOptions<Input> & QueueSubscribeOptions = {}
|
|
||||||
): Promise<Output> {
|
|
||||||
const { request_id: requestId } = await queue.submit(id, options);
|
|
||||||
if (options.onEnqueue) {
|
|
||||||
options.onEnqueue(requestId);
|
|
||||||
}
|
|
||||||
const timeout = options.timeout;
|
|
||||||
let timeoutId: TimeoutId = undefined;
|
|
||||||
if (timeout) {
|
|
||||||
timeoutId = setTimeout(() => {
|
|
||||||
queue.cancel(id, { requestId }).catch(console.warn);
|
|
||||||
throw new Error(
|
|
||||||
`Client timed out waiting for the request to complete after ${timeout}ms`
|
|
||||||
);
|
|
||||||
}, timeout);
|
|
||||||
}
|
|
||||||
if (options.mode === 'streaming') {
|
|
||||||
const status = await queue.streamStatus(id, {
|
|
||||||
requestId,
|
|
||||||
logs: options.logs,
|
|
||||||
});
|
|
||||||
const logs: RequestLog[] = [];
|
|
||||||
status.on('message', (data: QueueStatus) => {
|
|
||||||
if (options.onQueueUpdate) {
|
|
||||||
// accumulate logs to match previous polling behavior
|
|
||||||
if (
|
|
||||||
'logs' in data &&
|
|
||||||
Array.isArray(data.logs) &&
|
|
||||||
data.logs.length > 0
|
|
||||||
) {
|
|
||||||
logs.push(...data.logs);
|
|
||||||
}
|
|
||||||
options.onQueueUpdate('logs' in data ? { ...data, logs } : data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await status.done();
|
|
||||||
if (timeoutId) {
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
}
|
|
||||||
return queue.result<Output>(id, { requestId });
|
|
||||||
}
|
|
||||||
// default to polling until status streaming is stable and faster
|
|
||||||
return new Promise<Output>((resolve, reject) => {
|
|
||||||
let timeoutId: ReturnType<typeof setTimeout>;
|
|
||||||
const pollInterval = options.pollInterval ?? DEFAULT_POLL_INTERVAL;
|
|
||||||
const poll = async () => {
|
|
||||||
try {
|
|
||||||
const requestStatus = await queue.status(id, {
|
|
||||||
requestId,
|
|
||||||
logs: options.logs ?? false,
|
|
||||||
});
|
|
||||||
if (options.onQueueUpdate) {
|
|
||||||
options.onQueueUpdate(requestStatus);
|
|
||||||
}
|
|
||||||
if (requestStatus.status === 'COMPLETED') {
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
try {
|
|
||||||
const result = await queue.result<Output>(id, { requestId });
|
|
||||||
resolve(result);
|
|
||||||
} catch (error) {
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
timeoutId = setTimeout(poll, pollInterval);
|
|
||||||
} catch (error) {
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
poll().catch(reject);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options for subscribing to the request queue.
|
* Options for subscribing to the request queue.
|
||||||
*/
|
*/
|
||||||
@ -247,6 +172,10 @@ type QueueSubscribeOptions = {
|
|||||||
* The timeout (in milliseconds) for the request. If the request is not
|
* The timeout (in milliseconds) for the request. If the request is not
|
||||||
* completed within this time, the subscription will be cancelled.
|
* completed within this time, the subscription will be cancelled.
|
||||||
*
|
*
|
||||||
|
* Keep in mind that although the client resolves the function on a timeout,
|
||||||
|
* and will try to cancel the request on the server, the server might not be
|
||||||
|
* able to cancel the request if it's already running.
|
||||||
|
*
|
||||||
* Note: currently, the timeout is not enforced and the default is `undefined`.
|
* Note: currently, the timeout is not enforced and the default is `undefined`.
|
||||||
* This behavior might change in the future.
|
* This behavior might change in the future.
|
||||||
*/
|
*/
|
||||||
@ -325,6 +254,31 @@ interface Queue {
|
|||||||
*/
|
*/
|
||||||
status(endpointId: string, options: QueueStatusOptions): Promise<QueueStatus>;
|
status(endpointId: string, options: QueueStatusOptions): Promise<QueueStatus>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribes to updates for a specific request in the queue using HTTP streaming events.
|
||||||
|
*
|
||||||
|
* @param endpointId - The ID of the function web endpoint.
|
||||||
|
* @param options - Options to configure how the request is run and how updates are received.
|
||||||
|
* @returns The streaming object that can be used to listen for updates.
|
||||||
|
*/
|
||||||
|
streamStatus(
|
||||||
|
endpointId: string,
|
||||||
|
options: QueueStatusOptions
|
||||||
|
): Promise<FalStream<unknown, QueueStatus>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribes to updates for a specific request in the queue using polling or streaming.
|
||||||
|
* See `options.mode` for more details.
|
||||||
|
*
|
||||||
|
* @param endpointId - The ID of the function web endpoint.
|
||||||
|
* @param options - Options to configure how the request is run and how updates are received.
|
||||||
|
* @returns A promise that resolves to the final status of the request.
|
||||||
|
*/
|
||||||
|
subscribeToStatus(
|
||||||
|
endpointId: string,
|
||||||
|
options: QueueStatusSubscriptionOptions
|
||||||
|
): Promise<CompletedQueueStatus>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the result of a specific request from the queue.
|
* Retrieves the result of a specific request from the queue.
|
||||||
*
|
*
|
||||||
@ -337,25 +291,6 @@ interface Queue {
|
|||||||
options: BaseQueueOptions
|
options: BaseQueueOptions
|
||||||
): Promise<Output>;
|
): Promise<Output>;
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use `fal.subscribe` instead.
|
|
||||||
*/
|
|
||||||
subscribe<Input, Output>(
|
|
||||||
endpointId: string,
|
|
||||||
options: RunOptions<Input> & QueueSubscribeOptions
|
|
||||||
): Promise<Output>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscribes to updates for a specific request in the queue.
|
|
||||||
*
|
|
||||||
* @param endpointId - The ID of the function web endpoint.
|
|
||||||
* @param options - Options to configure how the request is run and how updates are received.
|
|
||||||
*/
|
|
||||||
streamStatus(
|
|
||||||
endpointId: string,
|
|
||||||
options: QueueStatusOptions
|
|
||||||
): Promise<FalStream<unknown, QueueStatus>>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels a request in the queue.
|
* Cancels a request in the queue.
|
||||||
*
|
*
|
||||||
@ -402,6 +337,7 @@ export const queue: Queue = {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async streamStatus(
|
async streamStatus(
|
||||||
endpointId: string,
|
endpointId: string,
|
||||||
{ requestId, logs = false }: QueueStatusOptions
|
{ requestId, logs = false }: QueueStatusOptions
|
||||||
@ -424,6 +360,108 @@ export const queue: Queue = {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async subscribeToStatus(endpointId, options): Promise<CompletedQueueStatus> {
|
||||||
|
const requestId = options.requestId;
|
||||||
|
const timeout = options.timeout;
|
||||||
|
let timeoutId: TimeoutId = undefined;
|
||||||
|
|
||||||
|
const handleCancelError = () => {
|
||||||
|
// Ignore errors as the client will follow through with the timeout
|
||||||
|
// regardless of the server response. In case cancelation fails, we
|
||||||
|
// still want to reject the promise and consider the client call canceled.
|
||||||
|
};
|
||||||
|
if (options.mode === 'streaming') {
|
||||||
|
const status = await queue.streamStatus(endpointId, {
|
||||||
|
requestId,
|
||||||
|
logs: options.logs,
|
||||||
|
});
|
||||||
|
const logs: RequestLog[] = [];
|
||||||
|
if (timeout) {
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
|
status.abort();
|
||||||
|
queue.cancel(endpointId, { requestId }).catch(handleCancelError);
|
||||||
|
// TODO this error cannot bubble up to the user since it's thrown in
|
||||||
|
// a closure in the global scope due to setTimeout behavior.
|
||||||
|
// User will get a platform error instead. We should find a way to
|
||||||
|
// make this behavior aligned with polling.
|
||||||
|
throw new Error(
|
||||||
|
`Client timed out waiting for the request to complete after ${timeout}ms`
|
||||||
|
);
|
||||||
|
}, timeout);
|
||||||
|
}
|
||||||
|
status.on('message', (data: QueueStatus) => {
|
||||||
|
if (options.onQueueUpdate) {
|
||||||
|
// accumulate logs to match previous polling behavior
|
||||||
|
if (
|
||||||
|
'logs' in data &&
|
||||||
|
Array.isArray(data.logs) &&
|
||||||
|
data.logs.length > 0
|
||||||
|
) {
|
||||||
|
logs.push(...data.logs);
|
||||||
|
}
|
||||||
|
options.onQueueUpdate('logs' in data ? { ...data, logs } : data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const doneStatus = await status.done();
|
||||||
|
if (timeoutId) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
return doneStatus as CompletedQueueStatus;
|
||||||
|
}
|
||||||
|
// default to polling until status streaming is stable and faster
|
||||||
|
return new Promise<CompletedQueueStatus>((resolve, reject) => {
|
||||||
|
let pollingTimeoutId: TimeoutId;
|
||||||
|
// type resolution isn't great in this case, so check for its presence
|
||||||
|
// and and type so the typechecker behaves as expected
|
||||||
|
const pollInterval =
|
||||||
|
'pollInterval' in options && typeof options.pollInterval === 'number'
|
||||||
|
? options.pollInterval ?? DEFAULT_POLL_INTERVAL
|
||||||
|
: DEFAULT_POLL_INTERVAL;
|
||||||
|
|
||||||
|
const clearScheduledTasks = () => {
|
||||||
|
if (timeoutId) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
if (pollingTimeoutId) {
|
||||||
|
clearTimeout(pollingTimeoutId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (timeout) {
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
|
clearScheduledTasks();
|
||||||
|
queue.cancel(endpointId, { requestId }).catch(handleCancelError);
|
||||||
|
reject(
|
||||||
|
new Error(
|
||||||
|
`Client timed out waiting for the request to complete after ${timeout}ms`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, timeout);
|
||||||
|
}
|
||||||
|
const poll = async () => {
|
||||||
|
try {
|
||||||
|
const requestStatus = await queue.status(endpointId, {
|
||||||
|
requestId,
|
||||||
|
logs: options.logs ?? false,
|
||||||
|
});
|
||||||
|
if (options.onQueueUpdate) {
|
||||||
|
options.onQueueUpdate(requestStatus);
|
||||||
|
}
|
||||||
|
if (requestStatus.status === 'COMPLETED') {
|
||||||
|
clearScheduledTasks();
|
||||||
|
resolve(requestStatus);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pollingTimeoutId = setTimeout(poll, pollInterval);
|
||||||
|
} catch (error) {
|
||||||
|
clearScheduledTasks();
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
poll().catch(reject);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
async result<Output>(
|
async result<Output>(
|
||||||
endpointId: string,
|
endpointId: string,
|
||||||
{ requestId }: BaseQueueOptions
|
{ requestId }: BaseQueueOptions
|
||||||
@ -436,6 +474,7 @@ export const queue: Queue = {
|
|||||||
path: `/requests/${requestId}`,
|
path: `/requests/${requestId}`,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async cancel(
|
async cancel(
|
||||||
endpointId: string,
|
endpointId: string,
|
||||||
{ requestId }: BaseQueueOptions
|
{ requestId }: BaseQueueOptions
|
||||||
@ -448,5 +487,23 @@ export const queue: Queue = {
|
|||||||
path: `/requests/${requestId}/cancel`,
|
path: `/requests/${requestId}/cancel`,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
subscribe,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribes to updates for a specific request in the queue.
|
||||||
|
*
|
||||||
|
* @param endpointId - The ID of the function web endpoint.
|
||||||
|
* @param options - Options to configure how the request is run and how updates are received.
|
||||||
|
* @returns A promise that resolves to the result of the request once it's completed.
|
||||||
|
*/
|
||||||
|
export async function subscribe<Input, Output>(
|
||||||
|
endpointId: string,
|
||||||
|
options: RunOptions<Input> & QueueSubscribeOptions = {}
|
||||||
|
): Promise<Output> {
|
||||||
|
const { request_id: requestId } = await queue.submit(endpointId, options);
|
||||||
|
if (options.onEnqueue) {
|
||||||
|
options.onEnqueue(requestId);
|
||||||
|
}
|
||||||
|
await queue.subscribeToStatus(endpointId, { requestId, ...options });
|
||||||
|
return queue.result(endpointId, { requestId });
|
||||||
|
}
|
||||||
|
|||||||
@ -56,6 +56,8 @@ export class FalStream<Input, Output> {
|
|||||||
private streamClosed = false;
|
private streamClosed = false;
|
||||||
private donePromise: Promise<Output>;
|
private donePromise: Promise<Output>;
|
||||||
|
|
||||||
|
private abortController = new AbortController();
|
||||||
|
|
||||||
constructor(url: string, options: StreamOptions<Input>) {
|
constructor(url: string, options: StreamOptions<Input>) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
@ -93,6 +95,7 @@ export class FalStream<Input, Output> {
|
|||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
},
|
},
|
||||||
body: input && method !== 'get' ? JSON.stringify(input) : undefined,
|
body: input && method !== 'get' ? JSON.stringify(input) : undefined,
|
||||||
|
signal: this.abortController.signal,
|
||||||
});
|
});
|
||||||
this.handleResponse(response);
|
this.handleResponse(response);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -225,6 +228,13 @@ export class FalStream<Input, Output> {
|
|||||||
* @returns the promise that resolves when the request is done.
|
* @returns the promise that resolves when the request is done.
|
||||||
*/
|
*/
|
||||||
public done = async () => this.donePromise;
|
public done = async () => this.donePromise;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aborts the streaming request.
|
||||||
|
*/
|
||||||
|
public abort = () => {
|
||||||
|
this.abortController.abort();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -17,28 +17,42 @@ export type Metrics = {
|
|||||||
inference_time: number | null;
|
inference_time: number | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type QueueStatus =
|
interface BaseQueueStatus {
|
||||||
| {
|
status: 'IN_PROGRESS' | 'COMPLETED' | 'IN_QUEUE';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InProgressQueueStatus extends BaseQueueStatus {
|
||||||
status: 'IN_PROGRESS';
|
status: 'IN_PROGRESS';
|
||||||
response_url: string;
|
response_url: string;
|
||||||
logs: null | RequestLog[];
|
logs: RequestLog[];
|
||||||
}
|
}
|
||||||
| {
|
|
||||||
|
export interface CompletedQueueStatus extends BaseQueueStatus {
|
||||||
status: 'COMPLETED';
|
status: 'COMPLETED';
|
||||||
response_url: string;
|
response_url: string;
|
||||||
logs: null | RequestLog[];
|
logs: RequestLog[];
|
||||||
metrics: Metrics;
|
metrics: Metrics;
|
||||||
}
|
}
|
||||||
| {
|
|
||||||
|
export interface EnqueuedQueueStatus extends BaseQueueStatus {
|
||||||
status: 'IN_QUEUE';
|
status: 'IN_QUEUE';
|
||||||
queue_position: number;
|
queue_position: number;
|
||||||
response_url: string;
|
response_url: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export type QueueStatus =
|
||||||
|
| InProgressQueueStatus
|
||||||
|
| CompletedQueueStatus
|
||||||
|
| EnqueuedQueueStatus;
|
||||||
|
|
||||||
export function isQueueStatus(obj: any): obj is QueueStatus {
|
export function isQueueStatus(obj: any): obj is QueueStatus {
|
||||||
return obj && obj.status && obj.response_url;
|
return obj && obj.status && obj.response_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isCompletedQueueStatus(obj: any): obj is CompletedQueueStatus {
|
||||||
|
return isQueueStatus(obj) && obj.status === 'COMPLETED';
|
||||||
|
}
|
||||||
|
|
||||||
export type ValidationErrorInfo = {
|
export type ValidationErrorInfo = {
|
||||||
msg: string;
|
msg: string;
|
||||||
loc: Array<string | number>;
|
loc: Array<string | number>;
|
||||||
|
|||||||
34
package-lock.json
generated
34
package-lock.json
generated
@ -105,7 +105,7 @@
|
|||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"ts-protoc-gen": "^0.15.0",
|
"ts-protoc-gen": "^0.15.0",
|
||||||
"tsconfig-paths": "^4.2.0",
|
"tsconfig-paths": "^4.2.0",
|
||||||
"typescript": "5.1.6"
|
"typescript": "^5.5.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aashutoshrathi/word-wrap": {
|
"node_modules/@aashutoshrathi/word-wrap": {
|
||||||
@ -2453,19 +2453,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@commitlint/load/node_modules/typescript": {
|
|
||||||
"version": "5.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
|
||||||
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
|
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
|
||||||
"tsc": "bin/tsc",
|
|
||||||
"tsserver": "bin/tsserver"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.17"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@commitlint/message": {
|
"node_modules/@commitlint/message": {
|
||||||
"version": "17.8.1",
|
"version": "17.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.8.1.tgz",
|
||||||
@ -5558,6 +5545,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@nx/linter/node_modules/typescript": {
|
||||||
|
"version": "5.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
|
||||||
|
"integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nx/next": {
|
"node_modules/@nx/next": {
|
||||||
"version": "16.10.0",
|
"version": "16.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@nx/next/-/next-16.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@nx/next/-/next-16.10.0.tgz",
|
||||||
@ -29617,9 +29617,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.1.6",
|
"version": "5.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
|
||||||
"integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
|
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|||||||
@ -121,6 +121,6 @@
|
|||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"ts-protoc-gen": "^0.15.0",
|
"ts-protoc-gen": "^0.15.0",
|
||||||
"tsconfig-paths": "^4.2.0",
|
"tsconfig-paths": "^4.2.0",
|
||||||
"typescript": "5.1.6"
|
"typescript": "^5.5.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user