@boostkit/queue-inngest 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +168 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/package.json +37 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 BoostKit
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# @boostkit/queue-inngest
|
|
2
|
+
|
|
3
|
+
Inngest serverless queue adapter for `@boostkit/queue`.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @boostkit/queue-inngest inngest
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### 1. Configure the queue
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
// config/queue.ts
|
|
17
|
+
import { Env } from '@boostkit/core/support'
|
|
18
|
+
import { SendWelcomeEmailJob } from '../app/Jobs/SendWelcomeEmailJob.js'
|
|
19
|
+
|
|
20
|
+
export default {
|
|
21
|
+
default: 'inngest',
|
|
22
|
+
connections: {
|
|
23
|
+
inngest: {
|
|
24
|
+
driver: 'inngest',
|
|
25
|
+
appId: Env.get('INNGEST_APP_ID', 'my-forge-app'),
|
|
26
|
+
signingKey: Env.get('INNGEST_SIGNING_KEY'),
|
|
27
|
+
eventKey: Env.get('INNGEST_EVENT_KEY'),
|
|
28
|
+
baseUrl: Env.get('APP_URL', 'http://localhost:3000'),
|
|
29
|
+
jobs: [
|
|
30
|
+
SendWelcomeEmailJob,
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 2. Register the provider
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// bootstrap/providers.ts
|
|
41
|
+
import { queue } from '@boostkit/queue'
|
|
42
|
+
import configs from '../config/index.js'
|
|
43
|
+
|
|
44
|
+
export default [
|
|
45
|
+
queue(configs.queue),
|
|
46
|
+
]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 3. Mount the Inngest endpoint
|
|
50
|
+
|
|
51
|
+
Inngest requires an HTTP endpoint to receive function registration and event delivery. Mount it in your routes:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
// routes/api.ts
|
|
55
|
+
import { router } from '@boostkit/router'
|
|
56
|
+
import { inngest } from '@boostkit/queue-inngest'
|
|
57
|
+
import configs from '../config/index.js'
|
|
58
|
+
|
|
59
|
+
// Mount the Inngest serve handler at /api/inngest
|
|
60
|
+
router.all('/api/inngest', inngest(configs.queue.connections.inngest).serveHandler())
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 4. Define jobs
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
// app/Jobs/SendWelcomeEmailJob.ts
|
|
67
|
+
import { Job } from '@boostkit/queue'
|
|
68
|
+
|
|
69
|
+
export class SendWelcomeEmailJob extends Job {
|
|
70
|
+
static retries = 3 // Optional: override default retry count
|
|
71
|
+
|
|
72
|
+
constructor(private readonly data: { email: string; name: string }) {
|
|
73
|
+
super()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async handle(): Promise<void> {
|
|
77
|
+
await sendEmail({
|
|
78
|
+
to: this.data.email,
|
|
79
|
+
subject: 'Welcome!',
|
|
80
|
+
body: `Hello, ${this.data.name}!`,
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 5. Dispatch jobs
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { SendWelcomeEmailJob } from '../app/Jobs/SendWelcomeEmailJob.js'
|
|
90
|
+
|
|
91
|
+
await SendWelcomeEmailJob.dispatch({
|
|
92
|
+
data: { email: 'alice@example.com', name: 'Alice' }
|
|
93
|
+
}).send()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## API Reference
|
|
97
|
+
|
|
98
|
+
- `InngestConfig`
|
|
99
|
+
- `inngest(config)` → `QueueAdapterProvider`
|
|
100
|
+
- `inngest(config).serveHandler()` → route handler for `router.all('/api/inngest', ...)`
|
|
101
|
+
|
|
102
|
+
## Configuration
|
|
103
|
+
|
|
104
|
+
- `InngestConfig`
|
|
105
|
+
- `driver` — `'inngest'`
|
|
106
|
+
- `appId` — unique app identifier shown in the Inngest dashboard
|
|
107
|
+
- `signingKey?` — Inngest signing key (required in production)
|
|
108
|
+
- `eventKey?` — Inngest event key (required in production)
|
|
109
|
+
- `baseUrl?` — app's public URL (Inngest uses this to call your serve endpoint)
|
|
110
|
+
- `jobs` — array of job classes to register with Inngest
|
|
111
|
+
|
|
112
|
+
## Event Naming
|
|
113
|
+
|
|
114
|
+
Forge maps job class names to Inngest event names using the pattern:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
forge/job.<ClassName>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
For example, `SendWelcomeEmailJob` is dispatched as the `forge/job.SendWelcomeEmailJob` event.
|
|
121
|
+
|
|
122
|
+
## Retries
|
|
123
|
+
|
|
124
|
+
Override the default retry count on a job class:
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
export class SendWelcomeEmailJob extends Job {
|
|
128
|
+
static retries = 5 // retry up to 5 times on failure
|
|
129
|
+
// ...
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Development Setup
|
|
134
|
+
|
|
135
|
+
In development, run the [Inngest Dev Server](https://www.inngest.com/docs/dev-server) locally:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npx inngest-cli@latest dev
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The dev server runs at `http://localhost:8288` and automatically discovers your functions by calling the `/api/inngest` endpoint. No signing key is needed in development.
|
|
142
|
+
|
|
143
|
+
Set `APP_URL` so Inngest can reach your endpoint:
|
|
144
|
+
|
|
145
|
+
```dotenv
|
|
146
|
+
APP_URL=http://localhost:3000
|
|
147
|
+
INNGEST_APP_ID=my-forge-app
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Production Setup
|
|
151
|
+
|
|
152
|
+
In production (Inngest Cloud):
|
|
153
|
+
|
|
154
|
+
```dotenv
|
|
155
|
+
INNGEST_APP_ID=my-forge-app
|
|
156
|
+
INNGEST_SIGNING_KEY=signkey-prod-...
|
|
157
|
+
INNGEST_EVENT_KEY=eventkey-...
|
|
158
|
+
APP_URL=https://my-app.example.com
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Inngest will call your `/api/inngest` endpoint to register functions and deliver events.
|
|
162
|
+
|
|
163
|
+
## Notes
|
|
164
|
+
|
|
165
|
+
- All job classes must be listed in the `jobs` array for Inngest to register them
|
|
166
|
+
- The `/api/inngest` route must be publicly reachable for Inngest to deliver events
|
|
167
|
+
- Inngest handles retries, concurrency, and observability automatically via the dashboard
|
|
168
|
+
- `serveHandler()` returns a `MiddlewareHandler` compatible with `router.all()`
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Job, QueueAdapterProvider } from '@boostkit/queue';
|
|
2
|
+
export interface InngestConfig {
|
|
3
|
+
appId?: string;
|
|
4
|
+
eventKey?: string;
|
|
5
|
+
signingKey?: string;
|
|
6
|
+
/**
|
|
7
|
+
* Job classes to register as Inngest functions.
|
|
8
|
+
* Each class is mapped to a "forge/job.<ClassName>" event.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* jobs: [WelcomeUserJob, ProcessOrderJob]
|
|
12
|
+
*/
|
|
13
|
+
jobs?: (new (...args: never[]) => Job)[];
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
export declare function inngest(config?: InngestConfig): QueueAdapterProvider;
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,GAAG,EAEH,oBAAoB,EAErB,MAAM,iBAAiB,CAAA;AAIxB,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAO,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAI,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,CAAA;IACxC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AA4ED,wBAAgB,OAAO,CAAC,MAAM,GAAE,aAAkB,GAAG,oBAAoB,CAMxE"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Inngest } from 'inngest';
|
|
2
|
+
import { serve } from 'inngest/hono';
|
|
3
|
+
// ─── Helpers ───────────────────────────────────────────────
|
|
4
|
+
/** Derives the Inngest event name from a Job class */
|
|
5
|
+
function eventName(JobClass) {
|
|
6
|
+
return `forge/job.${JobClass.name}`;
|
|
7
|
+
}
|
|
8
|
+
// ─── Inngest Adapter ───────────────────────────────────────
|
|
9
|
+
class InngestAdapter {
|
|
10
|
+
client;
|
|
11
|
+
handler;
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.client = new Inngest({
|
|
14
|
+
id: config.appId ?? 'forge-app',
|
|
15
|
+
...(config.eventKey ? { eventKey: config.eventKey } : {}),
|
|
16
|
+
});
|
|
17
|
+
// Register each job class as an Inngest function.
|
|
18
|
+
// When Inngest calls the serve endpoint for an event, it reconstructs
|
|
19
|
+
// the job from the serialized payload and calls handle().
|
|
20
|
+
const functions = (config.jobs ?? []).map((JobClass) => this.client.createFunction({
|
|
21
|
+
id: JobClass.name,
|
|
22
|
+
name: JobClass.name,
|
|
23
|
+
retries: (JobClass.retries ?? 3),
|
|
24
|
+
}, { event: eventName(JobClass) }, async ({ event }) => {
|
|
25
|
+
const payload = event.data['payload'] ?? {};
|
|
26
|
+
// Reconstruct the job instance from the serialized payload.
|
|
27
|
+
// TypeScript `private` fields are plain JS properties at runtime,
|
|
28
|
+
// so Object.assign correctly restores them.
|
|
29
|
+
const job = Object.assign(new JobClass(), payload);
|
|
30
|
+
await job.handle();
|
|
31
|
+
}));
|
|
32
|
+
// Build the Hono-compatible serve handler for GET + POST /api/inngest.
|
|
33
|
+
// The Hono Context (c) is passed directly — it IS req.raw in the Forge adapter.
|
|
34
|
+
this.handler = serve({
|
|
35
|
+
client: this.client,
|
|
36
|
+
functions,
|
|
37
|
+
...(config.signingKey ? { signingKey: config.signingKey } : {}),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
async dispatch(job, options = {}) {
|
|
41
|
+
const name = job.constructor.name;
|
|
42
|
+
await this.client.send({
|
|
43
|
+
name: eventName({ name }),
|
|
44
|
+
data: {
|
|
45
|
+
job: name,
|
|
46
|
+
payload: JSON.parse(JSON.stringify(job)),
|
|
47
|
+
queue: options.queue ?? 'default',
|
|
48
|
+
},
|
|
49
|
+
...(options.delay ? { ts: Date.now() + options.delay } : {}),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
serveHandler() {
|
|
53
|
+
return this.handler;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// ─── Factory ───────────────────────────────────────────────
|
|
57
|
+
export function inngest(config = {}) {
|
|
58
|
+
return {
|
|
59
|
+
create() {
|
|
60
|
+
return new InngestAdapter(config);
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@boostkit/queue-inngest",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/boostkitjs/boostkit",
|
|
8
|
+
"directory": "packages/queue-inngest"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": "./dist/index.js",
|
|
19
|
+
"default": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"inngest": "^3.0.0",
|
|
25
|
+
"@boostkit/queue": "0.0.1"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^20.0.0",
|
|
29
|
+
"typescript": "^5.4.0"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsc",
|
|
33
|
+
"dev": "tsc --watch",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"clean": "rm -rf dist"
|
|
36
|
+
}
|
|
37
|
+
}
|