@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.
- package/Atom/package.json +6 -0
- package/AtomRef/package.json +6 -0
- package/Hydration/package.json +6 -0
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/Registry/package.json +6 -0
- package/Result/package.json +6 -0
- package/dist/cjs/Atom.js +1079 -0
- package/dist/cjs/Atom.js.map +1 -0
- package/dist/cjs/AtomRef.js +261 -0
- package/dist/cjs/AtomRef.js.map +1 -0
- package/dist/cjs/Hydration.js +100 -0
- package/dist/cjs/Hydration.js.map +1 -0
- package/dist/cjs/Registry.js +128 -0
- package/dist/cjs/Registry.js.map +1 -0
- package/dist/cjs/Result.js +454 -0
- package/dist/cjs/Result.js.map +1 -0
- package/dist/cjs/index.js +37 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/internal/registry.js +701 -0
- package/dist/cjs/internal/registry.js.map +1 -0
- package/dist/cjs/internal/runtime.js +92 -0
- package/dist/cjs/internal/runtime.js.map +1 -0
- package/dist/dts/Atom.d.ts +597 -0
- package/dist/dts/Atom.d.ts.map +1 -0
- package/dist/dts/AtomRef.d.ts +55 -0
- package/dist/dts/AtomRef.d.ts.map +1 -0
- package/dist/dts/Hydration.d.ts +27 -0
- package/dist/dts/Hydration.d.ts.map +1 -0
- package/dist/dts/Registry.d.ts +115 -0
- package/dist/dts/Registry.d.ts.map +1 -0
- package/dist/dts/Result.d.ts +351 -0
- package/dist/dts/Result.d.ts.map +1 -0
- package/dist/dts/index.d.ts +21 -0
- package/dist/dts/index.d.ts.map +1 -0
- package/dist/dts/internal/registry.d.ts +2 -0
- package/dist/dts/internal/registry.d.ts.map +1 -0
- package/dist/dts/internal/runtime.d.ts +2 -0
- package/dist/dts/internal/runtime.d.ts.map +1 -0
- package/dist/esm/Atom.js +1029 -0
- package/dist/esm/Atom.js.map +1 -0
- package/dist/esm/AtomRef.js +232 -0
- package/dist/esm/AtomRef.js.map +1 -0
- package/dist/esm/Hydration.js +71 -0
- package/dist/esm/Hydration.js.map +1 -0
- package/dist/esm/Registry.js +98 -0
- package/dist/esm/Registry.js.map +1 -0
- package/dist/esm/Result.js +403 -0
- package/dist/esm/Result.js.map +1 -0
- package/dist/esm/index.js +21 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/registry.js +672 -0
- package/dist/esm/internal/registry.js.map +1 -0
- package/dist/esm/internal/runtime.js +64 -0
- package/dist/esm/internal/runtime.js.map +1 -0
- package/dist/esm/package.json +4 -0
- package/package.json +72 -0
- package/src/Atom.ts +1865 -0
- package/src/AtomRef.ts +282 -0
- package/src/Hydration.ts +98 -0
- package/src/Registry.ts +204 -0
- package/src/Result.ts +767 -0
- package/src/index.ts +24 -0
- package/src/internal/registry.ts +810 -0
- package/src/internal/runtime.ts +63 -0
package/dist/esm/Atom.js
ADDED
|
@@ -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
|