@blokjs/trigger-worker 0.6.17 → 0.6.19
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/dist/WorkerTrigger.d.ts +27 -3
- package/dist/WorkerTrigger.js +168 -26
- package/dist/adapters/KafkaAdapter.d.ts +5 -0
- package/dist/adapters/KafkaAdapter.js +12 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/package.json +5 -4
- package/CHANGELOG.md +0 -22
- package/__tests__/integration/nats-adapter.real-nats.test.ts +0 -116
- package/__tests__/integration/pgboss-adapter.real-pg.test.ts +0 -164
- package/__tests__/integration/rabbitmq-adapter.real-rabbitmq.test.ts +0 -179
- package/__tests__/integration/sqs-adapter.real-sqs.test.ts +0 -228
- package/src/WorkerTrigger.test.ts +0 -540
- package/src/WorkerTrigger.ts +0 -784
- package/src/adapters/BullMQAdapter.ts +0 -296
- package/src/adapters/InMemoryAdapter.ts +0 -280
- package/src/adapters/KafkaAdapter.ts +0 -277
- package/src/adapters/NATSAdapter.ts +0 -454
- package/src/adapters/PgBossAdapter.ts +0 -293
- package/src/adapters/RabbitMQAdapter.ts +0 -285
- package/src/adapters/RedisStreamsAdapter.ts +0 -286
- package/src/adapters/SQSAdapter.ts +0 -306
- package/src/adapters/factory.test.ts +0 -89
- package/src/adapters/factory.ts +0 -111
- package/src/adapters/new-adapters.test.ts +0 -130
- package/src/index.ts +0 -94
- package/template/.env.example +0 -13
- package/template/package.json +0 -45
- package/template/src/Nodes.ts +0 -10
- package/template/src/Workflows.ts +0 -8
- package/template/src/index.ts +0 -41
- package/template/src/runner/WorkerServer.ts +0 -34
- package/template/src/runner/types/Workflows.ts +0 -7
- package/template/src/workflows/jobs/process-job.ts +0 -47
- package/template/tsconfig.json +0 -31
- package/template/vitest.config.ts +0 -39
- package/tsconfig.json +0 -32
package/src/adapters/factory.ts
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* v0.7 PR 5 — adapter factory.
|
|
3
|
-
*
|
|
4
|
-
* Resolves a `provider` string to a concrete `WorkerAdapter` instance.
|
|
5
|
-
* Used by the `WorkerTrigger` (to pick the right adapter per workflow
|
|
6
|
-
* based on `trigger.worker.provider`) and by the `@blokjs/worker-publish`
|
|
7
|
-
* helper node (to enqueue jobs from any workflow without bundling all
|
|
8
|
-
* broker clients).
|
|
9
|
-
*
|
|
10
|
-
* Provider resolution order:
|
|
11
|
-
* 1. Explicit `provider` field on the workflow (highest priority).
|
|
12
|
-
* 2. `BLOK_WORKER_ADAPTER` env var (per Q7 resolution in the plan).
|
|
13
|
-
* 3. `"in-memory"` fallback (zero-infra default for dev/tests).
|
|
14
|
-
*
|
|
15
|
-
* Each adapter beyond `in-memory` lazy-imports its broker SDK on first
|
|
16
|
-
* use (BullMQ does this today). Workflows that don't use a given
|
|
17
|
-
* provider don't pay the install or import cost.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import type { WorkerProvider } from "@blokjs/helper";
|
|
21
|
-
import type { WorkerAdapter } from "../WorkerTrigger";
|
|
22
|
-
import { BullMQAdapter } from "./BullMQAdapter";
|
|
23
|
-
import { InMemoryAdapter } from "./InMemoryAdapter";
|
|
24
|
-
import { KafkaAdapter } from "./KafkaAdapter";
|
|
25
|
-
import { NATSWorkerAdapter } from "./NATSAdapter";
|
|
26
|
-
import { PgBossAdapter } from "./PgBossAdapter";
|
|
27
|
-
import { RabbitMQAdapter } from "./RabbitMQAdapter";
|
|
28
|
-
import { RedisStreamsAdapter } from "./RedisStreamsAdapter";
|
|
29
|
-
import { SQSAdapter } from "./SQSAdapter";
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Resolve the effective provider for a workflow. The trigger's
|
|
33
|
-
* `provider` field always wins; otherwise fall back to the
|
|
34
|
-
* `BLOK_WORKER_ADAPTER` env var; otherwise `"in-memory"`.
|
|
35
|
-
*/
|
|
36
|
-
export function resolveProvider(provider?: WorkerProvider): WorkerProvider {
|
|
37
|
-
if (provider) return provider;
|
|
38
|
-
const envValue = process.env.BLOK_WORKER_ADAPTER;
|
|
39
|
-
if (envValue && isWorkerProvider(envValue)) return envValue;
|
|
40
|
-
return "in-memory";
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function isWorkerProvider(value: string): value is WorkerProvider {
|
|
44
|
-
return (
|
|
45
|
-
value === "in-memory" ||
|
|
46
|
-
value === "nats" ||
|
|
47
|
-
value === "bullmq" ||
|
|
48
|
-
value === "kafka" ||
|
|
49
|
-
value === "rabbitmq" ||
|
|
50
|
-
value === "sqs" ||
|
|
51
|
-
value === "redis" ||
|
|
52
|
-
value === "pg-boss"
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Construct an adapter for the named provider. Throws a clear error
|
|
58
|
-
* for unknown names — keeps the schema validation and runtime
|
|
59
|
-
* behaviour in sync (the Zod enum catches typos at workflow load).
|
|
60
|
-
*/
|
|
61
|
-
export function createWorkerAdapter(provider: WorkerProvider): WorkerAdapter {
|
|
62
|
-
switch (provider) {
|
|
63
|
-
case "in-memory":
|
|
64
|
-
return new InMemoryAdapter();
|
|
65
|
-
case "nats":
|
|
66
|
-
return new NATSWorkerAdapter();
|
|
67
|
-
case "bullmq":
|
|
68
|
-
return new BullMQAdapter();
|
|
69
|
-
case "kafka":
|
|
70
|
-
return new KafkaAdapter();
|
|
71
|
-
case "rabbitmq":
|
|
72
|
-
return new RabbitMQAdapter();
|
|
73
|
-
case "sqs":
|
|
74
|
-
return new SQSAdapter();
|
|
75
|
-
case "redis":
|
|
76
|
-
return new RedisStreamsAdapter();
|
|
77
|
-
case "pg-boss":
|
|
78
|
-
return new PgBossAdapter();
|
|
79
|
-
default: {
|
|
80
|
-
const exhaustive: never = provider;
|
|
81
|
-
throw new Error(`[blok][worker] unknown provider "${exhaustive as string}". Check WorkerProviderSchema.`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Process-singleton adapter pool — one instance per provider. The
|
|
88
|
-
* trigger calls `getOrCreateAdapter("kafka")` once per workflow, and
|
|
89
|
-
* subsequent workflows on the same provider share the broker
|
|
90
|
-
* connection. Adapters are connected lazily — `getOrCreateAdapter`
|
|
91
|
-
* never connects on its own; the caller calls `adapter.connect()`.
|
|
92
|
-
*
|
|
93
|
-
* Reset via `_resetAdapterPoolForTests()` between vitest suites.
|
|
94
|
-
*/
|
|
95
|
-
const pool: Map<WorkerProvider, WorkerAdapter> = new Map();
|
|
96
|
-
|
|
97
|
-
export function getOrCreateAdapter(provider: WorkerProvider): WorkerAdapter {
|
|
98
|
-
let adapter = pool.get(provider);
|
|
99
|
-
if (!adapter) {
|
|
100
|
-
adapter = createWorkerAdapter(provider);
|
|
101
|
-
pool.set(provider, adapter);
|
|
102
|
-
}
|
|
103
|
-
return adapter;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export function _resetAdapterPoolForTests(): void {
|
|
107
|
-
for (const adapter of pool.values()) {
|
|
108
|
-
void adapter.disconnect?.().catch(() => {});
|
|
109
|
-
}
|
|
110
|
-
pool.clear();
|
|
111
|
-
}
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Smoke tests for the v0.7 PR 5 adapters (Kafka, RabbitMQ, SQS, Redis
|
|
3
|
-
* Streams, pg-boss). Each adapter is exercised at the boundaries
|
|
4
|
-
* that don't require a live broker connection:
|
|
5
|
-
*
|
|
6
|
-
* - Constructor + provider name + initial connected state.
|
|
7
|
-
* - `disconnect()` is a no-op before connect.
|
|
8
|
-
*
|
|
9
|
-
* The peer-dep error paths and the broker round-trips need real
|
|
10
|
-
* brokers — those live in the docker-compose integration suite that
|
|
11
|
-
* we'll wire up as a follow-up (see PR 5 plan, "Out of scope:
|
|
12
|
-
* docker-compose CI").
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { describe, expect, it } from "vitest";
|
|
16
|
-
|
|
17
|
-
import { KafkaAdapter } from "./KafkaAdapter";
|
|
18
|
-
import { PgBossAdapter } from "./PgBossAdapter";
|
|
19
|
-
import { RabbitMQAdapter } from "./RabbitMQAdapter";
|
|
20
|
-
import { RedisStreamsAdapter } from "./RedisStreamsAdapter";
|
|
21
|
-
import { SQSAdapter } from "./SQSAdapter";
|
|
22
|
-
|
|
23
|
-
describe("KafkaAdapter — v0.7 PR 5", () => {
|
|
24
|
-
it("reports provider 'kafka'", () => {
|
|
25
|
-
expect(new KafkaAdapter().provider).toBe("kafka");
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("is not connected before connect()", () => {
|
|
29
|
-
expect(new KafkaAdapter().isConnected()).toBe(false);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("disconnect() before connect is a no-op", async () => {
|
|
33
|
-
await expect(new KafkaAdapter().disconnect()).resolves.toBeUndefined();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("reads broker list from KAFKA_BROKERS env var", () => {
|
|
37
|
-
process.env.KAFKA_BROKERS = "broker-a:9092,broker-b:9092";
|
|
38
|
-
const adapter = new KafkaAdapter();
|
|
39
|
-
expect((adapter as unknown as { config: { brokers: string[] } }).config.brokers).toEqual([
|
|
40
|
-
"broker-a:9092",
|
|
41
|
-
"broker-b:9092",
|
|
42
|
-
]);
|
|
43
|
-
process.env.KAFKA_BROKERS = undefined;
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe("RabbitMQAdapter — v0.7 PR 5", () => {
|
|
48
|
-
it("reports provider 'rabbitmq'", () => {
|
|
49
|
-
expect(new RabbitMQAdapter().provider).toBe("rabbitmq");
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("is not connected before connect()", () => {
|
|
53
|
-
expect(new RabbitMQAdapter().isConnected()).toBe(false);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("disconnect() before connect is a no-op", async () => {
|
|
57
|
-
await expect(new RabbitMQAdapter().disconnect()).resolves.toBeUndefined();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("reads AMQP_URL from env var", () => {
|
|
61
|
-
process.env.AMQP_URL = "amqp://prod.example:5672";
|
|
62
|
-
const adapter = new RabbitMQAdapter();
|
|
63
|
-
expect((adapter as unknown as { config: { url: string } }).config.url).toBe("amqp://prod.example:5672");
|
|
64
|
-
process.env.AMQP_URL = undefined;
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
describe("SQSAdapter — v0.7 PR 5", () => {
|
|
69
|
-
it("reports provider 'sqs'", () => {
|
|
70
|
-
expect(new SQSAdapter().provider).toBe("sqs");
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("is not connected before connect()", () => {
|
|
74
|
-
expect(new SQSAdapter().isConnected()).toBe(false);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("disconnect() before connect is a no-op", async () => {
|
|
78
|
-
await expect(new SQSAdapter().disconnect()).resolves.toBeUndefined();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("honors the explicit region override", () => {
|
|
82
|
-
const adapter = new SQSAdapter({ region: "eu-west-2" });
|
|
83
|
-
expect((adapter as unknown as { config: { region: string } }).config.region).toBe("eu-west-2");
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
describe("RedisStreamsAdapter — v0.7 PR 5", () => {
|
|
88
|
-
it("reports provider 'redis'", () => {
|
|
89
|
-
expect(new RedisStreamsAdapter().provider).toBe("redis");
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it("is not connected before connect()", () => {
|
|
93
|
-
expect(new RedisStreamsAdapter().isConnected()).toBe(false);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("disconnect() before connect is a no-op", async () => {
|
|
97
|
-
await expect(new RedisStreamsAdapter().disconnect()).resolves.toBeUndefined();
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it("generates a unique consumer name per instance", () => {
|
|
101
|
-
const a = new RedisStreamsAdapter();
|
|
102
|
-
const b = new RedisStreamsAdapter();
|
|
103
|
-
expect((a as unknown as { consumerName: string }).consumerName).not.toBe(
|
|
104
|
-
(b as unknown as { consumerName: string }).consumerName,
|
|
105
|
-
);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
describe("PgBossAdapter — v0.7 PR 5", () => {
|
|
110
|
-
it("reports provider 'pg-boss'", () => {
|
|
111
|
-
expect(new PgBossAdapter().provider).toBe("pg-boss");
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("is not connected before connect()", () => {
|
|
115
|
-
expect(new PgBossAdapter().isConnected()).toBe(false);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it("disconnect() before connect is a no-op", async () => {
|
|
119
|
-
await expect(new PgBossAdapter().disconnect()).resolves.toBeUndefined();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it("connect() throws a clear peer-dep error when pg-boss is absent", async () => {
|
|
123
|
-
// pg-boss is the only adapter SDK NOT pre-installed in this monorepo,
|
|
124
|
-
// so the lazy-import path actually throws here. The other adapters'
|
|
125
|
-
// SDKs ARE installed (other workspaces use them) — their peer-dep
|
|
126
|
-
// error paths get exercised in the docker-compose integration suite.
|
|
127
|
-
const adapter = new PgBossAdapter();
|
|
128
|
-
await expect(adapter.connect()).rejects.toThrow(/pg-boss/);
|
|
129
|
-
});
|
|
130
|
-
});
|
package/src/index.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @blokjs/trigger-worker
|
|
3
|
-
*
|
|
4
|
-
* Worker-based trigger for Blok workflows.
|
|
5
|
-
* Supports background job processing with:
|
|
6
|
-
* - Configurable concurrency per queue
|
|
7
|
-
* - Automatic retries with exponential backoff
|
|
8
|
-
* - Job timeouts
|
|
9
|
-
* - Priority-based job ordering
|
|
10
|
-
* - Delayed job scheduling
|
|
11
|
-
* - Queue statistics and monitoring
|
|
12
|
-
*
|
|
13
|
-
* Adapters (v0.7+):
|
|
14
|
-
* - BullMQ — Redis-backed, ops-style queues (`bullmq` peer dep)
|
|
15
|
-
* - InMemory — development / tests (no peer deps)
|
|
16
|
-
* - NATS — JetStream durable streams (`nats` peer dep)
|
|
17
|
-
* - Kafka — high-throughput streaming (`kafkajs` peer dep)
|
|
18
|
-
* - RabbitMQ — reliable enterprise queues (`amqplib` peer dep)
|
|
19
|
-
* - SQS — AWS cloud queues (`@aws-sdk/client-sqs` peer dep)
|
|
20
|
-
* - Redis Streams — when Redis is already in stack (`ioredis` peer dep)
|
|
21
|
-
* - pg-boss — no extra infra (`pg-boss` peer dep)
|
|
22
|
-
*
|
|
23
|
-
* v0.7+ — pick the adapter per workflow via `trigger.worker.provider`.
|
|
24
|
-
* `BLOK_WORKER_ADAPTER` env var sets the default. Subclasses can still
|
|
25
|
-
* set `protected adapter` directly for back-compat with the pre-v0.7
|
|
26
|
-
* single-adapter pattern.
|
|
27
|
-
*
|
|
28
|
-
* @example BullMQ
|
|
29
|
-
* ```typescript
|
|
30
|
-
* import { WorkerTrigger, BullMQAdapter } from "@blokjs/trigger-worker";
|
|
31
|
-
*
|
|
32
|
-
* class MyWorkerTrigger extends WorkerTrigger {
|
|
33
|
-
* protected adapter = new BullMQAdapter({
|
|
34
|
-
* host: "localhost",
|
|
35
|
-
* port: 6379,
|
|
36
|
-
* });
|
|
37
|
-
*
|
|
38
|
-
* protected nodes = myNodes;
|
|
39
|
-
* protected workflows = myWorkflows;
|
|
40
|
-
* }
|
|
41
|
-
*
|
|
42
|
-
* const trigger = new MyWorkerTrigger();
|
|
43
|
-
* await trigger.listen();
|
|
44
|
-
*
|
|
45
|
-
* // Dispatch a job
|
|
46
|
-
* await trigger.dispatch("background-jobs", { userId: "123" }, {
|
|
47
|
-
* priority: 10,
|
|
48
|
-
* retries: 3,
|
|
49
|
-
* delay: 5000, // delay 5 seconds
|
|
50
|
-
* });
|
|
51
|
-
* ```
|
|
52
|
-
*
|
|
53
|
-
* @example InMemory (development)
|
|
54
|
-
* ```typescript
|
|
55
|
-
* import { WorkerTrigger, InMemoryAdapter } from "@blokjs/trigger-worker";
|
|
56
|
-
*
|
|
57
|
-
* class DevWorkerTrigger extends WorkerTrigger {
|
|
58
|
-
* protected adapter = new InMemoryAdapter();
|
|
59
|
-
* protected nodes = myNodes;
|
|
60
|
-
* protected workflows = myWorkflows;
|
|
61
|
-
* }
|
|
62
|
-
* ```
|
|
63
|
-
*/
|
|
64
|
-
|
|
65
|
-
// Core exports
|
|
66
|
-
export {
|
|
67
|
-
WorkerTrigger,
|
|
68
|
-
type WorkerAdapter,
|
|
69
|
-
type WorkerJob,
|
|
70
|
-
type WorkerQueueStats,
|
|
71
|
-
} from "./WorkerTrigger";
|
|
72
|
-
|
|
73
|
-
// Adapters
|
|
74
|
-
export { BullMQAdapter, type BullMQConfig } from "./adapters/BullMQAdapter";
|
|
75
|
-
export { InMemoryAdapter } from "./adapters/InMemoryAdapter";
|
|
76
|
-
export { KafkaAdapter, type KafkaConfig } from "./adapters/KafkaAdapter";
|
|
77
|
-
export { NATSWorkerAdapter, type NATSWorkerConfig } from "./adapters/NATSAdapter";
|
|
78
|
-
export { PgBossAdapter, type PgBossConfig } from "./adapters/PgBossAdapter";
|
|
79
|
-
export { RabbitMQAdapter, type RabbitMQConfig } from "./adapters/RabbitMQAdapter";
|
|
80
|
-
export { RedisStreamsAdapter, type RedisStreamsConfig } from "./adapters/RedisStreamsAdapter";
|
|
81
|
-
export { SQSAdapter, type SQSConfig } from "./adapters/SQSAdapter";
|
|
82
|
-
|
|
83
|
-
// v0.7 PR 5 — factory + pool. Used by WorkerTrigger and exposed for
|
|
84
|
-
// helper nodes (`@blokjs/worker-publish`) that need to enqueue jobs
|
|
85
|
-
// from any workflow without bundling all broker SDKs.
|
|
86
|
-
export {
|
|
87
|
-
_resetAdapterPoolForTests,
|
|
88
|
-
createWorkerAdapter,
|
|
89
|
-
getOrCreateAdapter,
|
|
90
|
-
resolveProvider,
|
|
91
|
-
} from "./adapters/factory";
|
|
92
|
-
|
|
93
|
-
// Re-export types from helper for convenience
|
|
94
|
-
export type { WorkerProvider, WorkerTriggerOpts } from "@blokjs/helper";
|
package/template/.env.example
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
PROJECT_NAME=trigger-worker-server
|
|
2
|
-
PROJECT_VERSION=0.0.1
|
|
3
|
-
PORT=4008
|
|
4
|
-
WORKFLOWS_PATH=PROJECT_PATH/workflows
|
|
5
|
-
NODES_PATH=PROJECT_PATH/src/nodes
|
|
6
|
-
CONSOLE_LOG_ACTIVE=true
|
|
7
|
-
APP_NAME=blok-worker
|
|
8
|
-
DISABLE_TRIGGER_RUN=false # Set to true to disable trigger run and use this project as a module
|
|
9
|
-
|
|
10
|
-
# NATS JetStream Configuration
|
|
11
|
-
NATS_SERVERS=localhost:4222
|
|
12
|
-
NATS_STREAM_NAME=blok-worker
|
|
13
|
-
# NATS_TOKEN=your-auth-token
|
package/template/package.json
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "blok-worker-trigger",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"description": "Worker trigger for Blok background job workflows",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"engines": {
|
|
7
|
-
"node": ">=18.0.0"
|
|
8
|
-
},
|
|
9
|
-
"main": "dist/index.js",
|
|
10
|
-
"types": "dist/index.d.ts",
|
|
11
|
-
"author": "",
|
|
12
|
-
"license": "MIT",
|
|
13
|
-
"scripts": {
|
|
14
|
-
"dev": "bun --watch run src/index.ts",
|
|
15
|
-
"start": "bun run dist/index.js",
|
|
16
|
-
"build": "rimraf ./dist && tsc",
|
|
17
|
-
"test": "vitest run",
|
|
18
|
-
"test:dev": "vitest"
|
|
19
|
-
},
|
|
20
|
-
"devDependencies": {
|
|
21
|
-
"@types/node": "^22.15.21",
|
|
22
|
-
"@types/uuid": "^11.0.0",
|
|
23
|
-
"rimraf": "^6.1.2",
|
|
24
|
-
"typescript": "^5.8.3",
|
|
25
|
-
"vitest": "^4.0.18"
|
|
26
|
-
},
|
|
27
|
-
"dependencies": {
|
|
28
|
-
"@blokjs/api-call": "^0.6.17",
|
|
29
|
-
"@blokjs/helper": "^0.6.17",
|
|
30
|
-
"@blokjs/if-else": "^0.6.17",
|
|
31
|
-
"@blokjs/runner": "^0.6.17",
|
|
32
|
-
"@blokjs/shared": "^0.6.17",
|
|
33
|
-
"@blokjs/trigger-worker": "^0.6.17",
|
|
34
|
-
"@opentelemetry/api": "^1.9.0",
|
|
35
|
-
"@opentelemetry/exporter-prometheus": "^0.57.2",
|
|
36
|
-
"@opentelemetry/resources": "^1.30.1",
|
|
37
|
-
"@opentelemetry/sdk-metrics": "^1.30.1",
|
|
38
|
-
"@opentelemetry/sdk-trace-base": "^1.30.1",
|
|
39
|
-
"@opentelemetry/semantic-conventions": "^1.39.0",
|
|
40
|
-
"nats": "^2.28.0",
|
|
41
|
-
"uuid": "^11.1.0",
|
|
42
|
-
"zod": "^3.24.2"
|
|
43
|
-
},
|
|
44
|
-
"private": true
|
|
45
|
-
}
|
package/template/src/Nodes.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import ApiCall from "@blokjs/api-call";
|
|
2
|
-
import IfElse from "@blokjs/if-else";
|
|
3
|
-
import type { BlokService } from "@blokjs/runner";
|
|
4
|
-
|
|
5
|
-
const nodes: Record<string, BlokService<unknown>> = {
|
|
6
|
-
"@blokjs/api-call": ApiCall,
|
|
7
|
-
"@blokjs/if-else": IfElse,
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export default nodes;
|
package/template/src/index.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { DefaultLogger } from "@blokjs/runner";
|
|
2
|
-
import { type Span, metrics, trace } from "@opentelemetry/api";
|
|
3
|
-
import WorkerServer from "./runner/WorkerServer";
|
|
4
|
-
|
|
5
|
-
export default class App {
|
|
6
|
-
private workerServer: WorkerServer = <WorkerServer>{};
|
|
7
|
-
protected trigger_initializer = 0;
|
|
8
|
-
protected initializer = 0;
|
|
9
|
-
protected tracer = trace.getTracer(
|
|
10
|
-
process.env.PROJECT_NAME || "trigger-worker-server",
|
|
11
|
-
process.env.PROJECT_VERSION || "0.0.1",
|
|
12
|
-
);
|
|
13
|
-
private logger = new DefaultLogger();
|
|
14
|
-
protected app_cold_start = metrics.getMeter("default").createGauge("initialization", {
|
|
15
|
-
description: "Application cold start",
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
constructor() {
|
|
19
|
-
this.initializer = performance.now();
|
|
20
|
-
this.workerServer = new WorkerServer();
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async run() {
|
|
24
|
-
this.tracer.startActiveSpan("initialization", async (span: Span) => {
|
|
25
|
-
await this.workerServer.listen();
|
|
26
|
-
this.initializer = performance.now() - this.initializer;
|
|
27
|
-
|
|
28
|
-
this.logger.log(`Worker trigger initialized in ${this.initializer.toFixed(2)}ms`);
|
|
29
|
-
this.app_cold_start.record(this.initializer, {
|
|
30
|
-
pid: process.pid,
|
|
31
|
-
env: process.env.NODE_ENV,
|
|
32
|
-
app: process.env.APP_NAME,
|
|
33
|
-
});
|
|
34
|
-
span.end();
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (process.env.DISABLE_TRIGGER_RUN !== "true") {
|
|
40
|
-
new App().run();
|
|
41
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { NATSWorkerAdapter, WorkerTrigger } from "@blokjs/trigger-worker";
|
|
2
|
-
import nodes from "../Nodes";
|
|
3
|
-
import workflows from "../Workflows";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* WorkerServer - Concrete Worker trigger implementation using NATS JetStream
|
|
7
|
-
*
|
|
8
|
-
* This server extends the abstract WorkerTrigger and provides:
|
|
9
|
-
* - NATS JetStream adapter for persistent job queues
|
|
10
|
-
* - Node and workflow registries
|
|
11
|
-
* - Configurable concurrency, retries, and timeouts
|
|
12
|
-
*
|
|
13
|
-
* Environment variables:
|
|
14
|
-
* - NATS_SERVERS: Comma-separated NATS server URLs (default: localhost:4222)
|
|
15
|
-
* - NATS_STREAM_NAME: JetStream stream name (default: blok-worker)
|
|
16
|
-
* - NATS_TOKEN: Authentication token (optional)
|
|
17
|
-
*
|
|
18
|
-
* @example BullMQ (Redis) alternative
|
|
19
|
-
* ```typescript
|
|
20
|
-
* import { BullMQAdapter } from "@blokjs/trigger-worker";
|
|
21
|
-
* protected adapter = new BullMQAdapter({
|
|
22
|
-
* host: process.env.REDIS_HOST || "localhost",
|
|
23
|
-
* port: Number(process.env.REDIS_PORT) || 6379,
|
|
24
|
-
* });
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
export default class WorkerServer extends WorkerTrigger {
|
|
28
|
-
protected adapter = new NATSWorkerAdapter({
|
|
29
|
-
servers: (process.env.NATS_SERVERS || "localhost:4222").split(","),
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
protected nodes: Record<string, import("@blokjs/runner").BlokService<unknown>> = nodes;
|
|
33
|
-
protected workflows: Record<string, import("@blokjs/helper").HelperResponse> = workflows;
|
|
34
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { workflow } from "@blokjs/helper";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Example Worker workflow — fires when a job is received from the queue.
|
|
5
|
-
*
|
|
6
|
-
* The job payload + metadata land on ctx.request:
|
|
7
|
-
* - ctx.request.body — the job payload as posted
|
|
8
|
-
* - ctx.request.headers — job headers
|
|
9
|
-
* - ctx.request.params.queue — queue name
|
|
10
|
-
* - ctx.request.params.jobId — unique job ID
|
|
11
|
-
* - ctx.request.params.attempt — retry attempt (0-based)
|
|
12
|
-
* - ctx.vars._worker_job — full job metadata
|
|
13
|
-
*
|
|
14
|
-
* v2 reliability knobs available on each step (uncomment to use):
|
|
15
|
-
* idempotencyKey: "$.req.params.jobId" — skip re-runs of the same job
|
|
16
|
-
* retry: { maxAttempts: 3 } — retry on transient failures
|
|
17
|
-
* maxDuration: "30s" — fail the step if it hangs
|
|
18
|
-
*
|
|
19
|
-
* Trigger-level reliability:
|
|
20
|
-
* concurrencyKey: "$.req.body.tenantId" — per-tenant fairness
|
|
21
|
-
* onLimit: "queue" — defer instead of reject
|
|
22
|
-
*/
|
|
23
|
-
export default workflow({
|
|
24
|
-
name: "Process Background Job",
|
|
25
|
-
version: "1.0.0",
|
|
26
|
-
description: "Handles incoming worker jobs from the queue",
|
|
27
|
-
trigger: {
|
|
28
|
-
worker: { queue: "background-jobs" },
|
|
29
|
-
},
|
|
30
|
-
steps: [
|
|
31
|
-
{
|
|
32
|
-
id: "process-job",
|
|
33
|
-
use: "@blokjs/api-call",
|
|
34
|
-
type: "module",
|
|
35
|
-
inputs: {
|
|
36
|
-
url: "https://httpbin.org/post",
|
|
37
|
-
method: "POST",
|
|
38
|
-
body: {
|
|
39
|
-
job: "js/ctx.request.body",
|
|
40
|
-
queue: "js/ctx.request.params.queue",
|
|
41
|
-
jobId: "js/ctx.request.params.jobId",
|
|
42
|
-
attempt: "js/ctx.request.params.attempt",
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
],
|
|
47
|
-
});
|
package/template/tsconfig.json
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"ts-node": {
|
|
3
|
-
"transpileOnly": true
|
|
4
|
-
},
|
|
5
|
-
"compilerOptions": {
|
|
6
|
-
"target": "ES2022",
|
|
7
|
-
"module": "es2022",
|
|
8
|
-
"moduleResolution": "bundler",
|
|
9
|
-
"rootDir": "./src",
|
|
10
|
-
"baseUrl": ".",
|
|
11
|
-
"paths": {
|
|
12
|
-
"@nodes/*": ["./src/nodes/*"],
|
|
13
|
-
"@src/*": ["src/*"]
|
|
14
|
-
},
|
|
15
|
-
"allowJs": true,
|
|
16
|
-
"declaration": true,
|
|
17
|
-
"declarationMap": true,
|
|
18
|
-
"sourceMap": true,
|
|
19
|
-
"outDir": "./dist",
|
|
20
|
-
"esModuleInterop": true,
|
|
21
|
-
"forceConsistentCasingInFileNames": true,
|
|
22
|
-
"strict": true,
|
|
23
|
-
"noUnusedLocals": true,
|
|
24
|
-
"noImplicitReturns": true,
|
|
25
|
-
"skipLibCheck": true,
|
|
26
|
-
"resolveJsonModule": true
|
|
27
|
-
},
|
|
28
|
-
"compileOnSave": true,
|
|
29
|
-
"include": ["./src", "./src/nodes/**/*.json", "./src/nodes/**/*.md"],
|
|
30
|
-
"exclude": ["src/nodes/**/test/*.ts", "**/*.test.ts", "node_modules", "dist"]
|
|
31
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { defineConfig } from "vitest/config";
|
|
3
|
-
|
|
4
|
-
export default defineConfig({
|
|
5
|
-
test: {
|
|
6
|
-
globals: true,
|
|
7
|
-
environment: "node",
|
|
8
|
-
coverage: {
|
|
9
|
-
provider: "istanbul",
|
|
10
|
-
reporter: ["text", "json", "html", "lcov"],
|
|
11
|
-
exclude: [
|
|
12
|
-
"node_modules/",
|
|
13
|
-
"dist/",
|
|
14
|
-
"**/*.d.ts",
|
|
15
|
-
"**/*.config.ts",
|
|
16
|
-
"__tests__/",
|
|
17
|
-
"src/nodes/",
|
|
18
|
-
"src/workflows/",
|
|
19
|
-
"src/runner/types/",
|
|
20
|
-
"src/runner/metrics/",
|
|
21
|
-
],
|
|
22
|
-
thresholds: {
|
|
23
|
-
lines: 90,
|
|
24
|
-
functions: 90,
|
|
25
|
-
branches: 85,
|
|
26
|
-
statements: 90,
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
include: ["__tests__/**/*.test.ts"],
|
|
30
|
-
exclude: ["node_modules", "dist"],
|
|
31
|
-
testTimeout: 10000,
|
|
32
|
-
hookTimeout: 10000,
|
|
33
|
-
},
|
|
34
|
-
resolve: {
|
|
35
|
-
alias: {
|
|
36
|
-
"@": path.resolve(__dirname, "./src"),
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"ts-node": {
|
|
3
|
-
"transpileOnly": true
|
|
4
|
-
},
|
|
5
|
-
"compilerOptions": {
|
|
6
|
-
"target": "ES2022",
|
|
7
|
-
"module": "es2022",
|
|
8
|
-
"lib": ["ES2022"],
|
|
9
|
-
"declaration": true,
|
|
10
|
-
"strict": true,
|
|
11
|
-
"noImplicitAny": true,
|
|
12
|
-
"strictNullChecks": true,
|
|
13
|
-
"noImplicitThis": true,
|
|
14
|
-
"alwaysStrict": true,
|
|
15
|
-
"noUnusedLocals": false,
|
|
16
|
-
"noUnusedParameters": false,
|
|
17
|
-
"noImplicitReturns": true,
|
|
18
|
-
"noFallthroughCasesInSwitch": false,
|
|
19
|
-
"inlineSourceMap": true,
|
|
20
|
-
"inlineSources": true,
|
|
21
|
-
"experimentalDecorators": true,
|
|
22
|
-
"emitDecoratorMetadata": true,
|
|
23
|
-
"skipLibCheck": true,
|
|
24
|
-
"esModuleInterop": true,
|
|
25
|
-
"resolveJsonModule": true,
|
|
26
|
-
"outDir": "./dist",
|
|
27
|
-
"rootDir": "./src",
|
|
28
|
-
"moduleResolution": "bundler"
|
|
29
|
-
},
|
|
30
|
-
"include": ["src/**/*"],
|
|
31
|
-
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
32
|
-
}
|