@smthrs/flow 1.0.0-rc.8 → 1.0.0-rc.9

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 (39) hide show
  1. package/dist/cjs/Deadline.d.ts +5 -0
  2. package/dist/cjs/Deadline.d.ts.map +1 -1
  3. package/dist/cjs/Deadline.js +9 -1
  4. package/dist/cjs/Deadline.js.map +2 -2
  5. package/dist/cjs/DurableClock.d.ts +9 -10
  6. package/dist/cjs/DurableClock.d.ts.map +1 -1
  7. package/dist/cjs/DurableClock.js +19 -2
  8. package/dist/cjs/DurableClock.js.map +2 -2
  9. package/dist/cjs/Flow/make.d.ts +2 -1
  10. package/dist/cjs/Flow/make.d.ts.map +1 -1
  11. package/dist/cjs/Flow/make.js +3 -0
  12. package/dist/cjs/Flow/make.js.map +2 -2
  13. package/dist/cjs/HumanTask.d.ts.map +1 -1
  14. package/dist/cjs/HumanTask.js +2 -2
  15. package/dist/cjs/HumanTask.js.map +2 -2
  16. package/dist/esm/Deadline.d.ts +5 -0
  17. package/dist/esm/Deadline.d.ts.map +1 -1
  18. package/dist/esm/Deadline.js +13 -1
  19. package/dist/esm/Deadline.js.map +1 -1
  20. package/dist/esm/DurableClock.d.ts +9 -10
  21. package/dist/esm/DurableClock.d.ts.map +1 -1
  22. package/dist/esm/DurableClock.js +49 -2
  23. package/dist/esm/DurableClock.js.map +1 -1
  24. package/dist/esm/Flow/make.d.ts +2 -1
  25. package/dist/esm/Flow/make.d.ts.map +1 -1
  26. package/dist/esm/Flow/make.js +5 -0
  27. package/dist/esm/Flow/make.js.map +1 -1
  28. package/dist/esm/HumanTask.d.ts.map +1 -1
  29. package/dist/esm/HumanTask.js +5 -3
  30. package/dist/esm/HumanTask.js.map +1 -1
  31. package/docs/api.md +15 -11
  32. package/docs/guides/ask-a-person.md +5 -5
  33. package/docs/guides/inspect-the-plan.md +1 -1
  34. package/docs/reference/flow.md +4 -4
  35. package/package.json +7 -7
  36. package/src/Deadline.ts +17 -1
  37. package/src/DurableClock.ts +45 -2
  38. package/src/Flow/make.ts +7 -1
  39. package/src/HumanTask.ts +5 -3
@@ -11,6 +11,7 @@
11
11
  * @since 0.1.0
12
12
  */
13
13
 
14
+ import * as Clock from "effect/Clock"
14
15
  import type * as Crypto from "effect/Crypto"
15
16
  import * as Duration from "effect/Duration"
16
17
  import * as Effect from "effect/Effect"
@@ -56,6 +57,28 @@ const durationFromInput = (input: Duration.Input, field: string): Duration.Durat
56
57
  return parsed.value
57
58
  }
58
59
 
60
+ /**
61
+ * The latest due time a durable timer records, in epoch milliseconds: a store
62
+ * keeps a clock's due time as a safe integer.
63
+ *
64
+ * @private
65
+ */
66
+ const latestDueAtMs = Number.MAX_SAFE_INTEGER
67
+
68
+ /**
69
+ * Parses a clock's own duration, which a store adds to the current time and
70
+ * records, so it is at most {@link latestDueAtMs} milliseconds.
71
+ *
72
+ * @private
73
+ */
74
+ const clockDuration = (input: Duration.Input): Duration.Duration => {
75
+ const duration = durationFromInput(input, "duration")
76
+ if (Duration.toMillis(duration) > latestDueAtMs) {
77
+ throw new RangeError(`DurableClock: "duration" must be at most ${latestDueAtMs} milliseconds.`)
78
+ }
79
+ return duration
80
+ }
81
+
59
82
  const makeClock = (name: string, duration: Duration.Duration): DurableClock => ({
60
83
  [TypeId]: TypeId,
61
84
  name,
@@ -66,18 +89,27 @@ const makeClock = (name: string, duration: Duration.Duration): DurableClock => (
66
89
  /**
67
90
  * Declares one durable clock after validating its finite nonnegative duration.
68
91
  *
92
+ * Throws a `RangeError` for a malformed, infinite or negative duration, or one
93
+ * longer than `Number.MAX_SAFE_INTEGER` milliseconds: a durable store records
94
+ * a clock's due time as a safe integer.
95
+ *
69
96
  * @category constructors
70
97
  * @since 0.1.0
71
98
  */
72
99
  export const make = (options: {
73
100
  readonly name: string
74
101
  readonly duration: Duration.Input
75
- }): DurableClock => makeClock(options.name, durationFromInput(options.duration, "duration"))
102
+ }): DurableClock => makeClock(options.name, clockDuration(options.duration))
76
103
 
77
104
  /**
78
105
  * Waits inside a flow, using an in-memory action for durations at or
79
106
  * below the threshold and scheduling a durable clock for longer durations.
80
107
  *
108
+ * Throws a `RangeError` for the same durations {@link make} refuses, and for
109
+ * a malformed, infinite or negative threshold. A durable sleep that would
110
+ * wake after epoch millisecond `Number.MAX_SAFE_INTEGER` dies with a
111
+ * `RangeError` instead of arming its clock.
112
+ *
81
113
  * @category sleeping
82
114
  * @since 0.1.0
83
115
  */
@@ -99,7 +131,7 @@ export const sleep = (
99
131
  Crypto.Crypto | FlowRuntime | FlowInstance
100
132
  > => {
101
133
  const name = options.name
102
- const duration = durationFromInput(options.duration, "duration")
134
+ const duration = clockDuration(options.duration)
103
135
  const inMemoryThreshold = durationFromInput(
104
136
  options.inMemoryThreshold ?? defaultInMemoryThreshold,
105
137
  "inMemoryThreshold"
@@ -119,6 +151,17 @@ export const sleep = (
119
151
  })
120
152
  }
121
153
 
154
+ // A due time past the latest one a store records would fail the park
155
+ // write deep in the engine, so the sleep refuses it here by name.
156
+ const now = yield* Clock.currentTimeMillis
157
+ if (now + Duration.toMillis(duration) > latestDueAtMs) {
158
+ return yield* Effect.die(
159
+ new RangeError(
160
+ `DurableClock: "${name}" would wake after epoch millisecond ${latestDueAtMs}, ` +
161
+ `the latest due time a durable timer records.`
162
+ )
163
+ )
164
+ }
122
165
  const engine = yield* FlowRuntime
123
166
  const instance = yield* FlowInstance
124
167
  const clock = makeClock(name, duration)
package/src/Flow/make.ts CHANGED
@@ -99,7 +99,8 @@ interface MakeOptions<
99
99
  readonly maxRounds?: number | undefined
100
100
  /**
101
101
  * How long one execution may take, counted from its journaled first start.
102
- * A positive finite duration; see {@link Flow.deadline}.
102
+ * A positive finite duration of at most `Number.MAX_SAFE_INTEGER`
103
+ * milliseconds; see {@link Flow.deadline}.
103
104
  */
104
105
  readonly deadline?: Duration.Input | undefined
105
106
  readonly annotations?: Context.Context<never>
@@ -222,6 +223,11 @@ export const make: {
222
223
  ) {
223
224
  throw new RangeError(`Flow.make: "${tag}" deadline must be a positive finite duration`)
224
225
  }
226
+ // The durable clock that enforces a deadline is due at its start plus the
227
+ // deadline, and a store records that due time as a safe integer.
228
+ if (deadline !== undefined && Duration.toMillis(deadline) > Number.MAX_SAFE_INTEGER) {
229
+ throw new RangeError(`Flow.make: "${tag}" deadline must be at most ${Number.MAX_SAFE_INTEGER} ms`)
230
+ }
225
231
  // Captured here, where the stack still names the author's file, and carried
226
232
  // as a non-enumerable property so no digest can see it
227
233
  // (`internal/DeclarationSite.ts`).
package/src/HumanTask.ts CHANGED
@@ -680,7 +680,9 @@ const budgetOf = (payload: Payload): Effect.Effect<number, HumanTaskFailed> => {
680
680
  const deadlineOf = (payload: Payload): Effect.Effect<Duration.Duration | undefined, HumanTaskFailed> => {
681
681
  const timeoutMs = payload.timeoutMs
682
682
  if (timeoutMs === undefined) return Effect.succeed(undefined)
683
- return Number.isFinite(timeoutMs) && timeoutMs >= 0
683
+ // The timeout clock is due at its start plus the deadline, and a durable
684
+ // store records that due time as a safe integer.
685
+ return Number.isFinite(timeoutMs) && timeoutMs >= 0 && timeoutMs <= Number.MAX_SAFE_INTEGER
684
686
  ? Effect.succeed(Duration.millis(timeoutMs))
685
687
  : Effect.fail(
686
688
  failed(
@@ -688,8 +690,8 @@ const deadlineOf = (payload: Payload): Effect.Effect<Duration.Duration | undefin
688
690
  "request_invalid",
689
691
  0,
690
692
  [],
691
- `${tag} was called with timeoutMs ${timeoutMs}. A deadline is a finite number of ` +
692
- "milliseconds that is not negative; the question would be over before it was asked, or " +
693
+ `${tag} was called with timeoutMs ${timeoutMs}. A deadline is a number of milliseconds ` +
694
+ `from 0 to ${Number.MAX_SAFE_INTEGER}; the question would be over before it was asked, or ` +
693
695
  "never over at all."
694
696
  )
695
697
  )