@livestore/utils 0.4.0-dev.21 → 0.4.0-dev.23
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/.tsbuildinfo +1 -0
- package/dist/NoopTracer.d.ts.map +1 -1
- package/dist/NoopTracer.js +15 -5
- package/dist/NoopTracer.js.map +1 -1
- package/dist/binary.js +1 -1
- package/dist/binary.js.map +1 -1
- package/dist/browser/Opfs/Opfs.d.ts +3 -3
- package/dist/browser/Opfs/Opfs.d.ts.map +1 -1
- package/dist/browser/Opfs/Opfs.js +1 -1
- package/dist/browser/Opfs/Opfs.js.map +1 -1
- package/dist/browser/Opfs/debug-utils.d.ts.map +1 -1
- package/dist/browser/Opfs/debug-utils.js +2 -2
- package/dist/browser/Opfs/debug-utils.js.map +1 -1
- package/dist/browser/Opfs/utils.d.ts.map +1 -1
- package/dist/browser/Opfs/utils.js +10 -10
- package/dist/browser/Opfs/utils.js.map +1 -1
- package/dist/browser/QuotaExceededError.d.ts.map +1 -1
- package/dist/browser/QuotaExceededError.js.map +1 -1
- package/dist/browser/WebChannelBrowser.d.ts +1 -1
- package/dist/browser/WebChannelBrowser.d.ts.map +1 -1
- package/dist/browser/WebError.d.ts +50 -54
- package/dist/browser/WebError.d.ts.map +1 -1
- package/dist/browser/WebError.js +25 -23
- package/dist/browser/WebError.js.map +1 -1
- package/dist/browser/WebLock.js +9 -9
- package/dist/browser/WebLock.js.map +1 -1
- package/dist/browser/detect.js +6 -6
- package/dist/browser/detect.js.map +1 -1
- package/dist/cuid/cuid.browser.js +1 -1
- package/dist/cuid/cuid.browser.js.map +1 -1
- package/dist/cuid/cuid.node.js +1 -1
- package/dist/cuid/cuid.node.js.map +1 -1
- package/dist/effect/BucketQueue.d.ts +1 -1
- package/dist/effect/BucketQueue.d.ts.map +1 -1
- package/dist/effect/BucketQueue.js.map +1 -1
- package/dist/effect/Debug.d.ts +7 -2
- package/dist/effect/Debug.d.ts.map +1 -1
- package/dist/effect/Debug.js +69 -61
- package/dist/effect/Debug.js.map +1 -1
- package/dist/effect/Effect.d.ts +72 -12
- package/dist/effect/Effect.d.ts.map +1 -1
- package/dist/effect/Effect.js +96 -12
- package/dist/effect/Effect.js.map +1 -1
- package/dist/effect/Error.js +1 -1
- package/dist/effect/Error.js.map +1 -1
- package/dist/effect/Logger.js +2 -2
- package/dist/effect/Logger.js.map +1 -1
- package/dist/effect/RpcClient.d.ts.map +1 -1
- package/dist/effect/RpcClient.js +4 -4
- package/dist/effect/RpcClient.js.map +1 -1
- package/dist/effect/Schema/debug-diff.js +5 -4
- package/dist/effect/Schema/debug-diff.js.map +1 -1
- package/dist/effect/Schema/index.d.ts +5 -3
- package/dist/effect/Schema/index.d.ts.map +1 -1
- package/dist/effect/Schema/index.js +1 -1
- package/dist/effect/Schema/index.js.map +1 -1
- package/dist/effect/ServiceContext.js +6 -6
- package/dist/effect/ServiceContext.js.map +1 -1
- package/dist/effect/Stream.test.js +3 -3
- package/dist/effect/Stream.test.js.map +1 -1
- package/dist/effect/SubscriptionRef.d.ts +4 -4
- package/dist/effect/SubscriptionRef.d.ts.map +1 -1
- package/dist/effect/WebChannel/WebChannel.d.ts +2 -2
- package/dist/effect/WebChannel/WebChannel.d.ts.map +1 -1
- package/dist/effect/WebChannel/WebChannel.js +4 -4
- package/dist/effect/WebChannel/WebChannel.js.map +1 -1
- package/dist/effect/WebChannel/broadcastChannelWithAck.js +4 -4
- package/dist/effect/WebChannel/broadcastChannelWithAck.js.map +1 -1
- package/dist/effect/WebChannel/common.d.ts +1 -1
- package/dist/effect/WebChannel/common.d.ts.map +1 -1
- package/dist/effect/WebChannel/common.js +2 -2
- package/dist/effect/WebChannel/common.js.map +1 -1
- package/dist/effect/WebSocket.js +3 -3
- package/dist/effect/WebSocket.js.map +1 -1
- package/dist/effect/mod.d.ts +1 -1
- package/dist/effect/mod.d.ts.map +1 -1
- package/dist/effect/mod.js +1 -1
- package/dist/effect/mod.js.map +1 -1
- package/dist/effect/spanEvent.d.ts +12 -0
- package/dist/effect/spanEvent.d.ts.map +1 -0
- package/dist/effect/spanEvent.js +12 -0
- package/dist/effect/spanEvent.js.map +1 -0
- package/dist/effect/spanEvent.test.d.ts +2 -0
- package/dist/effect/spanEvent.test.d.ts.map +1 -0
- package/dist/effect/spanEvent.test.js +82 -0
- package/dist/effect/spanEvent.test.js.map +1 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +1 -1
- package/dist/env.js.map +1 -1
- package/dist/fast-deep-equal.js +9 -9
- package/dist/fast-deep-equal.js.map +1 -1
- package/dist/global.d.ts.map +1 -1
- package/dist/global.js.map +1 -1
- package/dist/misc.d.ts +9 -1
- package/dist/misc.d.ts.map +1 -1
- package/dist/misc.js +11 -3
- package/dist/misc.js.map +1 -1
- package/dist/mod.d.ts +1 -1
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +12 -12
- package/dist/mod.js.map +1 -1
- package/dist/node/ChildProcessRunner/ChildProcessRunner.d.ts.map +1 -1
- package/dist/node/ChildProcessRunner/ChildProcessRunner.js +15 -9
- package/dist/node/ChildProcessRunner/ChildProcessRunner.js.map +1 -1
- package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.d.ts +8 -0
- package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.d.ts.map +1 -1
- package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.js +10 -6
- package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.js.map +1 -1
- package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.js +2 -2
- package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.js.map +1 -1
- package/dist/node/ChildProcessRunner/ChildProcessWorker.d.ts.map +1 -1
- package/dist/node/ChildProcessRunner/ChildProcessWorker.js +4 -4
- package/dist/node/ChildProcessRunner/ChildProcessWorker.js.map +1 -1
- package/dist/node/mod.d.ts +31 -4
- package/dist/node/mod.d.ts.map +1 -1
- package/dist/node/mod.js +33 -6
- package/dist/node/mod.js.map +1 -1
- package/dist/object/index.d.ts.map +1 -1
- package/dist/object/index.js.map +1 -1
- package/dist/object/omit.js +1 -1
- package/dist/object/omit.js.map +1 -1
- package/dist/object/stringify-object.js +2 -2
- package/dist/object/stringify-object.js.map +1 -1
- package/dist/object/stringify-object.test.js.map +1 -1
- package/dist/qr.js +11 -11
- package/dist/qr.js.map +1 -1
- package/dist/set.js +1 -1
- package/dist/set.js.map +1 -1
- package/dist/time.js +1 -1
- package/dist/time.js.map +1 -1
- package/package.json +66 -52
- package/src/NoopTracer.ts +20 -9
- package/src/binary.ts +1 -1
- package/src/browser/Opfs/Opfs.ts +12 -4
- package/src/browser/Opfs/debug-utils.ts +8 -6
- package/src/browser/Opfs/utils.ts +11 -10
- package/src/browser/QuotaExceededError.ts +0 -2
- package/src/browser/WebChannelBrowser.ts +1 -1
- package/src/browser/WebError.ts +100 -86
- package/src/browser/WebLock.ts +15 -15
- package/src/browser/detect.ts +6 -6
- package/src/cuid/cuid.browser.ts +1 -1
- package/src/cuid/cuid.node.ts +1 -1
- package/src/effect/BucketQueue.ts +1 -1
- package/src/effect/Debug.ts +73 -55
- package/src/effect/Effect.ts +118 -36
- package/src/effect/Error.ts +1 -1
- package/src/effect/Logger.ts +2 -2
- package/src/effect/RpcClient.ts +7 -6
- package/src/effect/Schema/debug-diff.ts +6 -5
- package/src/effect/Schema/index.ts +9 -6
- package/src/effect/ServiceContext.ts +6 -6
- package/src/effect/Stream.test.ts +5 -4
- package/src/effect/SubscriptionRef.ts +5 -5
- package/src/effect/WebChannel/WebChannel.ts +6 -6
- package/src/effect/WebChannel/broadcastChannelWithAck.ts +4 -4
- package/src/effect/WebChannel/common.ts +4 -4
- package/src/effect/WebSocket.ts +3 -3
- package/src/effect/mod.ts +1 -0
- package/src/effect/spanEvent.test.ts +95 -0
- package/src/effect/spanEvent.ts +15 -0
- package/src/env.ts +2 -1
- package/src/fast-deep-equal.ts +9 -9
- package/src/global.ts +0 -2
- package/src/misc.ts +12 -4
- package/src/mod.ts +11 -11
- package/src/node/ChildProcessRunner/ChildProcessRunner.ts +23 -10
- package/src/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.ts +11 -7
- package/src/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.ts +10 -11
- package/src/node/ChildProcessRunner/ChildProcessWorker.ts +5 -4
- package/src/node/mod.ts +38 -6
- package/src/object/index.ts +1 -1
- package/src/object/omit.ts +1 -1
- package/src/object/stringify-object.test.ts +1 -0
- package/src/object/stringify-object.ts +2 -2
- package/src/qr.ts +11 -11
- package/src/set.ts +1 -1
- package/src/time.ts +1 -1
- package/dist/.tsbuildinfo.json +0 -1
package/src/effect/Debug.ts
CHANGED
|
@@ -9,6 +9,12 @@ import * as RuntimeFlags from 'effect/RuntimeFlags'
|
|
|
9
9
|
import * as Scope from 'effect/Scope'
|
|
10
10
|
import type * as Tracer from 'effect/Tracer'
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* How to use:
|
|
14
|
+
* 1. Call `Debug.attachSlowDebugInstrumentation` in the root/main file of your program to ensure it is loaded as soon as possible.
|
|
15
|
+
* 2. Call `Debug.logDebug` to log the current state of the effect system.
|
|
16
|
+
*/
|
|
17
|
+
|
|
12
18
|
interface SpanEvent {
|
|
13
19
|
readonly name: string
|
|
14
20
|
readonly startTime: bigint
|
|
@@ -29,7 +35,7 @@ export type MutableSpanGraphInfo = {
|
|
|
29
35
|
|
|
30
36
|
const graphByTraceId = new Map<string, MutableSpanGraphInfo>()
|
|
31
37
|
|
|
32
|
-
|
|
38
|
+
const ensureSpan = (traceId: string, spanId: string): [MutableSpanGraph, number] => {
|
|
33
39
|
let info = graphByTraceId.get(traceId)
|
|
34
40
|
if (info === undefined) {
|
|
35
41
|
info = {
|
|
@@ -50,20 +56,20 @@ function ensureSpan(traceId: string, spanId: string): [MutableSpanGraph, number]
|
|
|
50
56
|
return [info.graph, nodeId]
|
|
51
57
|
}
|
|
52
58
|
|
|
53
|
-
|
|
59
|
+
const sortSpan = (
|
|
54
60
|
prev: Tracer.AnySpan,
|
|
55
61
|
next: Tracer.AnySpan,
|
|
56
|
-
): [info: Tracer.AnySpan, isUpgrade: boolean, timingUpdated: boolean] {
|
|
62
|
+
): [info: Tracer.AnySpan, isUpgrade: boolean, timingUpdated: boolean] => {
|
|
57
63
|
if (prev._tag === 'ExternalSpan' && next._tag === 'Span') return [next, true, true]
|
|
58
64
|
if (prev._tag === 'Span' && next._tag === 'Span' && next.status._tag === 'Ended') return [next, false, true]
|
|
59
65
|
return [prev, false, false]
|
|
60
66
|
}
|
|
61
67
|
|
|
62
|
-
|
|
68
|
+
const addNode = (span: Tracer.AnySpan) => {
|
|
63
69
|
const [mutableGraph, nodeId] = ensureSpan(span.traceId, span.spanId)
|
|
64
70
|
Graph.updateNode(mutableGraph, nodeId, (previousInfo) => {
|
|
65
71
|
const [latestInfo, upgraded] = sortSpan(previousInfo.span, span)
|
|
66
|
-
if (upgraded && latestInfo._tag === 'Span' && Option.isSome(latestInfo.parent)) {
|
|
72
|
+
if (upgraded === true && latestInfo._tag === 'Span' && Option.isSome(latestInfo.parent) === true) {
|
|
67
73
|
const parentNodeId = addNode(latestInfo.parent.value)
|
|
68
74
|
Graph.addEdge(mutableGraph, parentNodeId, nodeId, undefined)
|
|
69
75
|
}
|
|
@@ -72,30 +78,30 @@ function addNode(span: Tracer.AnySpan) {
|
|
|
72
78
|
return nodeId
|
|
73
79
|
}
|
|
74
80
|
|
|
75
|
-
|
|
81
|
+
const addEvent = (traceId: string, spanId: string, event: SpanEvent) => {
|
|
76
82
|
const [mutableGraph, nodeId] = ensureSpan(traceId, spanId)
|
|
77
83
|
Graph.updateNode(mutableGraph, nodeId, (previousInfo) => {
|
|
78
84
|
return { ...previousInfo, events: [...previousInfo.events, event] }
|
|
79
85
|
})
|
|
80
86
|
return nodeId
|
|
81
87
|
}
|
|
82
|
-
|
|
88
|
+
const addNodeExit = (traceId: string, spanId: string, exit: Exit.Exit<any, any>) => {
|
|
83
89
|
const [mutableGraph, nodeId] = ensureSpan(traceId, spanId)
|
|
84
90
|
Graph.updateNode(mutableGraph, nodeId, (previousInfo) => {
|
|
85
91
|
const isInterruptedOnly = exit._tag === 'Failure' && Cause.isInterruptedOnly(exit.cause)
|
|
86
92
|
return {
|
|
87
93
|
...previousInfo,
|
|
88
|
-
exitTag: isInterruptedOnly ? ('Interrupted' as const) : exit._tag,
|
|
94
|
+
exitTag: isInterruptedOnly === true ? ('Interrupted' as const) : exit._tag,
|
|
89
95
|
}
|
|
90
96
|
})
|
|
91
97
|
return nodeId
|
|
92
98
|
}
|
|
93
99
|
|
|
94
|
-
|
|
100
|
+
const createPropertyInterceptor = <T extends object, K extends keyof T>(
|
|
95
101
|
obj: T,
|
|
96
102
|
property: K,
|
|
97
103
|
interceptor: (value: T[K]) => void,
|
|
98
|
-
): void {
|
|
104
|
+
): void => {
|
|
99
105
|
const descriptor = Object.getOwnPropertyDescriptor(obj, property)
|
|
100
106
|
|
|
101
107
|
const previousSetter = descriptor?.set
|
|
@@ -103,19 +109,19 @@ function createPropertyInterceptor<T extends object, K extends keyof T>(
|
|
|
103
109
|
let currentValue: T[K]
|
|
104
110
|
const previousGetter = descriptor?.get
|
|
105
111
|
|
|
106
|
-
if (
|
|
112
|
+
if (previousGetter == null) {
|
|
107
113
|
currentValue = obj[property]
|
|
108
114
|
}
|
|
109
115
|
|
|
110
116
|
Object.defineProperty(obj, property, {
|
|
111
117
|
get(): T[K] {
|
|
112
|
-
if (previousGetter) {
|
|
118
|
+
if (previousGetter !== undefined) {
|
|
113
119
|
return previousGetter.call(obj)
|
|
114
120
|
}
|
|
115
121
|
return currentValue
|
|
116
122
|
},
|
|
117
123
|
set(value: T[K]) {
|
|
118
|
-
if (previousSetter) {
|
|
124
|
+
if (previousSetter !== undefined) {
|
|
119
125
|
previousSetter.call(obj, value)
|
|
120
126
|
} else {
|
|
121
127
|
currentValue = value
|
|
@@ -145,8 +151,8 @@ type GlobalWithFiberCurrent = {
|
|
|
145
151
|
}
|
|
146
152
|
|
|
147
153
|
const patchedTracer = new WeakSet<Tracer.Tracer>()
|
|
148
|
-
|
|
149
|
-
if (patchedTracer.has(currentTracer)) {
|
|
154
|
+
const ensureTracerPatched = (currentTracer: Tracer.Tracer) => {
|
|
155
|
+
if (patchedTracer.has(currentTracer) === true) {
|
|
150
156
|
return
|
|
151
157
|
}
|
|
152
158
|
patchedTracer.add(currentTracer)
|
|
@@ -175,6 +181,7 @@ function ensureTracerPatched(currentTracer: Tracer.Tracer) {
|
|
|
175
181
|
currentTracer.context = function (f, fiber, ...args) {
|
|
176
182
|
const context = oldContext.apply(this, [f, fiber, ...args])
|
|
177
183
|
ensureFiberPatched(fiber)
|
|
184
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- Effect Tracer.context return type is opaque; patching requires cast
|
|
178
185
|
return context as any
|
|
179
186
|
}
|
|
180
187
|
}
|
|
@@ -196,14 +203,16 @@ const knownScopes = new Map<
|
|
|
196
203
|
{ id: number; allocationFiber: Fiber.RuntimeFiber<any, any> | undefined; allocationSpan: Tracer.AnySpan | undefined }
|
|
197
204
|
>()
|
|
198
205
|
let lastScopeId = 0
|
|
199
|
-
|
|
206
|
+
const ensureScopePatched = (scope: ScopeImpl, allocationFiber: Fiber.RuntimeFiber<any, any> | undefined) => {
|
|
200
207
|
if (scope.state._tag === 'Closed') return
|
|
201
|
-
if (knownScopes.has(scope)) return
|
|
208
|
+
if (knownScopes.has(scope) === true) return
|
|
202
209
|
const id = lastScopeId++
|
|
203
|
-
if (patchScopeClose) {
|
|
210
|
+
if (patchScopeClose === true) {
|
|
211
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- patching Scope.close; ScopeImpl is an internal interface not exported by Effect
|
|
204
212
|
const oldClose = (scope as any).close
|
|
213
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- patching Scope.close; ScopeImpl is an internal interface not exported by Effect
|
|
205
214
|
;(scope as any).close = function (...args: any[]) {
|
|
206
|
-
return oldClose.apply(this
|
|
215
|
+
return oldClose.apply(this, args).pipe(
|
|
207
216
|
Effect.withSpan(`scope.${id}.closeRunFinalizers`),
|
|
208
217
|
Effect.ensuring(
|
|
209
218
|
Effect.sync(() => {
|
|
@@ -225,14 +234,15 @@ const cleanupScopes = () => {
|
|
|
225
234
|
}
|
|
226
235
|
|
|
227
236
|
const knownFibers = new Set<Fiber.RuntimeFiber<any, any>>()
|
|
228
|
-
|
|
237
|
+
const ensureFiberPatched = (fiber: Fiber.RuntimeFiber<any, any>) => {
|
|
229
238
|
// patch tracer
|
|
230
239
|
ensureTracerPatched(fiber.currentTracer)
|
|
231
240
|
// patch scope
|
|
232
241
|
const currentScope = Context.getOrElse(fiber.currentContext, Scope.Scope, () => undefined)
|
|
233
|
-
|
|
242
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- casting Scope to ScopeImpl; internal Effect type not publicly exported
|
|
243
|
+
if (currentScope !== undefined) ensureScopePatched(currentScope as any as ScopeImpl, undefined)
|
|
234
244
|
// patch fiber
|
|
235
|
-
if (knownFibers.has(fiber)) return
|
|
245
|
+
if (knownFibers.has(fiber) === true) return
|
|
236
246
|
knownFibers.add(fiber)
|
|
237
247
|
fiber.addObserver((exit) => {
|
|
238
248
|
knownFibers.delete(fiber)
|
|
@@ -244,7 +254,7 @@ let patchScopeClose = false
|
|
|
244
254
|
let onFiberResumed: undefined | ((fiber: Fiber.RuntimeFiber<any, any>) => void)
|
|
245
255
|
let onFiberSuspended: undefined | ((fiber: Fiber.RuntimeFiber<any, any>) => void)
|
|
246
256
|
let onFiberCompleted: undefined | ((fiber: Fiber.RuntimeFiber<any, any>, exit: Exit.Exit<any, any>) => void)
|
|
247
|
-
export
|
|
257
|
+
export const attachSlowDebugInstrumentation = (options: {
|
|
248
258
|
/** If set to true, the scope prototype will be patched to attach a span to visualize pending scope closing */
|
|
249
259
|
readonly patchScopeClose?: boolean
|
|
250
260
|
/** An optional callback that will be called when any fiber resumes performing a run loop */
|
|
@@ -253,9 +263,10 @@ export function attachSlowDebugInstrumentation(options: {
|
|
|
253
263
|
readonly onFiberSuspended?: (fiber: Fiber.RuntimeFiber<any, any>) => void
|
|
254
264
|
/** An optional callback that will be called when any fiber completes with a exit */
|
|
255
265
|
readonly onFiberCompleted?: (fiber: Fiber.RuntimeFiber<any, any>, exit: Exit.Exit<any, any>) => void
|
|
256
|
-
}) {
|
|
266
|
+
}): void => {
|
|
267
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- accessing Effect's global fiber tracking via well-known symbol keys
|
|
257
268
|
const _globalThis = globalThis as any as GlobalWithFiberCurrent
|
|
258
|
-
if (_globalThis['effect/DevtoolsHook']) {
|
|
269
|
+
if (_globalThis['effect/DevtoolsHook'] !== undefined) {
|
|
259
270
|
return console.error(
|
|
260
271
|
'attachDebugInstrumentation has already been called! To show the tree, call attachDebugInstrumentation() in the root/main file of your program to ensure it is loaded as soon as possible.',
|
|
261
272
|
)
|
|
@@ -265,10 +276,11 @@ export function attachSlowDebugInstrumentation(options: {
|
|
|
265
276
|
onFiberSuspended = options.onFiberSuspended
|
|
266
277
|
onFiberCompleted = options.onFiberCompleted
|
|
267
278
|
let lastFiber: undefined | Fiber.RuntimeFiber<any, any>
|
|
279
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- accessing Effect's global fiber tracking via well-known symbol keys
|
|
268
280
|
createPropertyInterceptor(globalThis as any as GlobalWithFiberCurrent, 'effect/FiberCurrent', (value) => {
|
|
269
|
-
if (value && knownFibers.has(value)) onFiberResumed?.(value)
|
|
270
|
-
if (value) ensureFiberPatched(value)
|
|
271
|
-
if (
|
|
281
|
+
if (value !== undefined && knownFibers.has(value) === true) onFiberResumed?.(value)
|
|
282
|
+
if (value !== undefined) ensureFiberPatched(value)
|
|
283
|
+
if (value == null && lastFiber !== undefined && knownFibers.has(lastFiber) === true) onFiberSuspended?.(lastFiber)
|
|
272
284
|
lastFiber = value
|
|
273
285
|
})
|
|
274
286
|
_globalThis['effect/DevtoolsHook'] = {
|
|
@@ -276,6 +288,7 @@ export function attachSlowDebugInstrumentation(options: {
|
|
|
276
288
|
console.log('onEvent', event)
|
|
277
289
|
switch (event._tag) {
|
|
278
290
|
case 'ScopeAllocated':
|
|
291
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- casting Scope to ScopeImpl; internal Effect type not publicly exported
|
|
279
292
|
ensureScopePatched(event.scope as any as ScopeImpl, _globalThis['effect/FiberCurrent'])
|
|
280
293
|
break
|
|
281
294
|
case 'FiberAllocated':
|
|
@@ -286,7 +299,7 @@ export function attachSlowDebugInstrumentation(options: {
|
|
|
286
299
|
}
|
|
287
300
|
}
|
|
288
301
|
|
|
289
|
-
|
|
302
|
+
const formatDuration = (startTime: bigint, endTime: bigint | undefined): string => {
|
|
290
303
|
if (endTime === undefined) return '[running]'
|
|
291
304
|
const durationMs = Number(endTime - startTime) / 1000000 // Convert nanoseconds to milliseconds
|
|
292
305
|
if (durationMs < 1000) return `${durationMs.toFixed(0)}ms`
|
|
@@ -294,12 +307,12 @@ function formatDuration(startTime: bigint, endTime: bigint | undefined): string
|
|
|
294
307
|
return `${(durationMs / 60000).toFixed(2)}m`
|
|
295
308
|
}
|
|
296
309
|
|
|
297
|
-
|
|
310
|
+
const getSpanName = (span: Tracer.AnySpan): string => {
|
|
298
311
|
if (span._tag === 'ExternalSpan') return `[external] ${span.spanId}`
|
|
299
312
|
return span.name
|
|
300
313
|
}
|
|
301
314
|
|
|
302
|
-
|
|
315
|
+
const getSpanStatus = (info: GraphNodeInfo): string => {
|
|
303
316
|
if (info.span._tag === 'ExternalSpan') return '?'
|
|
304
317
|
if (info.exitTag === 'Success') return '✓'
|
|
305
318
|
if (info.exitTag === 'Failure') return '✗'
|
|
@@ -307,16 +320,16 @@ function getSpanStatus(info: GraphNodeInfo): string {
|
|
|
307
320
|
return '⋮'
|
|
308
321
|
}
|
|
309
322
|
|
|
310
|
-
|
|
323
|
+
const getSpanDuration = (span: Tracer.AnySpan): string => {
|
|
311
324
|
if (span._tag === 'ExternalSpan') return ''
|
|
312
325
|
const endTime = span.status._tag === 'Ended' ? span.status.endTime : undefined
|
|
313
326
|
return formatDuration(span.status.startTime, endTime)
|
|
314
327
|
}
|
|
315
328
|
|
|
316
|
-
|
|
329
|
+
const filterGraphKeepAncestors = <N, E>(
|
|
317
330
|
graph: Graph.Graph<N, E>,
|
|
318
331
|
predicate: (nodeData: N, nodeId: number) => boolean,
|
|
319
|
-
): Graph.Graph<N, E> {
|
|
332
|
+
): Graph.Graph<N, E> => {
|
|
320
333
|
// Find all root nodes (nodes with no incoming edges)
|
|
321
334
|
const rootNodes = Array.from(Graph.indices(Graph.externals(graph, { direction: 'incoming' })))
|
|
322
335
|
const shouldInclude = new Set<number>()
|
|
@@ -324,35 +337,35 @@ function filterGraphKeepAncestors<N, E>(
|
|
|
324
337
|
// Use postorder DFS to evaluate children before parents
|
|
325
338
|
for (const nodeId of Graph.indices(Graph.dfsPostOrder(graph, { start: rootNodes, direction: 'outgoing' }))) {
|
|
326
339
|
const node = Graph.getNode(graph, nodeId)
|
|
327
|
-
if (Option.isNone(node)) continue
|
|
340
|
+
if (Option.isNone(node) === true) continue
|
|
328
341
|
|
|
329
342
|
const matchesPredicate = predicate(node.value, nodeId)
|
|
330
|
-
if (matchesPredicate) {
|
|
343
|
+
if (matchesPredicate === true) {
|
|
331
344
|
shouldInclude.add(nodeId)
|
|
332
345
|
} else {
|
|
333
346
|
const children = Graph.neighborsDirected(graph, nodeId, 'outgoing')
|
|
334
347
|
const hasMatchingChildren = children.some((childId) => shouldInclude.has(childId))
|
|
335
|
-
if (hasMatchingChildren) shouldInclude.add(nodeId)
|
|
348
|
+
if (hasMatchingChildren === true) shouldInclude.add(nodeId)
|
|
336
349
|
}
|
|
337
350
|
}
|
|
338
351
|
|
|
339
352
|
// Create a filtered copy of the graph
|
|
340
353
|
return Graph.mutate(graph, (mutable) => {
|
|
341
354
|
for (const [nodeId] of mutable.nodes) {
|
|
342
|
-
if (shouldInclude.has(nodeId)) continue
|
|
355
|
+
if (shouldInclude.has(nodeId) === true) continue
|
|
343
356
|
Graph.removeNode(mutable, nodeId)
|
|
344
357
|
}
|
|
345
358
|
})
|
|
346
359
|
}
|
|
347
360
|
|
|
348
|
-
|
|
361
|
+
const renderSpanNode = (graph: Graph.Graph<GraphNodeInfo, void>, nodeId: number): string[] => {
|
|
349
362
|
const node = Graph.getNode(graph, nodeId)
|
|
350
|
-
if (Option.isNone(node)) return []
|
|
363
|
+
if (Option.isNone(node) === true) return []
|
|
351
364
|
const info = node.value
|
|
352
365
|
const status = getSpanStatus(info)
|
|
353
366
|
const name = getSpanName(info.span)
|
|
354
367
|
const duration = getSpanDuration(info.span)
|
|
355
|
-
const durationStr = duration ? ` ${duration}` : ''
|
|
368
|
+
const durationStr = duration !== undefined ? ` ${duration}` : ''
|
|
356
369
|
|
|
357
370
|
const fiberIds = Array.from(knownFibers)
|
|
358
371
|
.filter(
|
|
@@ -365,11 +378,11 @@ function renderSpanNode(graph: Graph.Graph<GraphNodeInfo, void, 'directed'>, nod
|
|
|
365
378
|
return [` ${status} ${name}${durationStr}${runningOnFibers}`]
|
|
366
379
|
}
|
|
367
380
|
|
|
368
|
-
|
|
381
|
+
const renderTree = <N, E, T extends Graph.Kind>(
|
|
369
382
|
graph: Graph.Graph<N, E, T>,
|
|
370
383
|
nodeIds: Array<number>,
|
|
371
384
|
renderNode: (graph: Graph.Graph<N, E, T>, nodeId: number) => string[],
|
|
372
|
-
): string[] {
|
|
385
|
+
): string[] => {
|
|
373
386
|
let lines: string[] = []
|
|
374
387
|
for (let childIndex = 0; childIndex < nodeIds.length; childIndex++) {
|
|
375
388
|
const isLastChild = childIndex === nodeIds.length - 1
|
|
@@ -380,9 +393,9 @@ function renderTree<N, E, T extends Graph.Kind>(
|
|
|
380
393
|
...lines,
|
|
381
394
|
...childLines.map((l, lineIndex) => {
|
|
382
395
|
if (lineIndex === 0) {
|
|
383
|
-
return (isLastChild ? ' └─' : ' ├─') + l
|
|
396
|
+
return (isLastChild === true ? ' └─' : ' ├─') + l
|
|
384
397
|
}
|
|
385
|
-
return (isLastChild ? ' ' : ' │') + l
|
|
398
|
+
return (isLastChild === true ? ' ' : ' │') + l
|
|
386
399
|
}),
|
|
387
400
|
]
|
|
388
401
|
}
|
|
@@ -396,8 +409,9 @@ export interface LogDebugOptions {
|
|
|
396
409
|
}
|
|
397
410
|
|
|
398
411
|
export const logDebug = (options: LogDebugOptions = {}) => {
|
|
412
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- accessing Effect's global fiber tracking via well-known symbol keys
|
|
399
413
|
const _globalThis = globalThis as any as GlobalWithFiberCurrent
|
|
400
|
-
if (
|
|
414
|
+
if (_globalThis['effect/DevtoolsHook'] == null) {
|
|
401
415
|
return console.error(
|
|
402
416
|
'attachDebugInstrumentation has not been called! To show the tree, call attachDebugInstrumentation() in the root/main file of your program to ensure it is loaded as soon as possible.',
|
|
403
417
|
)
|
|
@@ -408,8 +422,9 @@ export const logDebug = (options: LogDebugOptions = {}) => {
|
|
|
408
422
|
// fibers
|
|
409
423
|
lines = [...lines, 'Active Fibers:']
|
|
410
424
|
for (const fiber of knownFibers) {
|
|
425
|
+
// oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- accessing fiber.currentRuntimeFlags; internal Effect runtime property
|
|
411
426
|
const interruptible = RuntimeFlags.interruptible((fiber as any).currentRuntimeFlags)
|
|
412
|
-
lines = [...lines, `- #${fiber.id().id}${
|
|
427
|
+
lines = [...lines, `- #${fiber.id().id}${interruptible === false ? ' [uninterruptible]' : ''}`]
|
|
413
428
|
}
|
|
414
429
|
if (knownFibers.size === 0) {
|
|
415
430
|
lines = [...lines, '- No active effect fibers']
|
|
@@ -419,12 +434,13 @@ export const logDebug = (options: LogDebugOptions = {}) => {
|
|
|
419
434
|
// spans
|
|
420
435
|
for (const [traceId, info] of graphByTraceId) {
|
|
421
436
|
const graph = Graph.endMutation(info.graph)
|
|
422
|
-
const filteredGraph =
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
437
|
+
const filteredGraph =
|
|
438
|
+
options.regex !== undefined
|
|
439
|
+
? filterGraphKeepAncestors(graph, (nodeData, _nodeId) => {
|
|
440
|
+
const name = getSpanName(nodeData.span)
|
|
441
|
+
return options.regex!.test(name)
|
|
442
|
+
})
|
|
443
|
+
: graph
|
|
428
444
|
const filteredRootNodes = Array.from(Graph.indices(Graph.externals(filteredGraph, { direction: 'incoming' })))
|
|
429
445
|
|
|
430
446
|
lines = [...lines, `Spans Trace ${traceId}:`, ...renderTree(filteredGraph, filteredRootNodes, renderSpanNode)]
|
|
@@ -439,8 +455,10 @@ export const logDebug = (options: LogDebugOptions = {}) => {
|
|
|
439
455
|
.map((fiber) => `#${fiber.id().id}`)
|
|
440
456
|
.join(', ')
|
|
441
457
|
const usedByFibers = fiberIds.length > 0 ? ` [used by: ${fiberIds}]` : ''
|
|
442
|
-
const allocationFiber =
|
|
443
|
-
|
|
458
|
+
const allocationFiber =
|
|
459
|
+
info.allocationFiber !== undefined ? ` [allocated in fiber #${info.allocationFiber.id().id}]` : ''
|
|
460
|
+
const allocationSpan =
|
|
461
|
+
info.allocationSpan !== undefined ? ` [allocated in span: ${getSpanName(info.allocationSpan)}]` : ''
|
|
444
462
|
lines = [...lines, `- #${info.id}${usedByFibers}${allocationFiber}${allocationSpan}`]
|
|
445
463
|
}
|
|
446
464
|
if (knownScopes.size === 0) {
|
package/src/effect/Effect.ts
CHANGED
|
@@ -6,9 +6,6 @@ import {
|
|
|
6
6
|
Duration,
|
|
7
7
|
Effect,
|
|
8
8
|
Fiber,
|
|
9
|
-
FiberRef,
|
|
10
|
-
HashSet,
|
|
11
|
-
Logger,
|
|
12
9
|
pipe,
|
|
13
10
|
Scope,
|
|
14
11
|
type Stream,
|
|
@@ -18,10 +15,11 @@ import { log } from 'effect/Console'
|
|
|
18
15
|
import { dual, type LazyArg } from 'effect/Function'
|
|
19
16
|
import type { Predicate, Refinement } from 'effect/Predicate'
|
|
20
17
|
|
|
21
|
-
import { isPromise } from '../mod.ts'
|
|
18
|
+
import { isDevEnv, isPromise, objectToString } from '../mod.ts'
|
|
22
19
|
import { UnknownError } from './Error.ts'
|
|
23
20
|
|
|
24
21
|
export * from 'effect/Effect'
|
|
22
|
+
export { spanEvent } from './spanEvent.ts'
|
|
25
23
|
|
|
26
24
|
// export const log = <A>(message: A, ...rest: any[]): Effect.Effect<void> =>
|
|
27
25
|
// Effect.sync(() => {
|
|
@@ -57,16 +55,16 @@ export type SyncOrPromiseOrEffect<TResult, TError = never, TContext = never> =
|
|
|
57
55
|
|
|
58
56
|
export const tryAll = <Res>(
|
|
59
57
|
fn: () => Res,
|
|
60
|
-
): Res extends Effect.Effect<infer A, infer E
|
|
61
|
-
? Effect.Effect<A, E | UnknownException
|
|
58
|
+
): Res extends Effect.Effect<infer A, infer E>
|
|
59
|
+
? Effect.Effect<A, E | UnknownException>
|
|
62
60
|
: Res extends Promise<infer A>
|
|
63
|
-
? Effect.Effect<A, UnknownException
|
|
64
|
-
: Effect.Effect<Res, UnknownException
|
|
61
|
+
? Effect.Effect<A, UnknownException>
|
|
62
|
+
: Effect.Effect<Res, UnknownException> =>
|
|
65
63
|
Effect.try(() => fn()).pipe(
|
|
66
64
|
Effect.andThen((fnRes) =>
|
|
67
|
-
Effect.isEffect(fnRes)
|
|
65
|
+
Effect.isEffect(fnRes) === true
|
|
68
66
|
? (fnRes as any as Effect.Effect<any>)
|
|
69
|
-
: isPromise(fnRes)
|
|
67
|
+
: isPromise(fnRes) === true
|
|
70
68
|
? Effect.promise(() => fnRes)
|
|
71
69
|
: Effect.succeed(fnRes),
|
|
72
70
|
),
|
|
@@ -89,7 +87,7 @@ export const logBefore =
|
|
|
89
87
|
export const tapCauseLogPretty = <R, E, A>(eff: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
|
|
90
88
|
Effect.tapErrorCause(eff, (cause) =>
|
|
91
89
|
Effect.gen(function* () {
|
|
92
|
-
if (Cause.isInterruptedOnly(cause)) {
|
|
90
|
+
if (Cause.isInterruptedOnly(cause) === true) {
|
|
93
91
|
// console.log('interrupted', Cause.pretty(err), err)
|
|
94
92
|
return
|
|
95
93
|
}
|
|
@@ -107,6 +105,47 @@ export const tapCauseLogPretty = <R, E, A>(eff: Effect.Effect<A, E, R>): Effect.
|
|
|
107
105
|
}),
|
|
108
106
|
)
|
|
109
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Creates a defect, pausing at a breakpoint in development.
|
|
110
|
+
*
|
|
111
|
+
* @param msg - The error message to include in the defect.
|
|
112
|
+
* @param args - Arbitrary arguments available for inspection during debugging.
|
|
113
|
+
*
|
|
114
|
+
* @see {@link shouldNeverHappen} for the non-Effect equivalent that throws synchronously.
|
|
115
|
+
* @see {@link orDieDebugger}
|
|
116
|
+
*/
|
|
117
|
+
export const dieDebugger = (msg: string, ...args: ReadonlyArray<unknown>): Effect.Effect<never> =>
|
|
118
|
+
Effect.suspend(() => {
|
|
119
|
+
if (isDevEnv() === true) {
|
|
120
|
+
// oxlint-disable-next-line eslint(no-debugger) -- intentional breakpoint during development
|
|
121
|
+
debugger
|
|
122
|
+
void args // Keeps the variable in scope so it's inspectable when the debugger pauses
|
|
123
|
+
}
|
|
124
|
+
return Effect.dieMessage(msg)
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Converts a failure into a defect, pausing at a breakpoint in development.
|
|
129
|
+
*
|
|
130
|
+
* @param self - The effect on which to apply the operation.
|
|
131
|
+
*
|
|
132
|
+
* @see {@link Effect.orDie}
|
|
133
|
+
* @see {@link dieDebugger}
|
|
134
|
+
*/
|
|
135
|
+
export const orDieDebugger = <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, never, R> =>
|
|
136
|
+
Effect.matchEffect(self, {
|
|
137
|
+
onFailure: (error) =>
|
|
138
|
+
// Keep the debugger hook so that `debugger` runs only when the wrapped effect actually fails, not while building the wrapper.
|
|
139
|
+
Effect.dieSync(() => {
|
|
140
|
+
if (isDevEnv() === true) {
|
|
141
|
+
// oxlint-disable-next-line eslint(no-debugger) -- intentional breakpoint for impossible states during development
|
|
142
|
+
debugger
|
|
143
|
+
}
|
|
144
|
+
return error
|
|
145
|
+
}),
|
|
146
|
+
onSuccess: Effect.succeed,
|
|
147
|
+
})
|
|
148
|
+
|
|
110
149
|
export const ignoreIf: {
|
|
111
150
|
<E, EB extends E>(
|
|
112
151
|
refinement: Refinement<NoInfer<E>, EB>,
|
|
@@ -124,11 +163,11 @@ export const ignoreIf: {
|
|
|
124
163
|
export const eventListener = <TEvent = unknown>(
|
|
125
164
|
target: Stream.EventListener<TEvent>,
|
|
126
165
|
type: string,
|
|
127
|
-
handler: (event: TEvent) => Effect.Effect<void
|
|
166
|
+
handler: (event: TEvent) => Effect.Effect<void>,
|
|
128
167
|
options?: { once?: boolean },
|
|
129
168
|
) =>
|
|
130
169
|
Effect.gen(function* () {
|
|
131
|
-
const runtime = yield* Effect.runtime
|
|
170
|
+
const runtime = yield* Effect.runtime()
|
|
132
171
|
|
|
133
172
|
const handlerFn = (event: TEvent) => handler(event).pipe(Effect.provide(runtime), Effect.runFork)
|
|
134
173
|
|
|
@@ -137,16 +176,11 @@ export const eventListener = <TEvent = unknown>(
|
|
|
137
176
|
yield* Effect.addFinalizer(() => Effect.sync(() => target.removeEventListener(type, handlerFn)))
|
|
138
177
|
})
|
|
139
178
|
|
|
140
|
-
export const spanEvent = (message: any, attributes?: Record<string, any>) =>
|
|
141
|
-
Effect.locallyWith(Effect.log(message).pipe(Effect.annotateLogs(attributes ?? {})), FiberRef.currentLoggers, () =>
|
|
142
|
-
HashSet.make(Logger.tracerLogger),
|
|
143
|
-
)
|
|
144
|
-
|
|
145
179
|
export const logWarnIfTakesLongerThan =
|
|
146
180
|
({ label, duration }: { label: string; duration: Duration.DurationInput }) =>
|
|
147
181
|
<R, E, A>(eff: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
|
|
148
182
|
Effect.gen(function* () {
|
|
149
|
-
const runtime = yield* Effect.runtime
|
|
183
|
+
const runtime = yield* Effect.runtime()
|
|
150
184
|
|
|
151
185
|
let tookLongerThanTimer = false
|
|
152
186
|
|
|
@@ -154,7 +188,7 @@ export const logWarnIfTakesLongerThan =
|
|
|
154
188
|
Effect.tap(() => {
|
|
155
189
|
tookLongerThanTimer = true
|
|
156
190
|
// TODO include span info
|
|
157
|
-
return Effect.logWarning(`${label}: Took longer than ${duration}ms`)
|
|
191
|
+
return Effect.logWarning(`${label}: Took longer than ${objectToString(duration)}ms`)
|
|
158
192
|
}),
|
|
159
193
|
Effect.provide(runtime),
|
|
160
194
|
Effect.runFork,
|
|
@@ -169,13 +203,14 @@ export const logWarnIfTakesLongerThan =
|
|
|
169
203
|
|
|
170
204
|
yield* Fiber.interrupt(timeoutFiber)
|
|
171
205
|
|
|
172
|
-
if (tookLongerThanTimer) {
|
|
206
|
+
if (tookLongerThanTimer === true) {
|
|
173
207
|
yield* Effect.logWarning(`${label}: Interrupted after ${end - start}ms`)
|
|
174
208
|
}
|
|
175
209
|
}),
|
|
176
210
|
),
|
|
177
211
|
)
|
|
178
212
|
|
|
213
|
+
// eslint-disable-next-line overeng/explicit-boolean-compare -- mutated in forked fiber; TS can't see the mutation
|
|
179
214
|
if (tookLongerThanTimer) {
|
|
180
215
|
const end = Date.now()
|
|
181
216
|
yield* Effect.logWarning(`${label}: Actual duration: ${end - start}ms`)
|
|
@@ -208,19 +243,61 @@ export const debugLogEnv = (msg?: string): Effect.Effect<Context.Context<never>>
|
|
|
208
243
|
Effect.tap((env) => log(msg ?? 'debugLogEnv', env)),
|
|
209
244
|
)
|
|
210
245
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
246
|
+
/**
|
|
247
|
+
* Enforces a time limit on an effect, triggering a defect on timeout.
|
|
248
|
+
*
|
|
249
|
+
* @remarks
|
|
250
|
+
*
|
|
251
|
+
* This function allows you to enforce a time limit on the execution of an
|
|
252
|
+
* effect. If the effect does not complete within the given duration, it dies
|
|
253
|
+
* with a {@link Cause.TimeoutException} as an unchecked defect. Unlike
|
|
254
|
+
* {@link Effect.timeout}, which adds `TimeoutException` to the error channel,
|
|
255
|
+
* this function keeps the error channel unchanged by treating the timeout as
|
|
256
|
+
* a defect.
|
|
257
|
+
*
|
|
258
|
+
* The returned effect will either:
|
|
259
|
+
* - Succeed with the original effect's result if it completes within the
|
|
260
|
+
* specified duration.
|
|
261
|
+
* - Die with a {@link Cause.TimeoutException} defect if the time limit is exceeded.
|
|
262
|
+
*
|
|
263
|
+
* @see {@link timeoutOrDieMessage} for a version with a custom message.
|
|
264
|
+
* @see {@link Effect.timeout} for a version that raises a `TimeoutException` as a typed error.
|
|
265
|
+
* @see {@link Effect.timeoutFailCause} for a version that raises a custom defect.
|
|
266
|
+
*/
|
|
267
|
+
export const timeoutOrDie = (duration: Duration.DurationInput) =>
|
|
268
|
+
<A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
|
|
269
|
+
Effect.timeoutFailCause(self, {
|
|
270
|
+
duration,
|
|
271
|
+
onTimeout: () => Cause.die(new Cause.TimeoutException())
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Enforces a time limit on an effect, triggering a defect with a custom
|
|
276
|
+
* message on timeout.
|
|
277
|
+
*
|
|
278
|
+
* @remarks
|
|
279
|
+
*
|
|
280
|
+
* This function behaves like {@link timeoutOrDie}, but allows you to provide
|
|
281
|
+
* a custom message for the {@link Cause.TimeoutException} defect. This is useful
|
|
282
|
+
* for adding context about which operation timed out, making it easier to
|
|
283
|
+
* diagnose issues in logs or error reports.
|
|
284
|
+
*
|
|
285
|
+
* The returned effect will either:
|
|
286
|
+
* - Succeed with the original effect's result if it completes within the
|
|
287
|
+
* specified duration.
|
|
288
|
+
* - Die with a {@link Cause.TimeoutException} defect containing the provided
|
|
289
|
+
* message if the time limit is exceeded.
|
|
290
|
+
*
|
|
291
|
+
* @see {@link timeoutOrDie} for a version without a custom message.
|
|
292
|
+
* @see {@link Effect.timeout} for a version that raises a `TimeoutException` as a typed error.
|
|
293
|
+
* @see {@link Effect.timeoutFailCause} for a version that raises a custom defect.
|
|
294
|
+
*/
|
|
295
|
+
export const timeoutOrDieMessage = (duration: Duration.DurationInput, message: string) =>
|
|
296
|
+
<A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
|
|
297
|
+
Effect.timeoutFailCause(self, {
|
|
298
|
+
duration,
|
|
299
|
+
onTimeout: () => Cause.die(new Cause.TimeoutException(message))
|
|
300
|
+
})
|
|
224
301
|
|
|
225
302
|
export const toForkedDeferred = <R, E, A>(
|
|
226
303
|
eff: Effect.Effect<A, E, R>,
|
|
@@ -274,7 +351,12 @@ const getSpanTrace = () => {
|
|
|
274
351
|
|
|
275
352
|
const logSpanTrace = () => console.log(getSpanTrace())
|
|
276
353
|
|
|
277
|
-
|
|
354
|
+
declare global {
|
|
355
|
+
/** Debug helper: returns the current Effect span trace */
|
|
356
|
+
var getSpanTrace: () => string
|
|
357
|
+
/** Debug helper: logs the current Effect span trace */
|
|
358
|
+
var logSpanTrace: () => void
|
|
359
|
+
}
|
|
360
|
+
|
|
278
361
|
globalThis.getSpanTrace = getSpanTrace
|
|
279
|
-
// @ts-expect-error TODO fix types
|
|
280
362
|
globalThis.logSpanTrace = logSpanTrace
|
package/src/effect/Error.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Schema } from 'effect'
|
|
2
2
|
|
|
3
|
-
export class UnknownError extends Schema.TaggedError<UnknownError>()('UnknownError', {
|
|
3
|
+
export class UnknownError extends Schema.TaggedError<UnknownError>('~@livestore/utils/UnknownError')('UnknownError', {
|
|
4
4
|
cause: Schema.Any,
|
|
5
5
|
payload: Schema.optional(Schema.Any),
|
|
6
6
|
}) {}
|
package/src/effect/Logger.ts
CHANGED
|
@@ -23,7 +23,7 @@ export const consoleLogger = (threadName: string) =>
|
|
|
23
23
|
const consoleFn =
|
|
24
24
|
logLevel === LogLevel.Debug
|
|
25
25
|
? // Cloudflare Workers doesn't support console.debug 🤷
|
|
26
|
-
isCloudflareWorker
|
|
26
|
+
isCloudflareWorker === true
|
|
27
27
|
? console.log
|
|
28
28
|
: console.debug
|
|
29
29
|
: logLevel === LogLevel.Info
|
|
@@ -34,7 +34,7 @@ export const consoleLogger = (threadName: string) =>
|
|
|
34
34
|
|
|
35
35
|
const annotationsObj = Object.fromEntries(HashMap.entries(annotations))
|
|
36
36
|
|
|
37
|
-
const messages = Array.isArray(message) ? message : [message]
|
|
37
|
+
const messages = Array.isArray(message) === true ? message : [message]
|
|
38
38
|
if (Cause.isEmpty(cause) === false) {
|
|
39
39
|
messages.push(Cause.pretty(cause, { renderErrorCause: true }))
|
|
40
40
|
}
|