libshv-js 3.1.6 → 3.2.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/.github/workflows/publish.yml +24 -0
- package/package.json +1 -1
- package/src/chainpack.ts +34 -23
- package/src/cpon.ts +34 -25
- package/src/rpcmessage.ts +31 -28
- package/src/rpcvalue.ts +37 -15
- package/src/ws-client.ts +8 -8
- package/src/zod.ts +4 -4
- package/deno.json +0 -8
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# vim: sw=2
|
|
2
|
+
name: Publish
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: [ "main" ]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: Publish new version
|
|
11
|
+
runs-on: ubuntu-22.04
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v3
|
|
14
|
+
|
|
15
|
+
- name: Setup node
|
|
16
|
+
uses: actions/setup-node@v4
|
|
17
|
+
with:
|
|
18
|
+
node-version: '20.x'
|
|
19
|
+
registry-url: 'https://registry.npmjs.org'
|
|
20
|
+
|
|
21
|
+
- name: Publish
|
|
22
|
+
run: npm publish
|
|
23
|
+
env:
|
|
24
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/package.json
CHANGED
package/src/chainpack.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {utf8ToString} from './cpon';
|
|
2
|
-
import {type RpcValue, type RpcValueType, type DateTime, Decimal, Double, IMap, Int, MetaMap, RpcValueWithMetaData, ShvMap, UInt, withOffset} from './rpcvalue';
|
|
2
|
+
import {type RpcValue, type RpcValueType, type DateTime, Decimal, Double, type IMap, Int, type MetaMap, RpcValueWithMetaData, type ShvMap, UInt, withOffset, shvMapType} from './rpcvalue';
|
|
3
3
|
import {UnpackContext, PackContext} from './cpcontext';
|
|
4
4
|
|
|
5
5
|
enum PackingSchema {
|
|
@@ -297,19 +297,24 @@ class ChainPackReader {
|
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
readMetaMap() {
|
|
300
|
-
return this.implReadMap(
|
|
300
|
+
return this.implReadMap('metamap');
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
readMap() {
|
|
304
|
-
return this.implReadMap(
|
|
304
|
+
return this.implReadMap('map');
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
readIMap() {
|
|
308
|
-
return this.implReadMap(
|
|
308
|
+
return this.implReadMap('imap');
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
-
private implReadMap
|
|
312
|
-
|
|
311
|
+
private implReadMap(map_type: 'map'): ShvMap;
|
|
312
|
+
private implReadMap(map_type: 'imap'): IMap;
|
|
313
|
+
private implReadMap(map_type: 'metamap'): MetaMap;
|
|
314
|
+
private implReadMap(map_type: 'map' | 'imap' | 'metamap') {
|
|
315
|
+
const map: ShvMap | IMap | MetaMap = {
|
|
316
|
+
[shvMapType]: map_type,
|
|
317
|
+
};
|
|
313
318
|
while (true) {
|
|
314
319
|
const b = this.ctx.peekByte();
|
|
315
320
|
if (b as PackingSchema === PackingSchema.Term) {
|
|
@@ -318,14 +323,14 @@ class ChainPackReader {
|
|
|
318
323
|
}
|
|
319
324
|
const key = this.read();
|
|
320
325
|
const val = this.read();
|
|
321
|
-
if (map
|
|
322
|
-
map
|
|
323
|
-
} else if (map
|
|
324
|
-
map
|
|
325
|
-
} else if (map
|
|
326
|
-
map
|
|
327
|
-
} else if (map
|
|
328
|
-
map
|
|
326
|
+
if (map[shvMapType] === 'metamap' && typeof key === 'string') {
|
|
327
|
+
map[key] = val;
|
|
328
|
+
} else if (map[shvMapType] === 'metamap' && key instanceof Int) {
|
|
329
|
+
map[Number(key)] = val;
|
|
330
|
+
} else if (map[shvMapType] === 'map' && typeof key === 'string') {
|
|
331
|
+
map[key] = val;
|
|
332
|
+
} else if (map[shvMapType] === 'imap' && key instanceof Int) {
|
|
333
|
+
map[Number(key)] = val;
|
|
329
334
|
} else {
|
|
330
335
|
throw new TypeError('Malformed map, invalid key');
|
|
331
336
|
}
|
|
@@ -371,15 +376,21 @@ class ChainPackWriter {
|
|
|
371
376
|
case Array.isArray(rpc_val):
|
|
372
377
|
this.writeList(rpc_val);
|
|
373
378
|
break;
|
|
374
|
-
case rpc_val instanceof IMap:
|
|
375
|
-
this.writeIMap(rpc_val);
|
|
376
|
-
break;
|
|
377
|
-
case rpc_val instanceof ShvMap:
|
|
378
|
-
this.writeMap(rpc_val);
|
|
379
|
-
break;
|
|
380
379
|
case rpc_val instanceof Date:
|
|
381
380
|
this.writeDateTime(rpc_val);
|
|
382
381
|
break;
|
|
382
|
+
case rpc_val instanceof Double:
|
|
383
|
+
throw new Error('writing doubles not implemented');
|
|
384
|
+
case typeof rpc_val === 'object':
|
|
385
|
+
switch (rpc_val[shvMapType]) {
|
|
386
|
+
case 'imap':
|
|
387
|
+
this.writeIMap(rpc_val);
|
|
388
|
+
break;
|
|
389
|
+
case 'map':
|
|
390
|
+
this.writeMap(rpc_val);
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
break;
|
|
383
394
|
default:
|
|
384
395
|
console.log('Can\'t serialize', rpc_val);
|
|
385
396
|
throw new Error('Can\'t serialize');
|
|
@@ -545,19 +556,19 @@ class ChainPackWriter {
|
|
|
545
556
|
}
|
|
546
557
|
|
|
547
558
|
writeMapContent(map: MetaMap | ShvMap | IMap) {
|
|
548
|
-
for (const [key, value] of Object.entries(map
|
|
559
|
+
for (const [key, value] of Object.entries(map)) {
|
|
549
560
|
if (value === undefined) {
|
|
550
561
|
continue;
|
|
551
562
|
}
|
|
552
563
|
|
|
553
|
-
if (map
|
|
564
|
+
if (map[shvMapType] === 'imap') {
|
|
554
565
|
const int_key = Number(key);
|
|
555
566
|
if (Number.isNaN(int_key)) {
|
|
556
567
|
throw new TypeError('Invalid NaN IMap key');
|
|
557
568
|
}
|
|
558
569
|
|
|
559
570
|
this.writeInt(new Int(int_key));
|
|
560
|
-
} else if (map
|
|
571
|
+
} else if (map[shvMapType] === 'metamap') {
|
|
561
572
|
const int_key = Number(key);
|
|
562
573
|
if (Number.isNaN(int_key)) {
|
|
563
574
|
this.writeJSString(key.toString());
|
package/src/cpon.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {type RpcValue, type RpcValueType, type DateTime, type List, Decimal, Double, IMap, Int, MetaMap, RpcValueWithMetaData, ShvMap, UInt, withOffset} from './rpcvalue';
|
|
1
|
+
import {type RpcValue, type RpcValueType, type DateTime, type List, Decimal, Double, type IMap, Int, type MetaMap, RpcValueWithMetaData, type ShvMap, UInt, withOffset, shvMapType, isShvMap, isIMap} from './rpcvalue';
|
|
2
2
|
import {PackContext, UnpackContext} from './cpcontext';
|
|
3
3
|
|
|
4
4
|
const hexify = (byte: number) => {
|
|
@@ -412,15 +412,15 @@ class CponReader {
|
|
|
412
412
|
}
|
|
413
413
|
|
|
414
414
|
readMetaMap() {
|
|
415
|
-
return this.implReadMap(
|
|
415
|
+
return this.implReadMap('metamap', '>'.codePointAt(0)!);
|
|
416
416
|
}
|
|
417
417
|
|
|
418
418
|
readMap() {
|
|
419
|
-
return this.implReadMap(
|
|
419
|
+
return this.implReadMap('map', '}'.codePointAt(0)!);
|
|
420
420
|
}
|
|
421
421
|
|
|
422
422
|
readIMap() {
|
|
423
|
-
return this.implReadMap(
|
|
423
|
+
return this.implReadMap('imap', '}'.codePointAt(0)!);
|
|
424
424
|
}
|
|
425
425
|
|
|
426
426
|
readInt() {
|
|
@@ -550,8 +550,13 @@ class CponReader {
|
|
|
550
550
|
return new Int(is_neg ? -mantisa : mantisa);
|
|
551
551
|
}
|
|
552
552
|
|
|
553
|
-
private implReadMap
|
|
554
|
-
|
|
553
|
+
private implReadMap(map_type: 'map', terminator: number): ShvMap;
|
|
554
|
+
private implReadMap(map_type: 'imap', terminator: number): IMap;
|
|
555
|
+
private implReadMap(map_type: 'metamap', terminator: number): MetaMap;
|
|
556
|
+
private implReadMap(map_type: 'map' | 'imap' | 'metamap', terminator: number) {
|
|
557
|
+
const map: MetaMap | ShvMap | IMap = {
|
|
558
|
+
[shvMapType]: map_type,
|
|
559
|
+
};
|
|
555
560
|
this.ctx.getByte(); // eat start
|
|
556
561
|
while (true) {
|
|
557
562
|
this.skipWhitespace();
|
|
@@ -568,14 +573,14 @@ class CponReader {
|
|
|
568
573
|
this.skipWhitespace();
|
|
569
574
|
const val = this.read();
|
|
570
575
|
|
|
571
|
-
if (map
|
|
572
|
-
map
|
|
573
|
-
} else if (map
|
|
574
|
-
map
|
|
575
|
-
} else if (map
|
|
576
|
-
map
|
|
577
|
-
} else if (map
|
|
578
|
-
map
|
|
576
|
+
if (map[shvMapType] === 'metamap' && typeof key === 'string') {
|
|
577
|
+
map[key] = val;
|
|
578
|
+
} else if (map[shvMapType] === 'metamap' && (key instanceof UInt || key instanceof Int)) {
|
|
579
|
+
map[Number(key)] = val;
|
|
580
|
+
} else if (map[shvMapType] === 'map' && typeof key === 'string') {
|
|
581
|
+
map[key] = val;
|
|
582
|
+
} else if (map[shvMapType] === 'imap' && (key instanceof UInt || key instanceof Int)) {
|
|
583
|
+
map[Number(key)] = val;
|
|
579
584
|
} else {
|
|
580
585
|
throw new TypeError('Malformed map, invalid key');
|
|
581
586
|
}
|
|
@@ -625,15 +630,19 @@ class CponWriter {
|
|
|
625
630
|
case Array.isArray(rpc_val):
|
|
626
631
|
this.writeList(rpc_val);
|
|
627
632
|
break;
|
|
628
|
-
case rpc_val instanceof IMap:
|
|
629
|
-
this.writeIMap(rpc_val);
|
|
630
|
-
break;
|
|
631
|
-
case rpc_val instanceof ShvMap:
|
|
632
|
-
this.writeMap(rpc_val);
|
|
633
|
-
break;
|
|
634
633
|
case rpc_val instanceof Date:
|
|
635
634
|
this.writeDateTime(rpc_val);
|
|
636
635
|
break;
|
|
636
|
+
case typeof rpc_val === 'object':
|
|
637
|
+
switch (rpc_val[shvMapType]) {
|
|
638
|
+
case 'imap':
|
|
639
|
+
this.writeIMap(rpc_val);
|
|
640
|
+
break;
|
|
641
|
+
case 'map':
|
|
642
|
+
this.writeMap(rpc_val);
|
|
643
|
+
break;
|
|
644
|
+
}
|
|
645
|
+
break;
|
|
637
646
|
default:
|
|
638
647
|
console.log('Can\'t serialize rpc value', rpc_val);
|
|
639
648
|
throw new Error('Can\'t serialize rpc value');
|
|
@@ -761,7 +770,7 @@ class CponWriter {
|
|
|
761
770
|
this.increaseIndentIfNotOneLiner(map);
|
|
762
771
|
this.doIndentIfNotOneliner(map);
|
|
763
772
|
let first = true;
|
|
764
|
-
for (const [key, value] of Object.entries(map
|
|
773
|
+
for (const [key, value] of Object.entries(map)) {
|
|
765
774
|
if (value === undefined) {
|
|
766
775
|
continue;
|
|
767
776
|
}
|
|
@@ -771,14 +780,14 @@ class CponWriter {
|
|
|
771
780
|
}
|
|
772
781
|
first = false;
|
|
773
782
|
|
|
774
|
-
if (map
|
|
783
|
+
if (map[shvMapType] === 'imap') {
|
|
775
784
|
const int_key = Number(key);
|
|
776
785
|
if (Number.isNaN(int_key)) {
|
|
777
786
|
throw new TypeError('Invalid NaN IMap key');
|
|
778
787
|
}
|
|
779
788
|
|
|
780
789
|
this.writeInt(new Int(int_key));
|
|
781
|
-
} else if (map
|
|
790
|
+
} else if (map[shvMapType] === 'metamap') {
|
|
782
791
|
const int_key = Number(key);
|
|
783
792
|
if (Number.isNaN(int_key)) {
|
|
784
793
|
this.ctx.putByte('"'.codePointAt(0)!);
|
|
@@ -881,10 +890,10 @@ class CponWriter {
|
|
|
881
890
|
const keyThreshold = Array.isArray(value) ? 10 : 5;
|
|
882
891
|
|
|
883
892
|
if (Array.isArray(value)) {
|
|
884
|
-
return value.length <= keyThreshold && !(value.some(x => Array.isArray(x) || x
|
|
893
|
+
return value.length <= keyThreshold && !(value.some(x => Array.isArray(x) || isShvMap(x) || isIMap(x)));
|
|
885
894
|
}
|
|
886
895
|
|
|
887
|
-
return Object.keys(value
|
|
896
|
+
return Object.keys(value).length <= keyThreshold && !(Object.values(value).some(x => Array.isArray(x) || isShvMap(x) || isIMap(x)));
|
|
888
897
|
}
|
|
889
898
|
|
|
890
899
|
private increaseIndentIfNotOneLiner(value: MetaMap | ShvMap | IMap | List) {
|
package/src/rpcmessage.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {IMap, Int, MetaMap, type RpcValue, RpcValueWithMetaData} from './rpcvalue';
|
|
1
|
+
import {type IMap, Int, isIMap, makeIMap, makeMetaMap, type MetaMap, type RpcValue, RpcValueWithMetaData} from './rpcvalue';
|
|
2
2
|
import {toCpon} from './cpon';
|
|
3
3
|
import {toChainPack} from './chainpack';
|
|
4
4
|
|
|
@@ -30,19 +30,19 @@ enum ErrorCode {
|
|
|
30
30
|
NotImplemented = 12,
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
type ErrorMap =
|
|
33
|
+
export type ErrorMap = {
|
|
34
34
|
[ERROR_CODE]: Int<ErrorCode>;
|
|
35
35
|
[ERROR_MESSAGE]?: string;
|
|
36
36
|
[ERROR_DATA]?: RpcValue;
|
|
37
|
-
}
|
|
37
|
+
};
|
|
38
38
|
|
|
39
39
|
class RpcError extends Error {
|
|
40
40
|
constructor(private readonly err_info: ErrorMap) {
|
|
41
|
-
super(err_info
|
|
41
|
+
super(err_info[ERROR_MESSAGE] ?? 'Unknown RpcError');
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
data() {
|
|
45
|
-
return this.err_info
|
|
45
|
+
return this.err_info[ERROR_DATA];
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -66,12 +66,12 @@ class RpcMessage {
|
|
|
66
66
|
meta: MetaMap;
|
|
67
67
|
constructor(rpc_val?: RpcValue) {
|
|
68
68
|
if (rpc_val === undefined) {
|
|
69
|
-
this.value =
|
|
70
|
-
this.meta =
|
|
69
|
+
this.value = makeIMap({});
|
|
70
|
+
this.meta = makeMetaMap({});
|
|
71
71
|
return;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
if (!(rpc_val instanceof RpcValueWithMetaData && rpc_val.value
|
|
74
|
+
if (!(rpc_val instanceof RpcValueWithMetaData && isIMap(rpc_val.value))) {
|
|
75
75
|
throw new TypeError(`RpcMessage initialized with a non-IMap: ${toCpon(rpc_val)}`);
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -96,58 +96,60 @@ class RpcMessage {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
requestId(): Int | undefined {
|
|
99
|
-
return (this.meta
|
|
99
|
+
return (this.meta[TagRequestId] as Int);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
setRequestId(id: number | Int) {
|
|
103
|
-
this.meta
|
|
103
|
+
this.meta[TagRequestId] = new Int(id);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
callerIds(): RpcValue[] | undefined {
|
|
107
|
-
return this.meta
|
|
107
|
+
return this.meta[TagCallerIds] as RpcValue[];
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
setCallerIds(ids: RpcValue[]) {
|
|
111
|
-
this.meta
|
|
111
|
+
this.meta[TagCallerIds] = ids;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
shvPath(): string | undefined {
|
|
115
|
-
return (this.meta
|
|
115
|
+
return (this.meta[TagShvPath] as string);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
setShvPath(val: string) {
|
|
119
|
-
this.meta
|
|
119
|
+
this.meta[TagShvPath] = val;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
method(): string | undefined {
|
|
123
|
-
return (this.meta
|
|
123
|
+
return (this.meta[TagMethod] as string);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
setMethod(val: string) {
|
|
127
|
-
this.meta
|
|
127
|
+
this.meta[TagMethod] = val;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
params() {
|
|
131
|
-
return this.value
|
|
131
|
+
return this.value[KeyParams];
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
setParams(params: RpcValue) {
|
|
135
|
-
this.value
|
|
135
|
+
this.value[KeyParams] = params;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
resultOrError() {
|
|
139
|
-
if (this.value
|
|
140
|
-
if (!(this.value
|
|
139
|
+
if (Object.hasOwn(this.value, KeyError)) {
|
|
140
|
+
if (!isIMap(this.value[KeyError])) {
|
|
141
141
|
return new ProtocolError('Response had an error, but this error was not a map');
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
const error_map = this.value
|
|
145
|
-
if (error_map
|
|
144
|
+
const error_map = this.value[KeyError];
|
|
145
|
+
if (!(error_map[ERROR_CODE] instanceof Int)) {
|
|
146
146
|
return new ProtocolError('Response had an error, but this error did not contain at least an error code');
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
const code = error_map[ERROR_CODE];
|
|
150
|
+
|
|
149
151
|
const ErrorType = (() => {
|
|
150
|
-
switch (
|
|
152
|
+
switch (code.value) {
|
|
151
153
|
case ErrorCode.InvalidRequest: return InvalidRequest;
|
|
152
154
|
case ErrorCode.MethodNotFound: return MethodNotFound;
|
|
153
155
|
case ErrorCode.InvalidParams: return InvalidParams;
|
|
@@ -160,25 +162,26 @@ class RpcMessage {
|
|
|
160
162
|
case ErrorCode.LoginRequired: return LoginRequired;
|
|
161
163
|
case ErrorCode.UserIDRequired: return UserIDRequired;
|
|
162
164
|
case ErrorCode.NotImplemented: return NotImplemented;
|
|
165
|
+
default: return Unknown;
|
|
163
166
|
}
|
|
164
167
|
})();
|
|
165
168
|
|
|
166
|
-
return new ErrorType(this.value
|
|
169
|
+
return new ErrorType(this.value[KeyError] as unknown as ErrorMap);
|
|
167
170
|
}
|
|
168
171
|
|
|
169
|
-
if (Object.hasOwn(this.value
|
|
170
|
-
return this.value
|
|
172
|
+
if (Object.hasOwn(this.value, KeyResult)) {
|
|
173
|
+
return this.value[KeyResult];
|
|
171
174
|
}
|
|
172
175
|
|
|
173
176
|
return new ProtocolError('Response included neither result nor error');
|
|
174
177
|
}
|
|
175
178
|
|
|
176
179
|
setResult(result: RpcValue) {
|
|
177
|
-
this.value
|
|
180
|
+
this.value[KeyResult] = result;
|
|
178
181
|
}
|
|
179
182
|
|
|
180
183
|
setError(error: string) {
|
|
181
|
-
this.value
|
|
184
|
+
this.value[KeyError] = error;
|
|
182
185
|
}
|
|
183
186
|
|
|
184
187
|
toCpon() {
|
package/src/rpcvalue.ts
CHANGED
|
@@ -71,15 +71,19 @@ const withOffset = (date: Date, utc_offset?: number) => {
|
|
|
71
71
|
};
|
|
72
72
|
export type List = RpcValue[];
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
class ShvMap<T extends ShvMapDefaultType = ShvMapDefaultType> {
|
|
76
|
-
constructor(public value: T = {} as T) {}
|
|
77
|
-
}
|
|
74
|
+
const shvMapType = Symbol('shvMapType');
|
|
78
75
|
|
|
79
|
-
type
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
type IMap<T extends Record<number, RpcValue> = Record<number, any>> = {
|
|
77
|
+
[Key in keyof T]: T[Key];
|
|
78
|
+
} & {
|
|
79
|
+
[shvMapType]: 'imap';
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
type ShvMap<T extends Record<string, RpcValue> = Record<string, any>> = {
|
|
83
|
+
[Key in keyof T]: T[Key];
|
|
84
|
+
} & {
|
|
85
|
+
[shvMapType]: 'map';
|
|
86
|
+
};
|
|
83
87
|
|
|
84
88
|
export type RpcValueType =
|
|
85
89
|
Null |
|
|
@@ -95,12 +99,30 @@ export type RpcValueType =
|
|
|
95
99
|
ShvMap |
|
|
96
100
|
IMap;
|
|
97
101
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
type MetaMap<T extends Record<string | number, RpcValue> = Record<string | number, RpcValue>> = {
|
|
103
|
+
[Key in keyof T]: T[Key];
|
|
104
|
+
} & {
|
|
105
|
+
[shvMapType]: 'metamap';
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const isShvMap = (x: RpcValue): x is ShvMap => typeof x === 'object' && (x as ShvMap)[shvMapType] === 'map';
|
|
109
|
+
|
|
110
|
+
const isIMap = (x: RpcValue): x is IMap => typeof x === 'object' && (x as IMap)[shvMapType] === 'imap';
|
|
111
|
+
|
|
112
|
+
const makeMetaMap = <T extends Record<string | number, RpcValue> = Record<string | number, RpcValue>>(x: T = {} as T): MetaMap<T> => ({
|
|
113
|
+
...x,
|
|
114
|
+
[shvMapType]: 'metamap',
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const makeIMap = <T extends Record<number, RpcValue> = Record<number, RpcValue>>(x: T = {} as T): IMap<T> => ({
|
|
118
|
+
...x,
|
|
119
|
+
[shvMapType]: 'imap',
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const makeMap = <T extends Record<string, RpcValue> = Record<string, RpcValue>>(x: T = {} as T): ShvMap<T> => ({
|
|
123
|
+
...x,
|
|
124
|
+
[shvMapType]: 'map',
|
|
125
|
+
});
|
|
104
126
|
|
|
105
127
|
class RpcValueWithMetaData {
|
|
106
128
|
constructor(public value: RpcValueType, public meta: MetaMap) {}
|
|
@@ -108,4 +130,4 @@ class RpcValueWithMetaData {
|
|
|
108
130
|
|
|
109
131
|
export type RpcValue = RpcValueType | RpcValueWithMetaData;
|
|
110
132
|
|
|
111
|
-
export {Decimal, Double,
|
|
133
|
+
export {shvMapType, Decimal, Double, type IMap, Int, type MetaMap, RpcValueWithMetaData, type ShvMap, UInt, withOffset, makeMap, makeIMap, makeMetaMap, isIMap, isShvMap};
|
package/src/ws-client.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {ChainPackReader, ChainpackProtocolType, ChainPackWriter} from './chainpack';
|
|
2
2
|
import {type CponReader, CponProtocolType} from './cpon';
|
|
3
3
|
import {ERROR_MESSAGE, ErrorCode, ERROR_CODE, RpcMessage, type RpcResponse, MethodCallTimeout} from './rpcmessage';
|
|
4
|
-
import {type RpcValue, type Null, Int, IMap, ShvMap} from './rpcvalue';
|
|
4
|
+
import {type RpcValue, type Null, Int, type IMap, type ShvMap, makeMap, makeIMap} from './rpcvalue';
|
|
5
5
|
|
|
6
6
|
const DEFAULT_TIMEOUT = 5000;
|
|
7
7
|
|
|
@@ -115,14 +115,14 @@ class WsClient {
|
|
|
115
115
|
this.websocket.addEventListener('open', () => {
|
|
116
116
|
this.logDebug('CONNECTED');
|
|
117
117
|
this.callRpcMethod(undefined, 'hello').then(() => {
|
|
118
|
-
const params =
|
|
119
|
-
login:
|
|
118
|
+
const params = makeMap({
|
|
119
|
+
login: makeMap({
|
|
120
120
|
password: this.password,
|
|
121
121
|
type: this.loginType,
|
|
122
122
|
user: this.user,
|
|
123
123
|
}),
|
|
124
|
-
options:
|
|
125
|
-
device: this.mountPoint === 'string' ?
|
|
124
|
+
options: makeMap({
|
|
125
|
+
device: this.mountPoint === 'string' ? makeMap({mountPoint: this.mountPoint}) : undefined,
|
|
126
126
|
}),
|
|
127
127
|
});
|
|
128
128
|
return this.callRpcMethod(undefined, 'login', params);
|
|
@@ -196,7 +196,7 @@ class WsClient {
|
|
|
196
196
|
|
|
197
197
|
const promise = new Promise<RpcResponse>(resolve => {
|
|
198
198
|
this.rpcHandlers[rq_id] = {resolve, timeout_handle: self.setTimeout(() => {
|
|
199
|
-
resolve(new MethodCallTimeout(
|
|
199
|
+
resolve(new MethodCallTimeout(makeIMap({
|
|
200
200
|
[ERROR_CODE]: new Int(ErrorCode.MethodCallTimeout),
|
|
201
201
|
[ERROR_MESSAGE]: `Shv call timeout after: ${this.timeout} msec.`,
|
|
202
202
|
})));
|
|
@@ -236,7 +236,7 @@ class WsClient {
|
|
|
236
236
|
}
|
|
237
237
|
// If this path:method has not been subscribed on the broker, do it now
|
|
238
238
|
if (!this.subscriptions.some(val => val.path === path && val.method === method)) {
|
|
239
|
-
this.callRpcMethod('.broker/app', 'subscribe',
|
|
239
|
+
this.callRpcMethod('.broker/app', 'subscribe', makeMap({
|
|
240
240
|
method, path,
|
|
241
241
|
})).catch(() => {
|
|
242
242
|
this.logDebug(`Couldn't subscribe to ${path}, ${method}`);
|
|
@@ -261,7 +261,7 @@ class WsClient {
|
|
|
261
261
|
if (this.subscriptions.some(val => val.path === path && val.method === method)) {
|
|
262
262
|
return;
|
|
263
263
|
}
|
|
264
|
-
this.callRpcMethod('.broker/app', 'unsubscribe',
|
|
264
|
+
this.callRpcMethod('.broker/app', 'unsubscribe', makeMap({
|
|
265
265
|
method, path,
|
|
266
266
|
})).catch(() => {
|
|
267
267
|
this.logDebug(`Couldn't unsubscribe ${path}, ${method}`);
|
package/src/zod.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {z, type ZodType} from 'zod';
|
|
2
|
-
import {IMap, Int, type RpcValue, ShvMap} from './rpcvalue';
|
|
2
|
+
import {type IMap, Int, isIMap, isShvMap, type RpcValue, type ShvMap} from './rpcvalue';
|
|
3
3
|
|
|
4
|
-
export const map = <T extends Record<string, ZodType<any, any, any>>>(schema: T) => z.custom<ShvMap<{[K in keyof T]: z.infer<T[K]>}>>((data: RpcValue) => data
|
|
5
|
-
export const recmap = <T extends ZodType<any, any, any>>(schema: T) => z.custom<ShvMap<Record<string, z.infer<T>>>>((data: RpcValue) => data
|
|
6
|
-
export const imap = <T extends Record<string, ZodType<any, any, any>>>(schema: T) => z.custom<IMap<{[K in keyof T]: z.infer<T[K]>}>>((data: RpcValue) => data
|
|
4
|
+
export const map = <T extends Record<string, ZodType<any, any, any>>>(schema: T) => z.custom<ShvMap<{[K in keyof T]: z.infer<T[K]>}>>((data: RpcValue) => isShvMap(data) && z.object(schema).safeParse(data).success);
|
|
5
|
+
export const recmap = <T extends ZodType<any, any, any>>(schema: T) => z.custom<ShvMap<Record<string, z.infer<T>>>>((data: RpcValue) => isShvMap(data) && z.record(z.string(), schema).safeParse(data).success);
|
|
6
|
+
export const imap = <T extends Record<string, ZodType<any, any, any>>>(schema: T) => z.custom<IMap<{[K in keyof T]: z.infer<T[K]>}>>((data: RpcValue) => isIMap(data) && z.object(schema).safeParse(data).success);
|
|
7
7
|
export const int = () => z.custom<Int>((data: RpcValue) => data instanceof Int);
|
|
8
8
|
|
|
9
9
|
export * from 'zod';
|