Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 321378db4f | |||
| c8b10ce188 | |||
| 57ed2b2d0d | |||
| fa3a22eb78 | |||
| a1bc15d18b | |||
| 3f48dd0a68 | |||
| 5c71868a73 | |||
| 3ce6decef4 | |||
| ab007f2708 | |||
| 8412680863 | |||
| a90908df6e | |||
| a82b86210f | |||
| 0367a92731 | |||
| d230cf622f | |||
| a1bf073b43 | |||
| 5588108b97 | |||
| b9bcecffe3 | |||
| 59075f5fc2 | |||
| fa942fc9f5 | |||
| 47a8a557cf | |||
| b51d0b5399 | |||
| 763c84e3b3 | |||
| dfb00e84e9 | |||
| b5a64545bb | |||
| 3cf3eaff48 | |||
| 4ca758a3de | |||
| da81537009 | |||
| 7c1b13ba3b | |||
| 4b654e5b3b |
@@ -64,8 +64,92 @@ jobs:
|
||||
- name: 🔎 Type check
|
||||
run: pnpm run typecheck --filter webapp
|
||||
|
||||
unitTests:
|
||||
name: Unit Tests
|
||||
runs-on: buildjet-4vcpu-ubuntu-2204
|
||||
steps:
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@v2.2.4
|
||||
with:
|
||||
version: 7.18
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: buildjet/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: "pnpm"
|
||||
|
||||
- name: 📥 Download deps
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
pnpm run test
|
||||
|
||||
e2e:
|
||||
name: e2e Tests
|
||||
runs-on: buildjet-4vcpu-ubuntu-2204
|
||||
steps:
|
||||
- name: ⬇️ Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: ⎔ Setup pnpm
|
||||
uses: pnpm/action-setup@v2.2.4
|
||||
with:
|
||||
version: 7.18
|
||||
|
||||
- name: ⎔ Setup node
|
||||
uses: buildjet/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: "pnpm"
|
||||
|
||||
- name: 📥 Download deps
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install --with-deps
|
||||
|
||||
- name: Run Playwright tests
|
||||
run: |
|
||||
# Setup environment variables
|
||||
cp ./.env.example ./.env
|
||||
cp ./examples/nextjs-test/.env.example ./examples/nextjs-test/.env.local
|
||||
cp ./packages/database/.env.example ./packages/database/.env
|
||||
|
||||
# Build packages
|
||||
pnpm run build --filter @examples/nextjs-test^...
|
||||
pnpm --filter @trigger.dev/database generate
|
||||
|
||||
# Move trigger-cli bin to correct place
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
# Execute tests
|
||||
pnpm run docker
|
||||
pnpm run db:migrate
|
||||
pnpm run db:seed
|
||||
pnpm run test:e2e
|
||||
|
||||
# Cleanup
|
||||
pnpm run docker:stop
|
||||
|
||||
- name: Upload Playwright report
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
|
||||
publish:
|
||||
needs: [typecheck]
|
||||
needs: [typecheck, unitTests, e2e]
|
||||
runs-on: buildjet-4vcpu-ubuntu-2204
|
||||
outputs:
|
||||
version: ${{ steps.get_version.outputs.version }}
|
||||
|
||||
@@ -48,4 +48,7 @@ apps/**/public/build
|
||||
.sentryclirc
|
||||
.buildt
|
||||
|
||||
**/tmp/
|
||||
**/tmp/
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
|
||||
@@ -169,6 +169,52 @@ pnpm exec trigger-cli dev
|
||||
|
||||
9. Please remember to delete the temporary project you created after you've tested the changes, and before you raise a PR.
|
||||
|
||||
## Running end-to-end webapp tests
|
||||
|
||||
To run the end-to-end tests, follow the steps below:
|
||||
|
||||
1. Set up environment variables (copy example envs into the correct place)
|
||||
|
||||
```sh
|
||||
cp ./.env.example ./.env
|
||||
cp ./examples/nextjs-test/.env.example ./examples/nextjs-test/.env.local
|
||||
cp ./packages/database/.env.example ./packages/database/.env
|
||||
```
|
||||
|
||||
2. Set up dependencies
|
||||
|
||||
```sh
|
||||
# Build packages
|
||||
pnpm run build --filter @examples/nextjs-test^...
|
||||
pnpm --filter @trigger.dev/database generate
|
||||
|
||||
# Move trigger-cli bin to correct place
|
||||
pnpm install --frozen-lockfile
|
||||
```
|
||||
|
||||
3. Set up the database
|
||||
|
||||
```sh
|
||||
pnpm run docker
|
||||
pnpm run db:migrate
|
||||
pnpm run db:seed
|
||||
```
|
||||
|
||||
4. Run the end-to-end tests
|
||||
|
||||
```sh
|
||||
pnpm run test:e2e
|
||||
```
|
||||
|
||||
### Cleanup
|
||||
|
||||
The end-to-end tests use a `setup` and `teardown` script to seed the database with test data. If the test runner doesn't exit cleanly, then the database can be left in a state where the tests can't run because the `setup` script will try to create data that already exists. If this happens, you can manually delete the `users` and `organizations` from the database using prisma studio:
|
||||
|
||||
```sh
|
||||
# With the database running (i.e. pnpm run docker)
|
||||
pnpm run db:studio
|
||||
```
|
||||
|
||||
## Add sample jobs
|
||||
|
||||
The [examples/jobs-starter](./examples/jobs-starter/) project defines simple jobs you can get started with.
|
||||
|
||||
@@ -96,6 +96,13 @@ export default function Page() {
|
||||
};
|
||||
}, [selected, clients]);
|
||||
|
||||
const isAnyClientFullyConfigured = useMemo(() => {
|
||||
return clients.some((client) => {
|
||||
const { DEVELOPMENT, PRODUCTION } = client.endpoints;
|
||||
return PRODUCTION.state === "configured" && DEVELOPMENT.state === PRODUCTION.state;
|
||||
});
|
||||
}, [clients]);
|
||||
|
||||
const organization = useOrganization();
|
||||
const project = useProject();
|
||||
|
||||
@@ -120,7 +127,7 @@ export default function Page() {
|
||||
<PageDescription>API Keys and endpoints for your environments.</PageDescription>
|
||||
</PageHeader>
|
||||
<PageBody>
|
||||
<Help defaultOpen>
|
||||
<Help defaultOpen={!isAnyClientFullyConfigured}>
|
||||
{(open) => (
|
||||
<div className={cn("grid h-full gap-4", open ? "grid-cols-2" : "grid-cols-1")}>
|
||||
<div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { DeliverEmail } from "emails";
|
||||
import { EmailClient } from "emails";
|
||||
import type { SendEmailOptions } from "remix-auth-email-link";
|
||||
import { redirect } from "remix-typedjson";
|
||||
import { env } from "~/env.server";
|
||||
import type { User } from "~/models/user.server";
|
||||
import type { AuthUser } from "./authUser";
|
||||
@@ -14,6 +15,11 @@ const client = new EmailClient({
|
||||
});
|
||||
|
||||
export async function sendMagicLinkEmail(options: SendEmailOptions<AuthUser>): Promise<void> {
|
||||
// Auto redirect when in development mode
|
||||
if (env.NODE_ENV === "development") {
|
||||
throw redirect(options.magicLink);
|
||||
}
|
||||
|
||||
return client.send({
|
||||
email: "magic_link",
|
||||
to: options.emailAddress,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { EventDispatcher, EventRecord } from "@trigger.dev/database";
|
||||
import type { EventFilter } from "@trigger.dev/core";
|
||||
import { EventFilterSchema } from "@trigger.dev/core";
|
||||
import { EventFilterSchema, eventFilterMatches } from "@trigger.dev/core";
|
||||
import { $transaction, PrismaClientOrTransaction, prisma } from "~/db.server";
|
||||
import { logger } from "~/services/logger.server";
|
||||
import { workerQueue } from "../worker.server";
|
||||
@@ -124,29 +124,6 @@ export class EventMatcher {
|
||||
}
|
||||
|
||||
public matches(filter: EventFilter) {
|
||||
return patternMatches(this.event, filter);
|
||||
return eventFilterMatches(this.event, filter);
|
||||
}
|
||||
}
|
||||
|
||||
function patternMatches(payload: any, pattern: any): boolean {
|
||||
for (const [patternKey, patternValue] of Object.entries(pattern)) {
|
||||
const payloadValue = payload[patternKey];
|
||||
|
||||
if (Array.isArray(patternValue)) {
|
||||
if (patternValue.length > 0 && !patternValue.includes(payloadValue)) {
|
||||
return false;
|
||||
}
|
||||
} else if (typeof patternValue === "object") {
|
||||
if (Array.isArray(payloadValue)) {
|
||||
if (!payloadValue.some((item) => patternMatches(item, patternValue))) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!patternMatches(payloadValue, patternValue)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@ const supabase = new SupabaseManagement({
|
||||
});
|
||||
|
||||
new Job(client, {
|
||||
id: "on-new-users",
|
||||
name: "On New Users",
|
||||
id: "on-new-todos",
|
||||
name: "On New Todos",
|
||||
version: "0.1.1",
|
||||
trigger: supabase.onInsert({
|
||||
table: "users",
|
||||
trigger: supabase.onInserted({
|
||||
table: "todos",
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
},
|
||||
@@ -33,11 +33,11 @@ const supabase = new SupabaseManagement({
|
||||
});
|
||||
|
||||
new Job(client, {
|
||||
id: "on-new-users",
|
||||
name: "On New Users",
|
||||
id: "on-new-todos",
|
||||
name: "On New Todos",
|
||||
version: "0.1.1",
|
||||
trigger: supabase.onInsert({
|
||||
table: "users",
|
||||
trigger: supabase.onInserted({
|
||||
table: "todos",
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
},
|
||||
|
||||
@@ -180,7 +180,7 @@ function getWorkerQueue() {
|
||||
},
|
||||
},
|
||||
deliverHttpSourceRequest: {
|
||||
maxAttempts: 5,
|
||||
maxAttempts: 25,
|
||||
handler: async (payload, job) => {
|
||||
const service = new DeliverHttpSourceRequestService();
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"start": "cross-env NODE_ENV=production node --max-old-space-size=8192 ./build/server.js",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"db:seed": "node prisma/seed.js",
|
||||
"db:seed:local": "ts-node prisma/seed.ts",
|
||||
"generate:sourcemaps": "remix build --sourcemap",
|
||||
"clean:sourcemaps": "run-s clean:sourcemaps:*",
|
||||
"clean:sourcemaps:public": "rimraf ./build/**/*.map",
|
||||
@@ -61,7 +62,7 @@
|
||||
"@trigger.dev/companyicons": "^1.5.14",
|
||||
"@trigger.dev/database": "workspace:*",
|
||||
"@trigger.dev/core": "workspace:*",
|
||||
"@trigger.dev/sdk": "workspace:2.0.4",
|
||||
"@trigger.dev/sdk": "workspace:*",
|
||||
"@uiw/react-codemirror": "^4.19.5",
|
||||
"class-variance-authority": "^0.5.2",
|
||||
"clsx": "^1.2.1",
|
||||
|
||||
@@ -12,8 +12,19 @@ module.exports = {
|
||||
ignoredRouteFiles: ["**/.*"],
|
||||
devServerPort: 8002,
|
||||
serverModuleFormat: "cjs",
|
||||
serverDependenciesToBundle: ["marked", "axios", "@trigger.dev/core", "emails", "highlight.run"],
|
||||
serverDependenciesToBundle: [
|
||||
"marked",
|
||||
"axios",
|
||||
"@trigger.dev/core",
|
||||
"@trigger.dev/sdk",
|
||||
"emails",
|
||||
"highlight.run",
|
||||
],
|
||||
watchPaths: async () => {
|
||||
return ["../../packages/core/src/**/*", "../../packages/emails/src/**/*"];
|
||||
return [
|
||||
"../../packages/core/src/**/*",
|
||||
"../../packages/trigger-sdk/src/**/*",
|
||||
"../../packages/emails/src/**/*",
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
---
|
||||
title: Using Manual Setup
|
||||
description: How to Manually Initialize Trigger.dev in your Next.js project
|
||||
---
|
||||
|
||||
<Accordion defaultOpen title="Don't have a Next.js project yet to add Trigger.dev to? No problem, you can complete the Manual Setup using a blank Next.js project:">
|
||||
Create a blank project by running the `create-next-app` command in your terminal:
|
||||
|
||||
```bash
|
||||
npx create-next-app@latest
|
||||
```
|
||||
|
||||
Trigger.dev works with either the Pages or App Router configuration.
|
||||
|
||||
</Accordion>
|
||||
|
||||
## Installing Required Packages
|
||||
To begin, install the necessary packages in your Next.js project directory. You can choose one of the following package managers:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash npm
|
||||
|
||||
npm i @trigger.dev/sdk @trigger-dev/nextjs
|
||||
|
||||
```
|
||||
|
||||
```bash pnpm
|
||||
pnpm install @trigger.dev/sdk @trigger-dev/nextjs
|
||||
|
||||
```
|
||||
|
||||
```bash yarn
|
||||
yarn add @trigger.dev/sdk @trigger-dev/nextjs
|
||||
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<Note>Ensure that you execute this command within a Next.js project.</Note>
|
||||
|
||||
## Obtaining the Development API Key
|
||||
To locate your development API key, login to the [Trigger.dev
|
||||
dashboard](https://cloud.trigger.dev) and select the Project you want to
|
||||
connect to. Then click on the Environments & API Keys tab in the left menu.
|
||||
You can copy your development API Key from the field at the top of this page.
|
||||
(Your development key will start with `tr_dev_`).
|
||||
|
||||
## Adding Environment Variables
|
||||
Create a `.env.local` file at the root of your project and include your Trigger API key and URL like this:
|
||||
|
||||
```bash
|
||||
|
||||
TRIGGER_API_KEY=ENTER_YOUR_DEVELOPMENT_API_KEY_HERE
|
||||
TRIGGER_API_URL=https://cloud.trigger.dev
|
||||
|
||||
```
|
||||
|
||||
Replace `ENTER_YOUR_DEVELOPMENT_API_KEY_HERE` with the actual API key obtained from the previous step.
|
||||
|
||||
## Configuring the Trigger Client
|
||||
|
||||
To set up the Trigger Client for your project, follow these steps:
|
||||
|
||||
1. **Create Configuration File:**
|
||||
|
||||
In your project directory, create a configuration file named `trigger.ts` or `trigger.js`, depending on whether your project uses TypeScript (`.ts`) or JavaScript (`.js`).
|
||||
|
||||
2. **Choose Directory:**
|
||||
|
||||
Depending on your project structure, choose the appropriate directory for the configuration file. If your project uses a `src` directory, create the file within it. Otherwise, create it directly in the project root.
|
||||
|
||||
3. **Add Configuration Code:**
|
||||
|
||||
Open the configuration file you created and add the following code:
|
||||
|
||||
```typescript
|
||||
// trigger.ts (for TypeScript) or trigger.js (for JavaScript)
|
||||
|
||||
import { TriggerClient } from "@trigger.dev/sdk";
|
||||
|
||||
export const client = new TriggerClient({
|
||||
id: "my-app",
|
||||
apiKey: process.env.TRIGGER_API_KEY,
|
||||
apiUrl: process.env.TRIGGER_API_URL,
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
Replace **"my-app"** with an appropriate identifier for your project. The **apiKey** and **apiUrl** are obtained from the environment variables you set earlier.
|
||||
|
||||
4. **File Location:**
|
||||
|
||||
Depending on your project structure, save the configuration file in the appropriate location:
|
||||
- If your project uses a **src** directory, save the file within the **src** directory.
|
||||
- If your project does not use a **src** directory, save the file in the project root.
|
||||
|
||||
**Example Directory Structure with src:**
|
||||
|
||||
```
|
||||
project-root/
|
||||
├── src/
|
||||
├── trigger.ts
|
||||
├── other files...
|
||||
```
|
||||
|
||||
**Example Directory Structure without src:**
|
||||
|
||||
```
|
||||
project-root/
|
||||
├── trigger.ts
|
||||
├── other files...
|
||||
```
|
||||
|
||||
By following these steps, you'll configure the Trigger Client to work with your project, regardless of whether you have a separate **src** directory and whether you're using TypeScript or JavaScript files.
|
||||
|
||||
## Creating the API Route
|
||||
|
||||
To establish an API route for interacting with Trigger.dev, follow these steps based on your project's file type and structure
|
||||
|
||||
<Tabs>
|
||||
<Tab title="App Directory">
|
||||
1. Create a new file named `route.(ts/js)` within the `app/api/trigger/` directory.
|
||||
2. Add the following code to `route.(ts/js)`:
|
||||
|
||||
```typescript
|
||||
import { createAppRoute } from "@trigger.dev/nextjs";
|
||||
import { client } from "@/trigger";
|
||||
import "@/Jobs";
|
||||
|
||||
export const { POST, dynamic } = createAppRoute(client);
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="Pages Directory">
|
||||
1. Create a new file named `trigger.(ts/js)` within the `pages/api/` directory.
|
||||
2. Add the following code to `trigger.(ts/js)`:
|
||||
|
||||
```
|
||||
import { createPagesRoute } from "@trigger.dev/nextjs";
|
||||
import { client } from "@/trigger";
|
||||
import "@/Jobs";
|
||||
|
||||
const { handler, config } = createPagesRoute(client);
|
||||
export { config };
|
||||
export default handler;
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Warning>In the code blocks, replace "@/trigger" with the appropriate path to your Trigger Client configuration file, and adjust the path to the Jobs folder accordingly. Make sure to provide the correct paths if your project isn't utilizing the Next.js alias feature.</Warning>
|
||||
|
||||
## Creating the Example Job
|
||||
1. Create a folder named `Jobs` alongside your `app` or `pages` directory
|
||||
2. Inside the `Jobs` folder, add two files named `example.(ts/js)` and `index.(ts/js)`.
|
||||
<CodeGroup>
|
||||
|
||||
```typescript example.(ts/js)
|
||||
import { eventTrigger } from "@trigger.dev/sdk";
|
||||
import { client } from "@/trigger";
|
||||
|
||||
// your first job
|
||||
client.defineJob({
|
||||
id: "example-job",
|
||||
name: "Example Job",
|
||||
version: "0.0.1",
|
||||
trigger: eventTrigger({
|
||||
name: "example.event",
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
await io.logger.info("Hello world!", { payload });
|
||||
|
||||
return {
|
||||
message: "Hello world!",
|
||||
};
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```typescript index.ts/index.(ts/js)
|
||||
// import all your job files here
|
||||
|
||||
export * from "./examples"
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Additonal Job Definations
|
||||
You can define more job definitions by creating additional files in the `Jobs` folder and exporting them in `index` file.
|
||||
|
||||
For example, in `index.(ts/js)`, you can export other job files like this:
|
||||
|
||||
```typescript
|
||||
// import all your job files here
|
||||
|
||||
export * from "./examples"
|
||||
export * from "./other-job-file";
|
||||
```
|
||||
|
||||
## Adding Configuration to `package.json`
|
||||
|
||||
Inside the `package.json` file, add the following configuration under the root object:
|
||||
|
||||
```json
|
||||
"trigger.dev": {
|
||||
"endpointId": "my-app"
|
||||
}
|
||||
```
|
||||
|
||||
Your `package.json` file might look something like this:
|
||||
```json
|
||||
{
|
||||
"name": "my-app",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
// ... other dependencies
|
||||
},
|
||||
"trigger.dev": {
|
||||
"endpointId": "my-app"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Replace **"my-app"** with the appropriate identifier you used during the step for creating the Trigger Client.
|
||||
|
||||
## Next Steps
|
||||
|
||||
Start your Next.js project locally locally, and then execute the `dev` CLI command to run Trigger.dev locally. You should run this command every time you want to use Trigger.dev locally.
|
||||
|
||||

|
||||
|
||||
<Warning>
|
||||
Make sure your Next.js site is running locally before continuing. You must
|
||||
also leave this `dev` terminal command running while you develop.
|
||||
</Warning>
|
||||
|
||||
In a **new terminal window or tab** run:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash npm
|
||||
npx @trigger.dev/cli@latest dev
|
||||
```
|
||||
|
||||
```bash pnpm
|
||||
pnpm dlx @trigger.dev/cli@latest dev
|
||||
```
|
||||
|
||||
```bash yarn
|
||||
yarn dlx @trigger.dev/cli@latest dev
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
<br />
|
||||
<Note>
|
||||
You can optionally pass the port if you're not running on 3000 by adding
|
||||
`--port 3001` to the end
|
||||
</Note>
|
||||
|
||||
<Tip>If your existing Next.js project utilizes middleware and you encounter any issues, such as potential conflicts with Trigger.dev, it's recommended to refer to the troubleshooting guide at [Middleware](/documentation/guides/platforms/nextjs#middleware) for assistance. This guide can help you address any concerns related to middleware conflicts and ensure the smooth functioning of your project with Trigger.dev.</Tip>
|
||||
@@ -57,7 +57,7 @@ First, cd into your Next.js project, then run the `@trigger.dev/cli init` comman
|
||||
npx @trigger.dev/cli@latest init -t "https://<your render app name>.onrender.com"
|
||||
```
|
||||
|
||||
When it asks for your development API key, head over to your self-hosted Trigger.dev dashboard and select the initial project you created when signing up, and head to the `Environments & API Keys` page to copy your `dev` API key:
|
||||
When it asks for your development API key, head over to your self-hosted Trigger.dev dashboard and select the initial project you created when signing up, and head to the `Environments & API Keys` page to copy your `dev` **SERVER** API key:
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ Once you've created an account, follow the steps to:
|
||||
1. Go to the "Environments & API Keys" page in your project.
|
||||

|
||||
|
||||
2. Copy the `DEV` API key.
|
||||
2. Copy the `DEV` **SERVER** API key.
|
||||

|
||||
|
||||
## Run the CLI `init` command
|
||||
|
||||
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 69 KiB |
@@ -83,6 +83,8 @@ Tasks that are marked as "long-running" can last longer than your serverless tim
|
||||
| `listModels` | Lists the available models. |
|
||||
| `createEdit` | Edits a given text prompt. |
|
||||
| `createImage` | Generates images from textual descriptions. |
|
||||
| `createImageEdit` | Creates an edited or extended image given an original image and a prompt |
|
||||
| `createImageVariation` | Creates a variation of a given image. |
|
||||
| `createEmbedding` | Generates embeddings for a given text. |
|
||||
| `createFile` | Uploads a file to the OpenAI API. |
|
||||
| `listFiles` | Lists the uploaded files. |
|
||||
|
||||
@@ -7,8 +7,8 @@ description: "Interact with your Supabase project using the Supabase JS Client."
|
||||
Our `@trigger.dev/supabase` package provides an integration that wraps the [@supabase/supabase-js](https://github.com/supabase/supabase-js) package, allowing you to run tasks to interact with your Supabase project.
|
||||
|
||||
<Note>
|
||||
If you want to trigger jobs based on changes in your Supabase database, you'll
|
||||
need to use the [Supabase Management API](../management) integration
|
||||
If you want to trigger jobs based on changes in your Supabase database, you'll need to use the
|
||||
[Supabase Management API](/integrations/apis/supabase/management) integration
|
||||
</Note>
|
||||
|
||||
## Usage
|
||||
@@ -26,8 +26,7 @@ const supabase = new Supabase({
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Never expose the `service_role` key in a browser or anywhere where a user can
|
||||
see it.
|
||||
Never expose the `service_role` key in a browser or anywhere where a user can see it.
|
||||
</Warning>
|
||||
|
||||
You can then use the `supabase` integration to run tasks in your jobs:
|
||||
@@ -39,21 +38,17 @@ client.defineJob({
|
||||
supabase,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
const { data: users, error } = await io.supabase.runTask(
|
||||
"find-users",
|
||||
async (db) => {
|
||||
return db.from("users").select("*");
|
||||
}
|
||||
);
|
||||
const { data: todos, error } = await io.supabase.runTask("find-todos", async (db) => {
|
||||
return db.from("todos").select("*");
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
<Note>
|
||||
By using `runTask` instead of the `@supabase/supabase-js` client directly
|
||||
inside your job run, you'll be able to create tasks that can be run
|
||||
idempotently and also retried. For more, see our guide on
|
||||
[Resumability](http://localhost:3050/documentation/concepts/resumability)
|
||||
By using `runTask` instead of the `@supabase/supabase-js` client directly inside your job run,
|
||||
you'll be able to create tasks that can be run idempotently and also retried. For more, see our
|
||||
guide on [Resumability](http://localhost:3050/documentation/concepts/resumability)
|
||||
</Note>
|
||||
|
||||
You can also choose to throw an error if the query fails and abort the job run:
|
||||
@@ -65,8 +60,8 @@ client.defineJob({
|
||||
supabase,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
const users = await io.supabase.runTask("find-users", async (db) => {
|
||||
const { data, error } = await db.from("users").select("*");
|
||||
const todos = await io.supabase.runTask("find-todos", async (db) => {
|
||||
const { data, error } = await db.from("todos").select("*");
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
@@ -83,10 +78,7 @@ The `db` object passed to the callback is an instance of the [@supabase/supabase
|
||||
- [Invoking Functions](https://supabase.com/docs/reference/javascript/functions-invoke)
|
||||
- [Storage](https://supabase.com/docs/reference/javascript/storage-createbucket)
|
||||
|
||||
<Warning>
|
||||
Currently we do not support Supabase Realtime (such as subscribing to a
|
||||
channel)
|
||||
</Warning>
|
||||
<Warning>Currently we do not support Supabase Realtime (such as subscribing to a channel)</Warning>
|
||||
|
||||
## Typescript Support
|
||||
|
||||
@@ -108,15 +100,15 @@ client.defineJob({
|
||||
supabase,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
const users = await io.supabase.runTask("find-users", async (db) => {
|
||||
const { data, error } = await db.from("users").select("*");
|
||||
const todos = await io.supabase.runTask("find-todos", async (db) => {
|
||||
const { data, error } = await db.from("todos").select("*");
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
// users is now typed as User[] instead of any[]
|
||||
// todos is now typed as Todo[] instead of any[]
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
@@ -126,7 +126,7 @@ client.defineJob({
|
||||
id: "supabase-trigger",
|
||||
name: "Supabase Trigger",
|
||||
trigger: db.onInserted({
|
||||
table: "users",
|
||||
table: "todos",
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
// payload is the database webhook body (see https://supabase.com/docs/guides/database/webhooks#payload)
|
||||
@@ -137,20 +137,6 @@ client.defineJob({
|
||||
You can add additional filters to the trigger by passing a `filter` object:
|
||||
|
||||
```ts
|
||||
client.defineJob({
|
||||
id: "supabase-trigger",
|
||||
name: "Supabase Trigger",
|
||||
trigger: db.onUpdated({
|
||||
table: "users",
|
||||
filter: {
|
||||
country: ["USA", "Canada"], // This will only trigger the job if the user.country is USA or Canada
|
||||
},
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
// payload is the database webhook body (see https://supabase.com/docs/guides/database/webhooks#payload)
|
||||
},
|
||||
});
|
||||
|
||||
client.defineJob({
|
||||
id: "supabase-trigger",
|
||||
name: "Supabase Trigger",
|
||||
@@ -172,6 +158,31 @@ client.defineJob({
|
||||
});
|
||||
```
|
||||
|
||||
You can also listen for multiple different events using the `on` trigger:
|
||||
|
||||
```ts
|
||||
client.defineJob({
|
||||
id: "supabase-trigger",
|
||||
name: "Supabase Trigger",
|
||||
trigger: db.on({
|
||||
table: "todos",
|
||||
events: ["INSERT", "UPDATE"] // Trigger on both insert and update events
|
||||
filter: {
|
||||
record: {
|
||||
is_completed: [false],
|
||||
},
|
||||
},
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
if (payload.type === "INSERT") {
|
||||
// payload will be typed as the INSERT payload
|
||||
} else {
|
||||
// payload will be typed as the UPDATE payload
|
||||
}
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
<Note>
|
||||
We will only create at most 1 database webhook per table, to limit resource usage when writing to
|
||||
your database. This means we cannot support scoping updated triggers to specific columns.
|
||||
@@ -196,10 +207,10 @@ client.defineJob({
|
||||
id: "supabase-trigger",
|
||||
name: "Supabase Trigger",
|
||||
trigger: db.onUpdated({
|
||||
table: "users",
|
||||
table: "todos",
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
// payload.record and payload.old_record are now correctly typed to match the users table
|
||||
// payload.record and payload.old_record are now correctly typed to match the todos table
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
]
|
||||
},
|
||||
"documentation/guides/cli",
|
||||
"documentation/guides/manual",
|
||||
"documentation/guides/running-jobs",
|
||||
{
|
||||
"group": "Using the Dashboard",
|
||||
|
||||
@@ -2,8 +2,18 @@
|
||||
|
||||
This project is meant to be used to create a catalog of jobs, usually to test something in an integration or the SDK.
|
||||
|
||||
## Setup
|
||||
|
||||
You will need to create a `.env` file. You can duplicate the `.env.example` file and set your local `TRIGGER_API_KEY` value.
|
||||
|
||||
### Running
|
||||
|
||||
You need to build the CLI:
|
||||
|
||||
```sh
|
||||
pnpm run build --filter @trigger.dev/cli
|
||||
```
|
||||
|
||||
Each file in `src` is a separate set of jobs that can be run separately. For example, the `src/stripe.ts` file can be run with:
|
||||
|
||||
```sh
|
||||
@@ -15,7 +25,7 @@ This will open up a local server using `express` on port 8080. Then in a new ter
|
||||
|
||||
```sh
|
||||
cd examples/job-catalog
|
||||
pnpm run trigger:dev
|
||||
pnpm run dev:trigger
|
||||
```
|
||||
|
||||
### Adding a new file
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"scripts": {
|
||||
"stripe": "nodemon --watch src/stripe.ts -r tsconfig-paths/register -r dotenv/config src/stripe.ts",
|
||||
"supabase": "nodemon --watch src/supabase.ts -r tsconfig-paths/register -r dotenv/config src/supabase.ts",
|
||||
"supabase:types": "npx supabase gen types typescript --project-id $SUPABASE_PROJECT_ID --schema public --schema auth --schema storage > src/supabase-types.ts",
|
||||
"dev:trigger": "trigger-cli dev --port 8080"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -0,0 +1,843 @@
|
||||
export type Json =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| { [key: string]: Json | undefined }
|
||||
| Json[]
|
||||
|
||||
export interface Database {
|
||||
auth: {
|
||||
Tables: {
|
||||
audit_log_entries: {
|
||||
Row: {
|
||||
created_at: string | null
|
||||
id: string
|
||||
instance_id: string | null
|
||||
ip_address: string
|
||||
payload: Json | null
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string | null
|
||||
id: string
|
||||
instance_id?: string | null
|
||||
ip_address?: string
|
||||
payload?: Json | null
|
||||
}
|
||||
Update: {
|
||||
created_at?: string | null
|
||||
id?: string
|
||||
instance_id?: string | null
|
||||
ip_address?: string
|
||||
payload?: Json | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
flow_state: {
|
||||
Row: {
|
||||
auth_code: string
|
||||
authentication_method: string
|
||||
code_challenge: string
|
||||
code_challenge_method: Database["auth"]["Enums"]["code_challenge_method"]
|
||||
created_at: string | null
|
||||
id: string
|
||||
provider_access_token: string | null
|
||||
provider_refresh_token: string | null
|
||||
provider_type: string
|
||||
updated_at: string | null
|
||||
user_id: string | null
|
||||
}
|
||||
Insert: {
|
||||
auth_code: string
|
||||
authentication_method: string
|
||||
code_challenge: string
|
||||
code_challenge_method: Database["auth"]["Enums"]["code_challenge_method"]
|
||||
created_at?: string | null
|
||||
id: string
|
||||
provider_access_token?: string | null
|
||||
provider_refresh_token?: string | null
|
||||
provider_type: string
|
||||
updated_at?: string | null
|
||||
user_id?: string | null
|
||||
}
|
||||
Update: {
|
||||
auth_code?: string
|
||||
authentication_method?: string
|
||||
code_challenge?: string
|
||||
code_challenge_method?: Database["auth"]["Enums"]["code_challenge_method"]
|
||||
created_at?: string | null
|
||||
id?: string
|
||||
provider_access_token?: string | null
|
||||
provider_refresh_token?: string | null
|
||||
provider_type?: string
|
||||
updated_at?: string | null
|
||||
user_id?: string | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
identities: {
|
||||
Row: {
|
||||
created_at: string | null
|
||||
email: string | null
|
||||
id: string
|
||||
identity_data: Json
|
||||
last_sign_in_at: string | null
|
||||
provider: string
|
||||
updated_at: string | null
|
||||
user_id: string
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string | null
|
||||
email?: string | null
|
||||
id: string
|
||||
identity_data: Json
|
||||
last_sign_in_at?: string | null
|
||||
provider: string
|
||||
updated_at?: string | null
|
||||
user_id: string
|
||||
}
|
||||
Update: {
|
||||
created_at?: string | null
|
||||
email?: string | null
|
||||
id?: string
|
||||
identity_data?: Json
|
||||
last_sign_in_at?: string | null
|
||||
provider?: string
|
||||
updated_at?: string | null
|
||||
user_id?: string
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "identities_user_id_fkey"
|
||||
columns: ["user_id"]
|
||||
referencedRelation: "users"
|
||||
referencedColumns: ["id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
instances: {
|
||||
Row: {
|
||||
created_at: string | null
|
||||
id: string
|
||||
raw_base_config: string | null
|
||||
updated_at: string | null
|
||||
uuid: string | null
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string | null
|
||||
id: string
|
||||
raw_base_config?: string | null
|
||||
updated_at?: string | null
|
||||
uuid?: string | null
|
||||
}
|
||||
Update: {
|
||||
created_at?: string | null
|
||||
id?: string
|
||||
raw_base_config?: string | null
|
||||
updated_at?: string | null
|
||||
uuid?: string | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
mfa_amr_claims: {
|
||||
Row: {
|
||||
authentication_method: string
|
||||
created_at: string
|
||||
id: string
|
||||
session_id: string
|
||||
updated_at: string
|
||||
}
|
||||
Insert: {
|
||||
authentication_method: string
|
||||
created_at: string
|
||||
id: string
|
||||
session_id: string
|
||||
updated_at: string
|
||||
}
|
||||
Update: {
|
||||
authentication_method?: string
|
||||
created_at?: string
|
||||
id?: string
|
||||
session_id?: string
|
||||
updated_at?: string
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "mfa_amr_claims_session_id_fkey"
|
||||
columns: ["session_id"]
|
||||
referencedRelation: "sessions"
|
||||
referencedColumns: ["id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
mfa_challenges: {
|
||||
Row: {
|
||||
created_at: string
|
||||
factor_id: string
|
||||
id: string
|
||||
ip_address: unknown
|
||||
verified_at: string | null
|
||||
}
|
||||
Insert: {
|
||||
created_at: string
|
||||
factor_id: string
|
||||
id: string
|
||||
ip_address: unknown
|
||||
verified_at?: string | null
|
||||
}
|
||||
Update: {
|
||||
created_at?: string
|
||||
factor_id?: string
|
||||
id?: string
|
||||
ip_address?: unknown
|
||||
verified_at?: string | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "mfa_challenges_auth_factor_id_fkey"
|
||||
columns: ["factor_id"]
|
||||
referencedRelation: "mfa_factors"
|
||||
referencedColumns: ["id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
mfa_factors: {
|
||||
Row: {
|
||||
created_at: string
|
||||
factor_type: Database["auth"]["Enums"]["factor_type"]
|
||||
friendly_name: string | null
|
||||
id: string
|
||||
secret: string | null
|
||||
status: Database["auth"]["Enums"]["factor_status"]
|
||||
updated_at: string
|
||||
user_id: string
|
||||
}
|
||||
Insert: {
|
||||
created_at: string
|
||||
factor_type: Database["auth"]["Enums"]["factor_type"]
|
||||
friendly_name?: string | null
|
||||
id: string
|
||||
secret?: string | null
|
||||
status: Database["auth"]["Enums"]["factor_status"]
|
||||
updated_at: string
|
||||
user_id: string
|
||||
}
|
||||
Update: {
|
||||
created_at?: string
|
||||
factor_type?: Database["auth"]["Enums"]["factor_type"]
|
||||
friendly_name?: string | null
|
||||
id?: string
|
||||
secret?: string | null
|
||||
status?: Database["auth"]["Enums"]["factor_status"]
|
||||
updated_at?: string
|
||||
user_id?: string
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "mfa_factors_user_id_fkey"
|
||||
columns: ["user_id"]
|
||||
referencedRelation: "users"
|
||||
referencedColumns: ["id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
refresh_tokens: {
|
||||
Row: {
|
||||
created_at: string | null
|
||||
id: number
|
||||
instance_id: string | null
|
||||
parent: string | null
|
||||
revoked: boolean | null
|
||||
session_id: string | null
|
||||
token: string | null
|
||||
updated_at: string | null
|
||||
user_id: string | null
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string | null
|
||||
id?: number
|
||||
instance_id?: string | null
|
||||
parent?: string | null
|
||||
revoked?: boolean | null
|
||||
session_id?: string | null
|
||||
token?: string | null
|
||||
updated_at?: string | null
|
||||
user_id?: string | null
|
||||
}
|
||||
Update: {
|
||||
created_at?: string | null
|
||||
id?: number
|
||||
instance_id?: string | null
|
||||
parent?: string | null
|
||||
revoked?: boolean | null
|
||||
session_id?: string | null
|
||||
token?: string | null
|
||||
updated_at?: string | null
|
||||
user_id?: string | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "refresh_tokens_session_id_fkey"
|
||||
columns: ["session_id"]
|
||||
referencedRelation: "sessions"
|
||||
referencedColumns: ["id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
saml_providers: {
|
||||
Row: {
|
||||
attribute_mapping: Json | null
|
||||
created_at: string | null
|
||||
entity_id: string
|
||||
id: string
|
||||
metadata_url: string | null
|
||||
metadata_xml: string
|
||||
sso_provider_id: string
|
||||
updated_at: string | null
|
||||
}
|
||||
Insert: {
|
||||
attribute_mapping?: Json | null
|
||||
created_at?: string | null
|
||||
entity_id: string
|
||||
id: string
|
||||
metadata_url?: string | null
|
||||
metadata_xml: string
|
||||
sso_provider_id: string
|
||||
updated_at?: string | null
|
||||
}
|
||||
Update: {
|
||||
attribute_mapping?: Json | null
|
||||
created_at?: string | null
|
||||
entity_id?: string
|
||||
id?: string
|
||||
metadata_url?: string | null
|
||||
metadata_xml?: string
|
||||
sso_provider_id?: string
|
||||
updated_at?: string | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "saml_providers_sso_provider_id_fkey"
|
||||
columns: ["sso_provider_id"]
|
||||
referencedRelation: "sso_providers"
|
||||
referencedColumns: ["id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
saml_relay_states: {
|
||||
Row: {
|
||||
created_at: string | null
|
||||
for_email: string | null
|
||||
from_ip_address: unknown | null
|
||||
id: string
|
||||
redirect_to: string | null
|
||||
request_id: string
|
||||
sso_provider_id: string
|
||||
updated_at: string | null
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string | null
|
||||
for_email?: string | null
|
||||
from_ip_address?: unknown | null
|
||||
id: string
|
||||
redirect_to?: string | null
|
||||
request_id: string
|
||||
sso_provider_id: string
|
||||
updated_at?: string | null
|
||||
}
|
||||
Update: {
|
||||
created_at?: string | null
|
||||
for_email?: string | null
|
||||
from_ip_address?: unknown | null
|
||||
id?: string
|
||||
redirect_to?: string | null
|
||||
request_id?: string
|
||||
sso_provider_id?: string
|
||||
updated_at?: string | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "saml_relay_states_sso_provider_id_fkey"
|
||||
columns: ["sso_provider_id"]
|
||||
referencedRelation: "sso_providers"
|
||||
referencedColumns: ["id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
schema_migrations: {
|
||||
Row: {
|
||||
version: string
|
||||
}
|
||||
Insert: {
|
||||
version: string
|
||||
}
|
||||
Update: {
|
||||
version?: string
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
sessions: {
|
||||
Row: {
|
||||
aal: Database["auth"]["Enums"]["aal_level"] | null
|
||||
created_at: string | null
|
||||
factor_id: string | null
|
||||
id: string
|
||||
not_after: string | null
|
||||
updated_at: string | null
|
||||
user_id: string
|
||||
}
|
||||
Insert: {
|
||||
aal?: Database["auth"]["Enums"]["aal_level"] | null
|
||||
created_at?: string | null
|
||||
factor_id?: string | null
|
||||
id: string
|
||||
not_after?: string | null
|
||||
updated_at?: string | null
|
||||
user_id: string
|
||||
}
|
||||
Update: {
|
||||
aal?: Database["auth"]["Enums"]["aal_level"] | null
|
||||
created_at?: string | null
|
||||
factor_id?: string | null
|
||||
id?: string
|
||||
not_after?: string | null
|
||||
updated_at?: string | null
|
||||
user_id?: string
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "sessions_user_id_fkey"
|
||||
columns: ["user_id"]
|
||||
referencedRelation: "users"
|
||||
referencedColumns: ["id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
sso_domains: {
|
||||
Row: {
|
||||
created_at: string | null
|
||||
domain: string
|
||||
id: string
|
||||
sso_provider_id: string
|
||||
updated_at: string | null
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string | null
|
||||
domain: string
|
||||
id: string
|
||||
sso_provider_id: string
|
||||
updated_at?: string | null
|
||||
}
|
||||
Update: {
|
||||
created_at?: string | null
|
||||
domain?: string
|
||||
id?: string
|
||||
sso_provider_id?: string
|
||||
updated_at?: string | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "sso_domains_sso_provider_id_fkey"
|
||||
columns: ["sso_provider_id"]
|
||||
referencedRelation: "sso_providers"
|
||||
referencedColumns: ["id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
sso_providers: {
|
||||
Row: {
|
||||
created_at: string | null
|
||||
id: string
|
||||
resource_id: string | null
|
||||
updated_at: string | null
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string | null
|
||||
id: string
|
||||
resource_id?: string | null
|
||||
updated_at?: string | null
|
||||
}
|
||||
Update: {
|
||||
created_at?: string | null
|
||||
id?: string
|
||||
resource_id?: string | null
|
||||
updated_at?: string | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
users: {
|
||||
Row: {
|
||||
aud: string | null
|
||||
banned_until: string | null
|
||||
confirmation_sent_at: string | null
|
||||
confirmation_token: string | null
|
||||
confirmed_at: string | null
|
||||
created_at: string | null
|
||||
deleted_at: string | null
|
||||
email: string | null
|
||||
email_change: string | null
|
||||
email_change_confirm_status: number | null
|
||||
email_change_sent_at: string | null
|
||||
email_change_token_current: string | null
|
||||
email_change_token_new: string | null
|
||||
email_confirmed_at: string | null
|
||||
encrypted_password: string | null
|
||||
id: string
|
||||
instance_id: string | null
|
||||
invited_at: string | null
|
||||
is_sso_user: boolean
|
||||
is_super_admin: boolean | null
|
||||
last_sign_in_at: string | null
|
||||
phone: string | null
|
||||
phone_change: string | null
|
||||
phone_change_sent_at: string | null
|
||||
phone_change_token: string | null
|
||||
phone_confirmed_at: string | null
|
||||
raw_app_meta_data: Json | null
|
||||
raw_user_meta_data: Json | null
|
||||
reauthentication_sent_at: string | null
|
||||
reauthentication_token: string | null
|
||||
recovery_sent_at: string | null
|
||||
recovery_token: string | null
|
||||
role: string | null
|
||||
updated_at: string | null
|
||||
}
|
||||
Insert: {
|
||||
aud?: string | null
|
||||
banned_until?: string | null
|
||||
confirmation_sent_at?: string | null
|
||||
confirmation_token?: string | null
|
||||
confirmed_at?: string | null
|
||||
created_at?: string | null
|
||||
deleted_at?: string | null
|
||||
email?: string | null
|
||||
email_change?: string | null
|
||||
email_change_confirm_status?: number | null
|
||||
email_change_sent_at?: string | null
|
||||
email_change_token_current?: string | null
|
||||
email_change_token_new?: string | null
|
||||
email_confirmed_at?: string | null
|
||||
encrypted_password?: string | null
|
||||
id: string
|
||||
instance_id?: string | null
|
||||
invited_at?: string | null
|
||||
is_sso_user?: boolean
|
||||
is_super_admin?: boolean | null
|
||||
last_sign_in_at?: string | null
|
||||
phone?: string | null
|
||||
phone_change?: string | null
|
||||
phone_change_sent_at?: string | null
|
||||
phone_change_token?: string | null
|
||||
phone_confirmed_at?: string | null
|
||||
raw_app_meta_data?: Json | null
|
||||
raw_user_meta_data?: Json | null
|
||||
reauthentication_sent_at?: string | null
|
||||
reauthentication_token?: string | null
|
||||
recovery_sent_at?: string | null
|
||||
recovery_token?: string | null
|
||||
role?: string | null
|
||||
updated_at?: string | null
|
||||
}
|
||||
Update: {
|
||||
aud?: string | null
|
||||
banned_until?: string | null
|
||||
confirmation_sent_at?: string | null
|
||||
confirmation_token?: string | null
|
||||
confirmed_at?: string | null
|
||||
created_at?: string | null
|
||||
deleted_at?: string | null
|
||||
email?: string | null
|
||||
email_change?: string | null
|
||||
email_change_confirm_status?: number | null
|
||||
email_change_sent_at?: string | null
|
||||
email_change_token_current?: string | null
|
||||
email_change_token_new?: string | null
|
||||
email_confirmed_at?: string | null
|
||||
encrypted_password?: string | null
|
||||
id?: string
|
||||
instance_id?: string | null
|
||||
invited_at?: string | null
|
||||
is_sso_user?: boolean
|
||||
is_super_admin?: boolean | null
|
||||
last_sign_in_at?: string | null
|
||||
phone?: string | null
|
||||
phone_change?: string | null
|
||||
phone_change_sent_at?: string | null
|
||||
phone_change_token?: string | null
|
||||
phone_confirmed_at?: string | null
|
||||
raw_app_meta_data?: Json | null
|
||||
raw_user_meta_data?: Json | null
|
||||
reauthentication_sent_at?: string | null
|
||||
reauthentication_token?: string | null
|
||||
recovery_sent_at?: string | null
|
||||
recovery_token?: string | null
|
||||
role?: string | null
|
||||
updated_at?: string | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
}
|
||||
Views: {
|
||||
[_ in never]: never
|
||||
}
|
||||
Functions: {
|
||||
email: {
|
||||
Args: Record<PropertyKey, never>
|
||||
Returns: string
|
||||
}
|
||||
jwt: {
|
||||
Args: Record<PropertyKey, never>
|
||||
Returns: Json
|
||||
}
|
||||
role: {
|
||||
Args: Record<PropertyKey, never>
|
||||
Returns: string
|
||||
}
|
||||
uid: {
|
||||
Args: Record<PropertyKey, never>
|
||||
Returns: string
|
||||
}
|
||||
}
|
||||
Enums: {
|
||||
aal_level: "aal1" | "aal2" | "aal3"
|
||||
code_challenge_method: "s256" | "plain"
|
||||
factor_status: "unverified" | "verified"
|
||||
factor_type: "totp" | "webauthn"
|
||||
}
|
||||
CompositeTypes: {
|
||||
[_ in never]: never
|
||||
}
|
||||
}
|
||||
public: {
|
||||
Tables: {
|
||||
todos: {
|
||||
Row: {
|
||||
id: number
|
||||
inserted_at: string
|
||||
is_complete: boolean | null
|
||||
task: string | null
|
||||
user_id: string
|
||||
}
|
||||
Insert: {
|
||||
id?: number
|
||||
inserted_at?: string
|
||||
is_complete?: boolean | null
|
||||
task?: string | null
|
||||
user_id: string
|
||||
}
|
||||
Update: {
|
||||
id?: number
|
||||
inserted_at?: string
|
||||
is_complete?: boolean | null
|
||||
task?: string | null
|
||||
user_id?: string
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "todos_user_id_fkey"
|
||||
columns: ["user_id"]
|
||||
referencedRelation: "users"
|
||||
referencedColumns: ["id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Views: {
|
||||
[_ in never]: never
|
||||
}
|
||||
Functions: {
|
||||
[_ in never]: never
|
||||
}
|
||||
Enums: {
|
||||
[_ in never]: never
|
||||
}
|
||||
CompositeTypes: {
|
||||
[_ in never]: never
|
||||
}
|
||||
}
|
||||
storage: {
|
||||
Tables: {
|
||||
buckets: {
|
||||
Row: {
|
||||
allowed_mime_types: string[] | null
|
||||
avif_autodetection: boolean | null
|
||||
created_at: string | null
|
||||
file_size_limit: number | null
|
||||
id: string
|
||||
name: string
|
||||
owner: string | null
|
||||
public: boolean | null
|
||||
updated_at: string | null
|
||||
}
|
||||
Insert: {
|
||||
allowed_mime_types?: string[] | null
|
||||
avif_autodetection?: boolean | null
|
||||
created_at?: string | null
|
||||
file_size_limit?: number | null
|
||||
id: string
|
||||
name: string
|
||||
owner?: string | null
|
||||
public?: boolean | null
|
||||
updated_at?: string | null
|
||||
}
|
||||
Update: {
|
||||
allowed_mime_types?: string[] | null
|
||||
avif_autodetection?: boolean | null
|
||||
created_at?: string | null
|
||||
file_size_limit?: number | null
|
||||
id?: string
|
||||
name?: string
|
||||
owner?: string | null
|
||||
public?: boolean | null
|
||||
updated_at?: string | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "buckets_owner_fkey"
|
||||
columns: ["owner"]
|
||||
referencedRelation: "users"
|
||||
referencedColumns: ["id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
migrations: {
|
||||
Row: {
|
||||
executed_at: string | null
|
||||
hash: string
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
Insert: {
|
||||
executed_at?: string | null
|
||||
hash: string
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
Update: {
|
||||
executed_at?: string | null
|
||||
hash?: string
|
||||
id?: number
|
||||
name?: string
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
objects: {
|
||||
Row: {
|
||||
bucket_id: string | null
|
||||
created_at: string | null
|
||||
id: string
|
||||
last_accessed_at: string | null
|
||||
metadata: Json | null
|
||||
name: string | null
|
||||
owner: string | null
|
||||
path_tokens: string[] | null
|
||||
updated_at: string | null
|
||||
version: string | null
|
||||
}
|
||||
Insert: {
|
||||
bucket_id?: string | null
|
||||
created_at?: string | null
|
||||
id?: string
|
||||
last_accessed_at?: string | null
|
||||
metadata?: Json | null
|
||||
name?: string | null
|
||||
owner?: string | null
|
||||
path_tokens?: string[] | null
|
||||
updated_at?: string | null
|
||||
version?: string | null
|
||||
}
|
||||
Update: {
|
||||
bucket_id?: string | null
|
||||
created_at?: string | null
|
||||
id?: string
|
||||
last_accessed_at?: string | null
|
||||
metadata?: Json | null
|
||||
name?: string | null
|
||||
owner?: string | null
|
||||
path_tokens?: string[] | null
|
||||
updated_at?: string | null
|
||||
version?: string | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "objects_bucketId_fkey"
|
||||
columns: ["bucket_id"]
|
||||
referencedRelation: "buckets"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
{
|
||||
foreignKeyName: "objects_owner_fkey"
|
||||
columns: ["owner"]
|
||||
referencedRelation: "users"
|
||||
referencedColumns: ["id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Views: {
|
||||
[_ in never]: never
|
||||
}
|
||||
Functions: {
|
||||
can_insert_object: {
|
||||
Args: {
|
||||
bucketid: string
|
||||
name: string
|
||||
owner: string
|
||||
metadata: Json
|
||||
}
|
||||
Returns: undefined
|
||||
}
|
||||
extension: {
|
||||
Args: {
|
||||
name: string
|
||||
}
|
||||
Returns: string
|
||||
}
|
||||
filename: {
|
||||
Args: {
|
||||
name: string
|
||||
}
|
||||
Returns: string
|
||||
}
|
||||
foldername: {
|
||||
Args: {
|
||||
name: string
|
||||
}
|
||||
Returns: unknown
|
||||
}
|
||||
get_size_by_bucket: {
|
||||
Args: Record<PropertyKey, never>
|
||||
Returns: {
|
||||
size: number
|
||||
bucket_id: string
|
||||
}[]
|
||||
}
|
||||
search: {
|
||||
Args: {
|
||||
prefix: string
|
||||
bucketname: string
|
||||
limits?: number
|
||||
levels?: number
|
||||
offsets?: number
|
||||
search?: string
|
||||
sortcolumn?: string
|
||||
sortorder?: string
|
||||
}
|
||||
Returns: {
|
||||
name: string
|
||||
id: string
|
||||
updated_at: string
|
||||
created_at: string
|
||||
last_accessed_at: string
|
||||
metadata: Json
|
||||
}[]
|
||||
}
|
||||
}
|
||||
Enums: {
|
||||
[_ in never]: never
|
||||
}
|
||||
CompositeTypes: {
|
||||
[_ in never]: never
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,26 @@
|
||||
import { TriggerClient, eventTrigger } from "@trigger.dev/sdk";
|
||||
import { TriggerClient } from "@trigger.dev/sdk";
|
||||
import { createExpressServer } from "@trigger.dev/express";
|
||||
import { z } from "zod";
|
||||
import { SupabaseManagement } from "@trigger.dev/supabase";
|
||||
import { Supabase, SupabaseManagement } from "@trigger.dev/supabase";
|
||||
import { OpenAI } from "@trigger.dev/openai";
|
||||
import { Database } from "./supabase-types";
|
||||
|
||||
const supabaseManagement = new SupabaseManagement({
|
||||
id: "supabase-management",
|
||||
apiKey: process.env["SUPABASE_API_KEY"]!,
|
||||
});
|
||||
|
||||
const db = supabaseManagement.db(process.env["SUPABASE_ID"]!);
|
||||
const triggers = supabaseManagement.db<Database>(process.env["SUPABASE_ID"]!);
|
||||
|
||||
const supabase = new Supabase({
|
||||
id: "supabase",
|
||||
supabaseKey: process.env["SUPABASE_SERVICE_ROLE_KEY"]!,
|
||||
supabaseUrl: process.env["SUPABASE_URL"]!,
|
||||
});
|
||||
|
||||
const openai = new OpenAI({
|
||||
id: "open-ai",
|
||||
apiKey: process.env["OPENAI_API_KEY"]!,
|
||||
});
|
||||
|
||||
export const client = new TriggerClient({
|
||||
id: "job-catalog",
|
||||
@@ -24,8 +36,8 @@ client.defineJob({
|
||||
id: "supabase-management-example-1",
|
||||
name: "Supabase Management Example 1",
|
||||
version: "0.1.0",
|
||||
trigger: db.onInserted({
|
||||
table: "users",
|
||||
trigger: triggers.onInserted({
|
||||
table: "todos",
|
||||
}),
|
||||
run: async (payload, io, ctx) => {},
|
||||
});
|
||||
@@ -34,8 +46,100 @@ client.defineJob({
|
||||
id: "supabase-management-example-2",
|
||||
name: "Supabase Management Example 2",
|
||||
version: "0.1.0",
|
||||
trigger: db.onUpdated({
|
||||
table: "users",
|
||||
trigger: triggers.onUpdated({
|
||||
table: "todos",
|
||||
}),
|
||||
run: async (payload, io, ctx) => {},
|
||||
});
|
||||
|
||||
client.defineJob({
|
||||
id: "supabase-management-example-users-auth",
|
||||
name: "Supabase Management Example Users Auth",
|
||||
version: "0.1.0",
|
||||
trigger: triggers.onInserted({
|
||||
table: "users",
|
||||
schema: "auth",
|
||||
}),
|
||||
run: async (payload, io, ctx) => {},
|
||||
});
|
||||
|
||||
client.defineJob({
|
||||
id: "supabase-management-example-objects-storage",
|
||||
name: "Supabase Management Example Object Storage",
|
||||
version: "0.1.0",
|
||||
trigger: triggers.onInserted({
|
||||
schema: "storage",
|
||||
table: "objects",
|
||||
filter: {
|
||||
record: {
|
||||
bucket_id: ["example_bucket"],
|
||||
name: [
|
||||
{
|
||||
$endsWith: ".png",
|
||||
},
|
||||
],
|
||||
path_tokens: [
|
||||
{
|
||||
$includes: "images",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
integrations: {
|
||||
openai,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
if (!payload.record.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
data: { publicUrl },
|
||||
} = supabase.native.storage.from("example_bucket").getPublicUrl(payload.record.name);
|
||||
|
||||
const imageVariation = await io.openai.createImageVariation("variation-image", {
|
||||
image: publicUrl,
|
||||
n: 2,
|
||||
response_format: "url",
|
||||
size: "512x512",
|
||||
});
|
||||
|
||||
const imageEdit = await io.openai.createImageEdit("edit-image", {
|
||||
image: publicUrl,
|
||||
prompt:
|
||||
"Fill in the background to make it seem like the cat is on the moon with a beautiful view of the earth.",
|
||||
n: 2,
|
||||
response_format: "url",
|
||||
size: "512x512",
|
||||
});
|
||||
|
||||
// return imageEdit;
|
||||
},
|
||||
});
|
||||
|
||||
client.defineJob({
|
||||
id: "supabase-management-example-on",
|
||||
name: "Supabase Management Example On",
|
||||
version: "0.1.0",
|
||||
trigger: triggers.on({
|
||||
table: "todos",
|
||||
events: ["INSERT", "UPDATE"],
|
||||
}),
|
||||
integrations: {
|
||||
supabase,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
const user = await io.supabase.runTask("fetch-user", async (db) => {
|
||||
const { data, error } = await db.auth.admin.getUserById(payload.record.user_id);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return data.user;
|
||||
});
|
||||
|
||||
return user;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
TRIGGER_API_KEY=tr_dev_test-api-key
|
||||
TRIGGER_API_URL=http://localhost:3030
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
@@ -0,0 +1,34 @@
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {}
|
||||
|
||||
module.exports = nextConfig
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "@examples/nextjs-test",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "concurrently --kill-others \"pnpm dev:next\" \"wait-on http://localhost:3000 && pnpm dev:trigger\"",
|
||||
"dev:next": "next dev",
|
||||
"dev:trigger": "trigger-cli dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "13.4.12",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"@trigger.dev/sdk": "^2.0.0-next.22",
|
||||
"@trigger.dev/nextjs": "^1.0.0-next.8"
|
||||
},
|
||||
"trigger.dev": {
|
||||
"endpointId": "nextjs-test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@trigger.dev/cli": "workspace:*",
|
||||
"concurrently": "^8.2.0",
|
||||
"eslint": "8.42.0",
|
||||
"eslint-config-next": "13.4.6",
|
||||
"wait-on": "^7.0.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
|
||||
|
After Width: | Height: | Size: 629 B |
@@ -0,0 +1,9 @@
|
||||
|
||||
import { createAppRoute } from "@trigger.dev/nextjs";
|
||||
import { client } from "@/trigger";
|
||||
|
||||
// Replace this with your own jobs
|
||||
import "@/jobs/examples";
|
||||
|
||||
//this route is used to send and receive data with Trigger.dev
|
||||
export const { POST, dynamic } = createAppRoute(client);
|
||||
|
After Width: | Height: | Size: 25 KiB |
@@ -0,0 +1,107 @@
|
||||
:root {
|
||||
--max-width: 1100px;
|
||||
--border-radius: 12px;
|
||||
--font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono',
|
||||
'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro',
|
||||
'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace;
|
||||
|
||||
--foreground-rgb: 0, 0, 0;
|
||||
--background-start-rgb: 214, 219, 220;
|
||||
--background-end-rgb: 255, 255, 255;
|
||||
|
||||
--primary-glow: conic-gradient(
|
||||
from 180deg at 50% 50%,
|
||||
#16abff33 0deg,
|
||||
#0885ff33 55deg,
|
||||
#54d6ff33 120deg,
|
||||
#0071ff33 160deg,
|
||||
transparent 360deg
|
||||
);
|
||||
--secondary-glow: radial-gradient(
|
||||
rgba(255, 255, 255, 1),
|
||||
rgba(255, 255, 255, 0)
|
||||
);
|
||||
|
||||
--tile-start-rgb: 239, 245, 249;
|
||||
--tile-end-rgb: 228, 232, 233;
|
||||
--tile-border: conic-gradient(
|
||||
#00000080,
|
||||
#00000040,
|
||||
#00000030,
|
||||
#00000020,
|
||||
#00000010,
|
||||
#00000010,
|
||||
#00000080
|
||||
);
|
||||
|
||||
--callout-rgb: 238, 240, 241;
|
||||
--callout-border-rgb: 172, 175, 176;
|
||||
--card-rgb: 180, 185, 188;
|
||||
--card-border-rgb: 131, 134, 135;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--foreground-rgb: 255, 255, 255;
|
||||
--background-start-rgb: 0, 0, 0;
|
||||
--background-end-rgb: 0, 0, 0;
|
||||
|
||||
--primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));
|
||||
--secondary-glow: linear-gradient(
|
||||
to bottom right,
|
||||
rgba(1, 65, 255, 0),
|
||||
rgba(1, 65, 255, 0),
|
||||
rgba(1, 65, 255, 0.3)
|
||||
);
|
||||
|
||||
--tile-start-rgb: 2, 13, 46;
|
||||
--tile-end-rgb: 2, 5, 19;
|
||||
--tile-border: conic-gradient(
|
||||
#ffffff80,
|
||||
#ffffff40,
|
||||
#ffffff30,
|
||||
#ffffff20,
|
||||
#ffffff10,
|
||||
#ffffff10,
|
||||
#ffffff80
|
||||
);
|
||||
|
||||
--callout-rgb: 20, 20, 20;
|
||||
--callout-border-rgb: 108, 108, 108;
|
||||
--card-rgb: 100, 100, 100;
|
||||
--card-border-rgb: 200, 200, 200;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
max-width: 100vw;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
color: rgb(var(--foreground-rgb));
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
rgb(var(--background-end-rgb))
|
||||
)
|
||||
rgb(var(--background-start-rgb));
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import './globals.css'
|
||||
import { Inter } from 'next/font/google'
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] })
|
||||
|
||||
export const metadata = {
|
||||
title: 'Create Next App',
|
||||
description: 'Generated by create next app',
|
||||
}
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import Image from 'next/image'
|
||||
import styles from './page.module.css'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<div className={styles.description}>
|
||||
<p>
|
||||
Get started by editing
|
||||
<code className={styles.code}>src/app/page.js</code>
|
||||
</p>
|
||||
<div>
|
||||
<a
|
||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
By{' '}
|
||||
<Image
|
||||
src="/vercel.svg"
|
||||
alt="Vercel Logo"
|
||||
className={styles.vercelLogo}
|
||||
width={100}
|
||||
height={24}
|
||||
priority
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.center}>
|
||||
<Image
|
||||
className={styles.logo}
|
||||
src="/next.svg"
|
||||
alt="Next.js Logo"
|
||||
width={180}
|
||||
height={37}
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.grid}>
|
||||
<a
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2>
|
||||
Docs <span>-></span>
|
||||
</h2>
|
||||
<p>Find in-depth information about Next.js features and API.</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2>
|
||||
Learn <span>-></span>
|
||||
</h2>
|
||||
<p>Learn about Next.js in an interactive course with quizzes!</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2>
|
||||
Templates <span>-></span>
|
||||
</h2>
|
||||
<p>Explore the Next.js 13 playground.</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<h2>
|
||||
Deploy <span>-></span>
|
||||
</h2>
|
||||
<p>
|
||||
Instantly deploy your Next.js site to a shareable URL with Vercel.
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
.main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 6rem;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.description {
|
||||
display: inherit;
|
||||
justify-content: inherit;
|
||||
align-items: inherit;
|
||||
font-size: 0.85rem;
|
||||
max-width: var(--max-width);
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
.description a {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.description p {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
background-color: rgba(var(--callout-rgb), 0.5);
|
||||
border: 1px solid rgba(var(--callout-border-rgb), 0.3);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.code {
|
||||
font-weight: 700;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(25%, auto));
|
||||
width: var(--max-width);
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 1rem 1.2rem;
|
||||
border-radius: var(--border-radius);
|
||||
background: rgba(var(--card-rgb), 0);
|
||||
border: 1px solid rgba(var(--card-border-rgb), 0);
|
||||
transition: background 200ms, border 200ms;
|
||||
}
|
||||
|
||||
.card span {
|
||||
display: inline-block;
|
||||
transition: transform 200ms;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.7rem;
|
||||
}
|
||||
|
||||
.card p {
|
||||
margin: 0;
|
||||
opacity: 0.6;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
max-width: 30ch;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding: 4rem 0;
|
||||
}
|
||||
|
||||
.center::before {
|
||||
background: var(--secondary-glow);
|
||||
border-radius: 50%;
|
||||
width: 480px;
|
||||
height: 360px;
|
||||
margin-left: -400px;
|
||||
}
|
||||
|
||||
.center::after {
|
||||
background: var(--primary-glow);
|
||||
width: 240px;
|
||||
height: 180px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.center::before,
|
||||
.center::after {
|
||||
content: '';
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
filter: blur(45px);
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.logo {
|
||||
position: relative;
|
||||
}
|
||||
/* Enable hover only on non-touch devices */
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
.card:hover {
|
||||
background: rgba(var(--card-rgb), 0.1);
|
||||
border: 1px solid rgba(var(--card-border-rgb), 0.15);
|
||||
}
|
||||
|
||||
.card:hover span {
|
||||
transform: translateX(4px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion) {
|
||||
.card:hover span {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile */
|
||||
@media (max-width: 700px) {
|
||||
.content {
|
||||
padding: 4rem;
|
||||
}
|
||||
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
margin-bottom: 120px;
|
||||
max-width: 320px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 1rem 2.5rem;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.center {
|
||||
padding: 8rem 0 6rem;
|
||||
}
|
||||
|
||||
.center::before {
|
||||
transform: none;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.description a {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.description p,
|
||||
.description div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.description p {
|
||||
align-items: center;
|
||||
inset: 0 0 auto;
|
||||
padding: 2rem 1rem 1.4rem;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25);
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
rgba(var(--background-start-rgb), 1),
|
||||
rgba(var(--callout-rgb), 0.5)
|
||||
);
|
||||
background-clip: padding-box;
|
||||
backdrop-filter: blur(24px);
|
||||
}
|
||||
|
||||
.description div {
|
||||
align-items: flex-end;
|
||||
pointer-events: none;
|
||||
inset: auto 0 0;
|
||||
padding: 2rem;
|
||||
height: 200px;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent 0%,
|
||||
rgb(var(--background-end-rgb)) 40%
|
||||
);
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tablet and Smaller Desktop */
|
||||
@media (min-width: 701px) and (max-width: 1120px) {
|
||||
.grid {
|
||||
grid-template-columns: repeat(2, 50%);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.vercelLogo {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
.logo {
|
||||
filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
import { eventTrigger } from "@trigger.dev/sdk";
|
||||
import { client } from "@/trigger";
|
||||
|
||||
// your first job
|
||||
client.defineJob({
|
||||
id: "test-job",
|
||||
name: "Test Job One",
|
||||
version: "0.0.1",
|
||||
trigger: eventTrigger({
|
||||
name: "test.event",
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
await io.logger.info("Hello world!", { payload });
|
||||
|
||||
return {
|
||||
message: "Hello world!",
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
import { TriggerClient } from "@trigger.dev/sdk";
|
||||
|
||||
export const client = new TriggerClient({
|
||||
id: "nextjs-test",
|
||||
apiKey: process.env.TRIGGER_API_KEY,
|
||||
apiUrl: process.env.TRIGGER_API_URL,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# @trigger.dev/github
|
||||
|
||||
## 2.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [84126808]
|
||||
- @trigger.dev/integration-kit@2.0.7
|
||||
- @trigger.dev/sdk@2.0.7
|
||||
|
||||
## 2.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.0.6
|
||||
- @trigger.dev/sdk@2.0.6
|
||||
|
||||
## 2.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.0.5
|
||||
- @trigger.dev/sdk@2.0.5
|
||||
|
||||
## 2.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/github",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.7",
|
||||
"description": "The official GitHub integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -29,8 +29,8 @@
|
||||
"@octokit/request": "^6.2.5",
|
||||
"@octokit/request-error": "^4.0.1",
|
||||
"@octokit/webhooks": "^10.4.0",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.4",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.0.4",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.7",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.0.7",
|
||||
"octokit": "^2.0.14",
|
||||
"zod": "3.21.4"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
# @trigger.dev/slack
|
||||
|
||||
## 2.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 84126808: Adding createImageEdit and createImageVariation tasks to openai
|
||||
- Updated dependencies [84126808]
|
||||
- @trigger.dev/integration-kit@2.0.7
|
||||
- @trigger.dev/sdk@2.0.7
|
||||
|
||||
## 2.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.0.6
|
||||
- @trigger.dev/sdk@2.0.6
|
||||
|
||||
## 2.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.0.5
|
||||
- @trigger.dev/sdk@2.0.5
|
||||
|
||||
## 2.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/openai",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.7",
|
||||
"description": "The official OpenAI integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -25,8 +25,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"openai": "^3.3.0",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.4",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.0.4",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.7",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.0.7",
|
||||
"zod": "3.21.4"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from "openai";
|
||||
import { OpenAIIntegrationAuth } from "./types";
|
||||
import { redactString } from "@trigger.dev/sdk";
|
||||
import { Prettify, fileFromString, truncate } from "@trigger.dev/integration-kit";
|
||||
import { Prettify, fileFromString, fileFromUrl, truncate } from "@trigger.dev/integration-kit";
|
||||
import { createTaskUsageProperties, onTaskError } from "./taskUtils";
|
||||
|
||||
type OpenAIClientType = InstanceType<typeof OpenAIApi>;
|
||||
@@ -326,6 +326,156 @@ export const createImage: AuthenticatedTask<
|
||||
},
|
||||
};
|
||||
|
||||
export type CreateImageEditRequest = {
|
||||
image: string | File;
|
||||
prompt: string;
|
||||
mask?: string | File;
|
||||
n?: number;
|
||||
size?: "256x256" | "512x512" | "1024x1024";
|
||||
response_format?: "url" | "b64_json";
|
||||
user?: string;
|
||||
};
|
||||
|
||||
type CreateImageEditResponseData = Prettify<
|
||||
Awaited<ReturnType<OpenAIClientType["createImageEdit"]>>["data"]
|
||||
>;
|
||||
|
||||
export const createImageEdit: AuthenticatedTask<
|
||||
OpenAIClientType,
|
||||
Prettify<CreateImageEditRequest>,
|
||||
CreateImageEditResponseData
|
||||
> = {
|
||||
onError: onTaskError,
|
||||
run: async (params, client, task) => {
|
||||
const file = typeof params.image === "string" ? await fileFromUrl(params.image) : params.image;
|
||||
const mask = typeof params.mask === "string" ? await fileFromUrl(params.mask) : params.mask;
|
||||
|
||||
const response = await client.createImageEdit(
|
||||
file,
|
||||
params.prompt,
|
||||
mask,
|
||||
params.n,
|
||||
params.size,
|
||||
params.response_format,
|
||||
params.user
|
||||
);
|
||||
|
||||
return response.data;
|
||||
},
|
||||
init: (params) => {
|
||||
let properties = [];
|
||||
|
||||
properties.push({
|
||||
label: "Prompt",
|
||||
text: params.prompt,
|
||||
});
|
||||
|
||||
if (params.n) {
|
||||
properties.push({
|
||||
label: "Number of images",
|
||||
text: params.n.toString(),
|
||||
});
|
||||
}
|
||||
|
||||
if (params.size) {
|
||||
properties.push({
|
||||
label: "Size",
|
||||
text: params.size,
|
||||
});
|
||||
}
|
||||
|
||||
if (params.response_format) {
|
||||
properties.push({
|
||||
label: "Response format",
|
||||
text: params.response_format,
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof params.image === "string") {
|
||||
properties.push({
|
||||
label: "Image URL",
|
||||
text: params.image,
|
||||
url: params.image,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
name: "Create image edit",
|
||||
params,
|
||||
icon: "openai",
|
||||
properties,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
type CreateImageVariationResponseData = Prettify<
|
||||
Awaited<ReturnType<OpenAIClientType["createImageVariation"]>>["data"]
|
||||
>;
|
||||
|
||||
export type CreateImageVariationRequest = {
|
||||
image: string | File;
|
||||
n?: number;
|
||||
size?: "256x256" | "512x512" | "1024x1024";
|
||||
response_format?: "url" | "b64_json";
|
||||
user?: string;
|
||||
};
|
||||
|
||||
export const createImageVariation: AuthenticatedTask<
|
||||
OpenAIClientType,
|
||||
Prettify<CreateImageVariationRequest>,
|
||||
CreateImageVariationResponseData
|
||||
> = {
|
||||
onError: onTaskError,
|
||||
run: async (params, client, task) => {
|
||||
const file = typeof params.image === "string" ? await fileFromUrl(params.image) : params.image;
|
||||
|
||||
const response = await client
|
||||
.createImageVariation(file, params.n, params.size, params.response_format, params.user)
|
||||
.then((res) => res.data);
|
||||
|
||||
return response;
|
||||
},
|
||||
init: (params) => {
|
||||
let properties = [];
|
||||
|
||||
if (params.n) {
|
||||
properties.push({
|
||||
label: "Number of images",
|
||||
text: params.n.toString(),
|
||||
});
|
||||
}
|
||||
|
||||
if (params.size) {
|
||||
properties.push({
|
||||
label: "Size",
|
||||
text: params.size,
|
||||
});
|
||||
}
|
||||
|
||||
if (params.response_format) {
|
||||
properties.push({
|
||||
label: "Response format",
|
||||
text: params.response_format,
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof params.image === "string") {
|
||||
properties.push({
|
||||
label: "Image URL",
|
||||
text: params.image,
|
||||
url: params.image,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
name: "Create image variation",
|
||||
params,
|
||||
icon: "openai",
|
||||
properties,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
type CreateEmbeddingResponseData = Prettify<
|
||||
Awaited<ReturnType<OpenAIClientType["createEmbedding"]>>["data"]
|
||||
>;
|
||||
@@ -383,7 +533,7 @@ export const createFile: AuthenticatedTask<
|
||||
let file: File;
|
||||
|
||||
if (typeof params.file === "string") {
|
||||
file = (await fileFromString(params.file, params.fileName ?? "file.txt")) as any;
|
||||
file = await fileFromString(params.file, params.fileName ?? "file.txt");
|
||||
} else {
|
||||
file = params.file;
|
||||
}
|
||||
@@ -441,10 +591,10 @@ export const createFineTuneFile: AuthenticatedTask<
|
||||
> = {
|
||||
onError: onTaskError,
|
||||
run: async (params, client) => {
|
||||
const file = (await fileFromString(
|
||||
const file = await fileFromString(
|
||||
params.examples.map((d) => JSON.stringify(d)).join("\n"),
|
||||
params.fileName
|
||||
)) as any;
|
||||
);
|
||||
|
||||
return client.createFile(file, "fine-tune").then((res) => res.data);
|
||||
},
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# @trigger.dev/plain
|
||||
|
||||
## 2.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [84126808]
|
||||
- @trigger.dev/integration-kit@2.0.7
|
||||
- @trigger.dev/sdk@2.0.7
|
||||
|
||||
## 2.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.0.6
|
||||
- @trigger.dev/sdk@2.0.6
|
||||
|
||||
## 2.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.0.5
|
||||
- @trigger.dev/sdk@2.0.5
|
||||
|
||||
## 2.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/plain",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.7",
|
||||
"description": "The official Plain.com integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -24,8 +24,8 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/integration-kit": "workspace:^2.0.4",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.4",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.0.7",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.7",
|
||||
"@team-plain/typescript-sdk": "^2.7.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# @trigger.dev/resend
|
||||
|
||||
## 2.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [84126808]
|
||||
- @trigger.dev/integration-kit@2.0.7
|
||||
- @trigger.dev/sdk@2.0.7
|
||||
|
||||
## 2.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.0.6
|
||||
- @trigger.dev/sdk@2.0.6
|
||||
|
||||
## 2.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.0.5
|
||||
- @trigger.dev/sdk@2.0.5
|
||||
|
||||
## 2.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/resend",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.7",
|
||||
"description": "The official Resend.com integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -24,8 +24,8 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/integration-kit": "workspace:^2.0.4",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.4",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.0.7",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.7",
|
||||
"resend": "^0.9.1"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# @trigger.dev/slack
|
||||
|
||||
## 2.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.0.7
|
||||
|
||||
## 2.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.0.6
|
||||
|
||||
## 2.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.0.5
|
||||
|
||||
## 2.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/slack",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.7",
|
||||
"description": "The official Slack integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@slack/web-api": "^6.8.1",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.4",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.7",
|
||||
"zod": "3.21.4"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# @trigger.dev/stripe
|
||||
|
||||
## 2.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [84126808]
|
||||
- @trigger.dev/integration-kit@2.0.7
|
||||
- @trigger.dev/sdk@2.0.7
|
||||
|
||||
## 2.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.0.6
|
||||
- @trigger.dev/sdk@2.0.6
|
||||
|
||||
## 2.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.0.5
|
||||
- @trigger.dev/sdk@2.0.5
|
||||
|
||||
## 2.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/stripe",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.7",
|
||||
"description": "Trigger.dev integration for stripe",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -26,8 +26,8 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/integration-kit": "workspace:^2.0.4",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.4",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.0.7",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.7",
|
||||
"stripe": "^12.14.0",
|
||||
"zod": "3.21.4"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
# @trigger.dev/supabase
|
||||
|
||||
## 2.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [84126808]
|
||||
- @trigger.dev/integration-kit@2.0.7
|
||||
- @trigger.dev/sdk@2.0.7
|
||||
|
||||
## 2.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.0.6
|
||||
- @trigger.dev/sdk@2.0.6
|
||||
|
||||
## 2.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 3cf3eaff: You can now trigger on multiple database events in the same job
|
||||
- @trigger.dev/integration-kit@2.0.5
|
||||
- @trigger.dev/sdk@2.0.5
|
||||
|
||||
## 2.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/supabase",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.7",
|
||||
"description": "Trigger.dev integration for @supabase/supabase-js",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -26,8 +26,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@supabase/supabase-js": "^2.26.0",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.0.4",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.4",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.0.7",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.7",
|
||||
"supabase-management-js": "^0.1.4",
|
||||
"zod": "3.21.4"
|
||||
},
|
||||
|
||||
@@ -34,6 +34,91 @@ class SupabaseDatabase<Database = any> {
|
||||
private projectRef: string
|
||||
) {}
|
||||
|
||||
/**
|
||||
* The function `on` creates a trigger for when a record is inserted, updated, or deleted on a
|
||||
* specific table in a database schema.
|
||||
* @param params - The `params` parameter is an object that contains the following properties:
|
||||
* @param params.table - The `table` property is a string that specifies the name of the table
|
||||
* that the trigger will be created for.
|
||||
* @param params.events - The `events` property is an array of events that specifies the events
|
||||
* that the trigger will be called for. The events that can be specified are `INSERT`, `UPDATE`, or `DELETE`.
|
||||
* By default, the trigger will be called for all events.
|
||||
* @param params.schema - The `schema` property is a string that specifies the name of the schema
|
||||
* that the trigger will be created for. If the schema is not specified, the default schema will
|
||||
* be used. (public)
|
||||
* @param params.filter - The `filter` property is an object that specifies the filter that will
|
||||
* be used to determine if the trigger should be called. If the filter is not specified, the
|
||||
* trigger will be called for all records.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* const supabase = new SupabaseManagement({ id: "supabase" });
|
||||
* const database = supabase.database<Database>("https://<project-id>.supabase.co");
|
||||
*
|
||||
* client.defineJob({
|
||||
* trigger: database.on({
|
||||
* table: "todos",
|
||||
* events: ["INSERTED", "UPDATED"],
|
||||
* schema: "public",
|
||||
* filter: {
|
||||
* record: { is_completed: [false] },
|
||||
* },
|
||||
* }),
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
on<
|
||||
SchemaName extends string & keyof Database = "public" extends keyof Database
|
||||
? "public"
|
||||
: string & keyof Database,
|
||||
Schema extends GenericSchema = Database[SchemaName] extends GenericSchema
|
||||
? Database[SchemaName]
|
||||
: any,
|
||||
TTableName extends string & keyof Schema["Tables"] = string & keyof Schema["Tables"],
|
||||
TTable extends Schema["Tables"][TTableName] = Schema["Tables"][TTableName],
|
||||
TEvents extends WebhookEvents[] = ["INSERT", "UPDATE", "DELETE"],
|
||||
>(params: { table: TTableName; events?: TEvents; schema?: SchemaName; filter?: EventFilter }) {
|
||||
return createTrigger<Prettify<UnionPayloads<TEvents, TTableName, SchemaName, TTable["Row"]>>>(
|
||||
this.integration.source,
|
||||
{
|
||||
event: params.events ?? ["INSERT", "UPDATE", "DELETE"],
|
||||
projectRef: this.projectRef,
|
||||
...params,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `onInserted` creates a trigger for when a new record is inserted into a specific
|
||||
* table in a database schema.
|
||||
* @param params - The `params` parameter is an object that contains the following properties:
|
||||
* @param params.table - The `table` property is a string that specifies the name of the table
|
||||
* that the trigger will be created for.
|
||||
* @param params.schema - The `schema` property is a string that specifies the name of the schema
|
||||
* that the trigger will be created for. If the schema is not specified, the default schema will
|
||||
* be used. (public)
|
||||
* @param params.filter - The `filter` property is an object that specifies the filter that will
|
||||
* be used to determine if the trigger should be called. If the filter is not specified, the
|
||||
* trigger will be called for all records.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* const supabase = new SupabaseManagement({ id: "supabase" });
|
||||
* const database = supabase.database<Database>("https://<project-id>.supabase.co");
|
||||
*
|
||||
* client.defineJob({
|
||||
* trigger: database.onInserted({
|
||||
* table: "todos",
|
||||
* schema: "public",
|
||||
* filter: {
|
||||
* record: { is_completed: [false] },
|
||||
* },
|
||||
* }),
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
onInserted<
|
||||
SchemaName extends string & keyof Database = "public" extends keyof Database
|
||||
? "public"
|
||||
@@ -57,6 +142,37 @@ class SupabaseDatabase<Database = any> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `onUpdated` creates a trigger for when a new record is updated on a specific
|
||||
* table in a database schema.
|
||||
* @param params - The `params` parameter is an object that contains the following properties:
|
||||
* @param params.table - The `table` property is a string that specifies the name of the table
|
||||
* that the trigger will be created for.
|
||||
* @param params.schema - The `schema` property is a string that specifies the name of the schema
|
||||
* that the trigger will be created for. If the schema is not specified, the default schema will
|
||||
* be used. (public)
|
||||
* @param params.filter - The `filter` property is an object that specifies the filter that will
|
||||
* be used to determine if the trigger should be called. If the filter is not specified, the
|
||||
* trigger will be called for all records.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* const supabase = new SupabaseManagement({ id: "supabase" });
|
||||
* const database = supabase.database<Database>("https://<project-id>.supabase.co");
|
||||
*
|
||||
* client.defineJob({
|
||||
* trigger: database.onUpdated({
|
||||
* table: "todos",
|
||||
* schema: "public",
|
||||
* filter: {
|
||||
* record: { completed: [true] },
|
||||
* old_record: { completed: [false] },
|
||||
* },
|
||||
* }),
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
onUpdated<
|
||||
SchemaName extends string & keyof Database = "public" extends keyof Database
|
||||
? "public"
|
||||
@@ -80,6 +196,36 @@ class SupabaseDatabase<Database = any> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `onDeleted` creates a trigger for when a new record is deleted from a specific
|
||||
* table in a database schema.
|
||||
* @param params - The `params` parameter is an object that contains the following properties:
|
||||
* @param params.table - The `table` property is a string that specifies the name of the table
|
||||
* that the trigger will be created for.
|
||||
* @param params.schema - The `schema` property is a string that specifies the name of the schema
|
||||
* that the trigger will be created for. If the schema is not specified, the default schema will
|
||||
* be used. (public)
|
||||
* @param params.filter - The `filter` property is an object that specifies the filter that will
|
||||
* be used to determine if the trigger should be called. If the filter is not specified, the
|
||||
* trigger will be called for all records.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* const supabase = new SupabaseManagement({ id: "supabase" });
|
||||
* const database = supabase.database<Database>("https://<project-id>.supabase.co");
|
||||
*
|
||||
* client.defineJob({
|
||||
* trigger: database.onDeleted({
|
||||
* table: "todos",
|
||||
* schema: "public",
|
||||
* filter: {
|
||||
* old_record: { is_completed: [true] },
|
||||
* },
|
||||
* }),
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
onDeleted<
|
||||
SchemaName extends string & keyof Database = "public" extends keyof Database
|
||||
? "public"
|
||||
@@ -175,9 +321,44 @@ type WebhookEventSource = ReturnType<typeof createWebhookEventSource>;
|
||||
|
||||
type WebhookEvents = "INSERT" | "UPDATE" | "DELETE";
|
||||
|
||||
type WebhookEventPayloads<
|
||||
TTableName extends string,
|
||||
TSchemaName extends string = "public",
|
||||
TRecord = any,
|
||||
> = {
|
||||
INSERT: {
|
||||
table: TTableName;
|
||||
record: Prettify<TRecord>;
|
||||
type: "INSERT";
|
||||
schema: TSchemaName;
|
||||
old_record: null;
|
||||
};
|
||||
UPDATE: {
|
||||
table: TTableName;
|
||||
record: Prettify<TRecord>;
|
||||
type: "UPDATE";
|
||||
schema: TSchemaName;
|
||||
old_record: Prettify<TRecord>;
|
||||
};
|
||||
DELETE: {
|
||||
table: TTableName;
|
||||
record: null;
|
||||
type: "DELETE";
|
||||
schema: TSchemaName;
|
||||
old_record: Prettify<TRecord>;
|
||||
};
|
||||
};
|
||||
|
||||
type UnionPayloads<
|
||||
T extends WebhookEvents[],
|
||||
TTableName extends string,
|
||||
TSchemaName extends string = "public",
|
||||
TRecord = any,
|
||||
> = WebhookEventPayloads<TTableName, TSchemaName, TRecord>[T[number]];
|
||||
|
||||
function createTrigger<TEvent extends any>(
|
||||
source: WebhookEventSource,
|
||||
params: { event: WebhookEvents; filter?: EventFilter } & {
|
||||
params: { event: WebhookEvents | WebhookEvents[]; filter?: EventFilter } & {
|
||||
projectRef: string;
|
||||
table: string;
|
||||
schema?: string;
|
||||
@@ -190,7 +371,7 @@ function createTrigger<TEvent extends any>(
|
||||
icon: "supabase",
|
||||
filter: {
|
||||
...params.filter,
|
||||
type: [params.event],
|
||||
type: typeof params.event === "string" ? [params.event] : params.event,
|
||||
schema: [params.schema ?? "public"],
|
||||
},
|
||||
properties: [],
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# @trigger.dev/typeform
|
||||
|
||||
## 2.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [84126808]
|
||||
- @trigger.dev/integration-kit@2.0.7
|
||||
- @trigger.dev/sdk@2.0.7
|
||||
|
||||
## 2.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.0.6
|
||||
- @trigger.dev/sdk@2.0.6
|
||||
|
||||
## 2.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/integration-kit@2.0.5
|
||||
- @trigger.dev/sdk@2.0.5
|
||||
|
||||
## 2.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/typeform",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.7",
|
||||
"description": "The official Typeform integration for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -26,8 +26,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@typeform/api-client": "^1.8.0",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.4",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.0.4",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.7",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.0.7",
|
||||
"zod": "3.21.4"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"clean": "turbo run clean",
|
||||
"clean:node_modules": "find . -name 'node_modules' -type d -prune -exec rm -rf '{}' +",
|
||||
"typecheck": "turbo run typecheck",
|
||||
"test:e2e": "playwright test",
|
||||
"test:e2e:dev": "turbo run test:e2e:dev",
|
||||
"test:e2e:ci": "turbo run test:e2e:ci",
|
||||
"setup": "turbo run generate db:migrate:force db:seed",
|
||||
@@ -43,9 +44,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@manypkg/cli": "^0.19.2",
|
||||
"@playwright/test": "^1.36.2",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@tailwindcss/typography": "^0.5.8",
|
||||
"@trigger.dev/cli": "workspace:*",
|
||||
"@trigger.dev/database": "workspace:*",
|
||||
"@types/node": "18.17.1",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"eslint-config-custom": "workspace:*",
|
||||
"postcss": "^8.4.17",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# create-trigger
|
||||
|
||||
## 2.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- a1bc15d1: Updated the example job
|
||||
- 3ce6dece: creates a job catalog file when a new integration is created with the create-integration command
|
||||
- a90908df: CLI init fixes: don't ask for endpoint slug, fix for next.config and package manager artifact issue
|
||||
|
||||
## 2.0.6
|
||||
|
||||
## 2.0.5
|
||||
|
||||
## 2.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/cli",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.7",
|
||||
"description": "The Trigger.dev CLI",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@@ -6,6 +6,7 @@ import { COMMAND_NAME } from "../consts.js";
|
||||
import { getLatestPackageVersion } from "../utils/addDependencies.js";
|
||||
import { createFile, pathExists, readJSONFile, writeJSONFile } from "../utils/fileSystem.js";
|
||||
import { generateIntegrationFiles } from "../utils/generateIntegrationFiles.js";
|
||||
import { getPackageName } from "../utils/getPackagName.js";
|
||||
import { installDependencies } from "../utils/installDependencies.js";
|
||||
import { logger } from "../utils/logger.js";
|
||||
import { resolvePath } from "../utils/parseNameAndPath.js";
|
||||
@@ -196,13 +197,9 @@ export default defineConfig([
|
||||
|
||||
// If inside the monorepo:
|
||||
if (triggerMonorepoPath) {
|
||||
// Add the dependency to the nextjs-example package.json
|
||||
// Add the paths to the nextjs-example tsconfig paths
|
||||
await updateNextJsExampleProjectWithNewIntegration(
|
||||
triggerMonorepoPath,
|
||||
resolvedPath,
|
||||
resolvedOptions
|
||||
);
|
||||
//adds the integration run script to the job-catalog/package.json
|
||||
//adds the path to the integration to the job-catalog/tsconfig.json
|
||||
await updateJobCatalogWithNewIntegration(triggerMonorepoPath, resolvedPath, resolvedOptions);
|
||||
}
|
||||
|
||||
// Install the dependencies
|
||||
@@ -490,27 +487,44 @@ async function findGitPath(path: string): Promise<string | undefined> {
|
||||
return findGitPath(parentPath);
|
||||
}
|
||||
|
||||
async function updateNextJsExampleProjectWithNewIntegration(
|
||||
async function updateJobCatalogWithNewIntegration(
|
||||
monorepoPath: string,
|
||||
integrationPath: string,
|
||||
resolvedOptions: ResolvedCLIOptions
|
||||
) {
|
||||
const nextjsPath = pathModule.join(monorepoPath, "examples", "nextjs-example");
|
||||
const packageName = getPackageName(resolvedOptions.packageName);
|
||||
const jobCatalogPath = pathModule.join(monorepoPath, "examples", "job-catalog");
|
||||
const jobCatalogSrcFolder = pathModule.join(jobCatalogPath, "src");
|
||||
|
||||
const integrationFile = `export {}`;
|
||||
|
||||
await createFileInPath(jobCatalogSrcFolder, `${packageName}.ts`, integrationFile);
|
||||
|
||||
const packageJsonPath = pathModule.join(jobCatalogPath, "package.json");
|
||||
|
||||
const packageJsonPath = pathModule.join(nextjsPath, "package.json");
|
||||
const packageJson = await readJSONFile(packageJsonPath);
|
||||
|
||||
const newPackageJson = {
|
||||
...packageJson,
|
||||
scripts: {
|
||||
...packageJson.scripts,
|
||||
[packageName]: `nodemon --watch src/${packageName}.ts -r tsconfig-paths/register -r dotenv/config src/${packageName}.ts`,
|
||||
},
|
||||
dependencies: {
|
||||
...packageJson.dependencies,
|
||||
[resolvedOptions.packageName]: `workspace:*`,
|
||||
},
|
||||
};
|
||||
|
||||
// Move "dev:trigger script" to the last position in the scripts object
|
||||
if (newPackageJson.scripts.hasOwnProperty("dev:trigger")) {
|
||||
const devTriggerScript = newPackageJson.scripts["dev:trigger"];
|
||||
delete newPackageJson.scripts["dev:trigger"];
|
||||
newPackageJson.scripts["dev:trigger"] = devTriggerScript;
|
||||
}
|
||||
|
||||
await writeJSONFile(packageJsonPath, newPackageJson);
|
||||
|
||||
const tsConfigPath = pathModule.join(nextjsPath, "tsconfig.json");
|
||||
const tsConfigPath = pathModule.join(jobCatalogPath, "tsconfig.json");
|
||||
const tsConfig = await readJSONFile(tsConfigPath);
|
||||
|
||||
const newTsConfig = {
|
||||
@@ -528,6 +542,5 @@ async function updateNextJsExampleProjectWithNewIntegration(
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await writeJSONFile(tsConfigPath, newTsConfig);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { pathToRegexp } from "path-to-regexp";
|
||||
import { simpleGit } from "simple-git";
|
||||
import { parse } from "tsconfck";
|
||||
import { pathToFileURL } from "url";
|
||||
import { promptApiKey, promptEndpointSlug, promptTriggerUrl } from "../cli/index.js";
|
||||
import { promptApiKey, promptTriggerUrl } from "../cli/index.js";
|
||||
import { CLOUD_API_URL, CLOUD_TRIGGER_URL, COMMAND_NAME } from "../consts.js";
|
||||
import { TelemetryClient, telemetryClient } from "../telemetry/telemetry.js";
|
||||
import { addDependencies } from "../utils/addDependencies.js";
|
||||
@@ -67,24 +67,28 @@ export const initCommand = async (options: InitCommandOptions) => {
|
||||
const isTypescriptProject = await detectTypescriptProject(resolvedPath);
|
||||
telemetryClient.init.isTypescriptProject(isTypescriptProject, options);
|
||||
|
||||
const resolvedOptions = await resolveOptionsWithPrompts(options, resolvedPath, telemetryClient);
|
||||
const apiKey = resolvedOptions.apiKey;
|
||||
const optionsAfterPrompts = await resolveOptionsWithPrompts(
|
||||
options,
|
||||
resolvedPath,
|
||||
telemetryClient
|
||||
);
|
||||
const apiKey = optionsAfterPrompts.apiKey;
|
||||
|
||||
if (!apiKey) {
|
||||
logger.error("You must provide an API key to continue.");
|
||||
telemetryClient.init.failed("no_api_key", resolvedOptions);
|
||||
telemetryClient.init.failed("no_api_key", optionsAfterPrompts);
|
||||
return;
|
||||
}
|
||||
|
||||
const apiClient = new TriggerApi(apiKey, resolvedOptions.apiUrl);
|
||||
const apiClient = new TriggerApi(apiKey, optionsAfterPrompts.apiUrl);
|
||||
const authorizedKey = await apiClient.whoami(apiKey);
|
||||
|
||||
if (!authorizedKey) {
|
||||
logger.error(
|
||||
`🛑 The API key you provided is not authorized. Try visiting your dashboard at ${resolvedOptions.triggerUrl} to get a new API key.`
|
||||
`🛑 The API key you provided is not authorized. Try visiting your dashboard at ${optionsAfterPrompts.triggerUrl} to get a new API key.`
|
||||
);
|
||||
|
||||
telemetryClient.init.failed("invalid_api_key", resolvedOptions);
|
||||
telemetryClient.init.failed("invalid_api_key", optionsAfterPrompts);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -94,6 +98,9 @@ export const initCommand = async (options: InitCommandOptions) => {
|
||||
authorizedKey.userId
|
||||
);
|
||||
|
||||
const endpointSlug = authorizedKey.project.slug;
|
||||
const resolvedOptions: ResolvedOptions = { ...optionsAfterPrompts, endpointSlug };
|
||||
|
||||
await addDependencies(resolvedPath, [
|
||||
{ name: "@trigger.dev/sdk", tag: "latest" },
|
||||
{ name: "@trigger.dev/nextjs", tag: "latest" },
|
||||
@@ -150,7 +157,7 @@ async function printNextSteps(options: ResolvedOptions, authorizedKey: WhoamiRes
|
||||
logger.info("Next steps:");
|
||||
logger.info(` 1. Run your Next.js project locally with 'npm run dev'`);
|
||||
logger.info(
|
||||
` 2. Run 'npx @trigger.dev/cli@latest dev' to watch for changes and automatically register Trigger.dev jobs`
|
||||
` 2. In a separate terminal, run 'npx @trigger.dev/cli@latest dev' to watch for changes and automatically register Trigger.dev jobs`
|
||||
);
|
||||
logger.info(` 3. View your jobs at ${projectUrl}`);
|
||||
|
||||
@@ -174,11 +181,15 @@ async function addConfigurationToPackageJson(path: string, options: ResolvedOpti
|
||||
logger.success(`✅ Wrote trigger.dev config to package.json`);
|
||||
}
|
||||
|
||||
type OptionsAfterPrompts = Required<Omit<InitCommandOptions, "endpointSlug">> & {
|
||||
endpointSlug: InitCommandOptions["endpointSlug"];
|
||||
};
|
||||
|
||||
const resolveOptionsWithPrompts = async (
|
||||
options: InitCommandOptions,
|
||||
path: string,
|
||||
telemetryClient: TelemetryClient
|
||||
): Promise<ResolvedOptions> => {
|
||||
): Promise<OptionsAfterPrompts> => {
|
||||
const resolvedOptions: InitCommandOptions = { ...options };
|
||||
|
||||
try {
|
||||
@@ -205,11 +216,8 @@ const resolveOptionsWithPrompts = async (
|
||||
|
||||
if (packageJSON && packageJSON["trigger.dev"] && packageJSON["trigger.dev"].endpointId) {
|
||||
resolvedOptions.endpointSlug = packageJSON["trigger.dev"].endpointId;
|
||||
} else {
|
||||
resolvedOptions.endpointSlug = await promptEndpointSlug(path);
|
||||
telemetryClient.init.resolvedEndpointSlug(resolvedOptions);
|
||||
}
|
||||
|
||||
telemetryClient.init.resolvedEndpointSlug(resolvedOptions);
|
||||
}
|
||||
} catch (err) {
|
||||
// If the user is not calling the command from an interactive terminal, inquirer will throw an error with isTTYError = true
|
||||
@@ -239,7 +247,7 @@ const resolveOptionsWithPrompts = async (
|
||||
}
|
||||
}
|
||||
|
||||
return resolvedOptions as ResolvedOptions;
|
||||
return resolvedOptions as OptionsAfterPrompts;
|
||||
};
|
||||
|
||||
// Detects if there are any uncommitted git changes at path
|
||||
@@ -278,7 +286,7 @@ async function detectPagesOrAppDir(
|
||||
isTypescriptProject = false
|
||||
): Promise<"pages" | "app"> {
|
||||
const nextConfigPath = pathModule.join(path, "next.config.js");
|
||||
const importedConfig = await import(pathToFileURL(nextConfigPath).toString());
|
||||
const importedConfig = await import(pathToFileURL(nextConfigPath).toString()).catch(() => ({}));
|
||||
|
||||
if (importedConfig?.default?.experimental?.appDir) {
|
||||
return "app";
|
||||
@@ -437,11 +445,11 @@ async function createTriggerAppRoute(
|
||||
const tsConfigPath = pathModule.join(projectPath, configFileName);
|
||||
const { tsconfig } = await parse(tsConfigPath);
|
||||
|
||||
const extension = isTypescriptProject ? ".ts" : ".js"
|
||||
const triggerFileName = `trigger${extension}`
|
||||
const examplesFileName = `examples${extension}`
|
||||
const examplesIndexFileName = `index${extension}`
|
||||
const routeFileName = `route${extension}`
|
||||
const extension = isTypescriptProject ? ".ts" : ".js";
|
||||
const triggerFileName = `trigger${extension}`;
|
||||
const examplesFileName = `examples${extension}`;
|
||||
const examplesIndexFileName = `index${extension}`;
|
||||
const routeFileName = `route${extension}`;
|
||||
|
||||
const pathAlias = getPathAlias(tsconfig, usesSrcDir);
|
||||
const routePathPrefix = pathAlias ? pathAlias + "/" : "../../../";
|
||||
@@ -473,29 +481,38 @@ export const client = new TriggerClient({
|
||||
import { eventTrigger } from "@trigger.dev/sdk";
|
||||
import { client } from "${jobsPathPrefix}trigger";
|
||||
|
||||
// your first job
|
||||
// Your first job
|
||||
// This Job will be triggered by an event, log a joke to the console, and then wait 5 seconds before logging the punchline
|
||||
client.defineJob({
|
||||
// This is the unique identifier for your Job, it must be unique across all Jobs in your project
|
||||
id: "example-job",
|
||||
name: "Example Job",
|
||||
name: "Example Job: a joke with a delay",
|
||||
version: "0.0.1",
|
||||
// This is triggered by an event using eventTrigger. You can also trigger Jobs with webhooks, on schedules, and more: https://trigger.dev/docs/documentation/concepts/triggers/introduction
|
||||
trigger: eventTrigger({
|
||||
name: "example.event",
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
await io.logger.info("Hello world!", { payload });
|
||||
|
||||
return {
|
||||
message: "Hello world!",
|
||||
};
|
||||
// This logs a message to the console
|
||||
await io.logger.info("🧪 Example Job: a joke with a delay");
|
||||
await io.logger.info("How do you comfort a JavaScript bug?");
|
||||
// This waits for 5 seconds, the second parameter is the number of seconds to wait, you can add delays of up to a year
|
||||
await io.wait("Wait 5 seconds for the punchline...", 5);
|
||||
await io.logger.info("You console it! 🤦");
|
||||
await io.logger.info(
|
||||
"✨ Congratulations, You just ran your first successful Trigger.dev Job! ✨"
|
||||
);
|
||||
// To learn how to write much more complex (and probably funnier) Jobs, check out our docs: https://trigger.dev/docs/documentation/guides/create-a-job
|
||||
},
|
||||
});
|
||||
|
||||
`;
|
||||
|
||||
const examplesIndexContent = `
|
||||
// import all your job files here
|
||||
|
||||
export * from "./examples"
|
||||
`
|
||||
`;
|
||||
|
||||
const directories = pathModule.join(path, "app", "api", "trigger");
|
||||
await fs.mkdir(directories, { recursive: true });
|
||||
@@ -552,10 +569,10 @@ async function createTriggerPageRoute(
|
||||
const pathAlias = getPathAlias(tsconfig, usesSrcDir);
|
||||
const routePathPrefix = pathAlias ? pathAlias + "/" : "../..";
|
||||
|
||||
const extension = isTypescriptProject ? ".ts" : ".js"
|
||||
const triggerFileName = `trigger${extension}`
|
||||
const examplesFileName = `examples${extension}`
|
||||
const examplesIndexFileName = `index${extension}`
|
||||
const extension = isTypescriptProject ? ".ts" : ".js";
|
||||
const triggerFileName = `trigger${extension}`;
|
||||
const examplesFileName = `examples${extension}`;
|
||||
const examplesIndexFileName = `index${extension}`;
|
||||
|
||||
const routeContent = `
|
||||
import { createPagesRoute } from "@trigger.dev/nextjs";
|
||||
@@ -608,7 +625,7 @@ client.defineJob({
|
||||
// import all your job files here
|
||||
|
||||
export * from "./examples"
|
||||
`
|
||||
`;
|
||||
|
||||
const directories = pathModule.join(path, "pages", "api");
|
||||
await fs.mkdir(directories, { recursive: true });
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export function getPackageName(inputString: string): string {
|
||||
const packageName = inputString.split("/")[1];
|
||||
return packageName ?? "";
|
||||
}
|
||||
@@ -5,7 +5,7 @@ export type PackageManager = "npm" | "pnpm" | "yarn";
|
||||
|
||||
export async function getUserPackageManager(path: string): Promise<PackageManager> {
|
||||
try {
|
||||
return detectPackageManagerFromArtifacts(path);
|
||||
return await detectPackageManagerFromArtifacts(path);
|
||||
} catch (error) {
|
||||
return detectPackageManagerFromCurrentCommand();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# internal-platform
|
||||
|
||||
## 2.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- fa3a22eb: Added an $isNull EventFilter condition matcher
|
||||
|
||||
## 2.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 59075f5f: EventFilter now supports more complex condition filters #271
|
||||
|
||||
## 2.0.5
|
||||
|
||||
## 2.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
moduleFileExtensions: ["ts", "tsx", "js"],
|
||||
transform: {
|
||||
"^.+\\.(ts|tsx)$": "ts-jest",
|
||||
},
|
||||
testMatch: ["<rootDir>/test/**/*.ts?(x)", "<rootDir>/test/**/?(*.)+(spec|test).ts?(x)"],
|
||||
testEnvironment: "node",
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@trigger.dev/core",
|
||||
"version": "2.0.4",
|
||||
"description": "Common code used across the Trigger.dev SDK and platform",
|
||||
"version": "2.0.7",
|
||||
"description": "Core code used across the Trigger.dev SDK and platform",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"publishConfig": {
|
||||
@@ -22,7 +22,8 @@
|
||||
"clean": "rimraf dist",
|
||||
"build": "npm run clean && npm run build:tsup",
|
||||
"build:tsup": "tsup --dts-resolve",
|
||||
"typecheck": "tsc --noEmit"
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"ulid": "^2.3.0",
|
||||
@@ -31,12 +32,15 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@trigger.dev/tsconfig": "workspace:*",
|
||||
"@types/jest": "^29.5.3",
|
||||
"@types/node": "16",
|
||||
"jest": "^29.6.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"ts-jest": "^29.1.1",
|
||||
"tsup": "^7.1.0",
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.8.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
import { EventFilter } from "./schemas/eventFilter";
|
||||
|
||||
// EventFilter is a recursive type, where the keys are strings and the values are an array of strings, numbers, booleans, or objects.
|
||||
// If the values of the array are strings, numbers, or booleans, than we are matching against the value of the payload.
|
||||
// If the values of the array are objects, then we are doing content filtering
|
||||
// An example would be [{ $endsWith: ".png" }, { $startsWith: "images/" } ]
|
||||
export function eventFilterMatches(payload: any, filter: EventFilter): boolean {
|
||||
for (const [patternKey, patternValue] of Object.entries(filter)) {
|
||||
const payloadValue = payload[patternKey];
|
||||
|
||||
if (Array.isArray(patternValue)) {
|
||||
if (patternValue.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check to see if all the items in the array are a string
|
||||
if ((patternValue as unknown[]).every((item) => typeof item === "string")) {
|
||||
if ((patternValue as string[]).includes(payloadValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check to see if all the items in the array are a number
|
||||
if ((patternValue as unknown[]).every((item) => typeof item === "number")) {
|
||||
if ((patternValue as number[]).includes(payloadValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check to see if all the items in the array are a boolean
|
||||
if ((patternValue as unknown[]).every((item) => typeof item === "boolean")) {
|
||||
if ((patternValue as boolean[]).includes(payloadValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now we know that all the items in the array are objects
|
||||
const objectArray = patternValue as Exclude<
|
||||
typeof patternValue,
|
||||
number[] | string[] | boolean[]
|
||||
>;
|
||||
|
||||
if (!contentFiltersMatches(payloadValue, objectArray)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if (typeof patternValue === "object") {
|
||||
if (Array.isArray(payloadValue)) {
|
||||
if (!payloadValue.some((item) => eventFilterMatches(item, patternValue))) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!eventFilterMatches(payloadValue, patternValue)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
type ContentFilters = Exclude<EventFilter[string], EventFilter | string[] | number[] | boolean[]>;
|
||||
|
||||
function contentFiltersMatches(actualValue: any, contentFilters: ContentFilters): boolean {
|
||||
for (const contentFilter of contentFilters) {
|
||||
if (typeof contentFilter === "object") {
|
||||
const [key, value] = Object.entries(contentFilter)[0];
|
||||
|
||||
if (!contentFilterMatches(actualValue, contentFilter)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function contentFilterMatches(actualValue: any, contentFilter: ContentFilters[number]): boolean {
|
||||
if ("$endsWith" in contentFilter) {
|
||||
if (typeof actualValue !== "string") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return actualValue.endsWith(contentFilter.$endsWith);
|
||||
}
|
||||
|
||||
if ("$startsWith" in contentFilter) {
|
||||
if (typeof actualValue !== "string") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return actualValue.startsWith(contentFilter.$startsWith);
|
||||
}
|
||||
|
||||
if ("$anythingBut" in contentFilter) {
|
||||
if (Array.isArray(contentFilter.$anythingBut)) {
|
||||
if ((contentFilter.$anythingBut as any[]).includes(actualValue)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (contentFilter.$anythingBut === actualValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ("$exists" in contentFilter) {
|
||||
if (contentFilter.$exists) {
|
||||
return actualValue !== undefined;
|
||||
}
|
||||
|
||||
return actualValue === undefined;
|
||||
}
|
||||
|
||||
if ("$gt" in contentFilter) {
|
||||
if (typeof actualValue !== "number") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return actualValue > contentFilter.$gt;
|
||||
}
|
||||
|
||||
if ("$lt" in contentFilter) {
|
||||
if (typeof actualValue !== "number") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return actualValue < contentFilter.$lt;
|
||||
}
|
||||
|
||||
if ("$gte" in contentFilter) {
|
||||
if (typeof actualValue !== "number") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return actualValue >= contentFilter.$gte;
|
||||
}
|
||||
|
||||
if ("$lte" in contentFilter) {
|
||||
if (typeof actualValue !== "number") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return actualValue <= contentFilter.$lte;
|
||||
}
|
||||
|
||||
if ("$between" in contentFilter) {
|
||||
if (typeof actualValue !== "number") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return actualValue >= contentFilter.$between[0] && actualValue <= contentFilter.$between[1];
|
||||
}
|
||||
|
||||
if ("$includes" in contentFilter) {
|
||||
if (Array.isArray(actualValue)) {
|
||||
return actualValue.includes(contentFilter.$includes);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use localCompare
|
||||
if ("$ignoreCaseEquals" in contentFilter) {
|
||||
if (typeof actualValue !== "string") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
actualValue.localeCompare(contentFilter.$ignoreCaseEquals, undefined, {
|
||||
sensitivity: "accent",
|
||||
}) === 0
|
||||
);
|
||||
}
|
||||
|
||||
if ("$isNull" in contentFilter) {
|
||||
if (contentFilter.$isNull) {
|
||||
return actualValue === null;
|
||||
}
|
||||
|
||||
return actualValue !== null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -5,3 +5,4 @@ export * from "./utils";
|
||||
export * from "./retry";
|
||||
export * from "./replacements";
|
||||
export * from "./searchParams";
|
||||
export * from "./eventFilterMatches";
|
||||
|
||||
@@ -7,6 +7,49 @@ const EventMatcherSchema = z.union([
|
||||
z.array(z.number()),
|
||||
/** Match against a boolean */
|
||||
z.array(z.boolean()),
|
||||
z.array(
|
||||
z.union([
|
||||
z.object({
|
||||
$endsWith: z.string(),
|
||||
}),
|
||||
z.object({
|
||||
$startsWith: z.string(),
|
||||
}),
|
||||
z.object({
|
||||
$exists: z.boolean(),
|
||||
}),
|
||||
z.object({
|
||||
$isNull: z.boolean(),
|
||||
}),
|
||||
z.object({
|
||||
$anythingBut: z.union([z.string(), z.number(), z.boolean()]),
|
||||
}),
|
||||
z.object({
|
||||
$anythingBut: z.union([z.array(z.string()), z.array(z.number()), z.array(z.boolean())]),
|
||||
}),
|
||||
z.object({
|
||||
$gt: z.number(),
|
||||
}),
|
||||
z.object({
|
||||
$lt: z.number(),
|
||||
}),
|
||||
z.object({
|
||||
$gte: z.number(),
|
||||
}),
|
||||
z.object({
|
||||
$lte: z.number(),
|
||||
}),
|
||||
z.object({
|
||||
$between: z.tuple([z.number(), z.number()]),
|
||||
}),
|
||||
z.object({
|
||||
$includes: z.union([z.string(), z.number(), z.boolean()]),
|
||||
}),
|
||||
z.object({
|
||||
$ignoreCaseEquals: z.string(),
|
||||
}),
|
||||
])
|
||||
),
|
||||
]);
|
||||
|
||||
type EventMatcher = z.infer<typeof EventMatcherSchema>;
|
||||
|
||||
@@ -0,0 +1,612 @@
|
||||
import { EventFilter } from "../src";
|
||||
import { eventFilterMatches } from "../src/eventFilterMatches";
|
||||
|
||||
describe("eventFilterMatches", () => {
|
||||
it("should return true when payload matches string filter", () => {
|
||||
const payload = {
|
||||
name: "John",
|
||||
age: 30,
|
||||
isAdmin: false,
|
||||
hobbies: ["reading", "swimming"],
|
||||
address: {
|
||||
street: "123 Main St",
|
||||
city: "Anytown",
|
||||
state: "CA",
|
||||
zip: "12345",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
name: ["John"],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when payload matches boolean filter", () => {
|
||||
const payload = {
|
||||
name: "John",
|
||||
age: 30,
|
||||
isAdmin: false,
|
||||
hobbies: ["reading", "swimming"],
|
||||
address: {
|
||||
street: "123 Main St",
|
||||
city: "Anytown",
|
||||
state: "CA",
|
||||
zip: "12345",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
isAdmin: [false],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when payload matches number filter", () => {
|
||||
const payload = {
|
||||
name: "John",
|
||||
age: 30,
|
||||
isAdmin: false,
|
||||
hobbies: ["reading", "swimming"],
|
||||
address: {
|
||||
street: "123 Main St",
|
||||
city: "Anytown",
|
||||
state: "CA",
|
||||
zip: "12345",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
age: [30],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when payload matches $startsWith content filter", () => {
|
||||
const payload = {
|
||||
name: "John",
|
||||
age: 30,
|
||||
isAdmin: false,
|
||||
hobbies: ["reading", "swimming"],
|
||||
address: {
|
||||
street: "123 Main St",
|
||||
city: "Anytown",
|
||||
state: "CA",
|
||||
zip: "12345",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
name: [{ $startsWith: "Jo" }],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when payload matches $endsWith content filter", () => {
|
||||
const payload = {
|
||||
name: "John",
|
||||
age: 30,
|
||||
isAdmin: false,
|
||||
hobbies: ["reading", "swimming"],
|
||||
address: {
|
||||
street: "123 Main St",
|
||||
city: "Anytown",
|
||||
state: "CA",
|
||||
zip: "12345",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
name: [{ $endsWith: "hn" }],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when payload matches $startsWith and $endsWith content filters", () => {
|
||||
const payload = {
|
||||
name: "John",
|
||||
age: 30,
|
||||
isAdmin: false,
|
||||
hobbies: ["reading", "swimming"],
|
||||
address: {
|
||||
street: "123 Main St",
|
||||
city: "Anytown",
|
||||
state: "CA",
|
||||
zip: "12345",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
name: [{ $startsWith: "Jo" }, { $endsWith: "hn" }],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when payload does not match $anythingBut filter", () => {
|
||||
const payload = {
|
||||
name: "John",
|
||||
age: 30,
|
||||
isAdmin: false,
|
||||
hobbies: ["reading", "swimming"],
|
||||
address: {
|
||||
street: "123 Main St",
|
||||
city: "Anytown",
|
||||
state: "CA",
|
||||
zip: "12345",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
name: [{ $anythingBut: "Jane" }],
|
||||
address: {
|
||||
street: [{ $anythingBut: "456 Elm St" }],
|
||||
},
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when payload does not match $anythingBut filter with an array", () => {
|
||||
const payload = {
|
||||
name: "John",
|
||||
age: 30,
|
||||
isAdmin: false,
|
||||
hobbies: ["reading", "swimming"],
|
||||
address: {
|
||||
street: "123 Main St",
|
||||
city: "Anytown",
|
||||
state: "CA",
|
||||
zip: "12345",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
name: [{ $anythingBut: ["Jane", "Joe"] }],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when payload does have a key that $exists = true", () => {
|
||||
const payload = {
|
||||
name: "John",
|
||||
age: 30,
|
||||
isAdmin: false,
|
||||
hobbies: ["reading", "swimming"],
|
||||
address: {
|
||||
street: "123 Main St",
|
||||
city: "Anytown",
|
||||
state: "CA",
|
||||
zip: "12345",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
name: [{ $exists: true }],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when payload does NOT have a key that $exists = false", () => {
|
||||
const payload = {
|
||||
name: "John",
|
||||
age: 30,
|
||||
isAdmin: false,
|
||||
hobbies: ["reading", "swimming"],
|
||||
address: {
|
||||
street: "123 Main St",
|
||||
city: "Anytown",
|
||||
state: "CA",
|
||||
zip: "12345",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
foo: [{ $exists: false }],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when payload does match numeric condition", () => {
|
||||
const payload = {
|
||||
name: "John",
|
||||
age: 30,
|
||||
score: 100,
|
||||
isAdmin: false,
|
||||
hobbies: ["reading", "swimming"],
|
||||
address: {
|
||||
street: "123 Main St",
|
||||
city: "Anytown",
|
||||
state: "CA",
|
||||
zip: "12345",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
age: [{ $gt: 20 }, { $lt: 40 }],
|
||||
score: [{ $between: [90, 110] }],
|
||||
};
|
||||
|
||||
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when payload matches an includes condition", () => {
|
||||
const payload = {
|
||||
name: "John",
|
||||
age: 30,
|
||||
score: 100,
|
||||
isAdmin: false,
|
||||
hobbies: ["reading", "swimming"],
|
||||
address: {
|
||||
street: "123 Main St",
|
||||
city: "Anytown",
|
||||
state: "CA",
|
||||
zip: "12345",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
hobbies: [{ $includes: "reading" }],
|
||||
};
|
||||
|
||||
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when payload matches an ignoreCaseEquals condition", () => {
|
||||
const payload = {
|
||||
name: "John",
|
||||
age: 30,
|
||||
score: 100,
|
||||
isAdmin: false,
|
||||
hobbies: ["reading", "swimming"],
|
||||
address: {
|
||||
street: "123 Main St",
|
||||
city: "Anytown",
|
||||
state: "CA",
|
||||
zip: "12345",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
name: [{ $ignoreCaseEquals: "john" }],
|
||||
};
|
||||
|
||||
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when payload matches an isNull condition", () => {
|
||||
const payload = {
|
||||
name: "John",
|
||||
age: 30,
|
||||
score: 100,
|
||||
isAdmin: false,
|
||||
confirmedAt: null,
|
||||
hobbies: ["reading", "swimming"],
|
||||
address: {
|
||||
street: "123 Main St",
|
||||
city: "Anytown",
|
||||
state: "CA",
|
||||
zip: "12345",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
confirmedAt: [{ $isNull: true }],
|
||||
};
|
||||
|
||||
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when payload matches an isNull condition (flipped)", () => {
|
||||
const payload = {
|
||||
name: "John",
|
||||
age: 30,
|
||||
score: 100,
|
||||
isAdmin: false,
|
||||
confirmedAt: "2020-01-01T00:00:00.000Z",
|
||||
hobbies: ["reading", "swimming"],
|
||||
address: {
|
||||
street: "123 Main St",
|
||||
city: "Anytown",
|
||||
state: "CA",
|
||||
zip: "12345",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
confirmedAt: [{ $isNull: false }],
|
||||
};
|
||||
|
||||
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false when payload does not match string filter", () => {
|
||||
const payload = {
|
||||
name: "Jane",
|
||||
age: 25,
|
||||
hobbies: ["running", "yoga"],
|
||||
address: {
|
||||
street: "456 Elm St",
|
||||
city: "Othertown",
|
||||
state: "NY",
|
||||
zip: "67890",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
name: ["John"],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false when payload does not match string filter because it's the wrong type", () => {
|
||||
const payload = {
|
||||
name: "Jane",
|
||||
age: 25,
|
||||
hobbies: ["running", "yoga"],
|
||||
address: {
|
||||
street: "456 Elm St",
|
||||
city: "Othertown",
|
||||
state: "NY",
|
||||
zip: "67890",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
age: ["John"],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false when payload does not match boolean filter", () => {
|
||||
const payload = {
|
||||
name: "Jane",
|
||||
age: 25,
|
||||
isAdmin: true,
|
||||
hobbies: ["running", "yoga"],
|
||||
address: {
|
||||
street: "456 Elm St",
|
||||
city: "Othertown",
|
||||
state: "NY",
|
||||
zip: "67890",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
isAdmin: [false],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false when payload does not match number filter", () => {
|
||||
const payload = {
|
||||
name: "Jane",
|
||||
age: 25,
|
||||
isAdmin: true,
|
||||
hobbies: ["running", "yoga"],
|
||||
address: {
|
||||
street: "456 Elm St",
|
||||
city: "Othertown",
|
||||
state: "NY",
|
||||
zip: "67890",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
age: [30],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false when payload does not match $startsWith content filter", () => {
|
||||
const payload = {
|
||||
name: "Jane",
|
||||
age: 25,
|
||||
isAdmin: true,
|
||||
hobbies: ["running", "yoga"],
|
||||
address: {
|
||||
street: "456 Elm St",
|
||||
city: "Othertown",
|
||||
state: "NY",
|
||||
zip: "67890",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
name: [{ $startsWith: "Jo" }],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false when payload does not match $endsWith content filter", () => {
|
||||
const payload = {
|
||||
name: "Jane",
|
||||
age: 25,
|
||||
isAdmin: true,
|
||||
hobbies: ["running", "yoga"],
|
||||
address: {
|
||||
street: "456 Elm St",
|
||||
city: "Othertown",
|
||||
state: "NY",
|
||||
zip: "67890",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
name: [{ $startsWith: "Ja" }, { $endsWith: "hn" }],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false when payload does match $anythingBut content filters", () => {
|
||||
const payload = {
|
||||
name: "Jane",
|
||||
age: 25,
|
||||
isAdmin: true,
|
||||
hobbies: ["running", "yoga"],
|
||||
address: {
|
||||
street: "456 Elm St",
|
||||
city: "Othertown",
|
||||
state: "NY",
|
||||
zip: "67890",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
name: [{ $anythingBut: "Jane" }],
|
||||
address: {
|
||||
street: [{ $anythingBut: "456 Elm St" }],
|
||||
},
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false when payload does match $anythingBut content filters with an array", () => {
|
||||
const payload = {
|
||||
name: "Jane",
|
||||
age: 25,
|
||||
isAdmin: true,
|
||||
hobbies: ["running", "yoga"],
|
||||
address: {
|
||||
street: "456 Elm St",
|
||||
city: "Othertown",
|
||||
state: "NY",
|
||||
zip: "67890",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
name: [{ $anythingBut: ["Jane", "John"] }],
|
||||
address: {
|
||||
street: [{ $anythingBut: "456 Elm St" }],
|
||||
},
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false when payload does not have a key that $exists = true", () => {
|
||||
const payload = {
|
||||
name: "Jane",
|
||||
age: 25,
|
||||
isAdmin: true,
|
||||
hobbies: ["running", "yoga"],
|
||||
address: {
|
||||
street: "456 Elm St",
|
||||
city: "Othertown",
|
||||
state: "NY",
|
||||
zip: "67890",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
foo: [{ $exists: true }],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false when payload does have a key that $exists = false", () => {
|
||||
const payload = {
|
||||
name: "Jane",
|
||||
age: 25,
|
||||
score: 100,
|
||||
isAdmin: true,
|
||||
hobbies: ["running", "yoga"],
|
||||
address: {
|
||||
street: "456 Elm St",
|
||||
city: "Othertown",
|
||||
state: "NY",
|
||||
zip: "67890",
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
name: [{ $exists: false }],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false when the payload does not match the numeric filters", () => {
|
||||
const payload = {
|
||||
name: "Jane",
|
||||
age: 25,
|
||||
score: 100,
|
||||
isAdmin: true,
|
||||
hobbies: ["running", "yoga"],
|
||||
address: {
|
||||
street: "456 Elm St",
|
||||
city: "Othertown",
|
||||
state: "NY",
|
||||
zip: "67890",
|
||||
latitude: 37.7749,
|
||||
longitude: 122.4194,
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
age: [{ $gt: 30 }],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||
});
|
||||
|
||||
it("Should return false when the payload does not match an includes filter", () => {
|
||||
const payload = {
|
||||
name: "Jane",
|
||||
age: 25,
|
||||
score: 100,
|
||||
isAdmin: true,
|
||||
hobbies: ["running", "yoga"],
|
||||
address: {
|
||||
street: "456 Elm St",
|
||||
city: "San Francisco",
|
||||
state: "CA",
|
||||
zip: "67890",
|
||||
latitude: 37.7749,
|
||||
longitude: 122.4194,
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
hobbies: [{ $includes: "swimming" }],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||
});
|
||||
|
||||
it("Should return false when the payload does not match any ignoreCaseEquals condition", () => {
|
||||
const payload = {
|
||||
name: "Jane",
|
||||
age: 25,
|
||||
score: 100,
|
||||
isAdmin: true,
|
||||
hobbies: ["running", "yoga"],
|
||||
address: {
|
||||
street: "456 Elm St",
|
||||
city: "San Francisco",
|
||||
state: "CA",
|
||||
zip: "67890",
|
||||
latitude: 37.7749,
|
||||
longitude: 122.4194,
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
name: [{ $ignoreCaseEquals: "john" }],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false when the payload does not match an isNull condition", () => {
|
||||
const payload = {
|
||||
name: "Jane",
|
||||
age: 25,
|
||||
score: 100,
|
||||
isAdmin: true,
|
||||
hobbies: ["running", "yoga"],
|
||||
confirmedAt: "2020-01-01T00:00:00.000Z",
|
||||
address: {
|
||||
street: "456 Elm St",
|
||||
city: "San Francisco",
|
||||
state: "CA",
|
||||
zip: "67890",
|
||||
latitude: 37.7749,
|
||||
longitude: 122.4194,
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
confirmedAt: [{ $isNull: true }],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false when the payload does not match an isNull condition (flipped)", () => {
|
||||
const payload = {
|
||||
name: "Jane",
|
||||
age: 25,
|
||||
score: 100,
|
||||
isAdmin: true,
|
||||
hobbies: ["running", "yoga"],
|
||||
confirmedAt: null,
|
||||
address: {
|
||||
street: "456 Elm St",
|
||||
city: "San Francisco",
|
||||
state: "CA",
|
||||
zip: "67890",
|
||||
latitude: 37.7749,
|
||||
longitude: 122.4194,
|
||||
},
|
||||
};
|
||||
const filter: EventFilter = {
|
||||
confirmedAt: [{ $isNull: false }],
|
||||
};
|
||||
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "@trigger.dev/tsconfig/node18.json",
|
||||
"include": ["src/globals.d.ts", "./src/**/*.ts", "tsup.config.ts"],
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"declaration": false,
|
||||
"declarationMap": false
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"extends": "@trigger.dev/tsconfig/node18.json",
|
||||
"include": ["src/globals.d.ts", "./src/**/*.ts", "tsup.config.ts"],
|
||||
"include": ["src/globals.d.ts", "./src/**/*.ts", "tsup.config.ts", "./test/**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"declaration": false,
|
||||
"declarationMap": false
|
||||
"declarationMap": false,
|
||||
"types": ["jest"]
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { defineConfig } from "tsup";
|
||||
export default defineConfig([
|
||||
{
|
||||
name: "main",
|
||||
config: "tsconfig.build.json",
|
||||
entry: ["./src/index.ts"],
|
||||
outDir: "./dist",
|
||||
platform: "neutral",
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# @trigger.dev/express
|
||||
|
||||
## 2.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.0.7
|
||||
|
||||
## 2.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.0.6
|
||||
|
||||
## 2.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.0.5
|
||||
|
||||
## 2.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/express",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.7",
|
||||
"description": "Official Express adapter for Trigger.dev",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -18,7 +18,7 @@
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^2.0.4",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.7",
|
||||
"@trigger.dev/tsconfig": "workspace:*",
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/express": "^4.17.13",
|
||||
@@ -32,7 +32,7 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^2.0.4"
|
||||
"@trigger.dev/sdk": "workspace:^2.0.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@remix-run/web-fetch": "^4.3.5",
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# @trigger.dev/integration-kit
|
||||
|
||||
## 2.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 84126808: Adding createImageEdit and createImageVariation tasks to openai
|
||||
|
||||
## 2.0.6
|
||||
|
||||
## 2.0.5
|
||||
|
||||
## 2.0.4
|
||||
|
||||
## 2.0.3
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/integration-kit",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.7",
|
||||
"description": "Trigger.dev Integration Kit has helpers to make creating integrations easier",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -20,6 +20,7 @@
|
||||
"devDependencies": {
|
||||
"@trigger.dev/tsconfig": "workspace:*",
|
||||
"@types/node": "18",
|
||||
"@types/node-fetch": "2.6.x",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"tsup": "^6.5.0",
|
||||
@@ -32,6 +33,7 @@
|
||||
"typecheck": "tsup --dts-resolve --no-dts"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-fetch": "2.6.x",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
import fs, { promises } from "fs";
|
||||
import path from "path";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import fetch from "node-fetch";
|
||||
|
||||
export async function fileFromString(contents: string, fileName: string) {
|
||||
export async function fileFromString(contents: string | Buffer, fileName: string): Promise<File> {
|
||||
const directory = path.join("tmp", uuidv4());
|
||||
await promises.mkdir(directory, { recursive: true });
|
||||
const filePath = path.join(directory, fileName);
|
||||
await promises.writeFile(filePath, contents);
|
||||
return fs.createReadStream(filePath);
|
||||
return fs.createReadStream(filePath) as unknown as File;
|
||||
}
|
||||
|
||||
export async function fileFromUrl(url: string) {
|
||||
const response = await fetch(url);
|
||||
const content = await response.buffer();
|
||||
const fileName = path.basename(url);
|
||||
|
||||
return fileFromString(content, fileName);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# @trigger.dev/nextjs
|
||||
|
||||
## 2.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.0.7
|
||||
|
||||
## 2.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.0.6
|
||||
|
||||
## 2.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/sdk@2.0.5
|
||||
|
||||
## 2.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/nextjs",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.7",
|
||||
"description": "Trigger.dev Next.js integration",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -32,7 +32,7 @@
|
||||
"build:tsup": "tsup"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trigger.dev/sdk": "workspace:^2.0.4",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.7",
|
||||
"next": ">=12.0.0 <14.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
# @trigger.dev/react
|
||||
|
||||
## 2.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [fa3a22eb]
|
||||
- @trigger.dev/core@2.0.7
|
||||
|
||||
## 2.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [59075f5f]
|
||||
- @trigger.dev/core@2.0.6
|
||||
|
||||
## 2.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/core@2.0.5
|
||||
|
||||
## 2.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/react",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.7",
|
||||
"description": "Trigger.dev React SDK",
|
||||
"types": "dist/index.d.ts",
|
||||
"main": "dist/index.cjs",
|
||||
@@ -26,7 +26,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "5.0.0-beta.2",
|
||||
"@trigger.dev/core": "workspace:^2.0.4",
|
||||
"@trigger.dev/core": "workspace:^2.0.7",
|
||||
"debug": "^4.3.4",
|
||||
"zod": "3.21.4"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
# @trigger.dev/sdk
|
||||
|
||||
## 2.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [fa3a22eb]
|
||||
- @trigger.dev/core@2.0.7
|
||||
|
||||
## 2.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [59075f5f]
|
||||
- @trigger.dev/core@2.0.6
|
||||
|
||||
## 2.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @trigger.dev/core@2.0.5
|
||||
|
||||
## 2.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@trigger.dev/sdk",
|
||||
"version": "2.0.4",
|
||||
"version": "2.0.7",
|
||||
"description": "trigger.dev Node.JS SDK",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -24,7 +24,7 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/core": "workspace:^2.0.4",
|
||||
"@trigger.dev/core": "workspace:^2.0.7",
|
||||
"chalk": "^5.2.0",
|
||||
"cronstrue": "^2.21.0",
|
||||
"debug": "^4.3.4",
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// require('dotenv').config();
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
testDir: "./tests/e2e",
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: "html",
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
baseURL: "http://localhost:3030",
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: "on-first-retry",
|
||||
},
|
||||
|
||||
globalSetup: require.resolve("./tests/global.setup.ts"),
|
||||
globalTeardown: require.resolve("./tests/global.teardown.ts"),
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
},
|
||||
|
||||
{
|
||||
name: "firefox",
|
||||
use: { ...devices["Desktop Firefox"] },
|
||||
},
|
||||
|
||||
{
|
||||
name: "webkit",
|
||||
use: { ...devices["Desktop Safari"] },
|
||||
},
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: [
|
||||
{
|
||||
command: "pnpm run --filter webapp dev",
|
||||
url: "http://localhost:3030",
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
{
|
||||
command: "pnpm run --filter @examples/nextjs-test dev",
|
||||
url: "http://localhost:3000",
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
test("Create an account", async ({ page }) => {
|
||||
await page.goto("/login");
|
||||
await page.getByRole("link", { name: "Continue with Email" }).click();
|
||||
|
||||
await page.getByPlaceholder("Email Address").type(`test_${Math.random()}@test.com`);
|
||||
await page.getByRole("button", { name: "Send a magic link" }).click();
|
||||
|
||||
await expect(page.getByText("Welcome to Trigger.dev")).toBeVisible();
|
||||
await page.getByLabel("Full name").type("John Doe");
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
|
||||
await page.getByLabel("Organization name").type("Test Org");
|
||||
await page.getByLabel("Project name").type("Test Project");
|
||||
await page.getByRole("button", { name: "Create" }).click();
|
||||
|
||||
await expect(page.locator("h1").filter({ hasText: /^Jobs$/ })).toBeVisible();
|
||||
});
|
||||
|
||||
test("Verify jobs from the test nextjs project", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.getByRole("link", { name: "Continue with Email" }).click();
|
||||
|
||||
await page.getByPlaceholder("Email Address").type("test-user@test.com");
|
||||
await page.getByRole("button", { name: "Send a magic link" }).click();
|
||||
|
||||
await page.getByRole("link", { name: /Test Project/ }).click();
|
||||
await expect(page.locator("h1").filter({ hasText: /^Jobs$/ })).toBeVisible();
|
||||
|
||||
await page.getByRole("link", { name: "Environments & API Keys" }).click();
|
||||
await expect(page.locator("h1").filter({ hasText: "Environments & API Keys" })).toBeVisible();
|
||||
await expect(
|
||||
page.locator("h3").filter({ hasText: "nextjs-test" })
|
||||
// Set the timeout high to allow the cli to register jobs
|
||||
).toBeVisible({ timeout: 15000 });
|
||||
|
||||
await page.getByRole("link", { name: "Jobs" }).click();
|
||||
await expect(page.locator("h1").filter({ hasText: /^Jobs$/ })).toBeVisible();
|
||||
await expect(page.getByRole("link", { name: /Test Job One/ })).toBeVisible();
|
||||
});
|
||||
@@ -0,0 +1,99 @@
|
||||
import { setDB } from "./utils";
|
||||
|
||||
const setup = async () => {
|
||||
await setDB(async (prisma) => {
|
||||
// Create test user
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
email: "test-user@test.com",
|
||||
name: "Test User",
|
||||
authenticationMethod: "MAGIC_LINK",
|
||||
confirmedBasicDetails: true
|
||||
},
|
||||
});
|
||||
|
||||
// Create test organization
|
||||
const organization = await prisma.organization.create({
|
||||
data: {
|
||||
title: "Test Organization",
|
||||
slug: "test-org",
|
||||
members: {
|
||||
create: {
|
||||
userId: user.id,
|
||||
role: "ADMIN",
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
members: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Create test project
|
||||
const project = await prisma.project.create({
|
||||
data: {
|
||||
name: "Test Project",
|
||||
slug: "test-project",
|
||||
organization: {
|
||||
connect: {
|
||||
slug: organization.slug,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
organization: {
|
||||
include: {
|
||||
members: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Create test environment
|
||||
await prisma.runtimeEnvironment.create({
|
||||
data: {
|
||||
slug: "dev",
|
||||
// Defined in @examples/nextjs-test
|
||||
apiKey: "tr_dev_test-api-key",
|
||||
pkApiKey: "tr_dev_pk_test-api-key",
|
||||
autoEnableInternalSources: false,
|
||||
organization: {
|
||||
connect: {
|
||||
id: organization.id,
|
||||
},
|
||||
},
|
||||
project: {
|
||||
connect: {
|
||||
id: project.id,
|
||||
},
|
||||
},
|
||||
orgMember: { connect: { id: project.organization.members[0].id } },
|
||||
type: "DEVELOPMENT",
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.runtimeEnvironment.create({
|
||||
data: {
|
||||
slug: "prod",
|
||||
// Defined in @examples/nextjs-test
|
||||
apiKey: "tr_prod_test-api-key",
|
||||
pkApiKey: "tr_prod_pk_test-api-key",
|
||||
autoEnableInternalSources: false,
|
||||
organization: {
|
||||
connect: {
|
||||
id: organization.id,
|
||||
},
|
||||
},
|
||||
project: {
|
||||
connect: {
|
||||
id: project.id,
|
||||
},
|
||||
},
|
||||
orgMember: { connect: { id: project.organization.members[0].id } },
|
||||
type: "PRODUCTION",
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export default setup;
|
||||