@camera.ui/rpc 1.0.3 → 1.0.4

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.
Files changed (32) hide show
  1. package/externals/nats.js/core/src/authenticator.ts +159 -0
  2. package/externals/nats.js/core/src/bench.ts +426 -0
  3. package/externals/nats.js/core/src/codec.ts +28 -0
  4. package/externals/nats.js/core/src/core.ts +1219 -0
  5. package/externals/nats.js/core/src/databuffer.ts +129 -0
  6. package/externals/nats.js/core/src/denobuffer.ts +248 -0
  7. package/externals/nats.js/core/src/encoders.ts +53 -0
  8. package/externals/nats.js/core/src/errors.ts +300 -0
  9. package/externals/nats.js/core/src/headers.ts +315 -0
  10. package/externals/nats.js/core/src/heartbeats.ts +114 -0
  11. package/externals/nats.js/core/src/idleheartbeat_monitor.ts +140 -0
  12. package/externals/nats.js/core/src/internal_mod.ts +167 -0
  13. package/externals/nats.js/core/src/ipparser.ts +215 -0
  14. package/externals/nats.js/core/src/mod.ts +113 -0
  15. package/externals/nats.js/core/src/msg.ts +120 -0
  16. package/externals/nats.js/core/src/muxsubscription.ts +111 -0
  17. package/externals/nats.js/core/src/nats.ts +650 -0
  18. package/externals/nats.js/core/src/nkeys.ts +1 -0
  19. package/externals/nats.js/core/src/nuid.ts +16 -0
  20. package/externals/nats.js/core/src/options.ts +202 -0
  21. package/externals/nats.js/core/src/parser.ts +756 -0
  22. package/externals/nats.js/core/src/protocol.ts +1304 -0
  23. package/externals/nats.js/core/src/queued_iterator.ts +171 -0
  24. package/externals/nats.js/core/src/request.ts +177 -0
  25. package/externals/nats.js/core/src/semver.ts +165 -0
  26. package/externals/nats.js/core/src/servers.ts +424 -0
  27. package/externals/nats.js/core/src/transport.ts +117 -0
  28. package/externals/nats.js/core/src/types.ts +17 -0
  29. package/externals/nats.js/core/src/util.ts +367 -0
  30. package/externals/nats.js/core/src/version.ts +2 -0
  31. package/externals/nats.js/core/src/ws_transport.ts +391 -0
  32. package/package.json +2 -1
@@ -0,0 +1,650 @@
1
+ /*
2
+ * Copyright 2018-2023 The NATS Authors
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ */
15
+
16
+ import { deferred } from "./util.ts";
17
+ import { ProtocolHandler, SubscriptionImpl } from "./protocol.ts";
18
+ import { Empty } from "./encoders.ts";
19
+ import { headers } from "./headers.ts";
20
+
21
+ import type { Features, SemVer } from "./semver.ts";
22
+ import { parseSemVer } from "./semver.ts";
23
+
24
+ import { parseOptions } from "./options.ts";
25
+ import { QueuedIteratorImpl } from "./queued_iterator.ts";
26
+ import type { RequestManyOptionsInternal } from "./request.ts";
27
+ import { RequestMany, RequestOne } from "./request.ts";
28
+
29
+ import type {
30
+ ConnectionClosedListener,
31
+ ConnectionOptions,
32
+ Context,
33
+ Msg,
34
+ NatsConnection,
35
+ Payload,
36
+ PublishOptions,
37
+ QueuedIterator,
38
+ RequestManyOptions,
39
+ RequestOptions,
40
+ Server,
41
+ ServerInfo,
42
+ Stats,
43
+ Status,
44
+ Subscription,
45
+ SubscriptionOptions,
46
+ } from "./core.ts";
47
+ import { createInbox } from "./core.ts";
48
+ import { errors, InvalidArgumentError, TimeoutError } from "./errors.ts";
49
+
50
+ const whitespaceRegex = /[ \n\r\t]/;
51
+
52
+ export class NatsConnectionImpl implements NatsConnection {
53
+ options: ConnectionOptions;
54
+ protocol!: ProtocolHandler;
55
+ draining: boolean;
56
+ closeListeners?: CloseListeners;
57
+
58
+ private constructor(opts: ConnectionOptions) {
59
+ this.draining = false;
60
+ this.options = parseOptions(opts);
61
+ }
62
+
63
+ public static connect(opts: ConnectionOptions = {}): Promise<NatsConnection> {
64
+ return new Promise<NatsConnection>((resolve, reject) => {
65
+ const nc = new NatsConnectionImpl(opts);
66
+ ProtocolHandler.connect(nc.options, nc)
67
+ .then((ph: ProtocolHandler) => {
68
+ nc.protocol = ph;
69
+ resolve(nc);
70
+ })
71
+ .catch((err: Error) => {
72
+ reject(err);
73
+ });
74
+ });
75
+ }
76
+
77
+ closed(): Promise<void | Error> {
78
+ return this.protocol.closed;
79
+ }
80
+
81
+ async close() {
82
+ await this.protocol.close();
83
+ }
84
+
85
+ // camera.ui fork patch — see ProtocolHandler.abortClose for semantics.
86
+ abortClose(err?: Error): void {
87
+ this.protocol.abortClose(err);
88
+ }
89
+
90
+ _check(subject: string, sub: boolean, pub: boolean) {
91
+ if (this.isClosed()) {
92
+ throw new errors.ClosedConnectionError();
93
+ }
94
+ if (sub && this.isDraining()) {
95
+ throw new errors.DrainingConnectionError();
96
+ }
97
+ if (pub && this.protocol.noMorePublishing) {
98
+ throw new errors.DrainingConnectionError();
99
+ }
100
+ subject = subject || "";
101
+ if (subject.length === 0 || whitespaceRegex.test(subject)) {
102
+ throw new errors.InvalidSubjectError(subject);
103
+ }
104
+ }
105
+
106
+ publish(
107
+ subject: string,
108
+ data?: Payload,
109
+ options?: PublishOptions,
110
+ ): void {
111
+ this._check(subject, false, true);
112
+ if (options?.reply) {
113
+ this._check(options.reply, false, true);
114
+ }
115
+
116
+ if (typeof options?.traceOnly === "boolean") {
117
+ const hdrs = options.headers || headers();
118
+ hdrs.set("Nats-Trace-Only", "true");
119
+ options.headers = hdrs;
120
+ }
121
+ if (typeof options?.traceDestination === "string") {
122
+ const hdrs = options.headers || headers();
123
+ hdrs.set("Nats-Trace-Dest", options.traceDestination);
124
+ options.headers = hdrs;
125
+ }
126
+
127
+ this.protocol.publish(subject, data, options);
128
+ }
129
+
130
+ publishMessage(msg: Msg): void {
131
+ return this.publish(msg.subject, msg.data, {
132
+ reply: msg.reply,
133
+ headers: msg.headers,
134
+ });
135
+ }
136
+
137
+ respondMessage(msg: Msg): boolean {
138
+ if (msg.reply) {
139
+ this.publish(msg.reply, msg.data, {
140
+ reply: msg.reply,
141
+ headers: msg.headers,
142
+ });
143
+ return true;
144
+ }
145
+ return false;
146
+ }
147
+
148
+ subscribe(
149
+ subject: string,
150
+ opts: SubscriptionOptions = {},
151
+ ): Subscription {
152
+ this._check(subject, true, false);
153
+ const sub = new SubscriptionImpl(this.protocol, subject, opts);
154
+
155
+ if (typeof opts.callback !== "function" && typeof opts.slow === "number") {
156
+ sub.setSlowNotificationFn(opts.slow, (pending: number) => {
157
+ this.protocol.dispatchStatus({
158
+ type: "slowConsumer",
159
+ sub,
160
+ pending,
161
+ });
162
+ });
163
+ }
164
+ this.protocol.subscribe(sub);
165
+ return sub;
166
+ }
167
+
168
+ _resub(s: Subscription, subject: string, max?: number) {
169
+ this._check(subject, true, false);
170
+ const si = s as SubscriptionImpl;
171
+ // FIXME: need way of understanding a callbacks processed
172
+ // count without it, we cannot really do much - ie
173
+ // for rejected messages, the count would be lower, etc.
174
+ // To handle cases were for example KV is building a map
175
+ // the consumer would say how many messages we need to do
176
+ // a proper build before we can handle updates.
177
+ si.max = max; // this might clear it
178
+ if (max) {
179
+ // we cannot auto-unsub, because we don't know the
180
+ // number of messages we processed vs received
181
+ // allow the auto-unsub on processMsg to work if they
182
+ // we were called with a new max
183
+ si.max = max + si.received;
184
+ }
185
+ this.protocol.resub(si, subject);
186
+ }
187
+
188
+ // possibilities are:
189
+ // stop on error or any non-100 status
190
+ // AND:
191
+ // - wait for timer
192
+ // - wait for n messages or timer
193
+ // - wait for unknown messages, done when empty or reset timer expires (with possible alt wait)
194
+ // - wait for unknown messages, done when an empty payload is received or timer expires (with possible alt wait)
195
+ requestMany(
196
+ subject: string,
197
+ data: Payload = Empty,
198
+ opts: Partial<RequestManyOptions> = { maxWait: 1000, maxMessages: -1 },
199
+ ): Promise<QueuedIterator<Msg>> {
200
+ const asyncTraces = !(this.protocol.options.noAsyncTraces || false);
201
+
202
+ try {
203
+ this._check(subject, true, true);
204
+ } catch (err) {
205
+ return Promise.reject(err);
206
+ }
207
+
208
+ opts.strategy = opts.strategy || "timer";
209
+ opts.maxWait = opts.maxWait || 1000;
210
+ if (opts.maxWait < 1) {
211
+ return Promise.reject(
212
+ InvalidArgumentError.format("timeout", "must be greater than 0"),
213
+ );
214
+ }
215
+
216
+ // the iterator for user results
217
+ const qi = new QueuedIteratorImpl<Msg>();
218
+ function stop(err?: Error) {
219
+ qi.push(() => {
220
+ qi.stop(err);
221
+ });
222
+ }
223
+
224
+ // callback for the subscription or the mux handler
225
+ // simply pushes errors and messages into the iterator
226
+ function callback(err: Error | null, msg: Msg | null) {
227
+ if (err || msg === null) {
228
+ stop(err === null ? undefined : err);
229
+ } else {
230
+ qi.push(msg);
231
+ }
232
+ }
233
+
234
+ if (opts.noMux) {
235
+ // we setup a subscription and manage it
236
+ const stack = asyncTraces ? new Error().stack : null;
237
+ let max = typeof opts.maxMessages === "number" && opts.maxMessages > 0
238
+ ? opts.maxMessages
239
+ : -1;
240
+
241
+ const sub = this.subscribe(createInbox(this.options.inboxPrefix), {
242
+ callback: (err, msg) => {
243
+ // we only expect runtime errors or a no responders
244
+ if (
245
+ msg?.data?.length === 0 &&
246
+ msg?.headers?.status === "503"
247
+ ) {
248
+ err = new errors.NoRespondersError(subject);
249
+ }
250
+ // augment any error with the current stack to provide context
251
+ // for the error in the user code
252
+ if (err) {
253
+ if (stack) {
254
+ err.stack += `\n\n${stack}`;
255
+ }
256
+ cancel(err);
257
+ return;
258
+ }
259
+ // push the message
260
+ callback(null, msg);
261
+ // see if the m request is completed
262
+ if (opts.strategy === "count") {
263
+ max--;
264
+ if (max === 0) {
265
+ cancel();
266
+ }
267
+ }
268
+ if (opts.strategy === "stall") {
269
+ clearTimers();
270
+ timer = setTimeout(() => {
271
+ cancel();
272
+ }, 300);
273
+ }
274
+ if (opts.strategy === "sentinel") {
275
+ if (msg && msg.data.length === 0) {
276
+ cancel();
277
+ }
278
+ }
279
+ },
280
+ });
281
+ (sub as SubscriptionImpl).requestSubject = subject;
282
+
283
+ sub.closed
284
+ .then(() => {
285
+ stop();
286
+ })
287
+ .catch((err: Error) => {
288
+ qi.stop(err);
289
+ });
290
+
291
+ const cancel = (err?: Error) => {
292
+ if (err) {
293
+ qi.push(() => {
294
+ throw err;
295
+ });
296
+ }
297
+ clearTimers();
298
+ sub.drain()
299
+ .then(() => {
300
+ stop();
301
+ })
302
+ .catch((_err: Error) => {
303
+ stop();
304
+ });
305
+ };
306
+
307
+ qi.iterClosed
308
+ .then(() => {
309
+ clearTimers();
310
+ sub?.unsubscribe();
311
+ })
312
+ .catch((_err) => {
313
+ clearTimers();
314
+ sub?.unsubscribe();
315
+ });
316
+
317
+ const { headers, traceDestination, traceOnly } = opts;
318
+ try {
319
+ this.publish(subject, data, {
320
+ reply: sub.getSubject(),
321
+ headers,
322
+ traceDestination,
323
+ traceOnly,
324
+ });
325
+ } catch (err) {
326
+ cancel(err as Error);
327
+ }
328
+
329
+ let timer = setTimeout(() => {
330
+ cancel();
331
+ }, opts.maxWait);
332
+
333
+ const clearTimers = () => {
334
+ if (timer) {
335
+ clearTimeout(timer);
336
+ }
337
+ };
338
+ } else {
339
+ // the ingestion is the RequestMany
340
+ const rmo = opts as RequestManyOptionsInternal;
341
+ rmo.callback = callback;
342
+
343
+ qi.iterClosed.then(() => {
344
+ r.cancel();
345
+ }).catch((err) => {
346
+ r.cancel(err);
347
+ });
348
+
349
+ const r = new RequestMany(this.protocol.muxSubscriptions, subject, rmo);
350
+ this.protocol.request(r);
351
+
352
+ const { headers, traceDestination, traceOnly } = opts;
353
+
354
+ try {
355
+ this.publish(
356
+ subject,
357
+ data,
358
+ {
359
+ reply: `${this.protocol.muxSubscriptions.baseInbox}${r.token}`,
360
+ headers,
361
+ traceDestination,
362
+ traceOnly,
363
+ },
364
+ );
365
+ } catch (err) {
366
+ r.cancel(err as Error);
367
+ }
368
+ }
369
+
370
+ return Promise.resolve(qi);
371
+ }
372
+
373
+ request(
374
+ subject: string,
375
+ data?: Payload,
376
+ opts: RequestOptions = { timeout: 1000, noMux: false },
377
+ ): Promise<Msg> {
378
+ try {
379
+ this._check(subject, true, true);
380
+ } catch (err) {
381
+ return Promise.reject(err);
382
+ }
383
+ const asyncTraces = !(this.protocol.options.noAsyncTraces || false);
384
+ opts.timeout = opts.timeout || 1000;
385
+ if (opts.timeout < 1) {
386
+ return Promise.reject(
387
+ InvalidArgumentError.format("timeout", `must be greater than 0`),
388
+ );
389
+ }
390
+ if (!opts.noMux && opts.reply) {
391
+ return Promise.reject(
392
+ InvalidArgumentError.format(
393
+ ["reply", "noMux"],
394
+ "are mutually exclusive",
395
+ ),
396
+ );
397
+ }
398
+
399
+ if (opts.noMux) {
400
+ const inbox = opts.reply
401
+ ? opts.reply
402
+ : createInbox(this.options.inboxPrefix);
403
+ const d = deferred<Msg>();
404
+ const errCtx = asyncTraces ? new errors.RequestError("") : null;
405
+ const sub = this.subscribe(
406
+ inbox,
407
+ {
408
+ max: 1,
409
+ timeout: opts.timeout,
410
+ callback: (err, msg) => {
411
+ // check for no responders status
412
+ if (msg && msg.data?.length === 0 && msg.headers?.code === 503) {
413
+ err = new errors.NoRespondersError(subject);
414
+ }
415
+ if (err) {
416
+ // we have a proper stack on timeout
417
+ if (!(err instanceof TimeoutError)) {
418
+ if (errCtx) {
419
+ errCtx.message = err.message;
420
+ errCtx.cause = err;
421
+ err = errCtx;
422
+ } else {
423
+ err = new errors.RequestError(err.message, { cause: err });
424
+ }
425
+ }
426
+ d.reject(err);
427
+ sub.unsubscribe();
428
+ } else {
429
+ d.resolve(msg);
430
+ }
431
+ },
432
+ },
433
+ );
434
+ (sub as SubscriptionImpl).requestSubject = subject;
435
+ this.protocol.publish(subject, data, {
436
+ reply: inbox,
437
+ headers: opts.headers,
438
+ });
439
+ return d;
440
+ } else {
441
+ const r = new RequestOne(
442
+ this.protocol.muxSubscriptions,
443
+ subject,
444
+ opts,
445
+ asyncTraces,
446
+ );
447
+ this.protocol.request(r);
448
+
449
+ const { headers, traceDestination, traceOnly } = opts;
450
+
451
+ try {
452
+ this.publish(
453
+ subject,
454
+ data,
455
+ {
456
+ reply: `${this.protocol.muxSubscriptions.baseInbox}${r.token}`,
457
+ headers,
458
+ traceDestination,
459
+ traceOnly,
460
+ },
461
+ );
462
+ } catch (err) {
463
+ r.cancel(err as Error);
464
+ }
465
+
466
+ const p = Promise.race([r.timer, r.deferred]);
467
+ p.catch(() => {
468
+ r.cancel();
469
+ });
470
+ return p;
471
+ }
472
+ }
473
+
474
+ /** *
475
+ * Flushes to the server. Promise resolves when round-trip completes.
476
+ * @returns {Promise<void>}
477
+ */
478
+ flush(): Promise<void> {
479
+ if (this.isClosed()) {
480
+ return Promise.reject(new errors.ClosedConnectionError());
481
+ }
482
+ return this.protocol.flush();
483
+ }
484
+
485
+ drain(): Promise<void> {
486
+ if (this.isClosed()) {
487
+ return Promise.reject(new errors.ClosedConnectionError());
488
+ }
489
+ if (this.isDraining()) {
490
+ return Promise.reject(new errors.DrainingConnectionError());
491
+ }
492
+ this.draining = true;
493
+ return this.protocol.drain();
494
+ }
495
+
496
+ async [Symbol.asyncDispose](): Promise<void> {
497
+ if (this.isClosed()) {
498
+ return;
499
+ }
500
+ if (this.isDraining()) {
501
+ await this.closed();
502
+ return;
503
+ }
504
+ await this.drain();
505
+ }
506
+
507
+ isClosed(): boolean {
508
+ return this.protocol.isClosed();
509
+ }
510
+
511
+ isDraining(): boolean {
512
+ return this.draining;
513
+ }
514
+
515
+ getServer(): string {
516
+ const srv = this.protocol.getServer();
517
+ return srv ? srv.listen : "";
518
+ }
519
+
520
+ setServers(servers: string[]): void {
521
+ this.protocol.servers.setServers(servers);
522
+ }
523
+
524
+ getServers(): ReadonlyArray<Server> {
525
+ return this.protocol.servers.snapshot();
526
+ }
527
+
528
+ status(): AsyncIterable<Status> {
529
+ const iter = new QueuedIteratorImpl<Status>();
530
+ iter.iterClosed.then(() => {
531
+ const idx = this.protocol.listeners.indexOf(iter);
532
+ if (idx > -1) {
533
+ this.protocol.listeners.splice(idx, 1);
534
+ }
535
+ });
536
+ this.protocol.listeners.push(iter);
537
+ return iter;
538
+ }
539
+
540
+ get info(): ServerInfo | undefined {
541
+ return this.protocol.isClosed() ? undefined : this.protocol.info;
542
+ }
543
+
544
+ async context(): Promise<Context> {
545
+ const r = await this.request(`$SYS.REQ.USER.INFO`);
546
+ return r.json<Context>((key, value) => {
547
+ if (key === "time") {
548
+ return new Date(Date.parse(value));
549
+ }
550
+ return value;
551
+ });
552
+ }
553
+
554
+ stats(): Stats {
555
+ return {
556
+ inBytes: this.protocol.inBytes,
557
+ outBytes: this.protocol.outBytes,
558
+ inMsgs: this.protocol.inMsgs,
559
+ outMsgs: this.protocol.outMsgs,
560
+ };
561
+ }
562
+
563
+ getServerVersion(): SemVer | undefined {
564
+ const info = this.info;
565
+ return info ? parseSemVer(info.version) : undefined;
566
+ }
567
+
568
+ async rtt(): Promise<number> {
569
+ if (this.isClosed()) {
570
+ throw new errors.ClosedConnectionError();
571
+ }
572
+ if (!this.protocol.connected) {
573
+ throw new errors.RequestError("connection disconnected");
574
+ }
575
+ const start = Date.now();
576
+ await this.flush();
577
+ return Date.now() - start;
578
+ }
579
+
580
+ get features(): Features {
581
+ return this.protocol.features;
582
+ }
583
+
584
+ reconnect(): Promise<void> {
585
+ if (this.isClosed()) {
586
+ return Promise.reject(new errors.ClosedConnectionError());
587
+ }
588
+ if (this.isDraining()) {
589
+ return Promise.reject(new errors.DrainingConnectionError());
590
+ }
591
+ return this.protocol.reconnect();
592
+ }
593
+
594
+ // camera.ui fork patch — see ProtocolHandler.forceReconnect for semantics.
595
+ forceReconnect(): Promise<void> {
596
+ if (this.isClosed()) {
597
+ return Promise.reject(new errors.ClosedConnectionError());
598
+ }
599
+ if (this.isDraining()) {
600
+ return Promise.reject(new errors.DrainingConnectionError());
601
+ }
602
+ return this.protocol.forceReconnect();
603
+ }
604
+
605
+ // internal
606
+ addCloseListener(listener: ConnectionClosedListener) {
607
+ if (this.closeListeners === undefined) {
608
+ this.closeListeners = new CloseListeners(this.closed());
609
+ }
610
+ this.closeListeners.add(listener);
611
+ }
612
+ // internal
613
+ removeCloseListener(listener: ConnectionClosedListener) {
614
+ if (this.closeListeners) {
615
+ this.closeListeners.remove(listener);
616
+ }
617
+ }
618
+ }
619
+
620
+ class CloseListeners {
621
+ listeners: ConnectionClosedListener[];
622
+
623
+ constructor(closed: Promise<void | Error>) {
624
+ this.listeners = [];
625
+ closed.then((err) => {
626
+ this.notify(err);
627
+ });
628
+ }
629
+
630
+ add(listener: ConnectionClosedListener) {
631
+ this.listeners.push(listener);
632
+ }
633
+
634
+ remove(listener: ConnectionClosedListener) {
635
+ this.listeners = this.listeners.filter((l) => l !== listener);
636
+ }
637
+
638
+ notify(err: void | Error) {
639
+ this.listeners.forEach((l) => {
640
+ if (typeof l.connectionClosedCallback === "function") {
641
+ try {
642
+ l.connectionClosedCallback(err);
643
+ } catch (_) {
644
+ // ignored
645
+ }
646
+ }
647
+ });
648
+ this.listeners = [];
649
+ }
650
+ }
@@ -0,0 +1 @@
1
+ export * as nkeys from "@nats-io/nkeys";
@@ -0,0 +1,16 @@
1
+ /*
2
+ * Copyright 2016-2021 The NATS Authors
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ */
15
+
16
+ export { Nuid, nuid } from "@nats-io/nuid";