effect 3.10.0 → 3.10.2

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/src/index.ts CHANGED
@@ -323,6 +323,17 @@ export * as FiberStatus from "./FiberStatus.js"
323
323
  export * as Function from "./Function.js"
324
324
 
325
325
  /**
326
+ * The `GlobalValue` module ensures that a single instance of a value is created globally,
327
+ * even when modules are imported multiple times (e.g., due to mixing CommonJS and ESM builds)
328
+ * or during hot-reloading in development environments like Next.js or Remix.
329
+ *
330
+ * It achieves this by using a versioned global store, identified by a unique `Symbol` tied to
331
+ * the current version of the `effect` library. The store holds values that are keyed by an identifier,
332
+ * allowing the reuse of previously computed instances across imports or reloads.
333
+ *
334
+ * This pattern is particularly useful in scenarios where frequent reloading can cause services or
335
+ * single-instance objects to be recreated unnecessarily, such as in development environments with hot-reloading.
336
+ *
326
337
  * @since 2.0.0
327
338
  */
328
339
  export * as GlobalValue from "./GlobalValue.js"
@@ -101,247 +101,246 @@ export class ChannelExecutor<
101
101
  } else {
102
102
  if (Effect.isEffect(this._currentChannel)) {
103
103
  this._currentChannel = core.fromEffect(this._currentChannel) as core.Primitive
104
- } else {
105
- switch (this._currentChannel._tag) {
106
- case ChannelOpCodes.OP_BRACKET_OUT: {
107
- result = this.runBracketOut(this._currentChannel)
108
- break
109
- }
104
+ }
105
+ switch (this._currentChannel._tag) {
106
+ case ChannelOpCodes.OP_BRACKET_OUT: {
107
+ result = this.runBracketOut(this._currentChannel)
108
+ break
109
+ }
110
110
 
111
- case ChannelOpCodes.OP_BRIDGE: {
112
- const bridgeInput = this._currentChannel.input
113
-
114
- // PipeTo(left, Bridge(queue, channel))
115
- // In a fiber: repeatedly run left and push its outputs to the queue
116
- // Add a finalizer to interrupt the fiber and close the executor
117
- this._currentChannel = this._currentChannel.channel as core.Primitive
118
-
119
- if (this._input !== undefined) {
120
- const inputExecutor = this._input
121
- this._input = undefined
122
-
123
- const drainer = (): Effect.Effect<unknown, never, Env> =>
124
- Effect.flatMap(bridgeInput.awaitRead(), () =>
125
- Effect.suspend(() => {
126
- const state = inputExecutor.run() as ChannelState.Primitive
127
- switch (state._tag) {
128
- case ChannelStateOpCodes.OP_DONE: {
129
- return Exit.match(inputExecutor.getDone(), {
130
- onFailure: (cause) => bridgeInput.error(cause),
131
- onSuccess: (value) => bridgeInput.done(value)
132
- })
133
- }
134
- case ChannelStateOpCodes.OP_EMIT: {
135
- return Effect.flatMap(
136
- bridgeInput.emit(inputExecutor.getEmit()),
137
- () => drainer()
138
- )
139
- }
140
- case ChannelStateOpCodes.OP_FROM_EFFECT: {
141
- return Effect.matchCauseEffect(state.effect, {
142
- onFailure: (cause) => bridgeInput.error(cause),
143
- onSuccess: () => drainer()
144
- })
145
- }
146
- case ChannelStateOpCodes.OP_READ: {
147
- return readUpstream(
148
- state,
149
- () => drainer(),
150
- (cause) => bridgeInput.error(cause)
151
- )
152
- }
111
+ case ChannelOpCodes.OP_BRIDGE: {
112
+ const bridgeInput = this._currentChannel.input
113
+
114
+ // PipeTo(left, Bridge(queue, channel))
115
+ // In a fiber: repeatedly run left and push its outputs to the queue
116
+ // Add a finalizer to interrupt the fiber and close the executor
117
+ this._currentChannel = this._currentChannel.channel as core.Primitive
118
+
119
+ if (this._input !== undefined) {
120
+ const inputExecutor = this._input
121
+ this._input = undefined
122
+
123
+ const drainer = (): Effect.Effect<unknown, never, Env> =>
124
+ Effect.flatMap(bridgeInput.awaitRead(), () =>
125
+ Effect.suspend(() => {
126
+ const state = inputExecutor.run() as ChannelState.Primitive
127
+ switch (state._tag) {
128
+ case ChannelStateOpCodes.OP_DONE: {
129
+ return Exit.match(inputExecutor.getDone(), {
130
+ onFailure: (cause) => bridgeInput.error(cause),
131
+ onSuccess: (value) => bridgeInput.done(value)
132
+ })
133
+ }
134
+ case ChannelStateOpCodes.OP_EMIT: {
135
+ return Effect.flatMap(
136
+ bridgeInput.emit(inputExecutor.getEmit()),
137
+ () => drainer()
138
+ )
139
+ }
140
+ case ChannelStateOpCodes.OP_FROM_EFFECT: {
141
+ return Effect.matchCauseEffect(state.effect, {
142
+ onFailure: (cause) => bridgeInput.error(cause),
143
+ onSuccess: () => drainer()
144
+ })
153
145
  }
154
- })) as Effect.Effect<unknown, never, Env>
155
-
156
- result = ChannelState.fromEffect(
157
- Effect.flatMap(
158
- Effect.forkDaemon(drainer()),
159
- (fiber) =>
160
- Effect.sync(() =>
161
- this.addFinalizer((exit) =>
162
- Effect.flatMap(Fiber.interrupt(fiber), () =>
163
- Effect.suspend(() => {
164
- const effect = this.restorePipe(exit, inputExecutor)
165
- return effect !== undefined ? effect : Effect.void
166
- }))
146
+ case ChannelStateOpCodes.OP_READ: {
147
+ return readUpstream(
148
+ state,
149
+ () => drainer(),
150
+ (cause) => bridgeInput.error(cause)
167
151
  )
152
+ }
153
+ }
154
+ })) as Effect.Effect<unknown, never, Env>
155
+
156
+ result = ChannelState.fromEffect(
157
+ Effect.flatMap(
158
+ Effect.forkDaemon(Effect.interruptible(drainer())),
159
+ (fiber) =>
160
+ Effect.sync(() =>
161
+ this.addFinalizer((exit) =>
162
+ Effect.flatMap(Fiber.interrupt(fiber), () =>
163
+ Effect.suspend(() => {
164
+ const effect = this.restorePipe(exit, inputExecutor)
165
+ return effect !== undefined ? effect : Effect.void
166
+ }))
168
167
  )
169
- )
168
+ )
170
169
  )
171
- }
172
-
173
- break
174
- }
175
-
176
- case ChannelOpCodes.OP_CONCAT_ALL: {
177
- const executor: ErasedExecutor<Env> = new ChannelExecutor(
178
- this._currentChannel.value() as Channel.Channel<
179
- never,
180
- unknown,
181
- never,
182
- unknown,
183
- never,
184
- unknown,
185
- Env
186
- >,
187
- this._providedEnv,
188
- (effect) =>
189
- Effect.sync(() => {
190
- const prevLastClose = this._closeLastSubstream === undefined
191
- ? Effect.void
192
- : this._closeLastSubstream
193
- this._closeLastSubstream = pipe(prevLastClose, Effect.zipRight(effect))
194
- })
195
- )
196
- executor._input = this._input
197
-
198
- const channel = this._currentChannel
199
- this._activeSubexecutor = new Subexecutor.PullFromUpstream(
200
- executor,
201
- (value) => channel.k(value),
202
- undefined,
203
- [],
204
- (x, y) => channel.combineInners(x, y),
205
- (x, y) => channel.combineAll(x, y),
206
- (request) => channel.onPull(request),
207
- (value) => channel.onEmit(value)
208
170
  )
171
+ }
209
172
 
210
- this._closeLastSubstream = undefined
211
- this._currentChannel = undefined
173
+ break
174
+ }
212
175
 
213
- break
214
- }
176
+ case ChannelOpCodes.OP_CONCAT_ALL: {
177
+ const executor: ErasedExecutor<Env> = new ChannelExecutor(
178
+ this._currentChannel.value() as Channel.Channel<
179
+ never,
180
+ unknown,
181
+ never,
182
+ unknown,
183
+ never,
184
+ unknown,
185
+ Env
186
+ >,
187
+ this._providedEnv,
188
+ (effect) =>
189
+ Effect.sync(() => {
190
+ const prevLastClose = this._closeLastSubstream === undefined
191
+ ? Effect.void
192
+ : this._closeLastSubstream
193
+ this._closeLastSubstream = pipe(prevLastClose, Effect.zipRight(effect))
194
+ })
195
+ )
196
+ executor._input = this._input
197
+
198
+ const channel = this._currentChannel
199
+ this._activeSubexecutor = new Subexecutor.PullFromUpstream(
200
+ executor,
201
+ (value) => channel.k(value),
202
+ undefined,
203
+ [],
204
+ (x, y) => channel.combineInners(x, y),
205
+ (x, y) => channel.combineAll(x, y),
206
+ (request) => channel.onPull(request),
207
+ (value) => channel.onEmit(value)
208
+ )
209
+
210
+ this._closeLastSubstream = undefined
211
+ this._currentChannel = undefined
212
+
213
+ break
214
+ }
215
215
 
216
- case ChannelOpCodes.OP_EMIT: {
217
- this._emitted = this._currentChannel.out
218
- this._currentChannel = (this._activeSubexecutor !== undefined ?
219
- undefined :
220
- core.void) as core.Primitive | undefined
221
- result = ChannelState.Emit()
222
- break
223
- }
216
+ case ChannelOpCodes.OP_EMIT: {
217
+ this._emitted = this._currentChannel.out
218
+ this._currentChannel = (this._activeSubexecutor !== undefined ?
219
+ undefined :
220
+ core.void) as core.Primitive | undefined
221
+ result = ChannelState.Emit()
222
+ break
223
+ }
224
224
 
225
- case ChannelOpCodes.OP_ENSURING: {
226
- this.runEnsuring(this._currentChannel)
227
- break
228
- }
225
+ case ChannelOpCodes.OP_ENSURING: {
226
+ this.runEnsuring(this._currentChannel)
227
+ break
228
+ }
229
229
 
230
- case ChannelOpCodes.OP_FAIL: {
231
- result = this.doneHalt(this._currentChannel.error())
232
- break
233
- }
230
+ case ChannelOpCodes.OP_FAIL: {
231
+ result = this.doneHalt(this._currentChannel.error())
232
+ break
233
+ }
234
234
 
235
- case ChannelOpCodes.OP_FOLD: {
236
- this._doneStack.push(this._currentChannel.k as ErasedContinuation<Env>)
237
- this._currentChannel = this._currentChannel.channel as core.Primitive
238
- break
239
- }
235
+ case ChannelOpCodes.OP_FOLD: {
236
+ this._doneStack.push(this._currentChannel.k as ErasedContinuation<Env>)
237
+ this._currentChannel = this._currentChannel.channel as core.Primitive
238
+ break
239
+ }
240
240
 
241
- case ChannelOpCodes.OP_FROM_EFFECT: {
242
- const effect = this._providedEnv === undefined ?
243
- this._currentChannel.effect() :
244
- pipe(
245
- this._currentChannel.effect(),
246
- Effect.provide(this._providedEnv)
247
- )
241
+ case ChannelOpCodes.OP_FROM_EFFECT: {
242
+ const effect = this._providedEnv === undefined ?
243
+ this._currentChannel.effect() :
244
+ pipe(
245
+ this._currentChannel.effect(),
246
+ Effect.provide(this._providedEnv)
247
+ )
248
248
 
249
- result = ChannelState.fromEffect(
250
- Effect.matchCauseEffect(effect, {
251
- onFailure: (cause) => {
252
- const state = this.doneHalt(cause)
253
- return state !== undefined && ChannelState.isFromEffect(state) ?
254
- state.effect :
255
- Effect.void
256
- },
257
- onSuccess: (value) => {
258
- const state = this.doneSucceed(value)
259
- return state !== undefined && ChannelState.isFromEffect(state) ?
260
- state.effect :
261
- Effect.void
262
- }
263
- })
264
- ) as ChannelState.ChannelState<unknown, Env> | undefined
249
+ result = ChannelState.fromEffect(
250
+ Effect.matchCauseEffect(effect, {
251
+ onFailure: (cause) => {
252
+ const state = this.doneHalt(cause)
253
+ return state !== undefined && ChannelState.isFromEffect(state) ?
254
+ state.effect :
255
+ Effect.void
256
+ },
257
+ onSuccess: (value) => {
258
+ const state = this.doneSucceed(value)
259
+ return state !== undefined && ChannelState.isFromEffect(state) ?
260
+ state.effect :
261
+ Effect.void
262
+ }
263
+ })
264
+ ) as ChannelState.ChannelState<unknown, Env> | undefined
265
265
 
266
- break
267
- }
266
+ break
267
+ }
268
268
 
269
- case ChannelOpCodes.OP_PIPE_TO: {
270
- const previousInput = this._input
269
+ case ChannelOpCodes.OP_PIPE_TO: {
270
+ const previousInput = this._input
271
271
 
272
- const leftExec: ErasedExecutor<Env> = new ChannelExecutor(
273
- this._currentChannel.left() as Channel.Channel<never, unknown, never, unknown, never, unknown, Env>,
274
- this._providedEnv,
275
- (effect) => this._executeCloseLastSubstream(effect)
276
- )
277
- leftExec._input = previousInput
278
- this._input = leftExec
272
+ const leftExec: ErasedExecutor<Env> = new ChannelExecutor(
273
+ this._currentChannel.left() as Channel.Channel<never, unknown, never, unknown, never, unknown, Env>,
274
+ this._providedEnv,
275
+ (effect) => this._executeCloseLastSubstream(effect)
276
+ )
277
+ leftExec._input = previousInput
278
+ this._input = leftExec
279
279
 
280
- this.addFinalizer((exit) => {
281
- const effect = this.restorePipe(exit, previousInput)
282
- return effect !== undefined ? effect : Effect.void
283
- })
280
+ this.addFinalizer((exit) => {
281
+ const effect = this.restorePipe(exit, previousInput)
282
+ return effect !== undefined ? effect : Effect.void
283
+ })
284
284
 
285
- this._currentChannel = this._currentChannel.right() as core.Primitive
285
+ this._currentChannel = this._currentChannel.right() as core.Primitive
286
286
 
287
- break
288
- }
287
+ break
288
+ }
289
289
 
290
- case ChannelOpCodes.OP_PROVIDE: {
291
- const previousEnv = this._providedEnv
292
- this._providedEnv = this._currentChannel.context()
293
- this._currentChannel = this._currentChannel.inner as core.Primitive
294
- this.addFinalizer(() =>
295
- Effect.sync(() => {
296
- this._providedEnv = previousEnv
297
- })
298
- )
299
- break
300
- }
290
+ case ChannelOpCodes.OP_PROVIDE: {
291
+ const previousEnv = this._providedEnv
292
+ this._providedEnv = this._currentChannel.context()
293
+ this._currentChannel = this._currentChannel.inner as core.Primitive
294
+ this.addFinalizer(() =>
295
+ Effect.sync(() => {
296
+ this._providedEnv = previousEnv
297
+ })
298
+ )
299
+ break
300
+ }
301
301
 
302
- case ChannelOpCodes.OP_READ: {
303
- const read = this._currentChannel
304
- result = ChannelState.Read(
305
- this._input!,
306
- identity,
307
- (emitted) => {
308
- try {
309
- this._currentChannel = read.more(emitted) as core.Primitive
310
- } catch (error) {
311
- this._currentChannel = read.done.onExit(Exit.die(error)) as core.Primitive
312
- }
313
- return undefined
314
- },
315
- (exit) => {
316
- const onExit = (exit: Exit.Exit<unknown, unknown>): core.Primitive => {
317
- return read.done.onExit(exit) as core.Primitive
318
- }
319
- this._currentChannel = onExit(exit)
320
- return undefined
302
+ case ChannelOpCodes.OP_READ: {
303
+ const read = this._currentChannel
304
+ result = ChannelState.Read(
305
+ this._input!,
306
+ identity,
307
+ (emitted) => {
308
+ try {
309
+ this._currentChannel = read.more(emitted) as core.Primitive
310
+ } catch (error) {
311
+ this._currentChannel = read.done.onExit(Exit.die(error)) as core.Primitive
321
312
  }
322
- )
323
- break
324
- }
313
+ return undefined
314
+ },
315
+ (exit) => {
316
+ const onExit = (exit: Exit.Exit<unknown, unknown>): core.Primitive => {
317
+ return read.done.onExit(exit) as core.Primitive
318
+ }
319
+ this._currentChannel = onExit(exit)
320
+ return undefined
321
+ }
322
+ )
323
+ break
324
+ }
325
325
 
326
- case ChannelOpCodes.OP_SUCCEED: {
327
- result = this.doneSucceed(this._currentChannel.evaluate())
328
- break
329
- }
326
+ case ChannelOpCodes.OP_SUCCEED: {
327
+ result = this.doneSucceed(this._currentChannel.evaluate())
328
+ break
329
+ }
330
330
 
331
- case ChannelOpCodes.OP_SUCCEED_NOW: {
332
- result = this.doneSucceed(this._currentChannel.terminal)
333
- break
334
- }
331
+ case ChannelOpCodes.OP_SUCCEED_NOW: {
332
+ result = this.doneSucceed(this._currentChannel.terminal)
333
+ break
334
+ }
335
335
 
336
- case ChannelOpCodes.OP_SUSPEND: {
337
- this._currentChannel = this._currentChannel.channel() as core.Primitive
338
- break
339
- }
336
+ case ChannelOpCodes.OP_SUSPEND: {
337
+ this._currentChannel = this._currentChannel.channel() as core.Primitive
338
+ break
339
+ }
340
340
 
341
- default: {
342
- // @ts-expect-error
343
- this._currentChannel._tag
344
- }
341
+ default: {
342
+ // @ts-expect-error
343
+ this._currentChannel._tag
345
344
  }
346
345
  }
347
346
  }
@@ -1,4 +1,4 @@
1
- let moduleVersion = "3.10.0"
1
+ let moduleVersion = "3.10.2"
2
2
 
3
3
  export const getCurrentVersion = () => moduleVersion
4
4