@adonis-agora/durable 0.19.2 → 0.21.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 (56) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/dist/src/codec-state-store.d.ts +2 -1
  3. package/dist/src/codec-state-store.d.ts.map +1 -1
  4. package/dist/src/codec-state-store.js +8 -0
  5. package/dist/src/codec-state-store.js.map +1 -1
  6. package/dist/src/control-plane-redis/index.d.ts +1 -1
  7. package/dist/src/control-plane-redis/index.d.ts.map +1 -1
  8. package/dist/src/control-plane-redis/index.js.map +1 -1
  9. package/dist/src/control-plane-redis/redis-control-plane.d.ts +70 -29
  10. package/dist/src/control-plane-redis/redis-control-plane.d.ts.map +1 -1
  11. package/dist/src/control-plane-redis/redis-control-plane.js +16 -6
  12. package/dist/src/control-plane-redis/redis-control-plane.js.map +1 -1
  13. package/dist/src/dashboard/index.d.ts +1 -1
  14. package/dist/src/dashboard/index.js +1 -1
  15. package/dist/src/engine.d.ts +10 -1
  16. package/dist/src/engine.d.ts.map +1 -1
  17. package/dist/src/engine.js +86 -18
  18. package/dist/src/engine.js.map +1 -1
  19. package/dist/src/index.d.ts +3 -2
  20. package/dist/src/index.d.ts.map +1 -1
  21. package/dist/src/index.js +5 -1
  22. package/dist/src/index.js.map +1 -1
  23. package/dist/src/interfaces.d.ts +16 -0
  24. package/dist/src/interfaces.d.ts.map +1 -1
  25. package/dist/src/step-discovery.d.ts +3 -2
  26. package/dist/src/step-discovery.d.ts.map +1 -1
  27. package/dist/src/step-discovery.js +8 -5
  28. package/dist/src/step-discovery.js.map +1 -1
  29. package/dist/src/stores/lucid-schema.d.ts +18 -1
  30. package/dist/src/stores/lucid-schema.d.ts.map +1 -1
  31. package/dist/src/stores/lucid-schema.js +44 -1
  32. package/dist/src/stores/lucid-schema.js.map +1 -1
  33. package/dist/src/stores/lucid.d.ts +2 -1
  34. package/dist/src/stores/lucid.d.ts.map +1 -1
  35. package/dist/src/stores/lucid.js +31 -0
  36. package/dist/src/stores/lucid.js.map +1 -1
  37. package/dist/src/testing/in-memory-state-store.d.ts +2 -1
  38. package/dist/src/testing/in-memory-state-store.d.ts.map +1 -1
  39. package/dist/src/testing/in-memory-state-store.js +12 -0
  40. package/dist/src/testing/in-memory-state-store.js.map +1 -1
  41. package/dist/src/testing-kit/state-store-conformance.d.ts.map +1 -1
  42. package/dist/src/testing-kit/state-store-conformance.js +47 -0
  43. package/dist/src/testing-kit/state-store-conformance.js.map +1 -1
  44. package/dist/src/transports/bullmq/bullmq-transport.d.ts.map +1 -1
  45. package/dist/src/transports/bullmq/bullmq-transport.js +1 -1
  46. package/dist/src/transports/bullmq/bullmq-transport.js.map +1 -1
  47. package/dist/src/workflow-discovery.d.ts +33 -3
  48. package/dist/src/workflow-discovery.d.ts.map +1 -1
  49. package/dist/src/workflow-discovery.js +40 -8
  50. package/dist/src/workflow-discovery.js.map +1 -1
  51. package/dist/src/workflow-ref.d.ts +25 -1
  52. package/dist/src/workflow-ref.d.ts.map +1 -1
  53. package/dist/src/workflow-ref.js +31 -0
  54. package/dist/src/workflow-ref.js.map +1 -1
  55. package/dist/stubs/database/migrations/create_durable_tables.stub +33 -89
  56. package/package.json +7 -4
@@ -6,7 +6,7 @@
6
6
  * string stays available for the cross-runtime case.
7
7
  */
8
8
  import type { SingletonConfig } from './engine.js';
9
- import type { ScheduledWorkflow } from './scheduler.js';
9
+ import type { ScheduledWorkflow, WorkflowScheduleConfig } from './scheduler.js';
10
10
  /** Options for a workflow's `static workflow = { name, version, … }` config (see {@link BaseWorkflow}). */
11
11
  export interface WorkflowOptions {
12
12
  /** The registered workflow name (the cross-runtime identity, e.g. `order`). */
@@ -54,6 +54,30 @@ export declare function workflowMeta(target: unknown): WorkflowMeta | undefined;
54
54
  * (not a registrable workflow) or no `static schedule`.
55
55
  */
56
56
  export declare function workflowSchedules(target: unknown): ScheduledWorkflow[];
57
+ /**
58
+ * Class-decorator form of the colocated schedule — the same declaration `static schedule = {...}`
59
+ * makes, for codebases that prefer decorators:
60
+ *
61
+ * ```ts
62
+ * @Scheduled({ cron: '0 4 * * *', timezone: 'America/Sao_Paulo' })
63
+ * export default class CrawlWorkflow extends BaseWorkflow {
64
+ * static workflow = { name: 'crawl' }
65
+ * async run(ctx: WorkflowCtx) { … }
66
+ * }
67
+ * ```
68
+ *
69
+ * It only stamps `static schedule` on the class — normalization (key defaults, `workflow` fill-in)
70
+ * stays in {@link workflowSchedules}, so both authoring forms behave identically, including the
71
+ * requirement of a `static workflow` config (a decorated class without one is not registrable and
72
+ * its schedules are ignored, same as a bare `static schedule`).
73
+ *
74
+ * Composes: repeated `@Scheduled(...)` applications and an existing `static schedule` accumulate.
75
+ * Decorators apply bottom-up, so each application PREPENDS — the final array reads in **source
76
+ * order** (top decorator first, then lower ones, then the `static schedule` literal). With several
77
+ * schedules on one class, prefer explicit `key`s over the positional `${name}:${i}` defaults: the
78
+ * key is part of the deterministic run id, and reordering declarations would silently re-key them.
79
+ */
80
+ export declare function Scheduled(config: WorkflowScheduleConfig | WorkflowScheduleConfig[]): ClassDecorator;
57
81
  /** Structural shape of a workflow class — its `run(ctx, input)` carries the input/output types. */
58
82
  export type WorkflowClass<TInput = unknown, TOutput = unknown> = abstract new (...args: never[]) => {
59
83
  run(ctx: never, input: TInput): Promise<TOutput> | TOutput;
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-ref.d.ts","sourceRoot":"","sources":["../../src/workflow-ref.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,iBAAiB,EAA0B,MAAM,gBAAgB,CAAC;AAEhF,2GAA2G;AAC3G,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,kFAAkF;IAClF,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,oFAAoF;IACpF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;CAC7B;AAED,sGAAsG;AACtG,MAAM,WAAW,YAAa,SAAQ,eAAe;IACnD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,YAAY,GAAG,SAAS,CAOtE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,iBAAiB,EAAE,CAWtE;AAED,mGAAmG;AACnG,MAAM,MAAM,aAAa,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,QAAQ,MACvE,GAAG,IAAI,EAAE,KAAK,EAAE,KACb;IACH,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAC5D,CAAC;AAEF,yGAAyG;AACzG,MAAM,MAAM,WAAW,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,IACvD,MAAM,GACN,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEnC,uDAAuD;AACvD,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS,QAAQ,MACjD,GAAG,IAAI,EAAE,KAAK,EAAE,KACb;IACH,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;CAC1C,GACG,CAAC,GACD,OAAO,CAAC;AAEZ,gFAAgF;AAChF,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAAC,SAAS,QAAQ,MAClD,GAAG,IAAI,EAAE,KAAK,EAAE,KACb;IACH,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;CACxC,GACG,OAAO,CAAC,CAAC,CAAC,GACV,OAAO,CAAC;AAEZ;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CASrD"}
1
+ {"version":3,"file":"workflow-ref.d.ts","sourceRoot":"","sources":["../../src/workflow-ref.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAEhF,2GAA2G;AAC3G,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,kFAAkF;IAClF,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,oFAAoF;IACpF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;CAC7B;AAED,sGAAsG;AACtG,MAAM,WAAW,YAAa,SAAQ,eAAe;IACnD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,YAAY,GAAG,SAAS,CAOtE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,iBAAiB,EAAE,CAWtE;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,sBAAsB,GAAG,sBAAsB,EAAE,GACxD,cAAc,CAUhB;AAED,mGAAmG;AACnG,MAAM,MAAM,aAAa,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,QAAQ,MACvE,GAAG,IAAI,EAAE,KAAK,EAAE,KACb;IACH,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAC5D,CAAC;AAEF,yGAAyG;AACzG,MAAM,MAAM,WAAW,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,IACvD,MAAM,GACN,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEnC,uDAAuD;AACvD,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS,QAAQ,MACjD,GAAG,IAAI,EAAE,KAAK,EAAE,KACb;IACH,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;CAC1C,GACG,CAAC,GACD,OAAO,CAAC;AAEZ,gFAAgF;AAChF,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAAC,SAAS,QAAQ,MAClD,GAAG,IAAI,EAAE,KAAK,EAAE,KACb;IACH,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;CACxC,GACG,OAAO,CAAC,CAAC,CAAC,GACV,OAAO,CAAC;AAEZ;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CASrD"}
@@ -46,6 +46,37 @@ export function workflowSchedules(target) {
46
46
  key: s.key ?? (arr.length > 1 ? `${meta.name}:${i}` : meta.name),
47
47
  }));
48
48
  }
49
+ /**
50
+ * Class-decorator form of the colocated schedule — the same declaration `static schedule = {...}`
51
+ * makes, for codebases that prefer decorators:
52
+ *
53
+ * ```ts
54
+ * @Scheduled({ cron: '0 4 * * *', timezone: 'America/Sao_Paulo' })
55
+ * export default class CrawlWorkflow extends BaseWorkflow {
56
+ * static workflow = { name: 'crawl' }
57
+ * async run(ctx: WorkflowCtx) { … }
58
+ * }
59
+ * ```
60
+ *
61
+ * It only stamps `static schedule` on the class — normalization (key defaults, `workflow` fill-in)
62
+ * stays in {@link workflowSchedules}, so both authoring forms behave identically, including the
63
+ * requirement of a `static workflow` config (a decorated class without one is not registrable and
64
+ * its schedules are ignored, same as a bare `static schedule`).
65
+ *
66
+ * Composes: repeated `@Scheduled(...)` applications and an existing `static schedule` accumulate.
67
+ * Decorators apply bottom-up, so each application PREPENDS — the final array reads in **source
68
+ * order** (top decorator first, then lower ones, then the `static schedule` literal). With several
69
+ * schedules on one class, prefer explicit `key`s over the positional `${name}:${i}` defaults: the
70
+ * key is part of the deterministic run id, and reordering declarations would silently re-key them.
71
+ */
72
+ export function Scheduled(config) {
73
+ return (target) => {
74
+ const cls = target;
75
+ const existing = cls.schedule === undefined ? [] : Array.isArray(cls.schedule) ? cls.schedule : [cls.schedule];
76
+ const added = Array.isArray(config) ? config : [config];
77
+ cls.schedule = [...added, ...existing];
78
+ };
79
+ }
49
80
  /**
50
81
  * Resolve a {@link WorkflowRef} to its registered workflow name: a string is returned as-is; a
51
82
  * workflow class is resolved via the name on its `static workflow` config. Throws if a class carries
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-ref.js","sourceRoot":"","sources":["../../src/workflow-ref.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAkCH;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,MAAe;IAC1C,IAAI,OAAO,MAAM,KAAK,UAAU;QAAE,OAAO,SAAS,CAAC;IACnD,MAAM,MAAM,GAAI,MAAyC,CAAC,QAAQ,CAAC;IACnE,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC5E,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,GAAG,EAAE,CAAC;IACvD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAe;IAC/C,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,MAAM,GAAG,GAAI,MAA2E,CAAC,QAAQ,CAAC;IAClG,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC7C,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACxB,GAAG,CAAC;QACJ,QAAQ,EAAE,IAAI,CAAC,IAAI;QACnB,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;KACjE,CAAC,CAAC,CAAC;AACN,CAAC;AAgCD;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,GAAgB;IAC3C,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACxC,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC;IACrC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,kBAAkB,GAAG,CAAC,IAAI,2EAA2E,CACtG,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"workflow-ref.js","sourceRoot":"","sources":["../../src/workflow-ref.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAkCH;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,MAAe;IAC1C,IAAI,OAAO,MAAM,KAAK,UAAU;QAAE,OAAO,SAAS,CAAC;IACnD,MAAM,MAAM,GAAI,MAAyC,CAAC,QAAQ,CAAC;IACnE,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC5E,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,GAAG,EAAE,CAAC;IACvD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAe;IAC/C,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,MAAM,GAAG,GAAI,MAA2E,CAAC,QAAQ,CAAC;IAClG,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC7C,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACxB,GAAG,CAAC;QACJ,QAAQ,EAAE,IAAI,CAAC,IAAI;QACnB,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;KACjE,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,SAAS,CACvB,MAAyD;IAEzD,OAAO,CAAC,MAAM,EAAE,EAAE;QAChB,MAAM,GAAG,GAAG,MAEX,CAAC;QACF,MAAM,QAAQ,GACZ,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAChG,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACxD,GAAG,CAAC,QAAQ,GAAG,CAAC,GAAG,KAAK,EAAE,GAAG,QAAQ,CAAC,CAAC;IACzC,CAAC,CAAC;AACJ,CAAC;AAgCD;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,GAAgB;IAC3C,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACxC,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC;IACrC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,kBAAkB,GAAG,CAAC,IAAI,2EAA2E,CACtG,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -2,102 +2,46 @@
2
2
  exports({ to: app.migrationsPath(`${new Date().getTime()}_create_durable_tables.ts`) })
3
3
  }}}
4
4
  import { BaseSchema } from '@adonisjs/lucid/schema'
5
+ import db from '@adonisjs/lucid/services/db'
6
+ import { createDurableTables, dropDurableTables } from '@adonis-agora/durable'
5
7
 
6
8
  /**
7
- * Durable workflow tables for the @adonis-agora/durable `lucid` state store: runs, step checkpoints, the
8
- * search-attribute side-table, signal waiters, buffered signals and buffered events. JSON payloads are stored as TEXT (the store
9
- * (de)serializes them) and timestamps as epoch-ms integers, so the schema is portable across
10
- * SQLite / Postgres / MySQL.
9
+ * Durable workflow tables for the @adonis-agora/durable `lucid` state store: runs, step checkpoints,
10
+ * the search-attribute side-table, signal waiters, buffered signals and buffered events.
11
+ *
12
+ * This migration DELEGATES to the library instead of reproducing its DDL. The schema belongs to the
13
+ * library — `LucidStateStore` decides what columns it reads and writes — so a hand-copied snapshot
14
+ * here can only ever be right until the next release. It was wrong before: the stub shipped without
15
+ * `last_heartbeat_at` / `heartbeat_progress`, which the store writes on every step beat, and nothing
16
+ * failed only because the provider's boot-time `ensureSchema()` silently `ALTER`ed them in. Calling
17
+ * `createDurableTables` makes that drift impossible rather than merely tested for.
18
+ *
19
+ * `createDurableTables` is idempotent (`hasTable` / `hasColumn` guarded) and additive-only, so it is
20
+ * safe to run against a database that already has some or all of the tables.
21
+ *
22
+ * `db` (the `Database` manager from `@adonisjs/lucid/services/db`) rather than `this.db`:
23
+ * `createDurableTables` needs `db.connection(name).schema`, and the `this.db` a migration gets is a
24
+ * `QueryClientContract` — one already-resolved client, with no `.connection()`. `this.db` still
25
+ * supplies the connection NAME, so `node ace migration:run --connection=x` provisions the tables on
26
+ * connection `x` and not on the default.
11
27
  */
12
28
  export default class extends BaseSchema {
13
- async up() {
14
- this.schema.createTable('durable_workflow_runs', (table) => {
15
- table.string('id').primary()
16
- table.string('workflow').notNullable()
17
- table.string('workflow_version').notNullable()
18
- table.string('status').notNullable()
19
- table.string('namespace').notNullable().defaultTo('default')
20
- table.text('input')
21
- table.text('output')
22
- table.text('error')
23
- table.bigInteger('wake_at')
24
- table.string('locked_by')
25
- table.bigInteger('locked_until')
26
- table.integer('recovery_attempts')
27
- table.text('tags')
28
- table.text('search_attributes')
29
- table.integer('priority')
30
- table.bigInteger('created_at').notNullable()
31
- table.bigInteger('updated_at').notNullable()
32
- table.index(['status'], 'durable_runs_status_idx')
33
- table.index(['status', 'wake_at'], 'durable_runs_due_idx')
34
- table.index(['namespace', 'status', 'created_at'], 'durable_runs_namespace_idx')
35
- })
36
-
37
- this.schema.createTable('durable_step_checkpoints', (table) => {
38
- table.string('run_id').notNullable()
39
- table.integer('seq').notNullable()
40
- table.string('name').notNullable()
41
- table.string('kind').notNullable()
42
- table.string('step_id').notNullable()
43
- table.string('status').notNullable()
44
- table.text('input')
45
- table.text('output')
46
- table.text('error')
47
- table.text('events')
48
- table.integer('attempts').notNullable()
49
- table.string('worker_group')
50
- table.bigInteger('wake_at')
51
- table.string('parallel_group')
52
- table.bigInteger('enqueued_at')
53
- table.bigInteger('started_at').notNullable()
54
- table.bigInteger('finished_at').notNullable()
55
- table.primary(['run_id', 'seq'])
56
- table.index(['run_id', 'name'], 'durable_checkpoints_name_idx')
57
- })
58
-
59
- this.schema.createTable('durable_run_attributes', (table) => {
60
- table.string('run_id').notNullable()
61
- table.string('key').notNullable()
62
- table.string('str_value')
63
- table.double('num_value')
64
- table.primary(['run_id', 'key'])
65
- table.index(['key', 'num_value'], 'durable_run_attributes_num_idx')
66
- table.index(['key', 'str_value'], 'durable_run_attributes_str_idx')
67
- })
29
+ /**
30
+ * REQUIRED, not a preference. The DDL below runs through the `Database` manager, which checks out
31
+ * its OWN connection from the pool — it is not part of the transaction the migrator would otherwise
32
+ * open for this migration. With `pool: { max: 1 }` (Adonis's own SQLite guidance) that transaction
33
+ * would hold the only connection while `createDurableTables` waited for a free one, and the
34
+ * migration would hang until the acquire timeout. Nothing is lost by opting out: the statements were
35
+ * never inside that transaction anyway, and re-running an interrupted `createDurableTables` is a
36
+ * no-op.
37
+ */
38
+ static disableTransactions = true
68
39
 
69
- this.schema.createTable('durable_signal_waiters', (table) => {
70
- table.string('token').primary()
71
- table.string('run_id').notNullable()
72
- table.integer('seq').notNullable()
73
- table.string('parallel_group')
74
- })
75
-
76
- this.schema.createTable('durable_buffered_signals', (table) => {
77
- table.increments('id').primary()
78
- table.string('token').notNullable()
79
- table.text('payload')
80
- table.index(['token'], 'durable_buffered_signals_token_idx')
81
- })
82
-
83
- // Reliable (buffered) events: a publish matching no live waiter keeps ONE copy here, consumed by
84
- // the first future matching `waitForEvent`. Keyed by `name` (many waiters share a name with
85
- // different `match`); `id` is the caller-minted PK; `published_at` is epoch-ms.
86
- this.schema.createTable('durable_buffered_events', (table) => {
87
- table.string('id').primary()
88
- table.string('name').notNullable()
89
- table.text('payload')
90
- table.bigInteger('published_at').notNullable()
91
- table.index(['name', 'published_at'], 'durable_buffered_events_name_idx')
92
- })
40
+ async up() {
41
+ await createDurableTables(db, this.db.connectionName)
93
42
  }
94
43
 
95
44
  async down() {
96
- this.schema.dropTableIfExists('durable_buffered_events')
97
- this.schema.dropTableIfExists('durable_buffered_signals')
98
- this.schema.dropTableIfExists('durable_signal_waiters')
99
- this.schema.dropTableIfExists('durable_run_attributes')
100
- this.schema.dropTableIfExists('durable_step_checkpoints')
101
- this.schema.dropTableIfExists('durable_workflow_runs')
45
+ await dropDurableTables(db, this.db.connectionName)
102
46
  }
103
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonis-agora/durable",
3
- "version": "0.19.2",
3
+ "version": "0.21.0",
4
4
  "description": "Durable cross-app workflows for AdonisJS — deterministic replay engine, AdonisJS binding, ace commands, dashboard, OpenTelemetry, Telescope, testing harness and Redis admission, all in one package. Part of the Agora ecosystem.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -158,6 +158,7 @@
158
158
  "@opentelemetry/api": "^1.9.0",
159
159
  "@opentelemetry/context-async-hooks": "^1.30.0",
160
160
  "@opentelemetry/sdk-trace-base": "^1.30.0",
161
+ "@types/better-sqlite3": "^7.6.0",
161
162
  "@types/node": "^20.0.0",
162
163
  "better-sqlite3": "^11.0.0",
163
164
  "bullmq": "^5.0.0",
@@ -168,7 +169,7 @@
168
169
  "zod": "^3.25.0"
169
170
  },
170
171
  "engines": {
171
- "node": ">=20.6.0"
172
+ "node": ">=20.6.0 <23"
172
173
  },
173
174
  "keywords": [
174
175
  "adonisjs",
@@ -184,10 +185,12 @@
184
185
  "agora"
185
186
  ],
186
187
  "scripts": {
187
- "build": "tsc -p tsconfig.json && pnpm run copy:stubs",
188
+ "build": "pnpm run clean && tsc -p tsconfig.build.json && pnpm run copy:stubs && pnpm run check:dist",
189
+ "check:dist": "node ../../scripts/assert-build-output.mjs dist src/index.js",
190
+ "clean": "rm -rf dist",
188
191
  "copy:stubs": "mkdir -p dist/stubs/config dist/stubs/database/migrations dist/stubs/make/workflow dist/assets && cp stubs/config/durable.stub dist/stubs/config/durable.stub && cp stubs/config/durable_dashboard.stub dist/stubs/config/durable_dashboard.stub && cp -r stubs/database/migrations/. dist/stubs/database/migrations/ && cp stubs/make/workflow/main.stub dist/stubs/make/workflow/main.stub && cp assets/dashboard.html dist/assets/dashboard.html",
189
192
  "test": "vitest run --passWithNoTests",
190
193
  "test:watch": "vitest",
191
- "typecheck": "tsc -p tsconfig.json --noEmit"
194
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.tests.json"
192
195
  }
193
196
  }