diff --git a/examples/prebuilt-react-integration/src/main.css b/examples/prebuilt-react-integration/src/main.css index f736378b9a..83b726cacd 100644 --- a/examples/prebuilt-react-integration/src/main.css +++ b/examples/prebuilt-react-integration/src/main.css @@ -1,3 +1,5 @@ +@import url('@100mslive/roomkit-react/index.css'); + html, body, #root { @@ -17,3 +19,7 @@ body { width: 100%; margin: 0; } + +.tl-container { + border-radius: 0.75rem; +} diff --git a/packages/hms-video-store/src/index.ts b/packages/hms-video-store/src/index.ts index 2b1c4f816d..7e3783a783 100644 --- a/packages/hms-video-store/src/index.ts +++ b/packages/hms-video-store/src/index.ts @@ -53,6 +53,7 @@ export type { HMSQuizLeaderboardResponse, HMSQuizLeaderboardSummary, HMSTranscriptionInfo, + HMSICEServer, } from './internal'; export { EventBus } from './events/EventBus'; diff --git a/packages/hms-video-store/src/interfaces/config.ts b/packages/hms-video-store/src/interfaces/config.ts index 6a2f33e370..6246882735 100644 --- a/packages/hms-video-store/src/interfaces/config.ts +++ b/packages/hms-video-store/src/interfaces/config.ts @@ -5,6 +5,13 @@ import InitialSettings from './settings'; * @link https://docs.100ms.live/javascript/v2/features/preview * @link https://docs.100ms.live/javascript/v2/features/join */ + +export type HMSICEServer = { + urls: string[]; + userName?: string; + password?: string; +}; + export interface HMSConfig { /** * the name of the peer, can be later accessed via peer.name and can also be changed mid call. @@ -53,10 +60,14 @@ export interface HMSConfig { */ autoManageVideo?: boolean; /** - * if this flag is enabled, wake lock will be acquired automatically(if supported) when joining the room, so the device + * if this flag is enabled, wake lock will be acquired automatically (if supported) when joining the room, so the device * will be kept awake. */ autoManageWakeLock?: boolean; + /** + * use custom STUN/TURN servers for media connection (advanced) + */ + iceServers?: HMSICEServer[]; } export interface HMSMidCallPreviewConfig { diff --git a/packages/hms-video-store/src/notification-manager/managers/PollsManager.ts b/packages/hms-video-store/src/notification-manager/managers/PollsManager.ts index 94d6820ee1..1db9d4776a 100644 --- a/packages/hms-video-store/src/notification-manager/managers/PollsManager.ts +++ b/packages/hms-video-store/src/notification-manager/managers/PollsManager.ts @@ -88,7 +88,7 @@ export class PollsManager { } this.updatePollResult(savedPoll, updatedPoll); - await this.updatePollResponses(savedPoll, false); + await this.updatePollResponses(savedPoll, true); updatedPolls.push(savedPoll); } diff --git a/packages/hms-video-store/src/sdk/index.ts b/packages/hms-video-store/src/sdk/index.ts index a89e83fa72..19d0ef83af 100644 --- a/packages/hms-video-store/src/sdk/index.ts +++ b/packages/hms-video-store/src/sdk/index.ts @@ -424,6 +424,7 @@ export class HMSSdk implements HMSInterface { this.localPeer!.peerId, { name: config.userName, metaData: config.metaData || '' }, config.autoVideoSubscribe, + config.iceServers, ) .then((initConfig: InitConfig | void) => { initSuccessful = true; @@ -565,6 +566,7 @@ export class HMSSdk implements HMSInterface { { name: config.userName, metaData: config.metaData! }, config.initEndpoint!, config.autoVideoSubscribe, + config.iceServers, ); HMSLogger.d(this.TAG, `✅ Joined room ${roomId}`); this.analyticsTimer.start(TimedEvent.PEER_LIST); diff --git a/packages/hms-video-store/src/session-store/interactivity-center/HMSWhiteboardCenter.ts b/packages/hms-video-store/src/session-store/interactivity-center/HMSWhiteboardCenter.ts index 0e80bc95b7..3fc7479e89 100644 --- a/packages/hms-video-store/src/session-store/interactivity-center/HMSWhiteboardCenter.ts +++ b/packages/hms-video-store/src/session-store/interactivity-center/HMSWhiteboardCenter.ts @@ -78,6 +78,11 @@ export class WhiteboardInteractivityCenter implements HMSWhiteboardInteractivity for (const whiteboard of whiteboards.values()) { if (whiteboard.url) { const response = await this.transport.signal.getWhiteboard({ id: whiteboard.id }); + const localPeer = this.store.getLocalPeer(); + const isOwner = localPeer?.customerUserId === response.owner; + const open = isOwner + ? localPeer.role?.permissions.whiteboard?.includes('admin') + : response.permissions.length > 0; const newWhiteboard: HMSWhiteboard = { ...whiteboard, id: response.id, @@ -86,7 +91,7 @@ export class WhiteboardInteractivityCenter implements HMSWhiteboardInteractivity addr: response.addr, owner: response.owner, permissions: response.permissions, - open: response.permissions.length > 0, + open, }; this.store.setWhiteboard(newWhiteboard); diff --git a/packages/hms-video-store/src/signal/init/index.ts b/packages/hms-video-store/src/signal/init/index.ts index e48260e498..0d2d44612d 100644 --- a/packages/hms-video-store/src/signal/init/index.ts +++ b/packages/hms-video-store/src/signal/init/index.ts @@ -1,6 +1,8 @@ import { InitConfig } from './models'; import { ErrorFactory } from '../../error/ErrorFactory'; import { HMSAction } from '../../error/HMSAction'; +import { HMSICEServer } from '../../interfaces'; +import { transformIceServerConfig } from '../../utils/ice-server-config'; import HMSLogger from '../../utils/logger'; const TAG = '[InitService]'; @@ -26,26 +28,26 @@ export default class InitService { userAgent, initEndpoint = 'https://prod-init.100ms.live', region = '', + iceServers, }: { token: string; peerId: string; userAgent: string; initEndpoint?: string; region?: string; + iceServers?: HMSICEServer[]; }): Promise { HMSLogger.d(TAG, `fetchInitConfig: initEndpoint=${initEndpoint} token=${token} peerId=${peerId} region=${region} `); const url = getUrl(initEndpoint, peerId, userAgent, region); try { const response = await fetch(url, { - headers: { - Authorization: `Bearer ${token}`, - }, + headers: { Authorization: `Bearer ${token}` }, }); try { const config = await response.clone().json(); this.handleError(response, config); HMSLogger.d(TAG, `config is ${JSON.stringify(config, null, 2)}`); - return transformInitConfig(config); + return transformInitConfig(config, iceServers); } catch (err) { const text = await response.text(); HMSLogger.e(TAG, 'json error', (err as Error).message, text); @@ -78,9 +80,12 @@ export function getUrl(endpoint: string, peerId: string, userAgent: string, regi } } -export function transformInitConfig(config: any): InitConfig { +export function transformInitConfig(config: any, iceServers?: HMSICEServer[]): InitConfig { return { ...config, - rtcConfiguration: { ...config.rtcConfiguration, iceServers: config.rtcConfiguration?.ice_servers }, + rtcConfiguration: { + ...config.rtcConfiguration, + iceServers: transformIceServerConfig(config.rtcConfiguration?.ice_servers, iceServers), + }, }; } diff --git a/packages/hms-video-store/src/transport/index.ts b/packages/hms-video-store/src/transport/index.ts index 92b8e581d1..8df8228fde 100644 --- a/packages/hms-video-store/src/transport/index.ts +++ b/packages/hms-video-store/src/transport/index.ts @@ -23,7 +23,7 @@ import { ErrorFactory } from '../error/ErrorFactory'; import { HMSAction } from '../error/HMSAction'; import { HMSException } from '../error/HMSException'; import { EventBus } from '../events/EventBus'; -import { HMSRole } from '../interfaces'; +import { HMSICEServer, HMSRole } from '../interfaces'; import { HMSLocalStream } from '../media/streams/HMSLocalStream'; import { HMSLocalTrack, HMSLocalVideoTrack, HMSTrack } from '../media/tracks'; import { TrackState } from '../notification-manager'; @@ -397,8 +397,9 @@ export default class HMSTransport { peerId: string, customData: { name: string; metaData: string }, autoSubscribeVideo = false, + iceServers?: HMSICEServer[], ): Promise { - const initConfig = await this.connect(token, endpoint, peerId, customData, autoSubscribeVideo); + const initConfig = await this.connect(token, endpoint, peerId, customData, autoSubscribeVideo, iceServers); this.state = TransportState.Preview; this.observer.onStateChange(this.state); return initConfig; @@ -410,11 +411,12 @@ export default class HMSTransport { customData: { name: string; metaData: string }, initEndpoint: string, autoSubscribeVideo = false, + iceServers?: HMSICEServer[], ): Promise { HMSLogger.d(TAG, 'join: started ⏰'); try { if (!this.signal.isConnected || !this.initConfig) { - await this.connect(authToken, initEndpoint, peerId, customData, autoSubscribeVideo); + await this.connect(authToken, initEndpoint, peerId, customData, autoSubscribeVideo, iceServers); } this.validateNotDisconnected('connect'); @@ -447,6 +449,7 @@ export default class HMSTransport { peerId: string, customData: { name: string; metaData: string }, autoSubscribeVideo = false, + iceServers?: HMSICEServer[], ): Promise { this.setTransportStateForConnect(); this.joinParameters = new JoinParameters( @@ -456,9 +459,10 @@ export default class HMSTransport { customData.metaData, endpoint, autoSubscribeVideo, + iceServers, ); try { - const response = await this.internalConnect(token, endpoint, peerId); + const response = await this.internalConnect(token, endpoint, peerId, iceServers); return response; } catch (error) { const shouldRetry = @@ -474,7 +478,7 @@ export default class HMSTransport { if (shouldRetry) { const task = async () => { - await this.internalConnect(token, endpoint, peerId); + await this.internalConnect(token, endpoint, peerId, iceServers); return Boolean(this.initConfig && this.initConfig.endpoint); }; @@ -898,7 +902,7 @@ export default class HMSTransport { } } - private async internalConnect(token: string, initEndpoint: string, peerId: string) { + private async internalConnect(token: string, initEndpoint: string, peerId: string, iceServers?: HMSICEServer[]) { HMSLogger.d(TAG, 'connect: started ⏰'); const connectRequestedAt = new Date(); try { @@ -908,6 +912,7 @@ export default class HMSTransport { peerId, userAgent: this.store.getUserAgent(), initEndpoint, + iceServers, }); const room = this.store.getRoom(); if (room) { @@ -1093,6 +1098,7 @@ export default class HMSTransport { this.joinParameters!.authToken, this.joinParameters!.endpoint, this.joinParameters!.peerId, + this.joinParameters!.iceServers, ); } diff --git a/packages/hms-video-store/src/transport/models/JoinParameters.ts b/packages/hms-video-store/src/transport/models/JoinParameters.ts index b60829abc7..4e71d97146 100644 --- a/packages/hms-video-store/src/transport/models/JoinParameters.ts +++ b/packages/hms-video-store/src/transport/models/JoinParameters.ts @@ -1,3 +1,5 @@ +import { HMSICEServer } from '../../interfaces'; + export class JoinParameters { constructor( public authToken: string, @@ -6,5 +8,6 @@ export class JoinParameters { public data: string = '', public endpoint: string = 'https://prod-init.100ms.live/init', public autoSubscribeVideo: boolean = false, + public iceServers?: HMSICEServer[], ) {} } diff --git a/packages/hms-video-store/src/utils/ice-server-config.ts b/packages/hms-video-store/src/utils/ice-server-config.ts new file mode 100644 index 0000000000..4af82722f1 --- /dev/null +++ b/packages/hms-video-store/src/utils/ice-server-config.ts @@ -0,0 +1,11 @@ +import { HMSICEServer } from '../interfaces'; + +export const transformIceServerConfig = (defaultConfig?: RTCIceServer[], iceServers?: HMSICEServer[]) => { + if (!iceServers || iceServers.length === 0) { + return defaultConfig; + } + const transformedIceServers = iceServers.map(server => { + return { urls: server.urls, credentialType: 'password', credential: server.password, username: server.userName }; + }); + return transformedIceServers; +}; diff --git a/packages/hms-whiteboard/.eslintrc b/packages/hms-whiteboard/.eslintrc new file mode 100644 index 0000000000..3bac8a72fa --- /dev/null +++ b/packages/hms-whiteboard/.eslintrc @@ -0,0 +1,87 @@ +{ + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:import/recommended", + "plugin:import/typescript", + "react-app", + "react-app/jest" + ], + "plugins": ["prettier", "simple-import-sort"], + "rules": { + "@typescript-eslint/ban-ts-comment": 0, + "@typescript-eslint/no-unused-vars": "error", + "no-nested-ternary": 1, + "no-unused-vars": [ + "error", + { + "vars": "all", + "args": "none", + "ignoreRestSiblings": false + } + ], + "react/react-in-jsx-scope": "error", + "react/jsx-curly-brace-presence": [ + 2, + { + "props": "never" + } + ], + "react/self-closing-comp": [ + "error", + { + "component": true, + "html": false + } + ], + "prettier/prettier": [ + "error", + { + "endOfLine": "auto" // This is need to handle different end-of-line in windows/mac + } + ], + "import/first": "error", + "import/namespace": [2, { "allowComputed": true }], + "import/no-duplicates": "error", + "simple-import-sort/imports": [ + "error", + { + "groups": [ + [ + // Packages `react` related packages come first. + "^react", + "^@?\\w", + // Internal packages. + "^@100mslive/react-sdk", + "^@100mslive/react-icons", + // Side effect imports. + "^\\u0000", + + "(components)", + // Other relative imports. Put same-folder imports and `.`. + "^\\./(?=.*/)(?!/?$)", + "^\\.(?!/?$)", + "^\\./?$", + "(plugins)", + "(components)?(.*)(/use.*)", + ".*(hooks)", + "(common)", + "(services)", + "(utils)", + "(constants)", + // Style imports. + "^.+\\.?(css)$" + ] + ] + } + ] + }, + "settings": { + "import/resolver": { + "node": { + "extensions": [".js", ".jsx", ".ts", ".tsx"] + } + } + }, + "ignorePatterns": ["src/*.css", "src/images/*", "src/grpc/*", "dist/**"] +} diff --git a/packages/hms-whiteboard/package.json b/packages/hms-whiteboard/package.json new file mode 100644 index 0000000000..fb67876922 --- /dev/null +++ b/packages/hms-whiteboard/package.json @@ -0,0 +1,61 @@ +{ + "name": "@100mslive/hms-whiteboard", + "author": "100ms", + "license": "MIT", + "version": "0.0.0-alpha.2", + "main": "dist/index.cjs.js", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "exports": { + ".": { + "require": "./dist/index.cjs.js", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./index.css": "./dist/index.css" + }, + "sideEffects": false, + "scripts": { + "prestart": "rm -rf dist", + "start": "rollup -c -w", + "build": "rm -rf dist && NODE_ENV=production rollup -c", + "build:proto": "protoc --ts_opt long_type_string --ts_out ./src/grpc -I=./proto proto/*proto", + "lint": "eslint -c .eslintrc src", + "lint:fix": "eslint -c .eslintrc src --fix", + "format": "prettier -w src/**" + }, + "dependencies": { + "@protobuf-ts/grpcweb-transport": "^2.9.1", + "@protobuf-ts/runtime": "^2.9.1", + "@protobuf-ts/runtime-rpc": "^2.9.1", + "@tldraw/tldraw": "2.0.0-alpha.19" + }, + "peerDependencies": { + "react": ">=17.0.2 <19.0.0", + "react-dom": ">=17.0.2 <19.0.0" + }, + "devDependencies": { + "@protobuf-ts/plugin": "^2.9.1", + "@types/node": "^20.12.5", + "@types/react": "^18.1.0", + "@types/react-dom": "^18.1.0", + "eslint": "^8.53.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.4", + "grpc-tools": "^1.12.4", + "react": "^18.1.0", + "react-dom": "^18.1.0", + "typescript": "^5.2.2", + "@rollup/plugin-commonjs": "^21.0.3", + "@rollup/plugin-node-resolve": "^13.1.3", + "@rollup/plugin-replace": "^5.0.1", + "@rollup/plugin-typescript": "^8.3.1", + "rollup": "^2.70.1", + "rollup-plugin-esbuild": "^4.9.3", + "rollup-plugin-import-css": "^3.5.0", + "rollup-plugin-terser": "^7.0.2" + } +} diff --git a/packages/hms-whiteboard/proto/sessionstore.proto b/packages/hms-whiteboard/proto/sessionstore.proto new file mode 100644 index 0000000000..fc7190cbe8 --- /dev/null +++ b/packages/hms-whiteboard/proto/sessionstore.proto @@ -0,0 +1,129 @@ +syntax = "proto3"; + +package sessionstorepb; + +option go_package = "./internal;sessionstorepb"; + +import "google/protobuf/timestamp.proto"; + +service Api { + rpc Hello(HelloRequest) returns (HelloResponse) {} + rpc Subscribe(SubscribeRequest) returns (stream Event) {} +} + +message HelloRequest { + string name = 1; +} + +message HelloResponse { + string response = 1; +} + +message SubscribeRequest { + string name = 1; + int64 offset = 2; +} + +message Event { + string message = 1; + int64 sequence = 2; +} + +// metadata token -> session id, room id, user id, username + +// open is used for presence + +// change stream will return all keys in order of oldest to newsest. + +// max number of keys -> 5000 +// max size per key -> 10240 Bytes + +service Store { + // open - start listening to updates in keys with provided match patterns + // provide change_id as last received ID to resume updates + rpc open(OpenRequest) returns (stream ChangeStream) {} + + // get last stored value in given key + rpc get(GetRequest) returns (GetResponse) {} + + // set key value + rpc set(SetRequest) returns (SetResponse) {} + + // delete key from store + rpc delete (DeleteRequest) returns (DeleteResponse) {} + + // count get count of keys + rpc count(CountRequest) returns (CountResponse) {} +} + +// define new structure for value based on client needs and add support in +// following message +message Value { + enum Type { + NONE = 0; + BYTES = 1; + STRING = 2; + INTEGER = 3; + FLOAT = 4; + } + Type type = 1; + oneof Data { + int64 number = 2; + float float = 3; + string str = 4; + bytes raw_bytes = 5; + } +} + +message GetRequest { + string key = 1; +} + +message GetResponse { + string key = 1; + string namespace = 2; + Value value = 3; +} + +message DeleteRequest { + string key = 1; +} + +message DeleteResponse {} + +message SetRequest { + string key = 1; + Value value = 3; +} + +message SetResponse {} + +message ChangeStream { + string change_id = 1; + string key = 2; + string namespace = 3; + Value value = 4; + string from_id = 5; +} + +message Select { + oneof match { + string all = 1; // match all keys + string key = 2; // match key + string prefix = 3; // match keys with given prefix + string suffix = 4; // match keys with given suffix + } +} + +message OpenRequest { + // last received change_id for reconnection, "" if first connection + string change_id = 1; + repeated Select select = 3; +} + +message CountRequest {} + +message CountResponse { + int64 count = 1; +} + diff --git a/packages/hms-whiteboard/rollup.config.js b/packages/hms-whiteboard/rollup.config.js new file mode 100644 index 0000000000..80f752c7c1 --- /dev/null +++ b/packages/hms-whiteboard/rollup.config.js @@ -0,0 +1,31 @@ +import resolve from '@rollup/plugin-node-resolve'; +import typescript from '@rollup/plugin-typescript'; +import esbuild from 'rollup-plugin-esbuild'; +import { terser } from 'rollup-plugin-terser'; +import pkg from './package.json'; +import commonjs from '@rollup/plugin-commonjs'; +import css from 'rollup-plugin-import-css'; + +const isProduction = process.env.NODE_ENV === 'production'; + +const deps = Object.keys(pkg.dependencies || {}); +const peerDeps = Object.keys(pkg.peerDependencies || {}); + +const config = { + input: 'src/index.ts', + external: [...deps, ...peerDeps], + output: [ + { file: pkg.main, format: 'cjs', sourcemap: true }, + { dir: 'dist', format: 'esm', preserveModules: true, preserveModulesRoot: 'src', sourcemap: true }, + ], + plugins: [ + commonjs(), + css({ output: 'index.css' }), + esbuild({ format: 'esm' }), + resolve(), + isProduction && terser(), + typescript({ sourceMap: true }), + ], +}; + +export default config; diff --git a/packages/hms-whiteboard/src/ErrorFallback.tsx b/packages/hms-whiteboard/src/ErrorFallback.tsx new file mode 100644 index 0000000000..871a924079 --- /dev/null +++ b/packages/hms-whiteboard/src/ErrorFallback.tsx @@ -0,0 +1,168 @@ +import React, { ComponentType, useEffect, useLayoutEffect, useRef, useState } from 'react'; +import { useValue } from '@tldraw/state'; +import { Editor, hardResetEditor, refreshPage } from '@tldraw/tldraw'; +import classNames from 'classnames'; + +const DISCORD_URL = 'https://discord.gg/pTge2BwDBq'; + +export type TLErrorFallbackComponent = ComponentType<{ + error: unknown; + editor?: Editor; +}>; + +export const ErrorFallback: TLErrorFallbackComponent = ({ error, editor }) => { + const containerRef = useRef(null); + const [shouldShowError, setShouldShowError] = useState(process.env.NODE_ENV !== 'production'); + const [didCopy, setDidCopy] = useState(false); + const [shouldShowResetConfirmation, setShouldShowResetConfirmation] = useState(false); + + const errorMessage = error instanceof Error ? error.message : String(error); + const errorStack = error instanceof Error ? error.stack : null; + + const isDarkModeFromApp = useValue( + 'isDarkMode', + () => { + try { + if (editor) { + return editor.user.isDarkMode; + } + } catch { + // we're in a funky error state so this might not work for spooky + // reasons. if not, we'll have another attempt later: + } + return null; + }, + [editor], + ); + const [isDarkMode, setIsDarkMode] = useState(null); + useLayoutEffect(() => { + // if we found a theme class from the app, we can just use that + if (isDarkModeFromApp !== null) { + setIsDarkMode(isDarkModeFromApp); + } + + // do any of our parents have a theme class? if yes then we can just + // rely on that and don't need to set our own class + let parent = containerRef.current?.parentElement; + let foundParentThemeClass = false; + while (parent) { + if (parent.classList.contains('tl-theme__dark') || parent.classList.contains('tl-theme__light')) { + foundParentThemeClass = true; + break; + } + parent = parent.parentElement; + } + if (foundParentThemeClass) { + setIsDarkMode(null); + return; + } + + // if we can't find a theme class from the app or from a parent, we have + // to fall back on using a media query: + setIsDarkMode(window.matchMedia('(prefers-color-scheme: dark)').matches); + }, [isDarkModeFromApp]); + + useEffect(() => { + if (didCopy) { + const timeout = setTimeout(() => { + setDidCopy(false); + }, 2000); + return () => clearTimeout(timeout); + } + }, [didCopy]); + + const copyError = () => { + const textarea = document.createElement('textarea'); + textarea.value = errorStack ?? errorMessage; + document.body.appendChild(textarea); + textarea.select(); + document.execCommand('copy'); + textarea.remove(); + setDidCopy(true); + }; + + const refresh = () => { + refreshPage(); + }; + + const resetLocalState = async () => { + hardResetEditor(); + }; + + return ( +
+
+ {/* {editor && ( + // opportunistically attempt to render the canvas to reassure + // the user that their document is still there. there's a good + // chance this won't work (ie the error that we're currently + // notifying the user about originates in the canvas) so it's + // not a big deal if it doesn't work - in that case we just have + // a plain grey background. + null}> + +
+ +
+
+
+ )} */} +
+ {shouldShowResetConfirmation ? ( + <> +

Are you sure?

+

Resetting your data will delete your drawing and cannot be undone.

+
+ + +
+ + ) : ( + <> +

Something's gone wrong.

+

+ Sorry, we encountered an error. Please refresh the page to continue. If you keep seeing this error, you + can ask for help on Discord. +

+ {shouldShowError && ( +
+
+                  {errorStack ?? errorMessage}
+                
+ +
+ )} +
+ +
+ + +
+
+ + )} +
+
+ ); +}; diff --git a/packages/hms-whiteboard/src/Whiteboard.tsx b/packages/hms-whiteboard/src/Whiteboard.tsx new file mode 100644 index 0000000000..c47d58f22e --- /dev/null +++ b/packages/hms-whiteboard/src/Whiteboard.tsx @@ -0,0 +1,41 @@ +import React, { useState } from 'react'; +import { Editor, Tldraw } from '@tldraw/tldraw'; +import { ErrorFallback } from './ErrorFallback'; +import { useCollaboration } from './hooks/useCollaboration'; +import './index.css'; + +export interface WhiteboardProps { + endpoint?: string; + token?: string; + zoomToContent?: boolean; + transparentCanvas?: boolean; + onMount?: (args: { store?: unknown; editor?: unknown }) => void; +} +export function Whiteboard({ onMount, endpoint, token, zoomToContent, transparentCanvas }: WhiteboardProps) { + const [editor, setEditor] = useState(); + const store = useCollaboration({ + endpoint, + token, + editor, + zoomToContent, + }); + + const handleMount = (editor: Editor) => { + setEditor(editor); + // @ts-expect-error - for debugging + window.editor = editor; + onMount?.({ store: store.store, editor }); + }; + + return ( + + ); +} diff --git a/packages/hms-whiteboard/src/grpc/sessionstore.client.ts b/packages/hms-whiteboard/src/grpc/sessionstore.client.ts new file mode 100644 index 0000000000..d5a584987b --- /dev/null +++ b/packages/hms-whiteboard/src/grpc/sessionstore.client.ts @@ -0,0 +1,174 @@ +// @generated by protobuf-ts 2.9.4 with parameter long_type_string +// @generated from protobuf file "sessionstore.proto" (package "sessionstorepb", syntax proto3) +// tslint:disable +import type { RpcOptions, RpcTransport, ServerStreamingCall, ServiceInfo, UnaryCall } from '@protobuf-ts/runtime-rpc'; +import { stackIntercept } from '@protobuf-ts/runtime-rpc'; +import type { + ChangeStream, + CountRequest, + CountResponse, + DeleteRequest, + DeleteResponse, + Event, + GetRequest, + GetResponse, + HelloRequest, + HelloResponse, + OpenRequest, + SetRequest, + SetResponse, + SubscribeRequest, +} from './sessionstore'; +import { Api, Store } from './sessionstore'; +/** + * @generated from protobuf service sessionstorepb.Api + */ +export interface IApiClient { + /** + * @generated from protobuf rpc: Hello(sessionstorepb.HelloRequest) returns (sessionstorepb.HelloResponse); + */ + hello(input: HelloRequest, options?: RpcOptions): UnaryCall; + /** + * @generated from protobuf rpc: Subscribe(sessionstorepb.SubscribeRequest) returns (stream sessionstorepb.Event); + */ + subscribe(input: SubscribeRequest, options?: RpcOptions): ServerStreamingCall; +} +/** + * @generated from protobuf service sessionstorepb.Api + */ +export class ApiClient implements IApiClient, ServiceInfo { + typeName = Api.typeName; + methods = Api.methods; + options = Api.options; + constructor(private readonly _transport: RpcTransport) {} + /** + * @generated from protobuf rpc: Hello(sessionstorepb.HelloRequest) returns (sessionstorepb.HelloResponse); + */ + hello(input: HelloRequest, options?: RpcOptions): UnaryCall { + const method = this.methods[0], + opt = this._transport.mergeOptions(options); + return stackIntercept('unary', this._transport, method, opt, input); + } + /** + * @generated from protobuf rpc: Subscribe(sessionstorepb.SubscribeRequest) returns (stream sessionstorepb.Event); + */ + subscribe(input: SubscribeRequest, options?: RpcOptions): ServerStreamingCall { + const method = this.methods[1], + opt = this._transport.mergeOptions(options); + return stackIntercept('serverStreaming', this._transport, method, opt, input); + } +} +// metadata token -> session id, room id, user id, username + +// open is used for presence + +// change stream will return all keys in order of oldest to newsest. + +// max number of keys -> 5000 +// max size per key -> 10240 Bytes + +/** + * @generated from protobuf service sessionstorepb.Store + */ +export interface IStoreClient { + /** + * open - start listening to updates in keys with provided match patterns + * provide change_id as last received ID to resume updates + * + * @generated from protobuf rpc: open(sessionstorepb.OpenRequest) returns (stream sessionstorepb.ChangeStream); + */ + open(input: OpenRequest, options?: RpcOptions): ServerStreamingCall; + /** + * get last stored value in given key + * + * @generated from protobuf rpc: get(sessionstorepb.GetRequest) returns (sessionstorepb.GetResponse); + */ + get(input: GetRequest, options?: RpcOptions): UnaryCall; + /** + * set key value + * + * @generated from protobuf rpc: set(sessionstorepb.SetRequest) returns (sessionstorepb.SetResponse); + */ + set(input: SetRequest, options?: RpcOptions): UnaryCall; + /** + * delete key from store + * + * @generated from protobuf rpc: delete(sessionstorepb.DeleteRequest) returns (sessionstorepb.DeleteResponse); + */ + delete(input: DeleteRequest, options?: RpcOptions): UnaryCall; + /** + * count get count of keys + * + * @generated from protobuf rpc: count(sessionstorepb.CountRequest) returns (sessionstorepb.CountResponse); + */ + count(input: CountRequest, options?: RpcOptions): UnaryCall; +} +// metadata token -> session id, room id, user id, username + +// open is used for presence + +// change stream will return all keys in order of oldest to newsest. + +// max number of keys -> 5000 +// max size per key -> 10240 Bytes + +/** + * @generated from protobuf service sessionstorepb.Store + */ +export class StoreClient implements IStoreClient, ServiceInfo { + typeName = Store.typeName; + methods = Store.methods; + options = Store.options; + constructor(private readonly _transport: RpcTransport) {} + /** + * open - start listening to updates in keys with provided match patterns + * provide change_id as last received ID to resume updates + * + * @generated from protobuf rpc: open(sessionstorepb.OpenRequest) returns (stream sessionstorepb.ChangeStream); + */ + open(input: OpenRequest, options?: RpcOptions): ServerStreamingCall { + const method = this.methods[0], + opt = this._transport.mergeOptions(options); + return stackIntercept('serverStreaming', this._transport, method, opt, input); + } + /** + * get last stored value in given key + * + * @generated from protobuf rpc: get(sessionstorepb.GetRequest) returns (sessionstorepb.GetResponse); + */ + get(input: GetRequest, options?: RpcOptions): UnaryCall { + const method = this.methods[1], + opt = this._transport.mergeOptions(options); + return stackIntercept('unary', this._transport, method, opt, input); + } + /** + * set key value + * + * @generated from protobuf rpc: set(sessionstorepb.SetRequest) returns (sessionstorepb.SetResponse); + */ + set(input: SetRequest, options?: RpcOptions): UnaryCall { + const method = this.methods[2], + opt = this._transport.mergeOptions(options); + return stackIntercept('unary', this._transport, method, opt, input); + } + /** + * delete key from store + * + * @generated from protobuf rpc: delete(sessionstorepb.DeleteRequest) returns (sessionstorepb.DeleteResponse); + */ + delete(input: DeleteRequest, options?: RpcOptions): UnaryCall { + const method = this.methods[3], + opt = this._transport.mergeOptions(options); + return stackIntercept('unary', this._transport, method, opt, input); + } + /** + * count get count of keys + * + * @generated from protobuf rpc: count(sessionstorepb.CountRequest) returns (sessionstorepb.CountResponse); + */ + count(input: CountRequest, options?: RpcOptions): UnaryCall { + const method = this.methods[4], + opt = this._transport.mergeOptions(options); + return stackIntercept('unary', this._transport, method, opt, input); + } +} diff --git a/packages/hms-whiteboard/src/grpc/sessionstore.ts b/packages/hms-whiteboard/src/grpc/sessionstore.ts new file mode 100644 index 0000000000..62c29f3d98 --- /dev/null +++ b/packages/hms-whiteboard/src/grpc/sessionstore.ts @@ -0,0 +1,1128 @@ +// @generated by protobuf-ts 2.9.4 with parameter long_type_string +// @generated from protobuf file "sessionstore.proto" (package "sessionstorepb", syntax proto3) +// tslint:disable +import type { + BinaryReadOptions, + BinaryWriteOptions, + IBinaryReader, + IBinaryWriter, + PartialMessage, +} from '@protobuf-ts/runtime'; +import { MessageType, UnknownFieldHandler, WireType, reflectionMergePartial } from '@protobuf-ts/runtime'; +import { ServiceType } from '@protobuf-ts/runtime-rpc'; +/** + * @generated from protobuf message sessionstorepb.HelloRequest + */ +export interface HelloRequest { + /** + * @generated from protobuf field: string name = 1; + */ + name: string; +} +/** + * @generated from protobuf message sessionstorepb.HelloResponse + */ +export interface HelloResponse { + /** + * @generated from protobuf field: string response = 1; + */ + response: string; +} +/** + * @generated from protobuf message sessionstorepb.SubscribeRequest + */ +export interface SubscribeRequest { + /** + * @generated from protobuf field: string name = 1; + */ + name: string; + /** + * @generated from protobuf field: int64 offset = 2; + */ + offset: string; +} +/** + * @generated from protobuf message sessionstorepb.Event + */ +export interface Event { + /** + * @generated from protobuf field: string message = 1; + */ + message: string; + /** + * @generated from protobuf field: int64 sequence = 2; + */ + sequence: string; +} +/** + * define new structure for value based on client needs and add support in + * following message + * + * @generated from protobuf message sessionstorepb.Value + */ +export interface Value { + /** + * @generated from protobuf field: sessionstorepb.Value.Type type = 1; + */ + type: Value_Type; + /** + * @generated from protobuf oneof: Data + */ + data: + | { + oneofKind: 'number'; + /** + * @generated from protobuf field: int64 number = 2; + */ + number: string; + } + | { + oneofKind: 'float'; + /** + * @generated from protobuf field: float float = 3; + */ + float: number; + } + | { + oneofKind: 'str'; + /** + * @generated from protobuf field: string str = 4; + */ + str: string; + } + | { + oneofKind: 'rawBytes'; + /** + * @generated from protobuf field: bytes raw_bytes = 5; + */ + rawBytes: Uint8Array; + } + | { + oneofKind: undefined; + }; +} +/** + * @generated from protobuf enum sessionstorepb.Value.Type + */ +export enum Value_Type { + /** + * @generated from protobuf enum value: NONE = 0; + */ + NONE = 0, + /** + * @generated from protobuf enum value: BYTES = 1; + */ + BYTES = 1, + /** + * @generated from protobuf enum value: STRING = 2; + */ + STRING = 2, + /** + * @generated from protobuf enum value: INTEGER = 3; + */ + INTEGER = 3, + /** + * @generated from protobuf enum value: FLOAT = 4; + */ + FLOAT = 4, +} +/** + * @generated from protobuf message sessionstorepb.GetRequest + */ +export interface GetRequest { + /** + * @generated from protobuf field: string key = 1; + */ + key: string; +} +/** + * @generated from protobuf message sessionstorepb.GetResponse + */ +export interface GetResponse { + /** + * @generated from protobuf field: string key = 1; + */ + key: string; + /** + * @generated from protobuf field: string namespace = 2; + */ + namespace: string; + /** + * @generated from protobuf field: sessionstorepb.Value value = 3; + */ + value?: Value; +} +/** + * @generated from protobuf message sessionstorepb.DeleteRequest + */ +export interface DeleteRequest { + /** + * @generated from protobuf field: string key = 1; + */ + key: string; +} +/** + * @generated from protobuf message sessionstorepb.DeleteResponse + */ +export interface DeleteResponse {} +/** + * @generated from protobuf message sessionstorepb.SetRequest + */ +export interface SetRequest { + /** + * @generated from protobuf field: string key = 1; + */ + key: string; + /** + * @generated from protobuf field: sessionstorepb.Value value = 3; + */ + value?: Value; +} +/** + * @generated from protobuf message sessionstorepb.SetResponse + */ +export interface SetResponse {} +/** + * @generated from protobuf message sessionstorepb.ChangeStream + */ +export interface ChangeStream { + /** + * @generated from protobuf field: string change_id = 1; + */ + changeId: string; + /** + * @generated from protobuf field: string key = 2; + */ + key: string; + /** + * @generated from protobuf field: string namespace = 3; + */ + namespace: string; + /** + * @generated from protobuf field: sessionstorepb.Value value = 4; + */ + value?: Value; + /** + * @generated from protobuf field: string from_id = 5; + */ + fromId: string; +} +/** + * @generated from protobuf message sessionstorepb.Select + */ +export interface Select { + /** + * @generated from protobuf oneof: match + */ + match: + | { + oneofKind: 'all'; + /** + * @generated from protobuf field: string all = 1; + */ + all: string; // match all keys + } + | { + oneofKind: 'key'; + /** + * @generated from protobuf field: string key = 2; + */ + key: string; // match key + } + | { + oneofKind: 'prefix'; + /** + * @generated from protobuf field: string prefix = 3; + */ + prefix: string; // match keys with given prefix + } + | { + oneofKind: 'suffix'; + /** + * @generated from protobuf field: string suffix = 4; + */ + suffix: string; // match keys with given suffix + } + | { + oneofKind: undefined; + }; +} +/** + * @generated from protobuf message sessionstorepb.OpenRequest + */ +export interface OpenRequest { + /** + * last received change_id for reconnection, "" if first connection + * + * @generated from protobuf field: string change_id = 1; + */ + changeId: string; + /** + * @generated from protobuf field: repeated sessionstorepb.Select select = 3; + */ + select: Select[]; +} +/** + * @generated from protobuf message sessionstorepb.CountRequest + */ +export interface CountRequest {} +/** + * @generated from protobuf message sessionstorepb.CountResponse + */ +export interface CountResponse { + /** + * @generated from protobuf field: int64 count = 1; + */ + count: string; +} +// @generated message type with reflection information, may provide speed optimized methods +class HelloRequest$Type extends MessageType { + constructor() { + super('sessionstorepb.HelloRequest', [{ no: 1, name: 'name', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }]); + } + create(value?: PartialMessage): HelloRequest { + const message = globalThis.Object.create(this.messagePrototype!); + message.name = ''; + if (value !== undefined) reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead( + reader: IBinaryReader, + length: number, + options: BinaryReadOptions, + target?: HelloRequest, + ): HelloRequest { + let message = target ?? this.create(), + end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string name */ 1: + message.name = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === 'throw') + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: HelloRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string name = 1; */ + if (message.name !== '') writer.tag(1, WireType.LengthDelimited).string(message.name); + let u = options.writeUnknownFields; + if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message sessionstorepb.HelloRequest + */ +export const HelloRequest = new HelloRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class HelloResponse$Type extends MessageType { + constructor() { + super('sessionstorepb.HelloResponse', [{ no: 1, name: 'response', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }]); + } + create(value?: PartialMessage): HelloResponse { + const message = globalThis.Object.create(this.messagePrototype!); + message.response = ''; + if (value !== undefined) reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead( + reader: IBinaryReader, + length: number, + options: BinaryReadOptions, + target?: HelloResponse, + ): HelloResponse { + let message = target ?? this.create(), + end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string response */ 1: + message.response = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === 'throw') + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: HelloResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string response = 1; */ + if (message.response !== '') writer.tag(1, WireType.LengthDelimited).string(message.response); + let u = options.writeUnknownFields; + if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message sessionstorepb.HelloResponse + */ +export const HelloResponse = new HelloResponse$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class SubscribeRequest$Type extends MessageType { + constructor() { + super('sessionstorepb.SubscribeRequest', [ + { no: 1, name: 'name', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: 'offset', kind: 'scalar', T: 3 /*ScalarType.INT64*/ }, + ]); + } + create(value?: PartialMessage): SubscribeRequest { + const message = globalThis.Object.create(this.messagePrototype!); + message.name = ''; + message.offset = '0'; + if (value !== undefined) reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead( + reader: IBinaryReader, + length: number, + options: BinaryReadOptions, + target?: SubscribeRequest, + ): SubscribeRequest { + let message = target ?? this.create(), + end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string name */ 1: + message.name = reader.string(); + break; + case /* int64 offset */ 2: + message.offset = reader.int64().toString(); + break; + default: + let u = options.readUnknownField; + if (u === 'throw') + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: SubscribeRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string name = 1; */ + if (message.name !== '') writer.tag(1, WireType.LengthDelimited).string(message.name); + /* int64 offset = 2; */ + if (message.offset !== '0') writer.tag(2, WireType.Varint).int64(message.offset); + let u = options.writeUnknownFields; + if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message sessionstorepb.SubscribeRequest + */ +export const SubscribeRequest = new SubscribeRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class Event$Type extends MessageType { + constructor() { + super('sessionstorepb.Event', [ + { no: 1, name: 'message', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: 'sequence', kind: 'scalar', T: 3 /*ScalarType.INT64*/ }, + ]); + } + create(value?: PartialMessage): Event { + const message = globalThis.Object.create(this.messagePrototype!); + message.message = ''; + message.sequence = '0'; + if (value !== undefined) reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Event): Event { + let message = target ?? this.create(), + end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string message */ 1: + message.message = reader.string(); + break; + case /* int64 sequence */ 2: + message.sequence = reader.int64().toString(); + break; + default: + let u = options.readUnknownField; + if (u === 'throw') + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: Event, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string message = 1; */ + if (message.message !== '') writer.tag(1, WireType.LengthDelimited).string(message.message); + /* int64 sequence = 2; */ + if (message.sequence !== '0') writer.tag(2, WireType.Varint).int64(message.sequence); + let u = options.writeUnknownFields; + if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message sessionstorepb.Event + */ +export const Event = new Event$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class Value$Type extends MessageType { + constructor() { + super('sessionstorepb.Value', [ + { no: 1, name: 'type', kind: 'enum', T: () => ['sessionstorepb.Value.Type', Value_Type] }, + { no: 2, name: 'number', kind: 'scalar', oneof: 'data', T: 3 /*ScalarType.INT64*/ }, + { no: 3, name: 'float', kind: 'scalar', oneof: 'data', T: 2 /*ScalarType.FLOAT*/ }, + { no: 4, name: 'str', kind: 'scalar', oneof: 'data', T: 9 /*ScalarType.STRING*/ }, + { no: 5, name: 'raw_bytes', kind: 'scalar', oneof: 'data', T: 12 /*ScalarType.BYTES*/ }, + ]); + } + create(value?: PartialMessage): Value { + const message = globalThis.Object.create(this.messagePrototype!); + message.type = 0; + message.data = { oneofKind: undefined }; + if (value !== undefined) reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Value): Value { + let message = target ?? this.create(), + end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* sessionstorepb.Value.Type type */ 1: + message.type = reader.int32(); + break; + case /* int64 number */ 2: + message.data = { + oneofKind: 'number', + number: reader.int64().toString(), + }; + break; + case /* float float */ 3: + message.data = { + oneofKind: 'float', + float: reader.float(), + }; + break; + case /* string str */ 4: + message.data = { + oneofKind: 'str', + str: reader.string(), + }; + break; + case /* bytes raw_bytes */ 5: + message.data = { + oneofKind: 'rawBytes', + rawBytes: reader.bytes(), + }; + break; + default: + let u = options.readUnknownField; + if (u === 'throw') + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: Value, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* sessionstorepb.Value.Type type = 1; */ + if (message.type !== 0) writer.tag(1, WireType.Varint).int32(message.type); + /* int64 number = 2; */ + if (message.data.oneofKind === 'number') writer.tag(2, WireType.Varint).int64(message.data.number); + /* float float = 3; */ + if (message.data.oneofKind === 'float') writer.tag(3, WireType.Bit32).float(message.data.float); + /* string str = 4; */ + if (message.data.oneofKind === 'str') writer.tag(4, WireType.LengthDelimited).string(message.data.str); + /* bytes raw_bytes = 5; */ + if (message.data.oneofKind === 'rawBytes') writer.tag(5, WireType.LengthDelimited).bytes(message.data.rawBytes); + let u = options.writeUnknownFields; + if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message sessionstorepb.Value + */ +export const Value = new Value$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class GetRequest$Type extends MessageType { + constructor() { + super('sessionstorepb.GetRequest', [{ no: 1, name: 'key', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }]); + } + create(value?: PartialMessage): GetRequest { + const message = globalThis.Object.create(this.messagePrototype!); + message.key = ''; + if (value !== undefined) reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead( + reader: IBinaryReader, + length: number, + options: BinaryReadOptions, + target?: GetRequest, + ): GetRequest { + let message = target ?? this.create(), + end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string key */ 1: + message.key = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === 'throw') + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: GetRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string key = 1; */ + if (message.key !== '') writer.tag(1, WireType.LengthDelimited).string(message.key); + let u = options.writeUnknownFields; + if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message sessionstorepb.GetRequest + */ +export const GetRequest = new GetRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class GetResponse$Type extends MessageType { + constructor() { + super('sessionstorepb.GetResponse', [ + { no: 1, name: 'key', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: 'namespace', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: 'value', kind: 'message', T: () => Value }, + ]); + } + create(value?: PartialMessage): GetResponse { + const message = globalThis.Object.create(this.messagePrototype!); + message.key = ''; + message.namespace = ''; + if (value !== undefined) reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead( + reader: IBinaryReader, + length: number, + options: BinaryReadOptions, + target?: GetResponse, + ): GetResponse { + let message = target ?? this.create(), + end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string key */ 1: + message.key = reader.string(); + break; + case /* string namespace */ 2: + message.namespace = reader.string(); + break; + case /* sessionstorepb.Value value */ 3: + message.value = Value.internalBinaryRead(reader, reader.uint32(), options, message.value); + break; + default: + let u = options.readUnknownField; + if (u === 'throw') + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: GetResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string key = 1; */ + if (message.key !== '') writer.tag(1, WireType.LengthDelimited).string(message.key); + /* string namespace = 2; */ + if (message.namespace !== '') writer.tag(2, WireType.LengthDelimited).string(message.namespace); + /* sessionstorepb.Value value = 3; */ + if (message.value) + Value.internalBinaryWrite(message.value, writer.tag(3, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message sessionstorepb.GetResponse + */ +export const GetResponse = new GetResponse$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class DeleteRequest$Type extends MessageType { + constructor() { + super('sessionstorepb.DeleteRequest', [{ no: 1, name: 'key', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }]); + } + create(value?: PartialMessage): DeleteRequest { + const message = globalThis.Object.create(this.messagePrototype!); + message.key = ''; + if (value !== undefined) reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead( + reader: IBinaryReader, + length: number, + options: BinaryReadOptions, + target?: DeleteRequest, + ): DeleteRequest { + let message = target ?? this.create(), + end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string key */ 1: + message.key = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === 'throw') + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: DeleteRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string key = 1; */ + if (message.key !== '') writer.tag(1, WireType.LengthDelimited).string(message.key); + let u = options.writeUnknownFields; + if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message sessionstorepb.DeleteRequest + */ +export const DeleteRequest = new DeleteRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class DeleteResponse$Type extends MessageType { + constructor() { + super('sessionstorepb.DeleteResponse', []); + } + create(value?: PartialMessage): DeleteResponse { + const message = globalThis.Object.create(this.messagePrototype!); + if (value !== undefined) reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead( + reader: IBinaryReader, + length: number, + options: BinaryReadOptions, + target?: DeleteResponse, + ): DeleteResponse { + return target ?? this.create(); + } + internalBinaryWrite(message: DeleteResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + let u = options.writeUnknownFields; + if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message sessionstorepb.DeleteResponse + */ +export const DeleteResponse = new DeleteResponse$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class SetRequest$Type extends MessageType { + constructor() { + super('sessionstorepb.SetRequest', [ + { no: 1, name: 'key', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: 'value', kind: 'message', T: () => Value }, + ]); + } + create(value?: PartialMessage): SetRequest { + const message = globalThis.Object.create(this.messagePrototype!); + message.key = ''; + if (value !== undefined) reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead( + reader: IBinaryReader, + length: number, + options: BinaryReadOptions, + target?: SetRequest, + ): SetRequest { + let message = target ?? this.create(), + end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string key */ 1: + message.key = reader.string(); + break; + case /* sessionstorepb.Value value */ 3: + message.value = Value.internalBinaryRead(reader, reader.uint32(), options, message.value); + break; + default: + let u = options.readUnknownField; + if (u === 'throw') + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: SetRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string key = 1; */ + if (message.key !== '') writer.tag(1, WireType.LengthDelimited).string(message.key); + /* sessionstorepb.Value value = 3; */ + if (message.value) + Value.internalBinaryWrite(message.value, writer.tag(3, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message sessionstorepb.SetRequest + */ +export const SetRequest = new SetRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class SetResponse$Type extends MessageType { + constructor() { + super('sessionstorepb.SetResponse', []); + } + create(value?: PartialMessage): SetResponse { + const message = globalThis.Object.create(this.messagePrototype!); + if (value !== undefined) reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead( + reader: IBinaryReader, + length: number, + options: BinaryReadOptions, + target?: SetResponse, + ): SetResponse { + return target ?? this.create(); + } + internalBinaryWrite(message: SetResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + let u = options.writeUnknownFields; + if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message sessionstorepb.SetResponse + */ +export const SetResponse = new SetResponse$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class ChangeStream$Type extends MessageType { + constructor() { + super('sessionstorepb.ChangeStream', [ + { no: 1, name: 'change_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: 'key', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: 'namespace', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }, + { no: 4, name: 'value', kind: 'message', T: () => Value }, + { no: 5, name: 'from_id', kind: 'scalar', T: 9 /*ScalarType.STRING*/ }, + ]); + } + create(value?: PartialMessage): ChangeStream { + const message = globalThis.Object.create(this.messagePrototype!); + message.changeId = ''; + message.key = ''; + message.namespace = ''; + message.fromId = ''; + if (value !== undefined) reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead( + reader: IBinaryReader, + length: number, + options: BinaryReadOptions, + target?: ChangeStream, + ): ChangeStream { + let message = target ?? this.create(), + end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string change_id */ 1: + message.changeId = reader.string(); + break; + case /* string key */ 2: + message.key = reader.string(); + break; + case /* string namespace */ 3: + message.namespace = reader.string(); + break; + case /* sessionstorepb.Value value */ 4: + message.value = Value.internalBinaryRead(reader, reader.uint32(), options, message.value); + break; + case /* string from_id */ 5: + message.fromId = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === 'throw') + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: ChangeStream, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string change_id = 1; */ + if (message.changeId !== '') writer.tag(1, WireType.LengthDelimited).string(message.changeId); + /* string key = 2; */ + if (message.key !== '') writer.tag(2, WireType.LengthDelimited).string(message.key); + /* string namespace = 3; */ + if (message.namespace !== '') writer.tag(3, WireType.LengthDelimited).string(message.namespace); + /* sessionstorepb.Value value = 4; */ + if (message.value) + Value.internalBinaryWrite(message.value, writer.tag(4, WireType.LengthDelimited).fork(), options).join(); + /* string from_id = 5; */ + if (message.fromId !== '') writer.tag(5, WireType.LengthDelimited).string(message.fromId); + let u = options.writeUnknownFields; + if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message sessionstorepb.ChangeStream + */ +export const ChangeStream = new ChangeStream$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class Select$Type extends MessageType): Select { + const message = globalThis.Object.create(this.messagePrototype!); + message.match = { oneofKind: undefined }; + if (value !== undefined) reflectionMergePartial