@effect-atom/atom 0.1.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 (65) hide show
  1. package/Atom/package.json +6 -0
  2. package/AtomRef/package.json +6 -0
  3. package/Hydration/package.json +6 -0
  4. package/LICENSE +21 -0
  5. package/README.md +3 -0
  6. package/Registry/package.json +6 -0
  7. package/Result/package.json +6 -0
  8. package/dist/cjs/Atom.js +1079 -0
  9. package/dist/cjs/Atom.js.map +1 -0
  10. package/dist/cjs/AtomRef.js +261 -0
  11. package/dist/cjs/AtomRef.js.map +1 -0
  12. package/dist/cjs/Hydration.js +100 -0
  13. package/dist/cjs/Hydration.js.map +1 -0
  14. package/dist/cjs/Registry.js +128 -0
  15. package/dist/cjs/Registry.js.map +1 -0
  16. package/dist/cjs/Result.js +454 -0
  17. package/dist/cjs/Result.js.map +1 -0
  18. package/dist/cjs/index.js +37 -0
  19. package/dist/cjs/index.js.map +1 -0
  20. package/dist/cjs/internal/registry.js +701 -0
  21. package/dist/cjs/internal/registry.js.map +1 -0
  22. package/dist/cjs/internal/runtime.js +92 -0
  23. package/dist/cjs/internal/runtime.js.map +1 -0
  24. package/dist/dts/Atom.d.ts +597 -0
  25. package/dist/dts/Atom.d.ts.map +1 -0
  26. package/dist/dts/AtomRef.d.ts +55 -0
  27. package/dist/dts/AtomRef.d.ts.map +1 -0
  28. package/dist/dts/Hydration.d.ts +27 -0
  29. package/dist/dts/Hydration.d.ts.map +1 -0
  30. package/dist/dts/Registry.d.ts +115 -0
  31. package/dist/dts/Registry.d.ts.map +1 -0
  32. package/dist/dts/Result.d.ts +351 -0
  33. package/dist/dts/Result.d.ts.map +1 -0
  34. package/dist/dts/index.d.ts +21 -0
  35. package/dist/dts/index.d.ts.map +1 -0
  36. package/dist/dts/internal/registry.d.ts +2 -0
  37. package/dist/dts/internal/registry.d.ts.map +1 -0
  38. package/dist/dts/internal/runtime.d.ts +2 -0
  39. package/dist/dts/internal/runtime.d.ts.map +1 -0
  40. package/dist/esm/Atom.js +1029 -0
  41. package/dist/esm/Atom.js.map +1 -0
  42. package/dist/esm/AtomRef.js +232 -0
  43. package/dist/esm/AtomRef.js.map +1 -0
  44. package/dist/esm/Hydration.js +71 -0
  45. package/dist/esm/Hydration.js.map +1 -0
  46. package/dist/esm/Registry.js +98 -0
  47. package/dist/esm/Registry.js.map +1 -0
  48. package/dist/esm/Result.js +403 -0
  49. package/dist/esm/Result.js.map +1 -0
  50. package/dist/esm/index.js +21 -0
  51. package/dist/esm/index.js.map +1 -0
  52. package/dist/esm/internal/registry.js +672 -0
  53. package/dist/esm/internal/registry.js.map +1 -0
  54. package/dist/esm/internal/runtime.js +64 -0
  55. package/dist/esm/internal/runtime.js.map +1 -0
  56. package/dist/esm/package.json +4 -0
  57. package/package.json +72 -0
  58. package/src/Atom.ts +1865 -0
  59. package/src/AtomRef.ts +282 -0
  60. package/src/Hydration.ts +98 -0
  61. package/src/Registry.ts +204 -0
  62. package/src/Result.ts +767 -0
  63. package/src/index.ts +24 -0
  64. package/src/internal/registry.ts +810 -0
  65. package/src/internal/runtime.ts +63 -0
@@ -0,0 +1,1029 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+ /* eslint-disable @typescript-eslint/no-empty-object-type */
5
+ import * as KeyValueStore from "@effect/platform/KeyValueStore";
6
+ import * as Arr from "effect/Array";
7
+ import { NoSuchElementException } from "effect/Cause";
8
+ import * as Cause from "effect/Cause";
9
+ import * as Channel from "effect/Channel";
10
+ import * as Chunk from "effect/Chunk";
11
+ import * as EffectContext from "effect/Context";
12
+ import * as Duration from "effect/Duration";
13
+ import * as Effect from "effect/Effect";
14
+ import * as Either from "effect/Either";
15
+ import * as Exit from "effect/Exit";
16
+ import * as Fiber from "effect/Fiber";
17
+ import * as FiberRef from "effect/FiberRef";
18
+ import { constant, constVoid, dual, pipe } from "effect/Function";
19
+ import { globalValue } from "effect/GlobalValue";
20
+ import * as Inspectable from "effect/Inspectable";
21
+ import * as Layer from "effect/Layer";
22
+ import * as MutableHashMap from "effect/MutableHashMap";
23
+ import * as Option from "effect/Option";
24
+ import { pipeArguments } from "effect/Pipeable";
25
+ import * as Runtime from "effect/Runtime";
26
+ import * as Schema from "effect/Schema";
27
+ import * as Scope from "effect/Scope";
28
+ import * as Stream from "effect/Stream";
29
+ import * as Subscribable from "effect/Subscribable";
30
+ import * as SubscriptionRef from "effect/SubscriptionRef";
31
+ import * as internalRegistry from "./internal/registry.js";
32
+ import { runCallbackSync } from "./internal/runtime.js";
33
+ import * as Registry from "./Registry.js";
34
+ import { AtomRegistry } from "./Registry.js";
35
+ import * as Result from "./Result.js";
36
+ /**
37
+ * @since 1.0.0
38
+ * @category type ids
39
+ */
40
+ export const TypeId = "~effect-atom/atom/Atom";
41
+ /**
42
+ * @since 1.0.0
43
+ * @category type ids
44
+ */
45
+ export const WritableTypeId = "~effect-atom/atom/Atom/Writable";
46
+ const AtomProto = {
47
+ [TypeId]: TypeId,
48
+ pipe() {
49
+ return pipeArguments(this, arguments);
50
+ },
51
+ toJSON() {
52
+ return {
53
+ _id: "Atom",
54
+ keepAlive: this.keepAlive,
55
+ lazy: this.lazy,
56
+ label: this.label
57
+ };
58
+ },
59
+ toString() {
60
+ return Inspectable.format(this);
61
+ },
62
+ [Inspectable.NodeInspectSymbol]() {
63
+ return this.toJSON();
64
+ }
65
+ };
66
+ const RuntimeProto = {
67
+ ...AtomProto,
68
+ atom(arg, options) {
69
+ const read = makeRead(arg, options);
70
+ return readable(get => {
71
+ const previous = get.self();
72
+ const runtimeResult = get(this);
73
+ if (runtimeResult._tag !== "Success") {
74
+ return Result.replacePrevious(runtimeResult, previous);
75
+ }
76
+ return read(get, runtimeResult.value);
77
+ });
78
+ },
79
+ fn(arg, options) {
80
+ if (arguments.length === 0) {
81
+ return (arg, options) => makeFnRuntime(this, arg, options);
82
+ }
83
+ return makeFnRuntime(this, arg, options);
84
+ },
85
+ pull(arg, options) {
86
+ const pullSignal = state(0);
87
+ const pullAtom = readable(get => {
88
+ const previous = get.self();
89
+ const runtimeResult = get(this);
90
+ if (runtimeResult._tag !== "Success") {
91
+ return Result.replacePrevious(runtimeResult, previous);
92
+ }
93
+ return makeEffect(get, makeStreamPullEffect(get, pullSignal, arg, options), Result.initial(true), runtimeResult.value);
94
+ });
95
+ return makeStreamPull(pullSignal, pullAtom);
96
+ },
97
+ subscriptionRef(ref) {
98
+ return makeSubRef(readable(get => {
99
+ const previous = get.self();
100
+ const runtimeResult = get(this);
101
+ if (runtimeResult._tag !== "Success") {
102
+ return Result.replacePrevious(runtimeResult, previous);
103
+ }
104
+ const value = typeof ref === "function" ? ref(get) : ref;
105
+ return SubscriptionRef.SubscriptionRefTypeId in value ? value : makeEffect(get, value, Result.initial(true), runtimeResult.value);
106
+ }), (get, ref) => {
107
+ const runtime = Result.getOrThrow(get(this));
108
+ return readSubscribable(get, ref, runtime);
109
+ });
110
+ },
111
+ subscribable(arg) {
112
+ return makeSubscribable(readable(get => {
113
+ const previous = get.self();
114
+ const runtimeResult = get(this);
115
+ if (runtimeResult._tag !== "Success") {
116
+ return Result.replacePrevious(runtimeResult, previous);
117
+ }
118
+ const value = typeof arg === "function" ? arg(get) : arg;
119
+ return Subscribable.isSubscribable(value) ? value : makeEffect(get, value, Result.initial(true), runtimeResult.value);
120
+ }), (get, ref) => {
121
+ const runtime = Result.getOrThrow(get(this));
122
+ return readSubscribable(get, ref, runtime);
123
+ });
124
+ }
125
+ };
126
+ const makeFnRuntime = (self, arg, options) => {
127
+ const [read, write, argAtom] = makeResultFn(arg, options);
128
+ return writable(get => {
129
+ get.get(argAtom);
130
+ const previous = get.self();
131
+ const runtimeResult = get.get(self);
132
+ if (runtimeResult._tag !== "Success") {
133
+ return Result.replacePrevious(runtimeResult, previous);
134
+ }
135
+ return read(get, runtimeResult.value);
136
+ }, write);
137
+ };
138
+ const WritableProto = {
139
+ ...AtomProto,
140
+ [WritableTypeId]: WritableTypeId
141
+ };
142
+ /**
143
+ * @since 1.0.0
144
+ * @category refinements
145
+ */
146
+ export const isWritable = atom => WritableTypeId in atom;
147
+ /**
148
+ * @since 1.0.0
149
+ * @category constructors
150
+ */
151
+ export const readable = (read, refresh) => {
152
+ const self = Object.create(AtomProto);
153
+ self.keepAlive = false;
154
+ self.lazy = true;
155
+ self.read = read;
156
+ self.refresh = refresh;
157
+ return self;
158
+ };
159
+ /**
160
+ * @since 1.0.0
161
+ * @category constructors
162
+ */
163
+ export const writable = (read, write, refresh) => {
164
+ const self = Object.create(WritableProto);
165
+ self.keepAlive = false;
166
+ self.lazy = true;
167
+ self.read = read;
168
+ self.write = write;
169
+ self.refresh = refresh;
170
+ return self;
171
+ };
172
+ function constSetSelf(ctx, value) {
173
+ ctx.setSelf(value);
174
+ }
175
+ // -----------------------------------------------------------------------------
176
+ // constructors
177
+ // -----------------------------------------------------------------------------
178
+ /**
179
+ * @since 1.0.0
180
+ * @category constructors
181
+ */
182
+ export const make = (arg, options) => {
183
+ const readOrAtom = makeRead(arg, options);
184
+ if (TypeId in readOrAtom) {
185
+ return readOrAtom;
186
+ }
187
+ return readable(readOrAtom);
188
+ };
189
+ // -----------------------------------------------------------------------------
190
+ // constructors - effect
191
+ // -----------------------------------------------------------------------------
192
+ const isDataType = u => Option.TypeId in u || Either.TypeId in u;
193
+ const makeRead = (arg, options) => {
194
+ if (typeof arg === "function") {
195
+ const create = arg;
196
+ return function (get, providedRuntime) {
197
+ const value = create(get);
198
+ if (typeof value === "object" && value !== null) {
199
+ if (isDataType(value)) {
200
+ return value;
201
+ } else if (Effect.EffectTypeId in value) {
202
+ return effect(get, value, options, providedRuntime);
203
+ } else if (Stream.StreamTypeId in value) {
204
+ return stream(get, value, options, providedRuntime);
205
+ }
206
+ }
207
+ return value;
208
+ };
209
+ } else if (typeof arg === "object" && arg !== null) {
210
+ if (isDataType(arg)) {
211
+ return state(arg);
212
+ } else if (Effect.EffectTypeId in arg) {
213
+ return function (get, providedRuntime) {
214
+ return effect(get, arg, options, providedRuntime);
215
+ };
216
+ } else if (Stream.StreamTypeId in arg) {
217
+ return function (get, providedRuntime) {
218
+ return stream(get, arg, options, providedRuntime);
219
+ };
220
+ }
221
+ }
222
+ return state(arg);
223
+ };
224
+ const state = initialValue => writable(function (_get) {
225
+ return initialValue;
226
+ }, constSetSelf);
227
+ const effect = (get, effect, options, runtime) => {
228
+ const initialValue = options?.initialValue !== undefined ? Result.success(options.initialValue) : Result.initial();
229
+ return makeEffect(get, effect, initialValue, runtime, options?.uninterruptible);
230
+ };
231
+ function makeEffect(ctx, effect, initialValue, runtime = Runtime.defaultRuntime, uninterruptible = false) {
232
+ const previous = ctx.self();
233
+ const scope = Effect.runSync(Scope.make());
234
+ ctx.addFinalizer(() => {
235
+ Effect.runFork(Scope.close(scope, Exit.void));
236
+ });
237
+ const contextMap = new Map(runtime.context.unsafeMap);
238
+ contextMap.set(Scope.Scope.key, scope);
239
+ contextMap.set(AtomRegistry.key, ctx.registry);
240
+ const scopedRuntime = Runtime.make({
241
+ context: EffectContext.unsafeMake(contextMap),
242
+ fiberRefs: runtime.fiberRefs,
243
+ runtimeFlags: runtime.runtimeFlags
244
+ });
245
+ let syncResult;
246
+ let isAsync = false;
247
+ const cancel = runCallbackSync(scopedRuntime)(effect, function (exit) {
248
+ syncResult = Result.fromExitWithPrevious(exit, previous);
249
+ if (isAsync) ctx.setSelf(syncResult);
250
+ }, uninterruptible);
251
+ isAsync = true;
252
+ if (cancel !== undefined) {
253
+ ctx.addFinalizer(cancel);
254
+ }
255
+ if (syncResult !== undefined) {
256
+ return syncResult;
257
+ } else if (previous._tag === "Some") {
258
+ return Result.waitingFrom(previous);
259
+ }
260
+ return Result.waiting(initialValue);
261
+ }
262
+ /**
263
+ * @since 1.0.0
264
+ * @category constructors
265
+ */
266
+ export const context = options => {
267
+ let globalLayer;
268
+ function factory(create) {
269
+ const self = Object.create(RuntimeProto);
270
+ self.keepAlive = false;
271
+ self.lazy = true;
272
+ self.refresh = undefined;
273
+ const layerAtom = keepAlive(typeof create === "function" ? readable(get => globalLayer ? Layer.provideMerge(create(get), globalLayer) : create(get)) : readable(() => globalLayer ? Layer.provideMerge(create, globalLayer) : create));
274
+ self.layer = layerAtom;
275
+ self.read = function read(get) {
276
+ const layer = get(layerAtom);
277
+ const build = Effect.flatMap(Effect.flatMap(Effect.scope, scope => Layer.buildWithMemoMap(layer, options.memoMap, scope)), context => Effect.provide(Effect.runtime(), context));
278
+ return effect(get, build, {
279
+ uninterruptible: true
280
+ });
281
+ };
282
+ return self;
283
+ }
284
+ factory.memoMap = options.memoMap;
285
+ factory.addGlobalLayer = layer => {
286
+ if (globalLayer === undefined) {
287
+ globalLayer = layer;
288
+ } else {
289
+ globalLayer = Layer.provideMerge(globalLayer, layer);
290
+ }
291
+ };
292
+ return factory;
293
+ };
294
+ /**
295
+ * @since 1.0.0
296
+ * @category context
297
+ */
298
+ export const defaultMemoMap = /*#__PURE__*/globalValue("@effect-atom/atom/Atom/defaultMemoMap", () => Effect.runSync(Layer.makeMemoMap));
299
+ /**
300
+ * @since 1.0.0
301
+ * @category context
302
+ */
303
+ export const runtime = /*#__PURE__*/globalValue("@effect-atom/atom/Atom/defaultContext", () => context({
304
+ memoMap: defaultMemoMap
305
+ }));
306
+ // -----------------------------------------------------------------------------
307
+ // constructors - stream
308
+ // -----------------------------------------------------------------------------
309
+ const stream = (get, stream, options, runtime) => {
310
+ const initialValue = options?.initialValue !== undefined ? Result.success(options.initialValue) : Result.initial();
311
+ return makeStream(get, stream, initialValue, runtime);
312
+ };
313
+ function makeStream(ctx, stream, initialValue, runtime = Runtime.defaultRuntime) {
314
+ const previous = ctx.self();
315
+ const writer = Channel.readWithCause({
316
+ onInput(input) {
317
+ return Channel.suspend(() => {
318
+ const last = Chunk.last(input);
319
+ if (last._tag === "Some") {
320
+ ctx.setSelf(Result.success(last.value, {
321
+ waiting: true
322
+ }));
323
+ }
324
+ return writer;
325
+ });
326
+ },
327
+ onFailure(cause) {
328
+ return Channel.sync(() => {
329
+ ctx.setSelf(Result.failureWithPrevious(cause, {
330
+ previous
331
+ }));
332
+ });
333
+ },
334
+ onDone(_done) {
335
+ return Channel.sync(() => {
336
+ pipe(ctx.self(), Option.flatMap(Result.value), Option.match({
337
+ onNone: () => ctx.setSelf(Result.failWithPrevious(new NoSuchElementException(), {
338
+ previous
339
+ })),
340
+ onSome: a => ctx.setSelf(Result.success(a))
341
+ }));
342
+ });
343
+ }
344
+ });
345
+ const registryRuntime = Runtime.make({
346
+ context: EffectContext.add(runtime.context, AtomRegistry, ctx.registry),
347
+ fiberRefs: runtime.fiberRefs,
348
+ runtimeFlags: runtime.runtimeFlags
349
+ });
350
+ const cancel = runCallbackSync(registryRuntime)(Channel.runDrain(Channel.pipeTo(Stream.toChannel(stream), writer)), constVoid);
351
+ if (cancel !== undefined) {
352
+ ctx.addFinalizer(cancel);
353
+ }
354
+ if (previous._tag === "Some") {
355
+ return Result.waitingFrom(previous);
356
+ }
357
+ return Result.waiting(initialValue);
358
+ }
359
+ // -----------------------------------------------------------------------------
360
+ // constructors - subscription ref
361
+ // -----------------------------------------------------------------------------
362
+ /** @internal */
363
+ const readSubscribable = (get, sub, runtime = Runtime.defaultRuntime) => {
364
+ if (Subscribable.TypeId in sub) {
365
+ get.addFinalizer(sub.changes.pipe(Stream.runForEach(value => {
366
+ get.setSelf(value);
367
+ return Effect.void;
368
+ }), Runtime.runCallback(runtime)));
369
+ return Runtime.runSync(runtime)(sub.get);
370
+ } else if (sub._tag !== "Success") {
371
+ return sub;
372
+ }
373
+ return makeStream(get, sub.value.changes, Result.initial(true), runtime);
374
+ };
375
+ const makeSubRef = (refAtom, read) => {
376
+ function write(ctx, value) {
377
+ const ref = ctx.get(refAtom);
378
+ if (SubscriptionRef.SubscriptionRefTypeId in ref) {
379
+ Effect.runSync(SubscriptionRef.set(ref, value));
380
+ } else if (Result.isSuccess(ref)) {
381
+ Effect.runSync(SubscriptionRef.set(ref.value, value));
382
+ }
383
+ }
384
+ return writable(get => {
385
+ const ref = get(refAtom);
386
+ if (SubscriptionRef.SubscriptionRefTypeId in ref) {
387
+ return read(get, ref);
388
+ } else if (Result.isSuccess(ref)) {
389
+ return read(get, ref);
390
+ }
391
+ return ref;
392
+ }, write);
393
+ };
394
+ /**
395
+ * @since 1.0.0
396
+ * @category constructors
397
+ */
398
+ export const subscriptionRef = ref => makeSubRef(readable(get => {
399
+ const value = typeof ref === "function" ? ref(get) : ref;
400
+ return SubscriptionRef.SubscriptionRefTypeId in value ? value : makeEffect(get, value, Result.initial(true));
401
+ }), readSubscribable);
402
+ // -----------------------------------------------------------------------------
403
+ // constructors - subscribable
404
+ // -----------------------------------------------------------------------------
405
+ /**
406
+ * @since 1.0.0
407
+ * @category constructors
408
+ */
409
+ export const subscribable = ref => makeSubscribable(readable(get => {
410
+ const value = typeof ref === "function" ? ref(get) : ref;
411
+ return Subscribable.isSubscribable(value) ? value : makeEffect(get, value, Result.initial(true));
412
+ }), readSubscribable);
413
+ const makeSubscribable = (subAtom, read) => readable(get => {
414
+ const sub = get(subAtom);
415
+ if (Subscribable.isSubscribable(sub)) {
416
+ return read(get, sub);
417
+ } else if (Result.isSuccess(sub)) {
418
+ return read(get, sub);
419
+ }
420
+ return sub;
421
+ });
422
+ /**
423
+ * @since 1.0.0
424
+ * @category constructors
425
+ */
426
+ export const fnSync = function (...args) {
427
+ if (args.length === 0) {
428
+ return makeFnSync;
429
+ }
430
+ return makeFnSync(...args);
431
+ };
432
+ const makeFnSync = (f, options) => {
433
+ const argAtom = state([0, undefined]);
434
+ const hasInitialValue = options?.initialValue !== undefined;
435
+ return writable(function (get) {
436
+ ;
437
+ get.isFn = true;
438
+ const [counter, arg] = get.get(argAtom);
439
+ if (counter === 0) {
440
+ return hasInitialValue ? options.initialValue : Option.none();
441
+ }
442
+ return hasInitialValue ? f(arg, get) : Option.some(f(arg, get));
443
+ }, function (ctx, arg) {
444
+ batch(() => {
445
+ ctx.set(argAtom, [ctx.get(argAtom)[0] + 1, arg]);
446
+ ctx.refreshSelf();
447
+ });
448
+ });
449
+ };
450
+ /**
451
+ * @since 1.0.0
452
+ * @category symbols
453
+ */
454
+ export const Reset = /*#__PURE__*/Symbol.for("@effect-atom/atom/Atom/Reset");
455
+ /**
456
+ * @since 1.0.0
457
+ * @category constructors
458
+ */
459
+ export const fn = function (...args) {
460
+ if (args.length === 0) {
461
+ return makeFn;
462
+ }
463
+ return makeFn(...args);
464
+ };
465
+ const makeFn = (f, options) => {
466
+ const [read, write] = makeResultFn(f, options);
467
+ return writable(read, write);
468
+ };
469
+ function makeResultFn(f, options) {
470
+ const argAtom = state([0, undefined]);
471
+ const initialValue = options?.initialValue !== undefined ? Result.success(options.initialValue) : Result.initial();
472
+ function read(get, runtime) {
473
+ ;
474
+ get.isFn = true;
475
+ const [counter, arg] = get.get(argAtom);
476
+ if (counter === 0) {
477
+ return initialValue;
478
+ }
479
+ const value = f(arg, get);
480
+ if (Effect.EffectTypeId in value) {
481
+ return makeEffect(get, value, initialValue, runtime);
482
+ }
483
+ return makeStream(get, value, initialValue, runtime);
484
+ }
485
+ function write(ctx, arg) {
486
+ batch(() => {
487
+ if (arg === Reset) {
488
+ ctx.set(argAtom, [0, undefined]);
489
+ } else {
490
+ ctx.set(argAtom, [ctx.get(argAtom)[0] + 1, arg]);
491
+ }
492
+ ctx.refreshSelf();
493
+ });
494
+ }
495
+ return [read, write, argAtom];
496
+ }
497
+ /**
498
+ * @since 1.0.0
499
+ * @category constructors
500
+ */
501
+ export const pull = (create, options) => {
502
+ const pullSignal = state(0);
503
+ const pullAtom = readable(makeRead(function (get) {
504
+ return makeStreamPullEffect(get, pullSignal, create, options);
505
+ }));
506
+ return makeStreamPull(pullSignal, pullAtom);
507
+ };
508
+ const makeStreamPullEffect = (get, pullSignal, create, options) => Effect.flatMap(Channel.toPull(Stream.toChannel(typeof create === "function" ? create(get) : create)), pullChunk => {
509
+ const semaphore = Effect.unsafeMakeSemaphore(1);
510
+ const fiber = Option.getOrThrow(Fiber.getCurrentFiber());
511
+ const context = fiber.currentContext;
512
+ let acc = Chunk.empty();
513
+ const pull = Effect.flatMap(Effect.locally(Effect.suspend(() => pullChunk), FiberRef.currentContext, context), Either.match({
514
+ onLeft: () => {
515
+ const items = Chunk.toReadonlyArray(acc);
516
+ if (!Arr.isNonEmptyArray(items)) {
517
+ return Effect.fail(new Cause.NoSuchElementException(`Atom.pull: no items`));
518
+ }
519
+ return Effect.succeed({
520
+ done: true,
521
+ items
522
+ });
523
+ },
524
+ onRight(chunk) {
525
+ let items;
526
+ if (options?.disableAccumulation) {
527
+ items = chunk;
528
+ } else {
529
+ items = Chunk.appendAll(acc, chunk);
530
+ acc = items;
531
+ }
532
+ const arr = Chunk.toReadonlyArray(items);
533
+ if (!Arr.isNonEmptyArray(arr)) {
534
+ return pull;
535
+ }
536
+ return Effect.succeed({
537
+ done: false,
538
+ items: arr
539
+ });
540
+ }
541
+ }));
542
+ const pullWithSemaphore = semaphore.withPermits(1)(pull);
543
+ const runCallback = runCallbackSync(Runtime.make({
544
+ context,
545
+ fiberRefs: fiber.getFiberRefs(),
546
+ runtimeFlags: Runtime.defaultRuntime.runtimeFlags
547
+ }));
548
+ const cancels = new Set();
549
+ get.addFinalizer(() => {
550
+ for (const cancel of cancels) cancel();
551
+ });
552
+ get.once(pullSignal);
553
+ get.subscribe(pullSignal, () => {
554
+ get.setSelf(Result.waitingFrom(get.self()));
555
+ let cancel;
556
+ // eslint-disable-next-line prefer-const
557
+ cancel = runCallback(pullWithSemaphore, exit => {
558
+ if (cancel) cancels.delete(cancel);
559
+ const result = Result.fromExitWithPrevious(exit, get.self());
560
+ const pending = cancels.size > 0;
561
+ get.setSelf(pending ? Result.waiting(result) : result);
562
+ });
563
+ if (cancel) cancels.add(cancel);
564
+ });
565
+ return pull;
566
+ });
567
+ const makeStreamPull = (pullSignal, pullAtom) => writable(pullAtom.read, function (ctx, _) {
568
+ ctx.set(pullSignal, ctx.get(pullSignal) + 1);
569
+ });
570
+ /**
571
+ * @since 1.0.0
572
+ * @category constructors
573
+ */
574
+ export const family = typeof WeakRef === "undefined" || typeof FinalizationRegistry === "undefined" ? f => {
575
+ const atoms = MutableHashMap.empty();
576
+ return function (arg) {
577
+ const atomEntry = MutableHashMap.get(atoms, arg);
578
+ if (atomEntry._tag === "Some") {
579
+ return atomEntry.value;
580
+ }
581
+ const newAtom = f(arg);
582
+ MutableHashMap.set(atoms, arg, newAtom);
583
+ return newAtom;
584
+ };
585
+ } : f => {
586
+ const atoms = MutableHashMap.empty();
587
+ const registry = new FinalizationRegistry(arg => {
588
+ MutableHashMap.remove(atoms, arg);
589
+ });
590
+ return function (arg) {
591
+ const atomEntry = MutableHashMap.get(atoms, arg).pipe(Option.flatMapNullable(ref => ref.deref()));
592
+ if (atomEntry._tag === "Some") {
593
+ return atomEntry.value;
594
+ }
595
+ const newAtom = f(arg);
596
+ MutableHashMap.set(atoms, arg, new WeakRef(newAtom));
597
+ registry.register(newAtom, arg);
598
+ return newAtom;
599
+ };
600
+ };
601
+ /**
602
+ * @since 1.0.0
603
+ * @category combinators
604
+ */
605
+ export const withFallback = /*#__PURE__*/dual(2, (self, fallback) => {
606
+ function withFallback(get) {
607
+ const result = get(self);
608
+ if (result._tag === "Initial") {
609
+ return Result.waiting(get(fallback));
610
+ }
611
+ return result;
612
+ }
613
+ return isWritable(self) ? writable(withFallback, self.write, self.refresh ?? function (refresh) {
614
+ refresh(self);
615
+ }) : readable(withFallback, self.refresh ?? function (refresh) {
616
+ refresh(self);
617
+ });
618
+ });
619
+ /**
620
+ * @since 1.0.0
621
+ * @category combinators
622
+ */
623
+ export const keepAlive = self => Object.assign(Object.create(Object.getPrototypeOf(self)), {
624
+ ...self,
625
+ keepAlive: true
626
+ });
627
+ /**
628
+ * Reverts the `keepAlive` behavior of a reactive value, allowing it to be
629
+ * disposed of when not in use.
630
+ *
631
+ * Note that Atom's have this behavior by default.
632
+ *
633
+ * @since 1.0.0
634
+ * @category combinators
635
+ */
636
+ export const autoDispose = self => Object.assign(Object.create(Object.getPrototypeOf(self)), {
637
+ ...self,
638
+ keepAlive: false
639
+ });
640
+ /**
641
+ * @since 1.0.0
642
+ * @category combinators
643
+ */
644
+ export const setLazy = /*#__PURE__*/dual(2, (self, lazy) => Object.assign(Object.create(Object.getPrototypeOf(self)), {
645
+ ...self,
646
+ lazy
647
+ }));
648
+ /**
649
+ * @since 1.0.0
650
+ * @category combinators
651
+ */
652
+ export const withLabel = /*#__PURE__*/dual(2, (self, name) => Object.assign(Object.create(Object.getPrototypeOf(self)), {
653
+ ...self,
654
+ label: [name, new Error().stack?.split("\n")[5] ?? ""]
655
+ }));
656
+ /**
657
+ * @since 1.0.0
658
+ * @category combinators
659
+ */
660
+ export const setIdleTTL = /*#__PURE__*/dual(2, (self, duration) => Object.assign(Object.create(Object.getPrototypeOf(self)), {
661
+ ...self,
662
+ keepAlive: false,
663
+ idleTTL: Duration.toMillis(duration)
664
+ }));
665
+ /**
666
+ * @since 1.0.0
667
+ * @category combinators
668
+ */
669
+ export const initialValue = /*#__PURE__*/dual(2, (self, initialValue) => [self, initialValue]);
670
+ /**
671
+ * @since 1.0.0
672
+ * @category combinators
673
+ */
674
+ export const transform = /*#__PURE__*/dual(2, (self, f) => isWritable(self) ? writable(f, function (ctx, value) {
675
+ ctx.set(self, value);
676
+ }, self.refresh ?? function (refresh) {
677
+ refresh(self);
678
+ }) : readable(f, self.refresh ?? function (refresh) {
679
+ refresh(self);
680
+ }));
681
+ /**
682
+ * @since 1.0.0
683
+ * @category combinators
684
+ */
685
+ export const map = /*#__PURE__*/dual(2, (self, f) => transform(self, get => f(get(self))));
686
+ /**
687
+ * @since 1.0.0
688
+ * @category combinators
689
+ */
690
+ export const mapResult = /*#__PURE__*/dual(2, (self, f) => map(self, Result.map(f)));
691
+ /**
692
+ * @since 1.0.0
693
+ * @category combinators
694
+ */
695
+ export const debounce = /*#__PURE__*/dual(2, (self, duration) => {
696
+ const millis = Duration.toMillis(duration);
697
+ return transform(self, function (get) {
698
+ let timeout;
699
+ let value = get.once(self);
700
+ function update() {
701
+ timeout = undefined;
702
+ get.setSelf(value);
703
+ }
704
+ get.addFinalizer(function () {
705
+ if (timeout) clearTimeout(timeout);
706
+ });
707
+ get.subscribe(self, function (val) {
708
+ value = val;
709
+ if (timeout) clearTimeout(timeout);
710
+ timeout = setTimeout(update, millis);
711
+ });
712
+ return value;
713
+ });
714
+ });
715
+ /**
716
+ * @since 1.0.0
717
+ * @category Optimistic
718
+ */
719
+ export const optimistic = self => {
720
+ let counter = 0;
721
+ const writeAtom = state([counter, undefined]);
722
+ return writable(get => {
723
+ let lastValue = get.once(self);
724
+ get.subscribe(self, value => {
725
+ lastValue = value;
726
+ if (!Result.isResult(value)) {
727
+ return get.setSelf(value);
728
+ }
729
+ const current = Option.getOrUndefined(get.self());
730
+ if (Result.isSuccess(current) && Result.isSuccess(value)) {
731
+ if (value.timestamp >= current.timestamp) {
732
+ get.setSelf(value);
733
+ }
734
+ } else {
735
+ get.setSelf(value);
736
+ }
737
+ });
738
+ const transitions = new Set();
739
+ const cancels = new Set();
740
+ get.subscribe(writeAtom, ([, atom]) => {
741
+ if (transitions.has(atom)) return;
742
+ transitions.add(atom);
743
+ let cancel;
744
+ // eslint-disable-next-line prefer-const
745
+ cancel = get.registry.subscribe(atom, result => {
746
+ if (Result.isSuccess(result) && result.waiting) {
747
+ return get.setSelf(result.value);
748
+ }
749
+ transitions.delete(atom);
750
+ if (cancel) {
751
+ cancels.delete(cancel);
752
+ cancel();
753
+ }
754
+ if (transitions.size === 0) {
755
+ if (Result.isFailure(result)) {
756
+ get.setSelf(lastValue);
757
+ }
758
+ get.refresh(self);
759
+ }
760
+ }, {
761
+ immediate: true
762
+ });
763
+ if (transitions.has(atom)) {
764
+ cancels.add(cancel);
765
+ } else {
766
+ cancel();
767
+ }
768
+ });
769
+ get.addFinalizer(() => {
770
+ for (const cancel of cancels) cancel();
771
+ transitions.clear();
772
+ cancels.clear();
773
+ });
774
+ return lastValue;
775
+ }, (ctx, atom) => ctx.set(writeAtom, [++counter, atom]), refresh => refresh(self));
776
+ };
777
+ /**
778
+ * @since 1.0.0
779
+ * @category Optimistic
780
+ */
781
+ export const optimisticFn = /*#__PURE__*/dual(2, (self, options) => {
782
+ const transition = state(Result.initial());
783
+ return fn((arg, get) => {
784
+ let value = options.reducer(get(self), arg);
785
+ if (Result.isResult(value) && !value.waiting) {
786
+ value = Result.waiting(value);
787
+ }
788
+ get.set(transition, Result.success(value, {
789
+ waiting: true
790
+ }));
791
+ get.set(self, transition);
792
+ const fn = typeof options.fn === "function" ? autoDispose(options.fn(value => get.set(transition, Result.success(Result.isResult(value) ? Result.waiting(value) : value, {
793
+ waiting: true
794
+ })))) : options.fn;
795
+ get.set(fn, arg);
796
+ return Effect.onExit(get.result(fn, {
797
+ suspendOnWaiting: true
798
+ }), exit => {
799
+ get.set(transition, Result.fromExit(Exit.as(exit, value)));
800
+ return Effect.void;
801
+ });
802
+ });
803
+ });
804
+ /**
805
+ * @since 1.0.0
806
+ * @category batching
807
+ */
808
+ export const batch = internalRegistry.batch;
809
+ // -----------------------------------------------------------------------------
810
+ // Focus
811
+ // -----------------------------------------------------------------------------
812
+ /**
813
+ * @since 1.0.0
814
+ * @category Focus
815
+ */
816
+ export const windowFocusSignal = /*#__PURE__*/readable(get => {
817
+ let count = 0;
818
+ function update() {
819
+ if (document.visibilityState === "visible") {
820
+ get.setSelf(++count);
821
+ }
822
+ }
823
+ window.addEventListener("visibilitychange", update);
824
+ get.addFinalizer(() => {
825
+ window.removeEventListener("visibilitychange", update);
826
+ });
827
+ return count;
828
+ });
829
+ /**
830
+ * @since 1.0.0
831
+ * @category Focus
832
+ */
833
+ export const makeRefreshOnSignal = signal => self => transform(self, get => {
834
+ get.subscribe(signal, _ => get.refresh(self));
835
+ get.subscribe(self, value => get.setSelf(value));
836
+ return get.once(self);
837
+ });
838
+ /**
839
+ * @since 1.0.0
840
+ * @category Focus
841
+ */
842
+ export const refreshOnWindowFocus = /*#__PURE__*/makeRefreshOnSignal(windowFocusSignal);
843
+ // -----------------------------------------------------------------------------
844
+ // KeyValueStore
845
+ // -----------------------------------------------------------------------------
846
+ /**
847
+ * @since 1.0.0
848
+ * @category KeyValueStore
849
+ */
850
+ export const kvs = options => {
851
+ const setAtom = options.runtime.fn(Effect.fnUntraced(function* (value) {
852
+ const store = (yield* KeyValueStore.KeyValueStore).forSchema(options.schema);
853
+ yield* store.set(options.key, value);
854
+ }));
855
+ const resultAtom = options.runtime.atom(Effect.flatMap(KeyValueStore.KeyValueStore, store => Effect.flatten(store.forSchema(options.schema).get(options.key))));
856
+ return writable(get => {
857
+ get.mount(setAtom);
858
+ return Result.getOrElse(get(resultAtom), options.defaultValue);
859
+ }, (ctx, value) => {
860
+ ctx.set(setAtom, value);
861
+ ctx.setSelf(value);
862
+ });
863
+ };
864
+ // -----------------------------------------------------------------------------
865
+ // URL search params
866
+ // -----------------------------------------------------------------------------
867
+ /**
868
+ * Create an Atom that reads and writes a URL search parameter.
869
+ *
870
+ * Note: If you pass a schema, it has to be synchronous and have no context.
871
+ *
872
+ * @since 1.0.0
873
+ * @category URL search params
874
+ */
875
+ export const searchParam = (name, options) => {
876
+ const decode = options?.schema && Schema.decodeEither(options.schema);
877
+ const encode = options?.schema && Schema.encodeEither(options.schema);
878
+ return writable(get => {
879
+ const handleUpdate = () => {
880
+ if (searchParamState.updating) return;
881
+ const searchParams = new URLSearchParams(window.location.search);
882
+ const newValue = searchParams.get(name) || "";
883
+ if (decode) {
884
+ get.setSelf(Either.getRight(decode(newValue)));
885
+ } else if (newValue !== Option.getOrUndefined(get.self())) {
886
+ get.setSelf(newValue);
887
+ }
888
+ };
889
+ window.addEventListener("popstate", handleUpdate);
890
+ window.addEventListener("pushstate", handleUpdate);
891
+ get.addFinalizer(() => {
892
+ window.removeEventListener("popstate", handleUpdate);
893
+ window.removeEventListener("pushstate", handleUpdate);
894
+ });
895
+ const value = new URLSearchParams(window.location.search).get(name) || "";
896
+ return decode ? Either.getRight(decode(value)) : value;
897
+ }, (ctx, value) => {
898
+ if (encode) {
899
+ const encoded = Option.flatMap(value, v => Either.getRight(encode(v)));
900
+ searchParamState.updates.set(name, Option.getOrElse(encoded, () => ""));
901
+ value = Option.zipRight(encoded, value);
902
+ } else {
903
+ searchParamState.updates.set(name, value);
904
+ }
905
+ ctx.setSelf(value);
906
+ if (searchParamState.timeout) {
907
+ clearTimeout(searchParamState.timeout);
908
+ }
909
+ searchParamState.timeout = setTimeout(updateSearchParams, 500);
910
+ });
911
+ };
912
+ const searchParamState = {
913
+ timeout: undefined,
914
+ updates: /*#__PURE__*/new Map(),
915
+ updating: false
916
+ };
917
+ function updateSearchParams() {
918
+ searchParamState.timeout = undefined;
919
+ searchParamState.updating = true;
920
+ const searchParams = new URLSearchParams(window.location.search);
921
+ for (const [key, value] of searchParamState.updates.entries()) {
922
+ if (value.length > 0) {
923
+ searchParams.set(key, value);
924
+ } else {
925
+ searchParams.delete(key);
926
+ }
927
+ }
928
+ searchParamState.updates.clear();
929
+ const newUrl = `${window.location.pathname}?${searchParams.toString()}`;
930
+ window.history.pushState({}, "", newUrl);
931
+ searchParamState.updating = false;
932
+ }
933
+ // -----------------------------------------------------------------------------
934
+ // conversions
935
+ // -----------------------------------------------------------------------------
936
+ /**
937
+ * @since 1.0.0
938
+ * @category Conversions
939
+ */
940
+ export const toStream = self => Stream.unwrap(Effect.map(AtomRegistry, Registry.toStream(self)));
941
+ /**
942
+ * @since 1.0.0
943
+ * @category Conversions
944
+ */
945
+ export const toStreamResult = self => Stream.unwrap(Effect.map(AtomRegistry, Registry.toStreamResult(self)));
946
+ /**
947
+ * @since 1.0.0
948
+ * @category Conversions
949
+ */
950
+ export const get = self => Effect.map(AtomRegistry, _ => _.get(self));
951
+ /**
952
+ * @since 1.0.0
953
+ * @category Conversions
954
+ */
955
+ export const modify = /*#__PURE__*/dual(2, (self, f) => Effect.map(AtomRegistry, _ => _.modify(self, f)));
956
+ /**
957
+ * @since 1.0.0
958
+ * @category Conversions
959
+ */
960
+ export const set = /*#__PURE__*/dual(2, (self, value) => Effect.map(AtomRegistry, _ => _.set(self, value)));
961
+ /**
962
+ * @since 1.0.0
963
+ * @category Conversions
964
+ */
965
+ export const update = /*#__PURE__*/dual(2, (self, f) => Effect.map(AtomRegistry, _ => _.update(self, f)));
966
+ /**
967
+ * @since 1.0.0
968
+ * @category Conversions
969
+ */
970
+ export const getResult = (self, options) => Effect.flatMap(AtomRegistry, Registry.getResult(self, options));
971
+ /**
972
+ * @since 1.0.0
973
+ * @category Conversions
974
+ */
975
+ export const refresh = self => Effect.map(AtomRegistry, _ => _.refresh(self));
976
+ // -----------------------------------------------------------------------------
977
+ // Serializable
978
+ // -----------------------------------------------------------------------------
979
+ /**
980
+ * @since 1.0.0
981
+ * @category Serializable
982
+ */
983
+ export const SerializableTypeId = "~effect-atom/atom/Atom/Serializable";
984
+ /**
985
+ * @since 1.0.0
986
+ * @category Serializable
987
+ */
988
+ export const isSerializable = self => SerializableTypeId in self;
989
+ /**
990
+ * @since 1.0.0
991
+ * @category combinators
992
+ */
993
+ export const serializable = /*#__PURE__*/dual(2, (self, options) => Object.assign(Object.create(Object.getPrototypeOf(self)), {
994
+ ...self,
995
+ label: self.label ?? [options.key, new Error().stack?.split("\n")[5] ?? ""],
996
+ [SerializableTypeId]: {
997
+ key: options.key,
998
+ encode: Schema.encodeSync(options.schema),
999
+ decode: Schema.decodeSync(options.schema)
1000
+ }
1001
+ }));
1002
+ /**
1003
+ * @since 1.0.0
1004
+ * @category ServerValue
1005
+ */
1006
+ export const ServerValueTypeId = "~effect-atom/atom/Atom/ServerValue";
1007
+ /**
1008
+ * Overrides the value of an Atom when read on the server.
1009
+ *
1010
+ * @since 1.0.0
1011
+ * @category ServerValue
1012
+ */
1013
+ export const withServerValue = /*#__PURE__*/dual(2, (self, read) => Object.assign(Object.create(Object.getPrototypeOf(self)), {
1014
+ ...self,
1015
+ [ServerValueTypeId]: read
1016
+ }));
1017
+ /**
1018
+ * Sets the Atom's server value to `Result.initial(true)`.
1019
+ *
1020
+ * @since 1.0.0
1021
+ * @category ServerValue
1022
+ */
1023
+ export const withServerValueInitial = self => withServerValue(self, constant(Result.initial(true)));
1024
+ /**
1025
+ * @since 1.0.0
1026
+ * @category ServerValue
1027
+ */
1028
+ export const getServerValue = /*#__PURE__*/dual(2, (self, registry) => ServerValueTypeId in self ? self[ServerValueTypeId](atom => registry.get(atom)) : registry.get(self));
1029
+ //# sourceMappingURL=Atom.js.map