@wooksjs/event-wf 0.7.14 → 0.7.15
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/index.cjs +37 -220
- package/dist/index.mjs +1 -214
- package/package.json +9 -9
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
2
2
|
let _wooksjs_event_core = require("@wooksjs/event-core");
|
|
3
3
|
let _wooksjs_event_http = require("@wooksjs/event-http");
|
|
4
4
|
let _wooksjs_http_body = require("@wooksjs/http-body");
|
|
5
|
-
let
|
|
5
|
+
let _prostojs_wf_outlets = require("@prostojs/wf/outlets");
|
|
6
6
|
let _prostojs_wf = require("@prostojs/wf");
|
|
7
7
|
let wooks = require("wooks");
|
|
8
8
|
|
|
@@ -320,219 +320,6 @@ function createOutletHandler(wfApp) {
|
|
|
320
320
|
});
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
-
//#endregion
|
|
324
|
-
//#region node_modules/.pnpm/@prostojs+wf@0.2.1/node_modules/@prostojs/wf/dist/outlets/index.mjs
|
|
325
|
-
/**
|
|
326
|
-
* Generic outlet request. Use for custom outlets.
|
|
327
|
-
*
|
|
328
|
-
* @example
|
|
329
|
-
* return outlet('pending-task', {
|
|
330
|
-
* payload: ApprovalForm,
|
|
331
|
-
* target: managerId,
|
|
332
|
-
* context: { orderId, amount },
|
|
333
|
-
* })
|
|
334
|
-
*/
|
|
335
|
-
function outlet(name, data) {
|
|
336
|
-
return { inputRequired: {
|
|
337
|
-
outlet: name,
|
|
338
|
-
...data
|
|
339
|
-
} };
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* Pause for HTTP form input. The outlet returns the payload (form definition)
|
|
343
|
-
* and state token in the HTTP response.
|
|
344
|
-
*
|
|
345
|
-
* @example
|
|
346
|
-
* return outletHttp(LoginForm)
|
|
347
|
-
* return outletHttp(LoginForm, { error: 'Invalid credentials' })
|
|
348
|
-
*/
|
|
349
|
-
function outletHttp(payload, context) {
|
|
350
|
-
return outlet("http", {
|
|
351
|
-
payload,
|
|
352
|
-
context
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* Pause and send email with a magic link containing the state token.
|
|
357
|
-
*
|
|
358
|
-
* @example
|
|
359
|
-
* return outletEmail('user@test.com', 'invite', { name: 'Alice' })
|
|
360
|
-
*/
|
|
361
|
-
function outletEmail(target, template, context) {
|
|
362
|
-
return outlet("email", {
|
|
363
|
-
target,
|
|
364
|
-
template,
|
|
365
|
-
context
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* Self-contained AES-256-GCM encrypted state strategy.
|
|
370
|
-
*
|
|
371
|
-
* Workflow state is encrypted into a base64url token that travels with the
|
|
372
|
-
* transport (cookie, URL param, hidden field). No server-side storage needed.
|
|
373
|
-
*
|
|
374
|
-
* Token format: `base64url(iv[12] + authTag[16] + ciphertext)`
|
|
375
|
-
*
|
|
376
|
-
* ## Security warning — replay
|
|
377
|
-
*
|
|
378
|
-
* This strategy is STATELESS. It cannot enforce single-use semantics:
|
|
379
|
-
* `consume()` is a no-op alias for `retrieve()` because there is no
|
|
380
|
-
* server-side record to delete. Anyone who obtains a copy of the token
|
|
381
|
-
* (browser history, server logs, shoulder-surfing, intermediate proxy,
|
|
382
|
-
* shared device) can replay it until the TTL expires.
|
|
383
|
-
*
|
|
384
|
-
* Use `EncapsulatedStateStrategy` ONLY when BOTH of the following hold:
|
|
385
|
-
*
|
|
386
|
-
* 1. Every workflow step is idempotent — re-executing a step with the same
|
|
387
|
-
* input produces no harmful side effects (pure data collection,
|
|
388
|
-
* validation-only steps).
|
|
389
|
-
* 2. The flow is not security-sensitive — no credential changes, financial
|
|
390
|
-
* operations, account provisioning, permission grants, or any other
|
|
391
|
-
* privileged action.
|
|
392
|
-
*
|
|
393
|
-
* For auth flows (login, password reset, invite accept), financial
|
|
394
|
-
* operations, or anything with meaningful side effects, use
|
|
395
|
-
* `HandleStateStrategy` with a durable `WfStateStore`. `HandleStateStrategy`
|
|
396
|
-
* supports true single-use tokens via atomic `getAndDelete` at the store
|
|
397
|
-
* layer.
|
|
398
|
-
*
|
|
399
|
-
* @example
|
|
400
|
-
* const strategy = new EncapsulatedStateStrategy({
|
|
401
|
-
* secret: crypto.randomBytes(32),
|
|
402
|
-
* defaultTtl: 3600_000, // 1 hour
|
|
403
|
-
* });
|
|
404
|
-
* const token = await strategy.persist(state);
|
|
405
|
-
* const recovered = await strategy.retrieve(token);
|
|
406
|
-
*/
|
|
407
|
-
var EncapsulatedStateStrategy = class {
|
|
408
|
-
/** @throws if secret is not exactly 32 bytes */
|
|
409
|
-
constructor(config) {
|
|
410
|
-
this.config = config;
|
|
411
|
-
this.key = typeof config.secret === "string" ? Buffer.from(config.secret, "hex") : config.secret;
|
|
412
|
-
if (this.key.length !== 32) throw new Error("EncapsulatedStateStrategy: secret must be exactly 32 bytes");
|
|
413
|
-
}
|
|
414
|
-
/**
|
|
415
|
-
* Encrypt workflow state into a self-contained token.
|
|
416
|
-
*
|
|
417
|
-
* NOTE: the `overrides.handle` hint from `WfStateStrategy` is silently
|
|
418
|
-
* ignored — the encapsulated token IS the ciphertext of the state, so a
|
|
419
|
-
* fixed handle cannot map to changing state. Callers that need handle
|
|
420
|
-
* stability across calls must use `HandleStateStrategy`.
|
|
421
|
-
*
|
|
422
|
-
* @param state — workflow state to persist
|
|
423
|
-
* @param options.ttl — time-to-live in ms (overrides defaultTtl)
|
|
424
|
-
* @returns base64url-encoded encrypted token
|
|
425
|
-
*/
|
|
426
|
-
async persist(state, options, _overrides) {
|
|
427
|
-
const ttl = options?.ttl ?? this.config.defaultTtl ?? 0;
|
|
428
|
-
const exp = ttl > 0 ? Date.now() + ttl : 0;
|
|
429
|
-
const payload = JSON.stringify({
|
|
430
|
-
s: state,
|
|
431
|
-
e: exp
|
|
432
|
-
});
|
|
433
|
-
const iv = (0, node_crypto.randomBytes)(12);
|
|
434
|
-
const cipher = (0, node_crypto.createCipheriv)("aes-256-gcm", this.key, iv);
|
|
435
|
-
const encrypted = Buffer.concat([cipher.update(payload, "utf8"), cipher.final()]);
|
|
436
|
-
const tag = cipher.getAuthTag();
|
|
437
|
-
return Buffer.concat([
|
|
438
|
-
iv,
|
|
439
|
-
tag,
|
|
440
|
-
encrypted
|
|
441
|
-
]).toString("base64url");
|
|
442
|
-
}
|
|
443
|
-
/** Decrypt and return workflow state. Returns null if token is invalid, expired, or tampered. */
|
|
444
|
-
async retrieve(token) {
|
|
445
|
-
return this.decrypt(token);
|
|
446
|
-
}
|
|
447
|
-
/**
|
|
448
|
-
* Stateless — CANNOT invalidate the token. Returns identical result to
|
|
449
|
-
* `retrieve()`. See the class-level security warning.
|
|
450
|
-
*
|
|
451
|
-
* This method exists only to satisfy the `WfStateStrategy` contract.
|
|
452
|
-
* Callers that need true single-use semantics must use
|
|
453
|
-
* `HandleStateStrategy`.
|
|
454
|
-
*/
|
|
455
|
-
async consume(token) {
|
|
456
|
-
return this.decrypt(token);
|
|
457
|
-
}
|
|
458
|
-
decrypt(token) {
|
|
459
|
-
try {
|
|
460
|
-
const buf = Buffer.from(token, "base64url");
|
|
461
|
-
if (buf.length < 28) return null;
|
|
462
|
-
const iv = buf.subarray(0, 12);
|
|
463
|
-
const tag = buf.subarray(12, 28);
|
|
464
|
-
const ciphertext = buf.subarray(28);
|
|
465
|
-
const decipher = (0, node_crypto.createDecipheriv)("aes-256-gcm", this.key, iv);
|
|
466
|
-
decipher.setAuthTag(tag);
|
|
467
|
-
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
468
|
-
const { s: state, e: exp } = JSON.parse(decrypted.toString("utf8"));
|
|
469
|
-
if (exp > 0 && Date.now() > exp) return null;
|
|
470
|
-
return state;
|
|
471
|
-
} catch {
|
|
472
|
-
return null;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
};
|
|
476
|
-
var HandleStateStrategy = class {
|
|
477
|
-
constructor(config) {
|
|
478
|
-
this.config = config;
|
|
479
|
-
}
|
|
480
|
-
async persist(state, options, overrides) {
|
|
481
|
-
const handle = overrides?.handle ?? (this.config.generateHandle ?? node_crypto.randomUUID)();
|
|
482
|
-
const ttl = options?.ttl ?? this.config.defaultTtl ?? 0;
|
|
483
|
-
const expiresAt = ttl > 0 ? Date.now() + ttl : void 0;
|
|
484
|
-
await this.config.store.set(handle, state, expiresAt);
|
|
485
|
-
return handle;
|
|
486
|
-
}
|
|
487
|
-
async retrieve(token) {
|
|
488
|
-
return (await this.config.store.get(token))?.state ?? null;
|
|
489
|
-
}
|
|
490
|
-
async consume(token) {
|
|
491
|
-
return (await this.config.store.getAndDelete(token))?.state ?? null;
|
|
492
|
-
}
|
|
493
|
-
};
|
|
494
|
-
/**
|
|
495
|
-
* In-memory state store for development and testing.
|
|
496
|
-
* State is lost on process restart.
|
|
497
|
-
*/
|
|
498
|
-
var WfStateStoreMemory = class {
|
|
499
|
-
constructor() {
|
|
500
|
-
this.store = /* @__PURE__ */ new Map();
|
|
501
|
-
}
|
|
502
|
-
async set(handle, state, expiresAt) {
|
|
503
|
-
this.store.set(handle, {
|
|
504
|
-
state,
|
|
505
|
-
expiresAt
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
async get(handle) {
|
|
509
|
-
const entry = this.store.get(handle);
|
|
510
|
-
if (!entry) return null;
|
|
511
|
-
if (entry.expiresAt && Date.now() > entry.expiresAt) {
|
|
512
|
-
this.store.delete(handle);
|
|
513
|
-
return null;
|
|
514
|
-
}
|
|
515
|
-
return entry;
|
|
516
|
-
}
|
|
517
|
-
async delete(handle) {
|
|
518
|
-
this.store.delete(handle);
|
|
519
|
-
}
|
|
520
|
-
async getAndDelete(handle) {
|
|
521
|
-
const entry = await this.get(handle);
|
|
522
|
-
if (entry) this.store.delete(handle);
|
|
523
|
-
return entry;
|
|
524
|
-
}
|
|
525
|
-
async cleanup() {
|
|
526
|
-
const now = Date.now();
|
|
527
|
-
let count = 0;
|
|
528
|
-
for (const [handle, entry] of this.store) if (entry.expiresAt && now > entry.expiresAt) {
|
|
529
|
-
this.store.delete(handle);
|
|
530
|
-
count++;
|
|
531
|
-
}
|
|
532
|
-
return count;
|
|
533
|
-
}
|
|
534
|
-
};
|
|
535
|
-
|
|
536
323
|
//#endregion
|
|
537
324
|
//#region packages/event-wf/src/workflow.ts
|
|
538
325
|
/** Workflow engine that resolves steps via Wooks router lookup. */
|
|
@@ -717,15 +504,30 @@ function createWfApp(opts, wooks$2) {
|
|
|
717
504
|
}
|
|
718
505
|
|
|
719
506
|
//#endregion
|
|
720
|
-
exports
|
|
721
|
-
|
|
507
|
+
Object.defineProperty(exports, 'EncapsulatedStateStrategy', {
|
|
508
|
+
enumerable: true,
|
|
509
|
+
get: function () {
|
|
510
|
+
return _prostojs_wf_outlets.EncapsulatedStateStrategy;
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
Object.defineProperty(exports, 'HandleStateStrategy', {
|
|
514
|
+
enumerable: true,
|
|
515
|
+
get: function () {
|
|
516
|
+
return _prostojs_wf_outlets.HandleStateStrategy;
|
|
517
|
+
}
|
|
518
|
+
});
|
|
722
519
|
Object.defineProperty(exports, 'StepRetriableError', {
|
|
723
520
|
enumerable: true,
|
|
724
521
|
get: function () {
|
|
725
522
|
return _prostojs_wf.StepRetriableError;
|
|
726
523
|
}
|
|
727
524
|
});
|
|
728
|
-
exports
|
|
525
|
+
Object.defineProperty(exports, 'WfStateStoreMemory', {
|
|
526
|
+
enumerable: true,
|
|
527
|
+
get: function () {
|
|
528
|
+
return _prostojs_wf_outlets.WfStateStoreMemory;
|
|
529
|
+
}
|
|
530
|
+
});
|
|
729
531
|
exports.WooksWf = WooksWf;
|
|
730
532
|
exports.createEmailOutlet = createEmailOutlet;
|
|
731
533
|
exports.createHttpOutlet = createHttpOutlet;
|
|
@@ -733,9 +535,24 @@ exports.createOutletHandler = createOutletHandler;
|
|
|
733
535
|
exports.createWfApp = createWfApp;
|
|
734
536
|
exports.createWfContext = createWfContext;
|
|
735
537
|
exports.handleWfOutletRequest = handleWfOutletRequest;
|
|
736
|
-
exports
|
|
737
|
-
|
|
738
|
-
|
|
538
|
+
Object.defineProperty(exports, 'outlet', {
|
|
539
|
+
enumerable: true,
|
|
540
|
+
get: function () {
|
|
541
|
+
return _prostojs_wf_outlets.outlet;
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
Object.defineProperty(exports, 'outletEmail', {
|
|
545
|
+
enumerable: true,
|
|
546
|
+
get: function () {
|
|
547
|
+
return _prostojs_wf_outlets.outletEmail;
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
Object.defineProperty(exports, 'outletHttp', {
|
|
551
|
+
enumerable: true,
|
|
552
|
+
get: function () {
|
|
553
|
+
return _prostojs_wf_outlets.outletHttp;
|
|
554
|
+
}
|
|
555
|
+
});
|
|
739
556
|
exports.resumeKey = resumeKey;
|
|
740
557
|
exports.resumeWfContext = resumeWfContext;
|
|
741
558
|
Object.defineProperty(exports, 'useLogger', {
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createEventContext, current, defineEventKind, defineWook, key, slot, useLogger, useRouteParams } from "@wooksjs/event-core";
|
|
2
2
|
import { useCookies, useResponse, useUrlParams } from "@wooksjs/event-http";
|
|
3
3
|
import { useBody } from "@wooksjs/http-body";
|
|
4
|
-
import {
|
|
4
|
+
import { EncapsulatedStateStrategy, HandleStateStrategy, WfStateStoreMemory, outlet, outletEmail, outletHttp } from "@prostojs/wf/outlets";
|
|
5
5
|
import { StepRetriableError, Workflow, createStep } from "@prostojs/wf";
|
|
6
6
|
import { WooksAdapterBase } from "wooks";
|
|
7
7
|
|
|
@@ -319,219 +319,6 @@ function createOutletHandler(wfApp) {
|
|
|
319
319
|
});
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
-
//#endregion
|
|
323
|
-
//#region node_modules/.pnpm/@prostojs+wf@0.2.1/node_modules/@prostojs/wf/dist/outlets/index.mjs
|
|
324
|
-
/**
|
|
325
|
-
* Generic outlet request. Use for custom outlets.
|
|
326
|
-
*
|
|
327
|
-
* @example
|
|
328
|
-
* return outlet('pending-task', {
|
|
329
|
-
* payload: ApprovalForm,
|
|
330
|
-
* target: managerId,
|
|
331
|
-
* context: { orderId, amount },
|
|
332
|
-
* })
|
|
333
|
-
*/
|
|
334
|
-
function outlet(name, data) {
|
|
335
|
-
return { inputRequired: {
|
|
336
|
-
outlet: name,
|
|
337
|
-
...data
|
|
338
|
-
} };
|
|
339
|
-
}
|
|
340
|
-
/**
|
|
341
|
-
* Pause for HTTP form input. The outlet returns the payload (form definition)
|
|
342
|
-
* and state token in the HTTP response.
|
|
343
|
-
*
|
|
344
|
-
* @example
|
|
345
|
-
* return outletHttp(LoginForm)
|
|
346
|
-
* return outletHttp(LoginForm, { error: 'Invalid credentials' })
|
|
347
|
-
*/
|
|
348
|
-
function outletHttp(payload, context) {
|
|
349
|
-
return outlet("http", {
|
|
350
|
-
payload,
|
|
351
|
-
context
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Pause and send email with a magic link containing the state token.
|
|
356
|
-
*
|
|
357
|
-
* @example
|
|
358
|
-
* return outletEmail('user@test.com', 'invite', { name: 'Alice' })
|
|
359
|
-
*/
|
|
360
|
-
function outletEmail(target, template, context) {
|
|
361
|
-
return outlet("email", {
|
|
362
|
-
target,
|
|
363
|
-
template,
|
|
364
|
-
context
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* Self-contained AES-256-GCM encrypted state strategy.
|
|
369
|
-
*
|
|
370
|
-
* Workflow state is encrypted into a base64url token that travels with the
|
|
371
|
-
* transport (cookie, URL param, hidden field). No server-side storage needed.
|
|
372
|
-
*
|
|
373
|
-
* Token format: `base64url(iv[12] + authTag[16] + ciphertext)`
|
|
374
|
-
*
|
|
375
|
-
* ## Security warning — replay
|
|
376
|
-
*
|
|
377
|
-
* This strategy is STATELESS. It cannot enforce single-use semantics:
|
|
378
|
-
* `consume()` is a no-op alias for `retrieve()` because there is no
|
|
379
|
-
* server-side record to delete. Anyone who obtains a copy of the token
|
|
380
|
-
* (browser history, server logs, shoulder-surfing, intermediate proxy,
|
|
381
|
-
* shared device) can replay it until the TTL expires.
|
|
382
|
-
*
|
|
383
|
-
* Use `EncapsulatedStateStrategy` ONLY when BOTH of the following hold:
|
|
384
|
-
*
|
|
385
|
-
* 1. Every workflow step is idempotent — re-executing a step with the same
|
|
386
|
-
* input produces no harmful side effects (pure data collection,
|
|
387
|
-
* validation-only steps).
|
|
388
|
-
* 2. The flow is not security-sensitive — no credential changes, financial
|
|
389
|
-
* operations, account provisioning, permission grants, or any other
|
|
390
|
-
* privileged action.
|
|
391
|
-
*
|
|
392
|
-
* For auth flows (login, password reset, invite accept), financial
|
|
393
|
-
* operations, or anything with meaningful side effects, use
|
|
394
|
-
* `HandleStateStrategy` with a durable `WfStateStore`. `HandleStateStrategy`
|
|
395
|
-
* supports true single-use tokens via atomic `getAndDelete` at the store
|
|
396
|
-
* layer.
|
|
397
|
-
*
|
|
398
|
-
* @example
|
|
399
|
-
* const strategy = new EncapsulatedStateStrategy({
|
|
400
|
-
* secret: crypto.randomBytes(32),
|
|
401
|
-
* defaultTtl: 3600_000, // 1 hour
|
|
402
|
-
* });
|
|
403
|
-
* const token = await strategy.persist(state);
|
|
404
|
-
* const recovered = await strategy.retrieve(token);
|
|
405
|
-
*/
|
|
406
|
-
var EncapsulatedStateStrategy = class {
|
|
407
|
-
/** @throws if secret is not exactly 32 bytes */
|
|
408
|
-
constructor(config) {
|
|
409
|
-
this.config = config;
|
|
410
|
-
this.key = typeof config.secret === "string" ? Buffer.from(config.secret, "hex") : config.secret;
|
|
411
|
-
if (this.key.length !== 32) throw new Error("EncapsulatedStateStrategy: secret must be exactly 32 bytes");
|
|
412
|
-
}
|
|
413
|
-
/**
|
|
414
|
-
* Encrypt workflow state into a self-contained token.
|
|
415
|
-
*
|
|
416
|
-
* NOTE: the `overrides.handle` hint from `WfStateStrategy` is silently
|
|
417
|
-
* ignored — the encapsulated token IS the ciphertext of the state, so a
|
|
418
|
-
* fixed handle cannot map to changing state. Callers that need handle
|
|
419
|
-
* stability across calls must use `HandleStateStrategy`.
|
|
420
|
-
*
|
|
421
|
-
* @param state — workflow state to persist
|
|
422
|
-
* @param options.ttl — time-to-live in ms (overrides defaultTtl)
|
|
423
|
-
* @returns base64url-encoded encrypted token
|
|
424
|
-
*/
|
|
425
|
-
async persist(state, options, _overrides) {
|
|
426
|
-
const ttl = options?.ttl ?? this.config.defaultTtl ?? 0;
|
|
427
|
-
const exp = ttl > 0 ? Date.now() + ttl : 0;
|
|
428
|
-
const payload = JSON.stringify({
|
|
429
|
-
s: state,
|
|
430
|
-
e: exp
|
|
431
|
-
});
|
|
432
|
-
const iv = randomBytes(12);
|
|
433
|
-
const cipher = createCipheriv("aes-256-gcm", this.key, iv);
|
|
434
|
-
const encrypted = Buffer.concat([cipher.update(payload, "utf8"), cipher.final()]);
|
|
435
|
-
const tag = cipher.getAuthTag();
|
|
436
|
-
return Buffer.concat([
|
|
437
|
-
iv,
|
|
438
|
-
tag,
|
|
439
|
-
encrypted
|
|
440
|
-
]).toString("base64url");
|
|
441
|
-
}
|
|
442
|
-
/** Decrypt and return workflow state. Returns null if token is invalid, expired, or tampered. */
|
|
443
|
-
async retrieve(token) {
|
|
444
|
-
return this.decrypt(token);
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* Stateless — CANNOT invalidate the token. Returns identical result to
|
|
448
|
-
* `retrieve()`. See the class-level security warning.
|
|
449
|
-
*
|
|
450
|
-
* This method exists only to satisfy the `WfStateStrategy` contract.
|
|
451
|
-
* Callers that need true single-use semantics must use
|
|
452
|
-
* `HandleStateStrategy`.
|
|
453
|
-
*/
|
|
454
|
-
async consume(token) {
|
|
455
|
-
return this.decrypt(token);
|
|
456
|
-
}
|
|
457
|
-
decrypt(token) {
|
|
458
|
-
try {
|
|
459
|
-
const buf = Buffer.from(token, "base64url");
|
|
460
|
-
if (buf.length < 28) return null;
|
|
461
|
-
const iv = buf.subarray(0, 12);
|
|
462
|
-
const tag = buf.subarray(12, 28);
|
|
463
|
-
const ciphertext = buf.subarray(28);
|
|
464
|
-
const decipher = createDecipheriv("aes-256-gcm", this.key, iv);
|
|
465
|
-
decipher.setAuthTag(tag);
|
|
466
|
-
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
467
|
-
const { s: state, e: exp } = JSON.parse(decrypted.toString("utf8"));
|
|
468
|
-
if (exp > 0 && Date.now() > exp) return null;
|
|
469
|
-
return state;
|
|
470
|
-
} catch {
|
|
471
|
-
return null;
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
};
|
|
475
|
-
var HandleStateStrategy = class {
|
|
476
|
-
constructor(config) {
|
|
477
|
-
this.config = config;
|
|
478
|
-
}
|
|
479
|
-
async persist(state, options, overrides) {
|
|
480
|
-
const handle = overrides?.handle ?? (this.config.generateHandle ?? randomUUID)();
|
|
481
|
-
const ttl = options?.ttl ?? this.config.defaultTtl ?? 0;
|
|
482
|
-
const expiresAt = ttl > 0 ? Date.now() + ttl : void 0;
|
|
483
|
-
await this.config.store.set(handle, state, expiresAt);
|
|
484
|
-
return handle;
|
|
485
|
-
}
|
|
486
|
-
async retrieve(token) {
|
|
487
|
-
return (await this.config.store.get(token))?.state ?? null;
|
|
488
|
-
}
|
|
489
|
-
async consume(token) {
|
|
490
|
-
return (await this.config.store.getAndDelete(token))?.state ?? null;
|
|
491
|
-
}
|
|
492
|
-
};
|
|
493
|
-
/**
|
|
494
|
-
* In-memory state store for development and testing.
|
|
495
|
-
* State is lost on process restart.
|
|
496
|
-
*/
|
|
497
|
-
var WfStateStoreMemory = class {
|
|
498
|
-
constructor() {
|
|
499
|
-
this.store = /* @__PURE__ */ new Map();
|
|
500
|
-
}
|
|
501
|
-
async set(handle, state, expiresAt) {
|
|
502
|
-
this.store.set(handle, {
|
|
503
|
-
state,
|
|
504
|
-
expiresAt
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
async get(handle) {
|
|
508
|
-
const entry = this.store.get(handle);
|
|
509
|
-
if (!entry) return null;
|
|
510
|
-
if (entry.expiresAt && Date.now() > entry.expiresAt) {
|
|
511
|
-
this.store.delete(handle);
|
|
512
|
-
return null;
|
|
513
|
-
}
|
|
514
|
-
return entry;
|
|
515
|
-
}
|
|
516
|
-
async delete(handle) {
|
|
517
|
-
this.store.delete(handle);
|
|
518
|
-
}
|
|
519
|
-
async getAndDelete(handle) {
|
|
520
|
-
const entry = await this.get(handle);
|
|
521
|
-
if (entry) this.store.delete(handle);
|
|
522
|
-
return entry;
|
|
523
|
-
}
|
|
524
|
-
async cleanup() {
|
|
525
|
-
const now = Date.now();
|
|
526
|
-
let count = 0;
|
|
527
|
-
for (const [handle, entry] of this.store) if (entry.expiresAt && now > entry.expiresAt) {
|
|
528
|
-
this.store.delete(handle);
|
|
529
|
-
count++;
|
|
530
|
-
}
|
|
531
|
-
return count;
|
|
532
|
-
}
|
|
533
|
-
};
|
|
534
|
-
|
|
535
322
|
//#endregion
|
|
536
323
|
//#region packages/event-wf/src/workflow.ts
|
|
537
324
|
/** Workflow engine that resolves steps via Wooks router lookup. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wooksjs/event-wf",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.15",
|
|
4
4
|
"description": "@wooksjs/event-wf",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"app",
|
|
@@ -42,17 +42,17 @@
|
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"typescript": "^5.9.3",
|
|
44
44
|
"vitest": "^3.2.4",
|
|
45
|
-
"@wooksjs/event-
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"@wooksjs/
|
|
45
|
+
"@wooksjs/event-core": "^0.7.15",
|
|
46
|
+
"@wooksjs/event-http": "^0.7.15",
|
|
47
|
+
"wooks": "^0.7.15",
|
|
48
|
+
"@wooksjs/http-body": "^0.7.15"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"@prostojs/logger": "^0.4.3",
|
|
52
|
-
"@wooksjs/event-core": "^0.7.
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"@wooksjs/http-body": "^0.7.
|
|
52
|
+
"@wooksjs/event-core": "^0.7.15",
|
|
53
|
+
"wooks": "^0.7.15",
|
|
54
|
+
"@wooksjs/event-http": "^0.7.15",
|
|
55
|
+
"@wooksjs/http-body": "^0.7.15"
|
|
56
56
|
},
|
|
57
57
|
"peerDependenciesMeta": {
|
|
58
58
|
"@wooksjs/event-http": {
|