relayx-js 1.0.15 → 1.0.17
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/CHANGELOG.md +7 -0
- package/examples/example_chat.js +20 -7
- package/package.json +1 -1
- package/realtime/realtime.js +136 -38
- package/tests/test.js +141 -3
- package/.github/workflows/jekyll-gh-pages.yml +0 -51
package/CHANGELOG.md
CHANGED
package/examples/example_chat.js
CHANGED
|
@@ -28,13 +28,26 @@ async function run(){
|
|
|
28
28
|
console.log(`[IMPL] DISONNECT`)
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
await realtime.on("power-telemetry", (data) => {
|
|
32
|
-
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
await realtime.on("hello.*", (data) => {
|
|
36
|
-
|
|
37
|
-
});
|
|
31
|
+
// await realtime.on("power-telemetry", (data) => {
|
|
32
|
+
// console.log("power-telemetry", data);
|
|
33
|
+
// });
|
|
34
|
+
|
|
35
|
+
// await realtime.on("hello.*", (data) => {
|
|
36
|
+
// console.log("hello.*", data);
|
|
37
|
+
// });
|
|
38
|
+
|
|
39
|
+
// await realtime.on("hello.>", async (data) => {
|
|
40
|
+
// await realtime.sleep(10000)
|
|
41
|
+
// console.log("hello.>", data);
|
|
42
|
+
// });
|
|
43
|
+
|
|
44
|
+
// await realtime.on("hello.hey.*", (data) => {
|
|
45
|
+
// console.log("hell.hey.*", data);
|
|
46
|
+
// });
|
|
47
|
+
|
|
48
|
+
// await realtime.on("hello.hey.>", (data) => {
|
|
49
|
+
// console.log("hello.hey.>", data);
|
|
50
|
+
// });
|
|
38
51
|
|
|
39
52
|
realtime.on(MESSAGE_RESEND, (data) => {
|
|
40
53
|
console.log(`[MSG RESEND] => ${data}`)
|
package/package.json
CHANGED
package/realtime/realtime.js
CHANGED
|
@@ -12,6 +12,7 @@ export class Realtime {
|
|
|
12
12
|
#codec = JSONCodec();
|
|
13
13
|
#jetstream = null;
|
|
14
14
|
#consumerMap = {};
|
|
15
|
+
#consumer = null;
|
|
15
16
|
|
|
16
17
|
#event_func = {};
|
|
17
18
|
#topicMap = [];
|
|
@@ -121,9 +122,9 @@ export class Realtime {
|
|
|
121
122
|
"nats://0.0.0.0:4223",
|
|
122
123
|
] :
|
|
123
124
|
[
|
|
124
|
-
`tls://
|
|
125
|
-
`tls://
|
|
126
|
-
`tls://
|
|
125
|
+
`tls://api.relay-x.io:4221`,
|
|
126
|
+
`tls://api.relay-x.io:4222`,
|
|
127
|
+
`tls://api.relay-x.io:4223`
|
|
127
128
|
];
|
|
128
129
|
}else{
|
|
129
130
|
this.#baseUrl = [
|
|
@@ -232,7 +233,6 @@ export class Realtime {
|
|
|
232
233
|
this.#log(`client disconnected - ${s.data}`);
|
|
233
234
|
|
|
234
235
|
this.connected = false;
|
|
235
|
-
this.#consumerMap = {};
|
|
236
236
|
|
|
237
237
|
if (DISCONNECTED in this.#event_func){
|
|
238
238
|
if (this.#event_func[DISCONNECTED] !== null || this.#event_func[DISCONNECTED] !== undefined){
|
|
@@ -291,13 +291,17 @@ export class Realtime {
|
|
|
291
291
|
/**
|
|
292
292
|
* Closes connection
|
|
293
293
|
*/
|
|
294
|
-
close(){
|
|
294
|
+
async close(){
|
|
295
295
|
if(this.#natsClient !== null){
|
|
296
296
|
this.reconnected = false;
|
|
297
297
|
this.disconnected = true;
|
|
298
298
|
|
|
299
|
+
this.#consumer?.delete()
|
|
300
|
+
|
|
299
301
|
this.#offlineMessageBuffer.length = 0;
|
|
300
302
|
|
|
303
|
+
await this.#deleteConsumer();
|
|
304
|
+
|
|
301
305
|
this.#natsClient.close();
|
|
302
306
|
}else{
|
|
303
307
|
this.#log("Null / undefined socket, cannot close connection");
|
|
@@ -308,10 +312,9 @@ export class Realtime {
|
|
|
308
312
|
* Start consumers for topics initialized by user
|
|
309
313
|
*/
|
|
310
314
|
async #subscribeToTopics(){
|
|
311
|
-
this.#topicMap.
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
});
|
|
315
|
+
if(this.#topicMap.length > 0){
|
|
316
|
+
await this.#startConsumer();
|
|
317
|
+
}
|
|
315
318
|
}
|
|
316
319
|
|
|
317
320
|
/**
|
|
@@ -332,8 +335,6 @@ export class Realtime {
|
|
|
332
335
|
this.#topicMap = this.#topicMap.filter(item => item !== topic);
|
|
333
336
|
|
|
334
337
|
delete this.#event_func[topic];
|
|
335
|
-
|
|
336
|
-
return await this.#deleteConsumer(topic);
|
|
337
338
|
}
|
|
338
339
|
|
|
339
340
|
/**
|
|
@@ -476,6 +477,10 @@ export class Realtime {
|
|
|
476
477
|
throw new Error(`Expected $topic type -> string. Instead receieved -> ${typeof topic}`);
|
|
477
478
|
}
|
|
478
479
|
|
|
480
|
+
if(!this.isTopicValid(topic)){
|
|
481
|
+
throw new Error("Invalid topic, use isTopicValid($topic) to validate topic")
|
|
482
|
+
}
|
|
483
|
+
|
|
479
484
|
if(start == undefined || start == null){
|
|
480
485
|
throw new Error(`$start must be provided. $start is => ${start}`)
|
|
481
486
|
}
|
|
@@ -576,40 +581,48 @@ export class Realtime {
|
|
|
576
581
|
* Starts consumer for particular topic if stream exists
|
|
577
582
|
* @param {string} topic
|
|
578
583
|
*/
|
|
579
|
-
async #startConsumer(
|
|
580
|
-
this.#
|
|
584
|
+
async #startConsumer(){
|
|
585
|
+
if(this.#consumer != null){
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
581
588
|
|
|
582
589
|
var opts = {
|
|
583
|
-
name: `${
|
|
584
|
-
filter_subjects: [this.#getStreamTopic(
|
|
590
|
+
name: `${uuidv4()}`,
|
|
591
|
+
filter_subjects: [this.#getStreamTopic(">")],
|
|
585
592
|
replay_policy: ReplayPolicy.Instant,
|
|
586
593
|
opt_start_time: new Date(),
|
|
587
594
|
ack_policy: AckPolicy.Explicit,
|
|
588
595
|
delivery_policy: DeliverPolicy.New
|
|
589
596
|
}
|
|
590
597
|
|
|
591
|
-
|
|
598
|
+
this.#consumer = await this.#jetstream.consumers.get(this.#getStreamName(), opts);
|
|
592
599
|
this.#log(this.#topicMap)
|
|
593
600
|
|
|
594
|
-
this.#
|
|
595
|
-
|
|
596
|
-
await consumer.consume({
|
|
601
|
+
await this.#consumer.consume({
|
|
597
602
|
callback: async (msg) => {
|
|
598
603
|
try{
|
|
599
604
|
const now = Date.now();
|
|
600
605
|
this.#log("Decoding msgpack message...")
|
|
601
606
|
var data = decode(msg.data);
|
|
602
607
|
|
|
603
|
-
var room =
|
|
608
|
+
var room = this.#stripStreamHash(msg.subject);
|
|
604
609
|
|
|
605
610
|
this.#log(data);
|
|
606
611
|
|
|
607
612
|
// Push topic message to main thread
|
|
608
|
-
if (
|
|
609
|
-
this.#
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
+
if (data.client_id != this.#getClientId()){
|
|
614
|
+
var topics = this.#getCallbackTopics(room);
|
|
615
|
+
this.#log(topics)
|
|
616
|
+
|
|
617
|
+
for(let i = 0; i < topics.length; i++){
|
|
618
|
+
var top = topics[i];
|
|
619
|
+
|
|
620
|
+
this.#event_func[top]({
|
|
621
|
+
"id": data.id,
|
|
622
|
+
"topic": room,
|
|
623
|
+
"data": data.message
|
|
624
|
+
});
|
|
625
|
+
}
|
|
613
626
|
}
|
|
614
627
|
|
|
615
628
|
msg.ack();
|
|
@@ -617,30 +630,22 @@ export class Realtime {
|
|
|
617
630
|
await this.#logLatency(now, data);
|
|
618
631
|
}catch(err){
|
|
619
632
|
this.#log("Consumer err " + err);
|
|
620
|
-
msg.
|
|
633
|
+
msg.nak(5000);
|
|
621
634
|
}
|
|
622
635
|
}
|
|
623
636
|
});
|
|
624
637
|
this.#log("Consumer is consuming");
|
|
625
638
|
}
|
|
626
639
|
|
|
627
|
-
|
|
628
|
-
* Deletes consumer
|
|
629
|
-
* @param {string} topic
|
|
630
|
-
*/
|
|
631
|
-
async #deleteConsumer(topic){
|
|
632
|
-
const consumer = this.#consumerMap[topic]
|
|
633
|
-
|
|
640
|
+
async #deleteConsumer(){
|
|
634
641
|
var del = false;
|
|
635
642
|
|
|
636
|
-
if (consumer != null && consumer != undefined){
|
|
637
|
-
del = await consumer.delete();
|
|
643
|
+
if (this.#consumer != null && this.#consumer != undefined){
|
|
644
|
+
del = await this.#consumer.delete();
|
|
638
645
|
}else{
|
|
639
646
|
del = false
|
|
640
647
|
}
|
|
641
648
|
|
|
642
|
-
delete this.#consumerMap[topic];
|
|
643
|
-
|
|
644
649
|
return del;
|
|
645
650
|
}
|
|
646
651
|
|
|
@@ -743,7 +748,7 @@ export class Realtime {
|
|
|
743
748
|
var arrayCheck = ![CONNECTED, DISCONNECTED, RECONNECT, this.#RECONNECTED,
|
|
744
749
|
this.#RECONNECTING, this.#RECONN_FAIL, MESSAGE_RESEND, SERVER_DISCONNECT].includes(topic);
|
|
745
750
|
|
|
746
|
-
const TOPIC_REGEX = /^(
|
|
751
|
+
const TOPIC_REGEX = /^(?!.*\$)(?:[A-Za-z0-9_*~-]+(?:\.[A-Za-z0-9_*~-]+)*(?:\.>)?|>)$/u;
|
|
747
752
|
|
|
748
753
|
var spaceStarCheck = !topic.includes(" ") && TOPIC_REGEX.test(topic);
|
|
749
754
|
|
|
@@ -800,6 +805,91 @@ export class Realtime {
|
|
|
800
805
|
}
|
|
801
806
|
}
|
|
802
807
|
|
|
808
|
+
#stripStreamHash(topic){
|
|
809
|
+
return topic.replace(`${this.topicHash}.`, "")
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
#getCallbackTopics(topic){
|
|
813
|
+
var validTopics = [];
|
|
814
|
+
|
|
815
|
+
var topicPatterns = Object.keys(this.#event_func);
|
|
816
|
+
|
|
817
|
+
for(let i = 0; i < topicPatterns.length; i++){
|
|
818
|
+
var pattern = topicPatterns[i];
|
|
819
|
+
|
|
820
|
+
if([CONNECTED, RECONNECT, MESSAGE_RESEND, DISCONNECTED, SERVER_DISCONNECT].includes(pattern)){
|
|
821
|
+
continue;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
var match = this.#topicPatternMatcher(pattern, topic);
|
|
825
|
+
|
|
826
|
+
if(match){
|
|
827
|
+
validTopics.push(pattern)
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
return validTopics;
|
|
832
|
+
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
#topicPatternMatcher(patternA, patternB) {
|
|
836
|
+
const a = patternA.split(".");
|
|
837
|
+
const b = patternB.split(".");
|
|
838
|
+
|
|
839
|
+
let i = 0, j = 0; // cursors in a & b
|
|
840
|
+
let starAi = -1, starAj = -1; // last '>' position in A and the token count it has consumed
|
|
841
|
+
let starBi = -1, starBj = -1; // same for pattern B
|
|
842
|
+
|
|
843
|
+
while (i < a.length || j < b.length) {
|
|
844
|
+
const tokA = a[i];
|
|
845
|
+
const tokB = b[j];
|
|
846
|
+
|
|
847
|
+
/*──────────── literal match or single‑token wildcard on either side ────────────*/
|
|
848
|
+
const singleWildcard =
|
|
849
|
+
(tokA === "*" && j < b.length) ||
|
|
850
|
+
(tokB === "*" && i < a.length);
|
|
851
|
+
|
|
852
|
+
if (
|
|
853
|
+
(tokA !== undefined && tokA === tokB) ||
|
|
854
|
+
singleWildcard
|
|
855
|
+
) {
|
|
856
|
+
i++; j++;
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/*────────────────── multi‑token wildcard ">" — must be **final** ───────────────*/
|
|
861
|
+
if (tokA === ">") {
|
|
862
|
+
if (i !== a.length - 1) return false; // '>' not in last position → invalid
|
|
863
|
+
if (j >= b.length) return false; // must consume at least one token
|
|
864
|
+
starAi = i++; // remember where '>' is
|
|
865
|
+
starAj = ++j; // gobble one token from B
|
|
866
|
+
continue;
|
|
867
|
+
}
|
|
868
|
+
if (tokB === ">") {
|
|
869
|
+
if (j !== b.length - 1) return false; // same rule for patternB
|
|
870
|
+
if (i >= a.length) return false;
|
|
871
|
+
starBi = j++;
|
|
872
|
+
starBj = ++i;
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/*───────────────────────────── back‑track using last '>' ───────────────────────*/
|
|
877
|
+
if (starAi !== -1) { // let patternA's '>' absorb one more token of B
|
|
878
|
+
j = ++starAj;
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
if (starBi !== -1) { // let patternB's '>' absorb one more token of A
|
|
882
|
+
i = ++starBj;
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/*────────────────────────────────── dead‑end ───────────────────────────────────*/
|
|
887
|
+
return false;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
return true;
|
|
891
|
+
}
|
|
892
|
+
|
|
803
893
|
sleep(ms) {
|
|
804
894
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
805
895
|
}
|
|
@@ -930,6 +1020,14 @@ export class Realtime {
|
|
|
930
1020
|
return null;
|
|
931
1021
|
}
|
|
932
1022
|
}
|
|
1023
|
+
|
|
1024
|
+
testPatternMatcher(){
|
|
1025
|
+
if(process.env.NODE_ENV == "test"){
|
|
1026
|
+
return this.#topicPatternMatcher.bind(this)
|
|
1027
|
+
}else{
|
|
1028
|
+
return null;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
933
1031
|
}
|
|
934
1032
|
|
|
935
1033
|
export const CONNECTED = "CONNECTED";
|
package/tests/test.js
CHANGED
|
@@ -475,17 +475,82 @@ test("Test isTopicValidMethod()", () => {
|
|
|
475
475
|
assert.strictEqual(valid, false);
|
|
476
476
|
});
|
|
477
477
|
|
|
478
|
-
unreservedInvalidTopics = [
|
|
478
|
+
unreservedInvalidTopics = [
|
|
479
|
+
"$foo",
|
|
480
|
+
"foo$",
|
|
481
|
+
"foo.$.bar",
|
|
482
|
+
"foo..bar",
|
|
483
|
+
".foo",
|
|
484
|
+
"foo.",
|
|
485
|
+
"foo.>.bar",
|
|
486
|
+
">foo",
|
|
487
|
+
"foo>bar",
|
|
488
|
+
"foo.>bar",
|
|
489
|
+
"foo.bar.>.",
|
|
490
|
+
"foo bar",
|
|
491
|
+
"foo/bar",
|
|
492
|
+
"foo#bar",
|
|
493
|
+
"",
|
|
494
|
+
" ",
|
|
495
|
+
"..",
|
|
496
|
+
".>",
|
|
497
|
+
"foo..",
|
|
498
|
+
".",
|
|
499
|
+
">.",
|
|
500
|
+
"foo,baz",
|
|
501
|
+
"αbeta",
|
|
502
|
+
"foo|bar",
|
|
503
|
+
"foo;bar",
|
|
504
|
+
"foo:bar",
|
|
505
|
+
"foo%bar",
|
|
506
|
+
"foo.*.>.bar",
|
|
507
|
+
"foo.*.>.",
|
|
508
|
+
"foo.*..bar",
|
|
509
|
+
"foo.>.bar",
|
|
510
|
+
"foo>"
|
|
511
|
+
];
|
|
479
512
|
|
|
480
513
|
unreservedInvalidTopics.forEach(topic => {
|
|
481
514
|
var valid = realTimeEnabled.isTopicValid(topic);
|
|
482
515
|
assert.strictEqual(valid, false);
|
|
483
516
|
});
|
|
484
517
|
|
|
485
|
-
var unreservedValidTopics = [
|
|
518
|
+
var unreservedValidTopics = [
|
|
519
|
+
"foo",
|
|
520
|
+
"foo.bar",
|
|
521
|
+
"foo.bar.baz",
|
|
522
|
+
"*",
|
|
523
|
+
"foo.*",
|
|
524
|
+
"*.bar",
|
|
525
|
+
"foo.*.baz",
|
|
526
|
+
">",
|
|
527
|
+
"foo.>",
|
|
528
|
+
"foo.bar.>",
|
|
529
|
+
"*.*.>",
|
|
530
|
+
"alpha_beta",
|
|
531
|
+
"alpha-beta",
|
|
532
|
+
"alpha~beta",
|
|
533
|
+
"abc123",
|
|
534
|
+
"123abc",
|
|
535
|
+
"~",
|
|
536
|
+
"alpha.*.>",
|
|
537
|
+
"alpha.*",
|
|
538
|
+
"alpha.*.*",
|
|
539
|
+
"-foo",
|
|
540
|
+
"foo_bar-baz~qux",
|
|
541
|
+
"A.B.C",
|
|
542
|
+
"sensor.temperature",
|
|
543
|
+
"metric.cpu.load",
|
|
544
|
+
"foo.*.*",
|
|
545
|
+
"foo.*.>",
|
|
546
|
+
"foo_bar.*",
|
|
547
|
+
"*.*",
|
|
548
|
+
"metrics.>"
|
|
549
|
+
];
|
|
486
550
|
|
|
487
551
|
unreservedValidTopics.forEach(topic => {
|
|
488
552
|
var valid = realTimeEnabled.isTopicValid(topic);
|
|
553
|
+
console.log(topic)
|
|
489
554
|
assert.strictEqual(valid, true);
|
|
490
555
|
});
|
|
491
556
|
});
|
|
@@ -544,4 +609,77 @@ test("History test", async () => {
|
|
|
544
609
|
},
|
|
545
610
|
new Error("$start must be a Date object"),
|
|
546
611
|
"Expected error was not thrown");
|
|
547
|
-
})
|
|
612
|
+
})
|
|
613
|
+
|
|
614
|
+
test("Pattern matcher test", async () => {
|
|
615
|
+
var cases = [
|
|
616
|
+
["foo", "foo", true], // 1
|
|
617
|
+
["foo", "bar", false], // 2
|
|
618
|
+
["foo.*", "foo.bar", true], // 3
|
|
619
|
+
["foo.bar", "foo.*", true], // 4
|
|
620
|
+
["*", "token", true], // 5
|
|
621
|
+
["*", "*", true], // 6
|
|
622
|
+
["foo.*", "foo.bar.baz", false], // 7
|
|
623
|
+
["foo.>", "foo.bar.baz", true], // 8
|
|
624
|
+
["foo.>", "foo", false], // 9 (zero‑token '>' now invalid)
|
|
625
|
+
["foo.bar.baz", "foo.>", true], // 10
|
|
626
|
+
["foo.bar.>", "foo.bar", false], // 11
|
|
627
|
+
["foo", "foo.>", false], // 12
|
|
628
|
+
["foo.*.>", "foo.bar.baz.qux", true], // 13
|
|
629
|
+
["foo.*.baz", "foo.bar.>", true], // 14
|
|
630
|
+
["alpha.*", "beta.gamma", false], // 15
|
|
631
|
+
["alpha.beta", "alpha.*.*", false], // 16
|
|
632
|
+
["foo.>.bar", "foo.any.bar", false], // 17 ('>' mid‑pattern)
|
|
633
|
+
[">", "foo.bar", true], // 18
|
|
634
|
+
[">", ">", true], // 19
|
|
635
|
+
["*", ">", true], // 20
|
|
636
|
+
["*.>", "foo.bar", true], // 21
|
|
637
|
+
["*.*.*", "a.b.c", true], // 22
|
|
638
|
+
["*.*.*", "a.b", false], // 23
|
|
639
|
+
["a.b.c.d.e", "a.b.c.d.e", true], // 24
|
|
640
|
+
["a.b.c.d.e", "a.b.c.d.f", false], // 25
|
|
641
|
+
["a.b.*.d", "a.b.c.d", true], // 26
|
|
642
|
+
["a.b.*.d", "a.b.c.e", false], // 27
|
|
643
|
+
["a.b.>", "a.b", false], // 28
|
|
644
|
+
["a.b", "a.b.c.d.>", false], // 29
|
|
645
|
+
["a.b.>.c", "a.b.x.c", false], // 30
|
|
646
|
+
["a.*.*", "a.b", false], // 31
|
|
647
|
+
["a.*", "a.b.c", false], // 32
|
|
648
|
+
["metrics.cpu.load", "metrics.*.load", true], // 33
|
|
649
|
+
["metrics.cpu.load", "metrics.cpu.*", true], // 34
|
|
650
|
+
["metrics.cpu.load", "metrics.>.load", false], // 35
|
|
651
|
+
["metrics.>", "metrics", false], // 36
|
|
652
|
+
["metrics.>", "othermetrics.cpu", false], // 37
|
|
653
|
+
["*.*.>", "a.b", false], // 38
|
|
654
|
+
["*.*.>", "a.b.c.d", true], // 39
|
|
655
|
+
["a.b.c", "*.*.*", true], // 40
|
|
656
|
+
["a.b.c", "*.*", false], // 41
|
|
657
|
+
["alpha.*.>", "alpha", false], // 42
|
|
658
|
+
["alpha.*.>", "alpha.beta", false], // 43
|
|
659
|
+
["alpha.*.>", "alpha.beta.gamma", true], // 44
|
|
660
|
+
["alpha.*.>", "beta.alpha.gamma", false], // 45
|
|
661
|
+
["foo-bar_baz", "foo-bar_baz", true], // 46
|
|
662
|
+
["foo-bar_*", "foo-bar_123", false], // 47 ( '*' here is literal )
|
|
663
|
+
["foo-bar_*", "foo-bar_*", true], // 48
|
|
664
|
+
["order-*", "order-123", false], // 49
|
|
665
|
+
["hello.hey.*", "hello.hey.>", true] // 50
|
|
666
|
+
];
|
|
667
|
+
|
|
668
|
+
var realtime = new Realtime({
|
|
669
|
+
api_key: process.env.AUTH_JWT,
|
|
670
|
+
secret: process.env.AUTH_SECRET
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
var patternMatcher = realtime.testPatternMatcher();
|
|
674
|
+
|
|
675
|
+
cases.forEach(testCase => {
|
|
676
|
+
var tokenA = testCase[0];
|
|
677
|
+
var tokenB = testCase[1];
|
|
678
|
+
var expectedResult = testCase[2];
|
|
679
|
+
|
|
680
|
+
console.log(`${tokenA} ⇆ ${tokenB} → ${expectedResult}`)
|
|
681
|
+
|
|
682
|
+
var result = patternMatcher(tokenA, tokenB)
|
|
683
|
+
assert.strictEqual(expectedResult, result)
|
|
684
|
+
});
|
|
685
|
+
})
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
|
|
2
|
-
name: Deploy Jekyll with GitHub Pages dependencies preinstalled
|
|
3
|
-
|
|
4
|
-
on:
|
|
5
|
-
# Runs on pushes targeting the default branch
|
|
6
|
-
push:
|
|
7
|
-
branches: ["main"]
|
|
8
|
-
|
|
9
|
-
# Allows you to run this workflow manually from the Actions tab
|
|
10
|
-
workflow_dispatch:
|
|
11
|
-
|
|
12
|
-
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
|
13
|
-
permissions:
|
|
14
|
-
contents: read
|
|
15
|
-
pages: write
|
|
16
|
-
id-token: write
|
|
17
|
-
|
|
18
|
-
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
|
19
|
-
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
|
20
|
-
concurrency:
|
|
21
|
-
group: "pages"
|
|
22
|
-
cancel-in-progress: false
|
|
23
|
-
|
|
24
|
-
jobs:
|
|
25
|
-
# Build job
|
|
26
|
-
build:
|
|
27
|
-
runs-on: ubuntu-latest
|
|
28
|
-
steps:
|
|
29
|
-
- name: Checkout
|
|
30
|
-
uses: actions/checkout@v4
|
|
31
|
-
- name: Setup Pages
|
|
32
|
-
uses: actions/configure-pages@v5
|
|
33
|
-
- name: Build with Jekyll
|
|
34
|
-
uses: actions/jekyll-build-pages@v1
|
|
35
|
-
with:
|
|
36
|
-
source: ./
|
|
37
|
-
destination: ./_site
|
|
38
|
-
- name: Upload artifact
|
|
39
|
-
uses: actions/upload-pages-artifact@v3
|
|
40
|
-
|
|
41
|
-
# Deployment job
|
|
42
|
-
deploy:
|
|
43
|
-
environment:
|
|
44
|
-
name: github-pages
|
|
45
|
-
url: ${{ steps.deployment.outputs.page_url }}
|
|
46
|
-
runs-on: ubuntu-latest
|
|
47
|
-
needs: build
|
|
48
|
-
steps:
|
|
49
|
-
- name: Deploy to GitHub Pages
|
|
50
|
-
id: deployment
|
|
51
|
-
uses: actions/deploy-pages@v4
|