effect 4.0.0-beta.4 → 4.0.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/FileSystem.d.ts +1 -1
- package/dist/FileSystem.d.ts.map +1 -1
- package/dist/FileSystem.js +3 -3
- package/dist/FileSystem.js.map +1 -1
- package/dist/Filter.d.ts +30 -1
- package/dist/Filter.d.ts.map +1 -1
- package/dist/Filter.js +15 -0
- package/dist/Filter.js.map +1 -1
- package/dist/PlatformError.d.ts +8 -7
- package/dist/PlatformError.d.ts.map +1 -1
- package/dist/PlatformError.js +2 -2
- package/dist/PlatformError.js.map +1 -1
- package/dist/Types.d.ts +6 -6
- package/dist/Types.d.ts.map +1 -1
- package/dist/unstable/persistence/KeyValueStore.js +2 -2
- package/dist/unstable/persistence/KeyValueStore.js.map +1 -1
- package/dist/unstable/reactivity/Atom.d.ts.map +1 -1
- package/dist/unstable/reactivity/Atom.js +12 -9
- package/dist/unstable/reactivity/Atom.js.map +1 -1
- package/dist/unstable/reactivity/Hydration.d.ts +39 -0
- package/dist/unstable/reactivity/Hydration.d.ts.map +1 -0
- package/dist/unstable/reactivity/Hydration.js +76 -0
- package/dist/unstable/reactivity/Hydration.js.map +1 -0
- package/dist/unstable/reactivity/index.d.ts +4 -0
- package/dist/unstable/reactivity/index.d.ts.map +1 -1
- package/dist/unstable/reactivity/index.js +4 -0
- package/dist/unstable/reactivity/index.js.map +1 -1
- package/package.json +1 -1
- package/src/FileSystem.ts +3 -4
- package/src/Filter.ts +48 -1
- package/src/PlatformError.ts +5 -5
- package/src/Types.ts +3 -2
- package/src/unstable/persistence/KeyValueStore.ts +2 -2
- package/src/unstable/reactivity/Atom.ts +7 -5
- package/src/unstable/reactivity/Hydration.ts +112 -0
- package/src/unstable/reactivity/index.ts +5 -0
package/src/Filter.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { dual } from "./Function.ts"
|
|
|
7
7
|
import * as Option from "./Option.ts"
|
|
8
8
|
import * as Predicate from "./Predicate.ts"
|
|
9
9
|
import * as Result from "./Result.ts"
|
|
10
|
-
import type { EqualsWith, ExcludeTag, ExtractTag, Tags } from "./Types.ts"
|
|
10
|
+
import type { EqualsWith, ExcludeTag, ExtractReason, ExtractTag, ReasonTags, Tags } from "./Types.ts"
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Represents a filter function that can transform inputs to outputs or filter them out.
|
|
@@ -456,6 +456,53 @@ const taggedImpl =
|
|
|
456
456
|
<Input>(input: Input): Result.Result<ExtractTag<Input, Tag>, ExcludeTag<Input, Tag>> =>
|
|
457
457
|
Predicate.isTagged(input, tag) ? Result.succeed(input as any) : Result.fail(input as ExcludeTag<Input, Tag>)
|
|
458
458
|
|
|
459
|
+
/**
|
|
460
|
+
* Creates a filter that extracts a reason from a tagged error.
|
|
461
|
+
*
|
|
462
|
+
* @since 4.0.0
|
|
463
|
+
* @category Constructors
|
|
464
|
+
*/
|
|
465
|
+
export const reason: {
|
|
466
|
+
/**
|
|
467
|
+
* Creates a filter that extracts a reason from a tagged error.
|
|
468
|
+
*
|
|
469
|
+
* @since 4.0.0
|
|
470
|
+
* @category Constructors
|
|
471
|
+
*/
|
|
472
|
+
<Input>(): <const Tag extends Tags<Input>, const ReasonTag extends ReasonTags<ExtractTag<Input, Tag>>>(
|
|
473
|
+
tag: Tag,
|
|
474
|
+
reasonTag: ReasonTag
|
|
475
|
+
) => Filter<Input, ExtractReason<ExtractTag<Input, Tag>, ReasonTag>, Input>
|
|
476
|
+
/**
|
|
477
|
+
* Creates a filter that extracts a reason from a tagged error.
|
|
478
|
+
*
|
|
479
|
+
* @since 4.0.0
|
|
480
|
+
* @category Constructors
|
|
481
|
+
*/
|
|
482
|
+
<Input, const Tag extends Tags<Input>, const ReasonTag extends ReasonTags<ExtractTag<Input, Tag>>>(tag: Tag, reasonTag: ReasonTag): Filter<Input, ExtractReason<ExtractTag<Input, Tag>, ReasonTag>, Input>
|
|
483
|
+
/**
|
|
484
|
+
* Creates a filter that extracts a reason from a tagged error.
|
|
485
|
+
*
|
|
486
|
+
* @since 4.0.0
|
|
487
|
+
* @category Constructors
|
|
488
|
+
*/
|
|
489
|
+
<const Tag extends string, const ReasonTag extends string>(tag: Tag, reasonTag: ReasonTag): <Input>(input: Input) => Result.Result<ExtractReason<ExtractTag<Input, Tag>, ReasonTag>, Input>
|
|
490
|
+
} = function() {
|
|
491
|
+
return arguments.length === 0 ? reasonImpl : reasonImpl(arguments[0] as any, arguments[1] as any)
|
|
492
|
+
} as any
|
|
493
|
+
|
|
494
|
+
const reasonImpl =
|
|
495
|
+
<const Tag extends string, const ReasonTag extends string>(tag: Tag, reasonTag: ReasonTag) =>
|
|
496
|
+
<Input>(input: Input): Result.Result<ExtractTag<Input, Tag>, ExcludeTag<Input, Tag>> => {
|
|
497
|
+
if (
|
|
498
|
+
Predicate.isTagged(input, tag) && Predicate.hasProperty(input, "reason") &&
|
|
499
|
+
Predicate.isTagged(input.reason, reasonTag)
|
|
500
|
+
) {
|
|
501
|
+
return Result.succeed(input.reason as any)
|
|
502
|
+
}
|
|
503
|
+
return Result.fail(input as any)
|
|
504
|
+
}
|
|
505
|
+
|
|
459
506
|
/**
|
|
460
507
|
* Creates a filter that only passes values equal to the specified value using structural equality.
|
|
461
508
|
*
|
package/src/PlatformError.ts
CHANGED
|
@@ -27,7 +27,7 @@ export class BadArgument extends Data.TaggedError("BadArgument")<{
|
|
|
27
27
|
* @since 4.0.0
|
|
28
28
|
* @category Model
|
|
29
29
|
*/
|
|
30
|
-
export type
|
|
30
|
+
export type SystemErrorTag =
|
|
31
31
|
| "AlreadyExists"
|
|
32
32
|
| "BadResource"
|
|
33
33
|
| "Busy"
|
|
@@ -44,8 +44,8 @@ export type SystemErrorKind =
|
|
|
44
44
|
* @since 4.0.0
|
|
45
45
|
* @category models
|
|
46
46
|
*/
|
|
47
|
-
export class SystemError extends Data.
|
|
48
|
-
|
|
47
|
+
export class SystemError extends Data.Error<{
|
|
48
|
+
_tag: SystemErrorTag
|
|
49
49
|
module: string
|
|
50
50
|
method: string
|
|
51
51
|
description?: string | undefined
|
|
@@ -57,7 +57,7 @@ export class SystemError extends Data.TaggedError("SystemError")<{
|
|
|
57
57
|
* @since 4.0.0
|
|
58
58
|
*/
|
|
59
59
|
override get message(): string {
|
|
60
|
-
return `${this.
|
|
60
|
+
return `${this._tag}: ${this.module}.${this.method}${
|
|
61
61
|
this.pathOrDescriptor !== undefined ? ` (${this.pathOrDescriptor})` : ""
|
|
62
62
|
}${this.description ? `: ${this.description}` : ""}`
|
|
63
63
|
}
|
|
@@ -93,7 +93,7 @@ export class PlatformError extends Data.TaggedError("PlatformError")<{
|
|
|
93
93
|
* @category constructors
|
|
94
94
|
*/
|
|
95
95
|
export const systemError = (options: {
|
|
96
|
-
readonly
|
|
96
|
+
readonly _tag: SystemErrorTag
|
|
97
97
|
readonly module: string
|
|
98
98
|
readonly method: string
|
|
99
99
|
readonly description?: string | undefined
|
package/src/Types.ts
CHANGED
|
@@ -205,7 +205,7 @@ export type ExcludeTag<E, K extends string> = Exclude<E, { readonly _tag: K }>
|
|
|
205
205
|
* @category types
|
|
206
206
|
* @since 2.0.0
|
|
207
207
|
*/
|
|
208
|
-
export type ExtractTag<E, K extends string> =
|
|
208
|
+
export type ExtractTag<E, K extends string> = E extends { readonly _tag: infer T } ? K extends T ? E : never : never
|
|
209
209
|
|
|
210
210
|
/**
|
|
211
211
|
* Transforms a union type into an intersection type.
|
|
@@ -914,7 +914,8 @@ export type ReasonTags<E> = E extends { readonly reason: { readonly _tag: string
|
|
|
914
914
|
* @category types
|
|
915
915
|
*/
|
|
916
916
|
export type ExtractReason<E, K extends string> = E extends { readonly reason: infer R }
|
|
917
|
-
?
|
|
917
|
+
? R extends { readonly _tag: infer T } ? K extends T ? R : never
|
|
918
|
+
: never
|
|
918
919
|
: never
|
|
919
920
|
|
|
920
921
|
/**
|
|
@@ -312,7 +312,7 @@ export const layerFileSystem = (
|
|
|
312
312
|
fs.readFileString(keyPath(key)),
|
|
313
313
|
"PlatformError",
|
|
314
314
|
(cause) =>
|
|
315
|
-
cause.reason._tag === "
|
|
315
|
+
cause.reason._tag === "NotFound" ? Effect.undefined : Effect.fail(
|
|
316
316
|
new KeyValueStoreError({
|
|
317
317
|
method: "get",
|
|
318
318
|
key,
|
|
@@ -326,7 +326,7 @@ export const layerFileSystem = (
|
|
|
326
326
|
fs.readFile(keyPath(key)),
|
|
327
327
|
"PlatformError",
|
|
328
328
|
(cause) =>
|
|
329
|
-
cause.reason._tag === "
|
|
329
|
+
cause.reason._tag === "NotFound" ? Effect.undefined : Effect.fail(
|
|
330
330
|
new KeyValueStoreError({
|
|
331
331
|
method: "getUint8Array",
|
|
332
332
|
key,
|
|
@@ -2291,16 +2291,18 @@ export const serializable: {
|
|
|
2291
2291
|
} = dual(2, <R extends Atom<any>, A, I>(self: R, options: {
|
|
2292
2292
|
readonly key: string
|
|
2293
2293
|
readonly schema: Schema.Codec<A, I>
|
|
2294
|
-
}): R & Serializable<any> =>
|
|
2295
|
-
|
|
2294
|
+
}): R & Serializable<any> => {
|
|
2295
|
+
const codecJson = Schema.toCodecJson(options.schema)
|
|
2296
|
+
return Object.assign(Object.create(Object.getPrototypeOf(self)), {
|
|
2296
2297
|
...self,
|
|
2297
2298
|
label: self.label ?? [options.key, new Error().stack?.split("\n")[5] ?? ""],
|
|
2298
2299
|
[SerializableTypeId]: {
|
|
2299
2300
|
key: options.key,
|
|
2300
|
-
encode: Schema.encodeSync(
|
|
2301
|
-
decode: Schema.decodeSync(
|
|
2301
|
+
encode: Schema.encodeSync(codecJson),
|
|
2302
|
+
decode: Schema.decodeSync(codecJson)
|
|
2302
2303
|
}
|
|
2303
|
-
})
|
|
2304
|
+
})
|
|
2305
|
+
})
|
|
2304
2306
|
|
|
2305
2307
|
/**
|
|
2306
2308
|
* @since 4.0.0
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 4.0.0
|
|
3
|
+
*/
|
|
4
|
+
import * as AsyncResult from "./AsyncResult.ts"
|
|
5
|
+
import * as Atom from "./Atom.ts"
|
|
6
|
+
import type * as AtomRegistry from "./AtomRegistry.ts"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @since 4.0.0
|
|
10
|
+
* @category models
|
|
11
|
+
*/
|
|
12
|
+
export interface DehydratedAtom {
|
|
13
|
+
readonly "~effect/reactivity/DehydratedAtom": true
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @since 4.0.0
|
|
18
|
+
* @category models
|
|
19
|
+
*/
|
|
20
|
+
export interface DehydratedAtomValue extends DehydratedAtom {
|
|
21
|
+
readonly key: string
|
|
22
|
+
readonly value: unknown
|
|
23
|
+
readonly dehydratedAt: number
|
|
24
|
+
readonly resultPromise?: Promise<unknown> | undefined
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @since 4.0.0
|
|
29
|
+
* @category dehydration
|
|
30
|
+
*/
|
|
31
|
+
export const dehydrate = (
|
|
32
|
+
registry: AtomRegistry.AtomRegistry,
|
|
33
|
+
options?: {
|
|
34
|
+
/**
|
|
35
|
+
* How to encode `AsyncResult.Initial` values. Default is "ignore".
|
|
36
|
+
*/
|
|
37
|
+
readonly encodeInitialAs?: "ignore" | "promise" | "value-only" | undefined
|
|
38
|
+
}
|
|
39
|
+
): Array<DehydratedAtom> => {
|
|
40
|
+
const encodeInitialResultMode = options?.encodeInitialAs ?? "ignore"
|
|
41
|
+
const arr: Array<DehydratedAtomValue> = []
|
|
42
|
+
const now = Date.now()
|
|
43
|
+
registry.getNodes().forEach((node, key) => {
|
|
44
|
+
if (!Atom.isSerializable(node.atom)) return
|
|
45
|
+
const atom = node.atom
|
|
46
|
+
const value = node.value()
|
|
47
|
+
const isInitial = AsyncResult.isAsyncResult(value) && AsyncResult.isInitial(value)
|
|
48
|
+
if (encodeInitialResultMode === "ignore" && isInitial) return
|
|
49
|
+
const encodedValue = atom[Atom.SerializableTypeId].encode(value)
|
|
50
|
+
|
|
51
|
+
// Create a promise that resolves when the atom moves out of Initial state
|
|
52
|
+
let resultPromise: Promise<unknown> | undefined
|
|
53
|
+
if (encodeInitialResultMode === "promise" && isInitial) {
|
|
54
|
+
resultPromise = new Promise((resolve) => {
|
|
55
|
+
const unsubscribe = registry.subscribe(atom, (newValue) => {
|
|
56
|
+
if (AsyncResult.isAsyncResult(newValue) && !AsyncResult.isInitial(newValue)) {
|
|
57
|
+
resolve(atom[Atom.SerializableTypeId].encode(newValue))
|
|
58
|
+
unsubscribe()
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
arr.push({
|
|
65
|
+
"~effect/reactivity/DehydratedAtom": true,
|
|
66
|
+
key: key as string,
|
|
67
|
+
value: encodedValue,
|
|
68
|
+
dehydratedAt: now,
|
|
69
|
+
resultPromise
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
return arr as any
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @since 4.0.0
|
|
77
|
+
* @category dehydration
|
|
78
|
+
*/
|
|
79
|
+
export const toValues = (state: ReadonlyArray<DehydratedAtom>): Array<DehydratedAtomValue> => state as any
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @since 4.0.0
|
|
83
|
+
* @category hydration
|
|
84
|
+
*/
|
|
85
|
+
export const hydrate = (
|
|
86
|
+
registry: AtomRegistry.AtomRegistry,
|
|
87
|
+
dehydratedState: Iterable<DehydratedAtom>
|
|
88
|
+
): void => {
|
|
89
|
+
for (const datom of (dehydratedState as Iterable<DehydratedAtomValue>)) {
|
|
90
|
+
registry.setSerializable(datom.key, datom.value)
|
|
91
|
+
|
|
92
|
+
// If there's a resultPromise, it means this was in Initial state when dehydrated
|
|
93
|
+
// and we should wait for it to resolve to a non-Initial state, then update the registry
|
|
94
|
+
if (!datom.resultPromise) continue
|
|
95
|
+
datom.resultPromise.then((resolvedValue) => {
|
|
96
|
+
// Try to update the existing node directly instead of using setSerializable
|
|
97
|
+
const nodes = registry.getNodes()
|
|
98
|
+
const node = nodes.get(datom.key)
|
|
99
|
+
if (node) {
|
|
100
|
+
// Decode the resolved value using the node's atom serializable decoder
|
|
101
|
+
const atom = node.atom as any
|
|
102
|
+
if (atom[Atom.SerializableTypeId]) {
|
|
103
|
+
const decoded = atom[Atom.SerializableTypeId].decode(resolvedValue)
|
|
104
|
+
;(node as any).setValue(decoded)
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
// Fallback to setSerializable if node doesn't exist yet
|
|
108
|
+
registry.setSerializable(datom.key, resolvedValue)
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
}
|