@livestore/utils 0.4.0-dev.8 → 0.4.0
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 +17 -4
- 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 +51 -0
- package/dist/browser/Opfs/Opfs.d.ts.map +1 -0
- package/dist/browser/Opfs/Opfs.js +345 -0
- package/dist/browser/Opfs/Opfs.js.map +1 -0
- package/dist/browser/Opfs/debug-utils.d.ts +20 -0
- package/dist/browser/Opfs/debug-utils.d.ts.map +1 -0
- package/dist/browser/Opfs/debug-utils.js +94 -0
- package/dist/browser/Opfs/debug-utils.js.map +1 -0
- package/dist/browser/Opfs/mod.d.ts +4 -0
- package/dist/browser/Opfs/mod.d.ts.map +1 -0
- package/dist/browser/Opfs/mod.js +4 -0
- package/dist/browser/Opfs/mod.js.map +1 -0
- package/dist/browser/Opfs/utils.d.ts +71 -0
- package/dist/browser/Opfs/utils.d.ts.map +1 -0
- package/dist/browser/Opfs/utils.js +218 -0
- package/dist/browser/Opfs/utils.js.map +1 -0
- package/dist/browser/QuotaExceededError.d.ts +59 -0
- package/dist/browser/QuotaExceededError.d.ts.map +1 -0
- package/dist/browser/QuotaExceededError.js +2 -0
- package/dist/browser/QuotaExceededError.js.map +1 -0
- package/dist/browser/WebChannelBrowser.d.ts +22 -0
- package/dist/browser/WebChannelBrowser.d.ts.map +1 -0
- package/dist/browser/WebChannelBrowser.js +76 -0
- package/dist/browser/WebChannelBrowser.js.map +1 -0
- package/dist/browser/WebError.d.ts +421 -0
- package/dist/browser/WebError.d.ts.map +1 -0
- package/dist/browser/WebError.js +416 -0
- package/dist/browser/WebError.js.map +1 -0
- package/dist/browser/WebError.test.d.ts +2 -0
- package/dist/browser/WebError.test.d.ts.map +1 -0
- package/dist/browser/WebError.test.js +46 -0
- package/dist/browser/WebError.test.js.map +1 -0
- package/dist/browser/WebLock.d.ts.map +1 -0
- package/dist/{effect → browser}/WebLock.js +9 -9
- package/dist/browser/WebLock.js.map +1 -0
- package/dist/{browser.d.ts → browser/detect.d.ts} +1 -1
- package/dist/browser/detect.d.ts.map +1 -0
- package/dist/{browser.js → browser/detect.js} +7 -7
- package/dist/browser/detect.js.map +1 -0
- package/dist/browser/mod.d.ts +8 -0
- package/dist/browser/mod.d.ts.map +1 -0
- package/dist/browser/mod.js +8 -0
- package/dist/browser/mod.js.map +1 -0
- 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 +41 -0
- package/dist/effect/Debug.d.ts.map +1 -0
- package/dist/effect/Debug.js +354 -0
- package/dist/effect/Debug.js.map +1 -0
- 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 +11 -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/debug-diff.test.js +1 -1
- package/dist/effect/Schema/debug-diff.test.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 +2 -2
- 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 +4 -23
- package/dist/effect/WebChannel/WebChannel.d.ts.map +1 -1
- package/dist/effect/WebChannel/WebChannel.js +9 -85
- package/dist/effect/WebChannel/WebChannel.js.map +1 -1
- package/dist/effect/WebChannel/WebChannel.test.js +1 -1
- package/dist/effect/WebChannel/WebChannel.test.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 +2 -2
- 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.d.ts.map +1 -1
- package/dist/effect/WebSocket.js +14 -14
- package/dist/effect/WebSocket.js.map +1 -1
- package/dist/effect/{index.d.ts → mod.d.ts} +4 -4
- package/dist/effect/mod.d.ts.map +1 -0
- package/dist/effect/{index.js → mod.js} +5 -5
- package/dist/effect/mod.js.map +1 -0
- 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 +3 -0
- package/dist/global.d.ts.map +1 -1
- package/dist/global.js.map +1 -1
- package/dist/guards.d.ts +14 -0
- package/dist/guards.d.ts.map +1 -1
- package/dist/guards.js +14 -0
- package/dist/guards.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 +197 -5
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +162 -17
- 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 +16 -17
- package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.js.map +1 -1
- package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/schema.d.ts +4 -4
- package/dist/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.js +3 -3
- 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 +34 -1
- package/dist/node/mod.d.ts.map +1 -1
- package/dist/node/mod.js +37 -2
- 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.d.ts +38 -0
- package/dist/qr.d.ts.map +1 -0
- package/dist/qr.js +109 -0
- package/dist/qr.js.map +1 -0
- 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 +78 -54
- package/src/NoopTracer.ts +22 -8
- package/src/binary.ts +1 -1
- package/src/browser/Opfs/Opfs.ts +436 -0
- package/src/browser/Opfs/debug-utils.ts +153 -0
- package/src/browser/Opfs/mod.ts +3 -0
- package/src/browser/Opfs/utils.ts +287 -0
- package/src/browser/QuotaExceededError.ts +57 -0
- package/src/browser/WebChannelBrowser.ts +131 -0
- package/src/browser/WebError.test.ts +66 -0
- package/src/browser/WebError.ts +613 -0
- package/src/{effect → browser}/WebLock.ts +15 -15
- package/src/{browser.ts → browser/detect.ts} +6 -6
- package/src/browser/mod.ts +8 -0
- 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 +470 -0
- 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 +14 -6
- package/src/effect/Schema/debug-diff.test.ts +2 -2
- package/src/effect/Schema/debug-diff.ts +6 -5
- package/src/effect/Schema/index.ts +10 -7
- 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.test.ts +1 -1
- package/src/effect/WebChannel/WebChannel.ts +19 -141
- package/src/effect/WebChannel/broadcastChannelWithAck.ts +4 -4
- package/src/effect/WebChannel/common.ts +5 -5
- package/src/effect/WebSocket.ts +13 -12
- package/src/effect/{index.ts → mod.ts} +10 -2
- 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 +4 -2
- package/src/guards.ts +15 -0
- package/src/misc.ts +12 -4
- package/src/mod.ts +209 -17
- package/src/node/ChildProcessRunner/ChildProcessRunner.ts +23 -10
- package/src/node/ChildProcessRunner/ChildProcessRunnerTest/ChildProcessRunner.test.ts +30 -21
- package/src/node/ChildProcessRunner/ChildProcessRunnerTest/serializedWorker.ts +10 -11
- package/src/node/ChildProcessRunner/ChildProcessWorker.ts +5 -4
- package/src/node/mod.ts +41 -2
- 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 +125 -0
- package/src/set.ts +1 -1
- package/src/time.ts +1 -1
- package/dist/.tsbuildinfo.json +0 -1
- package/dist/browser.d.ts.map +0 -1
- package/dist/browser.js.map +0 -1
- package/dist/effect/WebLock.d.ts.map +0 -1
- package/dist/effect/WebLock.js.map +0 -1
- package/dist/effect/index.d.ts.map +0 -1
- package/dist/effect/index.js.map +0 -1
- /package/dist/{effect → browser}/WebLock.d.ts +0 -0
package/src/mod.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export { default as prettyBytes } from 'pretty-bytes'
|
|
2
2
|
export * as base64 from './base64.ts'
|
|
3
3
|
export * from './binary.ts'
|
|
4
|
-
export * from './browser.ts'
|
|
5
4
|
export * from './Deferred.ts'
|
|
6
5
|
export * from './env.ts'
|
|
7
6
|
export * from './fast-deep-equal.ts'
|
|
@@ -10,6 +9,7 @@ export * from './misc.ts'
|
|
|
10
9
|
export * from './NoopTracer.ts'
|
|
11
10
|
export * from './object/index.ts'
|
|
12
11
|
export * from './promise.ts'
|
|
12
|
+
export * as QR from './qr.ts'
|
|
13
13
|
export * from './set.ts'
|
|
14
14
|
export * from './string.ts'
|
|
15
15
|
export * from './time.ts'
|
|
@@ -19,49 +19,118 @@ import type { Types } from 'effect'
|
|
|
19
19
|
|
|
20
20
|
import { objectToString } from './misc.ts'
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Recursively expands type aliases for better IDE hover display.
|
|
24
|
+
*
|
|
25
|
+
* Transforms `{ a: string } & { b: number }` into `{ a: string; b: number }`.
|
|
26
|
+
*/
|
|
22
27
|
export type Prettify<T> = T extends infer U ? { [K in keyof U]: Prettify<U[K]> } : never
|
|
23
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Type-level equality check. Returns `true` if `A` and `B` are exactly the same type.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* type Test1 = TypeEq<string, string> // true
|
|
35
|
+
* type Test2 = TypeEq<string, number> // false
|
|
36
|
+
* type Test3 = TypeEq<{ a: 1 }, { a: 1 }> // true
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
24
39
|
export type TypeEq<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false
|
|
25
40
|
|
|
26
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Type-level subtype check. Returns `true` if `A` extends `B`.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* type Test1 = IsSubtype<'foo', string> // true
|
|
47
|
+
* type Test2 = IsSubtype<string, 'foo'> // false
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
27
50
|
export type IsSubtype<A, B> = A extends B ? true : false
|
|
51
|
+
|
|
52
|
+
/** Compile-time assertion that `T` is `true`. Useful for type tests. */
|
|
28
53
|
export type AssertTrue<T extends true> = T
|
|
29
54
|
|
|
55
|
+
/** Removes `readonly` modifier from all properties of `T`. */
|
|
30
56
|
export type Writeable<T> = { -readonly [P in keyof T]: T[P] }
|
|
57
|
+
|
|
58
|
+
/** Recursively removes `readonly` modifier from all properties. */
|
|
31
59
|
export type DeepWriteable<T> = { -readonly [P in keyof T]: DeepWriteable<T[P]> }
|
|
32
60
|
|
|
61
|
+
/** Makes all properties of `T` nullable (allows `null`). */
|
|
33
62
|
export type Nullable<T> = { [K in keyof T]: T[K] | null }
|
|
34
63
|
|
|
64
|
+
/** Union of JavaScript primitive types. */
|
|
35
65
|
export type Primitive = null | undefined | string | number | boolean | symbol | bigint
|
|
36
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Creates a union type that allows specific literals while still accepting the base type.
|
|
69
|
+
* Useful for string/number enums with autocomplete support.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* type Status = LiteralUnion<'pending' | 'active', string>
|
|
74
|
+
* // Allows 'pending', 'active', or any other string
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
37
77
|
export type LiteralUnion<LiteralType, BaseType extends Primitive> = LiteralType | (BaseType & Record<never, never>)
|
|
38
78
|
|
|
79
|
+
/** Extracts the value type for key `K` from object type `T`, or `never` if key doesn't exist. */
|
|
39
80
|
export type GetValForKey<T, K> = K extends keyof T ? T[K] : never
|
|
40
81
|
|
|
82
|
+
/** Accepts either a single value or a readonly array of values. */
|
|
41
83
|
export type SingleOrReadonlyArray<T> = T | ReadonlyArray<T>
|
|
42
84
|
|
|
85
|
+
/** Returns a Promise that resolves after `ms` milliseconds. */
|
|
43
86
|
export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
44
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Creates a mutable reference object with a `current` property.
|
|
90
|
+
* Similar to React's `useRef` but works outside of React.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* const counter = ref(0)
|
|
95
|
+
* counter.current += 1
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
45
98
|
export const ref = <T>(val: T): { current: T } => ({ current: val })
|
|
46
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Calls a function `n` times with the current index.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* times(3, (i) => console.log(i)) // logs 0, 1, 2
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
47
108
|
export const times = (n: number, fn: (index: number) => {}): void => {
|
|
48
109
|
for (let i = 0; i < n; i++) {
|
|
49
110
|
fn(i)
|
|
50
111
|
}
|
|
51
112
|
}
|
|
52
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Wraps a function call in a try/catch that triggers the debugger on error.
|
|
116
|
+
* Useful for debugging exceptions during development.
|
|
117
|
+
*/
|
|
53
118
|
export const debugCatch = <T>(try_: () => T): T => {
|
|
54
119
|
try {
|
|
55
120
|
return try_()
|
|
56
121
|
} catch (e: any) {
|
|
57
|
-
//
|
|
122
|
+
// oxlint-disable-next-line eslint(no-debugger) -- intentional breakpoint for debugging exceptions
|
|
58
123
|
debugger
|
|
59
124
|
throw e
|
|
60
125
|
}
|
|
61
126
|
}
|
|
62
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Recursively removes `undefined` values from an object or array in place.
|
|
130
|
+
* Mutates the input value.
|
|
131
|
+
*/
|
|
63
132
|
export const recRemoveUndefinedValues = (val: any): void => {
|
|
64
|
-
if (Array.isArray(val)) {
|
|
133
|
+
if (Array.isArray(val) === true) {
|
|
65
134
|
val.forEach(recRemoveUndefinedValues)
|
|
66
135
|
} else if (typeof val === 'object') {
|
|
67
136
|
Object.keys(val).forEach((key) => {
|
|
@@ -79,13 +148,24 @@ export const recRemoveUndefinedValues = (val: any): void => {
|
|
|
79
148
|
*/
|
|
80
149
|
export const sluggify = (str: string, separator = '-') => str.replace(/[^a-zA-Z0-9]/g, separator)
|
|
81
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Creates a property accessor function for use in pipelines.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* const users = [{ name: 'Alice' }, { name: 'Bob' }]
|
|
157
|
+
* const names = users.map(prop('name')) // ['Alice', 'Bob']
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
82
160
|
export const prop =
|
|
83
161
|
<T extends {}, K extends keyof T>(key: K) =>
|
|
84
162
|
(obj: T): T[K] =>
|
|
85
163
|
obj[key]
|
|
86
164
|
|
|
165
|
+
/** Capitalizes the first letter of a string. */
|
|
87
166
|
export const capitalizeFirstLetter = (str: string): string => str.charAt(0).toUpperCase() + str.slice(1)
|
|
88
167
|
|
|
168
|
+
/** Type guard that checks if a value is a readonly array. */
|
|
89
169
|
export const isReadonlyArray = <I, T>(value: ReadonlyArray<I> | T): value is ReadonlyArray<I> => Array.isArray(value)
|
|
90
170
|
|
|
91
171
|
/**
|
|
@@ -93,22 +173,38 @@ export const isReadonlyArray = <I, T>(value: ReadonlyArray<I> | T): value is Rea
|
|
|
93
173
|
* union have been accounted for.
|
|
94
174
|
*/
|
|
95
175
|
|
|
96
|
-
export
|
|
97
|
-
//
|
|
176
|
+
export const casesHandled = (unexpectedCase: never): never => {
|
|
177
|
+
// oxlint-disable-next-line eslint(no-debugger) -- intentional breakpoint for unhandled cases
|
|
98
178
|
debugger
|
|
99
179
|
throw new Error(`A case was not handled for value: ${truncate(objectToString(unexpectedCase), 1000)}`)
|
|
100
180
|
}
|
|
101
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Throws if the condition is false. Use for runtime assertions that should never fail.
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```ts
|
|
187
|
+
* assertNever(user !== undefined, 'User must be loaded')
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
102
190
|
export const assertNever = (failIfFalse: boolean, msg?: string): void => {
|
|
103
191
|
if (failIfFalse === false) {
|
|
104
|
-
//
|
|
192
|
+
// oxlint-disable-next-line eslint(no-debugger) -- intentional breakpoint for impossible states
|
|
105
193
|
debugger
|
|
106
194
|
throw new Error(`This should never happen: ${msg}`)
|
|
107
195
|
}
|
|
108
196
|
}
|
|
109
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Identity function that triggers the debugger. Useful for debugging pipelines.
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```ts
|
|
203
|
+
* data.pipe(transform, debuggerPipe, format) // Pauses debugger here
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
110
206
|
export const debuggerPipe = <T>(val: T): T => {
|
|
111
|
-
//
|
|
207
|
+
// oxlint-disable-next-line eslint(no-debugger) -- intentional: this function's purpose is to trigger debugger
|
|
112
208
|
debugger
|
|
113
209
|
return val
|
|
114
210
|
}
|
|
@@ -121,16 +217,40 @@ const truncate = (str: string, length: number): string => {
|
|
|
121
217
|
}
|
|
122
218
|
}
|
|
123
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Placeholder for unimplemented code paths. Triggers debugger and throws.
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```ts
|
|
225
|
+
* const parseFormat = (format: Format) => {
|
|
226
|
+
* switch (format) {
|
|
227
|
+
* case 'json': return parseJson
|
|
228
|
+
* case 'xml': return notYetImplemented('XML parsing')
|
|
229
|
+
* }
|
|
230
|
+
* }
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
124
233
|
export const notYetImplemented = (msg?: string): never => {
|
|
125
|
-
//
|
|
234
|
+
// oxlint-disable-next-line eslint(no-debugger) -- intentional breakpoint for unimplemented code paths
|
|
126
235
|
debugger
|
|
127
236
|
throw new Error(`Not yet implemented: ${msg}`)
|
|
128
237
|
}
|
|
129
238
|
|
|
239
|
+
/** A function that does nothing. Useful as a default callback. */
|
|
130
240
|
export const noop = () => {}
|
|
131
241
|
|
|
242
|
+
/** A function that returns a value of type `T`. */
|
|
132
243
|
export type Thunk<T> = () => T
|
|
133
244
|
|
|
245
|
+
/**
|
|
246
|
+
* If the input is a function, calls it and returns the result. Otherwise returns the value directly.
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```ts
|
|
250
|
+
* unwrapThunk(5) // 5
|
|
251
|
+
* unwrapThunk(() => 5) // 5
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
134
254
|
export const unwrapThunk = <T>(_: T | (() => T)): T => {
|
|
135
255
|
if (typeof _ === 'function') {
|
|
136
256
|
return (_ as any)()
|
|
@@ -139,6 +259,10 @@ export const unwrapThunk = <T>(_: T | (() => T)): T => {
|
|
|
139
259
|
}
|
|
140
260
|
}
|
|
141
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Transforms nullable fields (those that include `null`) into optional fields.
|
|
264
|
+
* Useful for converting database schemas to TypeScript types.
|
|
265
|
+
*/
|
|
142
266
|
export type NullableFieldsToOptional<T> = Types.Simplify<
|
|
143
267
|
Partial<T> & {
|
|
144
268
|
[K in keyof T as null extends T[K] ? K : never]?: Exclude<T[K], null>
|
|
@@ -147,18 +271,39 @@ export type NullableFieldsToOptional<T> = Types.Simplify<
|
|
|
147
271
|
}
|
|
148
272
|
>
|
|
149
273
|
|
|
150
|
-
/**
|
|
274
|
+
/**
|
|
275
|
+
* Creates an array of numbers from `start` (inclusive) to `end` (exclusive).
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* ```ts
|
|
279
|
+
* range(0, 5) // [0, 1, 2, 3, 4]
|
|
280
|
+
* range(3, 7) // [3, 4, 5, 6]
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
151
283
|
export const range = (start: number, end: number): number[] => {
|
|
152
284
|
const length = end - start
|
|
153
285
|
return Array.from({ length }, (_, i) => start + i)
|
|
154
286
|
}
|
|
155
287
|
|
|
288
|
+
/**
|
|
289
|
+
* Rate-limits function calls to at most once per `ms` milliseconds.
|
|
290
|
+
* Trailing calls are preserved—if called during the wait period, the function
|
|
291
|
+
* will be called again after the timeout.
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* ```ts
|
|
295
|
+
* const throttledSave = throttle(() => saveData(), 1000)
|
|
296
|
+
* throttledSave() // Executes immediately
|
|
297
|
+
* throttledSave() // Queued, executes after 1 second
|
|
298
|
+
* throttledSave() // Ignored (already queued)
|
|
299
|
+
* ```
|
|
300
|
+
*/
|
|
156
301
|
export const throttle = (fn: () => void, ms: number) => {
|
|
157
302
|
let shouldWait = false
|
|
158
303
|
let shouldCallAgain = false
|
|
159
304
|
|
|
160
305
|
const timeoutFunc = () => {
|
|
161
|
-
if (shouldCallAgain) {
|
|
306
|
+
if (shouldCallAgain === true) {
|
|
162
307
|
fn()
|
|
163
308
|
shouldCallAgain = false
|
|
164
309
|
setTimeout(timeoutFunc, ms)
|
|
@@ -168,7 +313,7 @@ export const throttle = (fn: () => void, ms: number) => {
|
|
|
168
313
|
}
|
|
169
314
|
|
|
170
315
|
return () => {
|
|
171
|
-
if (shouldWait) {
|
|
316
|
+
if (shouldWait === true) {
|
|
172
317
|
shouldCallAgain = true
|
|
173
318
|
return
|
|
174
319
|
}
|
|
@@ -179,13 +324,26 @@ export const throttle = (fn: () => void, ms: number) => {
|
|
|
179
324
|
}
|
|
180
325
|
}
|
|
181
326
|
|
|
327
|
+
/**
|
|
328
|
+
* Generates a W3C Trace Context `traceparent` header from an OpenTelemetry span.
|
|
329
|
+
* @see https://www.w3.org/TR/trace-context/#examples-of-http-traceparent-headers
|
|
330
|
+
*/
|
|
182
331
|
export const getTraceParentHeader = (parentSpan: otel.Span) => {
|
|
183
332
|
const spanContext = parentSpan.spanContext()
|
|
184
|
-
// Format: {version}-{trace_id}-{span_id}-{trace_flags}
|
|
185
|
-
// https://www.w3.org/TR/trace-context/#examples-of-http-traceparent-headers
|
|
186
333
|
return `00-${spanContext.traceId}-${spanContext.spanId}-01`
|
|
187
334
|
}
|
|
188
335
|
|
|
336
|
+
/**
|
|
337
|
+
* Asserts that a tagged union value has a specific tag, narrowing its type.
|
|
338
|
+
* Throws if the tag doesn't match.
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```ts
|
|
342
|
+
* type Result = { _tag: 'ok'; value: number } | { _tag: 'error'; message: string }
|
|
343
|
+
* const result: Result = ...
|
|
344
|
+
* const ok = assertTag(result, 'ok') // Type is { _tag: 'ok'; value: number }
|
|
345
|
+
* ```
|
|
346
|
+
*/
|
|
189
347
|
export const assertTag = <TObj extends { _tag: string }, TTag extends TObj['_tag']>(
|
|
190
348
|
obj: TObj,
|
|
191
349
|
tag: TTag,
|
|
@@ -197,12 +355,26 @@ export const assertTag = <TObj extends { _tag: string }, TTag extends TObj['_tag
|
|
|
197
355
|
return obj as any
|
|
198
356
|
}
|
|
199
357
|
|
|
358
|
+
/**
|
|
359
|
+
* Memoizes a function by JSON-stringifying its arguments as the cache key.
|
|
360
|
+
* Suitable for functions with serializable arguments.
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```ts
|
|
364
|
+
* const expensiveCalc = memoizeByStringifyArgs((a: number, b: number) => {
|
|
365
|
+
* console.log('Computing...')
|
|
366
|
+
* return a + b
|
|
367
|
+
* })
|
|
368
|
+
* expensiveCalc(1, 2) // logs 'Computing...', returns 3
|
|
369
|
+
* expensiveCalc(1, 2) // returns 3 (cached, no log)
|
|
370
|
+
* ```
|
|
371
|
+
*/
|
|
200
372
|
export const memoizeByStringifyArgs = <T extends (...args: any[]) => any>(fn: T): T => {
|
|
201
373
|
const cache = new Map<string, ReturnType<T>>()
|
|
202
374
|
|
|
203
375
|
return ((...args: any[]) => {
|
|
204
376
|
const key = JSON.stringify(args)
|
|
205
|
-
if (cache.has(key)) {
|
|
377
|
+
if (cache.has(key) === true) {
|
|
206
378
|
return cache.get(key)
|
|
207
379
|
}
|
|
208
380
|
|
|
@@ -212,11 +384,23 @@ export const memoizeByStringifyArgs = <T extends (...args: any[]) => any>(fn: T)
|
|
|
212
384
|
}) as any
|
|
213
385
|
}
|
|
214
386
|
|
|
387
|
+
/**
|
|
388
|
+
* Memoizes a single-argument function using reference equality for cache lookup.
|
|
389
|
+
* Suitable for functions where arguments are objects that should be compared by reference.
|
|
390
|
+
*
|
|
391
|
+
* @example
|
|
392
|
+
* ```ts
|
|
393
|
+
* const processUser = memoizeByRef((user: User) => expensiveTransform(user))
|
|
394
|
+
* processUser(userA) // Computes
|
|
395
|
+
* processUser(userA) // Returns cached (same reference)
|
|
396
|
+
* processUser(userB) // Computes (different reference)
|
|
397
|
+
* ```
|
|
398
|
+
*/
|
|
215
399
|
export const memoizeByRef = <T extends (arg: any) => any>(fn: T): T => {
|
|
216
400
|
const cache = new Map<Parameters<T>[0], ReturnType<T>>()
|
|
217
401
|
|
|
218
402
|
return ((arg: any) => {
|
|
219
|
-
if (cache.has(arg)) {
|
|
403
|
+
if (cache.has(arg) === true) {
|
|
220
404
|
return cache.get(arg)
|
|
221
405
|
}
|
|
222
406
|
|
|
@@ -226,15 +410,23 @@ export const memoizeByRef = <T extends (arg: any) => any>(fn: T): T => {
|
|
|
226
410
|
}) as any
|
|
227
411
|
}
|
|
228
412
|
|
|
413
|
+
/** Type guard that checks if a value is a non-empty string. */
|
|
229
414
|
export const isNonEmptyString = (str: string | undefined | null): str is string => {
|
|
230
415
|
return typeof str === 'string' && str.length > 0
|
|
231
416
|
}
|
|
232
417
|
|
|
418
|
+
/** Type guard that checks if a value is a Promise (has a `then` method). */
|
|
233
419
|
export const isPromise = (value: any): value is Promise<unknown> => typeof value?.then === 'function'
|
|
234
420
|
|
|
421
|
+
/** Type guard that checks if a value is iterable (has a `Symbol.iterator` method). */
|
|
235
422
|
export const isIterable = <T>(value: any): value is Iterable<T> => typeof value?.[Symbol.iterator] === 'function'
|
|
236
423
|
|
|
237
|
-
/**
|
|
424
|
+
/**
|
|
425
|
+
* Type-level utility that removes `undefined` from all property types.
|
|
426
|
+
* Used for compatibility with libraries that don't type optionals as `| undefined`.
|
|
427
|
+
*
|
|
428
|
+
* Note: This is a type-level lie—the runtime value is unchanged.
|
|
429
|
+
*/
|
|
238
430
|
export const omitUndefineds = <T extends Record<keyof any, unknown>>(
|
|
239
431
|
rec: T,
|
|
240
432
|
): {
|
|
@@ -17,16 +17,29 @@ import * as Scope from 'effect/Scope'
|
|
|
17
17
|
let parentDeathDetectionEnabled = false
|
|
18
18
|
let parentDeathTimer: NodeJS.Timeout | null = null
|
|
19
19
|
|
|
20
|
+
type SetupParentDeathDetectionMessage = ['setup-parent-death-detection', { parentPid: number }]
|
|
21
|
+
type RunnerMessage<I> = Runner.BackingRunner.Message<I> | SetupParentDeathDetectionMessage
|
|
22
|
+
|
|
23
|
+
const isSetupParentDeathDetectionMessage = (
|
|
24
|
+
message: unknown,
|
|
25
|
+
): message is SetupParentDeathDetectionMessage =>
|
|
26
|
+
Array.isArray(message) &&
|
|
27
|
+
message[0] === 'setup-parent-death-detection' &&
|
|
28
|
+
typeof message[1] === 'object' &&
|
|
29
|
+
message[1] !== null &&
|
|
30
|
+
'parentPid' in message[1] &&
|
|
31
|
+
typeof (message[1] as { parentPid: unknown }).parentPid === 'number'
|
|
32
|
+
|
|
20
33
|
const stopParentDeathMonitoring = () => {
|
|
21
34
|
parentDeathDetectionEnabled = false
|
|
22
|
-
if (parentDeathTimer) {
|
|
35
|
+
if (parentDeathTimer !== null) {
|
|
23
36
|
clearTimeout(parentDeathTimer)
|
|
24
37
|
parentDeathTimer = null
|
|
25
38
|
}
|
|
26
39
|
}
|
|
27
40
|
|
|
28
41
|
const setupParentDeathMonitoring = (parentPid: number) => {
|
|
29
|
-
if (parentDeathDetectionEnabled) return
|
|
42
|
+
if (parentDeathDetectionEnabled === true) return
|
|
30
43
|
parentDeathDetectionEnabled = true
|
|
31
44
|
|
|
32
45
|
let consecutiveFailures = 0
|
|
@@ -34,7 +47,7 @@ const setupParentDeathMonitoring = (parentPid: number) => {
|
|
|
34
47
|
|
|
35
48
|
// Check if parent is still alive every 2 seconds (more conservative)
|
|
36
49
|
const checkParentAlive = () => {
|
|
37
|
-
if (
|
|
50
|
+
if (parentDeathDetectionEnabled === false) return
|
|
38
51
|
try {
|
|
39
52
|
// Send signal 0 to check if process exists (doesn't actually send signal)
|
|
40
53
|
process.kill(parentPid, 0)
|
|
@@ -64,13 +77,13 @@ const platformRunnerImpl = Runner.PlatformRunner.of({
|
|
|
64
77
|
[Runner.PlatformRunnerTypeId]: Runner.PlatformRunnerTypeId,
|
|
65
78
|
start<I, O>(closeLatch: typeof CloseLatch.Service) {
|
|
66
79
|
return Effect.gen(function* () {
|
|
67
|
-
if (
|
|
80
|
+
if (process.send == null) {
|
|
68
81
|
return yield* new WorkerError({ reason: 'spawn', cause: new Error('not in a child process') })
|
|
69
82
|
}
|
|
70
83
|
const port = {
|
|
71
84
|
postMessage: (message: any) => process.send!(message),
|
|
72
85
|
on: (event: string, handler: (message: any) => void) => process.on(event, handler),
|
|
73
|
-
close: () => process.disconnect(),
|
|
86
|
+
close: () => process.disconnect?.(),
|
|
74
87
|
}
|
|
75
88
|
const send = (_portId: number, message: O, _transfers?: ReadonlyArray<unknown>) =>
|
|
76
89
|
Effect.sync(() => port.postMessage([1, message] /*, transfers as any*/))
|
|
@@ -85,16 +98,16 @@ const platformRunnerImpl = Runner.PlatformRunner.of({
|
|
|
85
98
|
const fiberSet = yield* FiberSet.make<any, WorkerError | E>()
|
|
86
99
|
const runFork = Runtime.runFork(runtime)
|
|
87
100
|
const onExit = (exit: Exit.Exit<any, E>) => {
|
|
88
|
-
if (exit._tag === 'Failure' &&
|
|
101
|
+
if (exit._tag === 'Failure' && Cause.isInterruptedOnly(exit.cause) === false) {
|
|
89
102
|
// Deferred.unsafeDone(closeLatch, Exit.die(Cause.squash(exit.cause)))
|
|
90
103
|
Deferred.unsafeDone(closeLatch, Exit.die(exit.cause))
|
|
91
104
|
}
|
|
92
105
|
}
|
|
93
|
-
|
|
106
|
+
port.on('message', (message: RunnerMessage<I>) => {
|
|
94
107
|
// console.log('message', message)
|
|
95
108
|
|
|
96
109
|
// Handle parent death detection setup messages
|
|
97
|
-
if (
|
|
110
|
+
if (isSetupParentDeathDetectionMessage(message) === true) {
|
|
98
111
|
const parentPid = message[1].parentPid
|
|
99
112
|
// console.log(`[Worker ${process.pid}] Setting up parent death detection for parent ${parentPid}`)
|
|
100
113
|
setupParentDeathMonitoring(parentPid)
|
|
@@ -102,10 +115,10 @@ const platformRunnerImpl = Runner.PlatformRunner.of({
|
|
|
102
115
|
}
|
|
103
116
|
|
|
104
117
|
// Handle normal Effect worker messages
|
|
105
|
-
if (Array.isArray(message) && typeof message[0] === 'number') {
|
|
118
|
+
if (Array.isArray(message) === true && typeof message[0] === 'number') {
|
|
106
119
|
if (message[0] === 0) {
|
|
107
120
|
const result = handler(0, message[1])
|
|
108
|
-
if (Effect.isEffect(result)) {
|
|
121
|
+
if (Effect.isEffect(result) === true) {
|
|
109
122
|
const fiber = runFork(result)
|
|
110
123
|
fiber.addObserver(onExit)
|
|
111
124
|
FiberSet.unsafeAdd(fiberSet, fiber)
|
|
@@ -2,7 +2,11 @@ import * as ChildProcess from 'node:child_process'
|
|
|
2
2
|
|
|
3
3
|
import * as EffectWorker from '@effect/platform/Worker'
|
|
4
4
|
import { assert, describe, it } from '@effect/vitest'
|
|
5
|
-
import { Chunk, Effect, Exit, Fiber, Scope, Stream } from 'effect'
|
|
5
|
+
import { Chunk, Deferred, Effect, Exit, Fiber, Schema, Scope, Stream } from 'effect'
|
|
6
|
+
|
|
7
|
+
export class TestError extends Schema.TaggedError<TestError>()('TestError', {
|
|
8
|
+
message: Schema.String,
|
|
9
|
+
}) {}
|
|
6
10
|
|
|
7
11
|
import * as ChildProcessWorker from '../ChildProcessWorker.ts'
|
|
8
12
|
import type { WorkerMessage } from './schema.ts'
|
|
@@ -61,7 +65,7 @@ describe('ChildProcessRunner', { timeout: 10_000 }, () => {
|
|
|
61
65
|
|
|
62
66
|
it('should clean up child processes when Effect is interrupted', () =>
|
|
63
67
|
Effect.gen(function* () {
|
|
64
|
-
|
|
68
|
+
const workerPidDeferred = yield* Deferred.make<number>()
|
|
65
69
|
|
|
66
70
|
const testEffect = Effect.gen(function* () {
|
|
67
71
|
const pool = yield* EffectWorker.makePoolSerialized<WorkerMessage>({
|
|
@@ -69,34 +73,39 @@ describe('ChildProcessRunner', { timeout: 10_000 }, () => {
|
|
|
69
73
|
initialMessage: () => new InitialMessage({ name: 'test', data: new Uint8Array([1, 2, 3]) }),
|
|
70
74
|
})
|
|
71
75
|
const result = yield* pool.executeEffect(new StartStubbornWorker({ blockDuration: 30_000 }))
|
|
72
|
-
|
|
76
|
+
yield* Deferred.succeed(workerPidDeferred, result.pid)
|
|
73
77
|
|
|
74
78
|
// Verify the worker process is running
|
|
75
|
-
assert.strictEqual(isProcessRunning(
|
|
79
|
+
assert.strictEqual(isProcessRunning(result.pid), true, 'Worker process should be running')
|
|
76
80
|
|
|
77
81
|
// Start a long-running operation that we'll interrupt
|
|
78
82
|
yield* Effect.sleep('60 seconds')
|
|
79
83
|
}).pipe(Effect.scoped, Effect.provide(WorkerLive))
|
|
80
84
|
|
|
81
85
|
// Run the test effect but interrupt it after 2 seconds
|
|
82
|
-
const fiber = yield* Effect.
|
|
86
|
+
const fiber = yield* Effect.forkScoped(testEffect)
|
|
87
|
+
|
|
88
|
+
const workerPid = yield* Deferred.await(workerPidDeferred).pipe(
|
|
89
|
+
Effect.raceFirst(
|
|
90
|
+
Fiber.join(fiber).pipe(
|
|
91
|
+
Effect.flatMap(() => new TestError({ message: 'testEffect completed before reporting worker PID' })),
|
|
92
|
+
),
|
|
93
|
+
),
|
|
94
|
+
Effect.timeout(10_000),
|
|
95
|
+
)
|
|
96
|
+
|
|
83
97
|
yield* Effect.sleep('2 seconds')
|
|
84
98
|
yield* Fiber.interrupt(fiber)
|
|
85
99
|
|
|
86
100
|
// Wait a moment for cleanup to complete
|
|
87
101
|
yield* Effect.sleep('1 second')
|
|
88
102
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
)
|
|
96
|
-
} else {
|
|
97
|
-
assert.fail('Worker PID was not captured')
|
|
98
|
-
}
|
|
99
|
-
}).pipe(Effect.runPromise))
|
|
103
|
+
assert.strictEqual(
|
|
104
|
+
isProcessRunning(workerPid),
|
|
105
|
+
false,
|
|
106
|
+
`Worker process ${workerPid} should be terminated after Effect interruption`,
|
|
107
|
+
)
|
|
108
|
+
}).pipe(Effect.scoped, Effect.runPromise))
|
|
100
109
|
|
|
101
110
|
it('should clean up child processes when scope is closed abruptly', () =>
|
|
102
111
|
Effect.gen(function* () {
|
|
@@ -115,7 +124,7 @@ describe('ChildProcessRunner', { timeout: 10_000 }, () => {
|
|
|
115
124
|
workerPid = result.pid
|
|
116
125
|
|
|
117
126
|
// Verify the worker is running
|
|
118
|
-
assert.strictEqual(isProcessRunning(workerPid
|
|
127
|
+
assert.strictEqual(isProcessRunning(workerPid), true, 'Worker process should be running')
|
|
119
128
|
} finally {
|
|
120
129
|
// Abruptly close the scope (simulating test abortion)
|
|
121
130
|
yield* Scope.close(scope, Exit.void)
|
|
@@ -125,7 +134,7 @@ describe('ChildProcessRunner', { timeout: 10_000 }, () => {
|
|
|
125
134
|
yield* Effect.sleep('1 second')
|
|
126
135
|
|
|
127
136
|
// This should pass but will initially fail due to zombie process issue
|
|
128
|
-
if (workerPid) {
|
|
137
|
+
if (workerPid !== undefined) {
|
|
129
138
|
assert.strictEqual(
|
|
130
139
|
isProcessRunning(workerPid),
|
|
131
140
|
false,
|
|
@@ -184,7 +193,7 @@ describe('ChildProcessRunner', { timeout: 10_000 }, () => {
|
|
|
184
193
|
yield* Effect.sleep('2 seconds')
|
|
185
194
|
|
|
186
195
|
// This test should initially fail - child process will still be running
|
|
187
|
-
if (workerPid) {
|
|
196
|
+
if (workerPid !== undefined) {
|
|
188
197
|
assert.strictEqual(
|
|
189
198
|
isProcessRunning(workerPid),
|
|
190
199
|
false,
|
|
@@ -269,7 +278,7 @@ describe('ChildProcessRunner', { timeout: 10_000 }, () => {
|
|
|
269
278
|
yield* worker.executeEffect(new StartStubbornWorker({ blockDuration: 60_000 }))
|
|
270
279
|
|
|
271
280
|
// Verify process is running
|
|
272
|
-
if (childPid) {
|
|
281
|
+
if (childPid !== undefined) {
|
|
273
282
|
assert.strictEqual(isProcessRunning(childPid), true, 'Child process should be running')
|
|
274
283
|
}
|
|
275
284
|
|
|
@@ -288,7 +297,7 @@ describe('ChildProcessRunner', { timeout: 10_000 }, () => {
|
|
|
288
297
|
yield* Effect.sleep('3 seconds')
|
|
289
298
|
|
|
290
299
|
// This test should initially fail - demonstrating the zombie process issue
|
|
291
|
-
if (childPid) {
|
|
300
|
+
if (childPid !== undefined) {
|
|
292
301
|
assert.strictEqual(
|
|
293
302
|
isProcessRunning(childPid),
|
|
294
303
|
false,
|
|
@@ -33,19 +33,18 @@ const WorkerLive = Runner.layerSerialized(WorkerMessage, {
|
|
|
33
33
|
// return req.name
|
|
34
34
|
// }),
|
|
35
35
|
// ),
|
|
36
|
-
GetSpan: (_)
|
|
37
|
-
Effect.
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
GetSpan: Effect.fn('GetSpan')(function* (_) {
|
|
37
|
+
const span = yield* Effect.currentSpan.pipe(Effect.orDie)
|
|
38
|
+
return {
|
|
39
|
+
traceId: span.traceId,
|
|
40
|
+
spanId: span.spanId,
|
|
41
|
+
name: span.name,
|
|
42
|
+
parent: Option.map(span.parent, (span) => ({
|
|
40
43
|
traceId: span.traceId,
|
|
41
44
|
spanId: span.spanId,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
spanId: span.spanId,
|
|
46
|
-
})),
|
|
47
|
-
}
|
|
48
|
-
}).pipe(Effect.withSpan('GetSpan')),
|
|
45
|
+
})),
|
|
46
|
+
}
|
|
47
|
+
}),
|
|
49
48
|
RunnerInterrupt: () => Effect.interrupt,
|
|
50
49
|
StartStubbornWorker: ({ blockDuration }) =>
|
|
51
50
|
Effect.gen(function* () {
|