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.
Files changed (36) hide show
  1. package/dist/FileSystem.d.ts +1 -1
  2. package/dist/FileSystem.d.ts.map +1 -1
  3. package/dist/FileSystem.js +3 -3
  4. package/dist/FileSystem.js.map +1 -1
  5. package/dist/Filter.d.ts +30 -1
  6. package/dist/Filter.d.ts.map +1 -1
  7. package/dist/Filter.js +15 -0
  8. package/dist/Filter.js.map +1 -1
  9. package/dist/PlatformError.d.ts +8 -7
  10. package/dist/PlatformError.d.ts.map +1 -1
  11. package/dist/PlatformError.js +2 -2
  12. package/dist/PlatformError.js.map +1 -1
  13. package/dist/Types.d.ts +6 -6
  14. package/dist/Types.d.ts.map +1 -1
  15. package/dist/unstable/persistence/KeyValueStore.js +2 -2
  16. package/dist/unstable/persistence/KeyValueStore.js.map +1 -1
  17. package/dist/unstable/reactivity/Atom.d.ts.map +1 -1
  18. package/dist/unstable/reactivity/Atom.js +12 -9
  19. package/dist/unstable/reactivity/Atom.js.map +1 -1
  20. package/dist/unstable/reactivity/Hydration.d.ts +39 -0
  21. package/dist/unstable/reactivity/Hydration.d.ts.map +1 -0
  22. package/dist/unstable/reactivity/Hydration.js +76 -0
  23. package/dist/unstable/reactivity/Hydration.js.map +1 -0
  24. package/dist/unstable/reactivity/index.d.ts +4 -0
  25. package/dist/unstable/reactivity/index.d.ts.map +1 -1
  26. package/dist/unstable/reactivity/index.js +4 -0
  27. package/dist/unstable/reactivity/index.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/FileSystem.ts +3 -4
  30. package/src/Filter.ts +48 -1
  31. package/src/PlatformError.ts +5 -5
  32. package/src/Types.ts +3 -2
  33. package/src/unstable/persistence/KeyValueStore.ts +2 -2
  34. package/src/unstable/reactivity/Atom.ts +7 -5
  35. package/src/unstable/reactivity/Hydration.ts +112 -0
  36. 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
  *
@@ -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 SystemErrorKind =
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.TaggedError("SystemError")<{
48
- kind: SystemErrorKind
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.kind}: ${this.module}.${this.method}${
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 kind: SystemErrorKind
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> = Extract<E, { readonly _tag: K }>
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
- ? Extract<R, { readonly _tag: K }>
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 === "SystemError" && cause.reason.kind === "NotFound" ? Effect.undefined : Effect.fail(
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 === "SystemError" && cause.reason.kind === "NotFound" ? Effect.undefined : Effect.fail(
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
- Object.assign(Object.create(Object.getPrototypeOf(self)), {
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(options.schema),
2301
- decode: Schema.decodeSync(options.schema)
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
+ }
@@ -29,6 +29,11 @@ export * as AtomRef from "./AtomRef.ts"
29
29
  */
30
30
  export * as AtomRegistry from "./AtomRegistry.ts"
31
31
 
32
+ /**
33
+ * @since 4.0.0
34
+ */
35
+ export * as Hydration from "./Hydration.ts"
36
+
32
37
  /**
33
38
  * @since 4.0.0
34
39
  */