effect 3.21.3 → 3.21.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Cron.js +96 -52
- package/dist/cjs/Cron.js.map +1 -1
- package/dist/cjs/Graph.js +62 -13
- package/dist/cjs/Graph.js.map +1 -1
- package/dist/cjs/internal/schedule.js +3 -0
- package/dist/cjs/internal/schedule.js.map +1 -1
- package/dist/cjs/internal/version.js +1 -1
- package/dist/dts/Cron.d.ts +1 -0
- package/dist/dts/Cron.d.ts.map +1 -1
- package/dist/dts/Graph.d.ts.map +1 -1
- package/dist/esm/Cron.js +96 -52
- package/dist/esm/Cron.js.map +1 -1
- package/dist/esm/Graph.js +62 -13
- package/dist/esm/Graph.js.map +1 -1
- package/dist/esm/internal/schedule.js +3 -0
- package/dist/esm/internal/schedule.js.map +1 -1
- package/dist/esm/internal/version.js +1 -1
- package/package.json +1 -1
- package/src/Cron.ts +107 -58
- package/src/Graph.ts +74 -13
- package/src/internal/schedule.ts +8 -0
- package/src/internal/version.ts +1 -1
package/package.json
CHANGED
package/src/Cron.ts
CHANGED
|
@@ -44,6 +44,8 @@ export interface Cron extends Pipeable, Equal.Equal, Inspectable {
|
|
|
44
44
|
readonly months: ReadonlySet<number>
|
|
45
45
|
readonly weekdays: ReadonlySet<number>
|
|
46
46
|
/** @internal */
|
|
47
|
+
readonly and: boolean
|
|
48
|
+
/** @internal */
|
|
47
49
|
readonly first: {
|
|
48
50
|
readonly second: number
|
|
49
51
|
readonly minute: number
|
|
@@ -89,6 +91,7 @@ const CronProto = {
|
|
|
89
91
|
[Hash.symbol](this: Cron): number {
|
|
90
92
|
return pipe(
|
|
91
93
|
Hash.hash(this.tz),
|
|
94
|
+
Hash.combine(Hash.hash(this.and)),
|
|
92
95
|
Hash.combine(Hash.array(Arr.fromIterable(this.seconds))),
|
|
93
96
|
Hash.combine(Hash.array(Arr.fromIterable(this.minutes))),
|
|
94
97
|
Hash.combine(Hash.array(Arr.fromIterable(this.hours))),
|
|
@@ -142,6 +145,7 @@ export const make = (values: {
|
|
|
142
145
|
readonly days: Iterable<number>
|
|
143
146
|
readonly months: Iterable<number>
|
|
144
147
|
readonly weekdays: Iterable<number>
|
|
148
|
+
readonly and?: boolean | undefined
|
|
145
149
|
readonly tz?: DateTime.TimeZone | undefined
|
|
146
150
|
}): Cron => {
|
|
147
151
|
const o: Mutable<Cron> = Object.create(CronProto)
|
|
@@ -151,6 +155,7 @@ export const make = (values: {
|
|
|
151
155
|
o.days = new Set(Arr.sort(values.days, N.Order))
|
|
152
156
|
o.months = new Set(Arr.sort(values.months, N.Order))
|
|
153
157
|
o.weekdays = new Set(Arr.sort(values.weekdays, N.Order))
|
|
158
|
+
o.and = values.and === true
|
|
154
159
|
o.tz = Option.fromNullable(values.tz)
|
|
155
160
|
|
|
156
161
|
const seconds = Array.from(o.seconds)
|
|
@@ -291,7 +296,7 @@ export const isParseError = (u: unknown): u is ParseError => hasProperty(u, Pars
|
|
|
291
296
|
* @category constructors
|
|
292
297
|
*/
|
|
293
298
|
export const parse = (cron: string, tz?: DateTime.TimeZone | string): Either.Either<Cron, ParseError> => {
|
|
294
|
-
const segments = cron.split(
|
|
299
|
+
const segments = cron.trim().split(/\s+/).filter(String.isNonEmpty)
|
|
295
300
|
if (segments.length !== 5 && segments.length !== 6) {
|
|
296
301
|
return Either.left(
|
|
297
302
|
new ParseError({
|
|
@@ -322,7 +327,18 @@ export const parse = (cron: string, tz?: DateTime.TimeZone | string): Either.Eit
|
|
|
322
327
|
days: parseSegment(days, dayOptions),
|
|
323
328
|
months: parseSegment(months, monthOptions),
|
|
324
329
|
weekdays: parseSegment(weekdays, weekdayOptions)
|
|
325
|
-
}).pipe(Either.map(
|
|
330
|
+
}).pipe(Either.map(({ days, hours, minutes, months, seconds, tz, weekdays }) =>
|
|
331
|
+
make({
|
|
332
|
+
tz,
|
|
333
|
+
seconds: seconds.values,
|
|
334
|
+
minutes: minutes.values,
|
|
335
|
+
hours: hours.values,
|
|
336
|
+
days: days.values,
|
|
337
|
+
months: months.values,
|
|
338
|
+
weekdays: weekdays.values,
|
|
339
|
+
and: (days.wildcard || weekdays.wildcard) && days.values.size !== 0 && weekdays.values.size !== 0
|
|
340
|
+
})
|
|
341
|
+
))
|
|
326
342
|
}
|
|
327
343
|
|
|
328
344
|
/**
|
|
@@ -410,6 +426,11 @@ export const match = (cron: Cron, date: DateTime.DateTime.Input): boolean => {
|
|
|
410
426
|
return true
|
|
411
427
|
}
|
|
412
428
|
|
|
429
|
+
if (cron.and) {
|
|
430
|
+
return (cron.days.size === 0 || cron.days.has(parts.day)) &&
|
|
431
|
+
(cron.weekdays.size === 0 || cron.weekdays.has(parts.weekDay))
|
|
432
|
+
}
|
|
433
|
+
|
|
413
434
|
if (cron.weekdays.size === 0) {
|
|
414
435
|
return cron.days.has(parts.day)
|
|
415
436
|
}
|
|
@@ -553,48 +574,60 @@ const stepCron = (cron: Cron, startFrom: DateTime.DateTime.Input | undefined, di
|
|
|
553
574
|
}
|
|
554
575
|
|
|
555
576
|
if (cron.weekdays.size !== 0 || cron.days.size !== 0) {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
? currentWeekday - 7 + boundary.weekday
|
|
565
|
-
: 7 - currentWeekday + boundary.weekday
|
|
566
|
-
} else {
|
|
567
|
-
a = nextWeekday - currentWeekday
|
|
577
|
+
if (cron.and) {
|
|
578
|
+
const matchesDay = cron.days.size === 0 || cron.days.has(current.getUTCDate())
|
|
579
|
+
const matchesWeekday = cron.weekdays.size === 0 || cron.weekdays.has(current.getUTCDay())
|
|
580
|
+
if (!matchesDay || !matchesWeekday) {
|
|
581
|
+
current.setUTCDate(current.getUTCDate() + tick)
|
|
582
|
+
current.setUTCHours(boundary.hour, boundary.minute, boundary.second)
|
|
583
|
+
adjustDst(current)
|
|
584
|
+
continue
|
|
568
585
|
}
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
if (
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
const prevMonthDays = daysInMonth(
|
|
581
|
-
new Date(Date.UTC(current.getUTCFullYear(), current.getUTCMonth(), 0))
|
|
582
|
-
)
|
|
583
|
-
b = -(currentDay + (prevMonthDays - boundary.day))
|
|
586
|
+
} else {
|
|
587
|
+
let a: number = prev ? -Infinity : Infinity
|
|
588
|
+
let b: number = prev ? -Infinity : Infinity
|
|
589
|
+
|
|
590
|
+
if (cron.weekdays.size !== 0) {
|
|
591
|
+
const currentWeekday = current.getUTCDay()
|
|
592
|
+
const nextWeekday = table.weekday[currentWeekday]
|
|
593
|
+
if (nextWeekday === undefined) {
|
|
594
|
+
a = prev
|
|
595
|
+
? currentWeekday - 7 + boundary.weekday
|
|
596
|
+
: 7 - currentWeekday + boundary.weekday
|
|
584
597
|
} else {
|
|
598
|
+
a = nextWeekday - currentWeekday
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Only check day-of-month if weekday constraint not already satisfied (they're OR'd)
|
|
603
|
+
if (cron.days.size !== 0 && a !== 0) {
|
|
604
|
+
const currentDay = current.getUTCDate()
|
|
605
|
+
const nextDay = table.day[currentDay]
|
|
606
|
+
if (nextDay === undefined) {
|
|
607
|
+
if (prev) {
|
|
608
|
+
const prevMonthDays = daysInMonth(
|
|
609
|
+
new Date(Date.UTC(current.getUTCFullYear(), current.getUTCMonth(), 0))
|
|
610
|
+
)
|
|
611
|
+
b = -(currentDay + (prevMonthDays - boundary.day))
|
|
612
|
+
} else {
|
|
613
|
+
b = daysInMonth(current) - currentDay + boundary.day
|
|
614
|
+
}
|
|
615
|
+
} else if (!prev && nextDay > daysInMonth(current)) {
|
|
616
|
+
// The next matching day does not exist in the current month. Setting it
|
|
617
|
+
// directly would overflow and skip earlier matching days next month.
|
|
585
618
|
b = daysInMonth(current) - currentDay + boundary.day
|
|
619
|
+
} else {
|
|
620
|
+
b = nextDay - currentDay
|
|
586
621
|
}
|
|
587
|
-
} else {
|
|
588
|
-
b = nextDay - currentDay
|
|
589
622
|
}
|
|
590
|
-
}
|
|
591
623
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
624
|
+
const addDays = prev ? Math.max(a, b) : Math.min(a, b)
|
|
625
|
+
if (addDays !== 0) {
|
|
626
|
+
current.setUTCDate(current.getUTCDate() + addDays)
|
|
627
|
+
current.setUTCHours(boundary.hour, boundary.minute, boundary.second)
|
|
628
|
+
adjustDst(current)
|
|
629
|
+
continue
|
|
630
|
+
}
|
|
598
631
|
}
|
|
599
632
|
}
|
|
600
633
|
|
|
@@ -602,11 +635,11 @@ const stepCron = (cron: Cron, startFrom: DateTime.DateTime.Input | undefined, di
|
|
|
602
635
|
const currentMonth = current.getUTCMonth() + 1
|
|
603
636
|
const nextMonth = table.month[currentMonth]
|
|
604
637
|
const clampBoundaryDay = (targetMonthIndex: number): number => {
|
|
605
|
-
if (cron.days.size !== 0) {
|
|
638
|
+
if (cron.days.size !== 0 && cron.weekdays.size === 0) {
|
|
606
639
|
return boundary.day
|
|
607
640
|
}
|
|
608
641
|
const maxDayInMonth = daysInMonth(new Date(Date.UTC(current.getUTCFullYear(), targetMonthIndex, 1)))
|
|
609
|
-
return
|
|
642
|
+
return prev ? maxDayInMonth : 1
|
|
610
643
|
}
|
|
611
644
|
if (nextMonth === undefined) {
|
|
612
645
|
current.setUTCFullYear(current.getUTCFullYear() + tick)
|
|
@@ -661,6 +694,7 @@ export const sequenceReverse = function*(cron: Cron, startFrom?: DateTime.DateTi
|
|
|
661
694
|
* @since 2.0.0
|
|
662
695
|
*/
|
|
663
696
|
export const Equivalence: equivalence.Equivalence<Cron> = equivalence.make((self, that) =>
|
|
697
|
+
self.and === that.and &&
|
|
664
698
|
restrictionsEquals(self.seconds, that.seconds) &&
|
|
665
699
|
restrictionsEquals(self.minutes, that.minutes) &&
|
|
666
700
|
restrictionsEquals(self.hours, that.hours) &&
|
|
@@ -700,6 +734,12 @@ interface SegmentOptions {
|
|
|
700
734
|
min: number
|
|
701
735
|
max: number
|
|
702
736
|
aliases?: Record<string, number> | undefined
|
|
737
|
+
normalize?: ((value: number) => number) | undefined
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
interface ParsedSegment {
|
|
741
|
+
readonly values: Set<number>
|
|
742
|
+
readonly wildcard: boolean
|
|
703
743
|
}
|
|
704
744
|
|
|
705
745
|
const secondOptions: SegmentOptions = {
|
|
@@ -743,7 +783,8 @@ const monthOptions: SegmentOptions = {
|
|
|
743
783
|
|
|
744
784
|
const weekdayOptions: SegmentOptions = {
|
|
745
785
|
min: 0,
|
|
746
|
-
max:
|
|
786
|
+
max: 7,
|
|
787
|
+
normalize: (value) => value === 7 ? 0 : value,
|
|
747
788
|
aliases: {
|
|
748
789
|
sun: 0,
|
|
749
790
|
mon: 1,
|
|
@@ -758,17 +799,21 @@ const weekdayOptions: SegmentOptions = {
|
|
|
758
799
|
const parseSegment = (
|
|
759
800
|
input: string,
|
|
760
801
|
options: SegmentOptions
|
|
761
|
-
): Either.Either<
|
|
762
|
-
const capacity = options.max - options.min + 1
|
|
802
|
+
): Either.Either<ParsedSegment, ParseError> => {
|
|
763
803
|
const values = new Set<number>()
|
|
764
804
|
const fields = input.split(",")
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
805
|
+
const first = splitStep(fields[0]!)
|
|
806
|
+
const wildcard = first[0] === "*"
|
|
807
|
+
const normalize = options.normalize ?? ((value: number) => value)
|
|
808
|
+
const add = wildcard && (first[1] === undefined || first[1] === 1)
|
|
809
|
+
? constVoid
|
|
810
|
+
: (value: number) => {
|
|
811
|
+
values.add(normalize(value))
|
|
770
812
|
}
|
|
771
813
|
|
|
814
|
+
for (let index = 0; index < fields.length; index++) {
|
|
815
|
+
const field = fields[index]!
|
|
816
|
+
const [raw, step] = index === 0 ? first : splitStep(field)
|
|
772
817
|
if (step !== undefined) {
|
|
773
818
|
if (!Number.isInteger(step)) {
|
|
774
819
|
return Either.left(new ParseError({ message: `Expected step value to be a positive integer`, input }))
|
|
@@ -782,8 +827,11 @@ const parseSegment = (
|
|
|
782
827
|
}
|
|
783
828
|
|
|
784
829
|
if (raw === "*") {
|
|
830
|
+
if (index === 0 && (step === undefined || step === 1)) {
|
|
831
|
+
continue
|
|
832
|
+
}
|
|
785
833
|
for (let i = options.min; i <= options.max; i += step ?? 1) {
|
|
786
|
-
|
|
834
|
+
add(i)
|
|
787
835
|
}
|
|
788
836
|
} else {
|
|
789
837
|
const [left, right] = splitRange(raw, options.aliases)
|
|
@@ -797,7 +845,9 @@ const parseSegment = (
|
|
|
797
845
|
}
|
|
798
846
|
|
|
799
847
|
if (right === undefined) {
|
|
800
|
-
|
|
848
|
+
for (let i = left; i <= (step === undefined ? left : options.max); i += step ?? 1) {
|
|
849
|
+
add(i)
|
|
850
|
+
}
|
|
801
851
|
} else {
|
|
802
852
|
if (!Number.isInteger(right)) {
|
|
803
853
|
return Either.left(new ParseError({ message: `Expected a positive integer`, input }))
|
|
@@ -812,23 +862,20 @@ const parseSegment = (
|
|
|
812
862
|
}
|
|
813
863
|
|
|
814
864
|
for (let i = left; i <= right; i += step ?? 1) {
|
|
815
|
-
|
|
865
|
+
add(i)
|
|
816
866
|
}
|
|
817
867
|
}
|
|
818
868
|
}
|
|
819
|
-
|
|
820
|
-
if (values.size >= capacity) {
|
|
821
|
-
return Either.right(new Set())
|
|
822
|
-
}
|
|
823
869
|
}
|
|
824
870
|
|
|
825
|
-
return Either.right(values)
|
|
871
|
+
return Either.right({ values, wildcard })
|
|
826
872
|
}
|
|
827
873
|
|
|
828
874
|
const splitStep = (input: string): [string, number | undefined] => {
|
|
829
875
|
const seperator = input.indexOf("/")
|
|
830
876
|
if (seperator !== -1) {
|
|
831
|
-
|
|
877
|
+
const step = input.slice(seperator + 1)
|
|
878
|
+
return [input.slice(0, seperator), decimalRegex.test(step) ? Number(step) : NaN]
|
|
832
879
|
}
|
|
833
880
|
|
|
834
881
|
return [input, undefined]
|
|
@@ -844,5 +891,7 @@ const splitRange = (input: string, aliases?: Record<string, number>): [number, n
|
|
|
844
891
|
}
|
|
845
892
|
|
|
846
893
|
function aliasOrValue(field: string, aliases?: Record<string, number>): number {
|
|
847
|
-
return aliases?.[field.toLocaleLowerCase()] ?? Number(field)
|
|
894
|
+
return aliases?.[field.toLocaleLowerCase()] ?? (decimalRegex.test(field) ? Number(field) : NaN)
|
|
848
895
|
}
|
|
896
|
+
|
|
897
|
+
const decimalRegex = /^\d+$/
|
package/src/Graph.ts
CHANGED
|
@@ -1923,6 +1923,37 @@ export const isAcyclic = <N, E, T extends Kind = "directed">(
|
|
|
1923
1923
|
return graph.isAcyclic.value
|
|
1924
1924
|
}
|
|
1925
1925
|
|
|
1926
|
+
if (graph.type === "undirected") {
|
|
1927
|
+
const visited = new Set<NodeIndex>()
|
|
1928
|
+
|
|
1929
|
+
for (const startNode of graph.nodes.keys()) {
|
|
1930
|
+
if (visited.has(startNode)) {
|
|
1931
|
+
continue
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
visited.add(startNode)
|
|
1935
|
+
const stack: Array<{ node: NodeIndex; parent: NodeIndex | null }> = [{ node: startNode, parent: null }]
|
|
1936
|
+
|
|
1937
|
+
while (stack.length > 0) {
|
|
1938
|
+
const { node, parent } = stack.pop()!
|
|
1939
|
+
const nodeNeighbors = getUndirectedNeighbors(graph as any, node)
|
|
1940
|
+
|
|
1941
|
+
for (const neighbor of nodeNeighbors) {
|
|
1942
|
+
if (!visited.has(neighbor)) {
|
|
1943
|
+
visited.add(neighbor)
|
|
1944
|
+
stack.push({ node: neighbor, parent: node })
|
|
1945
|
+
} else if (neighbor !== parent) {
|
|
1946
|
+
graph.isAcyclic = Option.some(false)
|
|
1947
|
+
return false
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
graph.isAcyclic = Option.some(true)
|
|
1954
|
+
return true
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1926
1957
|
// Stack-safe DFS cycle detection using iterative approach
|
|
1927
1958
|
const visited = new Set<NodeIndex>()
|
|
1928
1959
|
const recursionStack = new Set<NodeIndex>()
|
|
@@ -2103,6 +2134,21 @@ const getUndirectedNeighbors = <N, E>(
|
|
|
2103
2134
|
return Array.from(neighbors)
|
|
2104
2135
|
}
|
|
2105
2136
|
|
|
2137
|
+
const getTraversalNeighbors = <N, E, T extends Kind>(
|
|
2138
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
2139
|
+
nodeIndex: NodeIndex,
|
|
2140
|
+
direction: Direction
|
|
2141
|
+
): Array<NodeIndex> =>
|
|
2142
|
+
graph.type === "undirected"
|
|
2143
|
+
? getUndirectedNeighbors(graph as any, nodeIndex)
|
|
2144
|
+
: neighborsDirected(graph, nodeIndex, direction)
|
|
2145
|
+
|
|
2146
|
+
const getTraversableNeighbor = <N, E, T extends Kind>(
|
|
2147
|
+
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
2148
|
+
current: NodeIndex,
|
|
2149
|
+
edge: Edge<E>
|
|
2150
|
+
): NodeIndex => graph.type === "undirected" && edge.target === current ? edge.source : edge.target
|
|
2151
|
+
|
|
2106
2152
|
/**
|
|
2107
2153
|
* Find connected components in an undirected graph.
|
|
2108
2154
|
* Each component is represented as an array of node indices.
|
|
@@ -2432,7 +2478,7 @@ export const dijkstra = <N, E, T extends Kind = "directed">(
|
|
|
2432
2478
|
for (const edgeIndex of adjacencyList) {
|
|
2433
2479
|
const edge = graph.edges.get(edgeIndex)
|
|
2434
2480
|
if (edge !== undefined) {
|
|
2435
|
-
const neighbor = edge
|
|
2481
|
+
const neighbor = getTraversableNeighbor(graph, currentNode, edge)
|
|
2436
2482
|
const weight = cost(edge.data)
|
|
2437
2483
|
|
|
2438
2484
|
// Validate non-negative weights
|
|
@@ -2564,6 +2610,15 @@ export const floydWarshall = <N, E, T extends Kind = "directed">(
|
|
|
2564
2610
|
next.get(i)!.set(j, j)
|
|
2565
2611
|
edgeMatrix.get(i)!.set(j, edgeData.data)
|
|
2566
2612
|
}
|
|
2613
|
+
|
|
2614
|
+
if (graph.type === "undirected") {
|
|
2615
|
+
const reverseWeight = dist.get(j)!.get(i)!
|
|
2616
|
+
if (weight < reverseWeight) {
|
|
2617
|
+
dist.get(j)!.set(i, weight)
|
|
2618
|
+
next.get(j)!.set(i, i)
|
|
2619
|
+
edgeMatrix.get(j)!.set(i, edgeData.data)
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2567
2622
|
}
|
|
2568
2623
|
|
|
2569
2624
|
// Floyd-Warshall main loop
|
|
@@ -2757,7 +2812,7 @@ export const astar = <N, E, T extends Kind = "directed">(
|
|
|
2757
2812
|
for (const edgeIndex of adjacencyList) {
|
|
2758
2813
|
const edge = graph.edges.get(edgeIndex)
|
|
2759
2814
|
if (edge !== undefined) {
|
|
2760
|
-
const neighbor = edge
|
|
2815
|
+
const neighbor = getTraversableNeighbor(graph, currentNode, edge)
|
|
2761
2816
|
const weight = cost(edge.data)
|
|
2762
2817
|
|
|
2763
2818
|
// Validate non-negative weights
|
|
@@ -2893,6 +2948,14 @@ export const bellmanFord = <N, E, T extends Kind = "directed">(
|
|
|
2893
2948
|
weight,
|
|
2894
2949
|
edgeData: edgeData.data
|
|
2895
2950
|
})
|
|
2951
|
+
if (graph.type === "undirected" && edgeData.source !== edgeData.target) {
|
|
2952
|
+
edges.push({
|
|
2953
|
+
source: edgeData.target,
|
|
2954
|
+
target: edgeData.source,
|
|
2955
|
+
weight,
|
|
2956
|
+
edgeData: edgeData.data
|
|
2957
|
+
})
|
|
2958
|
+
}
|
|
2896
2959
|
}
|
|
2897
2960
|
|
|
2898
2961
|
// Relax edges up to V-1 times
|
|
@@ -2934,14 +2997,8 @@ export const bellmanFord = <N, E, T extends Kind = "directed">(
|
|
|
2934
2997
|
affectedNodes.add(node)
|
|
2935
2998
|
|
|
2936
2999
|
// Add all nodes reachable from this node
|
|
2937
|
-
const
|
|
2938
|
-
|
|
2939
|
-
for (const edgeIndex of adjacencyList) {
|
|
2940
|
-
const edge = graph.edges.get(edgeIndex)
|
|
2941
|
-
if (edge !== undefined) {
|
|
2942
|
-
queue.push(edge.target)
|
|
2943
|
-
}
|
|
2944
|
-
}
|
|
3000
|
+
for (const neighbor of getTraversalNeighbors(graph, node, "outgoing")) {
|
|
3001
|
+
queue.push(neighbor)
|
|
2945
3002
|
}
|
|
2946
3003
|
}
|
|
2947
3004
|
|
|
@@ -3259,7 +3316,7 @@ export const dfs = <N, E, T extends Kind = "directed">(
|
|
|
3259
3316
|
continue
|
|
3260
3317
|
}
|
|
3261
3318
|
|
|
3262
|
-
const neighbors =
|
|
3319
|
+
const neighbors = getTraversalNeighbors(graph, current, direction)
|
|
3263
3320
|
for (let i = neighbors.length - 1; i >= 0; i--) {
|
|
3264
3321
|
const neighbor = neighbors[i]
|
|
3265
3322
|
if (!discovered.has(neighbor)) {
|
|
@@ -3336,7 +3393,7 @@ export const bfs = <N, E, T extends Kind = "directed">(
|
|
|
3336
3393
|
if (!discovered.has(current)) {
|
|
3337
3394
|
discovered.add(current)
|
|
3338
3395
|
|
|
3339
|
-
const neighbors =
|
|
3396
|
+
const neighbors = getTraversalNeighbors(graph, current, direction)
|
|
3340
3397
|
for (const neighbor of neighbors) {
|
|
3341
3398
|
if (!discovered.has(neighbor)) {
|
|
3342
3399
|
queue.push(neighbor)
|
|
@@ -3418,6 +3475,10 @@ export const topo = <N, E, T extends Kind = "directed">(
|
|
|
3418
3475
|
graph: Graph<N, E, T> | MutableGraph<N, E, T>,
|
|
3419
3476
|
config: TopoConfig = {}
|
|
3420
3477
|
): NodeWalker<N> => {
|
|
3478
|
+
if (graph.type === "undirected") {
|
|
3479
|
+
throw new Error("Cannot perform topological sort on undirected graph")
|
|
3480
|
+
}
|
|
3481
|
+
|
|
3421
3482
|
// Check if graph is acyclic first
|
|
3422
3483
|
if (!isAcyclic(graph)) {
|
|
3423
3484
|
throw new Error("Cannot perform topological sort on cyclic graph")
|
|
@@ -3562,7 +3623,7 @@ export const dfsPostOrder = <N, E, T extends Kind = "directed">(
|
|
|
3562
3623
|
|
|
3563
3624
|
if (!current.visitedChildren) {
|
|
3564
3625
|
current.visitedChildren = true
|
|
3565
|
-
const neighbors =
|
|
3626
|
+
const neighbors = getTraversalNeighbors(graph, current.node, direction)
|
|
3566
3627
|
|
|
3567
3628
|
for (let i = neighbors.length - 1; i >= 0; i--) {
|
|
3568
3629
|
const neighbor = neighbors[i]
|
package/src/internal/schedule.ts
CHANGED
|
@@ -482,6 +482,14 @@ export const cron: {
|
|
|
482
482
|
return makeWithState<[boolean, [number, number, number]], unknown, [number, number]>(
|
|
483
483
|
[true, [Number.MIN_SAFE_INTEGER, 0, 0]],
|
|
484
484
|
(now, _, [initial, previous]) => {
|
|
485
|
+
if (now === Number.POSITIVE_INFINITY) {
|
|
486
|
+
return core.succeed([
|
|
487
|
+
[false, previous],
|
|
488
|
+
[previous[1], previous[2]],
|
|
489
|
+
ScheduleDecision.done
|
|
490
|
+
])
|
|
491
|
+
}
|
|
492
|
+
|
|
485
493
|
if (now < previous[0]) {
|
|
486
494
|
return core.succeed([
|
|
487
495
|
[false, previous],
|
package/src/internal/version.ts
CHANGED