@n8n/scheduler 0.2.2 → 0.3.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 (166) hide show
  1. package/README.md +60 -23
  2. package/dist/build.tsbuildinfo +1 -1
  3. package/dist/core/errors.d.ts +6 -0
  4. package/dist/core/errors.js +15 -1
  5. package/dist/core/errors.js.map +1 -1
  6. package/dist/core/executor/backoff.js.map +1 -0
  7. package/dist/core/executor/executor.d.ts +38 -0
  8. package/dist/core/executor/executor.js +137 -0
  9. package/dist/core/executor/executor.js.map +1 -0
  10. package/dist/core/executor/index.d.ts +8 -0
  11. package/dist/core/executor/index.js +16 -0
  12. package/dist/core/executor/index.js.map +1 -0
  13. package/dist/core/executor/options.d.ts +6 -0
  14. package/dist/core/executor/options.js +10 -0
  15. package/dist/core/executor/options.js.map +1 -0
  16. package/dist/{executor → core/executor}/precision-timer.d.ts +1 -0
  17. package/dist/{executor → core/executor}/precision-timer.js +3 -0
  18. package/dist/core/executor/precision-timer.js.map +1 -0
  19. package/dist/core/executor/store.d.ts +21 -0
  20. package/dist/core/executor/store.js +3 -0
  21. package/dist/core/executor/store.js.map +1 -0
  22. package/dist/{executor → core/executor}/task-handler.d.ts +1 -1
  23. package/dist/{executor → core/executor}/task-handler.js +2 -2
  24. package/dist/core/executor/task-handler.js.map +1 -0
  25. package/dist/core/executor/tracing.d.ts +11 -0
  26. package/dist/core/executor/tracing.js +7 -0
  27. package/dist/core/executor/tracing.js.map +1 -0
  28. package/dist/core/factory.d.ts +29 -0
  29. package/dist/core/factory.js +254 -0
  30. package/dist/core/factory.js.map +1 -0
  31. package/dist/core/index.d.ts +17 -9
  32. package/dist/core/index.js +15 -17
  33. package/dist/core/index.js.map +1 -1
  34. package/dist/core/lifecycle/alarm.d.ts +8 -0
  35. package/dist/core/lifecycle/alarm.js +32 -0
  36. package/dist/core/lifecycle/alarm.js.map +1 -0
  37. package/dist/core/lifecycle/index.d.ts +5 -0
  38. package/dist/core/lifecycle/index.js +11 -0
  39. package/dist/core/lifecycle/index.js.map +1 -0
  40. package/dist/core/lifecycle/lookahead.d.ts +1 -0
  41. package/dist/core/lifecycle/lookahead.js +7 -0
  42. package/dist/core/lifecycle/lookahead.js.map +1 -0
  43. package/dist/core/lifecycle/loop.d.ts +39 -0
  44. package/dist/core/lifecycle/loop.js +86 -0
  45. package/dist/core/lifecycle/loop.js.map +1 -0
  46. package/dist/core/lifecycle/options.d.ts +15 -0
  47. package/dist/core/lifecycle/options.js +18 -0
  48. package/dist/core/lifecycle/options.js.map +1 -0
  49. package/dist/core/lifecycle/timeline.d.ts +9 -0
  50. package/dist/core/lifecycle/timeline.js +22 -0
  51. package/dist/core/lifecycle/timeline.js.map +1 -0
  52. package/dist/core/materializer/index.d.ts +2 -2
  53. package/dist/core/materializer/index.js.map +1 -1
  54. package/dist/core/materializer/materialize.d.ts +10 -2
  55. package/dist/core/materializer/materialize.js +32 -6
  56. package/dist/core/materializer/materialize.js.map +1 -1
  57. package/dist/core/materializer/options.d.ts +1 -0
  58. package/dist/core/materializer/options.js +1 -0
  59. package/dist/core/materializer/options.js.map +1 -1
  60. package/dist/core/materializer/plan.d.ts +1 -0
  61. package/dist/core/materializer/plan.js +12 -6
  62. package/dist/core/materializer/plan.js.map +1 -1
  63. package/dist/core/materializer/transaction.d.ts +18 -1
  64. package/dist/core/provisioning/index.d.ts +5 -0
  65. package/dist/core/provisioning/index.js +9 -0
  66. package/dist/core/provisioning/index.js.map +1 -0
  67. package/dist/core/provisioning/provision.d.ts +6 -0
  68. package/dist/core/provisioning/provision.js +64 -0
  69. package/dist/core/provisioning/provision.js.map +1 -0
  70. package/dist/core/provisioning/provisioner.d.ts +15 -0
  71. package/dist/core/provisioning/provisioner.js +18 -0
  72. package/dist/core/provisioning/provisioner.js.map +1 -0
  73. package/dist/core/provisioning/transaction.d.ts +13 -0
  74. package/dist/core/provisioning/transaction.js +3 -0
  75. package/dist/core/provisioning/transaction.js.map +1 -0
  76. package/dist/core/provisioning/types.d.ts +43 -0
  77. package/dist/core/provisioning/types.js +3 -0
  78. package/dist/core/provisioning/types.js.map +1 -0
  79. package/dist/core/reaper/index.d.ts +1 -0
  80. package/dist/core/reaper/index.js +7 -0
  81. package/dist/core/reaper/index.js.map +1 -0
  82. package/dist/{reaper/reaper.d.ts → core/reaper/reap.d.ts} +13 -8
  83. package/dist/core/reaper/reap.js +51 -0
  84. package/dist/core/reaper/reap.js.map +1 -0
  85. package/dist/core/recurrence/field.d.ts +2 -0
  86. package/dist/core/recurrence/field.js +12 -0
  87. package/dist/core/recurrence/field.js.map +1 -0
  88. package/dist/core/recurrence/kinds/cron.d.ts +10 -0
  89. package/dist/core/recurrence/kinds/cron.js +64 -0
  90. package/dist/core/recurrence/kinds/cron.js.map +1 -0
  91. package/dist/core/recurrence/kinds/interval.d.ts +5 -0
  92. package/dist/core/recurrence/kinds/interval.js +29 -0
  93. package/dist/core/recurrence/kinds/interval.js.map +1 -0
  94. package/dist/core/recurrence/kinds/one-off.d.ts +5 -0
  95. package/dist/core/recurrence/kinds/one-off.js +24 -0
  96. package/dist/core/recurrence/kinds/one-off.js.map +1 -0
  97. package/dist/core/recurrence/kinds/recurring-cron.d.ts +7 -0
  98. package/dist/core/recurrence/kinds/recurring-cron.js +64 -0
  99. package/dist/core/recurrence/kinds/recurring-cron.js.map +1 -0
  100. package/dist/core/recurrence/next-run.d.ts +2 -0
  101. package/dist/core/recurrence/next-run.js +37 -24
  102. package/dist/core/recurrence/next-run.js.map +1 -1
  103. package/dist/core/recurrence/periods.d.ts +2 -0
  104. package/dist/core/recurrence/periods.js +30 -0
  105. package/dist/core/recurrence/periods.js.map +1 -0
  106. package/dist/core/recurrence/resolve.d.ts +2 -0
  107. package/dist/core/recurrence/resolve.js +25 -0
  108. package/dist/core/recurrence/resolve.js.map +1 -0
  109. package/dist/core/recurrence/validate.js +9 -38
  110. package/dist/core/recurrence/validate.js.map +1 -1
  111. package/dist/core/retention/prune.d.ts +1 -1
  112. package/dist/core/retention/prune.js +11 -9
  113. package/dist/core/retention/prune.js.map +1 -1
  114. package/dist/core/retention/store.d.ts +1 -1
  115. package/dist/core/scheduler.d.ts +16 -0
  116. package/dist/core/scheduler.js +3 -0
  117. package/dist/core/scheduler.js.map +1 -0
  118. package/dist/core/types/schedule.d.ts +17 -4
  119. package/dist/core/types/task.d.ts +2 -5
  120. package/dist/observability/attributes.d.ts +41 -0
  121. package/dist/observability/attributes.js +56 -0
  122. package/dist/observability/attributes.js.map +1 -0
  123. package/dist/observability/executor-tracing.d.ts +4 -0
  124. package/dist/observability/executor-tracing.js +61 -0
  125. package/dist/observability/executor-tracing.js.map +1 -0
  126. package/dist/observability/materializer-tracing.d.ts +3 -0
  127. package/dist/observability/materializer-tracing.js +22 -0
  128. package/dist/observability/materializer-tracing.js.map +1 -0
  129. package/dist/observability/metrics.d.ts +11 -0
  130. package/dist/observability/metrics.js +14 -0
  131. package/dist/observability/metrics.js.map +1 -0
  132. package/dist/observability/pass-tracing.d.ts +2 -0
  133. package/dist/observability/pass-tracing.js +24 -0
  134. package/dist/observability/pass-tracing.js.map +1 -0
  135. package/dist/observability/provisioner-tracing.d.ts +11 -0
  136. package/dist/observability/provisioner-tracing.js +51 -0
  137. package/dist/observability/provisioner-tracing.js.map +1 -0
  138. package/dist/observability/tracer.d.ts +21 -0
  139. package/dist/observability/tracer.js +10 -0
  140. package/dist/observability/tracer.js.map +1 -0
  141. package/package.json +13 -24
  142. package/dist/core/enums.d.ts +0 -1
  143. package/dist/core/enums.js +0 -10
  144. package/dist/core/enums.js.map +0 -1
  145. package/dist/executor/backoff.js.map +0 -1
  146. package/dist/executor/executor.d.ts +0 -23
  147. package/dist/executor/executor.js +0 -133
  148. package/dist/executor/executor.js.map +0 -1
  149. package/dist/executor/precision-timer.js.map +0 -1
  150. package/dist/executor/task-handler.js.map +0 -1
  151. package/dist/reaper/reaper.js +0 -37
  152. package/dist/reaper/reaper.js.map +0 -1
  153. package/dist/storage/index.d.ts +0 -3
  154. package/dist/storage/index.js +0 -12
  155. package/dist/storage/index.js.map +0 -1
  156. package/dist/storage/mappers.d.ts +0 -6
  157. package/dist/storage/mappers.js +0 -72
  158. package/dist/storage/mappers.js.map +0 -1
  159. package/dist/storage/materializer-store.d.ts +0 -14
  160. package/dist/storage/materializer-store.js +0 -83
  161. package/dist/storage/materializer-store.js.map +0 -1
  162. package/dist/storage/scheduler.service.d.ts +0 -16
  163. package/dist/storage/scheduler.service.js +0 -70
  164. package/dist/storage/scheduler.service.js.map +0 -1
  165. /package/dist/{executor → core/executor}/backoff.d.ts +0 -0
  166. /package/dist/{executor → core/executor}/backoff.js +0 -0
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Timeline = void 0;
4
+ class Timeline {
5
+ constructor(intervalMs, jitterRatio, random, startAt) {
6
+ this.intervalMs = intervalMs;
7
+ this.jitterRatio = jitterRatio;
8
+ this.random = random;
9
+ this.slotAt = startAt + intervalMs * random();
10
+ }
11
+ get firstTickAt() {
12
+ return this.slotAt;
13
+ }
14
+ nextTickAt(now) {
15
+ const missedSlots = Math.floor(Math.max(0, now - this.slotAt) / this.intervalMs);
16
+ this.slotAt += (missedSlots + 1) * this.intervalMs;
17
+ const jitterMs = this.intervalMs * this.jitterRatio * (2 * this.random() - 1);
18
+ return this.slotAt + jitterMs;
19
+ }
20
+ }
21
+ exports.Timeline = Timeline;
22
+ //# sourceMappingURL=timeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.js","sourceRoot":"","sources":["../../../src/core/lifecycle/timeline.ts"],"names":[],"mappings":";;;AAWA,MAAa,QAAQ;IAIpB,YACkB,UAAkB,EAClB,WAAmB,EACnB,MAAoB,EACrC,OAAe;QAHE,eAAU,GAAV,UAAU,CAAQ;QAClB,gBAAW,GAAX,WAAW,CAAQ;QACnB,WAAM,GAAN,MAAM,CAAc;QAGrC,IAAI,CAAC,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,EAAE,CAAC;IAC/C,CAAC;IAGD,IAAI,WAAW;QACd,OAAO,IAAI,CAAC,MAAM,CAAC;IACpB,CAAC;IAMD,UAAU,CAAC,GAAW;QACrB,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;QACjF,IAAI,CAAC,MAAM,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;QAC9E,OAAO,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;IAC/B,CAAC;CACD;AA5BD,4BA4BC"}
@@ -1,4 +1,4 @@
1
- export { materialize, type MaterializerSummary, type MaterializerOptions, type OnJobPlanError, } from './materialize';
1
+ export { materialize, type MaterializerHooks, type MaterializerSummary, type MaterializerOptions, type OnJobPlanError, } from './materialize';
2
2
  export { DEFAULT_MATERIALIZER_OPTIONS } from './options';
3
3
  export { planOccurrences, type OccurrencePlan } from './plan';
4
- export type { DueJobs, PlannedJob, RunInTransaction, MaterializerTransaction } from './transaction';
4
+ export type { DueJobs, NewOccurrence, PlannedJob, RecordedOccurrence, RecordOccurrencesResult, RunInTransaction, MaterializerTransaction, } from './transaction';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/materializer/index.ts"],"names":[],"mappings":";;;AAAA,6CAKuB;AAJtB,0GAAA,WAAW,OAAA;AAKZ,qCAAyD;AAAhD,uHAAA,4BAA4B,OAAA;AACrC,+BAA8D;AAArD,uGAAA,eAAe,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/materializer/index.ts"],"names":[],"mappings":";;;AAAA,6CAMuB;AALtB,0GAAA,WAAW,OAAA;AAMZ,qCAAyD;AAAhD,uHAAA,4BAA4B,OAAA;AACrC,+BAA8D;AAArD,uGAAA,eAAe,OAAA"}
@@ -1,11 +1,19 @@
1
1
  import type { ScheduledJob } from '../types';
2
2
  import { type MaterializerOptions } from './options';
3
- import type { RunInTransaction } from './transaction';
3
+ import type { RecordedOccurrence, RunInTransaction } from './transaction';
4
4
  export type { MaterializerOptions } from './options';
5
5
  export interface MaterializerSummary {
6
6
  claimedJobs: number;
7
7
  occurrences: number;
8
+ created: RecordedOccurrence[];
8
9
  deferredJobs: number;
9
10
  }
10
11
  export type OnJobPlanError = (job: ScheduledJob, error: unknown) => void;
11
- export declare function materialize(runInTransaction: RunInTransaction, options?: MaterializerOptions, onPlanError?: OnJobPlanError): Promise<MaterializerSummary>;
12
+ export interface MaterializerHooks {
13
+ onPlanError?: OnJobPlanError;
14
+ onSkippedDuplicates?: (context: {
15
+ planned: number;
16
+ recorded: number;
17
+ }) => void;
18
+ }
19
+ export declare function materialize(runInTransaction: RunInTransaction, options?: MaterializerOptions, hooks?: MaterializerHooks, signal?: AbortSignal): Promise<MaterializerSummary>;
@@ -4,22 +4,44 @@ exports.materialize = materialize;
4
4
  const constants_1 = require("@n8n/constants");
5
5
  const options_1 = require("./options");
6
6
  const plan_1 = require("./plan");
7
- async function materialize(runInTransaction, options = options_1.DEFAULT_MATERIALIZER_OPTIONS, onPlanError) {
7
+ async function materialize(runInTransaction, options = options_1.DEFAULT_MATERIALIZER_OPTIONS, hooks = {}, signal) {
8
+ signal?.throwIfAborted();
8
9
  return await runInTransaction(async (tx) => {
9
10
  const claimed = await tx.claimDueJobs(options.batchSize);
11
+ signal?.throwIfAborted();
10
12
  if (claimed === undefined) {
11
- return { claimedJobs: 0, occurrences: 0, deferredJobs: 0 };
13
+ return { claimedJobs: 0, occurrences: 0, created: [], deferredJobs: 0 };
14
+ }
15
+ const { occurrencesPlanned, numberOfJobsDeferred } = planOrDeferJobs(claimed, options, hooks.onPlanError);
16
+ const rows = toNewOccurrences(occurrencesPlanned);
17
+ const { recorded, created } = await tx.recordOccurrences(rows);
18
+ signal?.throwIfAborted();
19
+ if (recorded < rows.length) {
20
+ try {
21
+ hooks.onSkippedDuplicates?.({ planned: rows.length, recorded });
22
+ }
23
+ catch {
24
+ }
12
25
  }
13
- const { occurrencesPlanned, numberOfJobsDeferred } = planOrDeferJobs(claimed, options, onPlanError);
14
- const occurrences = await tx.recordOccurrences(occurrencesPlanned);
15
26
  await tx.advanceJobs(occurrencesPlanned);
16
27
  return {
17
28
  claimedJobs: claimed.jobs.length,
18
- occurrences,
29
+ occurrences: recorded,
30
+ created,
19
31
  deferredJobs: numberOfJobsDeferred,
20
32
  };
21
33
  });
22
34
  }
35
+ function toNewOccurrences(planned) {
36
+ return planned.flatMap(({ job, plan }) => plan.occurrences.map((when) => ({
37
+ jobId: job.id,
38
+ taskType: job.taskType,
39
+ payload: job.payload,
40
+ scheduledFor: when,
41
+ runAt: when,
42
+ maxAttempts: job.maxAttempts,
43
+ })));
44
+ }
23
45
  function planOrDeferJobs(dueJobs, options, onPlanError) {
24
46
  const plans = dueJobs.jobs.map((job) => planOrDeferJob(job, dueJobs.now, options, onPlanError));
25
47
  return {
@@ -35,7 +57,11 @@ function planOrDeferJob(job, now, options, onPlanError) {
35
57
  };
36
58
  }
37
59
  catch (error) {
38
- onPlanError?.(job, error);
60
+ try {
61
+ onPlanError?.(job, error);
62
+ }
63
+ catch {
64
+ }
39
65
  const retryAt = new Date(now.getTime() + options.planRetrySeconds * constants_1.Time.seconds.toMilliseconds);
40
66
  return {
41
67
  plannedJob: {
@@ -1 +1 @@
1
- {"version":3,"file":"materialize.js","sourceRoot":"","sources":["../../../src/core/materializer/materialize.ts"],"names":[],"mappings":";;AAqDA,kCAwBC;AA7ED,8CAAsC;AAGtC,uCAAmF;AACnF,iCAAyC;AAiDlC,KAAK,UAAU,WAAW,CAChC,gBAAkC,EAClC,UAA+B,sCAA4B,EAC3D,WAA4B;IAE5B,OAAO,MAAM,gBAAgB,CAAsB,KAAK,EAAE,EAAE,EAAE,EAAE;QAC/D,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACzD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;QAC5D,CAAC;QACD,MAAM,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,GAAG,eAAe,CACnE,OAAO,EACP,OAAO,EACP,WAAW,CACX,CAAC;QACF,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,CAAC;QACnE,MAAM,EAAE,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC;QAEzC,OAAO;YACN,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM;YAChC,WAAW;YACX,YAAY,EAAE,oBAAoB;SAClC,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CACvB,OAAgB,EAChB,OAA4B,EAC5B,WAA4B;IAK5B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;IAChG,OAAO;QACN,kBAAkB,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC;QAC7D,oBAAoB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,MAAM;KACrE,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CACtB,GAAiB,EACjB,GAAS,EACT,OAA4B,EAC5B,WAA4B;IAK5B,IAAI,CAAC;QACJ,OAAO;YACN,UAAU,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,IAAA,sBAAe,EAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE;YAC7D,QAAQ,EAAE,KAAK;SACf,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,WAAW,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAO1B,MAAM,OAAO,GAAG,IAAI,IAAI,CACvB,GAAG,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,gBAAgB,GAAG,gBAAI,CAAC,OAAO,CAAC,cAAc,CACtE,CAAC;QACF,OAAO;YACN,UAAU,EAAE;gBACX,GAAG;gBACH,IAAI,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE;aAC3E;YACD,QAAQ,EAAE,IAAI;SACd,CAAC;IACH,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"materialize.js","sourceRoot":"","sources":["../../../src/core/materializer/materialize.ts"],"names":[],"mappings":";;AAqFA,kCAqCC;AA1HD,8CAAsC;AAGtC,uCAAmF;AACnF,iCAAyC;AAiFlC,KAAK,UAAU,WAAW,CAChC,gBAAkC,EAClC,UAA+B,sCAA4B,EAC3D,QAA2B,EAAE,EAC7B,MAAoB;IAEpB,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,OAAO,MAAM,gBAAgB,CAAsB,KAAK,EAAE,EAAE,EAAE,EAAE;QAC/D,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACzD,MAAM,EAAE,cAAc,EAAE,CAAC;QACzB,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;QACzE,CAAC;QACD,MAAM,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,GAAG,eAAe,CACnE,OAAO,EACP,OAAO,EACP,KAAK,CAAC,WAAW,CACjB,CAAC;QACF,MAAM,IAAI,GAAG,gBAAgB,CAAC,kBAAkB,CAAC,CAAC;QAClD,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC/D,MAAM,EAAE,cAAc,EAAE,CAAC;QACzB,IAAI,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACJ,KAAK,CAAC,mBAAmB,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;YACjE,CAAC;YAAC,MAAM,CAAC;YAET,CAAC;QACF,CAAC;QACD,MAAM,EAAE,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC;QAEzC,OAAO;YACN,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM;YAChC,WAAW,EAAE,QAAQ;YACrB,OAAO;YACP,YAAY,EAAE,oBAAoB;SAClC,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAqB;IAC9C,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CACxC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC/B,KAAK,EAAE,GAAG,CAAC,EAAE;QACb,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,YAAY,EAAE,IAAI;QAClB,KAAK,EAAE,IAAI;QACX,WAAW,EAAE,GAAG,CAAC,WAAW;KAC5B,CAAC,CAAC,CACH,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CACvB,OAAgB,EAChB,OAA4B,EAC5B,WAA4B;IAK5B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;IAChG,OAAO;QACN,kBAAkB,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC;QAC7D,oBAAoB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,MAAM;KACrE,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CACtB,GAAiB,EACjB,GAAS,EACT,OAA4B,EAC5B,WAA4B;IAK5B,IAAI,CAAC;QACJ,OAAO;YACN,UAAU,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,IAAA,sBAAe,EAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE;YAC7D,QAAQ,EAAE,KAAK;SACf,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,CAAC;YACJ,WAAW,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;QAET,CAAC;QAQD,MAAM,OAAO,GAAG,IAAI,IAAI,CACvB,GAAG,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,gBAAgB,GAAG,gBAAI,CAAC,OAAO,CAAC,cAAc,CACtE,CAAC;QACF,OAAO;YACN,UAAU,EAAE;gBACX,GAAG;gBACH,IAAI,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE;aAC3E;YACD,QAAQ,EAAE,IAAI;SACd,CAAC;IACH,CAAC;AACF,CAAC"}
@@ -3,5 +3,6 @@ export interface MaterializerOptions {
3
3
  maxPerJob: number;
4
4
  batchSize: number;
5
5
  planRetrySeconds: number;
6
+ defaultTimezone: string;
6
7
  }
7
8
  export declare const DEFAULT_MATERIALIZER_OPTIONS: MaterializerOptions;
@@ -7,5 +7,6 @@ exports.DEFAULT_MATERIALIZER_OPTIONS = {
7
7
  batchSize: 100,
8
8
  maxPerJob: 1000,
9
9
  planRetrySeconds: constants_1.Time.hours.toSeconds,
10
+ defaultTimezone: 'UTC',
10
11
  };
11
12
  //# sourceMappingURL=options.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"options.js","sourceRoot":"","sources":["../../../src/core/materializer/options.ts"],"names":[],"mappings":";;;AAAA,8CAAsC;AA+BzB,QAAA,4BAA4B,GAAwB;IAChE,aAAa,EAAE,EAAE;IACjB,SAAS,EAAE,GAAG;IACd,SAAS,EAAE,IAAI;IACf,gBAAgB,EAAE,gBAAI,CAAC,KAAK,CAAC,SAAS;CACtC,CAAC"}
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../../../src/core/materializer/options.ts"],"names":[],"mappings":";;;AAAA,8CAAsC;AAqCzB,QAAA,4BAA4B,GAAwB;IAChE,aAAa,EAAE,EAAE;IACjB,SAAS,EAAE,GAAG;IACd,SAAS,EAAE,IAAI;IACf,gBAAgB,EAAE,gBAAI,CAAC,KAAK,CAAC,SAAS;IACtC,eAAe,EAAE,KAAK;CACtB,CAAC"}
@@ -7,4 +7,5 @@ export interface OccurrencePlan {
7
7
  export declare function planOccurrences(job: ScheduledJob, now: Date, options: {
8
8
  windowSeconds: number;
9
9
  maxPerJob: number;
10
+ defaultTimezone: string;
10
11
  }): OccurrencePlan;
@@ -3,19 +3,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.planOccurrences = planOccurrences;
4
4
  const constants_1 = require("@n8n/constants");
5
5
  const next_run_1 = require("../recurrence/next-run");
6
+ const resolve_1 = require("../recurrence/resolve");
6
7
  function planOccurrences(job, now, options) {
8
+ const schedule = (0, resolve_1.resolveSchedule)(job, options.defaultTimezone);
7
9
  const windowEnd = now.getTime() + options.windowSeconds * constants_1.Time.seconds.toMilliseconds;
10
+ if (job.nextRunAt === null) {
11
+ return { occurrences: [], nextRunAt: null, lastFiredAt: job.lastFiredAt };
12
+ }
8
13
  const occurrences = [];
9
- let cursor = job.nextRunAt;
10
- while (cursor !== null &&
11
- cursor.getTime() <= windowEnd &&
14
+ const fires = (0, next_run_1.occurrencesFrom)(schedule, job.nextRunAt);
15
+ let fire = fires.next();
16
+ while (!fire.done &&
17
+ fire.value.getTime() <= windowEnd &&
12
18
  occurrences.length < options.maxPerJob) {
13
- occurrences.push(cursor);
14
- cursor = (0, next_run_1.computeNextRunAt)(job.schedule, cursor);
19
+ occurrences.push(fire.value);
20
+ fire = fires.next();
15
21
  }
16
22
  return {
17
23
  occurrences,
18
- nextRunAt: cursor,
24
+ nextRunAt: fire.done ? null : fire.value,
19
25
  lastFiredAt: occurrences.at(-1) ?? job.lastFiredAt,
20
26
  };
21
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/core/materializer/plan.ts"],"names":[],"mappings":";;AA+BA,0CAuBC;AAtDD,8CAAsC;AAEtC,qDAA0D;AA6B1D,SAAgB,eAAe,CAC9B,GAAiB,EACjB,GAAS,EACT,OAAqD;IAErD,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,aAAa,GAAG,gBAAI,CAAC,OAAO,CAAC,cAAc,CAAC;IAEtF,MAAM,WAAW,GAAW,EAAE,CAAC;IAC/B,IAAI,MAAM,GAAG,GAAG,CAAC,SAAS,CAAC;IAC3B,OACC,MAAM,KAAK,IAAI;QACf,MAAM,CAAC,OAAO,EAAE,IAAI,SAAS;QAC7B,WAAW,CAAC,MAAM,GAAG,OAAO,CAAC,SAAS,EACrC,CAAC;QACF,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,MAAM,GAAG,IAAA,2BAAgB,EAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACjD,CAAC;IAED,OAAO;QACN,WAAW;QACX,SAAS,EAAE,MAAM;QACjB,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,WAAW;KAClD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/core/materializer/plan.ts"],"names":[],"mappings":";;AAgCA,0CA+BC;AA/DD,8CAAsC;AAEtC,qDAAyD;AACzD,mDAAwD;AA6BxD,SAAgB,eAAe,CAC9B,GAAiB,EACjB,GAAS,EACT,OAA8E;IAI9E,MAAM,QAAQ,GAAG,IAAA,yBAAe,EAAC,GAAG,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,aAAa,GAAG,gBAAI,CAAC,OAAO,CAAC,cAAc,CAAC;IAEtF,IAAI,GAAG,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;QAC5B,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC;IAC3E,CAAC;IAED,MAAM,WAAW,GAAW,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,IAAA,0BAAe,EAAC,QAAQ,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;IACvD,IAAI,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACxB,OACC,CAAC,IAAI,CAAC,IAAI;QACV,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,SAAS;QACjC,WAAW,CAAC,MAAM,GAAG,OAAO,CAAC,SAAS,EACrC,CAAC;QACF,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACrB,CAAC;IAED,OAAO;QACN,WAAW;QACX,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK;QACxC,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,WAAW;KAClD,CAAC;AACH,CAAC"}
@@ -4,13 +4,30 @@ export interface PlannedJob {
4
4
  job: ScheduledJob;
5
5
  plan: OccurrencePlan;
6
6
  }
7
+ export interface NewOccurrence {
8
+ jobId: number;
9
+ taskType: string;
10
+ payload: Record<string, unknown>;
11
+ scheduledFor: Date;
12
+ runAt: Date;
13
+ maxAttempts: number;
14
+ }
15
+ export interface RecordedOccurrence {
16
+ id: string;
17
+ jobId: number;
18
+ taskType: string;
19
+ }
20
+ export interface RecordOccurrencesResult {
21
+ recorded: number;
22
+ created: RecordedOccurrence[];
23
+ }
7
24
  export interface DueJobs {
8
25
  now: Date;
9
26
  jobs: ScheduledJob[];
10
27
  }
11
28
  export interface MaterializerTransaction {
12
29
  claimDueJobs(limit: number): Promise<DueJobs | undefined>;
13
- recordOccurrences(planned: PlannedJob[]): Promise<number>;
30
+ recordOccurrences(occurrences: NewOccurrence[]): Promise<RecordOccurrencesResult>;
14
31
  advanceJobs(planned: PlannedJob[]): Promise<void>;
15
32
  }
16
33
  export type RunInTransaction = <T>(work: (tx: MaterializerTransaction) => Promise<T>) => Promise<T>;
@@ -0,0 +1,5 @@
1
+ export { provision, deprovision } from './provision';
2
+ export { createJobProvisioner } from './provisioner';
3
+ export type { JobProvisioner, JobProvisionerDeps } from './provisioner';
4
+ export type { ProvisionTransaction, RunInProvisionTransaction, DeprovisionTransaction, RunInDeprovisionTransaction, } from './transaction';
5
+ export type { ScheduleDefinition, CronDefinition, RecurringCronDefinition, IntervalDefinition, OneOffDefinition, DesiredJob, ExistingJob, ProvisionedJob, ProvisionSummary, } from './types';
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createJobProvisioner = exports.deprovision = exports.provision = void 0;
4
+ var provision_1 = require("./provision");
5
+ Object.defineProperty(exports, "provision", { enumerable: true, get: function () { return provision_1.provision; } });
6
+ Object.defineProperty(exports, "deprovision", { enumerable: true, get: function () { return provision_1.deprovision; } });
7
+ var provisioner_1 = require("./provisioner");
8
+ Object.defineProperty(exports, "createJobProvisioner", { enumerable: true, get: function () { return provisioner_1.createJobProvisioner; } });
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/provisioning/index.ts"],"names":[],"mappings":";;;AAAA,yCAAqD;AAA5C,sGAAA,SAAS,OAAA;AAAE,wGAAA,WAAW,OAAA;AAC/B,6CAAqD;AAA5C,mHAAA,oBAAoB,OAAA"}
@@ -0,0 +1,6 @@
1
+ import type { RunInProvisionTransaction, RunInDeprovisionTransaction } from './transaction';
2
+ import type { DesiredJob, ProvisionSummary } from './types';
3
+ export declare function provision(runInTransaction: RunInProvisionTransaction, desired: DesiredJob[]): Promise<ProvisionSummary>;
4
+ export declare function deprovision(runInTransaction: RunInDeprovisionTransaction): Promise<{
5
+ removed: number;
6
+ }>;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.provision = provision;
4
+ exports.deprovision = deprovision;
5
+ const errors_1 = require("../errors");
6
+ async function provision(runInTransaction, desired) {
7
+ return await runInTransaction(async (tx) => {
8
+ const existing = await tx.findExisting();
9
+ const existingByName = new Map(existing.map((job) => [job.name, job]));
10
+ const desiredNames = new Set(desired.map((job) => job.name));
11
+ const inserts = [];
12
+ const redefined = [];
13
+ const unchanged = [];
14
+ for (const job of desired) {
15
+ const current = existingByName.get(job.name);
16
+ if (current === undefined) {
17
+ inserts.push(job);
18
+ }
19
+ else if (holdsDefinition(current, job)) {
20
+ unchanged.push({ id: current.id, name: current.name });
21
+ }
22
+ else {
23
+ redefined.push({ id: current.id, name: current.name });
24
+ await tx.redefine(current.id, job.schedule, job.firstRunAt);
25
+ }
26
+ }
27
+ const removed = existing
28
+ .filter((job) => !desiredNames.has(job.name))
29
+ .map((job) => ({ id: job.id, name: job.name }));
30
+ await tx.withdrawPendingTasks(redefined.map((job) => job.id));
31
+ await tx.deleteJobs(removed.map((job) => job.id));
32
+ const insertedIds = await tx.insert(inserts);
33
+ const inserted = inserts.map((job, index) => ({ id: insertedIds[index], name: job.name }));
34
+ return { inserted, redefined, unchanged, removed };
35
+ });
36
+ }
37
+ async function deprovision(runInTransaction) {
38
+ return await runInTransaction(async (tx) => ({ removed: await tx.deleteAll() }));
39
+ }
40
+ function holdsDefinition(current, desired) {
41
+ return (sameSchedule(current.schedule, desired.schedule) &&
42
+ current.hasClock === (desired.firstRunAt !== null));
43
+ }
44
+ function sameSchedule(a, b) {
45
+ switch (a.kind) {
46
+ case 'cron':
47
+ return (b.kind === 'cron' && a.cronExpression === b.cronExpression && a.timezone === b.timezone);
48
+ case 'recurring_cron':
49
+ return (b.kind === 'recurring_cron' &&
50
+ a.cronExpression === b.cronExpression &&
51
+ a.timezone === b.timezone &&
52
+ a.recurrenceUnit === b.recurrenceUnit &&
53
+ a.recurrenceSize === b.recurrenceSize);
54
+ case 'interval':
55
+ return b.kind === 'interval' && a.intervalSeconds === b.intervalSeconds;
56
+ case 'one_off':
57
+ return b.kind === 'one_off' && a.fireAt.getTime() === b.fireAt.getTime();
58
+ default: {
59
+ const exhaustive = a;
60
+ throw new errors_1.InvalidScheduleError(`Unexpected schedule kind: ${JSON.stringify(exhaustive)}`);
61
+ }
62
+ }
63
+ }
64
+ //# sourceMappingURL=provision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provision.js","sourceRoot":"","sources":["../../../src/core/provisioning/provision.ts"],"names":[],"mappings":";;AA2BA,8BA0CC;AAKD,kCAIC;AA9ED,sCAAiD;AA2B1C,KAAK,UAAU,SAAS,CAC9B,gBAA2C,EAC3C,OAAqB;IAErB,OAAO,MAAM,gBAAgB,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QAC1C,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,YAAY,EAAE,CAAC;QACzC,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;QACvE,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QAE7D,MAAM,OAAO,GAAiB,EAAE,CAAC;QACjC,MAAM,SAAS,GAAqB,EAAE,CAAC;QACvC,MAAM,SAAS,GAAqB,EAAE,CAAC;QAEvC,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC3B,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC7C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC3B,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnB,CAAC;iBAAM,IAAI,eAAe,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;gBAC1C,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YACxD,CAAC;iBAAM,CAAC;gBACP,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;gBACvD,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;YAC7D,CAAC;QACF,CAAC;QAED,MAAM,OAAO,GAAG,QAAQ;aACtB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;aAC5C,GAAG,CAAC,CAAC,GAAG,EAAkB,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAIjE,MAAM,EAAE,CAAC,oBAAoB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC9D,MAAM,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAGlD,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAC3B,CAAC,GAAG,EAAE,KAAK,EAAkB,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAC5E,CAAC;QAEF,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;IACpD,CAAC,CAAC,CAAC;AACJ,CAAC;AAKM,KAAK,UAAU,WAAW,CAChC,gBAA6C;IAE7C,OAAO,MAAM,gBAAgB,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;AAClF,CAAC;AAMD,SAAS,eAAe,CAAC,OAAoB,EAAE,OAAmB;IACjE,OAAO,CACN,YAAY,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC;QAEhD,OAAO,CAAC,QAAQ,KAAK,CAAC,OAAO,CAAC,UAAU,KAAK,IAAI,CAAC,CAClD,CAAC;AACH,CAAC;AAKD,SAAS,YAAY,CAAC,CAAqB,EAAE,CAAqB;IACjE,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QAChB,KAAK,MAAM;YACV,OAAO,CACN,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,cAAc,KAAK,CAAC,CAAC,cAAc,IAAI,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ,CACvF,CAAC;QACH,KAAK,gBAAgB;YACpB,OAAO,CACN,CAAC,CAAC,IAAI,KAAK,gBAAgB;gBAC3B,CAAC,CAAC,cAAc,KAAK,CAAC,CAAC,cAAc;gBACrC,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ;gBACzB,CAAC,CAAC,cAAc,KAAK,CAAC,CAAC,cAAc;gBACrC,CAAC,CAAC,cAAc,KAAK,CAAC,CAAC,cAAc,CACrC,CAAC;QACH,KAAK,UAAU;YACd,OAAO,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,eAAe,KAAK,CAAC,CAAC,eAAe,CAAC;QACzE,KAAK,SAAS;YACb,OAAO,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAC1E,OAAO,CAAC,CAAC,CAAC;YACT,MAAM,UAAU,GAAU,CAAC,CAAC;YAC5B,MAAM,IAAI,6BAAoB,CAAC,6BAA6B,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAC3F,CAAC;IACF,CAAC;AACF,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { RunInDeprovisionTransaction, RunInProvisionTransaction } from './transaction';
2
+ import type { DesiredJob, ProvisionSummary } from './types';
3
+ import { type Tracer } from '../../observability/tracer';
4
+ export interface JobProvisioner<PScope, DScope = PScope> {
5
+ provision(scope: PScope, desired: DesiredJob[]): Promise<ProvisionSummary>;
6
+ deprovision(scope: DScope): Promise<{
7
+ removed: number;
8
+ }>;
9
+ }
10
+ export interface JobProvisionerDeps<PScope, DScope = PScope> {
11
+ provisionTransaction: (scope: PScope) => RunInProvisionTransaction;
12
+ deprovisionTransaction: (scope: DScope) => RunInDeprovisionTransaction;
13
+ tracer?: Tracer;
14
+ }
15
+ export declare function createJobProvisioner<PScope, DScope = PScope>(deps: JobProvisionerDeps<PScope, DScope>): JobProvisioner<PScope, DScope>;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createJobProvisioner = createJobProvisioner;
4
+ const provision_1 = require("./provision");
5
+ const provisioner_tracing_1 = require("../../observability/provisioner-tracing");
6
+ const tracer_1 = require("../../observability/tracer");
7
+ function createJobProvisioner(deps) {
8
+ const tracing = (0, provisioner_tracing_1.createProvisionerTracing)(deps.tracer ?? tracer_1.noopTracer);
9
+ return {
10
+ async provision(scope, desired) {
11
+ return await tracing.provision(async () => await (0, provision_1.provision)(deps.provisionTransaction(scope), desired));
12
+ },
13
+ async deprovision(scope) {
14
+ return await tracing.deprovision(async () => await (0, provision_1.deprovision)(deps.deprovisionTransaction(scope)));
15
+ },
16
+ };
17
+ }
18
+ //# sourceMappingURL=provisioner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provisioner.js","sourceRoot":"","sources":["../../../src/core/provisioning/provisioner.ts"],"names":[],"mappings":";;AAsCA,oDAgBC;AAtDD,2CAAqD;AAGrD,iFAAmF;AACnF,uDAAqE;AAkCrE,SAAgB,oBAAoB,CACnC,IAAwC;IAExC,MAAM,OAAO,GAAG,IAAA,8CAAwB,EAAC,IAAI,CAAC,MAAM,IAAI,mBAAU,CAAC,CAAC;IACpE,OAAO;QACN,KAAK,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO;YAC7B,OAAO,MAAM,OAAO,CAAC,SAAS,CAC7B,KAAK,IAAI,EAAE,CAAC,MAAM,IAAA,qBAAS,EAAC,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CACtE,CAAC;QACH,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,KAAK;YACtB,OAAO,MAAM,OAAO,CAAC,WAAW,CAC/B,KAAK,IAAI,EAAE,CAAC,MAAM,IAAA,uBAAW,EAAC,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,CACjE,CAAC;QACH,CAAC;KACD,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { DesiredJob, ExistingJob, ScheduleDefinition } from './types';
2
+ export interface ProvisionTransaction {
3
+ findExisting(): Promise<ExistingJob[]>;
4
+ insert(jobs: DesiredJob[]): Promise<number[]>;
5
+ redefine(jobId: number, schedule: ScheduleDefinition, nextRunAt: Date | null): Promise<void>;
6
+ withdrawPendingTasks(jobIds: number[]): Promise<void>;
7
+ deleteJobs(jobIds: number[]): Promise<void>;
8
+ }
9
+ export type RunInProvisionTransaction = <T>(work: (tx: ProvisionTransaction) => Promise<T>) => Promise<T>;
10
+ export interface DeprovisionTransaction {
11
+ deleteAll(): Promise<number>;
12
+ }
13
+ export type RunInDeprovisionTransaction = <T>(work: (tx: DeprovisionTransaction) => Promise<T>) => Promise<T>;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=transaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction.js","sourceRoot":"","sources":["../../../src/core/provisioning/transaction.ts"],"names":[],"mappings":""}
@@ -0,0 +1,43 @@
1
+ import type { RecurringCronUnit } from '@n8n/constants';
2
+ export type ScheduleDefinition = CronDefinition | RecurringCronDefinition | IntervalDefinition | OneOffDefinition;
3
+ export interface CronDefinition {
4
+ kind: 'cron';
5
+ cronExpression: string;
6
+ timezone: string | null;
7
+ }
8
+ export interface RecurringCronDefinition {
9
+ kind: 'recurring_cron';
10
+ cronExpression: string;
11
+ timezone: string | null;
12
+ recurrenceUnit: RecurringCronUnit;
13
+ recurrenceSize: number;
14
+ }
15
+ export interface IntervalDefinition {
16
+ kind: 'interval';
17
+ intervalSeconds: number;
18
+ }
19
+ export interface OneOffDefinition {
20
+ kind: 'one_off';
21
+ fireAt: Date;
22
+ }
23
+ export interface DesiredJob {
24
+ name: string;
25
+ schedule: ScheduleDefinition;
26
+ firstRunAt: Date | null;
27
+ }
28
+ export interface ExistingJob {
29
+ id: number;
30
+ name: string;
31
+ schedule: ScheduleDefinition;
32
+ hasClock: boolean;
33
+ }
34
+ export interface ProvisionedJob {
35
+ id: number;
36
+ name: string;
37
+ }
38
+ export interface ProvisionSummary {
39
+ inserted: ProvisionedJob[];
40
+ redefined: ProvisionedJob[];
41
+ unchanged: ProvisionedJob[];
42
+ removed: ProvisionedJob[];
43
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/provisioning/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ export { reap, DEFAULT_REAPER_OPTIONS, type ReapResult, type ReaperOptions, type ReaperTaskStore, type ExpiredLeaseRef, type ExpiredLeaseRow, type ReaperHooks, } from './reap';
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_REAPER_OPTIONS = exports.reap = void 0;
4
+ var reap_1 = require("./reap");
5
+ Object.defineProperty(exports, "reap", { enumerable: true, get: function () { return reap_1.reap; } });
6
+ Object.defineProperty(exports, "DEFAULT_REAPER_OPTIONS", { enumerable: true, get: function () { return reap_1.DEFAULT_REAPER_OPTIONS; } });
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/reaper/index.ts"],"names":[],"mappings":";;;AAAA,+BASgB;AARf,4FAAA,IAAI,OAAA;AACJ,8GAAA,sBAAsB,OAAA"}
@@ -1,13 +1,12 @@
1
- import type { Logger } from '@n8n/backend-common';
2
1
  export interface ReapResult {
3
2
  reclaimed: number;
4
3
  deadLettered: number;
5
4
  }
6
- interface ExpiredLeaseRef {
5
+ export interface ExpiredLeaseRef {
7
6
  id: string;
8
7
  claimedEpoch: number;
9
8
  }
10
- interface ExpiredLeaseRow {
9
+ export interface ExpiredLeaseRow {
11
10
  id: string;
12
11
  attempts: number;
13
12
  maxAttempts: number;
@@ -18,10 +17,16 @@ export interface ReaperTaskStore {
18
17
  reclaimExpired(ref: ExpiredLeaseRef, backoffMs: number, errorMessage: string): Promise<number>;
19
18
  deadLetterExpired(ref: ExpiredLeaseRef, errorMessage: string): Promise<number>;
20
19
  }
21
- export interface ReapDeps {
22
- store: ReaperTaskStore;
23
- logger: Logger;
20
+ export interface ReaperOptions {
24
21
  batchSize: number;
25
22
  }
26
- export declare function reap({ store, logger, batchSize }: ReapDeps): Promise<ReapResult>;
27
- export {};
23
+ export declare const DEFAULT_REAPER_OPTIONS: ReaperOptions;
24
+ export interface ReaperHooks {
25
+ onRowError?: (taskId: string, error: unknown) => void;
26
+ onDeadLetter?: (task: {
27
+ taskId: string;
28
+ attempts: number;
29
+ maxAttempts: number;
30
+ }) => void;
31
+ }
32
+ export declare function reap(store: ReaperTaskStore, options?: ReaperOptions, hooks?: ReaperHooks, signal?: AbortSignal): Promise<ReapResult>;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_REAPER_OPTIONS = void 0;
4
+ exports.reap = reap;
5
+ const backoff_1 = require("../executor/backoff");
6
+ const LEASE_EXPIRED_MESSAGE = 'Lease expired before completion';
7
+ exports.DEFAULT_REAPER_OPTIONS = {
8
+ batchSize: 100,
9
+ };
10
+ async function reap(store, options = exports.DEFAULT_REAPER_OPTIONS, hooks = {}, signal) {
11
+ const expired = await store.findExpiredLeases(options.batchSize);
12
+ if (expired.length === 0)
13
+ return { reclaimed: 0, deadLettered: 0 };
14
+ let reclaimed = 0;
15
+ let deadLettered = 0;
16
+ for (const task of expired) {
17
+ if (signal?.aborted === true) {
18
+ break;
19
+ }
20
+ try {
21
+ const nextAttempts = task.attempts + 1;
22
+ if (nextAttempts >= task.maxAttempts) {
23
+ const affected = await store.deadLetterExpired({ id: task.id, claimedEpoch: task.leaseEpoch }, LEASE_EXPIRED_MESSAGE);
24
+ deadLettered += affected;
25
+ if (affected > 0) {
26
+ try {
27
+ hooks.onDeadLetter?.({
28
+ taskId: task.id,
29
+ attempts: nextAttempts,
30
+ maxAttempts: task.maxAttempts,
31
+ });
32
+ }
33
+ catch {
34
+ }
35
+ }
36
+ }
37
+ else {
38
+ reclaimed += await store.reclaimExpired({ id: task.id, claimedEpoch: task.leaseEpoch }, (0, backoff_1.backoff)(nextAttempts), LEASE_EXPIRED_MESSAGE);
39
+ }
40
+ }
41
+ catch (error) {
42
+ try {
43
+ hooks.onRowError?.(task.id, error);
44
+ }
45
+ catch {
46
+ }
47
+ }
48
+ }
49
+ return { reclaimed, deadLettered };
50
+ }
51
+ //# sourceMappingURL=reap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reap.js","sourceRoot":"","sources":["../../../src/core/reaper/reap.ts"],"names":[],"mappings":";;;AAoFA,oBA+DC;AAnJD,iDAA8C;AAG9C,MAAM,qBAAqB,GAAG,iCAAiC,CAAC;AA4CnD,QAAA,sBAAsB,GAAkB;IACpD,SAAS,EAAE,GAAG;CACd,CAAC;AAmCK,KAAK,UAAU,IAAI,CACzB,KAAsB,EACtB,UAAyB,8BAAsB,EAC/C,QAAqB,EAAE,EACvB,MAAoB;IAEpB,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAGjE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;IAEnE,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,MAAM,EAAE,OAAO,KAAK,IAAI,EAAE,CAAC;YAC9B,MAAM;QACP,CAAC;QAGD,IAAI,CAAC;YAGJ,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;YACvC,IAAI,YAAY,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACtC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,iBAAiB,CAC7C,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,UAAU,EAAE,EAC9C,qBAAqB,CACrB,CAAC;gBACF,YAAY,IAAI,QAAQ,CAAC;gBAGzB,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;oBAClB,IAAI,CAAC;wBAGJ,KAAK,CAAC,YAAY,EAAE,CAAC;4BACpB,MAAM,EAAE,IAAI,CAAC,EAAE;4BACf,QAAQ,EAAE,YAAY;4BACtB,WAAW,EAAE,IAAI,CAAC,WAAW;yBAC7B,CAAC,CAAC;oBACJ,CAAC;oBAAC,MAAM,CAAC;oBAET,CAAC;gBACF,CAAC;YACF,CAAC;iBAAM,CAAC;gBACP,SAAS,IAAI,MAAM,KAAK,CAAC,cAAc,CACtC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,CAAC,UAAU,EAAE,EAC9C,IAAA,iBAAO,EAAC,YAAY,CAAC,EACrB,qBAAqB,CACrB,CAAC;YACH,CAAC;QACF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC;gBAGJ,KAAK,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YACpC,CAAC;YAAC,MAAM,CAAC;YAET,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AACpC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ScheduledJob } from '../types';
2
+ export declare function required<K extends keyof ScheduledJob>(job: ScheduledJob, key: K): NonNullable<ScheduledJob[K]>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.required = required;
4
+ const errors_1 = require("../errors");
5
+ function required(job, key) {
6
+ const value = job[key];
7
+ if (value === null || value === undefined) {
8
+ throw new errors_1.CorruptStorageRowError(`scheduled_job ${job.id} of kind '${job.kind}' is missing '${String(key)}'`);
9
+ }
10
+ return value;
11
+ }
12
+ //# sourceMappingURL=field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field.js","sourceRoot":"","sources":["../../../src/core/recurrence/field.ts"],"names":[],"mappings":";;AAaA,4BAWC;AAxBD,sCAAmD;AAanD,SAAgB,QAAQ,CACvB,GAAiB,EACjB,GAAM;IAEN,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC3C,MAAM,IAAI,+BAAsB,CAC/B,iBAAiB,GAAG,CAAC,EAAE,aAAa,GAAG,CAAC,IAAI,iBAAiB,MAAM,CAAC,GAAG,CAAC,GAAG,CAC3E,CAAC;IACH,CAAC;IACD,OAAO,KAAqC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { CronExpressionParser } from 'cron-parser';
2
+ import type { CronExpression } from 'n8n-workflow';
3
+ import type { CronSchedule, RecurringCronSchedule, ScheduledJob } from '../../types';
4
+ export type CronCursor = ReturnType<typeof CronExpressionParser.parse>;
5
+ export declare function validateCron(schedule: CronSchedule | RecurringCronSchedule): void;
6
+ export declare function resolvedTimezone(schedule: CronSchedule | RecurringCronSchedule): string;
7
+ export declare function parseCron(cronExpression: CronExpression, after: Date, timezone: string): CronCursor;
8
+ export declare function cronNextRun(schedule: CronSchedule | RecurringCronSchedule, after: Date): Date;
9
+ export declare function cronOccurrences(schedule: CronSchedule, first: Date): Generator<Date>;
10
+ export declare function resolveCron(job: ScheduledJob, defaultTimezone: string): CronSchedule;