@streamlayer/sdk-web-api 0.18.0 → 0.20.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/lib/grpc/queries/user.d.ts +3 -3
- package/lib/grpc/queries/user.js +3 -8
- package/lib/grpc/subscription.d.ts +24 -6
- package/lib/grpc/subscription.js +99 -30
- package/lib/grpc/transport.d.ts +8 -6
- package/lib/grpc/transport.js +24 -16
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/utils/devtools.js +16 -5
- package/lib/utils/grpc-stub.js +1 -1
- package/package.json +10 -10
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { ReadableAtom } from 'nanostores';
|
|
2
|
-
import type { BypassAuthRequest
|
|
2
|
+
import type { BypassAuthRequest } from '@streamlayer/sl-eslib/users/users_pb';
|
|
3
3
|
import { PlainMessage } from '@bufbuild/protobuf';
|
|
4
4
|
import { Transport } from '../transport';
|
|
5
5
|
export declare const $user: ($userToken: ReadableAtom<string | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<import("@streamlayer/sl-eslib/users/users_pb").MeResponse, any>;
|
|
6
|
-
export declare const
|
|
6
|
+
export declare const bypassLogin: (transport: Transport) => ({ userKey, schema, init }: PlainMessage<BypassAuthRequest>) => Promise<import("@streamlayer/sl-eslib/users/users_pb").BypassAuthResponse>;
|
|
7
7
|
export declare const bypassAuth: (transport: Transport, params: {
|
|
8
8
|
userKey?: string;
|
|
9
9
|
schema?: string;
|
|
10
10
|
init?: boolean;
|
|
11
|
-
}) => Promise<BypassAuthResponse>;
|
|
11
|
+
}) => Promise<import("@streamlayer/sl-eslib/users/users_pb").BypassAuthResponse>;
|
|
12
12
|
export declare const $userSettings: ($userToken: ReadableAtom<string | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<import("@streamlayer/sl-eslib/sdkSettings/client/client_pb").ClientSettings | undefined, any>;
|
|
13
13
|
export declare const register: (transport: Transport, phone: string) => Promise<import("@streamlayer/sl-eslib/users/users_pb").RegisterResponse>;
|
package/lib/grpc/queries/user.js
CHANGED
|
@@ -12,14 +12,9 @@ export const $user = ($userToken, transport) => {
|
|
|
12
12
|
fetcher: () => client.me({}),
|
|
13
13
|
});
|
|
14
14
|
};
|
|
15
|
-
export const
|
|
16
|
-
const { client
|
|
17
|
-
return
|
|
18
|
-
const [updateCache] = getCacheUpdater(queryKeyStr);
|
|
19
|
-
const user = await client.bypassAuth({ userKey, schema, init });
|
|
20
|
-
updateCache(user);
|
|
21
|
-
return user;
|
|
22
|
-
});
|
|
15
|
+
export const bypassLogin = (transport) => {
|
|
16
|
+
const { client } = transport.createPromiseClient(Users, { method: 'bypassAuth' });
|
|
17
|
+
return ({ userKey, schema, init }) => client.bypassAuth({ userKey, schema, init });
|
|
23
18
|
};
|
|
24
19
|
export const bypassAuth = (transport, params) => {
|
|
25
20
|
const { client } = transport.createPromiseClient(Users, { method: 'bypassAuth' });
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PromiseClient } from '@connectrpc/connect';
|
|
2
2
|
import { Atom } from 'nanostores';
|
|
3
3
|
import type { ServiceType, Message, PlainMessage, MethodInfoServerStreaming } from '@bufbuild/protobuf';
|
|
4
4
|
import { Transport } from './transport';
|
|
5
|
+
type StreamPromiseClient<T extends ServiceType> = {
|
|
6
|
+
[P in keyof PromiseClient<T> as T['methods'][P] extends MethodInfoServerStreaming<any, any> ? P : never]: T['methods'][P] extends MethodInfoServerStreaming<any, any> ? PromiseClient<T>[P] : never;
|
|
7
|
+
};
|
|
5
8
|
type StreamMethods<T extends ServiceType> = {
|
|
6
|
-
[P in keyof
|
|
9
|
+
[P in keyof StreamPromiseClient<T>]: P;
|
|
7
10
|
};
|
|
8
|
-
export type StreamMethod<T extends ServiceType> = StreamMethods<T>[keyof StreamMethods<T>] extends keyof
|
|
11
|
+
export type StreamMethod<T extends ServiceType> = StreamMethods<T>[keyof StreamMethods<T>] extends keyof StreamPromiseClient<T> ? StreamMethods<T>[keyof StreamMethods<T>] : never;
|
|
9
12
|
declare enum ServerStreamSubscriptionStatus {
|
|
10
13
|
Init = "init",
|
|
11
14
|
Ready = "ready",
|
|
@@ -13,21 +16,28 @@ declare enum ServerStreamSubscriptionStatus {
|
|
|
13
16
|
Connected = "connected",
|
|
14
17
|
Disconnected = "disconnected",
|
|
15
18
|
Failed = "failed",
|
|
19
|
+
Reconnect = "reconnect",
|
|
16
20
|
Reconnecting = "reconnecting"
|
|
17
21
|
}
|
|
18
22
|
export type ServerStreamSubscriptionOptions = {
|
|
19
23
|
name: string;
|
|
20
24
|
withStore?: boolean;
|
|
25
|
+
reconnectDelay?: number;
|
|
26
|
+
reconnectMaxDelay?: number;
|
|
27
|
+
reconnectMaxAttempts?: number;
|
|
21
28
|
};
|
|
22
|
-
export declare class ServerStreamSubscription<T extends ServiceType, Request extends Message<Request>, Response extends Message<Response>, M extends
|
|
29
|
+
export declare class ServerStreamSubscription<T extends ServiceType, Request extends Message<Request>, Response extends Message<Response>, M extends keyof StreamMethods<T> = keyof StreamMethods<T>, Method extends StreamPromiseClient<T>[M] = StreamPromiseClient<T>[M]> {
|
|
23
30
|
params: Atom<PlainMessage<Request>> | PlainMessage<Request>;
|
|
24
|
-
private
|
|
31
|
+
private streamCancel?;
|
|
25
32
|
private method;
|
|
26
|
-
private
|
|
33
|
+
private options;
|
|
27
34
|
private headers;
|
|
28
35
|
private listeners;
|
|
29
36
|
private state;
|
|
30
37
|
private store?;
|
|
38
|
+
private paramsListener?;
|
|
39
|
+
private reconnectTimeout?;
|
|
40
|
+
private attempt;
|
|
31
41
|
constructor(headers: Transport['$headers'], method: Method, params: Atom<PlainMessage<Request>> | PlainMessage<Request>, options: ServerStreamSubscriptionOptions);
|
|
32
42
|
updateState: (status: ServerStreamSubscriptionStatus) => void;
|
|
33
43
|
addStateLog: (msg: string) => void;
|
|
@@ -35,9 +45,17 @@ export declare class ServerStreamSubscription<T extends ServiceType, Request ext
|
|
|
35
45
|
removeListener: (name: string) => void;
|
|
36
46
|
connect: () => void;
|
|
37
47
|
disconnect: () => void;
|
|
48
|
+
/**
|
|
49
|
+
* Reconnect after delay, if not already reconnecting, otherwise do nothing
|
|
50
|
+
*/
|
|
38
51
|
reconnect: () => void;
|
|
39
52
|
getStore: () => import("nanostores").WritableAtom<Response | null | undefined> | undefined;
|
|
40
53
|
private onData;
|
|
54
|
+
/**
|
|
55
|
+
* Disconnect if error is not instance of ConnectError or stream is Canceled,
|
|
56
|
+
* Reconnect in other cases
|
|
57
|
+
* Do nothing if error is undefined
|
|
58
|
+
*/
|
|
41
59
|
private onStreamError;
|
|
42
60
|
}
|
|
43
61
|
export {};
|
package/lib/grpc/subscription.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MapStore, SingleStore, createMapStore, createSingleStore } from '@streamlayer/sdk-web-interfaces';
|
|
2
|
-
import { Code } from '@connectrpc/connect';
|
|
2
|
+
import { ConnectError, Code } from '@connectrpc/connect';
|
|
3
3
|
var ServerStreamSubscriptionStatus;
|
|
4
4
|
(function (ServerStreamSubscriptionStatus) {
|
|
5
5
|
ServerStreamSubscriptionStatus["Init"] = "init";
|
|
@@ -8,17 +8,21 @@ var ServerStreamSubscriptionStatus;
|
|
|
8
8
|
ServerStreamSubscriptionStatus["Connected"] = "connected";
|
|
9
9
|
ServerStreamSubscriptionStatus["Disconnected"] = "disconnected";
|
|
10
10
|
ServerStreamSubscriptionStatus["Failed"] = "failed";
|
|
11
|
+
ServerStreamSubscriptionStatus["Reconnect"] = "reconnect";
|
|
11
12
|
ServerStreamSubscriptionStatus["Reconnecting"] = "reconnecting";
|
|
12
13
|
})(ServerStreamSubscriptionStatus || (ServerStreamSubscriptionStatus = {}));
|
|
13
14
|
export class ServerStreamSubscription {
|
|
14
15
|
params;
|
|
15
|
-
|
|
16
|
+
streamCancel;
|
|
16
17
|
method;
|
|
17
|
-
|
|
18
|
+
options;
|
|
18
19
|
headers;
|
|
19
20
|
listeners;
|
|
20
21
|
state;
|
|
21
22
|
store;
|
|
23
|
+
paramsListener;
|
|
24
|
+
reconnectTimeout;
|
|
25
|
+
attempt = 0;
|
|
22
26
|
constructor(headers, method, params, options) {
|
|
23
27
|
const initState = {
|
|
24
28
|
status: ServerStreamSubscriptionStatus.Init,
|
|
@@ -26,7 +30,13 @@ export class ServerStreamSubscription {
|
|
|
26
30
|
log: [],
|
|
27
31
|
};
|
|
28
32
|
this.state = new MapStore(createMapStore(initState), `subscription:${options.name}:state`);
|
|
29
|
-
this.
|
|
33
|
+
this.options = {
|
|
34
|
+
...options,
|
|
35
|
+
reconnectDelay: options.reconnectDelay ?? 1000,
|
|
36
|
+
reconnectMaxDelay: options.reconnectMaxDelay ?? 30000,
|
|
37
|
+
reconnectMaxAttempts: options.reconnectMaxAttempts ?? 10,
|
|
38
|
+
withStore: options.withStore ?? false,
|
|
39
|
+
};
|
|
30
40
|
this.headers = headers;
|
|
31
41
|
this.listeners = new Map();
|
|
32
42
|
this.params = params;
|
|
@@ -35,8 +45,11 @@ export class ServerStreamSubscription {
|
|
|
35
45
|
this.store = new SingleStore(createSingleStore(null), `subscription:${options.name}:store`);
|
|
36
46
|
}
|
|
37
47
|
if ('subscribe' in params && typeof params.subscribe === 'function') {
|
|
38
|
-
params.subscribe(() => {
|
|
39
|
-
this.
|
|
48
|
+
this.paramsListener = params.subscribe((newParams) => {
|
|
49
|
+
if (this.state.getValue('status') === ServerStreamSubscriptionStatus.Connected) {
|
|
50
|
+
this.addStateLog(`params updated, reconnect => ${JSON.stringify(newParams)}`);
|
|
51
|
+
this.reconnect();
|
|
52
|
+
}
|
|
40
53
|
});
|
|
41
54
|
}
|
|
42
55
|
this.updateState(ServerStreamSubscriptionStatus.Ready);
|
|
@@ -48,11 +61,14 @@ export class ServerStreamSubscription {
|
|
|
48
61
|
};
|
|
49
62
|
addStateLog = (msg) => {
|
|
50
63
|
const log = this.state.getStore().get()['log'];
|
|
51
|
-
this.
|
|
64
|
+
const newLog = `${this.options.name} ${new Date().toISOString()}: ${msg}`;
|
|
65
|
+
log.push(newLog);
|
|
66
|
+
this.state.setValue('log', log);
|
|
67
|
+
window.sessionStorage.setItem('slstreamlogs', window.sessionStorage.getItem('slstreamlogs') + '\n' + newLog);
|
|
52
68
|
};
|
|
53
69
|
addListener = (name, listener) => {
|
|
54
70
|
if (this.listeners.has(name)) {
|
|
55
|
-
this.addStateLog(`listener '${name}' not added`);
|
|
71
|
+
this.addStateLog(`listener '${name}' not added, already exists`);
|
|
56
72
|
return false;
|
|
57
73
|
}
|
|
58
74
|
this.listeners.set(name, listener);
|
|
@@ -65,50 +81,103 @@ export class ServerStreamSubscription {
|
|
|
65
81
|
};
|
|
66
82
|
connect = () => {
|
|
67
83
|
this.updateState(ServerStreamSubscriptionStatus.Connecting);
|
|
68
|
-
if (this.
|
|
84
|
+
if (this.streamCancel) {
|
|
69
85
|
this.addStateLog(`disconnect prev connection`);
|
|
70
|
-
this.
|
|
86
|
+
this.streamCancel.abort();
|
|
87
|
+
this.streamCancel = undefined;
|
|
71
88
|
}
|
|
72
|
-
const params = 'get' in this.params && typeof this.params.get === 'function' ? this.params.get() : this.params;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
89
|
+
const params = ('get' in this.params && typeof this.params.get === 'function' ? this.params.get() : this.params);
|
|
90
|
+
const fn = async () => {
|
|
91
|
+
this.streamCancel = new AbortController();
|
|
92
|
+
try {
|
|
93
|
+
const options = {
|
|
94
|
+
headers: this.headers.getValues(),
|
|
95
|
+
signal: this.streamCancel.signal,
|
|
96
|
+
};
|
|
97
|
+
const stream = this.method(params, options);
|
|
98
|
+
for await (const res of stream) {
|
|
99
|
+
this.attempt = 0;
|
|
100
|
+
this.onData(res);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
if (err instanceof ConnectError && err.code != Code.Canceled) {
|
|
105
|
+
void this.onStreamError(err);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
void fn();
|
|
78
110
|
this.updateState(ServerStreamSubscriptionStatus.Connected);
|
|
79
111
|
};
|
|
80
112
|
disconnect = () => {
|
|
81
|
-
if (this.
|
|
82
|
-
this.
|
|
113
|
+
if (this.streamCancel) {
|
|
114
|
+
this.streamCancel.abort();
|
|
83
115
|
}
|
|
84
116
|
this.listeners.clear();
|
|
117
|
+
this.paramsListener?.();
|
|
118
|
+
clearTimeout(this.reconnectTimeout);
|
|
119
|
+
this.reconnectTimeout = undefined;
|
|
85
120
|
this.updateState(ServerStreamSubscriptionStatus.Disconnected);
|
|
86
121
|
};
|
|
122
|
+
/**
|
|
123
|
+
* Reconnect after delay, if not already reconnecting, otherwise do nothing
|
|
124
|
+
*/
|
|
87
125
|
reconnect = () => {
|
|
88
|
-
this.
|
|
89
|
-
|
|
126
|
+
if (!this.reconnectTimeout) {
|
|
127
|
+
if (this.attempt < this.options.reconnectMaxAttempts) {
|
|
128
|
+
this.updateState(ServerStreamSubscriptionStatus.Reconnect);
|
|
129
|
+
// https://aws.amazon.com/ru/blogs/architecture/exponential-backoff-and-jitter/
|
|
130
|
+
const backoff = Math.min(this.options.reconnectMaxDelay, Math.pow(2, this.attempt) * this.options.reconnectDelay);
|
|
131
|
+
const delayMs = Math.round((backoff * (1 + Math.random())) / 2);
|
|
132
|
+
this.reconnectTimeout = setTimeout(() => {
|
|
133
|
+
this.attempt++;
|
|
134
|
+
this.updateState(ServerStreamSubscriptionStatus.Reconnecting);
|
|
135
|
+
this.addStateLog(`reconnect attempt ${this.attempt} after ${delayMs}ms`);
|
|
136
|
+
this.connect();
|
|
137
|
+
this.reconnectTimeout = undefined;
|
|
138
|
+
}, delayMs);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
this.addStateLog(`max reconnect attempts reached`);
|
|
142
|
+
this.disconnect();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
this.addStateLog(`already reconnecting`);
|
|
147
|
+
}
|
|
90
148
|
};
|
|
91
149
|
getStore = () => this.store?.getStore();
|
|
92
150
|
onData = (response) => {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
this.store
|
|
151
|
+
try {
|
|
152
|
+
this.addStateLog(`received data => ${JSON.stringify(response)}`);
|
|
153
|
+
if (this.store) {
|
|
154
|
+
this.store.setValue(response);
|
|
155
|
+
}
|
|
156
|
+
for (const [, listener] of this.listeners) {
|
|
157
|
+
listener(response);
|
|
158
|
+
}
|
|
159
|
+
this.addStateLog(`data routed to ${this.listeners.size} listeners`);
|
|
96
160
|
}
|
|
97
|
-
|
|
98
|
-
|
|
161
|
+
catch (err) {
|
|
162
|
+
this.addStateLog(`error process data => ${err}`);
|
|
99
163
|
}
|
|
100
|
-
this.addStateLog(`data routed to ${this.listeners.size} listeners`);
|
|
101
164
|
};
|
|
165
|
+
/**
|
|
166
|
+
* Disconnect if error is not instance of ConnectError or stream is Canceled,
|
|
167
|
+
* Reconnect in other cases
|
|
168
|
+
* Do nothing if error is undefined
|
|
169
|
+
*/
|
|
102
170
|
onStreamError = (error) => {
|
|
171
|
+
this.addStateLog(`error => ${error ? JSON.stringify(error) : 'undefined'}}`);
|
|
103
172
|
if (error === undefined) {
|
|
104
173
|
return;
|
|
105
174
|
}
|
|
106
|
-
if (error
|
|
175
|
+
if (error instanceof ConnectError && error.code !== Code.Canceled) {
|
|
107
176
|
this.updateState(ServerStreamSubscriptionStatus.Failed);
|
|
108
177
|
this.state.setValue('error', error);
|
|
178
|
+
this.reconnect();
|
|
179
|
+
return;
|
|
109
180
|
}
|
|
110
|
-
|
|
111
|
-
this.disconnect();
|
|
112
|
-
}
|
|
181
|
+
this.disconnect();
|
|
113
182
|
};
|
|
114
183
|
}
|
package/lib/grpc/transport.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createRouterTransport, ConnectRouter, Interceptor, PromiseClient,
|
|
1
|
+
import { createRouterTransport, ConnectRouter, Interceptor, PromiseClient, UnaryRequest, StreamRequest } from '@connectrpc/connect';
|
|
2
2
|
import type { ServiceType, Message, PlainMessage } from '@bufbuild/protobuf';
|
|
3
3
|
import { createGrpcWebTransport } from '@connectrpc/connect-web';
|
|
4
4
|
import type { KeyInput } from '@nanostores/query';
|
|
@@ -9,6 +9,7 @@ type KnownHeaders = {
|
|
|
9
9
|
authorization?: string;
|
|
10
10
|
sdk?: string;
|
|
11
11
|
'sl-device-id': string;
|
|
12
|
+
'sl-user-id'?: string;
|
|
12
13
|
} & Record<string, string>;
|
|
13
14
|
export type GrpcTransport = Transport['transport'];
|
|
14
15
|
type ReservedHeaders = 'sdk' | 'authorization';
|
|
@@ -34,6 +35,7 @@ export declare class Transport {
|
|
|
34
35
|
useProtoFieldName: boolean;
|
|
35
36
|
};
|
|
36
37
|
readonly transport: ReturnType<typeof createGrpcWebTransport>;
|
|
38
|
+
readonly streamTransport: ReturnType<typeof createGrpcWebTransport>;
|
|
37
39
|
readonly nanoquery: NanoqueryObjectType;
|
|
38
40
|
readonly host: string;
|
|
39
41
|
protected interceptors: Interceptor[];
|
|
@@ -42,13 +44,13 @@ export declare class Transport {
|
|
|
42
44
|
private callbackClients;
|
|
43
45
|
private subscriptions;
|
|
44
46
|
constructor(host: string);
|
|
45
|
-
addSubscription: <T extends ServiceType, Req extends Message<Req>, Res extends Message<Res>>(method:
|
|
47
|
+
addSubscription: <T extends ServiceType, Req extends Message<Req>, Res extends Message<Res>>(method: { [P in keyof PromiseClient<T> as T["methods"][P] extends import("@bufbuild/protobuf").MethodInfoServerStreaming<any, any> ? P : never]: T["methods"][P] extends import("@bufbuild/protobuf").MethodInfoServerStreaming<any, any> ? PromiseClient<T>[P] : never; }[keyof { [P in keyof PromiseClient<T> as T["methods"][P] extends import("@bufbuild/protobuf").MethodInfoServerStreaming<any, any> ? P : never]: T["methods"][P] extends import("@bufbuild/protobuf").MethodInfoServerStreaming<any, any> ? PromiseClient<T>[P] : never; }], params: PlainMessage<Req> | Atom<PlainMessage<Req>>, options: ServerStreamSubscriptionOptions) => ServerStreamSubscription<ServiceType, Message<import("@bufbuild/protobuf").AnyMessage>, Message<import("@bufbuild/protobuf").AnyMessage>, never, never> | ServerStreamSubscription<T, Req, Res, keyof { [P in keyof PromiseClient<T> as T["methods"][P] extends import("@bufbuild/protobuf").MethodInfoServerStreaming<any, any> ? P : never]: T["methods"][P] extends import("@bufbuild/protobuf").MethodInfoServerStreaming<any, any> ? PromiseClient<T>[P] : never; }, { [P in keyof PromiseClient<T> as T["methods"][P] extends import("@bufbuild/protobuf").MethodInfoServerStreaming<any, any> ? P : never]: T["methods"][P] extends import("@bufbuild/protobuf").MethodInfoServerStreaming<any, any> ? PromiseClient<T>[P] : never; }[keyof { [P in keyof PromiseClient<T> as T["methods"][P] extends import("@bufbuild/protobuf").MethodInfoServerStreaming<any, any> ? P : never]: T["methods"][P] extends import("@bufbuild/protobuf").MethodInfoServerStreaming<any, any> ? PromiseClient<T>[P] : never; }]>;
|
|
46
48
|
removeSubscription: (subscription: ServerStreamSubscription<ServiceType, Message, Message>) => void;
|
|
47
49
|
disconnect: () => void;
|
|
48
50
|
registerInterceptor: (interceptor: Interceptor) => void;
|
|
49
51
|
removeInterceptor: (interceptor: Interceptor) => void;
|
|
50
52
|
getClient: <T extends ServiceType>(service: T) => PromiseClient<T>;
|
|
51
|
-
|
|
53
|
+
getStreamClient: <T extends ServiceType>(service: T) => PromiseClient<T>;
|
|
52
54
|
createPromiseClient: <T extends ServiceType>(service: T, { params, method }: {
|
|
53
55
|
params?: KeyInput | undefined;
|
|
54
56
|
method: keyof T["methods"];
|
|
@@ -57,11 +59,11 @@ export declare class Transport {
|
|
|
57
59
|
queryKey: ((string | number | true) | import("nanostores").ReadableAtom<(string | number | true) | (false | void | null | undefined)> | import("@nanostores/query").FetcherStore<any, any>)[];
|
|
58
60
|
queryKeyStr: string;
|
|
59
61
|
};
|
|
60
|
-
|
|
61
|
-
client:
|
|
62
|
+
createStreamClient: <T extends ServiceType>(service: T) => {
|
|
63
|
+
client: PromiseClient<T>;
|
|
62
64
|
};
|
|
63
65
|
setSdkKey: (sdkKey: string) => void;
|
|
64
|
-
setAuth: (token
|
|
66
|
+
setAuth: (token: string, userId: string) => void;
|
|
65
67
|
setHeader: <T extends string = string>(name: ExcludeReservedHeaders<T>, value: string) => void;
|
|
66
68
|
getHeader: (name: keyof KnownHeaders) => string;
|
|
67
69
|
getHeaders: () => KnownHeaders;
|
package/lib/grpc/transport.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MapStore, createMapStore } from '@streamlayer/sdk-web-interfaces';
|
|
2
|
-
import { createRouterTransport, createPromiseClient,
|
|
2
|
+
import { createRouterTransport, createPromiseClient, } from '@connectrpc/connect';
|
|
3
3
|
import { createGrpcWebTransport } from '@connectrpc/connect-web';
|
|
4
4
|
import { nanoquery } from '@nanostores/query';
|
|
5
5
|
import { __GRPC_DEVTOOLS_EXTENSION__ } from '../utils/devtools';
|
|
@@ -13,7 +13,10 @@ export class Transport {
|
|
|
13
13
|
enumAsInteger: true,
|
|
14
14
|
useProtoFieldName: false,
|
|
15
15
|
};
|
|
16
|
+
// with timeout, using for unary requests
|
|
16
17
|
transport;
|
|
18
|
+
// without timeout, using for server streaming requests
|
|
19
|
+
streamTransport;
|
|
17
20
|
nanoquery;
|
|
18
21
|
host;
|
|
19
22
|
interceptors = [];
|
|
@@ -34,8 +37,12 @@ export class Transport {
|
|
|
34
37
|
this.nanoquery = { createFetcherStore, createMutatorStore, utils };
|
|
35
38
|
this.transport = createGrpcWebTransport({
|
|
36
39
|
baseUrl: host,
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
defaultTimeoutMs: 30000,
|
|
41
|
+
interceptors: this.interceptors,
|
|
42
|
+
useBinaryFormat: true,
|
|
43
|
+
});
|
|
44
|
+
this.streamTransport = createGrpcWebTransport({
|
|
45
|
+
baseUrl: host,
|
|
39
46
|
interceptors: this.interceptors,
|
|
40
47
|
useBinaryFormat: true,
|
|
41
48
|
});
|
|
@@ -61,9 +68,9 @@ export class Transport {
|
|
|
61
68
|
};
|
|
62
69
|
// cleanup subscriptions
|
|
63
70
|
disconnect = () => {
|
|
64
|
-
for (const [
|
|
71
|
+
for (const [params, subscription] of this.subscriptions) {
|
|
65
72
|
subscription.disconnect();
|
|
66
|
-
this.subscriptions.delete(
|
|
73
|
+
this.subscriptions.delete(params);
|
|
67
74
|
}
|
|
68
75
|
};
|
|
69
76
|
registerInterceptor = (interceptor) => {
|
|
@@ -81,13 +88,13 @@ export class Transport {
|
|
|
81
88
|
this.clients.set(serviceName, client);
|
|
82
89
|
return client;
|
|
83
90
|
};
|
|
84
|
-
|
|
85
|
-
const serviceName = service.typeName
|
|
86
|
-
if (this.
|
|
87
|
-
return this.
|
|
91
|
+
getStreamClient = (service) => {
|
|
92
|
+
const serviceName = `stream-${service.typeName}`;
|
|
93
|
+
if (this.clients.has(serviceName)) {
|
|
94
|
+
return this.clients.get(serviceName);
|
|
88
95
|
}
|
|
89
|
-
const client =
|
|
90
|
-
this.
|
|
96
|
+
const client = createPromiseClient(service, this.streamTransport);
|
|
97
|
+
this.clients.set(serviceName, client);
|
|
91
98
|
return client;
|
|
92
99
|
};
|
|
93
100
|
// create unary client, used for query request
|
|
@@ -102,16 +109,17 @@ export class Transport {
|
|
|
102
109
|
const queryKeyWithoutParams = [service.typeName, methodName.charAt(0).toLowerCase() + methodName.slice(1)];
|
|
103
110
|
return { client, queryKey, queryKeyStr: queryKeyWithoutParams.join('') };
|
|
104
111
|
};
|
|
105
|
-
// create
|
|
106
|
-
|
|
107
|
-
const client = this.
|
|
112
|
+
// create promise client, used for server stream subscriptions
|
|
113
|
+
createStreamClient = (service) => {
|
|
114
|
+
const client = this.getStreamClient(service);
|
|
108
115
|
return { client };
|
|
109
116
|
};
|
|
110
117
|
setSdkKey = (sdkKey) => {
|
|
111
118
|
this.$headers.setValue('sdk', sdkKey);
|
|
112
119
|
};
|
|
113
|
-
setAuth = (token) => {
|
|
120
|
+
setAuth = (token, userId) => {
|
|
114
121
|
this.$headers.setValue('authorization', token);
|
|
122
|
+
this.$headers.setValue('sl-user-id', userId);
|
|
115
123
|
};
|
|
116
124
|
setHeader = (name, value) => this.$headers.setValue(name, value);
|
|
117
125
|
getHeader = (name) => this.$headers.getValue(name);
|
|
@@ -145,7 +153,7 @@ export class Transport {
|
|
|
145
153
|
export class MockTransport extends Transport {
|
|
146
154
|
calls;
|
|
147
155
|
constructor(transport) {
|
|
148
|
-
super(process.env.NX_GRPC_HOST || 'https://grpc-
|
|
156
|
+
super(process.env.NX_GRPC_HOST || 'https://grpc-sdk.next.streamlayer.io:443');
|
|
149
157
|
this.calls = [];
|
|
150
158
|
this.interceptors.push((next) => (req) => {
|
|
151
159
|
this.calls.push(req);
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -5,5 +5,6 @@ export const transport = (instance, opts, done) => {
|
|
|
5
5
|
instance.transport = new Transport(opts.host);
|
|
6
6
|
instance.sdk.host = instance.transport.host;
|
|
7
7
|
instance.transport.setSdkKey(opts.sdkKey);
|
|
8
|
+
instance.transport.setHeader('sl-sdk-version', opts.version || '-');
|
|
8
9
|
done();
|
|
9
10
|
};
|
package/lib/utils/devtools.js
CHANGED
|
@@ -16,18 +16,20 @@ async function* logEach(store, stream) {
|
|
|
16
16
|
export const __GRPC_DEVTOOLS_EXTENSION__ = () => (next) => async (request) => {
|
|
17
17
|
const store = {
|
|
18
18
|
name: request.url,
|
|
19
|
-
|
|
19
|
+
stream: request.stream,
|
|
20
|
+
sent_at: Date.now(),
|
|
21
|
+
request: {
|
|
22
|
+
// clone headers to avoid mutating
|
|
23
|
+
header: Object.fromEntries(request.header.entries()),
|
|
24
|
+
},
|
|
20
25
|
response: {},
|
|
21
26
|
};
|
|
22
|
-
store.request.header = Object.fromEntries(request.header.entries());
|
|
23
|
-
store.sent_at = Date.now();
|
|
24
27
|
try {
|
|
25
28
|
const response = await next(request);
|
|
26
29
|
store.received_at = Date.now();
|
|
27
|
-
store.stream = response.stream;
|
|
28
30
|
store.response.header = Object.fromEntries(response.header.entries());
|
|
29
31
|
store.response.trailer = Object.fromEntries(response.trailer.entries());
|
|
30
|
-
if (!
|
|
32
|
+
if (!request.stream) {
|
|
31
33
|
store.request.message = request.message;
|
|
32
34
|
store.response.message = response.message;
|
|
33
35
|
store.latency = store.received_at - store.sent_at;
|
|
@@ -38,6 +40,15 @@ export const __GRPC_DEVTOOLS_EXTENSION__ = () => (next) => async (request) => {
|
|
|
38
40
|
window.postMessage(msg);
|
|
39
41
|
}
|
|
40
42
|
else {
|
|
43
|
+
store.request.message = request.message;
|
|
44
|
+
const msg = {
|
|
45
|
+
type: '__GRPC_DEVTOOLS_EXTENSION__',
|
|
46
|
+
data: {
|
|
47
|
+
...store,
|
|
48
|
+
stream: false,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
window.postMessage(msg);
|
|
41
52
|
return {
|
|
42
53
|
...response,
|
|
43
54
|
message: logEach(store, response.message),
|
package/lib/utils/grpc-stub.js
CHANGED
|
@@ -3,7 +3,7 @@ export class GrpcStub {
|
|
|
3
3
|
transport;
|
|
4
4
|
stubs;
|
|
5
5
|
constructor() {
|
|
6
|
-
this.transport = new Transport(process.env.NX_GRPC_HOST || 'https://grpc-
|
|
6
|
+
this.transport = new Transport(process.env.NX_GRPC_HOST || 'https://grpc-sdk.next.streamlayer.io:443');
|
|
7
7
|
this.stubs = new Map();
|
|
8
8
|
const stubbedUnary = (service, method, signal, timeoutMs, header, input) => {
|
|
9
9
|
const key = `${service.typeName}${method.name}`.toLowerCase();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamlayer/sdk-web-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"typings": "./lib/index.d.ts",
|
|
@@ -21,16 +21,16 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@streamlayer/sdk-web-interfaces": "^0.
|
|
24
|
+
"@streamlayer/sdk-web-interfaces": "^0.20.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@
|
|
28
|
-
"@
|
|
29
|
-
"@connectrpc/connect": "
|
|
30
|
-
"@
|
|
31
|
-
"@
|
|
32
|
-
"@
|
|
33
|
-
"nanostores": "
|
|
34
|
-
"tslib": "
|
|
27
|
+
"@bufbuild/protobuf": "^1.6.0",
|
|
28
|
+
"@connectrpc/connect": "^1.3.0",
|
|
29
|
+
"@connectrpc/connect-web": "^1.3.0",
|
|
30
|
+
"@nanostores/query": "^0.2.8",
|
|
31
|
+
"@streamlayer/sl-eslib": "^5.63.3",
|
|
32
|
+
"@swc/helpers": "^0.5.3",
|
|
33
|
+
"nanostores": "^0.9.5",
|
|
34
|
+
"tslib": "^2.6.2"
|
|
35
35
|
}
|
|
36
36
|
}
|