@jr200-labs/xstate-nats 0.6.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/LICENSE +21 -0
- package/README.md +251 -0
- package/dist/actions/connection.d.ts +28 -0
- package/dist/actions/connection.d.ts.map +1 -0
- package/dist/actions/connection.js +102 -0
- package/dist/actions/connection.js.map +1 -0
- package/dist/actions/kv.d.ts +21 -0
- package/dist/actions/kv.d.ts.map +1 -0
- package/dist/actions/kv.js +66 -0
- package/dist/actions/kv.js.map +1 -0
- package/dist/actions/subject.d.ts +39 -0
- package/dist/actions/subject.d.ts.map +1 -0
- package/dist/actions/subject.js +79 -0
- package/dist/actions/subject.js.map +1 -0
- package/dist/actions/types.d.ts +8 -0
- package/dist/actions/types.d.ts.map +1 -0
- package/dist/actions/types.js +2 -0
- package/dist/actions/types.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/machines/kv.d.ts +190 -0
- package/dist/machines/kv.d.ts.map +1 -0
- package/dist/machines/kv.js +273 -0
- package/dist/machines/kv.js.map +1 -0
- package/dist/machines/root.d.ts +510 -0
- package/dist/machines/root.d.ts.map +1 -0
- package/dist/machines/root.js +245 -0
- package/dist/machines/root.js.map +1 -0
- package/dist/machines/subject.d.ts +95 -0
- package/dist/machines/subject.d.ts.map +1 -0
- package/dist/machines/subject.js +162 -0
- package/dist/machines/subject.js.map +1 -0
- package/dist/utils.d.ts +10 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +27 -0
- package/dist/utils.js.map +1 -0
- package/package.json +55 -0
- package/src/actions/connection.test.ts +324 -0
- package/src/actions/connection.ts +135 -0
- package/src/actions/kv.test.ts +439 -0
- package/src/actions/kv.ts +92 -0
- package/src/actions/subject.test.ts +460 -0
- package/src/actions/subject.ts +127 -0
- package/src/actions/types.ts +7 -0
- package/src/index.ts +20 -0
- package/src/machines/kv.test.ts +720 -0
- package/src/machines/kv.ts +327 -0
- package/src/machines/root.test.ts +329 -0
- package/src/machines/root.ts +286 -0
- package/src/machines/subject.test.ts +272 -0
- package/src/machines/subject.ts +205 -0
- package/src/utils.test.ts +35 -0
- package/src/utils.ts +30 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
2
|
+
import { createActor } from 'xstate'
|
|
3
|
+
import { parseNatsResult, connectToNats, disconnectNats } from './connection'
|
|
4
|
+
|
|
5
|
+
vi.mock('@nats-io/nats-core', async () => {
|
|
6
|
+
const actual = await vi.importActual('@nats-io/nats-core')
|
|
7
|
+
return {
|
|
8
|
+
...actual,
|
|
9
|
+
wsconnect: vi.fn(),
|
|
10
|
+
credsAuthenticator: vi.fn((_creds: Uint8Array) => 'mock-authenticator'),
|
|
11
|
+
}
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
describe('parseNatsResult', () => {
|
|
15
|
+
it('should return null for null input', () => {
|
|
16
|
+
expect(parseNatsResult(null)).toBeNull()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('should return the Error for Error input', () => {
|
|
20
|
+
const error = new Error('test error')
|
|
21
|
+
expect(parseNatsResult(error)).toBe(error)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('should parse JSON message', () => {
|
|
25
|
+
const data = { foo: 'bar', num: 42 }
|
|
26
|
+
const msg = {
|
|
27
|
+
json: () => data,
|
|
28
|
+
string: () => JSON.stringify(data),
|
|
29
|
+
} as any
|
|
30
|
+
expect(parseNatsResult(msg)).toEqual(data)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should fall back to string when JSON parsing fails', () => {
|
|
34
|
+
const msg = {
|
|
35
|
+
json: () => {
|
|
36
|
+
throw new Error('not json')
|
|
37
|
+
},
|
|
38
|
+
string: () => 'plain text',
|
|
39
|
+
} as any
|
|
40
|
+
expect(parseNatsResult(msg)).toBe('plain text')
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
describe('connectToNats', () => {
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
vi.restoreAllMocks()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should call wsconnect with opts and no auth', async () => {
|
|
50
|
+
const { wsconnect } = await import('@nats-io/nats-core')
|
|
51
|
+
const mockConnection = {
|
|
52
|
+
status: () => ({
|
|
53
|
+
[Symbol.asyncIterator]: () => ({
|
|
54
|
+
next: () => new Promise(() => {}),
|
|
55
|
+
}),
|
|
56
|
+
}),
|
|
57
|
+
}
|
|
58
|
+
vi.mocked(wsconnect).mockResolvedValue(mockConnection as any)
|
|
59
|
+
vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
60
|
+
|
|
61
|
+
const actor = createActor(connectToNats, {
|
|
62
|
+
input: { opts: { servers: ['ws://localhost:4222'] } },
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const outputPromise = new Promise<any>((resolve) => {
|
|
66
|
+
actor.subscribe((snap) => {
|
|
67
|
+
if (snap.output !== undefined) resolve(snap.output)
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
actor.start()
|
|
71
|
+
|
|
72
|
+
const result = await outputPromise
|
|
73
|
+
expect(wsconnect).toHaveBeenCalledWith({ servers: ['ws://localhost:4222'] })
|
|
74
|
+
expect(result).toBe(mockConnection)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should merge userpass auth config', async () => {
|
|
78
|
+
const { wsconnect } = await import('@nats-io/nats-core')
|
|
79
|
+
const mockConnection = {
|
|
80
|
+
status: () => ({
|
|
81
|
+
[Symbol.asyncIterator]: () => ({
|
|
82
|
+
next: () => new Promise(() => {}),
|
|
83
|
+
}),
|
|
84
|
+
}),
|
|
85
|
+
}
|
|
86
|
+
vi.mocked(wsconnect).mockResolvedValue(mockConnection as any)
|
|
87
|
+
vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
88
|
+
|
|
89
|
+
const actor = createActor(connectToNats, {
|
|
90
|
+
input: {
|
|
91
|
+
opts: { servers: ['ws://localhost:4222'] },
|
|
92
|
+
auth: { type: 'userpass' as const, user: 'admin', pass: 'secret' },
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
const outputPromise = new Promise<any>((resolve) => {
|
|
97
|
+
actor.subscribe((snap) => {
|
|
98
|
+
if (snap.output !== undefined) resolve(snap.output)
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
actor.start()
|
|
102
|
+
await outputPromise
|
|
103
|
+
|
|
104
|
+
expect(wsconnect).toHaveBeenCalledWith({
|
|
105
|
+
servers: ['ws://localhost:4222'],
|
|
106
|
+
user: 'admin',
|
|
107
|
+
pass: 'secret',
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('should merge token auth config', async () => {
|
|
112
|
+
const { wsconnect } = await import('@nats-io/nats-core')
|
|
113
|
+
const mockConnection = {
|
|
114
|
+
status: () => ({
|
|
115
|
+
[Symbol.asyncIterator]: () => ({
|
|
116
|
+
next: () => new Promise(() => {}),
|
|
117
|
+
}),
|
|
118
|
+
}),
|
|
119
|
+
}
|
|
120
|
+
vi.mocked(wsconnect).mockResolvedValue(mockConnection as any)
|
|
121
|
+
vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
122
|
+
|
|
123
|
+
const actor = createActor(connectToNats, {
|
|
124
|
+
input: {
|
|
125
|
+
opts: { servers: ['ws://localhost:4222'] },
|
|
126
|
+
auth: { type: 'token' as const, token: 'my-token' },
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
const outputPromise = new Promise<any>((resolve) => {
|
|
131
|
+
actor.subscribe((snap) => {
|
|
132
|
+
if (snap.output !== undefined) resolve(snap.output)
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
actor.start()
|
|
136
|
+
await outputPromise
|
|
137
|
+
|
|
138
|
+
expect(wsconnect).toHaveBeenCalledWith({
|
|
139
|
+
servers: ['ws://localhost:4222'],
|
|
140
|
+
token: 'my-token',
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('should merge decentralised auth config', async () => {
|
|
145
|
+
const { wsconnect, credsAuthenticator } = await import('@nats-io/nats-core')
|
|
146
|
+
const mockConnection = {
|
|
147
|
+
status: () => ({
|
|
148
|
+
[Symbol.asyncIterator]: () => ({
|
|
149
|
+
next: () => new Promise(() => {}),
|
|
150
|
+
}),
|
|
151
|
+
}),
|
|
152
|
+
}
|
|
153
|
+
vi.mocked(wsconnect).mockResolvedValue(mockConnection as any)
|
|
154
|
+
vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
155
|
+
|
|
156
|
+
const actor = createActor(connectToNats, {
|
|
157
|
+
input: {
|
|
158
|
+
opts: { servers: ['ws://localhost:4222'] },
|
|
159
|
+
auth: {
|
|
160
|
+
type: 'decentralised' as const,
|
|
161
|
+
sentinelB64: btoa('test-creds'),
|
|
162
|
+
user: 'nkey-user',
|
|
163
|
+
pass: 'nkey-pass',
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
const outputPromise = new Promise<any>((resolve) => {
|
|
169
|
+
actor.subscribe((snap) => {
|
|
170
|
+
if (snap.output !== undefined) resolve(snap.output)
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
|
+
actor.start()
|
|
174
|
+
await outputPromise
|
|
175
|
+
|
|
176
|
+
expect(credsAuthenticator).toHaveBeenCalled()
|
|
177
|
+
expect(wsconnect).toHaveBeenCalledWith(
|
|
178
|
+
expect.objectContaining({
|
|
179
|
+
servers: ['ws://localhost:4222'],
|
|
180
|
+
authenticator: 'mock-authenticator',
|
|
181
|
+
user: 'nkey-user',
|
|
182
|
+
pass: 'nkey-pass',
|
|
183
|
+
}),
|
|
184
|
+
)
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('should process status events from the connection', async () => {
|
|
188
|
+
const { wsconnect } = await import('@nats-io/nats-core')
|
|
189
|
+
let statusIndex = 0
|
|
190
|
+
const statuses = [
|
|
191
|
+
{ type: 'disconnect', data: {} },
|
|
192
|
+
{ type: 'reconnect', data: {} },
|
|
193
|
+
{ type: 'error', data: {} },
|
|
194
|
+
{ type: 'close', data: {} },
|
|
195
|
+
{ type: 'ldm', data: {} },
|
|
196
|
+
{ type: 'ping', data: {} },
|
|
197
|
+
{ type: 'forceReconnect', data: {} },
|
|
198
|
+
{ type: 'reconnecting', data: {} },
|
|
199
|
+
{ type: 'slowConsumer', data: {} },
|
|
200
|
+
{ type: 'staleConnection', data: {} },
|
|
201
|
+
{ type: 'update', data: {} },
|
|
202
|
+
]
|
|
203
|
+
let resolveStatusDone: () => void
|
|
204
|
+
const statusDone = new Promise<void>((r) => (resolveStatusDone = r))
|
|
205
|
+
|
|
206
|
+
const mockConnection = {
|
|
207
|
+
status: () => ({
|
|
208
|
+
[Symbol.asyncIterator]: () => ({
|
|
209
|
+
next: () => {
|
|
210
|
+
if (statusIndex < statuses.length) {
|
|
211
|
+
return Promise.resolve({ value: statuses[statusIndex++], done: false })
|
|
212
|
+
}
|
|
213
|
+
// Signal done, then return done: true to exit the loop
|
|
214
|
+
resolveStatusDone!()
|
|
215
|
+
return Promise.resolve({ value: undefined, done: true })
|
|
216
|
+
},
|
|
217
|
+
}),
|
|
218
|
+
}),
|
|
219
|
+
}
|
|
220
|
+
vi.mocked(wsconnect).mockResolvedValue(mockConnection as any)
|
|
221
|
+
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
222
|
+
const debugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {})
|
|
223
|
+
|
|
224
|
+
const actor = createActor(connectToNats, {
|
|
225
|
+
input: { opts: { servers: ['ws://localhost:4222'] } },
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
const outputPromise = new Promise<any>((resolve) => {
|
|
229
|
+
actor.subscribe((snap) => {
|
|
230
|
+
if (snap.output !== undefined) resolve(snap.output)
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
actor.start()
|
|
234
|
+
await outputPromise
|
|
235
|
+
await statusDone
|
|
236
|
+
// Wait for the status loop to finish processing
|
|
237
|
+
await new Promise((r) => setTimeout(r, 50))
|
|
238
|
+
|
|
239
|
+
// Verify the status loop processed all events
|
|
240
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
241
|
+
'Status loop received status',
|
|
242
|
+
expect.objectContaining({ type: 'disconnect' }),
|
|
243
|
+
)
|
|
244
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
245
|
+
'Status loop received status',
|
|
246
|
+
expect.objectContaining({ type: 'reconnect' }),
|
|
247
|
+
)
|
|
248
|
+
expect(consoleSpy).toHaveBeenCalledWith('Exiting nats status loop')
|
|
249
|
+
consoleSpy.mockRestore()
|
|
250
|
+
debugSpy.mockRestore()
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
it('should throw for unsupported auth type', async () => {
|
|
254
|
+
const { wsconnect } = await import('@nats-io/nats-core')
|
|
255
|
+
vi.mocked(wsconnect).mockResolvedValue({} as any)
|
|
256
|
+
vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
257
|
+
|
|
258
|
+
// The error is thrown synchronously inside the fromPromise creator,
|
|
259
|
+
// so it becomes an unhandled exception. We test via the actor error status.
|
|
260
|
+
const actor = createActor(connectToNats, {
|
|
261
|
+
input: {
|
|
262
|
+
opts: { servers: ['ws://localhost:4222'] },
|
|
263
|
+
auth: { type: 'unknown' as any },
|
|
264
|
+
},
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
// Catch the unhandled error from the actor
|
|
268
|
+
let caughtError: Error | undefined
|
|
269
|
+
const origListeners = process.listeners('uncaughtException')
|
|
270
|
+
process.removeAllListeners('uncaughtException')
|
|
271
|
+
process.once('uncaughtException', (err: Error) => {
|
|
272
|
+
caughtError = err
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
actor.start()
|
|
276
|
+
|
|
277
|
+
await vi.waitFor(() => {
|
|
278
|
+
expect(caughtError).toBeDefined()
|
|
279
|
+
})
|
|
280
|
+
expect(caughtError!.message).toContain('Unsupported auth config type')
|
|
281
|
+
|
|
282
|
+
// Restore listeners
|
|
283
|
+
origListeners.forEach((l) => process.on('uncaughtException', l))
|
|
284
|
+
})
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
describe('disconnectNats', () => {
|
|
288
|
+
it('should drain and close connection', async () => {
|
|
289
|
+
const mockConnection = {
|
|
290
|
+
drain: vi.fn().mockResolvedValue(undefined),
|
|
291
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const actor = createActor(disconnectNats, {
|
|
295
|
+
input: { connection: mockConnection as any },
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
const donePromise = new Promise<void>((resolve) => {
|
|
299
|
+
actor.subscribe((snap) => {
|
|
300
|
+
if (snap.status === 'done') resolve()
|
|
301
|
+
})
|
|
302
|
+
})
|
|
303
|
+
actor.start()
|
|
304
|
+
await donePromise
|
|
305
|
+
|
|
306
|
+
expect(mockConnection.drain).toHaveBeenCalled()
|
|
307
|
+
expect(mockConnection.close).toHaveBeenCalled()
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
it('should handle null connection gracefully', async () => {
|
|
311
|
+
const actor = createActor(disconnectNats, {
|
|
312
|
+
input: { connection: null as any },
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
const donePromise = new Promise<void>((resolve) => {
|
|
316
|
+
actor.subscribe((snap) => {
|
|
317
|
+
if (snap.status === 'done') resolve()
|
|
318
|
+
})
|
|
319
|
+
})
|
|
320
|
+
actor.start()
|
|
321
|
+
|
|
322
|
+
await expect(donePromise).resolves.not.toThrow()
|
|
323
|
+
})
|
|
324
|
+
})
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { fromPromise } from 'xstate'
|
|
2
|
+
import {
|
|
3
|
+
ConnectionOptions,
|
|
4
|
+
credsAuthenticator,
|
|
5
|
+
Msg,
|
|
6
|
+
NatsConnection,
|
|
7
|
+
Status,
|
|
8
|
+
wsconnect,
|
|
9
|
+
} from '@nats-io/nats-core'
|
|
10
|
+
import { KvEntry } from '@nats-io/kv'
|
|
11
|
+
import { type AuthConfig } from './types'
|
|
12
|
+
import { sendParent } from 'xstate'
|
|
13
|
+
|
|
14
|
+
const makeAuthConfig = (auth?: AuthConfig) => {
|
|
15
|
+
if (!auth) {
|
|
16
|
+
return {}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (auth.type === 'decentralised') {
|
|
20
|
+
const decodedSentinel = atob(auth!.sentinelB64!)
|
|
21
|
+
return {
|
|
22
|
+
authenticator: credsAuthenticator(new TextEncoder().encode(decodedSentinel)),
|
|
23
|
+
user: auth.user,
|
|
24
|
+
pass: auth.pass,
|
|
25
|
+
}
|
|
26
|
+
} else if (auth.type === 'userpass') {
|
|
27
|
+
return {
|
|
28
|
+
user: auth.user,
|
|
29
|
+
pass: auth.pass,
|
|
30
|
+
}
|
|
31
|
+
} else if (auth.type === 'token') {
|
|
32
|
+
return {
|
|
33
|
+
token: auth.token,
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
throw new Error(`Unsupported auth config type ${auth.type}`)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type InternalStatusEvents =
|
|
41
|
+
| { type: 'NATS_CONNECTION.DISCONNECTED'; status: Status }
|
|
42
|
+
| { type: 'NATS_CONNECTION.RECONNECT'; status: Status }
|
|
43
|
+
| { type: 'NATS_CONNECTION.ERROR'; status: Status }
|
|
44
|
+
| { type: 'NATS_CONNECTION.CLOSE'; status: Status }
|
|
45
|
+
| { type: 'NATS_CONNECTION.RECONNECTING'; status: Status }
|
|
46
|
+
|
|
47
|
+
export const connectToNats = fromPromise(
|
|
48
|
+
async ({
|
|
49
|
+
input,
|
|
50
|
+
}: {
|
|
51
|
+
input: { opts: ConnectionOptions; auth?: AuthConfig }
|
|
52
|
+
}): Promise<NatsConnection> => {
|
|
53
|
+
const mergedOpts: ConnectionOptions = {
|
|
54
|
+
...input.opts,
|
|
55
|
+
...makeAuthConfig(input.auth),
|
|
56
|
+
}
|
|
57
|
+
const nc = await wsconnect(mergedOpts)
|
|
58
|
+
|
|
59
|
+
// bug: self refers to 'this' promise, which is short-lived....
|
|
60
|
+
// TODO: Emit status events into the machine instead
|
|
61
|
+
;(async () => {
|
|
62
|
+
for await (const status of nc.status()) {
|
|
63
|
+
console.log('Status loop received status', status)
|
|
64
|
+
const { type } = status
|
|
65
|
+
|
|
66
|
+
switch (type) {
|
|
67
|
+
case 'disconnect':
|
|
68
|
+
sendParent({ type: 'NATS_CONNECTION.DISCONNECTED', status })
|
|
69
|
+
break
|
|
70
|
+
case 'reconnect':
|
|
71
|
+
sendParent({ type: 'NATS_CONNECTION.RECONNECT', status })
|
|
72
|
+
break
|
|
73
|
+
case 'error':
|
|
74
|
+
sendParent({ type: 'NATS_CONNECTION.ERROR', status })
|
|
75
|
+
break
|
|
76
|
+
case 'close':
|
|
77
|
+
sendParent({ type: 'NATS_CONNECTION.CLOSE', status })
|
|
78
|
+
break
|
|
79
|
+
case 'ldm':
|
|
80
|
+
console.debug('LDM', status)
|
|
81
|
+
break
|
|
82
|
+
case 'ping':
|
|
83
|
+
// console.debug('Received ping, pong sent automatically')
|
|
84
|
+
break
|
|
85
|
+
case 'forceReconnect':
|
|
86
|
+
sendParent({ type: 'NATS_CONNECTION.RECONNECT', status })
|
|
87
|
+
break
|
|
88
|
+
case 'reconnecting':
|
|
89
|
+
sendParent({ type: 'NATS_CONNECTION.RECONNECTING', status })
|
|
90
|
+
break
|
|
91
|
+
case 'slowConsumer':
|
|
92
|
+
console.debug('SLOW_CONSUMER', status)
|
|
93
|
+
break
|
|
94
|
+
case 'staleConnection':
|
|
95
|
+
console.debug('STALE_CONNECTION', status)
|
|
96
|
+
break
|
|
97
|
+
case 'update':
|
|
98
|
+
console.debug('NATS_CONNECTION.UPDATE', status)
|
|
99
|
+
break
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
console.log('Exiting nats status loop')
|
|
103
|
+
})()
|
|
104
|
+
|
|
105
|
+
return nc
|
|
106
|
+
},
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
export const disconnectNats = fromPromise(
|
|
110
|
+
async ({ input }: { input: { connection: NatsConnection | null } }) => {
|
|
111
|
+
if (input.connection) {
|
|
112
|
+
await input.connection.drain()
|
|
113
|
+
await input.connection.close()
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
export const parseNatsResult = (msg: Msg | KvEntry | null | Error) => {
|
|
119
|
+
if (!msg) {
|
|
120
|
+
return null
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (msg instanceof Error) {
|
|
124
|
+
return msg
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let data
|
|
128
|
+
try {
|
|
129
|
+
data = msg.json()
|
|
130
|
+
} catch (jsonError) {
|
|
131
|
+
// If JSON parsing fails, use the raw string
|
|
132
|
+
data = msg.string()
|
|
133
|
+
}
|
|
134
|
+
return data
|
|
135
|
+
}
|