effect 3.10.19 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/dist/cjs/BigDecimal.js +125 -24
  2. package/dist/cjs/BigDecimal.js.map +1 -1
  3. package/dist/cjs/Channel.js +44 -4
  4. package/dist/cjs/Channel.js.map +1 -1
  5. package/dist/cjs/Config.js +8 -1
  6. package/dist/cjs/Config.js.map +1 -1
  7. package/dist/cjs/Context.js +26 -1
  8. package/dist/cjs/Context.js.map +1 -1
  9. package/dist/cjs/Cron.js +75 -67
  10. package/dist/cjs/Cron.js.map +1 -1
  11. package/dist/cjs/DateTime.js +114 -664
  12. package/dist/cjs/DateTime.js.map +1 -1
  13. package/dist/cjs/Effect.js +82 -4
  14. package/dist/cjs/Effect.js.map +1 -1
  15. package/dist/cjs/Inspectable.js +8 -4
  16. package/dist/cjs/Inspectable.js.map +1 -1
  17. package/dist/cjs/JSONSchema.js.map +1 -1
  18. package/dist/cjs/Micro.js +1099 -1072
  19. package/dist/cjs/Micro.js.map +1 -1
  20. package/dist/cjs/STM.js.map +1 -1
  21. package/dist/cjs/Schema.js +57 -8
  22. package/dist/cjs/Schema.js.map +1 -1
  23. package/dist/cjs/Sink.js +9 -1
  24. package/dist/cjs/Sink.js.map +1 -1
  25. package/dist/cjs/Stream.js +25 -7
  26. package/dist/cjs/Stream.js.map +1 -1
  27. package/dist/cjs/Utils.js +7 -1
  28. package/dist/cjs/Utils.js.map +1 -1
  29. package/dist/cjs/internal/channel/channelExecutor.js +5 -9
  30. package/dist/cjs/internal/channel/channelExecutor.js.map +1 -1
  31. package/dist/cjs/internal/channel.js +156 -130
  32. package/dist/cjs/internal/channel.js.map +1 -1
  33. package/dist/cjs/internal/config.js +13 -4
  34. package/dist/cjs/internal/config.js.map +1 -1
  35. package/dist/cjs/internal/context.js +46 -3
  36. package/dist/cjs/internal/context.js.map +1 -1
  37. package/dist/cjs/internal/dateTime.js +747 -0
  38. package/dist/cjs/internal/dateTime.js.map +1 -0
  39. package/dist/cjs/internal/fiberRuntime.js +34 -11
  40. package/dist/cjs/internal/fiberRuntime.js.map +1 -1
  41. package/dist/cjs/internal/groupBy.js +9 -3
  42. package/dist/cjs/internal/groupBy.js.map +1 -1
  43. package/dist/cjs/internal/layer.js +1 -1
  44. package/dist/cjs/internal/layer.js.map +1 -1
  45. package/dist/cjs/internal/mailbox.js +1 -1
  46. package/dist/cjs/internal/mailbox.js.map +1 -1
  47. package/dist/cjs/internal/sink.js +25 -21
  48. package/dist/cjs/internal/sink.js.map +1 -1
  49. package/dist/cjs/internal/stream.js +70 -71
  50. package/dist/cjs/internal/stream.js.map +1 -1
  51. package/dist/cjs/internal/version.js +1 -1
  52. package/dist/cjs/internal/version.js.map +1 -1
  53. package/dist/dts/BigDecimal.d.ts +56 -1
  54. package/dist/dts/BigDecimal.d.ts.map +1 -1
  55. package/dist/dts/Channel.d.ts +66 -5
  56. package/dist/dts/Channel.d.ts.map +1 -1
  57. package/dist/dts/Config.d.ts +23 -1
  58. package/dist/dts/Config.d.ts.map +1 -1
  59. package/dist/dts/Context.d.ts +111 -0
  60. package/dist/dts/Context.d.ts.map +1 -1
  61. package/dist/dts/Cron.d.ts +15 -6
  62. package/dist/dts/Cron.d.ts.map +1 -1
  63. package/dist/dts/DateTime.d.ts +40 -49
  64. package/dist/dts/DateTime.d.ts.map +1 -1
  65. package/dist/dts/Effect.d.ts +88 -1
  66. package/dist/dts/Effect.d.ts.map +1 -1
  67. package/dist/dts/Inspectable.d.ts.map +1 -1
  68. package/dist/dts/JSONSchema.d.ts +1 -0
  69. package/dist/dts/JSONSchema.d.ts.map +1 -1
  70. package/dist/dts/Micro.d.ts +875 -872
  71. package/dist/dts/Micro.d.ts.map +1 -1
  72. package/dist/dts/STM.d.ts +2 -0
  73. package/dist/dts/STM.d.ts.map +1 -1
  74. package/dist/dts/Schema.d.ts +32 -0
  75. package/dist/dts/Schema.d.ts.map +1 -1
  76. package/dist/dts/Sink.d.ts +8 -0
  77. package/dist/dts/Sink.d.ts.map +1 -1
  78. package/dist/dts/Stream.d.ts +50 -32
  79. package/dist/dts/Stream.d.ts.map +1 -1
  80. package/dist/dts/Utils.d.ts +4 -0
  81. package/dist/dts/Utils.d.ts.map +1 -1
  82. package/dist/dts/internal/context.d.ts +1 -1
  83. package/dist/dts/internal/context.d.ts.map +1 -1
  84. package/dist/dts/internal/dateTime.d.ts +2 -0
  85. package/dist/dts/internal/dateTime.d.ts.map +1 -0
  86. package/dist/dts/internal/fiberRuntime.d.ts.map +1 -1
  87. package/dist/dts/internal/stream.d.ts.map +1 -1
  88. package/dist/esm/BigDecimal.js +119 -20
  89. package/dist/esm/BigDecimal.js.map +1 -1
  90. package/dist/esm/Channel.js +42 -2
  91. package/dist/esm/Channel.js.map +1 -1
  92. package/dist/esm/Config.js +7 -0
  93. package/dist/esm/Config.js.map +1 -1
  94. package/dist/esm/Context.js +25 -0
  95. package/dist/esm/Context.js.map +1 -1
  96. package/dist/esm/Cron.js +75 -67
  97. package/dist/esm/Cron.js.map +1 -1
  98. package/dist/esm/DateTime.js +112 -627
  99. package/dist/esm/DateTime.js.map +1 -1
  100. package/dist/esm/Effect.js +77 -0
  101. package/dist/esm/Effect.js.map +1 -1
  102. package/dist/esm/Inspectable.js +8 -4
  103. package/dist/esm/Inspectable.js.map +1 -1
  104. package/dist/esm/JSONSchema.js.map +1 -1
  105. package/dist/esm/Micro.js +1077 -1047
  106. package/dist/esm/Micro.js.map +1 -1
  107. package/dist/esm/STM.js.map +1 -1
  108. package/dist/esm/Schema.js +54 -0
  109. package/dist/esm/Schema.js.map +1 -1
  110. package/dist/esm/Sink.js +8 -0
  111. package/dist/esm/Sink.js.map +1 -1
  112. package/dist/esm/Stream.js +23 -5
  113. package/dist/esm/Stream.js.map +1 -1
  114. package/dist/esm/Utils.js +5 -0
  115. package/dist/esm/Utils.js.map +1 -1
  116. package/dist/esm/internal/channel/channelExecutor.js +5 -7
  117. package/dist/esm/internal/channel/channelExecutor.js.map +1 -1
  118. package/dist/esm/internal/channel.js +152 -129
  119. package/dist/esm/internal/channel.js.map +1 -1
  120. package/dist/esm/internal/config.js +11 -3
  121. package/dist/esm/internal/config.js.map +1 -1
  122. package/dist/esm/internal/context.js +42 -2
  123. package/dist/esm/internal/context.js.map +1 -1
  124. package/dist/esm/internal/dateTime.js +704 -0
  125. package/dist/esm/internal/dateTime.js.map +1 -0
  126. package/dist/esm/internal/fiberRuntime.js +31 -9
  127. package/dist/esm/internal/fiberRuntime.js.map +1 -1
  128. package/dist/esm/internal/groupBy.js +9 -3
  129. package/dist/esm/internal/groupBy.js.map +1 -1
  130. package/dist/esm/internal/layer.js +1 -1
  131. package/dist/esm/internal/layer.js.map +1 -1
  132. package/dist/esm/internal/mailbox.js +1 -1
  133. package/dist/esm/internal/mailbox.js.map +1 -1
  134. package/dist/esm/internal/sink.js +23 -20
  135. package/dist/esm/internal/sink.js.map +1 -1
  136. package/dist/esm/internal/stream.js +66 -69
  137. package/dist/esm/internal/stream.js.map +1 -1
  138. package/dist/esm/internal/version.js +1 -1
  139. package/dist/esm/internal/version.js.map +1 -1
  140. package/package.json +1 -1
  141. package/src/BigDecimal.ts +131 -21
  142. package/src/Channel.ts +81 -5
  143. package/src/Config.ts +24 -1
  144. package/src/Context.ts +119 -0
  145. package/src/Cron.ts +85 -68
  146. package/src/DateTime.ts +155 -757
  147. package/src/Effect.ts +340 -1
  148. package/src/Inspectable.ts +11 -7
  149. package/src/JSONSchema.ts +1 -0
  150. package/src/Micro.ts +2005 -1757
  151. package/src/STM.ts +2 -0
  152. package/src/Schema.ts +60 -0
  153. package/src/Sink.ts +11 -0
  154. package/src/Stream.ts +55 -44
  155. package/src/Utils.ts +8 -0
  156. package/src/internal/channel/channelExecutor.ts +37 -33
  157. package/src/internal/channel.ts +504 -467
  158. package/src/internal/config.ts +18 -6
  159. package/src/internal/context.ts +56 -4
  160. package/src/internal/dateTime.ts +1126 -0
  161. package/src/internal/fiberRuntime.ts +35 -16
  162. package/src/internal/groupBy.ts +13 -22
  163. package/src/internal/layer.ts +5 -8
  164. package/src/internal/mailbox.ts +6 -4
  165. package/src/internal/sink.ts +55 -35
  166. package/src/internal/stream.ts +299 -299
  167. package/src/internal/version.ts +1 -1
package/src/Cron.ts CHANGED
@@ -2,13 +2,16 @@
2
2
  * @since 2.0.0
3
3
  */
4
4
  import * as Arr from "./Array.js"
5
+ import type * as DateTime from "./DateTime.js"
5
6
  import * as Either from "./Either.js"
6
7
  import * as Equal from "./Equal.js"
7
8
  import * as equivalence from "./Equivalence.js"
8
9
  import { dual, pipe } from "./Function.js"
9
10
  import * as Hash from "./Hash.js"
10
11
  import { format, type Inspectable, NodeInspectSymbol } from "./Inspectable.js"
12
+ import * as dateTime from "./internal/dateTime.js"
11
13
  import * as N from "./Number.js"
14
+ import * as Option from "./Option.js"
12
15
  import { type Pipeable, pipeArguments } from "./Pipeable.js"
13
16
  import { hasProperty } from "./Predicate.js"
14
17
  import * as String from "./String.js"
@@ -32,6 +35,7 @@ export type TypeId = typeof TypeId
32
35
  */
33
36
  export interface Cron extends Pipeable, Equal.Equal, Inspectable {
34
37
  readonly [TypeId]: TypeId
38
+ readonly tz: Option.Option<DateTime.TimeZone>
35
39
  readonly minutes: ReadonlySet<number>
36
40
  readonly hours: ReadonlySet<number>
37
41
  readonly days: ReadonlySet<number>
@@ -39,14 +43,15 @@ export interface Cron extends Pipeable, Equal.Equal, Inspectable {
39
43
  readonly weekdays: ReadonlySet<number>
40
44
  }
41
45
 
42
- const CronProto: Omit<Cron, "minutes" | "hours" | "days" | "months" | "weekdays"> = {
46
+ const CronProto: Omit<Cron, "minutes" | "hours" | "days" | "months" | "weekdays" | "tz"> = {
43
47
  [TypeId]: TypeId,
44
48
  [Equal.symbol](this: Cron, that: unknown) {
45
49
  return isCron(that) && equals(this, that)
46
50
  },
47
51
  [Hash.symbol](this: Cron): number {
48
52
  return pipe(
49
- Hash.array(Arr.fromIterable(this.minutes)),
53
+ Hash.hash(this.tz),
54
+ Hash.combine(Hash.array(Arr.fromIterable(this.minutes))),
50
55
  Hash.combine(Hash.array(Arr.fromIterable(this.hours))),
51
56
  Hash.combine(Hash.array(Arr.fromIterable(this.days))),
52
57
  Hash.combine(Hash.array(Arr.fromIterable(this.months))),
@@ -60,6 +65,7 @@ const CronProto: Omit<Cron, "minutes" | "hours" | "days" | "months" | "weekdays"
60
65
  toJSON(this: Cron) {
61
66
  return {
62
67
  _id: "Cron",
68
+ tz: this.tz,
63
69
  minutes: Arr.fromIterable(this.minutes),
64
70
  hours: Arr.fromIterable(this.hours),
65
71
  days: Arr.fromIterable(this.days),
@@ -86,7 +92,7 @@ const CronProto: Omit<Cron, "minutes" | "hours" | "days" | "months" | "weekdays"
86
92
  export const isCron = (u: unknown): u is Cron => hasProperty(u, TypeId)
87
93
 
88
94
  /**
89
- * Creates a `Cron` instance from.
95
+ * Creates a `Cron` instance.
90
96
  *
91
97
  * @param constraints - The cron constraints.
92
98
  *
@@ -98,6 +104,7 @@ export const make = ({
98
104
  hours,
99
105
  minutes,
100
106
  months,
107
+ tz,
101
108
  weekdays
102
109
  }: {
103
110
  readonly minutes: Iterable<number>
@@ -105,6 +112,7 @@ export const make = ({
105
112
  readonly days: Iterable<number>
106
113
  readonly months: Iterable<number>
107
114
  readonly weekdays: Iterable<number>
115
+ readonly tz?: DateTime.TimeZone | undefined
108
116
  }): Cron => {
109
117
  const o: Mutable<Cron> = Object.create(CronProto)
110
118
  o.minutes = new Set(Arr.sort(minutes, N.Order))
@@ -112,6 +120,7 @@ export const make = ({
112
120
  o.days = new Set(Arr.sort(days, N.Order))
113
121
  o.months = new Set(Arr.sort(months, N.Order))
114
122
  o.weekdays = new Set(Arr.sort(weekdays, N.Order))
123
+ o.tz = Option.fromNullable(tz)
115
124
  return o
116
125
  }
117
126
 
@@ -186,7 +195,7 @@ export const isParseError = (u: unknown): u is ParseError => hasProperty(u, Pars
186
195
  * @since 2.0.0
187
196
  * @category constructors
188
197
  */
189
- export const parse = (cron: string): Either.Either<Cron, ParseError> => {
198
+ export const parse = (cron: string, tz?: DateTime.TimeZone): Either.Either<Cron, ParseError> => {
190
199
  const segments = cron.split(" ").filter(String.isNonEmpty)
191
200
  if (segments.length !== 5) {
192
201
  return Either.left(ParseError(`Invalid number of segments in cron expression`, cron))
@@ -199,12 +208,14 @@ export const parse = (cron: string): Either.Either<Cron, ParseError> => {
199
208
  days: parseSegment(days, dayOptions),
200
209
  months: parseSegment(months, monthOptions),
201
210
  weekdays: parseSegment(weekdays, weekdayOptions)
202
- }).pipe(Either.map((segments) => make(segments)))
211
+ }).pipe(Either.map((segments) => make({ ...segments, tz })))
203
212
  }
204
213
 
205
214
  /**
206
215
  * Checks if a given `Date` falls within an active `Cron` time window.
207
216
  *
217
+ * @throws `IllegalArgumentException` if the given `DateTime.Input` is invalid.
218
+ *
208
219
  * @param cron - The `Cron` instance.
209
220
  * @param date - The `Date` to check against.
210
221
  *
@@ -219,39 +230,36 @@ export const parse = (cron: string): Either.Either<Cron, ParseError> => {
219
230
  *
220
231
  * @since 2.0.0
221
232
  */
222
- export const match = (cron: Cron, date: Date): boolean => {
223
- const { days, hours, minutes, months, weekdays } = cron
233
+ export const match = (cron: Cron, date: DateTime.DateTime.Input): boolean => {
234
+ const zoned = dateTime.unsafeMakeZoned(date)
235
+ const adjusted = Option.isSome(cron.tz) ? dateTime.setZone(zoned, cron.tz.value) : zoned
236
+ const parts = dateTime.toParts(adjusted)
224
237
 
225
- const minute = date.getMinutes()
226
- if (minutes.size !== 0 && !minutes.has(minute)) {
238
+ if (cron.minutes.size !== 0 && !cron.minutes.has(parts.minutes)) {
227
239
  return false
228
240
  }
229
241
 
230
- const hour = date.getHours()
231
- if (hours.size !== 0 && !hours.has(hour)) {
242
+ if (cron.hours.size !== 0 && !cron.hours.has(parts.hours)) {
232
243
  return false
233
244
  }
234
245
 
235
- const month = date.getMonth() + 1
236
- if (months.size !== 0 && !months.has(month)) {
246
+ if (cron.months.size !== 0 && !cron.months.has(parts.month)) {
237
247
  return false
238
248
  }
239
249
 
240
- if (days.size === 0 && weekdays.size === 0) {
250
+ if (cron.days.size === 0 && cron.weekdays.size === 0) {
241
251
  return true
242
252
  }
243
253
 
244
- const day = date.getDate()
245
- if (weekdays.size === 0) {
246
- return days.has(day)
254
+ if (cron.weekdays.size === 0) {
255
+ return cron.days.has(parts.day)
247
256
  }
248
257
 
249
- const weekday = date.getDay()
250
- if (days.size === 0) {
251
- return weekdays.has(weekday)
258
+ if (cron.days.size === 0) {
259
+ return cron.weekdays.has(parts.weekDay)
252
260
  }
253
261
 
254
- return days.has(day) || weekdays.has(weekday)
262
+ return cron.days.has(parts.day) || cron.weekdays.has(parts.weekDay)
255
263
  }
256
264
 
257
265
  /**
@@ -259,6 +267,9 @@ export const match = (cron: Cron, date: Date): boolean => {
259
267
  *
260
268
  * Uses the current time as a starting point if no value is provided for `now`.
261
269
  *
270
+ * @throws `IllegalArgumentException` if the given `DateTime.Input` is invalid.
271
+ * @throws `Error` if the next run date cannot be found within 10,000 iterations.
272
+ *
262
273
  * @example
263
274
  * ```ts
264
275
  * import { Cron, Either } from "effect"
@@ -273,7 +284,7 @@ export const match = (cron: Cron, date: Date): boolean => {
273
284
  *
274
285
  * @since 2.0.0
275
286
  */
276
- export const next = (cron: Cron, now?: Date): Date => {
287
+ export const next = (cron: Cron, now?: DateTime.DateTime.Input): Date => {
277
288
  const { days, hours, minutes, months, weekdays } = cron
278
289
 
279
290
  const restrictMinutes = minutes.size !== 0
@@ -282,61 +293,67 @@ export const next = (cron: Cron, now?: Date): Date => {
282
293
  const restrictMonths = months.size !== 0
283
294
  const restrictWeekdays = weekdays.size !== 0
284
295
 
285
- const current = now ? new Date(now.getTime()) : new Date()
286
- // Increment by one minute to ensure we don't match the current date.
287
- current.setMinutes(current.getMinutes() + 1)
288
- current.setSeconds(0)
289
- current.setMilliseconds(0)
290
-
291
- // Only search 8 years into the future.
292
- const limit = new Date(current).setFullYear(current.getFullYear() + 8)
293
- while (current.getTime() <= limit) {
294
- if (restrictMonths && !months.has(current.getMonth() + 1)) {
295
- current.setMonth(current.getMonth() + 1)
296
- current.setDate(1)
297
- current.setHours(0)
298
- current.setMinutes(0)
299
- continue
300
- }
301
-
302
- if (restrictDays && restrictWeekdays) {
303
- if (!days.has(current.getDate()) && !weekdays.has(current.getDay())) {
304
- current.setDate(current.getDate() + 1)
305
- current.setHours(0)
306
- current.setMinutes(0)
296
+ // TODO: This is unsafe.
297
+ const zoned = dateTime.unsafeMakeZoned(now ?? new Date())
298
+ const adjusted = Option.isSome(cron.tz) ? dateTime.setZone(zoned, cron.tz.value) : zoned
299
+
300
+ // TODO: This algorithm can be optimized to avoid some unnecessary iterations.
301
+ const result = dateTime.mutate(adjusted, (current) => {
302
+ // Increment by one minute to ensure we don't match the current date.
303
+ current.setUTCMinutes(current.getUTCMinutes() + 1)
304
+ current.setUTCSeconds(0)
305
+ current.setUTCMilliseconds(0)
306
+
307
+ for (let i = 0; i < 10_000; i++) {
308
+ if (restrictMonths && !months.has(current.getUTCMonth() + 1)) {
309
+ current.setUTCMonth(current.getUTCMonth() + 1)
310
+ current.setUTCDate(1)
311
+ current.setUTCHours(0)
312
+ current.setUTCMinutes(0)
307
313
  continue
308
314
  }
309
- } else if (restrictDays) {
310
- if (!days.has(current.getDate())) {
311
- current.setDate(current.getDate() + 1)
312
- current.setHours(0)
313
- current.setMinutes(0)
314
- continue
315
+
316
+ if (restrictDays && restrictWeekdays) {
317
+ if (!days.has(current.getUTCDate()) && !weekdays.has(current.getUTCDay())) {
318
+ current.setUTCDate(current.getUTCDate() + 1)
319
+ current.setUTCHours(0)
320
+ current.setUTCMinutes(0)
321
+ continue
322
+ }
323
+ } else if (restrictDays) {
324
+ if (!days.has(current.getUTCDate())) {
325
+ current.setUTCDate(current.getUTCDate() + 1)
326
+ current.setUTCHours(0)
327
+ current.setUTCMinutes(0)
328
+ continue
329
+ }
330
+ } else if (restrictWeekdays) {
331
+ if (!weekdays.has(current.getUTCDay())) {
332
+ current.setUTCDate(current.getUTCDate() + 1)
333
+ current.setUTCHours(0)
334
+ current.setUTCMinutes(0)
335
+ continue
336
+ }
315
337
  }
316
- } else if (restrictWeekdays) {
317
- if (!weekdays.has(current.getDay())) {
318
- current.setDate(current.getDate() + 1)
319
- current.setHours(0)
320
- current.setMinutes(0)
338
+
339
+ if (restrictHours && !hours.has(current.getUTCHours())) {
340
+ current.setUTCHours(current.getUTCHours() + 1)
341
+ current.setUTCMinutes(0)
321
342
  continue
322
343
  }
323
- }
324
344
 
325
- if (restrictHours && !hours.has(current.getHours())) {
326
- current.setHours(current.getHours() + 1)
327
- current.setMinutes(0)
328
- continue
329
- }
345
+ if (restrictMinutes && !minutes.has(current.getUTCMinutes())) {
346
+ current.setUTCMinutes(current.getUTCMinutes() + 1)
347
+ continue
348
+ }
330
349
 
331
- if (restrictMinutes && !minutes.has(current.getMinutes())) {
332
- current.setMinutes(current.getMinutes() + 1)
333
- continue
350
+ return
334
351
  }
335
352
 
336
- return current
337
- }
353
+ throw new Error("Unable to find next cron date")
354
+ })
338
355
 
339
- throw new Error("Unable to find next cron date")
356
+ return dateTime.toDateUtc(result)
340
357
  }
341
358
 
342
359
  /**
@@ -347,7 +364,7 @@ export const next = (cron: Cron, now?: Date): Date => {
347
364
  *
348
365
  * @since 2.0.0
349
366
  */
350
- export const sequence = function*(cron: Cron, now?: Date): IterableIterator<Date> {
367
+ export const sequence = function*(cron: Cron, now?: DateTime.DateTime.Input): IterableIterator<Date> {
351
368
  while (true) {
352
369
  yield now = next(cron, now)
353
370
  }