effect 4.0.0-beta.44 → 4.0.0-beta.45

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 (151) hide show
  1. package/dist/Equal.d.ts.map +1 -1
  2. package/dist/Equal.js +16 -0
  3. package/dist/Equal.js.map +1 -1
  4. package/dist/Hash.js +1 -1
  5. package/dist/Hash.js.map +1 -1
  6. package/dist/Semaphore.d.ts +1 -1
  7. package/dist/Semaphore.d.ts.map +1 -1
  8. package/dist/Semaphore.js +1 -3
  9. package/dist/Semaphore.js.map +1 -1
  10. package/dist/unstable/ai/McpServer.d.ts.map +1 -1
  11. package/dist/unstable/ai/McpServer.js +24 -21
  12. package/dist/unstable/ai/McpServer.js.map +1 -1
  13. package/dist/unstable/eventlog/Event.d.ts +0 -6
  14. package/dist/unstable/eventlog/Event.d.ts.map +1 -1
  15. package/dist/unstable/eventlog/Event.js +0 -5
  16. package/dist/unstable/eventlog/Event.js.map +1 -1
  17. package/dist/unstable/eventlog/EventGroup.d.ts +0 -2
  18. package/dist/unstable/eventlog/EventGroup.d.ts.map +1 -1
  19. package/dist/unstable/eventlog/EventGroup.js +0 -2
  20. package/dist/unstable/eventlog/EventGroup.js.map +1 -1
  21. package/dist/unstable/eventlog/EventJournal.d.ts +22 -5
  22. package/dist/unstable/eventlog/EventJournal.d.ts.map +1 -1
  23. package/dist/unstable/eventlog/EventJournal.js +126 -67
  24. package/dist/unstable/eventlog/EventJournal.js.map +1 -1
  25. package/dist/unstable/eventlog/EventLog.d.ts +88 -34
  26. package/dist/unstable/eventlog/EventLog.d.ts.map +1 -1
  27. package/dist/unstable/eventlog/EventLog.js +215 -141
  28. package/dist/unstable/eventlog/EventLog.js.map +1 -1
  29. package/dist/unstable/eventlog/EventLogEncryption.d.ts +9 -7
  30. package/dist/unstable/eventlog/EventLogEncryption.d.ts.map +1 -1
  31. package/dist/unstable/eventlog/EventLogEncryption.js +13 -15
  32. package/dist/unstable/eventlog/EventLogEncryption.js.map +1 -1
  33. package/dist/unstable/eventlog/EventLogMessage.d.ts +228 -0
  34. package/dist/unstable/eventlog/EventLogMessage.d.ts.map +1 -0
  35. package/dist/unstable/eventlog/EventLogMessage.js +214 -0
  36. package/dist/unstable/eventlog/EventLogMessage.js.map +1 -0
  37. package/dist/unstable/eventlog/EventLogRemote.d.ts +109 -194
  38. package/dist/unstable/eventlog/EventLogRemote.d.ts.map +1 -1
  39. package/dist/unstable/eventlog/EventLogRemote.js +165 -320
  40. package/dist/unstable/eventlog/EventLogRemote.js.map +1 -1
  41. package/dist/unstable/eventlog/EventLogServer.d.ts +25 -47
  42. package/dist/unstable/eventlog/EventLogServer.d.ts.map +1 -1
  43. package/dist/unstable/eventlog/EventLogServer.js +127 -198
  44. package/dist/unstable/eventlog/EventLogServer.js.map +1 -1
  45. package/dist/unstable/eventlog/EventLogServerEncrypted.d.ts +60 -0
  46. package/dist/unstable/eventlog/EventLogServerEncrypted.d.ts.map +1 -0
  47. package/dist/unstable/eventlog/EventLogServerEncrypted.js +166 -0
  48. package/dist/unstable/eventlog/EventLogServerEncrypted.js.map +1 -0
  49. package/dist/unstable/eventlog/EventLogServerUnencrypted.d.ts +183 -0
  50. package/dist/unstable/eventlog/EventLogServerUnencrypted.d.ts.map +1 -0
  51. package/dist/unstable/eventlog/EventLogServerUnencrypted.js +461 -0
  52. package/dist/unstable/eventlog/EventLogServerUnencrypted.js.map +1 -0
  53. package/dist/unstable/eventlog/EventLogSessionAuth.d.ts +117 -0
  54. package/dist/unstable/eventlog/EventLogSessionAuth.d.ts.map +1 -0
  55. package/dist/unstable/eventlog/EventLogSessionAuth.js +284 -0
  56. package/dist/unstable/eventlog/EventLogSessionAuth.js.map +1 -0
  57. package/dist/unstable/eventlog/{SqlEventLogJournal.d.ts → SqlEventJournal.d.ts} +2 -2
  58. package/dist/unstable/eventlog/SqlEventJournal.d.ts.map +1 -0
  59. package/dist/unstable/eventlog/{SqlEventLogJournal.js → SqlEventJournal.js} +20 -14
  60. package/dist/unstable/eventlog/SqlEventJournal.js.map +1 -0
  61. package/dist/unstable/eventlog/{SqlEventLogServer.d.ts → SqlEventLogServerEncrypted.d.ts} +5 -5
  62. package/dist/unstable/eventlog/SqlEventLogServerEncrypted.d.ts.map +1 -0
  63. package/dist/unstable/eventlog/{SqlEventLogServer.js → SqlEventLogServerEncrypted.js} +65 -24
  64. package/dist/unstable/eventlog/SqlEventLogServerEncrypted.js.map +1 -0
  65. package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.d.ts +25 -0
  66. package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.d.ts.map +1 -0
  67. package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.js +354 -0
  68. package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.js.map +1 -0
  69. package/dist/unstable/eventlog/index.d.ts +22 -2
  70. package/dist/unstable/eventlog/index.d.ts.map +1 -1
  71. package/dist/unstable/eventlog/index.js +22 -2
  72. package/dist/unstable/eventlog/index.js.map +1 -1
  73. package/dist/unstable/eventlog/internal/identityRootSecretDerivation.d.ts +2 -0
  74. package/dist/unstable/eventlog/internal/identityRootSecretDerivation.d.ts.map +1 -0
  75. package/dist/unstable/eventlog/internal/identityRootSecretDerivation.js +89 -0
  76. package/dist/unstable/eventlog/internal/identityRootSecretDerivation.js.map +1 -0
  77. package/dist/unstable/reactivity/AtomHttpApi.d.ts +1 -2
  78. package/dist/unstable/reactivity/AtomHttpApi.d.ts.map +1 -1
  79. package/dist/unstable/reactivity/AtomHttpApi.js +2 -2
  80. package/dist/unstable/reactivity/AtomHttpApi.js.map +1 -1
  81. package/dist/unstable/reactivity/AtomRpc.d.ts +1 -2
  82. package/dist/unstable/reactivity/AtomRpc.d.ts.map +1 -1
  83. package/dist/unstable/reactivity/AtomRpc.js +3 -3
  84. package/dist/unstable/reactivity/AtomRpc.js.map +1 -1
  85. package/dist/unstable/rpc/Rpc.d.ts +25 -4
  86. package/dist/unstable/rpc/Rpc.d.ts.map +1 -1
  87. package/dist/unstable/rpc/Rpc.js +26 -0
  88. package/dist/unstable/rpc/Rpc.js.map +1 -1
  89. package/dist/unstable/rpc/RpcClient.d.ts +3 -13
  90. package/dist/unstable/rpc/RpcClient.d.ts.map +1 -1
  91. package/dist/unstable/rpc/RpcClient.js +47 -23
  92. package/dist/unstable/rpc/RpcClient.js.map +1 -1
  93. package/dist/unstable/rpc/RpcGroup.d.ts +1 -1
  94. package/dist/unstable/rpc/RpcGroup.d.ts.map +1 -1
  95. package/dist/unstable/rpc/RpcMiddleware.d.ts +2 -2
  96. package/dist/unstable/rpc/RpcMiddleware.d.ts.map +1 -1
  97. package/dist/unstable/rpc/RpcServer.d.ts.map +1 -1
  98. package/dist/unstable/rpc/RpcServer.js +3 -2
  99. package/dist/unstable/rpc/RpcServer.js.map +1 -1
  100. package/dist/unstable/rpc/Utils.d.ts +6 -0
  101. package/dist/unstable/rpc/Utils.d.ts.map +1 -1
  102. package/dist/unstable/rpc/Utils.js +44 -0
  103. package/dist/unstable/rpc/Utils.js.map +1 -1
  104. package/dist/unstable/schema/Model.d.ts +2 -2
  105. package/dist/unstable/schema/Model.d.ts.map +1 -1
  106. package/dist/unstable/schema/Model.js +2 -4
  107. package/dist/unstable/schema/Model.js.map +1 -1
  108. package/dist/unstable/schema/VariantSchema.d.ts +1 -1
  109. package/dist/unstable/schema/VariantSchema.d.ts.map +1 -1
  110. package/dist/unstable/schema/VariantSchema.js +1 -12
  111. package/dist/unstable/schema/VariantSchema.js.map +1 -1
  112. package/dist/unstable/workers/Transferable.d.ts +1 -1
  113. package/dist/unstable/workers/Transferable.d.ts.map +1 -1
  114. package/dist/unstable/workers/Transferable.js +1 -1
  115. package/dist/unstable/workers/Transferable.js.map +1 -1
  116. package/package.json +1 -1
  117. package/src/Equal.ts +17 -0
  118. package/src/Hash.ts +2 -2
  119. package/src/Semaphore.ts +2 -4
  120. package/src/unstable/ai/McpServer.ts +24 -22
  121. package/src/unstable/eventlog/Event.ts +0 -8
  122. package/src/unstable/eventlog/EventGroup.ts +0 -4
  123. package/src/unstable/eventlog/EventJournal.ts +144 -76
  124. package/src/unstable/eventlog/EventLog.ts +342 -221
  125. package/src/unstable/eventlog/EventLogEncryption.ts +16 -30
  126. package/src/unstable/eventlog/EventLogMessage.ts +277 -0
  127. package/src/unstable/eventlog/EventLogRemote.ts +261 -408
  128. package/src/unstable/eventlog/EventLogServer.ts +182 -274
  129. package/src/unstable/eventlog/EventLogServerEncrypted.ts +206 -0
  130. package/src/unstable/eventlog/EventLogServerUnencrypted.ts +749 -0
  131. package/src/unstable/eventlog/EventLogSessionAuth.ts +437 -0
  132. package/src/unstable/eventlog/{SqlEventLogJournal.ts → SqlEventJournal.ts} +26 -18
  133. package/src/unstable/eventlog/{SqlEventLogServer.ts → SqlEventLogServerEncrypted.ts} +102 -40
  134. package/src/unstable/eventlog/SqlEventLogServerUnencrypted.ts +500 -0
  135. package/src/unstable/eventlog/index.ts +27 -2
  136. package/src/unstable/eventlog/internal/identityRootSecretDerivation.ts +153 -0
  137. package/src/unstable/reactivity/AtomHttpApi.ts +23 -8
  138. package/src/unstable/reactivity/AtomRpc.ts +16 -5
  139. package/src/unstable/rpc/Rpc.ts +42 -4
  140. package/src/unstable/rpc/RpcClient.ts +59 -24
  141. package/src/unstable/rpc/RpcGroup.ts +1 -1
  142. package/src/unstable/rpc/RpcMiddleware.ts +2 -2
  143. package/src/unstable/rpc/RpcServer.ts +5 -3
  144. package/src/unstable/rpc/Utils.ts +59 -0
  145. package/src/unstable/schema/Model.ts +4 -6
  146. package/src/unstable/schema/VariantSchema.ts +4 -17
  147. package/src/unstable/workers/Transferable.ts +9 -11
  148. package/dist/unstable/eventlog/SqlEventLogJournal.d.ts.map +0 -1
  149. package/dist/unstable/eventlog/SqlEventLogJournal.js.map +0 -1
  150. package/dist/unstable/eventlog/SqlEventLogServer.d.ts.map +0 -1
  151. package/dist/unstable/eventlog/SqlEventLogServer.js.map +0 -1
@@ -4,7 +4,7 @@
4
4
  import * as Context from "../../Context.ts"
5
5
  import * as Effect from "../../Effect.ts"
6
6
  import * as FiberMap from "../../FiberMap.ts"
7
- import { identity } from "../../Function.ts"
7
+ import { constant, identity } from "../../Function.ts"
8
8
  import * as Layer from "../../Layer.ts"
9
9
  import type { Pipeable } from "../../Pipeable.ts"
10
10
  import { pipeArguments } from "../../Pipeable.ts"
@@ -13,24 +13,146 @@ import * as PubSub from "../../PubSub.ts"
13
13
  import * as Queue from "../../Queue.ts"
14
14
  import type * as Record from "../../Record.ts"
15
15
  import * as Redacted from "../../Redacted.ts"
16
+ import * as Schedule from "../../Schedule.ts"
16
17
  import * as Schema from "../../Schema.ts"
18
+ import * as SchemaGetter from "../../SchemaGetter.ts"
17
19
  import type * as Scope from "../../Scope.ts"
18
- import * as Semaphore from "../../Semaphore.ts"
19
20
  import type { Covariant } from "../../Types.ts"
20
21
  import { Reactivity } from "../reactivity/Reactivity.ts"
21
22
  import * as ReactivityLayer from "../reactivity/Reactivity.ts"
22
- import * as Event from "./Event.ts"
23
+ import type * as Event from "./Event.ts"
23
24
  import type * as EventGroup from "./EventGroup.ts"
24
- import {
25
- Entry,
26
- EventJournal,
27
- type EventJournalError,
28
- makeEntryIdUnsafe,
29
- type RemoteEntry,
30
- type RemoteId
31
- } from "./EventJournal.ts"
25
+ import { Entry, EventJournal, type EventJournalError, makeEntryIdUnsafe, type RemoteId } from "./EventJournal.ts"
26
+ import * as EventLogEncryption from "./EventLogEncryption.ts"
27
+ import { StoreId } from "./EventLogMessage.ts"
32
28
  import type { EventLogRemote } from "./EventLogRemote.ts"
33
29
 
30
+ /**
31
+ * @since 4.0.0
32
+ * @category tags
33
+ */
34
+ export class EventLog extends Context.Service<EventLog, {
35
+ readonly write: <Groups extends EventGroup.Any, Tag extends Event.Tag<EventGroup.Events<Groups>>>(options: {
36
+ readonly schema: EventLogSchema<Groups>
37
+ readonly event: Tag
38
+ readonly payload: Event.PayloadWithTag<EventGroup.Events<Groups>, Tag>
39
+ }) => Effect.Effect<
40
+ Event.SuccessWithTag<EventGroup.Events<Groups>, Tag>,
41
+ Event.ErrorWithTag<EventGroup.Events<Groups>, Tag> | EventJournalError
42
+ >
43
+ readonly entries: Effect.Effect<ReadonlyArray<Entry>, EventJournalError>
44
+ readonly destroy: Effect.Effect<void, EventJournalError>
45
+ }>()("effect/eventlog/EventLog") {}
46
+
47
+ /**
48
+ * @since 4.0.0
49
+ * @category Registry
50
+ */
51
+ export class Registry extends Context.Service<Registry, {
52
+ readonly registerHandlerUnsafe: (options: {
53
+ readonly event: string
54
+ readonly handler: Handlers.Item<any>
55
+ }) => void
56
+
57
+ readonly handlers: ReadonlyMap<string, Handlers.Item<any>>
58
+
59
+ readonly registerCompaction: (options: {
60
+ readonly events: ReadonlyArray<string>
61
+ readonly effect: (options: {
62
+ readonly entries: ReadonlyArray<Entry>
63
+ readonly write: (entry: Entry) => Effect.Effect<void>
64
+ }) => Effect.Effect<void>
65
+ }) => Effect.Effect<void, never, Scope.Scope>
66
+
67
+ readonly compactors: ReadonlyMap<string, {
68
+ readonly events: ReadonlySet<string>
69
+ readonly effect: (options: {
70
+ readonly entries: ReadonlyArray<Entry>
71
+ readonly write: (entry: Entry) => Effect.Effect<void>
72
+ }) => Effect.Effect<void>
73
+ }>
74
+
75
+ readonly registerRemote: (remote: EventLogRemote["Service"]) => Effect.Effect<void, never, Scope.Scope>
76
+ readonly handleRemote: (handler: (remote: EventLogRemote["Service"]) => Effect.Effect<void>) => Effect.Effect<void>
77
+
78
+ readonly registerReactivity: (keys: Record<string, ReadonlyArray<string>>) => Effect.Effect<void, never, Scope.Scope>
79
+
80
+ readonly reactivityKeys: Record<string, ReadonlyArray<string>>
81
+ }>()("effect/unstable/eventlog/EventLog/Registry") {}
82
+
83
+ /**
84
+ * @since 4.0.0
85
+ * @category Registry
86
+ */
87
+ export const layerRegistry = Layer.effect(
88
+ Registry,
89
+ Effect.gen(function*() {
90
+ const handlers = new Map<string, Handlers.Item<any>>()
91
+ const compactors = new Map<string, {
92
+ readonly events: ReadonlySet<string>
93
+ readonly effect: (options: {
94
+ readonly entries: ReadonlyArray<Entry>
95
+ readonly write: (entry: Entry) => Effect.Effect<void>
96
+ }) => Effect.Effect<void>
97
+ }>()
98
+
99
+ const remoteFiberMap = yield* FiberMap.make<RemoteId>()
100
+ const remotes = new Map<RemoteId, EventLogRemote["Service"]>()
101
+ let remoteHandler: (remote: EventLogRemote["Service"]) => Effect.Effect<void> = (_) => Effect.void
102
+
103
+ const reactivityKeys: Record<string, ReadonlyArray<string>> = {}
104
+ return Registry.of({
105
+ registerHandlerUnsafe(options) {
106
+ handlers.set(options.event, options.handler)
107
+ },
108
+ handlers,
109
+ registerCompaction: (options) =>
110
+ Effect.sync(() => {
111
+ const events = new Set(options.events)
112
+ const compactor = {
113
+ events,
114
+ effect: options.effect
115
+ }
116
+ for (const event of options.events) {
117
+ compactors.set(event, compactor)
118
+ }
119
+ }),
120
+ compactors,
121
+ registerRemote: (remote) =>
122
+ Effect.acquireRelease(
123
+ Effect.suspend(() => {
124
+ remotes.set(remote.id, remote)
125
+ return Effect.asVoid(FiberMap.run(remoteFiberMap, remote.id, remoteHandler(remote)))
126
+ }),
127
+ () => {
128
+ remotes.delete(remote.id)
129
+ return FiberMap.remove(remoteFiberMap, remote.id)
130
+ }
131
+ ),
132
+ handleRemote(handler) {
133
+ remoteHandler = handler
134
+ return Effect.forEach(remotes, ([id, remote]) => FiberMap.run(remoteFiberMap, id, handler(remote)), {
135
+ discard: true
136
+ })
137
+ },
138
+ registerReactivity: (keys) =>
139
+ Effect.sync(() => {
140
+ Object.assign(reactivityKeys, keys)
141
+ }),
142
+ reactivityKeys
143
+ })
144
+ })
145
+ )
146
+
147
+ /**
148
+ * @since 4.0.0
149
+ * @category models
150
+ */
151
+ export class Identity extends Context.Service<Identity, {
152
+ readonly publicKey: string
153
+ readonly privateKey: Redacted.Redacted<Uint8Array<ArrayBuffer>>
154
+ }>()("effect/eventlog/EventLog/Identity") {}
155
+
34
156
  /**
35
157
  * @since 4.0.0
36
158
  * @category schema
@@ -107,16 +229,15 @@ export interface Handlers<
107
229
  */
108
230
  handle<Tag extends Event.Tag<Events>, R1>(
109
231
  name: Tag,
110
- handler: (
111
- options: {
112
- readonly payload: Event.PayloadWithTag<Events, Tag>
232
+ handler: (options: {
233
+ readonly storeId: StoreId
234
+ readonly payload: Event.PayloadWithTag<Events, Tag>
235
+ readonly entry: Entry
236
+ readonly conflicts: ReadonlyArray<{
113
237
  readonly entry: Entry
114
- readonly conflicts: ReadonlyArray<{
115
- readonly entry: Entry
116
- readonly payload: Event.PayloadWithTag<Events, Tag>
117
- }>
118
- }
119
- ) => Effect.Effect<Event.SuccessWithTag<Events, Tag>, Event.ErrorWithTag<Events, Tag>, R1>
238
+ readonly payload: Event.PayloadWithTag<Events, Tag>
239
+ }>
240
+ }) => Effect.Effect<Event.SuccessWithTag<Events, Tag>, Event.ErrorWithTag<Events, Tag>, R1>
120
241
  ): Handlers<
121
242
  R | R1,
122
243
  Event.ExcludeTag<Events, Tag>
@@ -144,6 +265,7 @@ export declare namespace Handlers {
144
265
  readonly event: Event.AnyWithProps
145
266
  readonly context: Context.Context<R>
146
267
  readonly handler: (options: {
268
+ readonly storeId: StoreId
147
269
  readonly payload: unknown
148
270
  readonly entry: Entry
149
271
  readonly conflicts: ReadonlyArray<{
@@ -211,10 +333,16 @@ export declare namespace Handlers {
211
333
  * @since 4.0.0
212
334
  * @category models
213
335
  */
214
- export class Identity extends Context.Service<Identity, {
215
- readonly publicKey: string
216
- readonly privateKey: Redacted.Redacted<Uint8Array>
217
- }>()("effect/eventlog/EventLog/Identity") {}
336
+ export class CurrentStoreId extends Context.Reference<StoreId>("effect/eventlog/EventLog/CurrentStoreId", {
337
+ defaultValue: constant(StoreId.make("default"))
338
+ }) {}
339
+
340
+ const RedactedUint8Array = Schema.Uint8ArrayFromBase64.pipe(
341
+ Schema.decodeTo(Schema.Redacted(Schema.Uint8Array), {
342
+ decode: SchemaGetter.transform((value) => Redacted.make(value)),
343
+ encode: SchemaGetter.transform((value) => Redacted.value(value))
344
+ })
345
+ )
218
346
 
219
347
  /**
220
348
  * @since 4.0.0
@@ -222,7 +350,7 @@ export class Identity extends Context.Service<Identity, {
222
350
  */
223
351
  export const IdentitySchema = Schema.Struct({
224
352
  publicKey: Schema.String,
225
- privateKey: Schema.Redacted(Schema.Uint8ArrayFromBase64)
353
+ privateKey: RedactedUint8Array
226
354
  })
227
355
 
228
356
  const IdentityEncodedSchema = Schema.Struct({
@@ -240,7 +368,7 @@ export const decodeIdentityString = (value: string): Identity["Service"] => {
240
368
  const decoded = Schema.decodeUnknownSync(IdentityStringSchema)(value)
241
369
  return {
242
370
  publicKey: decoded.publicKey,
243
- privateKey: Redacted.make(decoded.privateKey)
371
+ privateKey: Redacted.make(decoded.privateKey as Uint8Array<ArrayBuffer>)
244
372
  }
245
373
  }
246
374
 
@@ -258,10 +386,8 @@ export const encodeIdentityString = (identity: Identity["Service"]): string =>
258
386
  * @since 4.0.0
259
387
  * @category constructors
260
388
  */
261
- export const makeIdentityUnsafe = (): Identity["Service"] => ({
262
- publicKey: globalThis.crypto.randomUUID(),
263
- privateKey: Redacted.make(globalThis.crypto.getRandomValues(new Uint8Array(32)))
264
- })
389
+ export const makeIdentity: Effect.Effect<Identity["Service"], never, EventLogEncryption.EventLogEncryption> =
390
+ EventLogEncryption.EventLogEncryption.use((_) => _.generateIdentity)
265
391
 
266
392
  const handlersProto = {
267
393
  [HandlersTypeId]: {
@@ -303,10 +429,15 @@ const makeHandlers = (options: {
303
429
  export const group = <Events extends Event.Any, Return>(
304
430
  group: EventGroup.EventGroup<Events>,
305
431
  f: (handlers: Handlers<never, Events>) => Handlers.ValidateReturn<Return>
306
- ): Layer.Layer<Event.ToService<Events>, Handlers.Error<Return>, Exclude<Handlers.Services<Return>, Scope.Scope>> =>
307
- Layer.effectContext(
432
+ ): Layer.Layer<
433
+ Event.ToService<Events>,
434
+ Handlers.Error<Return>,
435
+ Exclude<Handlers.Services<Return>, Scope.Scope | Identity> | Registry
436
+ > =>
437
+ Layer.effectDiscard(
308
438
  Effect.gen(function*() {
309
- const context = yield* Effect.context<Handlers.Services<Return>>()
439
+ const registry = yield* Registry
440
+ const context = yield* Effect.context<Exclude<Handlers.Services<Return>, Scope.Scope | Identity>>()
310
441
  const result = f(makeHandlers({
311
442
  group: group as EventGroup.AnyWithProps,
312
443
  handlers: {},
@@ -315,14 +446,11 @@ export const group = <Events extends Event.Any, Return>(
315
446
  const handlers = Effect.isEffect(result)
316
447
  ? (yield* (result as unknown as Effect.Effect<Handlers<any>>))
317
448
  : (result as unknown as Handlers<any>)
318
- const contextMap = new Map<string, Handlers.Item<any>>()
319
449
  for (const tag in handlers.handlers) {
320
- const handler = handlers.handlers[tag]
321
- contextMap.set(handler.event.key, handlers.handlers[tag])
450
+ registry.registerHandlerUnsafe({ event: tag, handler: handlers.handlers[tag] })
322
451
  }
323
- return Context.makeUnsafe(contextMap)
324
452
  })
325
- )
453
+ ) as any
326
454
 
327
455
  /**
328
456
  * @since 4.0.0
@@ -339,13 +467,13 @@ export const groupCompaction = <Events extends Event.Any, R>(
339
467
  payload: Event.PayloadWithTag<Events, Tag>
340
468
  ) => Effect.Effect<void, never, Event.PayloadSchemaWithTag<Events, Tag>["EncodingServices"]>
341
469
  }) => Effect.Effect<void, never, R>
342
- ): Layer.Layer<never, never, Identity | EventJournal | R | Event.PayloadSchema<Events>["DecodingServices"]> =>
470
+ ): Layer.Layer<never, never, R | Event.PayloadSchema<Events>["DecodingServices"] | Registry> =>
343
471
  Layer.effectDiscard(
344
472
  Effect.gen(function*() {
345
- const log = yield* EventLog
473
+ const registry = yield* Registry
346
474
  const services = yield* Effect.context<R | Event.PayloadSchema<Events>["DecodingServices"]>()
347
475
 
348
- yield* log.registerCompaction({
476
+ yield* registry.registerCompaction({
349
477
  events: Object.keys(group.events),
350
478
  effect: Effect.fnUntraced(function*({ entries, write }): Effect.fn.Return<void> {
351
479
  const isEventTag = (tag: string): tag is Event.Tag<Events> => tag in group.events
@@ -413,8 +541,6 @@ export const groupCompaction = <Events extends Event.Any, R>(
413
541
  })
414
542
  })
415
543
  })
416
- ).pipe(
417
- Layer.provide(layerEventLog)
418
544
  )
419
545
 
420
546
  /**
@@ -426,175 +552,186 @@ export const groupReactivity = <Events extends Event.Any>(
426
552
  keys:
427
553
  | { readonly [Tag in Event.Tag<Events>]?: ReadonlyArray<string> }
428
554
  | ReadonlyArray<string>
429
- ): Layer.Layer<never, never, Identity | EventJournal> =>
555
+ ): Layer.Layer<never, never, Registry> =>
430
556
  Effect.gen(function*() {
431
- const log = yield* EventLog
557
+ const registry = yield* Registry
432
558
  if (!Array.isArray(keys)) {
433
- yield* log.registerReactivity(keys as Record.ReadonlyRecord<string, ReadonlyArray<string>>)
559
+ yield* registry.registerReactivity(keys as Record.ReadonlyRecord<string, ReadonlyArray<string>>)
434
560
  return
435
561
  }
436
562
  const obj: Record<string, ReadonlyArray<string>> = {}
437
563
  for (const tag in group.events) {
438
564
  obj[tag] = keys
439
565
  }
440
- yield* log.registerReactivity(obj)
566
+ yield* registry.registerReactivity(obj)
441
567
  }).pipe(
442
- Layer.effectDiscard,
443
- Layer.provide(layerEventLog)
568
+ Layer.effectDiscard
444
569
  )
445
570
 
446
571
  /**
447
572
  * @since 4.0.0
448
- * @category tags
573
+ * @category handlers
449
574
  */
450
- export class EventLog extends Context.Service<EventLog, {
451
- readonly write: <Groups extends EventGroup.Any, Tag extends Event.Tag<EventGroup.Events<Groups>>>(options: {
452
- readonly schema: EventLogSchema<Groups>
453
- readonly event: Tag
454
- readonly payload: Event.PayloadWithTag<EventGroup.Events<Groups>, Tag>
455
- }) => Effect.Effect<
456
- Event.SuccessWithTag<EventGroup.Events<Groups>, Tag>,
457
- Event.ErrorWithTag<EventGroup.Events<Groups>, Tag> | EventJournalError
458
- >
459
- readonly registerRemote: (remote: EventLogRemote["Service"]) => Effect.Effect<void, never, Scope.Scope>
460
- readonly registerCompaction: (options: {
461
- readonly events: ReadonlyArray<string>
462
- readonly effect: (options: {
463
- readonly entries: ReadonlyArray<Entry>
464
- readonly write: (entry: Entry) => Effect.Effect<void>
465
- }) => Effect.Effect<void>
466
- }) => Effect.Effect<void, never, Scope.Scope>
467
- readonly registerReactivity: (keys: Record<string, ReadonlyArray<string>>) => Effect.Effect<void, never, Scope.Scope>
468
- readonly entries: Effect.Effect<ReadonlyArray<Entry>, EventJournalError>
469
- readonly destroy: Effect.Effect<void, EventJournalError>
470
- }>()("effect/eventlog/EventLog") {}
575
+ export const makeReplayFromRemote = (options: {
576
+ readonly handlers: ReadonlyMap<string, Handlers.Item<any>>
577
+ readonly storeId: StoreId
578
+ readonly identity: Identity["Service"]
579
+ readonly reactivity: Reactivity["Service"]
580
+ readonly reactivityKeys: Record<string, ReadonlyArray<string>>
581
+ readonly logAnnotations: {
582
+ readonly service: string
583
+ readonly effect: string
584
+ }
585
+ }) =>
586
+ Effect.fnUntraced(
587
+ function*({ conflicts, entry }): Effect.fn.Return<void, Schema.SchemaError> {
588
+ const handler = options.handlers.get(entry.event) as Handlers.Item<any> | undefined
589
+ if (!handler) {
590
+ return yield* Effect.logDebug(`Event handler not found for: "${entry.event}"`)
591
+ }
592
+
593
+ const decodePayload = Schema.decodeUnknownEffect(handler.event.payloadMsgPack)
594
+ const decodedConflicts: Array<{ entry: Entry; payload: unknown }> = new Array(conflicts.length)
595
+ for (let i = 0; i < conflicts.length; i++) {
596
+ decodedConflicts[i] = {
597
+ entry: conflicts[i],
598
+ payload: yield* decodePayload(conflicts[i].payload).pipe(
599
+ Effect.updateContext((input) => Context.merge(handler.context, input))
600
+ ) as any
601
+ }
602
+ }
603
+
604
+ yield* decodePayload(entry.payload).pipe(
605
+ Effect.flatMap((payload) =>
606
+ handler.handler({
607
+ storeId: options.storeId,
608
+ payload,
609
+ entry,
610
+ conflicts: decodedConflicts
611
+ })
612
+ ),
613
+ Effect.provideService(Identity, options.identity),
614
+ Effect.updateContext((input) => Context.merge(handler.context, input)),
615
+ Effect.asVoid
616
+ ) as any
617
+
618
+ const keys = options.reactivityKeys[entry.event]
619
+ if (keys) {
620
+ for (const key of keys) {
621
+ options.reactivity.invalidateUnsafe({
622
+ [key]: [entry.primaryKey]
623
+ })
624
+ }
625
+ }
626
+ },
627
+ Effect.catchCause(Effect.logError),
628
+ (effect, { entry }) =>
629
+ Effect.annotateLogs(effect, {
630
+ ...options.logAnnotations,
631
+ entryId: entry.idString
632
+ })
633
+ )
471
634
 
472
635
  const make = Effect.gen(function*() {
636
+ const storeId = yield* CurrentStoreId
473
637
  const identity = yield* Identity
474
638
  const journal = yield* EventJournal
475
- const services = yield* Effect.context<never>()
476
-
477
- const remotes = yield* FiberMap.make<RemoteId>()
478
- const compactors = new Map<string, {
479
- readonly events: ReadonlySet<string>
480
- readonly effect: (options: {
481
- readonly entries: ReadonlyArray<Entry>
482
- readonly write: (entry: Entry) => Effect.Effect<void>
483
- }) => Effect.Effect<void>
484
- }>()
485
- const journalSemaphore = yield* Semaphore.make(1)
639
+ const registry = yield* Registry
486
640
 
487
641
  const reactivity = yield* Reactivity
488
- const reactivityKeys: Record<string, ReadonlyArray<string>> = {}
642
+ const replayFromRemote = makeReplayFromRemote({
643
+ handlers: registry.handlers,
644
+ storeId,
645
+ identity,
646
+ reactivity,
647
+ reactivityKeys: registry.reactivityKeys,
648
+ logAnnotations: {
649
+ service: "EventLog",
650
+ effect: "writeFromRemote"
651
+ }
652
+ })
653
+
654
+ const invalidateReactivityEntries = (entries: ReadonlyArray<Entry>) =>
655
+ Effect.sync(() => {
656
+ for (const entry of entries) {
657
+ const keys = registry.reactivityKeys[entry.event]
658
+ if (!keys) {
659
+ continue
660
+ }
661
+ for (const key of keys) {
662
+ reactivity.invalidateUnsafe({
663
+ [key]: [entry.primaryKey]
664
+ })
665
+ }
666
+ }
667
+ })
489
668
 
490
669
  const runRemote = Effect.fnUntraced(
491
670
  function*(remote: EventLogRemote["Service"]) {
492
671
  const startSequence = yield* journal.nextRemoteSequence(remote.id)
493
- const changes = yield* remote.changes(identity, startSequence)
494
672
 
495
- yield* Queue.takeAll(changes).pipe(
496
- Effect.flatMap((entries) =>
497
- journal.writeFromRemote({
673
+ yield* Effect.gen(function*() {
674
+ const changes = yield* remote.changes({ identity, startSequence, storeId })
675
+ while (true) {
676
+ const entries = yield* Queue.takeAll(changes)
677
+ yield* journal.writeFromRemote({
498
678
  remoteId: remote.id,
499
679
  entries: entries.flat(),
500
- compact: compactors.size > 0
680
+ compact: registry.compactors.size > 0
501
681
  ? Effect.fnUntraced(function*(remoteEntries) {
502
- const brackets: Array<[Array<Entry>, Array<RemoteEntry>]> = []
503
- let uncompacted: Array<Entry> = []
504
- let uncompactedRemote: Array<RemoteEntry> = []
505
- let index = 0
506
- while (index < remoteEntries.length) {
507
- const remoteEntry = remoteEntries[index]
508
- const compactor = compactors.get(remoteEntry.entry.event)
682
+ const finalEntries: Array<Entry> = []
683
+ const compactable = new Map<
684
+ (options: {
685
+ readonly entries: ReadonlyArray<Entry>
686
+ readonly write: (entry: Entry) => Effect.Effect<void>
687
+ }) => Effect.Effect<void>,
688
+ Array<Entry>
689
+ >()
690
+
691
+ for (let i = 0; i < remoteEntries.length; i++) {
692
+ const remoteEntry = remoteEntries[i]
693
+ const entry = remoteEntry.entry
694
+ const compactor = registry.compactors.get(entry.event)
509
695
  if (!compactor) {
510
- uncompacted.push(remoteEntry.entry)
511
- uncompactedRemote.push(remoteEntry)
512
- index++
696
+ finalEntries.push(entry)
513
697
  continue
514
698
  }
515
- if (uncompacted.length > 0) {
516
- brackets.push([uncompacted, uncompactedRemote])
517
- uncompacted = []
518
- uncompactedRemote = []
519
- }
520
- const entries = [remoteEntry.entry]
521
- const remoteGroup = [remoteEntry]
522
- const compacted: Array<Entry> = []
523
- index++
524
- while (index < remoteEntries.length) {
525
- const nextRemoteEntry = remoteEntries[index]
526
- if (!compactor.events.has(nextRemoteEntry.entry.event)) {
527
- break
528
- }
529
- entries.push(nextRemoteEntry.entry)
530
- remoteGroup.push(nextRemoteEntry)
531
- index++
699
+ let arr = compactable.get(compactor.effect)
700
+ if (!arr) {
701
+ arr = []
702
+ compactable.set(compactor.effect, arr)
532
703
  }
533
- yield* compactor.effect({
704
+ arr.push(entry)
705
+ }
706
+
707
+ for (const [compact, entries] of compactable) {
708
+ yield* compact({
534
709
  entries,
535
710
  write(entry) {
536
711
  return Effect.sync(() => {
537
- compacted.push(entry)
712
+ finalEntries.push(entry)
538
713
  })
539
714
  }
540
- }).pipe(Effect.orDie)
541
- brackets.push([compacted, remoteGroup])
542
- }
543
- if (uncompacted.length > 0) {
544
- brackets.push([uncompacted, uncompactedRemote])
715
+ })
545
716
  }
546
- return brackets
717
+
718
+ return finalEntries.sort(Entry.Order)
547
719
  })
548
720
  : undefined,
549
- effect: Effect.fnUntraced(
550
- function*({ conflicts, entry }): Effect.fn.Return<void, Schema.SchemaError> {
551
- const handler = services.mapUnsafe.get(Event.serviceKey(entry.event)) as Handlers.Item<any> | undefined
552
- if (!handler) {
553
- return yield* Effect.logDebug(`Event handler not found for: "${entry.event}"`)
554
- }
555
- const decodePayload = Schema.decodeUnknownEffect(handler.event.payloadMsgPack)
556
- const decodedConflicts: Array<{ entry: Entry; payload: unknown }> = new Array(conflicts.length)
557
- for (let i = 0; i < conflicts.length; i++) {
558
- decodedConflicts[i] = {
559
- entry: conflicts[i],
560
- payload: yield* decodePayload(conflicts[i].payload).pipe(
561
- Effect.updateContext((input) => Context.merge(handler.context, input))
562
- ) as any
563
- }
564
- }
565
- yield* decodePayload(entry.payload).pipe(
566
- Effect.flatMap((payload) =>
567
- handler.handler({
568
- payload,
569
- entry,
570
- conflicts: decodedConflicts
571
- })
572
- ),
573
- Effect.updateContext((input) => Context.merge(handler.context, input)),
574
- Effect.asVoid
575
- ) as any
576
- if (reactivityKeys[entry.event]) {
577
- for (const key of reactivityKeys[entry.event]) {
578
- yield* Effect.sync(() =>
579
- reactivity.invalidateUnsafe({
580
- [key]: [entry.primaryKey]
581
- })
582
- )
583
- }
584
- }
585
- },
586
- Effect.catchCause(Effect.logError),
587
- (effect, { entry }) =>
588
- Effect.annotateLogs(effect, {
589
- service: "EventLog",
590
- effect: "writeFromRemote",
591
- entryId: entry.idString
592
- })
593
- )
594
- }).pipe(journalSemaphore.withPermits(1))
595
- ),
721
+ effect: replayFromRemote
722
+ }).pipe(
723
+ Effect.tap(({ duplicateEntries }) => invalidateReactivityEntries(duplicateEntries)),
724
+ journal.withLock(storeId)
725
+ )
726
+ }
727
+ }).pipe(
728
+ Effect.scoped,
596
729
  Effect.catchCause(Effect.logError),
597
- Effect.forever,
730
+ Effect.repeat(
731
+ Schedule.exponential(200, 1.5).pipe(
732
+ Schedule.either(Schedule.spaced({ seconds: 10 }))
733
+ )
734
+ ),
598
735
  Effect.annotateLogs({
599
736
  service: "EventLog",
600
737
  effect: "runRemote consume"
@@ -602,12 +739,11 @@ const make = Effect.gen(function*() {
602
739
  Effect.forkScoped
603
740
  )
604
741
 
605
- const write = journal.withRemoteUncommited(remote.id, (entries) => remote.write(identity, entries))
742
+ const write = journal.withRemoteUncommited(remote.id, (entries) => remote.write({ identity, entries, storeId }))
606
743
  yield* Effect.addFinalizer(() => Effect.ignore(write))
607
744
  yield* write
608
745
  const changesSub = yield* journal.changes
609
746
  return yield* PubSub.takeAll(changesSub).pipe(
610
- Effect.andThen(Effect.sleep("500 millis")),
611
747
  Effect.andThen(write),
612
748
  Effect.catchCause(Effect.logError),
613
749
  Effect.forever
@@ -615,7 +751,7 @@ const make = Effect.gen(function*() {
615
751
  },
616
752
  Effect.scoped,
617
753
  Effect.provideService(Identity, identity),
618
- Effect.interruptible
754
+ Effect.orDie
619
755
  )
620
756
 
621
757
  const writeHandler = Effect.fnUntraced(function*(handler: Handlers.Item<any>, options: {
@@ -626,28 +762,29 @@ const make = Effect.gen(function*() {
626
762
  const payload = yield* Schema.encodeUnknownEffect(handler.event.payloadMsgPack)(options.payload).pipe(
627
763
  Effect.orDie
628
764
  )
629
- return yield* journalSemaphore.withPermits(1)(journal.write({
765
+ return yield* journal.withLock(storeId)(journal.write({
630
766
  event: options.event,
631
767
  primaryKey: handler.event.primaryKey(options.payload as never),
632
768
  payload,
633
769
  effect: (entry) =>
634
770
  handler.handler({
771
+ storeId,
635
772
  payload: options.payload,
636
773
  entry,
637
774
  conflicts: []
638
775
  }).pipe(
639
776
  Effect.updateContext((input) => Context.merge(handler.context, input)),
640
- Effect.tap(() =>
641
- Effect.sync(() => {
642
- if (reactivityKeys[entry.event]) {
643
- for (const key of reactivityKeys[entry.event]) {
644
- reactivity.invalidateUnsafe({
645
- [key]: [entry.primaryKey]
646
- })
647
- }
777
+ Effect.provideService(Identity, identity),
778
+ Effect.tap(() => {
779
+ if (registry.reactivityKeys[entry.event]) {
780
+ for (const key of registry.reactivityKeys[entry.event]) {
781
+ reactivity.invalidateUnsafe({
782
+ [key]: [entry.primaryKey]
783
+ })
648
784
  }
649
- })
650
- )
785
+ }
786
+ return Effect.void
787
+ })
651
788
  )
652
789
  }))
653
790
  })
@@ -657,44 +794,18 @@ const make = Effect.gen(function*() {
657
794
  readonly event: string
658
795
  readonly payload: unknown
659
796
  }) => {
660
- const handler = services.mapUnsafe.get(Event.serviceKey(options.event)) as Handlers.Item<any> | undefined
797
+ const handler = registry.handlers.get(options.event) as Handlers.Item<any> | undefined
661
798
  if (handler === undefined) {
662
799
  return Effect.die(`Event handler not found for: "${options.event}"`)
663
800
  }
664
801
  return writeHandler(handler, options)
665
802
  }
666
803
 
804
+ yield* registry.handleRemote(runRemote)
805
+
667
806
  return EventLog.of({
668
807
  write: eventLogWrite as EventLog["Service"]["write"],
669
808
  entries: journal.entries,
670
- registerRemote: (remote) =>
671
- Effect.acquireRelease(
672
- FiberMap.run(remotes, remote.id, runRemote(remote)),
673
- () => FiberMap.remove(remotes, remote.id)
674
- ).pipe(Effect.asVoid),
675
- registerCompaction: (options) =>
676
- Effect.acquireRelease(
677
- Effect.sync(() => {
678
- const events = new Set(options.events)
679
- const compactor = {
680
- events,
681
- effect: options.effect
682
- }
683
- for (const event of options.events) {
684
- compactors.set(event, compactor)
685
- }
686
- }),
687
- () =>
688
- Effect.sync(() => {
689
- for (const event of options.events) {
690
- compactors.delete(event)
691
- }
692
- })
693
- ),
694
- registerReactivity: (keys) =>
695
- Effect.sync(() => {
696
- Object.assign(reactivityKeys, keys)
697
- }),
698
809
  destroy: journal.destroy
699
810
  })
700
811
  })
@@ -703,19 +814,29 @@ const make = Effect.gen(function*() {
703
814
  * @since 4.0.0
704
815
  * @category layers
705
816
  */
706
- export const layerEventLog: Layer.Layer<EventLog, never, EventJournal | Identity> = Layer.effect(EventLog, make).pipe(
707
- Layer.provide(ReactivityLayer.layer)
817
+ export const layerEventLog: Layer.Layer<EventLog | Registry, never, EventJournal | Identity> = Layer.effect(
818
+ EventLog,
819
+ make
820
+ ).pipe(
821
+ Layer.provide(ReactivityLayer.layer),
822
+ Layer.provideMerge(layerRegistry)
708
823
  )
709
824
 
710
825
  /**
711
826
  * @since 4.0.0
712
827
  * @category layers
713
828
  */
714
- export const layer = <Groups extends EventGroup.Any>(_schema: EventLogSchema<Groups>): Layer.Layer<
715
- EventLog,
716
- never,
717
- EventGroup.ToService<Groups> | EventJournal | Identity
718
- > => layerEventLog as Layer.Layer<EventLog, never, EventGroup.ToService<Groups> | EventJournal | Identity>
829
+ export const layer = <Groups extends EventGroup.Any, E, R>(
830
+ _schema: EventLogSchema<Groups>,
831
+ layer: Layer.Layer<EventGroup.ToService<Groups>, E, R>
832
+ ): Layer.Layer<
833
+ EventLog | Registry,
834
+ E,
835
+ Exclude<R, EventLog | Registry> | EventJournal | Identity
836
+ > =>
837
+ layer.pipe(
838
+ Layer.provideMerge(layerEventLog)
839
+ )
719
840
 
720
841
  /**
721
842
  * @since 4.0.0