();
11 |
--------------------------------------------------------------------------------
/examples/framework-sveltekit/src/routes/+page.svelte:
--------------------------------------------------------------------------------
1 | Welcome to SvelteKit
2 | Visit kit.svelte.dev to read the documentation
3 |
--------------------------------------------------------------------------------
/examples/framework-sveltekit/src/routes/api/inngest/+server.ts:
--------------------------------------------------------------------------------
1 | import { functions, inngest } from '$lib/inngest';
2 | import { serve } from 'inngest/sveltekit';
3 |
4 | export const { GET, POST, PUT } = serve({ client: inngest, functions });
5 |
--------------------------------------------------------------------------------
/examples/framework-sveltekit/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inngest/inngest-js/2949293f64c65ddf30e50e4163eb8c0aec52cc83/examples/framework-sveltekit/static/favicon.png
--------------------------------------------------------------------------------
/examples/framework-sveltekit/svelte.config.js:
--------------------------------------------------------------------------------
1 | import adapter from '@sveltejs/adapter-auto';
2 | import { vitePreprocess } from '@sveltejs/kit/vite';
3 |
4 | /** @type {import('@sveltejs/kit').Config} */
5 | const config = {
6 | // Consult https://kit.svelte.dev/docs/integrations#preprocessors
7 | // for more information about preprocessors
8 | preprocess: vitePreprocess(),
9 |
10 | kit: {
11 | // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
12 | // If your environment is not supported or you settled on a specific environment, switch out the adapter.
13 | // See https://kit.svelte.dev/docs/adapters for more information about adapters.
14 | adapter: adapter()
15 | }
16 | };
17 |
18 | export default config;
19 |
--------------------------------------------------------------------------------
/examples/framework-sveltekit/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "checkJs": true,
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "resolveJsonModule": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "strict": true
12 | }
13 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
14 | //
15 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
16 | // from the referenced tsconfig.json - TypeScript does not merge them in
17 | }
18 |
--------------------------------------------------------------------------------
/examples/framework-sveltekit/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { sveltekit } from '@sveltejs/kit/vite';
2 | import { defineConfig } from 'vitest/config';
3 |
4 | export default defineConfig({
5 | plugins: [sveltekit()],
6 | test: {
7 | include: ['src/**/*.{test,spec}.{js,ts}']
8 | }
9 | });
10 |
--------------------------------------------------------------------------------
/examples/middleware-e2e-encryption/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "middleware-e2e-encryption",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "inngest": "^3.0.0"
14 | },
15 | "devDependencies": {
16 | "@types/node": "^20.9.1"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/.env.example:
--------------------------------------------------------------------------------
1 | POSTGRES_URL=your_neon_database_url_here
2 | OPENAI_API_KEY=your_openai_api_key_here
3 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 | .pnpm-debug.log*
32 |
33 | # env files (can opt-in for committing if needed)
34 | .env*
35 | !.env.example
36 |
37 | # vercel
38 | .vercel
39 |
40 | # typescript
41 | *.tsbuildinfo
42 | next-env.d.ts
43 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/app/actions.ts:
--------------------------------------------------------------------------------
1 | "use server";
2 | import { inngest } from "@/inngest/client";
3 |
4 | export async function generateMeal(formData: FormData) {
5 | const participantsCount = Number(formData.get("participantsCount"));
6 | const preferences =
7 | formData.get("preferences")?.toString().split("\n").filter(Boolean) || [];
8 |
9 | const { ids } = await inngest.send({
10 | name: "meal.generate",
11 | data: { participantsCount, preferences },
12 | });
13 |
14 | return ids[0];
15 | }
16 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/app/api/generationStatus/route.ts:
--------------------------------------------------------------------------------
1 | import { NextResponse } from "next/server";
2 | import { NextRequest } from "next/server";
3 |
4 | async function getRuns(eventId: string) {
5 | const response = await fetch(
6 | process.env.INNGEST_SIGNING_KEY
7 | ? `https://api.inngest.com/v1/events/${eventId}/runs`
8 | : `http://localhost:8288/v1/events/${eventId}/runs`,
9 | {
10 | ...(process.env.INNGEST_SIGNING_KEY
11 | ? {
12 | headers: {
13 | Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`,
14 | },
15 | }
16 | : {}),
17 | }
18 | );
19 | const json = await response.json();
20 | return json.data;
21 | }
22 |
23 | export async function GET(req: NextRequest) {
24 | const id = req.nextUrl.searchParams.get("id");
25 |
26 | const runs = await getRuns(id as string);
27 | if (runs[0] && runs[0].output) {
28 | const run = runs[0];
29 | console.log("run", JSON.stringify(run, null, 2));
30 | return NextResponse.json({
31 | menu: run.output.mealPlan.choices[0].message.content,
32 | });
33 | }
34 |
35 | return NextResponse.json({
36 | menu: null,
37 | });
38 | }
39 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/app/api/inngest/route.ts:
--------------------------------------------------------------------------------
1 | import { serve } from "inngest/next";
2 | import { inngest } from "@/inngest/client";
3 | import { generateMeal } from "@/inngest/functions";
4 |
5 | // Create an API route handler with all functions
6 | export const { GET, POST, PUT } = serve({
7 | client: inngest,
8 | functions: [generateMeal],
9 | });
10 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inngest/inngest-js/2949293f64c65ddf30e50e4163eb8c0aec52cc83/examples/nextjs-christmas-dinner-generator-challenge/app/favicon.ico
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --background: #ffffff;
7 | --foreground: #171717;
8 | }
9 |
10 | @media (prefers-color-scheme: dark) {
11 | :root {
12 | --background: #0a0a0a;
13 | --foreground: #ededed;
14 | }
15 | }
16 |
17 | body {
18 | color: var(--foreground);
19 | background: var(--background);
20 | font-family: Arial, Helvetica, sans-serif;
21 | }
22 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Geist, Geist_Mono } from "next/font/google";
3 | import "./globals.css";
4 |
5 | const geistSans = Geist({
6 | variable: "--font-geist-sans",
7 | subsets: ["latin"],
8 | });
9 |
10 | const geistMono = Geist_Mono({
11 | variable: "--font-geist-mono",
12 | subsets: ["latin"],
13 | });
14 |
15 | export const metadata: Metadata = {
16 | title: "Create Next App",
17 | description: "Generated by create next app",
18 | };
19 |
20 | export default function RootLayout({
21 | children,
22 | }: Readonly<{
23 | children: React.ReactNode;
24 | }>) {
25 | return (
26 |
27 |
30 | {children}
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/app/page.tsx:
--------------------------------------------------------------------------------
1 | "use server";
2 |
3 | import { Home } from "./components/home";
4 |
5 | export default async function HomePage() {
6 | return ;
7 | }
8 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import { dirname } from "path";
2 | import { fileURLToPath } from "url";
3 | import { FlatCompat } from "@eslint/eslintrc";
4 |
5 | const __filename = fileURLToPath(import.meta.url);
6 | const __dirname = dirname(__filename);
7 |
8 | const compat = new FlatCompat({
9 | baseDirectory: __dirname,
10 | });
11 |
12 | const eslintConfig = [
13 | ...compat.extends("next/core-web-vitals", "next/typescript"),
14 | ];
15 |
16 | export default eslintConfig;
17 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/inngest/client.ts:
--------------------------------------------------------------------------------
1 | import { typedInngest } from "./events";
2 |
3 | // Export the typed client instead of the untyped one
4 | export const inngest = typedInngest;
5 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/inngest/events.ts:
--------------------------------------------------------------------------------
1 | import { EventSchemas, Inngest } from "inngest";
2 | import { z } from "zod";
3 |
4 | // Define the meal generation event schema
5 | export const mealGenerateSchema = z.object({
6 | name: z.literal("meal.generate"),
7 | data: z.object({
8 | participantsCount: z.number().int().positive(),
9 | preferences: z.array(z.string()),
10 | }),
11 | });
12 |
13 | // Create type from the schema
14 | export type MealGenerateEvent = z.infer;
15 |
16 | // Define all events type
17 | export type Events = {
18 | "meal.generate": MealGenerateEvent;
19 | };
20 |
21 | // Create a typed client
22 | export const typedInngest = new Inngest({
23 | id: "my-app",
24 | schemas: new EventSchemas().fromZod([mealGenerateSchema]),
25 | });
26 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/inngest/functions.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "@/inngest/client";
2 |
3 | export const generateMeal = inngest.createFunction(
4 | { id: "generate-meal" },
5 | { event: "meal.generate" },
6 | async ({ event, step }) => {
7 | const { participantsCount, preferences } = event.data;
8 |
9 | await step.run("hello", async () => {
10 | console.log("Hello, world!");
11 | console.log(
12 | `Received ${participantsCount} participants with the following preferences: ${preferences}`
13 | );
14 | });
15 |
16 | await step.sleep("sleep-1s", 1000);
17 |
18 | await step.run("final-step", async () => {
19 | console.log("Final step");
20 | });
21 | }
22 | );
23 |
24 | export const functions = [generateMeal];
25 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/lib/vectorStore.ts:
--------------------------------------------------------------------------------
1 | import { NeonPostgres } from "@langchain/community/vectorstores/neon";
2 | import { OpenAIEmbeddings } from "@langchain/openai";
3 |
4 | const embeddings = new OpenAIEmbeddings({
5 | apiKey: process.env.OPENAI_API_KEY,
6 | dimensions: 512,
7 | model: "text-embedding-3-small",
8 | });
9 |
10 | // Each workspace has its own vector store
11 | export const loadVectorStore = async (tableName: string) => {
12 | return await NeonPostgres.initialize(embeddings, {
13 | connectionString: process.env.POSTGRES_URL!,
14 | tableName,
15 | columns: {
16 | contentColumnName: "content",
17 | metadataColumnName: "metadata",
18 | vectorColumnName: "embedding",
19 | },
20 | });
21 | };
22 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 |
3 | const nextConfig: NextConfig = {
4 | /* config options here */
5 | };
6 |
7 | export default nextConfig;
8 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextjs-christmas-dinner-generator-challenge",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "index-vector-store": "ts-node scripts/index.ts",
8 | "generate-jsons": "ts-node scripts/generate-jsons.ts",
9 | "inngest": "npx inngest-cli@latest dev",
10 | "build": "next build",
11 | "start": "next start",
12 | "lint": "next lint"
13 | },
14 | "dependencies": {
15 | "@langchain/community": "^0.3.11",
16 | "@langchain/core": "^0.3.17",
17 | "@langchain/openai": "^0.3.11",
18 | "@neondatabase/serverless": "^0.10.4",
19 | "@tanstack/react-query": "^5.62.7",
20 | "inngest": "^3.27.5",
21 | "next": "15.1.0",
22 | "react": "^19.0.0",
23 | "react-dom": "^19.0.0"
24 | },
25 | "devDependencies": {
26 | "@dsnp/parquetjs": "^1.8.5",
27 | "@eslint/eslintrc": "^3",
28 | "@types/node": "^20",
29 | "@types/react": "^19",
30 | "@types/react-dom": "^19",
31 | "eslint": "^9",
32 | "eslint-config-next": "15.1.0",
33 | "glob": "^11.0.0",
34 | "postcss": "^8",
35 | "tailwindcss": "^3.4.1",
36 | "typescript": "^5"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/public/file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/public/globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/public/window.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/readme-assets/Screenshot_2024-11-20_at_14.33.46.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inngest/inngest-js/2949293f64c65ddf30e50e4163eb8c0aec52cc83/examples/nextjs-christmas-dinner-generator-challenge/readme-assets/Screenshot_2024-11-20_at_14.33.46.png
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/readme-assets/Screenshot_2024-11-20_at_14.34.24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inngest/inngest-js/2949293f64c65ddf30e50e4163eb8c0aec52cc83/examples/nextjs-christmas-dinner-generator-challenge/readme-assets/Screenshot_2024-11-20_at_14.34.24.png
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/readme-assets/Screenshot_2024-11-20_at_14.59.42.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inngest/inngest-js/2949293f64c65ddf30e50e4163eb8c0aec52cc83/examples/nextjs-christmas-dinner-generator-challenge/readme-assets/Screenshot_2024-11-20_at_14.59.42.png
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/readme-assets/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inngest/inngest-js/2949293f64c65ddf30e50e4163eb8c0aec52cc83/examples/nextjs-christmas-dinner-generator-challenge/readme-assets/image.png
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 |
3 | export default {
4 | content: [
5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
7 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
8 | ],
9 | theme: {
10 | extend: {
11 | colors: {
12 | background: "var(--background)",
13 | foreground: "var(--foreground)",
14 | },
15 | },
16 | },
17 | plugins: [],
18 | } satisfies Config;
19 |
--------------------------------------------------------------------------------
/examples/nextjs-christmas-dinner-generator-challenge/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/examples/node-otel/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.js
3 |
--------------------------------------------------------------------------------
/examples/node-otel/README.md:
--------------------------------------------------------------------------------
1 | # Inngest Node.js Template
2 |
3 | This is a vanilla Node.js server project. It is a minimal zero-external dependencies example of how to run Inngest natively in Node.js.
4 |
5 | ## Getting Started
6 |
7 | Use [`create-next-app`](https://www.npmjs.com/package/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:
8 |
9 | ```bash
10 | npx create-next-app --example https://github.com/inngest/inngest-js/tree/main/examples/framework-node inngest-node
11 | ```
12 |
13 | ```bash
14 | yarn create next-app --example https://github.com/inngest/inngest-js/tree/main/examples/framework-node inngest-node
15 | ```
16 |
17 | ```bash
18 | pnpm create next-app --example https://github.com/inngest/inngest-js/tree/main/examples/framework-node inngest-node
19 | ```
20 |
21 | Open http://localhost:3000 with your browser to see the result.
22 |
23 | - [Inngest functions](https://www.inngest.com/docs/functions) are available at `inngest/`.
24 | - The [Inngest handler](https://www.inngest.com/docs/sdk/serve) is available at `index.ts`.
25 |
26 | ## Learn More
27 |
28 | - [Inngest Documentation](https://www.inngest.com/docs) - learn about the Inngest SDK, functions, and events
29 |
--------------------------------------------------------------------------------
/examples/node-otel/index.ts:
--------------------------------------------------------------------------------
1 | // Import the client first
2 | import { inngest } from './inngest';
3 |
4 | // Then import everything else
5 | import { createServer } from 'inngest/node';
6 | import { functions } from './inngest';
7 |
8 | const server = createServer({ client: inngest, functions });
9 |
10 | server.listen(3000, () => {
11 | console.log('Server running on http://localhost:3000');
12 | });
13 |
--------------------------------------------------------------------------------
/examples/node-otel/inngest/client.ts:
--------------------------------------------------------------------------------
1 | // Initialize otel middleware first
2 | import { otelMiddleware } from "inngest/experimental";
3 | const otel = otelMiddleware();
4 |
5 | // Then everything else
6 | import { Inngest } from "inngest";
7 | import { schemas } from "./types";
8 |
9 | export const inngest = new Inngest({
10 | id: "my-express-app",
11 | schemas,
12 | middleware: [otel],
13 | });
14 |
--------------------------------------------------------------------------------
/examples/node-otel/inngest/helloWorld.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "./client";
2 |
3 | export default inngest.createFunction(
4 | { id: "hello-world" },
5 | { event: "demo/event.sent" },
6 | async ({ event, step }) => {
7 | // A random call that will trigger automatic Node instrumentation and create
8 | // a span
9 | await fetch("http://localhost:3000/api/inngest");
10 |
11 | return {
12 | message: `Hello ${event.name}!`,
13 | };
14 | }
15 | );
16 |
--------------------------------------------------------------------------------
/examples/node-otel/inngest/index.ts:
--------------------------------------------------------------------------------
1 | import helloWorld from "./helloWorld";
2 |
3 | export const functions = [helloWorld];
4 |
5 | export { inngest } from "./client";
6 |
--------------------------------------------------------------------------------
/examples/node-otel/inngest/types.ts:
--------------------------------------------------------------------------------
1 | import { EventSchemas } from "inngest";
2 |
3 | type DemoEventSent = {
4 | name: "demo/event.sent";
5 | data: {
6 | message: string;
7 | };
8 | };
9 |
10 | export const schemas = new EventSchemas().fromUnion();
11 |
--------------------------------------------------------------------------------
/examples/node-otel/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "framework-nodejs",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "tsc -p tsconfig.json",
8 | "dev": "tsx index.ts",
9 | "start": "node dist/index.js"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "@types/node": "^20.5.2",
15 | "tsx": "^4.7.3",
16 | "typescript": "^5.1.6"
17 | },
18 | "dependencies": {
19 | "inngest": "file:../../packages/inngest/inngest.tgz"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/node-step-fetch/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "step-fetch",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "scripts": {
6 | "build": "tsc",
7 | "start": "node dist/index.js",
8 | "dev": "nodemon --watch src --ext ts --exec ts-node src/index.ts"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "description": "",
14 | "dependencies": {
15 | "express": "^5.1.0",
16 | "inngest": "^3.35.1"
17 | },
18 | "devDependencies": {
19 | "@types/express": "^5.0.1",
20 | "@types/node": "^22.15.16",
21 | "nodemon": "^3.1.10",
22 | "ts-node": "^10.9.2",
23 | "typescript": "^5.8.3"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/examples/node-step-fetch/src/index.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import { inngest } from "./inngest/client";
3 | import { serve } from "inngest/express";
4 | import { retrieveTextFile } from "./inngest/functions";
5 | const app = express();
6 |
7 | // Important: ensure you add JSON middleware to process incoming JSON POST payloads.
8 | app.use(express.json());
9 | app.use(
10 | // Expose the middleware on our recommended path at `/api/inngest`.
11 | "/api/inngest",
12 | serve({ client: inngest, functions: [retrieveTextFile] })
13 | );
14 |
15 | app.listen(3000, () => {
16 | console.log("Server is running on port 3000");
17 | });
18 |
--------------------------------------------------------------------------------
/examples/node-step-fetch/src/inngest/client.ts:
--------------------------------------------------------------------------------
1 | import { Inngest } from "inngest";
2 |
3 | export const inngest = new Inngest({ id: "node-step-fetch" });
4 |
--------------------------------------------------------------------------------
/examples/node-step-fetch/src/inngest/functions.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "./client";
2 |
3 | export const retrieveTextFile = inngest.createFunction(
4 | { id: "retrieveTextFile" },
5 | { event: "textFile/retrieve" },
6 | async ({ step }) => {
7 | const response = await step.fetch(
8 | "https://example-files.online-convert.com/document/txt/example.txt"
9 | );
10 |
11 | await step.run("extract-text", async () => {
12 | const text = await response.text();
13 | const exampleOccurences = text.match(/example/g);
14 | return exampleOccurences?.length;
15 | });
16 | }
17 | );
18 |
--------------------------------------------------------------------------------
/examples/node/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.js
3 |
--------------------------------------------------------------------------------
/examples/node/README.md:
--------------------------------------------------------------------------------
1 | # Inngest Node.js Template
2 |
3 | This is a vanilla Node.js server project. It is a minimal zero-external dependencies example of how to run Inngest natively in Node.js.
4 |
5 | ## Getting Started
6 |
7 | Use [`create-next-app`](https://www.npmjs.com/package/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:
8 |
9 | ```bash
10 | npx create-next-app --example https://github.com/inngest/inngest-js/tree/main/examples/framework-node inngest-node
11 | ```
12 |
13 | ```bash
14 | yarn create next-app --example https://github.com/inngest/inngest-js/tree/main/examples/framework-node inngest-node
15 | ```
16 |
17 | ```bash
18 | pnpm create next-app --example https://github.com/inngest/inngest-js/tree/main/examples/framework-node inngest-node
19 | ```
20 |
21 | Open http://localhost:3000 with your browser to see the result.
22 |
23 | - [Inngest functions](https://www.inngest.com/docs/functions) are available at `inngest/`.
24 | - The [Inngest handler](https://www.inngest.com/docs/sdk/serve) is available at `index.ts`.
25 |
26 | ## Learn More
27 |
28 | - [Inngest Documentation](https://www.inngest.com/docs) - learn about the Inngest SDK, functions, and events
29 |
--------------------------------------------------------------------------------
/examples/node/index.ts:
--------------------------------------------------------------------------------
1 | import { createServer } from 'inngest/node';
2 | import { functions, inngest } from './inngest';
3 |
4 | const server = createServer({ client: inngest, functions });
5 |
6 | server.listen(3000, () => {
7 | console.log('Server running on http://localhost:3000');
8 | });
9 |
--------------------------------------------------------------------------------
/examples/node/inngest/client.ts:
--------------------------------------------------------------------------------
1 | import { Inngest } from "inngest";
2 | import { schemas } from "./types";
3 |
4 | export const inngest = new Inngest({ id: "my-express-app", schemas });
5 |
--------------------------------------------------------------------------------
/examples/node/inngest/helloWorld.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "./client";
2 |
3 | export default inngest.createFunction(
4 | { id: "hello-world" },
5 | { event: "demo/event.sent" },
6 | async ({ event, step }) => {
7 | const result = await step.waitForSignal("wait-for-it", {
8 | signal: "hmm",
9 | timeout: "5m"
10 | })
11 |
12 | return {
13 | result,
14 | message: `Hello ${event.name}!`,
15 | };
16 | }
17 | );
18 |
--------------------------------------------------------------------------------
/examples/node/inngest/index.ts:
--------------------------------------------------------------------------------
1 | import helloWorld from "./helloWorld";
2 |
3 | export const functions = [helloWorld];
4 |
5 | export { inngest } from "./client";
6 |
--------------------------------------------------------------------------------
/examples/node/inngest/types.ts:
--------------------------------------------------------------------------------
1 | import { EventSchemas } from "inngest";
2 |
3 | type DemoEventSent = {
4 | name: "demo/event.sent";
5 | data: {
6 | message: string;
7 | };
8 | };
9 |
10 | export const schemas = new EventSchemas().fromUnion();
11 |
--------------------------------------------------------------------------------
/examples/node/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "framework-nodejs",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "tsc -p tsconfig.json",
8 | "dev": "tsx index.ts",
9 | "start": "node dist/index.js"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "@types/node": "^20.5.2",
15 | "tsx": "^4.7.3",
16 | "typescript": "^5.1.6"
17 | },
18 | "dependencies": {
19 | "inngest": "file:../../packages/inngest/inngest.tgz"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 | .pnpm-debug.log*
32 |
33 | # env files (can opt-in for committing if needed)
34 | .env*
35 |
36 | # vercel
37 | .vercel
38 |
39 | # typescript
40 | *.tsbuildinfo
41 | next-env.d.ts
42 |
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/app/actions.ts:
--------------------------------------------------------------------------------
1 | "use server";
2 |
3 | import { getInngestApp } from "@/inngest";
4 | import { helloChannel } from "@/inngest/functions/helloWorld";
5 | import { getSubscriptionToken, Realtime } from "@inngest/realtime";
6 |
7 | export type HelloToken = Realtime.Token;
8 |
9 | export async function fetchRealtimeSubscriptionToken(): Promise {
10 | const token = await getSubscriptionToken(getInngestApp(), {
11 | channel: helloChannel(),
12 | topics: ["logs"],
13 | });
14 |
15 | return token;
16 | }
17 |
18 | export async function pause(): Promise {
19 | const inngest = getInngestApp();
20 | await inngest.send({
21 | name: "test/cancel.signal",
22 | });
23 | }
24 |
25 | export async function resume(): Promise {
26 | const inngest = getInngestApp();
27 | await inngest.send({
28 | name: "test/hello.world",
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/app/api/inngest/route.ts:
--------------------------------------------------------------------------------
1 | import { getInngestApp } from "@/inngest";
2 | import { helloWorld } from "@/inngest/functions/helloWorld";
3 | import { serve } from "inngest/next";
4 |
5 | const inngest = getInngestApp();
6 |
7 | // Create an API that serves zero functions
8 | export const { GET, POST, PUT } = serve({
9 | client: inngest,
10 | functions: [
11 | /* your functions will be passed here later! */
12 | helloWorld,
13 | ],
14 | });
15 |
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/inngest/inngest-js/2949293f64c65ddf30e50e4163eb8c0aec52cc83/examples/realtime/next-realtime-hooks/app/favicon.ico
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/app/globals.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
2 |
3 | :root {
4 | --background: #ffffff;
5 | --foreground: #171717;
6 | }
7 |
8 | @theme inline {
9 | --color-background: var(--background);
10 | --color-foreground: var(--foreground);
11 | --font-sans: var(--font-geist-sans);
12 | --font-mono: var(--font-geist-mono);
13 | }
14 |
15 | @media (prefers-color-scheme: dark) {
16 | :root {
17 | --background: #0a0a0a;
18 | --foreground: #ededed;
19 | }
20 | }
21 |
22 | body {
23 | background: var(--background);
24 | color: var(--foreground);
25 | font-family: Arial, Helvetica, sans-serif;
26 | }
27 |
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Geist, Geist_Mono } from "next/font/google";
3 | import "./globals.css";
4 |
5 | const geistSans = Geist({
6 | variable: "--font-geist-sans",
7 | subsets: ["latin"],
8 | });
9 |
10 | const geistMono = Geist_Mono({
11 | variable: "--font-geist-mono",
12 | subsets: ["latin"],
13 | });
14 |
15 | export const metadata: Metadata = {
16 | title: "Inngest Realtime demo",
17 | description: "Inngest Realtime using React Hooks",
18 | };
19 |
20 | export default function RootLayout({
21 | children,
22 | }: Readonly<{
23 | children: React.ReactNode;
24 | }>) {
25 | return (
26 |
27 |
30 | {children}
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import { dirname } from "path";
2 | import { fileURLToPath } from "url";
3 | import { FlatCompat } from "@eslint/eslintrc";
4 |
5 | const __filename = fileURLToPath(import.meta.url);
6 | const __dirname = dirname(__filename);
7 |
8 | const compat = new FlatCompat({
9 | baseDirectory: __dirname,
10 | });
11 |
12 | const eslintConfig = [
13 | ...compat.extends("next/core-web-vitals", "next/typescript"),
14 | ];
15 |
16 | export default eslintConfig;
17 |
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/inngest/functions/helloWorld.ts:
--------------------------------------------------------------------------------
1 | import { channel, topic } from "@inngest/realtime";
2 | import { getInngestApp } from "..";
3 |
4 | const inngest = getInngestApp();
5 |
6 | export const helloChannel = channel("hello-world").addTopic(
7 | topic("logs").type()
8 | );
9 |
10 | export const helloWorld = inngest.createFunction(
11 | { id: "hello-world" },
12 | { event: "test/hello.world" },
13 | async ({ event, step, publish, runId }) => {
14 | publish(helloChannel().logs(`Hello from ${runId}`));
15 |
16 | // wait 2 seconds before next iteration while waiting for a potential cancel signal
17 | const result = await step.waitForEvent("cancel-signal", {
18 | event: "test/cancel.signal",
19 | timeout: 2000,
20 | });
21 |
22 | if (!result) {
23 | await step.sendEvent("retrigger", {
24 | name: "test/hello.world",
25 | data: { email: event.data.email },
26 | });
27 | }
28 |
29 | return { message: `Hello!`, runId };
30 | }
31 | );
32 |
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/inngest/index.ts:
--------------------------------------------------------------------------------
1 | import { realtimeMiddleware } from "@inngest/realtime";
2 | import { Inngest } from "inngest";
3 |
4 | let app: Inngest | undefined;
5 |
6 | export const getInngestApp = () => {
7 | return (app ??= new Inngest({
8 | id: typeof window !== "undefined" ? "client" : "server",
9 | middleware: [realtimeMiddleware()],
10 | }));
11 | };
12 |
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 |
3 | const nextConfig: NextConfig = {
4 | /* config options here */
5 | };
6 |
7 | export default nextConfig;
8 |
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "realtime-next",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev --turbopack",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@inngest/realtime": "0.3.1",
13 | "inngest": "^3.35.1",
14 | "next": "15.2.1",
15 | "react": "^19.0.0",
16 | "react-dom": "^19.0.0"
17 | },
18 | "devDependencies": {
19 | "@eslint/eslintrc": "^3",
20 | "@tailwindcss/postcss": "^4",
21 | "@types/node": "^20",
22 | "@types/react": "^19",
23 | "@types/react-dom": "^19",
24 | "eslint": "^9",
25 | "eslint-config-next": "15.2.1",
26 | "tailwindcss": "^4",
27 | "typescript": "^5"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | const config = {
2 | plugins: ["@tailwindcss/postcss"],
3 | };
4 |
5 | export default config;
6 |
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/public/file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/public/globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/public/window.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/realtime/next-realtime-hooks/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/examples/realtime/nodejs/realtime-across-multiple-channels/README.md:
--------------------------------------------------------------------------------
1 | # Realtime: stream updates from multiple function runs
2 |
3 | This demo Node.js project shows how to use [Realtime](https://www.inngest.com/docs/features/realtime) to stream updates from
4 | multiple Inngest Function runs by a mix of global and dynamics channels.
5 |
6 | ```
7 | npm install
8 | ```
9 |
10 | ```
11 | npm run dev
12 | ```
13 |
14 | The app will send periodic `app/post.like` events to the server, causing
15 | publishes and the subscriptions to fire.
16 |
--------------------------------------------------------------------------------
/examples/realtime/nodejs/realtime-across-multiple-channels/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "realtime-simple",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1",
7 | "dev": "concurrently -c blue,green \"npm:dev:*\"",
8 | "dev:server": "npx inngest-cli@latest dev --sdk-url http://localhost:3000/api/inngest --retry-interval 1",
9 | "dev:app": "wait-port 8288 && tsx index.ts"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "description": "",
15 | "dependencies": {
16 | "@inngest/realtime": "0.3.1",
17 | "inngest": "^3.35.1",
18 | "zod": "^3.24.2"
19 | },
20 | "devDependencies": {
21 | "@types/node": "^22.13.10",
22 | "concurrently": "^9.1.2",
23 | "tsx": "^4.19.3",
24 | "wait-port": "^1.1.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/realtime/nodejs/realtime-human-in-the-loop/README.md:
--------------------------------------------------------------------------------
1 | # Realtime: Implementing Human in the loop with `step.waitForEvent()`
2 |
3 | This demos showcases how to combine [Realtime](https://www.inngest.com/docs/features/realtime)'s `publish()` with `step.waitForEvent()` to
4 | enable users to interact with ongoing workflows.
5 |
6 | ```
7 | npm install
8 | ```
9 |
10 | ```
11 | npm run dev
12 | ```
13 |
14 | The app will send an event kicking off a workflow and prompt in the terminal to choose
15 | to stop or continue the workflow.
16 |
--------------------------------------------------------------------------------
/examples/realtime/nodejs/realtime-human-in-the-loop/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "realtime-simple",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1",
7 | "dev": "concurrently -c blue,green \"npm:dev:*\"",
8 | "dev:server": "npx inngest-cli@latest dev --sdk-url http://localhost:3000/api/inngest --retry-interval 1",
9 | "dev:app": "wait-port 8288 && tsx index.ts"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "description": "",
15 | "dependencies": {
16 | "@inngest/realtime": "0.3.1",
17 | "inngest": "^3.35.1",
18 | "zod": "^3.24.2"
19 | },
20 | "devDependencies": {
21 | "@types/node": "^22.13.10",
22 | "concurrently": "^9.1.2",
23 | "tsx": "^4.19.3",
24 | "wait-port": "^1.1.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/realtime/nodejs/realtime-single-run-subscription/README.md:
--------------------------------------------------------------------------------
1 | # Realtime: stream updates from a single function run
2 |
3 | This demo Node.js project shows how to [stream](https://www.inngest.com/docs/features/realtime) and subscribe to updates from a single Inngest Function run.
4 |
5 | ```
6 | npm install
7 | ```
8 |
9 | ```
10 | npm run dev
11 | ```
12 |
13 | The app will send 10 `app/process-upload` events while subscribing to a specific run using a `uuid`.
14 |
--------------------------------------------------------------------------------
/examples/realtime/nodejs/realtime-single-run-subscription/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "realtime-simple",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1",
7 | "dev": "concurrently -c blue,green \"npm:dev:*\"",
8 | "dev:server": "npx inngest-cli@latest dev --sdk-url http://localhost:3000/api/inngest --retry-interval 1",
9 | "dev:app": "wait-port 8288 && tsx index.ts"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "description": "",
15 | "dependencies": {
16 | "@inngest/realtime": "0.3.1",
17 | "inngest": "^3.35.1",
18 | "zod": "^3.24.2"
19 | },
20 | "devDependencies": {
21 | "@types/node": "^22.13.10",
22 | "concurrently": "^9.1.2",
23 | "tsx": "^4.19.3",
24 | "wait-port": "^1.1.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/step-ai/anthropic-claude-pdf-processing/.env.example:
--------------------------------------------------------------------------------
1 | ANTHROPIC_API_KEY=your_anthropic_api_key # Get one at https://console.anthropic.com/settings/keys
--------------------------------------------------------------------------------
/examples/step-ai/anthropic-claude-pdf-processing/README.md:
--------------------------------------------------------------------------------
1 | # Anthropic Claude PDF Processing with `step.ai.infer()`
2 |
3 | ## Description
4 |
5 | This example demonstrates how to process a PDF document [using Anthropic Claude's capabilities](https://docs.anthropic.com/en/docs/build-with-claude/pdf-support) with `step.ai.infer()`.
6 |
7 |
8 | ## Installation
9 |
10 | 1. Clone the repository:
11 | ```bash
12 | git clone git@github.com:inngest/inngest-js.git
13 | ```
14 | 2. Navigate to the project directory:
15 | ```bash
16 | cd examples/step-ai/anthropic-claude-pdf-processing
17 | ```
18 | 3. Install the dependencies:
19 | ```bash
20 | npm install
21 | ```
22 |
23 | ## Usage
24 |
25 | 1. Build the project:
26 | ```bash
27 | npm run build
28 | ```
29 | 2. Start the server:
30 | ```bash
31 | GEMINI_API_KEY= npm start
32 | ```
33 | 3. Ensure you have a `.env` file configured with the necessary environment variables. You can use `.env.example` as a template.
34 |
--------------------------------------------------------------------------------
/examples/step-ai/anthropic-claude-pdf-processing/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "anthropic-claude-pdf-processing",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "scripts": {
6 | "build": "tsc",
7 | "start": "node dist/index.js"
8 | },
9 | "author": "",
10 | "dependencies": {
11 | "dotenv": "^16.4.7",
12 | "express": "^4.21.2",
13 | "inngest": "3.32.4"
14 | },
15 | "license": "MIT",
16 | "description": "",
17 | "devDependencies": {
18 | "@types/express": "^5.0.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "preinstall": "npx only-allow pnpm",
4 | "release:all": "pnpm run build && pnpm run release:publish && pnpm run release:tag",
5 | "build": "pnpm run --if-present --recursive build",
6 | "release:version": "npx changeset version && pnpm run --if-present --recursive release:version",
7 | "release:publish": "pnpm run --if-present --recursive release",
8 | "release:tag": "node scripts/release/tag.js",
9 | "log": "pnpm run --if-present --recursive log"
10 | },
11 | "devDependencies": {
12 | "@actions/core": "^1.10.0",
13 | "@actions/exec": "^1.1.1",
14 | "@actions/github": "^6.0.0",
15 | "@changesets/changelog-github": "^0.4.8",
16 | "@changesets/cli": "^2.26.2",
17 | "cross-env": "^7.0.3"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/ai/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | *.tgz
4 |
--------------------------------------------------------------------------------
/packages/ai/README.md:
--------------------------------------------------------------------------------
1 | # @inngest/ai
2 |
--------------------------------------------------------------------------------
/packages/ai/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import pluginJs from "@eslint/js";
2 | import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
3 | import globals from "globals";
4 | import tseslint from "typescript-eslint";
5 |
6 | /** @type {import('eslint').Linter.Config[]} */
7 | export default [
8 | { files: ["**/*.{js,mjs,cjs,ts}"] },
9 | { languageOptions: { globals: globals.browser } },
10 | pluginJs.configs.recommended,
11 | ...tseslint.configs.recommended,
12 | eslintPluginPrettierRecommended,
13 | {
14 | rules: {
15 | "@typescript-eslint/no-namespace": "off",
16 | "@typescript-eslint/ban-types": "off",
17 | },
18 | },
19 | ];
20 |
--------------------------------------------------------------------------------
/packages/ai/jsr.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://jsr.io/schema/config-file.v1.json",
3 | "name": "@inngest/ai",
4 | "description": "",
5 | "version": "0.1.4",
6 | "include": [
7 | "./src/**/*.ts"
8 | ],
9 | "exports": {
10 | ".": "./src/index.ts",
11 | "./models": "./src/models/index.ts",
12 | "./adapters": "./src/adapters/index.ts"
13 | }
14 | }
--------------------------------------------------------------------------------
/packages/ai/src/adapters/grok.ts:
--------------------------------------------------------------------------------
1 | import { type AiAdapter } from "../adapter.js";
2 | import { OpenAiAiAdapter } from "./openai.js";
3 |
4 | // Grok is an exotic one, it is an OpenAI-compatible API,
5 | // but does not support strict mode Function Calling, requiring an adapter.
6 | export interface GrokAiAdapter extends AiAdapter {
7 | /**
8 | * Format of the IO for this model
9 | */
10 | format: "grok";
11 |
12 | "~types": {
13 | input: OpenAiAiAdapter["~types"]["input"];
14 | output: OpenAiAiAdapter["~types"]["output"];
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/packages/ai/src/adapters/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./anthropic.js";
2 | export * from "./gemini.js";
3 | export * from "./openai.js";
4 | export * from "./grok.js";
5 |
--------------------------------------------------------------------------------
/packages/ai/src/index.ts:
--------------------------------------------------------------------------------
1 | export type { AiAdapter, AiAdapters } from "./adapter.js";
2 | export * from "./adapters/index.js";
3 | export * from "./models/index.js";
4 | export * as models from "./models/index.js";
5 |
--------------------------------------------------------------------------------
/packages/ai/src/models/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./anthropic.js";
2 | export * from "./gemini.js";
3 | export * from "./openai.js";
4 | export * from "./deepseek.js";
5 | export * from "./grok.js";
6 |
--------------------------------------------------------------------------------
/packages/eslint-plugin-internal/README.md:
--------------------------------------------------------------------------------
1 | A directory for custom ESLint rules specific to this repo.
2 |
--------------------------------------------------------------------------------
/packages/eslint-plugin-internal/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | rules: {
3 | "process-warn": require("./process-warn"),
4 | },
5 | };
6 |
--------------------------------------------------------------------------------
/packages/eslint-plugin-internal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@inngest/eslint-plugin-internal",
3 | "version": "1.0.0",
4 | "private": true,
5 | "main": "index.js",
6 | "scripts": {
7 | "preinstall": "npx only-allow pnpm"
8 | },
9 | "peerDependencies": {
10 | "eslint": "^8.23.1"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/eslint-plugin-internal/process-warn.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Adapted from `eslint-plugin-node`'s `no-process-env` rule.
3 | *
4 | * {@link https://github.com/mysticatea/eslint-plugin-node/blob/master/lib/rules/no-process-env.js}
5 | */
6 | "use strict";
7 |
8 | //------------------------------------------------------------------------------
9 | // Rule Definition
10 | //------------------------------------------------------------------------------
11 |
12 | module.exports = {
13 | meta: {
14 | type: "suggestion",
15 | docs: {
16 | description: "warn on use of `process.env`",
17 | },
18 | fixable: null,
19 | schema: [],
20 | messages: {
21 | dangerousProcessUsage:
22 | "Ensure process is only used in non-shared locations",
23 | },
24 | },
25 |
26 | create(context) {
27 | return {
28 | MemberExpression(node) {
29 | const objectName = node.object.name;
30 |
31 | if (objectName === "process" && !node.computed) {
32 | context.report({ node, messageId: "dangerousProcessUsage" });
33 | }
34 | },
35 | };
36 | },
37 | };
38 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/jest.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
2 | module.exports = {
3 | preset: "ts-jest",
4 | testEnvironment: "node",
5 | testMatch: ["/src/**/*.test.ts"],
6 | roots: ["/src"],
7 | };
8 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@inngest/eslint-plugin",
3 | "version": "0.0.7",
4 | "description": "",
5 | "main": "dist/index.js",
6 | "publishConfig": {
7 | "registry": "https://registry.npmjs.org"
8 | },
9 | "scripts": {
10 | "build": "tsc",
11 | "postversion": "pnpm run build",
12 | "test": "jest --silent --ci",
13 | "release": "node ../../scripts/release/publish.js",
14 | "log": "node ./test.js"
15 | },
16 | "files": [
17 | "dist"
18 | ],
19 | "keywords": [
20 | "eslint",
21 | "eslintplugin",
22 | "inngest"
23 | ],
24 | "homepage": "https://github.com/inngest/inngest-js/tree/main/packages/eslint-plugin#readme",
25 | "repository": {
26 | "type": "git",
27 | "url": "git+https://github.com/inngest/inngest-js.git",
28 | "directory": "packages/eslint-plugin"
29 | },
30 | "author": "Inngest Inc. ",
31 | "license": "ISC",
32 | "dependencies": {
33 | "@typescript-eslint/utils": "^6.11.0",
34 | "typescript": "~5.5.2"
35 | },
36 | "devDependencies": {
37 | "@typescript-eslint/rule-tester": "^6.11.0",
38 | "eslint": "^8.0.0",
39 | "jest": "^29.3.1",
40 | "ts-jest": "^29.1.0"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/src/configs/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./recommended";
2 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/src/configs/recommended.ts:
--------------------------------------------------------------------------------
1 | export const recommended = {
2 | rules: {
3 | "@inngest/await-inngest-send": "warn",
4 | "@inngest/no-nested-steps": "error",
5 | "@inngest/no-variable-mutation-in-step": "error",
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/src/index.ts:
--------------------------------------------------------------------------------
1 | export * as configs from "./configs";
2 | export { rules } from "./rules";
3 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/src/rules/await-inngest-send.test.ts:
--------------------------------------------------------------------------------
1 | import { RuleTester } from "@typescript-eslint/rule-tester";
2 | import { awaitInngestSend } from "./await-inngest-send";
3 |
4 | const ruleTester = new RuleTester({
5 | parser: "@typescript-eslint/parser",
6 | });
7 |
8 | ruleTester.run("my-rule", awaitInngestSend, {
9 | valid: [
10 | 'await inngest.send({ name: "some.event" });',
11 | 'return inngest.send({ name: "some.event" });',
12 | 'void inngest.send({ name: "some.event" });',
13 | ],
14 | invalid: [
15 | {
16 | code: 'inngest.send({ name: "some.event" });',
17 | errors: [{ messageId: "await-inngest-send" }],
18 | },
19 | ],
20 | });
21 |
--------------------------------------------------------------------------------
/packages/eslint-plugin/src/rules/index.ts:
--------------------------------------------------------------------------------
1 | import { TSESLint } from "@typescript-eslint/utils";
2 | import { awaitInngestSend } from "./await-inngest-send";
3 | import { noNestedSteps } from "./no-nested-steps";
4 | import { noVariableMutationInStep } from "./no-variable-mutation-in-step";
5 |
6 | export const rules = {
7 | "await-inngest-send": awaitInngestSend,
8 | "no-nested-steps": noNestedSteps,
9 | "no-variable-mutation-in-step": noVariableMutationInStep,
10 | } satisfies Record>>;
11 |
--------------------------------------------------------------------------------
/packages/inngest/.gitignore:
--------------------------------------------------------------------------------
1 | # Built files
2 | dist
3 |
4 | # Generated version file; always created pre-publish
5 | src/version.ts
6 |
7 | # Release-only files; tree must be clean on release, so these can't be modified
8 | .npmrc
9 |
10 | # Local dev files
11 | coverage/
12 | inngest.tgz
13 |
14 | tsdoc-metadata.json
15 |
--------------------------------------------------------------------------------
/packages/inngest/jest.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
2 | module.exports = {
3 | preset: "ts-jest",
4 | testEnvironment: "node",
5 | testMatch: ["/src/**/*.test.ts", "!**/test/functions/**/*.test.ts"],
6 | roots: ["/src"],
7 | moduleNameMapper: {
8 | "(\\..+)\\.js": "$1",
9 | "^inngest$": "/src",
10 | "^@local$": "/src",
11 | "^@local/(.*)": "/src/$1",
12 | "^@local/(.*)\\.js": "/src/$1",
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/packages/inngest/src/components/NonRetriableError.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * An error that, when thrown, indicates to Inngest that the function should
3 | * cease all execution and not retry.
4 | *
5 | * A `message` must be provided, and an optional `cause` can be provided to
6 | * provide more context to the error.
7 | *
8 | * @public
9 | */
10 | export class NonRetriableError extends Error {
11 | /**
12 | * The underlying cause of the error, if any.
13 | *
14 | * This will be serialized and sent to Inngest.
15 | */
16 | public readonly cause?: unknown;
17 |
18 | constructor(
19 | message: string,
20 | options?: {
21 | /**
22 | * The underlying cause of the error, if any.
23 | *
24 | * This will be serialized and sent to Inngest.
25 | */
26 | cause?: unknown;
27 | }
28 | ) {
29 | super(message);
30 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
31 | this.cause = options?.cause;
32 |
33 | this.name = "NonRetriableError";
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/inngest/src/components/StepError.ts:
--------------------------------------------------------------------------------
1 | import { deserializeError } from "../helpers/errors.js";
2 | import { jsonErrorSchema } from "../types.js";
3 |
4 | /**
5 | * An error that represents a step exhausting all retries and failing. This is
6 | * thrown by an Inngest step if it fails.
7 | *
8 | * It's synonymous with an `Error`, with the addition of the `stepId` that
9 | * failed.
10 | *
11 | * @public
12 | */
13 | export class StepError extends Error {
14 | public cause?: unknown;
15 |
16 | constructor(
17 | /**
18 | * The ID of the step that failed.
19 | */
20 | public readonly stepId: string,
21 | err: unknown
22 | ) {
23 | const parsedErr = jsonErrorSchema.parse(err);
24 |
25 | super(parsedErr.message);
26 | this.name = parsedErr.name;
27 | this.stepId = stepId;
28 |
29 | // Don't show the internal stack trace if we don't have one.
30 | this.stack = parsedErr.stack ?? undefined;
31 |
32 | // Try setting the cause if we have one
33 | this.cause = parsedErr.cause
34 | ? deserializeError(parsedErr.cause)
35 | : undefined;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/inngest/src/components/connect/messages.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ConnectMessage,
3 | GatewayExecutorRequestData,
4 | StartRequest,
5 | StartResponse,
6 | WorkerReplyAckData,
7 | } from "../../proto/src/components/connect/protobuf/connect.js";
8 |
9 | export function createStartRequest(excludeGateways: string[]) {
10 | return StartRequest.encode(
11 | StartRequest.create({
12 | excludeGateways,
13 | })
14 | ).finish();
15 | }
16 |
17 | export async function parseStartResponse(r: Response) {
18 | const startResp = StartResponse.decode(new Uint8Array(await r.arrayBuffer()));
19 | return startResp;
20 | }
21 |
22 | export function parseConnectMessage(r: Uint8Array) {
23 | const connectMessage = ConnectMessage.decode(r);
24 | return connectMessage;
25 | }
26 |
27 | export function parseGatewayExecutorRequest(r: Uint8Array) {
28 | const gatewayExecutorRequest = GatewayExecutorRequestData.decode(r);
29 | return gatewayExecutorRequest;
30 | }
31 |
32 | export function parseWorkerReplyAck(r: Uint8Array) {
33 | const workerReplyAck = WorkerReplyAckData.decode(r);
34 | return workerReplyAck;
35 | }
36 |
--------------------------------------------------------------------------------
/packages/inngest/src/components/execution/otel/access.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A file used to access client processors safely without also importing any
3 | * otel-specific libraries. Useful for ensuring that the otel libraries can be
4 | * tree-shaken if they're not used directly by the user.
5 | */
6 |
7 | import { type Inngest } from "../../Inngest.js";
8 | import { type InngestSpanProcessor } from "./processor.js";
9 |
10 | /**
11 | * A map of Inngest clients to their OTel span processors. This is used to
12 | * ensure that we only create one span processor per client, and that we can
13 | * access the span processor from the client without exposing the OTel
14 | * libraries to the user.
15 | */
16 | export const clientProcessorMap = new WeakMap<
17 | Inngest.Any,
18 | InngestSpanProcessor
19 | >();
20 |
--------------------------------------------------------------------------------
/packages/inngest/src/components/execution/otel/consts.ts:
--------------------------------------------------------------------------------
1 | export const debugPrefix = "inngest:otel";
2 |
3 | export enum TraceStateKey {
4 | AppId = "inngest@app",
5 | FunctionId = "inngest@fn",
6 | }
7 |
8 | export enum Attribute {
9 | InngestTraceparent = "inngest.traceparent",
10 | InngestRunId = "sdk.run.id",
11 | InngestAppId1 = "sdk.app.id",
12 | InngestAppId2 = "sys.app.id",
13 | InngestFunctionId = "sys.function.id",
14 | }
15 |
--------------------------------------------------------------------------------
/packages/inngest/src/connect.ts:
--------------------------------------------------------------------------------
1 | export * from "./components/connect/index.js";
2 |
--------------------------------------------------------------------------------
/packages/inngest/src/experimental.ts:
--------------------------------------------------------------------------------
1 | // AsyncLocalStorage
2 | export { getAsyncCtx } from "./components/execution/als.js";
3 | export type { AsyncContext } from "./components/execution/als.js";
4 |
5 | // OpenTelemetry
6 | export { otelMiddleware } from "./components/execution/otel/middleware.js";
7 | export type { OTelMiddlewareOptions } from "./components/execution/otel/middleware.js";
8 | export { PublicInngestSpanProcessor as InngestSpanProcessor } from "./components/execution/otel/processor.js";
9 |
--------------------------------------------------------------------------------
/packages/inngest/src/express.test.ts:
--------------------------------------------------------------------------------
1 | import * as ExpressHandler from "@local/express";
2 | import { type VercelRequest, type VercelRequestQuery } from "@vercel/node";
3 | import { testFramework } from "./test/helpers";
4 |
5 | testFramework("Express", ExpressHandler);
6 |
7 | testFramework("Express (Vercel)", ExpressHandler, {
8 | transformReq: (expressReq, res) => {
9 | const req: Partial = {
10 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
11 | body: expressReq.body,
12 | headers: expressReq.headers,
13 | query: expressReq.query as VercelRequestQuery,
14 | method: expressReq.method,
15 | url: expressReq.url,
16 | };
17 |
18 | return [req, res];
19 | },
20 | });
21 |
--------------------------------------------------------------------------------
/packages/inngest/src/fastify.test.ts:
--------------------------------------------------------------------------------
1 | import * as FastifyHandler from "@local/fastify";
2 | import { fromAny } from "@total-typescript/shoehorn";
3 | import { type Response } from "express";
4 | import { type FastifyReply, type FastifyRequest } from "fastify";
5 | import { testFramework } from "./test/helpers";
6 |
7 | class MockFastifyReply {
8 | constructor(public res: Response) {}
9 |
10 | public header(key: string, value: string) {
11 | this.res.header(key, value);
12 | }
13 |
14 | public code(code: number) {
15 | this.res.statusCode = code;
16 | }
17 |
18 | public send(body: unknown) {
19 | this.res.send(body);
20 | }
21 | }
22 |
23 | testFramework("Fastify", FastifyHandler, {
24 | transformReq: (req, res): [req: FastifyRequest, reply: FastifyReply] => {
25 | return [fromAny(req), fromAny(new MockFastifyReply(res))];
26 | },
27 | });
28 |
--------------------------------------------------------------------------------
/packages/inngest/src/h3.test.ts:
--------------------------------------------------------------------------------
1 | import * as h3Handler from "@local/h3";
2 | import { createEvent } from "h3";
3 | import { testFramework } from "./test/helpers";
4 |
5 | testFramework("h3", h3Handler, {
6 | transformReq(req, res) {
7 | return [createEvent(req, res)];
8 | },
9 | });
10 |
--------------------------------------------------------------------------------
/packages/inngest/src/helpers/crypto.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Create a cryptographically secure random value.
3 | *
4 | * @throws {Error} If the crypto module is not available.
5 | */
6 | export function createEntropy(byteLength: number): Uint8Array {
7 | const bytes = new Uint8Array(byteLength);
8 |
9 | // https://developer.mozilla.org/en-US/docs/Web/API/Crypto#browser_compatibility
10 | const { crypto } = globalThis;
11 | if (!crypto) {
12 | // This should only happen in Node <19.
13 | throw new Error("missing crypto module");
14 | }
15 | if (!crypto.getRandomValues) {
16 | throw new Error("missing crypto.getRandomValues");
17 | }
18 |
19 | crypto.getRandomValues(bytes);
20 | return bytes;
21 | }
22 |
--------------------------------------------------------------------------------
/packages/inngest/src/helpers/enum.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Returns the value of an enum from a string value.
3 | *
4 | * If the value given is not a value from the enum, `undefined` is returned.
5 | */
6 | export const enumFromValue = >(
7 | enumType: T,
8 | value: unknown
9 | ): T[keyof T] | undefined => {
10 | if (Object.values(enumType).includes(value)) {
11 | return value as T[keyof T];
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/packages/inngest/src/helpers/validators/index.ts:
--------------------------------------------------------------------------------
1 | import { type IsUnknown } from "../types.js";
2 | import type * as z from "./zod";
3 |
4 | /**
5 | * Given an input value, infer the output type.
6 | *
7 | * This is a helper type to infer the output type of schemas, ensuring we can
8 | * support many validation libraries here without having to write custom
9 | * validators for each.
10 | *
11 | * @public
12 | */
13 | export type ResolveSchema<
14 | TInput,
15 | TFallback = TInput,
16 | TUnknownFallback = TFallback,
17 | > = IsUnknown extends true
18 | ? TUnknownFallback
19 | : TInput extends z.ZodTypeAny
20 | ? z.infer
21 | : TFallback;
22 |
23 | /**
24 | * A valid input schema for an event's `data`.
25 | *
26 | * @public
27 | */
28 | export type ValidSchemaInput = z.ValidZodValue;
29 |
30 | /**
31 | * A valid output schema.
32 | *
33 | * @public
34 | */
35 | export type ValidSchemaOutput = z.ZodTypeAny;
36 |
--------------------------------------------------------------------------------
/packages/inngest/src/koa.test.ts:
--------------------------------------------------------------------------------
1 | import * as KoaHandler from "@local/koa";
2 | import { createMockContext } from "@shopify/jest-koa-mocks";
3 | import { type Dictionary } from "@shopify/jest-koa-mocks/build/ts/create-mock-cookies";
4 | import { type RequestMethod } from "node-mocks-http";
5 | import { testFramework } from "./test/helpers";
6 |
7 | testFramework("Koa", KoaHandler, {
8 | transformReq: (req, _res, _env) => {
9 | const ctx = createMockContext({
10 | url: `https://${req.headers.host || req.hostname}${req.url}`,
11 | method: req.method as RequestMethod,
12 | statusCode: req.statusCode,
13 | headers: req.headers as Dictionary,
14 | host: req.hostname,
15 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
16 | requestBody: req.body,
17 | });
18 |
19 | return [ctx];
20 | },
21 | transformRes: (args) => {
22 | const ctx = args[0] as ReturnType;
23 |
24 | return Promise.resolve({
25 | status: ctx.status,
26 | body: ctx.body as string,
27 | headers: ctx.response.headers as Record,
28 | });
29 | },
30 | });
31 |
--------------------------------------------------------------------------------
/packages/inngest/src/lambda.test.ts:
--------------------------------------------------------------------------------
1 | import * as LambdaHandler from "@local/lambda";
2 | import { type APIGatewayProxyResult } from "aws-lambda";
3 | import { testFramework } from "./test/helpers";
4 |
5 | testFramework("AWS Lambda", LambdaHandler, {
6 | transformReq: (req, _res, _env) => {
7 | return [
8 | {
9 | path: req.path,
10 | // Intentionally make headers uppercase to ensure we test normalizing
11 | // them for mocked Lambda requests, which do not normalize.
12 | // See https://github.com/inngest/inngest-js/pull/937
13 | headers: Object.fromEntries(
14 | Object.entries(req.headers).map(([key, value]) => [
15 | key.toUpperCase(),
16 | value,
17 | ])
18 | ),
19 | httpMethod: req.method,
20 | queryStringParameters: req.query,
21 | body:
22 | typeof req.body === "string" ? req.body : JSON.stringify(req.body),
23 | },
24 | {},
25 | ];
26 | },
27 |
28 | transformRes: async (_args, retP: Promise) => {
29 | const ret = await retP;
30 |
31 | return {
32 | status: ret.statusCode,
33 | body: ret.body || "",
34 | headers: (ret.headers || {}) as Record,
35 | };
36 | },
37 | });
38 |
--------------------------------------------------------------------------------
/packages/inngest/src/middleware/dependencyInjection.test.ts:
--------------------------------------------------------------------------------
1 | import { Inngest } from "@local/components/Inngest";
2 | import { dependencyInjectionMiddleware } from "@local/middleware/dependencyInjection";
3 | import { assertType } from "../test/helpers";
4 |
5 | describe("Mutates ctx", () => {
6 | test("ctx is injected into the function input", () => {
7 | const inngest = new Inngest({
8 | id: "test",
9 | middleware: [
10 | dependencyInjectionMiddleware({
11 | foo: "bar",
12 | }),
13 | ],
14 | });
15 |
16 | inngest.createFunction({ id: "test" }, { event: "" }, (ctx) => {
17 | assertType(ctx.foo);
18 | });
19 | });
20 |
21 | test("can infer const ctx type", () => {
22 | const inngest = new Inngest({
23 | id: "test",
24 | middleware: [
25 | dependencyInjectionMiddleware({
26 | foo: "bar",
27 | } as const),
28 | ],
29 | });
30 |
31 | inngest.createFunction({ id: "test" }, { event: "" }, (ctx) => {
32 | assertType<"bar">(ctx.foo);
33 | });
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/packages/inngest/src/middleware/dependencyInjection.ts:
--------------------------------------------------------------------------------
1 | import { InngestMiddleware } from "../components/InngestMiddleware.js";
2 |
3 | /**
4 | * Adds properties to the function input for every function created using this
5 | * app.
6 | */
7 | // We can use `const` here yet due to TS constraints.
8 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
9 | export const dependencyInjectionMiddleware = >(
10 | /**
11 | * The context to inject into the function input.
12 | */
13 | ctx: TCtx
14 | ) => {
15 | return new InngestMiddleware({
16 | name: "Inngest: Dependency Injection",
17 | init() {
18 | return {
19 | onFunctionRun() {
20 | return {
21 | transformInput() {
22 | return {
23 | ctx,
24 | };
25 | },
26 | };
27 | },
28 | };
29 | },
30 | });
31 | };
32 |
--------------------------------------------------------------------------------
/packages/inngest/src/next.test.ts:
--------------------------------------------------------------------------------
1 | import * as NextHandler from "@local/next";
2 | import { testFramework } from "./test/helpers";
3 |
4 | testFramework("Next", NextHandler);
5 |
--------------------------------------------------------------------------------
/packages/inngest/src/nitro.test.ts:
--------------------------------------------------------------------------------
1 | import * as NitroHandler from "@local/nitro";
2 | import { createEvent } from "h3";
3 | import { testFramework } from "./test/helpers";
4 |
5 | testFramework("Nitro", NitroHandler, {
6 | transformReq(req, res) {
7 | return [createEvent(req, res)];
8 | },
9 | });
10 |
--------------------------------------------------------------------------------
/packages/inngest/src/nitro.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * An adapter for Nitro to serve and register any declared functions with
3 | * Inngest, making them available to be triggered by events.
4 | *
5 | * @module
6 | */
7 |
8 | import {
9 | type InternalServeHandlerOptions,
10 | type ServeHandlerOptions,
11 | } from "./components/InngestCommHandler.js";
12 | import { serve as serveH3 } from "./h3.js";
13 | import { type SupportedFrameworkName } from "./types.js";
14 |
15 | /**
16 | * The name of the framework, used to identify the framework in Inngest
17 | * dashboards and during testing.
18 | */
19 | export const frameworkName: SupportedFrameworkName = "nitro";
20 |
21 | /**
22 | * In Nitro, serve and register any declared functions with Inngest, making them
23 | * available to be triggered by events.
24 | *
25 | * @public
26 | */
27 | // Has explicit return type to avoid JSR-defined "slow types"
28 | export const serve = (
29 | options: ServeHandlerOptions
30 | ): ReturnType => {
31 | const optsOverrides: InternalServeHandlerOptions = {
32 | ...options,
33 | frameworkName,
34 | };
35 |
36 | return serveH3(optsOverrides);
37 | };
38 |
--------------------------------------------------------------------------------
/packages/inngest/src/node.test.ts:
--------------------------------------------------------------------------------
1 | import http from "node:http";
2 | import { Socket } from "node:net";
3 | import { Buffer } from "node:buffer";
4 | import * as NodeHandler from "@local/node";
5 | import { testFramework } from "./test/helpers";
6 |
7 | testFramework("Node", NodeHandler, {
8 | transformReq: (req, res) => {
9 | const socket = new Socket();
10 | const nodeReq = new http.IncomingMessage(socket);
11 |
12 | // Set the method and URL
13 | nodeReq.method = req.method;
14 | nodeReq.url = req.url;
15 |
16 | if (req.protocol === "https") {
17 | nodeReq.headers["x-forwarded-proto"] = req.protocol;
18 | }
19 |
20 | // Set headers
21 | for (const [key, value] of Object.entries(req.headers)) {
22 | nodeReq.headers[key.toLowerCase()] = value;
23 | }
24 |
25 | // Mock the body data
26 | const bodyData = Buffer.from(JSON.stringify(req.body));
27 |
28 | // Override the read methods to return the body data
29 | nodeReq.push(bodyData);
30 | nodeReq.push(null); // Signals the end of the stream
31 |
32 | return [nodeReq, res];
33 | },
34 | });
35 |
--------------------------------------------------------------------------------
/packages/inngest/src/nuxt.test.ts:
--------------------------------------------------------------------------------
1 | import * as NuxtHandler from "@local/nuxt";
2 | import { createEvent } from "h3";
3 | import { testFramework } from "./test/helpers";
4 |
5 | testFramework("Nuxt", NuxtHandler, {
6 | transformReq(req, res) {
7 | return [createEvent(req, res)];
8 | },
9 | });
10 |
--------------------------------------------------------------------------------
/packages/inngest/src/nuxt.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * An adapter for Nuxt to serve and register any declared functions with
3 | * Inngest, making them available to be triggered by events.
4 | *
5 | * @module
6 | */
7 |
8 | import {
9 | type InternalServeHandlerOptions,
10 | type ServeHandlerOptions,
11 | } from "./components/InngestCommHandler.js";
12 | import { serve as serveH3 } from "./h3.js";
13 | import { type SupportedFrameworkName } from "./types.js";
14 |
15 | /**
16 | * The name of the framework, used to identify the framework in Inngest
17 | * dashboards and during testing.
18 | */
19 | export const frameworkName: SupportedFrameworkName = "nuxt";
20 |
21 | /**
22 | * In Nuxt 3, serve and register any declared functions with Inngest, making
23 | * them available to be triggered by events.
24 | *
25 | * @public
26 | */
27 | // Has explicit return type to avoid JSR-defined "slow types"
28 | export const serve = (
29 | options: ServeHandlerOptions
30 | ): ReturnType => {
31 | const optsOverrides: InternalServeHandlerOptions = {
32 | ...options,
33 | frameworkName,
34 | };
35 |
36 | return serveH3(optsOverrides);
37 | };
38 |
--------------------------------------------------------------------------------
/packages/inngest/src/redwood.test.ts:
--------------------------------------------------------------------------------
1 | import * as RedwoodHandler from "@local/redwood";
2 | import { testFramework } from "./test/helpers";
3 |
4 | testFramework("Redwood.js", RedwoodHandler, {
5 | transformReq: (req, _res, _env) => {
6 | return [
7 | {
8 | path: req.path,
9 | headers: req.headers,
10 | httpMethod: req.method,
11 | queryStringParameters: req.query,
12 | body:
13 | typeof req.body === "string" ? req.body : JSON.stringify(req.body),
14 | },
15 | {},
16 | ];
17 | },
18 |
19 | // eslint-disable-next-line @typescript-eslint/require-await
20 | transformRes: async (_args, ret: RedwoodHandler.RedwoodResponse) => {
21 | return {
22 | status: ret.statusCode,
23 | body: ret.body || "",
24 | headers: ret.headers || {},
25 | };
26 | },
27 | });
28 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/README.md:
--------------------------------------------------------------------------------
1 | # Inngest SDK Examples
2 |
3 | This directory contains examples of how to use the Inngest SDK.
4 |
5 | They are copied to various example repos to test against when building releases.
6 |
7 | - [**Hello World**](/src/examples/hello-world) - The simplest function
8 | - [**Parallel reduce**](/src/examples/parallel-reduce) - Accumulate values using reduce in parallel
9 | - [**Parallel work**](/src/examples/parallel-work) - Run concurrent chains of work in the same function
10 | - [**Polling**](/src/examples/polling) - Poll an external API for a change, timing out after a given period
11 | - [**Promise.all**](/src/examples/promise-all) - Basic usage of `Promise.all` with step tooling
12 | - [**Promise.race**](/src/examples/promise-race) - Basic usage of `Promise.race` with step tooling
13 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/client.ts:
--------------------------------------------------------------------------------
1 | import { Inngest } from "inngest";
2 |
3 | export const inngest = new Inngest({
4 | id: "example-app",
5 | eventKey: "test-key-123",
6 | });
7 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/handling-step-errors/index.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "../client";
2 |
3 | export default inngest.createFunction(
4 | { id: "handling-step-errors", retries: 1 },
5 | { event: "demo/handling.step.errors" },
6 | async ({ step }) => {
7 | try {
8 | await step.run("a", () => {
9 | throw new Error("Oh no!", {
10 | cause: new Error("This is the cause"),
11 | });
12 | });
13 | } catch (err) {
14 | await step.run("b", () => {
15 | return `err was: "${err.message}" and the cause was: "${err.cause.message}"`;
16 | });
17 | }
18 |
19 | await Promise.all([
20 | step.run("c succeeds", () => "c succeeds"),
21 | step
22 | .run("d fails", () => {
23 | throw new Error("D failed!");
24 | })
25 | .catch((err: Error) => {
26 | return step.run("e succeeds", () => {
27 | return {
28 | errMessage: err.message,
29 | };
30 | });
31 | }),
32 | ]);
33 | }
34 | );
35 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/hello-world/README.md:
--------------------------------------------------------------------------------
1 | # Hello World Example
2 |
3 | This demonstrates the simplest possible function. It's triggered by a `demo/hello.world` event and returns `"Hello, Inngest!"`.
4 |
5 | ```mermaid
6 | graph TD
7 | Inngest -->|demo/hello.world| Function
8 | Function -->|Returns| Hello[Hello, Inngest!]
9 | ```
10 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/hello-world/index.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2 | import {
3 | checkIntrospection,
4 | eventRunWithName,
5 | runHasTimeline,
6 | sendEvent,
7 | } from "@local/test/helpers";
8 |
9 | checkIntrospection({
10 | name: "hello-world",
11 | triggers: [{ event: "demo/hello.world" }],
12 | });
13 |
14 | describe("run", () => {
15 | let eventId: string;
16 | let runId: string;
17 |
18 | beforeAll(async () => {
19 | eventId = await sendEvent("demo/hello.world");
20 | });
21 |
22 | test("runs in response to 'demo/hello.world'", async () => {
23 | runId = await eventRunWithName(eventId, "hello-world");
24 | expect(runId).toEqual(expect.any(String));
25 | }, 60000);
26 |
27 | test("returns 'Hello, Inngest!'", async () => {
28 | const item = await runHasTimeline(runId, {
29 | type: "StepCompleted",
30 | stepName: "step",
31 | });
32 |
33 | expect(item).toBeDefined();
34 |
35 | const output = await item?.getOutput();
36 | expect(output).toEqual("Hello, Inngest!");
37 | }, 60000);
38 | });
39 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/hello-world/index.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "../client";
2 |
3 | export default inngest.createFunction(
4 | { id: "hello-world" },
5 | { event: "demo/hello.world" },
6 | () => "Hello, Inngest!"
7 | );
8 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/index.ts:
--------------------------------------------------------------------------------
1 | import handlingStepErrors from "./handling-step-errors";
2 | import helloWorld from "./hello-world";
3 | import multipleTriggers from "./multiple-triggers";
4 | import parallelReduce from "./parallel-reduce";
5 | import parallelWork from "./parallel-work";
6 | import polling from "./polling";
7 | import promiseAll from "./promise-all";
8 | import promiseRace from "./promise-race";
9 | import sendEvent from "./send-event";
10 | import sequentialReduce from "./sequential-reduce";
11 | import stepInvokeFunctions from "./step-invoke";
12 | import stepInvokeNotFound from "./step-invoke-not-found";
13 | import undefinedData from "./undefined-data";
14 | import unhandledStepErrors from "./unhandled-step-errors";
15 |
16 | export const functions = [
17 | helloWorld,
18 | promiseAll,
19 | promiseRace,
20 | parallelWork,
21 | parallelReduce,
22 | sequentialReduce,
23 | polling,
24 | sendEvent,
25 | undefinedData,
26 | ...stepInvokeFunctions,
27 | stepInvokeNotFound,
28 | handlingStepErrors,
29 | unhandledStepErrors,
30 | multipleTriggers,
31 | ];
32 |
33 | export { inngest } from "./client";
34 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/multiple-triggers/index.test.ts:
--------------------------------------------------------------------------------
1 | import {
2 | checkIntrospection,
3 | eventRunWithName,
4 | runHasTimeline,
5 | sendEvent,
6 | } from "@local/test/helpers";
7 | import { events } from ".";
8 |
9 | checkIntrospection({
10 | name: "multiple-triggers",
11 | triggers: events.map((event) => ({ event })),
12 | });
13 |
14 | describe("run", () => {
15 | events.forEach((eventName) => {
16 | let eventId: string;
17 | let runId: string;
18 |
19 | beforeAll(async () => {
20 | eventId = await sendEvent(eventName);
21 | });
22 |
23 | test(`runs in response to '${eventName}'`, async () => {
24 | runId = await eventRunWithName(eventId, "multiple-triggers");
25 | expect(runId).toEqual(expect.any(String));
26 | }, 60000);
27 |
28 | test(`returns 'Hello, ${eventName}!'`, async () => {
29 | const item = await runHasTimeline(runId, {
30 | type: "StepCompleted",
31 | stepName: "step",
32 | });
33 |
34 | expect(item).toBeDefined();
35 |
36 | const output = await item?.getOutput();
37 | expect(output).toEqual(`Hello, ${eventName}!`);
38 | }, 60000);
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/multiple-triggers/index.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "../client";
2 |
3 | export const events = ["demo/multiple-triggers.1", "demo/multiple-triggers.2"];
4 |
5 | export default inngest.createFunction(
6 | { id: "multiple-triggers" },
7 | events.map((event) => ({ event })),
8 | ({ event }) => `Hello, ${event.name}!`
9 | );
10 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@inngest/example-functions",
3 | "private": true,
4 | "version": "1.0.0",
5 | "description": "",
6 | "main": "index.js",
7 | "scripts": {
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "devDependencies": {
14 | "@types/jest": "^27.4.1"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/parallel-reduce/README.md:
--------------------------------------------------------------------------------
1 | # Parallel Reduce Example
2 |
3 | This example demonstrates how to run multiple steps in parallel to accumulate a value using `Array.prototype.reduce`.
4 |
5 | It is triggered by a `demo/parallel.reduce` event, runs three steps in parallel to fetch scores from a database, and accumulates the total of all of the scores.
6 |
7 | ```mermaid
8 | graph TD
9 | Inngest -->|demo/parallel.reduce| Function
10 | Function -->|Run step| blue[Get blue team score]
11 | Function -->|Run step| red[Get red team score]
12 | Function -->|Run step| green[Get green team score]
13 | blue & red & green --> total[Accumulate score]
14 | total -->|Returns| done[150]
15 | ```
16 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/parallel-reduce/index.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "../client";
2 |
3 | const scoresDb: Record = {
4 | blue: 50,
5 | red: 25,
6 | green: 75,
7 | };
8 |
9 | export default inngest.createFunction(
10 | { id: "parallel-reduce" },
11 | { event: "demo/parallel.reduce" },
12 | async ({ step }) => {
13 | const teams = Object.keys(scoresDb);
14 |
15 | // Fetch every team's score in parallel and add them up
16 | const totalScores = await teams.reduce(async (score, team) => {
17 | const teamScore = await step.run(
18 | `Get ${team} team score`,
19 | () => scoresDb[team]
20 | );
21 |
22 | return (await score) + teamScore;
23 | }, Promise.resolve(0));
24 |
25 | return totalScores;
26 | }
27 | );
28 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/parallel-work/README.md:
--------------------------------------------------------------------------------
1 | # Parallel Work Example
2 |
3 | This example demonstrates how to run concurrent chains of work in the same step function, bringing their values together at the end.
4 |
5 | It is triggered by a `demo/parallel.work` event, runs 2 separate chains of work in parallel: `getScore()` and `getFruits()`.
6 |
7 | `getScore()` will run 3 steps sequentially, returning a `number` score.
8 |
9 | `getFruits()` will run 3 steps in parallel, returning an array of fruits.
10 |
11 | Finally, we return the result of these two chains of work at the end of the function.
12 |
13 | ```mermaid
14 | graph TD
15 | Inngest -->|demo/parallel.work| Function
16 |
17 | subgraph getScore["getScore (sequential)"]
18 | score1[First score]
19 | score1 -->|Run step| score2[Second score]
20 | score2 -->|Run step| score3[Third score]
21 | end
22 |
23 | subgraph getFruits["getFruits (parallel)"]
24 | apple[Get apple]
25 | banana[Get banana]
26 | orange[Get orange]
27 | end
28 |
29 | Function -->|Run steps| getScore & getFruits
30 |
31 | getFruits ----> ret[Accumulate results]
32 | score3 --> ret
33 | ret -->|Returns| done["[ 6, 'Apple, Banana, Orange' ]"]
34 | ```
35 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/parallel-work/index.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "../client";
2 |
3 | export default inngest.createFunction(
4 | { id: "parallel-work" },
5 | { event: "demo/parallel.work" },
6 | async ({ step }) => {
7 | // Run some steps in sequence to add up scores
8 | const getScore = async () => {
9 | let score = await step.run("First score", () => 1);
10 | score += await step.run("Second score", () => 2);
11 | score += await step.run("Third score", () => 3);
12 |
13 | return score;
14 | };
15 |
16 | // Retrieve some fruits in parallel and return them as an array
17 | const getFruits = async () => {
18 | return Promise.all([
19 | step.run("Get apple", () => "Apple"),
20 | step.run("Get banana", () => "Banana"),
21 | step.run("Get orange", () => "Orange"),
22 | ]);
23 | };
24 |
25 | // Run both of the above functions in parallel and return the results
26 | return Promise.all([
27 | getScore(),
28 | getFruits().then((fruits) => fruits.join(", ")),
29 | ]);
30 | }
31 | );
32 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/polling/README.md:
--------------------------------------------------------------------------------
1 | # Parallel Work Example
2 |
3 | This example demonstrates how to run concurrent chains of work in the same step function to create a step that polls on a schedule, either returning a result or timing out.
4 |
5 | It is triggered by a `demo/polling` event, and runs 2 separate chains of work in parallel: one for the timeout, and one for the poll. We declare this logic as a `poll()` function within the body of the step function.
6 |
7 | ```mermaid
8 | graph TD
9 | Inngest -->|demo/polling| Function
10 |
11 | subgraph poll
12 | timeout[["step.sleep('30s')"]]
13 | timeout ------>|30s up|timeoutDone[Set timedOut=true]
14 | api[["step.run('Check if external job complete')"]]
15 | api --> check{Job returned data?}
16 | check -->|No| timeoutCheck{timedOut==true?}
17 | timeoutCheck -->|No| iterate["step.sleep('10s')"]
18 | iterate -->|10s up|timeoutCheck2{timedOut==true?}
19 | timeoutCheck2 -->|No|api
20 | end
21 |
22 | Function -->|Runs| poll
23 |
24 | timeoutCheck2 --->|Yes|noJob[jobData = null]
25 | check ------>|Yes| yesJob["jobData = {...}"]
26 | timeoutCheck -->|Yes|noJob
27 |
28 | yesJob -->|Runs|doData["step.run('Do something with data')"]
29 | doData & noJob --> finish([End])
30 | ```
31 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/polling/index.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2 | import { checkIntrospection } from "@local/test/helpers";
3 |
4 | checkIntrospection({
5 | name: "polling",
6 | triggers: [{ event: "demo/polling" }],
7 | });
8 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/polling/index.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "../client";
2 |
3 | export default inngest.createFunction(
4 | { id: "polling" },
5 | { event: "demo/polling" },
6 | async ({ step }) => {
7 | const poll = async () => {
8 | let timedOut = false;
9 | void step.sleep("polling-time-out", "30s").then(() => (timedOut = true));
10 | let interval = 0;
11 |
12 | do {
13 | const jobData = await step.run("Check if external job complete", () => {
14 | const jobComplete = Math.random() > 0.5;
15 |
16 | if (jobComplete) {
17 | return { data: { foo: "bar" } };
18 | }
19 |
20 | return null;
21 | });
22 |
23 | if (jobData !== null) {
24 | return jobData;
25 | }
26 |
27 | await step.sleep(`interval-${interval++}`, "10s");
28 | } while (!timedOut);
29 |
30 | return null;
31 | };
32 |
33 | const jobData = await poll();
34 |
35 | if (jobData) {
36 | await step.run("Do something with data", () => {
37 | console.log(jobData);
38 | });
39 | }
40 | }
41 | );
42 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/promise-all/README.md:
--------------------------------------------------------------------------------
1 | # Promise.all Example
2 |
3 | This example demonstrates using `Promise.all()` to wait for concurrent chains of work to resolve before continuing; all step tooling returns a promise, so any pattern using async JavaScript is supported.
4 |
5 | It is triggered by a `demo/promise.all` event, and runs 2 separate steps in parallel. Once both are complete, Step 3 runs and adds the result of both.
6 |
7 | ```mermaid
8 | graph TD
9 | Inngest -->|demo/promise.all| Function
10 |
11 | Function --> step1["steps.run('Step 1')"]
12 | Function --> step2["steps.run('Step 2')"]
13 | step1 & step2 --> step3["steps.run('Step 3')"]
14 | step3 --> ret["3"]
15 | ```
16 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/promise-all/index.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "../client";
2 |
3 | export default inngest.createFunction(
4 | { id: "promise-all" },
5 | { event: "demo/promise.all" },
6 | async ({ step }) => {
7 | const [one, two] = await Promise.all([
8 | step.run("Step 1", () => 1),
9 | step.run("Step 2", () => 2),
10 | ]);
11 |
12 | return step.run("Step 3", () => one + two);
13 | }
14 | );
15 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/promise-race/README.md:
--------------------------------------------------------------------------------
1 | # Promise.race Example
2 |
3 | This example demonstrates using `Promise.race()` to race concurrent chains of work against each other before continuing; all step tooling returns a promise, so any pattern using async JavaScript is supported.
4 |
5 | It is triggered by a `demo/promise.race` event, and runs 2 separate steps in parallel. The first one to complete wins, and the final step logs the winner.
6 |
7 | ```mermaid
8 | graph TD
9 | Inngest -->|demo/promise.race| Function
10 |
11 | Function --> step1["steps.run('Step A')"]
12 | Function --> step2["steps.run('Step B')"]
13 | step1 -->|A wins| step3["steps.run('Step C')"]
14 | step2 -->|B wins| step3
15 | step3 --> ret["'A is the winner!'
or
'B is the winner!'"]
16 | ```
17 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/promise-race/index.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "../client";
2 |
3 | export default inngest.createFunction(
4 | { id: "promise-race" },
5 | { event: "demo/promise.race" },
6 | async ({ step }) => {
7 | const winner = await Promise.race([
8 | step.run("Step A", () => "A"),
9 | step.run("Step B", () => "B"),
10 | ]);
11 |
12 | await step.run("Step C", () => `${winner} is the winner!`);
13 | }
14 | );
15 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/send-event/README.md:
--------------------------------------------------------------------------------
1 | # Sending an Event Example
2 |
3 | This examples demonstrates sending an event within a step function.
4 |
5 | It is triggered by a `demo/send.event` event, and runs a single step to reliably send an event to Inngest.
6 |
7 | ```mermaid
8 | graph TD
9 | Inngest -->|demo/send.event| Function
10 | Function --> step1["step.sendEvent('app/my.event.happened')"]
11 | step1 --> ret[Complete]
12 | ```
13 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/send-event/index.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "../client";
2 |
3 | export default inngest.createFunction(
4 | { id: "send-event" },
5 | { event: "demo/send.event" },
6 | async ({ step }) => {
7 | await Promise.all([
8 | // Send a single event
9 | step.sendEvent("single-event", {
10 | name: "app/my.event.happened",
11 | data: { foo: "bar" },
12 | }),
13 |
14 | // Send multiple events
15 | step.sendEvent("multiple-events", [
16 | {
17 | name: "app/my.event.happened.multiple.1",
18 | data: { foo: "bar" },
19 | },
20 | {
21 | name: "app/my.event.happened.multiple.2",
22 | data: { foo: "bar" },
23 | },
24 | ]),
25 | ]);
26 | }
27 | );
28 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/sequential-reduce/README.md:
--------------------------------------------------------------------------------
1 | # Sequential Reduce Example
2 |
3 | This example demonstrates how to run multiple steps in sequence to accumulate a value using `Array.prototype.reduce`.
4 |
5 | It is triggered by a `demo/sequential.reduce` event, runs three steps sequentially to fetch scores from a database, and accumulates the total of all of the scores.
6 |
7 | To see a parallel version of this pattern, see the [parallel reduce example](/src/examples/parallel-reduce).
8 |
9 | ```mermaid
10 | graph TD
11 | Inngest -->|demo/sequential.reduce| Function
12 | Function -->|Run step| blue[Get blue team score]
13 | blue -->|Run step| red[Get red team score]
14 | red -->|Run step| green[Get green team score]
15 | green --> total[Accumulate score]
16 | total -->|Returns| done[150]
17 | ```
18 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/sequential-reduce/index.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "../client";
2 |
3 | const scoresDb: Record = {
4 | blue: 50,
5 | red: 25,
6 | green: 75,
7 | };
8 |
9 | export default inngest.createFunction(
10 | { id: "sequential-reduce" },
11 | { event: "demo/sequential.reduce" },
12 | async ({ step }) => {
13 | const teams = Object.keys(scoresDb);
14 |
15 | // Fetch every team's score sequentially and add them up
16 | const totalScores = await teams.reduce(async (score, team) => {
17 | const currentScore = await score;
18 |
19 | const teamScore = await step.run(
20 | `Get ${team} team score`,
21 | () => scoresDb[team]
22 | );
23 |
24 | return currentScore + teamScore;
25 | }, Promise.resolve(0));
26 |
27 | return totalScores;
28 | }
29 | );
30 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/step-invoke-not-found/index.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-unsafe-member-access */
2 | /* eslint-disable @typescript-eslint/no-unsafe-assignment */
3 | import {
4 | checkIntrospection,
5 | eventRunWithName,
6 | runHasTimeline,
7 | sendEvent,
8 | } from "@local/test/helpers";
9 |
10 | checkIntrospection({
11 | name: "step-invoke-not-found",
12 | triggers: [{ event: "demo/step.invoke.not-found" }],
13 | });
14 |
15 | describe("run", () => {
16 | let eventId: string;
17 | let runId: string;
18 |
19 | beforeAll(async () => {
20 | eventId = await sendEvent("demo/step.invoke.not-found");
21 | });
22 |
23 | test("runs in response to 'demo/step.invoke.not-found'", async () => {
24 | runId = await eventRunWithName(eventId, "step-invoke-not-found");
25 | expect(runId).toEqual(expect.any(String));
26 | }, 20000);
27 |
28 | test("ran 'invoke-non-existent-fn' step", async () => {
29 | const item = await runHasTimeline(runId, {
30 | type: "StepFailed",
31 | stepName: "step",
32 | });
33 | expect(item).toBeDefined();
34 |
35 | const output = await item?.getOutput();
36 | expect(output?.name).toEqual("Error");
37 | expect(output?.message).toContain("could not find function");
38 | }, 20000);
39 | });
40 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/step-invoke-not-found/index.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "../client";
2 |
3 | export default inngest.createFunction(
4 | { id: "step-invoke-not-found" },
5 | { event: "demo/step.invoke.not-found" },
6 | async ({ step }) => {
7 | await step.invoke("invoke-non-existent-fn", {
8 | function: "non-existant-fn",
9 | });
10 | }
11 | );
12 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/step-invoke/index.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "../client";
2 |
3 | const cronInvokeFn = inngest.createFunction(
4 | { id: "step-invoke-cron" },
5 | { cron: "59 23 31 12 *" },
6 | async ({ step }) => {
7 | await step.sleep("wait-a-moment", "1s");
8 |
9 | return {
10 | cronInvokeDone: true,
11 | };
12 | }
13 | );
14 |
15 | const eventInvokeFn = inngest.createFunction(
16 | { id: "step-invoke-event" },
17 | { event: "demo/step.invoke.other" },
18 | async ({ step }) => {
19 | await step.sleep("wait-a-moment", "1s");
20 |
21 | return {
22 | eventInvokeDone: true,
23 | };
24 | }
25 | );
26 |
27 | const mainFn = inngest.createFunction(
28 | { id: "step-invoke" },
29 | { event: "demo/step.invoke" },
30 | async ({ step }) => {
31 | return Promise.all([
32 | step.invoke("event-fn", { function: eventInvokeFn }),
33 | step.invoke("cron-fn", { function: cronInvokeFn }),
34 | ]);
35 | }
36 | );
37 |
38 | export default [mainFn, eventInvokeFn, cronInvokeFn];
39 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "inngest": ["../../../dist"],
5 | "@local/*": ["../../*"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/undefined-data/index.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "../client";
2 |
3 | export default inngest.createFunction(
4 | { id: "undefined-data" },
5 | { event: "demo/undefined.data" },
6 | async ({ step }) => {
7 | await step.run("step1res", () => "step1res");
8 |
9 | await step.run("step1", () => {
10 | // no-op
11 | });
12 |
13 | await Promise.all([
14 | step.run("step2res", () => "step2res"),
15 | step.run("step2nores", () => {
16 | // no-op
17 | }),
18 | step.run("step2res2", () => "step2res2"),
19 | ]);
20 |
21 | await step.run("step2", async () => {
22 | // no-op
23 | });
24 |
25 | await step.run("step3", async () => {
26 | // no-op
27 | });
28 | }
29 | );
30 |
--------------------------------------------------------------------------------
/packages/inngest/src/test/functions/unhandled-step-errors/index.ts:
--------------------------------------------------------------------------------
1 | import { inngest } from "../client";
2 |
3 | export default inngest.createFunction(
4 | { id: "unhandled-step-errors", retries: 1 },
5 | { event: "demo/unhandled.step.errors" },
6 | async ({ step }) => {
7 | await step.run("a fails", () => {
8 | throw new Error("A failed!");
9 | });
10 |
11 | await step.run("b never runs", () => "b");
12 | }
13 | );
14 |
--------------------------------------------------------------------------------
/packages/inngest/test/benchmark/execution/index.ts:
--------------------------------------------------------------------------------
1 | import { group } from "mitata";
2 | import { loadBenchmarks } from "../util";
3 |
4 | // Give me Bun
5 | export const register = async () => {
6 | const benchmarks = await loadBenchmarks(["memoized-steps"]);
7 |
8 | group("execution", () => {
9 | benchmarks.register();
10 | });
11 | };
12 |
--------------------------------------------------------------------------------
/packages/inngest/test/benchmark/execution/memoized-steps.ts:
--------------------------------------------------------------------------------
1 | import { createExecutionWithMemoizedSteps } from "./util";
2 |
3 | const stepCounts = [0, 1, 10, 100, 500, 1000, 2000, 5000, 10000];
4 |
5 | export default stepCounts.reduce((acc, stepCount) => {
6 | const { run } = createExecutionWithMemoizedSteps({ stepCount });
7 |
8 | return {
9 | ...acc,
10 | [stepCount]: run,
11 | };
12 | }, {});
13 |
--------------------------------------------------------------------------------
/packages/inngest/test/benchmark/main.ts:
--------------------------------------------------------------------------------
1 | import { run } from "mitata";
2 | import { register } from "./execution";
3 |
4 | register().then(() => {
5 | return run();
6 | });
7 |
--------------------------------------------------------------------------------
/packages/inngest/test/composite_project/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | tsconfig.tsbuildinfo
4 | package-lock.json
5 |
--------------------------------------------------------------------------------
/packages/inngest/test/composite_project/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "t_composite_test",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "tsc --build --force"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "devDependencies": {
13 | "typescript": "^5.3.3"
14 | },
15 | "dependencies": {
16 | "inngest": "file:../../inngest.tgz"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/inngest/test/composite_project/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "target": "es5",
5 | "forceConsistentCasingInFileNames": true,
6 | "composite": true,
7 | "lib": ["dom", "dom.iterable", "esnext"],
8 | "allowJs": true,
9 | "skipLibCheck": false,
10 | "strict": true,
11 | "noEmit": false,
12 | "outDir": "dist",
13 | "esModuleInterop": true,
14 | "module": "esnext",
15 | "moduleResolution": "bundler",
16 | "resolveJsonModule": true,
17 | "isolatedModules": true,
18 | "jsx": "preserve",
19 | "incremental": true,
20 | "typeRoots": ["./types"],
21 | "plugins": [
22 | {
23 | "name": "next",
24 | },
25 | ],
26 | "paths": {
27 | "~/*": ["./src/*"],
28 | },
29 | },
30 | "include": [
31 | "next-env.d.ts",
32 | "src/**/*.ts",
33 | "**/*.tsx",
34 | ".next/types/**/*.ts",
35 | ],
36 | "exclude": ["node_modules"],
37 | }
38 |
--------------------------------------------------------------------------------
/packages/inngest/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": [
4 | "src/**/*.spec.ts",
5 | "src/**/*.test.ts",
6 | "src/test/**/*",
7 | "scripts/**/*"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/inngest/tsconfig.types.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": ["./**/*.test.ts"],
4 | "exclude": ["src/test/functions/**/*"],
5 | "compilerOptions": {
6 | "paths": {
7 | "@local": ["./dist"],
8 | "@local/*": ["./dist/*"]
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/middleware-encryption/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 |
--------------------------------------------------------------------------------
/packages/middleware-encryption/eslint.config.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | import eslint from "@eslint/js";
4 | import tseslint from "typescript-eslint";
5 |
6 | export default tseslint.config(
7 | eslint.configs.recommended,
8 | ...tseslint.configs.recommended
9 | );
10 |
--------------------------------------------------------------------------------
/packages/middleware-encryption/jest.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('ts-jest').JestConfigWithTsJest} **/
2 | module.exports = {
3 | testEnvironment: "node",
4 | transform: {
5 | "^.+.tsx?$": ["ts-jest", {}],
6 | },
7 | roots: ["/src"],
8 | };
9 |
--------------------------------------------------------------------------------
/packages/middleware-encryption/jsr.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://jsr.io/schema/config-file.v1.json",
3 | "name": "@inngest/middleware-encryption",
4 | "description": "E2E encryption middleware for Inngest.",
5 | "version": "1.0.1",
6 | "include": [
7 | "./src/**/*.ts"
8 | ],
9 | "exclude": [],
10 | "exports": {
11 | ".": "./src/index.ts",
12 | "./manual": "./src/manual.ts",
13 | "./strategies/aes": "./src/strategies/aes.ts",
14 | "./strategies/libsodium": "./src/strategies/libSodium.ts"
15 | }
16 | }
--------------------------------------------------------------------------------
/packages/middleware-encryption/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./middleware";
2 |
--------------------------------------------------------------------------------
/packages/middleware-encryption/src/manual.ts:
--------------------------------------------------------------------------------
1 | import { InngestMiddleware, type MiddlewareOptions } from "inngest";
2 | import { type EncryptionMiddlewareOptions } from "./middleware";
3 | import {
4 | getEncryptionStages,
5 | } from "./stages";
6 |
7 | /**
8 | * Encrypts and decrypts data sent to and from Inngest.
9 | *
10 | * Returns two separate middlewares: one for encrypting data, and one for
11 | * decrypting data, used in special circumstances pre-v4.
12 | */
13 | export const manualEncryptionMiddleware = (
14 | /**
15 | * Options used to configure the encryption middleware. If a custom
16 | * `encryptionService` is not provided, the `key` option is required.
17 | */
18 | opts: EncryptionMiddlewareOptions
19 | ): {
20 | encryptionMiddleware: InngestMiddleware;
21 | decryptionMiddleware: InngestMiddleware;
22 | } => {
23 | const { encrypt, decrypt } = getEncryptionStages(opts);
24 |
25 | return {
26 | encryptionMiddleware: new InngestMiddleware({
27 | name: "@inngest/middleware-encryption/manual/encrypt",
28 | init: () => encrypt,
29 | }),
30 |
31 | decryptionMiddleware: new InngestMiddleware({
32 | name: "@inngest/middleware-encryption/manual/decrypt",
33 | init: () => decrypt,
34 | }),
35 | };
36 | };
37 |
--------------------------------------------------------------------------------
/packages/middleware-encryption/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/middleware-encryption/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2016",
4 | "module": "commonjs",
5 | "rootDir": "./src",
6 | "declaration": true,
7 | "outDir": "./dist",
8 | "esModuleInterop": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "strict": true,
11 | "skipLibCheck": true
12 | },
13 | "include": ["./src/**/*"]
14 | }
15 |
--------------------------------------------------------------------------------
/packages/middleware-sentry/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 |
--------------------------------------------------------------------------------
/packages/middleware-sentry/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @inngest/middleware-sentry
2 |
3 | ## 0.1.2
4 |
5 | ### Patch Changes
6 |
7 | - [#810](https://github.com/inngest/inngest-js/pull/810) [`47b08dd`](https://github.com/inngest/inngest-js/commit/47b08dd8e5d1a47c28be528e8df9f44244578ac8) Thanks [@djfarrelly](https://github.com/djfarrelly)! - Updated to use Sentry's withIsolationScope
8 |
9 | ## 0.1.1
10 |
11 | ### Patch Changes
12 |
13 | - [#673](https://github.com/inngest/inngest-js/pull/673) [`42f0e71`](https://github.com/inngest/inngest-js/commit/42f0e71e55186941378159e57c752c177bf79b42) Thanks [@mattddean](https://github.com/mattddean)! - Add event ID as a Sentry tag
14 |
15 | - [#672](https://github.com/inngest/inngest-js/pull/672) [`b637d3a`](https://github.com/inngest/inngest-js/commit/b637d3a5cee9bd4792912185077ab9184ba6d364) Thanks [@mattddean](https://github.com/mattddean)! - Set sentry transaction name according to Inngest function
16 |
17 | ## 0.1.0
18 |
19 | ### Minor Changes
20 |
21 | - [#598](https://github.com/inngest/inngest-js/pull/598) [`cb4fdfd`](https://github.com/inngest/inngest-js/commit/cb4fdfdcd39b5051b87e736d3c18948dec9c2b30) Thanks [@jpwilliams](https://github.com/jpwilliams)! - Initial release!
22 |
--------------------------------------------------------------------------------
/packages/middleware-sentry/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import pluginJs from "@eslint/js";
2 | import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
3 | import globals from "globals";
4 | import tseslint from "typescript-eslint";
5 |
6 | /** @type {import('eslint').Linter.Config[]} */
7 | export default [
8 | { files: ["**/src/*.{js,mjs,cjs,ts}"] },
9 | { languageOptions: { globals: globals.browser } },
10 | { ignores: ["**/node_modules/**", "**/dist/**"] },
11 | pluginJs.configs.recommended,
12 | ...tseslint.configs.recommended,
13 | eslintPluginPrettierRecommended,
14 | ];
15 |
--------------------------------------------------------------------------------
/packages/middleware-sentry/jsr.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://jsr.io/schema/config-file.v1.json",
3 | "name": "@inngest/middleware-sentry",
4 | "description": "Sentry middleware for Inngest.",
5 | "version": "0.1.2",
6 | "include": [
7 | "./src/**/*.ts"
8 | ],
9 | "exclude": [],
10 | "exports": {
11 | ".": "./src/index.ts"
12 | }
13 | }
--------------------------------------------------------------------------------
/packages/middleware-sentry/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./middleware";
2 |
--------------------------------------------------------------------------------
/packages/middleware-validation/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 |
--------------------------------------------------------------------------------
/packages/middleware-validation/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @inngest/middleware-validation
2 |
3 | ## 0.0.2
4 |
5 | ### Patch Changes
6 |
7 | - [#953](https://github.com/inngest/inngest-js/pull/953) [`6ac90b5`](https://github.com/inngest/inngest-js/commit/6ac90b5680c8f4f7dbe9fcdc3c6fda3a5d4e284c) Thanks [@jpwilliams](https://github.com/jpwilliams)! - Fix possibility of silently failing when multiple versions of Zod are installed
8 |
9 | - Updated dependencies [[`a641cc2`](https://github.com/inngest/inngest-js/commit/a641cc219846a2c6ef66ad62fb371725555e7caa)]:
10 | - inngest@3.35.0
11 |
12 | ## 0.0.1
13 |
14 | ### Patch Changes
15 |
16 | - [#744](https://github.com/inngest/inngest-js/pull/744) [`0c2bb8e`](https://github.com/inngest/inngest-js/commit/0c2bb8e048f39500e25ed0b521db210bbc4a757d) Thanks [@jpwilliams](https://github.com/jpwilliams)! - Initial release of `@inngest/middleware-validation`
17 |
18 | - Updated dependencies [[`255416c`](https://github.com/inngest/inngest-js/commit/255416c4478ac367381da0c166b6762056d94e1d), [`efc6c79`](https://github.com/inngest/inngest-js/commit/efc6c79d5a1baf7a011396b8406aea4982f03778)]:
19 | - inngest@3.27.0
20 |
--------------------------------------------------------------------------------
/packages/middleware-validation/eslint.config.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | import eslint from "@eslint/js";
4 | import tseslint from "typescript-eslint";
5 |
6 | export default tseslint.config(
7 | eslint.configs.recommended,
8 | ...tseslint.configs.recommended
9 | );
10 |
--------------------------------------------------------------------------------
/packages/middleware-validation/jest.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('ts-jest').JestConfigWithTsJest} **/
2 | module.exports = {
3 | testEnvironment: "node",
4 | transform: {
5 | "^.+.tsx?$": ["ts-jest", {}],
6 | },
7 | roots: ["/src"],
8 | };
9 |
--------------------------------------------------------------------------------
/packages/middleware-validation/jsr.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://jsr.io/schema/config-file.v1.json",
3 | "name": "@inngest/middleware-validation",
4 | "description": "Schema validation middleware for Inngest.",
5 | "version": "0.0.2",
6 | "include": [
7 | "./src/**/*.ts"
8 | ],
9 | "exclude": [
10 | "**/*.test.*",
11 | "*.js",
12 | "**/tsconfig.*"
13 | ],
14 | "exports": {
15 | ".": "./src/index.ts"
16 | }
17 | }
--------------------------------------------------------------------------------
/packages/middleware-validation/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./middleware";
2 |
--------------------------------------------------------------------------------
/packages/middleware-validation/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/middleware-validation/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2016",
4 | "module": "commonjs",
5 | "rootDir": "./src",
6 | "declaration": true,
7 | "outDir": "./dist",
8 | "esModuleInterop": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "strict": true,
11 | "skipLibCheck": true
12 | },
13 | "include": ["./src/**/*"]
14 | }
15 |
--------------------------------------------------------------------------------
/packages/realtime/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | *.tgz
4 |
--------------------------------------------------------------------------------
/packages/realtime/README.md:
--------------------------------------------------------------------------------
1 | # @inngest/realtime
2 |
--------------------------------------------------------------------------------
/packages/realtime/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import pluginJs from "@eslint/js";
2 | import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
3 | import globals from "globals";
4 | import tseslint from "typescript-eslint";
5 |
6 | /** @type {import('eslint').Linter.Config[]} */
7 | export default [
8 | { files: ["**/*.{js,mjs,cjs,ts}"] },
9 | { languageOptions: { globals: globals.browser } },
10 | pluginJs.configs.recommended,
11 | ...tseslint.configs.recommended,
12 | eslintPluginPrettierRecommended,
13 | {
14 | rules: {
15 | "@typescript-eslint/no-namespace": "off",
16 | "@typescript-eslint/ban-types": "off",
17 | },
18 | },
19 | ];
20 |
--------------------------------------------------------------------------------
/packages/realtime/jest.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('ts-jest').JestConfigWithTsJest} **/
2 | module.exports = {
3 | testEnvironment: "node",
4 | transform: {
5 | "^.+.tsx?$": ["ts-jest", {}],
6 | },
7 | roots: ["/src"],
8 | };
9 |
--------------------------------------------------------------------------------
/packages/realtime/jsr.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://jsr.io/schema/config-file.v1.json",
3 | "name": "@inngest/realtime",
4 | "description": "",
5 | "version": "0.3.1",
6 | "include": [
7 | "./src/**/*.ts"
8 | ],
9 | "exports": {
10 | ".": "./src/index.ts",
11 | "./hooks": "./src/hooks.ts"
12 | }
13 | }
--------------------------------------------------------------------------------
/packages/realtime/src/env.d.ts:
--------------------------------------------------------------------------------
1 | // For Vite
2 | interface ImportMeta {
3 | env: {
4 | INNGEST_DEV?: string;
5 | VITE_INNGEST_DEV?: string;
6 | MODE: "development" | "production";
7 | VITE_MODE: "development" | "production";
8 | INNGEST_BASE_URL?: string;
9 | VITE_INNGEST_BASE_URL?: string;
10 | INNGEST_API_BASE_URL?: string;
11 | VITE_INNGEST_API_BASE_URL?: string;
12 | INNGEST_SIGNING_KEY?: string;
13 | INNGEST_SIGNING_KEY_FALLBACK?: string;
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/packages/realtime/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./channel";
2 | export * from "./middleware";
3 | export * from "./subscribe";
4 | export * from "./topic";
5 | export * from "./types";
6 |
--------------------------------------------------------------------------------
/packages/realtime/src/subscribe/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./helpers";
2 |
--------------------------------------------------------------------------------
/packages/test/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | inngest-test.tgz
4 |
--------------------------------------------------------------------------------
/packages/test/eslint.config.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | import eslint from "@eslint/js";
4 | import tseslint from "typescript-eslint";
5 |
6 | export default tseslint.config(
7 | eslint.configs.recommended,
8 | ...tseslint.configs.recommended
9 | );
10 |
--------------------------------------------------------------------------------
/packages/test/jest.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('ts-jest').JestConfigWithTsJest} **/
2 | module.exports = {
3 | testEnvironment: "node",
4 | transform: {
5 | "^.+.tsx?$": ["ts-jest", {}],
6 | },
7 | roots: ["/src"],
8 | };
9 |
--------------------------------------------------------------------------------
/packages/test/jsr.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://jsr.io/schema/config-file.v1.json",
3 | "name": "@inngest/test",
4 | "description": "Tooling for testing Inngest functions.",
5 | "version": "0.1.6",
6 | "include": [
7 | "./src/**/*.ts"
8 | ],
9 | "exclude": [],
10 | "exports": {
11 | ".": "./src/index.ts"
12 | }
13 | }
--------------------------------------------------------------------------------
/packages/test/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./InngestTestEngine";
2 | export * from "./InngestTestRun";
3 | export * from "./util";
4 |
--------------------------------------------------------------------------------
/packages/test/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/test/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2016",
4 | "module": "commonjs",
5 | "rootDir": "./src",
6 | "declaration": true,
7 | "sourceMap": true,
8 | "outDir": "./dist",
9 | "esModuleInterop": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "strict": true,
12 | "skipLibCheck": true,
13 | "noUncheckedIndexedAccess": true,
14 | "strictNullChecks": true,
15 | "moduleResolution": "node"
16 | },
17 | "include": ["./src/**/*"]
18 | }
19 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - 'packages/*'
3 |
--------------------------------------------------------------------------------
/scripts/generateReleaseConfig.js:
--------------------------------------------------------------------------------
1 | const fs = require("node:fs");
2 | const path = require("node:path");
3 |
4 | const config = {
5 | $schema: "https://unpkg.com/@changesets/config@2.3.0/schema.json",
6 | changelog: "@changesets/cli/changelog",
7 | commit: false,
8 | fixed: [],
9 | linked: [],
10 | access: "public",
11 | baseBranch: process.env.BRANCH || "main",
12 | updateInternalDependencies: "patch",
13 | ignore: [],
14 | };
15 |
16 | console.log("Writing release config:", config);
17 |
18 | const rootDir = path.join(__dirname, "..");
19 | process.chdir(rootDir);
20 |
21 | const changesetDir = path.join(rootDir, ".changeset");
22 | const configName = "config.json";
23 | const configPath = path.join(changesetDir, configName);
24 |
25 | const serializedConfig = JSON.stringify(config, null, 2);
26 |
27 | fs.writeFileSync(configPath, serializedConfig);
28 |
--------------------------------------------------------------------------------
/scripts/release/jsrVersion.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const fs = require("fs");
3 | const { exec: rawExec, getExecOutput } = require("@actions/exec");
4 |
5 | const packageRootDir = process.cwd();
6 | const version = process.env.npm_package_version;
7 |
8 | const exec = async (...args) => {
9 | const exitCode = await rawExec(...args);
10 | if (exitCode !== 0) {
11 | throw new Error(`Command exited with ${exitCode}`);
12 | }
13 | };
14 |
15 | (async () => {
16 | // If this package has a `jsr.json` file, also update the version there
17 | const jsrFilePath = path.join(packageRootDir, "jsr.json");
18 | if (!fs.existsSync(jsrFilePath)) {
19 | return;
20 | }
21 |
22 | const packageJson = JSON.parse(
23 | fs.readFileSync(path.join(packageRootDir, "package.json"), "utf8")
24 | );
25 |
26 | const version = packageJson.version;
27 | console.log({ version });
28 |
29 | const jsr = JSON.parse(fs.readFileSync(jsrFilePath, "utf8"));
30 | jsr.version = version;
31 | fs.writeFileSync(jsrFilePath, JSON.stringify(jsr, null, 2));
32 | })();
33 |
--------------------------------------------------------------------------------
/scripts/release/tag.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const { exec: rawExec, getExecOutput } = require("@actions/exec");
3 |
4 | const branch = process.env.BRANCH;
5 | if (branch !== "main" && !branch.endsWith(".x")) {
6 | throw new Error(
7 | `Stopping release from branch ${branch}; only "main" and "v*.x" branches are allowed to release`
8 | );
9 | }
10 | console.log("branch:", branch);
11 |
12 | const exec = async (...args) => {
13 | const exitCode = await rawExec(...args);
14 | if (exitCode !== 0) {
15 | throw new Error(`Command exited with ${exitCode}`);
16 | }
17 | };
18 |
19 | const repoRootDir = path.join(__dirname, "..", "..");
20 |
21 | (async () => {
22 | // Tag and push the release commit
23 | console.log('running "changeset tag" to tag the release commit');
24 | await exec("changeset", ["tag"], { cwd: repoRootDir });
25 |
26 | console.log(`pushing git tags to origin/${branch}`);
27 | await exec("git", ["push", "--follow-tags", "origin", branch]);
28 | })();
29 |
--------------------------------------------------------------------------------