bringing back the background worker stuff
This commit is contained in:
@@ -21,11 +21,9 @@ import {
|
||||
import { zodfetch, ApiError } from "@trigger.dev/core/v3/zodfetch";
|
||||
|
||||
export class CliApiClient {
|
||||
private readonly apiURL: string;
|
||||
|
||||
constructor(
|
||||
apiURL: string,
|
||||
private readonly accessToken?: string
|
||||
public readonly apiURL: string,
|
||||
public readonly accessToken?: string
|
||||
) {
|
||||
this.apiURL = apiURL.replace(/\/$/, "");
|
||||
}
|
||||
|
||||
@@ -152,6 +152,7 @@ async function startDev(options: StartDevOptions) {
|
||||
initialMode="local"
|
||||
showInteractiveDevSession={true}
|
||||
client={projectClient.client}
|
||||
dashboardUrl={options.login.dashboardUrl}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,9 +21,11 @@ import { type DevCommandOptions } from "../commands/dev.js";
|
||||
import { logger } from "../utilities/logger.js";
|
||||
import { EphemeralDirectory, getTmpDir } from "../utilities/tempDirectories.js";
|
||||
import { copyManifestToDir } from "../build/manifests.js";
|
||||
import { startWorkerRuntime } from "./workerRuntime.js";
|
||||
|
||||
export type DevSessionOptions = {
|
||||
name: string | undefined;
|
||||
dashboardUrl: string;
|
||||
initialMode: "local";
|
||||
showInteractiveDevSession: boolean | undefined;
|
||||
rawConfig: ResolvedConfig;
|
||||
@@ -31,9 +33,23 @@ export type DevSessionOptions = {
|
||||
client: CliApiClient;
|
||||
};
|
||||
|
||||
export async function startDevSession({ rawConfig }: DevSessionOptions) {
|
||||
export async function startDevSession({
|
||||
rawConfig,
|
||||
name,
|
||||
rawArgs,
|
||||
client,
|
||||
dashboardUrl,
|
||||
}: DevSessionOptions) {
|
||||
const destination = getTmpDir(rawConfig.workingDir, "build");
|
||||
|
||||
const runtime = await startWorkerRuntime({
|
||||
name,
|
||||
config: rawConfig,
|
||||
args: rawArgs,
|
||||
client,
|
||||
dashboardUrl,
|
||||
});
|
||||
|
||||
logger.debug("Starting dev session", { destination: destination.path, rawConfig });
|
||||
|
||||
const externalsExtension = createExternalsBuildExtension("dev", rawConfig);
|
||||
@@ -124,6 +140,7 @@ export async function startDevSession({ rawConfig }: DevSessionOptions) {
|
||||
|
||||
destination.remove();
|
||||
stopBundling?.().catch((error) => {});
|
||||
runtime.shutdown().catch((error) => {});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export class UncaughtExceptionError extends Error {
|
||||
constructor(
|
||||
public readonly originalError: { name: string; message: string; stack?: string },
|
||||
public readonly origin: "uncaughtException" | "unhandledRejection"
|
||||
) {
|
||||
super(`Uncaught exception: ${originalError.message}`);
|
||||
|
||||
this.name = "UncaughtExceptionError";
|
||||
}
|
||||
}
|
||||
|
||||
export class TaskMetadataParseError extends Error {
|
||||
constructor(
|
||||
public readonly zodIssues: z.ZodIssue[],
|
||||
public readonly tasks: any
|
||||
) {
|
||||
super(`Failed to parse task metadata`);
|
||||
|
||||
this.name = "TaskMetadataParseError";
|
||||
}
|
||||
}
|
||||
|
||||
export class UnexpectedExitError extends Error {
|
||||
constructor(
|
||||
public code: number,
|
||||
public signal: NodeJS.Signals | null,
|
||||
public stderr: string | undefined
|
||||
) {
|
||||
super(`Unexpected exit with code ${code}`);
|
||||
|
||||
this.name = "UnexpectedExitError";
|
||||
}
|
||||
}
|
||||
|
||||
export class CleanupProcessError extends Error {
|
||||
constructor() {
|
||||
super("Cancelled");
|
||||
|
||||
this.name = "CleanupProcessError";
|
||||
}
|
||||
}
|
||||
|
||||
export class CancelledProcessError extends Error {
|
||||
constructor() {
|
||||
super("Cancelled");
|
||||
|
||||
this.name = "CancelledProcessError";
|
||||
}
|
||||
}
|
||||
|
||||
export class SigKillTimeoutProcessError extends Error {
|
||||
constructor() {
|
||||
super("Process kill timeout");
|
||||
|
||||
this.name = "SigKillTimeoutProcessError";
|
||||
}
|
||||
}
|
||||
|
||||
export class GracefulExitTimeoutError extends Error {
|
||||
constructor() {
|
||||
super("Graceful exit timeout");
|
||||
|
||||
this.name = "GracefulExitTimeoutError";
|
||||
}
|
||||
}
|
||||
|
||||
export function getFriendlyErrorMessage(
|
||||
code: number,
|
||||
signal: NodeJS.Signals | null,
|
||||
stderr: string | undefined,
|
||||
dockerMode = true
|
||||
) {
|
||||
const message = (text: string) => {
|
||||
if (signal) {
|
||||
return `[${signal}] ${text}`;
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
if (code === 137) {
|
||||
if (dockerMode) {
|
||||
return message(
|
||||
"Process ran out of memory! Try choosing a machine preset with more memory for this task."
|
||||
);
|
||||
} else {
|
||||
// Note: containerState reason and message should be checked to clarify the error
|
||||
return message(
|
||||
"Process most likely ran out of memory, but we can't be certain. Try choosing a machine preset with more memory for this task."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (stderr?.includes("OOMErrorHandler")) {
|
||||
return message(
|
||||
"Process ran out of memory! Try choosing a machine preset with more memory for this task."
|
||||
);
|
||||
}
|
||||
|
||||
return message(`Process exited with code ${code}.`);
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
import {
|
||||
BuildManifest,
|
||||
clientWebsocketMessages,
|
||||
SemanticInternalAttributes,
|
||||
serverWebsocketMessages,
|
||||
TaskRunExecutionLazyAttemptPayload,
|
||||
} from "@trigger.dev/core/v3";
|
||||
import { ResolvedConfig } from "@trigger.dev/core/v3/build";
|
||||
import { ClientRequestArgs } from "node:http";
|
||||
import { WebSocket } from "partysocket";
|
||||
import { ClientOptions, WebSocket as wsWebSocket } from "ws";
|
||||
import { CliApiClient } from "../apiClient.js";
|
||||
import { DevCommandOptions } from "../commands/dev.js";
|
||||
import { chalkError } from "../utilities/cliOutput.js";
|
||||
import { logger } from "../utilities/logger.js";
|
||||
import { BackgroundWorker, BackgroundWorkerCoordinator } from "./backgroundWorker.js";
|
||||
import {
|
||||
MessagePayloadFromSchema,
|
||||
ZodMessageHandler,
|
||||
ZodMessageSender,
|
||||
} from "@trigger.dev/core/v3/zodMessageHandler";
|
||||
import { resolveDotEnvVars } from "../utilities/dotEnv.js";
|
||||
|
||||
export interface WorkerRuntime {
|
||||
shutdown(): Promise<void>;
|
||||
initializeWorker(manifest: BuildManifest): Promise<void>;
|
||||
}
|
||||
|
||||
export type WorkerRuntimeOptions = {
|
||||
name: string | undefined;
|
||||
config: ResolvedConfig;
|
||||
args: DevCommandOptions;
|
||||
client: CliApiClient;
|
||||
dashboardUrl: string;
|
||||
};
|
||||
|
||||
export async function startWorkerRuntime(options: WorkerRuntimeOptions): Promise<WorkerRuntime> {
|
||||
const runtime = new DevWorkerRuntime(options);
|
||||
|
||||
await runtime.init();
|
||||
|
||||
return runtime;
|
||||
}
|
||||
|
||||
class DevWorkerRuntime implements WorkerRuntime {
|
||||
private websocket: WebSocket;
|
||||
private backgroundWorkerCoordinator: BackgroundWorkerCoordinator;
|
||||
private sender: ZodMessageSender<typeof clientWebsocketMessages>;
|
||||
private websocketMessageHandler: ZodMessageHandler<typeof serverWebsocketMessages>;
|
||||
|
||||
constructor(public readonly options: WorkerRuntimeOptions) {
|
||||
const websocketUrl = new URL(this.options.client.apiURL);
|
||||
websocketUrl.protocol = websocketUrl.protocol.replace("http", "ws");
|
||||
websocketUrl.pathname = `/ws`;
|
||||
|
||||
this.sender = new ZodMessageSender({
|
||||
schema: clientWebsocketMessages,
|
||||
sender: async (message) => {
|
||||
this.websocket.send(JSON.stringify(message));
|
||||
},
|
||||
});
|
||||
|
||||
this.backgroundWorkerCoordinator = new BackgroundWorkerCoordinator(
|
||||
`${options.dashboardUrl}/projects/v3/${options.config.project}`
|
||||
);
|
||||
|
||||
this.backgroundWorkerCoordinator.onWorkerTaskRunHeartbeat.attach(
|
||||
async ({ worker, backgroundWorkerId, id }) => {
|
||||
await this.sender.send("BACKGROUND_WORKER_MESSAGE", {
|
||||
backgroundWorkerId,
|
||||
data: {
|
||||
type: "TASK_RUN_HEARTBEAT",
|
||||
id,
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.backgroundWorkerCoordinator.onTaskCompleted.attach(
|
||||
async ({ backgroundWorkerId, completion, execution }) => {
|
||||
await this.sender.send("BACKGROUND_WORKER_MESSAGE", {
|
||||
backgroundWorkerId,
|
||||
data: {
|
||||
type: "TASK_RUN_COMPLETED",
|
||||
completion,
|
||||
execution,
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.backgroundWorkerCoordinator.onTaskFailedToRun.attach(
|
||||
async ({ backgroundWorkerId, completion }) => {
|
||||
await this.sender.send("BACKGROUND_WORKER_MESSAGE", {
|
||||
backgroundWorkerId,
|
||||
data: {
|
||||
type: "TASK_RUN_FAILED_TO_RUN",
|
||||
completion,
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.backgroundWorkerCoordinator.onWorkerRegistered.attach(async ({ id, worker, record }) => {
|
||||
await this.sender.send("READY_FOR_TASKS", {
|
||||
backgroundWorkerId: id,
|
||||
});
|
||||
});
|
||||
|
||||
this.websocketMessageHandler = new ZodMessageHandler({
|
||||
schema: serverWebsocketMessages,
|
||||
messages: {
|
||||
SERVER_READY: async (payload) => {
|
||||
await this.#serverReady(payload);
|
||||
},
|
||||
BACKGROUND_WORKER_MESSAGE: async (payload) => {
|
||||
await this.#backgroundWorkerMessage(payload);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
this.websocket = new WebSocket(websocketUrl.href, [], {
|
||||
WebSocket: WebsocketFactory(this.options.client.accessToken!),
|
||||
connectionTimeout: 10000,
|
||||
maxRetries: 10,
|
||||
minReconnectionDelay: 1000,
|
||||
maxReconnectionDelay: 30000,
|
||||
reconnectionDelayGrowFactor: 1.4, // This leads to the following retry times: 1, 1.4, 1.96, 2.74, 3.84, 5.38, 7.53, 10.54, 14.76, 20.66
|
||||
maxEnqueuedMessages: 250,
|
||||
});
|
||||
|
||||
this.websocket.addEventListener("open", async (event) => {
|
||||
logger.debug("WebSocket opened", { event });
|
||||
});
|
||||
|
||||
this.websocket.addEventListener("close", (event) => {
|
||||
logger.debug("WebSocket closed", { event });
|
||||
});
|
||||
|
||||
this.websocket.addEventListener("error", (event) => {
|
||||
logger.log(`${chalkError("WebSocketError:")} ${event.error.message}`);
|
||||
});
|
||||
|
||||
this.websocket.addEventListener("message", this.#handleWebsocketMessage.bind(this));
|
||||
}
|
||||
|
||||
async init(): Promise<void> {}
|
||||
|
||||
async shutdown(): Promise<void> {
|
||||
this.websocket.close();
|
||||
}
|
||||
|
||||
async initializeWorker(manifest: BuildManifest, options?: { cwd?: string }): Promise<void> {
|
||||
const env = await this.#getEnvVars();
|
||||
|
||||
const backgroundWorker = new BackgroundWorker(manifest, {
|
||||
env,
|
||||
cwd: this.options.config.workingDir,
|
||||
});
|
||||
|
||||
await backgroundWorker.initialize();
|
||||
}
|
||||
|
||||
async #getEnvVars(): Promise<Record<string, string>> {
|
||||
const environmentVariablesResponse = await this.options.client.getEnvironmentVariables(
|
||||
this.options.config.project
|
||||
);
|
||||
|
||||
const processEnv = gatherProcessEnv();
|
||||
const dotEnvVars = resolveDotEnvVars();
|
||||
|
||||
return {
|
||||
...processEnv,
|
||||
...(environmentVariablesResponse.success ? environmentVariablesResponse.data.variables : {}),
|
||||
...dotEnvVars,
|
||||
TRIGGER_API_URL: this.options.client.apiURL,
|
||||
TRIGGER_SECRET_KEY: this.options.client.accessToken!,
|
||||
OTEL_EXPORTER_OTLP_COMPRESSION: "none",
|
||||
OTEL_RESOURCE_ATTRIBUTES: JSON.stringify({
|
||||
[SemanticInternalAttributes.PROJECT_DIR]: this.options.config.workingDir,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
async #handleWebsocketMessage(event: MessageEvent) {
|
||||
try {
|
||||
const data = JSON.parse(
|
||||
typeof event.data === "string" ? event.data : new TextDecoder("utf-8").decode(event.data)
|
||||
);
|
||||
|
||||
await this.websocketMessageHandler.handleMessage(data);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
logger.error("Error while handling websocket message", { error: error.message });
|
||||
} else {
|
||||
logger.error(
|
||||
"Unkown error while handling websocket message, use `-l debug` for additional output"
|
||||
);
|
||||
logger.debug("Error while handling websocket message", { error });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async #serverReady(
|
||||
payload: MessagePayloadFromSchema<"SERVER_READY", typeof serverWebsocketMessages>
|
||||
) {
|
||||
for (const worker of this.backgroundWorkerCoordinator.currentWorkers) {
|
||||
await this.sender.send("READY_FOR_TASKS", {
|
||||
backgroundWorkerId: worker.id,
|
||||
inProgressRuns: worker.worker.inProgressRuns,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async #backgroundWorkerMessage(
|
||||
payload: MessagePayloadFromSchema<"BACKGROUND_WORKER_MESSAGE", typeof serverWebsocketMessages>
|
||||
) {
|
||||
const message = payload.data;
|
||||
|
||||
logger.debug(
|
||||
`Received message from worker ${payload.backgroundWorkerId}`,
|
||||
JSON.stringify({ workerMessage: message })
|
||||
);
|
||||
|
||||
switch (message.type) {
|
||||
case "CANCEL_ATTEMPT": {
|
||||
// Need to cancel the attempt somehow here
|
||||
this.backgroundWorkerCoordinator.cancelRun(payload.backgroundWorkerId, message.taskRunId);
|
||||
break;
|
||||
}
|
||||
case "EXECUTE_RUN_LAZY_ATTEMPT": {
|
||||
await this.#executeTaskRunLazyAttempt(payload.backgroundWorkerId, message.payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async #executeTaskRunLazyAttempt(id: string, payload: TaskRunExecutionLazyAttemptPayload) {
|
||||
const attemptResponse = await this.options.client.createTaskRunAttempt(payload.runId);
|
||||
|
||||
if (!attemptResponse.success) {
|
||||
throw new Error(`Failed to create task run attempt: ${attemptResponse.error}`);
|
||||
}
|
||||
|
||||
const execution = attemptResponse.data;
|
||||
|
||||
const completion = await this.backgroundWorkerCoordinator.executeTaskRun(
|
||||
id,
|
||||
{ execution, traceContext: payload.traceContext, environment: payload.environment },
|
||||
payload.messageId
|
||||
);
|
||||
|
||||
return { execution, completion };
|
||||
}
|
||||
}
|
||||
|
||||
function WebsocketFactory(apiKey: string) {
|
||||
return class extends wsWebSocket {
|
||||
constructor(address: string | URL, options?: ClientOptions | ClientRequestArgs) {
|
||||
super(address, { ...(options ?? {}), headers: { Authorization: `Bearer ${apiKey}` } });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function gatherProcessEnv() {
|
||||
const env = {
|
||||
...process.env,
|
||||
NODE_ENV: "development",
|
||||
};
|
||||
|
||||
// Filter out undefined values
|
||||
return Object.fromEntries(Object.entries(env).filter(([key, value]) => value !== undefined));
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import dotenv from "dotenv";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
export function resolveDotEnvVars(cwd?: string) {
|
||||
const result: { [key: string]: string } = {};
|
||||
|
||||
dotenv.config({
|
||||
processEnv: result,
|
||||
path: [".env", ".env.local", ".env.development.local"].map((p) =>
|
||||
resolve(cwd ?? process.cwd(), p)
|
||||
),
|
||||
});
|
||||
|
||||
process.env.TRIGGER_API_URL && (result.TRIGGER_API_URL = process.env.TRIGGER_API_URL);
|
||||
|
||||
// remove TRIGGER_API_URL and TRIGGER_SECRET_KEY, since those should be coming from the worker
|
||||
delete result.TRIGGER_API_URL;
|
||||
delete result.TRIGGER_SECRET_KEY;
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,12 +1,6 @@
|
||||
import { z } from "zod";
|
||||
import { ConfigManifest } from "./config.js";
|
||||
|
||||
export const TaskFile = z.object({
|
||||
entry: z.string(),
|
||||
out: z.string(),
|
||||
});
|
||||
|
||||
export type TaskFile = z.infer<typeof TaskFile>;
|
||||
import { TaskFile, TaskManifest } from "./schemas.js";
|
||||
|
||||
export const BuildExternal = z.object({
|
||||
name: z.string(),
|
||||
@@ -53,14 +47,6 @@ export const IndexMessage = z.object({
|
||||
|
||||
export type IndexMessage = z.infer<typeof IndexMessage>;
|
||||
|
||||
export const TaskManifest = z.object({
|
||||
id: z.string(),
|
||||
exportName: z.string(),
|
||||
file: TaskFile,
|
||||
});
|
||||
|
||||
export type TaskManifest = z.infer<typeof TaskManifest>;
|
||||
|
||||
export const WorkerManifest = z.object({
|
||||
tasks: TaskManifest.array(),
|
||||
});
|
||||
|
||||
@@ -9,18 +9,15 @@ import {
|
||||
EnvironmentType,
|
||||
ProdTaskRunExecution,
|
||||
ProdTaskRunExecutionPayload,
|
||||
TaskMetadataWithFilePath,
|
||||
TaskManifest,
|
||||
TaskRunExecutionLazyAttemptPayload,
|
||||
TaskRunExecutionPayload,
|
||||
WaitReason,
|
||||
} from "./schemas.js";
|
||||
import { TaskResource } from "./resources.js";
|
||||
import { WorkerManifest } from "./build.js";
|
||||
|
||||
export const BackgroundWorkerServerMessages = z.discriminatedUnion("type", [
|
||||
z.object({
|
||||
type: z.literal("EXECUTE_RUNS"),
|
||||
payloads: z.array(TaskRunExecutionPayload),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal("CANCEL_ATTEMPT"),
|
||||
taskAttemptId: z.string(),
|
||||
@@ -86,13 +83,13 @@ export const BackgroundWorkerClientMessages = z.discriminatedUnion("type", [
|
||||
|
||||
export type BackgroundWorkerClientMessages = z.infer<typeof BackgroundWorkerClientMessages>;
|
||||
|
||||
export const BackgroundWorkerProperties = z.object({
|
||||
export const ServerBackgroundWorker = z.object({
|
||||
id: z.string(),
|
||||
version: z.string(),
|
||||
contentHash: z.string(),
|
||||
});
|
||||
|
||||
export type BackgroundWorkerProperties = z.infer<typeof BackgroundWorkerProperties>;
|
||||
export type ServerBackgroundWorker = z.infer<typeof ServerBackgroundWorker>;
|
||||
|
||||
export const clientWebsocketMessages = {
|
||||
READY_FOR_TASKS: z.object({
|
||||
@@ -116,7 +113,7 @@ export const workerToChildMessages = {
|
||||
version: z.literal("v1").default("v1"),
|
||||
execution: TaskRunExecution,
|
||||
traceContext: z.record(z.unknown()),
|
||||
metadata: BackgroundWorkerProperties,
|
||||
metadata: ServerBackgroundWorker,
|
||||
}),
|
||||
TASK_RUN_COMPLETED_NOTIFICATION: z.discriminatedUnion("version", [
|
||||
z.object({
|
||||
@@ -160,9 +157,9 @@ export const childToWorkerMessages = {
|
||||
execution: TaskRunExecution,
|
||||
result: TaskRunExecutionResult,
|
||||
}),
|
||||
TASKS_READY: z.object({
|
||||
INDEX_COMPLETE: z.object({
|
||||
version: z.literal("v1").default("v1"),
|
||||
tasks: TaskMetadataWithFilePath.array(),
|
||||
manifest: WorkerManifest,
|
||||
}),
|
||||
TASKS_FAILED_TO_PARSE: TaskMetadataFailedToParseData,
|
||||
TASK_HEARTBEAT: z.object({
|
||||
@@ -201,7 +198,7 @@ export const ProdChildToWorkerMessages = {
|
||||
TASKS_READY: {
|
||||
message: z.object({
|
||||
version: z.literal("v1").default("v1"),
|
||||
tasks: TaskMetadataWithFilePath.array(),
|
||||
tasks: TaskManifest.array(),
|
||||
}),
|
||||
},
|
||||
TASKS_FAILED_TO_PARSE: {
|
||||
@@ -248,7 +245,7 @@ export const ProdWorkerToChildMessages = {
|
||||
version: z.literal("v1").default("v1"),
|
||||
execution: TaskRunExecution,
|
||||
traceContext: z.record(z.unknown()),
|
||||
metadata: BackgroundWorkerProperties,
|
||||
metadata: ServerBackgroundWorker,
|
||||
}),
|
||||
},
|
||||
TASK_RUN_COMPLETED_NOTIFICATION: {
|
||||
|
||||
@@ -147,38 +147,41 @@ export const ScheduleMetadata = z.object({
|
||||
timezone: z.string(),
|
||||
});
|
||||
|
||||
export const TaskMetadata = z.object({
|
||||
const taskMetadata = {
|
||||
id: z.string(),
|
||||
packageVersion: z.string(),
|
||||
queue: QueueOptions.optional(),
|
||||
retry: RetryOptions.optional(),
|
||||
machine: MachineConfig.optional(),
|
||||
triggerSource: z.string().optional(),
|
||||
schedule: ScheduleMetadata.optional(),
|
||||
});
|
||||
};
|
||||
|
||||
export const TaskMetadata = z.object(taskMetadata);
|
||||
|
||||
export type TaskMetadata = z.infer<typeof TaskMetadata>;
|
||||
|
||||
export const TaskFileMetadata = z.object({
|
||||
filePath: z.string(),
|
||||
exportName: z.string(),
|
||||
export const TaskFile = z.object({
|
||||
entry: z.string(),
|
||||
out: z.string(),
|
||||
});
|
||||
|
||||
export type TaskFile = z.infer<typeof TaskFile>;
|
||||
|
||||
const taskFileMetadata = {
|
||||
file: TaskFile,
|
||||
exportName: z.string(),
|
||||
};
|
||||
|
||||
export const TaskFileMetadata = z.object(taskFileMetadata);
|
||||
|
||||
export type TaskFileMetadata = z.infer<typeof TaskFileMetadata>;
|
||||
|
||||
export const TaskMetadataWithFilePath = z.object({
|
||||
id: z.string(),
|
||||
packageVersion: z.string(),
|
||||
queue: QueueOptions.optional(),
|
||||
retry: RetryOptions.optional(),
|
||||
machine: MachineConfig.optional(),
|
||||
triggerSource: z.string().optional(),
|
||||
schedule: ScheduleMetadata.optional(),
|
||||
filePath: z.string(),
|
||||
exportName: z.string(),
|
||||
export const TaskManifest = z.object({
|
||||
...taskMetadata,
|
||||
...taskFileMetadata,
|
||||
});
|
||||
|
||||
export type TaskMetadataWithFilePath = z.infer<typeof TaskMetadataWithFilePath>;
|
||||
export type TaskManifest = z.infer<typeof TaskManifest>;
|
||||
|
||||
export const PostStartCauses = z.enum(["index", "create", "restore"]);
|
||||
export type PostStartCauses = z.infer<typeof PostStartCauses>;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { TaskFileMetadata, TaskMetadataWithFilePath } from "../schemas/index.js";
|
||||
import { TaskFileMetadata, TaskManifest } from "../schemas/index.js";
|
||||
import { TaskMetadataWithFunctions } from "../types/index.js";
|
||||
|
||||
export interface TaskCatalog {
|
||||
registerTaskMetadata(task: TaskMetadataWithFunctions): void;
|
||||
updateTaskMetadata(id: string, task: Partial<TaskMetadataWithFunctions>): void;
|
||||
registerTaskFileMetadata(id: string, metadata: TaskFileMetadata): void;
|
||||
getAllTaskMetadata(): Array<TaskMetadataWithFilePath>;
|
||||
getTaskMetadata(id: string): TaskMetadataWithFilePath | undefined;
|
||||
listTaskManifests(): Array<TaskManifest>;
|
||||
getTaskManifest(id: string): TaskManifest | undefined;
|
||||
getTask(id: string): TaskMetadataWithFunctions | undefined;
|
||||
taskExists(id: string): boolean;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const API_NAME = "task-catalog";
|
||||
|
||||
import { TaskFileMetadata, TaskMetadataWithFilePath } from "../schemas/index.js";
|
||||
import { TaskFileMetadata, TaskManifest } from "../schemas/index.js";
|
||||
import { TaskMetadataWithFunctions } from "../types/index.js";
|
||||
import { getGlobal, registerGlobal, unregisterGlobal } from "../utils/globals.js";
|
||||
import { type TaskCatalog } from "./catalog.js";
|
||||
@@ -41,12 +41,12 @@ export class TaskCatalogAPI {
|
||||
this.#getCatalog().registerTaskFileMetadata(id, metadata);
|
||||
}
|
||||
|
||||
public getAllTaskMetadata(): Array<TaskMetadataWithFilePath> {
|
||||
return this.#getCatalog().getAllTaskMetadata();
|
||||
public listTaskManifests(): Array<TaskManifest> {
|
||||
return this.#getCatalog().listTaskManifests();
|
||||
}
|
||||
|
||||
public getTaskMetadata(id: string): TaskMetadataWithFilePath | undefined {
|
||||
return this.#getCatalog().getTaskMetadata(id);
|
||||
public getTaskManifest(id: string): TaskManifest | undefined {
|
||||
return this.#getCatalog().getTaskManifest(id);
|
||||
}
|
||||
|
||||
public getTask(id: string): TaskMetadataWithFunctions | undefined {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TaskFileMetadata, TaskMetadataWithFilePath } from "../schemas/index.js";
|
||||
import { TaskFileMetadata, TaskManifest } from "../schemas/index.js";
|
||||
import { TaskMetadataWithFunctions } from "../types/index.js";
|
||||
import { TaskCatalog } from "./catalog.js";
|
||||
|
||||
@@ -15,11 +15,11 @@ export class NoopTaskCatalog implements TaskCatalog {
|
||||
// noop
|
||||
}
|
||||
|
||||
getAllTaskMetadata(): Array<TaskMetadataWithFilePath> {
|
||||
listTaskManifests(): Array<TaskManifest> {
|
||||
return [];
|
||||
}
|
||||
|
||||
getTaskMetadata(id: string): TaskMetadataWithFilePath | undefined {
|
||||
getTaskManifest(id: string): TaskManifest | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TaskFileMetadata, TaskMetadata, TaskMetadataWithFilePath } from "../schemas/index.js";
|
||||
import { TaskFileMetadata, TaskMetadata, TaskManifest } from "../schemas/index.js";
|
||||
import { TaskMetadataWithFunctions } from "../types/index.js";
|
||||
import { TaskCatalog } from "./catalog.js";
|
||||
|
||||
@@ -45,8 +45,8 @@ export class StandardTaskCatalog implements TaskCatalog {
|
||||
}
|
||||
|
||||
// Return all the tasks, without the functions
|
||||
getAllTaskMetadata(): Array<TaskMetadataWithFilePath> {
|
||||
const result: Array<TaskMetadataWithFilePath> = [];
|
||||
listTaskManifests(): Array<TaskManifest> {
|
||||
const result: Array<TaskManifest> = [];
|
||||
|
||||
for (const [id, metadata] of this._taskMetadata) {
|
||||
const fileMetadata = this._taskFileMetadata.get(id);
|
||||
@@ -64,7 +64,7 @@ export class StandardTaskCatalog implements TaskCatalog {
|
||||
return result;
|
||||
}
|
||||
|
||||
getTaskMetadata(id: string): TaskMetadataWithFilePath | undefined {
|
||||
getTaskManifest(id: string): TaskManifest | undefined {
|
||||
const metadata = this._taskMetadata.get(id);
|
||||
const fileMetadata = this._taskFileMetadata.get(id);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Attributes } from "@opentelemetry/api";
|
||||
import { BackgroundWorkerProperties, TaskRunContext } from "../schemas/index.js";
|
||||
import { ServerBackgroundWorker, TaskRunContext } from "../schemas/index.js";
|
||||
import { getGlobal, registerGlobal, unregisterGlobal } from "../utils/globals.js";
|
||||
import { TaskContext } from "./types.js";
|
||||
import { SemanticInternalAttributes } from "../semanticInternalAttributes.js";
|
||||
@@ -27,7 +27,7 @@ export class TaskContextAPI {
|
||||
return this.#getTaskContext()?.ctx;
|
||||
}
|
||||
|
||||
get worker(): BackgroundWorkerProperties | undefined {
|
||||
get worker(): ServerBackgroundWorker | undefined {
|
||||
return this.#getTaskContext()?.worker;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BackgroundWorkerProperties, TaskRunContext } from "../schemas/index.js";
|
||||
import { ServerBackgroundWorker, TaskRunContext } from "../schemas/index.js";
|
||||
|
||||
export type TaskContext = {
|
||||
ctx: TaskRunContext;
|
||||
worker: BackgroundWorkerProperties;
|
||||
worker: ServerBackgroundWorker;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
RetryOptions,
|
||||
TaskMetadata,
|
||||
TaskMetadataWithFilePath,
|
||||
TaskRunContext,
|
||||
} from "../schemas/index.js";
|
||||
import { RetryOptions, TaskMetadata, TaskManifest, TaskRunContext } from "../schemas/index.js";
|
||||
import { Prettify } from "./utils.js";
|
||||
|
||||
export * from "./utils.js";
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ConsoleInterceptor } from "../consoleInterceptor.js";
|
||||
import { parseError, sanitizeError } from "../errors.js";
|
||||
import { TracingSDK, recordSpanException } from "../otel/index.js";
|
||||
import {
|
||||
BackgroundWorkerProperties,
|
||||
ServerBackgroundWorker,
|
||||
Config,
|
||||
TaskRunContext,
|
||||
TaskRunErrorCodes,
|
||||
@@ -27,6 +27,7 @@ import { accessoryAttributes } from "../utils/styleAttributes.js";
|
||||
import { UsageMeasurement } from "../usage/types.js";
|
||||
import { ApiError, RateLimitError } from "../apiClient/errors.js";
|
||||
import { TriggerConfig } from "../index.js";
|
||||
import { pkg } from "../../pkg.js";
|
||||
|
||||
export type TaskExecutorOptions = {
|
||||
tracingSDK: TracingSDK;
|
||||
@@ -59,7 +60,7 @@ export class TaskExecutor {
|
||||
|
||||
async execute(
|
||||
execution: TaskRunExecution,
|
||||
worker: BackgroundWorkerProperties,
|
||||
worker: ServerBackgroundWorker,
|
||||
traceContext: Record<string, unknown>,
|
||||
usage: UsageMeasurement
|
||||
): Promise<{ result: TaskRunExecutionResult }> {
|
||||
@@ -78,7 +79,7 @@ export class TaskExecutor {
|
||||
|
||||
this._tracingSDK.asyncResourceDetector.resolveWithAttributes({
|
||||
...taskContext.attributes,
|
||||
[SemanticInternalAttributes.SDK_VERSION]: this.task.packageVersion,
|
||||
[SemanticInternalAttributes.SDK_VERSION]: pkg.version,
|
||||
[SemanticInternalAttributes.SDK_LANGUAGE]: "typescript",
|
||||
});
|
||||
|
||||
|
||||
@@ -36,6 +36,11 @@ export type MessageFromSchema<
|
||||
payload: z.input<TMessageCatalog[K]>;
|
||||
};
|
||||
|
||||
export type MessagePayloadFromSchema<
|
||||
K extends keyof TMessageCatalog,
|
||||
TMessageCatalog extends ZodMessageCatalogSchema,
|
||||
> = z.output<TMessageCatalog[K]>;
|
||||
|
||||
export type MessageFromCatalog<TMessageCatalog extends ZodMessageCatalogSchema> = {
|
||||
[K in keyof TMessageCatalog]: MessageFromSchema<K, TMessageCatalog>;
|
||||
}[keyof TMessageCatalog];
|
||||
|
||||
Reference in New Issue
Block a user