@blokjs/runner 0.6.20 → 0.7.0
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/Blok.d.ts +2 -0
- package/dist/Blok.js +42 -110
- package/dist/Blok.js.map +1 -1
- package/dist/DefaultLogger.d.ts +13 -0
- package/dist/DefaultLogger.js +25 -0
- package/dist/DefaultLogger.js.map +1 -1
- package/dist/RunnerSteps.d.ts +23 -0
- package/dist/RunnerSteps.js +128 -87
- package/dist/RunnerSteps.js.map +1 -1
- package/dist/SubworkflowNode.js +19 -0
- package/dist/SubworkflowNode.js.map +1 -1
- package/dist/TriggerBase.d.ts +12 -0
- package/dist/TriggerBase.js +216 -181
- package/dist/TriggerBase.js.map +1 -1
- package/dist/adapters/grpc/GrpcRuntimeAdapter.d.ts +9 -0
- package/dist/adapters/grpc/GrpcRuntimeAdapter.js +76 -6
- package/dist/adapters/grpc/GrpcRuntimeAdapter.js.map +1 -1
- package/dist/index.d.ts +4 -39
- package/dist/index.js +7 -32
- package/dist/index.js.map +1 -1
- package/dist/monitoring/JanitorMetrics.d.ts +3 -0
- package/dist/monitoring/JanitorMetrics.js +11 -0
- package/dist/monitoring/JanitorMetrics.js.map +1 -1
- package/dist/monitoring/ProcessErrorMetrics.d.ts +32 -0
- package/dist/monitoring/ProcessErrorMetrics.js +43 -0
- package/dist/monitoring/ProcessErrorMetrics.js.map +1 -0
- package/dist/monitoring/PrometheusMetricsBridge.d.ts +7 -0
- package/dist/monitoring/PrometheusMetricsBridge.js +8 -2
- package/dist/monitoring/PrometheusMetricsBridge.js.map +1 -1
- package/dist/monitoring/SubworkflowMetrics.d.ts +25 -0
- package/dist/monitoring/SubworkflowMetrics.js +38 -0
- package/dist/monitoring/SubworkflowMetrics.js.map +1 -0
- package/dist/observability/ErrorSink.d.ts +23 -0
- package/dist/observability/ErrorSink.js +32 -0
- package/dist/observability/ErrorSink.js.map +1 -0
- package/dist/observability/SentryIntegration.d.ts +9 -0
- package/dist/observability/SentryIntegration.js +31 -0
- package/dist/observability/SentryIntegration.js.map +1 -0
- package/dist/scheduling/DebounceCoordinator.d.ts +7 -53
- package/dist/scheduling/DebounceCoordinator.js +8 -207
- package/dist/scheduling/DebounceCoordinator.js.map +1 -1
- package/dist/tracing/InMemoryRunStore.d.ts +5 -1
- package/dist/tracing/InMemoryRunStore.js +14 -0
- package/dist/tracing/InMemoryRunStore.js.map +1 -1
- package/dist/tracing/Janitor.js +3 -0
- package/dist/tracing/Janitor.js.map +1 -1
- package/dist/tracing/PostgresRunStore.d.ts +4 -1
- package/dist/tracing/PostgresRunStore.js +73 -3
- package/dist/tracing/PostgresRunStore.js.map +1 -1
- package/dist/tracing/RunStore.d.ts +17 -1
- package/dist/tracing/RunTracker.d.ts +13 -34
- package/dist/tracing/RunTracker.js +62 -32
- package/dist/tracing/RunTracker.js.map +1 -1
- package/dist/tracing/SqliteRunStore.d.ts +4 -1
- package/dist/tracing/SqliteRunStore.js +60 -0
- package/dist/tracing/SqliteRunStore.js.map +1 -1
- package/dist/tracing/TraceRouter.d.ts +13 -0
- package/dist/tracing/TraceRouter.js +43 -11
- package/dist/tracing/TraceRouter.js.map +1 -1
- package/dist/tracing/TracingLogger.js +22 -0
- package/dist/tracing/TracingLogger.js.map +1 -1
- package/dist/tracing/createStore.js +51 -22
- package/dist/tracing/createStore.js.map +1 -1
- package/dist/tracing/types.d.ts +22 -0
- package/dist/types/GlobalOptions.d.ts +5 -7
- package/dist/workflow/WorkflowNormalizer.js +63 -0
- package/dist/workflow/WorkflowNormalizer.js.map +1 -1
- package/package.json +7 -4
- package/dist/cache/NodeResultCache.d.ts +0 -286
- package/dist/cache/NodeResultCache.js +0 -506
- package/dist/cache/NodeResultCache.js.map +0 -1
- package/dist/cache/index.d.ts +0 -1
- package/dist/cache/index.js +0 -2
- package/dist/cache/index.js.map +0 -1
- package/dist/concurrency/ConcurrencyBackend.d.ts +0 -61
- package/dist/concurrency/ConcurrencyBackend.js +0 -20
- package/dist/concurrency/ConcurrencyBackend.js.map +0 -1
- package/dist/concurrency/NatsKvConcurrencyBackend.d.ts +0 -64
- package/dist/concurrency/NatsKvConcurrencyBackend.js +0 -310
- package/dist/concurrency/NatsKvConcurrencyBackend.js.map +0 -1
- package/dist/concurrency/RedisConcurrencyBackend.d.ts +0 -64
- package/dist/concurrency/RedisConcurrencyBackend.js +0 -374
- package/dist/concurrency/RedisConcurrencyBackend.js.map +0 -1
- package/dist/concurrency/createConcurrencyBackend.d.ts +0 -24
- package/dist/concurrency/createConcurrencyBackend.js +0 -38
- package/dist/concurrency/createConcurrencyBackend.js.map +0 -1
- package/dist/graphql/GraphQLSchemaGenerator.d.ts +0 -129
- package/dist/graphql/GraphQLSchemaGenerator.js +0 -425
- package/dist/graphql/GraphQLSchemaGenerator.js.map +0 -1
- package/dist/integrations/APMIntegration.d.ts +0 -141
- package/dist/integrations/APMIntegration.js +0 -212
- package/dist/integrations/APMIntegration.js.map +0 -1
- package/dist/integrations/AzureMonitorIntegration.d.ts +0 -118
- package/dist/integrations/AzureMonitorIntegration.js +0 -254
- package/dist/integrations/AzureMonitorIntegration.js.map +0 -1
- package/dist/integrations/CloudWatchIntegration.d.ts +0 -135
- package/dist/integrations/CloudWatchIntegration.js +0 -293
- package/dist/integrations/CloudWatchIntegration.js.map +0 -1
- package/dist/integrations/SentryIntegration.d.ts +0 -153
- package/dist/integrations/SentryIntegration.js +0 -200
- package/dist/integrations/SentryIntegration.js.map +0 -1
- package/dist/integrations/index.d.ts +0 -19
- package/dist/integrations/index.js +0 -16
- package/dist/integrations/index.js.map +0 -1
- package/dist/marketplace/RuntimeAutoScaler.d.ts +0 -148
- package/dist/marketplace/RuntimeAutoScaler.js +0 -366
- package/dist/marketplace/RuntimeAutoScaler.js.map +0 -1
- package/dist/marketplace/RuntimeCatalog.d.ts +0 -180
- package/dist/marketplace/RuntimeCatalog.js +0 -339
- package/dist/marketplace/RuntimeCatalog.js.map +0 -1
- package/dist/marketplace/RuntimeDiscovery.d.ts +0 -86
- package/dist/marketplace/RuntimeDiscovery.js +0 -231
- package/dist/marketplace/RuntimeDiscovery.js.map +0 -1
- package/dist/marketplace/RuntimeHealthMonitor.d.ts +0 -100
- package/dist/marketplace/RuntimeHealthMonitor.js +0 -241
- package/dist/marketplace/RuntimeHealthMonitor.js.map +0 -1
- package/dist/marketplace/RuntimeMetricsDashboard.d.ts +0 -113
- package/dist/marketplace/RuntimeMetricsDashboard.js +0 -293
- package/dist/marketplace/RuntimeMetricsDashboard.js.map +0 -1
- package/dist/openapi/OpenAPIGenerator.d.ts +0 -192
- package/dist/openapi/OpenAPIGenerator.js +0 -378
- package/dist/openapi/OpenAPIGenerator.js.map +0 -1
- package/dist/openapi/index.d.ts +0 -20
- package/dist/openapi/index.js +0 -20
- package/dist/openapi/index.js.map +0 -1
- package/dist/scheduling/DebounceBackend.d.ts +0 -108
- package/dist/scheduling/DebounceBackend.js +0 -23
- package/dist/scheduling/DebounceBackend.js.map +0 -1
- package/dist/scheduling/NatsKvDebounceBackend.d.ts +0 -53
- package/dist/scheduling/NatsKvDebounceBackend.js +0 -334
- package/dist/scheduling/NatsKvDebounceBackend.js.map +0 -1
- package/dist/scheduling/RedisDebounceBackend.d.ts +0 -49
- package/dist/scheduling/RedisDebounceBackend.js +0 -356
- package/dist/scheduling/RedisDebounceBackend.js.map +0 -1
- package/dist/scheduling/createDebounceBackend.d.ts +0 -25
- package/dist/scheduling/createDebounceBackend.js +0 -39
- package/dist/scheduling/createDebounceBackend.js.map +0 -1
- package/dist/security/ABAC.d.ts +0 -224
- package/dist/security/ABAC.js +0 -380
- package/dist/security/ABAC.js.map +0 -1
- package/dist/security/AuditLogger.d.ts +0 -242
- package/dist/security/AuditLogger.js +0 -317
- package/dist/security/AuditLogger.js.map +0 -1
- package/dist/security/AuthMiddleware.d.ts +0 -162
- package/dist/security/AuthMiddleware.js +0 -289
- package/dist/security/AuthMiddleware.js.map +0 -1
- package/dist/security/EncryptionAtRest.d.ts +0 -206
- package/dist/security/EncryptionAtRest.js +0 -236
- package/dist/security/EncryptionAtRest.js.map +0 -1
- package/dist/security/OAuthProvider.d.ts +0 -334
- package/dist/security/OAuthProvider.js +0 -719
- package/dist/security/OAuthProvider.js.map +0 -1
- package/dist/security/PIIDetector.d.ts +0 -233
- package/dist/security/PIIDetector.js +0 -354
- package/dist/security/PIIDetector.js.map +0 -1
- package/dist/security/RBAC.d.ts +0 -143
- package/dist/security/RBAC.js +0 -285
- package/dist/security/RBAC.js.map +0 -1
- package/dist/security/SecretManager.d.ts +0 -652
- package/dist/security/SecretManager.js +0 -1147
- package/dist/security/SecretManager.js.map +0 -1
- package/dist/security/TLSConfig.d.ts +0 -305
- package/dist/security/TLSConfig.js +0 -550
- package/dist/security/TLSConfig.js.map +0 -1
- package/dist/security/index.d.ts +0 -81
- package/dist/security/index.js +0 -82
- package/dist/security/index.js.map +0 -1
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tier C #4 follow-up · Redis-backed concurrency backend.
|
|
3
|
-
*
|
|
4
|
-
* Coordinates per-(workflow, concurrencyKey) lease state across processes
|
|
5
|
-
* via a single Redis key per bucket. Atomicity comes from server-side Lua
|
|
6
|
-
* scripts — `EVAL` runs single-threaded against the keyspace, so the
|
|
7
|
-
* read → filter → check-limit → write sequence is a single round-trip
|
|
8
|
-
* with no OCC retry loop (the headline win over the NATS KV backend's
|
|
9
|
-
* `WATCH`/`MULTI`/`EXEC`-style optimistic concurrency).
|
|
10
|
-
*
|
|
11
|
-
* Storage model: one Redis string key per `(workflowName, concurrencyKey)`
|
|
12
|
-
* bucket. Value is a JSON-encoded `{leases: [{runId, expiresAt}]}`
|
|
13
|
-
* document. Bounded-cardinality assumption identical to NATS KV — typical
|
|
14
|
-
* concurrency keys hold 1-50 active leases.
|
|
15
|
-
*
|
|
16
|
-
* Lease leak: each lease carries an `expiresAt`. Expired leases are
|
|
17
|
-
* lazy-purged inside the Lua script that observes them; an explicit
|
|
18
|
-
* `purgeExpired` SCAN sweep is also exposed for janitor use.
|
|
19
|
-
*
|
|
20
|
-
* Connection: ioredis is loaded via dynamic `import("ioredis")` so the
|
|
21
|
-
* dependency stays optional. Matches the existing pattern used by
|
|
22
|
-
* `triggers/worker`'s `RedisStreamsAdapter` and
|
|
23
|
-
* `triggers/pubsub`'s `RedisStreamsPubSubAdapter`.
|
|
24
|
-
*/
|
|
25
|
-
import { ConcurrencyMetrics } from "../monitoring/ConcurrencyMetrics";
|
|
26
|
-
const DEFAULT_KEY_PREFIX = "blok-concurrency";
|
|
27
|
-
/**
|
|
28
|
-
* Read configuration from environment variables. Used by
|
|
29
|
-
* {@link createConcurrencyBackend} when the operator opts into Redis.
|
|
30
|
-
*/
|
|
31
|
-
export function readRedisConfigFromEnv() {
|
|
32
|
-
const url = process.env.BLOK_CONCURRENCY_REDIS_URL?.trim() || undefined;
|
|
33
|
-
const host = process.env.BLOK_CONCURRENCY_REDIS_HOST?.trim() || undefined;
|
|
34
|
-
const portRaw = process.env.BLOK_CONCURRENCY_REDIS_PORT?.trim();
|
|
35
|
-
const port = portRaw && /^\d+$/.test(portRaw) ? Number(portRaw) : undefined;
|
|
36
|
-
const dbRaw = process.env.BLOK_CONCURRENCY_REDIS_DB?.trim();
|
|
37
|
-
const db = dbRaw && /^\d+$/.test(dbRaw) ? Number(dbRaw) : undefined;
|
|
38
|
-
const tls = process.env.BLOK_CONCURRENCY_REDIS_TLS === "1" || process.env.BLOK_CONCURRENCY_REDIS_TLS === "true";
|
|
39
|
-
return {
|
|
40
|
-
url,
|
|
41
|
-
host,
|
|
42
|
-
port,
|
|
43
|
-
password: process.env.BLOK_CONCURRENCY_REDIS_PASSWORD,
|
|
44
|
-
username: process.env.BLOK_CONCURRENCY_REDIS_USERNAME,
|
|
45
|
-
db,
|
|
46
|
-
tls,
|
|
47
|
-
keyPrefix: process.env.BLOK_CONCURRENCY_REDIS_KEY_PREFIX?.trim() || DEFAULT_KEY_PREFIX,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Atomic acquire. Returns `{acquired, currentInFlight}` as a 2-element array.
|
|
52
|
-
*
|
|
53
|
-
* Storage shape: the bucket is either MISSING (no active leases) or a
|
|
54
|
-
* JSON string `{"leases":[{"runId":"...","expiresAt":<ms>}, ...]}`.
|
|
55
|
-
* When the leases array would become empty we DEL the key — we never
|
|
56
|
-
* encode the empty array (sidesteps the cjson empty-table-as-object trap).
|
|
57
|
-
*
|
|
58
|
-
* KEYS[1] = bucket key
|
|
59
|
-
* ARGV[1] = limit (int as string)
|
|
60
|
-
* ARGV[2] = runId
|
|
61
|
-
* ARGV[3] = leaseExpiresAt (ms as string)
|
|
62
|
-
* ARGV[4] = now (ms as string)
|
|
63
|
-
*
|
|
64
|
-
* Returns: {acquired, currentInFlight}
|
|
65
|
-
* - acquired: 1 = granted, 0 = denied
|
|
66
|
-
* - currentInFlight: in-flight count INCLUDING the granted slot on success,
|
|
67
|
-
* count at denial on rejection.
|
|
68
|
-
*/
|
|
69
|
-
const ACQUIRE_LUA = `
|
|
70
|
-
local raw = redis.call('GET', KEYS[1])
|
|
71
|
-
local leases = {}
|
|
72
|
-
if raw and raw ~= '' then
|
|
73
|
-
local ok, parsed = pcall(cjson.decode, raw)
|
|
74
|
-
if ok and type(parsed) == 'table' and type(parsed.leases) == 'table' then
|
|
75
|
-
leases = parsed.leases
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
local now = tonumber(ARGV[4])
|
|
80
|
-
local active = {}
|
|
81
|
-
for i = 1, #leases do
|
|
82
|
-
local l = leases[i]
|
|
83
|
-
if type(l) == 'table' and tonumber(l.expiresAt) and tonumber(l.expiresAt) > now then
|
|
84
|
-
active[#active + 1] = { runId = tostring(l.runId), expiresAt = tonumber(l.expiresAt) }
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
local runId = ARGV[2]
|
|
89
|
-
local newExpires = tonumber(ARGV[3])
|
|
90
|
-
|
|
91
|
-
-- Idempotent re-acquire: refresh lease, don't grow count.
|
|
92
|
-
for i = 1, #active do
|
|
93
|
-
if active[i].runId == runId then
|
|
94
|
-
active[i] = { runId = runId, expiresAt = newExpires }
|
|
95
|
-
redis.call('SET', KEYS[1], cjson.encode({ leases = active }))
|
|
96
|
-
return { 1, #active }
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
local limit = tonumber(ARGV[1])
|
|
101
|
-
if #active >= limit then
|
|
102
|
-
-- Persist the purge of expired entries (if any) so the bucket stays clean.
|
|
103
|
-
if #active < #leases then
|
|
104
|
-
if #active == 0 then
|
|
105
|
-
redis.call('DEL', KEYS[1])
|
|
106
|
-
else
|
|
107
|
-
redis.call('SET', KEYS[1], cjson.encode({ leases = active }))
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
return { 0, #active }
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
active[#active + 1] = { runId = runId, expiresAt = newExpires }
|
|
114
|
-
redis.call('SET', KEYS[1], cjson.encode({ leases = active }))
|
|
115
|
-
return { 1, #active }
|
|
116
|
-
`;
|
|
117
|
-
/**
|
|
118
|
-
* Atomic release. Removes a lease by runId. DELs the bucket when empty.
|
|
119
|
-
*
|
|
120
|
-
* KEYS[1] = bucket key
|
|
121
|
-
* ARGV[1] = runId
|
|
122
|
-
*
|
|
123
|
-
* Returns: 1 if a lease was removed, 0 if no-op (bucket missing or runId not present).
|
|
124
|
-
*/
|
|
125
|
-
const RELEASE_LUA = `
|
|
126
|
-
local raw = redis.call('GET', KEYS[1])
|
|
127
|
-
if not raw or raw == '' then return 0 end
|
|
128
|
-
|
|
129
|
-
local ok, parsed = pcall(cjson.decode, raw)
|
|
130
|
-
if not ok or type(parsed) ~= 'table' or type(parsed.leases) ~= 'table' then return 0 end
|
|
131
|
-
|
|
132
|
-
local target = ARGV[1]
|
|
133
|
-
local next_leases = {}
|
|
134
|
-
local removed = 0
|
|
135
|
-
for i = 1, #parsed.leases do
|
|
136
|
-
local l = parsed.leases[i]
|
|
137
|
-
if type(l) == 'table' and tostring(l.runId) == target then
|
|
138
|
-
removed = 1
|
|
139
|
-
else
|
|
140
|
-
next_leases[#next_leases + 1] = { runId = tostring(l.runId), expiresAt = tonumber(l.expiresAt) }
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
if removed == 0 then return 0 end
|
|
145
|
-
|
|
146
|
-
if #next_leases == 0 then
|
|
147
|
-
redis.call('DEL', KEYS[1])
|
|
148
|
-
else
|
|
149
|
-
redis.call('SET', KEYS[1], cjson.encode({ leases = next_leases }))
|
|
150
|
-
end
|
|
151
|
-
return 1
|
|
152
|
-
`;
|
|
153
|
-
/**
|
|
154
|
-
* Purge expired leases from a single bucket. Atomic.
|
|
155
|
-
*
|
|
156
|
-
* KEYS[1] = bucket key
|
|
157
|
-
* ARGV[1] = now (ms as string)
|
|
158
|
-
*
|
|
159
|
-
* Returns: number of leases purged.
|
|
160
|
-
*/
|
|
161
|
-
const PURGE_BUCKET_LUA = `
|
|
162
|
-
local raw = redis.call('GET', KEYS[1])
|
|
163
|
-
if not raw or raw == '' then return 0 end
|
|
164
|
-
|
|
165
|
-
local ok, parsed = pcall(cjson.decode, raw)
|
|
166
|
-
if not ok or type(parsed) ~= 'table' or type(parsed.leases) ~= 'table' then return 0 end
|
|
167
|
-
|
|
168
|
-
local now = tonumber(ARGV[1])
|
|
169
|
-
local active = {}
|
|
170
|
-
for i = 1, #parsed.leases do
|
|
171
|
-
local l = parsed.leases[i]
|
|
172
|
-
if type(l) == 'table' and tonumber(l.expiresAt) and tonumber(l.expiresAt) > now then
|
|
173
|
-
active[#active + 1] = { runId = tostring(l.runId), expiresAt = tonumber(l.expiresAt) }
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
local purged = #parsed.leases - #active
|
|
178
|
-
if purged == 0 then return 0 end
|
|
179
|
-
|
|
180
|
-
if #active == 0 then
|
|
181
|
-
redis.call('DEL', KEYS[1])
|
|
182
|
-
else
|
|
183
|
-
redis.call('SET', KEYS[1], cjson.encode({ leases = active }))
|
|
184
|
-
end
|
|
185
|
-
return purged
|
|
186
|
-
`;
|
|
187
|
-
export class RedisConcurrencyBackend {
|
|
188
|
-
name = "redis";
|
|
189
|
-
client = null;
|
|
190
|
-
config;
|
|
191
|
-
connected = false;
|
|
192
|
-
constructor(config) {
|
|
193
|
-
const env = readRedisConfigFromEnv();
|
|
194
|
-
this.config = {
|
|
195
|
-
url: config?.url ?? env.url,
|
|
196
|
-
host: config?.host ?? env.host,
|
|
197
|
-
port: config?.port ?? env.port,
|
|
198
|
-
password: config?.password ?? env.password,
|
|
199
|
-
username: config?.username ?? env.username,
|
|
200
|
-
db: config?.db ?? env.db,
|
|
201
|
-
tls: config?.tls ?? env.tls,
|
|
202
|
-
keyPrefix: config?.keyPrefix ?? env.keyPrefix,
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
async connect() {
|
|
206
|
-
if (this.connected)
|
|
207
|
-
return;
|
|
208
|
-
// Security review FW-5 parity — refuse to start in production with
|
|
209
|
-
// the default key prefix. Two deployments sharing a Redis instance
|
|
210
|
-
// would silently contend on the same `(workflow, key)` buckets,
|
|
211
|
-
// corrupting concurrency state across tenants.
|
|
212
|
-
const blokEnv = process.env.BLOK_ENV;
|
|
213
|
-
const nodeEnv = process.env.NODE_ENV;
|
|
214
|
-
const isProd = blokEnv === "production" || nodeEnv === "production";
|
|
215
|
-
if (isProd && this.config.keyPrefix === DEFAULT_KEY_PREFIX) {
|
|
216
|
-
throw new Error(`[blok] Redis concurrency backend refuses to start in production with the default key prefix ('${DEFAULT_KEY_PREFIX}'). Set BLOK_CONCURRENCY_REDIS_KEY_PREFIX to a deployment-unique value (e.g. 'blok-concurrency-acme-prod') to prevent cross-deployment collision on a shared Redis instance.`);
|
|
217
|
-
}
|
|
218
|
-
let ioredisModule;
|
|
219
|
-
try {
|
|
220
|
-
ioredisModule = (await import("ioredis"));
|
|
221
|
-
}
|
|
222
|
-
catch (err) {
|
|
223
|
-
throw new Error(`RedisConcurrencyBackend requires the 'ioredis' package. Install it: \`bun add ioredis\` or \`npm install ioredis\`. Underlying error: ${err instanceof Error ? err.message : String(err)}`);
|
|
224
|
-
}
|
|
225
|
-
const IORedisCtor = ioredisModule.default ?? ioredisModule.Redis;
|
|
226
|
-
if (!IORedisCtor) {
|
|
227
|
-
throw new Error("RedisConcurrencyBackend could not locate the ioredis constructor on the imported module. Reinstall ioredis or report this issue.");
|
|
228
|
-
}
|
|
229
|
-
// Production-friendly defaults: fail fast on connection trouble
|
|
230
|
-
// rather than hanging triggers on broker outage. Operators who
|
|
231
|
-
// want different semantics can layer a wrapper or fork the
|
|
232
|
-
// backend — these are intentional opinions matching the "trigger
|
|
233
|
-
// startup should not block indefinitely on broker reachability"
|
|
234
|
-
// posture of the rest of the runner.
|
|
235
|
-
const failFastDefaults = {
|
|
236
|
-
connectTimeout: 5_000,
|
|
237
|
-
maxRetriesPerRequest: 0,
|
|
238
|
-
enableOfflineQueue: false,
|
|
239
|
-
lazyConnect: true,
|
|
240
|
-
};
|
|
241
|
-
if (this.config.url) {
|
|
242
|
-
this.client = new IORedisCtor(this.config.url);
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
const opts = { ...failFastDefaults };
|
|
246
|
-
if (this.config.host)
|
|
247
|
-
opts.host = this.config.host;
|
|
248
|
-
if (this.config.port)
|
|
249
|
-
opts.port = this.config.port;
|
|
250
|
-
if (this.config.username)
|
|
251
|
-
opts.username = this.config.username;
|
|
252
|
-
if (this.config.password)
|
|
253
|
-
opts.password = this.config.password;
|
|
254
|
-
if (typeof this.config.db === "number")
|
|
255
|
-
opts.db = this.config.db;
|
|
256
|
-
if (this.config.tls)
|
|
257
|
-
opts.tls = {};
|
|
258
|
-
this.client = new IORedisCtor(opts);
|
|
259
|
-
}
|
|
260
|
-
// Surface async errors instead of crashing the process.
|
|
261
|
-
this.client.on("error", (err) => {
|
|
262
|
-
console.warn(`[blok][concurrency][redis] client error: ${err.message}`);
|
|
263
|
-
});
|
|
264
|
-
await this.client.ping();
|
|
265
|
-
this.connected = true;
|
|
266
|
-
}
|
|
267
|
-
async disconnect() {
|
|
268
|
-
if (!this.connected)
|
|
269
|
-
return;
|
|
270
|
-
try {
|
|
271
|
-
await this.client?.quit();
|
|
272
|
-
}
|
|
273
|
-
catch {
|
|
274
|
-
// quit() can reject if the connection is already torn down; ignore.
|
|
275
|
-
}
|
|
276
|
-
finally {
|
|
277
|
-
this.client = null;
|
|
278
|
-
this.connected = false;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
bucketKey(workflowName, concurrencyKey) {
|
|
282
|
-
// Mirror NATS KV's hex-escape scheme so workflow/key strings with
|
|
283
|
-
// special characters (`:`, `>`, etc.) round-trip without collision.
|
|
284
|
-
// Cross-backend portability matters when operators migrate between
|
|
285
|
-
// backends: the same `(workflow, key)` pair maps to the same bucket
|
|
286
|
-
// identity modulo the prefix.
|
|
287
|
-
return `${this.config.keyPrefix}:${this.encodeSegment(workflowName)}__${this.encodeSegment(concurrencyKey)}`;
|
|
288
|
-
}
|
|
289
|
-
encodeSegment(s) {
|
|
290
|
-
// Same regex as NATS KV — replace anything outside the safe set
|
|
291
|
-
// with hex escape `_HHHH_` so the encoding is lossless and matches
|
|
292
|
-
// the NATS backend byte-for-byte modulo prefix.
|
|
293
|
-
return s.replace(/[^-_=.a-zA-Z0-9]/g, (ch) => `_${ch.codePointAt(0)?.toString(16)}_`);
|
|
294
|
-
}
|
|
295
|
-
requireClient() {
|
|
296
|
-
if (!this.client) {
|
|
297
|
-
throw new Error("RedisConcurrencyBackend not connected — call connect() first.");
|
|
298
|
-
}
|
|
299
|
-
return this.client;
|
|
300
|
-
}
|
|
301
|
-
async acquireSlot(workflowName, concurrencyKey, concurrencyLimit, runId, leaseExpiresAt) {
|
|
302
|
-
const client = this.requireClient();
|
|
303
|
-
const key = this.bucketKey(workflowName, concurrencyKey);
|
|
304
|
-
const metricAttrs = { workflow_name: workflowName, concurrency_key: concurrencyKey };
|
|
305
|
-
try {
|
|
306
|
-
const raw = await client.eval(ACQUIRE_LUA, 1, key, String(concurrencyLimit), runId, String(leaseExpiresAt), String(Date.now()));
|
|
307
|
-
const [acquiredFlag, currentInFlight] = this.parsePair(raw);
|
|
308
|
-
const outcome = acquiredFlag === 1 ? "success" : "denied";
|
|
309
|
-
// Lua is single-shot — attempt depth is always 0.
|
|
310
|
-
ConcurrencyMetrics.getInstance().recordOccRetries({ ...metricAttrs, outcome }, 0);
|
|
311
|
-
return { acquired: acquiredFlag === 1, currentInFlight };
|
|
312
|
-
}
|
|
313
|
-
catch (err) {
|
|
314
|
-
console.warn(`[blok][concurrency][redis] acquireSlot eval failed for ${workflowName}:${concurrencyKey}: ${err instanceof Error ? err.message : String(err)}; failing closed`);
|
|
315
|
-
ConcurrencyMetrics.getInstance().recordOccRetries({ ...metricAttrs, outcome: "fail-closed" }, 0);
|
|
316
|
-
return { acquired: false, currentInFlight: -1 };
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
async releaseSlot(workflowName, concurrencyKey, runId) {
|
|
320
|
-
const client = this.requireClient();
|
|
321
|
-
const key = this.bucketKey(workflowName, concurrencyKey);
|
|
322
|
-
try {
|
|
323
|
-
await client.eval(RELEASE_LUA, 1, key, runId);
|
|
324
|
-
}
|
|
325
|
-
catch (err) {
|
|
326
|
-
// Lease will expire via TTL — release is best-effort. Surface
|
|
327
|
-
// the error so operators can see broker outages.
|
|
328
|
-
console.warn(`[blok][concurrency][redis] releaseSlot eval failed for ${workflowName}:${concurrencyKey} runId=${runId}: ${err instanceof Error ? err.message : String(err)}; lease will expire via TTL`);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
async purgeExpired(now) {
|
|
332
|
-
const client = this.requireClient();
|
|
333
|
-
const pattern = `${this.config.keyPrefix}:*`;
|
|
334
|
-
let cursor = "0";
|
|
335
|
-
let purged = 0;
|
|
336
|
-
do {
|
|
337
|
-
let res;
|
|
338
|
-
try {
|
|
339
|
-
res = await client.scan(cursor, "MATCH", pattern, "COUNT", 100);
|
|
340
|
-
}
|
|
341
|
-
catch (err) {
|
|
342
|
-
console.warn(`[blok][concurrency][redis] purgeExpired SCAN failed: ${err instanceof Error ? err.message : String(err)}; aborting sweep`);
|
|
343
|
-
return purged;
|
|
344
|
-
}
|
|
345
|
-
const [nextCursor, keys] = res;
|
|
346
|
-
cursor = nextCursor;
|
|
347
|
-
for (const key of keys) {
|
|
348
|
-
try {
|
|
349
|
-
const raw = await client.eval(PURGE_BUCKET_LUA, 1, key, String(now));
|
|
350
|
-
const count = typeof raw === "number" ? raw : Number(raw);
|
|
351
|
-
if (!Number.isNaN(count))
|
|
352
|
-
purged += count;
|
|
353
|
-
}
|
|
354
|
-
catch {
|
|
355
|
-
// Best-effort — skip this bucket; janitor will retry on next sweep.
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
} while (cursor !== "0");
|
|
359
|
-
return purged;
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* Decode the `{acquired, currentInFlight}` pair from a Lua eval result.
|
|
363
|
-
* ioredis returns Redis arrays as plain JS arrays of (string | number)
|
|
364
|
-
* — the script returns integers, so both elements should be numbers.
|
|
365
|
-
*/
|
|
366
|
-
parsePair(raw) {
|
|
367
|
-
if (!Array.isArray(raw) || raw.length < 2)
|
|
368
|
-
return [0, -1];
|
|
369
|
-
const acquired = typeof raw[0] === "number" ? raw[0] : Number(raw[0]);
|
|
370
|
-
const current = typeof raw[1] === "number" ? raw[1] : Number(raw[1]);
|
|
371
|
-
return [Number.isFinite(acquired) ? acquired : 0, Number.isFinite(current) ? current : -1];
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
//# sourceMappingURL=RedisConcurrencyBackend.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RedisConcurrencyBackend.js","sourceRoot":"","sources":["../../src/concurrency/RedisConcurrencyBackend.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tier 2 #6 follow-up · concurrency backend factory.
|
|
3
|
-
*
|
|
4
|
-
* Reads `BLOK_CONCURRENCY_BACKEND` and returns the matching backend
|
|
5
|
-
* instance, or `null` when the user wants the default in-process behavior.
|
|
6
|
-
*
|
|
7
|
-
* Trigger packages call this in `listen()` and pass the result to
|
|
8
|
-
* `RunTracker.getInstance().setConcurrencyBackend(backend)`.
|
|
9
|
-
*/
|
|
10
|
-
import type { ConcurrencyBackend } from "./ConcurrencyBackend";
|
|
11
|
-
/**
|
|
12
|
-
* Returns a configured `ConcurrencyBackend` based on
|
|
13
|
-
* `BLOK_CONCURRENCY_BACKEND`, or `null` for the default in-process backend.
|
|
14
|
-
*
|
|
15
|
-
* Recognized values:
|
|
16
|
-
* - unset / `""` / `"memory"` — null (use default in-process via RunStore)
|
|
17
|
-
* - `"nats-kv"` — NATS KV backend (requires `nats` package + reachable NATS server)
|
|
18
|
-
* - `"redis"` — Redis backend (requires `ioredis` package + reachable Redis server)
|
|
19
|
-
*
|
|
20
|
-
* Unknown values throw at startup with a clear error message — silently
|
|
21
|
-
* falling back would be dangerous (operator thinks they configured cross-
|
|
22
|
-
* process coordination but they didn't).
|
|
23
|
-
*/
|
|
24
|
-
export declare function createConcurrencyBackend(): ConcurrencyBackend | null;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tier 2 #6 follow-up · concurrency backend factory.
|
|
3
|
-
*
|
|
4
|
-
* Reads `BLOK_CONCURRENCY_BACKEND` and returns the matching backend
|
|
5
|
-
* instance, or `null` when the user wants the default in-process behavior.
|
|
6
|
-
*
|
|
7
|
-
* Trigger packages call this in `listen()` and pass the result to
|
|
8
|
-
* `RunTracker.getInstance().setConcurrencyBackend(backend)`.
|
|
9
|
-
*/
|
|
10
|
-
import { NatsKvConcurrencyBackend } from "./NatsKvConcurrencyBackend";
|
|
11
|
-
import { RedisConcurrencyBackend } from "./RedisConcurrencyBackend";
|
|
12
|
-
/**
|
|
13
|
-
* Returns a configured `ConcurrencyBackend` based on
|
|
14
|
-
* `BLOK_CONCURRENCY_BACKEND`, or `null` for the default in-process backend.
|
|
15
|
-
*
|
|
16
|
-
* Recognized values:
|
|
17
|
-
* - unset / `""` / `"memory"` — null (use default in-process via RunStore)
|
|
18
|
-
* - `"nats-kv"` — NATS KV backend (requires `nats` package + reachable NATS server)
|
|
19
|
-
* - `"redis"` — Redis backend (requires `ioredis` package + reachable Redis server)
|
|
20
|
-
*
|
|
21
|
-
* Unknown values throw at startup with a clear error message — silently
|
|
22
|
-
* falling back would be dangerous (operator thinks they configured cross-
|
|
23
|
-
* process coordination but they didn't).
|
|
24
|
-
*/
|
|
25
|
-
export function createConcurrencyBackend() {
|
|
26
|
-
const kind = (process.env.BLOK_CONCURRENCY_BACKEND ?? "").trim().toLowerCase();
|
|
27
|
-
if (!kind || kind === "memory" || kind === "in-process")
|
|
28
|
-
return null;
|
|
29
|
-
switch (kind) {
|
|
30
|
-
case "nats-kv":
|
|
31
|
-
return new NatsKvConcurrencyBackend();
|
|
32
|
-
case "redis":
|
|
33
|
-
return new RedisConcurrencyBackend();
|
|
34
|
-
default:
|
|
35
|
-
throw new Error(`Unknown BLOK_CONCURRENCY_BACKEND='${kind}'. Expected one of: 'memory' (default), 'nats-kv', 'redis'.`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
//# sourceMappingURL=createConcurrencyBackend.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"createConcurrencyBackend.js","sourceRoot":"","sources":["../../src/concurrency/createConcurrencyBackend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAEpE;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,wBAAwB;IACvC,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC/E,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,YAAY;QAAE,OAAO,IAAI,CAAC;IAErE,QAAQ,IAAI,EAAE,CAAC;QACd,KAAK,SAAS;YACb,OAAO,IAAI,wBAAwB,EAAE,CAAC;QACvC,KAAK,OAAO;YACX,OAAO,IAAI,uBAAuB,EAAE,CAAC;QACtC;YACC,MAAM,IAAI,KAAK,CACd,qCAAqC,IAAI,6DAA6D,CACtG,CAAC;IACJ,CAAC;AACF,CAAC"}
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GraphQL Schema Generator for Blok Workflows
|
|
3
|
-
*
|
|
4
|
-
* Automatically generates GraphQL schema definitions (SDL) from workflow definitions.
|
|
5
|
-
* Introspects HTTP trigger configurations and node structures to produce
|
|
6
|
-
* Query/Mutation types with appropriate input/output types.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```typescript
|
|
10
|
-
* const generator = new GraphQLSchemaGenerator({
|
|
11
|
-
* schemaName: "BlokAPI",
|
|
12
|
-
* includeSubscriptions: true,
|
|
13
|
-
* });
|
|
14
|
-
*
|
|
15
|
-
* generator.addWorkflow({
|
|
16
|
-
* name: "get-user",
|
|
17
|
-
* version: "1.0.0",
|
|
18
|
-
* trigger: { http: { method: "GET", path: "/users/:id" } },
|
|
19
|
-
* steps: [{ name: "fetch", node: "db-query", type: "local" }],
|
|
20
|
-
* nodes: { "db-query": {} },
|
|
21
|
-
* });
|
|
22
|
-
*
|
|
23
|
-
* console.log(generator.generate());
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
export interface GraphQLGeneratorConfig {
|
|
27
|
-
/** Schema name used in documentation */
|
|
28
|
-
schemaName?: string;
|
|
29
|
-
/** Description for the schema */
|
|
30
|
-
description?: string;
|
|
31
|
-
/** Include subscription types for WebSocket/SSE workflows */
|
|
32
|
-
includeSubscriptions?: boolean;
|
|
33
|
-
/** Include workflow metadata in type descriptions */
|
|
34
|
-
includeMetadata?: boolean;
|
|
35
|
-
/** Custom scalar definitions */
|
|
36
|
-
customScalars?: Array<{
|
|
37
|
-
name: string;
|
|
38
|
-
description: string;
|
|
39
|
-
}>;
|
|
40
|
-
}
|
|
41
|
-
export interface GqlWorkflowDefinition {
|
|
42
|
-
name: string;
|
|
43
|
-
version: string;
|
|
44
|
-
description?: string;
|
|
45
|
-
trigger: {
|
|
46
|
-
http?: {
|
|
47
|
-
method: string;
|
|
48
|
-
path: string;
|
|
49
|
-
};
|
|
50
|
-
grpc?: {
|
|
51
|
-
service: string;
|
|
52
|
-
method: string;
|
|
53
|
-
};
|
|
54
|
-
websocket?: {
|
|
55
|
-
path?: string;
|
|
56
|
-
};
|
|
57
|
-
sse?: {
|
|
58
|
-
path?: string;
|
|
59
|
-
};
|
|
60
|
-
[key: string]: unknown;
|
|
61
|
-
};
|
|
62
|
-
steps: Array<{
|
|
63
|
-
name: string;
|
|
64
|
-
node: string;
|
|
65
|
-
type?: string;
|
|
66
|
-
runtime?: string;
|
|
67
|
-
}>;
|
|
68
|
-
nodes: Record<string, unknown>;
|
|
69
|
-
inputs?: Record<string, GqlFieldDef>;
|
|
70
|
-
outputs?: Record<string, GqlFieldDef>;
|
|
71
|
-
}
|
|
72
|
-
export interface GqlFieldDef {
|
|
73
|
-
type: string;
|
|
74
|
-
required?: boolean;
|
|
75
|
-
description?: string;
|
|
76
|
-
items?: GqlFieldDef;
|
|
77
|
-
fields?: Record<string, GqlFieldDef>;
|
|
78
|
-
}
|
|
79
|
-
export declare class GraphQLSchemaGenerator {
|
|
80
|
-
private config;
|
|
81
|
-
private workflows;
|
|
82
|
-
private customTypes;
|
|
83
|
-
constructor(config?: GraphQLGeneratorConfig);
|
|
84
|
-
addWorkflow(workflow: GqlWorkflowDefinition): void;
|
|
85
|
-
addWorkflows(workflows: GqlWorkflowDefinition[]): void;
|
|
86
|
-
addCustomType(name: string, definition: string): void;
|
|
87
|
-
/**
|
|
88
|
-
* Generate GraphQL Schema Definition Language (SDL)
|
|
89
|
-
*/
|
|
90
|
-
generate(): string;
|
|
91
|
-
/**
|
|
92
|
-
* Generate an introspection-friendly JSON representation
|
|
93
|
-
*/
|
|
94
|
-
toJSON(): GraphQLSchemaJSON;
|
|
95
|
-
private resolveWorkflow;
|
|
96
|
-
private generateInputType;
|
|
97
|
-
private generateOutputType;
|
|
98
|
-
private generateFieldDefinition;
|
|
99
|
-
private generateSubscriptionField;
|
|
100
|
-
private inferOutputFields;
|
|
101
|
-
private inferInputFields;
|
|
102
|
-
private fieldDefToGraphQLType;
|
|
103
|
-
private getTriggerType;
|
|
104
|
-
private extractPathParams;
|
|
105
|
-
private toPascalCase;
|
|
106
|
-
private toFieldName;
|
|
107
|
-
private emptySchema;
|
|
108
|
-
}
|
|
109
|
-
export interface GraphQLSchemaJSON {
|
|
110
|
-
schemaName: string;
|
|
111
|
-
types: GraphQLTypeInfo[];
|
|
112
|
-
queries: GraphQLFieldInfo[];
|
|
113
|
-
mutations: GraphQLFieldInfo[];
|
|
114
|
-
subscriptions: GraphQLFieldInfo[];
|
|
115
|
-
}
|
|
116
|
-
export interface GraphQLTypeInfo {
|
|
117
|
-
name: string;
|
|
118
|
-
kind: "OBJECT" | "INPUT_OBJECT" | "ENUM" | "SCALAR";
|
|
119
|
-
fields: GraphQLFieldInfo[];
|
|
120
|
-
}
|
|
121
|
-
export interface GraphQLFieldInfo {
|
|
122
|
-
name: string;
|
|
123
|
-
type: string;
|
|
124
|
-
description?: string;
|
|
125
|
-
args?: Array<{
|
|
126
|
-
name: string;
|
|
127
|
-
type: string;
|
|
128
|
-
}>;
|
|
129
|
-
}
|