diff --git a/.changeset/famous-files-talk.md b/.changeset/famous-files-talk.md new file mode 100644 index 000000000..dad0a1a34 --- /dev/null +++ b/.changeset/famous-files-talk.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/cli": patch +--- + +Detects JSRuntime (Node/Deno at the moment). Adds basic Deno support diff --git a/.changeset/gorgeous-panthers-run.md b/.changeset/gorgeous-panthers-run.md new file mode 100644 index 000000000..63a677380 --- /dev/null +++ b/.changeset/gorgeous-panthers-run.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/cli": patch +--- + +Improve create-integration output. Use templates and shared configs. diff --git a/.changeset/warm-elephants-battle.md b/.changeset/warm-elephants-battle.md new file mode 100644 index 000000000..58e9abcd5 --- /dev/null +++ b/.changeset/warm-elephants-battle.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/airtable": patch +--- + +Export Base and Table diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 4e9aa8c95..308437901 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,4 @@ -blank_issues_enabled: false +blank_issues_enabled: true contact_links: - name: Ask a Question url: https://trigger.dev/discord diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 000000000..a267eba82 --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,65 @@ +name: "πŸ§ͺ E2E Tests" +on: + workflow_call: +jobs: + e2e: + name: "πŸ§ͺ E2E Tests" + runs-on: buildjet-4vcpu-ubuntu-2204 + steps: + - name: 🐳 Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - 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 ./references/nextjs-test/.env.example ./references/nextjs-test/.env.local + + # Build packages + pnpm run build --filter @references/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 diff --git a/.github/workflows/pr_checks.yml b/.github/workflows/pr_checks.yml new file mode 100644 index 000000000..6a1cd2acc --- /dev/null +++ b/.github/workflows/pr_checks.yml @@ -0,0 +1,31 @@ +name: πŸ€– PR Checks + +on: + pull_request_target: + branches: + - main + paths-ignore: + - "**.md" + - ".github/CODEOWNERS" + - ".github/ISSUE_TEMPLATE/**" + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + id-token: write + +jobs: + typecheck: + uses: ./.github/workflows/typecheck.yml + secrets: inherit + + units: + uses: ./.github/workflows/unit-tests.yml + secrets: inherit + + e2e: + uses: ./.github/workflows/e2e.yml + secrets: inherit diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 74282ef69..3a4f2fb47 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -38,125 +38,19 @@ env: jobs: typecheck: - name: Κ¦ TypeScript - runs-on: buildjet-4vcpu-ubuntu-2204 - steps: - - name: ⬇️ Checkout repo - uses: actions/checkout@v3 - with: - fetch-depth: 0 + uses: ./.github/workflows/typecheck.yml + secrets: inherit - - 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: πŸ“€ Generate Prisma Client - run: pnpm run generate - - - 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 + units: + uses: ./.github/workflows/unit-tests.yml + secrets: inherit e2e: - name: e2e Tests - runs-on: buildjet-4vcpu-ubuntu-2204 - steps: - - name: 🐳 Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - 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 ./references/nextjs-test/.env.example ./references/nextjs-test/.env.local - - # Build packages - pnpm run build --filter @references/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 + uses: ./.github/workflows/e2e.yml + secrets: inherit publish: - needs: [typecheck, unitTests, e2e] + needs: [typecheck, units, e2e] runs-on: buildjet-4vcpu-ubuntu-2204 outputs: version: ${{ steps.get_version.outputs.version }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 212fa70d1..cb954be9d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,16 +4,11 @@ on: push: branches: - main - paths: - - ".github/workflows/release.yml" - - "packages/**" - - "!packages/**/*.md" - - ".changeset/**" - - "integrations/**" - - "!integrations/**/*.md" - - "pnpm-lock.yaml" - - "pnpm-workspace.yaml" - - "turbo.json" + paths-ignore: + - "**.md" + - ".github/CODEOWNERS" + - ".github/ISSUE_TEMPLATE/**" + jobs: release: diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml new file mode 100644 index 000000000..1d32f6945 --- /dev/null +++ b/.github/workflows/typecheck.yml @@ -0,0 +1,32 @@ +name: "Κ¦ TypeScript" +on: + workflow_call: +jobs: + typecheck: + 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: πŸ“€ Generate Prisma Client + run: pnpm run generate + + - name: πŸ”Ž Type check + run: pnpm run typecheck --filter webapp diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml new file mode 100644 index 000000000..f90f81fab --- /dev/null +++ b/.github/workflows/unit-tests.yml @@ -0,0 +1,30 @@ +name: "πŸ§ͺ Unit Tests" +on: + workflow_call: +jobs: + 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 diff --git a/.nvmrc b/.nvmrc index 95c758cad..b714151ef 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v18.12.1 \ No newline at end of file +v18.18.0 \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..312d6bbd4 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "astro-build.astro-vscode", + "denoland.vscode-deno" + ], + "unwantedRecommendations": [ + + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..5f5239032 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "deno.enablePaths": ["references/deno-reference"] +} diff --git a/CHANGESETS.md b/CHANGESETS.md index 9565e908c..dab130bc8 100644 --- a/CHANGESETS.md +++ b/CHANGESETS.md @@ -32,7 +32,26 @@ Please follow the best-practice of adding changesets in the same commit as the c !MAKE SURE TO UPDATE THE TAG IN THE INSTRUCTIONS BELOW! -1. Add changesets as usual `pnpm run changeset:add` -2. Create a snapshot version (replace "dev" with your tag) `pnpm exec changeset version --snapshot dev` -3. Build the packages: `pnpm run build --filter "@trigger.dev/*"` -4. Publish the snapshot (replace "dev" with your tag) `pnpm exec changeset publish --no-git-tag --snapshot --tag dev` +1. Add changesets as usual + +```sh +pnpm run changeset:add +``` + +2. Create a snapshot version (replace "prerelease" with your tag) + +```sh +pnpm exec changeset version --snapshot prerelease +``` + +3. Build the packages: + +```sh +pnpm run build --filter "@trigger.dev/*" +``` + +4. Publish the snapshot (replace "dev" with your tag) + +```sh +pnpm exec changeset publish --no-git-tag --snapshot --tag prerelease +``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 471123fd3..231cbdf1f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,9 +52,13 @@ branch are tagged into a release monthly. Feel free to update `SESSION_SECRET` and `MAGIC_LINK_SECRET` as well using the same method. 6. Start Docker. This starts the required services like Postgres. If this is your first time using Docker, consider going through this [guide](DOCKER_INSTALLATION.md) + ``` pnpm run docker ``` + + This will also start and run a local instance of [pgAdmin](https://www.pgadmin.org/) on [localhost:5480](http://localhost:5480), preconfigured with email `admin@example.com` and pwd `admin`. Then use `postgres` as the password to the Trigger.dev server. + 7. Migrate the database ``` pnpm run db:migrate diff --git a/README.md b/README.md index 5f6d3622d..609dfd8ab 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,18 @@ +# βœ¨πŸŽƒ Get involved with Hacktoberfest 2023! πŸŽƒβœ¨ + +All of October we're participating in Hacktoberfest and invite you to join us! We have a bunch of issues labeled `πŸŽƒ Hacktoberfest` that are ready for you to work on which will count towards Hacktoberfest. We are also running our own game, earn πŸ’Ž points to win swag! + +- Check out our [Hacktoberfest landing page](https://trigger.dev/hacktoberfest) for how to participate and win swag. +- Contribute to either our [/trigger.dev](https://github.com/triggerdotdev/trigger.dev/labels/%F0%9F%8E%83%20hacktoberfest) or [/jobs-showcase](https://github.com/triggerdotdev/jobs-showcase/labels/%F0%9F%8E%83%20hacktoberfest) repositories and complete issues marked `πŸŽƒ Hacktoberfest` to be eligible for swag. +- Join our [Discord](https://discord.gg/JtBAxBr2m3) and get involved in with the community. + +_New to Hacktober? Check out the [Hacktoberfest website](https://hacktoberfest.digitalocean.com/) for more information._ + +πŸŽƒ **Happy Hacking!** πŸŽƒ + # About Trigger.dev Create long-running jobs directly in your codebase with features like API integrations, webhooks, scheduling and delays. @@ -62,8 +74,8 @@ Click the links to join the discussions about our upcoming features. | Dashboard | View every Task in every Run | βœ… | | Serverless | Long-running Jobs on your serverless backend | βœ… | | React hooks | Easily update your UI with Job progress | βœ… | +| React frameworks | Support for Remix, Astro, RedwoodJS & more | βœ… | | [Background tasks](https://github.com/triggerdotdev/trigger.dev/discussions/400) | Offload long or intense Tasks to our infrastructure | πŸ› οΈ | -| [React frameworks](https://github.com/triggerdotdev/trigger.dev/discussions/411) | Support for Remix, Astro, RedwoodJS & more | πŸ› οΈ | | [Long-running servers](https://github.com/triggerdotdev/trigger.dev/discussions/430) | Run Jobs on your long-running backend | πŸ› οΈ | | Polling Triggers | Subscribe to changes without webhooks | πŸ• | | Vercel integration | Easy deploy and preview environment support | πŸ• | @@ -83,3 +95,9 @@ We provide an official trigger.dev docker image you can use to easily self-host ## Development To setup and develop locally or contribute to the open source project, follow our [development guide](./CONTRIBUTING.md). + +## πŸ™ to our contributors + + + + diff --git a/apps/webapp/app/components/SetupCommands.tsx b/apps/webapp/app/components/SetupCommands.tsx index 245eeff8b..5c3469369 100644 --- a/apps/webapp/app/components/SetupCommands.tsx +++ b/apps/webapp/app/components/SetupCommands.tsx @@ -44,28 +44,7 @@ export function InitCommand({ appOrigin, apiKey }: { appOrigin: string; apiKey: ); } -export function RunDevCommand() { - return ( - - - npm - pnpm - yarn - - - - - - - - - - - - ); -} - -export function TriggerDevCommand() { +export function RunDevCommand({ extra }: { extra?: string }) { return ( @@ -77,34 +56,67 @@ export function TriggerDevCommand() { ); } -export function TriggerDevStep() { +export function TriggerDevCommand({ extra }: { extra?: string }) { + return ( + + + npm + pnpm + yarn + + + + + + + + + + + + ); +} + +export function TriggerDevStep({ extra }: { extra?: string }) { return ( <> In a separate terminal window or tab run: - + If you’re not running on the default you can specify the port by adding{" "} --port 3001 to the end. diff --git a/apps/webapp/app/components/code/InstallPackages.tsx b/apps/webapp/app/components/code/InstallPackages.tsx new file mode 100644 index 000000000..791d101da --- /dev/null +++ b/apps/webapp/app/components/code/InstallPackages.tsx @@ -0,0 +1,44 @@ +import { + ClientTabs, + ClientTabsList, + ClientTabsTrigger, + ClientTabsContent, +} from "../primitives/ClientTabs"; +import { ClipboardField } from "../primitives/ClipboardField"; + +type InstallPackagesProps = { + packages: string[]; +}; + +export function InstallPackages({ packages }: InstallPackagesProps) { + return ( + + + npm + pnpm + yarn + + + + + + + + + + + + ); +} diff --git a/apps/webapp/app/components/code/JSONEditor.tsx b/apps/webapp/app/components/code/JSONEditor.tsx index 4b635904a..7d5ef0c23 100644 --- a/apps/webapp/app/components/code/JSONEditor.tsx +++ b/apps/webapp/app/components/code/JSONEditor.tsx @@ -1,11 +1,13 @@ import { json as jsonLang } from "@codemirror/lang-json"; import type { ViewUpdate } from "@codemirror/view"; +import { CheckIcon, ClipboardIcon } from "@heroicons/react/20/solid"; import type { ReactCodeMirrorProps, UseCodeMirror } from "@uiw/react-codemirror"; import { useCodeMirror } from "@uiw/react-codemirror"; -import { useRef, useEffect } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { cn } from "~/utils/cn"; +import { Button } from "../primitives/Buttons"; import { getEditorSetup } from "./codeMirrorSetup"; import { darkTheme } from "./codeMirrorTheme"; -import { cn } from "~/utils/cn"; export interface JSONEditorProps extends Omit { defaultValue?: string; @@ -14,6 +16,8 @@ export interface JSONEditorProps extends Omit { onChange?: (value: string) => void; onUpdate?: (update: ViewUpdate) => void; onBlur?: (code: string) => void; + showCopyButton?: boolean; + showClearButton?: boolean; } const languages = { @@ -38,6 +42,8 @@ export function JSONEditor(opts: JSONEditorProps) { onBlur, basicSetup, autoFocus, + showCopyButton = true, + showClearButton = true, } = { ...defaultProps, ...opts, @@ -65,7 +71,8 @@ export function JSONEditor(opts: JSONEditorProps) { onChange, onUpdate, }; - const { setContainer, state } = useCodeMirror(settings); + const { setContainer, view } = useCodeMirror(settings); + const [copied, setCopied] = useState(false); useEffect(() => { if (editor.current) { @@ -75,24 +82,71 @@ export function JSONEditor(opts: JSONEditorProps) { //if the defaultValue changes update the editor useEffect(() => { - if (state !== undefined) { - state.update({ - changes: { from: 0, to: state.doc.length, insert: defaultValue }, + if (view !== undefined) { + if (view.state.doc.toString() === defaultValue) return; + view.dispatch({ + changes: { from: 0, to: view.state.doc.length, insert: defaultValue }, }); } - }, [defaultValue, state]); + }, [defaultValue, view]); + + const clear = useCallback(() => { + if (view === undefined) return; + view.dispatch({ + changes: { from: 0, to: view.state.doc.length, insert: undefined }, + }); + onChange?.(""); + }, [view]); + + const copy = useCallback(() => { + if (view === undefined) return; + navigator.clipboard.writeText(view.state.doc.toString()); + setCopied(true); + setTimeout(() => { + setCopied(false); + }, 1500); + }, [view]); return ( -
{ - if (!onBlur) return; - onBlur(editor.current?.textContent ?? ""); - }} - /> +
+
{ + if (!onBlur) return; + onBlur(editor.current?.textContent ?? ""); + }} + /> +
+ {showClearButton && ( + + )} + {showCopyButton && ( + + )} +
+
); } diff --git a/apps/webapp/app/components/code/codeMirrorSetup.ts b/apps/webapp/app/components/code/codeMirrorSetup.ts index 9e7ff711d..89988c3d9 100644 --- a/apps/webapp/app/components/code/codeMirrorSetup.ts +++ b/apps/webapp/app/components/code/codeMirrorSetup.ts @@ -1,34 +1,19 @@ -import { - highlightSpecialChars, - drawSelection, - highlightActiveLine, - dropCursor, - lineNumbers, - highlightActiveLineGutter, - keymap, -} from "@codemirror/view"; -import type { Extension } from "@codemirror/state"; -import { highlightSelectionMatches } from "@codemirror/search"; -import { json as jsonLang } from "@codemirror/lang-json"; import { closeBrackets } from "@codemirror/autocomplete"; -import { bracketMatching } from "@codemirror/language"; import { indentWithTab } from "@codemirror/commands"; - -export function getPreviewSetup(): Array { - return [ - jsonLang(), - highlightSpecialChars(), - drawSelection(), - dropCursor(), - bracketMatching(), - highlightSelectionMatches(), - lineNumbers(), - ]; -} - -export function getViewerSetup(): Array { - return [drawSelection(), dropCursor(), bracketMatching(), lineNumbers()]; -} +import { jsonParseLinter } from "@codemirror/lang-json"; +import { bracketMatching } from "@codemirror/language"; +import { lintGutter, lintKeymap, linter } from "@codemirror/lint"; +import { highlightSelectionMatches } from "@codemirror/search"; +import { Prec, type Extension } from "@codemirror/state"; +import { + drawSelection, + dropCursor, + highlightActiveLine, + highlightActiveLineGutter, + highlightSpecialChars, + keymap, + lineNumbers, +} from "@codemirror/view"; export function getEditorSetup(showLineNumbers = true, showHighlights = true): Array { const options = [ @@ -36,7 +21,20 @@ export function getEditorSetup(showLineNumbers = true, showHighlights = true): A dropCursor(), bracketMatching(), closeBrackets(), - keymap.of([indentWithTab]), + lintGutter(), + linter(jsonParseLinter()), + Prec.highest( + keymap.of([ + { + key: "Mod-Enter", + run: () => { + return true; + }, + preventDefault: false, + }, + ]) + ), + keymap.of([indentWithTab, ...lintKeymap]), ]; if (showLineNumbers) { diff --git a/apps/webapp/app/components/code/codeMirrorTheme.ts b/apps/webapp/app/components/code/codeMirrorTheme.ts index 0492b80b9..babcccf09 100644 --- a/apps/webapp/app/components/code/codeMirrorTheme.ts +++ b/apps/webapp/app/components/code/codeMirrorTheme.ts @@ -17,10 +17,15 @@ export function darkTheme(): Extension { violet = "#c678dd", darkBackground = "#21252b", highlightBackground = "rgba(71,85,105,0.2)", - background = "#0f172a", + background = "rgba(11, 16, 24 ,100)", tooltipBackground = "#353a42", selection = "rgb(71 85 105)", - cursor = "#528bff"; + cursor = "#528bff", + scrollbarTrack = "#0E1521", + scrollbarTrackActive = "#131B2B", + scrollbarThumb = "#293649", + scrollbarThumbActive = "#3C4B62", + scrollbarBg = "#0E1521"; const jsonHeroEditorTheme = EditorView.theme( { @@ -94,6 +99,45 @@ export function darkTheme(): Extension { color: ivory, }, }, + ".cm-scroller": { + scrollbarWidth: "thin", + scrollbarColor: `${scrollbarThumb} ${scrollbarTrack}`, + }, + ".cm-scroller::-webkit-scrollbar": { + display: "block", + width: "8px", + height: "8px", + }, + ".cm-scroller::-webkit-scrollbar-track": { + backgroundColor: scrollbarTrack, + borderRadius: "0", + }, + ".cm-scroller::-webkit-scrollbar-track:hover": { + backgroundColor: scrollbarTrackActive, + }, + ".cm-scroller::-webkit-scrollbar-track:active": { + backgroundColor: scrollbarTrackActive, + }, + ".cm-scroller::-webkit-scrollbar-thumb": { + backgroundColor: scrollbarThumb, + borderRadius: "0", + }, + ".cm-scroller::-webkit-scrollbar-thumb:hover": { + backgroundColor: scrollbarThumbActive, + }, + ".cm-scroller::-webkit-scrollbar-thumb:active": { + backgroundColor: scrollbarThumbActive, + }, + ".cm-scroller::-webkit-scrollbar-corner": { + backgroundColor: scrollbarBg, + borderRadius: "0", + }, + ".cm-scroller::-webkit-scrollbar-corner:hover": { + backgroundColor: scrollbarBg, + }, + ".cm-scroller::-webkit-scrollbar-corner:active": { + backgroundColor: scrollbarBg, + }, }, { dark: true } ); @@ -155,157 +199,3 @@ export function darkTheme(): Extension { return [jsonHeroEditorTheme, syntaxHighlighting(jsonHeroHighlightStyle)]; } - -export function lightTheme(): Extension[] { - const stringColor = "text-[#53a053]", - numberColor = "text-[#447bef]", - variableColor = "text-[#a42ea2]", - booleanColor = "text-[#e2574e]", - coral = "text-[#e06c75]", - invalid = "text-[#ffffff]", - ivory = "text-[#abb2bf]", - stone = "text-[#7d8799]", - malibu = "text-[#61afef]", - whiskey = "text-[#d19a66]", - violet = "text-[#c678dd]", - darkBackground = "text-[#21252b]", - highlightBackground = "text-[#D0D0D0]", - background = "text-[#ffffff]", - tooltipBackground = "text-[#353a42]", - selection = "text-[#D0D0D0]", - cursor = "text-[#528bff]"; - - const jsonHeroEditorTheme = EditorView.theme( - { - "&": { - color: ivory, - backgroundColor: background, - }, - - ".cm-content": { - caretColor: cursor, - fontFamily: "monospace", - fontSize: "14px", - }, - - ".cm-cursor, .cm-dropCursor": { borderLeftColor: cursor }, - "&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": { - backgroundColor: selection, - }, - - ".cm-panels": { backgroundColor: darkBackground, color: ivory }, - ".cm-panels.cm-panels-top": { borderBottom: "2px solid black" }, - ".cm-panels.cm-panels-bottom": { borderTop: "2px solid black" }, - - ".cm-searchMatch": { - backgroundColor: "#72a1ff59", - outline: "1px solid #457dff", - }, - ".cm-searchMatch.cm-searchMatch-selected": { - backgroundColor: "#6199ff2f", - }, - - ".cm-activeLine": { backgroundColor: highlightBackground }, - ".cm-selectionMatch": { backgroundColor: "#aafe661a" }, - - "&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": { - backgroundColor: "#bad0f847", - outline: "1px solid #515a6b", - }, - - ".cm-gutters": { - backgroundColor: background, - color: stone, - border: "none", - }, - - ".cm-activeLineGutter": { - backgroundColor: highlightBackground, - }, - - ".cm-foldPlaceholder": { - backgroundColor: "transparent", - border: "none", - color: "#ddd", - }, - - ".cm-tooltip": { - border: "none", - backgroundColor: tooltipBackground, - }, - ".cm-tooltip .cm-tooltip-arrow:before": { - borderTopColor: "transparent", - borderBottomColor: "transparent", - }, - ".cm-tooltip .cm-tooltip-arrow:after": { - borderTopColor: tooltipBackground, - borderBottomColor: tooltipBackground, - }, - ".cm-tooltip-autocomplete": { - "& > ul > li[aria-selected]": { - backgroundColor: highlightBackground, - color: ivory, - }, - }, - }, - { dark: false } - ); - - /// The highlighting style for code in the JSON Hero theme. - const jsonHeroHighlightStyle = tagHighlighter([ - { tag: tags.keyword, class: violet }, - { - tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName], - class: variableColor, - }, - { - tag: [tags.function(tags.variableName), tags.labelName], - class: malibu, - }, - { - tag: [tags.color, tags.constant(tags.name), tags.standard(tags.name)], - class: whiskey, - }, - { tag: [tags.definition(tags.name), tags.separator], class: ivory }, - { - tag: [ - tags.typeName, - tags.className, - tags.number, - tags.changed, - tags.annotation, - tags.modifier, - tags.self, - tags.namespace, - ], - class: numberColor, - }, - { - tag: [ - tags.operator, - tags.operatorKeyword, - tags.url, - tags.escape, - tags.regexp, - tags.link, - tags.special(tags.string), - ], - class: stringColor, - }, - { tag: [tags.meta, tags.comment], class: stone }, - - { tag: tags.link, class: stone }, - { tag: tags.heading, class: coral }, - { - tag: [tags.atom, tags.bool, tags.special(tags.variableName)], - class: booleanColor, - }, - { - tag: [tags.processingInstruction, tags.string, tags.inserted], - class: stringColor, - }, - { tag: tags.invalid, class: invalid }, - ]); - - return [jsonHeroEditorTheme, syntaxHighlighting(jsonHeroHighlightStyle)]; -} diff --git a/apps/webapp/app/components/environments/EndpointIndexStatus.tsx b/apps/webapp/app/components/environments/EndpointIndexStatus.tsx new file mode 100644 index 000000000..67398b0b7 --- /dev/null +++ b/apps/webapp/app/components/environments/EndpointIndexStatus.tsx @@ -0,0 +1,74 @@ +import { CheckCircleIcon, ClockIcon, XCircleIcon } from "@heroicons/react/20/solid"; +import { EndpointIndexStatus } from "@trigger.dev/database"; +import { cn } from "~/utils/cn"; +import { Spinner } from "../primitives/Spinner"; + +export function EndpointIndexStatusIcon({ status }: { status: EndpointIndexStatus }) { + switch (status) { + case "PENDING": + return ; + case "STARTED": + return ; + case "SUCCESS": + return ( + + ); + case "FAILURE": + return ; + } +} + +export function EndpointIndexStatusLabel({ status }: { status: EndpointIndexStatus }) { + switch (status) { + case "PENDING": + return ( + + {endpointIndexStatusTitle(status)} + + ); + case "STARTED": + return ( + + {endpointIndexStatusTitle(status)} + + ); + case "SUCCESS": + return ( + + {endpointIndexStatusTitle(status)} + + ); + case "FAILURE": + return ( + + {endpointIndexStatusTitle(status)} + + ); + } +} + +export function endpointIndexStatusTitle(status: EndpointIndexStatus): string { + switch (status) { + case "PENDING": + return "Pending"; + case "STARTED": + return "Started"; + case "SUCCESS": + return "Success"; + case "FAILURE": + return "Failure"; + } +} + +export function endpointIndexStatusClassNameColor(status: EndpointIndexStatus): string { + switch (status) { + case "PENDING": + return "text-dimmed"; + case "STARTED": + return "text-blue-500"; + case "SUCCESS": + return "text-green-500"; + case "FAILURE": + return "text-rose-500"; + } +} diff --git a/apps/webapp/app/components/frameworks/FrameworkSelector.tsx b/apps/webapp/app/components/frameworks/FrameworkSelector.tsx index 0edbf578e..3ce50b1e5 100644 --- a/apps/webapp/app/components/frameworks/FrameworkSelector.tsx +++ b/apps/webapp/app/components/frameworks/FrameworkSelector.tsx @@ -66,13 +66,13 @@ export function FrameworkSelector() { - + - +
diff --git a/apps/webapp/app/components/helpContent/HelpContentText.tsx b/apps/webapp/app/components/helpContent/HelpContentText.tsx index 8e9a45871..ce057dae4 100644 --- a/apps/webapp/app/components/helpContent/HelpContentText.tsx +++ b/apps/webapp/app/components/helpContent/HelpContentText.tsx @@ -79,37 +79,6 @@ export function HowToRunYourJob() { ); } -export function HowToRunATest() { - return ( - <> - - - Select the environment you’d like the test to run against. - - - - - - Write your own payload specific to your Job. Some Triggers also provide example payloads - that you can select from. This will populate the code editor below. - - - - - - When you’re happy with the payload, click Run test. - - - Learn more about running tests. - - - ); -} - export function HowToConnectAnIntegration() { return ( <> diff --git a/apps/webapp/app/components/primitives/Buttons.tsx b/apps/webapp/app/components/primitives/Buttons.tsx index 532d36836..e469c2561 100644 --- a/apps/webapp/app/components/primitives/Buttons.tsx +++ b/apps/webapp/app/components/primitives/Buttons.tsx @@ -144,7 +144,7 @@ export function ButtonContent(props: ButtonContentPropsType) { const textColorClassName = variation.textColor; return ( -
+
, + textColor: "text-blue-300", + linkClassName: "transition hover:bg-blue-400/40", + }, } as const; +export type CalloutVariant = keyof typeof variantClasses; + export function Callout({ children, className, @@ -63,7 +72,7 @@ export function Callout({ children?: React.ReactNode; className?: string; icon?: React.ReactNode; - variant: keyof typeof variantClasses; + variant: CalloutVariant; to?: string; }) { const variantDefinition = variantClasses[variant]; diff --git a/apps/webapp/app/components/primitives/DateTime.tsx b/apps/webapp/app/components/primitives/DateTime.tsx index fcc9478cb..a9d618b86 100644 --- a/apps/webapp/app/components/primitives/DateTime.tsx +++ b/apps/webapp/app/components/primitives/DateTime.tsx @@ -63,16 +63,17 @@ export const DateTimeAccurate = ({ date, timeZone = "UTC" }: DateTimeProps) => { }; function formatDateTimeAccurate(date: Date, timeZone: string, locales: string[]): string { - const milliseconds = `00${date.getMilliseconds()}`.slice(-3); - const formattedDateTime = new Intl.DateTimeFormat(locales, { + year: "numeric", month: "short", - day: "2-digit", + day: "numeric", hour: "numeric", - minute: "2-digit", - second: "2-digit", + minute: "numeric", + second: "numeric", timeZone, + // @ts-ignore this works in 92.5% of browsers https://caniuse.com/mdn-javascript_builtins_intl_datetimeformat_datetimeformat_options_parameter_options_fractionalseconddigits_parameter + fractionalSecondDigits: 3, }).format(date); - return `${formatDateTime}.${milliseconds}`; + return formattedDateTime; } diff --git a/apps/webapp/app/components/primitives/DetailCell.tsx b/apps/webapp/app/components/primitives/DetailCell.tsx new file mode 100644 index 000000000..6047cc150 --- /dev/null +++ b/apps/webapp/app/components/primitives/DetailCell.tsx @@ -0,0 +1,95 @@ +import { cn } from "~/utils/cn"; +import { Icon, IconInBox, RenderIcon } from "./Icon"; +import { Paragraph } from "./Paragraph"; + +const variations = { + small: { + label: { + variant: "small" as const, + className: "m-0 leading-[1.1rem]", + }, + description: { + variant: "extra-small" as const, + className: "m-0", + }, + }, + base: { + label: { + variant: "base" as const, + className: "m-0 leading-[1.1rem] ", + }, + description: { + variant: "small" as const, + className: "m-0", + }, + }, +}; + +type DetailCellProps = { + leadingIcon?: RenderIcon; + leadingIconClassName?: string; + trailingIcon?: RenderIcon; + trailingIconClassName?: string; + label: string | React.ReactNode; + description?: string | React.ReactNode; + className?: string; + variant?: keyof typeof variations; +}; + +export function DetailCell({ + leadingIcon, + leadingIconClassName, + trailingIcon, + trailingIconClassName, + label, + description, + className, + variant = "small", +}: DetailCellProps) { + const variation = variations[variant]; + + return ( +
+ +
+ + {label} + + {description && ( + + {description} + + )} +
+
+ +
+
+ ); +} diff --git a/apps/webapp/app/components/primitives/FormError.tsx b/apps/webapp/app/components/primitives/FormError.tsx index 79d537d74..ed90f7e99 100644 --- a/apps/webapp/app/components/primitives/FormError.tsx +++ b/apps/webapp/app/components/primitives/FormError.tsx @@ -2,8 +2,17 @@ import type { z } from "zod"; import { Paragraph } from "./Paragraph"; import { NamedIcon } from "./NamedIcon"; import { motion } from "framer-motion"; +import { cn } from "~/utils/cn"; -export function FormError({ children, id }: { children: React.ReactNode; id?: string }) { +export function FormError({ + children, + id, + className, +}: { + children: React.ReactNode; + id?: string; + className?: string; +}) { return ( <> {children && ( @@ -11,7 +20,7 @@ export function FormError({ children, id }: { children: React.ReactNode; id?: st initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.3 }} - className="flex items-start gap-0.5" + className={cn("flex items-start gap-0.5", className)} > diff --git a/apps/webapp/app/components/primitives/Icon.tsx b/apps/webapp/app/components/primitives/Icon.tsx new file mode 100644 index 000000000..470d4cc80 --- /dev/null +++ b/apps/webapp/app/components/primitives/Icon.tsx @@ -0,0 +1,37 @@ +import { IconNamesOrString, NamedIcon } from "./NamedIcon"; +import { cn } from "~/utils/cn"; + +export type RenderIcon = IconNamesOrString | React.ComponentType; + +type IconProps = { + icon?: RenderIcon; + className?: string; +}; + +/** Use this icon to either render a passed in React component, or a NamedIcon/CompanyIcon */ +export function Icon(props: IconProps) { + if (typeof props.icon === "string") { + return } />; + } + + const Icon = props.icon; + + if (!Icon) { + return <>; + } + + return ; +} + +export function IconInBox({ boxClassName, ...props }: IconProps & { boxClassName?: string }) { + return ( +
+ +
+ ); +} diff --git a/apps/webapp/app/components/primitives/ShortcutKey.tsx b/apps/webapp/app/components/primitives/ShortcutKey.tsx index ff5d4f5f7..8be993c12 100644 --- a/apps/webapp/app/components/primitives/ShortcutKey.tsx +++ b/apps/webapp/app/components/primitives/ShortcutKey.tsx @@ -23,7 +23,7 @@ export function ShortcutKey({ shortcut, variant, className }: ShortcutKeyProps) const isMac = platform === "mac"; let relevantShortcut = "mac" in shortcut ? (isMac ? shortcut.mac : shortcut.windows) : shortcut; const modifiers = relevantShortcut.modifiers ?? []; - const character = relevantShortcut.key; + const character = keyString(relevantShortcut.key, isMac); return ( @@ -35,6 +35,15 @@ export function ShortcutKey({ shortcut, variant, className }: ShortcutKeyProps) ); } +function keyString(key: String, isMac: boolean) { + switch (key) { + case "enter": + return isMac ? "↡" : key; + default: + return key; + } +} + function modifierString(modifier: Modifier, isMac: boolean) { switch (modifier) { case "alt": @@ -42,8 +51,10 @@ function modifierString(modifier: Modifier, isMac: boolean) { case "ctrl": return isMac ? "βŒƒ" : "Ctrl+"; case "meta": - return isMac ? "⌘" : "⊞"; + return isMac ? "⌘" : "⊞+"; case "shift": return isMac ? "⇧" : "Shift+"; + case "mod": + return isMac ? "⌘" : "Ctrl+"; } } diff --git a/apps/webapp/app/components/primitives/Switch.tsx b/apps/webapp/app/components/primitives/Switch.tsx index fe4d69c46..cf592f8b8 100644 --- a/apps/webapp/app/components/primitives/Switch.tsx +++ b/apps/webapp/app/components/primitives/Switch.tsx @@ -15,7 +15,7 @@ const variations = { container: "flex items-center gap-x-1.5 rounded hover:bg-slate-850 pr-1 py-[0.1rem] pl-1.5", root: "h-3 w-6", thumb: "h-2.5 w-2.5 data-[state=checked]:translate-x-2.5 data-[state=unchecked]:translate-x-0", - text: "text-xs text-slate-400 group-hover:text-slate-200 mt-0.5", + text: "text-xs text-slate-400 group-hover:text-slate-200 hover:cursor-pointer", }, }; diff --git a/apps/webapp/app/components/primitives/Toast.tsx b/apps/webapp/app/components/primitives/Toast.tsx index 133bf6507..8a16cc984 100644 --- a/apps/webapp/app/components/primitives/Toast.tsx +++ b/apps/webapp/app/components/primitives/Toast.tsx @@ -1,7 +1,7 @@ import { ExclamationCircleIcon, XMarkIcon } from "@heroicons/react/20/solid"; import { CheckCircleIcon } from "@heroicons/react/24/solid"; -import { AnimatePresence, motion } from "framer-motion"; -import toast, { Toaster, resolveValue, useToasterStore } from "react-hot-toast"; +import { Toaster, toast } from "sonner"; + import { useTypedLoaderData } from "remix-typedjson"; import { loader } from "~/root"; import { useEffect } from "react"; @@ -11,79 +11,55 @@ const permanentToastDuration = 60 * 60 * 24 * 1000; export function Toast() { const { toastMessage } = useTypedLoaderData(); - useEffect(() => { if (!toastMessage) { return; } const { message, type, options } = toastMessage; - switch (type) { - case "success": - toast.success(message, { - duration: options.ephemeral ? defaultToastDuration : permanentToastDuration, - }); - break; - case "error": - toast.error(message, { - duration: options.ephemeral ? defaultToastDuration : permanentToastDuration, - }); - break; - default: - throw new Error(`${type} is not handled`); - } + toast.custom((t) => , { + duration: options.ephemeral ? defaultToastDuration : permanentToastDuration, + }); }, [toastMessage]); + return ; +} + +export function ToastUI({ + variant, + message, + t, + toastWidth = 356, // Default width, matches what sonner provides by default +}: { + variant: "error" | "success"; + message: string; + t: string; + toastWidth?: string | number; +}) { return ( - , - }, - error: { - icon: , - }, +
- {(t) => ( - - - {t.icon} - {resolveValue(t.message, t)} - - - - )} - +
+ {variant === "success" ? ( + + ) : ( + + )} + {message} + +
+
); } diff --git a/apps/webapp/app/components/run/RunOverview.tsx b/apps/webapp/app/components/run/RunOverview.tsx index 79bdfe22b..f9086d3c6 100644 --- a/apps/webapp/app/components/run/RunOverview.tsx +++ b/apps/webapp/app/components/run/RunOverview.tsx @@ -12,7 +12,7 @@ import { import { JobRunStatus, RuntimeEnvironmentType } from "@trigger.dev/database"; import { useMemo } from "react"; import { usePathName } from "~/hooks/usePathName"; -import { Run } from "~/presenters/RunPresenter.server"; +import { ViewRun } from "~/presenters/RunPresenter.server"; import { cancelSchema } from "~/routes/resources.runs.$runId.cancel"; import { schema } from "~/routes/resources.runs.$runId.rerun"; import { formatDuration } from "~/utils"; @@ -59,7 +59,7 @@ import { TaskCard } from "./TaskCard"; import { TaskCardSkeleton } from "./TaskCardSkeleton"; type RunOverviewProps = { - run: Run; + run: ViewRun; trigger: { icon: string; title: string; diff --git a/apps/webapp/app/components/run/TaskCard.tsx b/apps/webapp/app/components/run/TaskCard.tsx index c1a399d38..1bce2f482 100644 --- a/apps/webapp/app/components/run/TaskCard.tsx +++ b/apps/webapp/app/components/run/TaskCard.tsx @@ -3,7 +3,7 @@ import { AnimatePresence, motion } from "framer-motion"; import { Fragment, useState } from "react"; import simplur from "simplur"; import { Paragraph } from "~/components/primitives/Paragraph"; -import { Task } from "~/presenters/RunPresenter.server"; +import { ViewTask } from "~/presenters/RunPresenter.server"; import { formatDuration } from "~/utils"; import { cn } from "~/utils/cn"; import { @@ -22,7 +22,7 @@ import { } from "./RunCard"; import { TaskStatusIcon } from "./TaskStatus"; -type TaskCardProps = Task & { +type TaskCardProps = ViewTask & { selectedId?: string; selectedTask: (id: string) => void; isLast: boolean; diff --git a/apps/webapp/app/components/run/TriggerDetail.tsx b/apps/webapp/app/components/run/TriggerDetail.tsx index 7f5dd41e8..aee57b22e 100644 --- a/apps/webapp/app/components/run/TriggerDetail.tsx +++ b/apps/webapp/app/components/run/TriggerDetail.tsx @@ -25,7 +25,7 @@ export function TriggerDetail({ }; properties: DisplayProperty[]; }) { - const { id, name, payload, timestamp, deliveredAt } = trigger; + const { id, name, payload, context, timestamp, deliveredAt } = trigger; return ( @@ -45,6 +45,7 @@ export function TriggerDetail({ /> )} + {trigger.externalAccount && ( )} Payload - + + Context +
diff --git a/apps/webapp/app/components/stories/DetailCell.stories.tsx b/apps/webapp/app/components/stories/DetailCell.stories.tsx new file mode 100644 index 000000000..7f70e01a1 --- /dev/null +++ b/apps/webapp/app/components/stories/DetailCell.stories.tsx @@ -0,0 +1,46 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { DetailCell } from "../primitives/DetailCell"; +import { ClockIcon, CodeBracketIcon } from "@heroicons/react/24/outline"; +import { DateTime, DateTimeAccurate } from "../primitives/DateTime"; + +const meta: Meta = { + title: "Primitives/DetailCells", +}; + +export default meta; + +type Story = StoryObj; + +export const Basic: Story = { + render: () => , +}; + +function Examples() { + return ( +
+ + + } + description="Run #42 complete" + trailingIcon="plus" + trailingIconClassName="text-slate-500 group-hover:text-bright" + /> +
+ ); +} diff --git a/apps/webapp/app/components/stories/Shortcuts.stories.tsx b/apps/webapp/app/components/stories/Shortcuts.stories.tsx index a5ed28866..dc2f381a7 100644 --- a/apps/webapp/app/components/stories/Shortcuts.stories.tsx +++ b/apps/webapp/app/components/stories/Shortcuts.stories.tsx @@ -24,6 +24,8 @@ const shortcuts: ShortcutDefinition[] = [ { key: "f", modifiers: ["meta"] }, { key: "k", modifiers: ["meta"] }, { key: "del", modifiers: ["ctrl", "alt"] }, + { key: "enter", modifiers: ["meta"] }, + { key: "enter", modifiers: ["mod"] }, ]; function Collection() { @@ -67,6 +69,9 @@ function Set({ platform }: { platform: "mac" | "windows" }) { +
))} diff --git a/apps/webapp/app/components/stories/ToastUI.stories.tsx b/apps/webapp/app/components/stories/ToastUI.stories.tsx new file mode 100644 index 000000000..c4a7164d8 --- /dev/null +++ b/apps/webapp/app/components/stories/ToastUI.stories.tsx @@ -0,0 +1,47 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Toaster, toast } from "sonner"; +import { ToastUI } from "../primitives/Toast"; +import { Button } from "../primitives/Buttons"; + +const meta: Meta = { + title: "Primitives/Toast", +}; + +export default meta; + +type Story = StoryObj; + +export const Toasts: Story = { + render: () => , +}; + +function Collection() { + return ( +
+ + +
+ + + +
+ ); +} diff --git a/apps/webapp/app/consts.ts b/apps/webapp/app/consts.ts index c6e9c85cd..51ab2cc44 100644 --- a/apps/webapp/app/consts.ts +++ b/apps/webapp/app/consts.ts @@ -5,3 +5,4 @@ export const DEFAULT_MAX_CONCURRENT_RUNS = 10; export const MAX_CONCURRENT_RUNS_LIMIT = 20; export const PREPROCESS_RETRY_LIMIT = 2; export const EXECUTE_JOB_RETRY_LIMIT = 10; +export const MAX_RUN_YIELDED_EXECUTIONS = 100; diff --git a/apps/webapp/app/hooks/useFilterJobs.ts b/apps/webapp/app/hooks/useFilterJobs.ts index 5cad14da3..950e19a43 100644 --- a/apps/webapp/app/hooks/useFilterJobs.ts +++ b/apps/webapp/app/hooks/useFilterJobs.ts @@ -1,9 +1,21 @@ import { ProjectJob } from "./useJobs"; import { useTextFilter } from "./useTextFilter"; +import { useToggleFilter } from "./useToggleFilter"; -export function useFilterJobs(jobs: ProjectJob[]) { - const { filterText, setFilterText, filteredItems } = useTextFilter({ +export function useFilterJobs(jobs: ProjectJob[], onlyActiveJobs = false) { + const toggleFilterRes = useToggleFilter({ items: jobs, + filter: (job, onlyActiveJobs) => { + if (onlyActiveJobs && job.status !== "ACTIVE") { + return false; + } + return true; + }, + defaultValue: onlyActiveJobs, + }); + + const textFilterRes = useTextFilter({ + items: toggleFilterRes.filteredItems, filter: (job, text) => { if (job.slug.toLowerCase().includes(text.toLowerCase())) return true; if (job.title.toLowerCase().includes(text.toLowerCase())) return true; @@ -24,5 +36,11 @@ export function useFilterJobs(jobs: ProjectJob[]) { }, }); - return { filterText, setFilterText, filteredItems }; + return { + filteredItems: textFilterRes.filteredItems, + filterText: textFilterRes.filterText, + setFilterText: textFilterRes.setFilterText, + onlyActiveJobs: toggleFilterRes.isToggleActive, + setOnlyActiveJobs: toggleFilterRes.setToggleActive, + }; } diff --git a/apps/webapp/app/hooks/useShortcutKeys.tsx b/apps/webapp/app/hooks/useShortcutKeys.tsx index ccbebf618..092e16396 100644 --- a/apps/webapp/app/hooks/useShortcutKeys.tsx +++ b/apps/webapp/app/hooks/useShortcutKeys.tsx @@ -1,12 +1,12 @@ -import { useEffect, useState } from "react"; import { useHotkeys } from "react-hotkeys-hook"; import { useOperatingSystem } from "~/components/primitives/OperatingSystemProvider"; -export type Modifier = "alt" | "ctrl" | "meta" | "shift"; +export type Modifier = "alt" | "ctrl" | "meta" | "shift" | "mod"; export type Shortcut = { key: string; modifiers?: Modifier[]; + enabledOnInputElements?: boolean; }; export type ShortcutDefinition = @@ -20,19 +20,31 @@ type useShortcutKeysProps = { shortcut: ShortcutDefinition; action: (event: KeyboardEvent) => void; disabled?: boolean; + enabledOnInputElements?: boolean; }; export function useShortcutKeys({ shortcut, action, disabled = false }: useShortcutKeysProps) { - const keys = createKeysFromShortcut(shortcut); - useHotkeys(keys, action, { enabled: !disabled }); -} - -function createKeysFromShortcut(shortcut: ShortcutDefinition) { const { platform } = useOperatingSystem(); const isMac = platform === "mac"; - let relevantShortcut = "mac" in shortcut ? (isMac ? shortcut.mac : shortcut.windows) : shortcut; - const modifiers = relevantShortcut.modifiers; - const character = relevantShortcut.key; + const relevantShortcut = "mac" in shortcut ? (isMac ? shortcut.mac : shortcut.windows) : shortcut; - return modifiers ? modifiers.map((k) => k).join("+") + "+" : "" + character; + const keys = createKeysFromShortcut(relevantShortcut); + useHotkeys( + keys, + (event, hotkeysEvent) => { + action(event); + }, + { + enabled: !disabled, + enableOnFormTags: relevantShortcut.enabledOnInputElements, + enableOnContentEditable: relevantShortcut.enabledOnInputElements, + } + ); +} + +function createKeysFromShortcut(shortcut: Shortcut) { + const modifiers = shortcut.modifiers; + const character = shortcut.key; + + return modifiers ? modifiers.map((k) => k).join("+") + "+" + character : character; } diff --git a/apps/webapp/app/hooks/useToggleFilter.ts b/apps/webapp/app/hooks/useToggleFilter.ts new file mode 100644 index 000000000..2e099a9f9 --- /dev/null +++ b/apps/webapp/app/hooks/useToggleFilter.ts @@ -0,0 +1,21 @@ +import { useMemo, useState } from "react"; + +type ToggleFilterProps = { + items: T[]; + filter: (item: T, isToggleActive: boolean) => boolean; + defaultValue?: boolean; +}; + +export function useToggleFilter({ items, filter, defaultValue = false }: ToggleFilterProps) { + const [isToggleActive, setToggleActive] = useState(defaultValue); + + const filteredItems = useMemo(() => { + return items.filter((item) => filter(item, isToggleActive)); + }, [items, isToggleActive]); + + return { + isToggleActive, + setToggleActive, + filteredItems, + }; +} diff --git a/apps/webapp/app/models/indexEndpoint.server.ts b/apps/webapp/app/models/indexEndpoint.server.ts deleted file mode 100644 index 6a58660de..000000000 --- a/apps/webapp/app/models/indexEndpoint.server.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { z } from "zod"; - -const IndexEndpointStatsSchema = z.object({ - jobs: z.number(), - sources: z.number(), - dynamicTriggers: z.number(), - dynamicSchedules: z.number(), -}); - -export type IndexEndpointStats = z.infer; - -export function parseEndpointIndexStats(stats: unknown): IndexEndpointStats { - return IndexEndpointStatsSchema.parse(stats); -} diff --git a/apps/webapp/app/models/task.server.ts b/apps/webapp/app/models/task.server.ts index 19951eb6f..b674dd0a4 100644 --- a/apps/webapp/app/models/task.server.ts +++ b/apps/webapp/app/models/task.server.ts @@ -1,5 +1,5 @@ import type { Task, TaskAttempt } from "@trigger.dev/database"; -import { ServerTask } from "@trigger.dev/core"; +import { CachedTask, ServerTask } from "@trigger.dev/core"; export type TaskWithAttempts = Task & { attempts: TaskAttempt[] }; @@ -23,5 +23,90 @@ export function taskWithAttemptsToServerTask(task: TaskWithAttempts): ServerTask attempts: task.attempts.length, idempotencyKey: task.idempotencyKey, operation: task.operation, + callbackUrl: task.callbackUrl, }; } + +export type TaskForCaching = Pick< + Task, + "id" | "status" | "idempotencyKey" | "noop" | "output" | "parentId" +>; + +export function prepareTasksForCaching( + possibleTasks: TaskForCaching[], + maxSize: number +): { + tasks: CachedTask[]; + cursor: string | undefined; +} { + const tasks = possibleTasks.filter((task) => task.status === "COMPLETED" && !task.noop); + + // Select tasks using greedy approach + const tasksToRun: CachedTask[] = []; + let remainingSize = maxSize; + + for (const task of tasks) { + const cachedTask = prepareTaskForCaching(task); + const size = calculateCachedTaskSize(cachedTask); + + if (size <= remainingSize) { + tasksToRun.push(cachedTask); + remainingSize -= size; + } + } + + return { + tasks: tasksToRun, + cursor: tasks.length > tasksToRun.length ? tasks[tasksToRun.length].id : undefined, + }; +} + +export function prepareTasksForCachingLegacy( + possibleTasks: TaskForCaching[], + maxSize: number +): { + tasks: CachedTask[]; + cursor: string | undefined; +} { + const tasks = possibleTasks.filter((task) => task.status === "COMPLETED"); + + // Prepare tasks and calculate their sizes + const availableTasks = tasks.map((task) => { + const cachedTask = prepareTaskForCaching(task); + return { task: cachedTask, size: calculateCachedTaskSize(cachedTask) }; + }); + + // Sort tasks in ascending order by size + availableTasks.sort((a, b) => a.size - b.size); + + // Select tasks using greedy approach + const tasksToRun: CachedTask[] = []; + let remainingSize = maxSize; + + for (const { task, size } of availableTasks) { + if (size <= remainingSize) { + tasksToRun.push(task); + remainingSize -= size; + } + } + + return { + tasks: tasksToRun, + cursor: undefined, + }; +} + +function prepareTaskForCaching(task: TaskForCaching): CachedTask { + return { + id: task.idempotencyKey, // We should eventually move this back to task.id + status: task.status, + idempotencyKey: task.idempotencyKey, + noop: task.noop, + output: task.output as any, + parentId: task.parentId, + }; +} + +function calculateCachedTaskSize(task: CachedTask): number { + return JSON.stringify(task).length; +} diff --git a/apps/webapp/app/platform/zodWorker.server.ts b/apps/webapp/app/platform/zodWorker.server.ts index 3d8a01bbd..a151aefe3 100644 --- a/apps/webapp/app/platform/zodWorker.server.ts +++ b/apps/webapp/app/platform/zodWorker.server.ts @@ -14,7 +14,7 @@ import { run as graphileRun, parseCronItems } from "graphile-worker"; import omit from "lodash.omit"; import { z } from "zod"; import { PrismaClient, PrismaClientOrTransaction } from "~/db.server"; -import { logger } from "~/services/logger.server"; +import { workerLogger as logger } from "~/services/logger.server"; export interface MessageCatalogSchema { [key: string]: z.ZodFirstPartySchemaTypes | z.ZodDiscriminatedUnion; @@ -81,6 +81,18 @@ export type ZodWorkerDequeueOptions = { tx?: PrismaClientOrTransaction; }; +const CLEANUP_TASK_NAME = "__cleanupOldJobs"; +const REPORTER_TASK_NAME = "__reporter"; + +export type ZodWorkerCleanupOptions = { + frequencyExpression: string; // cron expression + ttl: number; + maxCount: number; + taskOptions?: CronItemOptions; +}; + +type ZodWorkerReporter = (event: string, properties: Record) => Promise; + export type ZodWorkerOptions = { name: string; runnerOptions: RunnerOptions; @@ -88,6 +100,8 @@ export type ZodWorkerOptions = { schema: TMessageCatalog; tasks: ZodTasks; recurringTasks?: ZodRecurringTasks; + cleanup?: ZodWorkerCleanupOptions; + reporter?: ZodWorkerReporter; }; export class ZodWorker { @@ -98,6 +112,8 @@ export class ZodWorker { #tasks: ZodTasks; #recurringTasks?: ZodRecurringTasks; #runner?: GraphileRunner; + #cleanup: ZodWorkerCleanupOptions | undefined; + #reporter?: ZodWorkerReporter; constructor(options: ZodWorkerOptions) { this.#name = options.name; @@ -106,6 +122,8 @@ export class ZodWorker { this.#runnerOptions = options.runnerOptions; this.#tasks = options.tasks; this.#recurringTasks = options.recurringTasks; + this.#cleanup = options.cleanup; + this.#reporter = options.reporter; } get graphileWorkerSchema() { @@ -337,12 +355,45 @@ export class ZodWorker { taskList[key] = task; } + if (this.#cleanup) { + const task: Task = (payload, helpers) => { + return this.#handleCleanup(payload, helpers); + }; + + taskList[CLEANUP_TASK_NAME] = task; + } + + if (this.#reporter) { + const task: Task = (payload, helpers) => { + return this.#handleReporter(payload, helpers); + }; + + taskList[REPORTER_TASK_NAME] = task; + } + return taskList; } #createCronItemsFromRecurringTasks() { const cronItems: CronItem[] = []; + if (this.#cleanup) { + cronItems.push({ + pattern: this.#cleanup.frequencyExpression, + identifier: CLEANUP_TASK_NAME, + task: CLEANUP_TASK_NAME, + options: this.#cleanup.taskOptions, + }); + } + + if (this.#reporter) { + cronItems.push({ + pattern: "50 * * * *", // Every hour at 50 minutes past the hour + identifier: REPORTER_TASK_NAME, + task: REPORTER_TASK_NAME, + }); + } + if (!this.#recurringTasks) { return cronItems; } @@ -434,6 +485,112 @@ export class ZodWorker { } } + async #handleCleanup(rawPayload: unknown, helpers: JobHelpers): Promise { + if (!this.#cleanup) { + return; + } + + const job = helpers.job; + + logger.debug("Received cleanup task", { + payload: rawPayload, + job, + }); + + const parsedPayload = RawCronPayloadSchema.safeParse(rawPayload); + + if (!parsedPayload.success) { + throw new Error( + `Failed to parse cleanup task payload: ${JSON.stringify(parsedPayload.error)}` + ); + } + + const payload = parsedPayload.data; + + // Add the this.#cleanup.ttl to the payload._cron.ts + const expirationDate = new Date(payload._cron.ts.getTime() - this.#cleanup.ttl); + + logger.debug("Cleaning up old jobs", { + expirationDate, + payload, + }); + + const rawResults = await this.#prisma.$queryRawUnsafe( + `WITH rows AS (SELECT id FROM ${this.graphileWorkerSchema}.jobs WHERE run_at < $1 AND locked_at IS NULL AND max_attempts = attempts LIMIT $2 FOR UPDATE) DELETE FROM ${this.graphileWorkerSchema}.jobs WHERE id IN (SELECT id FROM rows) RETURNING id`, + expirationDate, + this.#cleanup.maxCount + ); + + const results = Array.isArray(rawResults) ? rawResults : []; + + logger.debug("Cleaned up old jobs", { + count: results.length, + expirationDate, + payload, + }); + + if (this.#reporter) { + await this.#reporter("cleanup_stats", { + count: results.length, + expirationDate, + ts: payload._cron.ts, + }); + } + } + + async #handleReporter(rawPayload: unknown, helpers: JobHelpers): Promise { + if (!this.#reporter) { + return; + } + + logger.debug("Received reporter task", { + payload: rawPayload, + }); + + const parsedPayload = RawCronPayloadSchema.safeParse(rawPayload); + + if (!parsedPayload.success) { + throw new Error( + `Failed to parse cleanup task payload: ${JSON.stringify(parsedPayload.error)}` + ); + } + + const payload = parsedPayload.data; + + // Subtract an hour from the payload._cron.ts + const startAt = new Date(payload._cron.ts.getTime() - 1000 * 60 * 60); + + const schema = z.array(z.object({ count: z.coerce.number() })); + + // Count the number of jobs that have been added since the startAt date and before the payload._cron.ts date + const rawAddedResults = await this.#prisma.$queryRawUnsafe( + `SELECT COUNT(*) FROM ${this.graphileWorkerSchema}.jobs WHERE created_at > $1 AND created_at < $2`, + startAt, + payload._cron.ts + ); + + const addedCountResults = schema.parse(rawAddedResults)[0]; + + // Count the total number of jobs in the jobs table + const rawTotalResults = await this.#prisma.$queryRawUnsafe( + `SELECT COUNT(*) FROM ${this.graphileWorkerSchema}.jobs` + ); + + const totalCountResults = schema.parse(rawTotalResults)[0]; + + logger.debug("Calculated metrics about the jobs table", { + rawAddedResults, + rawTotalResults, + payload, + }); + + await this.#reporter("queue_metrics", { + addedCount: addedCountResults.count, + totalCount: totalCountResults.count, + ts: payload._cron.ts, + }); + } + #logDebug(message: string, args?: any) { logger.debug(`[worker][${this.#name}] ${message}`, args); } diff --git a/apps/webapp/app/presenters/ApiRunPresenter.server.ts b/apps/webapp/app/presenters/ApiRunPresenter.server.ts new file mode 100644 index 000000000..6e8ccd3d2 --- /dev/null +++ b/apps/webapp/app/presenters/ApiRunPresenter.server.ts @@ -0,0 +1,72 @@ +import { Job } from "@trigger.dev/database"; +import { PrismaClient, prisma } from "~/db.server"; + +type ApiRunOptions = { + runId: Job["id"]; + maxTasks?: number; + taskDetails?: boolean; + subTasks?: boolean; + cursor?: string; +}; + +export class ApiRunPresenter { + #prismaClient: PrismaClient; + + constructor(prismaClient: PrismaClient = prisma) { + this.#prismaClient = prismaClient; + } + + public async call({ + runId, + maxTasks = 20, + taskDetails = false, + subTasks = false, + cursor, + }: ApiRunOptions) { + const take = Math.min(maxTasks, 50); + + return await prisma.jobRun.findUnique({ + where: { + id: runId, + }, + select: { + id: true, + status: true, + startedAt: true, + updatedAt: true, + completedAt: true, + environmentId: true, + output: true, + tasks: { + select: { + id: true, + parentId: true, + displayKey: true, + status: true, + name: true, + icon: true, + startedAt: true, + completedAt: true, + params: taskDetails, + output: taskDetails, + }, + where: { + parentId: subTasks ? undefined : null, + }, + orderBy: { + id: "asc", + }, + take: take + 1, + cursor: cursor + ? { + id: cursor, + } + : undefined, + }, + statuses: { + select: { key: true, label: true, state: true, data: true, history: true }, + }, + }, + }); + } +} diff --git a/apps/webapp/app/presenters/EnvironmentsPresenter.server.ts b/apps/webapp/app/presenters/EnvironmentsPresenter.server.ts index 477691f62..bd5f4ef87 100644 --- a/apps/webapp/app/presenters/EnvironmentsPresenter.server.ts +++ b/apps/webapp/app/presenters/EnvironmentsPresenter.server.ts @@ -1,13 +1,19 @@ import { PrismaClient, prisma } from "~/db.server"; -import { IndexEndpointStats, parseEndpointIndexStats } from "~/models/indexEndpoint.server"; import { Project } from "~/models/project.server"; import { User } from "~/models/user.server"; import type { Endpoint, EndpointIndex, + EndpointIndexStatus, RuntimeEnvironment, RuntimeEnvironmentType, } from "@trigger.dev/database"; +import { + EndpointIndexError, + EndpointIndexErrorSchema, + IndexEndpointStats, + parseEndpointIndexStats, +} from "@trigger.dev/core"; export type Client = { slug: string; @@ -34,9 +40,11 @@ export type ClientEndpoint = url: string; indexWebhookPath: string; latestIndex?: { + status: EndpointIndexStatus; source: string; updatedAt: Date; - stats: IndexEndpointStats; + stats?: IndexEndpointStats; + error?: EndpointIndexError; }; environment: { id: string; @@ -81,9 +89,11 @@ export class EnvironmentsPresenter { indexingHookIdentifier: true, indexings: { select: { + status: true, source: true, updatedAt: true, stats: true, + error: true, }, take: 1, orderBy: { @@ -214,7 +224,7 @@ const environmentSortOrder: RuntimeEnvironmentType[] = [ function endpointClient( endpoint: Pick & { - indexings: Pick[]; + indexings: Pick[]; }, environment: Pick, baseUrl: string @@ -227,9 +237,13 @@ function endpointClient( indexWebhookPath: `${baseUrl}/api/v1/endpoints/${environment.id}/${endpoint.slug}/index/${endpoint.indexingHookIdentifier}`, latestIndex: endpoint.indexings[0] ? { + status: endpoint.indexings[0].status, source: endpoint.indexings[0].source, updatedAt: endpoint.indexings[0].updatedAt, stats: parseEndpointIndexStats(endpoint.indexings[0].stats), + error: endpoint.indexings[0].error + ? EndpointIndexErrorSchema.parse(endpoint.indexings[0].error) + : undefined, } : undefined, environment: environment, diff --git a/apps/webapp/app/presenters/OrgUsagePresenter.server.ts b/apps/webapp/app/presenters/OrgUsagePresenter.server.ts index 7c08a2468..c43b7fcf8 100644 --- a/apps/webapp/app/presenters/OrgUsagePresenter.server.ts +++ b/apps/webapp/app/presenters/OrgUsagePresenter.server.ts @@ -1,4 +1,5 @@ import { PrismaClient, prisma } from "~/db.server"; +import { logger } from "~/services/logger.server"; export class OrgUsagePresenter { #prismaClient: PrismaClient; @@ -33,6 +34,7 @@ export class OrgUsagePresenter { createdAt: { gte: new Date(new Date().getFullYear(), new Date().getMonth(), 1), }, + internal: false, }, }); @@ -44,6 +46,7 @@ export class OrgUsagePresenter { gte: startOfLastMonth, lt: startOfMonth, }, + internal: false, }, }); @@ -63,7 +66,7 @@ export class OrgUsagePresenter { month: string; count: number; }[] - >`SELECT TO_CHAR("createdAt", 'YYYY-MM') as month, COUNT(*) as count FROM "JobRun" WHERE "organizationId" = ${organization.id} AND "createdAt" >= NOW() - INTERVAL '6 months' GROUP BY month ORDER BY month ASC`; + >`SELECT TO_CHAR("createdAt", 'YYYY-MM') as month, COUNT(*) as count FROM "JobRun" WHERE "organizationId" = ${organization.id} AND "createdAt" >= NOW() - INTERVAL '6 months' AND "internal" = FALSE GROUP BY month ORDER BY month ASC`; const chartData = chartDataRaw.map((obj) => ({ name: obj.month, @@ -139,11 +142,13 @@ export class OrgUsagePresenter { }, }); + const chartDataDisplay = fillInMissingMonthlyData(chartData, 6); + return { id: organization.id, runsCount, runsCountLastMonth, - chartData: fillInMissingMonthlyData(chartData, 6), + chartData: chartDataDisplay, totalJobs, totalJobsLastMonth, totalIntegrations, @@ -166,7 +171,7 @@ function fillInMissingMonthlyData( const startMonth = new Date( new Date(currentMonth).getFullYear(), - new Date(currentMonth).getMonth() - totalNumberOfMonths, + new Date(currentMonth).getMonth() - (totalNumberOfMonths - 2), 1 ) .toISOString() @@ -182,17 +187,36 @@ function fillInMissingMonthlyData( return completeData; } +// Start month will be like 2023-03 and endMonth will be like 2023-10 +// The result should be an array of months between these two months, including the start and end month +// So for example, if startMonth is 2023-03 and endMonth is 2023-10, the result should be: +// ["2023-03", "2023-04", "2023-05", "2023-06", "2023-07", "2023-08", "2023-09", "2023-10"] function getMonthsBetween(startMonth: string, endMonth: string): string[] { - const startDate = new Date(startMonth); - const endDate = new Date(endMonth); + // Initialize result array + const result: string[] = []; - const months = []; - let currentDate = startDate; + // Parse the year and month from startMonth and endMonth + let [startYear, startMonthNum] = startMonth.split("-").map(Number); + let [endYear, endMonthNum] = endMonth.split("-").map(Number); - while (currentDate <= endDate) { - months.push(currentDate.toISOString().slice(0, 7)); - currentDate = new Date(currentDate.setMonth(currentDate.getMonth() + 1)); + // Loop through each month between startMonth and endMonth + for (let year = startYear; year <= endYear; year++) { + let monthStart = year === startYear ? startMonthNum : 1; + let monthEnd = year === endYear ? endMonthNum : 12; + + for (let month = monthStart; month <= monthEnd; month++) { + // Format the month into a string and add it to the result array + result.push(`${year}-${String(month).padStart(2, "0")}`); + } } - return months; + return result; +} + +function getLastSecondOfMonth(endMonth: string) { + const [year, month] = endMonth.split("-").map(Number); + const nextMonthFirstDay = new Date(year, month, 1); + nextMonthFirstDay.setDate(0); + nextMonthFirstDay.setHours(23, 59, 59); + return nextMonthFirstDay; } diff --git a/apps/webapp/app/presenters/RunPresenter.server.ts b/apps/webapp/app/presenters/RunPresenter.server.ts index 369ce882e..34934647d 100644 --- a/apps/webapp/app/presenters/RunPresenter.server.ts +++ b/apps/webapp/app/presenters/RunPresenter.server.ts @@ -13,10 +13,11 @@ type RunOptions = { userId: string; }; -export type Run = NonNullable>>; -export type Task = NonNullable>>["tasks"][number]; -export type Event = NonNullable>>["event"]; +export type ViewRun = NonNullable>>; +export type ViewTask = NonNullable>>["tasks"][number]; +export type ViewEvent = NonNullable>>["event"]; +type QueryEvent = NonNullable>>["event"]; type QueryTask = NonNullable>>["tasks"][number]; export class RunPresenter { @@ -76,7 +77,7 @@ export class RunPresenter { type: run.environment.type, slug: run.environment.slug, }, - event: run.event, + event: this.#prepareEventData(run.event), tasks, runConnections: run.runConnections, missingConnections: run.missingConnections, @@ -84,6 +85,22 @@ export class RunPresenter { }; } + #prepareEventData(event: QueryEvent) { + return { + id: event.eventId, + name: event.name, + payload: JSON.stringify(event.payload), + context: JSON.stringify(event.context), + timestamp: event.timestamp, + deliveredAt: event.deliveredAt, + externalAccount: event.externalAccount + ? { + identifier: event.externalAccount.identifier, + } + : undefined, + }; + } + query({ id, userId }: RunOptions) { return this.#prismaClient.jobRun.findFirst({ select: { @@ -110,9 +127,10 @@ export class RunPresenter { }, event: { select: { - id: true, + eventId: true, name: true, payload: true, + context: true, timestamp: true, deliveredAt: true, externalAccount: { diff --git a/apps/webapp/app/presenters/TestJobPresenter.server.ts b/apps/webapp/app/presenters/TestJobPresenter.server.ts index 3028199e2..c87b035c8 100644 --- a/apps/webapp/app/presenters/TestJobPresenter.server.ts +++ b/apps/webapp/app/presenters/TestJobPresenter.server.ts @@ -4,6 +4,7 @@ import { PrismaClient, prisma } from "~/db.server"; import { Job } from "~/models/job.server"; import { Organization } from "~/models/organization.server"; import { Project } from "~/models/project.server"; +import { EventExample } from "@trigger.dev/core"; export class TestJobPresenter { #prismaClient: PrismaClient; @@ -67,14 +68,22 @@ export class TestJobPresenter { name: "latest", }, }, - _count: { + runs: { select: { - runs: { - where: { - isTest: true, + id: true, + createdAt: true, + number: true, + status: true, + event: { + select: { + payload: true, }, }, }, + orderBy: { + createdAt: "desc", + }, + take: 5, }, }, where: { @@ -97,6 +106,15 @@ export class TestJobPresenter { throw new Error("Job not found"); } + //collect together the examples, we don't care about the environments + const examples = job.aliases.flatMap((alias) => + alias.version.examples.map((example) => ({ + ...example, + icon: example.icon ?? undefined, + payload: example.payload ? JSON.stringify(example.payload, exampleReplacer, 2) : undefined, + })) + ); + return { environments: job.aliases.map((alias) => ({ id: alias.environment.id, @@ -104,15 +122,18 @@ export class TestJobPresenter { slug: alias.environment.slug, userId: alias.environment.orgMember?.userId, versionId: alias.version.id, - examples: alias.version.examples.map((example) => ({ - ...example, - payload: JSON.stringify(example.payload, exampleReplacer, 2), - })), hasAuthResolver: alias.version.integrations.some( (i) => i.integration.authSource === "RESOLVER" ), })), - hasTestRuns: job._count.runs > 0, + examples, + runs: job.runs.map((r) => ({ + id: r.id, + number: r.number, + status: r.status, + created: r.createdAt, + payload: r.event.payload ? JSON.stringify(r.event.payload, null, 2) : undefined, + })), }; } } diff --git a/apps/webapp/app/presenters/TriggerDetailsPresenter.server.ts b/apps/webapp/app/presenters/TriggerDetailsPresenter.server.ts index d4b6fa6e3..7a93e77ac 100644 --- a/apps/webapp/app/presenters/TriggerDetailsPresenter.server.ts +++ b/apps/webapp/app/presenters/TriggerDetailsPresenter.server.ts @@ -17,9 +17,10 @@ export class TriggerDetailsPresenter { select: { event: { select: { - id: true, + eventId: true, name: true, payload: true, + context: true, timestamp: true, deliveredAt: true, externalAccount: { @@ -32,6 +33,18 @@ export class TriggerDetailsPresenter { }, }); - return event; + return { + id: event.eventId, + name: event.name, + payload: JSON.stringify(event.payload, null, 2), + context: JSON.stringify(event.context, null, 2), + timestamp: event.timestamp, + deliveredAt: event.deliveredAt, + externalAccount: event.externalAccount + ? { + identifier: event.externalAccount.identifier, + } + : undefined, + }; } } diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam._index/route.tsx index 0144db63b..88ce334c3 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam._index/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam._index/route.tsx @@ -19,6 +19,7 @@ import { PageTitleRow, } from "~/components/primitives/PageHeader"; import { Paragraph } from "~/components/primitives/Paragraph"; +import { Switch } from "~/components/primitives/Switch"; import { TextLink } from "~/components/primitives/TextLink"; import { useFilterJobs } from "~/hooks/useFilterJobs"; import { useOrganization } from "~/hooks/useOrganizations"; @@ -62,8 +63,11 @@ export default function Page() { const organization = useOrganization(); const project = useProject(); const { jobs } = useTypedLoaderData(); - const { filterText, setFilterText, filteredItems } = useFilterJobs(jobs); - const hasJobs = jobs.length > 0; + const { filterText, setFilterText, filteredItems, onlyActiveJobs, setOnlyActiveJobs } = + useFilterJobs(jobs); + const totalJobs = jobs.length; + const hasJobs = totalJobs > 0; + const activeJobCount = jobs.filter((j) => j.status === "ACTIVE").length; return ( @@ -74,7 +78,8 @@ export default function Page() { - + + @@ -96,7 +101,7 @@ export default function Page() { )}
-
+
setFilterText(e.target.value)} autoFocus /> +
diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.environments/ConfigureEndpointSheet.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.environments/ConfigureEndpointSheet.tsx index 5db9273da..171761967 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.environments/ConfigureEndpointSheet.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.environments/ConfigureEndpointSheet.tsx @@ -6,7 +6,7 @@ import { useEventSource } from "remix-utils"; import { InlineCode } from "~/components/code/InlineCode"; import { EnvironmentLabel } from "~/components/environments/EnvironmentLabel"; import { Button } from "~/components/primitives/Buttons"; -import { Callout } from "~/components/primitives/Callout"; +import { Callout, CalloutVariant } from "~/components/primitives/Callout"; import { ClipboardField } from "~/components/primitives/ClipboardField"; import { DateTime } from "~/components/primitives/DateTime"; import { FormError } from "~/components/primitives/FormError"; @@ -18,8 +18,14 @@ import { Paragraph } from "~/components/primitives/Paragraph"; import { Sheet, SheetBody, SheetContent, SheetHeader } from "~/components/primitives/Sheet"; import { ClientEndpoint } from "~/presenters/EnvironmentsPresenter.server"; import { endpointStreamingPath } from "~/utils/pathBuilder"; -import { RuntimeEnvironmentType } from "../../../../../packages/database/src"; +import { EndpointIndexStatus, RuntimeEnvironmentType } from "../../../../../packages/database/src"; import { bodySchema } from "../resources.environments.$environmentParam.endpoint"; +import { + EndpointIndexStatusIcon, + EndpointIndexStatusLabel, + endpointIndexStatusTitle, +} from "~/components/environments/EndpointIndexStatus"; +import { CodeBlock } from "~/components/code/CodeBlock"; type ConfigureEndpointSheetProps = { slug: string; @@ -119,15 +125,29 @@ export function ConfigureEndpointSheet({ slug, endpoint, onClose }: ConfigureEnd method="post" action={`/resources/environments/${endpoint.environment.id}/endpoint/${endpoint.id}`} > - - - Endpoint configured. Last refreshed:{" "} - {endpoint.latestIndex ? ( - - ) : ( - "–" - )} - + + } + className="justiy-between items-center" + > +
+ + + Last refreshed:{" "} + {endpoint.latestIndex ? ( + <> + + + ) : ( + "–" + )} + +
+
@@ -155,3 +180,16 @@ export function ConfigureEndpointSheet({ slug, endpoint, onClose }: ConfigureEnd ); } + +function calloutVariantFromStatus(status: EndpointIndexStatus): CalloutVariant { + switch (status) { + case "PENDING": + return "pending"; + case "STARTED": + return "pending"; + case "SUCCESS": + return "success"; + case "FAILURE": + return "error"; + } +} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.environments/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.environments/route.tsx index a335b1411..521fb993a 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.environments/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.environments/route.tsx @@ -3,11 +3,16 @@ import { LoaderArgs } from "@remix-run/server-runtime"; import { useEffect, useMemo, useState } from "react"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { useEventSource } from "remix-utils"; +import { + EndpointIndexStatusIcon, + EndpointIndexStatusLabel, +} from "~/components/environments/EndpointIndexStatus"; import { EnvironmentLabel, environmentTitle } from "~/components/environments/EnvironmentLabel"; import { HowToUseApiKeysAndEndpoints } from "~/components/helpContent/HelpContentText"; import { PageBody, PageContainer } from "~/components/layout/AppLayout"; import { BreadcrumbLink } from "~/components/navigation/NavBar"; -import { Button, ButtonContent } from "~/components/primitives/Buttons"; +import { Badge } from "~/components/primitives/Badge"; +import { ButtonContent } from "~/components/primitives/Buttons"; import { ClipboardField } from "~/components/primitives/ClipboardField"; import { DateTime } from "~/components/primitives/DateTime"; import { Header2, Header3 } from "~/components/primitives/Headers"; @@ -38,7 +43,6 @@ import { ProjectParamSchema, projectEnvironmentsStreamingPath } from "~/utils/pa import { requestUrl } from "~/utils/requestUrl.server"; import { RuntimeEnvironmentType } from "../../../../../packages/database/src"; import { ConfigureEndpointSheet } from "./ConfigureEndpointSheet"; -import { Badge } from "~/components/primitives/Badge"; import { FirstEndpointSheet } from "./FirstEndpointSheet"; export const loader = async ({ request, params }: LoaderArgs) => { @@ -180,6 +184,7 @@ export default function Page() { Environment Url Last refreshed + Last refresh Status Jobs Go to page @@ -268,7 +273,7 @@ function EndpointRow({
- +
The {environmentTitle({ type })} environment is not configured @@ -290,7 +295,17 @@ function EndpointRow({ {endpoint.latestIndex ? : "–"} - {endpoint.latestIndex?.stats.jobs ?? "–"} + + {endpoint.latestIndex ? ( +
+ + +
+ ) : ( + "–" + )} +
+ {endpoint.latestIndex?.stats?.jobs ?? "–"} ); diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.integrations/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.integrations/route.tsx index 1c653370a..f450aef11 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.integrations/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.integrations/route.tsx @@ -13,6 +13,7 @@ import { BreadcrumbLink } from "~/components/navigation/NavBar"; import { LinkButton } from "~/components/primitives/Buttons"; import { Callout } from "~/components/primitives/Callout"; import { DateTime } from "~/components/primitives/DateTime"; +import { DetailCell } from "~/components/primitives/DetailCell"; import { Header2 } from "~/components/primitives/Headers"; import { Help, HelpContent, HelpTrigger } from "~/components/primitives/Help"; import { Input } from "~/components/primitives/Input"; @@ -151,7 +152,7 @@ function PossibleIntegrationsList({ onCheckedChange={setOnlyShowIntegrations} variant="small" label={ - + Trigger.dev Integrations } @@ -209,10 +210,12 @@ function PossibleIntegrationsList({ - } @@ -221,10 +224,12 @@ function PossibleIntegrationsList({ Create an Integration -
@@ -482,77 +487,16 @@ function AddIntegrationConnection({ icon?: string; }) { return ( -
- - - {name} - -
- {isIntegration && } - -
-
- ); -} - -function ExternalIntegrationLink({ - name, - label, - trailingIcon, -}: { - name: string; - label: string; - trailingIcon: string; -}) { - return ( - - - - {label} - -
- -
-
+ ); } export function IntegrationIcon() { return ; } - -function InfoLink({ text }: { text: string }) { - return ( -
- - - {text} - -
- -
-
- ); -} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.integrations_.$clientParam._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.integrations_.$clientParam._index/route.tsx index 2193f90a8..35590ffa6 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.integrations_.$clientParam._index/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.integrations_.$clientParam._index/route.tsx @@ -8,6 +8,7 @@ import { Callout } from "~/components/primitives/Callout"; import { Header2 } from "~/components/primitives/Headers"; import { Help, HelpContent, HelpTrigger } from "~/components/primitives/Help"; import { Input } from "~/components/primitives/Input"; +import { Paragraph } from "~/components/primitives/Paragraph"; import { useFilterJobs } from "~/hooks/useFilterJobs"; import { useIntegrationClient } from "~/hooks/useIntegrationClient"; import { JobListPresenter } from "~/presenters/JobListPresenter.server"; @@ -52,10 +53,8 @@ export default function Page() { {(open) => (
-
- {jobs.length === 0 ? ( - Jobs using this integration will appear here - ) : ( +
+ {jobs.length !== 0 && (
{jobs.length === 0 ? ( - <> - - +
+ Jobs using this Integration will appear here. +
) : ( { @@ -39,14 +43,14 @@ export const loader = async ({ request, params }: LoaderArgs) => { const { organizationSlug, projectParam, jobParam } = JobParamsSchema.parse(params); const presenter = new TestJobPresenter(); - const { environments, hasTestRuns } = await presenter.call({ + const { environments, runs, examples } = await presenter.call({ userId, organizationSlug, projectSlug: projectParam, jobSlug: jobParam, }); - return typedjson({ environments, hasTestRuns }); + return typedjson({ environments, runs, examples }); }; const schema = z.object({ @@ -116,22 +120,30 @@ export const handle: Handle = { const startingJson = "{\n\n}"; export default function Page() { + const { environments, runs, examples } = useTypedLoaderData(); + + //form submission const submit = useSubmit(); const lastSubmission = useActionData(); - const [isExamplePopoverOpen, setIsExamplePopoverOpen] = useState(false); - const { environments, hasTestRuns } = useTypedLoaderData(); - const [defaultJson, setDefaultJson] = useState(startingJson); - const currentJson = useRef(defaultJson); + //examples + const [selectedCodeSampleId, setSelectedCodeSampleId] = useState( + examples.at(0)?.id ?? runs.at(0)?.id + ); + const selectedCodeSample = + examples.find((e) => e.id === selectedCodeSampleId)?.payload ?? + runs.find((r) => r.id === selectedCodeSampleId)?.payload; + + const [defaultJson, setDefaultJson] = useState(selectedCodeSample ?? startingJson); + const setCode = useCallback((code: string) => { + setDefaultJson(code); + }, []); + const [selectedEnvironmentId, setSelectedEnvironmentId] = useState(environments[0].id); - const [currentAccountId, setCurrentAccountId] = useState(undefined); - const selectedEnvironment = environments.find((e) => e.id === selectedEnvironmentId); - const insertCode = useCallback((code: string) => { - setDefaultJson(code); - setIsExamplePopoverOpen(false); - }, []); + const currentJson = useRef(defaultJson); + const [currentAccountId, setCurrentAccountId] = useState(undefined); const submitForm = useCallback( (e: React.FormEvent) => { @@ -170,120 +182,178 @@ export default function Page() { } return ( - - {(open) => ( -
-
-
submitForm(e)} - > -
-
- - - +
+
+ submitForm(e)} + > +
+
+ { + currentJson.current = v; - {selectedEnvironment && selectedEnvironment.examples.length > 0 && ( - setIsExamplePopoverOpen(open)} + //deselect the example if it's been edited + if (selectedCodeSampleId) { + if (v !== selectedCodeSample) { + setDefaultJson(v); + setSelectedCodeSampleId(undefined); + } + } + }} + height="100%" + min-height="100%" + max-height="100%" + autoFocus + placeholder="Use your schema to enter valid JSON or add one of the example payloads then click 'Run test'" + className="h-full" + /> +
+
+ {examples.length > 0 && ( +
+ Example payloads + {examples.map((example) => ( + - ))} - - - )} + + + ))}
- + )} +
+ Recent payloads + {runs.length === 0 ? ( + + Recent payloads will show here once you've completed a Run. + + ) : ( +
+ {runs.map((run) => ( + + ))} +
+ )}
- - -
- (currentJson.current = v)} - minHeight="150px" - /> -
-
{selectedEnvironment?.hasAuthResolver && ( - - - setCurrentAccountId(e.target.value)} - /> - {accountId.error} - +
+ Account ID + + setCurrentAccountId(e.target.value)} + /> + {accountId.error} + + Learn about testing Jobs with an Account ID in our{" "} + + BYOAuth docs + + + +
)} -
- {payload.error ? ( - {payload.error} - ) : ( -
- )} - -
- +
- - - -
- )} - +
+ + Learn more about running tests + +
+ {payload.error ? ( + {payload.error} + ) : ( +
+ )} + + + + +
+
+ +
+
); } diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.nestjs/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.nestjs/route.tsx index 255f31340..d13b547a0 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.nestjs/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.nestjs/route.tsx @@ -1,21 +1,220 @@ -import { NestjsLogo } from "~/assets/logos/NestjsLogo"; -import { FrameworkComingSoon } from "~/components/frameworks/FrameworkComingSoon"; +import { ChatBubbleLeftRightIcon, Squares2X2Icon } from "@heroicons/react/20/solid"; +import invariant from "tiny-invariant"; +import { Feedback } from "~/components/Feedback"; +import { PageGradient } from "~/components/PageGradient"; +import { StepContentContainer } from "~/components/StepContentContainer"; +import { InlineCode } from "~/components/code/InlineCode"; +import { InstallPackages } from "~/components/code/InstallPackages"; import { BreadcrumbLink } from "~/components/navigation/NavBar"; +import { Button, LinkButton } from "~/components/primitives/Buttons"; +import { Header1 } from "~/components/primitives/Headers"; +import { Paragraph } from "~/components/primitives/Paragraph"; +import { StepNumber } from "~/components/primitives/StepNumber"; +import { useAppOrigin } from "~/hooks/useAppOrigin"; +import { useDevEnvironment } from "~/hooks/useEnvironments"; +import { useOrganization } from "~/hooks/useOrganizations"; +import { useProject } from "~/hooks/useProject"; +import { useProjectSetupComplete } from "~/hooks/useProjectSetupComplete"; import { Handle } from "~/utils/handle"; -import { trimTrailingSlash } from "~/utils/pathBuilder"; +import { projectSetupPath, trimTrailingSlash } from "~/utils/pathBuilder"; +import { CodeBlock } from "../../components/code/CodeBlock"; +import { TriggerDevStep } from "~/components/SetupCommands"; export const handle: Handle = { - breadcrumb: (match) => , + breadcrumb: (match) => , }; -export default function Page() { +const AppModuleCode = ` +import { Module } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { TriggerDevModule } from '@trigger.dev/nestjs'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + }), + TriggerDevModule.registerAsync({ + inject: [ConfigService], + useFactory: (config: ConfigService) => ({ + id: 'my-nest-app', + apiKey: config.getOrThrow('TRIGGER_API_KEY'), + apiUrl: config.getOrThrow('TRIGGER_API_URL'), + verbose: false, + ioLogLocalEnabled: true, + }), + }), + ], +}) +export class AppModule {} +`; + +const JobControllerCode = ` +import { Controller, Get } from '@nestjs/common'; +import { InjectTriggerDevClient } from '@trigger.dev/nestjs'; +import { eventTrigger, TriggerClient } from '@trigger.dev/sdk'; + +@Controller() +export class JobController { + constructor( + @InjectTriggerDevClient() private readonly client: TriggerClient, + ) { + this.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!', + }; + }, + }); + } + + @Get() + getHello(): string { + return \`Running Trigger.dev with client-id \${this.client.id}\`; + } +}`; + +const AppModuleWithControllerCode = ` +import { Module } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { TriggerDevModule } from '@trigger.dev/nestjs'; +import { JobController } from './job.controller'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + }), + TriggerDevModule.registerAsync({ + inject: [ConfigService], + useFactory: (config: ConfigService) => ({ + id: 'my-nest-app', + apiKey: config.getOrThrow('TRIGGER_API_KEY'), + apiUrl: config.getOrThrow('TRIGGER_API_URL'), + verbose: false, + ioLogLocalEnabled: true, + }), + }), + ], + controllers: [ + //...existingControllers, + JobController + ], +}) +export class AppModule {} +`; + +const packageJsonCode = `"trigger.dev": { + "endpointId": "my-nest-app" +}`; + +export default function SetupNestJS() { + const organization = useOrganization(); + const project = useProject(); + useProjectSetupComplete(); + const devEnvironment = useDevEnvironment(); + const appOrigin = useAppOrigin(); + + invariant(devEnvironment, "devEnvironment is required"); + return ( - - - + +
+
+ + Get setup in 2 minutes + +
+ + Choose a different framework + + + I'm stuck! + + } + defaultValue="help" + /> +
+
+ <> + + + + + + + + Inside your .env file, create the following env variables: + + + + + + + Now, go to your app.module.ts and add the{" "} + TriggerDevModule: + + + + + + + Create a controller called{" "} + job.controller.ts and add the following code: + + + + + + + Now, add the new controller to your{" "} + app.module.ts: + + + + + + + Now, add this to the top-level of your package.json: + + + + + + + Finally, run your project with npm run start: + + + + + + + + + This page will automatically refresh. + + +
+
); } diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.sveltekit/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.sveltekit/route.tsx index 8083db44b..eb47e5fdf 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.sveltekit/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.setup.sveltekit/route.tsx @@ -1,23 +1,113 @@ -import { SvelteKitLogo } from "~/assets/logos/SveltekitLogo"; -import { FrameworkComingSoon } from "~/components/frameworks/FrameworkComingSoon"; +import { ChatBubbleLeftRightIcon, Squares2X2Icon } from "@heroicons/react/20/solid"; +import invariant from "tiny-invariant"; +import { Feedback } from "~/components/Feedback"; +import { PageGradient } from "~/components/PageGradient"; +import { RunDevCommand, TriggerDevStep } from "~/components/SetupCommands"; +import { StepContentContainer } from "~/components/StepContentContainer"; +import { InlineCode } from "~/components/code/InlineCode"; import { BreadcrumbLink } from "~/components/navigation/NavBar"; +import { Button, LinkButton } from "~/components/primitives/Buttons"; +import { ClipboardField } from "~/components/primitives/ClipboardField"; +import { Header1 } from "~/components/primitives/Headers"; +import { NamedIcon } from "~/components/primitives/NamedIcon"; +import { Paragraph } from "~/components/primitives/Paragraph"; +import { StepNumber } from "~/components/primitives/StepNumber"; +import { useAppOrigin } from "~/hooks/useAppOrigin"; +import { useProjectSetupComplete } from "~/hooks/useProjectSetupComplete"; +import { useDevEnvironment } from "~/hooks/useEnvironments"; +import { useOrganization } from "~/hooks/useOrganizations"; +import { useProject } from "~/hooks/useProject"; import { Handle } from "~/utils/handle"; -import { trimTrailingSlash } from "~/utils/pathBuilder"; - +import { projectSetupPath, trimTrailingSlash } from "~/utils/pathBuilder"; +import { Callout } from "~/components/primitives/Callout"; +import { Badge } from "~/components/primitives/Badge"; export const handle: Handle = { breadcrumb: (match) => ( ), }; -export default function Page() { +export default function SetUpSveltekit() { + const organization = useOrganization(); + const project = useProject(); + useProjectSetupComplete(); + const devEnvironment = useDevEnvironment(); + invariant(devEnvironment, "Dev environment must be defined"); return ( - - - + +
+
+ + Get setup in 5 minutes + +
+ + Choose a different framework + + + I'm stuck! + + } + defaultValue="help" + /> +
+
+
+ + Trigger.dev has full support for serverless. We will be adding support for long-running + servers soon. + +
+ + + Copy your server API Key to your clipboard: +
+ Server} + /> +
+ Now follow this guide: + + Manual installation guide + +
+
+ + + + + + + + + + + This page will automatically refresh. + +
+
+
+
); } diff --git a/apps/webapp/app/routes/api.v1.endpointindex.$indexId.ts b/apps/webapp/app/routes/api.v1.endpointindex.$indexId.ts new file mode 100644 index 000000000..fcf271b21 --- /dev/null +++ b/apps/webapp/app/routes/api.v1.endpointindex.$indexId.ts @@ -0,0 +1,68 @@ +import { ActionArgs, json } from "@remix-run/server-runtime"; +import { + EndpointIndexErrorSchema, + GetEndpointIndexResponse, + GetEndpointIndexResponseSchema, +} from "@trigger.dev/core"; +import { z } from "zod"; +import { prisma } from "~/db.server"; +import { authenticateApiRequest } from "~/services/apiAuth.server"; +import { IndexEndpointService } from "~/services/endpoints/indexEndpoint.server"; +import { logger } from "~/services/logger.server"; + +const ParamsSchema = z.object({ + indexId: z.string(), +}); + +export async function loader({ request, params }: ActionArgs) { + if (request.method.toUpperCase() !== "GET") { + return { status: 405, body: "Method Not Allowed" }; + } + + const parsedParams = ParamsSchema.safeParse(params); + + if (!parsedParams.success) { + return json({ error: "Invalid params" }, { status: 400 }); + } + + // Next authenticate the request + const authenticationResult = await authenticateApiRequest(request); + if (!authenticationResult) { + logger.info("Invalid or missing api key", { url: request.url }); + return json({ error: "Invalid or Missing API key" }, { status: 401 }); + } + + const authenticatedEnv = authenticationResult.environment; + + const { indexId } = parsedParams.data; + + const endpointIndex = await prisma.endpointIndex.findUnique({ + where: { + id: indexId, + endpoint: { + environmentId: authenticatedEnv.id, + }, + }, + }); + + if (!endpointIndex) { + logger.info("EndpointIndex not found", { url: request.url }); + return json({ error: "EndpointIndex not found" }, { status: 404 }); + } + + const parsed = GetEndpointIndexResponseSchema.safeParse(endpointIndex); + + if (!parsed.success) { + logger.info("EndpointIndex failed parsing", { errors: parsed.error.issues, endpointIndex }); + const parseFailResult: GetEndpointIndexResponse = { + status: "FAILURE", + error: { + message: "Invalid endpoint index", + }, + updatedAt: new Date(), + }; + return json(parseFailResult, { status: 500 }); + } + + return json(parsed.data); +} diff --git a/apps/webapp/app/routes/api.v1.endpoints.$environmentId.$endpointSlug.index.$indexHookIdentifier.ts b/apps/webapp/app/routes/api.v1.endpoints.$environmentId.$endpointSlug.index.$indexHookIdentifier.ts index 5429b9b21..0ccd1ed99 100644 --- a/apps/webapp/app/routes/api.v1.endpoints.$environmentId.$endpointSlug.index.$indexHookIdentifier.ts +++ b/apps/webapp/app/routes/api.v1.endpoints.$environmentId.$endpointSlug.index.$indexHookIdentifier.ts @@ -1,7 +1,7 @@ import { ActionArgs, LoaderArgs, json } from "@remix-run/server-runtime"; import { RuntimeEnvironmentType } from "@trigger.dev/database"; import { z } from "zod"; -import { PrismaClient, prisma } from "~/db.server"; +import { $transaction, PrismaClient, prisma } from "~/db.server"; import { logger } from "~/services/logger.server"; import { workerQueue } from "~/services/worker.server"; import { safeJsonParse } from "~/utils/json"; @@ -93,43 +93,53 @@ export class TriggerEndpointIndexHookService { body, }); - const endpoint = await this.#prismaClient.endpoint.findUnique({ - where: { - environmentId_slug: { - environmentId, - slug: endpointSlug, + await $transaction(this.#prismaClient, async (tx) => { + const endpoint = await tx.endpoint.findUnique({ + where: { + environmentId_slug: { + environmentId, + slug: endpointSlug, + }, }, - }, - include: { - environment: true, - }, - }); + include: { + environment: true, + }, + }); - if (!endpoint) { - throw new Error("Endpoint not found"); - } - - if (endpoint.indexingHookIdentifier !== indexHookIdentifier) { - throw new Error("Index hook identifier is invalid"); - } - - const reason = parseReasonFromBody(body); - - // Index the endpoint in 5 seconds from now - await workerQueue.enqueue( - "indexEndpoint", - { - id: endpoint.id, - source: "HOOK", - reason, - sourceData: body, - }, - { - runAt: new Date(Date.now() + 5000), - maxAttempts: - endpoint.environment.type === RuntimeEnvironmentType.DEVELOPMENT ? 1 : undefined, + if (!endpoint) { + throw new Error("Endpoint not found"); } - ); + + if (endpoint.indexingHookIdentifier !== indexHookIdentifier) { + throw new Error("Index hook identifier is invalid"); + } + + const reason = parseReasonFromBody(body); + + const index = await tx.endpointIndex.create({ + data: { + endpointId: endpoint.id, + status: "PENDING", + source: "HOOK", + reason, + sourceData: body, + }, + }); + + // Index the endpoint in 5 seconds from now + await workerQueue.enqueue( + "performEndpointIndexing", + { + id: index.id, + }, + { + runAt: new Date(Date.now() + 5000), + maxAttempts: + endpoint.environment.type === RuntimeEnvironmentType.DEVELOPMENT ? 1 : undefined, + tx, + } + ); + }); } } diff --git a/apps/webapp/app/routes/api.v1.runs.$runId.cancel.ts b/apps/webapp/app/routes/api.v1.runs.$runId.cancel.ts new file mode 100644 index 000000000..876d22d84 --- /dev/null +++ b/apps/webapp/app/routes/api.v1.runs.$runId.cancel.ts @@ -0,0 +1,71 @@ +import type { ActionArgs } from "@remix-run/server-runtime"; +import { json } from "@remix-run/server-runtime"; +import { PrismaErrorSchema } from "~/db.server"; +import { z } from "zod"; +import { authenticateApiRequest } from "~/services/apiAuth.server"; +import { CancelRunService } from "~/services/runs/cancelRun.server"; +import { ApiRunPresenter } from "~/presenters/ApiRunPresenter.server"; + +const ParamsSchema = z.object({ + runId: z.string(), +}); + +export async function action({ request, params }: ActionArgs) { + // Ensure this is a POST request + if (request.method.toUpperCase() !== "POST") { + return { status: 405, body: "Method Not Allowed" }; + } + + // Authenticate the request + const authenticationResult = await authenticateApiRequest(request); + + if (!authenticationResult) { + return json({ error: "Invalid or Missing API Key" }, { status: 401 }); + } + + const parsed = ParamsSchema.safeParse(params); + + if (!parsed.success) { + return json({ error: "Invalid or Missing runId" }, { status: 400 }); + } + + const { runId } = parsed.data; + + const service = new CancelRunService(); + try { + await service.call({ runId }); + } catch (error) { + const prismaError = PrismaErrorSchema.safeParse(error); + // Record not found in the database + if (prismaError.success && prismaError.data.code === "P2005") { + return json({ error: "Run not found" }, { status: 404 }); + } else { + return json({ error: "Internal Server Error" }, { status: 500 }); + } + } + + const presenter = new ApiRunPresenter(); + const jobRun = await presenter.call({ + runId: runId, + }); + + if (!jobRun) { + return json({ message: "Run not found" }, { status: 404 }); + } + + return json({ + id: jobRun.id, + status: jobRun.status, + startedAt: jobRun.startedAt, + updatedAt: jobRun.updatedAt, + completedAt: jobRun.completedAt, + output: jobRun.output, + tasks: jobRun.tasks, + statuses: jobRun.statuses.map((s) => ({ + ...s, + state: s.state ?? undefined, + data: s.data ?? undefined, + history: s.history ?? undefined, + })), + }); +} diff --git a/apps/webapp/app/routes/api.v1.runs.$runId.tasks.$id.callback.$secret.ts b/apps/webapp/app/routes/api.v1.runs.$runId.tasks.$id.callback.$secret.ts new file mode 100644 index 000000000..5c1425e2a --- /dev/null +++ b/apps/webapp/app/routes/api.v1.runs.$runId.tasks.$id.callback.$secret.ts @@ -0,0 +1,124 @@ +import type { ActionArgs } from "@remix-run/server-runtime"; +import { json } from "@remix-run/server-runtime"; +import { RuntimeEnvironmentType } from "@trigger.dev/database"; +import { z } from "zod"; +import { $transaction, PrismaClient, PrismaClientOrTransaction, prisma } from "~/db.server"; +import { enqueueRunExecutionV2 } from "~/models/jobRunExecution.server"; +import { logger } from "~/services/logger.server"; + +const ParamsSchema = z.object({ + runId: z.string(), + id: z.string(), + secret: z.string(), +}); + +export async function action({ request, params }: ActionArgs) { + // Ensure this is a POST request + if (request.method.toUpperCase() !== "POST") { + return { status: 405, body: "Method Not Allowed" }; + } + + const { runId, id } = ParamsSchema.parse(params); + + // Parse body as JSON (no schema parsing) + const body = await request.json(); + + const service = new CallbackRunTaskService(); + + try { + // Complete task with request body as output + await service.call(runId, id, body, request.url); + + return json({ success: true }); + } catch (error) { + if (error instanceof Error) { + logger.error("Error while processing task callback:", { error }); + } + + return json({ error: "Something went wrong" }, { status: 500 }); + } +} + +export class CallbackRunTaskService { + #prismaClient: PrismaClient; + + constructor(prismaClient: PrismaClient = prisma) { + this.#prismaClient = prismaClient; + } + + public async call(runId: string, id: string, taskBody: any, callbackUrl: string): Promise { + const task = await findTask(prisma, id); + + if (!task) { + return; + } + + if (task.runId !== runId) { + return; + } + + if (task.status !== "WAITING") { + return; + } + + if (!task.callbackUrl) { + return; + } + + if (new URL(task.callbackUrl).pathname !== new URL(callbackUrl).pathname) { + logger.error("Callback URLs don't match", { runId, taskId: id, callbackUrl }); + return; + } + + logger.debug("CallbackRunTaskService.call()", { task }); + + await this.#resumeTask(task, taskBody); + } + + async #resumeTask(task: NonNullable, output: any) { + await $transaction(this.#prismaClient, async (tx) => { + await tx.taskAttempt.updateMany({ + where: { + taskId: task.id, + status: "PENDING", + }, + data: { + status: "COMPLETED", + }, + }); + + await tx.task.update({ + where: { id: task.id }, + data: { + status: "COMPLETED", + completedAt: new Date(), + output: output ? output : undefined, + }, + }); + + await this.#resumeRunExecution(task, tx); + }); + } + + async #resumeRunExecution(task: NonNullable, prisma: PrismaClientOrTransaction) { + await enqueueRunExecutionV2(task.run, prisma, { + skipRetrying: task.run.environment.type === RuntimeEnvironmentType.DEVELOPMENT, + }); + } +} + +type FoundTask = Awaited>; + +async function findTask(prisma: PrismaClientOrTransaction, id: string) { + return prisma.task.findUnique({ + where: { id }, + include: { + run: { + include: { + environment: true, + queue: true, + }, + }, + }, + }); +} diff --git a/apps/webapp/app/routes/api.v1.runs.$runId.tasks.ts b/apps/webapp/app/routes/api.v1.runs.$runId.tasks.ts index 0e5eaecd9..871a48d92 100644 --- a/apps/webapp/app/routes/api.v1.runs.$runId.tasks.ts +++ b/apps/webapp/app/routes/api.v1.runs.$runId.tasks.ts @@ -1,14 +1,22 @@ import type { ActionArgs } from "@remix-run/server-runtime"; import { json } from "@remix-run/server-runtime"; import { TaskStatus } from "@trigger.dev/database"; -import { RunTaskBodyOutput, RunTaskBodyOutputSchema, ServerTask } from "@trigger.dev/core"; +import { + API_VERSIONS, + RunTaskBodyOutput, + RunTaskBodyOutputSchema, + RunTaskResponseWithCachedTasksBody, + ServerTask, +} from "@trigger.dev/core"; import { z } from "zod"; import { $transaction, PrismaClient, prisma } from "~/db.server"; -import { taskWithAttemptsToServerTask } from "~/models/task.server"; +import { prepareTasksForCaching, taskWithAttemptsToServerTask } from "~/models/task.server"; import { authenticateApiRequest } from "~/services/apiAuth.server"; import { logger } from "~/services/logger.server"; import { ulid } from "~/services/ulid.server"; import { workerQueue } from "~/services/worker.server"; +import { generateSecret } from "~/services/sources/utils.server"; +import { env } from "~/env.server"; const ParamsSchema = z.object({ runId: z.string(), @@ -16,6 +24,8 @@ const ParamsSchema = z.object({ const HeadersSchema = z.object({ "idempotency-key": z.string(), + "trigger-version": z.string().optional().nullable(), + "x-cached-tasks-cursor": z.string().optional().nullable(), }); export async function action({ request, params }: ActionArgs) { @@ -37,7 +47,11 @@ export async function action({ request, params }: ActionArgs) { return json({ error: "Invalid or Missing idempotency key" }, { status: 400 }); } - const { "idempotency-key": idempotencyKey } = headers.data; + const { + "idempotency-key": idempotencyKey, + "trigger-version": triggerVersion, + "x-cached-tasks-cursor": cachedTasksCursor, + } = headers.data; const { runId } = ParamsSchema.parse(params); @@ -48,6 +62,8 @@ export async function action({ request, params }: ActionArgs) { body: anyBody, runId, idempotencyKey, + triggerVersion, + cachedTasksCursor, }); const body = RunTaskBodyOutputSchema.safeParse(anyBody); @@ -71,6 +87,26 @@ export async function action({ request, params }: ActionArgs) { return json({ error: "Something went wrong" }, { status: 500 }); } + if (triggerVersion === API_VERSIONS.LAZY_LOADED_CACHED_TASKS) { + const requestMigration = new ChangeRequestLazyLoadedCachedTasks(); + + const responseBody = await requestMigration.call(runId, task, cachedTasksCursor); + + logger.debug( + "RunTaskService.call() response migrating with ChangeRequestLazyLoadedCachedTasks", + { + responseBody, + cachedTasksCursor, + } + ); + + return json(responseBody, { + headers: { + "trigger-version": API_VERSIONS.LAZY_LOADED_CACHED_TASKS, + }, + }); + } + return json(task); } catch (error) { if (error instanceof Error) { @@ -81,6 +117,51 @@ export async function action({ request, params }: ActionArgs) { } } +class ChangeRequestLazyLoadedCachedTasks { + #prismaClient: PrismaClient; + + constructor(prismaClient: PrismaClient = prisma) { + this.#prismaClient = prismaClient; + } + + public async call( + runId: string, + task: ServerTask, + cursor?: string | null + ): Promise { + if (!cursor) { + return { + task, + }; + } + + // We need to limit the cached tasks to not be too large >2MB when serialized + const TOTAL_CACHED_TASK_BYTE_LIMIT = 2000000; + + const nextTasks = await this.#prismaClient.task.findMany({ + where: { + runId, + status: "COMPLETED", + noop: false, + }, + take: 250, + cursor: { + id: cursor, + }, + orderBy: { + id: "asc", + }, + }); + + const preparedTasks = prepareTasksForCaching(nextTasks, TOTAL_CACHED_TASK_BYTE_LIMIT); + + return { + task, + cachedTasks: preparedTasks, + }; + } +} + export class RunTaskService { #prismaClient: PrismaClient; @@ -106,10 +187,13 @@ export class RunTaskService { }, }); + const delayUntilInFuture = taskBody.delayUntil && taskBody.delayUntil.getTime() > Date.now(); + const callbackEnabled = taskBody.callback?.enabled; + if (existingTask) { if (existingTask.status === "CANCELED") { const existingTaskStatus = - (taskBody.delayUntil && taskBody.delayUntil.getTime() > Date.now()) || taskBody.trigger + delayUntilInFuture || callbackEnabled || taskBody.trigger ? "WAITING" : taskBody.noop ? "COMPLETED" @@ -154,16 +238,21 @@ export class RunTaskService { status = "CANCELED"; } else { status = - (taskBody.delayUntil && taskBody.delayUntil.getTime() > Date.now()) || taskBody.trigger + delayUntilInFuture || callbackEnabled || taskBody.trigger ? "WAITING" : taskBody.noop ? "COMPLETED" : "RUNNING"; } + const taskId = ulid(); + const callbackUrl = callbackEnabled + ? `${env.APP_ORIGIN}/api/v1/runs/${runId}/tasks/${taskId}/callback/${generateSecret(12)}` + : undefined; + const task = await tx.task.create({ data: { - id: ulid(), + id: taskId, idempotencyKey, displayKey: taskBody.displayKey, runConnection: taskBody.connectionKey @@ -191,9 +280,10 @@ export class RunTaskService { noop: taskBody.noop, delayUntil: taskBody.delayUntil, params: taskBody.params ?? undefined, - properties: taskBody.properties ?? undefined, + properties: this.#filterProperties(taskBody.properties) ?? undefined, redact: taskBody.redact ?? undefined, operation: taskBody.operation, + callbackUrl, style: taskBody.style ?? { style: "normal" }, attempts: { create: { @@ -215,8 +305,19 @@ export class RunTaskService { { id: task.id, }, - { tx, runAt: task.delayUntil ?? undefined } + { tx, runAt: task.delayUntil ?? undefined, jobKey: `operation:${task.id}` } ); + } else if (task.status === "WAITING" && callbackUrl && taskBody.callback) { + if (taskBody.callback.timeoutInSeconds > 0) { + // We need to schedule the callback timeout + await workerQueue.enqueue( + "processCallbackTimeout", + { + id: task.id, + }, + { tx, runAt: new Date(Date.now() + taskBody.callback.timeoutInSeconds * 1000) } + ); + } } return task; @@ -224,4 +325,14 @@ export class RunTaskService { return task ? taskWithAttemptsToServerTask(task) : undefined; } + + #filterProperties(properties: RunTaskBodyOutput["properties"]): RunTaskBodyOutput["properties"] { + if (!properties) return; + + return properties.filter((property) => { + if (!property) return false; + + return typeof property.label === "string" && typeof property.text === "string"; + }); + } } diff --git a/apps/webapp/app/routes/api.v1.runs.$runId.ts b/apps/webapp/app/routes/api.v1.runs.$runId.ts index e1273654f..12fbaffc9 100644 --- a/apps/webapp/app/routes/api.v1.runs.$runId.ts +++ b/apps/webapp/app/routes/api.v1.runs.$runId.ts @@ -1,7 +1,7 @@ import type { LoaderArgs } from "@remix-run/server-runtime"; import { json } from "@remix-run/server-runtime"; import { z } from "zod"; -import { prisma } from "~/db.server"; +import { ApiRunPresenter } from "~/presenters/ApiRunPresenter.server"; import { authenticateApiRequest } from "~/services/apiAuth.server"; import { apiCors } from "~/utils/apiCors"; import { taskListToTree } from "~/utils/taskListToTree"; @@ -51,51 +51,15 @@ export async function loader({ request, params }: LoaderArgs) { const query = parsedQuery.data; const showTaskDetails = query.taskdetails && authenticationResult.type === "PRIVATE"; - const take = Math.min(query.take, 50); - const jobRun = await prisma.jobRun.findUnique({ - where: { - id: runId, - }, - select: { - id: true, - status: true, - startedAt: true, - updatedAt: true, - completedAt: true, - environmentId: true, - output: true, - tasks: { - select: { - id: true, - parentId: true, - displayKey: true, - status: true, - name: true, - icon: true, - startedAt: true, - completedAt: true, - params: showTaskDetails, - output: showTaskDetails, - }, - where: { - parentId: query.subtasks ? undefined : null, - }, - orderBy: { - id: "asc", - }, - take: take + 1, - cursor: query.cursor - ? { - id: query.cursor, - } - : undefined, - }, - statuses: { - select: { key: true, label: true, state: true, data: true, history: true }, - }, - }, + const presenter = new ApiRunPresenter(); + const jobRun = await presenter.call({ + runId: runId, + maxTasks: take, + taskDetails: showTaskDetails, + subTasks: query.subtasks, + cursor: query.cursor, }); if (!jobRun) { diff --git a/apps/webapp/app/routes/api.v1.sources.http.$id.ts b/apps/webapp/app/routes/api.v1.sources.http.$id.ts index 27092e881..c89a0a891 100644 --- a/apps/webapp/app/routes/api.v1.sources.http.$id.ts +++ b/apps/webapp/app/routes/api.v1.sources.http.$id.ts @@ -6,11 +6,24 @@ import { HandleHttpSourceService } from "~/services/sources/handleHttpSource.ser export async function action({ request, params }: ActionArgs) { logger.info("Handling http source", { url: request.url }); - const { id } = z.object({ id: z.string() }).parse(params); + try { + const { id } = z.object({ id: z.string() }).parse(params); + const service = new HandleHttpSourceService(); + const result = await service.call(id, request); - const service = new HandleHttpSourceService(); - - return await service.call(id, request); + return new Response(undefined, { + status: result.status, + }); + } catch (e) { + if (e instanceof Error) { + logger.error("Error handling http source", { error: e.message }); + } else { + logger.error("Error handling http source", { error: JSON.stringify(e) }); + } + return new Response(undefined, { + status: 500, + }); + } } export async function loader({ request, params }: LoaderArgs) { diff --git a/apps/webapp/app/routes/login._index/route.tsx b/apps/webapp/app/routes/login._index/route.tsx index 20bab4c24..ed85ab189 100644 --- a/apps/webapp/app/routes/login._index/route.tsx +++ b/apps/webapp/app/routes/login._index/route.tsx @@ -60,16 +60,29 @@ export default function LoginPage() { - + + + Create an account or login +
{data.showGithubAuth && ( - )} - + = ({ parentsData }) => ({ title: `Login to Trigger.dev${appEnvTitleTag(parentsData?.root.appEnv)}`, @@ -32,10 +32,26 @@ export async function loader({ request }: LoaderArgs) { }); const session = await getUserSession(request); + const error = session.get("auth:error"); - return typedjson({ - magicLinkSent: session.has("triggerdotdev:magiclink"), - }); + let magicLinkError: string | undefined; + if (error) { + if ("message" in error) { + magicLinkError = error.message; + } else { + magicLinkError = JSON.stringify(error, null, 2); + } + } + + return typedjson( + { + magicLinkSent: session.has("triggerdotdev:magiclink"), + magicLinkError, + }, + { + headers: { "Set-Cookie": await commitSession(session) }, + } + ); } export async function action({ request }: ActionArgs) { @@ -50,7 +66,7 @@ export async function action({ request }: ActionArgs) { .parse(payload); if (action === "send") { - await authenticator.authenticate("email-link", request, { + return authenticator.authenticate("email-link", request, { successRedirect: "/login/magic", failureRedirect: "/login/magic", }); @@ -67,13 +83,13 @@ export async function action({ request }: ActionArgs) { } export default function LoginMagicLinkPage() { - const { magicLinkSent } = useTypedLoaderData(); - const transition = useTransition(); + const { magicLinkSent, magicLinkError } = useTypedLoaderData(); + const navigate = useNavigation(); const isLoading = - (transition.state === "loading" || transition.state === "submitting") && - transition.type === "actionSubmission" && - transition.submission.formData.get("action") === "send"; + (navigate.state === "loading" || navigate.state === "submitting") && + navigate.formAction !== undefined && + navigate.formData?.get("action") === "send"; return ( @@ -102,12 +118,17 @@ export default function LoginMagicLinkPage() { variant="tertiary/small" LeadingIcon="arrow-left" leadingIconClassName="text-dimmed group-hover:text-bright transition" + data-action="re-enter email" > Re-enter email } confirmButton={ - + Log in using another option } @@ -116,7 +137,10 @@ export default function LoginMagicLinkPage() { ) : ( <> - + + + Create an account or login using your email +
@@ -137,6 +161,7 @@ export default function LoginMagicLinkPage() { variant="primary/medium" disabled={isLoading} fullWidth + data-action="send a magic link" > {isLoading ? "Sending…" : "Send a magic link"} + {magicLinkError && {magicLinkError}}
By logging in with your email you agree to our{" "} @@ -162,11 +188,28 @@ export default function LoginMagicLinkPage() { variant={"tertiary/small"} LeadingIcon={"arrow-left"} leadingIconClassName="text-dimmed group-hover:text-bright transition" + data-action="all login options" > All login options
)} +
+ + Having login issues? + + + Ensure the Magic Link email isn't in your spam folder. If the problem persists,{" "} + + drop us an email + {" "} + or let us know on{" "} + + Discord + + . + +
diff --git a/apps/webapp/app/routes/resources.environments.$environmentParam.endpoint.$endpointParam.ts b/apps/webapp/app/routes/resources.environments.$environmentParam.endpoint.$endpointParam.ts index 297595319..df8fcf927 100644 --- a/apps/webapp/app/routes/resources.environments.$environmentParam.endpoint.$endpointParam.ts +++ b/apps/webapp/app/routes/resources.environments.$environmentParam.endpoint.$endpointParam.ts @@ -1,11 +1,5 @@ -import { parse } from "@conform-to/zod"; import { ActionArgs, json } from "@remix-run/server-runtime"; import { z } from "zod"; -import { prisma } from "~/db.server"; -import { - CreateEndpointError, - CreateEndpointService, -} from "~/services/endpoints/createEndpoint.server"; import { IndexEndpointService } from "~/services/endpoints/indexEndpoint.server"; import { requireUserId } from "~/services/session.server"; diff --git a/apps/webapp/app/routes/resources.projects.$projectId.endpoint.ts b/apps/webapp/app/routes/resources.projects.$projectId.endpoint.ts index 0412919ec..ece220cb7 100644 --- a/apps/webapp/app/routes/resources.projects.$projectId.endpoint.ts +++ b/apps/webapp/app/routes/resources.projects.$projectId.endpoint.ts @@ -2,28 +2,15 @@ import { parse } from "@conform-to/zod"; import { ActionArgs, json } from "@remix-run/server-runtime"; import { z } from "zod"; import { prisma } from "~/db.server"; -import { - CreateEndpointError, - CreateEndpointService, -} from "~/services/endpoints/createEndpoint.server"; -import { requireUserId } from "~/services/session.server"; -import { RuntimeEnvironmentTypeSchema } from "@trigger.dev/core"; -import { env } from "process"; +import { CreateEndpointError } from "~/services/endpoints/createEndpoint.server"; import { ValidateCreateEndpointService } from "~/services/endpoints/validateCreateEndpoint.server"; -const ParamsSchema = z.object({ - projectId: z.string(), -}); - export const bodySchema = z.object({ environmentId: z.string(), url: z.string().url("Must be a valid URL"), }); -export async function action({ request, params }: ActionArgs) { - const userId = await requireUserId(request); - const { projectId } = ParamsSchema.parse(params); - +export async function action({ request }: ActionArgs) { const formData = await request.formData(); const submission = parse(formData, { schema: bodySchema }); @@ -48,7 +35,7 @@ export async function action({ request, params }: ActionArgs) { } const service = new ValidateCreateEndpointService(); - const result = await service.call({ + await service.call({ url: submission.value.url, environment, }); diff --git a/apps/webapp/app/services/email.server.ts b/apps/webapp/app/services/email.server.ts index bc8014fcc..04672d8be 100644 --- a/apps/webapp/app/services/email.server.ts +++ b/apps/webapp/app/services/email.server.ts @@ -1,4 +1,4 @@ -import type { DeliverEmail } from "emails"; +import type { DeliverEmail, SendPlainTextOptions } from "emails"; import { EmailClient } from "emails"; import type { SendEmailOptions } from "remix-auth-email-link"; import { redirect } from "remix-typedjson"; @@ -6,6 +6,7 @@ import { env } from "~/env.server"; import type { User } from "~/models/user.server"; import type { AuthUser } from "./authUser"; import { workerQueue } from "./worker.server"; +import { logger } from "./logger.server"; const client = new EmailClient({ apikey: env.RESEND_API_KEY, @@ -20,11 +21,22 @@ export async function sendMagicLinkEmail(options: SendEmailOptions): P throw redirect(options.magicLink); } - return client.send({ - email: "magic_link", - to: options.emailAddress, - magicLink: options.magicLink, - }); + logger.debug("Sending magic link email", { emailAddress: options.emailAddress }); + + try { + return await client.send({ + email: "magic_link", + to: options.emailAddress, + magicLink: options.magicLink, + }); + } catch (error) { + logger.error("Error sending magic link email", { error: JSON.stringify(error) }); + throw error; + } +} + +export async function sendPlainTextEmail(options: SendPlainTextOptions) { + return client.sendPlainText(options); } export async function scheduleWelcomeEmail(user: User) { diff --git a/apps/webapp/app/services/emailAuth.server.tsx b/apps/webapp/app/services/emailAuth.server.tsx index ff9ea4813..e48d405dd 100644 --- a/apps/webapp/app/services/emailAuth.server.tsx +++ b/apps/webapp/app/services/emailAuth.server.tsx @@ -5,6 +5,7 @@ import { findOrCreateUser } from "~/models/user.server"; import { env } from "~/env.server"; import { sendMagicLinkEmail } from "~/services/email.server"; import { postAuthentication } from "./postAuth.server"; +import { logger } from "./logger.server"; let secret = env.MAGIC_LINK_SECRET; if (!secret) throw new Error("Missing MAGIC_LINK_SECRET env variable."); @@ -25,6 +26,8 @@ const emailStrategy = new EmailLinkStrategy( form: FormData; magicLinkVerify: boolean; }) => { + logger.info("Magic link user authenticated", { email, magicLinkVerify }); + try { const { user, isNewUser } = await findOrCreateUser({ email, @@ -35,6 +38,7 @@ const emailStrategy = new EmailLinkStrategy( return { userId: user.id }; } catch (error) { + logger.debug("Magic link user failed to authenticate", { error: JSON.stringify(error) }); throw error; } } diff --git a/apps/webapp/app/services/endpointApi.server.ts b/apps/webapp/app/services/endpointApi.server.ts index 8cce435fa..708a62ee7 100644 --- a/apps/webapp/app/services/endpointApi.server.ts +++ b/apps/webapp/app/services/endpointApi.server.ts @@ -1,7 +1,9 @@ import { + API_VERSIONS, ApiEventLog, DeliverEventResponseSchema, DeserializedJson, + EndpointHeadersSchema, ErrorWithStackSchema, HttpSourceRequest, HttpSourceResponseSchema, @@ -89,10 +91,20 @@ export class EndpointApi { }; } + const headers = EndpointHeadersSchema.safeParse(Object.fromEntries(response.headers.entries())); + + if (headers.success && headers.data["trigger-version"]) { + return { + ...pongResponse.data, + triggerVersion: headers.data["trigger-version"], + }; + } + return pongResponse.data; } async indexEndpoint() { + const startTimeInMs = performance.now(); const response = await safeFetch(this.url, { method: "POST", headers: { @@ -102,66 +114,13 @@ export class EndpointApi { }, }); - if (!response) { - throw new Error(`Could not connect to endpoint ${this.url}`); - } - - if (response.status === 401) { - const body = await safeBodyFromResponse(response, ErrorWithStackSchema); - - if (body) { - return { - ok: false, - error: body.message, - } as const; - } - - return { - ok: false, - error: `Trigger API key is invalid`, - } as const; - } - - if (!response.ok) { - throw new Error(`Could not connect to endpoint ${this.url}. Status code: ${response.status}`); - } - - const anyBody = await response.json(); - - const data = IndexEndpointResponseSchema.parse(anyBody); - return { - ok: true, - data, - } as const; - } - - async deliverEvent(event: ApiEventLog) { - const response = await safeFetch(this.url, { - method: "POST", - headers: { - "Content-Type": "application/json", - "x-trigger-api-key": this.apiKey, - "x-trigger-action": "DELIVER_EVENT", - }, - body: JSON.stringify(event), - }); - - if (!response) { - throw new Error(`Could not connect to endpoint ${this.url}`); - } - - if (!response.ok) { - throw new Error(`Could not connect to endpoint ${this.url}. Status code: ${response.status}`); - } - - const anyBody = await response.json(); - - logger.debug("deliverEvent() response from endpoint", { - body: anyBody, - }); - - return DeliverEventResponseSchema.parse(anyBody); + response, + headerParser: EndpointHeadersSchema, + parser: IndexEndpointResponseSchema, + errorParser: ErrorWithStackSchema, + durationInMs: Math.floor(performance.now() - startTimeInMs), + }; } async executeJobRequest(options: RunJobBody) { @@ -338,6 +297,15 @@ export class EndpointApi { }; } + const headers = EndpointHeadersSchema.safeParse(Object.fromEntries(response.headers.entries())); + + if (headers.success && headers.data["trigger-version"]) { + return { + ...validateResponse.data, + triggerVersion: headers.data["trigger-version"], + }; + } + return validateResponse.data; } } @@ -359,6 +327,7 @@ function addStandardRequestOptions(options: RequestInit) { headers: { ...options.headers, "user-agent": "triggerdotdev-server/2.0.0", + "x-trigger-version": API_VERSIONS.LAZY_LOADED_CACHED_TASKS, }, }; } diff --git a/apps/webapp/app/services/endpoints/createEndpoint.server.ts b/apps/webapp/app/services/endpoints/createEndpoint.server.ts index bd1839222..a77bb844f 100644 --- a/apps/webapp/app/services/endpoints/createEndpoint.server.ts +++ b/apps/webapp/app/services/endpoints/createEndpoint.server.ts @@ -74,18 +74,27 @@ export class CreateEndpointService { slug: id, url: endpointUrl, indexingHookIdentifier: indexingHookIdentifier(), + version: pong.triggerVersion, }, update: { url: endpointUrl, + version: pong.triggerVersion, + }, + }); + + const endpointIndex = await tx.endpointIndex.create({ + data: { + endpointId: endpoint.id, + status: "PENDING", + source: "INTERNAL", }, }); // Kick off process to fetch the jobs for this endpoint await workerQueue.enqueue( - "indexEndpoint", + "performEndpointIndexing", { - id: endpoint.id, - source: "INTERNAL", + id: endpointIndex.id, }, { tx, @@ -94,7 +103,7 @@ export class CreateEndpointService { } ); - return endpoint; + return { ...endpoint, endpointIndex }; }); return result; diff --git a/apps/webapp/app/services/endpoints/indexEndpoint.server.ts b/apps/webapp/app/services/endpoints/indexEndpoint.server.ts index bc529fe0c..27df22c4f 100644 --- a/apps/webapp/app/services/endpoints/indexEndpoint.server.ts +++ b/apps/webapp/app/services/endpoints/indexEndpoint.server.ts @@ -1,23 +1,9 @@ import type { EndpointIndexSource } from "@trigger.dev/database"; import { PrismaClient, prisma } from "~/db.server"; -import { findEndpoint } from "~/models/endpoint.server"; -import { EndpointApi } from "../endpointApi.server"; -import { RegisterJobService } from "../jobs/registerJob.server"; -import { logger } from "../logger.server"; -import { RegisterSourceServiceV1 } from "../sources/registerSourceV1.server"; -import { RegisterDynamicScheduleService } from "../triggers/registerDynamicSchedule.server"; -import { RegisterDynamicTriggerService } from "../triggers/registerDynamicTrigger.server"; -import { DisableJobService } from "../jobs/disableJob.server"; -import { RegisterSourceServiceV2 } from "../sources/registerSourceV2.server"; +import { PerformEndpointIndexService } from "./performEndpointIndexService"; export class IndexEndpointService { #prismaClient: PrismaClient; - #registerJobService = new RegisterJobService(); - #disableJobService = new DisableJobService(); - #registerSourceServiceV1 = new RegisterSourceServiceV1(); - #registerSourceServiceV2 = new RegisterSourceServiceV2(); - #registerDynamicTriggerService = new RegisterDynamicTriggerService(); - #registerDynamicScheduleService = new RegisterDynamicScheduleService(); constructor(prismaClient: PrismaClient = prisma) { this.#prismaClient = prismaClient; @@ -29,207 +15,17 @@ export class IndexEndpointService { reason?: string, sourceData?: any ) { - const endpoint = await findEndpoint(id); - - // Make a request to the endpoint to fetch a list of jobs - const client = new EndpointApi(endpoint.environment.apiKey, endpoint.url); - - const indexResponse = await client.indexEndpoint(); - - if (!indexResponse.ok) { - throw new Error(indexResponse.error); - } - - const { jobs, sources, dynamicTriggers, dynamicSchedules } = indexResponse.data; - - logger.debug("Indexing endpoint", { - endpointId: endpoint.id, - endpointUrl: endpoint.url, - endpointSlug: endpoint.slug, - source: source, - sourceData: sourceData, - stats: { - jobs: jobs.length, - sources: sources.length, - dynamicTriggers: dynamicTriggers.length, - dynamicSchedules: dynamicSchedules.length, - }, - }); - - const indexStats = { - jobs: 0, - sources: 0, - dynamicTriggers: 0, - dynamicSchedules: 0, - disabledJobs: 0, - }; - - const existingJobs = await this.#prismaClient.job.findMany({ - where: { - projectId: endpoint.projectId, - deletedAt: null, - }, - include: { - aliases: { - where: { - name: "latest", - environmentId: endpoint.environmentId, - }, - include: { - version: true, - }, - take: 1, - }, - }, - }); - - for (const job of jobs) { - if (!job.enabled) { - const disabledJob = await this.#disableJobService - .call(endpoint, { slug: job.id, version: job.version }) - .catch((error) => { - logger.error("Failed to disable job", { - endpointId: endpoint.id, - job, - error, - }); - - return; - }); - - if (disabledJob) { - indexStats.disabledJobs++; - } - } else { - try { - const registeredVersion = await this.#registerJobService.call(endpoint, job); - - if (registeredVersion) { - indexStats.jobs++; - } - } catch (error) { - logger.error("Failed to register job", { - endpointId: endpoint.id, - job, - error, - }); - } - } - } - - // TODO: we need to do this for sources, dynamic triggers, and dynamic schedules - const missingJobs = existingJobs.filter((job) => { - return !jobs.find((j) => j.id === job.slug); - }); - - if (missingJobs.length > 0) { - logger.debug("Disabling missing jobs", { - endpointId: endpoint.id, - missingJobIds: missingJobs.map((job) => job.slug), - }); - - for (const job of missingJobs) { - const latestVersion = job.aliases[0]?.version; - - if (!latestVersion) { - continue; - } - - const disabledJob = await this.#disableJobService - .call(endpoint, { - slug: job.slug, - version: latestVersion.version, - }) - .catch((error) => { - logger.error("Failed to disable job", { - endpointId: endpoint.id, - job, - error, - }); - - return; - }); - - if (disabledJob) { - indexStats.disabledJobs++; - } - } - } - - for (const source of sources) { - try { - switch (source.version) { - default: - case "1": { - await this.#registerSourceServiceV1.call(endpoint, source); - break; - } - case "2": { - await this.#registerSourceServiceV2.call(endpoint, source); - break; - } - } - - indexStats.sources++; - } catch (error) { - logger.error("Failed to register source", { - endpointId: endpoint.id, - source, - error, - }); - } - } - - for (const dynamicTrigger of dynamicTriggers) { - try { - await this.#registerDynamicTriggerService.call(endpoint, dynamicTrigger); - - indexStats.dynamicTriggers++; - } catch (error) { - logger.error("Failed to register dynamic trigger", { - endpointId: endpoint.id, - dynamicTrigger, - error, - }); - } - } - - for (const dynamicSchedule of dynamicSchedules) { - try { - await this.#registerDynamicScheduleService.call(endpoint, dynamicSchedule); - - indexStats.dynamicSchedules++; - } catch (error) { - logger.error("Failed to register dynamic schedule", { - endpointId: endpoint.id, - dynamicSchedule, - error, - }); - } - } - - logger.debug("Endpoint indexing complete", { - endpointId: endpoint.id, - indexStats, - source, - sourceData, - reason, - }); - - return await this.#prismaClient.endpointIndex.create({ + const endpointIndex = await this.#prismaClient.endpointIndex.create({ data: { - endpointId: endpoint.id, - stats: indexStats, - data: { - jobs, - sources, - dynamicTriggers, - dynamicSchedules, - }, + endpointId: id, + status: "PENDING", source, - sourceData, reason, + sourceData, }, }); + + const performEndpointIndexService = new PerformEndpointIndexService(); + return await performEndpointIndexService.call(endpointIndex.id); } } diff --git a/apps/webapp/app/services/endpoints/performEndpointIndexService.ts b/apps/webapp/app/services/endpoints/performEndpointIndexService.ts new file mode 100644 index 000000000..ca1c0bcd1 --- /dev/null +++ b/apps/webapp/app/services/endpoints/performEndpointIndexService.ts @@ -0,0 +1,338 @@ +import type { EndpointIndexSource } from "@trigger.dev/database"; +import { PrismaClient, prisma } from "~/db.server"; +import { findEndpoint } from "~/models/endpoint.server"; +import { EndpointApi } from "../endpointApi.server"; +import { RegisterJobService } from "../jobs/registerJob.server"; +import { logger } from "../logger.server"; +import { RegisterSourceServiceV1 } from "../sources/registerSourceV1.server"; +import { RegisterDynamicScheduleService } from "../triggers/registerDynamicSchedule.server"; +import { RegisterDynamicTriggerService } from "../triggers/registerDynamicTrigger.server"; +import { DisableJobService } from "../jobs/disableJob.server"; +import { RegisterSourceServiceV2 } from "../sources/registerSourceV2.server"; +import { EndpointIndexError } from "@trigger.dev/core"; +import { safeBodyFromResponse } from "~/utils/json"; +import { fromZodError } from "zod-validation-error"; +import { IndexEndpointStats } from "@trigger.dev/core"; + +export class PerformEndpointIndexService { + #prismaClient: PrismaClient; + #registerJobService = new RegisterJobService(); + #disableJobService = new DisableJobService(); + #registerSourceServiceV1 = new RegisterSourceServiceV1(); + #registerSourceServiceV2 = new RegisterSourceServiceV2(); + #registerDynamicTriggerService = new RegisterDynamicTriggerService(); + #registerDynamicScheduleService = new RegisterDynamicScheduleService(); + + constructor(prismaClient: PrismaClient = prisma) { + this.#prismaClient = prismaClient; + } + + public async call(id: string) { + const endpointIndex = await this.#prismaClient.endpointIndex.update({ + where: { + id, + }, + data: { + status: "STARTED", + }, + include: { + endpoint: { + include: { + environment: { + include: { + organization: true, + project: true, + }, + }, + }, + }, + }, + }); + + logger.debug("Performing endpoint index", endpointIndex); + + // Make a request to the endpoint to fetch a list of jobs + const client = new EndpointApi( + endpointIndex.endpoint.environment.apiKey, + endpointIndex.endpoint.url + ); + const { response, parser, headerParser, errorParser } = await client.indexEndpoint(); + + if (!response) { + return updateEndpointIndexWithError(this.#prismaClient, id, { + message: `Could not connect to endpoint ${endpointIndex.endpoint.url}`, + }); + } + + if (response.status === 401) { + const body = await safeBodyFromResponse(response, errorParser); + + if (body) { + return updateEndpointIndexWithError(this.#prismaClient, id, { + message: body.message, + }); + } + + return updateEndpointIndexWithError(this.#prismaClient, id, { + message: "Trigger API key is invalid", + }); + } + + if (!response.ok) { + return updateEndpointIndexWithError(this.#prismaClient, id, { + message: `Could not connect to endpoint ${endpointIndex.endpoint.url}. Status code: ${response.status}`, + }); + } + + const anyBody = await response.json(); + const bodyResult = parser.safeParse(anyBody); + + if (!bodyResult.success) { + const issues: string[] = []; + bodyResult.error.issues.forEach((issue) => { + if (issue.path.at(0) === "jobs") { + const jobIndex = issue.path.at(1) as number; + const job = (anyBody as any).jobs[jobIndex]; + + if (job) { + issues.push(`Job "${job.id}": ${issue.message} at "${issue.path.slice(2).join(".")}".`); + } + } + }); + + let friendlyError: string | undefined; + if (issues.length > 0) { + friendlyError = `Your Jobs have issues:\n${issues.map((issue) => `- ${issue}`).join("\n")}`; + } else { + friendlyError = fromZodError(bodyResult.error, { + prefix: "There's an issue with the format of your Jobs", + }).message; + } + + return updateEndpointIndexWithError(this.#prismaClient, id, { + message: friendlyError, + raw: bodyResult.error.issues, + }); + } + + const headerResult = headerParser.safeParse(Object.fromEntries(response.headers.entries())); + if (!headerResult.success) { + const friendlyError = fromZodError(headerResult.error, { + prefix: "Your headers are invalid", + }); + return updateEndpointIndexWithError(this.#prismaClient, id, { + message: friendlyError.message, + raw: headerResult.error.issues, + }); + } + + const { jobs, sources, dynamicTriggers, dynamicSchedules } = bodyResult.data; + const { "trigger-version": triggerVersion } = headerResult.data; + const { endpoint } = endpointIndex; + + if (triggerVersion && triggerVersion !== endpoint.version) { + await this.#prismaClient.endpoint.update({ + where: { + id: endpoint.id, + }, + data: { + version: triggerVersion, + }, + }); + } + + const indexStats: IndexEndpointStats = { + jobs: 0, + sources: 0, + dynamicTriggers: 0, + dynamicSchedules: 0, + disabledJobs: 0, + }; + + const existingJobs = await this.#prismaClient.job.findMany({ + where: { + projectId: endpoint.projectId, + deletedAt: null, + }, + include: { + aliases: { + where: { + name: "latest", + environmentId: endpoint.environmentId, + }, + include: { + version: true, + }, + take: 1, + }, + }, + }); + + for (const job of jobs) { + if (!job.enabled) { + const disabledJob = await this.#disableJobService + .call(endpoint, { slug: job.id, version: job.version }) + .catch((error) => { + logger.error("Failed to disable job", { + endpointId: endpoint.id, + job, + error, + }); + + return; + }); + + if (disabledJob) { + indexStats.disabledJobs++; + } + } else { + try { + const registeredVersion = await this.#registerJobService.call(endpoint, job); + + if (registeredVersion) { + if (!job.internal) { + indexStats.jobs++; + } + } + } catch (error) { + logger.error("Failed to register job", { + endpointId: endpoint.id, + job, + error, + }); + } + } + } + + // TODO: we need to do this for sources, dynamic triggers, and dynamic schedules + const missingJobs = existingJobs.filter((job) => { + return !jobs.find((j) => j.id === job.slug); + }); + + if (missingJobs.length > 0) { + logger.debug("Disabling missing jobs", { + endpointId: endpoint.id, + missingJobIds: missingJobs.map((job) => job.slug), + }); + + for (const job of missingJobs) { + const latestVersion = job.aliases[0]?.version; + + if (!latestVersion) { + continue; + } + + const disabledJob = await this.#disableJobService + .call(endpoint, { + slug: job.slug, + version: latestVersion.version, + }) + .catch((error) => { + logger.error("Failed to disable job", { + endpointId: endpoint.id, + job, + error, + }); + + return; + }); + + if (disabledJob) { + indexStats.disabledJobs++; + } + } + } + + for (const source of sources) { + try { + switch (source.version) { + default: + case "1": { + await this.#registerSourceServiceV1.call(endpoint, source); + break; + } + case "2": { + await this.#registerSourceServiceV2.call(endpoint, source); + break; + } + } + + indexStats.sources++; + } catch (error) { + logger.error("Failed to register source", { + endpointId: endpoint.id, + source, + error, + }); + } + } + + for (const dynamicTrigger of dynamicTriggers) { + try { + await this.#registerDynamicTriggerService.call(endpoint, dynamicTrigger); + + indexStats.dynamicTriggers++; + } catch (error) { + logger.error("Failed to register dynamic trigger", { + endpointId: endpoint.id, + dynamicTrigger, + error, + }); + } + } + + for (const dynamicSchedule of dynamicSchedules) { + try { + await this.#registerDynamicScheduleService.call(endpoint, dynamicSchedule); + + indexStats.dynamicSchedules++; + } catch (error) { + logger.error("Failed to register dynamic schedule", { + endpointId: endpoint.id, + dynamicSchedule, + error, + }); + } + } + + logger.debug("Endpoint indexing complete", { + endpointId: endpoint.id, + indexStats, + source: endpointIndex.source, + sourceData: endpointIndex.sourceData, + reason: endpointIndex.reason, + }); + + return await this.#prismaClient.endpointIndex.update({ + where: { + id, + }, + data: { + status: "SUCCESS", + stats: indexStats, + data: { + jobs, + sources, + dynamicTriggers, + dynamicSchedules, + }, + }, + }); + } +} + +async function updateEndpointIndexWithError( + prismaClient: PrismaClient, + id: string, + error: EndpointIndexError +) { + return await prismaClient.endpointIndex.update({ + where: { + id, + }, + data: { + status: "FAILURE", + error, + }, + }); +} diff --git a/apps/webapp/app/services/endpoints/recurringEndpointIndex.server.ts b/apps/webapp/app/services/endpoints/recurringEndpointIndex.server.ts index 15bc6b015..72658c769 100644 --- a/apps/webapp/app/services/endpoints/recurringEndpointIndex.server.ts +++ b/apps/webapp/app/services/endpoints/recurringEndpointIndex.server.ts @@ -17,7 +17,9 @@ export class RecurringEndpointIndexService { const endpoints = await this.#prismaClient.endpoint.findMany({ where: { environment: { - type: RuntimeEnvironmentType.PRODUCTION, + type: { + in: [RuntimeEnvironmentType.PRODUCTION, RuntimeEnvironmentType.STAGING], + }, }, indexings: { none: { @@ -32,12 +34,18 @@ export class RecurringEndpointIndexService { logger.debug("Found endpoints that haven't been indexed in the last 10 minutes", { count: endpoints.length, }); - // Enqueue each endpoint for indexing for (const endpoint of endpoints) { - await workerQueue.enqueue("indexEndpoint", { - id: endpoint.id, - source: "INTERNAL", + const index = await this.#prismaClient.endpointIndex.create({ + data: { + endpointId: endpoint.id, + status: "PENDING", + source: "INTERNAL", + }, + }); + + await workerQueue.enqueue("performEndpointIndexing", { + id: index.id, }); } } diff --git a/apps/webapp/app/services/endpoints/validateCreateEndpoint.server.ts b/apps/webapp/app/services/endpoints/validateCreateEndpoint.server.ts index d578a26d9..84baa4c22 100644 --- a/apps/webapp/app/services/endpoints/validateCreateEndpoint.server.ts +++ b/apps/webapp/app/services/endpoints/validateCreateEndpoint.server.ts @@ -58,18 +58,23 @@ export class ValidateCreateEndpointService { slug: validationResult.endpointId, url: endpointUrl, indexingHookIdentifier: indexingHookIdentifier(), + version: validationResult.triggerVersion, }, update: { url: endpointUrl, + version: validationResult.triggerVersion, }, }); - // Kick off process to fetch the jobs for this endpoint + const index = await tx.endpointIndex.create({ + data: { endpointId: endpoint.id, status: "PENDING", source: "INTERNAL" }, + }); + + // Kick off process to fetch the jobs for this index await workerQueue.enqueue( - "indexEndpoint", + "performEndpointIndexing", { - id: endpoint.id, - source: "INTERNAL", + id: index.id, }, { tx, diff --git a/apps/webapp/app/services/events/ingestSendEvent.server.ts b/apps/webapp/app/services/events/ingestSendEvent.server.ts index b1de0e2a9..9e4a254bb 100644 --- a/apps/webapp/app/services/events/ingestSendEvent.server.ts +++ b/apps/webapp/app/services/events/ingestSendEvent.server.ts @@ -3,6 +3,25 @@ import { $transaction, PrismaClientOrTransaction, PrismaErrorSchema, prisma } fr import type { AuthenticatedEnvironment } from "~/services/apiAuth.server"; import { workerQueue } from "~/services/worker.server"; import { logger } from "../logger.server"; +import { EventRecord, ExternalAccount } from "@trigger.dev/database"; + +type UpdateEventInput = { + tx: PrismaClientOrTransaction; + existingEventLog: EventRecord; + reqEvent: RawEvent; + deliverAt?: Date; +}; + +type CreateEventInput = { + tx: PrismaClientOrTransaction; + event: RawEvent; + environment: AuthenticatedEnvironment; + deliverAt?: Date; + sourceContext?: { id: string; metadata?: any }; + externalAccount?: ExternalAccount; +}; + +const EVENT_UPDATE_THRESHOLD_WINDOW_IN_MSECS = 5 * 1000; // 5 seconds export class IngestSendEvent { #prismaClient: PrismaClientOrTransaction; @@ -52,34 +71,25 @@ export class IngestSendEvent { }) : undefined; - // Create a new event in the database - const eventLog = await tx.eventRecord.create({ - data: { - organizationId: environment.organizationId, - projectId: environment.projectId, - environmentId: environment.id, - eventId: event.id, - name: event.name, - timestamp: event.timestamp ?? new Date(), - payload: event.payload ?? {}, - context: event.context ?? {}, - source: event.source ?? "trigger.dev", - sourceContext, - deliverAt: deliverAt, - externalAccountId: externalAccount ? externalAccount.id : undefined, + const existingEventLog = await tx.eventRecord.findUnique({ + where: { + eventId_environmentId: { + eventId: event.id, + environmentId: environment.id, + }, }, }); - if (this.deliverEvents) { - // Produce a message to the event bus - await workerQueue.enqueue( - "deliverEvent", - { - id: eventLog.id, - }, - { runAt: eventLog.deliverAt, tx, jobKey: `event:${eventLog.id}` } - ); - } + const eventLog = await (existingEventLog + ? this.updateEvent({ tx, existingEventLog, reqEvent: event, deliverAt }) + : this.createEvent({ + tx, + event, + environment, + deliverAt, + sourceContext, + externalAccount, + })); return eventLog; }); @@ -95,21 +105,81 @@ export class IngestSendEvent { throw error; } - // If the error is a Prisma unique constraint error, it means that the event already exists - if (prismaError.success && prismaError.data.code === "P2002") { - logger.debug("Event already exists, finding and returning", { event, environment }); - - return this.#prismaClient.eventRecord.findUniqueOrThrow({ - where: { - eventId_environmentId: { - eventId: event.id, - environmentId: environment.id, - }, - }, - }); - } - throw error; } } + + private async createEvent({ + tx, + event, + environment, + deliverAt, + sourceContext, + externalAccount, + }: CreateEventInput) { + const eventLog = await tx.eventRecord.create({ + data: { + organizationId: environment.organizationId, + projectId: environment.projectId, + environmentId: environment.id, + eventId: event.id, + name: event.name, + timestamp: event.timestamp ?? new Date(), + payload: event.payload ?? {}, + context: event.context ?? {}, + source: event.source ?? "trigger.dev", + sourceContext, + deliverAt: deliverAt, + externalAccountId: externalAccount ? externalAccount.id : undefined, + }, + }); + + await this.enqueueWorkerEvent(tx, eventLog); + + return eventLog; + } + + private async updateEvent({ tx, existingEventLog, reqEvent, deliverAt }: UpdateEventInput) { + if (!this.shouldUpdateEvent(existingEventLog)) { + logger.debug(`not updating event for event id: ${existingEventLog.eventId}`); + return existingEventLog; + } + + const updatedEventLog = await tx.eventRecord.update({ + where: { + eventId_environmentId: { + eventId: existingEventLog.eventId, + environmentId: existingEventLog.environmentId, + }, + }, + data: { + payload: reqEvent.payload ?? existingEventLog.payload, + context: reqEvent.context ?? existingEventLog.context, + deliverAt: deliverAt ?? new Date(), + }, + }); + + await this.enqueueWorkerEvent(tx, updatedEventLog); + + return updatedEventLog; + } + + private shouldUpdateEvent(eventLog: EventRecord) { + const thresholdTime = new Date(Date.now() + EVENT_UPDATE_THRESHOLD_WINDOW_IN_MSECS); + + return eventLog.deliverAt >= thresholdTime; + } + + private async enqueueWorkerEvent(tx: PrismaClientOrTransaction, eventLog: EventRecord) { + if (this.deliverEvents) { + // Produce a message to the event bus + await workerQueue.enqueue( + "deliverEvent", + { + id: eventLog.id, + }, + { runAt: eventLog.deliverAt, tx, jobKey: `event:${eventLog.id}` } + ); + } + } } diff --git a/apps/webapp/app/services/externalApis/integrationCatalog.server.ts b/apps/webapp/app/services/externalApis/integrationCatalog.server.ts index b86c2e496..3c13f154a 100644 --- a/apps/webapp/app/services/externalApis/integrationCatalog.server.ts +++ b/apps/webapp/app/services/externalApis/integrationCatalog.server.ts @@ -3,6 +3,7 @@ import { github } from "./integrations/github"; import { linear } from "./integrations/linear"; import { openai } from "./integrations/openai"; import { plain } from "./integrations/plain"; +import { replicate } from "./integrations/replicate"; import { resend } from "./integrations/resend"; import { sendgrid } from "./integrations/sendgrid"; import { slack } from "./integrations/slack"; @@ -37,6 +38,7 @@ export const integrationCatalog = new IntegrationCatalog({ linear, openai, plain, + replicate, resend, slack, stripe, diff --git a/apps/webapp/app/services/externalApis/integrations/replicate.ts b/apps/webapp/app/services/externalApis/integrations/replicate.ts new file mode 100644 index 000000000..74f20cdaf --- /dev/null +++ b/apps/webapp/app/services/externalApis/integrations/replicate.ts @@ -0,0 +1,50 @@ +import type { HelpSample, Integration } from "../types"; + +function usageSample(hasApiKey: boolean): HelpSample { + const apiKeyPropertyName = "apiKey"; + + return { + title: "Using the client", + code: ` +import { Replicate } from "@trigger.dev/replicate"; + +const replicate = new Replicate({ + id: "__SLUG__",${hasApiKey ? `,\n ${apiKeyPropertyName}: process.env.REPLICATE_API_KEY!` : ""} +}); + +client.defineJob({ + id: "replicate-create-prediction", + name: "Replicate - Create Prediction", + version: "0.1.0", + integrations: { replicate }, + trigger: eventTrigger({ + name: "replicate.predict", + schema: z.object({ + prompt: z.string(), + version: z.string(), + }), + }), + run: async (payload, io, ctx) => { + return io.replicate.predictions.createAndAwait("await-prediction", { + version: payload.version, + input: { prompt: payload.prompt }, + }); + }, +}); + `, + }; +} + +export const replicate: Integration = { + identifier: "replicate", + name: "Replicate", + packageName: "@trigger.dev/replicate@latest", + authenticationMethods: { + apikey: { + type: "apikey", + help: { + samples: [usageSample(true)], + }, + }, + }, +}; diff --git a/apps/webapp/app/services/jobs/registerJob.server.ts b/apps/webapp/app/services/jobs/registerJob.server.ts index 401c69c8a..8e75e5376 100644 --- a/apps/webapp/app/services/jobs/registerJob.server.ts +++ b/apps/webapp/app/services/jobs/registerJob.server.ts @@ -4,14 +4,7 @@ import { SCHEDULED_EVENT, TriggerMetadata, } from "@trigger.dev/core"; -import type { - Endpoint, - Integration, - Job, - JobIntegration, - JobIntegrationPayload, - JobVersion, -} from "@trigger.dev/database"; +import type { Endpoint, Integration, Job, JobIntegration, JobVersion } from "@trigger.dev/database"; import { DEFAULT_MAX_CONCURRENT_RUNS } from "~/consts"; import type { PrismaClient } from "~/db.server"; import { prisma } from "~/db.server"; @@ -229,7 +222,7 @@ export class RegisterJobService { }, update: { name: example.name, - icon: example.icon, + icon: example.icon ?? null, payload: example.payload, }, }); diff --git a/apps/webapp/app/services/logger.server.ts b/apps/webapp/app/services/logger.server.ts index fdc886479..75bb91ca1 100644 --- a/apps/webapp/app/services/logger.server.ts +++ b/apps/webapp/app/services/logger.server.ts @@ -8,3 +8,10 @@ export const logger = new Logger( ["examples", "output", "connectionString", "payload"], sensitiveDataReplacer ); + +export const workerLogger = new Logger( + "worker", + (process.env.APP_LOG_LEVEL ?? "debug") as LogLevel, + ["examples", "output", "connectionString"], + sensitiveDataReplacer +); diff --git a/apps/webapp/app/services/runs/createRun.server.ts b/apps/webapp/app/services/runs/createRun.server.ts index a3c789193..7c1089772 100644 --- a/apps/webapp/app/services/runs/createRun.server.ts +++ b/apps/webapp/app/services/runs/createRun.server.ts @@ -70,6 +70,7 @@ export class CreateRunService { ? eventRecord.externalAccountId : undefined, isTest: eventRecord.isTest, + internal: job.internal, }, }); diff --git a/apps/webapp/app/services/runs/performRunExecutionV1.server.ts b/apps/webapp/app/services/runs/performRunExecutionV1.server.ts index 591f6f00b..c67a9dd18 100644 --- a/apps/webapp/app/services/runs/performRunExecutionV1.server.ts +++ b/apps/webapp/app/services/runs/performRunExecutionV1.server.ts @@ -263,6 +263,7 @@ export class PerformRunExecutionV1Service { .flat() .filter(Boolean) .map((t) => CachedTaskSchema.parse(t)), + yieldedExecutions: run.yieldedExecutions, }); if (!response) { @@ -354,6 +355,11 @@ export class PerformRunExecutionV1Service { break; } + case "YIELD_EXECUTION": { + await this.#resumeYieldedExecution(execution, safeBody.data.key); + + break; + } default: { const _exhaustiveCheck: never = status; throw new Error(`Non-exhaustive match for value: ${status}`); @@ -393,6 +399,40 @@ export class PerformRunExecutionV1Service { }); } + async #resumeYieldedExecution(execution: FoundRunExecution, key: string) { + const { run } = execution; + + return await $transaction(this.#prismaClient, async (tx) => { + await tx.jobRunExecution.update({ + where: { + id: execution.id, + }, + data: { + status: "SUCCESS", + completedAt: new Date(), + run: { + update: { + yieldedExecutions: { + push: key, + }, + }, + }, + }, + }); + + const newJobExecution = await tx.jobRunExecution.create({ + data: { + runId: run.id, + reason: "EXECUTE_JOB", + status: "PENDING", + retryLimit: EXECUTE_JOB_RETRY_LIMIT, + }, + }); + + await enqueueRunExecutionV1(newJobExecution, run.queue.id, run.queue.maxJobs, tx); + }); + } + async #resumeRunWithTask(execution: FoundRunExecution, data: RunJobResumeWithTask) { const { run } = execution; @@ -409,7 +449,9 @@ export class PerformRunExecutionV1Service { // If the task has an operation, then the next performRunExecution will occur // when that operation has finished - if (!data.task.operation) { + // Tasks with callbacks enabled will also get processed separately, i.e. when + // they time out, or on valid requests to their callbackUrl + if (!data.task.operation && !data.task.callbackUrl) { const newJobExecution = await tx.jobRunExecution.create({ data: { runId: run.id, diff --git a/apps/webapp/app/services/runs/performRunExecutionV2.server.ts b/apps/webapp/app/services/runs/performRunExecutionV2.server.ts index 61feab018..f22d13edb 100644 --- a/apps/webapp/app/services/runs/performRunExecutionV2.server.ts +++ b/apps/webapp/app/services/runs/performRunExecutionV2.server.ts @@ -1,12 +1,17 @@ import { - CachedTask, + API_VERSIONS, + BloomFilter, + ConnectionAuth, + EndpointHeadersSchema, RunJobError, RunJobInvalidPayloadError, RunJobResumeWithTask, RunJobRetryWithTask, RunJobSuccess, RunJobUnresolvedAuthError, + RunSourceContext, RunSourceContextSchema, + supportsFeature, } from "@trigger.dev/core"; import { RuntimeEnvironmentType, type Task } from "@trigger.dev/database"; import { generateErrorMessage } from "zod-error"; @@ -18,10 +23,17 @@ import { formatError } from "~/utils/formatErrors.server"; import { safeJsonZodParse } from "~/utils/json"; import { EndpointApi } from "../endpointApi.server"; import { logger } from "../logger.server"; +import { prepareTasksForCaching, prepareTasksForCachingLegacy } from "~/models/task.server"; +import { MAX_RUN_YIELDED_EXECUTIONS } from "~/consts"; +import { ApiEventLog } from "@trigger.dev/core"; +import { RunJobBody } from "@trigger.dev/core"; type FoundRun = NonNullable>>; type FoundTask = FoundRun["tasks"][number]; +// We need to limit the cached tasks to not be too large >3.5MB when serialized +const TOTAL_CACHED_TASK_BYTE_LIMIT = 3500000; + export type PerformRunExecutionV2Input = { id: string; reason: "PREPROCESS" | "EXECUTE_JOB"; @@ -230,38 +242,19 @@ export class PerformRunExecutionV2Service { const sourceContext = RunSourceContextSchema.safeParse(run.event.sourceContext); - const { response, parser, errorParser, durationInMs } = await client.executeJobRequest({ + const executionBody = await this.#createExecutionBody( + run, + [run.tasks, resumedTask].flat().filter(Boolean), + startedAt, + isRetry, + connections.auth, event, - job: { - id: run.version.job.slug, - version: run.version.version, - }, - run: { - id: run.id, - isTest: run.isTest, - startedAt, - isRetry, - }, - environment: { - id: run.environment.id, - slug: run.environment.slug, - type: run.environment.type, - }, - organization: { - id: run.organization.id, - slug: run.organization.slug, - title: run.organization.title, - }, - account: run.externalAccount - ? { - id: run.externalAccount.identifier, - metadata: run.externalAccount.metadata, - } - : undefined, - connections: connections.auth, - source: sourceContext.success ? sourceContext.data : undefined, - tasks: prepareTasksForRun([run.tasks, resumedTask].flat().filter(Boolean)), - }); + sourceContext.success ? sourceContext.data : undefined + ); + + const { response, parser, errorParser, durationInMs } = await client.executeJobRequest( + executionBody + ); if (!response) { return await this.#failRunExecutionWithRetry({ @@ -269,6 +262,25 @@ export class PerformRunExecutionV2Service { }); } + // Update the endpoint version if it has changed + const rawHeaders = Object.fromEntries(response.headers.entries()); + const headers = EndpointHeadersSchema.safeParse(rawHeaders); + + if ( + headers.success && + headers.data["trigger-version"] && + headers.data["trigger-version"] !== run.endpoint.version + ) { + await this.#prismaClient.endpoint.update({ + where: { + id: run.endpoint.id, + }, + data: { + version: headers.data["trigger-version"], + }, + }); + } + const rawBody = await response.text(); if (!response.ok) { @@ -389,6 +401,10 @@ export class PerformRunExecutionV2Service { break; } + case "YIELD_EXECUTION": { + await this.#resumeYieldedRun(run, safeBody.data.key, isRetry, durationInMs, executionCount); + break; + } default: { const _exhaustiveCheck: never = status; throw new Error(`Non-exhaustive match for value: ${status}`); @@ -396,6 +412,91 @@ export class PerformRunExecutionV2Service { } } + async #createExecutionBody( + run: FoundRun, + tasks: FoundTask[], + startedAt: Date, + isRetry: boolean, + connections: Record, + event: ApiEventLog, + source?: RunSourceContext + ): Promise { + if (supportsFeature("lazyLoadedCachedTasks", run.endpoint.version)) { + const preparedTasks = prepareTasksForCaching(tasks, TOTAL_CACHED_TASK_BYTE_LIMIT); + + return { + event, + job: { + id: run.version.job.slug, + version: run.version.version, + }, + run: { + id: run.id, + isTest: run.isTest, + startedAt, + isRetry, + }, + environment: { + id: run.environment.id, + slug: run.environment.slug, + type: run.environment.type, + }, + organization: { + id: run.organization.id, + slug: run.organization.slug, + title: run.organization.title, + }, + account: run.externalAccount + ? { + id: run.externalAccount.identifier, + metadata: run.externalAccount.metadata, + } + : undefined, + connections, + source, + tasks: preparedTasks.tasks, + cachedTaskCursor: preparedTasks.cursor, + noopTasksSet: prepareNoOpTasksBloomFilter(tasks), + yieldedExecutions: run.yieldedExecutions, + }; + } + + const preparedTasks = prepareTasksForCachingLegacy(tasks, TOTAL_CACHED_TASK_BYTE_LIMIT); + + return { + event, + job: { + id: run.version.job.slug, + version: run.version.version, + }, + run: { + id: run.id, + isTest: run.isTest, + startedAt, + isRetry, + }, + environment: { + id: run.environment.id, + slug: run.environment.slug, + type: run.environment.type, + }, + organization: { + id: run.organization.id, + slug: run.organization.slug, + title: run.organization.title, + }, + account: run.externalAccount + ? { + id: run.externalAccount.identifier, + metadata: run.externalAccount.metadata, + } + : undefined, + connections, + source, + tasks: preparedTasks.tasks, + }; + } + async #completeRunWithSuccess(run: FoundRun, data: RunJobSuccess, durationInMs: number) { await this.#prismaClient.jobRun.update({ where: { id: run.id }, @@ -429,7 +530,9 @@ export class PerformRunExecutionV2Service { // If the task has an operation, then the next performRunExecution will occur // when that operation has finished - if (!data.task.operation) { + // Tasks with callbacks enabled will also get processed separately, i.e. when + // they time out, or on valid requests to their callbackUrl + if (!data.task.operation && !data.task.callbackUrl) { await enqueueRunExecutionV2(run, tx, { runAt: data.task.delayUntil ?? undefined, resumeTaskId: data.task.id, @@ -501,6 +604,56 @@ export class PerformRunExecutionV2Service { }); } + async #resumeYieldedRun( + run: FoundRun, + key: string, + isRetry: boolean, + durationInMs: number, + executionCount: number + ) { + await $transaction(this.#prismaClient, async (tx) => { + if (run.yieldedExecutions.length + 1 > MAX_RUN_YIELDED_EXECUTIONS) { + return await this.#failRunExecution( + tx, + "EXECUTE_JOB", + run, + { + message: `Run has yielded too many times, the maximum is ${MAX_RUN_YIELDED_EXECUTIONS}`, + }, + "FAILURE", + durationInMs + ); + } + + await tx.jobRun.update({ + where: { + id: run.id, + }, + data: { + executionDuration: { + increment: durationInMs, + }, + executionCount: { + increment: 1, + }, + yieldedExecutions: { + push: key, + }, + }, + select: { + yieldedExecutions: true, + executionCount: true, + }, + }); + + await enqueueRunExecutionV2(run, tx, { + isRetry, + skipRetrying: run.environment.type === RuntimeEnvironmentType.DEVELOPMENT, + executionCount, + }); + }); + } + async #retryRunWithTask( run: FoundRun, data: RunJobRetryWithTask, @@ -686,69 +839,16 @@ export class PerformRunExecutionV2Service { } } -function prepareTasksForRun(possibleTasks: FoundTask[]): CachedTask[] { - const tasks = possibleTasks.filter((task) => task.status === "COMPLETED"); +function prepareNoOpTasksBloomFilter(possibleTasks: FoundTask[]): string { + const tasks = possibleTasks.filter((task) => task.status === "COMPLETED" && task.noop); - // We need to limit the cached tasks to not be too large >3.5MB when serialized - const TOTAL_CACHED_TASK_BYTE_LIMIT = 3500000; + const filter = new BloomFilter(BloomFilter.NOOP_TASK_SET_SIZE); - const cachedTasks = new Map(); // Cache for prepared tasks - const cachedTaskSizes = new Map(); // Cache for calculated task sizes - - // Helper function to get the cached prepared task, or prepare and cache if not already cached - function getCachedTask(task: FoundTask): CachedTask { - const taskId = task.id; - if (!cachedTasks.has(taskId)) { - cachedTasks.set(taskId, prepareTaskForRun(task)); - } - return cachedTasks.get(taskId)!; + for (const task of tasks) { + filter.add(task.idempotencyKey); } - // Helper function to get the cached task size, or calculate and cache if not already cached - function getCachedTaskSize(task: CachedTask): number { - const taskId = task.id; - if (!cachedTaskSizes.has(taskId)) { - cachedTaskSizes.set(taskId, calculateCachedTaskSize(task)); - } - return cachedTaskSizes.get(taskId)!; - } - - // Prepare tasks and calculate their sizes - const availableTasks = tasks.map((task) => { - const cachedTask = getCachedTask(task); - return { task: cachedTask, size: getCachedTaskSize(cachedTask) }; - }); - - // Sort tasks in ascending order by size - availableTasks.sort((a, b) => a.size - b.size); - - // Select tasks using greedy approach - const tasksToRun: CachedTask[] = []; - let remainingSize = TOTAL_CACHED_TASK_BYTE_LIMIT; - - for (const { task, size } of availableTasks) { - if (size <= remainingSize) { - tasksToRun.push(task); - remainingSize -= size; - } - } - - return tasksToRun; -} - -function prepareTaskForRun(task: FoundTask): CachedTask { - return { - id: task.idempotencyKey, // We should eventually move this back to task.id - status: task.status, - idempotencyKey: task.idempotencyKey, - noop: task.noop, - output: task.output as any, - parentId: task.parentId, - }; -} - -function calculateCachedTaskSize(task: CachedTask): number { - return JSON.stringify(task).length; + return filter.serialize(); } async function findRun(prisma: PrismaClientOrTransaction, id: string) { @@ -783,6 +883,9 @@ async function findRun(prisma: PrismaClientOrTransaction, id: string) { output: true, parentId: true, }, + orderBy: { + id: "asc", + }, }, event: true, version: { diff --git a/apps/webapp/app/services/schedules/nextScheduledEvent.server.ts b/apps/webapp/app/services/schedules/nextScheduledEvent.server.ts index 85f0f9811..762227624 100644 --- a/apps/webapp/app/services/schedules/nextScheduledEvent.server.ts +++ b/apps/webapp/app/services/schedules/nextScheduledEvent.server.ts @@ -36,7 +36,7 @@ export class NextScheduledEventService { const scheduleTime = calculateNextScheduledEvent( schedule.data, - scheduleSource.lastEventTimestamp + scheduleSource.lastEventTimestamp ?? scheduleSource.createdAt ); logger.debug("enqueuing scheduled event", { @@ -67,6 +67,7 @@ export class NextScheduledEventService { }, data: { workerJobId: workerJob.id, + nextEventTimestamp: scheduleTime, }, }); diff --git a/apps/webapp/app/services/sources/utils.server.ts b/apps/webapp/app/services/sources/utils.server.ts index 127ca4b7a..4c2bc7ae7 100644 --- a/apps/webapp/app/services/sources/utils.server.ts +++ b/apps/webapp/app/services/sources/utils.server.ts @@ -1,5 +1,5 @@ import crypto from "node:crypto"; -export function generateSecret(): string { - return crypto.randomBytes(32).toString("hex"); +export function generateSecret(sizeInBytes = 32): string { + return crypto.randomBytes(sizeInBytes).toString("hex"); } diff --git a/apps/webapp/app/services/tasks/performTaskOperation.server.ts b/apps/webapp/app/services/tasks/performTaskOperation.server.ts index fb5a6304a..987cb56fa 100644 --- a/apps/webapp/app/services/tasks/performTaskOperation.server.ts +++ b/apps/webapp/app/services/tasks/performTaskOperation.server.ts @@ -1,5 +1,3 @@ -import { env } from "process"; -import { Run } from "~/presenters/RunPresenter.server"; import { FetchOperationSchema, FetchRequestInit, diff --git a/apps/webapp/app/services/tasks/processCallbackTimeout.ts b/apps/webapp/app/services/tasks/processCallbackTimeout.ts new file mode 100644 index 000000000..948691990 --- /dev/null +++ b/apps/webapp/app/services/tasks/processCallbackTimeout.ts @@ -0,0 +1,76 @@ +import { RuntimeEnvironmentType } from "@trigger.dev/database"; +import { $transaction, PrismaClient, PrismaClientOrTransaction, prisma } from "~/db.server"; +import { enqueueRunExecutionV2 } from "~/models/jobRunExecution.server"; +import { logger } from "../logger.server"; + +type FoundTask = Awaited>; + +export class ProcessCallbackTimeoutService { + #prismaClient: PrismaClient; + + constructor(prismaClient: PrismaClient = prisma) { + this.#prismaClient = prismaClient; + } + + public async call(id: string) { + const task = await findTask(this.#prismaClient, id); + + if (!task) { + return; + } + + if (task.status !== "WAITING" || !task.callbackUrl) { + return; + } + + logger.debug("ProcessCallbackTimeoutService.call", { task }); + + return await this.#failTask(task, "Remote callback timeout - no requests received"); + } + + async #failTask(task: NonNullable, error: string) { + await $transaction(this.#prismaClient, async (tx) => { + await tx.taskAttempt.updateMany({ + where: { + taskId: task.id, + status: "PENDING", + }, + data: { + status: "ERRORED", + error + }, + }); + + await tx.task.update({ + where: { id: task.id }, + data: { + status: "ERRORED", + completedAt: new Date(), + output: error, + }, + }); + + await this.#resumeRunExecution(task, tx); + }); + } + + async #resumeRunExecution(task: NonNullable, prisma: PrismaClientOrTransaction) { + await enqueueRunExecutionV2(task.run, prisma, { + skipRetrying: task.run.environment.type === RuntimeEnvironmentType.DEVELOPMENT, + }); + } +} + +async function findTask(prisma: PrismaClient, id: string) { + return prisma.task.findUnique({ + where: { id }, + include: { + run: { + include: { + environment: true, + queue: true, + }, + }, + }, + }); +} diff --git a/apps/webapp/app/services/worker.server.ts b/apps/webapp/app/services/worker.server.ts index 90b710b3a..ecdfd8a97 100644 --- a/apps/webapp/app/services/worker.server.ts +++ b/apps/webapp/app/services/worker.server.ts @@ -1,16 +1,18 @@ import { DeliverEmailSchema } from "@/../../packages/emails/src"; -import { ScheduledPayloadSchema } from "@trigger.dev/core"; +import { ScheduledPayloadSchema, addMissingVersionField } from "@trigger.dev/core"; import { z } from "zod"; import { prisma } from "~/db.server"; import { env } from "~/env.server"; import { ZodWorker } from "~/platform/zodWorker.server"; import { sendEmail } from "./email.server"; import { IndexEndpointService } from "./endpoints/indexEndpoint.server"; +import { PerformEndpointIndexService } from "./endpoints/performEndpointIndexService"; import { RecurringEndpointIndexService } from "./endpoints/recurringEndpointIndex.server"; import { DeliverEventService } from "./events/deliverEvent.server"; import { InvokeDispatcherService } from "./events/invokeDispatcher.server"; import { integrationAuthRepository } from "./externalApis/integrationAuthRepository.server"; import { IntegrationConnectionCreatedService } from "./externalApis/integrationConnectionCreated.server"; +import { logger } from "./logger.server"; import { MissingConnectionCreatedService } from "./runs/missingConnectionCreated.server"; import { PerformRunExecutionV1Service } from "./runs/performRunExecutionV1.server"; import { PerformRunExecutionV2Service } from "./runs/performRunExecutionV2.server"; @@ -19,7 +21,7 @@ import { DeliverScheduledEventService } from "./schedules/deliverScheduledEvent. import { ActivateSourceService } from "./sources/activateSource.server"; import { DeliverHttpSourceRequestService } from "./sources/deliverHttpSourceRequest.server"; import { PerformTaskOperationService } from "./tasks/performTaskOperation.server"; -import { addMissingVersionField } from "@trigger.dev/core"; +import { ProcessCallbackTimeoutService } from "./tasks/processCallbackTimeout"; const workerCatalog = { indexEndpoint: z.object({ @@ -28,8 +30,14 @@ const workerCatalog = { sourceData: z.any().optional(), reason: z.string().optional(), }), + performEndpointIndexing: z.object({ + id: z.string(), + }), scheduleEmail: DeliverEmailSchema, startRun: z.object({ id: z.string() }), + processCallbackTimeout: z.object({ + id: z.string(), + }), performTaskOperation: z.object({ id: z.string(), }), @@ -186,6 +194,11 @@ function getWorkerQueue() { return new ZodWorker({ name: "workerQueue", prisma, + cleanup: { + frequencyExpression: "13,27,43 * * * *", + ttl: 7 * 24 * 60 * 60 * 1000, // 7 days + maxCount: 1000, + }, runnerOptions: { connectionString: env.DATABASE_URL, concurrency: env.WORKER_CONCURRENCY, @@ -287,6 +300,7 @@ function getWorkerQueue() { deliverHttpSourceRequest: { priority: 1, // smaller number = higher priority maxAttempts: 14, + queueName: (payload) => `sources:${payload.id}`, handler: async (payload, job) => { const service = new DeliverHttpSourceRequestService(); @@ -302,9 +316,17 @@ function getWorkerQueue() { await service.call(payload.id); }, }, + processCallbackTimeout: { + priority: 0, // smaller number = higher priority + maxAttempts: 3, + handler: async (payload, job) => { + const service = new ProcessCallbackTimeoutService(); + + await service.call(payload.id); + }, + }, performTaskOperation: { priority: 0, // smaller number = higher priority - queueName: (payload) => `tasks:${payload.id}`, maxAttempts: 3, handler: async (payload, job) => { const service = new PerformTaskOperationService(); @@ -313,7 +335,6 @@ function getWorkerQueue() { }, }, scheduleEmail: { - queueName: "internal-queue", priority: 100, maxAttempts: 3, handler: async (payload, job) => { @@ -325,10 +346,17 @@ function getWorkerQueue() { maxAttempts: 7, handler: async (payload, job) => { const service = new IndexEndpointService(); - await service.call(payload.id, payload.source, payload.reason, payload.sourceData); }, }, + performEndpointIndexing: { + priority: 1, // smaller number = higher priority + maxAttempts: 7, + handler: async (payload, job) => { + const service = new PerformEndpointIndexService(); + await service.call(payload.id); + }, + }, deliverEvent: { priority: 0, // smaller number = higher priority maxAttempts: 5, @@ -340,7 +368,6 @@ function getWorkerQueue() { }, refreshOAuthToken: { priority: 8, // smaller number = higher priority - queueName: "internal-queue", maxAttempts: 7, handler: async (payload, job) => { await integrationAuthRepository.refreshConnection({ diff --git a/apps/webapp/app/utils/icon.ts b/apps/webapp/app/utils/icon.ts new file mode 100644 index 000000000..d3ee34bcc --- /dev/null +++ b/apps/webapp/app/utils/icon.ts @@ -0,0 +1,9 @@ +import { hasIcon } from "@trigger.dev/companyicons"; +import { iconNames as namedIcons } from "~/components/primitives/NamedIcon"; + +export const isValidIcon = (icon?: string): boolean => { + if (!icon) { + return false; + } + return namedIcons.includes(icon) || hasIcon(icon); +}; diff --git a/apps/webapp/app/utils/sse.ts b/apps/webapp/app/utils/sse.ts index 4588cedc5..105911d7f 100644 --- a/apps/webapp/app/utils/sse.ts +++ b/apps/webapp/app/utils/sse.ts @@ -47,7 +47,7 @@ export function sse({ request, pingInterval = 1000, updateInterval = 348, run }: }); } } else { - logger.debug("Uknown error sending SSE, aborting", { + logger.debug("Unknown error sending SSE, aborting", { error, args, }); diff --git a/apps/webapp/package.json b/apps/webapp/package.json index 4d4f5ce16..7ee50248a 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -13,7 +13,7 @@ "lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .", "start": "cross-env NODE_ENV=production node --max-old-space-size=8192 ./build/server.js", "start:local": "cross-env node --max-old-space-size=8192 ./build/server.js", - "typecheck": "tsc --noEmit", + "typecheck": "tsc -p ./tsconfig.check.json", "db:seed": "node prisma/seed.js", "db:seed:local": "ts-node prisma/seed.ts", "generate:sourcemaps": "remix build --sourcemap", @@ -34,6 +34,7 @@ "@codemirror/lang-javascript": "^6.1.1", "@codemirror/lang-json": "^6.0.1", "@codemirror/language": "^6.3.1", + "@codemirror/lint": "^6.4.2", "@codemirror/search": "^6.2.3", "@codemirror/state": "^6.1.3", "@codemirror/view": "^6.5.0", @@ -93,7 +94,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-hot-toast": "^2.4.0", - "react-hotkeys-hook": "^3.4.7", + "react-hotkeys-hook": "^4.4.1", "react-use": "^17.4.0", "recharts": "^2.8.0", "remix-auth": "^3.2.2", @@ -105,13 +106,15 @@ "simple-oauth2": "^5.0.0", "simplur": "^3.0.1", "slug": "^6.0.0", + "sonner": "^1.0.3", "tailwind-merge": "^1.12.0", "tailwind-scrollbar-hide": "^1.1.7", "tailwindcss-animate": "^1.0.5", "tiny-invariant": "^1.2.0", "ulid": "^2.3.0", - "zod": "3.21.4", - "zod-error": "1.5.0" + "zod": "3.22.3", + "zod-error": "1.5.0", + "zod-validation-error": "^1.5.0" }, "devDependencies": { "@remix-run/dev": "1.19.2-pre.0", diff --git a/apps/webapp/server.ts b/apps/webapp/server.ts index 892a99430..55c9fc530 100644 --- a/apps/webapp/server.ts +++ b/apps/webapp/server.ts @@ -62,7 +62,23 @@ if (process.env.HTTP_SERVER_DISABLED !== "true") { }); // Handle shutdowns gracefully - createTerminus(server, { signals: ["SIGINT", "SIGTERM"], timeout: 5000 }); + createTerminus(server, { + signals: ["SIGINT", "SIGTERM"], + timeout: process.env.GRACEFUL_SHUTDOWN_TIMEOUT + ? Number(process.env.GRACEFUL_SHUTDOWN_TIMEOUT) + : 5000, + onSignal: async () => { + console.log("[terminus] onSignal: starting cleanup"); + }, + onShutdown: async () => { + console.log("[terminus] onShutdown: cleanup finished, server is shutting down"); + }, + onSendFailureDuringShutdown: async () => { + console.log( + "[terminus] onSendFailureDuringShutdown: cleanup finished, server is shutting down" + ); + }, + }); } else { console.log(`βœ… app ready (skipping http server)`); } diff --git a/apps/webapp/tsconfig.check.json b/apps/webapp/tsconfig.check.json new file mode 100644 index 000000000..f1adffe51 --- /dev/null +++ b/apps/webapp/tsconfig.check.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true, + "paths": { + "~/*": ["./app/*"], + "@/*": ["./*"] + } + } +} diff --git a/config-packages/tsconfig/integration.json b/config-packages/tsconfig/integration.json new file mode 100644 index 000000000..ff9d795e5 --- /dev/null +++ b/config-packages/tsconfig/integration.json @@ -0,0 +1,19 @@ +{ + "extends": "./node18.json", + "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ES2019"], + "paths": { + "@trigger.dev/tsup/*": ["../../config-packages/tsup/src/*"], + "@trigger.dev/tsup": ["../../config-packages/tsup/src/index"], + "@trigger.dev/sdk/*": ["../../packages/trigger-sdk/src/*"], + "@trigger.dev/sdk": ["../../packages/trigger-sdk/src/index"], + "@trigger.dev/integration-kit/*": ["../../packages/integration-kit/src/*"], + "@trigger.dev/integration-kit": ["../../packages/integration-kit/src/index"] + }, + "declaration": false, + "declarationMap": false, + "baseUrl": ".", + "stripInternal": true + }, + "exclude": ["node_modules"] +} diff --git a/config-packages/tsup/package.json b/config-packages/tsup/package.json new file mode 100644 index 000000000..8af32c7ae --- /dev/null +++ b/config-packages/tsup/package.json @@ -0,0 +1,9 @@ +{ + "name": "@trigger.dev/tsup", + "version": "0.0.0", + "private": true, + "license": "MIT", + "devDependencies": { + "tsup": "7.1.x" + } +} diff --git a/config-packages/tsup/src/index.ts b/config-packages/tsup/src/index.ts new file mode 100644 index 000000000..661e53cca --- /dev/null +++ b/config-packages/tsup/src/index.ts @@ -0,0 +1,3 @@ +export { defineConfig } from "tsup"; +export { deepMergeOptions } from "./utils"; +export { options as integrationOptions } from "./integration"; diff --git a/config-packages/tsup/src/integration.ts b/config-packages/tsup/src/integration.ts new file mode 100644 index 000000000..0fb2a3d7d --- /dev/null +++ b/config-packages/tsup/src/integration.ts @@ -0,0 +1,22 @@ +import { Options, defineConfig } from "tsup"; + +export const options: Options = { + name: "main", + entry: ["./src/index.ts"], + outDir: "./dist", + platform: "node", + format: ["cjs"], + legacyOutput: true, + sourcemap: true, + clean: true, + bundle: true, + splitting: false, + dts: true, + treeshake: { + preset: "smallest", + }, + esbuildPlugins: [], + external: ["http", "https", "util", "events", "tty", "os", "timers"], +}; + +export default defineConfig(options); diff --git a/config-packages/tsup/src/utils.ts b/config-packages/tsup/src/utils.ts new file mode 100644 index 000000000..6e46ff742 --- /dev/null +++ b/config-packages/tsup/src/utils.ts @@ -0,0 +1,32 @@ +import { Options } from "tsup"; + +export const deepMergeOptions = deepMergeRecords; + +function deepMergeRecords>(...options: TRecord[]): TRecord { + const result = {} as TRecord; + + for (const option of options) { + for (const key in option) { + if (option.hasOwnProperty(key)) { + const optionValue = option[key]; + const existingValue = result[key]; + + if ( + existingValue && + typeof existingValue === "object" && + typeof optionValue === "object" && + !Array.isArray(existingValue) && + !Array.isArray(optionValue) && + existingValue !== null && + optionValue !== null + ) { + result[key] = deepMergeRecords(existingValue, optionValue); + } else { + result[key] = optionValue; + } + } + } + } + + return result; +} diff --git a/docker/Dockerfile b/docker/Dockerfile index 2c7d3fc30..cf12ecd6a 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -32,7 +32,8 @@ ENV NODE_ENV production RUN pnpm install --prod --no-frozen-lockfile COPY --from=pruner --chown=node:node /triggerdotdev/packages/database/prisma/schema.prisma /triggerdotdev/packages/database/prisma/schema.prisma # RUN pnpm add @prisma/client@5.1.1 -w -RUN pnpx prisma@4.16.0 generate --schema /triggerdotdev/packages/database/prisma/schema.prisma +ENV NPM_CONFIG_IGNORE_WORKSPACE_ROOT_CHECK true +RUN pnpx prisma@5.4.1 generate --schema /triggerdotdev/packages/database/prisma/schema.prisma ## Builder (builds the webapp) FROM base AS builder diff --git a/docker/dev-compose.yml b/docker/dev-compose.yml index b5db14f4b..3f0003a57 100644 --- a/docker/dev-compose.yml +++ b/docker/dev-compose.yml @@ -35,6 +35,7 @@ services: DIRECT_URL: postgres://postgres:postgres@db:5432/postgres?schema=public SESSION_SECRET: secret123 MAGIC_LINK_SECRET: secret123 + ENCRYPTION_KEY: secret123 REMIX_APP_PORT: 3030 PORT: 3030 networks: diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 76017f376..41e935745 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -2,6 +2,7 @@ version: "3" volumes: database-data: + pgadmin-data: networks: app_network: @@ -22,3 +23,21 @@ services: - app_network ports: - 5432:5432 + + pgadmin: + container_name: pgadmin + image: dpage/pgadmin4:7 + restart: always + environment: + PGADMIN_DEFAULT_EMAIL: admin@example.com + PGADMIN_DEFAULT_PASSWORD: admin + PGADMIN_DISABLE_POSTFIX: "true" + volumes: + - pgadmin-data:/var/lib/pgadmin + - ./pgadmin/servers.json:/pgadmin4/servers.json + networks: + - app_network + ports: + - 5480:80 + depends_on: + - database diff --git a/docker/pgadmin/servers.json b/docker/pgadmin/servers.json new file mode 100644 index 000000000..83f7159bb --- /dev/null +++ b/docker/pgadmin/servers.json @@ -0,0 +1,13 @@ +{ + "Servers": { + "1": { + "Name": "Trigger.dev", + "Group": "Trigger.dev", + "Port": 5432, + "Username": "postgres", + "Host": "database", + "SSLMode": "prefer", + "MaintenanceDB": "postgres" + } + } +} diff --git a/docs/_snippets/frameworks/card-astro.mdx b/docs/_snippets/frameworks/card-astro.mdx new file mode 100644 index 000000000..6db902f93 --- /dev/null +++ b/docs/_snippets/frameworks/card-astro.mdx @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + } + href="/documentation/quickstarts/astro" +/> diff --git a/docs/_snippets/frameworks/card-express.mdx b/docs/_snippets/frameworks/card-express.mdx new file mode 100644 index 000000000..3fc9f18c1 --- /dev/null +++ b/docs/_snippets/frameworks/card-express.mdx @@ -0,0 +1,24 @@ + + + + + + + + + + + } + href="/documentation/quickstarts/express" +/> diff --git a/docs/_snippets/frameworks/card-fastify.mdx b/docs/_snippets/frameworks/card-fastify.mdx new file mode 100644 index 000000000..09ad6d051 --- /dev/null +++ b/docs/_snippets/frameworks/card-fastify.mdx @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + } + href="/documentation/quickstarts/fastify" +/> diff --git a/docs/_snippets/frameworks/card-nestjs.mdx b/docs/_snippets/frameworks/card-nestjs.mdx new file mode 100644 index 000000000..84aa84537 --- /dev/null +++ b/docs/_snippets/frameworks/card-nestjs.mdx @@ -0,0 +1,23 @@ + + + + + } + href="/documentation/quickstarts/nestjs" +/> diff --git a/docs/_snippets/frameworks/card-nextjs.mdx b/docs/_snippets/frameworks/card-nextjs.mdx new file mode 100644 index 000000000..9f6759574 --- /dev/null +++ b/docs/_snippets/frameworks/card-nextjs.mdx @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + +} + href="/documentation/quickstarts/nextjs" + +/> diff --git a/docs/_snippets/frameworks/card-nuxt.mdx b/docs/_snippets/frameworks/card-nuxt.mdx new file mode 100644 index 000000000..ec3561cb6 --- /dev/null +++ b/docs/_snippets/frameworks/card-nuxt.mdx @@ -0,0 +1,28 @@ + + + + + + + + + + + + } + href="/documentation/quickstarts/nuxt" +/> diff --git a/docs/_snippets/frameworks/card-redwood.mdx b/docs/_snippets/frameworks/card-redwood.mdx new file mode 100644 index 000000000..f37a1afa2 --- /dev/null +++ b/docs/_snippets/frameworks/card-redwood.mdx @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + } + href="/documentation/quickstarts/redwood" +/> diff --git a/docs/_snippets/frameworks/card-remix.mdx b/docs/_snippets/frameworks/card-remix.mdx new file mode 100644 index 000000000..9de8732f6 --- /dev/null +++ b/docs/_snippets/frameworks/card-remix.mdx @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + href="/documentation/quickstarts/remix" +/> diff --git a/docs/_snippets/frameworks/card-supabase.mdx b/docs/_snippets/frameworks/card-supabase.mdx new file mode 100644 index 000000000..84afe8c23 --- /dev/null +++ b/docs/_snippets/frameworks/card-supabase.mdx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + href="/documentation/quickstarts/supabase" +/> diff --git a/docs/_snippets/frameworks/card-sveltekit.mdx b/docs/_snippets/frameworks/card-sveltekit.mdx new file mode 100644 index 000000000..b6f5a768a --- /dev/null +++ b/docs/_snippets/frameworks/card-sveltekit.mdx @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + } + href="/documentation/quickstarts/sveltekit" +/> diff --git a/docs/_snippets/manual-setup-nestjs.mdx b/docs/_snippets/manual-setup-nestjs.mdx new file mode 100644 index 000000000..c053c49d4 --- /dev/null +++ b/docs/_snippets/manual-setup-nestjs.mdx @@ -0,0 +1,264 @@ + + Create a blank project by installing the NestJS CLI in your terminal: + +```bash +npm i -g @nestjs/cli +``` + +Then, create an empty project with: + +```bash +nest new project-name +``` + + + +## Installing Required Packages + +To begin, install the necessary packages in your NestJS project directory. You can choose one of the following package managers: + + +```bash npm +npm i @trigger.dev/sdk @trigger.dev/nestjs @nestjs/config +``` + +```bash pnpm +pnpm install @trigger.dev/sdk @trigger.dev/nestjs @nestjs/config +``` + +```bash yarn +yarn add @trigger.dev/sdk @trigger.dev/nestjs @nestjs/config +``` + + + +
+ +Ensure that you execute this command within a NestJS project. + +## 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` 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://api.trigger.dev # this line is only necessary if you are self-hosting Trigger +``` + +Replace `ENTER_YOUR_DEVELOPMENT_API_KEY_HERE` with the actual API key obtained from the previous step. + + + This configuration only will be loaded if you use [NestJS + Config](https://docs.nestjs.com/techniques/configuration) or + [dotenv](https://github.com/motdotla/dotenv). + + +## Adding TriggerDev Module + +Open your `app.module.ts`, and add the following inside your `imports`: + +```typescript +import { TriggerDevModule } from "@trigger.dev/nestjs"; +import { Module } from "@nestjs/common"; + +//you need to load the environment variables from .env, this is one way to do it +import "dotenv/config"; + +@Module({ + imports: [ + TriggerDevModule.register({ + id: "my-app", + apiKey: process.env.TRIGGER_API_KEY, + apiUrl: process.env.TRIGGER_API_URL, + }), + // if you use NestJS Config, you can do like this: + // TriggerDevModule.registerAsync({ + // useFactory: (configService: ConfigService) => ({ + // id: 'my-app', + // apiKey: configService.get("TRIGGER_API_KEY"), + // apiUrl: configService.get("TRIGGER_API_URL"), + // }), + // inject: [ConfigService], + // }), + ], +}) +export class AppModule { + //... +} +``` + +Replace **"my-app"** with an appropriate identifier for your project. The **apiKey** and **apiUrl** are obtained from the environment variables you set earlier. + +By following these steps, you'll configure the Trigger Client to work with your project. + +## Creating the Example Job + +When you add `TriggerDevModule` to your project, you will can have access to the `TriggerClient` instance by using the `@InjectTriggerDevClient()` decorator in the constructor. + +Now, let's create an example job to test the integration. + +1. Create a controller named `job.controller.ts` alongside your `app.module.ts` +2. Inside that controller, add the following code: + + + +```typescript job.controller.ts +import { Controller, Get } from "@nestjs/common"; +import { InjectTriggerDevClient } from "@trigger.dev/nestjs"; +import { eventTrigger, TriggerClient } from "@trigger.dev/sdk"; + +@Controller() +export class JobController { + constructor(@InjectTriggerDevClient() private readonly client: TriggerClient) { + this.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!", + }; + }, + }); + } + + @Get() + getHello(): string { + return `Running Trigger.dev with client-id ${this.client.id}`; + } +} +``` + +Now, add this controller to your `app.module.ts`: + +```typescript app.module.ts +import { TriggerDevModule } from "@trigger.dev/nestjs"; +import { Module } from "@nestjs/common"; +import { JobController } from "./job.controller"; + +//you need to load the environment variables from .env, this is one way to do it +import "dotenv/config"; + +@Module({ + controllers: [JobController], + imports: [ + TriggerDevModule.register({ + id: "my-app", + apiKey: process.env.TRIGGER_API_KEY, + apiUrl: process.env.TRIGGER_API_URL, + }), + // if you use NestJS Config, you can do like this: + // TriggerDevModule.registerAsync({ + // useFactory: (configService: ConfigService) => ({ + // id: 'my-app', + // apiKey: configService.get("TRIGGER_API_KEY"), + // apiUrl: configService.get("TRIGGER_API_URL"), + // }), + // inject: [ConfigService], + // }), + ], +}) +export class AppModule { + //... +} +``` + + + +
+ + You can import the Trigger.dev client inside any `service` or `controller`, we recommend you to + create specialized `service` for each job you have for a better maintainability. + + +## 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. + +## Running + +### Run your NestJS app + +Run your NestJS app locally, like you normally would. For example: + + + +```bash npm +npm run start +``` + +```bash pnpm +pnpm run start +``` + +```bash yarn +yarn run start +``` + + + +### Run the CLI 'dev' command + +In a **_separate terminal window or tab_** run: + + + +```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 +``` + + +
+ + You can optionally pass the port if you're not running on 3000 by adding + `--port 3001` to the end + + + + You can optionally pass the hostname if you're not running on localhost by adding + `--hostname `. Example, in case your Remix is running on 0.0.0.0: `--hostname 0.0.0.0`. + diff --git a/docs/_snippets/manual-setup-sveltekit.mdx b/docs/_snippets/manual-setup-sveltekit.mdx index 8e299631d..438005689 100644 --- a/docs/_snippets/manual-setup-sveltekit.mdx +++ b/docs/_snippets/manual-setup-sveltekit.mdx @@ -1 +1,217 @@ -We're in the process of building support for the SvelteKit framework. You can follow along with progress or contribute via [this GitHub issue](https://github.com/triggerdotdev/trigger.dev/issues). +## Installing Required Packages + +To begin, install the necessary packages in your Sveltekit project directory. You can choose one of the following package managers: + + + +```bash npm +npm i @trigger.dev/sdk @trigger.dev/sveltekit +``` + +```bash pnpm +pnpm install @trigger.dev/sdk @trigger.dev/sveltekit +``` + +```bash yarn +yarn add @trigger.dev/sdk @trigger.dev/sveltekit +``` + + +
+ +Ensure that you execute this command within a SvelteKit project. +## 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` 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://api.trigger.dev # this is only necessary if you are self-hosting +``` + +Replace `ENTER_YOUR_DEVELOPMENT_API_KEY_HERE` with the actual API key obtained from the previous step. + +## Syncing Environment Variable types (TypeScript) + +You will have type errors for your environment variables unless you run this command: + +```sh +npx svelte-kit sync +``` + +## Configuring the Trigger Client + +Create a file at `/src/trigger.ts` or `/trigger.ts` depending on whether you're using the `src` directory or not. `` represents the root directory of your project. + +Next, add the following code to the file which creates and exports a new `TriggerClient`: + +```typescript src/trigger.(ts/js) +// trigger.ts (for TypeScript) or trigger.js (for JavaScript) + +import { TriggerClient } from "@trigger.dev/sdk"; +import { TRIGGER_API_KEY, TRIGGER_API_URL } from "$env/static/private"; + +export const client = new TriggerClient({ + id: "my-app", + apiKey: TRIGGER_API_KEY, + apiUrl: TRIGGER_API_URL, +}); +``` + +Replace **"my-app"** with an appropriate identifier for your project. + +## 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 + +Create a new file named `+server.(ts/js)` within the `src/routes/api/trigger` directory, and add the following code: + +```typescript +import { createSvelteRoute } from "@trigger.dev/sveltekit"; +import { client } from "../../../trigger"; + +//import all jobs +import "../../../jobs"; + +// Create the Svelte route handler using the createSvelteRoute function +const svelteRoute = createSvelteRoute(client); + +// Define your API route handler +export const POST = svelteRoute.POST; +``` + +## Creating the Example Job + +1. Create a folder named `jobs` alongside your `src` directory +2. Inside the `jobs` folder, add two files named `example.(ts/js)` and `index.(ts/js)`. + + + +```typescript src/jobs/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 src/jobs/index.(ts/js) +// export all your job files here +export * from "./example"; +``` + + + +## Additonal Job Definitions + +You can define more job definitions by creating additional files in the `jobs` folder and exporting them in the `src/jobs/index` file. + +For example, in `index.(ts/js)`, you can export other job files like this: + +```typescript +// export all your job files here +export * from "./example"; +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. + +## Running + +### Run your Sveltekit app + +Run your Sveltekit app locally. You need to use the `--host` flag to allow the Trigger.dev CLI to connect to your app. + +For example: + + + +```bash npm +npm run dev -- --open --host +``` + +```bash pnpm +pnpm run dev -- --open --host +``` + +```bash yarn +yarn run dev -- --open --host +``` + + + +### Run the CLI 'dev' command + +In a **_separate terminal window or tab_** run: + + + +```bash npm +npx @trigger.dev/cli@latest dev --port 5173 +``` + +```bash pnpm +pnpm dlx @trigger.dev/cli@latest dev --port 5173 +``` + +```bash yarn +yarn dlx @trigger.dev/cli@latest dev --port 5173 +``` + + +
+ + You can optionally pass the port if you're not running on 3000 by adding + `--port 5173` to the end + + + You can optionally pass the hostname if you're not running on localhost by adding + `--hostname `. Example, in case your Sveltekit app is running on 0.0.0.0: `--hostname 0.0.0.0`. + diff --git a/docs/documentation/concepts/client-adaptors.mdx b/docs/documentation/concepts/client-adaptors.mdx index 98fc4dc56..7cd7c3a90 100644 --- a/docs/documentation/concepts/client-adaptors.mdx +++ b/docs/documentation/concepts/client-adaptors.mdx @@ -24,10 +24,12 @@ Adaptors allows Clients to receive data from the Trigger API. They do this by cr Each platform has one or more adaptors, see the guides below: -| Platform | Adaptor | -| ------------------------------------------------- | -------------------- | -| [Next.js](/documentation/guides/platforms/nextjs) | `createPagesRoute()` | -| [Next.js](/documentation/guides/platforms/nextjs) | `createAppRoute()` | -| [Astro](/documentation/guides/platforms/astro) | `createAstroRoute()` | -| [Remix](/documentation/guides/platforms/remix) | `createRemixRoute()` | -| Express | Coming soon | +| Platform | Adaptor | +| ------------------------------------------------------ | --------------------- | +| [Next.js](/documentation/guides/platforms/nextjs) | `createPagesRoute()` | +| [Next.js](/documentation/guides/platforms/nextjs) | `createAppRoute()` | +| [NestJS](/documentation/guides/manual/nestjs) | `TriggerDevModule` | +| [Astro](/documentation/guides/platforms/astro) | `createAstroRoute()` | +| [Remix](/documentation/guides/platforms/remix) | `createRemixRoute()` | +| [Sveltekit](/documentation/guides/platforms/sveltekit) | `createSvelteRoute()` | +| Express | Coming soon | diff --git a/docs/documentation/concepts/triggers/introduction.mdx b/docs/documentation/concepts/triggers/introduction.mdx index c626e7852..c4a56cee3 100644 --- a/docs/documentation/concepts/triggers/introduction.mdx +++ b/docs/documentation/concepts/triggers/introduction.mdx @@ -1,30 +1,19 @@ --- -title: Introduction +title: "Triggers: Introduction" +sidebarTitle: "Introduction" description: "A Trigger is what starts a Job Run. It can be a webhook, a schedule, or an event." --- We currently support three types of Triggers: Webhooks, Scheduled, and Events. You can use any of these to start a Job Run. - + Start your Jobs in realtime when events happen in APIs - + Run a Job on a repeating schedule - + Run your Job when you send events with data + This only needs to be done once for each environment @@ -32,11 +29,7 @@ There are two ways to do this: > Manually refresh in your Trigger.dev dashboard - + Automatically refresh by using our webhook diff --git a/docs/documentation/guides/manual/nestjs.mdx b/docs/documentation/guides/manual/nestjs.mdx new file mode 100644 index 000000000..e29c1ae27 --- /dev/null +++ b/docs/documentation/guides/manual/nestjs.mdx @@ -0,0 +1,7 @@ +--- +title: "NestJS" +sidebarTitle: "NestJS" +description: "How to manually setup Trigger.dev in your NestJS project" +--- + + diff --git a/docs/documentation/guides/react-hooks.mdx b/docs/documentation/guides/react-hooks.mdx index 648225a15..0132daae7 100644 --- a/docs/documentation/guides/react-hooks.mdx +++ b/docs/documentation/guides/react-hooks.mdx @@ -1,5 +1,6 @@ --- -title: "Overview" +title: "React hooks: Overview" +sidebarTitle: "Overview" description: "How to show the live status of Job Runs in your React app" --- diff --git a/docs/documentation/guides/testing-jobs.mdx b/docs/documentation/guides/testing-jobs.mdx index 04ce3bf51..d988ba20f 100644 --- a/docs/documentation/guides/testing-jobs.mdx +++ b/docs/documentation/guides/testing-jobs.mdx @@ -14,9 +14,32 @@ There's a tab on the Job page called **Test**. Or you can click the "Test" butto ![Your options on the Test page](/images/test-annotated.png) -1. Select the environment you'd like the test to run against. -2. Some Triggers provide example payloads that you can select from. This will populate the code editor below. -3. When you're happy with the payload, click **Run test**. + + + You will see errors inline if you have any syntax errors. You can use the *Clear* and *Copy* + buttons in the corner. + + + Some Triggers provide example payloads that you can select from. When selected they will + populate the code editor below. + + + If you have previously done Runs, you can select from the most recent payloads. When selected + they will populate the code editor below. + + + If this Job has associated Accounts, enter an Account ID. See [testing with account + ids](/documentation/guides/using-integrations-byo-auth#testing-jobs-with-account-id) for more + information. + + + Select the environment you'd like the test to run against. + + + When you're happy with the payload, click **Run test**. Or press the shortcut key: `Cmd + Enter` + on Mac, `Ctrl + Enter` on Windows. + + ## Identifying test runs diff --git a/docs/documentation/guides/using-integrations.mdx b/docs/documentation/guides/using-integrations.mdx index b2a3dc8bf..45833102e 100644 --- a/docs/documentation/guides/using-integrations.mdx +++ b/docs/documentation/guides/using-integrations.mdx @@ -1,7 +1,7 @@ --- -title: "Integrations Overview" -description: "How to use Trigger.dev Integrations" +title: "Integrations: Overview" sidebarTitle: "Overview" +description: "How to use Trigger.dev Integrations" --- diff --git a/docs/documentation/introduction.mdx b/docs/documentation/introduction.mdx index 69527d8ce..a9f68d1bf 100644 --- a/docs/documentation/introduction.mdx +++ b/docs/documentation/introduction.mdx @@ -1,5 +1,6 @@ --- -title: Introduction +title: "Getting Started: Introduction" +sidebarTitle: "Introduction" description: "Welcome to the Trigger.dev documentation." --- diff --git a/docs/documentation/quickstarts/introduction.mdx b/docs/documentation/quickstarts/introduction.mdx index 48891d398..e5e31232f 100644 --- a/docs/documentation/quickstarts/introduction.mdx +++ b/docs/documentation/quickstarts/introduction.mdx @@ -1,5 +1,5 @@ --- -title: "Introduction" +title: "Quick Starts: Introduction" sidebarTitle: "Introduction" --- @@ -8,261 +8,19 @@ sidebarTitle: "Introduction" ## Select a framework to get started… - - - - - - - - - - - - - - - - -} - href="/documentation/quickstarts/nextjs" - -> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -} href="/documentation/quickstarts/remix"> - - - - - - - - - - - -} href="/documentation/quickstarts/express"> - - - - - - - - - - - - - - - - - - - - -} href="/documentation/quickstarts/redwood"> - - - - - - - - - - - - - - - - - - - - - - - - - - -} href="/documentation/quickstarts/astro"> - - - - - - - - - - - - -} href="/documentation/quickstarts/nuxt"> - - - - - - - - - - - - - - -} href="/documentation/quickstarts/sveltkit"> - - - - - - - - - - - - - - - - - - -} href="/documentation/quickstarts/fastify"/> - + + + + + + + + + ## Or quickly setup Trigger.dev with… - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - } - href="/documentation/quickstarts/supabase" - -> - + diff --git a/docs/documentation/quickstarts/nestjs.mdx b/docs/documentation/quickstarts/nestjs.mdx new file mode 100644 index 000000000..46bc1b03b --- /dev/null +++ b/docs/documentation/quickstarts/nestjs.mdx @@ -0,0 +1,7 @@ +--- +title: "NestJS Quick Start" +sidebarTitle: "NestJS" +description: "Start creating Jobs in 5 minutes in your NestJS project." +--- + + diff --git a/docs/images/test-annotated.png b/docs/images/test-annotated.png index a224e3fe4..d34b50d33 100644 Binary files a/docs/images/test-annotated.png and b/docs/images/test-annotated.png differ diff --git a/docs/integrations/apis/github-tasks.mdx b/docs/integrations/apis/github-tasks.mdx index 410dc1170..2dbc2e33d 100644 --- a/docs/integrations/apis/github-tasks.mdx +++ b/docs/integrations/apis/github-tasks.mdx @@ -1,54 +1,263 @@ --- -title: Tasks +title: GitHub Tasks +sidebarTitle: Tasks +--- + +Tasks are executed after the job is triggered and are the main building blocks of a job. You can string together as many tasks as you want. + --- ## All tasks -| Function Name | Description | -| -------------------------------- | ----------------------------------------------------------- | -| `createIssue` | Creates a new issue in a repository. | -| `addIssueAssignees` | Adds assignees to an existing issue. | -| `addIssueLabels` | Adds labels to an existing issue. | -| `createIssueComment` | Creates a new comment on an existing issue. | -| `getRepo` | Retrieves information about a repository. | -| `createIssueCommentWithReaction` | Creates a new comment on an existing issue with a reaction. | -| `addIssueCommentReaction` | Adds a reaction to an existing issue comment. | -| `updateWebhook` | Updates an existing webhook. | -| `createWebhook` | Creates a new webhook. | -| `listWebhooks` | Lists the webhooks for a repository. | -| `updateOrgWebhook` | Updates an existing webhook for an organization. | -| `createOrgWebhook` | Creates a new webhook for an organization. | -| `listOrgWebhooks` | Lists the webhooks for an organization. | +### `createIssue` -## Usage +Creates a new issue in a repository. [Official GitHub docs](https://docs.github.com/en/free-pro-team@latest/rest/issues/issues?apiVersion=2022-11-28#create-an-issue). + +```ts example.ts +await io.github.createIssue("create issue", { + owner: "", // the name of the owner of the repository + repo: "", // the name of the repository + title: "", // the title of the issue + body: "", // the contents of the issue +}); +``` + +### `addIssueAssignees` + +Adds assignees to an existing issue. [Official GitHub docs](https://docs.github.com/en/free-pro-team@latest/rest/issues/assignees?apiVersion=2022-11-28#add-assignees-to-an-issue). + +```ts example.ts +await io.github.addIssueAssignees("add assignee", { + owner: "", // the name of the owner of the repository + repo: "", // the name of the repository + issueNumber: , // the number of the issue + assignees: [""], // the name(s) of the assignee(s) +}); +``` + +### `addIssueLabels` + +Adds labels to an existing issue. [Official GitHub docs](https://docs.github.com/en/free-pro-team@latest/rest/issues/labels?apiVersion=2022-11-28#add-labels-to-an-issue). + +```ts example.ts +await io.github.addIssueLabels("add label", { + owner: "", // the name of the owner of the repository + repo: "", // the name of the repository + issueNumber: , // the number of the issue + labels: [""], // the name(s) of the label(s) +}); +``` + +### `createIssueComment` + +Creates a new comment on an existing issue. [Official GitHub docs](https://docs.github.com/en/free-pro-team@latest/rest/issues/comments?apiVersion=2022-11-28#create-an-issue-comment). + +```ts example.ts +await io.github.createIssueComment("create comment", { + owner: "", // the name of the owner of the repository + repo: "", // the name of the repository + issueNumber: , // the number of the issue + body: "", // the contents of the comment +}); +``` + +### `getRepo` + +Retrieves information about a repository. [Official GitHub docs](https://docs.github.com/en/free-pro-team@latest/rest/repos/repos?apiVersion=2022-11-28#get-a-repository). + +```ts example.ts +const repoInfo = await io.github.getRepo({ + owner: "", // the name of the owner of the repository + repo: "", // the name of the repository +}); +``` + +### `createIssueCommentWithReaction` + +Creates a new comment on an existing issue with a reaction. [Official GitHub docs](https://docs.github.com/en/rest/issues/comments?apiVersion=2022-11-28#create-an-issue-comment). + +```ts example.ts +await io.github.createIssueCommentWithReaction("create comment with reaction", { + owner: "", // the name of the owner of the repository + repo: "", // the name of the repository + issueNumber: , // the number of the issue + body: "", // the contents of the comment + content: "", // the type of reaction +}); +``` + +### `addIssueCommentReaction` + +Adds a reaction to an existing issue comment. [Official GitHub docs](https://docs.github.com/en/free-pro-team@latest/rest/reactions/reactions?apiVersion=2022-11-28#create-reaction-for-an-issue-comment). + +```ts example.ts +await io.github.addIssueCommentReaction("add reaction", { + owner: "", // the name of the owner of the repository + repo: "", // the name of the repository + commentId: , // the id of the specific comment + content: "", // the type of reaction +}); +``` + +### `updateWebhook` + +Updates an existing webhook. [Official GitHub docs](https://docs.github.com/en/rest/webhooks/repos?apiVersion=2022-11-28#update-a-repository-webhook). + +```ts example.ts +await io.github.updateWebhook("update webhook", { + owner: "", // the name of the owner of the repository + repo: "", // the name of the repository + webhookId: , // the unique id of the webhook + config: { + url: "", // the url to which payloads will be delivered + contentType: "json", // the media type used to serialize the payloads + secret: "", // If provided, the secret will be used as the key to generate the HMAC hex digest value for delivery signature headers. + }, +}); +``` + +### `createWebhook` + +Creates a new webhook. [Official GitHub docs](https://docs.github.com/en/rest/webhooks/repos?apiVersion=2022-11-28#create-a-repository-webhook). + +```ts example.ts +await io.github.createWebhook("create webhook", { + owner: "", // the name of the owner of the repository + repo: "", // the name of the repository + config: { + url: "", // the url to which payloads will be delivered + contentType: "json", // the media type used to serialize the payloads + secret: "", // If provided, the secret will be used as the key to generate the HMAC hex digest value for delivery signature headers. + }, + events: [""], // the events for which the webhook will trigger +}); +``` + +### `listWebhooks` + +Lists the webhooks for a repository. [Official GitHub docs](https://docs.github.com/en/rest/webhooks/repos?apiVersion=2022-11-28#list-repository-webhooks). + +```ts example.ts +const webhooks = await io.github.listWebhooks({ + owner: "", // the name of the owner of the repository + repo: "", // the name of the repository +}); +``` + +### `updateOrgWebhook` + +Updates an existing webhook for an organization. [Official GitHub docs](https://docs.github.com/en/rest/orgs/webhooks?apiVersion=2022-11-28#update-an-organization-webhook). + +```ts +await io.github.updateOrgWebhook("update org webhook", { + org: "", // the name of the organization + webhookId: , // the unique id of the webhook + config: { + url: "", // the url to which payloads will be delivered + contentType: "json", // the media type used to serialize the payloads + secret: "", // If provided, the secret will be used as the key to generate the HMAC hex digest value for delivery signature headers. + }, +}); +``` + +### `createOrgWebhook` + +Creates a new webhook for an organization. [Official GitHub docs](https://docs.github.com/en/rest/orgs/webhooks?apiVersion=2022-11-28#create-an-organization-webhook). + +```ts example.ts +await io.github.createOrgWebhook("create org webhook", { + org: "", // the name of the organization + config: { + url: "", // the url to which payloads will be delivered + contentType: "json", // the media type used to serialize the payloads + secret: "", // If provided, the secret will be used as the key to generate the HMAC hex digest value for delivery signature headers. + }, + events: [""], // the events for which the webhook will trigger +}); +``` + +### `listOrgWebhooks` + +Lists the webhooks for an organization. [Official GitHub docs](https://docs.github.com/en/rest/orgs/webhooks?apiVersion=2022-11-28#list-organization-webhooks). + +```ts example.ts +const orgWebhooks = await io.github.listOrgWebhooks({ + org: "", // the name of the organization + per-page: , // the number of webhooks to return per page (max 100) + page: , // Page number of the results to fetch. +}); +``` + +## Example usage + +In this example we'll create a task that adds an assignee and a label to an issue when it's opened. ```ts client.defineJob({ id: "github-integration-on-issue-opened", name: "GitHub Integration - On Issue Opened", - version: "0.1.0", + version: "1.0.0", integrations: { github }, trigger: github.triggers.repo({ event: events.onIssueOpened, - owner: "triggerdotdev", - repo: "empty", + owner: "", + repo: "", }), run: async (payload, io, ctx) => { await io.github.addIssueAssignees("add assignee", { owner: payload.repository.owner.login, repo: payload.repository.name, issueNumber: payload.issue.number, - assignees: ["matt-aitken"], + assignees: [""], }); await io.github.addIssueLabels("add label", { owner: payload.repository.owner.login, repo: payload.repository.name, issueNumber: payload.issue.number, - labels: ["bug"], + labels: [""], }); return { payload, ctx }; }, }); ``` + +## Using the underlying GitHub client + +You can access the [Octokit instance](https://github.com/octokit/octokit.js#octokit-api-client) by using the `runTask` method on the integration: + +```ts +const github = new Github({ + id: "github", +}); + +client.defineJob({ + id: "github-example-1", + name: "GitHub Example 1", + version: "0.1.0", + trigger: eventTrigger({ + name: "github.example", + }), + integrations: { + github, + }, + run: async (payload, io, ctx) => { + const contributors = await io.github.runTask( + "get-contributors", + async (octokit, task) => { + const contributors = await octokit.rest.repos.listContributors({ + owner: "", + repo: "", + }); + + return contributors; + }, + //this is optional, it will appear on the Run page + { name: "List Contributors" } + ); + }, +}); +``` + +Make sure to pass the `idempotencyKey` to the underlying client to ensure that the API call is only executed once. This is only needed for mutating API calls. diff --git a/docs/integrations/apis/github-triggers.mdx b/docs/integrations/apis/github-triggers.mdx index d66e62478..3b21ee59e 100644 --- a/docs/integrations/apis/github-triggers.mdx +++ b/docs/integrations/apis/github-triggers.mdx @@ -1,46 +1,3053 @@ --- -title: Triggers +title: GitHub Triggers & Events +sidebarTitle: Triggers & Events --- -## All triggers +You can use these triggers to start a job when a GitHub event occurs. -| Function Name | Description | -| --------------------- | -------------------------------------------------------------------------------- | -| `onIssue` | When any action is performed on an issue. | -| `onIssueOpened` | When an issue is opened. | -| `onIssueAssigned` | When an issue is assigned. | -| `onIssueComment` | When an issue is commented on. | -| `onStar` | When a repo is starred or unstarred. | -| `onNewStar` | When a repo is starred. | -| `onNewRepository` | When a new repo is created. | -| `onNewBranchOrTag` | When a new branch or tag is created. | -| `onNewBranch` | When a new branch is created. | -| `onPush` | When a push is made to a repo. | -| `onPullRequest` | When activity occurs on a pull request (excluding reviews, issues, or comments). | -| `onPullRequestReview` | When a pull request review has activity. | +--- -## Usage +### Repo + +Repo triggers subscribe to a change in a GitHub repo. ```ts -import { Github, events } from "@trigger.dev/github"; - -const github = new Github({ - id: "github", - token: process.env.GITHUB_TOKEN!, +github.triggers.repo({ + event: events.onIssueOpened, + owner: "triggerdotdev", + repo: "trigger.dev", }); +``` + + + + The event to trigger the job on. + + + The owner of the repo. + + + The name of the repo. + + + + +### Org + +Org triggers subscribe to a change across an entire GitHub org. + +```ts +github.triggers.repo({ + event: events.onIssueOpened, + owner: "triggerdotdev", +}); +``` + +}) + + + + + The event to trigger the job on. + + + The owner of the repo. + + + + +## Events + +### `onIssue` + +When any action is performed on an issue. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issues). + +```ts usage.ts client.defineJob({ - id: "github-integration-on-issue", - name: "GitHub Integration - On Issue", + id: "", + name: "", version: "0.1.0", trigger: github.triggers.repo({ event: events.onIssue, - owner: "triggerdotdev", - repo: "empty", + owner: "", + repo: "", }), run: async (payload, io, ctx) => { - await io.logger.info("This is a simple log info message"); - //do stuff + // Add tasks here }, }); ``` + + +````json +{ + "issue": { + "id": 1754473379, + "url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21", + "body": "This is a *big* problem:\r\n\r\n```\r\nconst foo = \"bar\"\r\n```", + "user": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "state": "open", + "title": "This is a sample issue title #20", + "labels": [], + "locked": false, + "number": 21, + "node_id": "I_kwDOI-yZFc5okyOj", + "assignee": null, + "comments": 0, + "html_url": "https://github.com/ericallam/basic-starter-12k/issues/21", + "assignees": [], + "closed_at": null, + "milestone": null, + "reactions": { + "+1": 0, + "-1": 0, + "url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/reactions", + "eyes": 0, + "heart": 0, + "laugh": 0, + "hooray": 0, + "rocket": 0, + "confused": 0, + "total_count": 0 + }, + "created_at": "2023-06-13T09:42:02Z", + "events_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/events", + "labels_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/labels{/name}", + "updated_at": "2023-06-13T09:42:02Z", + "comments_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/comments", + "state_reason": null, + "timeline_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/timeline", + "repository_url": "https://api.github.com/repos/ericallam/basic-starter-12k", + "active_lock_reason": null, + "author_association": "NONE", + "performed_via_github_app": null + }, + "action": "opened", + "sender": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "repository": { + "id": 602708245, + "url": "https://api.github.com/repos/ericallam/basic-starter-12k", + "fork": false, + "name": "basic-starter-12k", + "size": 0, + "forks": 0, + "owner": { + "id": 534, + "url": "https://api.github.com/users/ericallam", + "type": "User", + "login": "ericallam", + "node_id": "MDQ6VXNlcjUzNA==", + "html_url": "https://github.com/ericallam", + "gists_url": "https://api.github.com/users/ericallam/gists{/gist_id}", + "repos_url": "https://api.github.com/users/ericallam/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/534?v=4", + "events_url": "https://api.github.com/users/ericallam/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/ericallam/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/ericallam/followers", + "following_url": "https://api.github.com/users/ericallam/following{/other_user}", + "organizations_url": "https://api.github.com/users/ericallam/orgs", + "subscriptions_url": "https://api.github.com/users/ericallam/subscriptions", + "received_events_url": "https://api.github.com/users/ericallam/received_events" + }, + "topics": [], + "git_url": "git://github.com/ericallam/basic-starter-12k.git", + "license": null, + "node_id": "R_kgDOI-yZFQ", + "private": false, + "ssh_url": "git@github.com:ericallam/basic-starter-12k.git", + "svn_url": "https://github.com/ericallam/basic-starter-12k", + "archived": false, + "disabled": false, + "has_wiki": true, + "homepage": null, + "html_url": "https://github.com/ericallam/basic-starter-12k", + "keys_url": "https://api.github.com/repos/ericallam/basic-starter-12k/keys{/key_id}", + "language": null, + "tags_url": "https://api.github.com/repos/ericallam/basic-starter-12k/tags", + "watchers": 0, + "blobs_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/blobs{/sha}", + "clone_url": "https://github.com/ericallam/basic-starter-12k.git", + "forks_url": "https://api.github.com/repos/ericallam/basic-starter-12k/forks", + "full_name": "ericallam/basic-starter-12k", + "has_pages": false, + "hooks_url": "https://api.github.com/repos/ericallam/basic-starter-12k/hooks", + "pulls_url": "https://api.github.com/repos/ericallam/basic-starter-12k/pulls{/number}", + "pushed_at": "2023-02-16T19:25:20Z", + "teams_url": "https://api.github.com/repos/ericallam/basic-starter-12k/teams", + "trees_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/trees{/sha}", + "created_at": "2023-02-16T19:25:19Z", + "events_url": "https://api.github.com/repos/ericallam/basic-starter-12k/events", + "has_issues": true, + "issues_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues{/number}", + "labels_url": "https://api.github.com/repos/ericallam/basic-starter-12k/labels{/name}", + "merges_url": "https://api.github.com/repos/ericallam/basic-starter-12k/merges", + "mirror_url": null, + "updated_at": "2023-02-16T19:25:19Z", + "visibility": "public", + "archive_url": "https://api.github.com/repos/ericallam/basic-starter-12k/{archive_format}{/ref}", + "commits_url": "https://api.github.com/repos/ericallam/basic-starter-12k/commits{/sha}", + "compare_url": "https://api.github.com/repos/ericallam/basic-starter-12k/compare/{base}...{head}", + "description": null, + "forks_count": 0, + "is_template": false, + "open_issues": 21, + "branches_url": "https://api.github.com/repos/ericallam/basic-starter-12k/branches{/branch}", + "comments_url": "https://api.github.com/repos/ericallam/basic-starter-12k/comments{/number}", + "contents_url": "https://api.github.com/repos/ericallam/basic-starter-12k/contents/{+path}", + "git_refs_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/tags{/sha}", + "has_projects": true, + "releases_url": "https://api.github.com/repos/ericallam/basic-starter-12k/releases{/id}", + "statuses_url": "https://api.github.com/repos/ericallam/basic-starter-12k/statuses/{sha}", + "allow_forking": true, + "assignees_url": "https://api.github.com/repos/ericallam/basic-starter-12k/assignees{/user}", + "downloads_url": "https://api.github.com/repos/ericallam/basic-starter-12k/downloads", + "has_downloads": true, + "languages_url": "https://api.github.com/repos/ericallam/basic-starter-12k/languages", + "default_branch": "main", + "milestones_url": "https://api.github.com/repos/ericallam/basic-starter-12k/milestones{/number}", + "stargazers_url": "https://api.github.com/repos/ericallam/basic-starter-12k/stargazers", + "watchers_count": 0, + "deployments_url": "https://api.github.com/repos/ericallam/basic-starter-12k/deployments", + "git_commits_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/commits{/sha}", + "has_discussions": false, + "subscribers_url": "https://api.github.com/repos/ericallam/basic-starter-12k/subscribers", + "contributors_url": "https://api.github.com/repos/ericallam/basic-starter-12k/contributors", + "issue_events_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/events{/number}", + "stargazers_count": 0, + "subscription_url": "https://api.github.com/repos/ericallam/basic-starter-12k/subscription", + "collaborators_url": "https://api.github.com/repos/ericallam/basic-starter-12k/collaborators{/collaborator}", + "issue_comment_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/comments{/number}", + "notifications_url": "https://api.github.com/repos/ericallam/basic-starter-12k/notifications{?since,all,participating}", + "open_issues_count": 21, + "web_commit_signoff_required": false + } +} +```` + + +### `onIssueOpened` + +Occurs when an issue is opened. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issues). + +```ts usage.ts +client.defineJob({ + id: "", + name: "", + version: "0.1.0", + trigger: github.triggers.org({ + event: events.onIssueOpened, + owner: "", + }), + run: async (payload, io, ctx) => { + // Add tasks here + }, +}); +``` + + +````json +{ + "issue": { + "id": 1754473379, + "url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21", + "body": "This is a *big* problem:\r\n\r\n```\r\nconst foo = \"bar\"\r\n```", + "user": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "state": "open", + "title": "This is a sample issue title #20", + "labels": [], + "locked": false, + "number": 21, + "node_id": "I_kwDOI-yZFc5okyOj", + "assignee": null, + "comments": 0, + "html_url": "https://github.com/ericallam/basic-starter-12k/issues/21", + "assignees": [], + "closed_at": null, + "milestone": null, + "reactions": { + "+1": 0, + "-1": 0, + "url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/reactions", + "eyes": 0, + "heart": 0, + "laugh": 0, + "hooray": 0, + "rocket": 0, + "confused": 0, + "total_count": 0 + }, + "created_at": "2023-06-13T09:42:02Z", + "events_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/events", + "labels_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/labels{/name}", + "updated_at": "2023-06-13T09:42:02Z", + "comments_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/comments", + "state_reason": null, + "timeline_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/21/timeline", + "repository_url": "https://api.github.com/repos/ericallam/basic-starter-12k", + "active_lock_reason": null, + "author_association": "NONE", + "performed_via_github_app": null + }, + "action": "opened", + "sender": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "repository": { + "id": 602708245, + "url": "https://api.github.com/repos/ericallam/basic-starter-12k", + "fork": false, + "name": "basic-starter-12k", + "size": 0, + "forks": 0, + "owner": { + "id": 534, + "url": "https://api.github.com/users/ericallam", + "type": "User", + "login": "ericallam", + "node_id": "MDQ6VXNlcjUzNA==", + "html_url": "https://github.com/ericallam", + "gists_url": "https://api.github.com/users/ericallam/gists{/gist_id}", + "repos_url": "https://api.github.com/users/ericallam/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/534?v=4", + "events_url": "https://api.github.com/users/ericallam/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/ericallam/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/ericallam/followers", + "following_url": "https://api.github.com/users/ericallam/following{/other_user}", + "organizations_url": "https://api.github.com/users/ericallam/orgs", + "subscriptions_url": "https://api.github.com/users/ericallam/subscriptions", + "received_events_url": "https://api.github.com/users/ericallam/received_events" + }, + "topics": [], + "git_url": "git://github.com/ericallam/basic-starter-12k.git", + "license": null, + "node_id": "R_kgDOI-yZFQ", + "private": false, + "ssh_url": "git@github.com:ericallam/basic-starter-12k.git", + "svn_url": "https://github.com/ericallam/basic-starter-12k", + "archived": false, + "disabled": false, + "has_wiki": true, + "homepage": null, + "html_url": "https://github.com/ericallam/basic-starter-12k", + "keys_url": "https://api.github.com/repos/ericallam/basic-starter-12k/keys{/key_id}", + "language": null, + "tags_url": "https://api.github.com/repos/ericallam/basic-starter-12k/tags", + "watchers": 0, + "blobs_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/blobs{/sha}", + "clone_url": "https://github.com/ericallam/basic-starter-12k.git", + "forks_url": "https://api.github.com/repos/ericallam/basic-starter-12k/forks", + "full_name": "ericallam/basic-starter-12k", + "has_pages": false, + "hooks_url": "https://api.github.com/repos/ericallam/basic-starter-12k/hooks", + "pulls_url": "https://api.github.com/repos/ericallam/basic-starter-12k/pulls{/number}", + "pushed_at": "2023-02-16T19:25:20Z", + "teams_url": "https://api.github.com/repos/ericallam/basic-starter-12k/teams", + "trees_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/trees{/sha}", + "created_at": "2023-02-16T19:25:19Z", + "events_url": "https://api.github.com/repos/ericallam/basic-starter-12k/events", + "has_issues": true, + "issues_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues{/number}", + "labels_url": "https://api.github.com/repos/ericallam/basic-starter-12k/labels{/name}", + "merges_url": "https://api.github.com/repos/ericallam/basic-starter-12k/merges", + "mirror_url": null, + "updated_at": "2023-02-16T19:25:19Z", + "visibility": "public", + "archive_url": "https://api.github.com/repos/ericallam/basic-starter-12k/{archive_format}{/ref}", + "commits_url": "https://api.github.com/repos/ericallam/basic-starter-12k/commits{/sha}", + "compare_url": "https://api.github.com/repos/ericallam/basic-starter-12k/compare/{base}...{head}", + "description": null, + "forks_count": 0, + "is_template": false, + "open_issues": 21, + "branches_url": "https://api.github.com/repos/ericallam/basic-starter-12k/branches{/branch}", + "comments_url": "https://api.github.com/repos/ericallam/basic-starter-12k/comments{/number}", + "contents_url": "https://api.github.com/repos/ericallam/basic-starter-12k/contents/{+path}", + "git_refs_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/tags{/sha}", + "has_projects": true, + "releases_url": "https://api.github.com/repos/ericallam/basic-starter-12k/releases{/id}", + "statuses_url": "https://api.github.com/repos/ericallam/basic-starter-12k/statuses/{sha}", + "allow_forking": true, + "assignees_url": "https://api.github.com/repos/ericallam/basic-starter-12k/assignees{/user}", + "downloads_url": "https://api.github.com/repos/ericallam/basic-starter-12k/downloads", + "has_downloads": true, + "languages_url": "https://api.github.com/repos/ericallam/basic-starter-12k/languages", + "default_branch": "main", + "milestones_url": "https://api.github.com/repos/ericallam/basic-starter-12k/milestones{/number}", + "stargazers_url": "https://api.github.com/repos/ericallam/basic-starter-12k/stargazers", + "watchers_count": 0, + "deployments_url": "https://api.github.com/repos/ericallam/basic-starter-12k/deployments", + "git_commits_url": "https://api.github.com/repos/ericallam/basic-starter-12k/git/commits{/sha}", + "has_discussions": false, + "subscribers_url": "https://api.github.com/repos/ericallam/basic-starter-12k/subscribers", + "contributors_url": "https://api.github.com/repos/ericallam/basic-starter-12k/contributors", + "issue_events_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/events{/number}", + "stargazers_count": 0, + "subscription_url": "https://api.github.com/repos/ericallam/basic-starter-12k/subscription", + "collaborators_url": "https://api.github.com/repos/ericallam/basic-starter-12k/collaborators{/collaborator}", + "issue_comment_url": "https://api.github.com/repos/ericallam/basic-starter-12k/issues/comments{/number}", + "notifications_url": "https://api.github.com/repos/ericallam/basic-starter-12k/notifications{?since,all,participating}", + "open_issues_count": 21, + "web_commit_signoff_required": false + } +} +```` + + +### `onIssueAssigned` + +When an issue is assigned. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issues). + +```ts usage.ts +client.defineJob({ + id: "", + name: "", + version: "0.1.0", + trigger: github.triggers.repo({ + event: events.onAssigned, + owner: "", + repo: "", + }), + run: async (payload, io, ctx) => { + // Add tasks here + }, +}); +``` + + +```json +{ + "id": "issue.assigned", + "name": "Issue assigned", + "payload": { + "issue": { + "id": 1767861922, + "url": "https://api.github.com/repos/triggerdotdev/empty/issues/4", + "body": "This is the bod", + "user": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "state": "open", + "title": "Fourth time lucky?", + "labels": [], + "locked": false, + "number": 4, + "node_id": "I_kwDOJyTwbc5pX26i", + "assignee": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM5OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "comments": 1, + "html_url": "https://github.com/triggerdotdev/empty/issues/4", + "assignees": [ + { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + } + ], + "closed_at": null, + "milestone": null, + "reactions": { + "+1": 0, + "-1": 0, + "url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/reactions", + "eyes": 0, + "heart": 0, + "laugh": 0, + "hooray": 0, + "rocket": 0, + "confused": 0, + "total_count": 0 + }, + "created_at": "2023-06-21T15:24:20Z", + "events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/events", + "labels_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/labels{/name}", + "updated_at": "2023-06-21T15:36:53Z", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/comments", + "state_reason": null, + "timeline_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/timeline", + "repository_url": "https://api.github.com/repos/triggerdotdev/empty", + "active_lock_reason": null, + "author_association": "MEMBER", + "performed_via_github_app": null + }, + "action": "assigned", + "sender": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "assignee": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM5OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "repository": { + "id": 656732269, + "url": "https://api.github.com/repos/triggerdotdev/empty", + "fork": false, + "name": "empty", + "size": 0, + "forks": 0, + "owner": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "type": "Organization", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "topics": [], + "git_url": "git://github.com/triggerdotdev/empty.git", + "license": null, + "node_id": "R_kgDOJyTwbQ", + "private": false, + "ssh_url": "git@github.com:triggerdotdev/empty.git", + "svn_url": "https://github.com/triggerdotdev/empty", + "archived": false, + "disabled": false, + "has_wiki": true, + "homepage": null, + "html_url": "https://github.com/triggerdotdev/empty", + "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}", + "language": null, + "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags", + "watchers": 0, + "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}", + "clone_url": "https://github.com/triggerdotdev/empty.git", + "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks", + "full_name": "triggerdotdev/empty", + "has_pages": false, + "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks", + "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}", + "pushed_at": "2023-06-21T14:21:08Z", + "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams", + "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}", + "created_at": "2023-06-21T14:21:07Z", + "events_url": "https://api.github.com/repos/triggerdotdev/empty/events", + "has_issues": true, + "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}", + "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}", + "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges", + "mirror_url": null, + "updated_at": "2023-06-21T14:21:08Z", + "visibility": "public", + "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}", + "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}", + "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}", + "description": "An empty repo that can be used to test the @trigger.dev/github integration", + "forks_count": 0, + "is_template": false, + "open_issues": 4, + "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}", + "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}", + "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}", + "has_projects": true, + "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}", + "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}", + "allow_forking": true, + "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}", + "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads", + "has_downloads": true, + "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages", + "default_branch": "main", + "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}", + "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers", + "watchers_count": 0, + "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments", + "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}", + "has_discussions": false, + "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers", + "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors", + "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}", + "stargazers_count": 0, + "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription", + "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}", + "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}", + "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}", + "open_issues_count": 4, + "web_commit_signoff_required": false + }, + "organization": { + "id": 95297378, + "url": "https://api.github.com/orgs/triggerdotdev", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks", + "repos_url": "https://api.github.com/orgs/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/orgs/triggerdotdev/events", + "issues_url": "https://api.github.com/orgs/triggerdotdev/issues", + "description": "", + "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}", + "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}" + } + } +} +``` + + +### `onIssueComment` + +When an issue is commented on. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issues). + +```ts usage.ts +client.defineJob({ + id: "", + name: "", + version: "0.1.0", + trigger: github.triggers.repo({ + event: events.onIssueComment, + owner: "", + repo: "", + }), + run: async (payload, io, ctx) => { + // Add tasks here + }, +}); +``` + + +```json +{ + "payload": { + "issue": { + "id": 1767861922, + "url": "https://api.github.com/repos/triggerdotdev/empty/issues/4", + "body": "This is the bod", + "user": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "state": "open", + "title": "Fourth time lucky?", + "labels": [], + "locked": false, + "number": 4, + "node_id": "I_kwDOJyTwbc5pX26i", + "assignee": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM5OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "comments": 2, + "html_url": "https://github.com/triggerdotdev/empty/issues/4", + "assignees": [ + { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + } + ], + "closed_at": null, + "milestone": null, + "reactions": { + "+1": 0, + "-1": 0, + "url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/reactions", + "eyes": 0, + "heart": 0, + "laugh": 0, + "hooray": 0, + "rocket": 0, + "confused": 0, + "total_count": 0 + }, + "created_at": "2023-06-21T15:24:20Z", + "events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/events", + "labels_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/labels{/name}", + "updated_at": "2023-06-21T16:02:28Z", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/comments", + "state_reason": null, + "timeline_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4/timeline", + "repository_url": "https://api.github.com/repos/triggerdotdev/empty", + "active_lock_reason": null, + "author_association": "MEMBER", + "performed_via_github_app": null + }, + "action": "created", + "sender": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "comment": { + "id": 1601114728, + "url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments/1601114728", + "body": "This is a short comment with short code snippet:\r\n\r\n```\r\nconst rick = \"astley\";\r\n```", + "user": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "node_id": "IC_kwDOJyTwbc5fbxJo", + "html_url": "https://github.com/triggerdotdev/empty/issues/4#issuecomment-1601114728", + "issue_url": "https://api.github.com/repos/triggerdotdev/empty/issues/4", + "reactions": { + "+1": 0, + "-1": 0, + "url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments/1601114728/reactions", + "eyes": 0, + "heart": 0, + "laugh": 0, + "hooray": 0, + "rocket": 0, + "confused": 0, + "total_count": 0 + }, + "created_at": "2023-06-21T16:02:27Z", + "updated_at": "2023-06-21T16:02:27Z", + "author_association": "MEMBER", + "performed_via_github_app": null + }, + "repository": { + "id": 656732269, + "url": "https://api.github.com/repos/triggerdotdev/empty", + "fork": false, + "name": "empty", + "size": 0, + "forks": 0, + "owner": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "type": "Organization", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "topics": [], + "git_url": "git://github.com/triggerdotdev/empty.git", + "license": null, + "node_id": "R_kgDOJyTwbQ", + "private": false, + "ssh_url": "git@github.com:triggerdotdev/empty.git", + "svn_url": "https://github.com/triggerdotdev/empty", + "archived": false, + "disabled": false, + "has_wiki": true, + "homepage": null, + "html_url": "https://github.com/triggerdotdev/empty", + "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}", + "language": null, + "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags", + "watchers": 0, + "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}", + "clone_url": "https://github.com/triggerdotdev/empty.git", + "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks", + "full_name": "triggerdotdev/empty", + "has_pages": false, + "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks", + "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}", + "pushed_at": "2023-06-21T14:21:08Z", + "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams", + "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}", + "created_at": "2023-06-21T14:21:07Z", + "events_url": "https://api.github.com/repos/triggerdotdev/empty/events", + "has_issues": true, + "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}", + "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}", + "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges", + "mirror_url": null, + "updated_at": "2023-06-21T14:21:08Z", + "visibility": "public", + "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}", + "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}", + "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}", + "description": "An empty repo that can be used to test the @trigger.dev/github integration", + "forks_count": 0, + "is_template": false, + "open_issues": 4, + "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}", + "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}", + "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}", + "has_projects": true, + "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}", + "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}", + "allow_forking": true, + "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}", + "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads", + "has_downloads": true, + "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages", + "default_branch": "main", + "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}", + "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers", + "watchers_count": 0, + "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments", + "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}", + "has_discussions": false, + "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers", + "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors", + "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}", + "stargazers_count": 0, + "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription", + "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}", + "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}", + "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}", + "open_issues_count": 4, + "web_commit_signoff_required": false + }, + "organization": { + "id": 95297378, + "url": "https://api.github.com/orgs/triggerdotdev", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks", + "repos_url": "https://api.github.com/orgs/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/orgs/triggerdotdev/events", + "issues_url": "https://api.github.com/orgs/triggerdotdev/issues", + "description": "", + "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}", + "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}" + } + } +} +``` + + +### `onStar` + +When a repo is starred or unstarred. [Official GitHub docs](https://docs.github.com/en/rest/activity/starring?apiVersion=2022-11-28#list-stargazers). + +```ts usage.ts +client.defineJob({ + id: "", + name: "", + version: "0.1.0", + trigger: github.triggers.repo({ + event: events.onStar, + owner: "", + repo: "", + }), + run: async (payload, io, ctx) => { + // Add tasks here + }, +}); +``` + + +```json +{ + "payload": { + "action": "created", + "sender": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "repository": { + "id": 656732269, + "url": "https://api.github.com/repos/triggerdotdev/empty", + "fork": false, + "name": "empty", + "size": 0, + "forks": 0, + "owner": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "type": "Organization", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "topics": [], + "git_url": "git://github.com/triggerdotdev/empty.git", + "license": null, + "node_id": "R_kgDOJyTwbQ", + "private": false, + "ssh_url": "git@github.com:triggerdotdev/empty.git", + "svn_url": "https://github.com/triggerdotdev/empty", + "archived": false, + "disabled": false, + "has_wiki": true, + "homepage": null, + "html_url": "https://github.com/triggerdotdev/empty", + "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}", + "language": null, + "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags", + "watchers": 1, + "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}", + "clone_url": "https://github.com/triggerdotdev/empty.git", + "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks", + "full_name": "triggerdotdev/empty", + "has_pages": false, + "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks", + "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}", + "pushed_at": "2023-06-21T14:21:08Z", + "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams", + "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}", + "created_at": "2023-06-21T14:21:07Z", + "events_url": "https://api.github.com/repos/triggerdotdev/empty/events", + "has_issues": true, + "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}", + "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}", + "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges", + "mirror_url": null, + "updated_at": "2023-06-21T17:22:03Z", + "visibility": "public", + "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}", + "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}", + "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}", + "description": "An empty repo that can be used to test the @trigger.dev/github integration", + "forks_count": 0, + "is_template": false, + "open_issues": 4, + "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}", + "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}", + "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}", + "has_projects": true, + "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}", + "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}", + "allow_forking": true, + "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}", + "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads", + "has_downloads": true, + "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages", + "default_branch": "main", + "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}", + "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers", + "watchers_count": 1, + "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments", + "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}", + "has_discussions": false, + "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers", + "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors", + "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}", + "stargazers_count": 1, + "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription", + "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}", + "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}", + "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}", + "open_issues_count": 4, + "web_commit_signoff_required": false + }, + "starred_at": "2023-06-21T17:22:03Z", + "organization": { + "id": 95297378, + "url": "https://api.github.com/orgs/triggerdotdev", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks", + "repos_url": "https://api.github.com/orgs/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/orgs/triggerdotdev/events", + "issues_url": "https://api.github.com/orgs/triggerdotdev/issues", + "description": "", + "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}", + "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}" + } + } +} +``` + + +### `onNewStar` + +When a repo is starred. [Official GitHub docs](https://docs.github.com/en/rest/activity/starring?apiVersion=2022-11-28#list-stargazers). + +```ts usage.ts +client.defineJob({ + id: "", + name: "", + version: "0.1.0", + trigger: github.triggers.repo({ + event: events.onNewStar, + owner: "", + repo: "", + }), + run: async (payload, io, ctx) => { + // Add tasks here + }, +}); +``` + + +```json +{ + "payload": { + "action": "created", + "sender": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "repository": { + "id": 656732269, + "url": "https://api.github.com/repos/triggerdotdev/empty", + "fork": false, + "name": "empty", + "size": 0, + "forks": 0, + "owner": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "type": "Organization", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "topics": [], + "git_url": "git://github.com/triggerdotdev/empty.git", + "license": null, + "node_id": "R_kgDOJyTwbQ", + "private": false, + "ssh_url": "git@github.com:triggerdotdev/empty.git", + "svn_url": "https://github.com/triggerdotdev/empty", + "archived": false, + "disabled": false, + "has_wiki": true, + "homepage": null, + "html_url": "https://github.com/triggerdotdev/empty", + "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}", + "language": null, + "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags", + "watchers": 1, + "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}", + "clone_url": "https://github.com/triggerdotdev/empty.git", + "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks", + "full_name": "triggerdotdev/empty", + "has_pages": false, + "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks", + "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}", + "pushed_at": "2023-06-21T14:21:08Z", + "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams", + "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}", + "created_at": "2023-06-21T14:21:07Z", + "events_url": "https://api.github.com/repos/triggerdotdev/empty/events", + "has_issues": true, + "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}", + "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}", + "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges", + "mirror_url": null, + "updated_at": "2023-06-21T17:22:03Z", + "visibility": "public", + "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}", + "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}", + "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}", + "description": "An empty repo that can be used to test the @trigger.dev/github integration", + "forks_count": 0, + "is_template": false, + "open_issues": 4, + "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}", + "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}", + "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}", + "has_projects": true, + "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}", + "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}", + "allow_forking": true, + "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}", + "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads", + "has_downloads": true, + "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages", + "default_branch": "main", + "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}", + "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers", + "watchers_count": 1, + "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments", + "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}", + "has_discussions": false, + "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers", + "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors", + "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}", + "stargazers_count": 1, + "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription", + "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}", + "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}", + "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}", + "open_issues_count": 4, + "web_commit_signoff_required": false + }, + "starred_at": "2023-06-21T17:22:03Z", + "organization": { + "id": 95297378, + "url": "https://api.github.com/orgs/triggerdotdev", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks", + "repos_url": "https://api.github.com/orgs/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/orgs/triggerdotdev/events", + "issues_url": "https://api.github.com/orgs/triggerdotdev/issues", + "description": "", + "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}", + "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}" + } + } +} +``` + + +### `onNewRepository` + +When a new repo is created. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#repository). + +```ts usage.ts +client.defineJob({ + id: "", + name: "", + version: "0.1.0", + trigger: github.triggers.repo({ + event: events.onNewRepository, + owner: "", + repo: "", + }), + run: async (payload, io, ctx) => { + // Add tasks here + }, +}); +``` + +### `onNewBranchOrTag` + +When a new branch or tag is created. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push). + +```ts usage.ts +client.defineJob({ + id: "", + name: "", + version: "0.1.0", + trigger: github.triggers.repo({ + event: events.onNewBranchOrTag, + owner: "", + repo: "", + }), + run: async (payload, io, ctx) => { + // Add tasks here + }, +}); +``` + + +```json +{ + "ref": "test", + "sender": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "ref_type": "branch", + "repository": { + "id": 656732269, + "url": "https://api.github.com/repos/triggerdotdev/empty", + "fork": false, + "name": "empty", + "size": 0, + "forks": 0, + "owner": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "type": "Organization", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "topics": [], + "git_url": "git://github.com/triggerdotdev/empty.git", + "license": null, + "node_id": "R_kgDOJyTwbQ", + "private": false, + "ssh_url": "git@github.com:triggerdotdev/empty.git", + "svn_url": "https://github.com/triggerdotdev/empty", + "archived": false, + "disabled": false, + "has_wiki": true, + "homepage": null, + "html_url": "https://github.com/triggerdotdev/empty", + "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}", + "language": null, + "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags", + "watchers": 0, + "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}", + "clone_url": "https://github.com/triggerdotdev/empty.git", + "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks", + "full_name": "triggerdotdev/empty", + "has_pages": false, + "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks", + "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}", + "pushed_at": "2023-06-21T17:54:25Z", + "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams", + "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}", + "created_at": "2023-06-21T14:21:07Z", + "events_url": "https://api.github.com/repos/triggerdotdev/empty/events", + "has_issues": true, + "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}", + "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}", + "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges", + "mirror_url": null, + "updated_at": "2023-06-21T17:23:26Z", + "visibility": "public", + "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}", + "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}", + "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}", + "description": "An empty repo that can be used to test the @trigger.dev/github integration", + "forks_count": 0, + "is_template": false, + "open_issues": 4, + "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}", + "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}", + "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}", + "has_projects": true, + "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}", + "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}", + "allow_forking": true, + "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}", + "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads", + "has_downloads": true, + "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages", + "default_branch": "main", + "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}", + "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers", + "watchers_count": 0, + "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments", + "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}", + "has_discussions": false, + "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers", + "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors", + "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}", + "stargazers_count": 0, + "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription", + "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}", + "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}", + "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}", + "open_issues_count": 4, + "web_commit_signoff_required": false + }, + "description": "An empty repo that can be used to test the @trigger.dev/github integration", + "pusher_type": "user", + "organization": { + "id": 95297378, + "url": "https://api.github.com/orgs/triggerdotdev", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks", + "repos_url": "https://api.github.com/orgs/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/orgs/triggerdotdev/events", + "issues_url": "https://api.github.com/orgs/triggerdotdev/issues", + "description": "", + "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}", + "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}" + }, + "master_branch": "main" +} +``` + + +### `onNewBranch` + +When a new branch is created. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push). + +```ts usage.ts +client.defineJob({ + id: "", + name: "", + version: "0.1.0", + trigger: github.triggers.repo({ + event: events.onNewBranch, + owner: "", + repo: "", + }), + run: async (payload, io, ctx) => { + // Add tasks here + }, +}); +``` + + +```json +{ + "ref": "test", + "sender": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "ref_type": "branch", + "repository": { + "id": 656732269, + "url": "https://api.github.com/repos/triggerdotdev/empty", + "fork": false, + "name": "empty", + "size": 0, + "forks": 0, + "owner": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "type": "Organization", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "topics": [], + "git_url": "git://github.com/triggerdotdev/empty.git", + "license": null, + "node_id": "R_kgDOJyTwbQ", + "private": false, + "ssh_url": "git@github.com:triggerdotdev/empty.git", + "svn_url": "https://github.com/triggerdotdev/empty", + "archived": false, + "disabled": false, + "has_wiki": true, + "homepage": null, + "html_url": "https://github.com/triggerdotdev/empty", + "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}", + "language": null, + "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags", + "watchers": 0, + "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}", + "clone_url": "https://github.com/triggerdotdev/empty.git", + "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks", + "full_name": "triggerdotdev/empty", + "has_pages": false, + "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks", + "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}", + "pushed_at": "2023-06-21T17:54:25Z", + "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams", + "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}", + "created_at": "2023-06-21T14:21:07Z", + "events_url": "https://api.github.com/repos/triggerdotdev/empty/events", + "has_issues": true, + "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}", + "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}", + "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges", + "mirror_url": null, + "updated_at": "2023-06-21T17:23:26Z", + "visibility": "public", + "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}", + "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}", + "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}", + "description": "An empty repo that can be used to test the @trigger.dev/github integration", + "forks_count": 0, + "is_template": false, + "open_issues": 4, + "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}", + "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}", + "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}", + "has_projects": true, + "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}", + "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}", + "allow_forking": true, + "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}", + "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads", + "has_downloads": true, + "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages", + "default_branch": "main", + "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}", + "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers", + "watchers_count": 0, + "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments", + "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}", + "has_discussions": false, + "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers", + "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors", + "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}", + "stargazers_count": 0, + "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription", + "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}", + "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}", + "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}", + "open_issues_count": 4, + "web_commit_signoff_required": false + }, + "description": "An empty repo that can be used to test the @trigger.dev/github integration", + "pusher_type": "user", + "organization": { + "id": 95297378, + "url": "https://api.github.com/orgs/triggerdotdev", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks", + "repos_url": "https://api.github.com/orgs/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/orgs/triggerdotdev/events", + "issues_url": "https://api.github.com/orgs/triggerdotdev/issues", + "description": "", + "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}", + "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}" + }, + "master_branch": "main" +} +``` + + +### `onPush` + +When a push is made to a repo. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push). + +```ts usage.ts +client.defineJob({ + id: "", + name: "", + version: "0.1.0", + trigger: github.triggers.repo({ + event: events.onPush, + owner: "", + repo: "", + }), + run: async (payload, io, ctx) => { + // Add tasks here + }, +}); +``` + + +```json +{ + "ref": "refs/heads/main", + "after": "1ca6a91f4a1385e448364a5de1960a3da60d3fe2", + "before": "7f0b6655803858ac09ae05354a679d6dad03120c", + "forced": false, + "pusher": { + "name": "matt-aitken", + "email": "matt@mattaitken.com" + }, + "sender": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "commits": [ + { + "id": "1ca6a91f4a1385e448364a5de1960a3da60d3fe2", + "url": "https://github.com/triggerdotdev/empty/commit/1ca6a91f4a1385e448364a5de1960a3da60d3fe2", + "added": [], + "author": { + "name": "Matt Aitken", + "email": "matt@mattaitken.com", + "username": "matt-aitken" + }, + "message": "Updated the readme", + "removed": [], + "tree_id": "3a584b2cae2fe34a195e1fe437cc62031ddff447", + "distinct": true, + "modified": ["README.md"], + "committer": { + "name": "Matt Aitken", + "email": "matt@mattaitken.com", + "username": "matt-aitken" + }, + "timestamp": "2023-06-21T19:27:16+01:00" + } + ], + "compare": "https://github.com/triggerdotdev/empty/compare/7f0b66558038...1ca6a91f4a13", + "created": false, + "deleted": false, + "base_ref": null, + "repository": { + "id": 656732269, + "url": "https://github.com/triggerdotdev/empty", + "fork": false, + "name": "empty", + "size": 0, + "forks": 0, + "owner": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "name": "triggerdotdev", + "type": "Organization", + "email": "hello@trigger.dev", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "topics": [], + "git_url": "git://github.com/triggerdotdev/empty.git", + "license": null, + "node_id": "R_kgDOJyTwbQ", + "private": false, + "ssh_url": "git@github.com:triggerdotdev/empty.git", + "svn_url": "https://github.com/triggerdotdev/empty", + "archived": false, + "disabled": false, + "has_wiki": true, + "homepage": null, + "html_url": "https://github.com/triggerdotdev/empty", + "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}", + "language": null, + "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags", + "watchers": 0, + "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}", + "clone_url": "https://github.com/triggerdotdev/empty.git", + "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks", + "full_name": "triggerdotdev/empty", + "has_pages": false, + "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks", + "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}", + "pushed_at": 1687372039, + "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams", + "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}", + "created_at": 1687357267, + "events_url": "https://api.github.com/repos/triggerdotdev/empty/events", + "has_issues": true, + "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}", + "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}", + "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges", + "mirror_url": null, + "stargazers": 0, + "updated_at": "2023-06-21T17:23:26Z", + "visibility": "public", + "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}", + "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}", + "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}", + "description": "An empty repo that can be used to test the @trigger.dev/github integration", + "forks_count": 0, + "is_template": false, + "open_issues": 4, + "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}", + "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}", + "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}", + "has_projects": true, + "organization": "triggerdotdev", + "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}", + "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}", + "allow_forking": true, + "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}", + "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads", + "has_downloads": true, + "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages", + "master_branch": "main", + "default_branch": "main", + "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}", + "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers", + "watchers_count": 0, + "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments", + "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}", + "has_discussions": false, + "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers", + "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors", + "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}", + "stargazers_count": 0, + "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription", + "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}", + "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}", + "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}", + "open_issues_count": 4, + "web_commit_signoff_required": false + }, + "head_commit": { + "id": "1ca6a91f4a1385e448364a5de1960a3da60d3fe2", + "url": "https://github.com/triggerdotdev/empty/commit/1ca6a91f4a1385e448364a5de1960a3da60d3fe2", + "added": [], + "author": { + "name": "Matt Aitken", + "email": "matt@mattaitken.com", + "username": "matt-aitken" + }, + "message": "Updated the readme", + "removed": [], + "tree_id": "3a584b2cae2fe34a195e1fe437cc62031ddff447", + "distinct": true, + "modified": ["README.md"], + "committer": { + "name": "Matt Aitken", + "email": "matt@mattaitken.com", + "username": "matt-aitken" + }, + "timestamp": "2023-06-21T19:27:16+01:00" + }, + "organization": { + "id": 95297378, + "url": "https://api.github.com/orgs/triggerdotdev", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks", + "repos_url": "https://api.github.com/orgs/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/orgs/triggerdotdev/events", + "issues_url": "https://api.github.com/orgs/triggerdotdev/issues", + "description": "", + "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}", + "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}" + } +} +``` + + +### `onPullRequest` + +When activity occurs on a pull request (excluding reviews, issues, or comments). [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request). + +```ts usage.ts +client.defineJob({ + id: "", + name: "", + version: "0.1.0", + trigger: github.triggers.repo({ + event: events.onPullRequest, + owner: "", + repo: "", + }), + run: async (payload, io, ctx) => { + // Add tasks here + }, +}); +``` + + +```json +{ + "action": "opened", + "number": 5, + "sender": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "repository": { + "id": 656732269, + "url": "https://api.github.com/repos/triggerdotdev/empty", + "fork": false, + "name": "empty", + "size": 0, + "forks": 0, + "owner": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "type": "Organization", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "topics": [], + "git_url": "git://github.com/triggerdotdev/empty.git", + "license": null, + "node_id": "R_kgDOJyTwbQ", + "private": false, + "ssh_url": "git@github.com:triggerdotdev/empty.git", + "svn_url": "https://github.com/triggerdotdev/empty", + "archived": false, + "disabled": false, + "has_wiki": true, + "homepage": null, + "html_url": "https://github.com/triggerdotdev/empty", + "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}", + "language": null, + "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags", + "watchers": 0, + "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}", + "clone_url": "https://github.com/triggerdotdev/empty.git", + "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks", + "full_name": "triggerdotdev/empty", + "has_pages": false, + "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks", + "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}", + "pushed_at": "2023-06-21T18:38:34Z", + "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams", + "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}", + "created_at": "2023-06-21T14:21:07Z", + "events_url": "https://api.github.com/repos/triggerdotdev/empty/events", + "has_issues": true, + "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}", + "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}", + "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges", + "mirror_url": null, + "updated_at": "2023-06-21T17:23:26Z", + "visibility": "public", + "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}", + "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}", + "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}", + "description": "An empty repo that can be used to test the @trigger.dev/github integration", + "forks_count": 0, + "is_template": false, + "open_issues": 5, + "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}", + "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}", + "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}", + "has_projects": true, + "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}", + "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}", + "allow_forking": true, + "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}", + "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads", + "has_downloads": true, + "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages", + "default_branch": "main", + "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}", + "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers", + "watchers_count": 0, + "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments", + "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}", + "has_discussions": false, + "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers", + "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors", + "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}", + "stargazers_count": 0, + "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription", + "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}", + "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}", + "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}", + "open_issues_count": 5, + "web_commit_signoff_required": false + }, + "organization": { + "id": 95297378, + "url": "https://api.github.com/orgs/triggerdotdev", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks", + "repos_url": "https://api.github.com/orgs/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/orgs/triggerdotdev/events", + "issues_url": "https://api.github.com/orgs/triggerdotdev/issues", + "description": "", + "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}", + "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}" + }, + "pull_request": { + "id": 1402223044, + "url": "https://api.github.com/repos/triggerdotdev/empty/pulls/5", + "base": { + "ref": "main", + "sha": "1ca6a91f4a1385e448364a5de1960a3da60d3fe2", + "repo": { + "id": 656732269, + "url": "https://api.github.com/repos/triggerdotdev/empty", + "fork": false, + "name": "empty", + "size": 0, + "forks": 0, + "owner": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "type": "Organization", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "topics": [], + "git_url": "git://github.com/triggerdotdev/empty.git", + "license": null, + "node_id": "R_kgDOJyTwbQ", + "private": false, + "ssh_url": "git@github.com:triggerdotdev/empty.git", + "svn_url": "https://github.com/triggerdotdev/empty", + "archived": false, + "disabled": false, + "has_wiki": true, + "homepage": null, + "html_url": "https://github.com/triggerdotdev/empty", + "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}", + "language": null, + "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags", + "watchers": 0, + "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}", + "clone_url": "https://github.com/triggerdotdev/empty.git", + "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks", + "full_name": "triggerdotdev/empty", + "has_pages": false, + "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks", + "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}", + "pushed_at": "2023-06-21T18:38:34Z", + "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams", + "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}", + "created_at": "2023-06-21T14:21:07Z", + "events_url": "https://api.github.com/repos/triggerdotdev/empty/events", + "has_issues": true, + "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}", + "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}", + "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges", + "mirror_url": null, + "updated_at": "2023-06-21T17:23:26Z", + "visibility": "public", + "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}", + "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}", + "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}", + "description": "An empty repo that can be used to test the @trigger.dev/github integration", + "forks_count": 0, + "is_template": false, + "open_issues": 5, + "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}", + "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}", + "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}", + "has_projects": true, + "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}", + "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}", + "allow_forking": true, + "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}", + "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads", + "has_downloads": true, + "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages", + "default_branch": "main", + "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}", + "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers", + "watchers_count": 0, + "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments", + "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}", + "has_discussions": false, + "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers", + "allow_auto_merge": false, + "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors", + "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}", + "stargazers_count": 0, + "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription", + "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}", + "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}", + "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}", + "open_issues_count": 5, + "allow_merge_commit": true, + "allow_rebase_merge": true, + "allow_squash_merge": true, + "merge_commit_title": "MERGE_MESSAGE", + "allow_update_branch": false, + "merge_commit_message": "PR_TITLE", + "delete_branch_on_merge": false, + "squash_merge_commit_title": "COMMIT_OR_PR_TITLE", + "squash_merge_commit_message": "COMMIT_MESSAGES", + "web_commit_signoff_required": false, + "use_squash_pr_title_as_default": false + }, + "user": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "type": "Organization", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "label": "triggerdotdev:main" + }, + "body": "A bit more added to the readme which could be useful", + "head": { + "ref": "test", + "sha": "073aa42afebca03c4b34361564e3976da5c65d48", + "repo": { + "id": 656732269, + "url": "https://api.github.com/repos/triggerdotdev/empty", + "fork": false, + "name": "empty", + "size": 0, + "forks": 0, + "owner": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "type": "Organization", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "topics": [], + "git_url": "git://github.com/triggerdotdev/empty.git", + "license": null, + "node_id": "R_kgDOJyTwbQ", + "private": false, + "ssh_url": "git@github.com:triggerdotdev/empty.git", + "svn_url": "https://github.com/triggerdotdev/empty", + "archived": false, + "disabled": false, + "has_wiki": true, + "homepage": null, + "html_url": "https://github.com/triggerdotdev/empty", + "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}", + "language": null, + "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags", + "watchers": 0, + "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}", + "clone_url": "https://github.com/triggerdotdev/empty.git", + "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks", + "full_name": "triggerdotdev/empty", + "has_pages": false, + "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks", + "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}", + "pushed_at": "2023-06-21T18:38:34Z", + "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams", + "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}", + "created_at": "2023-06-21T14:21:07Z", + "events_url": "https://api.github.com/repos/triggerdotdev/empty/events", + "has_issues": true, + "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}", + "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}", + "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges", + "mirror_url": null, + "updated_at": "2023-06-21T17:23:26Z", + "visibility": "public", + "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}", + "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}", + "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}", + "description": "An empty repo that can be used to test the @trigger.dev/github integration", + "forks_count": 0, + "is_template": false, + "open_issues": 5, + "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}", + "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}", + "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}", + "has_projects": true, + "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}", + "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}", + "allow_forking": true, + "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}", + "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads", + "has_downloads": true, + "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages", + "default_branch": "main", + "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}", + "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers", + "watchers_count": 0, + "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments", + "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}", + "has_discussions": false, + "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers", + "allow_auto_merge": false, + "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors", + "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}", + "stargazers_count": 0, + "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription", + "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}", + "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}", + "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}", + "open_issues_count": 5, + "allow_merge_commit": true, + "allow_rebase_merge": true, + "allow_squash_merge": true, + "merge_commit_title": "MERGE_MESSAGE", + "allow_update_branch": false, + "merge_commit_message": "PR_TITLE", + "delete_branch_on_merge": false, + "squash_merge_commit_title": "COMMIT_OR_PR_TITLE", + "squash_merge_commit_message": "COMMIT_MESSAGES", + "web_commit_signoff_required": false, + "use_squash_pr_title_as_default": false + }, + "user": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "type": "Organization", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "label": "triggerdotdev:test" + }, + "user": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "draft": false, + "state": "open", + "title": "Added more to the readme", + "_links": { + "html": { + "href": "https://github.com/triggerdotdev/empty/pull/5" + }, + "self": { + "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/5" + }, + "issue": { + "href": "https://api.github.com/repos/triggerdotdev/empty/issues/5" + }, + "commits": { + "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/5/commits" + }, + "comments": { + "href": "https://api.github.com/repos/triggerdotdev/empty/issues/5/comments" + }, + "statuses": { + "href": "https://api.github.com/repos/triggerdotdev/empty/statuses/073aa42afebca03c4b34361564e3976da5c65d48" + }, + "review_comment": { + "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/comments{/number}" + }, + "review_comments": { + "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/5/comments" + } + }, + "labels": [], + "locked": false, + "merged": false, + "number": 5, + "commits": 1, + "node_id": "PR_kwDOJyTwbc5TlDnE", + "assignee": null, + "comments": 0, + "diff_url": "https://github.com/triggerdotdev/empty/pull/5.diff", + "html_url": "https://github.com/triggerdotdev/empty/pull/5", + "additions": 2, + "assignees": [], + "closed_at": null, + "deletions": 0, + "issue_url": "https://api.github.com/repos/triggerdotdev/empty/issues/5", + "mergeable": null, + "merged_at": null, + "merged_by": null, + "milestone": null, + "patch_url": "https://github.com/triggerdotdev/empty/pull/5.patch", + "auto_merge": null, + "created_at": "2023-06-21T18:39:13Z", + "rebaseable": null, + "updated_at": "2023-06-21T18:39:13Z", + "commits_url": "https://api.github.com/repos/triggerdotdev/empty/pulls/5/commits", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/issues/5/comments", + "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/073aa42afebca03c4b34361564e3976da5c65d48", + "changed_files": 1, + "mergeable_state": "unknown", + "requested_teams": [], + "review_comments": 0, + "merge_commit_sha": null, + "active_lock_reason": null, + "author_association": "MEMBER", + "review_comment_url": "https://api.github.com/repos/triggerdotdev/empty/pulls/comments{/number}", + "requested_reviewers": [], + "review_comments_url": "https://api.github.com/repos/triggerdotdev/empty/pulls/5/comments", + "maintainer_can_modify": false + } +} +``` + + +### `onPullRequestReview` + +When a pull request review has activity. [Official GitHub docs](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_review). + +```ts usage.ts +client.defineJob({ + id: "", + name: "", + version: "0.1.0", + trigger: github.triggers.repo({ + event: events.onPullRequestReview, + owner: "", + repo: "", + }), + run: async (payload, io, ctx) => { + // Add tasks here + }, +}); +``` + + +```json +{ + "action": "submitted", + "review": { + "id": 1491475123, + "body": "This needs some work still", + "user": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "state": "commented", + "_links": { + "html": { + "href": "https://github.com/triggerdotdev/empty/pull/5#pullrequestreview-1491475123" + }, + "pull_request": { + "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/5" + } + }, + "node_id": "PRR_kwDOJyTwbc5Y5hqz", + "html_url": "https://github.com/triggerdotdev/empty/pull/5#pullrequestreview-1491475123", + "commit_id": "073aa42afebca03c4b34361564e3976da5c65d48", + "submitted_at": "2023-06-21T18:47:47Z", + "pull_request_url": "https://api.github.com/repos/triggerdotdev/empty/pulls/5", + "author_association": "MEMBER" + }, + "sender": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "repository": { + "id": 656732269, + "url": "https://api.github.com/repos/triggerdotdev/empty", + "fork": false, + "name": "empty", + "size": 0, + "forks": 0, + "owner": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "type": "Organization", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "topics": [], + "git_url": "git://github.com/triggerdotdev/empty.git", + "license": null, + "node_id": "R_kgDOJyTwbQ", + "private": false, + "ssh_url": "git@github.com:triggerdotdev/empty.git", + "svn_url": "https://github.com/triggerdotdev/empty", + "archived": false, + "disabled": false, + "has_wiki": true, + "homepage": null, + "html_url": "https://github.com/triggerdotdev/empty", + "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}", + "language": null, + "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags", + "watchers": 0, + "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}", + "clone_url": "https://github.com/triggerdotdev/empty.git", + "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks", + "full_name": "triggerdotdev/empty", + "has_pages": false, + "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks", + "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}", + "pushed_at": "2023-06-21T18:39:13Z", + "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams", + "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}", + "created_at": "2023-06-21T14:21:07Z", + "events_url": "https://api.github.com/repos/triggerdotdev/empty/events", + "has_issues": true, + "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}", + "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}", + "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges", + "mirror_url": null, + "updated_at": "2023-06-21T17:23:26Z", + "visibility": "public", + "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}", + "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}", + "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}", + "description": "An empty repo that can be used to test the @trigger.dev/github integration", + "forks_count": 0, + "is_template": false, + "open_issues": 5, + "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}", + "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}", + "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}", + "has_projects": true, + "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}", + "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}", + "allow_forking": true, + "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}", + "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads", + "has_downloads": true, + "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages", + "default_branch": "main", + "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}", + "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers", + "watchers_count": 0, + "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments", + "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}", + "has_discussions": false, + "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers", + "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors", + "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}", + "stargazers_count": 0, + "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription", + "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}", + "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}", + "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}", + "open_issues_count": 5, + "web_commit_signoff_required": false + }, + "organization": { + "id": 95297378, + "url": "https://api.github.com/orgs/triggerdotdev", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "hooks_url": "https://api.github.com/orgs/triggerdotdev/hooks", + "repos_url": "https://api.github.com/orgs/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/orgs/triggerdotdev/events", + "issues_url": "https://api.github.com/orgs/triggerdotdev/issues", + "description": "", + "members_url": "https://api.github.com/orgs/triggerdotdev/members{/member}", + "public_members_url": "https://api.github.com/orgs/triggerdotdev/public_members{/member}" + }, + "pull_request": { + "id": 1402223044, + "url": "https://api.github.com/repos/triggerdotdev/empty/pulls/5", + "base": { + "ref": "main", + "sha": "1ca6a91f4a1385e448364a5de1960a3da60d3fe2", + "repo": { + "id": 656732269, + "url": "https://api.github.com/repos/triggerdotdev/empty", + "fork": false, + "name": "empty", + "size": 0, + "forks": 0, + "owner": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "type": "Organization", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "topics": [], + "git_url": "git://github.com/triggerdotdev/empty.git", + "license": null, + "node_id": "R_kgDOJyTwbQ", + "private": false, + "ssh_url": "git@github.com:triggerdotdev/empty.git", + "svn_url": "https://github.com/triggerdotdev/empty", + "archived": false, + "disabled": false, + "has_wiki": true, + "homepage": null, + "html_url": "https://github.com/triggerdotdev/empty", + "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}", + "language": null, + "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags", + "watchers": 0, + "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}", + "clone_url": "https://github.com/triggerdotdev/empty.git", + "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks", + "full_name": "triggerdotdev/empty", + "has_pages": false, + "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks", + "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}", + "pushed_at": "2023-06-21T18:39:13Z", + "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams", + "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}", + "created_at": "2023-06-21T14:21:07Z", + "events_url": "https://api.github.com/repos/triggerdotdev/empty/events", + "has_issues": true, + "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}", + "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}", + "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges", + "mirror_url": null, + "updated_at": "2023-06-21T17:23:26Z", + "visibility": "public", + "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}", + "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}", + "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}", + "description": "An empty repo that can be used to test the @trigger.dev/github integration", + "forks_count": 0, + "is_template": false, + "open_issues": 5, + "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}", + "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}", + "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}", + "has_projects": true, + "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}", + "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}", + "allow_forking": true, + "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}", + "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads", + "has_downloads": true, + "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages", + "default_branch": "main", + "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}", + "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers", + "watchers_count": 0, + "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments", + "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}", + "has_discussions": false, + "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers", + "allow_auto_merge": false, + "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors", + "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}", + "stargazers_count": 0, + "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription", + "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}", + "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}", + "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}", + "open_issues_count": 5, + "allow_merge_commit": true, + "allow_rebase_merge": true, + "allow_squash_merge": true, + "merge_commit_title": "MERGE_MESSAGE", + "allow_update_branch": false, + "merge_commit_message": "PR_TITLE", + "delete_branch_on_merge": false, + "squash_merge_commit_title": "COMMIT_OR_PR_TITLE", + "squash_merge_commit_message": "COMMIT_MESSAGES", + "web_commit_signoff_required": false, + "use_squash_pr_title_as_default": false + }, + "user": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "type": "Organization", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "label": "triggerdotdev:main" + }, + "body": "A bit more added to the readme which could be useful", + "head": { + "ref": "test", + "sha": "073aa42afebca03c4b34361564e3976da5c65d48", + "repo": { + "id": 656732269, + "url": "https://api.github.com/repos/triggerdotdev/empty", + "fork": false, + "name": "empty", + "size": 0, + "forks": 0, + "owner": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "type": "Organization", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "topics": [], + "git_url": "git://github.com/triggerdotdev/empty.git", + "license": null, + "node_id": "R_kgDOJyTwbQ", + "private": false, + "ssh_url": "git@github.com:triggerdotdev/empty.git", + "svn_url": "https://github.com/triggerdotdev/empty", + "archived": false, + "disabled": false, + "has_wiki": true, + "homepage": null, + "html_url": "https://github.com/triggerdotdev/empty", + "keys_url": "https://api.github.com/repos/triggerdotdev/empty/keys{/key_id}", + "language": null, + "tags_url": "https://api.github.com/repos/triggerdotdev/empty/tags", + "watchers": 0, + "blobs_url": "https://api.github.com/repos/triggerdotdev/empty/git/blobs{/sha}", + "clone_url": "https://github.com/triggerdotdev/empty.git", + "forks_url": "https://api.github.com/repos/triggerdotdev/empty/forks", + "full_name": "triggerdotdev/empty", + "has_pages": false, + "hooks_url": "https://api.github.com/repos/triggerdotdev/empty/hooks", + "pulls_url": "https://api.github.com/repos/triggerdotdev/empty/pulls{/number}", + "pushed_at": "2023-06-21T18:39:13Z", + "teams_url": "https://api.github.com/repos/triggerdotdev/empty/teams", + "trees_url": "https://api.github.com/repos/triggerdotdev/empty/git/trees{/sha}", + "created_at": "2023-06-21T14:21:07Z", + "events_url": "https://api.github.com/repos/triggerdotdev/empty/events", + "has_issues": true, + "issues_url": "https://api.github.com/repos/triggerdotdev/empty/issues{/number}", + "labels_url": "https://api.github.com/repos/triggerdotdev/empty/labels{/name}", + "merges_url": "https://api.github.com/repos/triggerdotdev/empty/merges", + "mirror_url": null, + "updated_at": "2023-06-21T17:23:26Z", + "visibility": "public", + "archive_url": "https://api.github.com/repos/triggerdotdev/empty/{archive_format}{/ref}", + "commits_url": "https://api.github.com/repos/triggerdotdev/empty/commits{/sha}", + "compare_url": "https://api.github.com/repos/triggerdotdev/empty/compare/{base}...{head}", + "description": "An empty repo that can be used to test the @trigger.dev/github integration", + "forks_count": 0, + "is_template": false, + "open_issues": 5, + "branches_url": "https://api.github.com/repos/triggerdotdev/empty/branches{/branch}", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/comments{/number}", + "contents_url": "https://api.github.com/repos/triggerdotdev/empty/contents/{+path}", + "git_refs_url": "https://api.github.com/repos/triggerdotdev/empty/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/triggerdotdev/empty/git/tags{/sha}", + "has_projects": true, + "releases_url": "https://api.github.com/repos/triggerdotdev/empty/releases{/id}", + "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/{sha}", + "allow_forking": true, + "assignees_url": "https://api.github.com/repos/triggerdotdev/empty/assignees{/user}", + "downloads_url": "https://api.github.com/repos/triggerdotdev/empty/downloads", + "has_downloads": true, + "languages_url": "https://api.github.com/repos/triggerdotdev/empty/languages", + "default_branch": "main", + "milestones_url": "https://api.github.com/repos/triggerdotdev/empty/milestones{/number}", + "stargazers_url": "https://api.github.com/repos/triggerdotdev/empty/stargazers", + "watchers_count": 0, + "deployments_url": "https://api.github.com/repos/triggerdotdev/empty/deployments", + "git_commits_url": "https://api.github.com/repos/triggerdotdev/empty/git/commits{/sha}", + "has_discussions": false, + "subscribers_url": "https://api.github.com/repos/triggerdotdev/empty/subscribers", + "allow_auto_merge": false, + "contributors_url": "https://api.github.com/repos/triggerdotdev/empty/contributors", + "issue_events_url": "https://api.github.com/repos/triggerdotdev/empty/issues/events{/number}", + "stargazers_count": 0, + "subscription_url": "https://api.github.com/repos/triggerdotdev/empty/subscription", + "collaborators_url": "https://api.github.com/repos/triggerdotdev/empty/collaborators{/collaborator}", + "issue_comment_url": "https://api.github.com/repos/triggerdotdev/empty/issues/comments{/number}", + "notifications_url": "https://api.github.com/repos/triggerdotdev/empty/notifications{?since,all,participating}", + "open_issues_count": 5, + "allow_merge_commit": true, + "allow_rebase_merge": true, + "allow_squash_merge": true, + "merge_commit_title": "MERGE_MESSAGE", + "allow_update_branch": false, + "merge_commit_message": "PR_TITLE", + "delete_branch_on_merge": false, + "squash_merge_commit_title": "COMMIT_OR_PR_TITLE", + "squash_merge_commit_message": "COMMIT_MESSAGES", + "web_commit_signoff_required": false, + "use_squash_pr_title_as_default": false + }, + "user": { + "id": 95297378, + "url": "https://api.github.com/users/triggerdotdev", + "type": "Organization", + "login": "triggerdotdev", + "node_id": "O_kgDOBa4fYg", + "html_url": "https://github.com/triggerdotdev", + "gists_url": "https://api.github.com/users/triggerdotdev/gists{/gist_id}", + "repos_url": "https://api.github.com/users/triggerdotdev/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/95297378?v=4", + "events_url": "https://api.github.com/users/triggerdotdev/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/triggerdotdev/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/triggerdotdev/followers", + "following_url": "https://api.github.com/users/triggerdotdev/following{/other_user}", + "organizations_url": "https://api.github.com/users/triggerdotdev/orgs", + "subscriptions_url": "https://api.github.com/users/triggerdotdev/subscriptions", + "received_events_url": "https://api.github.com/users/triggerdotdev/received_events" + }, + "label": "triggerdotdev:test" + }, + "user": { + "id": 10635986, + "url": "https://api.github.com/users/matt-aitken", + "type": "User", + "login": "matt-aitken", + "node_id": "MDQ6VXNlcjEwNjM1OTg2", + "html_url": "https://github.com/matt-aitken", + "gists_url": "https://api.github.com/users/matt-aitken/gists{/gist_id}", + "repos_url": "https://api.github.com/users/matt-aitken/repos", + "avatar_url": "https://avatars.githubusercontent.com/u/10635986?v=4", + "events_url": "https://api.github.com/users/matt-aitken/events{/privacy}", + "site_admin": false, + "gravatar_id": "", + "starred_url": "https://api.github.com/users/matt-aitken/starred{/owner}{/repo}", + "followers_url": "https://api.github.com/users/matt-aitken/followers", + "following_url": "https://api.github.com/users/matt-aitken/following{/other_user}", + "organizations_url": "https://api.github.com/users/matt-aitken/orgs", + "subscriptions_url": "https://api.github.com/users/matt-aitken/subscriptions", + "received_events_url": "https://api.github.com/users/matt-aitken/received_events" + }, + "draft": false, + "state": "open", + "title": "Added more to the readme", + "_links": { + "html": { + "href": "https://github.com/triggerdotdev/empty/pull/5" + }, + "self": { + "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/5" + }, + "issue": { + "href": "https://api.github.com/repos/triggerdotdev/empty/issues/5" + }, + "commits": { + "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/5/commits" + }, + "comments": { + "href": "https://api.github.com/repos/triggerdotdev/empty/issues/5/comments" + }, + "statuses": { + "href": "https://api.github.com/repos/triggerdotdev/empty/statuses/073aa42afebca03c4b34361564e3976da5c65d48" + }, + "review_comment": { + "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/comments{/number}" + }, + "review_comments": { + "href": "https://api.github.com/repos/triggerdotdev/empty/pulls/5/comments" + } + }, + "labels": [], + "locked": false, + "number": 5, + "node_id": "PR_kwDOJyTwbc5TlDnE", + "assignee": null, + "diff_url": "https://github.com/triggerdotdev/empty/pull/5.diff", + "html_url": "https://github.com/triggerdotdev/empty/pull/5", + "assignees": [], + "closed_at": null, + "issue_url": "https://api.github.com/repos/triggerdotdev/empty/issues/5", + "merged_at": null, + "milestone": null, + "patch_url": "https://github.com/triggerdotdev/empty/pull/5.patch", + "auto_merge": null, + "created_at": "2023-06-21T18:39:13Z", + "updated_at": "2023-06-21T18:47:47Z", + "commits_url": "https://api.github.com/repos/triggerdotdev/empty/pulls/5/commits", + "comments_url": "https://api.github.com/repos/triggerdotdev/empty/issues/5/comments", + "statuses_url": "https://api.github.com/repos/triggerdotdev/empty/statuses/073aa42afebca03c4b34361564e3976da5c65d48", + "requested_teams": [], + "merge_commit_sha": "f146c57c260778db17300c47c366bce0e2dbd53d", + "active_lock_reason": null, + "author_association": "MEMBER", + "review_comment_url": "https://api.github.com/repos/triggerdotdev/empty/pulls/comments{/number}", + "requested_reviewers": [], + "review_comments_url": "https://api.github.com/repos/triggerdotdev/empty/pulls/5/comments" + } +} +``` + diff --git a/docs/integrations/apis/github.mdx b/docs/integrations/apis/github.mdx index c99e71dc0..4b00021be 100644 --- a/docs/integrations/apis/github.mdx +++ b/docs/integrations/apis/github.mdx @@ -1,10 +1,21 @@ --- -title: Introduction +title: GitHub overview & authentication +sidebarTitle: Overview & authentication --- - +## Overview -## Installation +Our GitHub integration allows you to create triggers and tasks that interact with GitHub. For examples of some of the things you can do with it, check out our Jobs Showcase: + + + Check out pre-built GitHub jobs in our showcase. + + +## Installing the GitHub packages @@ -24,25 +35,41 @@ yarn add @trigger.dev/github@latest ## Authentication -GitHub supports Personal Access Tokens and OAuth. +GitHub supports Personal Access Tokens and OAuth. You can use either of these to authenticate with GitHub. -```ts -import { Github } from "@trigger.dev/github"; +### Personal Access Token + +To create a personal access token on GitHub, login and [follow the instructions](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token). Information on the required scopes can be found [here](https://docs.github.com/en/developers/apps/scopes-for-oauth-apps). + +```ts my-job.ts +import { GitHub } from "@trigger.dev/github"; //create GitHub client using a token -const github = new Github({ +const github = new GitHub({ id: "github", token: process.env.GITHUB_TOKEN!, }); +... +``` + +### OAuth + +To use OAuth you can connect to GitHub via the Trigger.dev [web app](https://cloud.trigger.dev). Click 'Integrations' in the side panel of any project, and configure GitHub with the ID you want to use in your job and the required [scopes](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps). + +```ts my-job.ts +import { GitHub } from "@trigger.dev/github"; //create GitHub client using OAuth -const github2 = new Github({ - id: "github2", +const github = new GitHub({ + id: "github", }); +... ``` ## Triggers and Tasks +Once you have set up a GitHub client, you can use it to create triggers and tasks. + Trigger Jobs when events happen in GitHub, such as a new commit or a new issue. @@ -51,51 +78,3 @@ const github2 = new Github({ Perform tasks such as creating a new issue or a new comment. - -## Using the underlying client - -You can use the underlying client to do anything Octokit supports. In this example we create a project card when a new issue is opened.. - - - View [the official GitHub docs](https://docs.github.com/en/rest) for everything that is - supported{" "} - - -```ts -import { Github, events } from "@trigger.dev/github"; - -const github = new Github({ - id: "github", - token: process.env.GITHUB_TOKEN!, -}); - -client.defineJob({ - id: "alert-on-new-github-issues", - name: "Alert on new GitHub issues", - version: "0.1.1", - trigger: github.triggers.repo({ - event: events.onIssueOpened, - owner: "triggerdotdev", - repo: "trigger.dev", - }), - integrations: { - github, - }, - run: async (payload, io, ctx) => { - //io.github.runTask allows you to use the underlying SDK client - const { data } = await io.github.runTask( - "create-card", - async (client) => { - return client.rest.projects.createCard({ - column_id: 123, - note: "test", - }); - }, - { name: "Create card" } - ); - - //log the url of the created card - await io.logger.info(data.url); - }, -}); -``` diff --git a/docs/integrations/apis/replicate.mdx b/docs/integrations/apis/replicate.mdx new file mode 100644 index 000000000..978bb51c1 --- /dev/null +++ b/docs/integrations/apis/replicate.mdx @@ -0,0 +1,170 @@ +--- +title: Replicate +description: "Run machine learning tasks easily at scale" +--- + + + +## Installation + +To get started with the Replicate integration on Trigger.dev, you need to install the `@trigger.dev/replicate` package. +You can do this using npm, pnpm, or yarn: + + + +```bash npm +npm install @trigger.dev/replicate@latest +``` + +```bash pnpm +pnpm add @trigger.dev/replicate@latest +``` + +```bash yarn +yarn add @trigger.dev/replicate@latest +``` + + + +## Authentication + +To use the Replicate API with Trigger.dev, you have to provide an API Key. + +### API Key + +You can create an API Key in your [Account Settings](https://replicate.com/account/api-tokens). + +```ts +import { Replicate } from "@trigger.dev/replicate"; + +//this will use the passed in API key (defined in your environment variables) +const replicate = new Replicate({ + id: "replicate", + apiKey: process.env["REPLICATE_API_KEY"], +}); +``` + +## Usage + +Include the Replicate integration in your Trigger.dev job. + +```ts +client.defineJob({ + id: "replicate-cinematic-prompt", + name: "Replicate - Cinematic Prompt", + version: "0.1.0", + integrations: { replicate }, + trigger: eventTrigger({ + name: "replicate.cinematic", + schema: z.object({ + prompt: z.string().default("rick astley riding a harley through post-apocalyptic miami"), + version: z + .string() + .default("af1a68a271597604546c09c64aabcd7782c114a63539a4a8d14d1eeda5630c33"), + }), + }), + run: async (payload, io, ctx) => { + //wait for prediction completion (uses remote callbacks internally) + const prediction = await io.replicate.predictions.createAndAwait("await-prediction", { + version: payload.version, + input: { + prompt: `${payload.prompt}, cinematic, 70mm, anamorphic, bokeh`, + width: 1280, + height: 720, + }, + }); + return prediction.output; + }, +}); +``` + +### Pagination + +You can paginate responses: + +- Using the `getAll` helper +- Using the `paginate` helper + +```ts +client.defineJob({ + id: "replicate-pagination", + name: "Replicate Pagination", + version: "0.1.0", + integrations: { + replicate, + }, + trigger: eventTrigger({ + name: "replicate.paginate", + }), + run: async (payload, io, ctx) => { + // getAll - returns an array of all results (uses paginate internally) + const all = await io.replicate.getAll(io.replicate.predictions.list, "get-all"); + + // paginate - returns an async generator, useful to process one page at a time + for await (const predictions of io.replicate.paginate( + io.replicate.predictions.list, + "paginate-all" + )) { + await io.logger.info("stats", { + total: predictions.length, + versions: predictions.map((p) => p.version), + }); + } + + return { count: all.length }; + }, +}); +``` + +## Tasks + +### Collections + +| Function Name | Description | +| ------------------ | ---------------------------------------------------------------------- | +| `collections.get` | Gets a collection. | +| `collections.list` | Returns the first page of all collections. Use with pagination helper. | + +### Deployments + +| Function Name | Description | +| ---------------------------------------- | --------------------------------------------------------- | +| `deployments.predictions.create` | Creates a new prediction with a deployment. | +| `deployments.predictions.createAndAwait` | Creates and waits for a new prediction with a deployment. | + +### Models + +| Function Name | Description | +| ----------------- | ------------------------ | +| `models.get` | Gets a model. | +| `models.versions` | Gets a model version. | +| `models.versions` | Gets all model versions. | + +### Predictions + +| Function Name | Description | +| ---------------------------- | ---------------------------------------------------------------------- | +| `predictions.cancel` | Cancels a prediction. | +| `predictions.create` | Creates a prediction. | +| `predictions.createAndAwait` | Creates and waits for a prediction. | +| `predictions.get` | Gets a prediction. | +| `predictions.list` | Returns the first page of all predictions. Use with pagination helper. | + +### Trainings + +| Function Name | Description | +| -------------------------- | -------------------------------------------------------------------- | +| `trainings.cancel` | Cancels a training. | +| `trainings.create` | Creates a training. | +| `trainings.createAndAwait` | Creates and waits for a training. | +| `trainings.get` | Gets a training. | +| `trainings.list` | Returns the first page of all trainings. Use with pagination helper. | + +### Misc + +| Function Name | Description | +| ------------- | --------------------------------------------------- | +| `getAll` | Pagination helper that returns an array of results. | +| `paginate` | Pagination helper that returns an async generator. | +| `request` | Sends authenticated requests to the Replicate API. | +| `run` | Creates and waits for a prediction. | diff --git a/docs/integrations/apis/stripe.mdx b/docs/integrations/apis/stripe.mdx index 58901892a..87e2a10f5 100644 --- a/docs/integrations/apis/stripe.mdx +++ b/docs/integrations/apis/stripe.mdx @@ -39,6 +39,8 @@ const stripe = new Stripe({ The Stripe integration exposes a number of triggers that can be used on a job, powered by Stripe webhooks. +We recommend testing Stripe payloads using [Stripe Shell](https://stripe.com/docs/stripe-cli?shell=true), Stripe's browser-based shell with the Stripe CLI pre-installed. + ```ts client.defineJob({ id: "stripe-price", diff --git a/docs/integrations/apis/supabase/introduction.mdx b/docs/integrations/apis/supabase/introduction.mdx index 92485b6c5..af78305dc 100644 --- a/docs/integrations/apis/supabase/introduction.mdx +++ b/docs/integrations/apis/supabase/introduction.mdx @@ -1,5 +1,6 @@ --- -title: Introduction +title: "Supabase: Introduction" +sidebarTitle: "Introduction" --- diff --git a/docs/integrations/apis/supabase/management.mdx b/docs/integrations/apis/supabase/management.mdx index ec3e151d0..286fc93e7 100644 --- a/docs/integrations/apis/supabase/management.mdx +++ b/docs/integrations/apis/supabase/management.mdx @@ -125,6 +125,7 @@ Now, you can use the `db` instance to add a trigger to run a job when a row is i client.defineJob({ id: "supabase-trigger", name: "Supabase Trigger", + version: "1.0.0", trigger: db.onInserted({ table: "todos", }), @@ -140,6 +141,7 @@ You can add additional filters to the trigger by passing a `filter` object: client.defineJob({ id: "supabase-trigger", name: "Supabase Trigger", + version: "1.0.0", trigger: db.onUpdated({ table: "todos", // Only trigger if the todo is marked as completed @@ -164,6 +166,7 @@ You can also listen for multiple different events using the `on` trigger: client.defineJob({ id: "supabase-trigger", name: "Supabase Trigger", + version: "1.0.0", trigger: db.on({ table: "todos", events: ["INSERT", "UPDATE"] // Trigger on both insert and update events @@ -206,6 +209,7 @@ const db = supabase.db("https://.supabase.co"); client.defineJob({ id: "supabase-trigger", name: "Supabase Trigger", + version: "1.0.0", trigger: db.onUpdated({ table: "todos", }), diff --git a/docs/integrations/create-tasks.mdx b/docs/integrations/create-tasks.mdx index fe5386a18..2ed3b09df 100644 --- a/docs/integrations/create-tasks.mdx +++ b/docs/integrations/create-tasks.mdx @@ -24,7 +24,7 @@ export class Github implements TriggerIntegration { if (!this._io) throw new Error("No IO"); if (!this._connectionKey) throw new Error("No connection key"); - return this._io.runTask( + return this._io.runTask( key, (task, io) => { if (!this._client) throw new Error("No client"); diff --git a/docs/integrations/create.mdx b/docs/integrations/create.mdx index 5ae14721d..6d56309ca 100644 --- a/docs/integrations/create.mdx +++ b/docs/integrations/create.mdx @@ -1,5 +1,6 @@ --- -title: Introduction +title: "Create an Integration: Introduction" +sidebarTitle: "Introduction" description: "You can create Integrations of your own." --- diff --git a/docs/integrations/introduction.mdx b/docs/integrations/introduction.mdx index 30be9c1e3..d307fa403 100644 --- a/docs/integrations/introduction.mdx +++ b/docs/integrations/introduction.mdx @@ -1,5 +1,6 @@ --- -title: Introduction +title: "Integrations: Introduction" +sidebarTitle: "Introduction" description: "Integrations make it easy to authenticate and use APIs." --- @@ -30,14 +31,17 @@ description: "Integrations make it easy to authenticate and use APIs." Navigate the menu or select Integrations from the table below. -| API | Description | Webhooks | Tasks | -| --------------------------------------- | ---------------------------------------------------------------- | -------- | ----- | -| [GitHub](/integrations/apis/github) | Subscribe to webhooks and perform actions | βœ… | βœ… | -| [Linear](/integrations/apis/linear) | Streamline project and issue tracking | βœ… | βœ… | -| [OpenAI](/integrations/apis/openai) | Generate text and images. Including longer than 30s prompts | N/A | βœ… | -| [Plain](/integrations/apis/plain) | Perform customer support using Plain | πŸ•˜ | βœ… | -| [Resend](/integrations/apis/resend) | Send emails using Resend | πŸ•˜ | βœ… | -| [SendGrid](/integrations/apis/sendgrid) | Send emails using SendGrid | πŸ•˜ | βœ… | -| [Slack](/integrations/apis/slack) | Send Slack messages | πŸ•˜ | βœ… | -| [Supabase](/integrations/apis/supabase) | Interact with your projects and databases | βœ… | βœ… | -| [Typeform](/integrations/apis/typeform) | Interact with the Typeform API and get notified of new responses | βœ… | βœ… | +| API | Description | Webhooks | Tasks | +| ----------------------------------------- | ---------------------------------------------------------------- | -------- | ----- | +| [Airtable](/integrations/apis/airtable) | Interact with the Airtable API | πŸ•˜ | βœ… | +| [GitHub](/integrations/apis/github) | Subscribe to webhooks and perform actions | βœ… | βœ… | +| [Linear](/integrations/apis/linear) | Streamline project and issue tracking | βœ… | βœ… | +| [OpenAI](/integrations/apis/openai) | Generate text and images. Including longer than 30s prompts | N/A | βœ… | +| [Plain](/integrations/apis/plain) | Perform customer support using Plain | πŸ•˜ | βœ… | +| [Replicate](/integrations/apis/replicate) | Run machine learning tasks easily at scale | N/A | βœ… | +| [Resend](/integrations/apis/resend) | Send emails using Resend | πŸ•˜ | βœ… | +| [SendGrid](/integrations/apis/sendgrid) | Send emails using SendGrid | πŸ•˜ | βœ… | +| [Slack](/integrations/apis/slack) | Send Slack messages | πŸ•˜ | βœ… | +| [Stripe](/integrations/apis/stripe) | Interact with the Stripe API | βœ… | βœ… | +| [Supabase](/integrations/apis/supabase) | Interact with your projects and databases | βœ… | βœ… | +| [Typeform](/integrations/apis/typeform) | Interact with the Typeform API and get notified of new responses | βœ… | βœ… | diff --git a/docs/mint.json b/docs/mint.json index 66f51cf94..37110caf5 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -76,6 +76,7 @@ "documentation/quickstarts/express", "documentation/quickstarts/remix", "documentation/quickstarts/redwood", + "documentation/quickstarts/nestjs", "documentation/quickstarts/astro", "documentation/quickstarts/nuxt", "documentation/quickstarts/sveltekit", @@ -142,6 +143,7 @@ "group": "Manual setup", "pages": [ "documentation/guides/manual/nextjs", + "documentation/guides/manual/nestjs", "documentation/guides/manual/express", "documentation/guides/manual/remix", "documentation/guides/manual/redwood", @@ -248,6 +250,7 @@ "integrations/apis/linear", "integrations/apis/openai", "integrations/apis/plain", + "integrations/apis/replicate", "integrations/apis/resend", "integrations/apis/sendgrid", "integrations/apis/slack", @@ -317,10 +320,7 @@ "sdk/dynamictrigger/constructor", { "group": "Instance methods", - "pages": [ - "sdk/dynamictrigger/register", - "sdk/dynamictrigger/unregister" - ] + "pages": ["sdk/dynamictrigger/register", "sdk/dynamictrigger/unregister"] } ] }, @@ -331,10 +331,7 @@ "sdk/dynamicschedule/constructor", { "group": "Instance methods", - "pages": [ - "sdk/dynamicschedule/register", - "sdk/dynamicschedule/unregister" - ] + "pages": ["sdk/dynamicschedule/register", "sdk/dynamicschedule/unregister"] } ] }, @@ -355,9 +352,7 @@ }, { "group": "Overview", - "pages": [ - "examples/introduction" - ] + "pages": ["examples/introduction"] } ], "footerSocials": { @@ -370,4 +365,4 @@ "apiKey": "phc_hwYmedO564b3Ik8nhA4Csrb5SueY0EwFJWCbseGwWW" } } -} \ No newline at end of file +} diff --git a/docs/sdk/dynamicschedule/overview.mdx b/docs/sdk/dynamicschedule/overview.mdx index f6faaa0a8..2216bc92d 100644 --- a/docs/sdk/dynamicschedule/overview.mdx +++ b/docs/sdk/dynamicschedule/overview.mdx @@ -1,5 +1,6 @@ --- -title: "Overview" +title: "DynamicSchedule: Overview" +sidebarTitle: "Overview" description: "`DynamicSchedule` allows you to define a scheduled trigger that can be configured dynamically at runtime." --- diff --git a/docs/sdk/dynamictrigger/overview.mdx b/docs/sdk/dynamictrigger/overview.mdx index b78330cf3..c597b3783 100644 --- a/docs/sdk/dynamictrigger/overview.mdx +++ b/docs/sdk/dynamictrigger/overview.mdx @@ -1,5 +1,6 @@ --- -title: "Overview" +title: "DynamicTrigger: Overview" +sidebarTitle: "Overview" description: "`DynamicTrigger` allows you to define a trigger that can be configured dynamically at runtime." --- diff --git a/docs/sdk/eventtrigger.mdx b/docs/sdk/eventtrigger.mdx index fe94f8c96..5c7b30c95 100644 --- a/docs/sdk/eventtrigger.mdx +++ b/docs/sdk/eventtrigger.mdx @@ -45,6 +45,26 @@ You can have multiple Jobs that subscribe to the same event, they will all trigg ``` + + Used to provide example payloads that are accepted by the job. + + This will be available in the dashboard and can be used to trigger test runs. + + + + The example's ID. + + + The name that's displayed in the dashboard. + + + The payload that's accepted by the job. + + + The icon to use for this example in the dashboard. + + + @@ -70,6 +90,19 @@ client.defineJob({ filter: { tier: ["pro"], }, + //(optional) example event object + examples: [ + { + id: "issue.opened", + name: "Issue opened", + payload: { + userId: "1234", + tier: "free", + }, + //optional + icon: "github", + }, + ], }), run: async (payload, io, ctx) => { await io.logger.log("New pro user created", { userId: payload.userId }); diff --git a/docs/sdk/introduction.mdx b/docs/sdk/introduction.mdx index 1e2c7076e..0d4b28362 100644 --- a/docs/sdk/introduction.mdx +++ b/docs/sdk/introduction.mdx @@ -1,5 +1,6 @@ --- -title: "Introduction" +title: "SDK: Introduction" +sidebarTitle: "Introduction" description: "The SDK is how you interact with Trigger.dev" --- diff --git a/docs/sdk/io/overview.mdx b/docs/sdk/io/overview.mdx index 3a061e336..c44a34fc6 100644 --- a/docs/sdk/io/overview.mdx +++ b/docs/sdk/io/overview.mdx @@ -1,5 +1,6 @@ --- -title: "Overview" +title: "IO: Overview" +sidebarTitle: "Overview" description: "The second parameter in a Job's `run()` function. It holds Integrations and useful actions you can perform." --- @@ -63,5 +64,13 @@ If you want to send an event from outside a run (e.g. just from your backend) yo `io.registerTrigger()` allows you to register a [DynamicTrigger](/sdk/dynamictrigger) with the specified trigger data. +### yield() + +`io.yield()` allows you to yield the current run and resume it immediately in a different function execution context. Requires a single argument that defines the yield key which works similar to task keys. + +### brb() + +`io.brb()` is is alias for `io.yield()`. + {/* ### [unregisterTrigger()](/sdk/io/unregistertrigger) */} {/* `io.unregisterTrigger()` allows you to unregister a [DynamicTrigger](/sdk/dynamictrigger) that was previously registered with `io.registerTrigger()`. */} diff --git a/docs/sdk/io/runtask.mdx b/docs/sdk/io/runtask.mdx index 579142998..eca598386 100644 --- a/docs/sdk/io/runtask.mdx +++ b/docs/sdk/io/runtask.mdx @@ -1,10 +1,12 @@ --- title: "io.runTask()" sidebarTitle: "runTask()" -description: "`io.runTask()` allows you to run a [Task](/documentation/concepts/tasks) from inside a Job run." +description: "Creates and runs a Task inside a Run." --- -A Task is a resumable unit of a Run that can be retried, resumed and is logged. [Integrations](/integrations) use Tasks internally to perform their actions. +A [Task](/documentation/concepts/tasks) is a resumable unit of a Run that can be retried, resumed and is logged. [Integrations](/integrations) use Tasks internally to perform their actions. + +The wrappers at `io.integration.runTask()` expose the underlying Integration client as the first callback parameter (see examples on the right). They will have defaults set for options and `onError` handlers, but should otherwise be considered identical to raw `io.runTask()`. ## Parameters @@ -112,12 +114,29 @@ A Task is a resumable unit of a Run that can be retried, resumed and is logged. + + + An optional object that exposes settings for the remote callback feature. + + Enabling this feature will expose a `callbackUrl` property on the callback's Task parameter. Additionally, `io.runTask()` will now return a Promise that resolves with the body of the first request sent to that URL. + + + + Whether to enable the remote callback feature. + + + The value of the property. + + + + + An optional callback that will be called when the Task fails. You can perform - logic in here and optionally return a custom error object. Returning an object with `{ retryAt: Date, error?: Error }` will retry the Task at the specified Date. You can also just return a new `Error` object to throw a new error. Return nothing to rethrow the original error. + logic in here and optionally return a custom error object. Returning an object with `{ retryAt: Date, error?: Error }` will retry the Task at the specified Date. You can also just return a new `Error` object to throw a new error. Returning `null` or `undefined` will rethrow the original error. If you want to force retrying to be skipped, return `{ skipRetrying: true }`. @@ -133,6 +152,8 @@ A Task is a resumable unit of a Run that can be retried, resumed and is logged. A Promise that resolves with the returned value of the callback. +If the remote callback feature `options.callback` is enabled, the Promise will instead resolve with the body of the first request sent to `task.callbackUrl`. + ```typescript Run a task @@ -150,11 +171,11 @@ client.defineJob({ }, run: async (payload, io, ctx) => { //runTask - const response = await io.runTask( + const response = await io.github.runTask( "create-card", - async () => { + async (client) => { //create a project card using the underlying GitHub Integration client - return io.github.client.rest.projects.createCard({ + return client.rest.projects.createCard({ column_id: 123, note: "test", }); @@ -201,4 +222,43 @@ client.defineJob({ }); ``` +```typescript Remote callbacks +client.defineJob({ + id: "remote-callback-example", + name: "Remote Callback example", + version: "0.1.1", + trigger: eventTrigger({ name: "predict" }), + integrations: { replicate }, + run: async (payload, io, ctx) => { + //runTask + const prediction = await io.replicate.runTask( + "create-and-await-prediction", + async (client, task) => { + //create a prediction using the underlying Replicate Integration client + await client.predictions.create({ + ...payload, + webhook: task.callbackUrl ?? "", + webhook_events_filter: ["completed"], + }); + //the actual return value will be the data sent to callbackUrl + //cast to the exact data type you expect to receive or `any` if unsure + return {} as Prediction; + }, + { + name: "Create and await Prediction", + icon: "replicate", + //remote callback settings + callback: { + enabled: true, + timeoutInSeconds: 300, + }, + } + ); + + //log the prediction output + await io.logger.info(prediction.output); + }, +}); +``` + diff --git a/docs/sdk/react/introduction.mdx b/docs/sdk/react/introduction.mdx index 63c67ec93..5c255d15c 100644 --- a/docs/sdk/react/introduction.mdx +++ b/docs/sdk/react/introduction.mdx @@ -1,5 +1,6 @@ --- -title: "Introduction" +title: "React SDK: Introduction" +sidebarTitle: "Introduction" description: "The React SDK allows you to display the status of your Jobs and Runs in your React app." --- diff --git a/docs/sdk/triggerclient/overview.mdx b/docs/sdk/triggerclient/overview.mdx index a91880ef3..6d263dd0b 100644 --- a/docs/sdk/triggerclient/overview.mdx +++ b/docs/sdk/triggerclient/overview.mdx @@ -1,5 +1,6 @@ --- -title: "Overview" +title: "TriggerClient: Overview" +sidebarTitle: "Overview" description: "TriggerClient is used to create a client that connects to the Trigger.dev platform" --- diff --git a/integrations/airtable/CHANGELOG.md b/integrations/airtable/CHANGELOG.md index 9905b5f80..ba4997aab 100644 --- a/integrations/airtable/CHANGELOG.md +++ b/integrations/airtable/CHANGELOG.md @@ -1,5 +1,34 @@ # @trigger.dev/airtable +## 2.2.0 + +### Patch Changes + +- Updated dependencies [975c5f1d] + - @trigger.dev/integration-kit@2.2.0 + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- 9a187f9e: upgrade zod to 3.22.3 +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + - @trigger.dev/integration-kit@2.1.9 + +## 2.1.8 + +### Patch Changes + +- 6a992a19: First release of `@trigger.dev/replicate` integration with remote callback support. +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + - @trigger.dev/integration-kit@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/integrations/airtable/package.json b/integrations/airtable/package.json index 87d688f67..403c2bf95 100644 --- a/integrations/airtable/package.json +++ b/integrations/airtable/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/airtable", - "version": "2.1.7", + "version": "2.2.0", "description": "Trigger.dev integration for airtable", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -26,10 +26,10 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@trigger.dev/integration-kit": "workspace:^2.1.7", - "@trigger.dev/sdk": "workspace:^2.1.7", + "@trigger.dev/integration-kit": "workspace:^2.2.0", + "@trigger.dev/sdk": "workspace:^2.2.0", "airtable": "^0.12.1", - "zod": "3.21.4" + "zod": "3.22.3" }, "engines": { "node": ">=16.8.0" diff --git a/integrations/airtable/src/index.ts b/integrations/airtable/src/index.ts index f8c2d99f1..bc9ff2402 100644 --- a/integrations/airtable/src/index.ts +++ b/integrations/airtable/src/index.ts @@ -15,6 +15,7 @@ import { Base } from "./base"; import { Webhooks, createWebhookEventSource } from "./webhooks"; export * from "./types"; +export * from "./base"; export type AirtableIntegrationOptions = { /** An ID for this client */ @@ -92,7 +93,7 @@ export class Airtable implements TriggerIntegration { if (!this._io) throw new Error("No IO"); if (!this._connectionKey) throw new Error("No connection key"); - return this._io.runTask( + return this._io.runTask( key, (task, io) => { if (!this._client) throw new Error("No client"); diff --git a/integrations/github/CHANGELOG.md b/integrations/github/CHANGELOG.md index 0228577ca..405bcc8c9 100644 --- a/integrations/github/CHANGELOG.md +++ b/integrations/github/CHANGELOG.md @@ -1,5 +1,34 @@ # @trigger.dev/github +## 2.2.0 + +### Patch Changes + +- Updated dependencies [975c5f1d] + - @trigger.dev/integration-kit@2.2.0 + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- 9a187f9e: upgrade zod to 3.22.3 +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + - @trigger.dev/integration-kit@2.1.9 + +## 2.1.8 + +### Patch Changes + +- 6a992a19: First release of `@trigger.dev/replicate` integration with remote callback support. +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + - @trigger.dev/integration-kit@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/integrations/github/package.json b/integrations/github/package.json index 1354a1277..d9d9f641d 100644 --- a/integrations/github/package.json +++ b/integrations/github/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/github", - "version": "2.1.7", + "version": "2.2.0", "description": "The official GitHub integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -29,10 +29,10 @@ "@octokit/request": "^6.2.5", "@octokit/request-error": "^4.0.1", "@octokit/webhooks": "^10.4.0", - "@trigger.dev/sdk": "workspace:^2.1.7", - "@trigger.dev/integration-kit": "workspace:^2.1.7", + "@trigger.dev/integration-kit": "workspace:^2.2.0", + "@trigger.dev/sdk": "workspace:^2.2.0", "octokit": "^2.0.14", - "zod": "3.21.4" + "zod": "3.22.3" }, "engines": { "node": ">=16.8.0" diff --git a/integrations/github/src/index.ts b/integrations/github/src/index.ts index 59e2dc968..0716e849b 100644 --- a/integrations/github/src/index.ts +++ b/integrations/github/src/index.ts @@ -138,7 +138,7 @@ export class Github implements TriggerIntegration { if (!this._io) throw new Error("No IO"); if (!this._connectionKey) throw new Error("No connection key"); - return this._io.runTask( + return this._io.runTask( key, (task, io) => { if (!this._client) throw new Error("No client"); diff --git a/integrations/linear/CHANGELOG.md b/integrations/linear/CHANGELOG.md index 9bccb718a..25c7d6a61 100644 --- a/integrations/linear/CHANGELOG.md +++ b/integrations/linear/CHANGELOG.md @@ -1,5 +1,35 @@ # @trigger.dev/linear +## 2.2.0 + +### Patch Changes + +- Updated dependencies [975c5f1d] + - @trigger.dev/integration-kit@2.2.0 + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- 9a187f9e: upgrade zod to 3.22.3 +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + - @trigger.dev/integration-kit@2.1.9 + +## 2.1.8 + +### Patch Changes + +- 6a992a19: First release of `@trigger.dev/replicate` integration with remote callback support. +- 81e886a1: Fix `getAll` helper and search function params +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + - @trigger.dev/integration-kit@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/integrations/linear/package.json b/integrations/linear/package.json index eabcab053..909c88ee3 100644 --- a/integrations/linear/package.json +++ b/integrations/linear/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/linear", - "version": "2.1.7", + "version": "2.2.0", "description": "Trigger.dev integration for @linear/sdk", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -27,9 +27,9 @@ }, "dependencies": { "@linear/sdk": "^8.0.0", - "@trigger.dev/integration-kit": "workspace:^2.1.7", - "@trigger.dev/sdk": "workspace:^2.1.7", - "zod": "3.21.4" + "@trigger.dev/integration-kit": "workspace:^2.2.0", + "@trigger.dev/sdk": "workspace:^2.2.0", + "zod": "3.22.3" }, "engines": { "node": ">=16.8.0" diff --git a/integrations/linear/src/index.ts b/integrations/linear/src/index.ts index f6318ee7a..0f53f9e7a 100644 --- a/integrations/linear/src/index.ts +++ b/integrations/linear/src/index.ts @@ -158,7 +158,7 @@ export class Linear implements TriggerIntegration { if (!this._io) throw new Error("No IO"); if (!this._connectionKey) throw new Error("No connection key"); - return this._io.runTask( + return this._io.runTask( key, (task, io) => { if (!this._client) throw new Error("No client"); @@ -182,7 +182,7 @@ export class Linear implements TriggerIntegration { >( task: TTask, key: IntegrationTaskKey, - params: Nullable = {} + params: Parameters[1] = {} ): Promise>["nodes"]> { const boundTask = task.bind(this as any); @@ -695,7 +695,7 @@ export class Linear implements TriggerIntegration { key: IntegrationTaskKey, params: { term: string; - variables?: L.SearchDocumentsQueryVariables; + variables?: Parameters[1]; } ): LinearReturnType { return this.runTask( @@ -862,7 +862,7 @@ export class Linear implements TriggerIntegration { key: IntegrationTaskKey, params: { term: string; - variables?: L.SearchIssuesQueryVariables; + variables?: Parameters[1]; } ): LinearReturnType { return this.runTask( @@ -1273,7 +1273,7 @@ export class Linear implements TriggerIntegration { key: IntegrationTaskKey, params: { term: string; - variables?: L.SearchProjectsQueryVariables; + variables?: Parameters[1]; } ): LinearReturnType { return this.runTask( diff --git a/integrations/openai/CHANGELOG.md b/integrations/openai/CHANGELOG.md index b346fa3ae..c9fdbb7df 100644 --- a/integrations/openai/CHANGELOG.md +++ b/integrations/openai/CHANGELOG.md @@ -1,5 +1,32 @@ # @trigger.dev/slack +## 2.2.0 + +### Patch Changes + +- Updated dependencies [975c5f1d] + - @trigger.dev/integration-kit@2.2.0 + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + - @trigger.dev/integration-kit@2.1.9 + +## 2.1.8 + +### Patch Changes + +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + - @trigger.dev/integration-kit@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/integrations/openai/package.json b/integrations/openai/package.json index ce2ccbbb8..ccd647c9d 100644 --- a/integrations/openai/package.json +++ b/integrations/openai/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/openai", - "version": "2.1.7", + "version": "2.2.0", "description": "The official OpenAI integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -25,8 +25,8 @@ }, "dependencies": { "openai": "^4.2.0", - "@trigger.dev/sdk": "workspace:^2.1.7", - "@trigger.dev/integration-kit": "workspace:^2.1.7" + "@trigger.dev/sdk": "workspace:^2.2.0", + "@trigger.dev/integration-kit": "workspace:^2.2.0" }, "engines": { "node": ">=16.8.0" diff --git a/integrations/plain/CHANGELOG.md b/integrations/plain/CHANGELOG.md index 6baddddcb..b3beded98 100644 --- a/integrations/plain/CHANGELOG.md +++ b/integrations/plain/CHANGELOG.md @@ -1,5 +1,32 @@ # @trigger.dev/plain +## 2.2.0 + +### Patch Changes + +- Updated dependencies [975c5f1d] + - @trigger.dev/integration-kit@2.2.0 + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + - @trigger.dev/integration-kit@2.1.9 + +## 2.1.8 + +### Patch Changes + +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + - @trigger.dev/integration-kit@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/integrations/plain/package.json b/integrations/plain/package.json index 8a2e65509..846dc2376 100644 --- a/integrations/plain/package.json +++ b/integrations/plain/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/plain", - "version": "2.1.7", + "version": "2.2.0", "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.1.7", - "@trigger.dev/sdk": "workspace:^2.1.7", + "@trigger.dev/integration-kit": "workspace:^2.2.0", + "@trigger.dev/sdk": "workspace:^2.2.0", "@team-plain/typescript-sdk": "^2.7.0" }, "engines": { diff --git a/integrations/replicate/CHANGELOG.md b/integrations/replicate/CHANGELOG.md new file mode 100644 index 000000000..be38c5d7b --- /dev/null +++ b/integrations/replicate/CHANGELOG.md @@ -0,0 +1,30 @@ +# @trigger.dev/replicate + +## 2.2.0 + +### Patch Changes + +- Updated dependencies [975c5f1d] + - @trigger.dev/integration-kit@2.2.0 + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- 9a187f9e: upgrade zod to 3.22.3 +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + - @trigger.dev/integration-kit@2.1.9 + +## 2.1.8 + +### Patch Changes + +- 6a992a19: First release of `@trigger.dev/replicate` integration with remote callback support. +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + - @trigger.dev/integration-kit@2.1.8 diff --git a/integrations/replicate/README.md b/integrations/replicate/README.md new file mode 100644 index 000000000..67a4b88e8 --- /dev/null +++ b/integrations/replicate/README.md @@ -0,0 +1 @@ +# @trigger.dev/replicate diff --git a/integrations/replicate/package.json b/integrations/replicate/package.json new file mode 100644 index 000000000..41c4f76a9 --- /dev/null +++ b/integrations/replicate/package.json @@ -0,0 +1,37 @@ +{ + "name": "@trigger.dev/replicate", + "version": "2.2.0", + "description": "Trigger.dev integration for replicate", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "publishConfig": { + "access": "public" + }, + "files": [ + "dist/index.js", + "dist/index.d.ts", + "dist/index.js.map" + ], + "devDependencies": { + "@trigger.dev/tsconfig": "workspace:*", + "@types/node": "16.x", + "rimraf": "^3.0.2", + "tsup": "7.1.x", + "typescript": "4.9.4" + }, + "scripts": { + "clean": "rimraf dist", + "build": "npm run clean && npm run build:tsup", + "build:tsup": "tsup", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@trigger.dev/integration-kit": "workspace:^2.2.0", + "@trigger.dev/sdk": "workspace:^2.2.0", + "replicate": "^0.18.1", + "zod": "3.22.3" + }, + "engines": { + "node": ">=16.8.0" + } +} \ No newline at end of file diff --git a/integrations/replicate/src/collections.ts b/integrations/replicate/src/collections.ts new file mode 100644 index 000000000..b6f1c0100 --- /dev/null +++ b/integrations/replicate/src/collections.ts @@ -0,0 +1,37 @@ +import { IntegrationTaskKey } from "@trigger.dev/sdk"; +import { Page, Collection } from "replicate"; + +import { ReplicateRunTask } from "./index"; +import { ReplicateReturnType } from "./types"; + +export class Collections { + constructor(private runTask: ReplicateRunTask) {} + + /** Fetch a model collection. */ + get(key: IntegrationTaskKey, params: { slug: string }): ReplicateReturnType { + return this.runTask( + key, + (client) => { + return client.collections.get(params.slug); + }, + { + name: "Get Collection", + params, + properties: [{ label: "Collection Slug", text: params.slug }], + } + ); + } + + /** Fetch a list of model collections. */ + list(key: IntegrationTaskKey): ReplicateReturnType> { + return this.runTask( + key, + (client) => { + return client.collections.list(); + }, + { + name: "List Collections", + } + ); + } +} diff --git a/integrations/replicate/src/deployments.ts b/integrations/replicate/src/deployments.ts new file mode 100644 index 000000000..c5c1508d1 --- /dev/null +++ b/integrations/replicate/src/deployments.ts @@ -0,0 +1,76 @@ +import { IntegrationTaskKey } from "@trigger.dev/sdk"; +import ReplicateClient, { Prediction } from "replicate"; + +import { ReplicateRunTask } from "./index"; +import { callbackProperties, createDeploymentProperties } from "./utils"; +import { CallbackTimeout, ReplicateReturnType } from "./types"; + +export class Deployments { + constructor(private runTask: ReplicateRunTask) {} + + get predictions() { + return new Predictions(this.runTask); + } +} + +class Predictions { + constructor(private runTask: ReplicateRunTask) {} + + /** Create a new prediction with a deployment. */ + create( + key: IntegrationTaskKey, + params: { + deployment_owner: string; + deployment_name: string; + } & Parameters[2] + ): ReplicateReturnType { + return this.runTask( + key, + (client) => { + const { deployment_owner, deployment_name, ...options } = params; + + return client.deployments.predictions.create(deployment_owner, deployment_name, options); + }, + { + name: "Create Prediction With Deployment", + params, + properties: createDeploymentProperties(params), + } + ); + } + + /** Create a new prediction with a deployment and await the result. */ + createAndAwait( + key: IntegrationTaskKey, + params: { + deployment_owner: string; + deployment_name: string; + } & Omit< + Parameters[2], + "webhook" | "webhook_events_filter" + >, + options: CallbackTimeout = { timeoutInSeconds: 3600 } + ): ReplicateReturnType { + return this.runTask( + key, + (client, task) => { + const { deployment_owner, deployment_name, ...options } = params; + + return client.deployments.predictions.create(deployment_owner, deployment_name, { + ...options, + webhook: task.callbackUrl ?? "", + webhook_events_filter: ["completed"], + }); + }, + { + name: "Create And Await Prediction With Deployment", + params, + properties: [...createDeploymentProperties(params), ...callbackProperties(options)], + callback: { + enabled: true, + timeoutInSeconds: options.timeoutInSeconds, + }, + } + ); + } +} diff --git a/integrations/replicate/src/index.ts b/integrations/replicate/src/index.ts new file mode 100644 index 000000000..0093be164 --- /dev/null +++ b/integrations/replicate/src/index.ts @@ -0,0 +1,280 @@ +import { + TriggerIntegration, + RunTaskOptions, + IO, + IOTask, + IntegrationTaskKey, + RunTaskErrorCallback, + Json, + retry, + ConnectionAuth, +} from "@trigger.dev/sdk"; +import ReplicateClient, { Page, Prediction } from "replicate"; + +import { Predictions } from "./predictions"; +import { Models } from "./models"; +import { Trainings } from "./trainings"; +import { Collections } from "./collections"; +import { ReplicateReturnType } from "./types"; +import { Deployments } from "./deployments"; + +export type ReplicateIntegrationOptions = { + id: string; + apiKey: string; +}; + +export type ReplicateRunTask = InstanceType["runTask"]; + +export class Replicate implements TriggerIntegration { + private _options: ReplicateIntegrationOptions; + private _client?: any; + private _io?: IO; + private _connectionKey?: string; + + constructor(private options: ReplicateIntegrationOptions) { + if (Object.keys(options).includes("apiKey") && !options.apiKey) { + throw `Can't create Replicate integration (${options.id}) as apiKey was undefined`; + } + + this._options = options; + } + + get authSource() { + return "LOCAL" as const; + } + + get id() { + return this.options.id; + } + + get metadata() { + return { id: "replicate", name: "Replicate" }; + } + + cloneForRun(io: IO, connectionKey: string, auth?: ConnectionAuth) { + const replicate = new Replicate(this._options); + replicate._io = io; + replicate._connectionKey = connectionKey; + replicate._client = this.createClient(auth); + return replicate; + } + + createClient(auth?: ConnectionAuth) { + return new ReplicateClient({ + auth: this._options.apiKey, + }); + } + + runTask | void>( + key: IntegrationTaskKey, + callback: (client: ReplicateClient, task: IOTask, io: IO) => Promise, + options?: RunTaskOptions, + errorCallback?: RunTaskErrorCallback + ): Promise { + if (!this._io) throw new Error("No IO"); + if (!this._connectionKey) throw new Error("No connection key"); + + return this._io.runTask( + key, + (task, io) => { + if (!this._client) throw new Error("No client"); + return callback(this._client, task, io); + }, + { + icon: "replicate", + retry: retry.standardBackoff, + ...(options ?? {}), + connectionKey: this._connectionKey, + }, + errorCallback ?? onError + ); + } + + get collections() { + return new Collections(this.runTask.bind(this)); + } + + get deployments() { + return new Deployments(this.runTask.bind(this)); + } + + get models() { + return new Models(this.runTask.bind(this)); + } + + get predictions() { + return new Predictions(this.runTask.bind(this)); + } + + get trainings() { + return new Trainings(this.runTask.bind(this)); + } + + /** Paginate through a list of results. */ + async *paginate( + task: (key: string) => Promise>, + key: IntegrationTaskKey, + counter: number = 0 + ): AsyncGenerator { + const boundTask = task.bind(this as any); + + const page = await boundTask(`${key}-${counter}`); + yield page.results; + + if (page.next) { + const nextStep = counter++; + + const nextPage = () => { + return this.request>(`${key}-${nextStep}`, { + route: page.next!, + options: { method: "GET" }, + }); + }; + + yield* this.paginate(nextPage, key, nextStep); + } + } + + /** Auto-paginate and return all results. */ + async getAll( + task: (key: string) => Promise>, + key: IntegrationTaskKey + ): ReplicateReturnType { + const allResults: T[] = []; + + for await (const results of this.paginate(task, key)) { + allResults.push(...results); + } + + return allResults; + } + + /** Make a request to the Replicate API. */ + request( + key: IntegrationTaskKey, + params: { + route: string | URL; + options: Parameters[1]; + } + ): ReplicateReturnType { + return this.runTask( + key, + async (client) => { + const response = await client.request(params.route, params.options); + + return response.json(); + }, + { + name: "Send Request", + params, + properties: [ + { label: "Route", text: params.route.toString() }, + ...(params.options.method ? [{ label: "Method", text: params.options.method }] : []), + ], + callback: { enabled: true }, + } + ); + } + + /** Run a model and await the result. */ + run( + key: IntegrationTaskKey, + params: { + identifier: Parameters[0]; + } & Omit< + Parameters[1], + "webhook" | "webhook_events_filter" | "wait" | "signal" + > + ): ReplicateReturnType { + const { identifier, ...paramsWithoutIdentifier } = params; + + // see: https://github.com/replicate/replicate-javascript/blob/4b0d9cb0e226fab3d3d31de5b32261485acf5626/index.js#L102 + + const namePattern = /[a-zA-Z0-9]+(?:(?:[._]|__|[-]*)[a-zA-Z0-9]+)*/; + const pattern = new RegExp( + `^(?${namePattern.source})/(?${namePattern.source}):(?[0-9a-fA-F]+)$` + ); + + const match = identifier.match(pattern); + + if (!match || !match.groups) { + throw new Error('Invalid version. It must be in the format "owner/name:version"'); + } + + const { version } = match.groups; + + return this.predictions.createAndAwait(key, { ...paramsWithoutIdentifier, version }); + } + + // TODO: wait(prediction) - needs polling +} + +class ApiError extends Error { + constructor( + message: string, + readonly request: Request, + readonly response: Response + ) { + super(message); + this.name = "ApiError"; + } +} + +function isReplicateApiError(error: unknown): error is ApiError { + if (typeof error !== "object" || error === null) { + return false; + } + + const apiError = error as ApiError; + + return ( + apiError.name === "ApiError" && + apiError.request instanceof Request && + apiError.response instanceof Response + ); +} + +function shouldRetry(method: string, status: number) { + return status === 429 || (method === "GET" && status >= 500); +} + +export function onError(error: unknown): ReturnType { + if (!isReplicateApiError(error)) { + return; + } + + if (!shouldRetry(error.request.method, error.response.status)) { + return { + skipRetrying: true, + }; + } + + // see: https://github.com/replicate/replicate-javascript/blob/4b0d9cb0e226fab3d3d31de5b32261485acf5626/lib/util.js#L43 + + const retryAfter = error.response.headers.get("retry-after"); + + if (retryAfter) { + const resetDate = new Date(retryAfter); + + if (!Number.isNaN(resetDate.getTime())) { + return { + retryAt: resetDate, + error, + }; + } + } + + const rateLimitRemaining = error.response.headers.get("ratelimit-remaining"); + const rateLimitReset = error.response.headers.get("ratelimit-reset"); + + if (rateLimitRemaining === "0" && rateLimitReset) { + const resetDate = new Date(Number(rateLimitReset) * 1000); + + if (!Number.isNaN(resetDate.getTime())) { + return { + retryAt: resetDate, + error, + }; + } + } +} diff --git a/integrations/replicate/src/models.ts b/integrations/replicate/src/models.ts new file mode 100644 index 000000000..d4b3a78ac --- /dev/null +++ b/integrations/replicate/src/models.ts @@ -0,0 +1,82 @@ +import { IntegrationTaskKey } from "@trigger.dev/sdk"; +import { Model, ModelVersion } from "replicate"; + +import { ReplicateRunTask } from "./index"; +import { modelProperties } from "./utils"; +import { ReplicateReturnType } from "./types"; + +export class Models { + constructor(private runTask: ReplicateRunTask) {} + + /** Get information about a model. */ + get( + key: IntegrationTaskKey, + params: { + model_owner: string; + model_name: string; + } + ): ReplicateReturnType { + return this.runTask( + key, + (client) => { + return client.models.get(params.model_owner, params.model_name); + }, + { + name: "Get Model", + params, + properties: modelProperties(params), + } + ); + } + + get versions() { + return new Versions(this.runTask); + } +} + +class Versions { + constructor(private runTask: ReplicateRunTask) {} + + /** Get a specific model version. */ + get( + key: IntegrationTaskKey, + params: { + model_owner: string; + model_name: string; + version_id: string; + } + ): ReplicateReturnType { + return this.runTask( + key, + (client) => { + return client.models.versions.get(params.model_owner, params.model_name, params.version_id); + }, + { + name: "Get Model Version", + params, + properties: modelProperties(params), + } + ); + } + + /** List model versions. */ + list( + key: IntegrationTaskKey, + params: { + model_owner: string; + model_name: string; + } + ): ReplicateReturnType { + return this.runTask( + key, + (client) => { + return client.models.versions.list(params.model_owner, params.model_name); + }, + { + name: "List Models", + params, + properties: modelProperties(params), + } + ); + } +} diff --git a/integrations/replicate/src/predictions.ts b/integrations/replicate/src/predictions.ts new file mode 100644 index 000000000..9f6c604fd --- /dev/null +++ b/integrations/replicate/src/predictions.ts @@ -0,0 +1,101 @@ +import { IntegrationTaskKey } from "@trigger.dev/sdk"; +import ReplicateClient, { Page, Prediction } from "replicate"; + +import { ReplicateRunTask } from "./index"; +import { CallbackTimeout, ReplicateReturnType } from "./types"; +import { callbackProperties, createPredictionProperties } from "./utils"; + +export class Predictions { + constructor(private runTask: ReplicateRunTask) {} + + /** Cancel a prediction. */ + cancel(key: IntegrationTaskKey, params: { id: string }): ReplicateReturnType { + return this.runTask( + key, + (client) => { + return client.predictions.cancel(params.id); + }, + { + name: "Cancel Prediction", + params, + properties: [{ label: "Prediction ID", text: params.id }], + } + ); + } + + /** Create a new prediction. */ + create( + key: IntegrationTaskKey, + params: Parameters[0] + ): ReplicateReturnType { + return this.runTask( + key, + (client) => { + return client.predictions.create(params); + }, + { + name: "Create Prediction", + params, + properties: createPredictionProperties(params), + } + ); + } + + /** Create a new prediction and await the result. */ + createAndAwait( + key: IntegrationTaskKey, + params: Omit< + Parameters[0], + "webhook" | "webhook_events_filter" + >, + options: CallbackTimeout = { timeoutInSeconds: 3600 } + ): ReplicateReturnType { + return this.runTask( + key, + (client, task) => { + return client.predictions.create({ + ...params, + webhook: task.callbackUrl ?? "", + webhook_events_filter: ["completed"], + }); + }, + { + name: "Create And Await Prediction", + params, + properties: [...createPredictionProperties(params), ...callbackProperties(options)], + callback: { + enabled: true, + timeoutInSeconds: options.timeoutInSeconds, + }, + } + ); + } + + /** Fetch a prediction. */ + get(key: IntegrationTaskKey, params: { id: string }): ReplicateReturnType { + return this.runTask( + key, + (client) => { + return client.predictions.get(params.id); + }, + { + name: "Get Prediction", + params, + properties: [{ label: "Prediction ID", text: params.id }], + } + ); + } + + /** List all predictions. */ + list(key: IntegrationTaskKey): ReplicateReturnType> { + return this.runTask( + key, + (client) => { + return client.predictions.list(); + }, + { + name: "List Predictions", + } + ); + } +} diff --git a/integrations/replicate/src/trainings.ts b/integrations/replicate/src/trainings.ts new file mode 100644 index 000000000..10a3ae576 --- /dev/null +++ b/integrations/replicate/src/trainings.ts @@ -0,0 +1,113 @@ +import { IntegrationTaskKey } from "@trigger.dev/sdk"; +import ReplicateClient, { Page, Training } from "replicate"; + +import { ReplicateRunTask } from "./index"; +import { CallbackTimeout, ReplicateReturnType } from "./types"; +import { callbackProperties, modelProperties } from "./utils"; + +export class Trainings { + constructor(private runTask: ReplicateRunTask) {} + + /** Cancel a training. */ + cancel(key: IntegrationTaskKey, params: { id: string }): ReplicateReturnType { + return this.runTask( + key, + (client) => { + return client.trainings.cancel(params.id); + }, + { + name: "Cancel Training", + params, + properties: [{ label: "Training ID", text: params.id }], + } + ); + } + + /** Create a new training. */ + create( + key: IntegrationTaskKey, + params: { + model_owner: string; + model_name: string; + version_id: string; + } & Parameters[3] + ): ReplicateReturnType { + return this.runTask( + key, + (client) => { + const { model_owner, model_name, version_id, ...options } = params; + + return client.trainings.create(model_owner, model_name, version_id, options); + }, + { + name: "Create Training", + params, + properties: modelProperties(params), + } + ); + } + + /** Create a new training and await the result. */ + createAndAwait( + key: IntegrationTaskKey, + params: { + model_owner: string; + model_name: string; + version_id: string; + } & Omit< + Parameters[3], + "webhook" | "webhook_events_filter" + >, + options: CallbackTimeout = { timeoutInSeconds: 3600 } + ): ReplicateReturnType { + return this.runTask( + key, + (client, task) => { + const { model_owner, model_name, version_id, ...options } = params; + + return client.trainings.create(model_owner, model_name, version_id, { + ...options, + webhook: task.callbackUrl ?? "", + webhook_events_filter: ["completed"], + }); + }, + { + name: "Create And Await Training", + params, + properties: [...modelProperties(params), ...callbackProperties(options)], + callback: { + enabled: true, + timeoutInSeconds: options.timeoutInSeconds, + }, + } + ); + } + + /** Fetch a training. */ + get(key: IntegrationTaskKey, params: { id: string }): ReplicateReturnType { + return this.runTask( + key, + (client) => { + return client.trainings.get(params.id); + }, + { + name: "Get Training", + params, + properties: [{ label: "Training ID", text: params.id }], + } + ); + } + + /** List all trainings. */ + list(key: IntegrationTaskKey): ReplicateReturnType> { + return this.runTask( + key, + async (client) => { + return client.trainings.list(); + }, + { + name: "List Trainings", + } + ); + } +} diff --git a/integrations/replicate/src/types.ts b/integrations/replicate/src/types.ts new file mode 100644 index 000000000..d8fafcd1d --- /dev/null +++ b/integrations/replicate/src/types.ts @@ -0,0 +1,3 @@ +export type CallbackTimeout = { timeoutInSeconds?: number }; + +export type ReplicateReturnType = Promise; diff --git a/integrations/replicate/src/utils.ts b/integrations/replicate/src/utils.ts new file mode 100644 index 000000000..0a510690f --- /dev/null +++ b/integrations/replicate/src/utils.ts @@ -0,0 +1,58 @@ +import { CallbackTimeout } from "./types"; + +export const createPredictionProperties = ( + params: Partial<{ + version: string; + stream: boolean; + }> +) => { + return [ + ...(params.version ? [{ label: "Model Version", text: params.version }] : []), + ...streamingProperty(params), + ]; +}; + +export const createDeploymentProperties = ( + params: Partial<{ + deployment_owner: string; + deployment_name: string; + stream: boolean; + }> +) => { + return [ + ...(params.deployment_owner + ? [{ label: "Deployment Owner", text: params.deployment_owner }] + : []), + ...(params.deployment_name ? [{ label: "Deployment Name", text: params.deployment_name }] : []), + ...streamingProperty(params), + ]; +}; + +export const modelProperties = ( + params: Partial<{ + model_owner: string; + model_name: string; + version_id: string; + destination: string; + }> +) => { + return [ + ...(params.model_owner ? [{ label: "Model Owner", text: params.model_owner }] : []), + ...(params.model_name ? [{ label: "Model Name", text: params.model_name }] : []), + ...(params.version_id ? [{ label: "Model Version", text: params.version_id }] : []), + ...(params.destination ? [{ label: "Destination Model", text: params.destination }] : []), + ]; +}; + +export const streamingProperty = (params: { stream?: boolean }) => { + return [{ label: "Streaming Enabled", text: String(!!params.stream) }]; +}; + +export const callbackProperties = (options: CallbackTimeout) => { + return [ + { + label: "Callback Timeout", + text: options.timeoutInSeconds ? `${options.timeoutInSeconds}s` : "default", + }, + ]; +}; diff --git a/integrations/replicate/tsconfig.json b/integrations/replicate/tsconfig.json new file mode 100644 index 000000000..36ae307e4 --- /dev/null +++ b/integrations/replicate/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@trigger.dev/tsconfig/integration.json", + "include": ["./src/**/*.ts", "tsup.config.ts"], +} diff --git a/integrations/replicate/tsup.config.ts b/integrations/replicate/tsup.config.ts new file mode 100644 index 000000000..483aba1d5 --- /dev/null +++ b/integrations/replicate/tsup.config.ts @@ -0,0 +1,22 @@ +import { defineConfig } from "tsup"; + +export default defineConfig([ + { + name: "main", + entry: ["./src/index.ts"], + outDir: "./dist", + platform: "node", + format: ["cjs"], + legacyOutput: true, + sourcemap: true, + clean: true, + bundle: true, + splitting: false, + dts: true, + treeshake: { + preset: "smallest", + }, + esbuildPlugins: [], + external: ["http", "https", "util", "events", "tty", "os", "timers"], + }, +]); diff --git a/integrations/resend/CHANGELOG.md b/integrations/resend/CHANGELOG.md index a7e773f79..a77c82ce2 100644 --- a/integrations/resend/CHANGELOG.md +++ b/integrations/resend/CHANGELOG.md @@ -1,5 +1,34 @@ # @trigger.dev/resend +## 2.2.0 + +### Patch Changes + +- 59a94c71: Allow task property values to be blank, but strip them out before persisting them +- Updated dependencies [975c5f1d] + - @trigger.dev/integration-kit@2.2.0 + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + - @trigger.dev/integration-kit@2.1.9 + +## 2.1.8 + +### Patch Changes + +- 6a992a19: First release of `@trigger.dev/replicate` integration with remote callback support. +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + - @trigger.dev/integration-kit@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/integrations/resend/package.json b/integrations/resend/package.json index 63af38a55..87a825869 100644 --- a/integrations/resend/package.json +++ b/integrations/resend/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/resend", - "version": "2.1.7", + "version": "2.2.0", "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.1.7", - "@trigger.dev/sdk": "workspace:^2.1.7", + "@trigger.dev/integration-kit": "workspace:^2.2.0", + "@trigger.dev/sdk": "workspace:^2.2.0", "resend": "^1.0.0" }, "engines": { diff --git a/integrations/resend/src/index.ts b/integrations/resend/src/index.ts index f93be1964..bd2a663d6 100644 --- a/integrations/resend/src/index.ts +++ b/integrations/resend/src/index.ts @@ -25,6 +25,9 @@ function isRequestError(error: unknown): error is ErrorResponse { return typeof error === "object" && error !== null && "statusCode" in error; } +// See https://resend.com/docs/api-reference/errors +const skipRetryingErrors = [422, 401, 403, 404, 405, 422]; + function onError(error: unknown) { if (!isRequestError(error)) { if (error instanceof Error) { @@ -34,6 +37,12 @@ function onError(error: unknown) { return new Error("Unknown error"); } + if (skipRetryingErrors.includes(error.statusCode)) { + return { + skipRetrying: true, + }; + } + return new Error(error.message); } @@ -100,7 +109,7 @@ export class Resend implements TriggerIntegration { if (!this._io) throw new Error("No IO"); if (!this._connectionKey) throw new Error("No connection key"); - return this._io.runTask( + return this._io.runTask( key, (task, io) => { if (!this._client) throw new Error("No client"); diff --git a/integrations/sendgrid/CHANGELOG.md b/integrations/sendgrid/CHANGELOG.md index d949d7431..9086f33bd 100644 --- a/integrations/sendgrid/CHANGELOG.md +++ b/integrations/sendgrid/CHANGELOG.md @@ -1,5 +1,33 @@ # @trigger.dev/sendgrid +## 2.2.0 + +### Patch Changes + +- Updated dependencies [975c5f1d] + - @trigger.dev/integration-kit@2.2.0 + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + - @trigger.dev/integration-kit@2.1.9 + +## 2.1.8 + +### Patch Changes + +- 6a992a19: First release of `@trigger.dev/replicate` integration with remote callback support. +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + - @trigger.dev/integration-kit@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/integrations/sendgrid/package.json b/integrations/sendgrid/package.json index e5fbca810..82a18cd82 100644 --- a/integrations/sendgrid/package.json +++ b/integrations/sendgrid/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/sendgrid", - "version": "2.1.7", + "version": "2.2.0", "description": "Trigger.dev integration for @sendgrid/mail", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -27,8 +27,8 @@ }, "dependencies": { "@sendgrid/mail": "^7.7.0", - "@trigger.dev/sdk": "workspace:^2.1.7", - "@trigger.dev/integration-kit": "workspace:^2.1.7" + "@trigger.dev/sdk": "workspace:^2.2.0", + "@trigger.dev/integration-kit": "workspace:^2.2.0" }, "engines": { "node": ">=16.8.0" diff --git a/integrations/sendgrid/src/index.ts b/integrations/sendgrid/src/index.ts index 49a1a55f2..d08a39379 100644 --- a/integrations/sendgrid/src/index.ts +++ b/integrations/sendgrid/src/index.ts @@ -70,7 +70,7 @@ export class SendGrid implements TriggerIntegration { if (!this._io) throw new Error("No IO"); if (!this._connectionKey) throw new Error("No connection key"); - return this._io.runTask( + return this._io.runTask( key, (task, io) => { if (!this._client) throw new Error("No client"); diff --git a/integrations/slack/CHANGELOG.md b/integrations/slack/CHANGELOG.md index c8f1de0ec..8f52872fa 100644 --- a/integrations/slack/CHANGELOG.md +++ b/integrations/slack/CHANGELOG.md @@ -1,5 +1,31 @@ # @trigger.dev/slack +## 2.2.0 + +### Patch Changes + +- Updated dependencies [975c5f1d] + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- 9a187f9e: upgrade zod to 3.22.3 +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + +## 2.1.8 + +### Patch Changes + +- 6a992a19: First release of `@trigger.dev/replicate` integration with remote callback support. +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/integrations/slack/package.json b/integrations/slack/package.json index b14d9295b..2e6238277 100644 --- a/integrations/slack/package.json +++ b/integrations/slack/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/slack", - "version": "2.1.7", + "version": "2.2.0", "description": "The official Slack integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -25,8 +25,8 @@ }, "dependencies": { "@slack/web-api": "^6.8.1", - "@trigger.dev/sdk": "workspace:^2.1.7", - "zod": "3.21.4" + "@trigger.dev/sdk": "workspace:^2.2.0", + "zod": "3.22.3" }, "engines": { "node": ">=16.8.0" diff --git a/integrations/slack/src/index.ts b/integrations/slack/src/index.ts index 9a8571d4c..4934a99d1 100644 --- a/integrations/slack/src/index.ts +++ b/integrations/slack/src/index.ts @@ -92,7 +92,7 @@ export class Slack implements TriggerIntegration { if (!this._io) throw new Error("No IO"); if (!this._connectionKey) throw new Error("No connection key"); - return this._io.runTask( + return this._io.runTask( key, (task, io) => { if (!this._client) throw new Error("No client"); diff --git a/integrations/stripe/CHANGELOG.md b/integrations/stripe/CHANGELOG.md index 65cea354d..464c73e3c 100644 --- a/integrations/stripe/CHANGELOG.md +++ b/integrations/stripe/CHANGELOG.md @@ -1,5 +1,33 @@ # @trigger.dev/stripe +## 2.2.0 + +### Patch Changes + +- Updated dependencies [975c5f1d] + - @trigger.dev/integration-kit@2.2.0 + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- 9a187f9e: upgrade zod to 3.22.3 +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + - @trigger.dev/integration-kit@2.1.9 + +## 2.1.8 + +### Patch Changes + +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + - @trigger.dev/integration-kit@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/integrations/stripe/package.json b/integrations/stripe/package.json index a66585039..75e7ce2f1 100644 --- a/integrations/stripe/package.json +++ b/integrations/stripe/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/stripe", - "version": "2.1.7", + "version": "2.2.0", "description": "Trigger.dev integration for stripe", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -26,10 +26,10 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@trigger.dev/integration-kit": "workspace:^2.1.7", - "@trigger.dev/sdk": "workspace:^2.1.7", + "@trigger.dev/integration-kit": "workspace:^2.2.0", + "@trigger.dev/sdk": "workspace:^2.2.0", "stripe": "^12.14.0", - "zod": "3.21.4" + "zod": "3.22.3" }, "engines": { "node": ">=16.8.0" diff --git a/integrations/supabase/CHANGELOG.md b/integrations/supabase/CHANGELOG.md index 3c35fd456..6206167ed 100644 --- a/integrations/supabase/CHANGELOG.md +++ b/integrations/supabase/CHANGELOG.md @@ -1,5 +1,33 @@ # @trigger.dev/supabase +## 2.2.0 + +### Patch Changes + +- Updated dependencies [975c5f1d] + - @trigger.dev/integration-kit@2.2.0 + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- 9a187f9e: upgrade zod to 3.22.3 +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + - @trigger.dev/integration-kit@2.1.9 + +## 2.1.8 + +### Patch Changes + +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + - @trigger.dev/integration-kit@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/integrations/supabase/package.json b/integrations/supabase/package.json index 4600c57ad..1bf316418 100644 --- a/integrations/supabase/package.json +++ b/integrations/supabase/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/supabase", - "version": "2.1.7", + "version": "2.2.0", "description": "Trigger.dev integration for @supabase/supabase-js", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -27,10 +27,10 @@ }, "dependencies": { "@supabase/supabase-js": "^2.26.0", - "@trigger.dev/integration-kit": "workspace:^2.1.7", - "@trigger.dev/sdk": "workspace:^2.1.7", + "@trigger.dev/integration-kit": "workspace:^2.2.0", + "@trigger.dev/sdk": "workspace:^2.2.0", "supabase-management-js": "^0.1.4", - "zod": "3.21.4" + "zod": "3.22.3" }, "engines": { "node": ">=18.0.0" diff --git a/integrations/typeform/CHANGELOG.md b/integrations/typeform/CHANGELOG.md index d703453ac..9f89d3fae 100644 --- a/integrations/typeform/CHANGELOG.md +++ b/integrations/typeform/CHANGELOG.md @@ -1,5 +1,33 @@ # @trigger.dev/typeform +## 2.2.0 + +### Patch Changes + +- Updated dependencies [975c5f1d] + - @trigger.dev/integration-kit@2.2.0 + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- 9a187f9e: upgrade zod to 3.22.3 +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + - @trigger.dev/integration-kit@2.1.9 + +## 2.1.8 + +### Patch Changes + +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + - @trigger.dev/integration-kit@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/integrations/typeform/package.json b/integrations/typeform/package.json index 8c5a8d306..689ba1ef4 100644 --- a/integrations/typeform/package.json +++ b/integrations/typeform/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/typeform", - "version": "2.1.7", + "version": "2.2.0", "description": "The official Typeform integration for Trigger.dev", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -25,10 +25,10 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@trigger.dev/integration-kit": "workspace:^2.2.0", + "@trigger.dev/sdk": "workspace:^2.2.0", "@typeform/api-client": "^1.8.0", - "@trigger.dev/sdk": "workspace:^2.1.7", - "@trigger.dev/integration-kit": "workspace:^2.1.7", - "zod": "3.21.4" + "zod": "3.22.3" }, "engines": { "node": ">=16.8.0" diff --git a/packages/astro/CHANGELOG.md b/packages/astro/CHANGELOG.md index 5f9ca09e8..840cd2888 100644 --- a/packages/astro/CHANGELOG.md +++ b/packages/astro/CHANGELOG.md @@ -1,5 +1,34 @@ # @trigger.dev/astro +## 2.2.0 + +### Minor Changes + +- 975c5f1d: Drop support for Node v16, require Node >= 18. This allows us to use native fetch in our SDK which paves the way for multi-platform support. + +### Patch Changes + +- Updated dependencies [975c5f1d] + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + +## 2.1.8 + +### Patch Changes + +- ab9e4a98: Send client version back to the server via headers +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/packages/astro/package.json b/packages/astro/package.json index 328000d23..6f8bf02e2 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -1,7 +1,7 @@ { "name": "@trigger.dev/astro", "description": "An Astro-native integration for Trigger.dev background jobs platform", - "version": "2.1.7", + "version": "2.2.0", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ @@ -20,7 +20,7 @@ "build:tsup": "tsup" }, "peerDependencies": { - "@trigger.dev/sdk": "workspace:^2.1.7" + "@trigger.dev/sdk": "workspace:^2.2.0" }, "devDependencies": { "astro": "^3.0.12", @@ -33,7 +33,7 @@ "typescript": "^4.8.4" }, "engines": { - "node": ">=16.8.0" + "node": ">=18.0.0" }, "license": "MIT", "publishConfig": { diff --git a/packages/astro/src/index.ts b/packages/astro/src/index.ts index c90ff6ae0..e399aa0d7 100644 --- a/packages/astro/src/index.ts +++ b/packages/astro/src/index.ts @@ -42,6 +42,7 @@ export function createAstroRoute(client: TriggerClient) { // execution's response body return new Response(JSON.stringify(response.body), { status: response.status, + headers: response.headers, }); } catch (err) { return new Response(JSON.stringify({ error: "Internal server error" }), { diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index f93dc7de8..a2b8fe86b 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,31 @@ # create-trigger +## 2.2.0 + +### Minor Changes + +- 975c5f1d: Drop support for Node v16, require Node >= 18. This allows us to use native fetch in our SDK which paves the way for multi-platform support. + +### Patch Changes + +- c070e78d: allow users to update trigger-dev packages to a specific version +- 50e3d9e4: When indexing user's jobs errors are now stored and displayed +- be9b5113: allow CLI dev to use injected environment variable +- b12bc604: remove unused envFile param in sendEvent.ts cmd +- Updated dependencies [975c5f1d] +- Updated dependencies [50e3d9e4] +- Updated dependencies [59a94c71] + - @trigger.dev/core@2.2.0 + +## 2.1.9 + +### Patch Changes + +- 9a187f9e: upgrade zod to 3.22.3 +- 914745f6: Removed log when a file is changed + +## 2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 0b7749bd5..b87ac8dbe 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/cli", - "version": "2.1.7", + "version": "2.2.0", "description": "The Trigger.dev CLI", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -56,16 +56,19 @@ "test": "vitest" }, "dependencies": { + "@trigger.dev/core": "workspace:*", "@types/degit": "^2.8.3", "boxen": "^7.1.1", "chalk": "^5.2.0", "chokidar": "^3.5.3", "commander": "^9.4.1", + "console-table-printer": "^2.11.2", "degit": "^2.8.4", "dotenv": "^16.3.1", "execa": "^7.0.0", "gradient-string": "^2.0.2", "inquirer": "^9.1.4", + "liquidjs": "^10.9.2", "localtunnel": "^2.0.2", "mock-fs": "^5.2.0", "nanoid": "^4.0.2", @@ -74,6 +77,7 @@ "npm-check-updates": "^16.12.2", "openai": "^4.5.0", "ora": "^6.1.2", + "p-retry": "^6.1.0", "path-to-regexp": "^6.2.1", "posthog-node": "^3.1.1", "proxy-agent": "^6.3.0", @@ -81,9 +85,9 @@ "terminal-link": "^3.0.0", "tsconfck": "^2.1.2", "url": "^0.11.1", - "zod": "3.21.4" + "zod": "3.22.3" }, "engines": { - "node": ">=16.8.0" + "node": ">=18.0.0" } } diff --git a/packages/cli/src/cli/index.ts b/packages/cli/src/cli/index.ts index da8b68590..cc3bc8713 100644 --- a/packages/cli/src/cli/index.ts +++ b/packages/cli/src/cli/index.ts @@ -14,7 +14,10 @@ import { checkApiKeyIsDevServer } from "../utils/getApiKeyType"; export const program = new Command(); -program.name(COMMAND_NAME).description("The Trigger.dev CLI").version("0.0.1"); +program + .name(COMMAND_NAME) + .description("The Trigger.dev CLI") + .version(getVersion(), "-v, --version", "Display the version number"); program .command("init") @@ -93,10 +96,13 @@ program program .command("update") - .description("Updates all @trigger.dev/* packages to their latest compatible versions") + .description( + "Updates all @trigger.dev/* packages to their latest compatible versions or the specified version" + ) .argument("[path]", "The path to the directory that contains the package.json file", ".") - .action(async (path) => { - await updateCommand(path); + .option("--to ", "The version to update to (ex: 2.1.4)", "latest") + .action(async (path, options) => { + await updateCommand(path, options); }); program diff --git a/packages/cli/src/commands/createIntegration.ts b/packages/cli/src/commands/createIntegration.ts index 312daab0f..ebe1f0c27 100644 --- a/packages/cli/src/commands/createIntegration.ts +++ b/packages/cli/src/commands/createIntegration.ts @@ -9,7 +9,8 @@ import { generateIntegrationFiles } from "../utils/generateIntegrationFiles"; import { getPackageName } from "../utils/getPackagName"; import { installDependencies } from "../utils/installDependencies"; import { logger } from "../utils/logger"; -import { resolvePath } from "../utils/parseNameAndPath"; +import { relativePath, resolvePath } from "../utils/parseNameAndPath"; +import { createIntegrationFileFromTemplate } from "../utils/createIntegrationFileFromTemplate"; const CLIOptionsSchema = z.object({ packageName: z.string().optional(), @@ -91,108 +92,107 @@ export async function createIntegrationCommand(path: string, cliOptions: any) { process.exit(1); } - // Create the package.json - const packageJson = { - name: resolvedOptions.packageName, - version: "0.0.1", - description: `Trigger.dev integration for ${resolvedOptions.sdkPackage}`, - main: "./dist/index.js", - types: "./dist/index.d.ts", - publishConfig: { - access: "public", - }, - files: ["dist/index.js", "dist/index.d.ts", "dist/index.js.map"], - devDependencies: { - "@types/node": "16.x", - rimraf: "^3.0.2", - tsup: "7.1.x", - typescript: "4.9.4", - }, - scripts: { - clean: "rimraf dist", - build: "npm run clean && npm run build:tsup", - "build:tsup": "tsup", - typecheck: "tsc --noEmit", - }, - dependencies: { - [latestVersion.name]: `^${latestVersion.version}`, - [sdkVersion.name]: sdkVersion.version, - [integrationKitVersion.name]: integrationKitVersion.version, - }, - engines: { - node: ">=16.8.0", - }, + const integrationVersion = await getInternalOrExternalPackageVersion({ + path: "integrations/github", + packageName: "@trigger.dev/github", + tag: "latest", + monorepoPath: triggerMonorepoPath, + prependWorkspace: false, + }); + + if (!integrationVersion) { + logger.error( + `Could not find the latest version of @trigger.dev/github. Please try again later.` + ); + + process.exit(1); + } + + const baseVariables = { + packageName: resolvedOptions.packageName, + sdkPackage: resolvedOptions.sdkPackage, + integrationVersion: integrationVersion, + latestVersion: latestVersion, + sdkVersion: sdkVersion, + integrationKitVersion: integrationKitVersion, + triggerMonorepoPath, }; - await createFileInPath(resolvedPath, "package.json", JSON.stringify(packageJson, null, 2)); + const getOutputPath = (relativePath: string) => pathModule.join(resolvedPath, relativePath); - // Create the tsconfig.json - const tsconfigJson = { - compilerOptions: { - composite: false, - declaration: false, - declarationMap: false, - esModuleInterop: true, - forceConsistentCasingInFileNames: true, - inlineSources: false, - isolatedModules: true, - moduleResolution: "node16", - noUnusedLocals: false, - noUnusedParameters: false, - preserveWatchOutput: true, - skipLibCheck: true, - strict: true, - experimentalDecorators: true, - emitDecoratorMetadata: true, - sourceMap: true, - resolveJsonModule: true, - lib: ["es2019"], - module: "commonjs", - target: "es2021", + const miscIntegrationFiles = [ + { + relativeTemplatePath: "package.json.j2", + outputPath: getOutputPath("package.json"), }, - include: ["./src/**/*.ts", "tsup.config.ts"], - exclude: ["node_modules"], - }; - - await createFileInPath(resolvedPath, "tsconfig.json", JSON.stringify(tsconfigJson, null, 2)); - - const readme = ` -# ${resolvedOptions.packageName} - `; - - await createFileInPath(resolvedPath, "README.md", readme); - - // Create the tsup.config.ts - const tsupConfig = ` -import { defineConfig } from "tsup"; - -export default defineConfig([ - { - name: "main", - entry: ["./src/index.ts"], - outDir: "./dist", - platform: "node", - format: ["cjs"], - legacyOutput: true, - sourcemap: true, - clean: true, - bundle: true, - splitting: false, - dts: true, - treeshake: { - preset: "smallest", + { + // use `tsc --showConfig` to update external tsconfig + relativeTemplatePath: `tsconfig-${triggerMonorepoPath ? "internal" : "external"}.json.j2`, + outputPath: getOutputPath("tsconfig.json"), }, - esbuildPlugins: [], - external: ["http", "https", "util", "events", "tty", "os", "timers"], - }, -]); + { + relativeTemplatePath: `tsup.config-${triggerMonorepoPath ? "internal" : "external"}.js.j2`, + outputPath: getOutputPath("tsup.config.ts"), + }, + { + relativeTemplatePath: "README.md.j2", + outputPath: getOutputPath("README.md"), + }, + ]; -`; - - await createFileInPath(resolvedPath, "tsup.config.ts", tsupConfig); + await createIntegrationFiles(miscIntegrationFiles, baseVariables); + // create src/* if (resolvedOptions.skipGeneratingCode) { - await createFileInPath(resolvedPath, "src/index.ts", "export {}"); + const getSrcOutputPath = (relativePath: string) => + getOutputPath(pathModule.join("src", relativePath)); + + const srcIntegrationFiles = [ + { + relativeTemplatePath: pathModule.join("payload-examples", "index.js.j2"), + outputPath: getSrcOutputPath(pathModule.join("payload-examples", "index.ts")), + }, + { + relativeTemplatePath: "events.js.j2", + outputPath: getSrcOutputPath("events.ts"), + }, + { + relativeTemplatePath: "index.js.j2", + outputPath: getSrcOutputPath("index.ts"), + }, + { + relativeTemplatePath: "models.js.j2", + outputPath: getSrcOutputPath("models.ts"), + }, + { + relativeTemplatePath: "schemas.js.j2", + outputPath: getSrcOutputPath("schemas.ts"), + }, + { + relativeTemplatePath: "types.js.j2", + outputPath: getSrcOutputPath("types.ts"), + }, + { + relativeTemplatePath: "utils.js.j2", + outputPath: getSrcOutputPath("utils.ts"), + }, + { + relativeTemplatePath: "webhooks.js.j2", + outputPath: getSrcOutputPath("webhooks.ts"), + }, + ]; + + const validIdentifier = pathModule + .basename(path) + .replace(/[^a-zA-Z0-9]+/g, "") + .replace(/^[0-9]+/g, ""); + + await createIntegrationFiles(srcIntegrationFiles, { + ...baseVariables, + apiKeyPropertyName: "apiKey", // TODO: prompt for this + authMethod: resolvedOptions.authMethod, + identifier: validIdentifier.length ? validIdentifier : "packageName", + }); } else { await attemptToGenerateIntegrationFiles(pathModule.join(resolvedPath, "src"), resolvedOptions); } @@ -295,8 +295,9 @@ const resolveOptionsWithPrompts = async ( resolvedOptions.skipGeneratingCode = true; } + resolvedOptions.authMethod = await promptAuthMethod(); + if (!resolvedOptions.skipGeneratingCode) { - resolvedOptions.authMethod = await promptAuthMethod(); resolvedOptions.extraInfo = await promptExtraInfo(); } } catch (err) { @@ -448,11 +449,13 @@ async function getInternalOrExternalPackageVersion({ tag, path, monorepoPath, + prependWorkspace = true, }: { packageName: string; tag: string; path: string; monorepoPath?: string; + prependWorkspace?: boolean; }): Promise<{ name: string; version: string } | undefined> { if (!monorepoPath) { return await getLatestPackageVersion(packageName, tag); @@ -470,7 +473,7 @@ async function getInternalOrExternalPackageVersion({ return { name: packageJson.name, - version: `workspace:^${packageJson.version}`, + version: `${prependWorkspace ? "workspace:^" : ""}${packageJson.version}`, }; } @@ -550,3 +553,26 @@ async function updateJobCatalogWithNewIntegration( }; await writeJSONFile(tsConfigPath, newTsConfig); } + +const createIntegrationFiles = async ( + files: { + relativeTemplatePath: string; + outputPath: string; + }[], + variables?: Record +) => { + for (const file of files) { + const result = await createIntegrationFileFromTemplate({ ...file, variables }); + handleCreateResult(file.outputPath, result); + } +}; + +const handleCreateResult = ( + outputPath: string, + result: Awaited> +) => { + if (!result.success) { + throw new Error(`Failed to create ${pathModule.basename(outputPath)}: ${result.error}`); + } + logger.success(`βœ” Created ${pathModule.basename(outputPath)} at ${relativePath(outputPath)}`); +}; diff --git a/packages/cli/src/commands/dev.ts b/packages/cli/src/commands/dev.ts index 1984bb15f..3224c24d3 100644 --- a/packages/cli/src/commands/dev.ts +++ b/packages/cli/src/commands/dev.ts @@ -1,24 +1,24 @@ -import chalk from "chalk"; +import boxen from "boxen"; import childProcess from "child_process"; import chokidar from "chokidar"; -import fs from "fs/promises"; import ngrok from "ngrok"; -import { run as ncuRun } from "npm-check-updates"; import ora, { Ora } from "ora"; -import pathModule from "path"; +import pRetry, { AbortError } from "p-retry"; import util from "util"; import { z } from "zod"; -import { Framework, getFramework } from "../frameworks"; +import { Framework } from "../frameworks"; +import { standardWatchFilePaths, standardWatchIgnoreRegex } from "../frameworks/watchConfig"; import { telemetryClient } from "../telemetry/telemetry"; import { getEnvFilename } from "../utils/env"; import fetch from "../utils/fetchUseProxy"; import { getTriggerApiDetails } from "../utils/getTriggerApiDetails"; -import { getUserPackageManager } from "../utils/getUserPkgManager"; +import { JsRuntime, getJsRuntime } from "../utils/jsRuntime"; import { logger } from "../utils/logger"; import { resolvePath } from "../utils/parseNameAndPath"; import { RequireKeys } from "../utils/requiredKeys"; +import { Throttle } from "../utils/throttle"; import { TriggerApi } from "../utils/triggerApi"; -import { standardWatchIgnoreRegex, standardWatchFilePaths } from "../frameworks/watchConfig"; +import { wait } from "../utils/wait"; const asyncExecFile = util.promisify(childProcess.execFile); @@ -41,6 +41,8 @@ const formattedDate = new Intl.DateTimeFormat("en", { second: "numeric", }); +let runtime: JsRuntime; + export async function devCommand(path: string, anyOptions: any) { telemetryClient.dev.started(path, anyOptions); @@ -53,12 +55,12 @@ export async function devCommand(path: string, anyOptions: any) { const options = result.data; const resolvedPath = resolvePath(path); - + runtime = await getJsRuntime(resolvedPath, logger); //check for outdated packages, don't await this - checkForOutdatedPackages(resolvedPath); + runtime.checkForOutdatedPackages(); // Read from package.json to get the endpointId - const endpointId = await getEndpointIdFromPackageJson(resolvedPath, options); + const endpointId = await getEndpointId(runtime, options.clientId); if (!endpointId) { logger.error( "You must run the `init` command first to setup the project – you are missing \n'trigger.dev': { 'endpointId': 'your-client-id' } from your package.json file, or pass in the --client-id option to this command" @@ -69,8 +71,8 @@ export async function devCommand(path: string, anyOptions: any) { logger.success(`βœ”οΈ [trigger.dev] Detected TriggerClient id: ${endpointId}`); //resolve the options using the detected framework (use default if there isn't a matching framework) - const packageManager = await getUserPackageManager(resolvedPath); - const framework = await getFramework(resolvedPath, packageManager); + const packageManager = await runtime.getUserPackageManager(); + const framework = await runtime.getFramework(); const resolvedOptions = await resolveOptions(framework, resolvedPath, options); // Read from .env.local or .env to get the TRIGGER_API_KEY and TRIGGER_API_URL @@ -79,14 +81,14 @@ export async function devCommand(path: string, anyOptions: any) { telemetryClient.dev.failed("missing_api_key", resolvedOptions); return; } - const { apiUrl, envFile, apiKey } = apiDetails; - logger.success(`βœ”οΈ [trigger.dev] Found API Key in ${envFile} file`); + const { apiUrl, apiKey, apiKeySource } = apiDetails; + logger.success(`βœ”οΈ [trigger.dev] Found API Key in ${apiKeySource}`); //verify that the endpoint can be reached const verifiedEndpoint = await verifyEndpoint(resolvedOptions, endpointId, apiKey, framework); if (!verifiedEndpoint) { logger.error( - `βœ– [trigger.dev] Failed to find a valid Trigger.dev endpoint. Make sure your app is running and try again.` + `βœ– [trigger.dev] Your endpoint couldn't be verified. Make sure your app is running and try again. ${resolvedOptions.handlerPath}` ); logger.info(` [trigger.dev] You can use -H to specify a hostname, or -p to specify a port.`); telemetryClient.dev.failed("no_server_found", resolvedOptions); @@ -107,83 +109,6 @@ export async function devCommand(path: string, anyOptions: any) { const endpointHandlerUrl = `${endpointUrl}${handlerPath}`; telemetryClient.dev.tunnelRunning(path, resolvedOptions); - const connectingSpinner = ora(`[trigger.dev] Registering endpoint ${endpointHandlerUrl}...`); - - //refresh function - let hasConnected = false; - let attemptCount = 0; - const refresh = async () => { - connectingSpinner.start(); - - const refreshedEndpointId = await getEndpointIdFromPackageJson(resolvedPath, resolvedOptions); - - // Read from env file to get the TRIGGER_API_KEY and TRIGGER_API_URL - const apiDetails = await getTriggerApiDetails(resolvedPath, envFile); - - if (!apiDetails) { - connectingSpinner.fail(`[trigger.dev] Failed to connect: Missing API Key`); - logger.info(`Will attempt again on the next file change…`); - attemptCount = 0; - return; - } - - const { apiKey, apiUrl } = apiDetails; - const apiClient = new TriggerApi(apiKey, apiUrl); - - const authorizedKey = await apiClient.whoami(apiKey); - if (!authorizedKey) { - logger.error( - `βœ– [trigger.dev] The API key you provided is not authorized. Try visiting your dashboard to get a new API key.` - ); - - telemetryClient.dev.failed("invalid_api_key", resolvedOptions); - return; - } - - telemetryClient.identify( - authorizedKey.organization.id, - authorizedKey.project.id, - authorizedKey.userId - ); - - const result = await refreshEndpoint( - apiClient, - refreshedEndpointId ?? endpointId, - endpointHandlerUrl - ); - if (result.success) { - attemptCount = 0; - connectingSpinner.succeed( - `[trigger.dev] πŸ”„ Refreshed ${refreshedEndpointId ?? endpointId} ${formattedDate.format( - new Date(result.data.updatedAt) - )}` - ); - - if (!hasConnected) { - hasConnected = true; - telemetryClient.dev.connected(path, resolvedOptions); - } - } else { - attemptCount++; - - if (attemptCount === 10 || !result.retryable) { - connectingSpinner.fail(`Failed to connect: ${result.error}`); - logger.info(`Will attempt again on the next file change…`); - attemptCount = 0; - - if (!hasConnected) { - telemetryClient.dev.failed("failed_to_connect", resolvedOptions); - } - return; - } - - const delay = backoff(attemptCount); - // console.log(`Attempt: ${attemptCount}`, delay); - await wait(delay); - refresh(); - } - }; - // Watch for changes to files and refresh endpoints const watchPaths = (framework?.watchFilePaths ?? standardWatchFilePaths).map( (path) => `${resolvedPath}/${path}` @@ -195,13 +120,177 @@ export async function devCommand(path: string, anyOptions: any) { ignoreInitial: true, }); + const connectingSpinner = ora(`[trigger.dev] Registering endpoint ${endpointHandlerUrl}...`); + let hasConnected = false; + const abortController = new AbortController(); + + const r = () => { + refresh({ + endpointId, + spinner: connectingSpinner, + path: resolvedPath, + endpointHandlerUrl, + resolvedOptions, + hasConnected, + abortController, + }); + }; + + const throttle = new Throttle(r, throttleTimeMs); + watcher.on("all", (_event, _path) => { - console.log(_event, _path); - throttle(refresh, throttleTimeMs); + throttle.call(); }); //Do initial refresh - throttle(refresh, throttleTimeMs); + throttle.call(); +} + +type RefreshOptions = { + spinner: Ora; + path: string; + endpointId: string; + endpointHandlerUrl: string; + resolvedOptions: ResolvedOptions; + hasConnected: boolean; + abortController: AbortController; +}; + +async function refresh(options: RefreshOptions) { + //stop any existing refreshes + options.abortController.abort(); + options.abortController = new AbortController(); + + // Read from env file to get the TRIGGER_API_KEY and TRIGGER_API_URL + const apiDetails = await getTriggerApiDetails(options.path, options.resolvedOptions.envFile); + if (!apiDetails) { + options.spinner.fail("[trigger.dev] Failed to connect: Missing API Key"); + return; + } + + const { apiKey, apiUrl } = apiDetails; + const apiClient = new TriggerApi(apiKey, apiUrl); + + try { + const index = await pRetry(() => startIndexing({ ...options, apiClient }), { + retries: 5, + signal: options.abortController.signal, + maxTimeout: 5000, + }); + options.spinner.text = `[trigger.dev] Refreshing ${formattedDate.format(index.updatedAt)}`; + + if (!options.hasConnected) { + options.hasConnected = true; + telemetryClient.dev.connected(options.path, options.resolvedOptions); + } + + //this is for backwards-compatibility with older servers + if (index.id === undefined) { + options.spinner.succeed(`[trigger.dev] Refreshed ${formattedDate.format(index.updatedAt)}`); + return; + } + + //wait 750ms before attempting to get the indexing result + await wait(750); + + const indexResult = await pRetry(() => fetchIndexResult({ indexId: index.id, apiClient }), { + //this means we're polling, same distance between each attempt + factor: 1, + retries: 10, + signal: options.abortController.signal, + }); + + if (indexResult.status === "FAILURE") { + options.spinner.fail( + `[trigger.dev] Refreshing failed ${formattedDate.format(indexResult.updatedAt)}` + ); + logger.error( + boxen(indexResult.error.message, { + padding: 1, + borderStyle: "double", + }) + ); + return; + } + + options.spinner.succeed( + `[trigger.dev] Refreshed ${formattedDate.format(indexResult.updatedAt)}` + ); + } catch (e) { + if (e instanceof AbortError) { + options.spinner.fail(e.message); + logger.info(` [trigger.dev] Will attempt again on the next file change…`); + return; + } + + let message: string = ""; + if (e instanceof Error) { + message = e.message; + } else { + message = "Unknown error"; + } + + options.spinner.fail(message); + logger.info(` [trigger.dev] Will attempt again on the next file change…`); + + if (!options.hasConnected) { + telemetryClient.dev.failed("failed_to_connect", options.resolvedOptions); + } + } +} + +async function startIndexing({ + spinner, + path, + endpointId, + endpointHandlerUrl, + resolvedOptions, + apiClient, +}: RefreshOptions & { apiClient: TriggerApi }) { + spinner.start(); + const refreshedEndpointId = await getEndpointId(runtime, resolvedOptions.clientId); + + const authorizedKey = await apiClient.whoami(); + if (!authorizedKey) { + telemetryClient.dev.failed("invalid_api_key", resolvedOptions); + throw new AbortError( + "[trigger.dev] The API key you provided is not authorized. Try visiting your dashboard to get a new API key." + ); + } + + telemetryClient.identify( + authorizedKey.organization.id, + authorizedKey.project.id, + authorizedKey.userId + ); + + const result = await refreshEndpoint( + apiClient, + refreshedEndpointId ?? endpointId, + endpointHandlerUrl + ); + + if (!result.success) { + throw new Error(result.error); + } + + return { id: result.data.endpointIndex?.id, updatedAt: new Date(result.data.updatedAt) }; +} + +async function fetchIndexResult({ + indexId, + apiClient, +}: { + indexId: string; + apiClient: TriggerApi; +}) { + const result = await apiClient.getEndpointIndex(indexId); + + if (result.status === "STARTED" || result.status === "PENDING") { + throw new Error("Indexing is still in progress"); + } + + return result; } async function resolveOptions( @@ -210,7 +299,7 @@ async function resolveOptions( unresolvedOptions: DevCommandOptions ): Promise { if (!framework) { - logger.info("Failed to detect framework, using default values"); + logger.info(" [trigger.dev] Failed to detect framework, using default values"); return { port: unresolvedOptions.port ?? 3000, hostname: unresolvedOptions.hostname ?? "localhost", @@ -304,43 +393,10 @@ async function verifyEndpoint( return; } -export async function checkForOutdatedPackages(path: string) { - const updates = (await ncuRun({ - packageFile: `${path}/package.json`, - filter: "/trigger.dev/.+$/", - upgrade: false, - })) as { - [key: string]: string; - }; - - if (typeof updates === "undefined" || Object.keys(updates).length === 0) { - return; - } - - const packageFile = await fs.readFile(`${path}/package.json`); - const data = JSON.parse(Buffer.from(packageFile).toString("utf8")); - const dependencies = data.dependencies; - console.log(chalk.bgYellow("Updates available for trigger.dev packages")); - console.log(chalk.bgBlue("Run npx @trigger.dev/cli@latest update")); - - for (let dep in updates) { - console.log(`${dep} ${dependencies[dep]} β†’ ${updates[dep]}`); - } -} - -export async function getEndpointIdFromPackageJson(path: string, options: DevCommandOptions) { - if (options.clientId) { - return options.clientId; - } - - const pkgJsonPath = pathModule.join(path, "package.json"); - const pkgBuffer = await fs.readFile(pkgJsonPath); - const pkgJson = JSON.parse(pkgBuffer.toString()); - - const value = pkgJson["trigger.dev"]?.endpointId; - if (!value || typeof value !== "string") return; - - return value as string; +export function getEndpointId(runtime: JsRuntime, clientId?: string) { + if (clientId) { + return clientId; + } else return runtime.getEndpointId(); } async function resolveEndpointUrl(apiUrl: string, port: number, hostname: string) { @@ -432,25 +488,3 @@ async function refreshEndpoint(apiClient: TriggerApi, endpointId: string, endpoi } } } - -//wait function -async function wait(ms: number) { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); -} - -//throttle function -let throttleTimeout: NodeJS.Timeout | null = null; -function throttle(fn: () => any, delay: number) { - if (throttleTimeout) { - clearTimeout(throttleTimeout); - } - throttleTimeout = setTimeout(fn, delay); -} - -const maximum_backoff = 30; -const initial_backoff = 0.2; -function backoff(attempt: number) { - return Math.min((2 ^ attempt) * initial_backoff, maximum_backoff) * 1000; -} diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index bf934eed1..8bd7b0fbb 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -22,6 +22,7 @@ import { resolvePath } from "../utils/parseNameAndPath"; import { readPackageJson } from "../utils/readPackageJson"; import { renderTitle } from "../utils/renderTitle"; import { TriggerApi, WhoamiResponse } from "../utils/triggerApi"; +import { getJsRuntime } from "../utils/jsRuntime"; export type InitCommandOptions = { projectPath: string; @@ -38,6 +39,19 @@ export const initCommand = async (options: InitCommandOptions) => { const resolvedPath = resolvePath(options.projectPath); + // assuming nodejs by default + let runtimeId: string = "nodejs"; + try { + runtimeId = (await getJsRuntime(resolvedPath, logger)).id; + } catch {} + if (runtimeId !== "nodejs") { + logger.error( + `We currently only support automatic setup for NodeJS projects. This is a ${runtimeId} project. View our manual installation guides here: https://trigger.dev/docs/documentation/quickstarts/introduction` + ); + telemetryClient.init.failed("not_supported_runtime", options); + return; + } + await renderTitle(resolvedPath); if (options.triggerUrl === CLOUD_TRIGGER_URL) { @@ -81,7 +95,7 @@ export const initCommand = async (options: InitCommandOptions) => { } const apiClient = new TriggerApi(apiKey, optionsAfterPrompts.apiUrl); - const authorizedKey = await apiClient.whoami(apiKey); + const authorizedKey = await apiClient.whoami(); if (!authorizedKey) { logger.error( diff --git a/packages/cli/src/commands/sendEvent.ts b/packages/cli/src/commands/sendEvent.ts index 843fc71e6..b18b2fcdd 100644 --- a/packages/cli/src/commands/sendEvent.ts +++ b/packages/cli/src/commands/sendEvent.ts @@ -36,7 +36,7 @@ export async function sendEventCommand(path: string, anyOptions: any) { return; } - const { apiUrl, envFile, apiKey } = apiDetails; + const { apiUrl, apiKey } = apiDetails; const parsedPayload = safeJSONParse(options.payload); diff --git a/packages/cli/src/commands/update.ts b/packages/cli/src/commands/update.ts index ff6ed559f..e6a81c47d 100644 --- a/packages/cli/src/commands/update.ts +++ b/packages/cli/src/commands/update.ts @@ -4,8 +4,24 @@ import { run, RunOptions } from "npm-check-updates"; import { installDependencies } from "../utils/installDependencies.js"; import { readJSONFileSync, writeJSONFile } from "../utils/fileSystem.js"; import { logger } from "../utils/logger.js"; +import { z } from "zod"; + +export const UpdateCommandOptionsSchema = z.object({ + to: z.string().optional(), +}); + +export type UpdateCommandOptions = z.infer; + +type NcuRunOptionTarget = "latest" | `@${string}`; + +export async function updateCommand(projectPath: string, anyOptions: any) { + const parseRes = UpdateCommandOptionsSchema.safeParse(anyOptions); + if (!parseRes.success) { + logger.error(parseRes.error.message); + return; + } + const options = parseRes.data; -export async function updateCommand(projectPath: string) { const triggerDevPackage = "@trigger.dev"; const packageJSONPath = path.join(projectPath, "package.json"); const packageData = readJSONFileSync(packageJSONPath); @@ -27,12 +43,14 @@ export async function updateCommand(projectPath: string) { }; }); + const targetVersion = getTargetVersion(options.to); + // Use npm-check-updates to get updated dependency versions const ncuOptions: RunOptions = { packageData, upgrade: true, jsonUpgraded: true, - target: "latest", + target: targetVersion, }; // Can either give a json like package.json or just with deps and their new versions @@ -69,6 +87,39 @@ export async function updateCommand(projectPath: string) { return; } + let applyUpdates = targetVersion !== "latest"; + + if (targetVersion === "latest") { + applyUpdates = await hasUserConfirmed(packagesToUpdate, packageMaps, updatedDependencies); + } + + if (applyUpdates) { + const newPackageJSON = packageData; + packagesToUpdate.forEach((packageName) => { + const tmp = packageMaps[packageName]; + if (tmp) { + newPackageJSON[tmp.type][packageName] = updatedDependencies[packageName]; + } + }); + await writeJSONFile(packageJSONPath, newPackageJSON); + await installDependencies(projectPath); + } +} + +// expects a version number, or latest. +// if version number is specified, prepend it with '@' for ncu. +function getTargetVersion(toVersion?: string): NcuRunOptionTarget { + if (!toVersion) { + return "latest"; + } + return toVersion === "latest" ? "latest" : `@${toVersion}`; +} + +async function hasUserConfirmed( + packagesToUpdate: string[], + packageMaps: { [x: string]: { type: string; version: string } }, + updatedDependencies: { [x: string]: any } +): Promise { // Inform the user of the dependencies that can be updated console.log("\nNewer versions found for the following packages:"); console.table( @@ -86,15 +137,5 @@ export async function updateCommand(projectPath: string) { message: "Do you want to update these packages in package.json and re-install dependencies?", }); - if (confirm) { - const newPackageJSON = packageData; - packagesToUpdate.forEach((packageName) => { - const tmp = packageMaps[packageName]; - if (tmp) { - newPackageJSON[tmp.type][packageName] = updatedDependencies[packageName]; - } - }); - await writeJSONFile(packageJSONPath, newPackageJSON); - await installDependencies(projectPath); - } + return confirm; } diff --git a/packages/cli/src/commands/whoami.ts b/packages/cli/src/commands/whoami.ts index afe71cbad..315617e15 100644 --- a/packages/cli/src/commands/whoami.ts +++ b/packages/cli/src/commands/whoami.ts @@ -2,9 +2,10 @@ import { z } from "zod"; import { logger } from "../utils/logger"; import { resolvePath } from "../utils/parseNameAndPath"; import { TriggerApi } from "../utils/triggerApi"; -import { DevCommandOptions, getEndpointIdFromPackageJson } from "./dev"; +import { DevCommandOptions, getEndpointId } from "./dev"; import ora from "ora"; import { getTriggerApiDetails } from "../utils/getTriggerApiDetails"; +import { getJsRuntime } from "../utils/jsRuntime"; export const WhoAmICommandOptionsSchema = z.object({ envFile: z.string(), @@ -26,7 +27,8 @@ export async function whoamiCommand(path: string, anyOptions: any) { const resolvedPath = resolvePath(path); // Read from package.json to get the endpointId - const endpointId = await getEndpointIdFromPackageJson(resolvedPath, options as DevCommandOptions); + const runtime = await getJsRuntime(resolvedPath, logger); + const endpointId = await getEndpointId(runtime); if (!endpointId) { logger.error( "You must run the `init` command first to setup the project – you are missing \n'trigger.dev': { 'endpointId': 'your-client-id' } from your package.json file, or pass in the --client-id option to this command" @@ -42,7 +44,7 @@ export async function whoamiCommand(path: string, anyOptions: any) { } const triggerAPI = new TriggerApi(apiDetails.apiKey, apiDetails.apiUrl); - const userData = await triggerAPI.whoami(apiDetails.apiKey); + const userData = await triggerAPI.whoami(); loadingSpinner.stop(); diff --git a/packages/cli/src/templates/integration/README.md.j2 b/packages/cli/src/templates/integration/README.md.j2 new file mode 100644 index 000000000..f4e2541a2 --- /dev/null +++ b/packages/cli/src/templates/integration/README.md.j2 @@ -0,0 +1 @@ +# {{ packageName }} diff --git a/packages/cli/src/templates/integration/events.js.j2 b/packages/cli/src/templates/integration/events.js.j2 new file mode 100644 index 000000000..77d100907 --- /dev/null +++ b/packages/cli/src/templates/integration/events.js.j2 @@ -0,0 +1,118 @@ +import { EventSpecification } from "@trigger.dev/sdk"; +import { CommentEvent, IssueEvent } from "./schemas"; +import { Get{{ identifier | capitalize }}Payload } from "./types"; +import { + commentCreated, + commentRemoved, + commentUpdated, + issueCreated, + issueRemoved, + issueUpdated, +} from "./payload-examples"; +import { onCommentProperties, onIssueProperties, updatedFromProperties } from "./utils"; + +export const onComment: EventSpecification> = { + name: "Comment", + title: "On Comment", + source: "linear.app", + icon: "linear", + examples: [commentCreated, commentRemoved, commentUpdated], + parsePayload: (payload) => payload as Get{{ identifier | capitalize }}Payload, + runProperties: (payload) => [ + { label: "Event action", text: payload.action }, + ...onCommentProperties(payload), + ...updatedFromProperties(payload), + ], +}; + +export const onCommentCreated: EventSpecification> = { + name: "Comment", + title: "On Comment Created", + source: "linear.app", + icon: "linear", + filter: { + action: ["create"], + }, + examples: [commentCreated], + parsePayload: (payload) => payload as Get{{ identifier | capitalize }}Payload, + runProperties: (payload) => onCommentProperties(payload), +}; + +export const onCommentRemoved: EventSpecification> = { + name: "Comment", + title: "On Comment Removed", + source: "linear.app", + icon: "linear", + filter: { + action: ["remove"], + }, + examples: [commentRemoved], + parsePayload: (payload) => payload as Get{{ identifier | capitalize }}Payload, + runProperties: (payload) => onCommentProperties(payload), +}; + +export const onCommentUpdated: EventSpecification> = { + name: "Comment", + title: "On Comment Updated", + source: "linear.app", + icon: "linear", + filter: { + action: ["update"], + }, + examples: [commentUpdated], + parsePayload: (payload) => payload as Get{{ identifier | capitalize }}Payload, + runProperties: (payload) => [...onCommentProperties(payload), ...updatedFromProperties(payload)], +}; + +export const onIssue: EventSpecification> = { + name: "Issue", + title: "On Issue", + source: "linear.app", + icon: "linear", + examples: [issueCreated, issueRemoved, issueUpdated], + parsePayload: (payload) => payload as Get{{ identifier | capitalize }}Payload, + runProperties: (payload) => [ + { label: "Event action", text: payload.action }, + ...onIssueProperties(payload), + ...updatedFromProperties(payload), + ], +}; + +export const onIssueCreated: EventSpecification> = { + name: "Issue", + title: "On Issue Created", + source: "linear.app", + icon: "linear", + filter: { + action: ["create"], + }, + examples: [issueCreated], + parsePayload: (payload) => payload as Get{{ identifier | capitalize }}Payload, + runProperties: (payload) => onIssueProperties(payload), +}; + +export const onIssueRemoved: EventSpecification> = { + name: "Issue", + title: "On Issue Removed", + source: "linear.app", + icon: "linear", + filter: { + action: ["remove"], + }, + examples: [issueRemoved], + parsePayload: (payload) => payload as Get{{ identifier | capitalize }}Payload, + runProperties: (payload) => onIssueProperties(payload), +}; + +export const onIssueUpdated: EventSpecification> = { + name: "Issue", + title: "On Issue Updated", + source: "linear.app", + icon: "linear", + filter: { + action: ["update"], + }, + examples: [issueUpdated], + parsePayload: (payload) => payload as Get{{ identifier | capitalize }}Payload, + runProperties: (payload) => [...onIssueProperties(payload), ...updatedFromProperties(payload)], +}; diff --git a/packages/cli/src/templates/integration/index.js.j2 b/packages/cli/src/templates/integration/index.js.j2 new file mode 100644 index 000000000..289de3ee1 --- /dev/null +++ b/packages/cli/src/templates/integration/index.js.j2 @@ -0,0 +1,244 @@ +import { + TriggerIntegration, + RunTaskOptions, + IO, + IOTask, + IntegrationTaskKey, + RunTaskErrorCallback, + Json, + retry, + ConnectionAuth, + Prettify, +} from "@trigger.dev/sdk"; +import {{ identifier | capitalize }}Client from "{{ sdkPackage }}"; + +import * as events from "./events"; +import { {{ identifier | capitalize }}ReturnType, Serialized{{ identifier | capitalize }}Output } from "./types"; +import { TriggerParams, Webhooks, createTrigger, createWebhookEventSource } from "./webhooks"; +import { Models } from "./models"; + +export type {{ identifier | capitalize }}IntegrationOptions = { + id: string; + {{ apiKeyPropertyName }}: string; +}; + +export type {{ identifier | capitalize }}RunTask = InstanceType["runTask"]; + +export class {{ identifier | capitalize }}{{ " " }} implements TriggerIntegration { + private _options: {{ identifier | capitalize }}IntegrationOptions; + private _client?: any; + private _io?: IO; + private _connectionKey?: string; + + constructor(private options: {{ identifier | capitalize }}IntegrationOptions) { + if (Object.keys(options).includes("{{ apiKeyPropertyName }}") && !options.{{ apiKeyPropertyName }}) { + throw `Can't create {{ identifier | capitalize }} integration (${options.id}) as {{ apiKeyPropertyName }} was undefined`; + } + + this._options = options; + } + + get authSource() { + {% case authMethod %} + {% when "api-key" %} + return "LOCAL" as const; + {% when "oauth" %} + return "HOSTED" as const; + {% when "both-methods" %} + return this._options.{{ apiKeyPropertyName }} ? "LOCAL" : "HOSTED"; + {% endcase %} + } + + get id() { + return this.options.id; + } + + get metadata() { + return { id: "{{ identifier }}", name: "{{ identifier | capitalize }}" }; + } + + get source() { + return createWebhookEventSource(this); + } + + cloneForRun(io: IO, connectionKey: string, auth?: ConnectionAuth) { + const {{ identifier }} = new {{ identifier | capitalize }}(this._options); + {{ identifier }}._io = io; + {{ identifier }}._connectionKey = connectionKey; + {{ identifier }}._client = this.createClient(auth); + return {{ identifier }}; + } + + createClient(auth?: ConnectionAuth) { + // oauth + if (auth) { + return new {{ identifier | capitalize }}Client({ + auth: auth.accessToken, + }); + } + + // apiKey auth + if (this._options.{{ apiKeyPropertyName }}) { + return new {{ identifier | capitalize }}Client({ + apiKey: this._options.{{ apiKeyPropertyName }}, + }); + } + + throw new Error("No auth"); + } + + runTask | void>( + key: IntegrationTaskKey, + callback: (client: {{ identifier | capitalize }}Client, task: IOTask, io: IO) => Promise, + options?: RunTaskOptions, + errorCallback?: RunTaskErrorCallback + ): Promise { + if (!this._io) throw new Error("No IO"); + if (!this._connectionKey) throw new Error("No connection key"); + + return this._io.runTask( + key, + (task, io) => { + if (!this._client) throw new Error("No client"); + return callback(this._client, task, io); + }, + { + icon: "{{ identifier }}", + retry: retry.standardBackoff, + ...(options ?? {}), + connectionKey: this._connectionKey, + }, + errorCallback ?? onError + ); + } + + // top-level task + + request( + key: IntegrationTaskKey, + params: { + route: string | URL; + options: Parameters<{{ identifier | capitalize }}Client["request"]>[1]; + } + ): {{ identifier | capitalize }}ReturnType { + return this.runTask( + key, + async (client) => { + const response = await client.request(params.route, params.options); + + return response.json(); + }, + { + name: "Send Request", + params, + properties: [ + { label: "Route", text: params.route.toString() }, + ...(params.options.method ? [{ label: "Method", text: params.options.method }] : []), + ], + callback: { enabled: true }, + } + ); + } + + // nested tasks + + get models() { + return new Models(this.runTask.bind(this)); + } + + // events + + onComment(params: TriggerParams = {}) { + return createTrigger(this.source, events.onComment, params); + } + + onCommentCreated(params: TriggerParams = {}) { + return createTrigger(this.source, events.onCommentCreated, params); + } + + onCommentRemoved(params: TriggerParams = {}) { + return createTrigger(this.source, events.onCommentRemoved, params); + } + + onCommentUpdated(params: TriggerParams = {}) { + return createTrigger(this.source, events.onCommentUpdated, params); + } + + // triggers (webhooks) + + // private, just here to keep webhook logic in a separate file + get #webhooks() { + return new Webhooks(this.runTask.bind(this)); + } + + webhook = this.#webhooks.webhook; + webhooks = this.#webhooks.webhooks; + + createWebhook = this.#webhooks.createWebhook; + deleteWebhook = this.#webhooks.deleteWebhook; + updateWebhook = this.#webhooks.updateWebhook; +} + +class {{ identifier | capitalize }}ApiError extends Error { + constructor( + message: string, + readonly request: Request, + readonly response: Response + ) { + super(message); + this.name = "{{ identifier | capitalize }}ApiError"; + } +} + +function is{{ identifier | capitalize }}ApiError(error: unknown): error is {{ identifier | capitalize }}ApiError { + if (typeof error !== "object" || error === null) { + return false; + } + + const apiError = error as {{ identifier | capitalize }}ApiError; + + return ( + apiError.name === "{{ identifier | capitalize }}ApiError" && + apiError.request instanceof Request && + apiError.response instanceof Response + ); +} + +function shouldRetry(method: string, status: number) { + return status === 429 || (method === "GET" && status >= 500); +} + +export function onError(error: unknown): ReturnType { + if (!is{{ identifier | capitalize }}ApiError(error)) { + return; + } + + if (!shouldRetry(error.request.method, error.response.status)) { + return { + skipRetrying: true, + }; + } + + const rateLimitRemaining = error.response.headers.get("ratelimit-remaining"); + const rateLimitReset = error.response.headers.get("ratelimit-reset"); + + if (rateLimitRemaining === "0" && rateLimitReset) { + const resetDate = new Date(Number(rateLimitReset) * 1000); + + if (!Number.isNaN(resetDate.getTime())) { + return { + retryAt: resetDate, + error, + }; + } + } +} + +export const serialize{{ identifier | capitalize }}Output = (obj: T): Prettify> => { + return JSON.parse(JSON.stringify(obj), (key, value) => { + if (typeof value === "function" || key.startsWith("_")) { + return undefined; + } + return value; + }); +}; diff --git a/packages/cli/src/templates/integration/models.js.j2 b/packages/cli/src/templates/integration/models.js.j2 new file mode 100644 index 000000000..0d67c7952 --- /dev/null +++ b/packages/cli/src/templates/integration/models.js.j2 @@ -0,0 +1,79 @@ +import { IntegrationTaskKey } from "@trigger.dev/sdk"; +import { Model, ModelVersion } from "{{ sdkPackage }}"; + +import { {{ capitalizedIdentifier }}RunTask } from "./index"; +import { modelProperties } from "./utils"; +import { {{ capitalizedIdentifier }}ReturnType } from "./types"; + +export class Models { + constructor(private runTask: {{ capitalizedIdentifier }}RunTask) {} + + get( + key: IntegrationTaskKey, + params: { + model_owner: string; + model_name: string; + } + ): {{ capitalizedIdentifier }}ReturnType { + return this.runTask( + key, + (client) => { + return client.models.get(params.model_owner, params.model_name); + }, + { + name: "Get Model", + params, + properties: modelProperties(params), + } + ); + } + + get versions() { + return new Versions(this.runTask); + } +} + +class Versions { + constructor(private runTask: {{ capitalizedIdentifier }}RunTask) {} + + get( + key: IntegrationTaskKey, + params: { + model_owner: string; + model_name: string; + version_id: string; + } + ): {{ capitalizedIdentifier }}ReturnType { + return this.runTask( + key, + (client) => { + return client.models.versions.get(params.model_owner, params.model_name, params.version_id); + }, + { + name: "Get Model Version", + params, + properties: modelProperties(params), + } + ); + } + + list( + key: IntegrationTaskKey, + params: { + model_owner: string; + model_name: string; + } + ): {{ capitalizedIdentifier }}ReturnType { + return this.runTask( + key, + (client) => { + return client.models.versions.list(params.model_owner, params.model_name); + }, + { + name: "List Models", + params, + properties: modelProperties(params), + } + ); + } +} diff --git a/packages/cli/src/templates/integration/package.json.j2 b/packages/cli/src/templates/integration/package.json.j2 new file mode 100644 index 000000000..6e1391c90 --- /dev/null +++ b/packages/cli/src/templates/integration/package.json.j2 @@ -0,0 +1,40 @@ +{ + "name": "{{ packageName }}", + "version": "{{ integrationVersion.version }}", + "description": "Trigger.dev integration for {{ sdkPackage }}", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "publishConfig": { + "access": "public" + }, + "files": [ + "dist/index.js", + "dist/index.d.ts", + "dist/index.js.map" + ], + "devDependencies": { + {% if triggerMonorepoPath %} + "@trigger.dev/tsconfig": "workspace:*", + "@trigger.dev/tsup": "workspace:*", + {% endif %} + "@types/node": "16.x", + "rimraf": "^3.0.2", + "tsup": "7.1.x", + "typescript": "4.9.4" + }, + "scripts": { + "clean": "rimraf dist", + "build": "npm run clean && npm run build:tsup", + "build:tsup": "tsup", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "{{ latestVersion.name }}": "^{{ latestVersion.version }}", + "{{ sdkVersion.name }}": "{{ sdkVersion.version }}", + "{{ integrationKitVersion.name }}": "{{ integrationKitVersion.version }}", + "zod": "3.21.4" + }, + "engines": { + "node": ">=16.8.0" + } +} diff --git a/packages/cli/src/templates/integration/payload-examples/index.js.j2 b/packages/cli/src/templates/integration/payload-examples/index.js.j2 new file mode 100644 index 000000000..a35e18d3e --- /dev/null +++ b/packages/cli/src/templates/integration/payload-examples/index.js.j2 @@ -0,0 +1,40 @@ +import { EventSpecificationExample } from "@trigger.dev/sdk"; + +import CommentCreated from "./CommentCreated.json" +import CommentRemoved from "./CommentRemoved.json" +import CommentUpdated from "./CommentUpdated.json" +import IssueCreated from "./IssueCreated.json" +import IssueRemoved from "./IssueRemoved.json" +import IssueUpdated from "./IssueUpdated.json" + +export const commentCreated: EventSpecificationExample = { + id: "CommentCreated", + name: "Comment created", + payload: CommentCreated, +}; +export const commentRemoved: EventSpecificationExample = { + id: "CommentRemoved", + name: "Comment removed", + payload: CommentRemoved, +}; +export const commentUpdated: EventSpecificationExample = { + id: "CommentUpdated", + name: "Comment updated", + payload: CommentUpdated, +}; + +export const issueCreated: EventSpecificationExample = { + id: "IssueCreated", + name: "Issue created", + payload: IssueCreated, +}; +export const issueRemoved: EventSpecificationExample = { + id: "IssueRemoved", + name: "Issue removed", + payload: IssueRemoved, +}; +export const issueUpdated: EventSpecificationExample = { + id: "IssueUpdated", + name: "Issue updated", + payload: IssueUpdated, +}; diff --git a/packages/cli/src/templates/integration/schemas.js.j2 b/packages/cli/src/templates/integration/schemas.js.j2 new file mode 100644 index 000000000..9c51092e8 --- /dev/null +++ b/packages/cli/src/templates/integration/schemas.js.j2 @@ -0,0 +1,120 @@ +import { z } from "zod"; + +export const WebhookResourceTypeSchema = z.union([ + z.literal("Comment"), + z.literal("Issue"), +]); +export type WebhookResourceType = z.infer; + +export const WebhookActionTypeSchema = z.union([ + z.literal("create"), + z.literal("remove"), + z.literal("update"), +]); +export type WebhookActionType = z.infer; + +const IssueLabelDataSchema = z.object({ + archivedAt: z.coerce.date().optional().nullable(), + color: z.string(), + createdAt: z.coerce.date(), + creatorId: z.string().optional().nullable(), + description: z.string().optional().nullable(), + id: z.string(), + name: z.string(), + organizationId: z.string(), + parentId: z.string().optional().nullable(), + teamId: z.string().optional().nullable(), + updatedAt: z.coerce.date(), +}); + +const IssueDataSchema = z.object({ + archivedAt: z.coerce.date().optional().nullable(), + assignee: z.object({ id: z.string(), name: z.string() }).optional().nullable(), + assigneeId: z.string().optional().nullable(), + id: z.string(), + labelIds: z.array(z.string()), + labels: z.array(IssueLabelDataSchema.pick({ id: true, color: true, name: true })), + number: z.number(), + parentId: z.string().optional().nullable(), + previousIdentifiers: z.array(z.string()), + priority: z.number(), + priorityLabel: z.string(), + projectId: z.string().optional().nullable(), + sortOrder: z.number(), + team: z.object({ id: z.string(), key: z.string(), name: z.string() }), + teamId: z.string(), + title: z.string(), + trashed: z.boolean().optional().nullable(), + triagedAt: z.coerce.date().optional().nullable(), + updatedAt: z.coerce.date(), +}); + +const CommentDataSchema = z.object({ + archivedAt: z.coerce.date().optional().nullable(), + body: z.string(), + botActorId: z.string().optional().nullable(), + createdAt: z.coerce.date(), + editedAt: z.string().optional().nullable(), + id: z.string(), + issue: IssueDataSchema.pick({ id: true, title: true }), + issueId: z.string(), + parentId: z.string().optional().nullable(), + reactionData: z.array(z.object({}).passthrough()), + updatedAt: z.coerce.date(), + userId: z.string().optional().nullable(), +}); + +export const WebhookPayloadBaseSchema = z.object({ + createdAt: z.coerce.date(), + organizationId: z.string().optional().nullable(), + url: z.string().url().optional().nullable(), + webhookId: z.string(), + webhookTimestamp: z.coerce.date(), +}); + +const CREATE = z.literal("create"); +const REMOVE = z.literal("remove"); +const UPDATE = z.literal("update"); + +export const CommentEventBaseSchema = WebhookPayloadBaseSchema.extend({ + type: z.literal("Comment"), + data: CommentDataSchema, +}); +export const CommentEventSchema = z.discriminatedUnion("action", [ + CommentEventBaseSchema.extend({ + action: CREATE, + }), + CommentEventBaseSchema.extend({ + action: REMOVE, + }), + CommentEventBaseSchema.extend({ + action: UPDATE, + updatedFrom: CommentDataSchema.partial(), + }), +]); +export type CommentEvent = z.infer; + +export const IssueEventBaseSchema = WebhookPayloadBaseSchema.extend({ + type: z.literal("Issue"), + data: IssueDataSchema, +}); +export const IssueEventSchema = z.discriminatedUnion("action", [ + IssueEventBaseSchema.extend({ + action: CREATE, + }), + IssueEventBaseSchema.extend({ + action: REMOVE, + }), + IssueEventBaseSchema.extend({ + action: UPDATE, + updatedFrom: IssueDataSchema.partial(), + }), +]); +export type IssueEvent = z.infer; + +export const WebhookPayloadSchema = z.union([ + CommentEventSchema, + IssueEventSchema, +]); + +export type WebhookPayload = z.infer; diff --git a/packages/cli/src/templates/integration/tsconfig-external.json.j2 b/packages/cli/src/templates/integration/tsconfig-external.json.j2 new file mode 100644 index 000000000..2a94d35fa --- /dev/null +++ b/packages/cli/src/templates/integration/tsconfig-external.json.j2 @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "composite": false, + "declaration": false, + "declarationMap": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "inlineSources": false, + "isolatedModules": true, + "moduleResolution": "node", + "noUnusedLocals": false, + "noUnusedParameters": false, + "preserveWatchOutput": true, + "skipLibCheck": true, + "strict": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "sourceMap": true, + "resolveJsonModule": true, + "lib": ["dom", "dom.iterable", "es2019"], + "module": "commonjs", + "target": "es2021", + "stripInternal": true + }, + "include": ["./src/**/*.ts", "tsup.config.ts"], + "exclude": ["node_modules"] +} diff --git a/packages/cli/src/templates/integration/tsconfig-internal.json.j2 b/packages/cli/src/templates/integration/tsconfig-internal.json.j2 new file mode 100644 index 000000000..26ae70a15 --- /dev/null +++ b/packages/cli/src/templates/integration/tsconfig-internal.json.j2 @@ -0,0 +1,4 @@ +{ + "extends": "@trigger.dev/tsconfig/integration.json", + "include": ["./src/**/*.ts", "tsup.config.ts"] +} diff --git a/packages/cli/src/templates/integration/tsup.config-external.js.j2 b/packages/cli/src/templates/integration/tsup.config-external.js.j2 new file mode 100644 index 000000000..d1145e2b9 --- /dev/null +++ b/packages/cli/src/templates/integration/tsup.config-external.js.j2 @@ -0,0 +1,20 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + name: "main", + entry: ["./src/index.ts"], + outDir: "./dist", + platform: "node", + format: ["cjs"], + legacyOutput: true, + sourcemap: true, + clean: true, + bundle: true, + splitting: false, + dts: true, + treeshake: { + preset: "smallest", + }, + esbuildPlugins: [], + external: ["http", "https", "util", "events", "tty", "os", "timers"], +}); diff --git a/packages/cli/src/templates/integration/tsup.config-internal.js.j2 b/packages/cli/src/templates/integration/tsup.config-internal.js.j2 new file mode 100644 index 000000000..3071b229a --- /dev/null +++ b/packages/cli/src/templates/integration/tsup.config-internal.js.j2 @@ -0,0 +1,7 @@ +import { defineConfig, deepMergeOptions, integrationOptions } from "@trigger.dev/tsup"; + +const options = deepMergeOptions(integrationOptions, { + // extend base config here +}); + +export default defineConfig(options); diff --git a/packages/cli/src/templates/integration/types.js.j2 b/packages/cli/src/templates/integration/types.js.j2 new file mode 100644 index 000000000..1a7ff133a --- /dev/null +++ b/packages/cli/src/templates/integration/types.js.j2 @@ -0,0 +1,28 @@ +import { Request } from "{{ sdkPackage }}"; +import { WebhookActionType, WebhookPayload } from "./schemas"; + +export type Get{{ identifier | capitalize }}Payload< + TPayload extends WebhookPayload, + TAction extends any = any, +> = TAction extends WebhookActionType ? Extract : TPayload; + +type FunctionKeys = { + [K in keyof T]: T[K] extends Function ? K : never; +}[keyof T]; + +export type Serialized{{ identifier | capitalize }}Output = T extends object + ? T extends Array + ? Array> + : { [K in keyof T as Exclude | `_${string}`>]: Serialized{{ identifier | capitalize }}Output } + : T; + +export type {{ identifier | capitalize }}ReturnType< + TPayload extends Omit, + K extends unknown = unknown, +> = Promise< + Awaited>> +>; + +export type AwaitNested = Omit & { + [key in K]: Awaited; +}; diff --git a/packages/cli/src/templates/integration/utils.js.j2 b/packages/cli/src/templates/integration/utils.js.j2 new file mode 100644 index 000000000..f3fe00dc9 --- /dev/null +++ b/packages/cli/src/templates/integration/utils.js.j2 @@ -0,0 +1,77 @@ +import { CommentEvent, IssueEvent, WebhookPayload } from "./schemas"; +import { Get{{ identifier | capitalize }}Payload } from "./types"; + +export type QueryVariables = { + after: string; + before: string; + first: number; + includeArchived: boolean; + last: number; +}; + +export type Nullable = Partial<{ + [K in keyof T]: T[K] | null; +}>; + +export const onCommentProperties = (payload: Get{{ identifier | capitalize }}Payload) => { + return [ + { label: "Comment ID", text: payload.data.id }, + { label: "Issue ID", text: payload.data.issueId }, + { label: "Issue Title", text: payload.data.issue.title, url: payload.url ?? undefined }, + ]; +}; + +export const onIssueProperties = (payload: Get{{ identifier | capitalize }}Payload) => { + return [ + { label: "Issue ID", text: payload.data.id }, + { + label: "Issue", + text: `[${payload.data.team.key}-${payload.data.number}] ${payload.data.title}`, + url: payload.url ?? undefined, + }, + ]; +}; + +export const queryProperties = (query: Nullable) => { + return [ + ...(query.after ? [{ label: "After", text: query.after }] : []), + ...(query.before ? [{ label: "Before", text: query.before }] : []), + ...(query.first ? [{ label: "First", text: String(query.first) }] : []), + ...(query.last ? [{ label: "Last", text: String(query.last) }] : []), + ...(query.includeArchived + ? [{ label: "Include archived", text: String(query.includeArchived) }] + : []), + ]; +}; + +export const updatedFromProperties = (payload: WebhookPayload) => { + if (payload.action !== "update") return []; + return [ + { + label: "Updated Keys", + text: Object.keys(payload.updatedFrom) + .filter((key) => !["editedAt", "updatedAt"].includes(key)) + .join(", "), + }, + ]; +}; + +export const modelProperties = ( + params: Partial<{ + model_owner: string; + model_name: string; + version_id: string; + destination: string; + }> +) => { + return [ + ...(params.model_owner ? [{ label: "Model Owner", text: params.model_owner }] : []), + ...(params.model_name ? [{ label: "Model Name", text: params.model_name }] : []), + ...(params.version_id ? [{ label: "Model Version", text: params.version_id }] : []), + ...(params.destination ? [{ label: "Destination Model", text: params.destination }] : []), + ]; +}; + +export const streamingProperty = (params: { stream?: boolean }) => { + return [{ label: "Streaming Enabled", text: String(!!params.stream) }]; +}; diff --git a/packages/cli/src/templates/integration/webhooks.js.j2 b/packages/cli/src/templates/integration/webhooks.js.j2 new file mode 100644 index 000000000..9bb55e514 --- /dev/null +++ b/packages/cli/src/templates/integration/webhooks.js.j2 @@ -0,0 +1,297 @@ +import { + EventFilter, + ExternalSource, + ExternalSourceTrigger, + HandlerEvent, + IntegrationTaskKey, + Logger, +} from "@trigger.dev/sdk"; +import { + Document, + {{ identifier | capitalize }}Webhooks, + WebhookPayload, + DeletePayload, + Webhook, +} from "{{ sdkPackage }}"; +import { z } from "zod"; +import * as events from "./events"; +import { {{ identifier | capitalize }}, {{ identifier | capitalize }}RunTask, serialize{{ identifier | capitalize }}Output } from "./index"; +import { WebhookPayloadSchema } from "./schemas"; +import { {{ identifier | capitalize }}ReturnType } from "./types"; +import { queryProperties } from "./utils"; + +export class Webhooks { + runTask: {{ identifier | capitalize }}RunTask; + + constructor(runTask: {{ identifier | capitalize }}RunTask) { + this.runTask = runTask; + } + + webhook(key: IntegrationTaskKey, params: { id: string }): {{ identifier | capitalize }}ReturnType { + return this.runTask( + key, + async (client, task, io) => { + return serialize{{ identifier | capitalize }}Output(await client.webhook(params.id)); + }, + { + name: "Get Webhook", + params, + properties: [{ label: "Webhook ID", text: params.id }], + } + ); + } + + webhooks(key: IntegrationTaskKey, params?: Document.WebhooksQueryVariables): {{ identifier | capitalize }}ReturnType { + return this.runTask( + key, + async (client, task, io) => { + let connections = await client.webhooks(params); + const hooks = connections.nodes; + while (connections.pageInfo.hasNextPage) { + connections = await connections.fetchNext(); + hooks.push(...connections.nodes); + } + return serialize{{ identifier | capitalize }}Output(hooks); + }, + { + name: "List Webhooks", + params, + properties: queryProperties(params ?? {}), + } + ); + } + + createWebhook( + key: IntegrationTaskKey, + params: Document.WebhookCreateInput + ): {{ identifier | capitalize }}ReturnType & { webhook: Webhook | undefined }> { + return this.runTask( + key, + async (client, task, io) => { + const payload = await client.createWebhook({ ...params, allPublicTeams: !params.teamId }); + return serialize{{ identifier | capitalize }}Output({ + ...payload, + webhook: await payload.webhook, + }); + }, + { + name: "Create Webhook", + params, + properties: [ + { label: "Webhook URL", text: params.url }, + { label: "Resource Types", text: params.resourceTypes.join(", ") }, + ], + } + ); + } + + deleteWebhook(key: IntegrationTaskKey, params: { id: string }): {{ identifier | capitalize }}ReturnType { + return this.runTask( + key, + async (client, task, io) => { + return serialize{{ identifier | capitalize }}Output(await client.deleteWebhook(params.id)); + }, + { + name: "Delete Webhook", + params, + properties: [{ label: "Webhook ID", text: params.id }], + } + ); + } + + updateWebhook( + key: IntegrationTaskKey, + params: { id: string; input: Document.WebhookUpdateInput } + ): {{ identifier | capitalize }}ReturnType & { webhook: Webhook | undefined }> { + return this.runTask( + key, + async (client, task) => { + const payload = await client.updateWebhook(params.id, params.input); + return serialize{{ identifier | capitalize }}Output({ + ...payload, + webhook: await payload.webhook, + }); + }, + { + name: "Update Webhook", + params, + properties: [ + { label: "Webhook ID", text: params.id }, + ...(params.input.url ? [{ label: "Webhook URL", text: params.input.url }] : []), + ...(params.input.resourceTypes + ? [{ label: "Resource Types", text: params.input.resourceTypes.join(", ") }] + : []), + ], + } + ); + } +} + +type {{ identifier | capitalize }}Events = (typeof events)[keyof typeof events]; + +export type TriggerParams = { + teamId?: string; + filter?: EventFilter; +}; + +type CreateTriggersResult = ExternalSourceTrigger< + TEventSpecification, + ReturnType +>; + +export function createTrigger( + source: ReturnType, + event: TEventSpecification, + params: TriggerParams +): CreateTriggersResult { + return new ExternalSourceTrigger({ + event, + params, + source, + options: {}, + }); +} + +const WebhookRegistrationDataSchema = z.object({ + success: z.literal(true), + webhook: z.object({ + id: z.string(), + enabled: z.boolean(), + }), +}); + +export function createWebhookEventSource( + integration: {{ identifier | capitalize }} +): ExternalSource<{{ identifier | capitalize }}, TriggerParams, "HTTP", {}> { + return new ExternalSource("HTTP", { + id: "{{ identifier }}.webhook", + schema: z.object({ + teamId: z.string().optional(), + }), + version: "0.1.0", + integration, + key: (params) => `${params.teamId ? params.teamId : "all"}`, + handler: webhookHandler, + register: async (event, io, ctx) => { + const { params, source: httpSource, options } = event; + + // (key-specific) stored data, undefined if not registered yet + const webhookData = WebhookRegistrationDataSchema.safeParse(httpSource.data); + + // set of events to register + const allEvents = Array.from(new Set([...options.event.desired, ...options.event.missing])); + const registeredOptions = { + event: allEvents, + }; + + // easily identify webhooks on {{ identifier }} + const label = `trigger.${params.teamId ? params.teamId : "all"}`; + + if (httpSource.active && webhookData.success) { + const hasMissingOptions = Object.values(options).some( + (option) => option.missing.length > 0 + ); + if (!hasMissingOptions) return; + + const updatedWebhook = await io.integration.updateWebhook("update-webhook", { + id: webhookData.data.webhook.id, + input: { + label, + resourceTypes: allEvents, + secret: httpSource.secret, + url: httpSource.url, + }, + }); + + return { + data: WebhookRegistrationDataSchema.parse(updatedWebhook), + options: registeredOptions, + }; + } + + // check for existing hooks that match url + const listResponse = await io.integration.webhooks("list-webhooks"); + const existingWebhook = listResponse.find((w) => w.url === httpSource.url); + + if (existingWebhook) { + const updatedWebhook = await io.integration.updateWebhook("update-webhook", { + id: existingWebhook.id, + input: { + label, + resourceTypes: allEvents, + secret: httpSource.secret, + url: httpSource.url, + }, + }); + + return { + data: WebhookRegistrationDataSchema.parse(updatedWebhook), + options: registeredOptions, + }; + } + + const createPayload = await io.integration.createWebhook("create-webhook", { + label, + resourceTypes: allEvents, + secret: httpSource.secret, + teamId: params.teamId, + url: httpSource.url, + }); + + return { + data: WebhookRegistrationDataSchema.parse(createPayload), + secret: (await createPayload.webhook)?.secret, + options: registeredOptions, + }; + }, + }); +} + +async function webhookHandler(event: HandlerEvent<"HTTP">, logger: Logger, integration: {{ identifier | capitalize }}) { + logger.debug("[@trigger.dev/{{ identifier }}] Handling webhook payload"); + + const { rawEvent: request, source } = event; + + const payloadUuid = request.headers.get("{{ identifier | capitalize }}-Delivery"); + const payloadEvent = request.headers.get("{{ identifier | capitalize }}-Event"); + + if (!payloadUuid || !payloadEvent) { + logger.debug("[@trigger.dev/{{ identifier }}] Missing required {{ identifier | capitalize }} headers"); + return { events: [] }; + } + + if (!request.body) { + logger.debug("[@trigger.dev/{{ identifier }}] No body found"); + return { events: [] }; + } + + const signature = request.headers.get("WEBHOOK_SIGNATURE_HEADER"); + + if (!signature) { + logger.error("[@trigger.dev/{{ identifier }}] Error validating webhook signature, no signature found"); + throw Error("[@trigger.dev/{{ identifier }}] No signature found"); + } + + const rawBody = await request.text(); + const body = JSON.parse(rawBody); + const webhookHelper = new {{ identifier | capitalize }}Webhooks(source.secret); + + if (!webhookHelper.verify(Buffer.from(rawBody), signature)) { + logger.error("[@trigger.dev/{{ identifier }}] Error validating webhook signature, they don't match"); + throw Error("[@trigger.dev/{{ identifier }}] Invalid signature"); + } + + const webhookPayload = WebhookPayloadSchema.parse(body); + + return { + events: [ + { + id: payloadUuid, + name: payloadEvent, + source: "{{ identifier }}.app", + payload: webhookPayload, + context: {}, + }, + ], + }; +} diff --git a/packages/cli/src/utils/createIntegrationFileFromTemplate.ts b/packages/cli/src/utils/createIntegrationFileFromTemplate.ts new file mode 100644 index 000000000..8ac261a53 --- /dev/null +++ b/packages/cli/src/utils/createIntegrationFileFromTemplate.ts @@ -0,0 +1,61 @@ +import fs from "fs/promises"; +import { Liquid } from "liquidjs"; +import path from "path"; + +import { pathExists } from "./fileSystem"; +import { templatesPath } from "../paths"; + +type Result = + | { + success: true; + alreadyExisted: boolean; + } + | { + success: false; + error: string; + }; + +const templatesDir = path.join(templatesPath(), "integration"); + +const liquid = new Liquid({ + root: templatesDir, + trimTagRight: true, + trimOutputRight: true, +}); + +export async function createIntegrationFileFromTemplate(params: { + relativeTemplatePath: string; + variables?: Record; + outputPath: string; +}): Promise { + if (await pathExists(params.outputPath)) { + return { + success: true, + alreadyExisted: true, + }; + } + + try { + const output = await liquid.renderFile(params.relativeTemplatePath, params.variables); + + const directoryName = path.dirname(params.outputPath); + await fs.mkdir(directoryName, { recursive: true }); + await fs.writeFile(params.outputPath, output); + + return { + success: true, + alreadyExisted: false, + }; + } catch (e) { + if (e instanceof Error) { + return { + success: false, + error: e.message, + }; + } + return { + success: false, + error: JSON.stringify(e), + }; + } +} diff --git a/packages/cli/src/utils/getTriggerApiDetails.ts b/packages/cli/src/utils/getTriggerApiDetails.ts index 7ce20105c..274a9072b 100644 --- a/packages/cli/src/utils/getTriggerApiDetails.ts +++ b/packages/cli/src/utils/getTriggerApiDetails.ts @@ -1,66 +1,23 @@ -import pathModule from "path"; -import { pathExists, readFile } from "./fileSystem"; import { logger } from "./logger"; -import dotenv from "dotenv"; import { CLOUD_API_URL } from "../consts"; import { checkApiKeyIsDevServer } from "./getApiKeyType"; - -export async function readEnvFilesWithBackups( - path: string, - envFile: string, - backups: string[] -): Promise<{ content: string; fileName: string } | undefined> { - const envFilePath = pathModule.join(path, envFile); - const envFileExists = await pathExists(envFilePath); - - if (envFileExists) { - const content = await readFile(envFilePath); - - return { content, fileName: envFile }; - } - - for (const backup of backups) { - const backupPath = pathModule.join(path, backup); - const backupExists = await pathExists(backupPath); - - if (backupExists) { - const content = await readFile(backupPath); - - return { content, fileName: backup }; - } - } - - return; -} +import { readEnvVariables } from "./readEnvVariables"; export async function getTriggerApiDetails(path: string, envFile: string) { - const resolvedEnvFile = await readEnvFilesWithBackups(path, envFile, [ - ".env", - ".env.local", - ".env.development.local", - ]); + const envVarsToRead = ["TRIGGER_API_KEY", "TRIGGER_API_URL"]; + const resolvedEnvVars = await readEnvVariables(path, envFile, envVarsToRead); - if (!resolvedEnvFile) { - logger.error(`You must add TRIGGER_API_KEY to your ${envFile} file.`); - return; - } - - const parsedEnvFile = dotenv.parse(resolvedEnvFile.content); - - if (!parsedEnvFile) { - logger.error(`You must add TRIGGER_API_KEY to your ${envFile} file.`); - return; - } - - const apiKey = parsedEnvFile.TRIGGER_API_KEY; - const apiUrl = parsedEnvFile.TRIGGER_API_URL; + const apiKey = resolvedEnvVars.TRIGGER_API_KEY; + const apiUrl = resolvedEnvVars.TRIGGER_API_URL; if (!apiKey) { - logger.error(`You must add TRIGGER_API_KEY to your ${envFile} file.`); + logger.error( + `You must add TRIGGER_API_KEY to your ${envFile} file or set as runtime environment variable.` + ); return; } - const result = checkApiKeyIsDevServer(apiKey); + const result = checkApiKeyIsDevServer(apiKey.value); if (!result.success) { if (result.type) { @@ -75,5 +32,10 @@ export async function getTriggerApiDetails(path: string, envFile: string) { return; } - return { apiKey, apiUrl: apiUrl ?? CLOUD_API_URL, envFile: resolvedEnvFile.fileName }; + return { + apiKey: apiKey.value, + apiUrl: apiUrl?.value ?? CLOUD_API_URL, + apiKeySource: + apiKey.source.type === "runtime" ? "process runtime" : `${apiKey.source.name} file`, + }; } diff --git a/packages/cli/src/utils/jsRuntime.ts b/packages/cli/src/utils/jsRuntime.ts new file mode 100644 index 000000000..2e453c2aa --- /dev/null +++ b/packages/cli/src/utils/jsRuntime.ts @@ -0,0 +1,137 @@ +import { Framework, getFramework } from "../frameworks"; +import { PackageManager, getUserPackageManager } from "./getUserPkgManager"; +import { Logger } from "./logger"; +import { run as ncuRun } from "npm-check-updates"; +import chalk from "chalk"; +import fs from "fs/promises"; +import pathModule from "path"; + +export abstract class JsRuntime { + logger: Logger; + projectRootPath: string; + constructor(projectRootPath: string, logger: Logger) { + this.logger = logger; + this.projectRootPath = projectRootPath; + } + abstract get id(): string; + abstract checkForOutdatedPackages(): Promise; + abstract getUserPackageManager(): Promise; + abstract getFramework(): Promise; + abstract getEndpointId(): Promise; +} + +export async function getJsRuntime(projectRootPath: string, logger: Logger): Promise { + if (await NodeJsRuntime.isNodeJsRuntime(projectRootPath)) { + return new NodeJsRuntime(projectRootPath, logger); + } else if (await DenoRuntime.isDenoJsRuntime(projectRootPath)) { + return new DenoRuntime(projectRootPath, logger); + } + throw new Error("Unsupported runtime"); +} + +class NodeJsRuntime extends JsRuntime { + static async isNodeJsRuntime(projectRootPath: string): Promise { + try { + await fs.stat(pathModule.join(projectRootPath, "package.json")); + return true; + } catch { + return false; + } + } + + get id() { + return "nodejs"; + } + get packageJsonPath(): string { + return pathModule.join(this.projectRootPath, "package.json"); + } + + async checkForOutdatedPackages(): Promise { + const updates = (await ncuRun({ + packageFile: `${this.packageJsonPath}`, + filter: "/trigger.dev/.+$/", + upgrade: false, + })) as { + [key: string]: string; + }; + + if (typeof updates === "undefined" || Object.keys(updates).length === 0) { + return; + } + + const packageFile = await fs.readFile(this.packageJsonPath); + const data = JSON.parse(Buffer.from(packageFile).toString("utf8")); + const dependencies = data.dependencies; + console.log(chalk.bgYellow("Updates available for trigger.dev packages")); + console.log(chalk.bgBlue("Run npx @trigger.dev/cli@latest update")); + + for (let dep in updates) { + console.log(`${dep} ${dependencies[dep]} β†’ ${updates[dep]}`); + } + } + + async getUserPackageManager() { + return getUserPackageManager(this.projectRootPath); + } + + async getFramework() { + const userPackageManager = await this.getUserPackageManager(); + return getFramework(this.projectRootPath, userPackageManager); + } + async getEndpointId() { + const pkgJsonPath = pathModule.join(this.projectRootPath, "package.json"); + const pkgBuffer = await fs.readFile(pkgJsonPath); + const pkgJson = JSON.parse(pkgBuffer.toString()); + const value = pkgJson["trigger.dev"]?.endpointId; + if (!value || typeof value !== "string") return undefined; + return value; + } +} + +class DenoRuntime extends JsRuntime { + getDenoJsonPath(): Promise { + try { + return fs + .stat(pathModule.join(this.projectRootPath, "deno.json")) + .then(() => pathModule.join(this.projectRootPath, "deno.json")); + } catch { + return fs + .stat(pathModule.join(this.projectRootPath, "deno.jsonc")) + .then(() => pathModule.join(this.projectRootPath, "deno.jsonc")); + } + } + + get id() { + return "deno"; + } + + static async isDenoJsRuntime(projectRootPath: string): Promise { + try { + try { + await fs.stat(pathModule.join(projectRootPath, "deno.json")); + } catch (e) { + await fs.stat(pathModule.join(projectRootPath, "deno.jsonc")); + } + return true; + } catch { + return false; + } + } + + async checkForOutdatedPackages() { + // not implemented currently + } + async getUserPackageManager() { + return undefined; + } + async getFramework() { + // not implemented currently + return undefined; + } + async getEndpointId() { + const pkgJsonPath = await this.getDenoJsonPath(); + const pkgBuffer = await fs.readFile(pkgJsonPath); + const pkgJson = JSON.parse(pkgBuffer.toString()); + return pkgJson["trigger.dev"]?.endpointId; + } +} diff --git a/packages/cli/src/utils/logger.ts b/packages/cli/src/utils/logger.ts index e971b8b95..69b33fcef 100644 --- a/packages/cli/src/utils/logger.ts +++ b/packages/cli/src/utils/logger.ts @@ -1,5 +1,6 @@ import chalk from "chalk"; +export type Logger = typeof logger; export const logger = { error(...args: unknown[]) { console.log(chalk.red(...args)); @@ -13,4 +14,7 @@ export const logger = { success(...args: unknown[]) { console.log(chalk.green(...args)); }, + table(rows: any) { + console.table(rows); + }, }; diff --git a/packages/cli/src/utils/parseNameAndPath.ts b/packages/cli/src/utils/parseNameAndPath.ts index 400a534cf..e6016c0d9 100644 --- a/packages/cli/src/utils/parseNameAndPath.ts +++ b/packages/cli/src/utils/parseNameAndPath.ts @@ -4,3 +4,8 @@ import pathModule from "path"; export const resolvePath = (input: string) => { return pathModule.resolve(process.cwd(), input); }; + +// Takes an absolute path and derives the relative path from the current working directory +export const relativePath = (input: string) => { + return pathModule.relative(process.cwd(), input); +}; diff --git a/packages/cli/src/utils/readEnvVariables.ts b/packages/cli/src/utils/readEnvVariables.ts new file mode 100644 index 000000000..5ef8aa9b5 --- /dev/null +++ b/packages/cli/src/utils/readEnvVariables.ts @@ -0,0 +1,102 @@ +import pathModule from "path"; +import { pathExists, readFile } from "./fileSystem"; +import dotenv from "dotenv"; + +const ENV_FILES_FALLBACK = [".env", ".env.local", ".env.development.local"]; + +export type EnvVarSourceRuntime = { + type: "runtime"; +}; + +export type EnvVarSourceFile = { + type: "file"; + name: string; +}; + +export type EnvVarSource = EnvVarSourceRuntime | EnvVarSourceFile; + +export type EnvironmentVariable = { + value: string; + source: EnvVarSource; +}; + +export type EnvironmentVariables = { + [name: string]: EnvironmentVariable | undefined; +}; + +// Reads `varsToRead` from `process.env` and `envFile` (with fallbacks). +// `process.env` takes precedence over the `envFile`. +export async function readEnvVariables( + path: string, + envFile: string, + varsToRead: string[] +): Promise { + const resolvedEnvFile = await readEnvFilesWithBackups(path, envFile); + const parsedEnvFile = resolvedEnvFile + ? { output: dotenv.parse(resolvedEnvFile.content), filename: resolvedEnvFile.fileName } + : {}; + + return Object.fromEntries( + varsToRead.map((envVar) => [ + envVar, + readFromRuntime(envVar) ?? readFromFile(envVar, parsedEnvFile), + ]) + ); +} + +async function readEnvFilesWithBackups( + path: string, + envFile: string +): Promise<{ content: string; fileName: string } | undefined> { + const envFilePath = pathModule.join(path, envFile); + const envFileExists = await pathExists(envFilePath); + + if (envFileExists) { + const content = await readFile(envFilePath); + + return { content, fileName: envFile }; + } + + for (const fallBack of ENV_FILES_FALLBACK) { + const fallbackPath = pathModule.join(path, fallBack); + const fallbackExists = await pathExists(fallbackPath); + + if (fallbackExists) { + const content = await readFile(fallbackPath); + + return { content, fileName: fallBack }; + } + } + + return; +} + +function readFromRuntime(envVar: string): EnvironmentVariable | undefined { + const val = process.env[envVar]; + if (!val) { + return; + } + return { + value: val, + source: { + type: "runtime", + } as EnvVarSourceRuntime, + }; +} + +function readFromFile( + envVar: string, + parsedEnvFile: { output?: dotenv.DotenvParseOutput; filename?: string } +): EnvironmentVariable | undefined { + const val = parsedEnvFile.output ? parsedEnvFile.output[envVar] : undefined; + if (!val) { + return; + } + return { + value: val, + source: { + type: "file", + name: parsedEnvFile.filename, + } as EnvVarSourceFile, + }; +} diff --git a/packages/cli/src/utils/throttle.ts b/packages/cli/src/utils/throttle.ts new file mode 100644 index 000000000..e86341290 --- /dev/null +++ b/packages/cli/src/utils/throttle.ts @@ -0,0 +1,18 @@ +export class Throttle { + throttleTimeout: NodeJS.Timeout | null = null; + + constructor( + private readonly fn: () => any, + private readonly delay: number + ) { + this.fn = fn; + this.delay = delay; + } + + call() { + if (this.throttleTimeout) { + clearTimeout(this.throttleTimeout); + } + this.throttleTimeout = setTimeout(this.fn, this.delay); + } +} diff --git a/packages/cli/src/utils/triggerApi.ts b/packages/cli/src/utils/triggerApi.ts index 0f776f3ce..d900a00e4 100644 --- a/packages/cli/src/utils/triggerApi.ts +++ b/packages/cli/src/utils/triggerApi.ts @@ -1,5 +1,7 @@ import fetch from "./fetchUseProxy"; import { z } from "zod"; +import core from "@trigger.dev/core"; +const { GetEndpointIndexResponseSchema } = core; export type CreateEndpointOptions = { id: string; @@ -16,6 +18,9 @@ export type EndpointData = { createdAt: string; updatedAt: string; indexingHookIdentifier: string; + endpointIndex: { + id: string; + }; }; export type EndpointResponse = @@ -59,12 +64,12 @@ export class TriggerApi { private baseUrl: string ) {} - async whoami(apiKey: string): Promise { + async whoami(): Promise { const response = await fetch(`${this.baseUrl}/api/v1/whoami`, { method: "GET", headers: { Accept: "application/json", - Authorization: `Bearer ${apiKey}`, + Authorization: `Bearer ${this.apiKey}`, }, }); @@ -155,6 +160,33 @@ export class TriggerApi { data: data as any as EndpointData, }; } + + async getEndpointIndex(indexId: string) { + const response = await fetch(`${this.baseUrl}/api/v1/endpointindex/${indexId}`, { + method: "GET", + headers: { + Accept: "application/json", + Authorization: `Bearer ${this.apiKey}`, + }, + }); + + if (response.ok) { + const body = await response.json(); + const parsed = GetEndpointIndexResponseSchema.safeParse(body); + + if (parsed.success) { + return parsed.data; + } + } + + return { + status: "FAILURE" as const, + error: { + message: `Bad response from Trigger.dev (${response.status})`, + }, + updatedAt: new Date(), + }; + } } function safeJsonParse(raw: string | null | undefined): unknown { diff --git a/packages/cli/src/utils/wait.ts b/packages/cli/src/utils/wait.ts new file mode 100644 index 000000000..75b9dc68d --- /dev/null +++ b/packages/cli/src/utils/wait.ts @@ -0,0 +1,5 @@ +export async function wait(ms: number) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index 31b1728d4..b765e2ade 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -9,7 +9,6 @@ "resolveJsonModule": true, "allowJs": true, "checkJs": true, - /* EMIT RULES */ "outDir": "./dist", "noEmit": true, // TSUP takes care of emitting js for us, in a MUCH faster way diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index cb82d87da..8cce62069 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,30 @@ # internal-platform +## 2.2.0 + +### Minor Changes + +- 975c5f1d: Drop support for Node v16, require Node >= 18. This allows us to use native fetch in our SDK which paves the way for multi-platform support. + +### Patch Changes + +- 50e3d9e4: When indexing user's jobs errors are now stored and displayed +- 59a94c71: Allow task property values to be blank, but strip them out before persisting them + +## 2.1.9 + +### Patch Changes + +- 9a187f9e: upgrade zod to 3.22.3 + +## 2.1.8 + +### Patch Changes + +- 6a992a19: First release of `@trigger.dev/replicate` integration with remote callback support. +- ab9e4a98: Send client version back to the server via headers +- ab9e4a98: Better performance when resuming a run, especially one with a large amount of tasks + ## 2.1.7 ## 2.1.6 diff --git a/packages/core/package.json b/packages/core/package.json index 868806a71..be937d97f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/core", - "version": "2.1.7", + "version": "2.2.0", "description": "Core code used across the Trigger.dev SDK and platform", "license": "MIT", "main": "./dist/index.js", @@ -28,7 +28,7 @@ }, "dependencies": { "ulid": "^2.3.0", - "zod": "3.21.4", + "zod": "3.22.3", "zod-error": "1.5.0" }, "devDependencies": { @@ -42,6 +42,6 @@ "typescript": "^4.9.4" }, "engines": { - "node": ">=16.8.0" + "node": ">=18.0.0" } } \ No newline at end of file diff --git a/packages/core/src/bloom.ts b/packages/core/src/bloom.ts new file mode 100644 index 000000000..63428ab76 --- /dev/null +++ b/packages/core/src/bloom.ts @@ -0,0 +1,63 @@ +import { Buffer } from "node:buffer"; + +export class BloomFilter { + private size: number; + private bitArray: Uint8Array; + + constructor(size: number) { + this.size = size; + this.bitArray = new Uint8Array(Math.ceil(size / 8)); + } + + add(item: string): void { + const index = murmurHash3(item) % this.size; + this.bitArray[Math.floor(index / 8)] |= 1 << index % 8; + } + + test(item: string): boolean { + const index = murmurHash3(item) % this.size; + return (this.bitArray[Math.floor(index / 8)] & (1 << index % 8)) !== 0; + } + + // Serialize to a Base64 string + serialize(): string { + return Buffer.from(this.bitArray).toString("base64"); + } + + // Deserialize from a Base64 string + static deserialize(str: string, size: number): BloomFilter { + const filter = new BloomFilter(size); + filter.bitArray = Uint8Array.from(Buffer.from(str, "base64")); + return filter; + } + + static NOOP_TASK_SET_SIZE = 32_768; +} + +function murmurHash3(str: string, seed = 0): number { + let h1 = 0xdeadbeef ^ seed, + h2 = 0x41c6ce57 ^ seed; + for (let i = 0, ch; i < str.length; i++) { + ch = str.charCodeAt(i); + h1 = Math.imul(h1 ^ ch, 0xcc9e2d51); + h1 = (h1 << 15) | (h1 >>> 17); + h1 = Math.imul(h1, 0x1b873593); + + h2 = Math.imul(h2 ^ ch, 0x85ebca6b); + h2 = (h2 << 13) | (h2 >>> 19); + h2 = Math.imul(h2, 0xc2b2ae35); + } + + h1 ^= str.length; + h2 ^= str.length; + + h1 = Math.imul(h1 ^ (h1 >>> 16), 0x85ebca6b); + h1 = Math.imul(h1 ^ (h1 >>> 13), 0xc2b2ae35); + h1 ^= h1 >>> 16; + + h2 = Math.imul(h2 ^ (h2 >>> 16), 0x85ebca6b); + h2 = Math.imul(h2 ^ (h2 >>> 13), 0xc2b2ae35); + h2 ^= h2 >>> 16; + + return 4294967296 * (2097151 & h2) + (h1 >>> 0); +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 50aee5efa..0810ae835 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -6,3 +6,30 @@ export * from "./retry"; export * from "./replacements"; export * from "./searchParams"; export * from "./eventFilterMatches"; +export * from "./bloom"; + +export const API_VERSIONS = { + LAZY_LOADED_CACHED_TASKS: "2023-09-29", +} as const; + +export const PLATFORM_FEATURES = { + yieldExecution: API_VERSIONS.LAZY_LOADED_CACHED_TASKS, + lazyLoadedCachedTasks: API_VERSIONS.LAZY_LOADED_CACHED_TASKS, +}; + +export function supportsFeature( + featureName: TFeatureName, + version: string +): boolean { + if (version === "unversioned" || version === "unknown") { + return false; + } + + const supportedVersion = PLATFORM_FEATURES[featureName]; + + if (!supportedVersion) { + return false; + } + + return version >= supportedVersion; +} diff --git a/packages/core/src/schemas/api.ts b/packages/core/src/schemas/api.ts index b5f1f9161..4e16cbfc5 100644 --- a/packages/core/src/schemas/api.ts +++ b/packages/core/src/schemas/api.ts @@ -13,7 +13,7 @@ import { RegisterDynamicSchedulePayloadSchema, ScheduleMetadataSchema, } from "./schedules"; -import { CachedTaskSchema, TaskSchema } from "./tasks"; +import { CachedTaskSchema, ServerTaskSchema, TaskSchema } from "./tasks"; import { EventSpecificationSchema, TriggerMetadataSchema } from "./triggers"; import { RunStatusSchema } from "./runs"; import { JobRunStatusRecordSchema } from "./statuses"; @@ -176,11 +176,13 @@ export type HttpSourceRequestHeaders = z.output; export const ValidateSuccessResponseSchema = z.object({ ok: z.literal(true), endpointId: z.string(), + triggerVersion: z.string().optional(), }); export const ValidateErrorResponseSchema = z.object({ ok: z.literal(false), error: z.string(), + triggerVersion: z.string().optional(), }); export const ValidateResponseSchema = z.discriminatedUnion("ok", [ @@ -292,6 +296,57 @@ export const IndexEndpointResponseSchema = z.object({ export type IndexEndpointResponse = z.infer; +export const EndpointIndexErrorSchema = z.object({ + message: z.string(), + raw: z.any().optional(), +}); + +export type EndpointIndexError = z.infer; + +const IndexEndpointStatsSchema = z.object({ + jobs: z.number(), + sources: z.number(), + dynamicTriggers: z.number(), + dynamicSchedules: z.number(), + disabledJobs: z.number().default(0), +}); + +export type IndexEndpointStats = z.infer; + +export function parseEndpointIndexStats(stats: unknown): IndexEndpointStats | undefined { + if (stats === null || stats === undefined) { + return; + } + return IndexEndpointStatsSchema.parse(stats); +} + +export const GetEndpointIndexResponseSchema = z.discriminatedUnion("status", [ + z.object({ + status: z.literal("PENDING"), + updatedAt: z.coerce.date(), + }), + z.object({ + status: z.literal("STARTED"), + updatedAt: z.coerce.date(), + }), + z.object({ + status: z.literal("SUCCESS"), + stats: IndexEndpointStatsSchema, + updatedAt: z.coerce.date(), + }), + z.object({ + status: z.literal("FAILURE"), + error: EndpointIndexErrorSchema, + updatedAt: z.coerce.date(), + }), +]); + +export type GetEndpointIndexResponse = z.infer; + +export const EndpointHeadersSchema = z.object({ + "trigger-version": z.string().optional(), +}); + export const RawEventSchema = z.object({ /** The `name` property must exactly match any subscriptions you want to trigger. */ @@ -394,6 +449,8 @@ export const RunSourceContextSchema = z.object({ metadata: z.any(), }); +export type RunSourceContext = z.infer; + export const RunJobBodySchema = z.object({ event: ApiEventLogSchema, job: z.object({ @@ -424,7 +481,10 @@ export const RunJobBodySchema = z.object({ .optional(), source: RunSourceContextSchema.optional(), tasks: z.array(CachedTaskSchema).optional(), + cachedTaskCursor: z.string().optional(), + noopTasksSet: z.string().optional(), connections: z.record(ConnectionAuthSchema).optional(), + yieldedExecutions: z.string().array().optional(), }); export type RunJobBody = z.infer; @@ -437,6 +497,13 @@ export const RunJobErrorSchema = z.object({ export type RunJobError = z.infer; +export const RunJobYieldExecutionErrorSchema = z.object({ + status: z.literal("YIELD_EXECUTION"), + key: z.string(), +}); + +export type RunJobYieldExecutionError = z.infer; + export const RunJobInvalidPayloadErrorSchema = z.object({ status: z.literal("INVALID_PAYLOAD"), errors: z.array(SchemaErrorSchema), @@ -482,6 +549,7 @@ export const RunJobSuccessSchema = z.object({ export type RunJobSuccess = z.infer; export const RunJobResponseSchema = z.discriminatedUnion("status", [ + RunJobYieldExecutionErrorSchema, RunJobErrorSchema, RunJobUnresolvedAuthErrorSchema, RunJobInvalidPayloadErrorSchema, @@ -608,6 +676,16 @@ export const RunTaskOptionsSchema = z.object({ params: z.any(), /** The style of the log entry. */ style: StyleSchema.optional(), + /** Allows you to expose a `task.callbackUrl` to use in your tasks. Enabling this feature will cause the task to return the data sent to the callbackUrl instead of the usual async callback result. */ + callback: z + .object({ + /** Causes the task to wait for and return the data of the first request sent to `task.callbackUrl`. */ + enabled: z.boolean(), + /** Time to wait for the first request to `task.callbackUrl`. Default: One hour. */ + timeoutInSeconds: z.number(), + }) + .partial() + .optional(), /** Allows you to link the Integration connection in the logs. This is handled automatically in integrations. */ connectionKey: z.string().optional(), /** An operation you want to perform on the Trigger.dev platform, current only "fetch" is supported. If you wish to `fetch` use [`io.backgroundFetch()`](https://trigger.dev/docs/sdk/io/backgroundfetch) instead. */ @@ -633,11 +711,32 @@ export const RunTaskBodyInputSchema = RunTaskOptionsSchema.extend({ export type RunTaskBodyInput = z.infer; export const RunTaskBodyOutputSchema = RunTaskBodyInputSchema.extend({ + properties: z.array(DisplayPropertySchema.partial()).optional(), params: DeserializedJsonSchema.optional().nullable(), + callback: z + .object({ + enabled: z.boolean(), + timeoutInSeconds: z.number().default(3600), + }) + .optional(), }); export type RunTaskBodyOutput = z.infer; +export const RunTaskResponseWithCachedTasksBodySchema = z.object({ + task: ServerTaskSchema, + cachedTasks: z + .object({ + tasks: z.array(CachedTaskSchema), + cursor: z.string().optional(), + }) + .optional(), +}); + +export type RunTaskResponseWithCachedTasksBody = z.infer< + typeof RunTaskResponseWithCachedTasksBodySchema +>; + export const CompleteTaskBodyInputSchema = RunTaskBodyInputSchema.pick({ properties: true, description: true, diff --git a/packages/core/src/schemas/tasks.ts b/packages/core/src/schemas/tasks.ts index fe6d43bbd..559dc4bab 100644 --- a/packages/core/src/schemas/tasks.ts +++ b/packages/core/src/schemas/tasks.ts @@ -31,6 +31,7 @@ export const TaskSchema = z.object({ parentId: z.string().optional().nullable(), style: StyleSchema.optional().nullable(), operation: z.string().optional().nullable(), + callbackUrl: z.string().optional().nullable(), }); export const ServerTaskSchema = TaskSchema.extend({ diff --git a/packages/database/package.json b/packages/database/package.json index dda8fdef4..6c82f4588 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -5,17 +5,18 @@ "main": "./src/index.ts", "types": "./src/index.ts", "dependencies": { - "@prisma/client": "4.16.0", + "@prisma/client": "5.4.1", "typescript": "^4.8.4" }, "devDependencies": { - "prisma": "4.16.0" + "prisma": "5.4.1" }, "scripts": { "generate": "prisma generate", "db:migrate:dev": "prisma migrate dev", + "db:migrate:dev:create": "prisma migrate dev --create-only", "db:migrate:deploy": "prisma migrate deploy", "db:studio": "prisma studio", "typecheck": "tsc --noEmit" } -} \ No newline at end of file +} diff --git a/packages/database/prisma/migrations/20230925174509_add_callback_url/migration.sql b/packages/database/prisma/migrations/20230925174509_add_callback_url/migration.sql new file mode 100644 index 000000000..4808101ef --- /dev/null +++ b/packages/database/prisma/migrations/20230925174509_add_callback_url/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Task" ADD COLUMN "callbackUrl" TEXT; diff --git a/packages/database/prisma/migrations/20230929100348_add_yielded_executions_to_job_run/migration.sql b/packages/database/prisma/migrations/20230929100348_add_yielded_executions_to_job_run/migration.sql new file mode 100644 index 000000000..c5ccba086 --- /dev/null +++ b/packages/database/prisma/migrations/20230929100348_add_yielded_executions_to_job_run/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "JobRun" ADD COLUMN "yieldedExecutions" TEXT[]; diff --git a/packages/database/prisma/migrations/20231003092741_add_version_to_endpoint/migration.sql b/packages/database/prisma/migrations/20231003092741_add_version_to_endpoint/migration.sql new file mode 100644 index 000000000..0317eaaef --- /dev/null +++ b/packages/database/prisma/migrations/20231003092741_add_version_to_endpoint/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Endpoint" ADD COLUMN "version" TEXT NOT NULL DEFAULT 'unknown'; diff --git a/packages/database/prisma/migrations/20231005064823_add_job_run_internal/migration.sql b/packages/database/prisma/migrations/20231005064823_add_job_run_internal/migration.sql new file mode 100644 index 000000000..f226330db --- /dev/null +++ b/packages/database/prisma/migrations/20231005064823_add_job_run_internal/migration.sql @@ -0,0 +1,10 @@ +-- AlterTable +ALTER TABLE "JobRun" ADD COLUMN "internal" BOOLEAN NOT NULL DEFAULT false; + +/* + Backfill JobRun internal flag +*/ +UPDATE "JobRun" +SET "internal" = "Job"."internal" +FROM "Job" +WHERE "JobRun"."jobId" = "Job"."id" AND "JobRun"."internal" = TRUE; diff --git a/packages/database/prisma/migrations/20231010115840_endpoint_index_status_added/migration.sql b/packages/database/prisma/migrations/20231010115840_endpoint_index_status_added/migration.sql new file mode 100644 index 000000000..746234fca --- /dev/null +++ b/packages/database/prisma/migrations/20231010115840_endpoint_index_status_added/migration.sql @@ -0,0 +1,11 @@ +-- CreateEnum +CREATE TYPE "EndpointIndexStatus" AS ENUM ('PENDING', 'STARTED', 'SUCCESS', 'FAILURE'); + +-- AlterTable +ALTER TABLE "EndpointIndex" +ADD COLUMN "status" "EndpointIndexStatus" NOT NULL DEFAULT 'PENDING'; + +-- Update all existing rows to be SUCCESS. This isn't correct because some of them have failed, but we don't want them to be PENDING. +UPDATE "EndpointIndex" +SET + "status" = 'SUCCESS'; \ No newline at end of file diff --git a/packages/database/prisma/migrations/20231010120458_endpoint_index_data_and_stats_are_now_optional/migration.sql b/packages/database/prisma/migrations/20231010120458_endpoint_index_data_and_stats_are_now_optional/migration.sql new file mode 100644 index 000000000..bbcb52084 --- /dev/null +++ b/packages/database/prisma/migrations/20231010120458_endpoint_index_data_and_stats_are_now_optional/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "EndpointIndex" ALTER COLUMN "data" DROP NOT NULL, +ALTER COLUMN "stats" DROP NOT NULL; diff --git a/packages/database/prisma/migrations/20231010135433_endpoint_index_added_error_column/migration.sql b/packages/database/prisma/migrations/20231010135433_endpoint_index_added_error_column/migration.sql new file mode 100644 index 000000000..6e24e4dca --- /dev/null +++ b/packages/database/prisma/migrations/20231010135433_endpoint_index_added_error_column/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "EndpointIndex" ADD COLUMN "error" JSONB; diff --git a/packages/database/prisma/migrations/20231013083144_add_next_event_timestamp_to_schedule_source/migration.sql b/packages/database/prisma/migrations/20231013083144_add_next_event_timestamp_to_schedule_source/migration.sql new file mode 100644 index 000000000..ed5a2b48c --- /dev/null +++ b/packages/database/prisma/migrations/20231013083144_add_next_event_timestamp_to_schedule_source/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "ScheduleSource" ADD COLUMN "nextEventTimestamp" TIMESTAMP(3); diff --git a/packages/database/prisma/schema.prisma b/packages/database/prisma/schema.prisma index 9e8b66da8..7f1d21918 100644 --- a/packages/database/prisma/schema.prisma +++ b/packages/database/prisma/schema.prisma @@ -367,6 +367,7 @@ model Endpoint { updatedAt DateTime @updatedAt indexingHookIdentifier String? + version String @default("unknown") jobVersions JobVersion[] jobRuns JobRun[] @@ -390,9 +391,11 @@ model EndpointIndex { source EndpointIndexSource @default(MANUAL) sourceData Json? reason String? + status EndpointIndexStatus @default(PENDING) - data Json - stats Json + data Json? + stats Json? + error Json? } enum EndpointIndexSource { @@ -402,6 +405,13 @@ enum EndpointIndexSource { HOOK } +enum EndpointIndexStatus { + PENDING + STARTED + SUCCESS + FAILURE +} + model Job { id String @id @default(cuid()) slug String @@ -663,8 +673,9 @@ model EventRecord { } model JobRun { - id String @id @default(cuid()) - number Int + id String @id @default(cuid()) + number Int + internal Boolean @default(false) job Job @relation(fields: [jobId], references: [id], onDelete: Cascade, onUpdate: Cascade) jobId String @@ -713,6 +724,8 @@ model JobRun { isTest Boolean @default(false) preprocess Boolean @default(false) + yieldedExecutions String[] + tasks Task[] runConnections RunConnection[] missingConnections MissingConnection[] @@ -795,6 +808,7 @@ model Task { redact Json? style Json? operation String? + callbackUrl String? startedAt DateTime? completedAt DateTime? @@ -1035,6 +1049,7 @@ model ScheduleSource { dispatcherId String lastEventTimestamp DateTime? + nextEventTimestamp DateTime? workerJobId String? diff --git a/packages/emails/package.json b/packages/emails/package.json index 8f2ce6a45..48f855354 100644 --- a/packages/emails/package.json +++ b/packages/emails/package.json @@ -25,12 +25,12 @@ "react-email": "^1.6.1", "resend": "^0.9.1", "tiny-invariant": "^1.2.0", - "zod": "3.21.4" + "zod": "3.22.3" }, "devDependencies": { - "@types/react": "18.2.17", "@trigger.dev/tsconfig": "workspace:*", "@types/node": "16", + "@types/react": "18.2.17", "typescript": "^4.9.4" }, "engines": { diff --git a/packages/emails/src/index.tsx b/packages/emails/src/index.tsx index e07ee63c2..da8d2b39f 100644 --- a/packages/emails/src/index.tsx +++ b/packages/emails/src/index.tsx @@ -42,6 +42,8 @@ export const DeliverEmailSchema = z export type DeliverEmail = z.infer; +export type SendPlainTextOptions = { to: string; subject: string; text: string }; + export class EmailClient { #client?: Resend; #imagesBaseUrl: string; @@ -66,6 +68,20 @@ export class EmailClient { }); } + async sendPlainText(options: SendPlainTextOptions) { + if (this.#client) { + await this.#client.sendEmail({ + from: this.#from, + to: options.to, + replyTo: this.#replyTo, + subject: options.subject, + text: options.text, + }); + + return; + } + } + #getTemplate(data: DeliverEmail): { subject: string; component: ReactElement; diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index 9728ec0a9..9585d5522 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -1,5 +1,15 @@ # @trigger.dev/eslint-plugin +## 2.2.0 + +### Minor Changes + +- 975c5f1d: Drop support for Node v16, require Node >= 18. This allows us to use native fetch in our SDK which paves the way for multi-platform support. + +## 2.1.9 + +## 2.1.8 + ## 2.1.7 ## 2.1.6 diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 0d851c58b..5cef9a51d 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/eslint-plugin", - "version": "2.1.7", + "version": "2.2.0", "description": "ESLint plugin with trigger.dev best practices", "keywords": [ "eslint", @@ -29,7 +29,7 @@ "npm-run-all": "^4.1.5" }, "engines": { - "node": "^14.17.0 || ^16.0.0 || >= 18.0.0" + "node": ">=18.0.0" }, "peerDependencies": { "eslint": ">=7" diff --git a/packages/express/CHANGELOG.md b/packages/express/CHANGELOG.md index 2aa5ed0fa..cbf09e178 100644 --- a/packages/express/CHANGELOG.md +++ b/packages/express/CHANGELOG.md @@ -1,5 +1,34 @@ # @trigger.dev/express +## 2.2.0 + +### Minor Changes + +- 975c5f1d: Drop support for Node v16, require Node >= 18. This allows us to use native fetch in our SDK which paves the way for multi-platform support. + +### Patch Changes + +- Updated dependencies [975c5f1d] + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + +## 2.1.8 + +### Patch Changes + +- ab9e4a98: Send client version back to the server via headers +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/packages/express/package.json b/packages/express/package.json index 3377d07a2..99f807eb9 100644 --- a/packages/express/package.json +++ b/packages/express/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/express", - "version": "2.1.7", + "version": "2.2.0", "description": "Official Express adapter for Trigger.dev", "license": "MIT", "main": "./dist/index.js", @@ -19,7 +19,7 @@ "./package.json": "./package.json" }, "devDependencies": { - "@trigger.dev/sdk": "workspace:^2.1.7", + "@trigger.dev/sdk": "workspace:^2.2.0", "@trigger.dev/tsconfig": "workspace:*", "@types/debug": "^4.1.7", "@types/express": "^4.17.13", @@ -33,7 +33,7 @@ "build:tsup": "tsup" }, "peerDependencies": { - "@trigger.dev/sdk": "workspace:^2.1.7" + "@trigger.dev/sdk": "workspace:^2.2.0" }, "dependencies": { "@remix-run/web-fetch": "^4.3.5", @@ -41,6 +41,6 @@ "express": "^4.18.2" }, "engines": { - "node": ">=16.8.0" + "node": ">=18.0.0" } } \ No newline at end of file diff --git a/packages/express/src/index.ts b/packages/express/src/index.ts index fefc2f1d5..bd69a54fc 100644 --- a/packages/express/src/index.ts +++ b/packages/express/src/index.ts @@ -77,6 +77,14 @@ export function createMiddleware(client: TriggerClient, path: string = "/api/tri return; } + if (response.headers) { + for (const [key, value] of Object.entries(response.headers)) { + if (typeof value === "string") { + res.setHeader(key, value); + } + } + } + res.status(response.status).json(response.body); } catch (error) { next(error); diff --git a/packages/integration-kit/CHANGELOG.md b/packages/integration-kit/CHANGELOG.md index c637cf5db..732ab9845 100644 --- a/packages/integration-kit/CHANGELOG.md +++ b/packages/integration-kit/CHANGELOG.md @@ -1,5 +1,15 @@ # @trigger.dev/integration-kit +## 2.2.0 + +### Minor Changes + +- 975c5f1d: Drop support for Node v16, require Node >= 18. This allows us to use native fetch in our SDK which paves the way for multi-platform support. + +## 2.1.9 + +## 2.1.8 + ## 2.1.7 ## 2.1.6 diff --git a/packages/integration-kit/package.json b/packages/integration-kit/package.json index 049b50b53..461091e3c 100644 --- a/packages/integration-kit/package.json +++ b/packages/integration-kit/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/integration-kit", - "version": "2.1.7", + "version": "2.2.0", "description": "Trigger.dev Integration Kit has helpers to make creating integrations easier", "license": "MIT", "main": "./dist/index.js", @@ -21,7 +21,6 @@ "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", @@ -35,10 +34,9 @@ "typecheck": "tsup --dts-resolve --no-dts" }, "dependencies": { - "node-fetch": "2.6.x", "uuid": "^9.0.0" }, "engines": { - "node": ">=16.8.0" + "node": ">=18.0.0" } } diff --git a/packages/integration-kit/src/file.ts b/packages/integration-kit/src/file.ts index f5ae9f6df..216547e2c 100644 --- a/packages/integration-kit/src/file.ts +++ b/packages/integration-kit/src/file.ts @@ -1,7 +1,6 @@ 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 | Buffer, fileName: string): Promise { const directory = path.join("tmp", uuidv4()); @@ -13,7 +12,8 @@ export async function fileFromString(contents: string | Buffer, fileName: string export async function fileFromUrl(url: string) { const response = await fetch(url); - const content = await response.buffer(); + const arrayBuffer = await response.arrayBuffer(); + const content = Buffer.from(arrayBuffer); const fileName = path.basename(url); return fileFromString(content, fileName); diff --git a/packages/nestjs/CHANGELOG.md b/packages/nestjs/CHANGELOG.md new file mode 100644 index 000000000..82417c06b --- /dev/null +++ b/packages/nestjs/CHANGELOG.md @@ -0,0 +1,21 @@ +# @trigger.dev/nestjs + +## 2.2.0 + +### Minor Changes + +- 975c5f1d: Drop support for Node v16, require Node >= 18. This allows us to use native fetch in our SDK which paves the way for multi-platform support. + +### Patch Changes + +- Updated dependencies [975c5f1d] + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- a847b492: First release of NestJS adaptor +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 diff --git a/packages/nestjs/LICENSE b/packages/nestjs/LICENSE new file mode 100644 index 000000000..e51e7b10a --- /dev/null +++ b/packages/nestjs/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Trigger.dev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/nestjs/README.md b/packages/nestjs/README.md new file mode 100644 index 000000000..f0400f90f --- /dev/null +++ b/packages/nestjs/README.md @@ -0,0 +1,7 @@ +# NestJS and Trigger.dev + +Trigger.dev has full support for the NestJS framework. + +For information about the using Trigger.dev in your NestJS app, check out these useful docs links: + +- [Quick start guide for getting setup with Trigger.dev in a NestJS project](https://trigger.dev/docs/documentation/quickstarts/nestjs) diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json new file mode 100644 index 000000000..a7a5ee2db --- /dev/null +++ b/packages/nestjs/package.json @@ -0,0 +1,48 @@ +{ + "name": "@trigger.dev/nestjs", + "version": "2.2.0", + "description": "Official NestJS adapter for Trigger.dev", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "publishConfig": { + "access": "public" + }, + "files": [ + "dist" + ], + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.js" + }, + "./package.json": "./package.json" + }, + "devDependencies": { + "@trigger.dev/sdk": "workspace:^2.2.0", + "@trigger.dev/tsconfig": "workspace:*", + "@types/debug": "^4.1.7", + "@types/express": "^4.17.13", + "fastify": "^4.23.2", + "rimraf": "^3.0.2", + "tsup": "^6.5.0", + "tsx": "^3.12.1" + }, + "scripts": { + "clean": "rimraf dist", + "build": "npm run clean && npm run build:tsup", + "build:tsup": "tsup" + }, + "peerDependencies": { + "@nestjs/common": ">=10.0.0", + "@trigger.dev/sdk": "workspace:^2.2.0" + }, + "dependencies": { + "@nestjs/common": "^10.2.4", + "@remix-run/web-fetch": "^4.3.5", + "debug": "^4.3.4" + }, + "engines": { + "node": ">=18.0.0" + } +} diff --git a/packages/nestjs/src/index.ts b/packages/nestjs/src/index.ts new file mode 100644 index 000000000..e1fbfaebb --- /dev/null +++ b/packages/nestjs/src/index.ts @@ -0,0 +1,216 @@ +import { + Body, + ConfigurableModuleBuilder, + Controller, + DynamicModule, + Head, + Headers, + HttpCode, + Inject, + InjectionToken, + InternalServerErrorException, + Module, + NotFoundException, + Post, + Res, +} from "@nestjs/common"; +import { Headers as StandardHeaders, Request as StandardRequest } from "@remix-run/web-fetch"; +import { TriggerClient, TriggerClientOptions } from "@trigger.dev/sdk"; +import type { Response } from "express"; +import type { FastifyReply } from "fastify"; + +const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN, OPTIONS_TYPE, ASYNC_OPTIONS_TYPE } = + new ConfigurableModuleBuilder().build(); + +/** + * The injection token to use for the TriggerDev client. + */ +export const TriggerClientRef = Symbol("TriggerClientRef"); + +/** + * Injects the TriggerDev client. + * It will returns an instance of {@link TriggerClient} + */ +export const InjectTriggerDevClient = (customProviderToken: InjectionToken = TriggerClientRef) => + Inject(customProviderToken); + +/** + * The TriggerDev module for NestJS. + * + * Use {@link TriggerDevModule.register} to register the module, or {@link TriggerDevModule.registerAsync} to register it asynchronously. + * + * @example```ts + * import { Module } from '@nestjs/common'; + * import { TriggerDevModule } from '@trigger.dev/nestjs'; + * + * @Module({ + * imports: [ + * TriggerDevModule.register({ + * id: 'my-client', + * apiKey: process.env['TRIGGER_API_KEY']!, + * }), + * // you can also can configure it asynchnously + * TriggerDevModule.registerAsync({ + * inject: [YouConfigService], + * useFactory: (configService: YouConfigService) => { + * return { + * id: 'my-client', + * apiKey: configService.get('TRIGGER_API_KEY'), + * }; + * }, + * }), + * ], + * }) + * export class AppModule {} + * ``` + */ +@Module({}) +export class TriggerDevModule extends ConfigurableModuleClass { + /** + * Register the instance for the TriggerDev client. + * + * Hint: If you want to have multiple instances of the client, you can use the `customProviderToken` to create multiple instances. + * + * @param options The options to use for the client + * @param path The path to use for the controller (default: `/api/trigger`) + * @param customProviderToken The token to use for the provider (default: {@link TriggerClientRef}) + */ + static register( + options: typeof OPTIONS_TYPE, + path: string = "/api/trigger", + customProviderToken: InjectionToken = TriggerClientRef + ): DynamicModule { + const { providers, ...rest } = ConfigurableModuleClass.register(options); + const controller = createControllerByPath(customProviderToken, path); + + return { + ...rest, + controllers: [controller], + providers: [ + ...(providers || []), + { + provide: customProviderToken, + inject: [MODULE_OPTIONS_TOKEN], + useFactory: (options: TriggerClientOptions) => { + return new TriggerClient(options); + }, + }, + ], + exports: [customProviderToken], + }; + } + + /** + * Register the instance for the TriggerDev client asynchronously. + * + * Hint: If you want to have multiple instances of the client, you can use the `customProviderToken` to create multiple instances. + * + * @param options The options to use for the client + * @param path The path to use for the controller (default: `/api/trigger`) + * @param customProviderToken The token to use for the provider (default: {@link TriggerClientRef}) + */ + static registerAsync( + options: typeof ASYNC_OPTIONS_TYPE, + path: string = "/api/trigger", + customProviderToken: InjectionToken = TriggerClientRef + ): DynamicModule { + const { providers, ...rest } = ConfigurableModuleClass.registerAsync(options); + const controller = createControllerByPath(customProviderToken, path); + + return { + ...rest, + controllers: [controller], + providers: [ + ...(providers || []), + { + provide: customProviderToken, + inject: [MODULE_OPTIONS_TOKEN], + useFactory: (options: TriggerClientOptions) => { + return new TriggerClient(options); + }, + }, + ], + exports: [customProviderToken], + }; + } +} + +/** + * Used to create a custom controller for NestJS with specific path to handle TriggerDev requests. + * + * @param customProvider The provider to use to inject the TriggerDev client + * @param path The path to use for the controller + */ +function createControllerByPath(customProvider: InjectionToken, path: string) { + @Controller(path) + class TriggerDevController { + constructor( + @InjectTriggerDevClient(customProvider) + private readonly client: TriggerClient + ) {} + + @Head() + @HttpCode(200) + public empty() {} + + @Post() + public handleRequestPost( + @Res({ passthrough: true }) res: any, + @Headers() headers: unknown, + @Body() body?: unknown + ): Promise { + return this.handleRequest(res, "POST", headers, body); + } + + /** + * Forward the request to the TriggerDev client + */ + public async handleRequest( + res: any, + method: string, + requestHeaders: unknown, + requestBody?: unknown + ): Promise { + // try { + const headers = new StandardHeaders(); + + Object.entries(requestHeaders || {}).forEach(([key, value]) => { + headers.set(key, value as string); + }); + + // Create a new Request object (hardcode the url because it doesn't really matter what it is) + const standardRequest = new StandardRequest("https://nestjs.com/api/trigger", { + headers, + method, + // @ts-ignore + body: requestBody ? JSON.stringify(requestBody) : undefined, + }); + + const response = await this.client.handleRequest(standardRequest); + + if (!response) { + throw new NotFoundException({ error: "Not found" }); + } + + if (typeof res.status === "function") { + // express + (res as Response).status(response.status); + (res as Response).set(response.headers); + } else if (typeof res.code === "function") { + // fastify + (res as FastifyReply).code(response.status); + if (response.headers) { + (res as FastifyReply).headers(response.headers); + } + } else { + throw new InternalServerErrorException( + "Unable to indetify the framework to set the status code, are you using Express or Fastify?" + ); + } + + return response.body; + } + } + + return TriggerDevController; +} diff --git a/packages/nestjs/tsconfig.json b/packages/nestjs/tsconfig.json new file mode 100644 index 000000000..1eea77ed9 --- /dev/null +++ b/packages/nestjs/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@trigger.dev/tsconfig/node18.json", + "include": ["./src/**/*.ts", "tsup.config.ts"], + "compilerOptions": { + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "declaration": false, + "declarationMap": false + }, + "exclude": ["node_modules"] +} diff --git a/packages/nestjs/tsup.config.ts b/packages/nestjs/tsup.config.ts new file mode 100644 index 000000000..74c4f4dc1 --- /dev/null +++ b/packages/nestjs/tsup.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from "tsup"; + +export default defineConfig([ + { + name: "main", + entry: ["./src/index.ts"], + outDir: "./dist", + platform: "node", + format: ["cjs"], + legacyOutput: true, + sourcemap: true, + clean: true, + bundle: true, + splitting: false, + dts: true, + external: ["http", "https", "util", "events", "tty", "os", "timers"], + esbuildPlugins: [], + }, +]); diff --git a/packages/nextjs/CHANGELOG.md b/packages/nextjs/CHANGELOG.md index 61d1c6f63..35b288f62 100644 --- a/packages/nextjs/CHANGELOG.md +++ b/packages/nextjs/CHANGELOG.md @@ -1,5 +1,34 @@ # @trigger.dev/nextjs +## 2.2.0 + +### Minor Changes + +- 975c5f1d: Drop support for Node v16, require Node >= 18. This allows us to use native fetch in our SDK which paves the way for multi-platform support. + +### Patch Changes + +- Updated dependencies [975c5f1d] + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + +## 2.1.8 + +### Patch Changes + +- ab9e4a98: Send client version back to the server via headers +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index f3adf42ad..c2420baf8 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/nextjs", - "version": "2.1.7", + "version": "2.2.0", "description": "Trigger.dev Next.js integration", "license": "MIT", "main": "./dist/index.js", @@ -34,13 +34,13 @@ "build:tsup": "tsup" }, "peerDependencies": { - "@trigger.dev/sdk": "workspace:^2.1.7", + "@trigger.dev/sdk": "workspace:^2.2.0", "next": ">=12.0.0 <14.0.0" }, "dependencies": { "debug": "^4.3.4" }, "engines": { - "node": ">=16.8.0" + "node": ">=18.0.0" } } diff --git a/packages/nextjs/src/index.ts b/packages/nextjs/src/index.ts index b48dd9e87..1f6660cb0 100644 --- a/packages/nextjs/src/index.ts +++ b/packages/nextjs/src/index.ts @@ -14,6 +14,14 @@ export function createPagesRoute(client: TriggerClient) { return; } + if (response.headers) { + for (const [key, value] of Object.entries(response.headers)) { + if (typeof value === "string") { + res.setHeader(key, value); + } + } + } + res.status(response.status).json(response.body); }; @@ -35,7 +43,7 @@ export function createAppRoute(client: TriggerClient) { return NextResponse.json({ error: "Not found" }, { status: 404 }); } - return NextResponse.json(response.body, { status: response.status }); + return NextResponse.json(response.body, { status: response.status, headers: response.headers }); }; return { diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index e96c00e73..3007cc327 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,5 +1,31 @@ # @trigger.dev/react +## 2.2.0 + +### Patch Changes + +- Updated dependencies [975c5f1d] +- Updated dependencies [50e3d9e4] +- Updated dependencies [59a94c71] + - @trigger.dev/core@2.2.0 + +## 2.1.9 + +### Patch Changes + +- 9a187f9e: upgrade zod to 3.22.3 +- Updated dependencies [9a187f9e] + - @trigger.dev/core@2.1.9 + +## 2.1.8 + +### Patch Changes + +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/core@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/packages/react/package.json b/packages/react/package.json index d45c9dce5..063966831 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/react", - "version": "2.1.7", + "version": "2.2.0", "description": "Trigger.dev React SDK", "license": "MIT", "types": "dist/index.d.ts", @@ -27,9 +27,9 @@ }, "dependencies": { "@tanstack/react-query": "5.0.0-beta.2", - "@trigger.dev/core": "workspace:^2.1.7", + "@trigger.dev/core": "workspace:^2.2.0", "debug": "^4.3.4", - "zod": "3.21.4" + "zod": "3.22.3" }, "devDependencies": { "@trigger.dev/tsconfig": "workspace:*", diff --git a/packages/remix/CHANGELOG.md b/packages/remix/CHANGELOG.md index 4d7e08f25..6da59d203 100644 --- a/packages/remix/CHANGELOG.md +++ b/packages/remix/CHANGELOG.md @@ -1,5 +1,30 @@ # @trigger.dev/remix +## 2.2.0 + +### Patch Changes + +- Updated dependencies [975c5f1d] + - @trigger.dev/sdk@2.2.0 + +## 2.1.9 + +### Patch Changes + +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + +## 2.1.8 + +### Patch Changes + +- ab9e4a98: Send client version back to the server via headers +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/packages/remix/package.json b/packages/remix/package.json index 263d148b8..d5ef391b7 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/remix", - "version": "2.1.7", + "version": "2.2.0", "description": "Trigger.dev Remix integration", "license": "MIT", "main": "./dist/index.js", @@ -34,7 +34,7 @@ "build:tsup": "tsup" }, "peerDependencies": { - "@trigger.dev/sdk": "workspace:^2.1.7", + "@trigger.dev/sdk": "workspace:^2.2.0", "@remix-run/server-runtime": ">1.19.0" }, "dependencies": { diff --git a/packages/remix/src/index.ts b/packages/remix/src/index.ts index 347e34da8..04ec567e8 100644 --- a/packages/remix/src/index.ts +++ b/packages/remix/src/index.ts @@ -9,7 +9,7 @@ export function createRemixRoute(client: TriggerClient) { return json({ error: "Not found" }, { status: 404 }); } - return json(response.body, { status: response.status }); + return json(response.body, { status: response.status, headers: response.headers }); }; return { action }; } diff --git a/packages/sveltekit/CHANGELOG.md b/packages/sveltekit/CHANGELOG.md new file mode 100644 index 000000000..44af09954 --- /dev/null +++ b/packages/sveltekit/CHANGELOG.md @@ -0,0 +1,13 @@ +# @trigger.dev/sveltekit + +## 2.2.0 + +### Minor Changes + +- 975c5f1d: Drop support for Node v16, require Node >= 18. This allows us to use native fetch in our SDK which paves the way for multi-platform support. + +### Patch Changes + +- 0558b2c5: SvelteKit adaptor package +- Updated dependencies [975c5f1d] + - @trigger.dev/sdk@2.2.0 diff --git a/packages/sveltekit/LICENSE b/packages/sveltekit/LICENSE new file mode 100644 index 000000000..9cf106272 --- /dev/null +++ b/packages/sveltekit/LICENSE @@ -0,0 +1,19 @@ +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/sveltekit/README.md b/packages/sveltekit/README.md new file mode 100644 index 000000000..a1c4c89c8 --- /dev/null +++ b/packages/sveltekit/README.md @@ -0,0 +1,9 @@ +# SvelteKit and Trigger.dev + +Trigger.dev provides full support for the SvelteKit framework. + +For information about using Trigger.dev in your SvelteKit app, check out these useful documentation links: + +- [`Quick start guide for setting up Trigger.dev in a SvelteKit project`](https://trigger.dev/docs/documentation/quickstarts/sveltekit) +- [`Manually set up SvelteKit with Trigger.dev`](https://trigger.dev/docs/documentation/guides/manual/sveltekit) +- [`Using Trigger.dev with SvelteKit, handling middleware, and more`](https://trigger.dev/docs/documentation/guides/platforms/sveltekit) diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json new file mode 100644 index 000000000..df5044756 --- /dev/null +++ b/packages/sveltekit/package.json @@ -0,0 +1,44 @@ +{ + "name": "@trigger.dev/sveltekit", + "version": "2.2.0", + "description": "Trigger.dev svelteKit integration", + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "type": "commonjs", + "publishConfig": { + "access": "public" + }, + "files": [ + "dist" + ], + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.js" + }, + "./package.json": "./package.json" + }, + "devDependencies": { + "@sveltejs/kit": "^1.20.4", + "@trigger.dev/tsconfig": "workspace:*", + "@types/debug": "^4.1.7", + "@types/ws": "^8.5.3", + "rimraf": "^3.0.2", + "tsup": "^6.5.0" + }, + "scripts": { + "clean": "rimraf dist", + "build": "npm run clean && npm run build:tsup", + "build:tsup": "tsup" + }, + "peerDependencies": { + "@trigger.dev/sdk": "workspace:^2.2.0" + }, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">=18.0.0" + } +} diff --git a/packages/sveltekit/src/index.ts b/packages/sveltekit/src/index.ts new file mode 100644 index 000000000..f832d92f7 --- /dev/null +++ b/packages/sveltekit/src/index.ts @@ -0,0 +1,42 @@ +import type { TriggerClient } from "@trigger.dev/sdk"; + +import { json } from "@sveltejs/kit"; +import type { RequestHandler } from "@sveltejs/kit"; + +export function createSvelteRoute(client: TriggerClient) { + const POST: RequestHandler = async ({ request }) => { + const standardizedRequest = await convertToStandardRequest(request); + const response = await client.handleRequest(standardizedRequest); + + if (!response) { + return json({ error: "Resource not found" }, { status: 404 }); + } + + return json(response.body, { status: response.status, headers: response.headers }); + }; + return { POST }; +} + +async function convertToStandardRequest(req: Request): Promise { + // Prepare the request to be a fetch-compatible Request object: + const requestHeaders = req.headers; + const requestMethod = req.method; + const responseHeaders = Object.create(null); + + for (const [headerName, headerValue] of requestHeaders.entries()) { + responseHeaders[headerName] = headerValue; + } + + // Create a new Request object to be passed to the TriggerClient + // where we pass the clone the incoming request metadata such as + // headers, method, body. + const request = new Request("https://svelte/api/trigger", { + headers: responseHeaders, + method: requestMethod, + // @ts-ignore + body: req.body ? req.body : req, + duplex: "half", + }); + + return request; +} diff --git a/packages/sveltekit/tsconfig.json b/packages/sveltekit/tsconfig.json new file mode 100644 index 000000000..3421bbad1 --- /dev/null +++ b/packages/sveltekit/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "@trigger.dev/tsconfig/node18.json", + "include": ["./src/**/*.ts", "tsup.config.ts"], + "compilerOptions": { + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "declaration": false, + "declarationMap": false, + "lib": ["DOM", "DOM.Iterable"], + "paths": { + "@trigger.dev/sdk": ["../trigger-sdk/src/index"], + "@trigger.dev/sdk/*": ["../trigger-sdk/src/*"], + "@trigger.dev/core": ["../core/src/index"], + "@trigger.dev/core/*": ["../core/src/*"] + } + }, + "exclude": ["node_modules"] +} diff --git a/packages/sveltekit/tsup.config.ts b/packages/sveltekit/tsup.config.ts new file mode 100644 index 000000000..74c4f4dc1 --- /dev/null +++ b/packages/sveltekit/tsup.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from "tsup"; + +export default defineConfig([ + { + name: "main", + entry: ["./src/index.ts"], + outDir: "./dist", + platform: "node", + format: ["cjs"], + legacyOutput: true, + sourcemap: true, + clean: true, + bundle: true, + splitting: false, + dts: true, + external: ["http", "https", "util", "events", "tty", "os", "timers"], + esbuildPlugins: [], + }, +]); diff --git a/packages/testing/CHANGELOG.md b/packages/testing/CHANGELOG.md index df5e27f1c..fd21caf17 100644 --- a/packages/testing/CHANGELOG.md +++ b/packages/testing/CHANGELOG.md @@ -1,5 +1,35 @@ # @trigger.dev/testing +## 2.2.0 + +### Patch Changes + +- Updated dependencies [975c5f1d] +- Updated dependencies [50e3d9e4] +- Updated dependencies [59a94c71] + - @trigger.dev/sdk@2.2.0 + - @trigger.dev/core@2.2.0 + +## 2.1.9 + +### Patch Changes + +- 9a187f9e: upgrade zod to 3.22.3 +- Updated dependencies [9a187f9e] +- Updated dependencies [2e9452ab] + - @trigger.dev/sdk@2.1.9 + - @trigger.dev/core@2.1.9 + +## 2.1.8 + +### Patch Changes + +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/sdk@2.1.8 + - @trigger.dev/core@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/packages/testing/package.json b/packages/testing/package.json index dcb60e746..faa18e8a3 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -1,7 +1,7 @@ { "name": "@trigger.dev/testing", "description": "A collection of useful tools to write tests for Trigger.dev.", - "version": "2.1.7", + "version": "2.2.0", "license": "MIT", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -38,7 +38,7 @@ "@trigger.dev/core": "workspace:*", "@trigger.dev/sdk": "workspace:*", "vitest": "^0.34.3", - "zod": "3.21.4" + "zod": "3.22.3" }, "devDependencies": { "@trigger.dev/stripe": "workspace:*", diff --git a/packages/trigger-sdk/CHANGELOG.md b/packages/trigger-sdk/CHANGELOG.md index e1249e8ac..211449219 100644 --- a/packages/trigger-sdk/CHANGELOG.md +++ b/packages/trigger-sdk/CHANGELOG.md @@ -1,5 +1,39 @@ # @trigger.dev/sdk +## 2.2.0 + +### Minor Changes + +- 975c5f1d: Drop support for Node v16, require Node >= 18. This allows us to use native fetch in our SDK which paves the way for multi-platform support. + +### Patch Changes + +- Updated dependencies [975c5f1d] +- Updated dependencies [50e3d9e4] +- Updated dependencies [59a94c71] + - @trigger.dev/core@2.2.0 + +## 2.1.9 + +### Patch Changes + +- 9a187f9e: upgrade zod to 3.22.3 +- 2e9452ab: allow cancelling jobs from trigger-client +- Updated dependencies [9a187f9e] + - @trigger.dev/core@2.1.9 + +## 2.1.8 + +### Patch Changes + +- 6a992a19: First release of `@trigger.dev/replicate` integration with remote callback support. +- ab9e4a98: Send client version back to the server via headers +- ab9e4a98: Better performance when resuming a run, especially one with a large amount of tasks +- Updated dependencies [6a992a19] +- Updated dependencies [ab9e4a98] +- Updated dependencies [ab9e4a98] + - @trigger.dev/core@2.1.8 + ## 2.1.7 ### Patch Changes diff --git a/packages/trigger-sdk/package.json b/packages/trigger-sdk/package.json index b452ae686..9de7e97a4 100644 --- a/packages/trigger-sdk/package.json +++ b/packages/trigger-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/sdk", - "version": "2.1.7", + "version": "2.2.0", "description": "trigger.dev Node.JS SDK", "license": "MIT", "main": "./dist/index.js", @@ -25,7 +25,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@trigger.dev/core": "workspace:^2.1.7", + "@trigger.dev/core": "workspace:^2.2.0", "chalk": "^5.2.0", "cronstrue": "^2.21.0", "debug": "^4.3.4", @@ -33,19 +33,17 @@ "get-caller-file": "^2.0.5", "git-remote-origin-url": "^4.0.0", "git-repo-info": "^2.1.1", - "node-fetch": "2.6.x", "slug": "^6.0.0", "terminal-link": "^3.0.0", "ulid": "^2.3.0", "uuid": "^9.0.0", "ws": "^8.11.0", - "zod": "3.21.4" + "zod": "3.22.3" }, "devDependencies": { "@trigger.dev/tsconfig": "workspace:*", "@types/debug": "^4.1.7", "@types/node": "18", - "@types/node-fetch": "2.6.x", "@types/slug": "^5.0.3", "@types/uuid": "^9.0.0", "@types/ws": "^8.5.3", @@ -56,6 +54,6 @@ "typescript": "^4.8.4" }, "engines": { - "node": ">=16.8.0" + "node": ">=18.0.0" } } \ No newline at end of file diff --git a/packages/trigger-sdk/src/apiClient.ts b/packages/trigger-sdk/src/apiClient.ts index 744a5cb8c..2f0efc2e7 100644 --- a/packages/trigger-sdk/src/apiClient.ts +++ b/packages/trigger-sdk/src/apiClient.ts @@ -27,9 +27,10 @@ import { JobRunStatusRecordSchema, StatusUpdate, urlWithSearchParams, + RunTaskResponseWithCachedTasksBodySchema, + API_VERSIONS, } from "@trigger.dev/core"; -import fetch, { type RequestInit } from "node-fetch"; import { z } from "zod"; export type ApiClientOptions = { @@ -106,22 +107,35 @@ export class ApiClient { return await response.json(); } - async runTask(runId: string, task: RunTaskBodyInput) { + async runTask( + runId: string, + task: RunTaskBodyInput, + options: { cachedTasksCursor?: string } = {} + ) { const apiKey = await this.#apiKey(); this.#logger.debug("Running Task", { task, }); - return await zodfetch(ServerTaskSchema, `${this.#apiUrl}/api/v1/runs/${runId}/tasks`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${apiKey}`, - "Idempotency-Key": task.idempotencyKey, + return await zodfetchWithVersions( + { + [API_VERSIONS.LAZY_LOADED_CACHED_TASKS]: RunTaskResponseWithCachedTasksBodySchema, }, - body: JSON.stringify(task), - }); + ServerTaskSchema, + `${this.#apiUrl}/api/v1/runs/${runId}/tasks`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${apiKey}`, + "Idempotency-Key": task.idempotencyKey, + "X-Cached-Tasks-Cursor": options.cachedTasksCursor ?? "", + "Trigger-Version": API_VERSIONS.LAZY_LOADED_CACHED_TASKS, + }, + body: JSON.stringify(task), + } + ); } async completeTask(runId: string, id: string, task: CompleteTaskBodyInput) { @@ -384,6 +398,22 @@ export class ApiClient { ); } + async cancelRun(runId: string) { + const apiKey = await this.#apiKey(); + + this.#logger.debug("Cancelling Run", { + runId, + }); + + return await zodfetch(GetRunSchema, `${this.#apiUrl}/api/v1/runs/${runId}/cancel`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${apiKey}`, + }, + }); + } + async getRunStatuses(runId: string) { const apiKey = await this.#apiKey(); @@ -481,15 +511,103 @@ function getApiKey(key?: string) { return { status: "valid" as const, apiKey }; } -async function zodfetch( - schema: z.Schema, +type VersionedResponseBodyMap = { + [key: string]: z.ZodTypeAny; +}; + +// The resulting type should be a discriminating union +// For example, if the TVersions param is { "2023_09_29": z.string() } and the TUnversioned param is z.number(), the resulting type should be: +// type VersionedResponseBody = { version: "2023_09_29"; body: string } | { version: "unversioned"; body: number } +type VersionedResponseBody< + TVersions extends VersionedResponseBodyMap, + TUnversioned extends z.ZodTypeAny, +> = + | { + [TVersion in keyof TVersions]: { + version: TVersion; + body: z.infer; + }; + }[keyof TVersions] + | { + version: "unversioned"; + body: z.infer; + }; + +async function zodfetchWithVersions< + TVersionedResponseBodyMap extends VersionedResponseBodyMap, + TUnversionedResponseBodySchema extends z.ZodTypeAny, + TOptional extends boolean = false, +>( + versionedSchemaMap: TVersionedResponseBodyMap, + unversionedSchema: TUnversionedResponseBodySchema, url: string, requestInit?: RequestInit, options?: { errorMessage?: string; optional?: TOptional; } -): Promise { +): Promise< + TOptional extends true + ? VersionedResponseBody | undefined + : VersionedResponseBody +> { + const response = await fetch(url, requestInit); + + if ( + (!requestInit || requestInit.method === "GET") && + response.status === 404 && + options?.optional + ) { + // @ts-ignore + return; + } + + if (response.status >= 400 && response.status < 500) { + const body = await response.json(); + + throw new Error(body.error); + } + + if (response.status !== 200) { + throw new Error( + options?.errorMessage ?? `Failed to fetch ${url}, got status code ${response.status}` + ); + } + + const jsonBody = await response.json(); + + const version = response.headers.get("trigger-version"); + + if (!version) { + return { + version: "unversioned", + body: unversionedSchema.parse(jsonBody), + }; + } + + const versionedSchema = versionedSchemaMap[version]; + + if (!versionedSchema) { + throw new Error(`Unknown version ${version}`); + } + + return { + version, + body: versionedSchema.parse(jsonBody), + }; +} + +async function zodfetch( + schema: TResponseSchema, + url: string, + requestInit?: RequestInit, + options?: { + errorMessage?: string; + optional?: TOptional; + } +): Promise< + TOptional extends true ? z.infer | undefined : z.infer +> { const response = await fetch(url, requestInit); if ( diff --git a/packages/trigger-sdk/src/errors.ts b/packages/trigger-sdk/src/errors.ts index 5ef025203..9e2aeec0e 100644 --- a/packages/trigger-sdk/src/errors.ts +++ b/packages/trigger-sdk/src/errors.ts @@ -16,6 +16,10 @@ export class CanceledWithTaskError { constructor(public task: ServerTask) {} } +export class YieldExecutionError { + constructor(public key: string) {} +} + export class ParsedPayloadSchemaError { constructor(public schemaErrors: SchemaError[]) {} } @@ -32,6 +36,7 @@ export function isTriggerError( return ( err instanceof ResumeWithTaskError || err instanceof RetryWithTaskError || - err instanceof CanceledWithTaskError + err instanceof CanceledWithTaskError || + err instanceof YieldExecutionError ); } diff --git a/packages/trigger-sdk/src/io.ts b/packages/trigger-sdk/src/io.ts index 9286e7109..b4dacfc5e 100644 --- a/packages/trigger-sdk/src/io.ts +++ b/packages/trigger-sdk/src/io.ts @@ -1,4 +1,6 @@ import { + API_VERSIONS, + BloomFilter, CachedTask, ConnectionAuth, CronOptions, @@ -15,6 +17,7 @@ import { SerializableJsonSchema, ServerTask, UpdateTriggerSourceBodyV2, + supportsFeature, } from "@trigger.dev/core"; import { AsyncLocalStorage } from "node:async_hooks"; import { webcrypto } from "node:crypto"; @@ -23,6 +26,7 @@ import { CanceledWithTaskError, ResumeWithTaskError, RetryWithTaskError, + YieldExecutionError, isTriggerError, } from "./errors"; import { calculateRetryAt } from "./retry"; @@ -46,6 +50,10 @@ export type IOOptions = { jobLogger?: Logger; jobLogLevel: LogLevel; cachedTasks?: Array; + cachedTasksCursor?: string; + yieldedExecutions?: Array; + noopTasksSet?: string; + serverVersion?: string | null; }; type JsonPrimitive = string | number | boolean | null | undefined | Date | symbol; @@ -63,6 +71,16 @@ export type RunTaskErrorCallback = ( | undefined | void; +export type IOStats = { + initialCachedTasks: number; + lazyLoadedCachedTasks: number; + executedTasks: number; + cachedTaskHits: number; + cachedTaskMisses: number; + noopCachedTaskHits: number; + noopCachedTaskMisses: number; +}; + export class IO { private _id: string; private _apiClient: ApiClient; @@ -72,7 +90,16 @@ export class IO { private _jobLogLevel: LogLevel; private _cachedTasks: Map; private _taskStorage: AsyncLocalStorage<{ taskId: string }>; + private _cachedTasksCursor?: string; private _context: TriggerContext; + private _yieldedExecutions: Array; + private _noopTasksBloomFilter: BloomFilter | undefined; + private _stats: IOStats; + private _serverVersion: string; + + get stats() { + return this._stats; + } constructor(options: IOOptions) { this._id = options.id; @@ -83,14 +110,37 @@ export class IO { this._jobLogger = options.jobLogger; this._jobLogLevel = options.jobLogLevel; + this._stats = { + initialCachedTasks: 0, + lazyLoadedCachedTasks: 0, + executedTasks: 0, + cachedTaskHits: 0, + cachedTaskMisses: 0, + noopCachedTaskHits: 0, + noopCachedTaskMisses: 0, + }; + if (options.cachedTasks) { options.cachedTasks.forEach((task) => { this._cachedTasks.set(task.idempotencyKey, task); }); + + this._stats.initialCachedTasks = options.cachedTasks.length; } this._taskStorage = new AsyncLocalStorage(); this._context = options.context; + this._yieldedExecutions = options.yieldedExecutions ?? []; + + if (options.noopTasksSet) { + this._noopTasksBloomFilter = BloomFilter.deserialize( + options.noopTasksSet, + BloomFilter.NOOP_TASK_SET_SIZE + ); + } + + this._cachedTasksCursor = options.cachedTasksCursor; + this._serverVersion = options.serverVersion ?? "unversioned"; } /** @internal */ @@ -108,44 +158,48 @@ export class IO { return new IOLogger(async (level, message, data) => { let logLevel: LogLevel = "info"; - switch (level) { - case "LOG": { - this._jobLogger?.log(message, data); - logLevel = "log"; - break; - } - case "DEBUG": { - this._jobLogger?.debug(message, data); - logLevel = "debug"; - break; - } - case "INFO": { - this._jobLogger?.info(message, data); - logLevel = "info"; - break; - } - case "WARN": { - this._jobLogger?.warn(message, data); - logLevel = "warn"; - break; - } - case "ERROR": { - this._jobLogger?.error(message, data); - logLevel = "error"; - break; - } - } - if (Logger.satisfiesLogLevel(logLevel, this._jobLogLevel)) { - await this.runTask([message, level], async (task) => {}, { - name: "log", - icon: "log", - description: message, - params: data, - properties: [{ label: "Level", text: level }], - style: { style: "minimal", variant: level.toLowerCase() }, - noop: true, - }); + await this.runTask( + [message, level], + async (task) => { + switch (level) { + case "LOG": { + this._jobLogger?.log(message, data); + logLevel = "log"; + break; + } + case "DEBUG": { + this._jobLogger?.debug(message, data); + logLevel = "debug"; + break; + } + case "INFO": { + this._jobLogger?.info(message, data); + logLevel = "info"; + break; + } + case "WARN": { + this._jobLogger?.warn(message, data); + logLevel = "warn"; + break; + } + case "ERROR": { + this._jobLogger?.error(message, data); + logLevel = "error"; + break; + } + } + }, + { + name: "log", + icon: "log", + description: message, + params: data, + properties: [{ label: "Level", text: level }], + style: { style: "minimal", variant: level.toLowerCase() }, + noop: true, + } + ); } }); } @@ -549,19 +603,59 @@ export class IO { if (cachedTask && cachedTask.status === "COMPLETED") { this._logger.debug("Using completed cached task", { idempotencyKey, - cachedTask, }); + this._stats.cachedTaskHits++; + return cachedTask.output as T; } - const task = await this._apiClient.runTask(this._id, { - idempotencyKey, - displayKey: typeof key === "string" ? key : key.join("."), - noop: false, - ...(options ?? {}), - parentId, - }); + if (options?.noop && this._noopTasksBloomFilter) { + if (this._noopTasksBloomFilter.test(idempotencyKey)) { + this._logger.debug("task idempotency key exists in noopTasksBloomFilter", { + idempotencyKey, + }); + + this._stats.noopCachedTaskHits++; + + return {} as T; + } + } + + const response = await this._apiClient.runTask( + this._id, + { + idempotencyKey, + displayKey: typeof key === "string" ? key : undefined, + noop: false, + ...(options ?? {}), + parentId, + }, + { + cachedTasksCursor: this._cachedTasksCursor, + } + ); + + const task = + response.version === API_VERSIONS.LAZY_LOADED_CACHED_TASKS + ? response.body.task + : response.body; + + if (response.version === API_VERSIONS.LAZY_LOADED_CACHED_TASKS) { + this._cachedTasksCursor = response.body.cachedTasks?.cursor; + + for (const cachedTask of response.body.cachedTasks?.tasks ?? []) { + if (!this._cachedTasks.has(cachedTask.idempotencyKey)) { + this._cachedTasks.set(cachedTask.idempotencyKey, cachedTask); + + this._logger.debug("Injecting lazy loaded task into task cache", { + idempotencyKey: cachedTask.idempotencyKey, + }); + + this._stats.lazyLoadedCachedTasks++; + } + } + } if (task.status === "CANCELED") { this._logger.debug("Task canceled", { @@ -573,12 +667,20 @@ export class IO { } if (task.status === "COMPLETED") { - this._logger.debug("Using task output", { - idempotencyKey, - task, - }); + if (task.noop) { + this._logger.debug("Noop Task completed", { + idempotencyKey, + }); - this.#addToCachedTasks(task); + this._noopTasksBloomFilter?.add(task.idempotencyKey); + } else { + this._logger.debug("Cache miss", { + idempotencyKey, + }); + + this._stats.cachedTaskMisses++; + this.#addToCachedTasks(task); + } return task.output as T; } @@ -592,28 +694,18 @@ export class IO { throw new Error(task.error ?? task?.output ? JSON.stringify(task.output) : "Task errored"); } - if (task.status === "WAITING") { - this._logger.debug("Task waiting", { - idempotencyKey, - task, - }); - - throw new ResumeWithTaskError(task); - } - - if (task.status === "RUNNING" && typeof task.operation === "string") { - this._logger.debug("Task running operation", { - idempotencyKey, - task, - }); - - throw new ResumeWithTaskError(task); - } - const executeTask = async () => { try { const result = await callback(task, this); + if (task.status === "WAITING" && task.callbackUrl) { + this._logger.debug("Waiting for remote callback", { + idempotencyKey, + task, + }); + return {} as T; + } + const output = SerializableJsonSchema.parse(result) as T; this._logger.debug("Completing using output", { @@ -626,6 +718,8 @@ export class IO { properties: task.outputProperties ?? undefined, }); + this._stats.executedTasks++; + if (completedTask.status === "CANCELED") { throw new CanceledWithTaskError(completedTask); } @@ -696,9 +790,55 @@ export class IO { } }; + if (task.status === "WAITING") { + this._logger.debug("Task waiting", { + idempotencyKey, + task, + }); + + if (task.callbackUrl) { + await this._taskStorage.run({ taskId: task.id }, executeTask); + } + + throw new ResumeWithTaskError(task); + } + + if (task.status === "RUNNING" && typeof task.operation === "string") { + this._logger.debug("Task running operation", { + idempotencyKey, + task, + }); + + throw new ResumeWithTaskError(task); + } + return this._taskStorage.run({ taskId: task.id }, executeTask); } + /** + * `io.yield()` allows you to yield execution of the current run and resume it in a new function execution. Similar to `io.wait()` but does not create a task and resumes execution immediately. + */ + yield(key: string) { + if (!supportsFeature("yieldExecution", this._serverVersion)) { + console.warn( + "[trigger.dev] io.yield() is not support by the version of the Trigger.dev server you are using, you will need to upgrade your self-hosted Trigger.dev instance." + ); + + return; + } + + if (this._yieldedExecutions.includes(key)) { + return; + } + + throw new YieldExecutionError(key); + } + + /** + * `io.brb()` is an alias of `io.yield()` + */ + brb = this.yield.bind(this); + /** `io.try()` allows you to run Tasks and catch any errors that are thrown, it's similar to a normal `try/catch` block but works with [io.runTask()](/sdk/io/runtask). * A regular `try/catch` block on its own won't work as expected with Tasks. Internally `runTask()` throws some special errors to control flow execution. This is necessary to deal with resumability, serverless timeouts, and retrying Tasks. * @param tryCallback The code you wish to run diff --git a/packages/trigger-sdk/src/triggerClient.ts b/packages/trigger-sdk/src/triggerClient.ts index 44c638dcf..b1766f164 100644 --- a/packages/trigger-sdk/src/triggerClient.ts +++ b/packages/trigger-sdk/src/triggerClient.ts @@ -1,4 +1,5 @@ import { + API_VERSIONS, ConnectionAuth, DeserializedJson, ErrorWithStackSchema, @@ -36,9 +37,10 @@ import { ParsedPayloadSchemaError, ResumeWithTaskError, RetryWithTaskError, + YieldExecutionError, } from "./errors"; import { TriggerIntegration } from "./integrations"; -import { IO } from "./io"; +import { IO, IOStats } from "./io"; import { createIOWithIntegrations } from "./ioWithIntegrations"; import { Job, JobOptions } from "./job"; import { runLocalStorage } from "./runLocalStorage"; @@ -124,7 +126,10 @@ export class TriggerClient { this.id = options.id; this.#options = options; this.#client = new ApiClient(this.#options); - this.#internalLogger = new Logger("trigger.dev", this.#options.verbose ? "debug" : "log"); + this.#internalLogger = new Logger("trigger.dev", this.#options.verbose ? "debug" : "log", [ + "output", + "noopTasksSet", + ]); } async handleRequest(request: Request): Promise { @@ -135,6 +140,7 @@ export class TriggerClient { }); const apiKey = request.headers.get("x-trigger-api-key"); + const triggerVersion = request.headers.get("x-trigger-version"); const authorization = this.authorized(apiKey); @@ -148,6 +154,7 @@ export class TriggerClient { body: { message: "Unauthorized: client missing apiKey", }, + headers: this.#standardResponseHeaders, }; } case "missing-header": { @@ -156,6 +163,7 @@ export class TriggerClient { body: { message: "Unauthorized: missing x-trigger-api-key header", }, + headers: this.#standardResponseHeaders, }; } case "unauthorized": { @@ -164,6 +172,7 @@ export class TriggerClient { body: { message: `Forbidden: client apiKey mismatch: Make sure you are using the correct API Key for your environment`, }, + headers: this.#standardResponseHeaders, }; } } @@ -174,6 +183,7 @@ export class TriggerClient { body: { message: "Method not allowed (only POST is allowed)", }, + headers: this.#standardResponseHeaders, }; } @@ -185,6 +195,7 @@ export class TriggerClient { body: { message: "Missing x-trigger-action header", }, + headers: this.#standardResponseHeaders, }; } @@ -199,6 +210,7 @@ export class TriggerClient { ok: false, error: "Missing endpoint ID", }, + headers: this.#standardResponseHeaders, }; } @@ -209,6 +221,7 @@ export class TriggerClient { ok: false, error: `Endpoint ID mismatch error. Expected ${this.id}, got ${endpointId}`, }, + headers: this.#standardResponseHeaders, }; } @@ -217,6 +230,7 @@ export class TriggerClient { body: { ok: true, }, + headers: this.#standardResponseHeaders, }; } case "INDEX_ENDPOINT": { @@ -241,6 +255,7 @@ export class TriggerClient { return { status: 200, body, + headers: this.#standardResponseHeaders, }; } case "INITIALIZE_TRIGGER": { @@ -270,6 +285,7 @@ export class TriggerClient { return { status: 200, body: dynamicTrigger.registeredTriggerForParams(body.data.params), + headers: this.#standardResponseHeaders, }; } case "EXECUTE_JOB": { @@ -296,11 +312,12 @@ export class TriggerClient { }; } - const results = await this.#executeJob(execution.data, job); + const results = await this.#executeJob(execution.data, job, triggerVersion); return { status: 200, body: results, + headers: this.#standardResponseHeaders, }; } case "PREPROCESS_RUN": { @@ -335,6 +352,7 @@ export class TriggerClient { abort: results.abort, properties: results.properties, }, + headers: this.#standardResponseHeaders, }; } case "DELIVER_HTTP_SOURCE_REQUEST": { @@ -400,6 +418,7 @@ export class TriggerClient { response, metadata, }, + headers: this.#standardResponseHeaders, }; } case "VALIDATE": { @@ -409,6 +428,7 @@ export class TriggerClient { ok: true, endpointId: this.id, }, + headers: this.#standardResponseHeaders, }; } } @@ -418,6 +438,7 @@ export class TriggerClient { body: { message: "Method not allowed", }, + headers: this.#standardResponseHeaders, }; } @@ -621,6 +642,10 @@ export class TriggerClient { return this.#client.getRun(runId, options); } + async cancelRun(runId: string) { + return this.#client.cancelRun(runId); + } + async getRuns(jobSlug: string, options?: GetRunsOptions) { return this.#client.getRuns(jobSlug, options); } @@ -664,12 +689,14 @@ export class TriggerClient { async #executeJob( body: RunJobBody, - job: Job, Record> + job: Job, Record>, + triggerVersion: string | null ): Promise { this.#internalLogger.debug("executing job", { execution: body, job: job.id, version: job.version, + triggerVersion, }); const context = this.#createRunContext(body); @@ -677,6 +704,9 @@ export class TriggerClient { const io = new IO({ id: body.run.id, cachedTasks: body.tasks, + cachedTasksCursor: body.cachedTaskCursor, + yieldedExecutions: body.yieldedExecutions ?? [], + noopTasksSet: body.noopTasksSet, apiClient: this.#client, logger: this.#internalLogger, client: this, @@ -685,6 +715,7 @@ export class TriggerClient { jobLogger: this.#options.ioLogLocalEnabled ? new Logger(job.id, job.logLevel ?? this.#options.logLevel ?? "info") : undefined, + serverVersion: triggerVersion, }); const resolvedConnections = await this.#resolveConnections( @@ -715,8 +746,20 @@ export class TriggerClient { ); }); + if (this.#options.verbose) { + this.#logIOStats(io.stats); + } + return { status: "SUCCESS", output }; } catch (error) { + if (this.#options.verbose) { + this.#logIOStats(io.stats); + } + + if (error instanceof YieldExecutionError) { + return { status: "YIELD_EXECUTION", key: error.key }; + } + if (error instanceof ParsedPayloadSchemaError) { return { status: "INVALID_PAYLOAD", errors: error.schemaErrors }; } @@ -1108,6 +1151,18 @@ export class TriggerClient { authSource, }; } + + #logIOStats(stats: IOStats) { + this.#internalLogger.debug("IO stats", { + stats, + }); + } + + get #standardResponseHeaders() { + return { + "Trigger-Version": API_VERSIONS.LAZY_LOADED_CACHED_TASKS, + }; + } } function dynamicTriggerRegisterSourceJobId(id: string) { diff --git a/perf/package.json b/perf/package.json index d62f25191..81ccaf5f4 100644 --- a/perf/package.json +++ b/perf/package.json @@ -22,7 +22,7 @@ "@trigger.dev/stripe": "workspace:*", "@trigger.dev/supabase": "workspace:*", "@trigger.dev/typeform": "workspace:*", - "zod": "3.21.4" + "zod": "3.22.3" }, "devDependencies": { "@trigger.dev/cli": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cb63ba5d2..4b6207a94 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,7 @@ importers: '@codemirror/lang-javascript': ^6.1.1 '@codemirror/lang-json': ^6.0.1 '@codemirror/language': ^6.3.1 + '@codemirror/lint': ^6.4.2 '@codemirror/search': ^6.2.3 '@codemirror/state': ^6.1.3 '@codemirror/view': ^6.5.0 @@ -168,7 +169,7 @@ importers: react: ^18.2.0 react-dom: ^18.2.0 react-hot-toast: ^2.4.0 - react-hotkeys-hook: ^3.4.7 + react-hotkeys-hook: ^4.4.1 react-use: ^17.4.0 recharts: ^2.8.0 remix-auth: ^3.2.2 @@ -181,6 +182,7 @@ importers: simple-oauth2: ^5.0.0 simplur: ^3.0.1 slug: ^6.0.0 + sonner: ^1.0.3 storybook: ^7.0.7 storybook-addon-designs: 7.0.0-beta.2 storybook-addon-variants: ^0.2.0 @@ -194,19 +196,21 @@ importers: tsconfig-paths: ^3.14.1 typescript: ^4.8.4 ulid: ^2.3.0 - zod: 3.21.4 + zod: 3.22.3 zod-error: 1.5.0 + zod-validation-error: ^1.5.0 dependencies: '@codemirror/autocomplete': 6.4.0_czcfkg2f66rxeiodoti7r2gulu '@codemirror/commands': 6.1.3 '@codemirror/lang-javascript': 6.1.2 '@codemirror/lang-json': 6.0.1 '@codemirror/language': 6.3.2 + '@codemirror/lint': 6.4.2 '@codemirror/search': 6.2.3 '@codemirror/state': 6.2.0 '@codemirror/view': 6.7.2 '@conform-to/react': 0.6.1_react@18.2.0 - '@conform-to/zod': 0.6.1_zod@3.21.4 + '@conform-to/zod': 0.6.1_zod@3.22.3 '@godaddy/terminus': 4.12.1 '@headlessui/react': 1.7.8_biqbaboplfbrettd7655fr4n2y '@heroicons/react': 2.0.13_react@18.2.0 @@ -232,7 +236,7 @@ importers: '@trigger.dev/core': link:../../packages/core '@trigger.dev/database': link:../../packages/database '@trigger.dev/sdk': link:../../packages/trigger-sdk - '@uiw/react-codemirror': 4.19.5_k4ec5g7vuuzzonc3d6xbjnmmle + '@uiw/react-codemirror': 4.19.5_th22fcplkuhrqjnlojwclcaim4 class-variance-authority: 0.5.2_typescript@4.9.4 clsx: 1.2.1 compression: 1.7.4 @@ -261,25 +265,27 @@ importers: react: 18.2.0 react-dom: 18.2.0_react@18.2.0 react-hot-toast: 2.4.0_biqbaboplfbrettd7655fr4n2y - react-hotkeys-hook: 3.4.7_biqbaboplfbrettd7655fr4n2y + react-hotkeys-hook: 4.4.1_biqbaboplfbrettd7655fr4n2y react-use: 17.4.0_biqbaboplfbrettd7655fr4n2y recharts: 2.8.0_v2m5e27vhdewzwhryxwfaorcca remix-auth: 3.4.0_mrckq3wlqfipa3hs7ezq3k3x3y remix-auth-email-link: 1.5.2_xmjsiulzsxcc3znmuhq3turs2q remix-auth-github: 1.3.0_xmjsiulzsxcc3znmuhq3turs2q remix-typedjson: 0.1.7_bhtgrpeaoe6kbm4hb4gzl6x7c4 - remix-utils: 6.0.0_c5pntwu5f7mrfmmvuwtiprk4cy + remix-utils: 6.0.0_7krs2yfztxu2oblf77wwc4rpoe semver: 7.5.0 simple-oauth2: 5.0.0 simplur: 3.0.1 slug: 6.1.0 + sonner: 1.0.3_biqbaboplfbrettd7655fr4n2y tailwind-merge: 1.12.0 tailwind-scrollbar-hide: 1.1.7 tailwindcss-animate: 1.0.5_tailwindcss@3.3.2 tiny-invariant: 1.3.1 ulid: 2.3.0 - zod: 3.21.4 + zod: 3.22.3 zod-error: 1.5.0 + zod-validation-error: 1.5.0_zod@3.22.3 devDependencies: '@remix-run/dev': 1.19.2-pre.0_36n2i74sizt32vwpdxc4husnkq '@remix-run/eslint-config': 1.19.2-pre.0_ol4nhuzbuflsbzk2mijpqykzba @@ -351,7 +357,7 @@ importers: devDependencies: eslint: 8.31.0 eslint-config-prettier: 8.6.0_eslint@8.31.0 - eslint-config-turbo: 1.10.14_eslint@8.31.0 + eslint-config-turbo: 1.10.15_eslint@8.31.0 eslint-plugin-react: 7.31.8_eslint@8.31.0 typescript: 4.9.4 @@ -366,20 +372,20 @@ importers: integrations/airtable: specifiers: - '@trigger.dev/integration-kit': workspace:^2.1.7 - '@trigger.dev/sdk': workspace:^2.1.7 + '@trigger.dev/integration-kit': workspace:^2.2.0 + '@trigger.dev/sdk': workspace:^2.2.0 '@trigger.dev/tsconfig': workspace:* '@types/node': 16.x airtable: ^0.12.1 rimraf: ^3.0.2 tsup: 7.1.x typescript: 4.9.4 - zod: 3.21.4 + zod: 3.22.3 dependencies: '@trigger.dev/integration-kit': link:../../packages/integration-kit '@trigger.dev/sdk': link:../../packages/trigger-sdk airtable: 0.12.1 - zod: 3.21.4 + zod: 3.22.3 devDependencies: '@trigger.dev/tsconfig': link:../../config-packages/tsconfig '@types/node': 16.18.11 @@ -394,14 +400,14 @@ importers: '@octokit/types': ^9.2.3 '@octokit/webhooks': ^10.4.0 '@octokit/webhooks-types': ^6.10.0 - '@trigger.dev/integration-kit': workspace:^2.1.7 - '@trigger.dev/sdk': workspace:^2.1.7 + '@trigger.dev/integration-kit': workspace:^2.2.0 + '@trigger.dev/sdk': workspace:^2.2.0 '@trigger.dev/tsconfig': workspace:* '@types/node': '18' octokit: ^2.0.14 rimraf: ^3.0.2 tsup: ^6.5.0 - zod: 3.21.4 + zod: 3.22.3 dependencies: '@octokit/request': 6.2.5 '@octokit/request-error': 4.0.1 @@ -409,7 +415,7 @@ importers: '@trigger.dev/integration-kit': link:../../packages/integration-kit '@trigger.dev/sdk': link:../../packages/trigger-sdk octokit: 2.0.14 - zod: 3.21.4 + zod: 3.22.3 devDependencies: '@octokit/types': 9.2.3 '@octokit/webhooks-types': 6.10.0 @@ -421,19 +427,19 @@ importers: integrations/linear: specifiers: '@linear/sdk': ^8.0.0 - '@trigger.dev/integration-kit': workspace:^2.1.7 - '@trigger.dev/sdk': workspace:^2.1.7 + '@trigger.dev/integration-kit': workspace:^2.2.0 + '@trigger.dev/sdk': workspace:^2.2.0 '@trigger.dev/tsconfig': workspace:* '@types/node': 16.x rimraf: ^3.0.2 tsup: 7.1.x typescript: 4.9.4 - zod: 3.21.4 + zod: 3.22.3 dependencies: '@linear/sdk': 8.0.0 '@trigger.dev/integration-kit': link:../../packages/integration-kit '@trigger.dev/sdk': link:../../packages/trigger-sdk - zod: 3.21.4 + zod: 3.22.3 devDependencies: '@trigger.dev/tsconfig': link:../../config-packages/tsconfig '@types/node': 16.18.11 @@ -443,8 +449,8 @@ importers: integrations/openai: specifiers: - '@trigger.dev/integration-kit': workspace:^2.1.7 - '@trigger.dev/sdk': workspace:^2.1.7 + '@trigger.dev/integration-kit': workspace:^2.2.0 + '@trigger.dev/sdk': workspace:^2.2.0 '@trigger.dev/tsconfig': workspace:* '@types/node': '18' openai: ^4.2.0 @@ -463,8 +469,8 @@ importers: integrations/plain: specifiers: '@team-plain/typescript-sdk': ^2.7.0 - '@trigger.dev/integration-kit': workspace:^2.1.7 - '@trigger.dev/sdk': workspace:^2.1.7 + '@trigger.dev/integration-kit': workspace:^2.2.0 + '@trigger.dev/sdk': workspace:^2.2.0 '@trigger.dev/tsconfig': workspace:* '@types/node': '18' rimraf: ^3.0.2 @@ -479,10 +485,33 @@ importers: rimraf: 3.0.2 tsup: 6.6.3 + integrations/replicate: + specifiers: + '@trigger.dev/integration-kit': workspace:^2.2.0 + '@trigger.dev/sdk': workspace:^2.2.0 + '@trigger.dev/tsconfig': workspace:* + '@types/node': 16.x + replicate: ^0.18.1 + rimraf: ^3.0.2 + tsup: 7.1.x + typescript: 4.9.4 + zod: 3.22.3 + dependencies: + '@trigger.dev/integration-kit': link:../../packages/integration-kit + '@trigger.dev/sdk': link:../../packages/trigger-sdk + replicate: 0.18.1 + zod: 3.22.3 + devDependencies: + '@trigger.dev/tsconfig': link:../../config-packages/tsconfig + '@types/node': 16.18.11 + rimraf: 3.0.2 + tsup: 7.1.0_typescript@4.9.4 + typescript: 4.9.4 + integrations/resend: specifiers: - '@trigger.dev/integration-kit': workspace:^2.1.7 - '@trigger.dev/sdk': workspace:^2.1.7 + '@trigger.dev/integration-kit': workspace:^2.2.0 + '@trigger.dev/sdk': workspace:^2.2.0 '@trigger.dev/tsconfig': workspace:* '@types/node': '18' resend: ^1.0.0 @@ -501,8 +530,8 @@ importers: integrations/sendgrid: specifiers: '@sendgrid/mail': ^7.7.0 - '@trigger.dev/integration-kit': workspace:^2.1.7 - '@trigger.dev/sdk': workspace:^2.1.7 + '@trigger.dev/integration-kit': workspace:^2.2.0 + '@trigger.dev/sdk': workspace:^2.2.0 '@trigger.dev/tsconfig': workspace:* '@types/node': 16.x rimraf: ^3.0.2 @@ -522,16 +551,16 @@ importers: integrations/slack: specifiers: '@slack/web-api': ^6.8.1 - '@trigger.dev/sdk': workspace:^2.1.7 + '@trigger.dev/sdk': workspace:^2.2.0 '@trigger.dev/tsconfig': workspace:* '@types/node': '18' rimraf: ^3.0.2 tsup: ^6.5.0 - zod: 3.21.4 + zod: 3.22.3 dependencies: '@slack/web-api': 6.8.1 '@trigger.dev/sdk': link:../../packages/trigger-sdk - zod: 3.21.4 + zod: 3.22.3 devDependencies: '@trigger.dev/tsconfig': link:../../config-packages/tsconfig '@types/node': 18.15.13 @@ -540,20 +569,20 @@ importers: integrations/stripe: specifiers: - '@trigger.dev/integration-kit': workspace:^2.1.7 - '@trigger.dev/sdk': workspace:^2.1.7 + '@trigger.dev/integration-kit': workspace:^2.2.0 + '@trigger.dev/sdk': workspace:^2.2.0 '@types/node': 16.x rimraf: ^3.0.2 stripe: ^12.14.0 stripe-event-types: ^2.4.0 tsup: 7.1.x typescript: 4.9.4 - zod: 3.21.4 + zod: 3.22.3 dependencies: '@trigger.dev/integration-kit': link:../../packages/integration-kit '@trigger.dev/sdk': link:../../packages/trigger-sdk stripe: 12.14.0 - zod: 3.21.4 + zod: 3.22.3 devDependencies: '@types/node': 16.18.11 rimraf: 3.0.2 @@ -564,21 +593,21 @@ importers: integrations/supabase: specifiers: '@supabase/supabase-js': ^2.26.0 - '@trigger.dev/integration-kit': workspace:^2.1.7 - '@trigger.dev/sdk': workspace:^2.1.7 + '@trigger.dev/integration-kit': workspace:^2.2.0 + '@trigger.dev/sdk': workspace:^2.2.0 '@trigger.dev/tsconfig': workspace:* '@types/node': 18.x rimraf: ^3.0.2 supabase-management-js: ^0.1.4 tsup: 7.1.x typescript: 4.9.4 - zod: 3.21.4 + zod: 3.22.3 dependencies: '@supabase/supabase-js': 2.31.0 '@trigger.dev/integration-kit': link:../../packages/integration-kit '@trigger.dev/sdk': link:../../packages/trigger-sdk supabase-management-js: 0.1.4 - zod: 3.21.4 + zod: 3.22.3 devDependencies: '@trigger.dev/tsconfig': link:../../config-packages/tsconfig '@types/node': 18.15.13 @@ -588,19 +617,19 @@ importers: integrations/typeform: specifiers: - '@trigger.dev/integration-kit': workspace:^2.1.7 - '@trigger.dev/sdk': workspace:^2.1.7 + '@trigger.dev/integration-kit': workspace:^2.2.0 + '@trigger.dev/sdk': workspace:^2.2.0 '@typeform/api-client': ^1.8.0 '@types/node': 16.x rimraf: ^3.0.2 tsup: 7.1.x typescript: 4.9.4 - zod: 3.21.4 + zod: 3.22.3 dependencies: '@trigger.dev/integration-kit': link:../../packages/integration-kit '@trigger.dev/sdk': link:../../packages/trigger-sdk '@typeform/api-client': 1.8.0 - zod: 3.21.4 + zod: 3.22.3 devDependencies: '@types/node': 16.18.11 rimraf: 3.0.2 @@ -632,6 +661,7 @@ importers: packages/cli: specifiers: + '@trigger.dev/core': workspace:* '@trigger.dev/tsconfig': workspace:* '@types/degit': ^2.8.3 '@types/gradient-string': ^1.1.2 @@ -643,6 +673,7 @@ importers: chalk: ^5.2.0 chokidar: ^3.5.3 commander: ^9.4.1 + console-table-printer: ^2.11.2 degit: ^2.8.4 dotenv: ^16.3.1 execa: ^7.0.0 @@ -656,6 +687,7 @@ importers: npm-check-updates: ^16.12.2 openai: ^4.5.0 ora: ^6.1.2 + p-retry: ^6.1.0 path-to-regexp: ^6.2.1 posthog-node: ^3.1.1 proxy-agent: ^6.3.0 @@ -668,13 +700,15 @@ importers: typescript: ^4.9.5 url: ^0.11.1 vitest: ^0.34.4 - zod: 3.21.4 + zod: 3.22.3 dependencies: + '@trigger.dev/core': link:../core '@types/degit': 2.8.3 boxen: 7.1.1 chalk: 5.2.0 chokidar: 3.5.3 commander: 9.5.0 + console-table-printer: 2.11.2 degit: 2.8.4 dotenv: 16.3.1 execa: 7.0.0 @@ -688,6 +722,7 @@ importers: npm-check-updates: 16.12.3 openai: 4.5.0 ora: 6.1.2 + p-retry: 6.1.0 path-to-regexp: 6.2.1 posthog-node: 3.1.1 proxy-agent: 6.3.0 @@ -695,7 +730,7 @@ importers: terminal-link: 3.0.0 tsconfck: 2.1.2_typescript@4.9.5 url: 0.11.1 - zod: 3.21.4 + zod: 3.22.3 devDependencies: '@trigger.dev/tsconfig': link:../../config-packages/tsconfig '@types/gradient-string': 1.1.2 @@ -720,11 +755,11 @@ importers: tsup: ^7.1.0 typescript: ^4.9.4 ulid: ^2.3.0 - zod: 3.21.4 + zod: 3.22.3 zod-error: 1.5.0 dependencies: ulid: 2.3.0 - zod: 3.21.4 + zod: 3.22.3 zod-error: 1.5.0 devDependencies: '@trigger.dev/tsconfig': link:../../config-packages/tsconfig @@ -738,14 +773,14 @@ importers: packages/database: specifiers: - '@prisma/client': 4.16.0 - prisma: 4.16.0 + '@prisma/client': 5.4.1 + prisma: 5.4.1 typescript: ^4.8.4 dependencies: - '@prisma/client': 4.16.0_prisma@4.16.0 + '@prisma/client': 5.4.1_prisma@5.4.1 typescript: 4.9.5 devDependencies: - prisma: 4.16.0 + prisma: 5.4.1 packages/emails: specifiers: @@ -769,7 +804,7 @@ importers: resend: ^0.9.1 tiny-invariant: ^1.2.0 typescript: ^4.9.4 - zod: 3.21.4 + zod: 3.22.3 dependencies: '@react-email/button': 0.0.4 '@react-email/container': 0.0.4 @@ -787,7 +822,7 @@ importers: react-email: 1.6.1_react@18.2.0 resend: 0.9.1 tiny-invariant: 1.3.1 - zod: 3.21.4 + zod: 3.22.3 devDependencies: '@trigger.dev/tsconfig': link:../../config-packages/tsconfig '@types/node': 16.18.11 @@ -816,7 +851,7 @@ importers: packages/express: specifiers: '@remix-run/web-fetch': ^4.3.5 - '@trigger.dev/sdk': workspace:^2.1.7 + '@trigger.dev/sdk': workspace:^2.2.0 '@trigger.dev/tsconfig': workspace:* '@types/debug': ^4.1.7 '@types/express': ^4.17.13 @@ -842,27 +877,50 @@ importers: specifiers: '@trigger.dev/tsconfig': workspace:* '@types/node': '18' - '@types/node-fetch': 2.6.x '@types/uuid': ^9.0.0 - node-fetch: 2.6.x rimraf: ^3.0.2 tsup: ^6.5.0 tsx: ^3.12.1 typescript: ^4.8.4 uuid: ^9.0.0 dependencies: - node-fetch: 2.6.12 uuid: 9.0.0 devDependencies: '@trigger.dev/tsconfig': link:../../config-packages/tsconfig '@types/node': 18.15.13 - '@types/node-fetch': 2.6.2 '@types/uuid': 9.0.0 rimraf: 3.0.2 tsup: 6.6.3_typescript@4.9.5 tsx: 3.12.2 typescript: 4.9.5 + packages/nestjs: + specifiers: + '@nestjs/common': ^10.2.4 + '@remix-run/web-fetch': ^4.3.5 + '@trigger.dev/sdk': workspace:^2.2.0 + '@trigger.dev/tsconfig': workspace:* + '@types/debug': ^4.1.7 + '@types/express': ^4.17.13 + debug: ^4.3.4 + fastify: ^4.23.2 + rimraf: ^3.0.2 + tsup: ^6.5.0 + tsx: ^3.12.1 + dependencies: + '@nestjs/common': 10.2.7 + '@remix-run/web-fetch': 4.3.6 + debug: 4.3.4 + devDependencies: + '@trigger.dev/sdk': link:../trigger-sdk + '@trigger.dev/tsconfig': link:../../config-packages/tsconfig + '@types/debug': 4.1.7 + '@types/express': 4.17.15 + fastify: 4.23.2 + rimraf: 3.0.2 + tsup: 6.6.3 + tsx: 3.12.2 + packages/nextjs: specifiers: '@trigger.dev/tsconfig': workspace:* @@ -889,7 +947,7 @@ importers: packages/react: specifiers: '@tanstack/react-query': 5.0.0-beta.2 - '@trigger.dev/core': workspace:^2.1.7 + '@trigger.dev/core': workspace:^2.2.0 '@trigger.dev/tsconfig': workspace:* '@types/debug': ^4.1.7 '@types/react': 18.2.17 @@ -901,12 +959,12 @@ importers: tsup: ^7.1.0 tsx: ^3.12.1 typescript: ^4.8.4 - zod: 3.21.4 + zod: 3.22.3 dependencies: '@tanstack/react-query': 5.0.0-beta.2_react@18.2.0 '@trigger.dev/core': link:../core debug: 4.3.4 - zod: 3.21.4 + zod: 3.22.3 devDependencies: '@trigger.dev/tsconfig': link:../../config-packages/tsconfig '@types/debug': 4.1.7 @@ -942,6 +1000,25 @@ importers: tsx: 3.12.2 typescript: 4.9.5 + packages/sveltekit: + specifiers: + '@sveltejs/kit': ^1.20.4 + '@trigger.dev/tsconfig': workspace:* + '@types/debug': ^4.1.7 + '@types/ws': ^8.5.3 + debug: ^4.3.4 + rimraf: ^3.0.2 + tsup: ^6.5.0 + dependencies: + debug: 4.3.4 + devDependencies: + '@sveltejs/kit': 1.25.1 + '@trigger.dev/tsconfig': link:../../config-packages/tsconfig + '@types/debug': 4.1.7 + '@types/ws': 8.5.4 + rimraf: 3.0.2 + tsup: 6.6.3 + packages/testing: specifiers: '@trigger.dev/core': workspace:* @@ -951,12 +1028,12 @@ importers: tsup: ^7.2.0 typescript: ^5.2.2 vitest: ^0.34.3 - zod: 3.21.4 + zod: 3.22.3 dependencies: '@trigger.dev/core': link:../core '@trigger.dev/sdk': link:../trigger-sdk vitest: 0.34.4 - zod: 3.21.4 + zod: 3.22.3 devDependencies: '@trigger.dev/stripe': link:../../integrations/stripe '@trigger.dev/tsconfig': link:../../config-packages/tsconfig @@ -965,11 +1042,10 @@ importers: packages/trigger-sdk: specifiers: - '@trigger.dev/core': workspace:^2.1.7 + '@trigger.dev/core': workspace:^2.2.0 '@trigger.dev/tsconfig': workspace:* '@types/debug': ^4.1.7 '@types/node': '18' - '@types/node-fetch': 2.6.x '@types/slug': ^5.0.3 '@types/uuid': ^9.0.0 '@types/ws': ^8.5.3 @@ -981,7 +1057,6 @@ importers: get-caller-file: ^2.0.5 git-remote-origin-url: ^4.0.0 git-repo-info: ^2.1.1 - node-fetch: 2.6.x rimraf: ^3.0.2 slug: ^6.0.0 terminal-link: ^3.0.0 @@ -991,7 +1066,7 @@ importers: ulid: ^2.3.0 uuid: ^9.0.0 ws: ^8.11.0 - zod: 3.21.4 + zod: 3.22.3 dependencies: '@trigger.dev/core': link:../core chalk: 5.2.0 @@ -1001,18 +1076,16 @@ importers: get-caller-file: 2.0.5 git-remote-origin-url: 4.0.0 git-repo-info: 2.1.1 - node-fetch: 2.6.12_encoding@0.1.13 slug: 6.1.0 terminal-link: 3.0.0 ulid: 2.3.0 uuid: 9.0.0 ws: 8.12.0 - zod: 3.21.4 + zod: 3.22.3 devDependencies: '@trigger.dev/tsconfig': link:../../config-packages/tsconfig '@types/debug': 4.1.7 '@types/node': 18.14.0 - '@types/node-fetch': 2.6.4 '@types/slug': 5.0.3 '@types/uuid': 9.0.0 '@types/ws': 8.5.4 @@ -1044,7 +1117,7 @@ importers: ts-node: ^10.9.1 tsconfig-paths: ^3.14.1 typescript: ^5.1.6 - zod: 3.21.4 + zod: 3.22.3 dependencies: '@trigger.dev/express': link:../packages/express '@trigger.dev/github': link:../integrations/github @@ -1057,7 +1130,7 @@ importers: '@trigger.dev/stripe': link:../integrations/stripe '@trigger.dev/supabase': link:../integrations/supabase '@trigger.dev/typeform': link:../integrations/typeform - zod: 3.21.4 + zod: 3.22.3 devDependencies: '@trigger.dev/cli': link:../packages/cli '@trigger.dev/tsconfig': link:../config-packages/tsconfig @@ -1094,6 +1167,7 @@ importers: '@trigger.dev/linear': workspace:* '@trigger.dev/openai': workspace:* '@trigger.dev/plain': workspace:* + '@trigger.dev/replicate': workspace:* '@trigger.dev/resend': workspace:* '@trigger.dev/sdk': workspace:* '@trigger.dev/sendgrid': workspace:* @@ -1109,7 +1183,7 @@ importers: ts-node: ^10.9.1 tsconfig-paths: ^3.14.1 typescript: 5.1.6 - zod: 3.21.4 + zod: 3.22.3 dependencies: '@clerk/backend': 0.29.2 '@trigger.dev/airtable': link:../../integrations/airtable @@ -1118,6 +1192,7 @@ importers: '@trigger.dev/linear': link:../../integrations/linear '@trigger.dev/openai': link:../../integrations/openai '@trigger.dev/plain': link:../../integrations/plain + '@trigger.dev/replicate': link:../../integrations/replicate '@trigger.dev/resend': link:../../integrations/resend '@trigger.dev/sdk': link:../../packages/trigger-sdk '@trigger.dev/sendgrid': link:../../integrations/sendgrid @@ -1127,7 +1202,7 @@ importers: '@trigger.dev/typeform': link:../../integrations/typeform '@types/node': 20.4.2 typescript: 5.1.6 - zod: 3.21.4 + zod: 3.22.3 devDependencies: '@trigger.dev/cli': link:../../packages/cli '@trigger.dev/tsconfig': link:../../config-packages/tsconfig @@ -1137,6 +1212,63 @@ importers: ts-node: 10.9.1_xj5cs2fmhcigm4w5bhhtewqeja tsconfig-paths: 3.14.1 + references/nestjs-example: + specifiers: + '@nestjs/cli': ^10.0.0 + '@nestjs/common': ^10.0.0 + '@nestjs/config': ^3.0.1 + '@nestjs/core': ^10.0.0 + '@nestjs/platform-express': ^10.0.0 + '@nestjs/schematics': ^10.0.0 + '@nestjs/testing': ^10.0.0 + '@trigger.dev/nestjs': workspace:* + '@trigger.dev/sdk': workspace:* + '@types/express': ^4.17.17 + '@types/node': ^20.3.1 + '@types/supertest': ^2.0.12 + '@typescript-eslint/eslint-plugin': ^6.0.0 + '@typescript-eslint/parser': ^6.0.0 + eslint: ^8.42.0 + eslint-config-prettier: ^9.0.0 + eslint-plugin-prettier: ^5.0.0 + prettier: ^3.0.0 + reflect-metadata: ^0.1.13 + rxjs: ^7.8.1 + source-map-support: ^0.5.21 + supertest: ^6.3.3 + ts-loader: ^9.4.3 + ts-node: ^10.9.1 + tsconfig-paths: ^4.2.0 + typescript: ^5.1.3 + dependencies: + '@nestjs/common': 10.2.7_atc7tu2sld2m3nk4hmwkqn6qde + '@nestjs/config': 3.1.1_xtnakrwl23ehsubvekids4npxm + '@nestjs/core': 10.2.7_5hwp5tvaqax6kxnclaqixhass4 + '@nestjs/platform-express': 10.2.7_pyni7wzdujkbvmfeegxedz3mmy + '@trigger.dev/nestjs': link:../../packages/nestjs + '@trigger.dev/sdk': link:../../packages/trigger-sdk + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + devDependencies: + '@nestjs/cli': 10.1.18 + '@nestjs/schematics': 10.0.2_typescript@5.2.2 + '@nestjs/testing': 10.2.7_6ce6yeqrytunl27g3gdf6ye2ri + '@types/express': 4.17.18 + '@types/node': 20.6.0 + '@types/supertest': 2.0.14 + '@typescript-eslint/eslint-plugin': 6.7.4_ygtxu7ao4w7xzfo6eep522bcem + '@typescript-eslint/parser': 6.7.4_ox3na7ge7wjdarbyztnclevxam + eslint: 8.45.0 + eslint-config-prettier: 9.0.0_eslint@8.45.0 + eslint-plugin-prettier: 5.0.0_jybzfv6jdssomlxkhhfntuvyli + prettier: 3.0.0 + source-map-support: 0.5.21 + supertest: 6.3.3 + ts-loader: 9.4.4_typescript@5.2.2 + ts-node: 10.9.1_kpuv3buz4xyqturyqxj2gejvma + tsconfig-paths: 4.2.0 + typescript: 5.2.2 + references/nextjs-reference: specifiers: '@trigger.dev/cli': workspace:* @@ -1165,7 +1297,7 @@ importers: react-query: ^3.39.3 ts-loader: ^9.4.2 typescript: 5.0.4 - zod: 3.21.4 + zod: 3.22.3 dependencies: '@trigger.dev/eslint-plugin': link:../../packages/eslint-plugin '@trigger.dev/github': link:../../integrations/github @@ -1188,7 +1320,7 @@ importers: react-dom: 18.2.0_react@18.2.0 react-query: 3.39.3_biqbaboplfbrettd7655fr4n2y typescript: 5.0.4 - zod: 3.21.4 + zod: 3.22.3 devDependencies: '@trigger.dev/cli': link:../../packages/cli eslint: 8.42.0 @@ -1313,6 +1445,43 @@ importers: eslint: 8.45.0 typescript: 4.9.5 + references/svelte-example: + specifiers: + '@sveltejs/adapter-auto': ^2.0.0 + '@sveltejs/kit': ^1.20.4 + '@trigger.dev/sdk': workspace:* + '@trigger.dev/sveltekit': workspace:* + '@typescript-eslint/eslint-plugin': ^5.45.0 + '@typescript-eslint/parser': ^5.45.0 + eslint: ^8.28.0 + eslint-config-prettier: ^8.5.0 + eslint-plugin-svelte: ^2.30.0 + prettier: ^2.8.0 + prettier-plugin-svelte: ^2.10.1 + svelte: ^4.0.5 + svelte-check: ^3.4.3 + tslib: ^2.4.1 + typescript: ^5.0.0 + vite: ^4.4.2 + dependencies: + '@trigger.dev/sdk': link:../../packages/trigger-sdk + '@trigger.dev/sveltekit': link:../../packages/sveltekit + devDependencies: + '@sveltejs/adapter-auto': 2.1.0_@sveltejs+kit@1.25.1 + '@sveltejs/kit': 1.25.1_svelte@4.2.1+vite@4.4.9 + '@typescript-eslint/eslint-plugin': 5.59.6_ltg3s7zeaq5lfn26f6cgiedibm + '@typescript-eslint/parser': 5.59.6_ox3na7ge7wjdarbyztnclevxam + eslint: 8.45.0 + eslint-config-prettier: 8.6.0_eslint@8.45.0 + eslint-plugin-svelte: 2.34.0_eslint@8.45.0+svelte@4.2.1 + prettier: 2.8.8 + prettier-plugin-svelte: 2.10.1_hfafeyo6vw33o2ufgpsfnrywzu + svelte: 4.2.1 + svelte-check: 3.5.2_svelte@4.2.1 + tslib: 2.6.2 + typescript: 5.2.2 + vite: 4.4.9 + references/unit-testing: specifiers: '@trigger.dev/sdk': workspace:* @@ -1322,7 +1491,7 @@ importers: tsconfig-paths: ^3.14.1 typescript: ^5.2.2 vitest: ^0.34.3 - zod: 3.21.4 + zod: 3.22.3 dependencies: '@trigger.dev/sdk': link:../../packages/trigger-sdk '@trigger.dev/stripe': link:../../integrations/stripe @@ -1332,7 +1501,7 @@ importers: tsconfig-paths: 3.14.1 typescript: 5.2.2 vitest: 0.34.4 - zod: 3.21.4 + zod: 3.22.3 packages: @@ -1350,6 +1519,119 @@ packages: dependencies: '@jridgewell/gen-mapping': 0.1.1 '@jridgewell/trace-mapping': 0.3.19 + dev: true + + /@ampproject/remapping/2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.2 + '@jridgewell/trace-mapping': 0.3.19 + + /@angular-devkit/core/16.1.8: + resolution: {integrity: sha512-dSRD/+bGanArIXkj+kaU1kDFleZeQMzmBiOXX+pK0Ah9/0Yn1VmY3RZh1zcX9vgIQXV+t7UPrTpOjaERMUtVGw==} + engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + chokidar: ^3.5.2 + peerDependenciesMeta: + chokidar: + optional: true + dependencies: + ajv: 8.12.0 + ajv-formats: 2.1.1 + jsonc-parser: 3.2.0 + rxjs: 7.8.1 + source-map: 0.7.4 + dev: true + + /@angular-devkit/core/16.1.8_chokidar@3.5.3: + resolution: {integrity: sha512-dSRD/+bGanArIXkj+kaU1kDFleZeQMzmBiOXX+pK0Ah9/0Yn1VmY3RZh1zcX9vgIQXV+t7UPrTpOjaERMUtVGw==} + engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + chokidar: ^3.5.2 + peerDependenciesMeta: + chokidar: + optional: true + dependencies: + ajv: 8.12.0 + ajv-formats: 2.1.1 + chokidar: 3.5.3 + jsonc-parser: 3.2.0 + rxjs: 7.8.1 + source-map: 0.7.4 + dev: true + + /@angular-devkit/core/16.2.3_chokidar@3.5.3: + resolution: {integrity: sha512-oZLdg2XTx7likYAXRj1CU0XmrsCfe5f2grj3iwuI3OB1LXwwpdbHBztruj03y3yHES+TnO+dIbkvRnvMXs7uAA==} + engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + chokidar: ^3.5.2 + peerDependenciesMeta: + chokidar: + optional: true + dependencies: + ajv: 8.12.0 + ajv-formats: 2.1.1 + chokidar: 3.5.3 + jsonc-parser: 3.2.0 + picomatch: 2.3.1 + rxjs: 7.8.1 + source-map: 0.7.4 + dev: true + + /@angular-devkit/schematics-cli/16.2.3_chokidar@3.5.3: + resolution: {integrity: sha512-5YQCbQmY9Kc03a9Io4XHOrxGXjnzcVveUuUO64R1m5x2aA5I+mVR8NVvxuoGRAeoI1FWusAKRe9hH8nRCLrelA==} + engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + hasBin: true + dependencies: + '@angular-devkit/core': 16.2.3_chokidar@3.5.3 + '@angular-devkit/schematics': 16.2.3_chokidar@3.5.3 + ansi-colors: 4.1.3 + inquirer: 8.2.4 + symbol-observable: 4.0.0 + yargs-parser: 21.1.1 + transitivePeerDependencies: + - chokidar + dev: true + + /@angular-devkit/schematics/16.1.8: + resolution: {integrity: sha512-6LyzMdFJs337RTxxkI2U1Ndw0CW5mMX/aXWl8d7cW2odiSrAg8IdlMqpc+AM8+CPfsB0FtS1aWkEZqJLT0jHOg==} + engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + dependencies: + '@angular-devkit/core': 16.1.8 + jsonc-parser: 3.2.0 + magic-string: 0.30.0 + ora: 5.4.1 + rxjs: 7.8.1 + transitivePeerDependencies: + - chokidar + dev: true + + /@angular-devkit/schematics/16.1.8_chokidar@3.5.3: + resolution: {integrity: sha512-6LyzMdFJs337RTxxkI2U1Ndw0CW5mMX/aXWl8d7cW2odiSrAg8IdlMqpc+AM8+CPfsB0FtS1aWkEZqJLT0jHOg==} + engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + dependencies: + '@angular-devkit/core': 16.1.8_chokidar@3.5.3 + jsonc-parser: 3.2.0 + magic-string: 0.30.0 + ora: 5.4.1 + rxjs: 7.8.1 + transitivePeerDependencies: + - chokidar + dev: true + + /@angular-devkit/schematics/16.2.3_chokidar@3.5.3: + resolution: {integrity: sha512-+lBiHxi/C9HCfiCbtW25DldwvJDXXXv5oWw+Tg4s18BO/lYZLveGUEaZWu9ZJ5VIJ8GliUi2LohxhDxBkh4Oxg==} + engines: {node: ^16.14.0 || >=18.10.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + dependencies: + '@angular-devkit/core': 16.2.3_chokidar@3.5.3 + jsonc-parser: 3.2.0 + magic-string: 0.30.1 + ora: 5.4.1 + rxjs: 7.8.1 + transitivePeerDependencies: + - chokidar + dev: true /@astrojs/compiler/2.1.0: resolution: {integrity: sha512-Mp+qrNhly+27bL/Zq8lGeUY+YrdoU0eDfIlAeGIPrzt0PnI/jGpvPUdCaugv4zbCrDkOUScFfcbeEiYumrdJnw==} @@ -1407,7 +1689,7 @@ packages: dset: 3.1.2 is-docker: 3.0.0 is-wsl: 3.0.0 - undici: 5.24.0 + undici: 5.25.4 which-pm-runs: 1.1.0 transitivePeerDependencies: - supports-color @@ -1419,13 +1701,6 @@ packages: default-browser-id: 3.0.0 dev: true - /@babel/code-frame/7.18.6: - resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.22.13 - dev: true - /@babel/code-frame/7.21.4: resolution: {integrity: sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==} engines: {node: '>=6.9.0'} @@ -1453,7 +1728,7 @@ packages: resolution: {integrity: sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==} engines: {node: '>=6.9.0'} dependencies: - '@ampproject/remapping': 2.2.0 + '@ampproject/remapping': 2.2.1 '@babel/code-frame': 7.22.13 '@babel/generator': 7.22.15 '@babel/helper-compilation-targets': 7.22.15 @@ -1490,7 +1765,7 @@ packages: debug: 4.3.4 gensync: 1.0.0-beta.2 json5: 2.2.3 - semver: 6.3.0 + semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true @@ -1499,7 +1774,7 @@ packages: resolution: {integrity: sha512-2EENLmhpwplDux5PSsZnSbnSkB3tZ6QTksgO25xwEL7pIDcNOMhF5v/s6RzwjMZzZzw9Ofc30gHv5ChCC8pifQ==} engines: {node: '>=6.9.0'} dependencies: - '@ampproject/remapping': 2.2.0 + '@ampproject/remapping': 2.2.1 '@babel/code-frame': 7.22.13 '@babel/generator': 7.22.15 '@babel/helper-compilation-targets': 7.22.15 @@ -1528,7 +1803,7 @@ packages: '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 eslint: 8.45.0 eslint-visitor-keys: 2.1.0 - semver: 6.3.0 + semver: 6.3.1 dev: true /@babel/eslint-parser/7.21.8_mxgwiyfjuazeomuuf56n24ufpy: @@ -1542,7 +1817,7 @@ packages: '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 eslint: 8.31.0 eslint-visitor-keys: 2.1.0 - semver: 6.3.0 + semver: 6.3.1 dev: true /@babel/generator/7.21.5: @@ -4662,7 +4937,7 @@ packages: babel-plugin-polyfill-corejs3: 0.6.0_@babel+core@7.20.12 babel-plugin-polyfill-regenerator: 0.4.1_@babel+core@7.20.12 core-js-compat: 3.27.1 - semver: 6.3.0 + semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true @@ -4749,7 +5024,7 @@ packages: babel-plugin-polyfill-corejs3: 0.6.0_@babel+core@7.21.8 babel-plugin-polyfill-regenerator: 0.4.1_@babel+core@7.21.8 core-js-compat: 3.27.1 - semver: 6.3.0 + semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true @@ -4836,7 +5111,7 @@ packages: babel-plugin-polyfill-corejs3: 0.6.0_@babel+core@7.22.17 babel-plugin-polyfill-regenerator: 0.4.1_@babel+core@7.22.17 core-js-compat: 3.27.1 - semver: 6.3.0 + semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true @@ -5332,7 +5607,7 @@ packages: dependencies: '@codemirror/autocomplete': 6.4.0_czcfkg2f66rxeiodoti7r2gulu '@codemirror/language': 6.3.2 - '@codemirror/lint': 6.1.0 + '@codemirror/lint': 6.4.2 '@codemirror/state': 6.2.0 '@codemirror/view': 6.7.2 '@lezer/common': 1.0.2 @@ -5357,8 +5632,8 @@ packages: style-mod: 4.0.0 dev: false - /@codemirror/lint/6.1.0: - resolution: {integrity: sha512-mdvDQrjRmYPvQ3WrzF6Ewaao+NWERYtpthJvoQ3tK3t/44Ynhk8ZGjTSL9jMEv8CgSMogmt75X8ceOZRDSXHtQ==} + /@codemirror/lint/6.4.2: + resolution: {integrity: sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==} dependencies: '@codemirror/state': 6.2.0 '@codemirror/view': 6.7.2 @@ -5421,13 +5696,13 @@ packages: react: 18.2.0 dev: false - /@conform-to/zod/0.6.1_zod@3.21.4: + /@conform-to/zod/0.6.1_zod@3.22.3: resolution: {integrity: sha512-VYu44VfVeP0VyMrc2sNBagFAS66luZMIeFOfkHndGs1ep+LcR3Z4D+EOEqLZ2ECexmg9Y6nrX8Y2d5UUigEXCg==} peerDependencies: '@conform-to/dom': 0.6.1 zod: ^3.21.0 dependencies: - zod: 3.21.4 + zod: 3.22.3 dev: false /@cspotcode/source-map-support/0.8.1: @@ -6728,6 +7003,32 @@ packages: resolution: {integrity: sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==} dev: true + /@fastify/ajv-compiler/3.5.0: + resolution: {integrity: sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==} + dependencies: + ajv: 8.12.0 + ajv-formats: 2.1.1 + fast-uri: 2.2.0 + dev: true + + /@fastify/busboy/2.0.0: + resolution: {integrity: sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==} + engines: {node: '>=14'} + + /@fastify/deepmerge/1.3.0: + resolution: {integrity: sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==} + dev: true + + /@fastify/error/3.4.0: + resolution: {integrity: sha512-e/mafFwbK3MNqxUcFBLgHhgxsF8UT1m8aj0dAlqEa2nJEgPsRtpHTZ3ObgrgkZ2M1eJHPTwgyUl/tXkvabsZdQ==} + dev: true + + /@fastify/fast-json-stringify-compiler/4.3.0: + resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==} + dependencies: + fast-json-stringify: 5.8.0 + dev: true + /@figspec/components/1.0.1: resolution: {integrity: sha512-UvnEamPEAMh9HExViqpobWmX25g1+soA9kcJu+It3VerMa7CeVyaIbQydNf1Gys5v/rxJVdTDRgQ7OXW2zAAig==} dependencies: @@ -7261,6 +7562,7 @@ packages: dependencies: '@jridgewell/set-array': 1.1.2 '@jridgewell/sourcemap-codec': 1.4.15 + dev: true /@jridgewell/gen-mapping/0.3.2: resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} @@ -7386,6 +7688,10 @@ packages: '@lit-labs/ssr-dom-shim': 1.1.1 dev: true + /@lukeed/csprng/1.1.0: + resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} + engines: {node: '>=8'} + /@manypkg/cli/0.19.2: resolution: {integrity: sha512-DXx/P1lyunNoFWwOj1MWBucUhaIJljoiAGOpO2fE0GKMBCI6EZBZD0Up1+fQZoXBecKXRgV9mGgLvIB2fOQ0KQ==} hasBin: true @@ -7447,7 +7753,7 @@ packages: engines: {node: '>=14'} dependencies: '@types/set-cookie-parser': 2.4.2 - set-cookie-parser: 2.5.1 + set-cookie-parser: 2.6.0 dev: true /@mswjs/interceptors/0.17.6: @@ -7474,6 +7780,194 @@ packages: tar-fs: 2.1.1 dev: true + /@nestjs/cli/10.1.18: + resolution: {integrity: sha512-jQtG47keLsACt7b4YwJbTBYRm90n82gJpMaiR1HGAyQ9pccbctjSYu592eT4bxqkUWxPgBE3mpNynXj7dWAfrw==} + engines: {node: '>= 16'} + hasBin: true + peerDependencies: + '@swc/cli': ^0.1.62 + '@swc/core': ^1.3.62 + peerDependenciesMeta: + '@swc/cli': + optional: true + '@swc/core': + optional: true + dependencies: + '@angular-devkit/core': 16.2.3_chokidar@3.5.3 + '@angular-devkit/schematics': 16.2.3_chokidar@3.5.3 + '@angular-devkit/schematics-cli': 16.2.3_chokidar@3.5.3 + '@nestjs/schematics': 10.0.2_acogjgahz3bdlhk7may5ikc22y + chalk: 4.1.2 + chokidar: 3.5.3 + cli-table3: 0.6.3 + commander: 4.1.1 + fork-ts-checker-webpack-plugin: 8.0.0_dtthwp2bsqb7yvb7hoeealjg4i + inquirer: 8.2.6 + node-emoji: 1.11.0 + ora: 5.4.1 + os-name: 4.0.1 + rimraf: 4.4.1 + shelljs: 0.8.5 + source-map-support: 0.5.21 + tree-kill: 1.2.2 + tsconfig-paths: 4.2.0 + tsconfig-paths-webpack-plugin: 4.1.0 + typescript: 5.2.2 + webpack: 5.88.2 + webpack-node-externals: 3.0.0 + transitivePeerDependencies: + - esbuild + - uglify-js + - webpack-cli + dev: true + + /@nestjs/common/10.2.7: + resolution: {integrity: sha512-cUtCRXiUstDmh4bSBhVbq4cI439Gngp4LgLGLBmd5dqFQodfXKnSD441ldYfFiLz4rbUsnoMJz/8ZjuIEI+B7A==} + peerDependencies: + class-transformer: '*' + class-validator: '*' + reflect-metadata: ^0.1.12 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + dependencies: + iterare: 1.2.1 + tslib: 2.6.2 + uid: 2.0.2 + dev: false + + /@nestjs/common/10.2.7_atc7tu2sld2m3nk4hmwkqn6qde: + resolution: {integrity: sha512-cUtCRXiUstDmh4bSBhVbq4cI439Gngp4LgLGLBmd5dqFQodfXKnSD441ldYfFiLz4rbUsnoMJz/8ZjuIEI+B7A==} + peerDependencies: + class-transformer: '*' + class-validator: '*' + reflect-metadata: ^0.1.12 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + dependencies: + iterare: 1.2.1 + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + tslib: 2.6.2 + uid: 2.0.2 + + /@nestjs/config/3.1.1_xtnakrwl23ehsubvekids4npxm: + resolution: {integrity: sha512-qu5QlNiJdqQtOsnB6lx4JCXPQ96jkKUsOGd+JXfXwqJqZcOSAq6heNFg0opW4pq4J/VZoNwoo87TNnx9wthnqQ==} + peerDependencies: + '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 + reflect-metadata: ^0.1.13 + dependencies: + '@nestjs/common': 10.2.7_atc7tu2sld2m3nk4hmwkqn6qde + dotenv: 16.3.1 + dotenv-expand: 10.0.0 + lodash: 4.17.21 + reflect-metadata: 0.1.13 + uuid: 9.0.0 + dev: false + + /@nestjs/core/10.2.7_5hwp5tvaqax6kxnclaqixhass4: + resolution: {integrity: sha512-5GSu53QUUcwX17sNmlJPa1I0wIeAZOKbedyVuQx0ZAwWVa9g0wJBbsNP+R4EJ+j5Dkdzt/8xkiZvnKt8RFRR8g==} + requiresBuild: true + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/microservices': ^10.0.0 + '@nestjs/platform-express': ^10.0.0 + '@nestjs/websockets': ^10.0.0 + reflect-metadata: ^0.1.12 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + '@nestjs/websockets': + optional: true + dependencies: + '@nestjs/common': 10.2.7_atc7tu2sld2m3nk4hmwkqn6qde + '@nestjs/platform-express': 10.2.7_pyni7wzdujkbvmfeegxedz3mmy + '@nuxtjs/opencollective': 0.3.2 + fast-safe-stringify: 2.1.1 + iterare: 1.2.1 + path-to-regexp: 3.2.0 + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + tslib: 2.6.2 + uid: 2.0.2 + transitivePeerDependencies: + - encoding + + /@nestjs/platform-express/10.2.7_pyni7wzdujkbvmfeegxedz3mmy: + resolution: {integrity: sha512-p+kp6aJtkgAdVpUrCVmM6MKtOvjsbt7QofBiZMidjYesZkMeG5gZ1D2SK8XzvQ8VXHJfFgEdY2xcKGB+wJLOYQ==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/core': ^10.0.0 + dependencies: + '@nestjs/common': 10.2.7_atc7tu2sld2m3nk4hmwkqn6qde + '@nestjs/core': 10.2.7_5hwp5tvaqax6kxnclaqixhass4 + body-parser: 1.20.2 + cors: 2.8.5 + express: 4.18.2 + multer: 1.4.4-lts.1 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + + /@nestjs/schematics/10.0.2_acogjgahz3bdlhk7may5ikc22y: + resolution: {integrity: sha512-DaZZjymYoIfRqC5W62lnYXIIods1PDY6CGc8+IpRwyinzffjKxZ3DF3exu+mdyvllzkXo9DTXkoX4zOPSJHCkw==} + peerDependencies: + typescript: '>=4.8.2' + dependencies: + '@angular-devkit/core': 16.1.8_chokidar@3.5.3 + '@angular-devkit/schematics': 16.1.8_chokidar@3.5.3 + comment-json: 4.2.3 + jsonc-parser: 3.2.0 + pluralize: 8.0.0 + typescript: 5.2.2 + transitivePeerDependencies: + - chokidar + dev: true + + /@nestjs/schematics/10.0.2_typescript@5.2.2: + resolution: {integrity: sha512-DaZZjymYoIfRqC5W62lnYXIIods1PDY6CGc8+IpRwyinzffjKxZ3DF3exu+mdyvllzkXo9DTXkoX4zOPSJHCkw==} + peerDependencies: + typescript: '>=4.8.2' + dependencies: + '@angular-devkit/core': 16.1.8 + '@angular-devkit/schematics': 16.1.8 + comment-json: 4.2.3 + jsonc-parser: 3.2.0 + pluralize: 8.0.0 + typescript: 5.2.2 + transitivePeerDependencies: + - chokidar + dev: true + + /@nestjs/testing/10.2.7_6ce6yeqrytunl27g3gdf6ye2ri: + resolution: {integrity: sha512-d2SIqiJIf/7NSILeNNWSdRvTTpHSouGgisGHwf5PVDC7z4/yXZw/wPO9eJhegnxFlqk6n2LW4QBTmMzbqjAfHA==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/core': ^10.0.0 + '@nestjs/microservices': ^10.0.0 + '@nestjs/platform-express': ^10.0.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + dependencies: + '@nestjs/common': 10.2.7_atc7tu2sld2m3nk4hmwkqn6qde + '@nestjs/core': 10.2.7_5hwp5tvaqax6kxnclaqixhass4 + '@nestjs/platform-express': 10.2.7_pyni7wzdujkbvmfeegxedz3mmy + tslib: 2.6.2 + dev: true + /@next/env/13.3.1: resolution: {integrity: sha512-EDtCoedIZC7JlUQ3uaQpSc4aVmyhbLHmQVALg7pFfQgOTjgSnn7mKtA0DiCMkYvvsx6aFb5octGMtWrOtGXW9A==} @@ -7755,6 +8249,17 @@ packages: - supports-color dev: false + /@nuxtjs/opencollective/0.3.2: + resolution: {integrity: sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + dependencies: + chalk: 4.1.2 + consola: 2.15.3 + node-fetch: 2.6.12 + transitivePeerDependencies: + - encoding + /@octokit/app/13.1.2: resolution: {integrity: sha512-Kf+h5sa1SOI33hFsuHvTsWj1jUrjp1x4MuiJBq7U/NicfEGa6nArPUoDnyfP/YTmcQ5cQ5yvOgoIBkbwPg6kzQ==} engines: {node: '>= 14'} @@ -8950,14 +9455,14 @@ packages: dependencies: asn1js: 3.0.5 pvtsutils: 1.3.5 - tslib: 2.5.0 + tslib: 2.6.2 dev: false /@peculiar/json-schema/1.1.12: resolution: {integrity: sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==} engines: {node: '>=8.0.0'} dependencies: - tslib: 2.5.0 + tslib: 2.6.2 dev: false /@peculiar/webcrypto/1.4.1: @@ -8967,7 +9472,7 @@ packages: '@peculiar/asn1-schema': 2.3.6 '@peculiar/json-schema': 1.1.12 pvtsutils: 1.3.5 - tslib: 2.5.0 + tslib: 2.6.2 webcrypto-core: 1.7.7 dev: false @@ -9000,7 +9505,7 @@ packages: fsevents: 2.3.2 dev: true - /@pmmmwh/react-refresh-webpack-plugin/0.5.10_5p63spmmiprwsput7pcvbh7bwu: + /@pmmmwh/react-refresh-webpack-plugin/0.5.10_2kpgiq4mtlettjqmb64nc4esa4: resolution: {integrity: sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==} engines: {node: '>= 10.13'} peerDependencies: @@ -9034,9 +9539,9 @@ packages: html-entities: 2.3.3 loader-utils: 2.0.4 react-refresh: 0.11.0 - schema-utils: 3.1.2 + schema-utils: 3.3.0 source-map: 0.7.4 - webpack: 5.80.0_uhpfu7q6noim4yjdo6qt2aajgu + webpack: 5.88.2_uhpfu7q6noim4yjdo6qt2aajgu dev: true /@pnpm/config.env-replace/1.1.0: @@ -9060,9 +9565,13 @@ packages: config-chain: 1.1.13 dev: false - /@prisma/client/4.16.0_prisma@4.16.0: - resolution: {integrity: sha512-CBD+5IdZPiavhLkQokvsz1uz4r9ppixaqY/ajybWs4WXNnsDVMBKEqN3BiPzpSo79jiy22VKj/67pqt4VwIg9w==} - engines: {node: '>=14.17'} + /@polka/url/1.0.0-next.23: + resolution: {integrity: sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==} + dev: true + + /@prisma/client/5.4.1_prisma@5.4.1: + resolution: {integrity: sha512-xyD0DJ3gRNfLbPsC+YfMBBuLJtZKQfy1OD2qU/PZg+HKrr7SO+09174LMeTlWP0YF2wca9LxtVd4HnAiB5ketQ==} + engines: {node: '>=16.13'} requiresBuild: true peerDependencies: prisma: '*' @@ -9070,16 +9579,16 @@ packages: prisma: optional: true dependencies: - '@prisma/engines-version': 4.16.0-66.b20ead4d3ab9e78ac112966e242ded703f4a052c - prisma: 4.16.0 + '@prisma/engines-version': 5.4.1-1.2f302df92bd8945e20ad4595a73def5b96afa54f + prisma: 5.4.1 dev: false - /@prisma/engines-version/4.16.0-66.b20ead4d3ab9e78ac112966e242ded703f4a052c: - resolution: {integrity: sha512-tMWAF/qF00fbUH1HB4Yjmz6bjh7fzkb7Y3NRoUfMlHu6V+O45MGvqwYxqwBjn1BIUXkl3r04W351D4qdJjrgvA==} + /@prisma/engines-version/5.4.1-1.2f302df92bd8945e20ad4595a73def5b96afa54f: + resolution: {integrity: sha512-+nUQM/y8C+1GG5Ioeqcu6itFslCfxvQSAUVSMC9XM2G2Fcq0F4Afnp6m0pXF6X6iUBWen7jZBPmM9Qlq4Nr3/A==} dev: false - /@prisma/engines/4.16.0: - resolution: {integrity: sha512-M6XoMRXnqL0rqZGQS8ZpNiHYG4G1fKBdoqW/oBtHnr1in5UYgerZqal3CXchmd6OBD/770PE9dtjQuqcilZJUA==} + /@prisma/engines/5.4.1: + resolution: {integrity: sha512-vJTdY4la/5V3N7SFvWRmSMUh4mIQnyb/MNoDjzVbh9iLmEC+uEykj/1GPviVsorvfz7DbYSQC4RiwmlEpTEvGA==} requiresBuild: true /@protobufjs/aspromise/1.1.2: @@ -10284,7 +10793,7 @@ packages: semver: 7.5.4 sort-package-json: 1.57.0 tar-fs: 2.1.1 - tsconfig-paths: 4.1.2 + tsconfig-paths: 4.2.0 ws: 7.5.9 xdm: 2.1.0 transitivePeerDependencies: @@ -10501,7 +11010,7 @@ packages: '@types/cookie': 0.4.1 '@web3-storage/multipart-parser': 1.0.0 cookie: 0.4.2 - set-cookie-parser: 2.5.1 + set-cookie-parser: 2.6.0 source-map: 0.7.4 /@remix-run/server-runtime/2.0.1_typescript@4.9.5: @@ -10541,7 +11050,7 @@ packages: /@remix-run/web-blob/3.0.4: resolution: {integrity: sha512-AfegzZvSSDc+LwnXV+SwROTrDtoLiPxeFW+jxgvtDAnkuCX1rrzmVJ6CzqZ1Ai0bVfmJadkG5GxtAfYclpPmgw==} dependencies: - '@remix-run/web-stream': 1.0.3 + '@remix-run/web-stream': 1.0.4 web-encoding: 1.1.5 dev: false @@ -11270,28 +11779,28 @@ packages: '@storybook/theming': 7.0.9_biqbaboplfbrettd7655fr4n2y '@types/node': 16.18.11 '@types/semver': 7.3.13 - babel-loader: 9.1.2_25xk4kalzxoxom6k7ae7wqkwo4 + babel-loader: 9.1.2_ijmuqjuz7epdoeof4qwmt7scdi babel-plugin-named-exports-order: 0.0.2 browser-assert: 1.2.1 case-sensitive-paths-webpack-plugin: 2.4.0 - css-loader: 6.7.3_webpack@5.80.0 + css-loader: 6.7.3_webpack@5.88.2 express: 4.18.2 - fork-ts-checker-webpack-plugin: 7.3.0_vf4xkga2qinmx3cxkwrybccrqy + fork-ts-checker-webpack-plugin: 7.3.0_vf3ejk3u7gfag4p4x6gqje5yuq fs-extra: 11.1.0 - html-webpack-plugin: 5.5.1_webpack@5.80.0 + html-webpack-plugin: 5.5.1_webpack@5.88.2 path-browserify: 1.0.1 process: 0.11.10 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 semver: 7.5.4 - style-loader: 3.3.2_webpack@5.80.0 - terser-webpack-plugin: 5.3.7_5jgfnkl7fjuhakmzbjzotue6o4 + style-loader: 3.3.2_webpack@5.88.2 + terser-webpack-plugin: 5.3.7_nww33inhqu3uc3cp573wawoccu ts-dedent: 2.2.0 typescript: 4.9.4 util: 0.12.5 util-deprecate: 1.0.2 - webpack: 5.80.0_uhpfu7q6noim4yjdo6qt2aajgu - webpack-dev-middleware: 5.3.3_webpack@5.80.0 + webpack: 5.88.2_uhpfu7q6noim4yjdo6qt2aajgu + webpack-dev-middleware: 5.3.3_webpack@5.88.2 webpack-hot-middleware: 2.25.3 webpack-virtual-modules: 0.4.6 transitivePeerDependencies: @@ -11799,12 +12308,12 @@ packages: dependencies: '@babel/preset-flow': 7.18.6 '@babel/preset-react': 7.18.6 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10_5p63spmmiprwsput7pcvbh7bwu + '@pmmmwh/react-refresh-webpack-plugin': 0.5.10_2kpgiq4mtlettjqmb64nc4esa4 '@storybook/core-webpack': 7.0.9 '@storybook/docs-tools': 7.0.9 '@storybook/node-logger': 7.0.9 '@storybook/react': 7.0.9_o4scbtliisanygemawej7x2d6i - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0_vf4xkga2qinmx3cxkwrybccrqy + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0_vf3ejk3u7gfag4p4x6gqje5yuq '@types/node': 16.18.11 '@types/semver': 7.3.13 babel-plugin-add-react-displayname: 0.0.5 @@ -11815,7 +12324,7 @@ packages: react-refresh: 0.11.0 semver: 7.5.4 typescript: 4.9.4 - webpack: 5.80.0_uhpfu7q6noim4yjdo6qt2aajgu + webpack: 5.88.2_uhpfu7q6noim4yjdo6qt2aajgu transitivePeerDependencies: - '@swc/core' - '@types/webpack' @@ -11874,7 +12383,7 @@ packages: resolution: {integrity: sha512-09tD+rBWMqBAdVqKhyotO6bTTJlCbVX9uVmc8la4jBoLL1JdE3qkBBmDivEsMDK5AoVaM5Zg2maDO4jm2HyZFw==} dev: true - /@storybook/react-docgen-typescript-plugin/1.0.6--canary.9.0c3f3b7.0_vf4xkga2qinmx3cxkwrybccrqy: + /@storybook/react-docgen-typescript-plugin/1.0.6--canary.9.0c3f3b7.0_vf3ejk3u7gfag4p4x6gqje5yuq: resolution: {integrity: sha512-KUqXC3oa9JuQ0kZJLBhVdS4lOneKTOopnNBK4tUAgoxWQ3u/IjzdueZjFr7gyBrXMoU6duutk3RQR9u8ZpYJ4Q==} peerDependencies: typescript: '>= 4.x' @@ -11888,7 +12397,7 @@ packages: react-docgen-typescript: 2.2.2_typescript@4.9.4 tslib: 2.6.2 typescript: 4.9.4 - webpack: 5.80.0_uhpfu7q6noim4yjdo6qt2aajgu + webpack: 5.88.2_uhpfu7q6noim4yjdo6qt2aajgu transitivePeerDependencies: - supports-color dev: true @@ -12081,7 +12590,7 @@ packages: dependencies: '@storybook/channels': 7.0.12 '@types/babel__core': 7.20.0 - '@types/express': 4.17.15 + '@types/express': 4.17.18 file-system-cache: 2.1.1 dev: true @@ -12090,7 +12599,7 @@ packages: dependencies: '@storybook/channels': 7.0.9 '@types/babel__core': 7.20.0 - '@types/express': 4.17.15 + '@types/express': 4.17.18 file-system-cache: 2.1.1 dev: true @@ -12150,6 +12659,137 @@ packages: - supports-color dev: false + /@sveltejs/adapter-auto/2.1.0_@sveltejs+kit@1.25.1: + resolution: {integrity: sha512-o2pZCfATFtA/Gw/BB0Xm7k4EYaekXxaPGER3xGSY3FvzFJGTlJlZjBseaXwYSM94lZ0HniOjTokN3cWaLX6fow==} + peerDependencies: + '@sveltejs/kit': ^1.0.0 + dependencies: + '@sveltejs/kit': 1.25.1_svelte@4.2.1+vite@4.4.9 + import-meta-resolve: 3.0.0 + dev: true + + /@sveltejs/kit/1.25.1: + resolution: {integrity: sha512-pD8XsvNJNgTNkFngNlM60my/X8dXWPKVzN5RghEQr0NjGZmuCjy49AfFu2cGbZjNf5pBcqd2RCNMW912P5fkhA==} + engines: {node: ^16.14 || >=18} + hasBin: true + requiresBuild: true + peerDependencies: + svelte: ^3.54.0 || ^4.0.0-next.0 + vite: ^4.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte': 2.4.6 + '@types/cookie': 0.5.2 + cookie: 0.5.0 + devalue: 4.3.2 + esm-env: 1.0.0 + kleur: 4.1.5 + magic-string: 0.30.3 + mime: 3.0.0 + sade: 1.8.1 + set-cookie-parser: 2.6.0 + sirv: 2.0.3 + tiny-glob: 0.2.9 + undici: 5.25.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@sveltejs/kit/1.25.1_svelte@4.2.1+vite@4.4.9: + resolution: {integrity: sha512-pD8XsvNJNgTNkFngNlM60my/X8dXWPKVzN5RghEQr0NjGZmuCjy49AfFu2cGbZjNf5pBcqd2RCNMW912P5fkhA==} + engines: {node: ^16.14 || >=18} + hasBin: true + requiresBuild: true + peerDependencies: + svelte: ^3.54.0 || ^4.0.0-next.0 + vite: ^4.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte': 2.4.6_svelte@4.2.1+vite@4.4.9 + '@types/cookie': 0.5.2 + cookie: 0.5.0 + devalue: 4.3.2 + esm-env: 1.0.0 + kleur: 4.1.5 + magic-string: 0.30.3 + mime: 3.0.0 + sade: 1.8.1 + set-cookie-parser: 2.6.0 + sirv: 2.0.3 + svelte: 4.2.1 + tiny-glob: 0.2.9 + undici: 5.25.4 + vite: 4.4.9 + transitivePeerDependencies: + - supports-color + dev: true + + /@sveltejs/vite-plugin-svelte-inspector/1.0.4_25hzhjyralpt5lwm3sm7wb3ghq: + resolution: {integrity: sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ==} + engines: {node: ^14.18.0 || >= 16} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^2.2.0 + svelte: ^3.54.0 || ^4.0.0 + vite: ^4.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte': 2.4.6 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@sveltejs/vite-plugin-svelte-inspector/1.0.4_x762n3kazuogrgydgy4ei75iqi: + resolution: {integrity: sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ==} + engines: {node: ^14.18.0 || >= 16} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^2.2.0 + svelte: ^3.54.0 || ^4.0.0 + vite: ^4.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte': 2.4.6_svelte@4.2.1+vite@4.4.9 + debug: 4.3.4 + svelte: 4.2.1 + vite: 4.4.9 + transitivePeerDependencies: + - supports-color + dev: true + + /@sveltejs/vite-plugin-svelte/2.4.6: + resolution: {integrity: sha512-zO79p0+DZnXPnF0ltIigWDx/ux7Ni+HRaFOw720Qeivc1azFUrJxTl0OryXVibYNx1hCboGia1NRV3x8RNv4cA==} + engines: {node: ^14.18.0 || >= 16} + peerDependencies: + svelte: ^3.54.0 || ^4.0.0 + vite: ^4.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 1.0.4_25hzhjyralpt5lwm3sm7wb3ghq + debug: 4.3.4 + deepmerge: 4.3.1 + kleur: 4.1.5 + magic-string: 0.30.3 + svelte-hmr: 0.15.3 + vitefu: 0.2.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@sveltejs/vite-plugin-svelte/2.4.6_svelte@4.2.1+vite@4.4.9: + resolution: {integrity: sha512-zO79p0+DZnXPnF0ltIigWDx/ux7Ni+HRaFOw720Qeivc1azFUrJxTl0OryXVibYNx1hCboGia1NRV3x8RNv4cA==} + engines: {node: ^14.18.0 || >= 16} + peerDependencies: + svelte: ^3.54.0 || ^4.0.0 + vite: ^4.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 1.0.4_x762n3kazuogrgydgy4ei75iqi + debug: 4.3.4 + deepmerge: 4.3.1 + kleur: 4.1.5 + magic-string: 0.30.3 + svelte: 4.2.1 + svelte-hmr: 0.15.3_svelte@4.2.1 + vite: 4.4.9 + vitefu: 0.2.4_vite@4.4.9 + transitivePeerDependencies: + - supports-color + dev: true + /@swc/core-darwin-arm64/1.3.26: resolution: {integrity: sha512-FWWflBfKRYrUJtko2xiedC5XCa31O75IZZqnTWuLpe9g3C5tnUuF3M8LSXZS/dn6wprome1MhtG9GMPkSYkhkg==} engines: {node: '>=10'} @@ -12352,7 +12992,7 @@ packages: '@graphql-typed-document-node/core': 3.2.0_graphql@16.6.0 axios: 1.4.0 graphql: 16.6.0 - zod: 3.21.4 + zod: 3.22.3 transitivePeerDependencies: - debug dev: false @@ -12363,7 +13003,7 @@ packages: '@graphql-typed-document-node/core': 3.2.0_graphql@16.6.0 axios: 1.4.0 graphql: 16.6.0 - zod: 3.21.4 + zod: 3.22.4 transitivePeerDependencies: - debug dev: false @@ -12372,7 +13012,7 @@ packages: resolution: {integrity: sha512-P6iIPyYQ+qH8CvGauAqanhVnjrnRe0IZFSYCeGkSRW9q3u8bdVn2NPI+lasFyVsEQn1J/IFmp5Aax41+dAP9wg==} engines: {node: '>=12'} dependencies: - '@babel/code-frame': 7.18.6 + '@babel/code-frame': 7.22.13 '@babel/runtime': 7.22.5 '@types/aria-query': 5.0.1 aria-query: 5.1.3 @@ -12507,7 +13147,7 @@ packages: /@types/acorn/4.0.6: resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==} dependencies: - '@types/estree': 1.0.0 + '@types/estree': 1.0.2 dev: true /@types/aria-query/5.0.1: @@ -12615,11 +13255,19 @@ packages: /@types/cookie/0.4.1: resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} + /@types/cookie/0.5.2: + resolution: {integrity: sha512-DBpRoJGKJZn7RY92dPrgoMew8xCWc2P71beqsjyhEI/Ds9mOyVmBwtekyfhpwFIVt1WrxTonFifiOZ62V8CnNA==} + dev: true + + /@types/cookiejar/2.1.2: + resolution: {integrity: sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==} + dev: true + /@types/cookies/0.7.7: resolution: {integrity: sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA==} dependencies: '@types/connect': 3.4.35 - '@types/express': 4.17.15 + '@types/express': 4.17.18 '@types/keygrip': 1.0.2 '@types/node': 20.6.0 dev: false @@ -12695,7 +13343,7 @@ packages: resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==} dependencies: '@types/eslint': 8.4.10 - '@types/estree': 1.0.0 + '@types/estree': 1.0.2 dev: true /@types/eslint/8.4.10: @@ -12708,13 +13356,13 @@ packages: /@types/estree-jsx/0.0.1: resolution: {integrity: sha512-gcLAYiMfQklDCPjQegGn0TBAn9it05ISEsEhlKQUddIk7o2XDokOcTN7HBO8tznM0D9dGezvHEfRZBfZf6me0A==} dependencies: - '@types/estree': 1.0.0 + '@types/estree': 1.0.2 dev: true /@types/estree-jsx/1.0.0: resolution: {integrity: sha512-3qvGd0z8F2ENTGr/GG1yViqfiKmRfrXVx5sJyHGFu3z7m5g5utCQtGp/g29JnjflhtQJBv1WDQukHiT58xPcYQ==} dependencies: - '@types/estree': 1.0.0 + '@types/estree': 1.0.2 dev: true /@types/estree/0.0.51: @@ -12724,18 +13372,31 @@ packages: /@types/estree/1.0.0: resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==} + /@types/estree/1.0.2: + resolution: {integrity: sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==} + dev: true + /@types/express-serve-static-core/4.17.32: resolution: {integrity: sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA==} dependencies: '@types/node': 20.6.0 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 + dev: true + + /@types/express-serve-static-core/4.17.37: + resolution: {integrity: sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==} + dependencies: + '@types/node': 20.6.0 + '@types/qs': 6.9.7 + '@types/range-parser': 1.2.4 + '@types/send': 0.17.2 /@types/express/4.17.13: resolution: {integrity: sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==} dependencies: '@types/body-parser': 1.19.2 - '@types/express-serve-static-core': 4.17.32 + '@types/express-serve-static-core': 4.17.37 '@types/qs': 6.9.7 '@types/serve-static': 1.15.0 dev: false @@ -12747,6 +13408,15 @@ packages: '@types/express-serve-static-core': 4.17.32 '@types/qs': 6.9.7 '@types/serve-static': 1.15.0 + dev: true + + /@types/express/4.17.18: + resolution: {integrity: sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ==} + dependencies: + '@types/body-parser': 1.19.2 + '@types/express-serve-static-core': 4.17.37 + '@types/qs': 6.9.7 + '@types/serve-static': 1.15.0 /@types/find-cache-dir/3.2.1: resolution: {integrity: sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==} @@ -12901,6 +13571,10 @@ packages: resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} dev: true + /@types/json-schema/7.0.13: + resolution: {integrity: sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==} + dev: true + /@types/json5/0.0.29: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} @@ -12996,6 +13670,9 @@ packages: resolution: {integrity: sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==} dev: true + /@types/mime/1.3.3: + resolution: {integrity: sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==} + /@types/mime/3.0.1: resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==} @@ -13137,6 +13814,10 @@ packages: /@types/prop-types/15.7.5: resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} + /@types/pug/2.0.7: + resolution: {integrity: sha512-I469DU0UXNC1aHepwirWhu9YKg5fkxohZD95Ey/5A7lovC+Siu+MCLffva87lnfThaOrw9Vb1DUN5t55oULAAw==} + dev: true + /@types/qs/6.9.7: resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} @@ -13167,6 +13848,10 @@ packages: resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} dev: false + /@types/retry/0.12.2: + resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} + dev: false + /@types/scheduler/0.16.2: resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} @@ -13181,6 +13866,12 @@ packages: /@types/semver/7.5.1: resolution: {integrity: sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==} + /@types/send/0.17.2: + resolution: {integrity: sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==} + dependencies: + '@types/mime': 1.3.3 + '@types/node': 20.6.0 + /@types/serve-static/1.15.0: resolution: {integrity: sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==} dependencies: @@ -13209,6 +13900,19 @@ packages: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} dev: true + /@types/superagent/4.1.19: + resolution: {integrity: sha512-McM1mlc7PBZpCaw0fw/36uFqo0YeA6m8JqoyE4OfqXsZCIg0hPP2xdE6FM7r6fdprDZHlJwDpydUj1R++93hCA==} + dependencies: + '@types/cookiejar': 2.1.2 + '@types/node': 20.6.0 + dev: true + + /@types/supertest/2.0.14: + resolution: {integrity: sha512-Q900DeeHNFF3ZYYepf/EyJfZDA2JrnWLaSQ0YNV7+2GTo8IlJzauEnDGhya+hauncpBYTYGpVHwGdssJeAQ7eA==} + dependencies: + '@types/superagent': 4.1.19 + dev: true + /@types/tar/6.1.4: resolution: {integrity: sha512-Cp4oxpfIzWt7mr2pbhHT2OTXGMAL0szYCzuf8lRWyIMCgsx6/Hfc3ubztuhvzXHXgraTQxyOCmmg7TDGIMIJJQ==} dependencies: @@ -13270,8 +13974,8 @@ packages: '@types/yargs-parser': 21.0.0 dev: true - /@types/yauzl/2.10.0: - resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} + /@types/yauzl/2.10.1: + resolution: {integrity: sha512-CHzgNU3qYBnp/O4S3yv2tXPlvMTq0YWSTVg2/JYLqWZGHwwgJGAwd00poay/11asPq8wLFwHzubyInqHIFmmiw==} requiresBuild: true dependencies: '@types/node': 20.6.0 @@ -13334,6 +14038,63 @@ packages: - supports-color dev: true + /@typescript-eslint/eslint-plugin/5.59.6_ltg3s7zeaq5lfn26f6cgiedibm: + resolution: {integrity: sha512-sXtOgJNEuRU5RLwPUb1jxtToZbgvq3M6FPpY4QENxoOggK+UpTxUBpj6tD8+Qh2g46Pi9We87E+eHnUw8YcGsw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.5.1 + '@typescript-eslint/parser': 5.59.6_ox3na7ge7wjdarbyztnclevxam + '@typescript-eslint/scope-manager': 5.59.6 + '@typescript-eslint/type-utils': 5.59.6_ox3na7ge7wjdarbyztnclevxam + '@typescript-eslint/utils': 5.59.6_ox3na7ge7wjdarbyztnclevxam + debug: 4.3.4 + eslint: 8.45.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.4 + natural-compare-lite: 1.4.0 + semver: 7.5.0 + tsutils: 3.21.0_typescript@5.2.2 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/eslint-plugin/6.7.4_ygtxu7ao4w7xzfo6eep522bcem: + resolution: {integrity: sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.5.1 + '@typescript-eslint/parser': 6.7.4_ox3na7ge7wjdarbyztnclevxam + '@typescript-eslint/scope-manager': 6.7.4 + '@typescript-eslint/type-utils': 6.7.4_ox3na7ge7wjdarbyztnclevxam + '@typescript-eslint/utils': 6.7.4_ox3na7ge7wjdarbyztnclevxam + '@typescript-eslint/visitor-keys': 6.7.4 + debug: 4.3.4 + eslint: 8.45.0 + graphemer: 1.4.0 + ignore: 5.2.4 + natural-compare: 1.4.0 + semver: 7.5.4 + ts-api-utils: 1.0.3_typescript@5.2.2 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/parser/5.59.6_binxsscxvozjxebftqdoazsxm4: resolution: {integrity: sha512-7pCa6al03Pv1yf/dUg/s1pXz/yGMUBAw5EeWqNTFiSueKvRNonze3hma3lhdsOrQcaOXhbk5gKu2Fludiho9VA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -13432,6 +14193,47 @@ packages: - supports-color dev: true + /@typescript-eslint/parser/5.59.6_ox3na7ge7wjdarbyztnclevxam: + resolution: {integrity: sha512-7pCa6al03Pv1yf/dUg/s1pXz/yGMUBAw5EeWqNTFiSueKvRNonze3hma3lhdsOrQcaOXhbk5gKu2Fludiho9VA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.59.6 + '@typescript-eslint/types': 5.59.6 + '@typescript-eslint/typescript-estree': 5.59.6_typescript@5.2.2 + debug: 4.3.4 + eslint: 8.45.0 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser/6.7.4_ox3na7ge7wjdarbyztnclevxam: + resolution: {integrity: sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 6.7.4 + '@typescript-eslint/types': 6.7.4 + '@typescript-eslint/typescript-estree': 6.7.4_typescript@5.2.2 + '@typescript-eslint/visitor-keys': 6.7.4 + debug: 4.3.4 + eslint: 8.45.0 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/scope-manager/5.59.6: resolution: {integrity: sha512-gLbY3Le9Dxcb8KdpF0+SJr6EQ+hFGYFl6tVY8VxLPFDfUZC7BHFw+Vq7bM5lE9DwWPfx4vMWWTLGXgpc0mAYyQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -13439,6 +14241,14 @@ packages: '@typescript-eslint/types': 5.59.6 '@typescript-eslint/visitor-keys': 5.59.6 + /@typescript-eslint/scope-manager/6.7.4: + resolution: {integrity: sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.7.4 + '@typescript-eslint/visitor-keys': 6.7.4 + dev: true + /@typescript-eslint/type-utils/5.59.6_iukboom6ndih5an6iafl45j2fe: resolution: {integrity: sha512-A4tms2Mp5yNvLDlySF+kAThV9VTBPCvGf0Rp8nl/eoDX9Okun8byTKoj3fJ52IJitjWOk0fKPNQhXEB++eNozQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -13479,10 +14289,55 @@ packages: - supports-color dev: true + /@typescript-eslint/type-utils/5.59.6_ox3na7ge7wjdarbyztnclevxam: + resolution: {integrity: sha512-A4tms2Mp5yNvLDlySF+kAThV9VTBPCvGf0Rp8nl/eoDX9Okun8byTKoj3fJ52IJitjWOk0fKPNQhXEB++eNozQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.59.6_typescript@5.2.2 + '@typescript-eslint/utils': 5.59.6_ox3na7ge7wjdarbyztnclevxam + debug: 4.3.4 + eslint: 8.45.0 + tsutils: 3.21.0_typescript@5.2.2 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/type-utils/6.7.4_ox3na7ge7wjdarbyztnclevxam: + resolution: {integrity: sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 6.7.4_typescript@5.2.2 + '@typescript-eslint/utils': 6.7.4_ox3na7ge7wjdarbyztnclevxam + debug: 4.3.4 + eslint: 8.45.0 + ts-api-utils: 1.0.3_typescript@5.2.2 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/types/5.59.6: resolution: {integrity: sha512-tH5lBXZI7T2MOUgOWFdVNUILsI02shyQvfzG9EJkoONWugCG77NDDa1EeDGw7oJ5IvsTAAGVV8I3Tk2PNu9QfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@typescript-eslint/types/6.7.4: + resolution: {integrity: sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + /@typescript-eslint/typescript-estree/5.59.6: resolution: {integrity: sha512-vW6JP3lMAs/Tq4KjdI/RiHaaJSO7IUsbkz17it/Rl9Q+WkQ77EOuOnlbaU8kKfVIOJxMhnRiBG+olE7f3M16DA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -13586,6 +14441,48 @@ packages: transitivePeerDependencies: - supports-color + /@typescript-eslint/typescript-estree/5.59.6_typescript@5.2.2: + resolution: {integrity: sha512-vW6JP3lMAs/Tq4KjdI/RiHaaJSO7IUsbkz17it/Rl9Q+WkQ77EOuOnlbaU8kKfVIOJxMhnRiBG+olE7f3M16DA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.59.6 + '@typescript-eslint/visitor-keys': 5.59.6 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + tsutils: 3.21.0_typescript@5.2.2 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/typescript-estree/6.7.4_typescript@5.2.2: + resolution: {integrity: sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.7.4 + '@typescript-eslint/visitor-keys': 6.7.4 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + ts-api-utils: 1.0.3_typescript@5.2.2 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/utils/5.59.6_eslint@8.45.0: resolution: {integrity: sha512-vzaaD6EXbTS29cVH0JjXBdzMt6VBlv+hE31XktDRMX1j3462wZCJa7VzO2AxXEXcIl8GQqZPcOPuW/Z1tZVogg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -13646,6 +14543,45 @@ packages: - typescript dev: true + /@typescript-eslint/utils/5.59.6_ox3na7ge7wjdarbyztnclevxam: + resolution: {integrity: sha512-vzaaD6EXbTS29cVH0JjXBdzMt6VBlv+hE31XktDRMX1j3462wZCJa7VzO2AxXEXcIl8GQqZPcOPuW/Z1tZVogg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0_eslint@8.45.0 + '@types/json-schema': 7.0.11 + '@types/semver': 7.3.13 + '@typescript-eslint/scope-manager': 5.59.6 + '@typescript-eslint/types': 5.59.6 + '@typescript-eslint/typescript-estree': 5.59.6_typescript@5.2.2 + eslint: 8.45.0 + eslint-scope: 5.1.1 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/utils/6.7.4_ox3na7ge7wjdarbyztnclevxam: + resolution: {integrity: sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0_eslint@8.45.0 + '@types/json-schema': 7.0.13 + '@types/semver': 7.5.1 + '@typescript-eslint/scope-manager': 6.7.4 + '@typescript-eslint/types': 6.7.4 + '@typescript-eslint/typescript-estree': 6.7.4_typescript@5.2.2 + eslint: 8.45.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@typescript-eslint/visitor-keys/5.59.6: resolution: {integrity: sha512-zEfbFLzB9ETcEJ4HZEEsCR9HHeNku5/Qw1jSS5McYJv5BR+ftYXwFFAH5Al+xkGaZEqowMwl7uoJjQb1YSPF8Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -13653,12 +14589,21 @@ packages: '@typescript-eslint/types': 5.59.6 eslint-visitor-keys: 3.4.2 - /@uiw/codemirror-extensions-basic-setup/4.19.5_wd2tsis3in55bkaiwnc2c46tom: + /@typescript-eslint/visitor-keys/6.7.4: + resolution: {integrity: sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.7.4 + eslint-visitor-keys: 3.4.2 + dev: true + + /@uiw/codemirror-extensions-basic-setup/4.19.5_o3n2erwajrogdzfxqd6wu4qkza: resolution: {integrity: sha512-1zt7ZPJ01xKkSW/KDy0FZNga0bngN1fC594wCVG7FBi60ehfcAucpooQ+JSPScKXopxcb+ugPKZvVLzr9/OfzA==} peerDependencies: '@codemirror/autocomplete': '>=6.0.0' '@codemirror/commands': '>=6.0.0' '@codemirror/language': '>=6.0.0' + '@codemirror/lint': '>=6.0.0' '@codemirror/search': '>=6.0.0' '@codemirror/state': '>=6.0.0' '@codemirror/view': '>=6.0.0' @@ -13666,13 +14611,13 @@ packages: '@codemirror/autocomplete': 6.4.0_czcfkg2f66rxeiodoti7r2gulu '@codemirror/commands': 6.1.3 '@codemirror/language': 6.3.2 - '@codemirror/lint': 6.1.0 + '@codemirror/lint': 6.4.2 '@codemirror/search': 6.2.3 '@codemirror/state': 6.2.0 '@codemirror/view': 6.7.2 dev: false - /@uiw/react-codemirror/4.19.5_k4ec5g7vuuzzonc3d6xbjnmmle: + /@uiw/react-codemirror/4.19.5_th22fcplkuhrqjnlojwclcaim4: resolution: {integrity: sha512-ZCHh8d7beXbF8/t7F1+yHht6A9Y6CdKeOkZq4A09lxJEnyTQrj1FMf2zvfaqc7K23KNjkTCtSlbqKKbVDgrWaw==} peerDependencies: '@codemirror/state': '>=6.0.0' @@ -13685,13 +14630,14 @@ packages: '@codemirror/state': 6.2.0 '@codemirror/theme-one-dark': 6.1.0 '@codemirror/view': 6.7.2 - '@uiw/codemirror-extensions-basic-setup': 4.19.5_wd2tsis3in55bkaiwnc2c46tom + '@uiw/codemirror-extensions-basic-setup': 4.19.5_o3n2erwajrogdzfxqd6wu4qkza codemirror: 6.0.1 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 transitivePeerDependencies: - '@codemirror/autocomplete' - '@codemirror/language' + - '@codemirror/lint' - '@codemirror/search' dev: false @@ -13733,7 +14679,7 @@ packages: lodash: 4.17.21 mlly: 1.1.1 outdent: 0.8.0 - vite: 4.1.4 + vite: 4.4.9 vite-node: 0.28.5 transitivePeerDependencies: - '@types/node' @@ -13760,7 +14706,7 @@ packages: lodash: 4.17.21 mlly: 1.1.1 outdent: 0.8.0 - vite: 4.1.4_@types+node@18.11.18 + vite: 4.4.9_@types+node@18.11.18 vite-node: 0.28.5_@types+node@18.11.18 transitivePeerDependencies: - '@types/node' @@ -13996,6 +14942,10 @@ packages: resolution: {integrity: sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==} dev: false + /abstract-logging/2.0.1: + resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} + dev: true + /accepts/1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -14003,8 +14953,8 @@ packages: mime-types: 2.1.35 negotiator: 0.6.3 - /acorn-import-assertions/1.8.0_acorn@8.10.0: - resolution: {integrity: sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==} + /acorn-import-assertions/1.9.0_acorn@8.10.0: + resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} peerDependencies: acorn: ^8 dependencies: @@ -14194,7 +15144,6 @@ packages: /ansi-colors/4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} - dev: false /ansi-escapes/4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} @@ -14276,9 +15225,16 @@ packages: resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==} dev: true + /append-field/1.0.0: + resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} + /aproba/2.0.0: resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + /archy/1.0.0: + resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} + dev: true + /are-we-there-yet/2.0.0: resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} engines: {node: '>=10'} @@ -14321,6 +15277,12 @@ packages: dependencies: deep-equal: 2.2.0 + /aria-query/5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + dependencies: + dequal: 2.0.3 + dev: true + /arr-diff/1.1.0: resolution: {integrity: sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==} engines: {node: '>=0.10.0'} @@ -14377,6 +15339,10 @@ packages: engines: {node: '>=0.10.0'} dev: false + /array-timsort/1.0.3: + resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} + dev: true + /array-union/1.0.2: resolution: {integrity: sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==} engines: {node: '>=0.10.0'} @@ -14440,6 +15406,10 @@ packages: engines: {node: '>=8'} dev: false + /asap/2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + dev: true + /asn1js/3.0.5: resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==} engines: {node: '>=12.0.0'} @@ -14562,7 +15532,7 @@ packages: yargs-parser: 21.1.1 zod: 3.21.1 optionalDependencies: - sharp: 0.32.5 + sharp: 0.32.6 transitivePeerDependencies: - '@types/node' - less @@ -14590,6 +15560,11 @@ packages: hasBin: true dev: false + /atomic-sleep/1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + dev: true + /autoprefixer/10.4.13_postcss@8.4.21: resolution: {integrity: sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==} engines: {node: ^10 || ^12 || >=14} @@ -14626,6 +15601,16 @@ packages: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} + /avvio/8.2.1: + resolution: {integrity: sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==} + dependencies: + archy: 1.0.0 + debug: 4.3.4 + fastq: 1.15.0 + transitivePeerDependencies: + - supports-color + dev: true + /axe-core/4.6.2: resolution: {integrity: sha512-b1WlTV8+XKLj9gZy2DZXgQiyDp9xkkoe2a6U6UbYccScq2wgH/YwCeI2/Jq2mgo0HzQxqJOjWZBLeA/mqsk5Mg==} engines: {node: '>=4'} @@ -14677,6 +15662,12 @@ packages: dependencies: deep-equal: 2.2.0 + /axobject-query/3.2.1: + resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} + dependencies: + dequal: 2.0.3 + dev: true + /b4a/1.6.4: resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} optional: true @@ -14707,7 +15698,7 @@ packages: - supports-color dev: true - /babel-loader/9.1.2_25xk4kalzxoxom6k7ae7wqkwo4: + /babel-loader/9.1.2_ijmuqjuz7epdoeof4qwmt7scdi: resolution: {integrity: sha512-mN14niXW43tddohGl8HPu5yfQq70iUThvFL/4QzESA7GcZoC0eVOhvWdQ8+3UlSjaDE9MVtsW9mxDY07W7VpVA==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -14717,7 +15708,7 @@ packages: '@babel/core': 7.20.12 find-cache-dir: 3.3.2 schema-utils: 4.0.1 - webpack: 5.80.0_uhpfu7q6noim4yjdo6qt2aajgu + webpack: 5.88.2_uhpfu7q6noim4yjdo6qt2aajgu dev: true /babel-plugin-add-react-displayname/0.0.5: @@ -15003,7 +15994,7 @@ packages: engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} dependencies: bytes: 3.1.2 - content-type: 1.0.4 + content-type: 1.0.5 debug: 2.6.9 depd: 2.0.0 destroy: 1.2.0 @@ -15017,6 +16008,25 @@ packages: transitivePeerDependencies: - supports-color + /body-parser/1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + /boolbase/1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} dev: true @@ -15577,7 +16587,7 @@ packages: normalize-path: 3.0.0 readdirp: 3.6.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 /chownr/1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} @@ -15659,6 +16669,7 @@ packages: /cli-spinners/2.7.0: resolution: {integrity: sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==} engines: {node: '>=6'} + dev: false /cli-spinners/2.9.1: resolution: {integrity: sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==} @@ -15748,13 +16759,23 @@ packages: engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} dev: true + /code-red/1.0.4: + resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + '@types/estree': 1.0.2 + acorn: 8.10.0 + estree-walker: 3.0.3 + periscopic: 3.1.0 + dev: true + /codemirror/6.0.1: resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==} dependencies: '@codemirror/autocomplete': 6.4.0_czcfkg2f66rxeiodoti7r2gulu '@codemirror/commands': 6.1.3 '@codemirror/language': 6.3.2 - '@codemirror/lint': 6.1.0 + '@codemirror/lint': 6.4.2 '@codemirror/search': 6.2.3 '@codemirror/state': 6.2.0 '@codemirror/view': 6.7.2 @@ -15857,6 +16878,17 @@ packages: engines: {node: ^12.20.0 || >=14} dev: false + /comment-json/4.2.3: + resolution: {integrity: sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==} + engines: {node: '>= 6'} + dependencies: + array-timsort: 1.0.3 + core-util-is: 1.0.3 + esprima: 4.0.1 + has-own-prop: 2.0.0 + repeat-string: 1.6.1 + dev: true + /common-ancestor-path/1.0.1: resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} @@ -15870,7 +16902,6 @@ packages: /component-emitter/1.3.0: resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} - dev: false /compressible/2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} @@ -15903,7 +16934,6 @@ packages: inherits: 2.0.4 readable-stream: 2.3.7 typedarray: 0.0.6 - dev: true /concurrently/8.2.0: resolution: {integrity: sha512-nnLMxO2LU492mTUj9qX/az/lESonSZu81UznYDoXtz1IQf996ixVqPAgHXwvHiHCAef/7S8HIK+fTFK7Ifk8YA==} @@ -15948,9 +16978,18 @@ packages: xdg-basedir: 5.1.0 dev: false + /consola/2.15.3: + resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + /console-control-strings/1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + /console-table-printer/2.11.2: + resolution: {integrity: sha512-uuUHie0sfPP542TKGzPFal0W1wo1beuKAqIZdaavcONx8OoqdnJRKjkinbRTOta4FaCa1RcIL+7mMJWX3pQGVg==} + dependencies: + simple-wcswidth: 1.0.1 + dev: false + /content-disposition/0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -15961,6 +17000,10 @@ packages: resolution: {integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==} engines: {node: '>= 0.6'} + /content-type/1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + /convert-source-map/1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} @@ -15983,6 +17026,10 @@ packages: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} + /cookiejar/2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + dev: true + /copy-descriptor/0.1.1: resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==} engines: {node: '>=0.10.0'} @@ -16008,6 +17055,13 @@ packages: /core-util-is/1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + /cors/2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + /cosmiconfig/7.1.0: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} @@ -16157,7 +17211,7 @@ packages: semver: 7.5.4 dev: true - /css-loader/6.7.3_webpack@5.80.0: + /css-loader/6.7.3_webpack@5.88.2: resolution: {integrity: sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==} engines: {node: '>= 12.13.0'} peerDependencies: @@ -16171,7 +17225,7 @@ packages: postcss-modules-values: 4.0.0_postcss@8.4.29 postcss-value-parser: 4.2.0 semver: 7.5.4 - webpack: 5.80.0_uhpfu7q6noim4yjdo6qt2aajgu + webpack: 5.88.2_uhpfu7q6noim4yjdo6qt2aajgu dev: true /css-select/4.3.0: @@ -16192,6 +17246,14 @@ packages: source-map: 0.6.1 dev: false + /css-tree/2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.0.2 + dev: true + /css-unit-converter/1.1.2: resolution: {integrity: sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==} dev: false @@ -16523,6 +17585,11 @@ packages: resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} engines: {node: '>=0.10.0'} + /deepmerge/4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + dev: true + /default-browser-id/3.0.0: resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} engines: {node: '>=12'} @@ -16687,6 +17754,13 @@ packages: /devalue/4.3.2: resolution: {integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==} + /dezalgo/1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + dev: true + /didyoumean/1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -16830,7 +17904,6 @@ packages: /dotenv-expand/10.0.0: resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} engines: {node: '>=12'} - dev: true /dotenv/16.0.3: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} @@ -16955,6 +18028,14 @@ packages: graceful-fs: 4.2.10 tapable: 2.2.1 + /enhanced-resolve/5.15.0: + resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} + engines: {node: '>=10.13.0'} + dependencies: + graceful-fs: 4.2.10 + tapable: 2.2.1 + dev: true + /enquirer/2.3.6: resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} engines: {node: '>=8.6'} @@ -17105,6 +18186,10 @@ packages: resolution: {integrity: sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==} dev: true + /es6-promise/3.3.1: + resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} + dev: true + /es6-symbol/3.1.3: resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==} dependencies: @@ -17668,13 +18753,31 @@ packages: eslint: 8.31.0 dev: true - /eslint-config-turbo/1.10.14_eslint@8.31.0: - resolution: {integrity: sha512-ZeB+IcuFXy1OICkLuAplVa0euoYbhK+bMEQd0nH9+Lns18lgZRm33mVz/iSoH9VdUzl/1ZmFmoK+RpZc+8R80A==} + /eslint-config-prettier/8.6.0_eslint@8.45.0: + resolution: {integrity: sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.45.0 + dev: true + + /eslint-config-prettier/9.0.0_eslint@8.45.0: + resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.45.0 + dev: true + + /eslint-config-turbo/1.10.15_eslint@8.31.0: + resolution: {integrity: sha512-76mpx2x818JZE26euen14utYcFDxOahZ9NaWA+6Xa4pY2ezVKVschuOxS96EQz3o3ZRSmcgBOapw/gHbN+EKxQ==} peerDependencies: eslint: '>6.6.0' dependencies: eslint: 8.31.0 - eslint-plugin-turbo: 1.10.14_eslint@8.31.0 + eslint-plugin-turbo: 1.10.15_eslint@8.31.0 dev: true /eslint-doc-generator/1.4.3_eslint@8.45.0: @@ -17929,7 +19032,7 @@ packages: minimatch: 3.1.2 object.values: 1.1.6 resolve: 1.22.2 - semver: 6.3.0 + semver: 6.3.1 tsconfig-paths: 3.14.1 transitivePeerDependencies: - eslint-import-resolver-typescript @@ -17962,7 +19065,7 @@ packages: minimatch: 3.1.2 object.values: 1.1.6 resolve: 1.22.2 - semver: 6.3.0 + semver: 6.3.1 tsconfig-paths: 3.14.1 transitivePeerDependencies: - eslint-import-resolver-typescript @@ -17995,7 +19098,7 @@ packages: minimatch: 3.1.2 object.values: 1.1.6 resolve: 1.22.2 - semver: 6.3.0 + semver: 6.3.1 tsconfig-paths: 3.14.1 transitivePeerDependencies: - eslint-import-resolver-typescript @@ -18090,7 +19193,7 @@ packages: minimatch: 3.1.2 object.entries: 1.1.6 object.fromentries: 2.0.6 - semver: 6.3.0 + semver: 6.3.1 dev: true /eslint-plugin-jsx-a11y/6.7.1_eslint@8.42.0: @@ -18115,7 +19218,7 @@ packages: minimatch: 3.1.2 object.entries: 1.1.6 object.fromentries: 2.0.6 - semver: 6.3.0 + semver: 6.3.1 dev: true /eslint-plugin-jsx-a11y/6.7.1_eslint@8.45.0: @@ -18140,7 +19243,7 @@ packages: minimatch: 3.1.2 object.entries: 1.1.6 object.fromentries: 2.0.6 - semver: 6.3.0 + semver: 6.3.1 /eslint-plugin-node/11.1.0_eslint@8.31.0: resolution: {integrity: sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==} @@ -18172,6 +19275,27 @@ packages: semver: 6.3.0 dev: true + /eslint-plugin-prettier/5.0.0_jybzfv6jdssomlxkhhfntuvyli: + resolution: {integrity: sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '*' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.45.0 + eslint-config-prettier: 9.0.0_eslint@8.45.0 + prettier: 3.0.0 + prettier-linter-helpers: 1.0.0 + synckit: 0.8.5 + dev: true + /eslint-plugin-react-hooks/4.6.0_eslint@8.31.0: resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} engines: {node: '>=10'} @@ -18251,7 +19375,7 @@ packages: object.values: 1.1.6 prop-types: 15.8.1 resolve: 2.0.0-next.4 - semver: 6.3.0 + semver: 6.3.1 string.prototype.matchall: 4.0.8 dev: true @@ -18275,7 +19399,7 @@ packages: object.values: 1.1.6 prop-types: 15.8.1 resolve: 2.0.0-next.4 - semver: 6.3.0 + semver: 6.3.1 string.prototype.matchall: 4.0.8 dev: true @@ -18299,9 +19423,37 @@ packages: object.values: 1.1.6 prop-types: 15.8.1 resolve: 2.0.0-next.4 - semver: 6.3.0 + semver: 6.3.1 string.prototype.matchall: 4.0.8 + /eslint-plugin-svelte/2.34.0_eslint@8.45.0+svelte@4.2.1: + resolution: {integrity: sha512-4RYUgNai7wr0v+T/kljMiYSjC/oqwgq5i+cPppawryAayj4C7WK1ixFlWCGmNmBppnoKCl4iA4ZPzPtlHcb4CA==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0-0 + svelte: ^3.37.0 || ^4.0.0 + peerDependenciesMeta: + svelte: + optional: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0_eslint@8.45.0 + '@jridgewell/sourcemap-codec': 1.4.15 + debug: 4.3.4 + eslint: 8.45.0 + esutils: 2.0.3 + known-css-properties: 0.28.0 + postcss: 8.4.29 + postcss-load-config: 3.1.4_postcss@8.4.29 + postcss-safe-parser: 6.0.0_postcss@8.4.29 + postcss-selector-parser: 6.0.11 + semver: 7.5.4 + svelte: 4.2.1 + svelte-eslint-parser: 0.33.1_svelte@4.2.1 + transitivePeerDependencies: + - supports-color + - ts-node + dev: true + /eslint-plugin-testing-library/5.11.0_iukboom6ndih5an6iafl45j2fe: resolution: {integrity: sha512-ELY7Gefo+61OfXKlQeXNIDVVLPcvKTeiQOoMZG9TeuWa7Ln4dUNRv8JdRWBQI9Mbb427XGlVB1aa1QPZxBJM8Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6'} @@ -18328,8 +19480,8 @@ packages: - typescript dev: true - /eslint-plugin-turbo/1.10.14_eslint@8.31.0: - resolution: {integrity: sha512-sBdBDnYr9AjT1g4lR3PBkZDonTrMnR4TvuGv5W0OiF7z9az1rI68yj2UHJZvjkwwcGu5mazWA1AfB0oaagpmfg==} + /eslint-plugin-turbo/1.10.15_eslint@8.31.0: + resolution: {integrity: sha512-Tv4QSKV/U56qGcTqS/UgOvb9HcKFmWOQcVh3HEaj7of94lfaENgfrtK48E2CckQf7amhKs1i+imhCsNCKjkQyA==} peerDependencies: eslint: '>6.6.0' dependencies: @@ -18547,6 +19699,10 @@ packages: transitivePeerDependencies: - supports-color + /esm-env/1.0.0: + resolution: {integrity: sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==} + dev: true + /espree/9.4.1: resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -18611,7 +19767,7 @@ packages: /estree-util-attach-comments/2.1.0: resolution: {integrity: sha512-rJz6I4L0GaXYtHpoMScgDIwM0/Vwbu5shbMeER596rB2D1EWF6+Gj0e0UKzJPZrpoOc87+Q2kgVFHfjAymIqmw==} dependencies: - '@types/estree': 1.0.0 + '@types/estree': 1.0.2 dev: true /estree-util-build-jsx/2.2.2: @@ -18697,6 +19853,21 @@ packages: tsafe: 1.4.1 dev: false + /execa/4.1.0: + resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 5.2.0 + human-signals: 1.1.1 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + /execa/5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -18908,14 +20079,26 @@ packages: get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: - '@types/yauzl': 2.10.0 + '@types/yauzl': 2.10.1 transitivePeerDependencies: - supports-color dev: false + /fast-content-type-parse/1.1.0: + resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} + dev: true + + /fast-decode-uri-component/1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + dev: true + /fast-deep-equal/3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + /fast-diff/1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + dev: true + /fast-equals/5.0.1: resolution: {integrity: sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==} engines: {node: '>=6.0.0'} @@ -18985,6 +20168,17 @@ packages: /fast-json-stable-stringify/2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + /fast-json-stringify/5.8.0: + resolution: {integrity: sha512-VVwK8CFMSALIvt14U8AvrSzQAwN/0vaVRiFFUVlpnXSnDGrSkOAO5MtzyN8oQNjLd5AqTW5OZRgyjoNuAuR3jQ==} + dependencies: + '@fastify/deepmerge': 1.3.0 + ajv: 8.12.0 + ajv-formats: 2.1.1 + fast-deep-equal: 3.1.3 + fast-uri: 2.2.0 + rfdc: 1.3.0 + dev: true + /fast-levenshtein/2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} @@ -18996,14 +20190,55 @@ packages: resolution: {integrity: sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==} dev: false + /fast-querystring/1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + dependencies: + fast-decode-uri-component: 1.0.1 + dev: true + + /fast-redact/3.3.0: + resolution: {integrity: sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==} + engines: {node: '>=6'} + dev: true + + /fast-safe-stringify/2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + /fast-shallow-equal/1.0.0: resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==} dev: false + /fast-uri/2.2.0: + resolution: {integrity: sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg==} + dev: true + /fastest-stable-stringify/2.0.2: resolution: {integrity: sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==} dev: false + /fastify/4.23.2: + resolution: {integrity: sha512-WFSxsHES115svC7NrerNqZwwM0UOxbC/P6toT9LRHgAAFvG7o2AN5W+H4ihCtOGuYXjZf4z+2jXC89rVEoPWOA==} + dependencies: + '@fastify/ajv-compiler': 3.5.0 + '@fastify/error': 3.4.0 + '@fastify/fast-json-stringify-compiler': 4.3.0 + abstract-logging: 2.0.1 + avvio: 8.2.1 + fast-content-type-parse: 1.1.0 + fast-json-stringify: 5.8.0 + find-my-way: 7.6.2 + light-my-request: 5.11.0 + pino: 8.15.6 + process-warning: 2.2.0 + proxy-addr: 2.0.7 + rfdc: 1.3.0 + secure-json-parse: 2.7.0 + semver: 7.5.4 + toad-cache: 3.3.0 + transitivePeerDependencies: + - supports-color + dev: true + /fastq/1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: @@ -19129,6 +20364,15 @@ packages: pkg-dir: 4.2.0 dev: true + /find-my-way/7.6.2: + resolution: {integrity: sha512-0OjHn1b1nCX3eVbm9ByeEHiscPYiHLfhei1wOUU9qffQkk98wE0Lo8VrVYfSGMgnSnDh86DxedduAnBf4nwUEw==} + engines: {node: '>=14'} + dependencies: + fast-deep-equal: 3.1.3 + fast-querystring: 1.1.2 + safe-regex2: 2.0.0 + dev: true + /find-up/3.0.0: resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} engines: {node: '>=6'} @@ -19235,7 +20479,7 @@ packages: signal-exit: 4.1.0 dev: false - /fork-ts-checker-webpack-plugin/7.3.0_vf4xkga2qinmx3cxkwrybccrqy: + /fork-ts-checker-webpack-plugin/7.3.0_vf3ejk3u7gfag4p4x6gqje5yuq: resolution: {integrity: sha512-IN+XTzusCjR5VgntYFgxbxVx3WraPRnKehBFrf00cMSrtUuW9MsG9dhL6MWpY6MkjC3wVwoujfCDgZZCQwbswA==} engines: {node: '>=12.13.0', yarn: '>=1.0.0'} peerDependencies: @@ -19255,11 +20499,34 @@ packages: memfs: 3.5.3 minimatch: 3.1.2 node-abort-controller: 3.1.1 - schema-utils: 3.1.2 + schema-utils: 3.3.0 semver: 7.5.4 tapable: 2.2.1 typescript: 4.9.4 - webpack: 5.80.0_uhpfu7q6noim4yjdo6qt2aajgu + webpack: 5.88.2_uhpfu7q6noim4yjdo6qt2aajgu + dev: true + + /fork-ts-checker-webpack-plugin/8.0.0_dtthwp2bsqb7yvb7hoeealjg4i: + resolution: {integrity: sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==} + engines: {node: '>=12.13.0', yarn: '>=1.0.0'} + peerDependencies: + typescript: '>3.6.0' + webpack: ^5.11.0 + dependencies: + '@babel/code-frame': 7.22.13 + chalk: 4.1.2 + chokidar: 3.5.3 + cosmiconfig: 7.1.0 + deepmerge: 4.2.2 + fs-extra: 10.1.0 + memfs: 3.5.3 + minimatch: 3.1.2 + node-abort-controller: 3.1.1 + schema-utils: 3.1.2 + semver: 7.5.4 + tapable: 2.2.1 + typescript: 5.2.2 + webpack: 5.88.2 dev: true /form-data-encoder/1.7.2: @@ -19316,6 +20583,15 @@ packages: fetch-blob: 3.2.0 dev: false + /formidable/2.1.2: + resolution: {integrity: sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==} + dependencies: + dezalgo: 1.0.4 + hexoid: 1.0.0 + once: 1.4.0 + qs: 6.11.0 + dev: true + /forwarded/0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -19424,6 +20700,14 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true + dev: true + optional: true + + /fsevents/2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true optional: true /fstream/1.0.12: @@ -19739,6 +21023,16 @@ packages: minimatch: 5.1.2 once: 1.4.0 + /glob/9.3.5: + resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + fs.realpath: 1.0.0 + minimatch: 8.0.4 + minipass: 4.2.8 + path-scurry: 1.10.1 + dev: true + /global-dirs/3.0.1: resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} engines: {node: '>=10'} @@ -19983,6 +21277,11 @@ packages: is-glob: 3.1.0 dev: false + /has-own-prop/2.0.0: + resolution: {integrity: sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==} + engines: {node: '>=8'} + dev: true + /has-property-descriptors/1.0.0: resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} dependencies: @@ -20081,7 +21380,7 @@ packages: /hast-util-to-estree/2.1.0: resolution: {integrity: sha512-Vwch1etMRmm89xGgz+voWXvVHba2iiMdGMKmaMfYt35rbVtFDq8JNwwAIvi8zHMkO6Gvqo9oTMwJTmzVRfXh4g==} dependencies: - '@types/estree': 1.0.0 + '@types/estree': 1.0.2 '@types/estree-jsx': 1.0.0 '@types/hast': 2.3.4 '@types/unist': 2.0.6 @@ -20157,6 +21456,11 @@ packages: xtend: 4.0.2 dev: false + /hexoid/1.0.0: + resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==} + engines: {node: '>=8'} + dev: true + /highlight.run/7.2.0: resolution: {integrity: sha512-vTg0EWiKxHoRO1w9zm+KuPWkOAJN2Mh/8LRfgVqMjrR+sSfdOjuJeKBa9wfmV1GssrGCWNLwnuygDLGIJ4BSCw==} dev: false @@ -20182,10 +21486,6 @@ packages: lru-cache: 7.18.3 dev: false - /hotkeys-js/3.9.4: - resolution: {integrity: sha512-2zuLt85Ta+gIyvs4N88pCYskNrxf1TFv3LR9t5mdAZIX8BcgQQ48F2opUptvHa6m8zsy5v/a0i9mWzTrlNWU0Q==} - dev: false - /hpagent/0.1.2: resolution: {integrity: sha512-ePqFXHtSQWAFXYmj+JtOTHr84iNrII4/QRlAAPPE+zqnKy4xJo7Ie1Y4kC7AdB+LxLxSTTzBMASsEcy0q8YyvQ==} requiresBuild: true @@ -20236,7 +21536,7 @@ packages: /html-void-elements/2.0.1: resolution: {integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==} - /html-webpack-plugin/5.5.1_webpack@5.80.0: + /html-webpack-plugin/5.5.1_webpack@5.88.2: resolution: {integrity: sha512-cTUzZ1+NqjGEKjmVgZKLMdiFg3m9MdRXkZW2OEe69WYVi5ONLMmlnSZdXzGGMOq0C8jGDrL6EWyEDDUioHO/pA==} engines: {node: '>=10.13.0'} peerDependencies: @@ -20247,7 +21547,7 @@ packages: lodash: 4.17.21 pretty-error: 4.0.0 tapable: 2.2.1 - webpack: 5.80.0_uhpfu7q6noim4yjdo6qt2aajgu + webpack: 5.88.2_uhpfu7q6noim4yjdo6qt2aajgu dev: true /htmlparser2/6.1.0: @@ -20348,6 +21648,11 @@ packages: resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} dev: false + /human-signals/1.1.1: + resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} + engines: {node: '>=8.12.0'} + dev: true + /human-signals/2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -20503,6 +21808,27 @@ packages: fast-loops: 1.1.3 dev: false + /inquirer/8.2.4: + resolution: {integrity: sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==} + engines: {node: '>=12.0.0'} + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 7.0.0 + dev: true + /inquirer/8.2.5: resolution: {integrity: sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==} engines: {node: '>=12.0.0'} @@ -20524,6 +21850,27 @@ packages: wrap-ansi: 7.0.0 dev: true + /inquirer/8.2.6: + resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} + engines: {node: '>=12.0.0'} + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 6.2.0 + dev: true + /inquirer/9.1.4: resolution: {integrity: sha512-9hiJxE5gkK/cM2d1mTEnuurGTAoHebbkX0BYl3h7iEg7FYfuNIom+nDfBCSWtvSnoSrWCeBxqqBZu26xdlJlXA==} engines: {node: '>=12.0.0'} @@ -20845,6 +22192,11 @@ packages: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} engines: {node: '>= 0.4'} + /is-network-error/1.0.0: + resolution: {integrity: sha512-P3fxi10Aji2FZmHTrMPSNFbNC6nnp4U5juPAIjXPHkUNubi4+qK7vvdsaNpAUwXslhYm9oyjEYTxs1xd/+Ph0w==} + engines: {node: '>=16'} + dev: false + /is-node-process/1.0.1: resolution: {integrity: sha512-5IcdXuf++TTNt3oGl9EBdkvndXA8gmc4bz/Y+mdEpWh3Mcn/+kOw6hI7LD5CocqJWMzeb0I0ClndRVNdEPuJXQ==} dev: true @@ -21126,6 +22478,10 @@ packages: istanbul-lib-report: 3.0.0 dev: true + /iterare/1.2.1: + resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} + engines: {node: '>=6'} + /jackspeak/2.3.0: resolution: {integrity: sha512-uKmsITSsF4rUWQHzqaRUuyAir3fZfW3f202Ee34lz/gZCi970CPZwyQXLGNgWJvvZbvFyzeyGq0+4fcG/mBKZg==} engines: {node: '>=14'} @@ -21246,7 +22602,7 @@ packages: babel-jest: 29.6.2_@babel+core@7.22.17 chalk: 4.1.2 ci-info: 3.8.0 - deepmerge: 4.2.2 + deepmerge: 4.3.1 glob: 7.2.3 graceful-fs: 4.2.10 jest-circus: 29.6.2 @@ -21286,7 +22642,7 @@ packages: babel-jest: 29.6.2_@babel+core@7.22.17 chalk: 4.1.2 ci-info: 3.8.0 - deepmerge: 4.2.2 + deepmerge: 4.3.1 glob: 7.2.3 graceful-fs: 4.2.10 jest-circus: 29.6.2 @@ -21368,7 +22724,7 @@ packages: micromatch: 4.0.5 walker: 1.0.8 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /jest-haste-map/29.6.2: @@ -21387,7 +22743,7 @@ packages: micromatch: 4.0.5 walker: 1.0.8 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /jest-leak-detector/29.6.2: @@ -21931,6 +23287,10 @@ packages: engines: {node: '>= 8'} dev: true + /known-css-properties/0.28.0: + resolution: {integrity: sha512-9pSL5XB4J+ifHP0e0jmmC98OGC1nL8/JjS+fi6mnTlIf//yt/MfVLtKg7S6nCtj/8KTcWX7nRlY0XywoYY1ISQ==} + dev: true + /language-subtag-registry/0.3.22: resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} @@ -21994,6 +23354,14 @@ packages: prelude-ls: 1.2.1 type-check: 0.4.0 + /light-my-request/5.11.0: + resolution: {integrity: sha512-qkFCeloXCOMpmEdZ/MV91P8AT4fjwFXWaAFz3lUeStM8RcoM1ks4J/F8r1b3r6y/H4u3ACEJ1T+Gv5bopj7oDA==} + dependencies: + cookie: 0.5.0 + process-warning: 2.2.0 + set-cookie-parser: 2.6.0 + dev: true + /lilconfig/2.0.6: resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==} engines: {node: '>=10'} @@ -22096,6 +23464,10 @@ packages: - supports-color dev: false + /locate-character/3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + dev: true + /locate-path/3.0.0: resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} engines: {node: '>=6'} @@ -22225,7 +23597,6 @@ packages: /lru-cache/10.0.1: resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==} engines: {node: 14 || >=16.14} - dev: false /lru-cache/4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} @@ -22266,6 +23637,32 @@ packages: hasBin: true dev: true + /macos-release/2.5.1: + resolution: {integrity: sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==} + engines: {node: '>=6'} + dev: true + + /magic-string/0.27.0: + resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /magic-string/0.30.0: + resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /magic-string/0.30.1: + resolution: {integrity: sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /magic-string/0.30.3: resolution: {integrity: sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==} engines: {node: '>=12'} @@ -22600,6 +23997,10 @@ packages: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} dev: false + /mdn-data/2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + dev: true + /mdurl/1.0.1: resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} dev: true @@ -22908,7 +24309,7 @@ packages: resolution: {integrity: sha512-WWp3bf7xT9MppNuw3yPjpnOxa8cj5ACivEzXJKu0WwnjBYfzaBvIAT9KfeyI0Qkll+bfQtfftSwdgTH6QhTOKw==} dependencies: '@types/acorn': 4.0.6 - '@types/estree': 1.0.0 + '@types/estree': 1.0.2 estree-util-visit: 1.2.0 micromark-util-types: 1.0.2 uvu: 0.5.6 @@ -23083,6 +24484,13 @@ packages: dependencies: brace-expansion: 2.0.1 + /minimatch/8.0.4: + resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimatch/9.0.0: resolution: {integrity: sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==} engines: {node: '>=16 || 14 >=14.17'} @@ -23162,6 +24570,11 @@ packages: engines: {node: '>=8'} dependencies: yallist: 4.0.0 + dev: true + + /minipass/4.2.8: + resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} + engines: {node: '>=8'} /minipass/5.0.0: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} @@ -23171,7 +24584,6 @@ packages: /minipass/7.0.3: resolution: {integrity: sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==} engines: {node: '>=16 || 14 >=14.17'} - dev: false /minizlib/2.1.2: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} @@ -23331,6 +24743,18 @@ packages: - supports-color dev: true + /multer/1.4.4-lts.1: + resolution: {integrity: sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==} + engines: {node: '>= 6.0.0'} + dependencies: + append-field: 1.0.0 + busboy: 1.6.0 + concat-stream: 1.6.2 + mkdirp: 0.5.6 + object-assign: 4.1.1 + type-is: 1.6.18 + xtend: 4.0.2 + /mute-stream/0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} @@ -23629,6 +25053,12 @@ packages: engines: {node: '>=10.5.0'} dev: false + /node-emoji/1.11.0: + resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} + dependencies: + lodash: 4.17.21 + dev: true + /node-fetch-native/1.0.1: resolution: {integrity: sha512-VzW+TAk2wE4X9maiKMlT+GsPU4OMmR1U9CrHSmd3DFLn2IcZ9VJ6M6BBugGfYUnPCLSYxXdZy17M0BEJyhUTwg==} dev: false @@ -23660,19 +25090,6 @@ packages: dependencies: whatwg-url: 5.0.0 - /node-fetch/2.6.12_encoding@0.1.13: - resolution: {integrity: sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - dependencies: - encoding: 0.1.13 - whatwg-url: 5.0.0 - dev: false - /node-fetch/2.6.7: resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} engines: {node: 4.x || >=6.0.0} @@ -24096,6 +25513,11 @@ packages: - encoding dev: false + /on-exit-leak-free/2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + dev: true + /on-finished/2.3.0: resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} engines: {node: '>= 0.8'} @@ -24232,7 +25654,7 @@ packages: bl: 4.1.0 chalk: 4.1.2 cli-cursor: 3.1.0 - cli-spinners: 2.7.0 + cli-spinners: 2.9.1 is-interactive: 1.0.0 is-unicode-supported: 0.1.0 log-symbols: 4.1.0 @@ -24268,6 +25690,14 @@ packages: string-width: 6.1.0 strip-ansi: 7.1.0 + /os-name/4.0.1: + resolution: {integrity: sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==} + engines: {node: '>=10'} + dependencies: + macos-release: 2.5.1 + windows-release: 4.0.0 + dev: true + /os-tmpdir/1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -24395,6 +25825,15 @@ packages: retry: 0.13.1 dev: false + /p-retry/6.1.0: + resolution: {integrity: sha512-fJLEQ2KqYBJRuaA/8cKMnqhulqNM+bpcjYtXNex2t3mOXKRYPitAJt9NacSf8XAFzcYahSAbKpobiWDSqHSh2g==} + engines: {node: '>=16.17'} + dependencies: + '@types/retry': 0.12.2 + is-network-error: 1.0.0 + retry: 0.13.1 + dev: false + /p-timeout/3.2.0: resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} engines: {node: '>=8'} @@ -24612,11 +26051,13 @@ packages: dependencies: lru-cache: 10.0.1 minipass: 7.0.3 - dev: false /path-to-regexp/0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + /path-to-regexp/3.2.0: + resolution: {integrity: sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==} + /path-to-regexp/6.2.1: resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} @@ -24662,6 +26103,14 @@ packages: is-reference: 3.0.1 dev: true + /periscopic/3.1.0: + resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} + dependencies: + '@types/estree': 1.0.0 + estree-walker: 3.0.3 + is-reference: 3.0.1 + dev: true + /pg-connection-string/2.5.0: resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==} dev: false @@ -24748,6 +26197,34 @@ packages: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} + /pino-abstract-transport/1.1.0: + resolution: {integrity: sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==} + dependencies: + readable-stream: 4.4.2 + split2: 4.2.0 + dev: true + + /pino-std-serializers/6.2.2: + resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} + dev: true + + /pino/8.15.6: + resolution: {integrity: sha512-GuxHr61R0ZFD1npu58tB3a3FSVjuy21OwN/haw4OuKiZBL63Pg11Y51WWeD52RENS2mjwPZOwt+2OQOSkck6kQ==} + hasBin: true + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.3.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 1.1.0 + pino-std-serializers: 6.2.2 + process-warning: 2.2.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.4.3 + sonic-boom: 3.6.0 + thread-stream: 2.4.1 + dev: true + /pirates/4.0.5: resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} engines: {node: '>= 6'} @@ -24785,6 +26262,11 @@ packages: hasBin: true dev: true + /pluralize/8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + dev: true + /polished/4.2.2: resolution: {integrity: sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==} engines: {node: '>=10'} @@ -24911,6 +26393,23 @@ packages: yaml: 1.10.2 dev: true + /postcss-load-config/3.1.4_postcss@8.4.29: + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.1.0 + postcss: 8.4.29 + yaml: 1.10.2 + dev: true + /postcss-load-config/4.0.1: resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} engines: {node: '>= 14'} @@ -25135,6 +26634,24 @@ packages: postcss-selector-parser: 6.0.11 dev: false + /postcss-safe-parser/6.0.0_postcss@8.4.29: + resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.3.3 + dependencies: + postcss: 8.4.29 + dev: true + + /postcss-scss/4.0.9_postcss@8.4.29: + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + dependencies: + postcss: 8.4.29 + dev: true + /postcss-selector-parser/6.0.10: resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} engines: {node: '>=4'} @@ -25287,6 +26804,23 @@ packages: engines: {node: '>=4'} dev: true + /prettier-linter-helpers/1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + dependencies: + fast-diff: 1.3.0 + dev: true + + /prettier-plugin-svelte/2.10.1_hfafeyo6vw33o2ufgpsfnrywzu: + resolution: {integrity: sha512-Wlq7Z5v2ueCubWo0TZzKc9XHcm7TDxqcuzRuGd0gcENfzfT4JZ9yDlCbEgxWgiPmLHkBjfOtpAWkcT28MCDpUQ==} + peerDependencies: + prettier: ^1.16.4 || ^2.0.0 + svelte: ^3.2.0 || ^4.0.0-next.0 + dependencies: + prettier: 2.8.8 + svelte: 4.2.1 + dev: true + /prettier-plugin-tailwindcss/0.3.0_prettier@2.8.8: resolution: {integrity: sha512-009/Xqdy7UmkcTBpwlq7jsViDqXAYSOMLDrHAdTMlVZOrKfM2o9Ci7EMWTMZ7SkKBFTG04UM9F9iM2+4i6boDA==} engines: {node: '>=12.17.0'} @@ -25411,13 +26945,13 @@ packages: react: 18.2.0 dev: false - /prisma/4.16.0: - resolution: {integrity: sha512-kSCwbTm3LCephyGfZMJYqBXpPJXdJStg5xwfzeFmR5C05zfkOURK9pQpJF6uUQvFWm3lI9ZMSNkObmFkAPnB+g==} - engines: {node: '>=14.17'} + /prisma/5.4.1: + resolution: {integrity: sha512-op9PmU8Bcw5dNAas82wBYTG0yHnpq9/O3bhxbDBrNzwZTwBqsVCxxYRLf6wHNh9HVaDGhgjjHlu1+BcW8qdnBg==} + engines: {node: '>=16.13'} hasBin: true requiresBuild: true dependencies: - '@prisma/engines': 4.16.0 + '@prisma/engines': 5.4.1 /prismjs/1.29.0: resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} @@ -25431,6 +26965,10 @@ packages: /process-nextick-args/2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + /process-warning/2.2.0: + resolution: {integrity: sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg==} + dev: true + /process/0.10.1: resolution: {integrity: sha512-dyIett8dgGIZ/TXKUzeYExt7WA6ldDzys9vTDU/cCA9L17Ypme+KzS+NjQCjpn9xsvi/shbMC+yP/BcFMBz0NA==} engines: {node: '>= 0.6.0'} @@ -25625,6 +27163,10 @@ packages: resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} optional: true + /quick-format-unescaped/4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + dev: true + /quick-lru/4.0.1: resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} engines: {node: '>=8'} @@ -25657,6 +27199,15 @@ packages: iconv-lite: 0.4.24 unpipe: 1.0.0 + /raw-body/2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + /rc-config-loader/4.1.3: resolution: {integrity: sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==} dependencies: @@ -25774,13 +27325,12 @@ packages: - csstype dev: false - /react-hotkeys-hook/3.4.7_biqbaboplfbrettd7655fr4n2y: - resolution: {integrity: sha512-+bbPmhPAl6ns9VkXkNNyxlmCAIyDAcWbB76O4I0ntr3uWCRuIQf/aRLartUahe9chVMPj+OEzzfk3CQSjclUEQ==} + /react-hotkeys-hook/4.4.1_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-sClBMBioFEgFGYLTWWRKvhxcCx1DRznd+wkFHwQZspnRBkHTgruKIHptlK/U/2DPX8BhHoRGzpMVWUXMmdZlmw==} peerDependencies: react: '>=16.8.1' react-dom: '>=16.8.1' dependencies: - hotkeys-js: 3.9.4 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 dev: false @@ -26064,12 +27614,28 @@ packages: string_decoder: 1.3.0 util-deprecate: 1.0.2 + /readable-stream/4.4.2: + resolution: {integrity: sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + dev: true + /readdirp/3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} dependencies: picomatch: 2.3.1 + /real-require/0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + dev: true + /recast/0.21.5: resolution: {integrity: sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg==} engines: {node: '>= 4'} @@ -26140,6 +27706,9 @@ packages: postcss-value-parser: 3.3.1 dev: false + /reflect-metadata/0.1.13: + resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} + /regenerate-unicode-properties/10.1.0: resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==} engines: {node: '>=4'} @@ -26423,7 +27992,7 @@ packages: react: 18.2.0 dev: false - /remix-utils/6.0.0_c5pntwu5f7mrfmmvuwtiprk4cy: + /remix-utils/6.0.0_7krs2yfztxu2oblf77wwc4rpoe: resolution: {integrity: sha512-S7Xec0YHZxGFEDawWpIbU7HQAZC0j51FmAvcCyBRuxjo71aAIMdmez47dgF8T91yxpHV1xlIKPL7LhBzmYsOZw==} engines: {node: '>=14'} peerDependencies: @@ -26440,7 +28009,7 @@ packages: schema-dts: 1.1.0_typescript@4.9.4 type-fest: 2.19.0 uuid: 8.3.2 - zod: 3.21.4 + zod: 3.22.3 transitivePeerDependencies: - typescript dev: false @@ -26472,6 +28041,10 @@ packages: /repeat-string/1.6.1: resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} engines: {node: '>=0.10'} + + /replicate/0.18.1: + resolution: {integrity: sha512-JFK5qWL7AAajsIjtkW/nTaoX+yKp8zkaANe+pQpBh9RjH5vIy6/tn/sVNlRF1z5bCJLupshJBHFgnHRdWjbKXg==} + engines: {git: '>=2.11.0', node: '>=18.0.0', npm: '>=7.19.0', yarn: '>=1.7.0'} dev: false /require-directory/2.1.1: @@ -26659,6 +28232,11 @@ packages: engines: {node: '>=0.12'} dev: false + /ret/0.2.2: + resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} + engines: {node: '>=4'} + dev: true + /retext-latin/3.1.0: resolution: {integrity: sha512-5MrD1tuebzO8ppsja5eEu+ZbBeUNCjoEarn70tkXOS7Bdsdf6tNahsv2bY0Z8VooFF6cw7/6S+d3yI/TMlMVVQ==} dependencies: @@ -26704,6 +28282,10 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + /rfdc/1.3.0: + resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} + dev: true + /right-align/0.1.3: resolution: {integrity: sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg==} engines: {node: '>=0.10.0'} @@ -26730,6 +28312,14 @@ packages: dependencies: glob: 7.2.3 + /rimraf/4.4.1: + resolution: {integrity: sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==} + engines: {node: '>=14'} + hasBin: true + dependencies: + glob: 9.3.5 + dev: true + /rimraf/5.0.1: resolution: {integrity: sha512-OfFZdwtd3lZ+XZzYP/6gTACubwFcHdLRqS9UX3UwpU2dnGQYkPFISRwvM3w9IiB2w7bW5qGo/uAwE4SmXXSKvg==} engines: {node: '>=14'} @@ -26743,7 +28333,7 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /rollup/3.29.1: @@ -26751,7 +28341,7 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 /rtl-css-js/1.16.1: resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} @@ -26787,7 +28377,6 @@ packages: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: tslib: 2.6.2 - dev: true /sade/1.8.1: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} @@ -26818,9 +28407,29 @@ packages: ret: 0.1.15 dev: false + /safe-regex2/2.0.0: + resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} + dependencies: + ret: 0.2.2 + dev: true + + /safe-stable-stringify/2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} + dev: true + /safer-buffer/2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + /sander/0.5.1: + resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==} + dependencies: + es6-promise: 3.3.1 + graceful-fs: 4.2.10 + mkdirp: 0.5.6 + rimraf: 2.7.1 + dev: true + /sass-loader/13.2.2: resolution: {integrity: sha512-nrIdVAAte3B9icfBiGWvmMhT/D+eCDwnk+yA7VE/76dp/WkHX+i44Q/pfo71NYbwj0Ap+PGsn0ekOuU1WFJ2AA==} engines: {node: '>= 14.15.0'} @@ -26861,7 +28470,16 @@ packages: resolution: {integrity: sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/json-schema': 7.0.11 + '@types/json-schema': 7.0.13 + ajv: 6.12.6 + ajv-keywords: 3.5.2_ajv@6.12.6 + dev: true + + /schema-utils/3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + dependencies: + '@types/json-schema': 7.0.13 ajv: 6.12.6 ajv-keywords: 3.5.2_ajv@6.12.6 dev: true @@ -26870,7 +28488,7 @@ packages: resolution: {integrity: sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==} engines: {node: '>= 12.13.0'} dependencies: - '@types/json-schema': 7.0.11 + '@types/json-schema': 7.0.13 ajv: 8.12.0 ajv-formats: 2.1.1 ajv-keywords: 5.1.0_ajv@8.12.0 @@ -26888,6 +28506,10 @@ packages: extend-shallow: 2.0.1 kind-of: 6.0.3 + /secure-json-parse/2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + dev: true + /selderee/0.10.0: resolution: {integrity: sha512-DEL/RW/f4qLw/NrVg97xKaEBC8IpzIG2fvxnzCp3Z4yk4jQ3MXom+Imav9wApjxX2dfS3eW7x0DXafJr85i39A==} dependencies: @@ -26919,6 +28541,7 @@ packages: /semver/6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} hasBin: true + dev: true /semver/6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} @@ -27006,6 +28629,9 @@ packages: /set-cookie-parser/2.5.1: resolution: {integrity: sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==} + /set-cookie-parser/2.6.0: + resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + /set-harmonic-interval/1.0.1: resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==} engines: {node: '>=6.9'} @@ -27035,8 +28661,8 @@ packages: kind-of: 6.0.3 dev: true - /sharp/0.32.5: - resolution: {integrity: sha512-0dap3iysgDkNaPOaOL4X/0akdu0ma62GcdC2NBQ+93eqpePdDdr2/LM0sFdDSMmN7yS+odyZtPsb7tx/cYBKnQ==} + /sharp/0.32.6: + resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==} engines: {node: '>=14.15.0'} requiresBuild: true dependencies: @@ -27186,10 +28812,23 @@ packages: semver: 7.5.4 dev: true + /simple-wcswidth/1.0.1: + resolution: {integrity: sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg==} + dev: false + /simplur/3.0.1: resolution: {integrity: sha512-bBAoTn75tuKh83opmZ1VoyVoQIsvLCKzSxuasAxbnKofrT8eGyOEIaXSuNfhi/hI160+fwsR7ObcbBpOyzDvXg==} dev: false + /sirv/2.0.3: + resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} + engines: {node: '>= 10'} + dependencies: + '@polka/url': 1.0.0-next.23 + mrmime: 1.0.1 + totalist: 3.0.1 + dev: true + /sisteransi/1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -27239,7 +28878,7 @@ packages: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} dependencies: dot-case: 3.0.4 - tslib: 2.5.0 + tslib: 2.6.2 dev: false /snakecase-keys/5.4.4: @@ -27311,6 +28950,32 @@ packages: ip: 2.0.0 smart-buffer: 4.2.0 + /sonic-boom/3.6.0: + resolution: {integrity: sha512-5Rs7m4IO/mW1WHouC6q6PGJsXO6hSAduwB3ltTsKaDU0Bd7sc5QEUK/jF0YL583g3BG7QV0Dg0rQNZrwZhY6Xg==} + dependencies: + atomic-sleep: 1.0.0 + dev: true + + /sonner/1.0.3_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-hBoA2zKuYW3lUnpx4K0vAn8j77YuYiwvP9sLQfieNS2pd5FkT20sMyPTDJnl9S+5T27ZJbwQRPiujwvDBwhZQg==} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + dependencies: + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /sorcery/0.11.0: + resolution: {integrity: sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==} + hasBin: true + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + buffer-crc32: 0.2.13 + minimist: 1.2.7 + sander: 0.5.1 + dev: true + /sort-object-keys/1.1.3: resolution: {integrity: sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==} dev: true @@ -27442,7 +29107,6 @@ packages: /split2/4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} - dev: false /sprintf-js/1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -27793,13 +29457,13 @@ packages: webpack: ^5.0.0 dev: true - /style-loader/3.3.2_webpack@5.80.0: + /style-loader/3.3.2_webpack@5.88.2: resolution: {integrity: sha512-RHs/vcrKdQK8wZliteNK4NKzxvLBzpuHMqYmUVWeKa6MkaIQ97ZTOS0b+zapZhy6GcrgWnvWYCMHRirC3FsUmw==} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 dependencies: - webpack: 5.80.0_uhpfu7q6noim4yjdo6qt2aajgu + webpack: 5.88.2_uhpfu7q6noim4yjdo6qt2aajgu dev: true /style-mod/4.0.0: @@ -27882,6 +29546,34 @@ packages: openapi-fetch: 0.6.2 dev: false + /superagent/8.1.2: + resolution: {integrity: sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==} + engines: {node: '>=6.4.0 <13 || >=14'} + dependencies: + component-emitter: 1.3.0 + cookiejar: 2.1.4 + debug: 4.3.4 + fast-safe-stringify: 2.1.1 + form-data: 4.0.0 + formidable: 2.1.2 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.11.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + dev: true + + /supertest/6.3.3: + resolution: {integrity: sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==} + engines: {node: '>=6.4.0'} + dependencies: + methods: 1.1.2 + superagent: 8.1.2 + transitivePeerDependencies: + - supports-color + dev: true + /supports-color/5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -27913,6 +29605,137 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + /svelte-check/3.5.2_svelte@4.2.1: + resolution: {integrity: sha512-5a/YWbiH4c+AqAUP+0VneiV5bP8YOk9JL3jwvN+k2PEPLgpu85bjQc5eE67+eIZBBwUEJzmO3I92OqKcqbp3fw==} + hasBin: true + peerDependencies: + svelte: ^3.55.0 || ^4.0.0-next.0 || ^4.0.0 + dependencies: + '@jridgewell/trace-mapping': 0.3.19 + chokidar: 3.5.3 + fast-glob: 3.3.1 + import-fresh: 3.3.0 + picocolors: 1.0.0 + sade: 1.8.1 + svelte: 4.2.1 + svelte-preprocess: 5.0.4_ihwjmfflvgyqta4xkfhaggvtwe + typescript: 5.2.2 + transitivePeerDependencies: + - '@babel/core' + - coffeescript + - less + - postcss + - postcss-load-config + - pug + - sass + - stylus + - sugarss + dev: true + + /svelte-eslint-parser/0.33.1_svelte@4.2.1: + resolution: {integrity: sha512-vo7xPGTlKBGdLH8T5L64FipvTrqv3OQRx9d2z5X05KKZDlF4rQk8KViZO4flKERY+5BiVdOh7zZ7JGJWo5P0uA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + svelte: ^3.37.0 || ^4.0.0 + peerDependenciesMeta: + svelte: + optional: true + dependencies: + eslint-scope: 7.2.0 + eslint-visitor-keys: 3.4.2 + espree: 9.6.0 + postcss: 8.4.29 + postcss-scss: 4.0.9_postcss@8.4.29 + svelte: 4.2.1 + dev: true + + /svelte-hmr/0.15.3: + resolution: {integrity: sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==} + engines: {node: ^12.20 || ^14.13.1 || >= 16} + peerDependencies: + svelte: ^3.19.0 || ^4.0.0 + dev: true + + /svelte-hmr/0.15.3_svelte@4.2.1: + resolution: {integrity: sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==} + engines: {node: ^12.20 || ^14.13.1 || >= 16} + peerDependencies: + svelte: ^3.19.0 || ^4.0.0 + dependencies: + svelte: 4.2.1 + dev: true + + /svelte-preprocess/5.0.4_ihwjmfflvgyqta4xkfhaggvtwe: + resolution: {integrity: sha512-ABia2QegosxOGsVlsSBJvoWeXy1wUKSfF7SWJdTjLAbx/Y3SrVevvvbFNQqrSJw89+lNSsM58SipmZJ5SRi5iw==} + engines: {node: '>= 14.10.0'} + requiresBuild: true + peerDependencies: + '@babel/core': ^7.10.2 + coffeescript: ^2.5.1 + less: ^3.11.3 || ^4.0.0 + postcss: ^7 || ^8 + postcss-load-config: ^2.1.0 || ^3.0.0 || ^4.0.0 + pug: ^3.0.0 + sass: ^1.26.8 + stylus: ^0.55.0 + sugarss: ^2.0.0 || ^3.0.0 || ^4.0.0 + svelte: ^3.23.0 || ^4.0.0-next.0 || ^4.0.0 + typescript: '>=3.9.5 || ^4.0.0 || ^5.0.0' + peerDependenciesMeta: + '@babel/core': + optional: true + coffeescript: + optional: true + less: + optional: true + postcss: + optional: true + postcss-load-config: + optional: true + pug: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + typescript: + optional: true + dependencies: + '@types/pug': 2.0.7 + detect-indent: 6.1.0 + magic-string: 0.27.0 + sorcery: 0.11.0 + strip-indent: 3.0.0 + svelte: 4.2.1 + typescript: 5.2.2 + dev: true + + /svelte/4.2.1: + resolution: {integrity: sha512-LpLqY2Jr7cRxkrTc796/AaaoMLF/1ax7cto8Ot76wrvKQhrPmZ0JgajiWPmg9mTSDqO16SSLiD17r9MsvAPTmw==} + engines: {node: '>=16'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.19 + acorn: 8.10.0 + aria-query: 5.3.0 + axobject-query: 3.2.1 + code-red: 1.0.4 + css-tree: 2.3.1 + estree-walker: 3.0.3 + is-reference: 3.0.1 + locate-character: 3.0.0 + magic-string: 0.30.3 + periscopic: 3.1.0 + dev: true + + /symbol-observable/4.0.0: + resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} + engines: {node: '>=0.10'} + dev: true + /synchronous-promise/2.0.17: resolution: {integrity: sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g==} dev: true @@ -28088,7 +29911,7 @@ packages: dependencies: chownr: 2.0.0 fs-minipass: 2.1.0 - minipass: 4.0.0 + minipass: 4.2.8 minizlib: 2.1.2 mkdirp: 1.0.4 yallist: 4.0.0 @@ -28135,7 +29958,7 @@ packages: supports-hyperlinks: 2.3.0 dev: false - /terser-webpack-plugin/5.3.7_5jgfnkl7fjuhakmzbjzotue6o4: + /terser-webpack-plugin/5.3.7_nww33inhqu3uc3cp573wawoccu: resolution: {integrity: sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -28155,10 +29978,34 @@ packages: '@swc/core': 1.3.26 esbuild: 0.15.18 jest-worker: 27.5.1 - schema-utils: 3.1.2 + schema-utils: 3.3.0 serialize-javascript: 6.0.1 terser: 5.17.1 - webpack: 5.80.0_uhpfu7q6noim4yjdo6qt2aajgu + webpack: 5.88.2_uhpfu7q6noim4yjdo6qt2aajgu + dev: true + + /terser-webpack-plugin/5.3.7_webpack@5.88.2: + resolution: {integrity: sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + dependencies: + '@jridgewell/trace-mapping': 0.3.19 + jest-worker: 27.5.1 + schema-utils: 3.3.0 + serialize-javascript: 6.0.1 + terser: 5.17.1 + webpack: 5.88.2 dev: true /terser/5.17.1: @@ -28195,6 +30042,12 @@ packages: dependencies: any-promise: 1.3.0 + /thread-stream/2.4.1: + resolution: {integrity: sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==} + dependencies: + real-require: 0.2.0 + dev: true + /thriftrw/3.11.4: resolution: {integrity: sha512-UcuBd3eanB3T10nXWRRMwfwoaC6VMk7qe3/5YIWP2Jtw+EbHqJ0p1/K3x8ixiR5dozKSSfcg1W+0e33G1Di3XA==} engines: {node: '>= 0.10.x'} @@ -28316,6 +30169,11 @@ packages: safe-regex: 1.1.0 dev: false + /toad-cache/3.3.0: + resolution: {integrity: sha512-3oDzcogWGHZdkwrHyvJVpPjA7oNzY6ENOV3PsWJY9XYPZ6INo94Yd47s5may1U+nleBPwDhrRiTPMIvKaa3MQg==} + engines: {node: '>=12'} + dev: true + /toggle-selection/1.0.6: resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} dev: false @@ -28328,6 +30186,11 @@ packages: resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} dev: true + /totalist/3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + dev: true + /touch/3.1.0: resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==} hasBin: true @@ -28373,6 +30236,15 @@ packages: /trough/2.1.0: resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==} + /ts-api-utils/1.0.3_typescript@5.2.2: + resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} + engines: {node: '>=16.13.0'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.2.2 + dev: true + /ts-dedent/2.2.0: resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} engines: {node: '>=6.10'} @@ -28432,6 +30304,20 @@ packages: typescript: 5.0.4 dev: true + /ts-loader/9.4.4_typescript@5.2.2: + resolution: {integrity: sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==} + engines: {node: '>=12.0.0'} + peerDependencies: + typescript: '*' + webpack: ^5.0.0 + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.13.0 + micromatch: 4.0.5 + semver: 7.5.4 + typescript: 5.2.2 + dev: true + /ts-node/10.9.1_fodzh64fuekdilycyvke2qmf2e: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true @@ -28494,6 +30380,37 @@ packages: yn: 3.1.1 dev: true + /ts-node/10.9.1_kpuv3buz4xyqturyqxj2gejvma: + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.3 + '@types/node': 20.6.0 + acorn: 8.10.0 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.2.2 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + /ts-node/10.9.1_xj5cs2fmhcigm4w5bhhtewqeja: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true @@ -28553,6 +30470,15 @@ packages: typescript: 4.9.5 dev: false + /tsconfig-paths-webpack-plugin/4.1.0: + resolution: {integrity: sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==} + engines: {node: '>=10.13.0'} + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.13.0 + tsconfig-paths: 4.2.0 + dev: true + /tsconfig-paths/3.14.1: resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==} dependencies: @@ -28570,6 +30496,15 @@ packages: strip-bom: 3.0.0 dev: true + /tsconfig-paths/4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + dependencies: + json5: 2.2.3 + minimist: 1.2.7 + strip-bom: 3.0.0 + dev: true + /tsconfig-resolver/3.0.1: resolution: {integrity: sha512-ZHqlstlQF449v8glscGRXzL6l2dZvASPCdXJRWG4gHEZlUVx2Jtmr+a2zeVG4LCsKhDXKRj5R3h0C/98UcVAQg==} dependencies: @@ -28856,6 +30791,16 @@ packages: tslib: 1.14.1 typescript: 5.1.6 + /tsutils/3.21.0_typescript@5.2.2: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 5.2.2 + dev: true + /tsx/3.12.2: resolution: {integrity: sha512-ykAEkoBg30RXxeOMVeZwar+JH632dZn9EUJVyJwhfag62k6UO/dIyJEV58YuLF6e5BTdV/qmbQrpkWqjq9cUnQ==} hasBin: true @@ -28864,7 +30809,7 @@ packages: '@esbuild-kit/core-utils': 3.0.0 '@esbuild-kit/esm-loader': 2.5.4 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /tty-table/4.1.6: @@ -29056,7 +31001,6 @@ packages: /typedarray/0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - dev: true /typescript/4.9.4: resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==} @@ -29095,6 +31039,12 @@ packages: dev: true optional: true + /uid/2.0.2: + resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} + engines: {node: '>=8'} + dependencies: + '@lukeed/csprng': 1.1.0 + /ulid/2.3.0: resolution: {integrity: sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==} hasBin: true @@ -29118,6 +31068,12 @@ packages: dependencies: busboy: 1.6.0 + /undici/5.25.4: + resolution: {integrity: sha512-450yJxT29qKMf3aoudzFpIciqpx6Pji3hEWaXqXmanbXF58LTAGCKxcJjxMXWu3iG+Mudgo3ZUfDB6YDFd/dAw==} + engines: {node: '>=14.0'} + dependencies: + '@fastify/busboy': 2.0.0 + /unfetch/4.2.0: resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} @@ -29752,73 +31708,6 @@ packages: - typescript dev: true - /vite/4.1.4: - resolution: {integrity: sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==} - engines: {node: ^14.18.0 || >=16.0.0} - hasBin: true - peerDependencies: - '@types/node': '>= 14' - less: '*' - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - dependencies: - esbuild: 0.16.17 - postcss: 8.4.29 - resolve: 1.22.1 - rollup: 3.10.0 - optionalDependencies: - fsevents: 2.3.2 - dev: true - - /vite/4.1.4_@types+node@18.11.18: - resolution: {integrity: sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==} - engines: {node: ^14.18.0 || >=16.0.0} - hasBin: true - peerDependencies: - '@types/node': '>= 14' - less: '*' - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - dependencies: - '@types/node': 18.11.18 - esbuild: 0.16.17 - postcss: 8.4.29 - resolve: 1.22.1 - rollup: 3.10.0 - optionalDependencies: - fsevents: 2.3.2 - dev: true - /vite/4.1.4_@types+node@18.17.1: resolution: {integrity: sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==} engines: {node: ^14.18.0 || >=16.0.0} @@ -29850,7 +31739,7 @@ packages: resolve: 1.22.1 rollup: 3.10.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /vite/4.1.4_@types+node@20.6.0: @@ -29884,7 +31773,7 @@ packages: resolve: 1.22.1 rollup: 3.10.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /vite/4.4.9: @@ -29919,7 +31808,7 @@ packages: postcss: 8.4.29 rollup: 3.29.1 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 /vite/4.4.9_@types+node@18.11.18: resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==} @@ -29954,7 +31843,7 @@ packages: postcss: 8.4.29 rollup: 3.29.1 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /vite/4.4.9_@types+node@20.6.0: @@ -29990,7 +31879,16 @@ packages: postcss: 8.4.29 rollup: 3.29.1 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 + + /vitefu/0.2.4: + resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 + peerDependenciesMeta: + vite: + optional: true + dev: true /vitefu/0.2.4_vite@4.4.9: resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} @@ -30200,7 +32098,7 @@ packages: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} dev: true - /webpack-dev-middleware/5.3.3_webpack@5.80.0: + /webpack-dev-middleware/5.3.3_webpack@5.88.2: resolution: {integrity: sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==} engines: {node: '>= 12.13.0'} peerDependencies: @@ -30211,7 +32109,7 @@ packages: mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.0.1 - webpack: 5.80.0_uhpfu7q6noim4yjdo6qt2aajgu + webpack: 5.88.2_uhpfu7q6noim4yjdo6qt2aajgu dev: true /webpack-hot-middleware/2.25.3: @@ -30222,6 +32120,11 @@ packages: strip-ansi: 6.0.1 dev: true + /webpack-node-externals/3.0.0: + resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} + engines: {node: '>=6'} + dev: true + /webpack-sources/3.2.3: resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} engines: {node: '>=10.13.0'} @@ -30231,8 +32134,8 @@ packages: resolution: {integrity: sha512-5tyDlKLqPfMqjT3Q9TAqf2YqjwmnUleZwzJi1A5qXnlBCdj2AtOJ6wAWdglTIDOPgOiOrXeBeFcsQ8+aGQ6QbA==} dev: true - /webpack/5.80.0_uhpfu7q6noim4yjdo6qt2aajgu: - resolution: {integrity: sha512-OIMiq37XK1rWO8mH9ssfFKZsXg4n6klTEDL7S8/HqbAOBBaiy8ABvXvz0dDCXeEF9gqwxSvVk611zFPjS8hJxA==} + /webpack/5.88.2: + resolution: {integrity: sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -30242,15 +32145,15 @@ packages: optional: true dependencies: '@types/eslint-scope': 3.7.4 - '@types/estree': 1.0.0 + '@types/estree': 1.0.2 '@webassemblyjs/ast': 1.11.5 '@webassemblyjs/wasm-edit': 1.11.5 '@webassemblyjs/wasm-parser': 1.11.5 acorn: 8.10.0 - acorn-import-assertions: 1.8.0_acorn@8.10.0 + acorn-import-assertions: 1.9.0_acorn@8.10.0 browserslist: 4.21.10 chrome-trace-event: 1.0.3 - enhanced-resolve: 5.13.0 + enhanced-resolve: 5.15.0 es-module-lexer: 1.3.1 eslint-scope: 5.1.1 events: 3.3.0 @@ -30260,9 +32163,49 @@ packages: loader-runner: 4.3.0 mime-types: 2.1.35 neo-async: 2.6.2 - schema-utils: 3.1.2 + schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.7_5jgfnkl7fjuhakmzbjzotue6o4 + terser-webpack-plugin: 5.3.7_webpack@5.88.2 + watchpack: 2.4.0 + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + dev: true + + /webpack/5.88.2_uhpfu7q6noim4yjdo6qt2aajgu: + resolution: {integrity: sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + dependencies: + '@types/eslint-scope': 3.7.4 + '@types/estree': 1.0.2 + '@webassemblyjs/ast': 1.11.5 + '@webassemblyjs/wasm-edit': 1.11.5 + '@webassemblyjs/wasm-parser': 1.11.5 + acorn: 8.10.0 + acorn-import-assertions: 1.9.0_acorn@8.10.0 + browserslist: 4.21.10 + chrome-trace-event: 1.0.3 + enhanced-resolve: 5.15.0 + es-module-lexer: 1.3.1 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.10 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.7_nww33inhqu3uc3cp573wawoccu watchpack: 2.4.0 webpack-sources: 3.2.3 transitivePeerDependencies: @@ -30396,6 +32339,13 @@ packages: dependencies: string-width: 5.1.2 + /windows-release/4.0.0: + resolution: {integrity: sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==} + engines: {node: '>=10'} + dependencies: + execa: 4.1.0 + dev: true + /word-wrap/1.2.3: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} engines: {node: '>=0.10.0'} @@ -30415,7 +32365,6 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: false /wrap-ansi/7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} @@ -30679,7 +32628,16 @@ packages: /zod-error/1.5.0: resolution: {integrity: sha512-zzopKZ/skI9iXpqCEPj+iLCKl9b88E43ehcU+sbRoHuwGd9F1IDVGQ70TyO6kmfiRL1g4IXkjsXK+g1gLYl4WQ==} dependencies: - zod: 3.21.4 + zod: 3.22.3 + dev: false + + /zod-validation-error/1.5.0_zod@3.22.3: + resolution: {integrity: sha512-/7eFkAI4qV0tcxMBB/3+d2c1P6jzzZYdYSlBuAklzMuCrJu5bzJfHS0yVAS87dRHVlhftd6RFJDIvv03JgkSbw==} + engines: {node: '>=16.0.0'} + peerDependencies: + zod: ^3.18.0 + dependencies: + zod: 3.22.3 dev: false /zod/3.21.1: @@ -30687,6 +32645,14 @@ packages: /zod/3.21.4: resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} + dev: false + + /zod/3.22.3: + resolution: {integrity: sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==} + + /zod/3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + dev: false /zwitch/2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} diff --git a/references/deno-reference/.vscode/settings.json b/references/deno-reference/.vscode/settings.json new file mode 100644 index 000000000..b943dbc7a --- /dev/null +++ b/references/deno-reference/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "deno.enable": true +} \ No newline at end of file diff --git a/references/deno-reference/deno.json b/references/deno-reference/deno.json new file mode 100644 index 000000000..26c4ebaee --- /dev/null +++ b/references/deno-reference/deno.json @@ -0,0 +1,8 @@ +{ + "trigger.dev": { + "endpointId": "borderless" + }, + "tasks": { + "dev": "deno run --watch main.ts" + } +} diff --git a/references/deno-reference/deno.lock b/references/deno-reference/deno.lock new file mode 100644 index 000000000..0d0405f0d --- /dev/null +++ b/references/deno-reference/deno.lock @@ -0,0 +1,684 @@ +{ + "version": "3", + "packages": { + "specifiers": { + "npm:@trigger.dev/express": "npm:@trigger.dev/express@2.1.7_@trigger.dev+sdk@2.1.7", + "npm:@trigger.dev/sdk": "npm:@trigger.dev/sdk@2.1.7" + }, + "npm": { + "@remix-run/web-blob@3.1.0": { + "integrity": "sha512-owGzFLbqPH9PlKb8KvpNJ0NO74HWE2euAn61eEiyCXX/oteoVzTVSN8mpLgDjaxBf2btj5/nUllSUgpyd6IH6g==", + "dependencies": { + "@remix-run/web-stream": "@remix-run/web-stream@1.1.0", + "web-encoding": "web-encoding@1.1.5" + } + }, + "@remix-run/web-fetch@4.4.1": { + "integrity": "sha512-xMceEGn2kvfeWS91nHSOhEQHPGgjFnmDVpWFZrbWPVdiTByMZIn421/tdSF6Kd1RsNsY+5Iwt3JFEKZHAcMQHw==", + "dependencies": { + "@remix-run/web-blob": "@remix-run/web-blob@3.1.0", + "@remix-run/web-file": "@remix-run/web-file@3.1.0", + "@remix-run/web-form-data": "@remix-run/web-form-data@3.1.0", + "@remix-run/web-stream": "@remix-run/web-stream@1.1.0", + "@web3-storage/multipart-parser": "@web3-storage/multipart-parser@1.0.0", + "abort-controller": "abort-controller@3.0.0", + "data-uri-to-buffer": "data-uri-to-buffer@3.0.1", + "mrmime": "mrmime@1.0.1" + } + }, + "@remix-run/web-file@3.1.0": { + "integrity": "sha512-dW2MNGwoiEYhlspOAXFBasmLeYshyAyhIdrlXBi06Duex5tDr3ut2LFKVj7tyHLmn8nnNwFf1BjNbkQpygC2aQ==", + "dependencies": { + "@remix-run/web-blob": "@remix-run/web-blob@3.1.0" + } + }, + "@remix-run/web-form-data@3.1.0": { + "integrity": "sha512-NdeohLMdrb+pHxMQ/Geuzdp0eqPbea+Ieo8M8Jx2lGC6TBHsgHzYcBvr0LyPdPVycNRDEpWpiDdCOdCryo3f9A==", + "dependencies": { + "web-encoding": "web-encoding@1.1.5" + } + }, + "@remix-run/web-stream@1.1.0": { + "integrity": "sha512-KRJtwrjRV5Bb+pM7zxcTJkhIqWWSy+MYsIxHK+0m5atcznsf15YwUBWHWulZerV2+vvHH1Lp1DD7pw6qKW8SgA==", + "dependencies": { + "web-streams-polyfill": "web-streams-polyfill@3.2.1" + } + }, + "@trigger.dev/core@2.1.7": { + "integrity": "sha512-Ts0xMFiWi4ph4da6BIbuegz2mMSVYJxWEh5S2mhM5vc+S6cl+MEbTdh+neUVa/0N4qq3Io07e9yDTXnTfiifzg==", + "dependencies": { + "ulid": "ulid@2.3.0", + "zod": "zod@3.21.4", + "zod-error": "zod-error@1.5.0" + } + }, + "@trigger.dev/express@2.1.7_@trigger.dev+sdk@2.1.7": { + "integrity": "sha512-uMoSDpOZJdYs+UXwEkhWYCYm8T6e+cu33LXmXhoF2tpm7m8PritFgR7LPX4H9UdIddRATdsmWgOqPMOQneEtDg==", + "dependencies": { + "@remix-run/web-fetch": "@remix-run/web-fetch@4.4.1", + "@trigger.dev/sdk": "@trigger.dev/sdk@2.1.7", + "debug": "debug@4.3.4", + "express": "express@4.18.2" + } + }, + "@trigger.dev/sdk@2.1.7": { + "integrity": "sha512-t3pbXj6+I38a5LNGJg9Ed09NCt0AWnw7hJtUCZyuEfBQulRnVSekNLsxCyGlgiTPiyRvbyefzPrX1GhXh0MY1w==", + "dependencies": { + "@trigger.dev/core": "@trigger.dev/core@2.1.7", + "chalk": "chalk@5.3.0", + "cronstrue": "cronstrue@2.32.0", + "debug": "debug@4.3.4", + "evt": "evt@2.5.3", + "get-caller-file": "get-caller-file@2.0.5", + "git-remote-origin-url": "git-remote-origin-url@4.0.0", + "git-repo-info": "git-repo-info@2.1.1", + "node-fetch": "node-fetch@2.6.13", + "slug": "slug@6.1.0", + "terminal-link": "terminal-link@3.0.0", + "ulid": "ulid@2.3.0", + "uuid": "uuid@9.0.1", + "ws": "ws@8.13.0", + "zod": "zod@3.21.4" + } + }, + "@web3-storage/multipart-parser@1.0.0": { + "integrity": "sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==", + "dependencies": {} + }, + "@zxing/text-encoding@0.9.0": { + "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", + "dependencies": {} + }, + "abort-controller@3.0.0": { + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "event-target-shim@5.0.1" + } + }, + "accepts@1.3.8": { + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "mime-types@2.1.35", + "negotiator": "negotiator@0.6.3" + } + }, + "ansi-escapes@5.0.0": { + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "dependencies": { + "type-fest": "type-fest@1.4.0" + } + }, + "array-flatten@1.1.1": { + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dependencies": {} + }, + "available-typed-arrays@1.0.5": { + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dependencies": {} + }, + "body-parser@1.20.1": { + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "bytes@3.1.2", + "content-type": "content-type@1.0.5", + "debug": "debug@2.6.9", + "depd": "depd@2.0.0", + "destroy": "destroy@1.2.0", + "http-errors": "http-errors@2.0.0", + "iconv-lite": "iconv-lite@0.4.24", + "on-finished": "on-finished@2.4.1", + "qs": "qs@6.11.0", + "raw-body": "raw-body@2.5.1", + "type-is": "type-is@1.6.18", + "unpipe": "unpipe@1.0.0" + } + }, + "bytes@3.1.2": { + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dependencies": {} + }, + "call-bind@1.0.2": { + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "function-bind@1.1.1", + "get-intrinsic": "get-intrinsic@1.2.1" + } + }, + "chalk@5.3.0": { + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dependencies": {} + }, + "content-disposition@0.5.4": { + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "safe-buffer@5.2.1" + } + }, + "content-type@1.0.5": { + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dependencies": {} + }, + "cookie-signature@1.0.6": { + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dependencies": {} + }, + "cookie@0.5.0": { + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dependencies": {} + }, + "cronstrue@2.32.0": { + "integrity": "sha512-dmNflOCNJL6lZEj0dp2YhGIPY83VTjFue6d9feFhnNtrER6mAjBrUvSgK95j3IB/xNGpLjaZDIDG6ACKTZr9Yw==", + "dependencies": {} + }, + "data-uri-to-buffer@3.0.1": { + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", + "dependencies": {} + }, + "debug@2.6.9": { + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "ms@2.0.0" + } + }, + "debug@4.3.4": { + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "ms@2.1.2" + } + }, + "depd@2.0.0": { + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dependencies": {} + }, + "destroy@1.2.0": { + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dependencies": {} + }, + "ee-first@1.1.1": { + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dependencies": {} + }, + "encodeurl@1.0.2": { + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dependencies": {} + }, + "escape-html@1.0.3": { + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dependencies": {} + }, + "etag@1.8.1": { + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dependencies": {} + }, + "event-target-shim@5.0.1": { + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dependencies": {} + }, + "evt@2.5.3": { + "integrity": "sha512-wZKx0JgXaTOVOXI2saNVxINU6VToOHDowMwb3NRcU6l+C59eW3w9dZgNxjokiM8rvMgc7/11yFG0cSDxn4qxgA==", + "dependencies": { + "minimal-polyfills": "minimal-polyfills@2.2.3", + "run-exclusive": "run-exclusive@2.2.19", + "tsafe": "tsafe@1.6.5" + } + }, + "express@4.18.2": { + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "accepts@1.3.8", + "array-flatten": "array-flatten@1.1.1", + "body-parser": "body-parser@1.20.1", + "content-disposition": "content-disposition@0.5.4", + "content-type": "content-type@1.0.5", + "cookie": "cookie@0.5.0", + "cookie-signature": "cookie-signature@1.0.6", + "debug": "debug@2.6.9", + "depd": "depd@2.0.0", + "encodeurl": "encodeurl@1.0.2", + "escape-html": "escape-html@1.0.3", + "etag": "etag@1.8.1", + "finalhandler": "finalhandler@1.2.0", + "fresh": "fresh@0.5.2", + "http-errors": "http-errors@2.0.0", + "merge-descriptors": "merge-descriptors@1.0.1", + "methods": "methods@1.1.2", + "on-finished": "on-finished@2.4.1", + "parseurl": "parseurl@1.3.3", + "path-to-regexp": "path-to-regexp@0.1.7", + "proxy-addr": "proxy-addr@2.0.7", + "qs": "qs@6.11.0", + "range-parser": "range-parser@1.2.1", + "safe-buffer": "safe-buffer@5.2.1", + "send": "send@0.18.0", + "serve-static": "serve-static@1.15.0", + "setprototypeof": "setprototypeof@1.2.0", + "statuses": "statuses@2.0.1", + "type-is": "type-is@1.6.18", + "utils-merge": "utils-merge@1.0.1", + "vary": "vary@1.1.2" + } + }, + "finalhandler@1.2.0": { + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "debug@2.6.9", + "encodeurl": "encodeurl@1.0.2", + "escape-html": "escape-html@1.0.3", + "on-finished": "on-finished@2.4.1", + "parseurl": "parseurl@1.3.3", + "statuses": "statuses@2.0.1", + "unpipe": "unpipe@1.0.0" + } + }, + "for-each@0.3.3": { + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "is-callable@1.2.7" + } + }, + "forwarded@0.2.0": { + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dependencies": {} + }, + "fresh@0.5.2": { + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dependencies": {} + }, + "function-bind@1.1.1": { + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dependencies": {} + }, + "get-caller-file@2.0.5": { + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dependencies": {} + }, + "get-intrinsic@1.2.1": { + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dependencies": { + "function-bind": "function-bind@1.1.1", + "has": "has@1.0.3", + "has-proto": "has-proto@1.0.1", + "has-symbols": "has-symbols@1.0.3" + } + }, + "git-remote-origin-url@4.0.0": { + "integrity": "sha512-EAxDksNdjuWgmVW9pVvA9jQDi/dmTaiDONktIy7qiRRhBZUI4FQK1YvBvteuTSX24aNKg9lfgxNYJEeeSXe6DA==", + "dependencies": { + "gitconfiglocal": "gitconfiglocal@2.1.0" + } + }, + "git-repo-info@2.1.1": { + "integrity": "sha512-8aCohiDo4jwjOwma4FmYFd3i97urZulL8XL24nIPxuE+GZnfsAyy/g2Shqx6OjUiFKUXZM+Yy+KHnOmmA3FVcg==", + "dependencies": {} + }, + "gitconfiglocal@2.1.0": { + "integrity": "sha512-qoerOEliJn3z+Zyn1HW2F6eoYJqKwS6MgC9cztTLUB/xLWX8gD/6T60pKn4+t/d6tP7JlybI7Z3z+I572CR/Vg==", + "dependencies": { + "ini": "ini@1.3.8" + } + }, + "gopd@1.0.1": { + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "get-intrinsic@1.2.1" + } + }, + "has-flag@4.0.0": { + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dependencies": {} + }, + "has-proto@1.0.1": { + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dependencies": {} + }, + "has-symbols@1.0.3": { + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dependencies": {} + }, + "has-tostringtag@1.0.0": { + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "has-symbols@1.0.3" + } + }, + "has@1.0.3": { + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "function-bind@1.1.1" + } + }, + "http-errors@2.0.0": { + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "depd@2.0.0", + "inherits": "inherits@2.0.4", + "setprototypeof": "setprototypeof@1.2.0", + "statuses": "statuses@2.0.1", + "toidentifier": "toidentifier@1.0.1" + } + }, + "iconv-lite@0.4.24": { + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": "safer-buffer@2.1.2" + } + }, + "inherits@2.0.4": { + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dependencies": {} + }, + "ini@1.3.8": { + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dependencies": {} + }, + "ipaddr.js@1.9.1": { + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dependencies": {} + }, + "is-arguments@1.1.1": { + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "call-bind@1.0.2", + "has-tostringtag": "has-tostringtag@1.0.0" + } + }, + "is-callable@1.2.7": { + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dependencies": {} + }, + "is-generator-function@1.0.10": { + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "has-tostringtag@1.0.0" + } + }, + "is-typed-array@1.1.12": { + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dependencies": { + "which-typed-array": "which-typed-array@1.1.11" + } + }, + "media-typer@0.3.0": { + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dependencies": {} + }, + "merge-descriptors@1.0.1": { + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dependencies": {} + }, + "methods@1.1.2": { + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dependencies": {} + }, + "mime-db@1.52.0": { + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dependencies": {} + }, + "mime-types@2.1.35": { + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "mime-db@1.52.0" + } + }, + "mime@1.6.0": { + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dependencies": {} + }, + "minimal-polyfills@2.2.3": { + "integrity": "sha512-oxdmJ9cL+xV72h0xYxp4tP2d5/fTBpP45H8DIOn9pASuF8a3IYTf+25fMGDYGiWW+MFsuog6KD6nfmhZJQ+uUw==", + "dependencies": {} + }, + "mrmime@1.0.1": { + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "dependencies": {} + }, + "ms@2.0.0": { + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dependencies": {} + }, + "ms@2.1.2": { + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dependencies": {} + }, + "ms@2.1.3": { + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dependencies": {} + }, + "negotiator@0.6.3": { + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dependencies": {} + }, + "node-fetch@2.6.13": { + "integrity": "sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==", + "dependencies": { + "whatwg-url": "whatwg-url@5.0.0" + } + }, + "object-inspect@1.12.3": { + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dependencies": {} + }, + "on-finished@2.4.1": { + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "ee-first@1.1.1" + } + }, + "parseurl@1.3.3": { + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dependencies": {} + }, + "path-to-regexp@0.1.7": { + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dependencies": {} + }, + "proxy-addr@2.0.7": { + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "forwarded@0.2.0", + "ipaddr.js": "ipaddr.js@1.9.1" + } + }, + "qs@6.11.0": { + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "side-channel@1.0.4" + } + }, + "range-parser@1.2.1": { + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dependencies": {} + }, + "raw-body@2.5.1": { + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "bytes@3.1.2", + "http-errors": "http-errors@2.0.0", + "iconv-lite": "iconv-lite@0.4.24", + "unpipe": "unpipe@1.0.0" + } + }, + "run-exclusive@2.2.19": { + "integrity": "sha512-K3mdoAi7tjJ/qT7Flj90L7QyPozwUaAG+CVhkdDje4HLKXUYC3N/Jzkau3flHVDLQVhiHBtcimVodMjN9egYbA==", + "dependencies": { + "minimal-polyfills": "minimal-polyfills@2.2.3" + } + }, + "safe-buffer@5.2.1": { + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dependencies": {} + }, + "safer-buffer@2.1.2": { + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dependencies": {} + }, + "send@0.18.0": { + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "debug@2.6.9", + "depd": "depd@2.0.0", + "destroy": "destroy@1.2.0", + "encodeurl": "encodeurl@1.0.2", + "escape-html": "escape-html@1.0.3", + "etag": "etag@1.8.1", + "fresh": "fresh@0.5.2", + "http-errors": "http-errors@2.0.0", + "mime": "mime@1.6.0", + "ms": "ms@2.1.3", + "on-finished": "on-finished@2.4.1", + "range-parser": "range-parser@1.2.1", + "statuses": "statuses@2.0.1" + } + }, + "serve-static@1.15.0": { + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "encodeurl@1.0.2", + "escape-html": "escape-html@1.0.3", + "parseurl": "parseurl@1.3.3", + "send": "send@0.18.0" + } + }, + "setprototypeof@1.2.0": { + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dependencies": {} + }, + "side-channel@1.0.4": { + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "call-bind@1.0.2", + "get-intrinsic": "get-intrinsic@1.2.1", + "object-inspect": "object-inspect@1.12.3" + } + }, + "slug@6.1.0": { + "integrity": "sha512-x6vLHCMasg4DR2LPiyFGI0gJJhywY6DTiGhCrOMzb3SOk/0JVLIaL4UhyFSHu04SD3uAavrKY/K3zZ3i6iRcgA==", + "dependencies": {} + }, + "statuses@2.0.1": { + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dependencies": {} + }, + "supports-color@7.2.0": { + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "has-flag@4.0.0" + } + }, + "supports-hyperlinks@2.3.0": { + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dependencies": { + "has-flag": "has-flag@4.0.0", + "supports-color": "supports-color@7.2.0" + } + }, + "terminal-link@3.0.0": { + "integrity": "sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg==", + "dependencies": { + "ansi-escapes": "ansi-escapes@5.0.0", + "supports-hyperlinks": "supports-hyperlinks@2.3.0" + } + }, + "toidentifier@1.0.1": { + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dependencies": {} + }, + "tr46@0.0.3": { + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dependencies": {} + }, + "tsafe@1.6.5": { + "integrity": "sha512-895zss8xqqHKTc28sHGIfZKnt3C5jrstB1DyPr/h3/flK0zojsZUMQL1/W4ytdDW6KI4Oth62nb9rrxmA3s3Iw==", + "dependencies": {} + }, + "type-fest@1.4.0": { + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dependencies": {} + }, + "type-is@1.6.18": { + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "media-typer@0.3.0", + "mime-types": "mime-types@2.1.35" + } + }, + "ulid@2.3.0": { + "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==", + "dependencies": {} + }, + "unpipe@1.0.0": { + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dependencies": {} + }, + "util@0.12.5": { + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "inherits@2.0.4", + "is-arguments": "is-arguments@1.1.1", + "is-generator-function": "is-generator-function@1.0.10", + "is-typed-array": "is-typed-array@1.1.12", + "which-typed-array": "which-typed-array@1.1.11" + } + }, + "utils-merge@1.0.1": { + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dependencies": {} + }, + "uuid@9.0.1": { + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dependencies": {} + }, + "vary@1.1.2": { + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dependencies": {} + }, + "web-encoding@1.1.5": { + "integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==", + "dependencies": { + "@zxing/text-encoding": "@zxing/text-encoding@0.9.0", + "util": "util@0.12.5" + } + }, + "web-streams-polyfill@3.2.1": { + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "dependencies": {} + }, + "webidl-conversions@3.0.1": { + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dependencies": {} + }, + "whatwg-url@5.0.0": { + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "tr46@0.0.3", + "webidl-conversions": "webidl-conversions@3.0.1" + } + }, + "which-typed-array@1.1.11": { + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "dependencies": { + "available-typed-arrays": "available-typed-arrays@1.0.5", + "call-bind": "call-bind@1.0.2", + "for-each": "for-each@0.3.3", + "gopd": "gopd@1.0.1", + "has-tostringtag": "has-tostringtag@1.0.0" + } + }, + "ws@8.13.0": { + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dependencies": {} + }, + "zod-error@1.5.0": { + "integrity": "sha512-zzopKZ/skI9iXpqCEPj+iLCKl9b88E43ehcU+sbRoHuwGd9F1IDVGQ70TyO6kmfiRL1g4IXkjsXK+g1gLYl4WQ==", + "dependencies": { + "zod": "zod@3.21.4" + } + }, + "zod@3.21.4": { + "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==", + "dependencies": {} + } + } + }, + "remote": {} +} diff --git a/references/deno-reference/main.ts b/references/deno-reference/main.ts new file mode 100644 index 000000000..de9336dea --- /dev/null +++ b/references/deno-reference/main.ts @@ -0,0 +1,41 @@ +import { TriggerClient } from "npm:@trigger.dev/sdk"; +import { eventTrigger } from "npm:@trigger.dev/sdk"; + +export const triggerClient = new TriggerClient({ + id: "borderless", + apiKey: "...", +}); + +// your first job +triggerClient.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!", + }; + }, +}); + +Deno.serve(async (req) => { + const response = await triggerClient.handleRequest(req); + if (!response) { + return Response.json( + { error: "Not found" }, + { + status: 404, + } + ); + } + + return Response.json(response.body, { + status: response.status, + headers: response.headers, + }); +}); diff --git a/references/job-catalog/README.md b/references/job-catalog/README.md index a1893ce1e..e73669a52 100644 --- a/references/job-catalog/README.md +++ b/references/job-catalog/README.md @@ -28,6 +28,8 @@ cd references/job-catalog pnpm run dev:trigger ``` +Navigate to your trigger.dev instance ([http://localhost:3030](http://localhost:3030/) when running locally, or if you are using the cloud version it's [cloud.trigger.dev](https://cloud.trigger.dev)), to see the jobs. You can use the test feature to trigger them. See our [testing jobs docs](https://trigger.dev/docs/documentation/guides/testing-jobs) for more info. + ### Adding a new file You can add a new file to `src` with it's own `TriggerClient` and set of jobs (e.g. `src/events.ts`) diff --git a/references/job-catalog/package.json b/references/job-catalog/package.json index ab55bf836..0fcca5422 100644 --- a/references/job-catalog/package.json +++ b/references/job-catalog/package.json @@ -25,6 +25,8 @@ "status": "nodemon --watch src/status.ts -r tsconfig-paths/register -r dotenv/config src/status.ts", "byo-auth": "nodemon --watch src/byo-auth.ts -r tsconfig-paths/register -r dotenv/config src/byo-auth.ts", "redacted": "nodemon --watch src/redacted.ts -r tsconfig-paths/register -r dotenv/config src/redacted.ts", + "replicate": "nodemon --watch src/replicate.ts -r tsconfig-paths/register -r dotenv/config src/replicate.ts", + "misconfigured": "nodemon --watch src/misconfigured.ts -r tsconfig-paths/register -r dotenv/config src/misconfigured.ts", "dev:trigger": "trigger-cli dev --port 8080" }, "dependencies": { @@ -32,8 +34,10 @@ "@trigger.dev/airtable": "workspace:*", "@trigger.dev/express": "workspace:*", "@trigger.dev/github": "workspace:*", + "@trigger.dev/linear": "workspace:*", "@trigger.dev/openai": "workspace:*", "@trigger.dev/plain": "workspace:*", + "@trigger.dev/replicate": "workspace:*", "@trigger.dev/resend": "workspace:*", "@trigger.dev/sdk": "workspace:*", "@trigger.dev/sendgrid": "workspace:*", @@ -43,8 +47,7 @@ "@trigger.dev/typeform": "workspace:*", "@types/node": "20.4.2", "typescript": "5.1.6", - "zod": "3.21.4", - "@trigger.dev/linear": "workspace:*" + "zod": "3.22.3" }, "trigger.dev": { "endpointId": "job-catalog" @@ -58,4 +61,4 @@ "ts-node": "^10.9.1", "tsconfig-paths": "^3.14.1" } -} \ No newline at end of file +} diff --git a/references/job-catalog/src/byo-auth.ts b/references/job-catalog/src/byo-auth.ts index e77786bb1..b739f6912 100644 --- a/references/job-catalog/src/byo-auth.ts +++ b/references/job-catalog/src/byo-auth.ts @@ -23,7 +23,7 @@ const stripe = new Stripe({ }); const slack = new Slack({ id: "slack" }); const openai = new OpenAI({ id: "openai" }); -const github = new Github({ id: "github" }); +const github = new Github({ id: "github-byoa" }); client.defineAuthResolver(resend, async (ctx, integration) => { return { diff --git a/references/job-catalog/src/misconfigured.ts b/references/job-catalog/src/misconfigured.ts new file mode 100644 index 000000000..ba60910ab --- /dev/null +++ b/references/job-catalog/src/misconfigured.ts @@ -0,0 +1,35 @@ +import { createExpressServer } from "@trigger.dev/express"; +import { TriggerClient, intervalTrigger } from "@trigger.dev/sdk"; + +export const client = new TriggerClient({ + id: "job-catalog", + apiKey: process.env["TRIGGER_API_KEY"], + apiUrl: process.env["TRIGGER_API_URL"], + verbose: false, + ioLogLocalEnabled: true, +}); + +// This job is misconfigured because the interval trigger is less than 60s +client.defineJob({ + id: "bad-interval", + name: "Bad Interval", + version: "0.0.2", + trigger: intervalTrigger({ + seconds: 50, + }), + run: async (payload, io, ctx) => {}, +}); + +// This job is misconfigured because it has no name +//@ts-ignore +client.defineJob({ + id: "bad-cron", + // name: "Bad CRON expression", + version: "0.0.2", + trigger: intervalTrigger({ + seconds: 90, + }), + run: async (payload, io, ctx) => {}, +}); + +createExpressServer(client); diff --git a/references/job-catalog/src/replicate.ts b/references/job-catalog/src/replicate.ts new file mode 100644 index 000000000..5cd416bf0 --- /dev/null +++ b/references/job-catalog/src/replicate.ts @@ -0,0 +1,146 @@ +import { createExpressServer } from "@trigger.dev/express"; +import { TriggerClient, eventTrigger } from "@trigger.dev/sdk"; +import { Replicate } from "@trigger.dev/replicate"; +import { z } from "zod"; + +export const client = new TriggerClient({ + id: "job-catalog", + apiKey: process.env["TRIGGER_API_KEY"], + apiUrl: process.env["TRIGGER_API_URL"], + verbose: false, + ioLogLocalEnabled: true, +}); + +const replicate = new Replicate({ + id: "replicate", + apiKey: process.env["REPLICATE_API_KEY"]!, +}); + +client.defineJob({ + id: "replicate-forge-image", + name: "Replicate - Forge Image", + version: "0.1.0", + integrations: { replicate }, + trigger: eventTrigger({ + name: "replicate.bad.forgery", + schema: z.object({ + imageUrl: z + .string() + .url() + .default("https://trigger.dev/blog/supabase-integration/postgres-meme.png"), + }), + }), + run: async (payload, io, ctx) => { + const blipVersion = "2e1dddc8621f72155f24cf2e0adbde548458d3cab9f00c0139eea840d0ac4746"; + const sdVersion = "ac732df83cea7fff18b8472768c88ad041fa750ff7682a21affe81863cbe77e4"; + + const blipPrediction = await io.replicate.run("caption-image", { + identifier: `salesforce/blip:${blipVersion}`, + input: { + image: payload.imageUrl, + }, + }); + + if (typeof blipPrediction.output !== "string") { + throw new Error(`Expected string output, got ${typeof blipPrediction.output}`); + } + + const caption = blipPrediction.output.replace("Caption: ", ""); + + const sdPrediction = await io.replicate.predictions.createAndAwait("draw-image", { + version: sdVersion, + input: { + prompt: caption, + }, + }); + + return { + caption, + output: sdPrediction.output, + }; + }, +}); + +client.defineJob({ + id: "replicate-python-answers", + name: "Replicate - Python Answers", + version: "0.1.0", + integrations: { replicate }, + trigger: eventTrigger({ + name: "replicate.serious.monty", + schema: z.object({ + prompt: z.string().default("why are apples not oranges?"), + }), + }), + run: async (payload, io, ctx) => { + const prediction = await io.replicate.run("await-prediction", { + identifier: + "meta/llama-2-13b-chat:f4e2de70d66816a838a89eeeb621910adffb0dd0baba3976c96980970978018d", + input: { + prompt: payload.prompt, + system_prompt: "Answer like John Cleese. Don't be funny.", + max_new_tokens: 200, + }, + }); + + return Array.isArray(prediction.output) ? prediction.output.join("") : prediction.output; + }, +}); + +client.defineJob({ + id: "replicate-cinematic-prompt", + name: "Replicate - Cinematic Prompt", + version: "0.1.0", + integrations: { replicate }, + trigger: eventTrigger({ + name: "replicate.cinematic", + schema: z.object({ + prompt: z.string().default("rick astley riding a harley through post-apocalyptic miami"), + version: z + .string() + .default("af1a68a271597604546c09c64aabcd7782c114a63539a4a8d14d1eeda5630c33"), + }), + }), + run: async (payload, io, ctx) => { + const prediction = await io.replicate.predictions.createAndAwait("await-prediction", { + version: payload.version, + input: { + prompt: `${payload.prompt}, cinematic, 70mm, anamorphic, bokeh`, + width: 1280, + height: 720, + }, + }); + return prediction.output; + }, +}); + +client.defineJob({ + id: "replicate-pagination", + name: "Replicate - Pagination", + version: "0.1.0", + integrations: { + replicate, + }, + trigger: eventTrigger({ + name: "replicate.paginate", + }), + run: async (payload, io, ctx) => { + // getAll - returns an array of all results (uses paginate internally) + const all = await io.replicate.getAll(io.replicate.predictions.list, "get-all"); + + // paginate - returns an async generator, useful to process one page at a time + for await (const predictions of io.replicate.paginate( + io.replicate.predictions.list, + "paginate-all" + )) { + await io.logger.info("stats", { + total: predictions.length, + versions: predictions.map((p) => p.version), + }); + } + + return { count: all.length }; + }, +}); + +createExpressServer(client); diff --git a/references/job-catalog/src/resend.ts b/references/job-catalog/src/resend.ts index bbd585854..934529140 100644 --- a/references/job-catalog/src/resend.ts +++ b/references/job-catalog/src/resend.ts @@ -43,4 +43,32 @@ client.defineJob({ }, }); +client.defineJob({ + id: "send-resend-email-from-blank", + name: "Send Resend Email From Blank", + version: "0.1.0", + trigger: eventTrigger({ + name: "send.email", + schema: z.object({ + to: z.union([z.string(), z.array(z.string())]), + subject: z.string(), + text: z.string(), + from: z.string().optional(), + }), + }), + integrations: { + resend, + }, + run: async (payload, io, ctx) => { + const response = await io.resend.sendEmail("πŸ“§", { + to: payload.to, + subject: payload.subject, + text: payload.text, + from: payload.from!, + }); + + await io.logger.info("Sent email", { response }); + }, +}); + createExpressServer(client); diff --git a/references/job-catalog/src/schedules.ts b/references/job-catalog/src/schedules.ts index 501798c2e..89a687cb1 100644 --- a/references/job-catalog/src/schedules.ts +++ b/references/job-catalog/src/schedules.ts @@ -31,6 +31,27 @@ client.defineJob({ }, }); +client.defineJob({ + id: "schedule-example-2", + name: "Schedule Example 2", + version: "1.0.0", + enabled: true, + trigger: intervalTrigger({ + seconds: 60 * 30, // 30 minutes + }), + run: async (payload, io, ctx) => { + await io.runTask("task-example-1", async () => { + return { + message: "Hello World", + }; + }); + + await io.wait("wait-1", 1); + + await io.logger.info("Hello World", { ctx }); + }, +}); + const resend = new Resend({ id: "resend-client", apiKey: process.env.RESEND_API_KEY!, diff --git a/references/job-catalog/src/stressTest.ts b/references/job-catalog/src/stressTest.ts index a6414046c..305dc152d 100644 --- a/references/job-catalog/src/stressTest.ts +++ b/references/job-catalog/src/stressTest.ts @@ -5,7 +5,7 @@ export const client = new TriggerClient({ id: "job-catalog", apiKey: process.env["TRIGGER_API_KEY"], apiUrl: process.env["TRIGGER_API_URL"], - verbose: false, + verbose: true, ioLogLocalEnabled: true, }); @@ -139,4 +139,67 @@ client.defineJob({ }, }); +client.defineJob({ + id: "stress.logs-of-logs", + name: "Lots of Logs", + version: "1.0.0", + trigger: eventTrigger({ + name: "lots.of.logs", + }), + run: async (payload, io, ctx) => { + // Do lots of logs + for (let i = 0; i < payload.iterations; i++) { + await io.logger.info(`before-yield: Iteration ${i} started`); + } + + // Each are 300KB + for (let i = 0; i < payload.iterations; i++) { + await io.runTask( + `before.yield.${i}`, + async (task) => { + return { + i, + extra: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n".repeat( + (300 * payload.size) / 60 + ), + }; + }, + { name: `before-yield: Task ${i}` } + ); + } + + io.yield("yield 1"); + + // Do lots of logs + for (let i = 0; i < payload.iterations; i++) { + await io.logger.info(`after-yield: Iteration ${i} started`); + } + + for (let i = 0; i < payload.iterations; i++) { + await io.runTask( + `after-yield.task.${i}`, + async (task) => { + return { + i, + extra: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n".repeat( + (300 * payload.size) / 60 + ), + }; + }, + { name: `after-yield: Task ${i}` } + ); + } + + await io.wait("wait-1", 10); + + await io.runTask( + `after-wait.task`, + async (task) => { + return { i: 0 }; + }, + { name: `after-wait: Task 0` } + ); + }, +}); + createExpressServer(client); diff --git a/references/job-catalog/tsconfig.json b/references/job-catalog/tsconfig.json index 6ec3167e6..80823d1a8 100644 --- a/references/job-catalog/tsconfig.json +++ b/references/job-catalog/tsconfig.json @@ -97,6 +97,12 @@ ], "@trigger.dev/linear/*": [ "../../integrations/linear/src/*" + ], + "@trigger.dev/replicate": [ + "../../integrations/replicate/src/index" + ], + "@trigger.dev/replicate/*": [ + "../../integrations/replicate/src/*" ] } } diff --git a/references/nestjs-example/.env.example b/references/nestjs-example/.env.example new file mode 100644 index 000000000..4fdb8311f --- /dev/null +++ b/references/nestjs-example/.env.example @@ -0,0 +1,3 @@ + +TRIGGER_API_KEY=tr_dev_test-api-key +TRIGGER_API_URL=http://localhost:3030 diff --git a/references/nestjs-example/.eslintrc.js b/references/nestjs-example/.eslintrc.js new file mode 100644 index 000000000..259de13c7 --- /dev/null +++ b/references/nestjs-example/.eslintrc.js @@ -0,0 +1,25 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + tsconfigRootDir: __dirname, + sourceType: 'module', + }, + plugins: ['@typescript-eslint/eslint-plugin'], + extends: [ + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended', + ], + root: true, + env: { + node: true, + jest: true, + }, + ignorePatterns: ['.eslintrc.js'], + rules: { + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'off', + }, +}; diff --git a/references/nestjs-example/.gitignore b/references/nestjs-example/.gitignore new file mode 100644 index 000000000..2d33ffcac --- /dev/null +++ b/references/nestjs-example/.gitignore @@ -0,0 +1,38 @@ +# compiled output +/dist +/node_modules + +# Logs +logs +*.log +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# OS +.DS_Store + +# Tests +/coverage +/.nyc_output + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +.env + diff --git a/references/nestjs-example/.prettierrc b/references/nestjs-example/.prettierrc new file mode 100644 index 000000000..dcb72794f --- /dev/null +++ b/references/nestjs-example/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} \ No newline at end of file diff --git a/references/nestjs-example/README.md b/references/nestjs-example/README.md new file mode 100644 index 000000000..f5aa86c5d --- /dev/null +++ b/references/nestjs-example/README.md @@ -0,0 +1,73 @@ +

+ Nest Logo +

+ +[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 +[circleci-url]: https://circleci.com/gh/nestjs/nest + +

A progressive Node.js framework for building efficient and scalable server-side applications.

+

+NPM Version +Package License +NPM Downloads +CircleCI +Coverage +Discord +Backers on Open Collective +Sponsors on Open Collective + + Support us + +

+ + +## Description + +[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. + +## Installation + +```bash +$ pnpm install +``` + +## Running the app + +```bash +# development +$ pnpm run start + +# watch mode +$ pnpm run start:dev + +# production mode +$ pnpm run start:prod +``` + +## Test + +```bash +# unit tests +$ pnpm run test + +# e2e tests +$ pnpm run test:e2e + +# test coverage +$ pnpm run test:cov +``` + +## Support + +Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). + +## Stay in touch + +- Author - [Kamil MyΕ›liwiec](https://kamilmysliwiec.com) +- Website - [https://nestjs.com](https://nestjs.com/) +- Twitter - [@nestframework](https://twitter.com/nestframework) + +## License + +Nest is [MIT licensed](LICENSE). diff --git a/references/nestjs-example/nest-cli.json b/references/nestjs-example/nest-cli.json new file mode 100644 index 000000000..f9aa683b1 --- /dev/null +++ b/references/nestjs-example/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/references/nestjs-example/package.json b/references/nestjs-example/package.json new file mode 100644 index 000000000..78868e4b0 --- /dev/null +++ b/references/nestjs-example/package.json @@ -0,0 +1,47 @@ +{ + "name": "nestjs-example", + "version": "0.0.1", + "description": "", + "author": "", + "private": true, + "license": "UNLICENSED", + "scripts": { + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix" + }, + "dependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0", + "@nestjs/config": "^3.0.1", + "@nestjs/platform-express": "^10.0.0", + "reflect-metadata": "^0.1.13", + "@trigger.dev/sdk": "workspace:*", + "@trigger.dev/nestjs": "workspace:*", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@nestjs/cli": "^10.0.0", + "@nestjs/schematics": "^10.0.0", + "@nestjs/testing": "^10.0.0", + "@types/express": "^4.17.17", + "@types/node": "^20.3.1", + "@types/supertest": "^2.0.12", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "eslint": "^8.42.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", + "prettier": "^3.0.0", + "source-map-support": "^0.5.21", + "supertest": "^6.3.3", + "ts-loader": "^9.4.3", + "ts-node": "^10.9.1", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.1.3" + } +} diff --git a/references/nestjs-example/src/app.controller.ts b/references/nestjs-example/src/app.controller.ts new file mode 100644 index 000000000..462bab4c7 --- /dev/null +++ b/references/nestjs-example/src/app.controller.ts @@ -0,0 +1,31 @@ +import { Controller, Get } from '@nestjs/common'; +import { InjectTriggerDevClient } from '@trigger.dev/nestjs'; +import { eventTrigger, TriggerClient } from '@trigger.dev/sdk'; + +@Controller() +export class AppController { + constructor( + @InjectTriggerDevClient() private readonly client: TriggerClient, + ) { + this.client.defineJob({ + id: 'test-job', + name: 'Test Job One', + version: '0.0.1', + trigger: eventTrigger({ + name: 'test.event', + }), + run: async (payload, io) => { + await io.logger.info('Hello world!', { payload }); + + return { + message: 'Hello world!', + }; + }, + }); + } + + @Get() + getHello(): string { + return `Running Trigger.dev with client-id ${this.client.id}`; + } +} diff --git a/references/nestjs-example/src/app.module.ts b/references/nestjs-example/src/app.module.ts new file mode 100644 index 000000000..ad5755575 --- /dev/null +++ b/references/nestjs-example/src/app.module.ts @@ -0,0 +1,24 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { TriggerDevModule } from '@trigger.dev/nestjs'; +import { AppController } from './app.controller'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + }), + TriggerDevModule.registerAsync({ + inject: [ConfigService], + useFactory: (config: ConfigService) => ({ + id: 'my-nest-app', + apiKey: config.getOrThrow('TRIGGER_API_KEY'), + apiUrl: config.getOrThrow('TRIGGER_API_URL'), + verbose: false, + ioLogLocalEnabled: true, + }), + }), + ], + controllers: [AppController], +}) +export class AppModule {} diff --git a/references/nestjs-example/src/main.ts b/references/nestjs-example/src/main.ts new file mode 100644 index 000000000..13cad38cf --- /dev/null +++ b/references/nestjs-example/src/main.ts @@ -0,0 +1,8 @@ +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + await app.listen(3000); +} +bootstrap(); diff --git a/references/nestjs-example/tsconfig.build.json b/references/nestjs-example/tsconfig.build.json new file mode 100644 index 000000000..64f86c6bd --- /dev/null +++ b/references/nestjs-example/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] +} diff --git a/references/nestjs-example/tsconfig.json b/references/nestjs-example/tsconfig.json new file mode 100644 index 000000000..95f5641cf --- /dev/null +++ b/references/nestjs-example/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "ES2021", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": false, + "noImplicitAny": false, + "strictBindCallApply": false, + "forceConsistentCasingInFileNames": false, + "noFallthroughCasesInSwitch": false + } +} diff --git a/references/nextjs-reference/package.json b/references/nextjs-reference/package.json index f4ea60bcb..5e0b11963 100644 --- a/references/nextjs-reference/package.json +++ b/references/nextjs-reference/package.json @@ -10,6 +10,7 @@ "generate:types": "npx supabase gen types typescript --project-id axtbanoixaztvdntngew --schema public --schema public_2 > src/supabase.types.ts" }, "dependencies": { + "@trigger.dev/eslint-plugin": "workspace:*", "@trigger.dev/github": "workspace:*", "@trigger.dev/nextjs": "workspace:*", "@trigger.dev/openai": "workspace:*", @@ -17,9 +18,11 @@ "@trigger.dev/react": "workspace:*", "@trigger.dev/resend": "workspace:*", "@trigger.dev/sdk": "workspace:*", + "@trigger.dev/sendgrid": "workspace:*", "@trigger.dev/slack": "workspace:*", + "@trigger.dev/stripe": "workspace:*", + "@trigger.dev/supabase": "workspace:*", "@trigger.dev/typeform": "workspace:*", - "@trigger.dev/eslint-plugin": "workspace:*", "@types/node": "18.15.13", "@types/react": "18.2.17", "@types/react-dom": "18.2.7", @@ -28,10 +31,7 @@ "react-dom": "^18.2.0", "react-query": "^3.39.3", "typescript": "5.0.4", - "zod": "3.21.4", - "@trigger.dev/supabase": "workspace:*", - "@trigger.dev/stripe": "workspace:*", - "@trigger.dev/sendgrid": "workspace:*" + "zod": "3.22.3" }, "devDependencies": { "@trigger.dev/cli": "workspace:*", diff --git a/references/svelte-example/.env.example b/references/svelte-example/.env.example new file mode 100644 index 000000000..1482c408b --- /dev/null +++ b/references/svelte-example/.env.example @@ -0,0 +1,3 @@ +TRIGGER_API_KEY= +TRIGGER_API_URL= + diff --git a/references/svelte-example/.eslintignore b/references/svelte-example/.eslintignore new file mode 100644 index 000000000..38972655f --- /dev/null +++ b/references/svelte-example/.eslintignore @@ -0,0 +1,13 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example + +# Ignore files for PNPM, NPM and YARN +pnpm-lock.yaml +package-lock.json +yarn.lock diff --git a/references/svelte-example/.eslintrc.cjs b/references/svelte-example/.eslintrc.cjs new file mode 100644 index 000000000..ebc19589f --- /dev/null +++ b/references/svelte-example/.eslintrc.cjs @@ -0,0 +1,30 @@ +module.exports = { + root: true, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:svelte/recommended', + 'prettier' + ], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], + parserOptions: { + sourceType: 'module', + ecmaVersion: 2020, + extraFileExtensions: ['.svelte'] + }, + env: { + browser: true, + es2017: true, + node: true + }, + overrides: [ + { + files: ['*.svelte'], + parser: 'svelte-eslint-parser', + parserOptions: { + parser: '@typescript-eslint/parser' + } + } + ] +}; diff --git a/references/svelte-example/.gitignore b/references/svelte-example/.gitignore new file mode 100644 index 000000000..6635cf554 --- /dev/null +++ b/references/svelte-example/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/references/svelte-example/.npmrc b/references/svelte-example/.npmrc new file mode 100644 index 000000000..0c05da457 --- /dev/null +++ b/references/svelte-example/.npmrc @@ -0,0 +1,2 @@ +engine-strict=true +resolution-mode=highest diff --git a/references/svelte-example/.prettierignore b/references/svelte-example/.prettierignore new file mode 100644 index 000000000..38972655f --- /dev/null +++ b/references/svelte-example/.prettierignore @@ -0,0 +1,13 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example + +# Ignore files for PNPM, NPM and YARN +pnpm-lock.yaml +package-lock.json +yarn.lock diff --git a/references/svelte-example/.prettierrc b/references/svelte-example/.prettierrc new file mode 100644 index 000000000..a77fddea9 --- /dev/null +++ b/references/svelte-example/.prettierrc @@ -0,0 +1,9 @@ +{ + "useTabs": true, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "plugins": ["prettier-plugin-svelte"], + "pluginSearchDirs": ["."], + "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] +} diff --git a/references/svelte-example/README.md b/references/svelte-example/README.md new file mode 100644 index 000000000..5c91169b0 --- /dev/null +++ b/references/svelte-example/README.md @@ -0,0 +1,38 @@ +# create-svelte + +Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte). + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```bash +# create a new project in the current directory +npm create svelte@latest + +# create a new project in my-app +npm create svelte@latest my-app +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```bash +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```bash +npm run build +``` + +You can preview the production build with `npm run preview`. + +> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. diff --git a/references/svelte-example/package.json b/references/svelte-example/package.json new file mode 100644 index 000000000..92c355be2 --- /dev/null +++ b/references/svelte-example/package.json @@ -0,0 +1,38 @@ +{ + "name": "svelte-example", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "lint": "prettier --plugin-search-dir . --check . && eslint .", + "format": "prettier --plugin-search-dir . --write ." + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^2.0.0", + "@sveltejs/kit": "^1.20.4", + "@typescript-eslint/eslint-plugin": "^5.45.0", + "@typescript-eslint/parser": "^5.45.0", + "eslint": "^8.28.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-svelte": "^2.30.0", + "prettier": "^2.8.0", + "prettier-plugin-svelte": "^2.10.1", + "svelte": "^4.0.5", + "svelte-check": "^3.4.3", + "tslib": "^2.4.1", + "typescript": "^5.0.0", + "vite": "^4.4.2" + }, + "dependencies": { + "@trigger.dev/sdk": "workspace:*", + "@trigger.dev/sveltekit": "workspace:*" + }, + "trigger.dev": { + "endpointId": "sveltekit-example" + }, + "type": "module" +} diff --git a/references/svelte-example/src/app.d.ts b/references/svelte-example/src/app.d.ts new file mode 100644 index 000000000..f59b884c5 --- /dev/null +++ b/references/svelte-example/src/app.d.ts @@ -0,0 +1,12 @@ +// See https://kit.svelte.dev/docs/types#app +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface Platform {} + } +} + +export {}; diff --git a/references/svelte-example/src/app.html b/references/svelte-example/src/app.html new file mode 100644 index 000000000..effe0d0d2 --- /dev/null +++ b/references/svelte-example/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/references/svelte-example/src/jobs/example.ts b/references/svelte-example/src/jobs/example.ts new file mode 100644 index 000000000..aad34452b --- /dev/null +++ b/references/svelte-example/src/jobs/example.ts @@ -0,0 +1,20 @@ +import { eventTrigger } from '@trigger.dev/sdk'; +import { client } from '../trigger'; + +// your first job +client.defineJob({ + id: 'test-svelte-job', + name: 'Test sveltekit', + version: '0.0.1', + trigger: eventTrigger({ + name: 'test.event' + }), + run: async (payload, io, ctx) => { + await io.wait("waiting", 5) + await io.logger.info('Hello world!', { payload }); + + return { + message: 'Hello world!' + }; + } +}); diff --git a/references/svelte-example/src/lib/index.ts b/references/svelte-example/src/lib/index.ts new file mode 100644 index 000000000..856f2b6c3 --- /dev/null +++ b/references/svelte-example/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/references/svelte-example/src/routes/+page.svelte b/references/svelte-example/src/routes/+page.svelte new file mode 100644 index 000000000..5982b0ae3 --- /dev/null +++ b/references/svelte-example/src/routes/+page.svelte @@ -0,0 +1,2 @@ +

Welcome to SvelteKit

+

Visit kit.svelte.dev to read the documentation

diff --git a/references/svelte-example/src/routes/api/trigger/+server.ts b/references/svelte-example/src/routes/api/trigger/+server.ts new file mode 100644 index 000000000..bfe2c7294 --- /dev/null +++ b/references/svelte-example/src/routes/api/trigger/+server.ts @@ -0,0 +1,12 @@ +import { createSvelteRoute } from '@trigger.dev/sveltekit'; + +import { client } from '$trigger'; + +// // Replace this with your own jobs +import '$jobs/example'; + +// Create the Svelte route handler using the createSvelteRoute function +const svelteRoute = createSvelteRoute(client); + +// Define your API route handler +export const POST = svelteRoute.POST; diff --git a/references/svelte-example/src/trigger.ts b/references/svelte-example/src/trigger.ts new file mode 100644 index 000000000..acea0c199 --- /dev/null +++ b/references/svelte-example/src/trigger.ts @@ -0,0 +1,7 @@ +import { TriggerClient } from '@trigger.dev/sdk'; +import { TRIGGER_API_KEY } from '$env/static/private'; + +export const client = new TriggerClient({ + id: 'sveltekit-example', + apiKey: TRIGGER_API_KEY, +}); diff --git a/references/svelte-example/static/favicon.png b/references/svelte-example/static/favicon.png new file mode 100644 index 000000000..825b9e65a Binary files /dev/null and b/references/svelte-example/static/favicon.png differ diff --git a/references/svelte-example/svelte.config.js b/references/svelte-example/svelte.config.js new file mode 100644 index 000000000..e7e16ba2d --- /dev/null +++ b/references/svelte-example/svelte.config.js @@ -0,0 +1,28 @@ +import adapter from '@sveltejs/adapter-auto'; +import { vitePreprocess } from '@sveltejs/kit/vite'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + // Consult https://kit.svelte.dev/docs/integrations#preprocessors + // for more information about preprocessors + preprocess: vitePreprocess(), + + kit: { + // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. + // If your environment is not supported or you settled on a specific environment, switch out the adapter. + // See https://kit.svelte.dev/docs/adapters for more information about adapters. + adapter: adapter(), + alias: { + $trigger: 'src/trigger', + '$jobs/*': 'src/jobs/*', + '@trigger.dev/sveltekit': '../../packages/sveltekit/src/index', + '@trigger.dev/sveltekit/*': '../../packages/sveltekit/src/*', + '@trigger.dev/sdk': '../../packages/trigger-sdk/src/index', + '@trigger.dev/sdk/*': '../../packages/trigger-sdk/src/*', + '@trigger.dev/core': '../../packages/core/src/index', + '@trigger.dev/core/*': '../../packages/core/src/*' + } + } +}; + +export default config; diff --git a/references/svelte-example/tsconfig.json b/references/svelte-example/tsconfig.json new file mode 100644 index 000000000..7299ebd24 --- /dev/null +++ b/references/svelte-example/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ES2019"], + "isolatedModules": true, + "esModuleInterop": true, + "ignoreDeprecations": "5.0", + "moduleResolution": "node", + "resolveJsonModule": true, + "target": "ES2019", + "strict": true, + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "baseUrl": ".", + "paths": { + "$lib": ["src/lib"], + "$lib/*": ["src/lib/*"], + "$trigger": ["src/trigger"], + "$jobs/*": ["src/jobs/*"] + } + }, + "include": ["src/**/*", "src/node_modules", ".svelte-kit/ambient.d.ts"], // see last element + + "exclude": ["node_modules"] +} diff --git a/references/svelte-example/vite.config.ts b/references/svelte-example/vite.config.ts new file mode 100644 index 000000000..bbf8c7da4 --- /dev/null +++ b/references/svelte-example/vite.config.ts @@ -0,0 +1,6 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [sveltekit()] +}); diff --git a/references/unit-testing/package.json b/references/unit-testing/package.json index a827d24bd..bb42c57f6 100644 --- a/references/unit-testing/package.json +++ b/references/unit-testing/package.json @@ -19,6 +19,6 @@ "tsconfig-paths": "^3.14.1", "typescript": "^5.2.2", "vitest": "^0.34.3", - "zod": "3.21.4" + "zod": "3.22.3" } } \ No newline at end of file