@effect-atom/atom 0.4.2 → 0.4.4

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/src/Atom.ts CHANGED
@@ -140,6 +140,7 @@ export interface Context {
140
140
  self<A>(this: Context): Option.Option<A>
141
141
  setSelf<A>(this: Context, a: A): void
142
142
  set<R, W>(this: Context, atom: Writable<R, W>, value: W): void
143
+ setResult<A, E, W>(this: Context, atom: Writable<Result.Result<A, E>, W>, value: W): Effect.Effect<A, E>
143
144
  some<A>(this: Context, atom: Atom<Option.Option<A>>): Effect.Effect<A>
144
145
  someOnce<A>(this: Context, atom: Atom<Option.Option<A>>): Effect.Effect<A>
145
146
  stream<A>(this: Context, atom: Atom<A>, options?: {
@@ -167,6 +168,28 @@ export interface WriteContext<A> {
167
168
  set<R, W>(this: WriteContext<A>, atom: Writable<R, W>, value: W): void
168
169
  }
169
170
 
171
+ /**
172
+ * @since 1.0.0
173
+ * @category combinators
174
+ */
175
+ export const setIdleTTL: {
176
+ (duration: Duration.DurationInput): <A extends Atom<any>>(self: A) => A
177
+ <A extends Atom<any>>(self: A, duration: Duration.DurationInput): A
178
+ } = dual<
179
+ (duration: Duration.DurationInput) => <A extends Atom<any>>(self: A) => A,
180
+ <A extends Atom<any>>(self: A, duration: Duration.DurationInput) => A
181
+ >(2, (self, durationInput) => {
182
+ const duration = Duration.decode(durationInput)
183
+ const isFinite = Duration.isFinite(duration)
184
+ return Object.assign(Object.create(Object.getPrototypeOf(self)), {
185
+ ...self,
186
+ keepAlive: !isFinite,
187
+ idleTTL: isFinite ? Duration.toMillis(duration) : undefined
188
+ })
189
+ })
190
+
191
+ const removeTtl = setIdleTTL(0)
192
+
170
193
  const AtomProto = {
171
194
  [TypeId]: TypeId,
172
195
  pipe() {
@@ -217,7 +240,7 @@ const RuntimeProto = {
217
240
  readonly disableAccumulation?: boolean
218
241
  readonly initialValue?: ReadonlyArray<any>
219
242
  }) {
220
- const pullSignal = state(0)
243
+ const pullSignal = removeTtl(state(0))
221
244
  const pullAtom = readable((get) => {
222
245
  const previous = get.self<Result.Result<any, any>>()
223
246
  const runtimeResult = get(this)
@@ -236,7 +259,7 @@ const RuntimeProto = {
236
259
 
237
260
  subscriptionRef(this: AtomRuntime<any, any>, ref: any) {
238
261
  return makeSubRef(
239
- readable((get) => {
262
+ removeTtl(readable((get) => {
240
263
  const previous = get.self<Result.Result<any, any>>()
241
264
  const runtimeResult = get(this)
242
265
  if (runtimeResult._tag !== "Success") {
@@ -246,7 +269,7 @@ const RuntimeProto = {
246
269
  return SubscriptionRef.SubscriptionRefTypeId in value
247
270
  ? value
248
271
  : makeEffect(get, value, Result.initial(true), runtimeResult.value)
249
- }),
272
+ })),
250
273
  (get, ref) => {
251
274
  const runtime = Result.getOrThrow(get(this))
252
275
  return readSubscribable(get, ref, runtime)
@@ -677,11 +700,11 @@ export const context: (options: {
677
700
  factory.addGlobalLayer = (layer: Layer.Layer<any, any, AtomRegistry | Reactivity.Reactivity>) => {
678
701
  globalLayer = Layer.provideMerge(globalLayer, Layer.provide(layer, Reactivity.layer))
679
702
  }
680
- const reactivityAtom = make(
703
+ const reactivityAtom = removeTtl(make(
681
704
  Effect.scopeWith((scope) => Layer.buildWithMemoMap(Reactivity.layer, options.memoMap, scope)).pipe(
682
705
  Effect.map(EffectContext.get(Reactivity.Reactivity))
683
706
  )
684
- )
707
+ ))
685
708
  factory.withReactivity =
686
709
  (keys: ReadonlyArray<unknown> | ReadonlyRecord<string, ReadonlyArray<unknown>>) =>
687
710
  <A extends Atom<any>>(atom: A): A =>
@@ -961,6 +984,7 @@ export interface FnContext {
961
984
  self<A>(this: FnContext): Option.Option<A>
962
985
  setSelf<A>(this: FnContext, a: A): void
963
986
  set<R, W>(this: FnContext, atom: Writable<R, W>, value: W): void
987
+ setResult<A, E, W>(this: Context, atom: Writable<Result.Result<A, E>, W>, value: W): Effect.Effect<A, E>
964
988
  some<A>(this: FnContext, atom: Atom<Option.Option<A>>): Effect.Effect<A>
965
989
  stream<A>(this: FnContext, atom: Atom<A>, options?: {
966
990
  readonly withoutInitialValue?: boolean
@@ -1007,7 +1031,7 @@ export const fnSync: {
1007
1031
  const makeFnSync = <Arg, A>(f: (arg: Arg, get: FnContext) => A, options?: {
1008
1032
  readonly initialValue?: A
1009
1033
  }): Writable<Option.Option<A> | A, Arg> => {
1010
- const argAtom = state<[number, Arg]>([0, undefined as any])
1034
+ const argAtom = removeTtl(state<[number, Arg]>([0, undefined as any]))
1011
1035
  const hasInitialValue = options?.initialValue !== undefined
1012
1036
  return writable(function(get) {
1013
1037
  ;(get as any).isFn = true
@@ -1100,16 +1124,16 @@ function makeResultFn<Arg, E, A>(
1100
1124
  readonly concurrent?: boolean | undefined
1101
1125
  }
1102
1126
  ) {
1103
- const argAtom = state<[number, Arg | Interrupt]>([0, undefined as any])
1127
+ const argAtom = removeTtl(state<[number, Arg | Interrupt]>([0, undefined as any]))
1104
1128
  const initialValue = options?.initialValue !== undefined
1105
1129
  ? Result.success<A, E>(options.initialValue)
1106
1130
  : Result.initial<A, E>()
1107
1131
  const fibersAtom = options?.concurrent
1108
- ? make((get) => {
1132
+ ? removeTtl(readable((get) => {
1109
1133
  const fibers = new Set<Fiber.RuntimeFiber<any, any>>()
1110
1134
  get.addFinalizer(() => fibers.forEach((f) => f.unsafeInterruptAsFork(FiberId.none)))
1111
1135
  return fibers
1112
- })
1136
+ }))
1113
1137
  : undefined
1114
1138
 
1115
1139
  function read(get: Context, runtime?: Runtime.Runtime<any>): Result.Result<A, E | NoSuchElementException> {
@@ -1202,12 +1226,10 @@ export const pull = <A, E>(
1202
1226
  readonly disableAccumulation?: boolean | undefined
1203
1227
  }
1204
1228
  ): Writable<PullResult<A, E>, void> => {
1205
- const pullSignal = state(0)
1206
- const pullAtom = readable(
1207
- makeRead(function(get) {
1208
- return makeStreamPullEffect(get, pullSignal, create, options)
1209
- })
1210
- )
1229
+ const pullSignal = removeTtl(state(0))
1230
+ const pullAtom = readable(makeRead(function(get) {
1231
+ return makeStreamPullEffect(get, pullSignal, create, options)
1232
+ }))
1211
1233
  return makeStreamPull(pullSignal, pullAtom)
1212
1234
  }
1213
1235
 
@@ -1452,26 +1474,6 @@ export const withLabel: {
1452
1474
  label: [name, new Error().stack?.split("\n")[5] ?? ""]
1453
1475
  }))
1454
1476
 
1455
- /**
1456
- * @since 1.0.0
1457
- * @category combinators
1458
- */
1459
- export const setIdleTTL: {
1460
- (duration: Duration.DurationInput): <A extends Atom<any>>(self: A) => A
1461
- <A extends Atom<any>>(self: A, duration: Duration.DurationInput): A
1462
- } = dual<
1463
- (duration: Duration.DurationInput) => <A extends Atom<any>>(self: A) => A,
1464
- <A extends Atom<any>>(self: A, duration: Duration.DurationInput) => A
1465
- >(2, (self, durationInput) => {
1466
- const duration = Duration.decode(durationInput)
1467
- const isFinite = Duration.isFinite(duration)
1468
- return Object.assign(Object.create(Object.getPrototypeOf(self)), {
1469
- ...self,
1470
- keepAlive: !isFinite,
1471
- idleTTL: isFinite ? Duration.toMillis(duration) : undefined
1472
- })
1473
- })
1474
-
1475
1477
  /**
1476
1478
  * @since 1.0.0
1477
1479
  * @category combinators
@@ -1593,12 +1595,12 @@ export const debounce: {
1593
1595
  */
1594
1596
  export const optimistic = <A>(self: Atom<A>): Writable<A, Atom<Result.Result<A, unknown>>> => {
1595
1597
  let counter = 0
1596
- const writeAtom = state(
1598
+ const writeAtom = removeTtl(state(
1597
1599
  [
1598
1600
  counter,
1599
1601
  undefined as any as Atom<Result.Result<A, unknown>>
1600
1602
  ] as const
1601
- )
1603
+ ))
1602
1604
  return writable(
1603
1605
  (get) => {
1604
1606
  let lastValue = get.once(self)
@@ -1714,7 +1716,7 @@ export const optimisticFn: {
1714
1716
  | ((set: (result: NoInfer<W>) => void) => AtomResultFn<OW, XA, XE>)
1715
1717
  }
1716
1718
  ): AtomResultFn<OW, XA, XE> => {
1717
- const transition = state<Result.Result<W, unknown>>(Result.initial())
1719
+ const transition = removeTtl(state<Result.Result<W, unknown>>(Result.initial()))
1718
1720
  return fn((arg: OW, get) => {
1719
1721
  let value = options.reducer(get(self), arg)
1720
1722
  if (Result.isResult(value)) {
package/src/Result.ts CHANGED
@@ -233,7 +233,7 @@ export const isInterrupted = <A, E>(result: Result<A, E>): result is Failure<A,
233
233
  * @since 1.0.0
234
234
  * @category constructors
235
235
  */
236
- export const failure = <E, A = never>(
236
+ export const failure = <A, E = never>(
237
237
  cause: Cause.Cause<E>,
238
238
  options?: {
239
239
  readonly previousSuccess?: Option.Option<Success<A, E>> | undefined
@@ -417,6 +417,30 @@ export const map: {
417
417
  }
418
418
  })
419
419
 
420
+ /**
421
+ * @since 1.0.0
422
+ * @category combinators
423
+ */
424
+ export const flatMap: {
425
+ <A, E, B, E2>(f: (a: A, prev: Success<A, E>) => Result<A, E2>): (self: Result<A, E>) => Result<B, E | E2>
426
+ <E, A, B, E2>(self: Result<A, E>, f: (a: A, prev: Success<A, E>) => Result<B, E2>): Result<B, E | E2>
427
+ } = dual(2, <E, A, B, E2>(self: Result<A, E>, f: (a: A, prev: Success<A, E>) => Result<B, E2>): Result<B, E | E2> => {
428
+ switch (self._tag) {
429
+ case "Initial":
430
+ return self as any as Result<B, E>
431
+ case "Failure":
432
+ return failure<B, E | E2>(self.cause, {
433
+ previousSuccess: Option.flatMap(self.previousSuccess, (s) => {
434
+ const next = f(s.value, s)
435
+ return isSuccess(next) ? Option.some(next) : Option.none()
436
+ }),
437
+ waiting: self.waiting
438
+ })
439
+ case "Success":
440
+ return f(self.value, self)
441
+ }
442
+ })
443
+
420
444
  /**
421
445
  * @since 1.0.0
422
446
  * @category combinators
@@ -578,6 +578,14 @@ const LifetimeProto: Omit<Lifetime<any>, "node" | "finalizers" | "disposed" | "i
578
578
  })
579
579
  },
580
580
 
581
+ setResult<A, E, W>(this: Lifetime<any>, atom: Atom.Writable<Result.Result<A, E>, W>, value: W): Effect.Effect<A, E> {
582
+ if (this.disposed) {
583
+ throw disposedError(this.node.atom)
584
+ }
585
+ this.node.registry.set(atom, value)
586
+ return this.resultOnce(atom, { suspendOnWaiting: true })
587
+ },
588
+
581
589
  some<A>(this: Lifetime<any>, atom: Atom.Atom<Option.Option<A>>): Effect.Effect<A> {
582
590
  if (this.disposed) {
583
591
  throw disposedError(this.node.atom)