@blokjs/runner 0.6.21 → 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.
Files changed (167) hide show
  1. package/dist/Blok.d.ts +2 -0
  2. package/dist/Blok.js +42 -110
  3. package/dist/Blok.js.map +1 -1
  4. package/dist/DefaultLogger.d.ts +13 -0
  5. package/dist/DefaultLogger.js +25 -0
  6. package/dist/DefaultLogger.js.map +1 -1
  7. package/dist/RunnerSteps.d.ts +23 -0
  8. package/dist/RunnerSteps.js +128 -87
  9. package/dist/RunnerSteps.js.map +1 -1
  10. package/dist/SubworkflowNode.js +19 -0
  11. package/dist/SubworkflowNode.js.map +1 -1
  12. package/dist/TriggerBase.d.ts +12 -0
  13. package/dist/TriggerBase.js +216 -181
  14. package/dist/TriggerBase.js.map +1 -1
  15. package/dist/adapters/grpc/GrpcRuntimeAdapter.d.ts +9 -0
  16. package/dist/adapters/grpc/GrpcRuntimeAdapter.js +76 -6
  17. package/dist/adapters/grpc/GrpcRuntimeAdapter.js.map +1 -1
  18. package/dist/index.d.ts +4 -39
  19. package/dist/index.js +7 -32
  20. package/dist/index.js.map +1 -1
  21. package/dist/monitoring/JanitorMetrics.d.ts +3 -0
  22. package/dist/monitoring/JanitorMetrics.js +11 -0
  23. package/dist/monitoring/JanitorMetrics.js.map +1 -1
  24. package/dist/monitoring/ProcessErrorMetrics.d.ts +32 -0
  25. package/dist/monitoring/ProcessErrorMetrics.js +43 -0
  26. package/dist/monitoring/ProcessErrorMetrics.js.map +1 -0
  27. package/dist/monitoring/PrometheusMetricsBridge.d.ts +7 -0
  28. package/dist/monitoring/PrometheusMetricsBridge.js +8 -2
  29. package/dist/monitoring/PrometheusMetricsBridge.js.map +1 -1
  30. package/dist/monitoring/SubworkflowMetrics.d.ts +25 -0
  31. package/dist/monitoring/SubworkflowMetrics.js +38 -0
  32. package/dist/monitoring/SubworkflowMetrics.js.map +1 -0
  33. package/dist/observability/ErrorSink.d.ts +23 -0
  34. package/dist/observability/ErrorSink.js +32 -0
  35. package/dist/observability/ErrorSink.js.map +1 -0
  36. package/dist/observability/SentryIntegration.d.ts +9 -0
  37. package/dist/observability/SentryIntegration.js +31 -0
  38. package/dist/observability/SentryIntegration.js.map +1 -0
  39. package/dist/scheduling/DebounceCoordinator.d.ts +7 -53
  40. package/dist/scheduling/DebounceCoordinator.js +8 -207
  41. package/dist/scheduling/DebounceCoordinator.js.map +1 -1
  42. package/dist/tracing/InMemoryRunStore.d.ts +5 -1
  43. package/dist/tracing/InMemoryRunStore.js +14 -0
  44. package/dist/tracing/InMemoryRunStore.js.map +1 -1
  45. package/dist/tracing/Janitor.js +3 -0
  46. package/dist/tracing/Janitor.js.map +1 -1
  47. package/dist/tracing/PostgresRunStore.d.ts +4 -1
  48. package/dist/tracing/PostgresRunStore.js +73 -3
  49. package/dist/tracing/PostgresRunStore.js.map +1 -1
  50. package/dist/tracing/RunStore.d.ts +17 -1
  51. package/dist/tracing/RunTracker.d.ts +13 -34
  52. package/dist/tracing/RunTracker.js +62 -32
  53. package/dist/tracing/RunTracker.js.map +1 -1
  54. package/dist/tracing/SqliteRunStore.d.ts +4 -1
  55. package/dist/tracing/SqliteRunStore.js +60 -0
  56. package/dist/tracing/SqliteRunStore.js.map +1 -1
  57. package/dist/tracing/TraceRouter.d.ts +13 -0
  58. package/dist/tracing/TraceRouter.js +43 -11
  59. package/dist/tracing/TraceRouter.js.map +1 -1
  60. package/dist/tracing/TracingLogger.js +22 -0
  61. package/dist/tracing/TracingLogger.js.map +1 -1
  62. package/dist/tracing/createStore.js +51 -22
  63. package/dist/tracing/createStore.js.map +1 -1
  64. package/dist/tracing/types.d.ts +22 -0
  65. package/dist/types/GlobalOptions.d.ts +5 -7
  66. package/dist/workflow/WorkflowNormalizer.js +63 -0
  67. package/dist/workflow/WorkflowNormalizer.js.map +1 -1
  68. package/package.json +7 -4
  69. package/dist/cache/NodeResultCache.d.ts +0 -286
  70. package/dist/cache/NodeResultCache.js +0 -506
  71. package/dist/cache/NodeResultCache.js.map +0 -1
  72. package/dist/cache/index.d.ts +0 -1
  73. package/dist/cache/index.js +0 -2
  74. package/dist/cache/index.js.map +0 -1
  75. package/dist/concurrency/ConcurrencyBackend.d.ts +0 -61
  76. package/dist/concurrency/ConcurrencyBackend.js +0 -20
  77. package/dist/concurrency/ConcurrencyBackend.js.map +0 -1
  78. package/dist/concurrency/NatsKvConcurrencyBackend.d.ts +0 -64
  79. package/dist/concurrency/NatsKvConcurrencyBackend.js +0 -310
  80. package/dist/concurrency/NatsKvConcurrencyBackend.js.map +0 -1
  81. package/dist/concurrency/RedisConcurrencyBackend.d.ts +0 -64
  82. package/dist/concurrency/RedisConcurrencyBackend.js +0 -374
  83. package/dist/concurrency/RedisConcurrencyBackend.js.map +0 -1
  84. package/dist/concurrency/createConcurrencyBackend.d.ts +0 -24
  85. package/dist/concurrency/createConcurrencyBackend.js +0 -38
  86. package/dist/concurrency/createConcurrencyBackend.js.map +0 -1
  87. package/dist/graphql/GraphQLSchemaGenerator.d.ts +0 -129
  88. package/dist/graphql/GraphQLSchemaGenerator.js +0 -425
  89. package/dist/graphql/GraphQLSchemaGenerator.js.map +0 -1
  90. package/dist/integrations/APMIntegration.d.ts +0 -141
  91. package/dist/integrations/APMIntegration.js +0 -212
  92. package/dist/integrations/APMIntegration.js.map +0 -1
  93. package/dist/integrations/AzureMonitorIntegration.d.ts +0 -118
  94. package/dist/integrations/AzureMonitorIntegration.js +0 -254
  95. package/dist/integrations/AzureMonitorIntegration.js.map +0 -1
  96. package/dist/integrations/CloudWatchIntegration.d.ts +0 -135
  97. package/dist/integrations/CloudWatchIntegration.js +0 -293
  98. package/dist/integrations/CloudWatchIntegration.js.map +0 -1
  99. package/dist/integrations/SentryIntegration.d.ts +0 -153
  100. package/dist/integrations/SentryIntegration.js +0 -200
  101. package/dist/integrations/SentryIntegration.js.map +0 -1
  102. package/dist/integrations/index.d.ts +0 -19
  103. package/dist/integrations/index.js +0 -16
  104. package/dist/integrations/index.js.map +0 -1
  105. package/dist/marketplace/RuntimeAutoScaler.d.ts +0 -148
  106. package/dist/marketplace/RuntimeAutoScaler.js +0 -366
  107. package/dist/marketplace/RuntimeAutoScaler.js.map +0 -1
  108. package/dist/marketplace/RuntimeCatalog.d.ts +0 -180
  109. package/dist/marketplace/RuntimeCatalog.js +0 -339
  110. package/dist/marketplace/RuntimeCatalog.js.map +0 -1
  111. package/dist/marketplace/RuntimeDiscovery.d.ts +0 -86
  112. package/dist/marketplace/RuntimeDiscovery.js +0 -231
  113. package/dist/marketplace/RuntimeDiscovery.js.map +0 -1
  114. package/dist/marketplace/RuntimeHealthMonitor.d.ts +0 -100
  115. package/dist/marketplace/RuntimeHealthMonitor.js +0 -241
  116. package/dist/marketplace/RuntimeHealthMonitor.js.map +0 -1
  117. package/dist/marketplace/RuntimeMetricsDashboard.d.ts +0 -113
  118. package/dist/marketplace/RuntimeMetricsDashboard.js +0 -293
  119. package/dist/marketplace/RuntimeMetricsDashboard.js.map +0 -1
  120. package/dist/openapi/OpenAPIGenerator.d.ts +0 -192
  121. package/dist/openapi/OpenAPIGenerator.js +0 -378
  122. package/dist/openapi/OpenAPIGenerator.js.map +0 -1
  123. package/dist/openapi/index.d.ts +0 -20
  124. package/dist/openapi/index.js +0 -20
  125. package/dist/openapi/index.js.map +0 -1
  126. package/dist/scheduling/DebounceBackend.d.ts +0 -108
  127. package/dist/scheduling/DebounceBackend.js +0 -23
  128. package/dist/scheduling/DebounceBackend.js.map +0 -1
  129. package/dist/scheduling/NatsKvDebounceBackend.d.ts +0 -53
  130. package/dist/scheduling/NatsKvDebounceBackend.js +0 -334
  131. package/dist/scheduling/NatsKvDebounceBackend.js.map +0 -1
  132. package/dist/scheduling/RedisDebounceBackend.d.ts +0 -49
  133. package/dist/scheduling/RedisDebounceBackend.js +0 -356
  134. package/dist/scheduling/RedisDebounceBackend.js.map +0 -1
  135. package/dist/scheduling/createDebounceBackend.d.ts +0 -25
  136. package/dist/scheduling/createDebounceBackend.js +0 -39
  137. package/dist/scheduling/createDebounceBackend.js.map +0 -1
  138. package/dist/security/ABAC.d.ts +0 -224
  139. package/dist/security/ABAC.js +0 -380
  140. package/dist/security/ABAC.js.map +0 -1
  141. package/dist/security/AuditLogger.d.ts +0 -242
  142. package/dist/security/AuditLogger.js +0 -317
  143. package/dist/security/AuditLogger.js.map +0 -1
  144. package/dist/security/AuthMiddleware.d.ts +0 -162
  145. package/dist/security/AuthMiddleware.js +0 -289
  146. package/dist/security/AuthMiddleware.js.map +0 -1
  147. package/dist/security/EncryptionAtRest.d.ts +0 -206
  148. package/dist/security/EncryptionAtRest.js +0 -236
  149. package/dist/security/EncryptionAtRest.js.map +0 -1
  150. package/dist/security/OAuthProvider.d.ts +0 -334
  151. package/dist/security/OAuthProvider.js +0 -719
  152. package/dist/security/OAuthProvider.js.map +0 -1
  153. package/dist/security/PIIDetector.d.ts +0 -233
  154. package/dist/security/PIIDetector.js +0 -354
  155. package/dist/security/PIIDetector.js.map +0 -1
  156. package/dist/security/RBAC.d.ts +0 -143
  157. package/dist/security/RBAC.js +0 -285
  158. package/dist/security/RBAC.js.map +0 -1
  159. package/dist/security/SecretManager.d.ts +0 -652
  160. package/dist/security/SecretManager.js +0 -1147
  161. package/dist/security/SecretManager.js.map +0 -1
  162. package/dist/security/TLSConfig.d.ts +0 -305
  163. package/dist/security/TLSConfig.js +0 -550
  164. package/dist/security/TLSConfig.js.map +0 -1
  165. package/dist/security/index.d.ts +0 -81
  166. package/dist/security/index.js +0 -82
  167. package/dist/security/index.js.map +0 -1
@@ -1,356 +0,0 @@
1
- /**
2
- * Tier C #1 · Redis-backed debounce backend.
3
- *
4
- * Coordinates per-(workflow, debounceKey) window state across processes
5
- * via a single Redis string key per bucket. Atomicity comes from
6
- * server-side **Lua scripts** — `registerPing` / `finalize` /
7
- * `cancel` / `purgeExpired` each run as a single `EVAL` with no OCC
8
- * retry loop (Lua runs single-threaded against the keyspace).
9
- *
10
- * Storage shape: one JSON document per `(workflowName, debounceKey)`
11
- * bucket. Owner identity is encoded in the doc itself as
12
- * `(activeRunId, ownerProcessId, ownerLeaseExpiresAt)`; lease handoff
13
- * happens atomically when a ping arrives after the lease expired.
14
- *
15
- * **Owner-local payload**: this backend tracks `pingCount`,
16
- * `lastPingAt`, and `scheduledAt` only — payloads do NOT travel across
17
- * processes. The owning process's local `onFire` closure fires when
18
- * its timer elapses.
19
- */
20
- const DEFAULT_KEY_PREFIX = "blok-debounce";
21
- /**
22
- * Atomic registerPing. Reads the current doc (if any), decides
23
- * ownership, writes back the next state, and returns
24
- * `{outcome, activeRunId, scheduledAt, pingCount}`.
25
- *
26
- * KEYS[1] = bucket key
27
- * ARGV[1] = mode ("leading" | "trailing")
28
- * ARGV[2] = delayMs
29
- * ARGV[3] = maxDelayMs ("-1" when unset)
30
- * ARGV[4] = runId
31
- * ARGV[5] = processId
32
- * ARGV[6] = ownerLeaseMs
33
- * ARGV[7] = now
34
- *
35
- * Returns: { outcome ("owner-new" | "owner-extend" | "coalesce"),
36
- * activeRunId, scheduledAt, pingCount }
37
- */
38
- const REGISTER_PING_LUA = `
39
- local raw = redis.call('GET', KEYS[1])
40
- local existing = nil
41
- if raw and raw ~= '' then
42
- local ok, parsed = pcall(cjson.decode, raw)
43
- if ok and type(parsed) == 'table' then
44
- existing = parsed
45
- end
46
- end
47
-
48
- local mode = ARGV[1]
49
- local delayMs = tonumber(ARGV[2])
50
- local maxDelayMsRaw = tonumber(ARGV[3])
51
- local maxDelayMs
52
- if maxDelayMsRaw and maxDelayMsRaw >= 0 then maxDelayMs = maxDelayMsRaw end
53
- local runId = ARGV[4]
54
- local processId = ARGV[5]
55
- local ownerLeaseMs = tonumber(ARGV[6])
56
- local now = tonumber(ARGV[7])
57
-
58
- local function compute_scheduled_at(existing_doc, now_)
59
- local naive = now_ + delayMs
60
- local deadline
61
- if existing_doc and existing_doc.maxDelayDeadline then
62
- deadline = tonumber(existing_doc.maxDelayDeadline)
63
- elseif maxDelayMs then
64
- deadline = now_ + maxDelayMs
65
- end
66
- if deadline and deadline < naive then return deadline end
67
- return naive
68
- end
69
-
70
- local ownerActive = existing ~= nil and tonumber(existing.ownerLeaseExpiresAt) and tonumber(existing.ownerLeaseExpiresAt) > now
71
-
72
- if not existing or not ownerActive then
73
- local first_ping_at = (existing and existing.firstPingAt) and tonumber(existing.firstPingAt) or now
74
- local max_delay_deadline
75
- if existing and existing.maxDelayDeadline then
76
- max_delay_deadline = tonumber(existing.maxDelayDeadline)
77
- elseif maxDelayMs then
78
- max_delay_deadline = now + maxDelayMs
79
- end
80
- local prev_count = (existing and existing.pingCount) and tonumber(existing.pingCount) or 0
81
- local doc = {
82
- mode = mode,
83
- delayMs = delayMs,
84
- maxDelayMs = maxDelayMs,
85
- maxDelayDeadline = max_delay_deadline,
86
- firstPingAt = first_ping_at,
87
- lastPingAt = now,
88
- pingCount = prev_count + 1,
89
- activeRunId = runId,
90
- ownerProcessId = processId,
91
- ownerLeaseExpiresAt = now + ownerLeaseMs,
92
- scheduledAt = compute_scheduled_at(existing, now),
93
- }
94
- redis.call('SET', KEYS[1], cjson.encode(doc))
95
- return { "owner-new", doc.activeRunId, tostring(doc.scheduledAt), tostring(doc.pingCount) }
96
- end
97
-
98
- if tostring(existing.ownerProcessId) == processId then
99
- -- We still own — extend window.
100
- existing.lastPingAt = now
101
- existing.pingCount = (tonumber(existing.pingCount) or 0) + 1
102
- existing.ownerLeaseExpiresAt = now + ownerLeaseMs
103
- existing.scheduledAt = compute_scheduled_at(existing, now)
104
- redis.call('SET', KEYS[1], cjson.encode(existing))
105
- return { "owner-extend", tostring(existing.activeRunId), tostring(existing.scheduledAt), tostring(existing.pingCount) }
106
- end
107
-
108
- -- Different process owns — coalesce.
109
- existing.lastPingAt = now
110
- existing.pingCount = (tonumber(existing.pingCount) or 0) + 1
111
- existing.scheduledAt = compute_scheduled_at(existing, now)
112
- redis.call('SET', KEYS[1], cjson.encode(existing))
113
- return { "coalesce", tostring(existing.activeRunId), tostring(existing.scheduledAt), tostring(existing.pingCount) }
114
- `;
115
- /**
116
- * Atomic finalize. The owning process calls this on local timer fire.
117
- *
118
- * KEYS[1] = bucket key
119
- * ARGV[1] = runId (the OWNING runId from the caller's perspective)
120
- * ARGV[2] = now
121
- *
122
- * Returns:
123
- * { "fire" } — caller still owns AND silence elapsed; DELETE done.
124
- * { "reschedule", "<scheduledAt>" } — coalesce pings pushed scheduledAt forward.
125
- * { "abandoned" } — caller no longer owns OR bucket gone.
126
- */
127
- const FINALIZE_LUA = `
128
- local raw = redis.call('GET', KEYS[1])
129
- if not raw or raw == '' then return { "abandoned" } end
130
-
131
- local ok, doc = pcall(cjson.decode, raw)
132
- if not ok or type(doc) ~= 'table' then return { "abandoned" } end
133
-
134
- if tostring(doc.activeRunId) ~= ARGV[1] then return { "abandoned" } end
135
-
136
- local now = tonumber(ARGV[2])
137
- local scheduled = tonumber(doc.scheduledAt) or 0
138
- if now < scheduled then
139
- return { "reschedule", tostring(scheduled) }
140
- end
141
-
142
- redis.call('DEL', KEYS[1])
143
- return { "fire" }
144
- `;
145
- export function readRedisDebounceConfigFromEnv() {
146
- const url = process.env.BLOK_DEBOUNCE_REDIS_URL?.trim() || undefined;
147
- const host = process.env.BLOK_DEBOUNCE_REDIS_HOST?.trim() || undefined;
148
- const portRaw = process.env.BLOK_DEBOUNCE_REDIS_PORT?.trim();
149
- const port = portRaw && /^\d+$/.test(portRaw) ? Number(portRaw) : undefined;
150
- const dbRaw = process.env.BLOK_DEBOUNCE_REDIS_DB?.trim();
151
- const db = dbRaw && /^\d+$/.test(dbRaw) ? Number(dbRaw) : undefined;
152
- const tls = process.env.BLOK_DEBOUNCE_REDIS_TLS === "1" || process.env.BLOK_DEBOUNCE_REDIS_TLS === "true";
153
- return {
154
- url,
155
- host,
156
- port,
157
- password: process.env.BLOK_DEBOUNCE_REDIS_PASSWORD,
158
- username: process.env.BLOK_DEBOUNCE_REDIS_USERNAME,
159
- db,
160
- tls,
161
- keyPrefix: process.env.BLOK_DEBOUNCE_REDIS_KEY_PREFIX?.trim() || DEFAULT_KEY_PREFIX,
162
- };
163
- }
164
- export class RedisDebounceBackend {
165
- name = "redis";
166
- client = null;
167
- config;
168
- connected = false;
169
- constructor(config) {
170
- const env = readRedisDebounceConfigFromEnv();
171
- this.config = {
172
- url: config?.url ?? env.url,
173
- host: config?.host ?? env.host,
174
- port: config?.port ?? env.port,
175
- password: config?.password ?? env.password,
176
- username: config?.username ?? env.username,
177
- db: config?.db ?? env.db,
178
- tls: config?.tls ?? env.tls,
179
- keyPrefix: config?.keyPrefix ?? env.keyPrefix,
180
- };
181
- }
182
- async connect() {
183
- if (this.connected)
184
- return;
185
- const blokEnv = process.env.BLOK_ENV;
186
- const nodeEnv = process.env.NODE_ENV;
187
- const isProd = blokEnv === "production" || nodeEnv === "production";
188
- if (isProd && this.config.keyPrefix === DEFAULT_KEY_PREFIX) {
189
- throw new Error(`[blok] Redis debounce backend refuses to start in production with the default key prefix ('${DEFAULT_KEY_PREFIX}'). Set BLOK_DEBOUNCE_REDIS_KEY_PREFIX to a deployment-unique value (e.g. 'blok-debounce-acme-prod') to prevent cross-deployment collision on a shared Redis instance.`);
190
- }
191
- let ioredisModule;
192
- try {
193
- ioredisModule = (await import("ioredis"));
194
- }
195
- catch (err) {
196
- throw new Error(`RedisDebounceBackend requires the 'ioredis' package. Install it: \`bun add ioredis\` or \`npm install ioredis\`. Underlying error: ${err instanceof Error ? err.message : String(err)}`);
197
- }
198
- const IORedisCtor = ioredisModule.default ?? ioredisModule.Redis;
199
- if (!IORedisCtor) {
200
- throw new Error("RedisDebounceBackend could not locate the ioredis constructor on the imported module. Reinstall ioredis or report this issue.");
201
- }
202
- // Same fail-fast posture as RedisConcurrencyBackend.
203
- const failFastDefaults = {
204
- connectTimeout: 5_000,
205
- maxRetriesPerRequest: 0,
206
- enableOfflineQueue: false,
207
- lazyConnect: true,
208
- };
209
- if (this.config.url) {
210
- this.client = new IORedisCtor(this.config.url);
211
- }
212
- else {
213
- const opts = { ...failFastDefaults };
214
- if (this.config.host)
215
- opts.host = this.config.host;
216
- if (this.config.port)
217
- opts.port = this.config.port;
218
- if (this.config.username)
219
- opts.username = this.config.username;
220
- if (this.config.password)
221
- opts.password = this.config.password;
222
- if (typeof this.config.db === "number")
223
- opts.db = this.config.db;
224
- if (this.config.tls)
225
- opts.tls = {};
226
- this.client = new IORedisCtor(opts);
227
- }
228
- this.client.on("error", (err) => {
229
- console.warn(`[blok][debounce][redis] client error: ${err.message}`);
230
- });
231
- await this.client.ping();
232
- this.connected = true;
233
- }
234
- async disconnect() {
235
- if (!this.connected)
236
- return;
237
- try {
238
- await this.client?.quit();
239
- }
240
- catch {
241
- // best-effort
242
- }
243
- finally {
244
- this.client = null;
245
- this.connected = false;
246
- }
247
- }
248
- bucketKey(workflowName, debounceKey) {
249
- return `${this.config.keyPrefix}:${this.encodeSegment(workflowName)}__${this.encodeSegment(debounceKey)}`;
250
- }
251
- encodeSegment(s) {
252
- return s.replace(/[^-_=.a-zA-Z0-9]/g, (ch) => `_${ch.codePointAt(0)?.toString(16)}_`);
253
- }
254
- requireClient() {
255
- if (!this.client) {
256
- throw new Error("RedisDebounceBackend not connected — call connect() first.");
257
- }
258
- return this.client;
259
- }
260
- async registerPing(opts) {
261
- const client = this.requireClient();
262
- const key = this.bucketKey(opts.workflowName, opts.debounceKey);
263
- const raw = await client.eval(REGISTER_PING_LUA, 1, key, opts.mode, String(opts.delayMs), opts.maxDelayMs !== undefined ? String(opts.maxDelayMs) : "-1", opts.runId, opts.processId, String(opts.ownerLeaseMs), String(opts.now));
264
- return this.parseRegisterResult(raw);
265
- }
266
- async finalize(workflowName, debounceKey, runId, now) {
267
- const client = this.requireClient();
268
- const key = this.bucketKey(workflowName, debounceKey);
269
- const raw = await client.eval(FINALIZE_LUA, 1, key, runId, String(now));
270
- return this.parseFinalizeResult(raw);
271
- }
272
- async cancel(workflowName, debounceKey) {
273
- const client = this.requireClient();
274
- const key = this.bucketKey(workflowName, debounceKey);
275
- const deleted = await client.del(key);
276
- return deleted > 0;
277
- }
278
- async purgeExpired(now) {
279
- const client = this.requireClient();
280
- const pattern = `${this.config.keyPrefix}:*`;
281
- let cursor = "0";
282
- let purged = 0;
283
- do {
284
- let res;
285
- try {
286
- res = await client.scan(cursor, "MATCH", pattern, "COUNT", 100);
287
- }
288
- catch {
289
- return purged;
290
- }
291
- const [nextCursor, keys] = res;
292
- cursor = nextCursor;
293
- for (const key of keys) {
294
- try {
295
- const raw = await client.eval(PURGE_EXPIRED_BUCKET_LUA, 1, key, String(now));
296
- const count = typeof raw === "number" ? raw : Number(raw);
297
- if (!Number.isNaN(count))
298
- purged += count;
299
- }
300
- catch {
301
- // best-effort
302
- }
303
- }
304
- } while (cursor !== "0");
305
- return purged;
306
- }
307
- parseRegisterResult(raw) {
308
- if (!Array.isArray(raw) || raw.length < 4) {
309
- throw new Error(`Unexpected Lua result shape for registerPing: ${JSON.stringify(raw)}`);
310
- }
311
- const outcome = String(raw[0]);
312
- if (outcome !== "owner-new" && outcome !== "owner-extend" && outcome !== "coalesce") {
313
- throw new Error(`Unexpected outcome in Lua result: ${outcome}`);
314
- }
315
- return {
316
- outcome,
317
- activeRunId: String(raw[1]),
318
- scheduledAt: Number(raw[2]),
319
- pingCount: Number(raw[3]),
320
- };
321
- }
322
- parseFinalizeResult(raw) {
323
- if (!Array.isArray(raw) || raw.length < 1)
324
- return { finalize: "abandoned" };
325
- const tag = String(raw[0]);
326
- if (tag === "fire")
327
- return { finalize: "fire" };
328
- if (tag === "reschedule")
329
- return { finalize: "reschedule", scheduledAt: Number(raw[1]) };
330
- return { finalize: "abandoned" };
331
- }
332
- }
333
- /**
334
- * Per-bucket purge. Deletes the bucket iff the owner-lease has expired
335
- * AND scheduledAt has elapsed (no active owner with a pending fire).
336
- *
337
- * KEYS[1] = bucket key
338
- * ARGV[1] = now
339
- *
340
- * Returns: 1 if deleted, 0 otherwise.
341
- */
342
- const PURGE_EXPIRED_BUCKET_LUA = `
343
- local raw = redis.call('GET', KEYS[1])
344
- if not raw or raw == '' then return 0 end
345
- local ok, doc = pcall(cjson.decode, raw)
346
- if not ok or type(doc) ~= 'table' then return 0 end
347
- local now = tonumber(ARGV[1])
348
- local lease = tonumber(doc.ownerLeaseExpiresAt) or 0
349
- local sched = tonumber(doc.scheduledAt) or 0
350
- if lease <= now and sched <= now then
351
- redis.call('DEL', KEYS[1])
352
- return 1
353
- end
354
- return 0
355
- `;
356
- //# sourceMappingURL=RedisDebounceBackend.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"RedisDebounceBackend.js","sourceRoot":"","sources":["../../src/scheduling/RedisDebounceBackend.ts"],"names":[],"mappings":"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"}
@@ -1,25 +0,0 @@
1
- /**
2
- * Tier C #1 · Debounce backend factory.
3
- *
4
- * Reads `BLOK_DEBOUNCE_BACKEND` and returns the matching backend
5
- * instance, or `null` when the operator wants the default in-process
6
- * coordinator (no cross-process coordination).
7
- *
8
- * Trigger packages call this in `listen()` and pass the result to
9
- * `DebounceCoordinator.getInstance().setBackend(backend)`.
10
- */
11
- import type { DebounceBackend } from "./DebounceBackend";
12
- /**
13
- * Returns a configured `DebounceBackend` based on
14
- * `BLOK_DEBOUNCE_BACKEND`, or `null` for the default in-memory coordinator.
15
- *
16
- * Recognized values:
17
- * - unset / `""` / `"memory"` / `"in-process"` — null (use default in-memory)
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
23
- * cross-process debounce but they didn't).
24
- */
25
- export declare function createDebounceBackend(): DebounceBackend | null;
@@ -1,39 +0,0 @@
1
- /**
2
- * Tier C #1 · Debounce backend factory.
3
- *
4
- * Reads `BLOK_DEBOUNCE_BACKEND` and returns the matching backend
5
- * instance, or `null` when the operator wants the default in-process
6
- * coordinator (no cross-process coordination).
7
- *
8
- * Trigger packages call this in `listen()` and pass the result to
9
- * `DebounceCoordinator.getInstance().setBackend(backend)`.
10
- */
11
- import { NatsKvDebounceBackend } from "./NatsKvDebounceBackend";
12
- import { RedisDebounceBackend } from "./RedisDebounceBackend";
13
- /**
14
- * Returns a configured `DebounceBackend` based on
15
- * `BLOK_DEBOUNCE_BACKEND`, or `null` for the default in-memory coordinator.
16
- *
17
- * Recognized values:
18
- * - unset / `""` / `"memory"` / `"in-process"` — null (use default in-memory)
19
- * - `"nats-kv"` — NATS KV backend (requires `nats` package + reachable NATS server)
20
- * - `"redis"` — Redis backend (requires `ioredis` package + reachable Redis server)
21
- *
22
- * Unknown values throw at startup with a clear error message — silently
23
- * falling back would be dangerous (operator thinks they configured
24
- * cross-process debounce but they didn't).
25
- */
26
- export function createDebounceBackend() {
27
- const kind = (process.env.BLOK_DEBOUNCE_BACKEND ?? "").trim().toLowerCase();
28
- if (!kind || kind === "memory" || kind === "in-process")
29
- return null;
30
- switch (kind) {
31
- case "nats-kv":
32
- return new NatsKvDebounceBackend();
33
- case "redis":
34
- return new RedisDebounceBackend();
35
- default:
36
- throw new Error(`Unknown BLOK_DEBOUNCE_BACKEND='${kind}'. Expected one of: 'memory' (default), 'nats-kv', 'redis'.`);
37
- }
38
- }
39
- //# sourceMappingURL=createDebounceBackend.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"createDebounceBackend.js","sourceRoot":"","sources":["../../src/scheduling/createDebounceBackend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,qBAAqB;IACpC,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC5E,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,qBAAqB,EAAE,CAAC;QACpC,KAAK,OAAO;YACX,OAAO,IAAI,oBAAoB,EAAE,CAAC;QACnC;YACC,MAAM,IAAI,KAAK,CACd,kCAAkC,IAAI,6DAA6D,CACnG,CAAC;IACJ,CAAC;AACF,CAAC"}
@@ -1,224 +0,0 @@
1
- /**
2
- * Attribute-Based Access Control (ABAC) for Blok
3
- *
4
- * Provides fine-grained, attribute-driven access control that complements RBAC:
5
- * - Policies evaluate attributes of subject, resource, action, and environment
6
- * - Supports logical operators (AND, OR, NOT) for complex conditions
7
- * - Supports comparison operators (equals, not_equals, in, not_in, contains, matches, gt, lt, gte, lte, between)
8
- * - Supports attribute-to-attribute comparison via `valueRef` (e.g., resource.owner == subject.sub)
9
- * - Integrates with AuthIdentity claims and RBAC roles
10
- * - JSON-serializable policies for persistence and external management
11
- *
12
- * @example
13
- * ```typescript
14
- * const engine = new ABACEngine();
15
- *
16
- * engine.addPolicy({
17
- * id: "work-hours-only",
18
- * description: "Allow workflow execution only during business hours",
19
- * effect: "allow",
20
- * target: {
21
- * resource: "workflow",
22
- * actions: ["execute"],
23
- * },
24
- * conditions: {
25
- * all: [
26
- * { attribute: "environment.hour", operator: "gte", value: 9 },
27
- * { attribute: "environment.hour", operator: "lt", value: 17 },
28
- * { attribute: "subject.department", operator: "equals", value: "engineering" },
29
- * ],
30
- * },
31
- * });
32
- *
33
- * const result = engine.evaluate({
34
- * subject: { sub: "user-1", roles: ["developer"], department: "engineering" },
35
- * resource: { type: "workflow", id: "/api/users" },
36
- * action: "execute",
37
- * environment: { hour: 14, ip: "10.0.0.1" },
38
- * });
39
- * ```
40
- */
41
- export type ABACOperator = "equals" | "not_equals" | "in" | "not_in" | "contains" | "not_contains" | "matches" | "gt" | "lt" | "gte" | "lte" | "between" | "exists" | "not_exists";
42
- export type ABACEffect = "allow" | "deny";
43
- /**
44
- * A single attribute condition that compares an attribute path against a value.
45
- *
46
- * Attribute paths use dot notation to access nested properties:
47
- * - `subject.department` — the subject's department attribute
48
- * - `resource.owner` — the resource's owner attribute
49
- * - `environment.ip` — the environment's IP address
50
- * - `environment.hour` — the current hour (0-23)
51
- */
52
- export interface ABACCondition {
53
- /** Dot-separated path to the attribute (e.g., "subject.department") */
54
- attribute: string;
55
- /** Comparison operator */
56
- operator: ABACOperator;
57
- /** Static value to compare against (ignored for exists/not_exists operators) */
58
- value?: unknown;
59
- /** Attribute path to resolve as the comparison value (attribute-to-attribute comparison).
60
- * When set, `value` is ignored and the comparison value is resolved from the request. */
61
- valueRef?: string;
62
- }
63
- /**
64
- * Logical grouping of conditions.
65
- *
66
- * - `all`: Every condition must be true (AND)
67
- * - `any`: At least one condition must be true (OR)
68
- * - `none`: No condition may be true (NOT / NOR)
69
- *
70
- * Groups can be nested for complex logic.
71
- */
72
- export interface ABACConditionGroup {
73
- /** All conditions must be true (AND) */
74
- all?: Array<ABACCondition | ABACConditionGroup>;
75
- /** At least one condition must be true (OR) */
76
- any?: Array<ABACCondition | ABACConditionGroup>;
77
- /** No condition may be true (NOR) */
78
- none?: Array<ABACCondition | ABACConditionGroup>;
79
- }
80
- /**
81
- * Policy target restricts which requests the policy applies to.
82
- */
83
- export interface ABACPolicyTarget {
84
- /** Resource type (e.g., "workflow", "node", "*") */
85
- resource?: string;
86
- /** Resource ID pattern (supports * wildcards) */
87
- resourcePattern?: string;
88
- /** Actions this policy applies to */
89
- actions?: string[];
90
- }
91
- /**
92
- * An ABAC policy defines conditions under which access is allowed or denied.
93
- */
94
- export interface ABACPolicy {
95
- /** Unique policy identifier */
96
- id: string;
97
- /** Human-readable description */
98
- description?: string;
99
- /** Whether this policy grants or denies access */
100
- effect: ABACEffect;
101
- /** Target resource/action scope — if omitted, applies to all requests */
102
- target?: ABACPolicyTarget;
103
- /** Conditions that must be satisfied for the policy to apply */
104
- conditions: ABACConditionGroup;
105
- /** Priority (higher = evaluated first). Default: 0 */
106
- priority?: number;
107
- /** Whether the policy is active. Default: true */
108
- enabled?: boolean;
109
- }
110
- /**
111
- * Attributes about the requesting subject (user or service).
112
- */
113
- export interface SubjectAttributes {
114
- /** Unique identifier */
115
- sub: string;
116
- /** Assigned roles */
117
- roles?: string[];
118
- /** Additional attributes (department, clearance, team, etc.) */
119
- [key: string]: unknown;
120
- }
121
- /**
122
- * Attributes about the target resource.
123
- */
124
- export interface ResourceAttributes {
125
- /** Resource type (workflow, node, trigger, etc.) */
126
- type: string;
127
- /** Resource identifier */
128
- id: string;
129
- /** Additional attributes (owner, classification, sensitivity, etc.) */
130
- [key: string]: unknown;
131
- }
132
- /**
133
- * Attributes about the environment / context.
134
- */
135
- export interface EnvironmentAttributes {
136
- /** Additional attributes (ip, hour, dayOfWeek, location, etc.) */
137
- [key: string]: unknown;
138
- }
139
- /**
140
- * A complete ABAC evaluation request context.
141
- */
142
- export interface ABACRequest {
143
- subject: SubjectAttributes;
144
- resource: ResourceAttributes;
145
- action: string;
146
- environment?: EnvironmentAttributes;
147
- }
148
- /**
149
- * Result of an ABAC evaluation.
150
- */
151
- export interface ABACResult {
152
- /** Whether access is allowed */
153
- allowed: boolean;
154
- /** The policy that determined the decision (if any) */
155
- matchedPolicy?: ABACPolicy;
156
- /** All policies that were evaluated */
157
- evaluatedPolicies: Array<{
158
- policyId: string;
159
- effect: ABACEffect;
160
- matched: boolean;
161
- }>;
162
- /** Reason for the decision */
163
- reason: string;
164
- }
165
- export declare class ABACEngine {
166
- private policies;
167
- private defaultEffect;
168
- constructor(options?: {
169
- defaultEffect?: ABACEffect;
170
- });
171
- /**
172
- * Add or update a policy.
173
- */
174
- addPolicy(policy: ABACPolicy): void;
175
- /**
176
- * Remove a policy by ID.
177
- */
178
- removePolicy(id: string): void;
179
- /**
180
- * Get a policy by ID.
181
- */
182
- getPolicy(id: string): ABACPolicy | undefined;
183
- /**
184
- * Get all policies, sorted by priority (highest first).
185
- */
186
- getPolicies(): ABACPolicy[];
187
- /**
188
- * Evaluate an access request against all policies.
189
- *
190
- * Policy evaluation order:
191
- * 1. Policies are sorted by priority (highest first)
192
- * 2. Only enabled policies are considered
193
- * 3. Only policies whose target matches the request are considered
194
- * 4. The first matching "deny" policy short-circuits with denial
195
- * 5. Otherwise, at least one matching "allow" policy is required
196
- * 6. If no policy matches, the default effect applies
197
- */
198
- evaluate(request: ABACRequest): ABACResult;
199
- /**
200
- * Export all policies as JSON.
201
- */
202
- toJSON(): {
203
- policies: ABACPolicy[];
204
- defaultEffect: ABACEffect;
205
- };
206
- /**
207
- * Load policies from JSON (replaces all existing policies).
208
- */
209
- fromJSON(config: {
210
- policies: ABACPolicy[];
211
- defaultEffect?: ABACEffect;
212
- }): void;
213
- private matchesTarget;
214
- private evaluateConditionGroup;
215
- private evaluateItem;
216
- private evaluateCondition;
217
- private resolveAttribute;
218
- private compare;
219
- private matchesPattern;
220
- }
221
- /**
222
- * Create a preconfigured ABAC engine with common policies.
223
- */
224
- export declare function createDefaultABAC(): ABACEngine;