v3: Support decorators (#1060)

* Adds support for `emitDecoratorMetadata: true` and `experimentalDecorators: true` in your tsconfig

* Implement task.onSuccess/onFailure and config.onSuccess/onFailure

* Added onStart and more docs for lifecycle functions

* Use onStart instead of init for TypeORM
This commit is contained in:
Eric Allam
2024-04-25 11:17:13 +01:00
committed by GitHub
parent 0a5aa2dc15
commit 9491a1649c
19 changed files with 946 additions and 34 deletions
+7
View File
@@ -0,0 +1,7 @@
---
"@trigger.dev/sdk": patch
"trigger.dev": patch
"@trigger.dev/core": patch
---
Implement task.onSuccess/onFailure and config.onSuccess/onFailure
+83
View File
@@ -0,0 +1,83 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
Adds support for `emitDecoratorMetadata: true` and `experimentalDecorators: true` in your tsconfig using the [`@anatine/esbuild-decorators`](https://github.com/anatine/esbuildnx/tree/main/packages/esbuild-decorators) package. This allows you to use libraries like TypeORM:
```ts orm/index.ts
import "reflect-metadata";
import { DataSource } from "typeorm";
import { Entity, Column, PrimaryColumn } from "typeorm";
@Entity()
export class Photo {
@PrimaryColumn()
id!: number;
@Column()
name!: string;
@Column()
description!: string;
@Column()
filename!: string;
@Column()
views!: number;
@Column()
isPublished!: boolean;
}
export const AppDataSource = new DataSource({
type: "postgres",
host: "localhost",
port: 5432,
username: "postgres",
password: "postgres",
database: "v3-catalog",
entities: [Photo],
synchronize: true,
logging: false,
});
```
And then in your trigger.config.ts file you can initialize the datasource using the new `init` option:
```ts trigger.config.ts
import type { TriggerConfig } from "@trigger.dev/sdk/v3";
import { AppDataSource } from "@/trigger/orm";
export const config: TriggerConfig = {
// ... other options here
init: async (payload, { ctx }) => {
await AppDataSource.initialize();
},
};
```
Now you are ready to use this in your tasks:
```ts
import { task } from "@trigger.dev/sdk/v3";
import { AppDataSource, Photo } from "./orm";
export const taskThatUsesDecorators = task({
id: "taskThatUsesDecorators",
run: async (payload: { message: string }) => {
console.log("Creating a photo...");
const photo = new Photo();
photo.id = 2;
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg";
photo.views = 1;
photo.isPublished = true;
await AppDataSource.manager.save(photo);
},
});
```
+126 -10
View File
@@ -79,7 +79,7 @@ export const taskWithRetries = task({
maxTimeoutInMs: 30_000, maxTimeoutInMs: 30_000,
randomize: false, randomize: false,
}, },
run: async ({ payload, ctx }) => { run: async (payload: any, { ctx }) => {
//... //...
}, },
}); });
@@ -99,7 +99,7 @@ export const oneAtATime = task({
queue: { queue: {
concurrencyLimit: 1, concurrencyLimit: 1,
}, },
run: async ({ payload, ctx }) => { run: async (payload: any, { ctx }) => {
//... //...
}, },
}); });
@@ -116,7 +116,7 @@ export const heavyTask = task({
cpu: 2, cpu: 2,
memory: 4, memory: 4,
}, },
run: async ({ payload, ctx }) => { run: async (payload: any, { ctx }) => {
//... //...
}, },
}); });
@@ -124,27 +124,143 @@ export const heavyTask = task({
### `init` function ### `init` function
This function is called before a run attempt. This function is called before a run attempt:
```ts /trigger/init.ts
export const taskWithInit = task({
id: "task-with-init",
init: async (payload, { ctx }) => {
//...
},
run: async (payload: any, { ctx }) => {
//...
},
});
```
You can also return data from the `init` function that will be available in the params of the `run`, `cleanup`, `onSuccess`, and `onFailure` functions.
```ts /trigger/init-return.ts
export const taskWithInitReturn = task({
id: "task-with-init-return",
init: async (payload, { ctx }) => {
return { someData: "someValue" };
},
run: async (payload: any, { ctx, init }) => {
console.log(init.someData); // "someValue"
},
});
```
### `cleanup` function ### `cleanup` function
This function is called after a run attempt has succeeded or failed. This function is called after the `run` function is executed, regardless of whether the run was successful or not. It's useful for cleaning up resources, logging, or other side effects.
```ts /trigger/cleanup.ts
export const taskWithCleanup = task({
id: "task-with-cleanup",
cleanup: async (payload, { ctx }) => {
//...
},
run: async (payload: any, { ctx }) => {
//...
},
});
```
### `middleware` function ### `middleware` function
This function is called before the `run` function, it allows you to wrap the run function with custom code. For more information [read the guide](/v3/middleware). This function is called before the `run` function, it allows you to wrap the run function with custom code. For more information [read the guide](/v3/middleware).
### `onStart` function
When a task run starts, the `onStart` function is called. It's useful for sending notifications, logging, and other side effects. This function will only be called one per run (not per retry). If you want to run code before each retry, use the `init` function.
```ts /trigger/on-start.ts
export const taskWithOnStart = task({
id: "task-with-on-start",
onStart: async (payload, { ctx }) => {
//...
},
run: async (payload: any, { ctx }) => {
//...
},
});
```
You can also define an `onStart` function in your `trigger.config.ts` file to get notified when any task starts.
```ts trigger.config.ts
import type { TriggerConfig } from "@trigger.dev/sdk/v3";
export const config: TriggerConfig = {
onStart: async (payload, { ctx }) => {
console.log("Task started", ctx.task.id);
},
};
```
### `onSuccess` function ### `onSuccess` function
When a task attempt succeeds, the `onSuccess` function is called. It's useful for sending notifications, logging, or other side effects. When a task run succeeds, the `onSuccess` function is called. It's useful for sending notifications, logging, syncing state to your database, or other side effects.
<Snippet file="coming-soon-slim.mdx" /> ```ts /trigger/on-success.ts
export const taskWithOnSuccess = task({
id: "task-with-on-success",
onSuccess: async (payload, output, { ctx }) => {
//...
},
run: async (payload: any, { ctx }) => {
//...
},
});
```
### `onError` function You can also define an `onSuccess` function in your `trigger.config.ts` file to get notified when any task succeeds.
When a task attempt fails, the `onError` function is called. It's useful for sending notifications, logging, or other side effects. ```ts trigger.config.ts
import type { TriggerConfig } from "@trigger.dev/sdk/v3";
<Snippet file="coming-soon-slim.mdx" /> export const config: TriggerConfig = {
onSuccess: async (payload, output, { ctx }) => {
console.log("Task succeeded", ctx.task.id);
},
};
```
### `onFailure` function
When a task run fails, the `onFailure` function is called. It's useful for sending notifications, logging, or other side effects. It will only be executed once the task run has exhausted all its retries.
```ts /trigger/on-failure.ts
export const taskWithOnFailure = task({
id: "task-with-on-failure",
onFailure: async (payload, error, { ctx }) => {
//...
},
run: async (payload: any, { ctx }) => {
//...
},
});
```
You can also define an `onFailure` function in your `trigger.config.ts` file to get notified when any task fails.
```ts trigger.config.ts
import type { TriggerConfig } from "@trigger.dev/sdk/v3";
export const config: TriggerConfig = {
onFailure: async (payload, error, { ctx }) => {
console.log("Task failed", ctx.task.id);
},
};
```
### `handleError` functions
You can define a function that will be called when an error is thrown in the `run` function, that allows you to control how the error is handled and whether the task should be retried.
Read more about `handleError` in our [Errors and Retrying guide](/v3/errors-retrying).
## Next steps ## Next steps
+121 -1
View File
@@ -31,6 +31,46 @@ export const config: TriggerConfig = {
Most of the time you don't need to change anything in this file, or if you do then we will tell you when you the run the CLI command. Most of the time you don't need to change anything in this file, or if you do then we will tell you when you the run the CLI command.
## Global initialization
You can run code before any task is run by adding a `init` function to your `trigger.config.ts` file.
```ts trigger.config.ts
import type { TriggerConfig } from "@trigger.dev/sdk/v3";
export const config: TriggerConfig = {
//..other stuff
init: async (payload, { ctx }) => {
console.log("I run before any task is run");
},
};
```
You'll have access to the run payload and the context object. Currently you cannot return anything from this function.
## Lifecycle functions
You can add lifecycle functions to get notified when any task starts, succeeds, or fails using `onStart`, `onSuccess` and `onFailure`:
```ts trigger.config.ts
import type { TriggerConfig } from "@trigger.dev/sdk/v3";
export const config: TriggerConfig = {
//..other stuff
onSuccess: async (payload, output, { ctx }) => {
console.log("Task succeeded", ctx.task.id);
},
onFailure: async (payload, error, { ctx }) => {
console.log("Task failed", ctx.task.id);
},
onStart: async (payload, { ctx }) => {
console.log("Task started", ctx.task.id);
},
};
```
Read more about task lifecycle functions in the [tasks overview](/v3/tasks-overview).
## Instrumentations ## Instrumentations
We use OpenTelemetry (OTEL) for our run logs. This means you get a lot of information about your tasks with no effort. But you probably want to add more information to your logs. For example, here's all the Prisma calls automatically logged: We use OpenTelemetry (OTEL) for our run logs. This means you get a lot of information about your tasks with no effort. But you probably want to add more information to your logs. For example, here's all the Prisma calls automatically logged:
@@ -92,7 +132,6 @@ Prisma works by generating a client from your `prisma.schema` file. This means y
<Step title="package.json postinstall `prisma generate`"> <Step title="package.json postinstall `prisma generate`">
<CodeGroup> <CodeGroup>
```json default path ```json default path
@@ -140,6 +179,87 @@ Prisma works by generating a client from your `prisma.schema` file. This means y
</Steps> </Steps>
## TypeORM support
We support using TypeORM with Trigger. You can use decorators in your entities and then use them in your tasks. Here's an example:
```ts orm/index.ts
import "reflect-metadata";
import { DataSource } from "typeorm";
import { Entity, Column, PrimaryColumn } from "typeorm";
@Entity()
export class Photo {
@PrimaryColumn()
id!: number;
@Column()
name!: string;
@Column()
description!: string;
@Column()
filename!: string;
@Column()
views!: number;
@Column()
isPublished!: boolean;
}
export const AppDataSource = new DataSource({
type: "postgres",
host: "localhost",
port: 5432,
username: "postgres",
password: "postgres",
database: "my-database",
entities: [Photo],
synchronize: true,
logging: false,
});
```
And then in your trigger.config.ts file you can initialize the datasource using the `onStart` lifecycle function option:
```ts trigger.config.ts
import type { TriggerConfig } from "@trigger.dev/sdk/v3";
import { AppDataSource } from "@/trigger/orm";
export const config: TriggerConfig = {
// ... other options here
onStart: async (payload, { ctx }) => {
await AppDataSource.initialize();
},
};
```
Now you are ready to use this in your tasks:
```ts
import { task } from "@trigger.dev/sdk/v3";
import { AppDataSource, Photo } from "./orm";
export const taskThatUsesDecorators = task({
id: "task-that-uses-decorators",
run: async (payload: { message: string }) => {
console.log("Creating a photo...");
const photo = new Photo();
photo.id = 2;
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg";
photo.views = 1;
photo.isPublished = true;
await AppDataSource.manager.save(photo);
},
});
```
## Troubleshooting ## Troubleshooting
If you have an issue with bundling let us know on [Discord](https://trigger.dev/discord) or [via email](https://trigger.dev/contact). If you have an issue with bundling let us know on [Discord](https://trigger.dev/discord) or [via email](https://trigger.dev/contact).
+1
View File
@@ -71,6 +71,7 @@
"test": "vitest" "test": "vitest"
}, },
"dependencies": { "dependencies": {
"@anatine/esbuild-decorators": "^0.2.19",
"@clack/prompts": "^0.7.0", "@clack/prompts": "^0.7.0",
"@depot/cli": "0.0.1-cli.2.55.0", "@depot/cli": "0.0.1-cli.2.55.0",
"@opentelemetry/api": "^1.8.0", "@opentelemetry/api": "^1.8.0",
+10 -5
View File
@@ -8,14 +8,13 @@ import {
flattenAttributes, flattenAttributes,
} from "@trigger.dev/core/v3"; } from "@trigger.dev/core/v3";
import { recordSpanException } from "@trigger.dev/core/v3/workers"; import { recordSpanException } from "@trigger.dev/core/v3/workers";
import chalk from "chalk";
import { Command, Option as CommandOption } from "commander"; import { Command, Option as CommandOption } from "commander";
import { Metafile, build } from "esbuild"; import { Metafile, build } from "esbuild";
import { execa } from "execa"; import { execa } from "execa";
import { createHash } from "node:crypto"; import { createHash } from "node:crypto";
import { readFileSync } from "node:fs"; import { readFileSync } from "node:fs";
import { copyFile, mkdir, readFile, writeFile } from "node:fs/promises"; import { copyFile, mkdir, readFile, writeFile } from "node:fs/promises";
import { dirname, join, relative, posix } from "node:path"; import { dirname, join, posix, relative } from "node:path";
import { setTimeout } from "node:timers/promises"; import { setTimeout } from "node:timers/promises";
import terminalLink from "terminal-link"; import terminalLink from "terminal-link";
import invariant from "tiny-invariant"; import invariant from "tiny-invariant";
@@ -32,7 +31,7 @@ import {
wrapCommandAction, wrapCommandAction,
} from "../cli/common.js"; } from "../cli/common.js";
import { readConfig } from "../utilities/configFiles.js"; import { readConfig } from "../utilities/configFiles.js";
import { createTempDir, readJSONFile, writeJSONFile } from "../utilities/fileSystem"; import { createTempDir, writeJSONFile } from "../utilities/fileSystem";
import { printStandloneInitialBanner } from "../utilities/initialBanner.js"; import { printStandloneInitialBanner } from "../utilities/initialBanner.js";
import { import {
detectPackageNameFromImportPath, detectPackageNameFromImportPath,
@@ -43,6 +42,7 @@ import { logger } from "../utilities/logger.js";
import { createTaskFileImports, gatherTaskFiles } from "../utilities/taskFiles"; import { createTaskFileImports, gatherTaskFiles } from "../utilities/taskFiles";
import { login } from "./login"; import { login } from "./login";
import { esbuildDecorators } from "@anatine/esbuild-decorators";
import { Glob, GlobOptions } from "glob"; import { Glob, GlobOptions } from "glob";
import type { SetOptional } from "type-fest"; import type { SetOptional } from "type-fest";
import { bundleDependenciesPlugin, workerSetupImportConfigPlugin } from "../utilities/build"; import { bundleDependenciesPlugin, workerSetupImportConfigPlugin } from "../utilities/build";
@@ -53,12 +53,12 @@ import {
parseBuildErrorStack, parseBuildErrorStack,
parseNpmInstallError, parseNpmInstallError,
} from "../utilities/deployErrors"; } from "../utilities/deployErrors";
import { safeJsonParse } from "../utilities/safeJsonParse";
import { JavascriptProject } from "../utilities/javascriptProject"; import { JavascriptProject } from "../utilities/javascriptProject";
import { docs, getInTouch } from "../utilities/links";
import { cliRootPath } from "../utilities/resolveInternalFilePath"; import { cliRootPath } from "../utilities/resolveInternalFilePath";
import { safeJsonParse } from "../utilities/safeJsonParse";
import { escapeImportPath, spinner } from "../utilities/windows"; import { escapeImportPath, spinner } from "../utilities/windows";
import { updateTriggerPackages } from "./update"; import { updateTriggerPackages } from "./update";
import { docs, getInTouch } from "../utilities/links";
const DeployCommandOptions = CommonCommandOptions.extend({ const DeployCommandOptions = CommonCommandOptions.extend({
skipTypecheck: z.boolean().default(false), skipTypecheck: z.boolean().default(false),
@@ -1137,6 +1137,11 @@ async function compileProject(
config.tsconfigPath config.tsconfigPath
), ),
workerSetupImportConfigPlugin(configPath), workerSetupImportConfigPlugin(configPath),
esbuildDecorators({
tsconfig: config.tsconfigPath,
tsx: true,
force: false,
}),
], ],
}); });
+6
View File
@@ -53,6 +53,7 @@ import { findUp, pathExists } from "find-up";
import { cliRootPath } from "../utilities/resolveInternalFilePath"; import { cliRootPath } from "../utilities/resolveInternalFilePath";
import { escapeImportPath } from "../utilities/windows"; import { escapeImportPath } from "../utilities/windows";
import { updateTriggerPackages } from "./update"; import { updateTriggerPackages } from "./update";
import { esbuildDecorators } from "@anatine/esbuild-decorators";
let apiClient: CliApiClient | undefined; let apiClient: CliApiClient | undefined;
@@ -409,6 +410,11 @@ function useDev({
config.tsconfigPath config.tsconfigPath
), ),
workerSetupImportConfigPlugin(configPath), workerSetupImportConfigPlugin(configPath),
esbuildDecorators({
tsconfig: config.tsconfigPath,
tsx: true,
force: false,
}),
{ {
name: "trigger.dev v3", name: "trigger.dev v3",
setup(build) { setup(build) {
@@ -10,6 +10,7 @@ import { createTempDir, readJSONFileSync } from "./fileSystem.js";
import { logger } from "./logger.js"; import { logger } from "./logger.js";
import { findTriggerDirectories, resolveTriggerDirectories } from "./taskFiles.js"; import { findTriggerDirectories, resolveTriggerDirectories } from "./taskFiles.js";
import { build } from "esbuild"; import { build } from "esbuild";
import { esbuildDecorators } from "@anatine/esbuild-decorators";
function getGlobalConfigFolderPath() { function getGlobalConfigFolderPath() {
const configDir = xdgAppPaths("trigger").config(); const configDir = xdgAppPaths("trigger").config();
@@ -172,6 +173,13 @@ export async function readConfig(
target: ["es2018", "node18"], target: ["es2018", "node18"],
outfile: builtConfigFilePath, outfile: builtConfigFilePath,
logLevel: "silent", logLevel: "silent",
plugins: [
esbuildDecorators({
cwd: absoluteDir,
tsx: false,
force: false,
}),
],
}); });
// import the config file // import the config file
+21
View File
@@ -1,3 +1,4 @@
import { FailureFnParams, InitFnParams, StartFnParams, SuccessFnParams } from ".";
import { LogLevel } from "../logger/taskLogger"; import { LogLevel } from "../logger/taskLogger";
import { RetryOptions } from "../schemas"; import { RetryOptions } from "../schemas";
import type { InstrumentationOption } from "@opentelemetry/instrumentation"; import type { InstrumentationOption } from "@opentelemetry/instrumentation";
@@ -48,4 +49,24 @@ export interface ProjectConfig {
* Enable console logging while running the dev CLI. This will print out logs from console.log, console.warn, and console.error. By default all logs are sent to the trigger.dev backend, and not logged to the console. * Enable console logging while running the dev CLI. This will print out logs from console.log, console.warn, and console.error. By default all logs are sent to the trigger.dev backend, and not logged to the console.
*/ */
enableConsoleLogging?: boolean; enableConsoleLogging?: boolean;
/**
* Run before a task is executed, for all tasks. This is useful for setting up any global state that is needed for all tasks.
*/
init?: (payload: unknown, params: InitFnParams) => void | Promise<void>;
/**
* onSuccess is called after the run function has successfully completed.
*/
onSuccess?: (payload: unknown, output: unknown, params: SuccessFnParams<any>) => Promise<void>;
/**
* onFailure is called after a task run has failed (meaning the run function threw an error and won't be retried anymore)
*/
onFailure?: (payload: unknown, error: unknown, params: FailureFnParams<any>) => Promise<void>;
/**
* onStart is called the first time a task is executed in a run (not before every retry)
*/
onStart?: (payload: unknown, params: StartFnParams) => Promise<void>;
} }
+10 -4
View File
@@ -22,12 +22,15 @@ export type InitFnParams = Prettify<{
ctx: Context; ctx: Context;
}>; }>;
export type StartFnParams = Prettify<{
ctx: Context;
}>;
export type Context = TaskRunContext; export type Context = TaskRunContext;
export type SuccessFnParams<TOutput, TInitOutput extends InitOutput> = RunFnParams<TInitOutput> & export type SuccessFnParams<TInitOutput extends InitOutput> = RunFnParams<TInitOutput>;
Prettify<{
output: TOutput; export type FailureFnParams<TInitOutput extends InitOutput> = RunFnParams<TInitOutput>;
}>;
export type HandleErrorFnParams<TInitOutput extends InitOutput> = RunFnParams<TInitOutput> & export type HandleErrorFnParams<TInitOutput extends InitOutput> = RunFnParams<TInitOutput> &
Prettify<{ Prettify<{
@@ -74,5 +77,8 @@ export type TaskMetadataWithFunctions = TaskMetadata & {
error: unknown, error: unknown,
params: HandleErrorFnParams<any> params: HandleErrorFnParams<any>
) => HandleErrorResult; ) => HandleErrorResult;
onSuccess?: (payload: any, output: any, params: SuccessFnParams<any>) => Promise<void>;
onFailure?: (payload: any, error: unknown, params: FailureFnParams<any>) => Promise<void>;
onStart?: (payload: any, params: StartFnParams) => Promise<void>;
}; };
}; };
+198 -5
View File
@@ -90,10 +90,16 @@ export class TaskExecutor {
parsedPayload = await parsePacket(payloadPacket); parsedPayload = await parsePacket(payloadPacket);
initOutput = await this.#callTaskInit(parsedPayload, ctx); if (execution.attempt.number === 1) {
await this.#callOnStartFunctions(parsedPayload, ctx);
}
initOutput = await this.#callInitFunctions(parsedPayload, ctx);
const output = await this.#callRun(parsedPayload, ctx, initOutput); const output = await this.#callRun(parsedPayload, ctx, initOutput);
await this.#callOnSuccessFunctions(parsedPayload, output, ctx, initOutput);
try { try {
const stringifiedOutput = await stringifyIO(output); const stringifiedOutput = await stringifyIO(output);
@@ -148,6 +154,15 @@ export class TaskExecutor {
recordSpanException(span, handleErrorResult.error ?? runError); recordSpanException(span, handleErrorResult.error ?? runError);
if (handleErrorResult.status !== "retry") {
await this.#callOnFailureFunctions(
parsedPayload,
handleErrorResult.error ?? runError,
ctx,
initOutput
);
}
return { return {
id: execution.run.id, id: execution.run.id,
ok: false, ok: false,
@@ -218,16 +233,194 @@ export class TaskExecutor {
return middlewareFn(payload, { ctx, next: async () => runFn(payload, { ctx, init }) }); return middlewareFn(payload, { ctx, next: async () => runFn(payload, { ctx, init }) });
} }
async #callTaskInit(payload: unknown, ctx: TaskRunContext) { async #callInitFunctions(payload: unknown, ctx: TaskRunContext) {
await this.#callConfigInit(payload, ctx);
const initFn = this.task.fns.init; const initFn = this.task.fns.init;
if (!initFn) { if (!initFn) {
return {}; return {};
} }
return this._tracer.startActiveSpan("init", async (span) => { return this._tracer.startActiveSpan(
return await initFn(payload, { ctx }); "init",
}); async (span) => {
return await initFn(payload, { ctx });
},
{
attributes: {
[SemanticInternalAttributes.STYLE_ICON]: "function",
},
}
);
}
async #callConfigInit(payload: unknown, ctx: TaskRunContext) {
const initFn = this._importedConfig?.init;
if (!initFn) {
return {};
}
return this._tracer.startActiveSpan(
"config.init",
async (span) => {
return await initFn(payload, { ctx });
},
{
attributes: {
[SemanticInternalAttributes.STYLE_ICON]: "function",
},
}
);
}
async #callOnSuccessFunctions(
payload: unknown,
output: any,
ctx: TaskRunContext,
initOutput: any
) {
await this.#callOnSuccessFunction(
this.task.fns.onSuccess,
"task.onSuccess",
payload,
output,
ctx,
initOutput
);
await this.#callOnSuccessFunction(
this._importedConfig?.onSuccess,
"config.onSuccess",
payload,
output,
ctx,
initOutput
);
}
async #callOnSuccessFunction(
onSuccessFn: TaskMetadataWithFunctions["fns"]["onSuccess"],
name: string,
payload: unknown,
output: any,
ctx: TaskRunContext,
initOutput: any
) {
if (!onSuccessFn) {
return;
}
try {
await this._tracer.startActiveSpan(
name,
async (span) => {
return await onSuccessFn(payload, output, { ctx, init: initOutput });
},
{
attributes: {
[SemanticInternalAttributes.STYLE_ICON]: "function",
},
}
);
} catch {
// Ignore errors from onSuccess functions
}
}
async #callOnFailureFunctions(
payload: unknown,
error: unknown,
ctx: TaskRunContext,
initOutput: any
) {
await this.#callOnFailureFunction(
this.task.fns.onFailure,
"task.onFailure",
payload,
error,
ctx,
initOutput
);
await this.#callOnFailureFunction(
this._importedConfig?.onFailure,
"config.onFailure",
payload,
error,
ctx,
initOutput
);
}
async #callOnFailureFunction(
onFailureFn: TaskMetadataWithFunctions["fns"]["onFailure"],
name: string,
payload: unknown,
error: unknown,
ctx: TaskRunContext,
initOutput: any
) {
if (!onFailureFn) {
return;
}
try {
return await this._tracer.startActiveSpan(
name,
async (span) => {
return await onFailureFn(payload, error, { ctx, init: initOutput });
},
{
attributes: {
[SemanticInternalAttributes.STYLE_ICON]: "function",
},
}
);
} catch (e) {
// Ignore errors from onFailure functions
}
}
async #callOnStartFunctions(payload: unknown, ctx: TaskRunContext) {
await this.#callOnStartFunction(
this._importedConfig?.onStart,
"config.onStart",
payload,
ctx,
{}
);
await this.#callOnStartFunction(this.task.fns.onStart, "task.onStart", payload, ctx, {});
}
async #callOnStartFunction(
onStartFn: TaskMetadataWithFunctions["fns"]["onStart"],
name: string,
payload: unknown,
ctx: TaskRunContext,
initOutput: any
) {
if (!onStartFn) {
return;
}
try {
await this._tracer.startActiveSpan(
name,
async (span) => {
return await onStartFn(payload, { ctx });
},
{
attributes: {
[SemanticInternalAttributes.STYLE_ICON]: "function",
},
}
);
} catch {
// Ignore errors from onStart functions
}
} }
async #callTaskCleanup(payload: unknown, ctx: TaskRunContext, init: unknown) { async #callTaskCleanup(payload: unknown, ctx: TaskRunContext, init: unknown) {
+67 -2
View File
@@ -6,6 +6,7 @@ import {
} from "@opentelemetry/semantic-conventions"; } from "@opentelemetry/semantic-conventions";
import { import {
BatchTaskRunExecutionResult, BatchTaskRunExecutionResult,
FailureFnParams,
HandleErrorFnParams, HandleErrorFnParams,
HandleErrorResult, HandleErrorResult,
InitFnParams, InitFnParams,
@@ -17,6 +18,7 @@ import {
RetryOptions, RetryOptions,
RunFnParams, RunFnParams,
SemanticInternalAttributes, SemanticInternalAttributes,
StartFnParams,
SuccessFnParams, SuccessFnParams,
TaskRunContext, TaskRunContext,
TaskRunExecutionResult, TaskRunExecutionResult,
@@ -143,15 +145,67 @@ export type TaskOptions<
* @param params - Metadata about the run. * @param params - Metadata about the run.
*/ */
run: (payload: TPayload, params: RunFnParams<TInitOutput>) => Promise<TOutput>; run: (payload: TPayload, params: RunFnParams<TInitOutput>) => Promise<TOutput>;
/**
* init is called before the run function is called. It's useful for setting up any global state.
*/
init?: (payload: TPayload, params: InitFnParams) => Promise<TInitOutput>; init?: (payload: TPayload, params: InitFnParams) => Promise<TInitOutput>;
/**
* cleanup is called after the run function has completed.
*/
cleanup?: (payload: TPayload, params: RunFnParams<TInitOutput>) => Promise<void>;
/**
* handleError is called when the run function throws an error. It can be used to modify the error or return new retry options.
*/
handleError?: ( handleError?: (
payload: TPayload, payload: TPayload,
error: unknown, error: unknown,
params: HandleErrorFnParams<TInitOutput> params: HandleErrorFnParams<TInitOutput>
) => HandleErrorResult; ) => HandleErrorResult;
cleanup?: (payload: TPayload, params: RunFnParams<TInitOutput>) => Promise<void>;
/**
* middleware allows you to run code "around" the run function. This can be useful for logging, metrics, or other cross-cutting concerns.
*
* When writing middleware, you should always call `next()` to continue the execution of the task:
*
* ```ts
* export const middlewareTask = task({
* id: "middleware-task",
* middleware: async (payload, { ctx, next }) => {
* console.log("Before run");
* await next();
* console.log("After run");
* },
* run: async (payload, { ctx }) => {}
* });
* ```
*/
middleware?: (payload: TPayload, params: MiddlewareFnParams) => Promise<void>; middleware?: (payload: TPayload, params: MiddlewareFnParams) => Promise<void>;
onSuccess?: (payload: TPayload, params: SuccessFnParams<TOutput, TInitOutput>) => Promise<void>;
/**
* onStart is called the first time a task is executed in a run (not before every retry)
*/
onStart?: (payload: TPayload, params: StartFnParams) => Promise<void>;
/**
* onSuccess is called after the run function has successfully completed.
*/
onSuccess?: (
payload: TPayload,
output: TOutput,
params: SuccessFnParams<TInitOutput>
) => Promise<void>;
/**
* onFailure is called after a task run has failed (meaning the run function threw an error and won't be retried anymore)
*/
onFailure?: (
payload: TPayload,
error: unknown,
params: FailureFnParams<TInitOutput>
) => Promise<void>;
}; };
type InvokeHandle = { type InvokeHandle = {
@@ -248,6 +302,14 @@ export interface Task<TInput = void, TOutput = any> {
batchTriggerAndWait: (items: Array<BatchItem<TInput>>) => Promise<BatchResult<TOutput>>; batchTriggerAndWait: (items: Array<BatchItem<TInput>>) => Promise<BatchResult<TOutput>>;
} }
export type TaskPayload<TTask extends Task> = TTask extends Task<infer TInput, any>
? TInput
: never;
export type TaskOutput<TTask extends Task> = TTask extends Task<any, infer TOutput>
? TOutput
: never;
type TaskRunOptions = { type TaskRunOptions = {
idempotencyKey?: string; idempotencyKey?: string;
maxAttempts?: number; maxAttempts?: number;
@@ -624,6 +686,9 @@ export function createTask<TInput = void, TOutput = unknown, TInitOutput extends
cleanup: params.cleanup, cleanup: params.cleanup,
middleware: params.middleware, middleware: params.middleware,
handleError: params.handleError, handleError: params.handleError,
onSuccess: params.onSuccess,
onFailure: params.onFailure,
onStart: params.onStart,
}, },
}); });
+200 -3
View File
@@ -1459,6 +1459,9 @@ importers:
packages/cli-v3: packages/cli-v3:
dependencies: dependencies:
'@anatine/esbuild-decorators':
specifier: ^0.2.19
version: 0.2.19(esbuild@0.19.11)
'@clack/prompts': '@clack/prompts':
specifier: ^0.7.0 specifier: ^0.7.0
version: 0.7.0 version: 0.7.0
@@ -3095,9 +3098,18 @@ importers:
openai: openai:
specifier: ^4.28.0 specifier: ^4.28.0
version: 4.28.0 version: 4.28.0
pg:
specifier: ^8.11.5
version: 8.11.5
reflect-metadata:
specifier: ^0.1.13
version: 0.1.13
stripe: stripe:
specifier: ^12.14.0 specifier: ^12.14.0
version: 12.14.0 version: 12.14.0
typeorm:
specifier: ^0.3.20
version: 0.3.20(pg@8.11.5)(ts-node@10.9.2)
yt-dlp-wrap: yt-dlp-wrap:
specifier: ^2.3.12 specifier: ^2.3.12
version: 2.3.12 version: 2.3.12
@@ -3186,6 +3198,14 @@ packages:
'@jridgewell/gen-mapping': 0.3.2 '@jridgewell/gen-mapping': 0.3.2
'@jridgewell/trace-mapping': 0.3.19 '@jridgewell/trace-mapping': 0.3.19
/@anatine/esbuild-decorators@0.2.19(esbuild@0.19.11):
resolution: {integrity: sha512-pyj6ULyMacyzpDqlnbS2OCkOqxcVgk8IqiTMRJ5CrsF8Yl1azvlX/AM6xWR8UzHKUYDlWOw5mOpos3+7KKR0Lw==}
peerDependencies:
esbuild: ~0.14.29
dependencies:
esbuild: 0.19.11
dev: false
/@angular-devkit/core@16.1.8(chokidar@3.5.3): /@angular-devkit/core@16.1.8(chokidar@3.5.3):
resolution: {integrity: sha512-dSRD/+bGanArIXkj+kaU1kDFleZeQMzmBiOXX+pK0Ah9/0Yn1VmY3RZh1zcX9vgIQXV+t7UPrTpOjaERMUtVGw==} resolution: {integrity: sha512-dSRD/+bGanArIXkj+kaU1kDFleZeQMzmBiOXX+pK0Ah9/0Yn1VmY3RZh1zcX9vgIQXV+t7UPrTpOjaERMUtVGw==}
engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'}
@@ -13130,6 +13150,10 @@ packages:
- supports-color - supports-color
dev: false dev: false
/@sqltools/formatter@1.2.5:
resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
dev: false
/@supabase/functions-js@2.1.2: /@supabase/functions-js@2.1.2:
resolution: {integrity: sha512-QCR6pwJs9exCl37bmpMisUd6mf+0SUBJ6mUpiAjEkSJ/+xW8TCuO14bvkWHADd5hElJK9MxNlMQXxSA4DRz9nQ==} resolution: {integrity: sha512-QCR6pwJs9exCl37bmpMisUd6mf+0SUBJ6mUpiAjEkSJ/+xW8TCuO14bvkWHADd5hElJK9MxNlMQXxSA4DRz9nQ==}
dependencies: dependencies:
@@ -15437,7 +15461,6 @@ packages:
/acorn-walk@8.3.2: /acorn-walk@8.3.2:
resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==}
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
dev: true
/acorn@7.4.1: /acorn@7.4.1:
resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
@@ -15649,6 +15672,11 @@ packages:
normalize-path: 3.0.0 normalize-path: 3.0.0
picomatch: 2.3.1 picomatch: 2.3.1
/app-root-path@3.1.0:
resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==}
engines: {node: '>= 6.0.0'}
dev: false
/append-field@1.0.0: /append-field@1.0.0:
resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==}
@@ -17073,6 +17101,19 @@ packages:
dependencies: dependencies:
restore-cursor: 4.0.0 restore-cursor: 4.0.0
/cli-highlight@2.1.11:
resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==}
engines: {node: '>=8.0.0', npm: '>=5.0.0'}
hasBin: true
dependencies:
chalk: 4.1.2
highlight.js: 10.7.3
mz: 2.7.0
parse5: 5.1.1
parse5-htmlparser2-tree-adapter: 6.0.1
yargs: 16.2.0
dev: false
/cli-spinners@2.9.1: /cli-spinners@2.9.1:
resolution: {integrity: sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==} resolution: {integrity: sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
@@ -17869,6 +17910,10 @@ packages:
'@babel/runtime': 7.22.5 '@babel/runtime': 7.22.5
dev: true dev: true
/dayjs@1.11.10:
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
dev: false
/deasync@0.1.28: /deasync@0.1.28:
resolution: {integrity: sha512-QqLF6inIDwiATrfROIyQtwOQxjZuek13WRYZ7donU5wJPLoP67MnYxA6QtqdvdBy2mMqv5m3UefBVdJjvevOYg==} resolution: {integrity: sha512-QqLF6inIDwiATrfROIyQtwOQxjZuek13WRYZ7donU5wJPLoP67MnYxA6QtqdvdBy2mMqv5m3UefBVdJjvevOYg==}
engines: {node: '>=0.11.0'} engines: {node: '>=0.11.0'}
@@ -21385,6 +21430,7 @@ packages:
minimatch: 9.0.3 minimatch: 9.0.3
minipass: 7.0.3 minipass: 7.0.3
path-scurry: 1.10.1 path-scurry: 1.10.1
dev: true
/glob@7.1.6: /glob@7.1.6:
resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==}
@@ -21881,6 +21927,10 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dev: true dev: true
/highlight.js@10.7.3:
resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
dev: false
/highlight.run@7.3.4: /highlight.run@7.3.4:
resolution: {integrity: sha512-Rgx+gy0tb2tH4hNzxYi/VK5pL/msaAtaQBIy8XsPHLujdSgo5OPWO6vOdjjB7ufM1l/CI2RLmlQ+L2QZOuHBjw==} resolution: {integrity: sha512-Rgx+gy0tb2tH4hNzxYi/VK5pL/msaAtaQBIy8XsPHLujdSgo5OPWO6vOdjjB7ufM1l/CI2RLmlQ+L2QZOuHBjw==}
dev: false dev: false
@@ -22979,6 +23029,7 @@ packages:
'@isaacs/cliui': 8.0.2 '@isaacs/cliui': 8.0.2
optionalDependencies: optionalDependencies:
'@pkgjs/parseargs': 0.11.0 '@pkgjs/parseargs': 0.11.0
dev: true
/jackspeak@2.3.6: /jackspeak@2.3.6:
resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
@@ -23679,7 +23730,7 @@ packages:
dependencies: dependencies:
config-chain: 1.1.13 config-chain: 1.1.13
editorconfig: 1.0.4 editorconfig: 1.0.4
glob: 10.3.3 glob: 10.3.10
js-cookie: 3.0.5 js-cookie: 3.0.5
nopt: 7.2.0 nopt: 7.2.0
dev: false dev: false
@@ -25340,6 +25391,12 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
/mkdirp@2.1.6:
resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==}
engines: {node: '>=10'}
hasBin: true
dev: false
/mlly@1.4.2: /mlly@1.4.2:
resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==}
dependencies: dependencies:
@@ -26886,6 +26943,16 @@ packages:
engines: {node: '>=6'} engines: {node: '>=6'}
dev: true dev: true
/parse5-htmlparser2-tree-adapter@6.0.1:
resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==}
dependencies:
parse5: 6.0.1
dev: false
/parse5@5.1.1:
resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==}
dev: false
/parse5@6.0.1: /parse5@6.0.1:
resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
@@ -27021,10 +27088,20 @@ packages:
is-reference: 3.0.1 is-reference: 3.0.1
dev: true dev: true
/pg-cloudflare@1.1.1:
resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==}
requiresBuild: true
dev: false
optional: true
/pg-connection-string@2.5.0: /pg-connection-string@2.5.0:
resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==} resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==}
dev: false dev: false
/pg-connection-string@2.6.4:
resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==}
dev: false
/pg-int8@1.0.1: /pg-int8@1.0.1:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
engines: {node: '>=4.0.0'} engines: {node: '>=4.0.0'}
@@ -27038,10 +27115,22 @@ packages:
pg: 8.10.0 pg: 8.10.0
dev: false dev: false
/pg-pool@3.6.2(pg@8.11.5):
resolution: {integrity: sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==}
peerDependencies:
pg: '>=8.0'
dependencies:
pg: 8.11.5
dev: false
/pg-protocol@1.6.0: /pg-protocol@1.6.0:
resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==} resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==}
dev: false dev: false
/pg-protocol@1.6.1:
resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==}
dev: false
/pg-types@2.2.0: /pg-types@2.2.0:
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
engines: {node: '>=4'} engines: {node: '>=4'}
@@ -27071,6 +27160,24 @@ packages:
pgpass: 1.0.5 pgpass: 1.0.5
dev: false dev: false
/pg@8.11.5:
resolution: {integrity: sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==}
engines: {node: '>= 8.0.0'}
peerDependencies:
pg-native: '>=3.0.1'
peerDependenciesMeta:
pg-native:
optional: true
dependencies:
pg-connection-string: 2.6.4
pg-pool: 3.6.2(pg@8.11.5)
pg-protocol: 1.6.1
pg-types: 2.2.0
pgpass: 1.0.5
optionalDependencies:
pg-cloudflare: 1.1.1
dev: false
/pgpass@1.0.5: /pgpass@1.0.5:
resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
dependencies: dependencies:
@@ -28682,6 +28789,10 @@ packages:
/reflect-metadata@0.1.13: /reflect-metadata@0.1.13:
resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==}
/reflect-metadata@0.2.2:
resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==}
dev: false
/regenerate-unicode-properties@10.1.0: /regenerate-unicode-properties@10.1.0:
resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==} resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==}
engines: {node: '>=4'} engines: {node: '>=4'}
@@ -29655,6 +29766,14 @@ packages:
/setprototypeof@1.2.0: /setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
/sha.js@2.4.11:
resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==}
hasBin: true
dependencies:
inherits: 2.0.4
safe-buffer: 5.2.1
dev: false
/sharp@0.32.6: /sharp@0.32.6:
resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==} resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==}
engines: {node: '>=14.15.0'} engines: {node: '>=14.15.0'}
@@ -31500,7 +31619,6 @@ packages:
typescript: 5.3.3 typescript: 5.3.3
v8-compile-cache-lib: 3.0.1 v8-compile-cache-lib: 3.0.1
yn: 3.1.1 yn: 3.1.1
dev: true
/ts-poet@6.6.0: /ts-poet@6.6.0:
resolution: {integrity: sha512-4vEH/wkhcjRPFOdBwIh9ItO6jOoumVLRF4aABDX5JSNEubSqwOulihxQPqai+OkuygJm3WYMInxXQX4QwVNMuw==} resolution: {integrity: sha512-4vEH/wkhcjRPFOdBwIh9ItO6jOoumVLRF4aABDX5JSNEubSqwOulihxQPqai+OkuygJm3WYMInxXQX4QwVNMuw==}
@@ -32061,6 +32179,85 @@ packages:
/typedarray@0.0.6: /typedarray@0.0.6:
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
/typeorm@0.3.20(pg@8.11.5)(ts-node@10.9.2):
resolution: {integrity: sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==}
engines: {node: '>=16.13.0'}
hasBin: true
peerDependencies:
'@google-cloud/spanner': ^5.18.0
'@sap/hana-client': ^2.12.25
better-sqlite3: ^7.1.2 || ^8.0.0 || ^9.0.0
hdb-pool: ^0.1.6
ioredis: ^5.0.4
mongodb: ^5.8.0
mssql: ^9.1.1 || ^10.0.1
mysql2: ^2.2.5 || ^3.0.1
oracledb: ^6.3.0
pg: ^8.5.1
pg-native: ^3.0.0
pg-query-stream: ^4.0.0
redis: ^3.1.1 || ^4.0.0
sql.js: ^1.4.0
sqlite3: ^5.0.3
ts-node: ^10.7.0
typeorm-aurora-data-api-driver: ^2.0.0
peerDependenciesMeta:
'@google-cloud/spanner':
optional: true
'@sap/hana-client':
optional: true
better-sqlite3:
optional: true
hdb-pool:
optional: true
ioredis:
optional: true
mongodb:
optional: true
mssql:
optional: true
mysql2:
optional: true
oracledb:
optional: true
pg:
optional: true
pg-native:
optional: true
pg-query-stream:
optional: true
redis:
optional: true
sql.js:
optional: true
sqlite3:
optional: true
ts-node:
optional: true
typeorm-aurora-data-api-driver:
optional: true
dependencies:
'@sqltools/formatter': 1.2.5
app-root-path: 3.1.0
buffer: 6.0.3
chalk: 4.1.2
cli-highlight: 2.1.11
dayjs: 1.11.10
debug: 4.3.4(supports-color@8.1.1)
dotenv: 16.4.5
glob: 10.3.10
mkdirp: 2.1.6
pg: 8.11.5
reflect-metadata: 0.2.2
sha.js: 2.4.11
ts-node: 10.9.2(@types/node@20.4.2)(typescript@5.3.3)
tslib: 2.6.2
uuid: 9.0.0
yargs: 17.7.2
transitivePeerDependencies:
- supports-color
dev: false
/typescript@4.9.4: /typescript@4.9.4:
resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==} resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==}
engines: {node: '>=4.2.0'} engines: {node: '>=4.2.0'}
+3
View File
@@ -18,7 +18,10 @@
"execa": "^8.0.1", "execa": "^8.0.1",
"msw": "^2.2.1", "msw": "^2.2.1",
"openai": "^4.28.0", "openai": "^4.28.0",
"pg": "^8.11.5",
"reflect-metadata": "^0.1.13",
"stripe": "^12.14.0", "stripe": "^12.14.0",
"typeorm": "^0.3.20",
"yt-dlp-wrap": "^2.3.12" "yt-dlp-wrap": "^2.3.12"
}, },
"devDependencies": { "devDependencies": {
-2
View File
@@ -2,6 +2,4 @@ import { logger, type HandleErrorFunction } from "@trigger.dev/sdk/v3";
export const handleError: HandleErrorFunction = async (payload, error, { ctx, retry }) => { export const handleError: HandleErrorFunction = async (payload, error, { ctx, retry }) => {
logger.log("handling error", { error }); logger.log("handling error", { error });
return { skipRetrying: true };
}; };
@@ -0,0 +1,32 @@
import { logger, task } from "@trigger.dev/sdk/v3";
import { AppDataSource, Photo } from "./orm";
export const taskThatUsesDecorators = task({
id: "taskThatUsesDecorators",
run: async (payload: { message: string }) => {
console.log("Creating a photo...");
const photo = new Photo();
photo.id = 2;
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg";
photo.views = 1;
photo.isPublished = true;
await AppDataSource.manager.save(photo);
if (Math.random() > 0.5) {
throw new Error("Failed to create photo");
}
},
onSuccess: async (payload, output, { ctx }) => {
logger.log("Photo created successfully", { output, ctx });
},
onFailure: async (payload, error, { ctx }) => {
logger.error("Failed to create photo", { error, ctx });
},
onStart: async (payload, { ctx }) => {
logger.log("Starting to create photo", { ctx });
},
});
@@ -0,0 +1,36 @@
import "reflect-metadata";
import { DataSource } from "typeorm";
import { Entity, Column, PrimaryColumn } from "typeorm";
@Entity()
export class Photo {
@PrimaryColumn()
id!: number;
@Column()
name!: string;
@Column()
description!: string;
@Column()
filename!: string;
@Column()
views!: number;
@Column()
isPublished!: boolean;
}
export const AppDataSource = new DataSource({
type: "postgres",
host: "localhost",
port: 5432,
username: "postgres",
password: "postgres",
database: "v3-catalog",
entities: [Photo],
synchronize: true,
logging: false,
});
+13
View File
@@ -1,5 +1,6 @@
import type { TriggerConfig } from "@trigger.dev/sdk/v3"; import type { TriggerConfig } from "@trigger.dev/sdk/v3";
import { OpenAIInstrumentation } from "@traceloop/instrumentation-openai"; import { OpenAIInstrumentation } from "@traceloop/instrumentation-openai";
import { AppDataSource } from "@/trigger/orm";
export { handleError } from "./src/handleError"; export { handleError } from "./src/handleError";
@@ -21,4 +22,16 @@ export const config: TriggerConfig = {
instrumentations: [new OpenAIInstrumentation()], instrumentations: [new OpenAIInstrumentation()],
logLevel: "log", logLevel: "log",
enableConsoleLogging: true, enableConsoleLogging: true,
onStart: async (payload, { ctx }) => {
if (ctx.organization.id === "clsylhs0v0002dyx75xx4pod1") {
console.log("Initializing the app data source");
await AppDataSource.initialize();
}
},
onFailure: async (payload, error, { ctx }) => {
console.log(`Task ${ctx.task.id} failed ${ctx.run.id}`);
throw error;
},
}; };
+3 -1
View File
@@ -10,6 +10,8 @@
"@trigger.dev/core/v3/*": ["../../packages/core/src/v3/*"], "@trigger.dev/core/v3/*": ["../../packages/core/src/v3/*"],
"@trigger.dev/sdk/v3": ["../../packages/trigger-sdk/src/v3/index"], "@trigger.dev/sdk/v3": ["../../packages/trigger-sdk/src/v3/index"],
"@trigger.dev/sdk/v3/*": ["../../packages/trigger-sdk/src/v3/*"] "@trigger.dev/sdk/v3/*": ["../../packages/trigger-sdk/src/v3/*"]
} },
"emitDecoratorMetadata": true,
"experimentalDecorators": true
} }
} }