@livestore/utils 0.4.0-dev.9 → 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 +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/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 +7 -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
|
@@ -38,131 +38,10 @@ export const noopChannel = <MsgListen, MsgSend>(): Effect.Effect<WebChannel<MsgL
|
|
|
38
38
|
}).pipe(Effect.withSpan(`WebChannel:noopChannel`)),
|
|
39
39
|
)
|
|
40
40
|
|
|
41
|
-
/** Only works in browser environments */
|
|
42
|
-
export const broadcastChannel = <MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded>({
|
|
43
|
-
channelName,
|
|
44
|
-
schema: inputSchema,
|
|
45
|
-
}: {
|
|
46
|
-
channelName: string
|
|
47
|
-
schema: InputSchema<MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded>
|
|
48
|
-
}): Effect.Effect<WebChannel<MsgListen, MsgSend>, never, Scope.Scope> =>
|
|
49
|
-
Effect.scopeWithCloseable((scope) =>
|
|
50
|
-
Effect.gen(function* () {
|
|
51
|
-
const schema = mapSchema(inputSchema)
|
|
52
|
-
|
|
53
|
-
const channel = new BroadcastChannel(channelName)
|
|
54
|
-
|
|
55
|
-
yield* Effect.addFinalizer(() => Effect.try(() => channel.close()).pipe(Effect.ignoreLogged))
|
|
56
|
-
|
|
57
|
-
const send = (message: MsgSend) =>
|
|
58
|
-
Effect.gen(function* () {
|
|
59
|
-
const messageEncoded = yield* Schema.encode(schema.send)(message)
|
|
60
|
-
channel.postMessage(messageEncoded)
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
// TODO also listen to `messageerror` in parallel
|
|
64
|
-
const listen = Stream.fromEventListener<MessageEvent>(channel, 'message').pipe(
|
|
65
|
-
Stream.map((_) => Schema.decodeEither(schema.listen)(_.data)),
|
|
66
|
-
listenToDebugPing(channelName),
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
const closedDeferred = yield* Deferred.make<void>().pipe(Effect.acquireRelease(Deferred.done(Exit.void)))
|
|
70
|
-
const supportsTransferables = false
|
|
71
|
-
|
|
72
|
-
return {
|
|
73
|
-
[WebChannelSymbol]: WebChannelSymbol,
|
|
74
|
-
send,
|
|
75
|
-
listen,
|
|
76
|
-
closedDeferred,
|
|
77
|
-
shutdown: Scope.close(scope, Exit.succeed('shutdown')),
|
|
78
|
-
schema,
|
|
79
|
-
supportsTransferables,
|
|
80
|
-
}
|
|
81
|
-
}).pipe(Effect.withSpan(`WebChannel:broadcastChannel(${channelName})`)),
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* NOTE the `listenName` and `sendName` is needed for cases where both sides are using the same window
|
|
86
|
-
* e.g. for a browser extension, so we need a way to know for which side a message is intended for.
|
|
87
|
-
*/
|
|
88
|
-
export const windowChannel = <MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded>({
|
|
89
|
-
listenWindow,
|
|
90
|
-
sendWindow,
|
|
91
|
-
targetOrigin = '*',
|
|
92
|
-
ids,
|
|
93
|
-
schema: inputSchema,
|
|
94
|
-
}: {
|
|
95
|
-
listenWindow: Window
|
|
96
|
-
sendWindow: Window
|
|
97
|
-
targetOrigin?: string
|
|
98
|
-
ids: { own: string; other: string }
|
|
99
|
-
schema: InputSchema<MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded>
|
|
100
|
-
}): Effect.Effect<WebChannel<MsgListen, MsgSend>, never, Scope.Scope> =>
|
|
101
|
-
Effect.scopeWithCloseable((scope) =>
|
|
102
|
-
Effect.gen(function* () {
|
|
103
|
-
const schema = mapSchema(inputSchema)
|
|
104
|
-
|
|
105
|
-
const debugInfo = {
|
|
106
|
-
sendTotal: 0,
|
|
107
|
-
listenTotal: 0,
|
|
108
|
-
targetOrigin,
|
|
109
|
-
ids,
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const WindowMessageListen = Schema.Struct({
|
|
113
|
-
message: schema.listen,
|
|
114
|
-
from: Schema.Literal(ids.other),
|
|
115
|
-
to: Schema.Literal(ids.own),
|
|
116
|
-
}).annotations({ title: 'webmesh.WindowMessageListen' })
|
|
117
|
-
|
|
118
|
-
const WindowMessageSend = Schema.Struct({
|
|
119
|
-
message: schema.send,
|
|
120
|
-
from: Schema.Literal(ids.own),
|
|
121
|
-
to: Schema.Literal(ids.other),
|
|
122
|
-
}).annotations({ title: 'webmesh.WindowMessageSend' })
|
|
123
|
-
|
|
124
|
-
const send = (message: MsgSend) =>
|
|
125
|
-
Effect.gen(function* () {
|
|
126
|
-
debugInfo.sendTotal++
|
|
127
|
-
|
|
128
|
-
const [messageEncoded, transferables] = yield* Schema.encodeWithTransferables(WindowMessageSend)({
|
|
129
|
-
message,
|
|
130
|
-
from: ids.own,
|
|
131
|
-
to: ids.other,
|
|
132
|
-
})
|
|
133
|
-
sendWindow.postMessage(messageEncoded, targetOrigin, transferables)
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
const listen = Stream.fromEventListener<MessageEvent>(listenWindow, 'message').pipe(
|
|
137
|
-
// Stream.tap((_) => Effect.log(`${ids.other}→${ids.own}:message`, _.data)),
|
|
138
|
-
Stream.filter((_) => Schema.is(Schema.encodedSchema(WindowMessageListen))(_.data)),
|
|
139
|
-
Stream.map((_) => {
|
|
140
|
-
debugInfo.listenTotal++
|
|
141
|
-
return Schema.decodeEither(schema.listen)(_.data.message)
|
|
142
|
-
}),
|
|
143
|
-
listenToDebugPing('window'),
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
const closedDeferred = yield* Deferred.make<void>().pipe(Effect.acquireRelease(Deferred.done(Exit.void)))
|
|
147
|
-
const supportsTransferables = true
|
|
148
|
-
|
|
149
|
-
return {
|
|
150
|
-
[WebChannelSymbol]: WebChannelSymbol,
|
|
151
|
-
send,
|
|
152
|
-
listen,
|
|
153
|
-
closedDeferred,
|
|
154
|
-
shutdown: Scope.close(scope, Exit.succeed('shutdown')),
|
|
155
|
-
schema,
|
|
156
|
-
supportsTransferables,
|
|
157
|
-
debugInfo,
|
|
158
|
-
}
|
|
159
|
-
}).pipe(Effect.withSpan(`WebChannel:windowChannel`)),
|
|
160
|
-
)
|
|
161
|
-
|
|
162
41
|
export const messagePortChannel: <MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded>(args: {
|
|
163
42
|
port: MessagePort
|
|
164
43
|
schema: InputSchema<MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded>
|
|
165
|
-
debugId?: string | number
|
|
44
|
+
debugId?: string | number | undefined
|
|
166
45
|
}) => Effect.Effect<WebChannel<MsgListen, MsgSend>, never, Scope.Scope> = ({ port, schema: inputSchema, debugId }) =>
|
|
167
46
|
Effect.scopeWithCloseable((scope) =>
|
|
168
47
|
Effect.gen(function* () {
|
|
@@ -225,10 +104,7 @@ export const sameThreadChannel = <MsgListen, MsgSend, MsgListenEncoded, MsgSendE
|
|
|
225
104
|
|
|
226
105
|
const schema = mapSchema(inputSchema)
|
|
227
106
|
|
|
228
|
-
const send = (message: MsgSend) =>
|
|
229
|
-
Effect.gen(function* () {
|
|
230
|
-
yield* PubSub.publish(pubSub, message)
|
|
231
|
-
})
|
|
107
|
+
const send = (message: MsgSend) => PubSub.publish(pubSub, message)
|
|
232
108
|
|
|
233
109
|
const listen = Stream.fromPubSub(pubSub).pipe(Stream.map(Either.right), listenToDebugPing(channelName))
|
|
234
110
|
|
|
@@ -249,7 +125,7 @@ export const sameThreadChannel = <MsgListen, MsgSend, MsgListenEncoded, MsgSendE
|
|
|
249
125
|
export const messagePortChannelWithAck: <MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded>(args: {
|
|
250
126
|
port: MessagePort
|
|
251
127
|
schema: InputSchema<MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded>
|
|
252
|
-
debugId?: string | number
|
|
128
|
+
debugId?: string | number | undefined
|
|
253
129
|
}) => Effect.Effect<WebChannel<MsgListen, MsgSend>, never, Scope.Scope> = ({ port, schema: inputSchema, debugId }) =>
|
|
254
130
|
Effect.scopeWithCloseable((scope) =>
|
|
255
131
|
Effect.gen(function* () {
|
|
@@ -315,7 +191,7 @@ export const messagePortChannelWithAck: <MsgListen, MsgSend, MsgListenEncoded, M
|
|
|
315
191
|
yield* Deferred.succeed(requestAckMap.get(msg.right.reqId)!, void 0)
|
|
316
192
|
} else if (msg.right._tag === 'ChannelRequest') {
|
|
317
193
|
debugInfo.listenTotal++
|
|
318
|
-
port.postMessage(Schema.
|
|
194
|
+
port.postMessage(yield* Schema.encode(ChannelMessage)({ _tag: 'ChannelRequestAck', reqId: msg.right.id }))
|
|
319
195
|
}
|
|
320
196
|
}
|
|
321
197
|
}),
|
|
@@ -408,8 +284,8 @@ export const queueChannelProxy = <MsgListen, MsgSend>({
|
|
|
408
284
|
/**
|
|
409
285
|
* Eagerly starts listening to a channel by buffering incoming messages in a queue.
|
|
410
286
|
*/
|
|
411
|
-
export const toOpenChannel = (
|
|
412
|
-
channel: WebChannel<
|
|
287
|
+
export const toOpenChannel = <MsgListen, MsgSend>(
|
|
288
|
+
channel: WebChannel<MsgListen, MsgSend>,
|
|
413
289
|
options?: {
|
|
414
290
|
/**
|
|
415
291
|
* Sends a heartbeat message to the other end of the channel every `interval`.
|
|
@@ -420,9 +296,14 @@ export const toOpenChannel = (
|
|
|
420
296
|
timeout: DurationInput
|
|
421
297
|
}
|
|
422
298
|
},
|
|
423
|
-
): Effect.Effect<WebChannel<
|
|
299
|
+
): Effect.Effect<WebChannel<MsgListen, MsgSend>, never, Scope.Scope> =>
|
|
424
300
|
Effect.gen(function* () {
|
|
425
|
-
const queue = yield* Queue.unbounded<Either.Either<
|
|
301
|
+
const queue = yield* Queue.unbounded<Either.Either<MsgListen, any>>().pipe(Effect.acquireRelease(Queue.shutdown))
|
|
302
|
+
|
|
303
|
+
const heartbeatChannel = channel as WebChannel<
|
|
304
|
+
MsgListen | typeof WebChannelHeartbeat.Type,
|
|
305
|
+
MsgSend | typeof WebChannelHeartbeat.Type
|
|
306
|
+
>
|
|
426
307
|
|
|
427
308
|
const pendingPingDeferredRef = {
|
|
428
309
|
current: undefined as { deferred: Deferred.Deferred<void>; requestId: string } | undefined,
|
|
@@ -430,12 +311,12 @@ export const toOpenChannel = (
|
|
|
430
311
|
|
|
431
312
|
yield* channel.listen.pipe(
|
|
432
313
|
// TODO implement this on the "chunk" level for better performance
|
|
433
|
-
options?.heartbeat
|
|
314
|
+
options?.heartbeat !== undefined
|
|
434
315
|
? Stream.filterEffect(
|
|
435
316
|
Effect.fn(function* (msg) {
|
|
436
|
-
if (msg._tag === 'Right' && Schema.is(WebChannelHeartbeat)(msg.right)) {
|
|
317
|
+
if (msg._tag === 'Right' && Schema.is(WebChannelHeartbeat)(msg.right) === true) {
|
|
437
318
|
if (msg.right._tag === 'WebChannel.Ping') {
|
|
438
|
-
yield*
|
|
319
|
+
yield* heartbeatChannel.send(WebChannelPong.make({ requestId: msg.right.requestId }))
|
|
439
320
|
} else {
|
|
440
321
|
const { deferred, requestId } = pendingPingDeferredRef.current ?? shouldNeverHappen('No pending ping')
|
|
441
322
|
if (requestId !== msg.right.requestId) {
|
|
@@ -455,13 +336,13 @@ export const toOpenChannel = (
|
|
|
455
336
|
Effect.forkScoped,
|
|
456
337
|
)
|
|
457
338
|
|
|
458
|
-
if (options?.heartbeat) {
|
|
339
|
+
if (options?.heartbeat !== undefined) {
|
|
459
340
|
const { interval, timeout } = options.heartbeat
|
|
460
341
|
yield* Effect.gen(function* () {
|
|
461
342
|
while (true) {
|
|
462
343
|
yield* Effect.sleep(interval)
|
|
463
344
|
const requestId = crypto.randomUUID()
|
|
464
|
-
yield*
|
|
345
|
+
yield* heartbeatChannel.send(WebChannelPing.make({ requestId }))
|
|
465
346
|
const deferred = yield* Deferred.make<void>()
|
|
466
347
|
pendingPingDeferredRef.current = { deferred, requestId }
|
|
467
348
|
yield* deferred.pipe(
|
|
@@ -492,7 +373,4 @@ export const toOpenChannel = (
|
|
|
492
373
|
}
|
|
493
374
|
})
|
|
494
375
|
|
|
495
|
-
export const sendDebugPing = (channel: WebChannel<any, any>) =>
|
|
496
|
-
Effect.gen(function* () {
|
|
497
|
-
yield* channel.send(DebugPingMessage.make({ message: 'ping' }))
|
|
498
|
-
})
|
|
376
|
+
export const sendDebugPing = (channel: WebChannel<any, any>) => channel.send(DebugPingMessage.make({ message: 'ping' }))
|
|
@@ -69,7 +69,7 @@ export const broadcastChannelWithAck = <MsgListen, MsgSend, MsgListenEncoded, Ms
|
|
|
69
69
|
peerIdRef.current = data.from
|
|
70
70
|
postMessage(ConnectAckMessage.make({ from: connectionId, to: data.from }))
|
|
71
71
|
yield* connectedLatch.open
|
|
72
|
-
|
|
72
|
+
return undefined
|
|
73
73
|
}
|
|
74
74
|
// Case: other side sends connect-ack message (because otherside was already online when this side connected)
|
|
75
75
|
case 'ConnectAckMessage': {
|
|
@@ -77,7 +77,7 @@ export const broadcastChannelWithAck = <MsgListen, MsgSend, MsgListenEncoded, Ms
|
|
|
77
77
|
peerIdRef.current = data.from
|
|
78
78
|
yield* connectedLatch.open
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
return undefined
|
|
81
81
|
}
|
|
82
82
|
case 'DisconnectMessage': {
|
|
83
83
|
if (data.from === peerIdRef.current) {
|
|
@@ -85,13 +85,13 @@ export const broadcastChannelWithAck = <MsgListen, MsgSend, MsgListenEncoded, Ms
|
|
|
85
85
|
yield* connectedLatch.close
|
|
86
86
|
yield* establishConnection
|
|
87
87
|
}
|
|
88
|
-
|
|
88
|
+
return undefined
|
|
89
89
|
}
|
|
90
90
|
case 'PayloadMessage': {
|
|
91
91
|
if (data.to === connectionId) {
|
|
92
92
|
return Schema.decodeEither(schema.listen)(data.payload)
|
|
93
93
|
}
|
|
94
|
-
|
|
94
|
+
return undefined
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
}),
|
|
@@ -15,7 +15,7 @@ export interface WebChannel<MsgListen, MsgSend, E = never> {
|
|
|
15
15
|
closedDeferred: Deferred.Deferred<void>
|
|
16
16
|
shutdown: Effect.Effect<void>
|
|
17
17
|
schema: { listen: Schema.Schema<MsgListen, any>; send: Schema.Schema<MsgSend, any> }
|
|
18
|
-
debugInfo?: Record<string, any>
|
|
18
|
+
debugInfo?: Record<string, any> | undefined
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export const DebugPingMessage = Schema.TaggedStruct('WebChannel.DebugPing', {
|
|
@@ -54,19 +54,19 @@ export type OutputSchema<MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded> =
|
|
|
54
54
|
export const mapSchema = <MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded>(
|
|
55
55
|
schema: InputSchema<MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded>,
|
|
56
56
|
): OutputSchema<MsgListen, MsgSend, MsgListenEncoded, MsgSendEncoded> =>
|
|
57
|
-
Predicate.hasProperty(schema, 'send') && Predicate.hasProperty(schema, 'listen')
|
|
57
|
+
Predicate.hasProperty(schema, 'send') === true && Predicate.hasProperty(schema, 'listen') === true
|
|
58
58
|
? (schemaWithWebChannelMessages(schema) as any)
|
|
59
59
|
: (schemaWithWebChannelMessages({ send: schema, listen: schema }) as any)
|
|
60
60
|
|
|
61
61
|
export const listenToDebugPing =
|
|
62
62
|
(channelName: string) =>
|
|
63
63
|
<MsgListen>(
|
|
64
|
-
stream: Stream.Stream<Either.Either<MsgListen, ParseResult.ParseError
|
|
65
|
-
): Stream.Stream<Either.Either<MsgListen, ParseResult.ParseError
|
|
64
|
+
stream: Stream.Stream<Either.Either<MsgListen, ParseResult.ParseError>>,
|
|
65
|
+
): Stream.Stream<Either.Either<MsgListen, ParseResult.ParseError>> =>
|
|
66
66
|
stream.pipe(
|
|
67
67
|
Stream.filterEffect(
|
|
68
68
|
Effect.fn(function* (msg) {
|
|
69
|
-
if (msg._tag === 'Right' && Schema.is(DebugPingMessage)(msg.right)) {
|
|
69
|
+
if (msg._tag === 'Right' && Schema.is(DebugPingMessage)(msg.right) === true) {
|
|
70
70
|
yield* Effect.logDebug(`WebChannel:ping [${channelName}] ${msg.right.message}`, msg.right.payload)
|
|
71
71
|
return false
|
|
72
72
|
}
|
package/src/effect/WebSocket.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { HttpClient } from '@effect/platform'
|
|
|
2
2
|
import type { Schedule, Scope } from 'effect'
|
|
3
3
|
import { Effect, Exit, identity, Schema } from 'effect'
|
|
4
4
|
|
|
5
|
-
export class WebSocketError extends Schema.TaggedError<WebSocketError>()('WebSocketError', {
|
|
5
|
+
export class WebSocketError extends Schema.TaggedError<WebSocketError>('~@livestore/utils/WebSocketError')('WebSocketError', {
|
|
6
6
|
cause: Schema.Defect,
|
|
7
7
|
}) {}
|
|
8
8
|
|
|
@@ -63,7 +63,7 @@ export const makeWebSocket = ({
|
|
|
63
63
|
}
|
|
64
64
|
}).pipe(
|
|
65
65
|
Effect.tapErrorTag('WebSocketError', () => tryLogWebsocketConnectError(url)),
|
|
66
|
-
reconnect ? Effect.retry(reconnect) : identity,
|
|
66
|
+
reconnect !== undefined ? Effect.retry(reconnect) : identity,
|
|
67
67
|
)
|
|
68
68
|
|
|
69
69
|
/**
|
|
@@ -81,16 +81,17 @@ export const makeWebSocket = ({
|
|
|
81
81
|
*/
|
|
82
82
|
yield* Effect.addFinalizer(
|
|
83
83
|
Effect.fn(function* (exit) {
|
|
84
|
-
try
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
84
|
+
yield* Effect.try({
|
|
85
|
+
try: () => {
|
|
86
|
+
if (Exit.isFailure(exit) === true) {
|
|
87
|
+
socket.close(3000)
|
|
88
|
+
} else {
|
|
89
|
+
socket.close(1000)
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
catch: (error) => new WebSocketError({ cause: error }),
|
|
93
|
+
})
|
|
94
|
+
}, Effect.orDie),
|
|
94
95
|
)
|
|
95
96
|
|
|
96
97
|
return socket
|
|
@@ -48,7 +48,6 @@ export {
|
|
|
48
48
|
WorkerError,
|
|
49
49
|
WorkerRunner,
|
|
50
50
|
} from '@effect/platform'
|
|
51
|
-
export { BrowserWorker, BrowserWorkerRunner } from '@effect/platform-browser'
|
|
52
51
|
export {
|
|
53
52
|
Rpc,
|
|
54
53
|
// RpcClient, // TODO bring back "original" RpcClient from effect/rpc
|
|
@@ -72,6 +71,7 @@ export {
|
|
|
72
71
|
Chunk,
|
|
73
72
|
Config,
|
|
74
73
|
ConfigError,
|
|
74
|
+
ConfigProvider,
|
|
75
75
|
Console,
|
|
76
76
|
Context,
|
|
77
77
|
Data,
|
|
@@ -108,6 +108,7 @@ export {
|
|
|
108
108
|
MutableHashMap,
|
|
109
109
|
MutableHashSet,
|
|
110
110
|
Option,
|
|
111
|
+
Order,
|
|
111
112
|
ParseResult,
|
|
112
113
|
Predicate,
|
|
113
114
|
Pretty,
|
|
@@ -116,15 +117,21 @@ export {
|
|
|
116
117
|
// Subscribable,
|
|
117
118
|
pipe,
|
|
118
119
|
Queue,
|
|
120
|
+
RcMap,
|
|
121
|
+
RcRef,
|
|
119
122
|
Record as ReadonlyRecord,
|
|
123
|
+
Redacted,
|
|
120
124
|
Ref,
|
|
121
125
|
Request,
|
|
122
126
|
Runtime,
|
|
123
127
|
RuntimeFlags,
|
|
124
128
|
Scope,
|
|
129
|
+
ScopedRef,
|
|
130
|
+
Sink,
|
|
125
131
|
SortedMap,
|
|
126
132
|
STM,
|
|
127
133
|
SynchronizedRef,
|
|
134
|
+
TestClock,
|
|
128
135
|
TestServices,
|
|
129
136
|
TQueue,
|
|
130
137
|
TRef,
|
|
@@ -133,10 +140,12 @@ export {
|
|
|
133
140
|
} from 'effect'
|
|
134
141
|
export type { NonEmptyArray } from 'effect/Array'
|
|
135
142
|
export { constVoid, dual } from 'effect/Function'
|
|
143
|
+
export * as Graph from 'effect/Graph'
|
|
136
144
|
export { TreeFormatter } from 'effect/ParseResult'
|
|
137
145
|
export type { Serializable, SerializableWithResult } from 'effect/Schema'
|
|
138
146
|
export * as SchemaAST from 'effect/SchemaAST'
|
|
139
147
|
export * as BucketQueue from './BucketQueue.ts'
|
|
148
|
+
export * as Debug from './Debug.ts'
|
|
140
149
|
export * as Effect from './Effect.ts'
|
|
141
150
|
export * from './Error.ts'
|
|
142
151
|
export * as Logger from './Logger.ts'
|
|
@@ -151,5 +160,4 @@ export * as Subscribable from './Subscribable.ts'
|
|
|
151
160
|
export * as SubscriptionRef from './SubscriptionRef.ts'
|
|
152
161
|
export * as TaskTracing from './TaskTracing.ts'
|
|
153
162
|
export * as WebChannel from './WebChannel/mod.ts'
|
|
154
|
-
export * as WebLock from './WebLock.ts'
|
|
155
163
|
export * as WebSocket from './WebSocket.ts'
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { it, describe, expect } from '@effect/vitest'
|
|
2
|
+
import { Effect, Tracer } from 'effect'
|
|
3
|
+
|
|
4
|
+
import { spanEvent } from './spanEvent.ts'
|
|
5
|
+
|
|
6
|
+
type RecordedEvent = { name: string; attributes: Record<string, unknown> | undefined }
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates a test tracer that captures span events for assertion,
|
|
10
|
+
* using only the public `Tracer` API (no internal NativeSpan access).
|
|
11
|
+
* Each span records its own events, retrievable by span name.
|
|
12
|
+
*/
|
|
13
|
+
const makeTestTracer = () => {
|
|
14
|
+
const spanEvents = new Map<string, Array<RecordedEvent>>()
|
|
15
|
+
|
|
16
|
+
const tracer = Tracer.make({
|
|
17
|
+
span(name, parent, context, links, startTime, kind) {
|
|
18
|
+
const events: Array<RecordedEvent> = []
|
|
19
|
+
spanEvents.set(name, events)
|
|
20
|
+
const attributes = new Map<string, unknown>()
|
|
21
|
+
return {
|
|
22
|
+
_tag: 'Span' as const,
|
|
23
|
+
name,
|
|
24
|
+
spanId: `test-${name}`,
|
|
25
|
+
traceId: 'test-trace',
|
|
26
|
+
parent,
|
|
27
|
+
context,
|
|
28
|
+
status: { _tag: 'Started' as const, startTime },
|
|
29
|
+
attributes,
|
|
30
|
+
links: [...links],
|
|
31
|
+
sampled: true,
|
|
32
|
+
kind,
|
|
33
|
+
end() {},
|
|
34
|
+
attribute(key: string, value: unknown) {
|
|
35
|
+
attributes.set(key, value)
|
|
36
|
+
},
|
|
37
|
+
event(eventName: string, _startTime: bigint, attrs?: Record<string, unknown>) {
|
|
38
|
+
events.push({ name: eventName, attributes: attrs })
|
|
39
|
+
},
|
|
40
|
+
addLinks() {},
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
context(f) {
|
|
44
|
+
return f()
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
tracer,
|
|
50
|
+
getEvents: (spanName: string): Array<RecordedEvent> => spanEvents.get(spanName) ?? [],
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
describe('spanEvent', () => {
|
|
55
|
+
it.effect('should emit a span event with the given message', () => {
|
|
56
|
+
const { tracer, getEvents } = makeTestTracer()
|
|
57
|
+
return Effect.gen(function* () {
|
|
58
|
+
yield* spanEvent('test-event')
|
|
59
|
+
const events = getEvents('test-span')
|
|
60
|
+
expect(events).toHaveLength(1)
|
|
61
|
+
expect(events[0]!.name).toBe('test-event')
|
|
62
|
+
}).pipe(Effect.withSpan('test-span'), Effect.withTracer(tracer))
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it.effect('should emit a span event with attributes', () => {
|
|
66
|
+
const { tracer, getEvents } = makeTestTracer()
|
|
67
|
+
return Effect.gen(function* () {
|
|
68
|
+
yield* spanEvent('event-with-attrs', { key1: 'value1', key2: 42 })
|
|
69
|
+
const events = getEvents('test-span')
|
|
70
|
+
expect(events).toHaveLength(1)
|
|
71
|
+
expect(events[0]!.name).toBe('event-with-attrs')
|
|
72
|
+
expect(events[0]!.attributes).toMatchObject({ key1: 'value1', key2: 42 })
|
|
73
|
+
}).pipe(Effect.withSpan('test-span'), Effect.withTracer(tracer))
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it.effect('should emit to the nearest enclosing span', () => {
|
|
77
|
+
const { tracer, getEvents } = makeTestTracer()
|
|
78
|
+
return Effect.gen(function* () {
|
|
79
|
+
yield* spanEvent('outer-event')
|
|
80
|
+
|
|
81
|
+
yield* spanEvent('inner-event').pipe(Effect.withSpan('inner-span'))
|
|
82
|
+
|
|
83
|
+
const outerEvents = getEvents('outer-span')
|
|
84
|
+
const innerEvents = getEvents('inner-span')
|
|
85
|
+
|
|
86
|
+
expect(outerEvents).toHaveLength(1)
|
|
87
|
+
expect(outerEvents[0]!.name).toBe('outer-event')
|
|
88
|
+
|
|
89
|
+
expect(innerEvents).toHaveLength(1)
|
|
90
|
+
expect(innerEvents[0]!.name).toBe('inner-event')
|
|
91
|
+
}).pipe(Effect.withSpan('outer-span'), Effect.withTracer(tracer))
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it.effect('should be a no-op when no span is in context', () => spanEvent('orphan-event'))
|
|
95
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Effect, FiberRef, HashSet, Logger } from 'effect'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Emits a span event on the current Effect span via the tracer logger.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
*
|
|
8
|
+
* Unlike raw `otelSpan.addEvent`, this doesn't require manual span threading —
|
|
9
|
+
* it automatically targets the nearest enclosing `Effect.withSpan`. If no span
|
|
10
|
+
* is in context, the call is a no-op.
|
|
11
|
+
*/
|
|
12
|
+
export const spanEvent = (message: any, attributes?: Record<string, unknown>) =>
|
|
13
|
+
Effect.locallyWith(Effect.log(message).pipe(Effect.annotateLogs(attributes ?? {})), FiberRef.currentLoggers, () =>
|
|
14
|
+
HashSet.make(Logger.tracerLogger),
|
|
15
|
+
)
|
package/src/env.ts
CHANGED
|
@@ -26,4 +26,5 @@ export const IS_CI = envTruish(env('CI'))
|
|
|
26
26
|
|
|
27
27
|
export const IS_BUN = typeof Bun !== 'undefined'
|
|
28
28
|
|
|
29
|
-
export const IS_REACT_NATIVE =
|
|
29
|
+
export const IS_REACT_NATIVE =
|
|
30
|
+
typeof navigator !== 'undefined' && (navigator as unknown as Record<string, unknown>)['product'] === 'ReactNative'
|
package/src/fast-deep-equal.ts
CHANGED
|
@@ -4,36 +4,36 @@
|
|
|
4
4
|
export const deepEqual = <T>(a: T, b: T): boolean => {
|
|
5
5
|
if (a === b) return true
|
|
6
6
|
|
|
7
|
-
if (a && b && typeof a === 'object' && typeof b === 'object') {
|
|
7
|
+
if (a != null && b != null && typeof a === 'object' && typeof b === 'object') {
|
|
8
8
|
if (a.constructor !== b.constructor) return false
|
|
9
9
|
|
|
10
10
|
let length: number
|
|
11
11
|
let i: any
|
|
12
12
|
let keys: any
|
|
13
|
-
if (Array.isArray(a)) {
|
|
13
|
+
if (Array.isArray(a) === true) {
|
|
14
14
|
length = a.length
|
|
15
15
|
// @ts-expect-error ...
|
|
16
16
|
if (length !== b.length) return false
|
|
17
17
|
for (i = length; i-- !== 0; )
|
|
18
18
|
// @ts-expect-error ...
|
|
19
|
-
if (
|
|
19
|
+
if (deepEqual(a[i], b[i]) === false) return false
|
|
20
20
|
return true
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
if (a instanceof Map && b instanceof Map) {
|
|
24
24
|
if (a.size !== b.size) return false
|
|
25
|
-
for (i of a.entries()) if (
|
|
26
|
-
for (i of a.entries()) if (
|
|
25
|
+
for (i of a.entries()) if (b.has(i[0]) === false) return false
|
|
26
|
+
for (i of a.entries()) if (deepEqual(i[1], b.get(i[0])) === false) return false
|
|
27
27
|
return true
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
if (a instanceof Set && b instanceof Set) {
|
|
31
31
|
if (a.size !== b.size) return false
|
|
32
|
-
for (i of a.entries()) if (
|
|
32
|
+
for (i of a.entries()) if (b.has(i[0]) === false) return false
|
|
33
33
|
return true
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) {
|
|
36
|
+
if (ArrayBuffer.isView(a) === true && ArrayBuffer.isView(b) === true) {
|
|
37
37
|
// @ts-expect-error ...
|
|
38
38
|
length = a.length
|
|
39
39
|
// @ts-expect-error ...
|
|
@@ -53,13 +53,13 @@ export const deepEqual = <T>(a: T, b: T): boolean => {
|
|
|
53
53
|
length = keys.length
|
|
54
54
|
if (length !== Object.keys(b).length) return false
|
|
55
55
|
|
|
56
|
-
for (i = length; i-- !== 0; ) if (
|
|
56
|
+
for (i = length; i-- !== 0; ) if (Object.prototype.hasOwnProperty.call(b, keys[i]) === false) return false
|
|
57
57
|
|
|
58
58
|
for (i = length; i-- !== 0; ) {
|
|
59
59
|
const key = keys[i]
|
|
60
60
|
|
|
61
61
|
// @ts-expect-error ...
|
|
62
|
-
if (
|
|
62
|
+
if (deepEqual(a[key], b[key]) === false) return false
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
return true
|
package/src/global.ts
CHANGED
package/src/guards.ts
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
|
+
/** Type guard that narrows `T | undefined` to `T`. Useful for filtering arrays. */
|
|
1
2
|
export const isNotUndefined = <T>(_: T | undefined): _ is T => _ !== undefined
|
|
2
3
|
|
|
4
|
+
/** Type guard that narrows `T | null` to `T`. */
|
|
3
5
|
export const isNotNull = <T>(_: T | null): _ is T => _ !== null
|
|
6
|
+
|
|
7
|
+
/** Type guard that checks if a value is `undefined`. */
|
|
4
8
|
export const isUndefined = <T>(_: T | undefined): _ is undefined => _ === undefined
|
|
5
9
|
|
|
10
|
+
/** Type guard that checks if a value is `null` or `undefined`. */
|
|
6
11
|
export const isNil = (val: any): val is null | undefined => val === null || val === undefined
|
|
7
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Type guard that narrows `T | undefined | null` to `T`.
|
|
15
|
+
* Commonly used to filter out nullish values from arrays.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const values = [1, null, 2, undefined, 3]
|
|
20
|
+
* const nonNil = values.filter(isNotNil) // [1, 2, 3] (type: number[])
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
8
23
|
export const isNotNil = <T>(val: T | undefined | null): val is T => val !== null && val !== undefined
|
package/src/misc.ts
CHANGED
|
@@ -10,7 +10,7 @@ export const isDevEnv = () => {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
// @ts-expect-error Only exists in Expo / RN
|
|
13
|
-
if (globalThis?.__DEV__) {
|
|
13
|
+
if (globalThis?.__DEV__ === true) {
|
|
14
14
|
return true
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -46,10 +46,18 @@ export const tryAsFunctionAndNew = <TArg, TResult>(
|
|
|
46
46
|
export const envTruish = (env: string | undefined) =>
|
|
47
47
|
env !== undefined && env.toLowerCase() !== 'false' && env.toLowerCase() !== '0'
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Logs and throws for impossible states, pausing at a breakpoint in development.
|
|
51
|
+
*
|
|
52
|
+
* @param msg - The error message to log and pass to the error.
|
|
53
|
+
* @param args - Arbitrary arguments to include in the log.
|
|
54
|
+
*
|
|
55
|
+
* @see {@link dieDebugger} for the Effect equivalent.
|
|
56
|
+
*/
|
|
57
|
+
export const shouldNeverHappen = (msg?: string, ...args: ReadonlyArray<unknown>): never => {
|
|
50
58
|
console.error(msg, ...args)
|
|
51
|
-
if (isDevEnv()) {
|
|
52
|
-
//
|
|
59
|
+
if (isDevEnv() === true) {
|
|
60
|
+
// oxlint-disable-next-line eslint(no-debugger) -- intentional breakpoint during development
|
|
53
61
|
debugger
|
|
54
62
|
}
|
|
55
63
|
|