@streamlayer/sdk-web-api 0.0.1

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/README.md ADDED
@@ -0,0 +1,6 @@
1
+ # core-api
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+ Run `nx build core-api` to build the library.
@@ -0,0 +1,5 @@
1
+ import { ReadableAtom } from 'nanostores';
2
+ import type { StreamSettings } from '@streamlayer/sl-eslib/sdkSettings/sdkSettings.common_pb';
3
+ import { Transport } from '../transport';
4
+ export declare const $retrieveEventId: ($providerStreamId: ReadableAtom<string | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<string | undefined, any>;
5
+ export declare const $streamSettings: (slStreamId: ReadableAtom<string | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<StreamSettings | undefined, any>;
@@ -0,0 +1,38 @@
1
+ import { Events } from '@streamlayer/sl-eslib/sports/events/events_connect';
2
+ import { Client } from '@streamlayer/sl-eslib/sdkSettings/client/client_connect';
3
+ export const $retrieveEventId = ($providerStreamId, transport) => {
4
+ const { client, queryKey } = transport.createPromiseClient(Events, {
5
+ method: 'retrieveEventId',
6
+ params: [$providerStreamId],
7
+ });
8
+ return transport.nanoquery.createFetcherStore(queryKey, {
9
+ fetcher: async (_, __, id) => {
10
+ if (!id || typeof id !== 'string') {
11
+ return '';
12
+ }
13
+ try {
14
+ const res = await client.retrieveEventId({
15
+ id,
16
+ });
17
+ return res.data?.id || '';
18
+ }
19
+ catch (err) {
20
+ return '';
21
+ }
22
+ },
23
+ });
24
+ };
25
+ export const $streamSettings = (slStreamId, transport) => {
26
+ const { client, queryKey } = transport.createPromiseClient(Client, { method: 'getStream', params: [slStreamId] });
27
+ return transport.nanoquery.createFetcherStore(queryKey, {
28
+ fetcher: async (_, __, id) => {
29
+ if (!id) {
30
+ return undefined;
31
+ }
32
+ const res = await client.getStream({
33
+ id: id, // we are sure that id is a string
34
+ });
35
+ return res.data?.attributes;
36
+ },
37
+ });
38
+ };
@@ -0,0 +1,3 @@
1
+ export * from './event';
2
+ export * from './organization';
3
+ export * from './user';
@@ -0,0 +1,3 @@
1
+ export * from './event';
2
+ export * from './organization';
3
+ export * from './user';
@@ -0,0 +1,21 @@
1
+ import { ReadableAtom } from 'nanostores';
2
+ import { Transport } from '../transport';
3
+ export { $user } from './user';
4
+ export declare const $organizationSettings: ($enabled: ReadableAtom<'on' | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<{
5
+ id: string;
6
+ overlays?: import("@streamlayer/sl-eslib/sdkSettings/sdkSettings.common_pb").SdkOverlay[] | undefined;
7
+ buttonIcon?: string | undefined;
8
+ tinodeHost?: string | undefined;
9
+ audience?: string | undefined;
10
+ name?: string | undefined;
11
+ provider?: string | undefined;
12
+ primaryColor?: string | undefined;
13
+ secondaryColor?: string | undefined;
14
+ moderationPrimaryColor?: string | undefined;
15
+ linkShareIcon?: string | undefined;
16
+ linkShareText?: string | undefined;
17
+ brandDefaults?: import("@streamlayer/sl-eslib/sdkSettings/sdkSettings.common_pb").BrandDefaults | undefined;
18
+ pub?: import("@streamlayer/sl-eslib/sdkSettings/sdkSettings.common_pb").JWK | undefined;
19
+ getstream?: import("@streamlayer/sl-eslib/sdkSettings/sdkSettings.common_pb").GetStreamSettingsClient | undefined;
20
+ } | undefined, any>;
21
+ export declare const $organizationAdvertising: ($enabled: ReadableAtom<string | undefined>, transport: Transport) => import("@nanostores/query").FetcherStore<import("@streamlayer/sl-eslib/sdkSettings/sdkSettings.common_pb").Advertising | undefined, any>;
@@ -0,0 +1,28 @@
1
+ import { Client } from '@streamlayer/sl-eslib/sdkSettings/client/client_connect';
2
+ export { $user } from './user';
3
+ export const $organizationSettings = ($enabled, transport) => {
4
+ const { client, queryKey } = transport.createPromiseClient(Client, { method: 'getOrganization', params: [$enabled] });
5
+ return transport.nanoquery.createFetcherStore(queryKey, {
6
+ fetcher: async () => {
7
+ const res = await client.getOrganization({});
8
+ return res.data
9
+ ? {
10
+ ...res.data.attributes,
11
+ id: res.data.id,
12
+ }
13
+ : undefined;
14
+ },
15
+ });
16
+ };
17
+ export const $organizationAdvertising = ($enabled, transport) => {
18
+ const { client, queryKey } = transport.createPromiseClient(Client, {
19
+ method: 'getOrganizationAdvertising',
20
+ params: [$enabled],
21
+ });
22
+ return transport.nanoquery.createFetcherStore(queryKey, {
23
+ fetcher: async () => {
24
+ const res = await client.getOrganizationAdvertising({});
25
+ return res.data?.attributes;
26
+ },
27
+ });
28
+ };
@@ -0,0 +1,13 @@
1
+ import { ReadableAtom } from 'nanostores';
2
+ import type { BypassAuthRequest, BypassAuthResponse } from '@streamlayer/sl-eslib/users/users_pb';
3
+ import { PlainMessage } from '@bufbuild/protobuf';
4
+ import { Transport } from '../transport';
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 $bypassLogin: (transport: Transport) => import("@nanostores/query").MutatorStore<PlainMessage<BypassAuthRequest>, PlainMessage<BypassAuthResponse>, any>;
7
+ export declare const bypassAuth: (transport: Transport, params: {
8
+ userKey?: string;
9
+ schema?: string;
10
+ init?: boolean;
11
+ }) => Promise<BypassAuthResponse>;
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
+ export declare const register: (transport: Transport, phone: string) => Promise<import("@streamlayer/sl-eslib/users/users_pb").RegisterResponse>;
@@ -0,0 +1,41 @@
1
+ import { Users } from '@streamlayer/sl-eslib/users/users_connect';
2
+ import { Client } from '@streamlayer/sl-eslib/sdkSettings/client/client_connect';
3
+ // user query
4
+ // cache user
5
+ // invalidate token
6
+ // login
7
+ // save token
8
+ // called on update user key
9
+ export const $user = ($userToken, transport) => {
10
+ const { queryKey, client } = transport.createPromiseClient(Users, { method: 'me', params: [$userToken] });
11
+ return transport.nanoquery.createFetcherStore(queryKey, {
12
+ fetcher: () => client.me({}),
13
+ });
14
+ };
15
+ export const $bypassLogin = (transport) => {
16
+ const { client, queryKeyStr } = transport.createPromiseClient(Users, { method: 'bypassAuth' });
17
+ return transport.nanoquery.createMutatorStore(async ({ data: { userKey, schema, init }, getCacheUpdater }) => {
18
+ const [updateCache] = getCacheUpdater(queryKeyStr);
19
+ const user = await client.bypassAuth({ userKey, schema, init });
20
+ updateCache(user);
21
+ return user;
22
+ });
23
+ };
24
+ export const bypassAuth = (transport, params) => {
25
+ const { client } = transport.createPromiseClient(Users, { method: 'bypassAuth' });
26
+ return client.bypassAuth(params);
27
+ };
28
+ export const $userSettings = ($userToken, // sl user token
29
+ transport) => {
30
+ const { client, queryKey } = transport.createPromiseClient(Client, { method: 'get', params: [$userToken] });
31
+ return transport.nanoquery.createFetcherStore(queryKey, {
32
+ fetcher: async () => {
33
+ const data = await client.get({});
34
+ return data.data?.attributes;
35
+ },
36
+ });
37
+ };
38
+ export const register = (transport, phone) => {
39
+ const { client } = transport.createPromiseClient(Users, { method: 'register' });
40
+ return client.register({ id: phone });
41
+ };
@@ -0,0 +1,43 @@
1
+ import { CallbackClient } from '@connectrpc/connect';
2
+ import { Atom } from 'nanostores';
3
+ import type { ServiceType, Message, PlainMessage, MethodInfoServerStreaming } from '@bufbuild/protobuf';
4
+ import { Transport } from './transport';
5
+ type StreamMethods<T extends ServiceType> = {
6
+ [P in keyof CallbackClient<T> as T['methods'][P] extends MethodInfoServerStreaming<any, any> ? P : never]: P;
7
+ };
8
+ export type StreamMethod<T extends ServiceType> = StreamMethods<T>[keyof StreamMethods<T>] extends keyof CallbackClient<T> ? StreamMethods<T>[keyof StreamMethods<T>] : never;
9
+ declare enum ServerStreamSubscriptionStatus {
10
+ Init = "init",
11
+ Ready = "ready",
12
+ Connecting = "connecting",
13
+ Connected = "connected",
14
+ Disconnected = "disconnected",
15
+ Failed = "failed",
16
+ Reconnecting = "reconnecting"
17
+ }
18
+ export type ServerStreamSubscriptionOptions = {
19
+ name: string;
20
+ withStore?: boolean;
21
+ };
22
+ export declare class ServerStreamSubscription<T extends ServiceType, Request extends Message<Request>, Response extends Message<Response>, M extends StreamMethod<T> = StreamMethod<T>, Method extends CallbackClient<T>[M] = CallbackClient<T>[M]> {
23
+ params: Atom<PlainMessage<Request>> | PlainMessage<Request>;
24
+ private stream?;
25
+ private method;
26
+ private name;
27
+ private headers;
28
+ private listeners;
29
+ private state;
30
+ private store?;
31
+ constructor(headers: Transport['$headers'], method: Method, params: Atom<PlainMessage<Request>> | PlainMessage<Request>, options: ServerStreamSubscriptionOptions);
32
+ updateState: (status: ServerStreamSubscriptionStatus) => void;
33
+ addStateLog: (msg: string) => void;
34
+ addListener: (name: string, listener: (response: Response) => void) => boolean;
35
+ removeListener: (name: string) => void;
36
+ connect: () => void;
37
+ disconnect: () => void;
38
+ reconnect: () => void;
39
+ getStore: () => import("nanostores").WritableAtom<Response | null | undefined> | undefined;
40
+ private onData;
41
+ private onStreamError;
42
+ }
43
+ export {};
@@ -0,0 +1,114 @@
1
+ import { MapStore, SingleStore, createMapStore, createSingleStore } from '@streamlayer/sdk-web-interfaces';
2
+ import { Code } from '@connectrpc/connect';
3
+ var ServerStreamSubscriptionStatus;
4
+ (function (ServerStreamSubscriptionStatus) {
5
+ ServerStreamSubscriptionStatus["Init"] = "init";
6
+ ServerStreamSubscriptionStatus["Ready"] = "ready";
7
+ ServerStreamSubscriptionStatus["Connecting"] = "connecting";
8
+ ServerStreamSubscriptionStatus["Connected"] = "connected";
9
+ ServerStreamSubscriptionStatus["Disconnected"] = "disconnected";
10
+ ServerStreamSubscriptionStatus["Failed"] = "failed";
11
+ ServerStreamSubscriptionStatus["Reconnecting"] = "reconnecting";
12
+ })(ServerStreamSubscriptionStatus || (ServerStreamSubscriptionStatus = {}));
13
+ export class ServerStreamSubscription {
14
+ params;
15
+ stream;
16
+ method;
17
+ name;
18
+ headers;
19
+ listeners;
20
+ state;
21
+ store;
22
+ constructor(headers, method, params, options) {
23
+ const initState = {
24
+ status: ServerStreamSubscriptionStatus.Init,
25
+ ts: new Date(),
26
+ log: [],
27
+ };
28
+ this.state = new MapStore(createMapStore(initState), `subscription:${options.name}:state`);
29
+ this.name = options.name;
30
+ this.headers = headers;
31
+ this.listeners = new Map();
32
+ this.params = params;
33
+ this.method = method;
34
+ if (options.withStore) {
35
+ this.store = new SingleStore(createSingleStore(null), `subscription:${options.name}:store`);
36
+ }
37
+ if ('subscribe' in params && typeof params.subscribe === 'function') {
38
+ params.subscribe(() => {
39
+ this.reconnect();
40
+ });
41
+ }
42
+ this.updateState(ServerStreamSubscriptionStatus.Ready);
43
+ }
44
+ updateState = (status) => {
45
+ this.state.setValue('status', status);
46
+ this.state.setValue('ts', new Date());
47
+ this.addStateLog(`status => ${status}`);
48
+ };
49
+ addStateLog = (msg) => {
50
+ const log = this.state.getStore().get()['log'];
51
+ this.state.setValue('log', [...log, `${new Date().toLocaleString()}: ${msg}`]);
52
+ };
53
+ addListener = (name, listener) => {
54
+ if (this.listeners.has(name)) {
55
+ this.addStateLog(`listener '${name}' not added`);
56
+ return false;
57
+ }
58
+ this.listeners.set(name, listener);
59
+ this.addStateLog(`listener '${name}' added`);
60
+ return true;
61
+ };
62
+ removeListener = (name) => {
63
+ this.listeners.delete(name);
64
+ this.addStateLog(`listener '${name}' removed`);
65
+ };
66
+ connect = () => {
67
+ this.updateState(ServerStreamSubscriptionStatus.Connecting);
68
+ if (this.stream) {
69
+ this.addStateLog(`disconnect prev connection`);
70
+ this.stream();
71
+ }
72
+ const params = 'get' in this.params && typeof this.params.get === 'function' ? this.params.get() : this.params;
73
+ this.stream = this.method(params,
74
+ // ToDo: fix types
75
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
76
+ // @ts-ignore
77
+ this.onData, this.onStreamError, { headers: this.headers.getValues() });
78
+ this.updateState(ServerStreamSubscriptionStatus.Connected);
79
+ };
80
+ disconnect = () => {
81
+ if (this.stream) {
82
+ this.stream();
83
+ }
84
+ this.listeners.clear();
85
+ this.updateState(ServerStreamSubscriptionStatus.Disconnected);
86
+ };
87
+ reconnect = () => {
88
+ this.updateState(ServerStreamSubscriptionStatus.Reconnecting);
89
+ this.connect();
90
+ };
91
+ getStore = () => this.store?.getStore();
92
+ onData = (response) => {
93
+ this.addStateLog(`received data => ${JSON.stringify(response)}`);
94
+ if (this.store) {
95
+ this.store.setValue(response);
96
+ }
97
+ for (const [, listener] of this.listeners) {
98
+ listener(response);
99
+ }
100
+ this.addStateLog(`data routed to ${this.listeners.size} listeners`);
101
+ };
102
+ onStreamError = (error) => {
103
+ if (error === undefined) {
104
+ return;
105
+ }
106
+ if (error.code !== Code.Canceled && error.rawMessage !== '[canceled] BodyStreamBuffer was aborted') {
107
+ this.updateState(ServerStreamSubscriptionStatus.Failed);
108
+ this.state.setValue('error', error);
109
+ }
110
+ else {
111
+ this.disconnect();
112
+ }
113
+ };
114
+ }
@@ -0,0 +1,75 @@
1
+ import { createRouterTransport, ConnectRouter, Interceptor, PromiseClient, CallbackClient, UnaryRequest, StreamRequest } from '@connectrpc/connect';
2
+ import type { ServiceType, Message, PlainMessage } from '@bufbuild/protobuf';
3
+ import { createGrpcWebTransport } from '@connectrpc/connect-web';
4
+ import type { KeyInput } from '@nanostores/query';
5
+ import { nanoquery } from '@nanostores/query';
6
+ import { Atom } from 'nanostores';
7
+ import { ServerStreamSubscription, type ServerStreamSubscriptionOptions } from './subscription';
8
+ type KnownHeaders = {
9
+ authorization?: string;
10
+ sdk?: string;
11
+ 'sl-device-id': string;
12
+ } & Record<string, string>;
13
+ export type GrpcTransport = Transport['transport'];
14
+ type ReservedHeaders = 'sdk' | 'authorization';
15
+ type ExcludeReservedHeaders<T> = ReservedHeaders extends T ? never : T extends ReservedHeaders ? never : T;
16
+ declare global {
17
+ interface Window {
18
+ __GRPC_DEVTOOLS_EXTENSION__?: () => import('@connectrpc/connect').Interceptor;
19
+ }
20
+ }
21
+ type NanoqueryReturnType = ReturnType<typeof nanoquery>;
22
+ type NanoqueryObjectType = {
23
+ createFetcherStore: NanoqueryReturnType[0];
24
+ createMutatorStore: NanoqueryReturnType[1];
25
+ utils: NanoqueryReturnType[2];
26
+ };
27
+ /**
28
+ * transport wrapper, initialize grpc transport, store headers and connect interceptors
29
+ */
30
+ export declare class Transport {
31
+ toJsonOptions: {
32
+ emitDefaultValues: boolean;
33
+ enumAsInteger: boolean;
34
+ useProtoFieldName: boolean;
35
+ };
36
+ readonly transport: ReturnType<typeof createGrpcWebTransport>;
37
+ readonly nanoquery: NanoqueryObjectType;
38
+ readonly host: string;
39
+ protected interceptors: Interceptor[];
40
+ private readonly $headers;
41
+ private clients;
42
+ private callbackClients;
43
+ private subscriptions;
44
+ constructor(host: string);
45
+ addSubscription: <T extends ServiceType, Req extends Message<Req>, Res extends Message<Res>>(method: CallbackClient<T>[import("./subscription").StreamMethod<T>], 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, import("./subscription").StreamMethod<T>, CallbackClient<T>[import("./subscription").StreamMethod<T>]>;
46
+ removeSubscription: (subscription: ServerStreamSubscription<ServiceType, Message, Message>) => void;
47
+ disconnect: () => void;
48
+ registerInterceptor: (interceptor: Interceptor) => void;
49
+ removeInterceptor: (interceptor: Interceptor) => void;
50
+ getClient: <T extends ServiceType>(service: T) => PromiseClient<T>;
51
+ getCallbackClient: <T extends ServiceType>(service: T) => CallbackClient<T>;
52
+ createPromiseClient: <T extends ServiceType>(service: T, { params, method }: {
53
+ params?: KeyInput | undefined;
54
+ method: keyof T["methods"];
55
+ }) => {
56
+ client: PromiseClient<T>;
57
+ queryKey: ((string | number | true) | import("nanostores").ReadableAtom<(string | number | true) | (false | void | null | undefined)> | import("@nanostores/query").FetcherStore<any, any>)[];
58
+ queryKeyStr: string;
59
+ };
60
+ createCallbackClient: <T extends ServiceType>(service: T) => {
61
+ client: CallbackClient<T>;
62
+ };
63
+ setSdkKey: (sdkKey: string) => void;
64
+ setAuth: (token?: string) => void;
65
+ setHeader: <T extends string = string>(name: ExcludeReservedHeaders<T>, value: string) => void;
66
+ getHeader: (name: keyof KnownHeaders) => string;
67
+ getHeaders: () => KnownHeaders;
68
+ initInterceptors: () => void;
69
+ }
70
+ export declare class MockTransport extends Transport {
71
+ transport: ReturnType<typeof createRouterTransport>;
72
+ calls: Array<UnaryRequest | StreamRequest>;
73
+ constructor(transport: (router: ConnectRouter) => void);
74
+ }
75
+ export {};
@@ -0,0 +1,160 @@
1
+ import { MapStore, createMapStore } from '@streamlayer/sdk-web-interfaces';
2
+ import { createRouterTransport, createPromiseClient, createCallbackClient, } from '@connectrpc/connect';
3
+ import { createGrpcWebTransport } from '@connectrpc/connect-web';
4
+ import { nanoquery } from '@nanostores/query';
5
+ import { __GRPC_DEVTOOLS_EXTENSION__ } from '../utils/devtools';
6
+ import { ServerStreamSubscription } from './subscription';
7
+ /**
8
+ * transport wrapper, initialize grpc transport, store headers and connect interceptors
9
+ */
10
+ export class Transport {
11
+ toJsonOptions = {
12
+ emitDefaultValues: false,
13
+ enumAsInteger: true,
14
+ useProtoFieldName: false,
15
+ };
16
+ transport;
17
+ nanoquery;
18
+ host;
19
+ interceptors = [];
20
+ $headers;
21
+ clients;
22
+ callbackClients;
23
+ subscriptions;
24
+ constructor(host) {
25
+ this.host = host;
26
+ this.$headers = new MapStore(createMapStore({
27
+ ['sl-device-id']: process?.env?.NX_DEVICE_ID || 'sdk-web-dev',
28
+ }), 'transport:headers');
29
+ this.initInterceptors();
30
+ this.clients = new Map();
31
+ this.callbackClients = new Map();
32
+ this.subscriptions = new Map();
33
+ const [createFetcherStore, createMutatorStore, utils] = nanoquery();
34
+ this.nanoquery = { createFetcherStore, createMutatorStore, utils };
35
+ this.transport = createGrpcWebTransport({
36
+ baseUrl: host,
37
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
38
+ // @ts-ignore
39
+ interceptors: this.interceptors,
40
+ useBinaryFormat: true,
41
+ });
42
+ }
43
+ // use shared request params, based on Atom, each new Atom will create new subscription
44
+ // mutate Atom, subscription automatically reconnect with new params
45
+ // if headers changed, reconnect subscription with new headers automatically
46
+ addSubscription = (method, params, options) => {
47
+ const currentSubscription = this.subscriptions.get(params);
48
+ if (currentSubscription) {
49
+ return currentSubscription;
50
+ }
51
+ const subscription = new ServerStreamSubscription(this.$headers, method, params, options);
52
+ // ToDo: fix types
53
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
54
+ // @ts-ignore
55
+ this.subscriptions.set(params, subscription);
56
+ return subscription;
57
+ };
58
+ removeSubscription = (subscription) => {
59
+ subscription.disconnect();
60
+ this.subscriptions.delete(subscription.params);
61
+ };
62
+ // cleanup subscriptions
63
+ disconnect = () => {
64
+ for (const [name, subscription] of this.subscriptions) {
65
+ subscription.disconnect();
66
+ this.subscriptions.delete(name);
67
+ }
68
+ };
69
+ registerInterceptor = (interceptor) => {
70
+ this.interceptors.push(interceptor);
71
+ };
72
+ removeInterceptor = (interceptor) => {
73
+ this.interceptors = this.interceptors.filter((i) => i !== interceptor);
74
+ };
75
+ getClient = (service) => {
76
+ const serviceName = service.typeName;
77
+ if (this.clients.has(serviceName)) {
78
+ return this.clients.get(serviceName);
79
+ }
80
+ const client = createPromiseClient(service, this.transport);
81
+ this.clients.set(serviceName, client);
82
+ return client;
83
+ };
84
+ getCallbackClient = (service) => {
85
+ const serviceName = service.typeName;
86
+ if (this.callbackClients.has(serviceName)) {
87
+ return this.callbackClients.get(serviceName);
88
+ }
89
+ const client = createCallbackClient(service, this.transport);
90
+ this.callbackClients.set(serviceName, client);
91
+ return client;
92
+ };
93
+ // create unary client, used for query request
94
+ createPromiseClient = (service, { params = [], method }) => {
95
+ const client = this.getClient(service);
96
+ const methodName = service.methods[method].name;
97
+ const queryKey = [
98
+ service.typeName,
99
+ methodName.charAt(0).toLowerCase() + methodName.slice(1),
100
+ ...(Array.isArray(params) ? params : [params]),
101
+ ];
102
+ const queryKeyWithoutParams = [service.typeName, methodName.charAt(0).toLowerCase() + methodName.slice(1)];
103
+ return { client, queryKey, queryKeyStr: queryKeyWithoutParams.join('') };
104
+ };
105
+ // create callback client, used for server stream subscriptions
106
+ createCallbackClient = (service) => {
107
+ const client = this.getCallbackClient(service);
108
+ return { client };
109
+ };
110
+ setSdkKey = (sdkKey) => {
111
+ this.$headers.setValue('sdk', sdkKey);
112
+ };
113
+ setAuth = (token) => {
114
+ this.$headers.setValue('authorization', token);
115
+ };
116
+ setHeader = (name, value) => this.$headers.setValue(name, value);
117
+ getHeader = (name) => this.$headers.getValue(name);
118
+ getHeaders = () => this.$headers.getValues();
119
+ initInterceptors = () => {
120
+ if (this.interceptors.length !== 0) {
121
+ return;
122
+ }
123
+ const auth = (next) => (req) => {
124
+ const headers = this.$headers.getValues();
125
+ for (const header in headers) {
126
+ req.header.set(header, headers[header]);
127
+ }
128
+ return next(req);
129
+ };
130
+ this.interceptors.push(auth);
131
+ if (process.env.NODE_ENV !== 'test') {
132
+ this.interceptors.push(__GRPC_DEVTOOLS_EXTENSION__());
133
+ }
134
+ // if (window.__GRPC_DEVTOOLS_EXTENSION__) {
135
+ // this.interceptors.push(window.__GRPC_DEVTOOLS_EXTENSION__())
136
+ // } else {
137
+ // window.addEventListener('grpc_devtools_loaded', () => {
138
+ // if (window.__GRPC_DEVTOOLS_EXTENSION__) {
139
+ // this.interceptors.push(window.__GRPC_DEVTOOLS_EXTENSION__())
140
+ // }
141
+ // })
142
+ // }
143
+ };
144
+ }
145
+ export class MockTransport extends Transport {
146
+ calls;
147
+ constructor(transport) {
148
+ super(process.env.NX_GRPC_HOST || 'https://grpc-web.next.streamlayer.io:443');
149
+ this.calls = [];
150
+ this.interceptors.push((next) => (req) => {
151
+ this.calls.push(req);
152
+ return next(req);
153
+ });
154
+ this.transport = createRouterTransport(transport, {
155
+ transport: {
156
+ interceptors: this.interceptors,
157
+ },
158
+ });
159
+ }
160
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ import { StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
2
+ import { FetcherStore } from '@nanostores/query';
3
+ export type { ServerStreamSubscriptionOptions } from './grpc/subscription';
4
+ export { Transport } from './grpc/transport';
5
+ import { Transport } from './grpc/transport';
6
+ export type { GrpcTransport } from './grpc/transport';
7
+ export * as queries from './grpc/queries';
8
+ export type GetApiResponseType<T extends (...args: any[]) => FetcherStore> = ReturnType<ReturnType<T>['get']>['data'];
9
+ declare module '@streamlayer/sdk-web-interfaces' {
10
+ interface StreamLayerSDK {
11
+ host: string;
12
+ }
13
+ interface StreamLayerContext {
14
+ transport: Transport;
15
+ }
16
+ }
17
+ export declare const transport: (instance: StreamLayerContext, opts: {
18
+ sdkKey: string;
19
+ host: string;
20
+ }, done: () => void) => void;
package/lib/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export { Transport } from './grpc/transport';
2
+ import { Transport } from './grpc/transport';
3
+ export * as queries from './grpc/queries';
4
+ export const transport = (instance, opts, done) => {
5
+ instance.transport = new Transport(opts.host);
6
+ instance.sdk.host = instance.transport.host;
7
+ instance.transport.setSdkKey(opts.sdkKey);
8
+ done();
9
+ };
@@ -0,0 +1 @@
1
+ export declare const __GRPC_DEVTOOLS_EXTENSION__: any;
@@ -0,0 +1,65 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-nocheck
3
+ async function* logEach(store, stream) {
4
+ for await (const m of stream) {
5
+ store.response.message = m;
6
+ store.received_at = Date.now();
7
+ const msg = {
8
+ type: '__GRPC_DEVTOOLS_EXTENSION__',
9
+ data: store,
10
+ };
11
+ window.postMessage(msg);
12
+ yield m;
13
+ }
14
+ }
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ export const __GRPC_DEVTOOLS_EXTENSION__ = () => (next) => async (request) => {
17
+ const store = {
18
+ name: request.url,
19
+ request: {},
20
+ response: {},
21
+ };
22
+ store.request.header = Object.fromEntries(request.header.entries());
23
+ store.sent_at = Date.now();
24
+ try {
25
+ const response = await next(request);
26
+ store.received_at = Date.now();
27
+ store.stream = response.stream;
28
+ store.response.header = Object.fromEntries(response.header.entries());
29
+ store.response.trailer = Object.fromEntries(response.trailer.entries());
30
+ if (!response.stream) {
31
+ store.request.message = request.message;
32
+ store.response.message = response.message;
33
+ store.latency = store.received_at - store.sent_at;
34
+ const msg = {
35
+ type: '__GRPC_DEVTOOLS_EXTENSION__',
36
+ data: store,
37
+ };
38
+ window.postMessage(msg);
39
+ }
40
+ else {
41
+ return {
42
+ ...response,
43
+ message: logEach(store, response.message),
44
+ };
45
+ }
46
+ return response;
47
+ }
48
+ catch (e) {
49
+ store.received_at = Date.now();
50
+ store.request.message = request.message;
51
+ store.response.trailer = {
52
+ ['grpc-status']: e.code,
53
+ ['grpc-message']: e.rawMessage,
54
+ };
55
+ store.response.message = e.rawMessage;
56
+ store.latency = store.received_at - store.sent_at;
57
+ const msg = {
58
+ type: '__GRPC_DEVTOOLS_EXTENSION__',
59
+ data: store,
60
+ };
61
+ window.postMessage(msg);
62
+ throw e;
63
+ }
64
+ };
65
+ window.dispatchEvent(new CustomEvent('grpc_devtools_loaded'));
@@ -0,0 +1,7 @@
1
+ import { Transport } from '..';
2
+ export declare class GrpcStub {
3
+ readonly transport: Transport;
4
+ private stubs;
5
+ constructor();
6
+ stub: (method: string, response: Record<string, unknown>) => void;
7
+ }
@@ -0,0 +1,23 @@
1
+ import { Transport } from '..';
2
+ export class GrpcStub {
3
+ transport;
4
+ stubs;
5
+ constructor() {
6
+ this.transport = new Transport(process.env.NX_GRPC_HOST || 'https://grpc-web.next.streamlayer.io:443');
7
+ this.stubs = new Map();
8
+ const stubbedUnary = (service, method, signal, timeoutMs, header, input) => {
9
+ const key = `${service.typeName}${method.name}`.toLowerCase();
10
+ console.log('grpc unary', { key, input });
11
+ for (const [stubKey, stubValue] of this.stubs) {
12
+ if (stubKey === key) {
13
+ return { message: stubValue };
14
+ }
15
+ }
16
+ throw new Error(JSON.stringify({ message: 'not stubbed correctly', service, method, signal, timeoutMs, header, input }));
17
+ };
18
+ this.transport.transport.unary = stubbedUnary;
19
+ }
20
+ stub = (method, response) => {
21
+ this.stubs.set(method.toLowerCase(), response);
22
+ };
23
+ }
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@streamlayer/sdk-web-api",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "main": "./lib/index.js",
6
+ "typings": "./lib/index.d.ts",
7
+ "files": [
8
+ "lib/",
9
+ "package.json"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "types": "./lib/index.d.ts",
14
+ "import": "./lib/index.js",
15
+ "default": "./lib/index.js"
16
+ },
17
+ "./queries/*": {
18
+ "types": "./lib/grpc/queries/*.d.ts",
19
+ "import": "./lib/grpc/queries/*.js",
20
+ "default": "./lib/grpc/queries/*.js"
21
+ }
22
+ },
23
+ "dependencies": {
24
+ "@streamlayer/sdk-web-interfaces": "^0.17.3"
25
+ },
26
+ "devDependencies": {
27
+ "@swc/helpers": "~0.5.3",
28
+ "@bufbuild/protobuf": "^1.4.2",
29
+ "@connectrpc/connect": "^1.1.3",
30
+ "@connectrpc/connect-web": "^1.1.3",
31
+ "@nanostores/query": "^0.2.8",
32
+ "@streamlayer/sl-eslib": "^5.45.1",
33
+ "nanostores": "^0.9.4",
34
+ "tslib": "^2.6.2"
35
+ }
36
+ }