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:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@trigger.dev/sdk": patch
|
||||
"trigger.dev": patch
|
||||
"@trigger.dev/core": patch
|
||||
---
|
||||
|
||||
Implement task.onSuccess/onFailure and config.onSuccess/onFailure
|
||||
@@ -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
@@ -79,7 +79,7 @@ export const taskWithRetries = task({
|
||||
maxTimeoutInMs: 30_000,
|
||||
randomize: false,
|
||||
},
|
||||
run: async ({ payload, ctx }) => {
|
||||
run: async (payload: any, { ctx }) => {
|
||||
//...
|
||||
},
|
||||
});
|
||||
@@ -99,7 +99,7 @@ export const oneAtATime = task({
|
||||
queue: {
|
||||
concurrencyLimit: 1,
|
||||
},
|
||||
run: async ({ payload, ctx }) => {
|
||||
run: async (payload: any, { ctx }) => {
|
||||
//...
|
||||
},
|
||||
});
|
||||
@@ -116,7 +116,7 @@ export const heavyTask = task({
|
||||
cpu: 2,
|
||||
memory: 4,
|
||||
},
|
||||
run: async ({ payload, ctx }) => {
|
||||
run: async (payload: any, { ctx }) => {
|
||||
//...
|
||||
},
|
||||
});
|
||||
@@ -124,27 +124,143 @@ export const heavyTask = task({
|
||||
|
||||
### `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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
|
||||
+121
-1
@@ -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.
|
||||
|
||||
## 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
|
||||
|
||||
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`">
|
||||
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```json default path
|
||||
@@ -140,6 +179,87 @@ Prisma works by generating a client from your `prisma.schema` file. This means y
|
||||
|
||||
</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
|
||||
|
||||
If you have an issue with bundling let us know on [Discord](https://trigger.dev/discord) or [via email](https://trigger.dev/contact).
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
"test": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anatine/esbuild-decorators": "^0.2.19",
|
||||
"@clack/prompts": "^0.7.0",
|
||||
"@depot/cli": "0.0.1-cli.2.55.0",
|
||||
"@opentelemetry/api": "^1.8.0",
|
||||
|
||||
@@ -8,14 +8,13 @@ import {
|
||||
flattenAttributes,
|
||||
} from "@trigger.dev/core/v3";
|
||||
import { recordSpanException } from "@trigger.dev/core/v3/workers";
|
||||
import chalk from "chalk";
|
||||
import { Command, Option as CommandOption } from "commander";
|
||||
import { Metafile, build } from "esbuild";
|
||||
import { execa } from "execa";
|
||||
import { createHash } from "node:crypto";
|
||||
import { readFileSync } from "node:fs";
|
||||
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 terminalLink from "terminal-link";
|
||||
import invariant from "tiny-invariant";
|
||||
@@ -32,7 +31,7 @@ import {
|
||||
wrapCommandAction,
|
||||
} from "../cli/common.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 {
|
||||
detectPackageNameFromImportPath,
|
||||
@@ -43,6 +42,7 @@ import { logger } from "../utilities/logger.js";
|
||||
import { createTaskFileImports, gatherTaskFiles } from "../utilities/taskFiles";
|
||||
import { login } from "./login";
|
||||
|
||||
import { esbuildDecorators } from "@anatine/esbuild-decorators";
|
||||
import { Glob, GlobOptions } from "glob";
|
||||
import type { SetOptional } from "type-fest";
|
||||
import { bundleDependenciesPlugin, workerSetupImportConfigPlugin } from "../utilities/build";
|
||||
@@ -53,12 +53,12 @@ import {
|
||||
parseBuildErrorStack,
|
||||
parseNpmInstallError,
|
||||
} from "../utilities/deployErrors";
|
||||
import { safeJsonParse } from "../utilities/safeJsonParse";
|
||||
import { JavascriptProject } from "../utilities/javascriptProject";
|
||||
import { docs, getInTouch } from "../utilities/links";
|
||||
import { cliRootPath } from "../utilities/resolveInternalFilePath";
|
||||
import { safeJsonParse } from "../utilities/safeJsonParse";
|
||||
import { escapeImportPath, spinner } from "../utilities/windows";
|
||||
import { updateTriggerPackages } from "./update";
|
||||
import { docs, getInTouch } from "../utilities/links";
|
||||
|
||||
const DeployCommandOptions = CommonCommandOptions.extend({
|
||||
skipTypecheck: z.boolean().default(false),
|
||||
@@ -1137,6 +1137,11 @@ async function compileProject(
|
||||
config.tsconfigPath
|
||||
),
|
||||
workerSetupImportConfigPlugin(configPath),
|
||||
esbuildDecorators({
|
||||
tsconfig: config.tsconfigPath,
|
||||
tsx: true,
|
||||
force: false,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ import { findUp, pathExists } from "find-up";
|
||||
import { cliRootPath } from "../utilities/resolveInternalFilePath";
|
||||
import { escapeImportPath } from "../utilities/windows";
|
||||
import { updateTriggerPackages } from "./update";
|
||||
import { esbuildDecorators } from "@anatine/esbuild-decorators";
|
||||
|
||||
let apiClient: CliApiClient | undefined;
|
||||
|
||||
@@ -409,6 +410,11 @@ function useDev({
|
||||
config.tsconfigPath
|
||||
),
|
||||
workerSetupImportConfigPlugin(configPath),
|
||||
esbuildDecorators({
|
||||
tsconfig: config.tsconfigPath,
|
||||
tsx: true,
|
||||
force: false,
|
||||
}),
|
||||
{
|
||||
name: "trigger.dev v3",
|
||||
setup(build) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { createTempDir, readJSONFileSync } from "./fileSystem.js";
|
||||
import { logger } from "./logger.js";
|
||||
import { findTriggerDirectories, resolveTriggerDirectories } from "./taskFiles.js";
|
||||
import { build } from "esbuild";
|
||||
import { esbuildDecorators } from "@anatine/esbuild-decorators";
|
||||
|
||||
function getGlobalConfigFolderPath() {
|
||||
const configDir = xdgAppPaths("trigger").config();
|
||||
@@ -172,6 +173,13 @@ export async function readConfig(
|
||||
target: ["es2018", "node18"],
|
||||
outfile: builtConfigFilePath,
|
||||
logLevel: "silent",
|
||||
plugins: [
|
||||
esbuildDecorators({
|
||||
cwd: absoluteDir,
|
||||
tsx: false,
|
||||
force: false,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
// import the config file
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { FailureFnParams, InitFnParams, StartFnParams, SuccessFnParams } from ".";
|
||||
import { LogLevel } from "../logger/taskLogger";
|
||||
import { RetryOptions } from "../schemas";
|
||||
import type { InstrumentationOption } from "@opentelemetry/instrumentation";
|
||||
@@ -16,7 +17,7 @@ export interface ProjectConfig {
|
||||
* List of additional files to include in your trigger.dev bundle. e.g. ["./prisma/schema.prisma"]
|
||||
*
|
||||
* Supports glob patterns.
|
||||
*
|
||||
*
|
||||
* Note: The path separator for glob patterns is `/`, even on Windows!
|
||||
*/
|
||||
additionalFiles?: string[];
|
||||
@@ -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.
|
||||
*/
|
||||
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>;
|
||||
}
|
||||
|
||||
@@ -22,12 +22,15 @@ export type InitFnParams = Prettify<{
|
||||
ctx: Context;
|
||||
}>;
|
||||
|
||||
export type StartFnParams = Prettify<{
|
||||
ctx: Context;
|
||||
}>;
|
||||
|
||||
export type Context = TaskRunContext;
|
||||
|
||||
export type SuccessFnParams<TOutput, TInitOutput extends InitOutput> = RunFnParams<TInitOutput> &
|
||||
Prettify<{
|
||||
output: TOutput;
|
||||
}>;
|
||||
export type SuccessFnParams<TInitOutput extends InitOutput> = RunFnParams<TInitOutput>;
|
||||
|
||||
export type FailureFnParams<TInitOutput extends InitOutput> = RunFnParams<TInitOutput>;
|
||||
|
||||
export type HandleErrorFnParams<TInitOutput extends InitOutput> = RunFnParams<TInitOutput> &
|
||||
Prettify<{
|
||||
@@ -74,5 +77,8 @@ export type TaskMetadataWithFunctions = TaskMetadata & {
|
||||
error: unknown,
|
||||
params: HandleErrorFnParams<any>
|
||||
) => 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>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -90,10 +90,16 @@ export class TaskExecutor {
|
||||
|
||||
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);
|
||||
|
||||
await this.#callOnSuccessFunctions(parsedPayload, output, ctx, initOutput);
|
||||
|
||||
try {
|
||||
const stringifiedOutput = await stringifyIO(output);
|
||||
|
||||
@@ -148,6 +154,15 @@ export class TaskExecutor {
|
||||
|
||||
recordSpanException(span, handleErrorResult.error ?? runError);
|
||||
|
||||
if (handleErrorResult.status !== "retry") {
|
||||
await this.#callOnFailureFunctions(
|
||||
parsedPayload,
|
||||
handleErrorResult.error ?? runError,
|
||||
ctx,
|
||||
initOutput
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
id: execution.run.id,
|
||||
ok: false,
|
||||
@@ -218,16 +233,194 @@ export class TaskExecutor {
|
||||
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;
|
||||
|
||||
if (!initFn) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return this._tracer.startActiveSpan("init", async (span) => {
|
||||
return await initFn(payload, { ctx });
|
||||
});
|
||||
return this._tracer.startActiveSpan(
|
||||
"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) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
} from "@opentelemetry/semantic-conventions";
|
||||
import {
|
||||
BatchTaskRunExecutionResult,
|
||||
FailureFnParams,
|
||||
HandleErrorFnParams,
|
||||
HandleErrorResult,
|
||||
InitFnParams,
|
||||
@@ -17,6 +18,7 @@ import {
|
||||
RetryOptions,
|
||||
RunFnParams,
|
||||
SemanticInternalAttributes,
|
||||
StartFnParams,
|
||||
SuccessFnParams,
|
||||
TaskRunContext,
|
||||
TaskRunExecutionResult,
|
||||
@@ -143,15 +145,67 @@ export type TaskOptions<
|
||||
* @param params - Metadata about the run.
|
||||
*/
|
||||
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>;
|
||||
|
||||
/**
|
||||
* 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?: (
|
||||
payload: TPayload,
|
||||
error: unknown,
|
||||
params: HandleErrorFnParams<TInitOutput>
|
||||
) => 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>;
|
||||
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 = {
|
||||
@@ -248,6 +302,14 @@ export interface Task<TInput = void, TOutput = any> {
|
||||
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 = {
|
||||
idempotencyKey?: string;
|
||||
maxAttempts?: number;
|
||||
@@ -624,6 +686,9 @@ export function createTask<TInput = void, TOutput = unknown, TInitOutput extends
|
||||
cleanup: params.cleanup,
|
||||
middleware: params.middleware,
|
||||
handleError: params.handleError,
|
||||
onSuccess: params.onSuccess,
|
||||
onFailure: params.onFailure,
|
||||
onStart: params.onStart,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Generated
+200
-3
@@ -1459,6 +1459,9 @@ importers:
|
||||
|
||||
packages/cli-v3:
|
||||
dependencies:
|
||||
'@anatine/esbuild-decorators':
|
||||
specifier: ^0.2.19
|
||||
version: 0.2.19(esbuild@0.19.11)
|
||||
'@clack/prompts':
|
||||
specifier: ^0.7.0
|
||||
version: 0.7.0
|
||||
@@ -3095,9 +3098,18 @@ importers:
|
||||
openai:
|
||||
specifier: ^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:
|
||||
specifier: ^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:
|
||||
specifier: ^2.3.12
|
||||
version: 2.3.12
|
||||
@@ -3186,6 +3198,14 @@ packages:
|
||||
'@jridgewell/gen-mapping': 0.3.2
|
||||
'@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):
|
||||
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'}
|
||||
@@ -13130,6 +13150,10 @@ packages:
|
||||
- supports-color
|
||||
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:
|
||||
resolution: {integrity: sha512-QCR6pwJs9exCl37bmpMisUd6mf+0SUBJ6mUpiAjEkSJ/+xW8TCuO14bvkWHADd5hElJK9MxNlMQXxSA4DRz9nQ==}
|
||||
dependencies:
|
||||
@@ -15437,7 +15461,6 @@ packages:
|
||||
/acorn-walk@8.3.2:
|
||||
resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
dev: true
|
||||
|
||||
/acorn@7.4.1:
|
||||
resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
|
||||
@@ -15649,6 +15672,11 @@ packages:
|
||||
normalize-path: 3.0.0
|
||||
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:
|
||||
resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==}
|
||||
|
||||
@@ -17073,6 +17101,19 @@ packages:
|
||||
dependencies:
|
||||
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:
|
||||
resolution: {integrity: sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -17869,6 +17910,10 @@ packages:
|
||||
'@babel/runtime': 7.22.5
|
||||
dev: true
|
||||
|
||||
/dayjs@1.11.10:
|
||||
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
|
||||
dev: false
|
||||
|
||||
/deasync@0.1.28:
|
||||
resolution: {integrity: sha512-QqLF6inIDwiATrfROIyQtwOQxjZuek13WRYZ7donU5wJPLoP67MnYxA6QtqdvdBy2mMqv5m3UefBVdJjvevOYg==}
|
||||
engines: {node: '>=0.11.0'}
|
||||
@@ -21385,6 +21430,7 @@ packages:
|
||||
minimatch: 9.0.3
|
||||
minipass: 7.0.3
|
||||
path-scurry: 1.10.1
|
||||
dev: true
|
||||
|
||||
/glob@7.1.6:
|
||||
resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==}
|
||||
@@ -21881,6 +21927,10 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/highlight.js@10.7.3:
|
||||
resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
|
||||
dev: false
|
||||
|
||||
/highlight.run@7.3.4:
|
||||
resolution: {integrity: sha512-Rgx+gy0tb2tH4hNzxYi/VK5pL/msaAtaQBIy8XsPHLujdSgo5OPWO6vOdjjB7ufM1l/CI2RLmlQ+L2QZOuHBjw==}
|
||||
dev: false
|
||||
@@ -22979,6 +23029,7 @@ packages:
|
||||
'@isaacs/cliui': 8.0.2
|
||||
optionalDependencies:
|
||||
'@pkgjs/parseargs': 0.11.0
|
||||
dev: true
|
||||
|
||||
/jackspeak@2.3.6:
|
||||
resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
|
||||
@@ -23679,7 +23730,7 @@ packages:
|
||||
dependencies:
|
||||
config-chain: 1.1.13
|
||||
editorconfig: 1.0.4
|
||||
glob: 10.3.3
|
||||
glob: 10.3.10
|
||||
js-cookie: 3.0.5
|
||||
nopt: 7.2.0
|
||||
dev: false
|
||||
@@ -25340,6 +25391,12 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
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:
|
||||
resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==}
|
||||
dependencies:
|
||||
@@ -26886,6 +26943,16 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
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:
|
||||
resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
|
||||
|
||||
@@ -27021,10 +27088,20 @@ packages:
|
||||
is-reference: 3.0.1
|
||||
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:
|
||||
resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==}
|
||||
dev: false
|
||||
|
||||
/pg-connection-string@2.6.4:
|
||||
resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==}
|
||||
dev: false
|
||||
|
||||
/pg-int8@1.0.1:
|
||||
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
@@ -27038,10 +27115,22 @@ packages:
|
||||
pg: 8.10.0
|
||||
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:
|
||||
resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==}
|
||||
dev: false
|
||||
|
||||
/pg-protocol@1.6.1:
|
||||
resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==}
|
||||
dev: false
|
||||
|
||||
/pg-types@2.2.0:
|
||||
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -27071,6 +27160,24 @@ packages:
|
||||
pgpass: 1.0.5
|
||||
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:
|
||||
resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
|
||||
dependencies:
|
||||
@@ -28682,6 +28789,10 @@ packages:
|
||||
/reflect-metadata@0.1.13:
|
||||
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:
|
||||
resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -29655,6 +29766,14 @@ packages:
|
||||
/setprototypeof@1.2.0:
|
||||
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:
|
||||
resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==}
|
||||
engines: {node: '>=14.15.0'}
|
||||
@@ -31500,7 +31619,6 @@ packages:
|
||||
typescript: 5.3.3
|
||||
v8-compile-cache-lib: 3.0.1
|
||||
yn: 3.1.1
|
||||
dev: true
|
||||
|
||||
/ts-poet@6.6.0:
|
||||
resolution: {integrity: sha512-4vEH/wkhcjRPFOdBwIh9ItO6jOoumVLRF4aABDX5JSNEubSqwOulihxQPqai+OkuygJm3WYMInxXQX4QwVNMuw==}
|
||||
@@ -32061,6 +32179,85 @@ packages:
|
||||
/typedarray@0.0.6:
|
||||
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:
|
||||
resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==}
|
||||
engines: {node: '>=4.2.0'}
|
||||
|
||||
@@ -18,7 +18,10 @@
|
||||
"execa": "^8.0.1",
|
||||
"msw": "^2.2.1",
|
||||
"openai": "^4.28.0",
|
||||
"pg": "^8.11.5",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"stripe": "^12.14.0",
|
||||
"typeorm": "^0.3.20",
|
||||
"yt-dlp-wrap": "^2.3.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -2,6 +2,4 @@ import { logger, type HandleErrorFunction } from "@trigger.dev/sdk/v3";
|
||||
|
||||
export const handleError: HandleErrorFunction = async (payload, error, { ctx, retry }) => {
|
||||
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,
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { TriggerConfig } from "@trigger.dev/sdk/v3";
|
||||
import { OpenAIInstrumentation } from "@traceloop/instrumentation-openai";
|
||||
import { AppDataSource } from "@/trigger/orm";
|
||||
|
||||
export { handleError } from "./src/handleError";
|
||||
|
||||
@@ -21,4 +22,16 @@ export const config: TriggerConfig = {
|
||||
instrumentations: [new OpenAIInstrumentation()],
|
||||
logLevel: "log",
|
||||
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;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
"@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/*"]
|
||||
}
|
||||
},
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user