@hcikn/colibri 1.1.1 → 1.3.2
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 +29 -3
- package/dist/index.cjs +607 -152
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +121 -35
- package/dist/index.d.ts +121 -35
- package/dist/index.js +574 -128
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Networking.ts","../src/Broadcasting.ts","../src/RemoteLogger.ts","../src/ModelSynchronization.ts","../src/SyncModel.ts","../src/Synced.ts"],"names":["init","Subject"],"mappings":"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","sourcesContent":["import { Subject } from 'rxjs';\r\nimport * as io from 'socket.io-client';\r\n\r\nexport interface Message {\r\n channel: string;\r\n command: string;\r\n payload: unknown;\r\n}\r\n\r\nconst messageSubject = new Subject<Message>();\r\nexport const Messages = messageSubject.asObservable();\r\n\r\nlet location = '';\r\nif (typeof window !== 'undefined') {\r\n location = window.location.hostname || '';\r\n}\r\n\r\nconst socket = io.connect({\r\n autoConnect: false,\r\n});\r\n\r\nconst init = (app: string, server?: string, port?: number) => {\r\n server ??= location;\r\n port ??= 9011;\r\n\r\n if (!server.startsWith('http')) {\r\n server = 'http://' + server;\r\n }\r\n\r\n server += `:${port}`;\r\n\r\n // FIXME: terrible workaround because socket.io marked uri as readonly\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n (socket.io as any).uri = server;\r\n socket.io.opts.query = { app, version: '1' };\r\n socket.connect();\r\n\r\n console.debug(`Connecting to colibri server on ${server}`);\r\n socket.on('connect', () => {\r\n console.debug(`Connected to colibri server on ${server}`);\r\n });\r\n\r\n socket.onAny((channel, msg) => {\r\n // if (msg.command === 'rpcRequest') {\r\n // const result = CallRpc(msg.channel, msg.payload['parameters']);\r\n // socket.emit(msg.channel, {\r\n // group: APP_NAME,\r\n // command: 'rpcAnswer',\r\n // payload: {\r\n // result,\r\n // rpcId: msg.payload['rpcId']\r\n // }\r\n // });\r\n // }\r\n messageSubject.next({\r\n channel,\r\n command: msg.command,\r\n payload: msg.payload,\r\n });\r\n });\r\n};\r\n\r\nexport const Colibri = {\r\n init,\r\n};\r\n\r\n///////////////////////////\r\n/// Default communication\r\n///////////////////////////\r\nexport const SendMessage = (\r\n channel: string,\r\n command: string,\r\n payload: unknown = {}\r\n) => {\r\n socket.emit(channel, {\r\n command,\r\n payload,\r\n });\r\n};\r\n\r\n///////////////////////////////////////////\r\n/// Handlers (e.g. model synchronization)\r\n///////////////////////////////////////////\r\n\r\nexport const RegisterChannel = (\r\n channel: string,\r\n handler: (payload: Message) => void\r\n) => {\r\n socket.on(channel, handler);\r\n};\r\n\r\nexport const UnregisterChannel = (\r\n channel: string,\r\n handler: (payload: Message) => void\r\n) => {\r\n socket.off(channel, handler);\r\n};\r\n\r\nexport const RegisterOnce = (\r\n channel: string,\r\n handler: (payload: Message) => void\r\n) => {\r\n socket.once(channel, handler);\r\n};\r\n\r\n//////////////////\r\n/// RPC\r\n//////////////////\r\n// type rpcMethod = (payload: unknown) => unknown;\r\n// const rpcMethods: {[method: string]: rpcMethod } = {};\r\n// export const RegisterRpc = (method: string, fn: rpcMethod): void => {\r\n// rpcMethods[method] = fn;\r\n// };\r\n\r\n// export const SendRpc = async (method: string, payload: any): Promise<any> => {\r\n// // TODO\r\n// return null;\r\n// };\r\n\r\n// const CallRpc = (method: string, payload: any): any => {\r\n// if (rpcMethods[method])\r\n// return rpcMethods[method](payload);\r\n// else {\r\n// console.error(`Unknown rpc method ${method}`);\r\n// return 'error';\r\n// }\r\n// };\r\n\r\n// for debugging\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n// (window as any).SendMessage = SendMessage;\r\n// messageSubject.subscribe(console.log);\r\n","import { RegisterChannel, SendMessage } from './Networking';\r\n\r\nconst sendBool = (channel: string, val: boolean) => {\r\n SendMessage(channel, 'broadcast::bool', val);\r\n};\r\n\r\nconst sendBoolArray = (channel: string, val: boolean[]) => {\r\n SendMessage(channel, 'broadcast::bool[]', val);\r\n};\r\n\r\nconst sendNumber = (channel: string, val: number) => {\r\n SendMessage(channel, 'broadcast::float', val);\r\n};\r\n\r\nconst sendNumberArray = (channel: string, val: number[]) => {\r\n SendMessage(channel, 'broadcast::float[]', val);\r\n};\r\n\r\nconst sendString = (channel: string, val: string) => {\r\n SendMessage(channel, 'broadcast::string', val);\r\n};\r\n\r\nconst sendStringArray = (channel: string, val: string[]) => {\r\n SendMessage(channel, 'broadcast::string[]', val);\r\n};\r\n\r\nconst sendVector3 = (channel: string, val: [number, number, number]) => {\r\n SendMessage(channel, 'broadcast::vector3', val);\r\n};\r\n\r\nconst sendVector3Array = (channel: string, val: [number, number, number][]) => {\r\n SendMessage(channel, 'broadcast::vector3[]', val);\r\n};\r\n\r\nconst sendQuaternion = (channel: string, val: [number, number, number, number]) => {\r\n SendMessage(channel, 'broadcast::quaternion', val);\r\n};\r\n\r\nconst sendQuaternionArray = (channel: string, val: [number, number, number, number][]) => {\r\n SendMessage(channel, 'broadcast::quaternion[]', val);\r\n};\r\n\r\nconst sendColor = (channel: string, val: [number, number, number, number]) => {\r\n SendMessage(channel, 'broadcast::color', val);\r\n};\r\n\r\nconst sendColorArray = (channel: string, val: [number, number, number, number][]) => {\r\n SendMessage(channel, 'broadcast::color[]', val);\r\n};\r\n\r\nconst sendJson = (channel: string, val: { [key: string]: unknown }) => {\r\n SendMessage(channel, 'broadcast::json', val);\r\n};\r\n\r\n\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\ntype genericCallback = (val: any) => void;\r\nconst listeners: { [channel: string]: { [ command: string ]: genericCallback[] } } = { };\r\n\r\nconst registerListener = <T>(channel: string, type: string, callback: (val: T) => void) => {\r\n if (listeners[channel] === undefined) {\r\n listeners[channel] = { };\r\n RegisterChannel(channel, (msg) => {\r\n if (msg.command in listeners[channel]) {\r\n listeners[channel][msg.command].forEach(cb => cb(msg.payload as T));\r\n }\r\n });\r\n }\r\n\r\n if (listeners[channel][type] === undefined) {\r\n listeners[channel][type] = [];\r\n }\r\n\r\n listeners[channel][type].push(callback);\r\n};\r\n\r\nconst receiveBool = (channel: string, callback: (val: boolean) => void) => {\r\n registerListener<boolean>(channel, 'broadcast::bool', callback);\r\n};\r\n\r\nconst receiveBoolArray = (channel: string, callback: (val: boolean[]) => void) => {\r\n registerListener<boolean[]>(channel, 'broadcast::bool[]', callback);\r\n};\r\n\r\nconst receiveNumber = (channel: string, callback: (val: number) => void) => {\r\n registerListener<number>(channel, 'broadcast::float', callback);\r\n};\r\n\r\nconst receiveNumberArray = (channel: string, callback: (val: number[]) => void) => {\r\n registerListener<number[]>(channel, 'broadcast::float[]', callback);\r\n};\r\n\r\nconst receiveString = (channel: string, callback: (val: string) => void) => {\r\n registerListener<string>(channel, 'broadcast::string', callback);\r\n};\r\n\r\nconst receiveStringArray = (channel: string, callback: (val: string[]) => void) => {\r\n registerListener<string[]>(channel, 'broadcast::string[]', callback);\r\n};\r\n\r\nconst receiveVector3 = (channel: string, callback: (val: [number, number, number]) => void) => {\r\n registerListener<[number, number, number]>(channel, 'broadcast::vector3', callback);\r\n};\r\n\r\nconst receiveVector3Array = (channel: string, callback: (val: [number, number, number][]) => void) => {\r\n registerListener<[number, number, number][]>(channel, 'broadcast::vector3[]', callback);\r\n};\r\n\r\nconst receiveQuaternion = (channel: string, callback: (val: [number, number, number, number]) => void) => {\r\n registerListener<[number, number, number, number]>(channel, 'broadcast::quaternion', callback);\r\n};\r\n\r\nconst receiveQuaternionArray = (channel: string, callback: (val: [number, number, number, number][]) => void) => {\r\n registerListener<[number, number, number, number][]>(channel, 'broadcast::quaternion[]', callback);\r\n};\r\n\r\nconst receiveColor = (channel: string, callback: (val: string) => void) => {\r\n registerListener<string>(channel, 'broadcast::color', callback);\r\n};\r\n\r\nconst receiveColorArray = (channel: string, callback: (val: string[]) => void) => {\r\n registerListener<string[]>(channel, 'broadcast::color[]', callback);\r\n};\r\n\r\nconst receiveJson = (channel: string, callback: (val: { [key: string]: unknown }) => void) => {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n registerListener<any>(channel, 'broadcast::json', callback);\r\n};\r\n\r\n\r\nconst unregister = (channel: string, callback: genericCallback) => {\r\n for (const command in listeners[channel]) {\r\n const index = listeners[channel][command].indexOf(callback);\r\n if (index >= 0) {\r\n listeners[channel][command].splice(index, 1);\r\n }\r\n }\r\n\r\n // TODO: we should ideally unsubscribe from the channel if there are no more listeners\r\n};\r\n\r\nexport const Sync = {\r\n sendBool, sendBoolArray,\r\n sendNumber, sendNumberArray,\r\n sendString, sendStringArray,\r\n sendVector3, sendVector3Array,\r\n sendQuaternion, sendQuaternionArray,\r\n sendColor, sendColorArray,\r\n sendJson,\r\n\r\n receiveBool, receiveBoolArray,\r\n receiveNumber, receiveNumberArray,\r\n receiveString, receiveStringArray,\r\n receiveVector3, receiveVector3Array,\r\n receiveQuaternion, receiveQuaternionArray,\r\n receiveColor, receiveColorArray,\r\n receiveJson,\r\n\r\n unregister,\r\n\r\n // for better compatibility with Unity Colibri\r\n sendFloat: sendNumber,\r\n sendInt: sendNumber,\r\n sendFloatArray: sendNumberArray,\r\n sendIntArray: sendNumberArray,\r\n};\r\n","import { SendMessage } from './Networking';\r\n\r\nconst init = () => {\r\n // keep original console commands\r\n const consoleDebug = console.debug;\r\n const consoleLog = console.log;\r\n const consoleWarn = console.warn;\r\n const consoleError = console.error;\r\n\r\n const stringify = (obj: unknown): string => {\r\n if (obj instanceof Error) {\r\n return obj.message + '\\n' + obj.stack;\r\n }\r\n\r\n const cache: unknown[] = [];\r\n const str = JSON.stringify(obj, (_, value: unknown) => {\r\n if (typeof value === 'object' && value !== null) {\r\n if (cache.indexOf(value) !== -1) {\r\n // Circular reference found, discard key\r\n return;\r\n }\r\n // Store value in our collection\r\n cache.push(value);\r\n }\r\n return value;\r\n }, 2);\r\n return str;\r\n };\r\n\r\n // intercept calls from console\r\n console.debug = function (...args) {\r\n consoleDebug.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'debug', [...args].map(stringify).join().trim());\r\n };\r\n console.log = function (...args) {\r\n consoleLog.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'info', [...args].map(stringify).join().trim());\r\n };\r\n console.warn = function (...args) {\r\n consoleWarn.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'warning', [...args].map(stringify).join().trim());\r\n };\r\n console.error = function (...args) {\r\n consoleError.apply(this, Array.prototype.slice.call(args));\r\n SendMessage('log', 'error', [...args].map(stringify).join().trim());\r\n };\r\n};\r\n\r\n\r\nexport const RemoteLogger = {\r\n init\r\n};\r\n","import { BehaviorSubject, Observable } from 'rxjs';\r\nimport { Message, RegisterChannel, SendMessage } from './Networking';\r\nimport { SyncModel } from './SyncModel';\r\n\r\ninterface ModelSyncMsg<T extends SyncModel<T>> extends Message {\r\n command: 'model::update' | 'model::delete';\r\n payload: Partial<T> & { id: string };\r\n}\r\n\r\ninterface ModelSyncRegistration<T> {\r\n name?: string;\r\n type: { new(id: string): T };\r\n}\r\n\r\ntype ModelSync<T> = [ Observable<T[]>, (model: T) => void ];\r\n\r\nexport const RegisterModelSync = <T extends SyncModel<T>>(registration: ModelSyncRegistration<T>): ModelSync<T> => {\r\n const name = registration.name || registration.type.name.toLowerCase();\r\n\r\n // initial data fetch\r\n SendMessage(name, 'model::request');\r\n\r\n // Register for updates\r\n console.log(`Registering model '${name}'`);\r\n RegisterChannel(`${name}`, (payload: Message) => {\r\n const msg = payload as ModelSyncMsg<T>;\r\n if (msg.command === 'model::update') {\r\n onUpdate(msg.payload);\r\n } else if (msg.command === 'model::delete') {\r\n onDelete(msg.payload.id);\r\n } else {\r\n console.error(`Unknown model command: ${msg.command}`);\r\n }\r\n });\r\n\r\n // Handle updates\r\n const models = new BehaviorSubject<T[]>([]);\r\n\r\n const onUpdate = (modelData: Partial<T>) => {\r\n const model = models.value.find(m => m.id === modelData.id);\r\n if (model) {\r\n // Update existing model\r\n model.update(modelData);\r\n models.next([...models.value]);\r\n } else if (modelData.id) {\r\n const newModel = new registration.type(modelData.id);\r\n\r\n newModel.modelChanges$.subscribe(changes => {\r\n // If we did the changes, we'll ignore it\r\n if (!newModel.ignoreNextChange) {\r\n SendMessage(`${name}`, 'model::update', newModel.toJson(changes));\r\n }\r\n newModel.ignoreNextChange = false;\r\n models.next([...models.value]);\r\n });\r\n\r\n newModel.update(modelData);\r\n models.next([...models.value, newModel]);\r\n }\r\n };\r\n\r\n const onDelete = (id: string) => {\r\n const model = models.value.find(m => m.id === id);\r\n model?.delete();\r\n models.next(models.value.filter(m => m.id !== id));\r\n };\r\n\r\n const registerModel = (model: T) => {\r\n model.modelChanges$.subscribe(changes => {\r\n // If we did the changes, we'll ignore it\r\n if (!model.ignoreNextChange) {\r\n SendMessage(`${name}`, 'model::update', model.toJson(changes));\r\n }\r\n model.ignoreNextChange = false;\r\n });\r\n\r\n // send initial model\r\n SendMessage(`${name}`, 'model::update', model.toJson());\r\n models.next([...models.value, model]);\r\n };\r\n\r\n return [ models.asObservable(), registerModel ];\r\n};\r\n\r\n","import { Subject, bufferTime, filter, map, share } from 'rxjs';\r\n\r\nexport abstract class SyncModel<T> {\r\n public readonly id: string;\r\n\r\n // FIXME: this is set in 'registerSyncedProperty' but the decorator is called after the constructor\r\n // FIXME: -> therefore only available via prototype!\r\n private syncedProperties!: { [key: string]: (string | symbol) };\r\n\r\n // TODO: move to ModelSynchronization or use a much better structure or something like that?\r\n public ignoreNextChange = false;\r\n\r\n public readonly modelChanges = new Subject<string>();\r\n public readonly modelChanges$ = this.modelChanges.pipe(\r\n bufferTime(1),\r\n map(changes => changes.filter(this.uniq)),\r\n filter(changes => changes.length > 0),\r\n share());\r\n\r\n public constructor(id: string) {\r\n this.id = id;\r\n }\r\n\r\n private uniq(value: string, index: number, array: string[]) {\r\n return array.indexOf(value) === index;\r\n }\r\n\r\n protected onModelChanges(prop: string): void {\r\n this.modelChanges.next(prop);\r\n }\r\n\r\n public delete(): void {\r\n this.modelChanges.complete();\r\n }\r\n\r\n public registerSyncedProperty(name: string, prop: string | symbol): void {\r\n // workaround since decorators are called before constructors\r\n if (!this.syncedProperties) {\r\n this.syncedProperties = {};\r\n }\r\n\r\n this.syncedProperties[name] = prop;\r\n }\r\n\r\n public update(updates: Partial<T>): void {\r\n const syncedProps = Object.getPrototypeOf(this).syncedProperties;\r\n\r\n for (const key of Object.keys(updates)) {\r\n if (key !== 'id') {\r\n const localKey = syncedProps[key];\r\n if (localKey) {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n (this as any)[localKey as keyof SyncModel<T>] = updates[key as keyof T];\r\n this.ignoreNextChange = true;\r\n } else {\r\n console.warn(`Unknown property ${key} in ${this.constructor.name}`);\r\n }\r\n }\r\n }\r\n }\r\n\r\n public toJson(attributes: string[] = []): Partial<T> {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const json: any = { id: this.id };\r\n\r\n // TODO: only changed properties\r\n const syncedProps = Object.getPrototypeOf(this).syncedProperties;\r\n for (const key of Object.keys(syncedProps)) {\r\n const localKey = syncedProps[key] as keyof SyncModel<T>;\r\n if (attributes.length === 0 || attributes.includes(localKey)) {\r\n json[key] = this[localKey];\r\n }\r\n }\r\n\r\n return json;\r\n }\r\n}","import { SyncModel } from './SyncModel';\r\n\r\n/**\r\n * This decorator can be attached to any property or accessor of a SyncModel. \r\n * It will cause the property to be synchronized with the server.\r\n * Refer to https://www.typescriptlang.org/docs/handbook/decorators.html\r\n */\r\nexport const Synced = <T>(syncedName: string = ''): PropertyDecorator => {\r\n\r\n return function (target: unknown, key: string | symbol, descriptor?: PropertyDescriptor): void {\r\n if (!syncedName)\r\n syncedName = key.toString();\r\n syncedName = syncedName.toLowerCase();\r\n\r\n if (!(target instanceof SyncModel)) {\r\n console.error('Synced decorator can only be used on SyncModel properties');\r\n return;\r\n }\r\n\r\n let original = target[key as keyof Object];\r\n // FIXME: this sets the property on the prototype, not the instance!\r\n target.registerSyncedProperty(syncedName, key);\r\n\r\n if (!descriptor?.get) {\r\n /**\r\n * TODO: This (i.e., for properties) doesn't work in react for some reason!\r\n */\r\n Reflect.deleteProperty(target, key);\r\n Reflect.defineProperty(target, key, {\r\n get: () => original,\r\n set: newVal => {\r\n console.log(`Set ${key.toString()} to ${newVal}`);\r\n original = newVal;\r\n },\r\n enumerable: true,\r\n configurable: true\r\n });\r\n } else {\r\n const originalAccessors = {\r\n get: descriptor.get,\r\n set: descriptor.set\r\n };\r\n\r\n descriptor.get = function (): T {\r\n const ret: T = originalAccessors.get.call(this);\r\n return ret;\r\n };\r\n\r\n descriptor.set = function (newval: T) {\r\n const model = this as SyncModel<T>;\r\n model.modelChanges.next(String(key));\r\n\r\n if (originalAccessors.set) {\r\n originalAccessors.set.call(this, newval);\r\n }\r\n };\r\n }\r\n };\r\n};\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/Colibri.ts","../src/ColibriError.ts","../src/Broadcasting.ts","../src/RemoteLogger.ts","../src/ModelSynchronization.ts","../src/SyncModel.ts","../src/Synced.ts"],"names":["Subject"],"mappings":"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","sourcesContent":["import { Socket, connect } from 'socket.io-client';\r\nimport ColibriError from './ColibriError';\r\nimport { Subject } from 'rxjs';\r\n\r\nexport interface Message {\r\n channel: string;\r\n command: string;\r\n payload: unknown;\r\n}\r\n\r\nexport class Colibri {\r\n private static instance: Colibri | null = null;\r\n\r\n private readonly socket: Socket;\r\n private readonly messageSubject = new Subject<Message>();\r\n public readonly messages = this.messageSubject.asObservable();\r\n public readonly uri: string;\r\n public readonly uriRestApi: string;\r\n\r\n public constructor(\r\n public readonly app: string,\r\n public readonly server: string = window?.location?.hostname ?? '',\r\n public readonly port: number = 9011\r\n ) {\r\n if (server.trim().length <= 0) {\r\n throw new ColibriError('Server Address missing or empty!');\r\n }\r\n\r\n if (port < 1 || port > 65535) {\r\n throw new ColibriError('Port out of allowed range (0 - 65535)');\r\n }\r\n\r\n this.uri = `${server}:${port}`;\r\n if (!new RegExp('wss?://', 'i').test(this.uri)) {\r\n this.uri = `ws://${this.uri}`;\r\n }\r\n this.uriRestApi = `${this.uri}/api/store/${app}/`;\r\n\r\n // there is already an instance running\r\n // do we actually need a second ?!\r\n if (Colibri.instance)\r\n throw new ColibriError('A Colibri instance already exists!');\r\n else Colibri.instance = this;\r\n\r\n this.socket = connect(this.uri, {\r\n query: { app, version: '1' },\r\n transports: ['websocket']\r\n });\r\n this.socket.on('connect', this.onSocketConnect.bind(this));\r\n this.socket.onAny(this.onSocketAny.bind(this));\r\n\r\n // latency statistics\r\n this.registerChannel('colibri', (msg) => {\r\n if (msg.command === 'latency') {\r\n SendMessage('colibri', 'latency', msg.payload);\r\n }\r\n });\r\n }\r\n\r\n private onSocketConnect() {\r\n console.debug(`Connected to colibri server on ${this.server}`);\r\n }\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n private onSocketAny(channel: string, msg: any) {\r\n this.messageSubject.next({\r\n channel,\r\n command: msg.command,\r\n payload: msg.payload,\r\n });\r\n }\r\n\r\n /**\r\n * Returns the existing Colibri instance or null if there's none yet\r\n * @param warnIfNotInitialized true => print Log message if Colibri has not been initialized yet\r\n * @returns\r\n */\r\n public static getInstance(\r\n warnIfNotInitialized: boolean = true\r\n ): Colibri | null {\r\n if (warnIfNotInitialized && !Colibri.instance) {\r\n console.warn('Colibri not initialized yet! (Instance is null)');\r\n }\r\n return Colibri.instance;\r\n }\r\n\r\n /**\r\n * Sends a single message in the given `channel`.\r\n * @param channel message channel\r\n * @param command message command\r\n * @param payload message payload\r\n */\r\n public sendMessage(\r\n channel: string,\r\n command: string,\r\n payload: unknown = {}\r\n ) {\r\n this.socket.emit(channel, {\r\n command,\r\n payload,\r\n });\r\n }\r\n\r\n //#region Socket Events\r\n /**\r\n * Adds a `handler` function listening for messages in `channel`.\r\n * @param channel message channel\r\n * @param handler handler to be executed when a message is received\r\n */\r\n public registerChannel(\r\n channel: string,\r\n handler: (payload: Message) => void\r\n ) {\r\n this.socket.on(channel, handler);\r\n }\r\n\r\n /**\r\n * Removes the `handler` function listening for messages in `channel`.\r\n * @param channel message channel\r\n * @param handler handler to be executed when a message is received\r\n */\r\n public unregisterChannel(\r\n channel: string,\r\n handler: (payload: Message) => void\r\n ) {\r\n this.socket.off(channel, handler);\r\n }\r\n\r\n /**\r\n * Adds a one-time `handler` function listening for the next message in `channel`.\r\n * @param channel message channel\r\n * @param handler handler to be executed when a message is received\r\n */\r\n public registerOnce(channel: string, handler: (payload: Message) => void) {\r\n this.socket.once(channel, handler);\r\n }\r\n //#endregion\r\n\r\n //#region Rest API\r\n /**\r\n * Returns the REST API Endpoint for a given `key` or null of the key is empty.\r\n * @param key REST API storage key\r\n * @returns\r\n */\r\n public getRestUri(key: string): string | null {\r\n key = key.trim();\r\n while (key.startsWith('/')) key = key.substring(1);\r\n return key.length === 0 ? null : this.uriRestApi + key;\r\n }\r\n /**\r\n * Queries an object from the REST API identified by `key`.\r\n * @param key REST API storage key\r\n * @returns JSON object with data or null if object does not exist or any other error occurrs\r\n */\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n public async getRestObject(key: string): Promise<any | null> {\r\n const uri = this.getRestUri(key);\r\n if (!uri) return null;\r\n\r\n const response = await fetch(uri, {\r\n method: 'GET',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n });\r\n\r\n if (response.status >= 400) return null;\r\n else return response.json();\r\n }\r\n\r\n /**\r\n * Sets or updatees an object in the REST API identified by `key`.\r\n * @param key REST API storage key\r\n * @param data JSON data to write\r\n * @returns true if data was written to REST API\r\n */\r\n public async setRestObject(key: string, data: unknown): Promise<boolean> {\r\n const uri = this.getRestUri(key);\r\n if (!uri) return false;\r\n\r\n const response = await fetch(uri, {\r\n method: 'PUT',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n body: JSON.stringify(data),\r\n });\r\n\r\n return response.status >= 200 && response.status < 300;\r\n }\r\n //#endregion\r\n}\r\n\r\n/////////////////////////////////////\r\n/// Wrapper Functions\r\n// (also for backward compatibility)\r\n/////////////////////////////////////\r\n\r\n/**\r\n * @see {@link Colibri.sendMessage `Colibri.sendMessage()`}\r\n * @returns undefined if {@link Colibri `Colibri`} has not been initialized yet\r\n */\r\nexport const SendMessage = (\r\n channel: string,\r\n command: string,\r\n payload: unknown = {}\r\n) => Colibri.getInstance()?.sendMessage(channel, command, payload);\r\n\r\n/**\r\n * @see {@link Colibri.registerChannel `Colibri.registerChannel()`}\r\n * @returns undefined if {@link Colibri `Colibri`} has not been initialized yet\r\n */\r\nexport const RegisterChannel = (\r\n channel: string,\r\n handler: (payload: Message) => void\r\n) => Colibri.getInstance()?.registerChannel(channel, handler);\r\n\r\n/**\r\n * @see {@link Colibri.unregisterChannel `Colibri.unregisterChannel()`}\r\n * @returns undefined if {@link Colibri `Colibri`} has not been initialized yet\r\n */\r\nexport const UnregisterChannel = (\r\n channel: string,\r\n handler: (payload: Message) => void\r\n) => Colibri.getInstance()?.unregisterChannel(channel, handler);\r\n\r\n/**\r\n * @see {@link Colibri.registerOnce `Colibri.registerOnce()`}\r\n * @returns undefined if {@link Colibri `Colibri`} has not been initialized yet\r\n */\r\nexport const RegisterOnce = (\r\n channel: string,\r\n handler: (payload: Message) => void\r\n) => Colibri.getInstance()?.registerOnce(channel, handler);\r\n\r\n/**\r\n * @see {@link Colibri.getRestObject `Colibri.getRestObject()`}\r\n * @returns undefined if {@link Colibri `Colibri`} has not been initialized yet\r\n */\r\nexport const GetRestApi = (key: string) =>\r\n Colibri.getInstance()?.getRestObject(key);\r\n\r\n/**\r\n * @see {@link Colibri.setRestObject `Colibri.setRestObject()`}\r\n * @returns undefined if {@link Colibri `Colibri`} has not been initialized yet\r\n */\r\nexport const PutRestApi = (key: string, data: unknown) =>\r\n Colibri.getInstance()?.setRestObject(key, data);\r\n","import { CustomError } from 'ts-custom-error';\r\n\r\nexport default class ColibriError extends CustomError {\r\n constructor(message?: string) {\r\n super(message);\r\n }\r\n}\r\n","import { RegisterChannel, SendMessage } from './Colibri';\r\n\r\nconst sendBool = (channel: string, val: boolean) => {\r\n SendMessage(channel, 'broadcast::bool', val);\r\n};\r\n\r\nconst sendBoolArray = (channel: string, val: boolean[]) => {\r\n SendMessage(channel, 'broadcast::bool[]', val);\r\n};\r\n\r\nconst sendNumber = (channel: string, val: number) => {\r\n SendMessage(channel, 'broadcast::float', val);\r\n};\r\n\r\nconst sendNumberArray = (channel: string, val: number[]) => {\r\n SendMessage(channel, 'broadcast::float[]', val);\r\n};\r\n\r\nconst sendString = (channel: string, val: string) => {\r\n SendMessage(channel, 'broadcast::string', val);\r\n};\r\n\r\nconst sendStringArray = (channel: string, val: string[]) => {\r\n SendMessage(channel, 'broadcast::string[]', val);\r\n};\r\n\r\nconst sendVector3 = (channel: string, val: [number, number, number]) => {\r\n SendMessage(channel, 'broadcast::vector3', val);\r\n};\r\n\r\nconst sendVector3Array = (channel: string, val: [number, number, number][]) => {\r\n SendMessage(channel, 'broadcast::vector3[]', val);\r\n};\r\n\r\nconst sendQuaternion = (\r\n channel: string,\r\n val: [number, number, number, number]\r\n) => {\r\n SendMessage(channel, 'broadcast::quaternion', val);\r\n};\r\n\r\nconst sendQuaternionArray = (\r\n channel: string,\r\n val: [number, number, number, number][]\r\n) => {\r\n SendMessage(channel, 'broadcast::quaternion[]', val);\r\n};\r\n\r\nconst sendColor = (channel: string, val: [number, number, number, number]) => {\r\n SendMessage(channel, 'broadcast::color', val);\r\n};\r\n\r\nconst sendColorArray = (\r\n channel: string,\r\n val: [number, number, number, number][]\r\n) => {\r\n SendMessage(channel, 'broadcast::color[]', val);\r\n};\r\n\r\nconst sendJson = (channel: string, val: { [key: string]: unknown }) => {\r\n SendMessage(channel, 'broadcast::json', val);\r\n};\r\n\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\ntype genericCallback = (val: any) => void;\r\nconst listeners: {\r\n [channel: string]: { [command: string]: genericCallback[] };\r\n} = {};\r\n\r\nconst registerListener = <T>(\r\n channel: string,\r\n type: string,\r\n callback: (val: T) => void\r\n) => {\r\n if (listeners[channel] === undefined) {\r\n listeners[channel] = {};\r\n RegisterChannel(channel, (msg) => {\r\n if (msg.command in listeners[channel]) {\r\n listeners[channel][msg.command].forEach((cb) =>\r\n cb(msg.payload as T)\r\n );\r\n }\r\n });\r\n }\r\n\r\n if (listeners[channel][type] === undefined) {\r\n listeners[channel][type] = [];\r\n }\r\n\r\n listeners[channel][type].push(callback);\r\n};\r\n\r\nconst receiveBool = (channel: string, callback: (val: boolean) => void) => {\r\n registerListener<boolean>(channel, 'broadcast::bool', callback);\r\n};\r\n\r\nconst receiveBoolArray = (\r\n channel: string,\r\n callback: (val: boolean[]) => void\r\n) => {\r\n registerListener<boolean[]>(channel, 'broadcast::bool[]', callback);\r\n};\r\n\r\nconst receiveNumber = (channel: string, callback: (val: number) => void) => {\r\n registerListener<number>(channel, 'broadcast::float', callback);\r\n};\r\n\r\nconst receiveNumberArray = (\r\n channel: string,\r\n callback: (val: number[]) => void\r\n) => {\r\n registerListener<number[]>(channel, 'broadcast::float[]', callback);\r\n};\r\n\r\nconst receiveString = (channel: string, callback: (val: string) => void) => {\r\n registerListener<string>(channel, 'broadcast::string', callback);\r\n};\r\n\r\nconst receiveStringArray = (\r\n channel: string,\r\n callback: (val: string[]) => void\r\n) => {\r\n registerListener<string[]>(channel, 'broadcast::string[]', callback);\r\n};\r\n\r\nconst receiveVector3 = (\r\n channel: string,\r\n callback: (val: [number, number, number]) => void\r\n) => {\r\n registerListener<[number, number, number]>(\r\n channel,\r\n 'broadcast::vector3',\r\n callback\r\n );\r\n};\r\n\r\nconst receiveVector3Array = (\r\n channel: string,\r\n callback: (val: [number, number, number][]) => void\r\n) => {\r\n registerListener<[number, number, number][]>(\r\n channel,\r\n 'broadcast::vector3[]',\r\n callback\r\n );\r\n};\r\n\r\nconst receiveQuaternion = (\r\n channel: string,\r\n callback: (val: [number, number, number, number]) => void\r\n) => {\r\n registerListener<[number, number, number, number]>(\r\n channel,\r\n 'broadcast::quaternion',\r\n callback\r\n );\r\n};\r\n\r\nconst receiveQuaternionArray = (\r\n channel: string,\r\n callback: (val: [number, number, number, number][]) => void\r\n) => {\r\n registerListener<[number, number, number, number][]>(\r\n channel,\r\n 'broadcast::quaternion[]',\r\n callback\r\n );\r\n};\r\n\r\nconst receiveColor = (channel: string, callback: (val: string) => void) => {\r\n registerListener<string>(channel, 'broadcast::color', callback);\r\n};\r\n\r\nconst receiveColorArray = (\r\n channel: string,\r\n callback: (val: string[]) => void\r\n) => {\r\n registerListener<string[]>(channel, 'broadcast::color[]', callback);\r\n};\r\n\r\nconst receiveJson = (\r\n channel: string,\r\n callback: (val: { [key: string]: unknown }) => void\r\n) => {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n registerListener<any>(channel, 'broadcast::json', callback);\r\n};\r\n\r\nconst unregister = (channel: string, callback: genericCallback) => {\r\n for (const command in listeners[channel]) {\r\n const index = listeners[channel][command].indexOf(callback);\r\n if (index >= 0) {\r\n listeners[channel][command].splice(index, 1);\r\n }\r\n }\r\n\r\n // TODO: we should ideally unsubscribe from the channel if there are no more listeners\r\n};\r\n\r\nexport const Sync = {\r\n sendBool,\r\n sendBoolArray,\r\n sendNumber,\r\n sendNumberArray,\r\n sendString,\r\n sendStringArray,\r\n sendVector3,\r\n sendVector3Array,\r\n sendQuaternion,\r\n sendQuaternionArray,\r\n sendColor,\r\n sendColorArray,\r\n sendJson,\r\n\r\n receiveBool,\r\n receiveBoolArray,\r\n receiveNumber,\r\n receiveNumberArray,\r\n receiveString,\r\n receiveStringArray,\r\n receiveVector3,\r\n receiveVector3Array,\r\n receiveQuaternion,\r\n receiveQuaternionArray,\r\n receiveColor,\r\n receiveColorArray,\r\n receiveJson,\r\n\r\n unregister,\r\n\r\n // for better compatibility with Unity Colibri\r\n sendFloat: sendNumber,\r\n sendInt: sendNumber,\r\n sendFloatArray: sendNumberArray,\r\n sendIntArray: sendNumberArray,\r\n};\r\n","import { SendMessage } from './Colibri';\r\n\r\nexport class RemoteLogger {\r\n private readonly consoleDebug = console.debug;\r\n private readonly consoleLog = console.log;\r\n private readonly consoleInfo = console.info;\r\n private readonly consoleWarn = console.warn;\r\n private readonly consoleError = console.error;\r\n\r\n /**\r\n * @deprecated use new class constructor directly\r\n */\r\n public static init(enabled: boolean = true) {\r\n return new RemoteLogger(enabled);\r\n }\r\n\r\n public constructor(private enabled: boolean = true) {\r\n // intercept calls from console\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n console.debug = (...args: any[]) => {\r\n this.consoleDebug(...args);\r\n this.sendMessage('debug', args);\r\n };\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n console.log = (...args: any[]) => {\r\n this.consoleLog(...args);\r\n this.sendMessage('info', args);\r\n };\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n console.info = (...args: any[]) => {\r\n this.consoleInfo(...args);\r\n this.sendMessage('info', args);\r\n };\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n console.warn = (...args: any[]) => {\r\n this.consoleWarn(...args);\r\n this.sendMessage('warn', args);\r\n };\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n console.error = (...args: any[]) => {\r\n this.consoleError(...args);\r\n this.sendMessage('error', args);\r\n };\r\n }\r\n\r\n private sendMessage(\r\n level: 'error' | 'warn' | 'info' | 'debug',\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n args: any[]\r\n ) {\r\n if (!this.enabled) return;\r\n SendMessage('log', level, [...args].map(stringify).join().trim());\r\n }\r\n\r\n public enable() {\r\n this.enabled = true;\r\n }\r\n\r\n public disable() {\r\n this.enabled = false;\r\n }\r\n}\r\n\r\nconst stringify = (obj: unknown): string => {\r\n if (obj instanceof Error) {\r\n return obj.message + '\\n' + obj.stack;\r\n }\r\n\r\n if (typeof obj === 'string') return obj as string;\r\n\r\n const cache: unknown[] = [];\r\n const str = JSON.stringify(\r\n obj,\r\n (_, value: unknown) => {\r\n if (typeof value === 'object' && value !== null) {\r\n if (cache.indexOf(value) !== -1) {\r\n // Circular reference found, discard key\r\n return;\r\n }\r\n // Store value in our collection\r\n cache.push(value);\r\n }\r\n return value;\r\n },\r\n 2\r\n );\r\n return str;\r\n};\r\n","import { BehaviorSubject, Observable } from 'rxjs';\r\nimport { Message, RegisterChannel, SendMessage } from './Colibri';\r\nimport { SyncModel } from './SyncModel';\r\n\r\ninterface ModelSyncMsg<T extends SyncModel<T>> extends Message {\r\n command: 'model::update' | 'model::delete';\r\n payload: Partial<T> & { id: string };\r\n}\r\n\r\ninterface ModelSyncRegistration<T> {\r\n name?: string;\r\n type: { new (id: string): T };\r\n}\r\n\r\ntype ModelSync<T> = [Observable<T[]>, (model: T) => void];\r\n\r\nexport const RegisterModelSync = <T extends SyncModel<T>>(\r\n registration: ModelSyncRegistration<T>\r\n): ModelSync<T> => {\r\n const name = registration.name || registration.type.name.toLowerCase();\r\n\r\n // initial data fetch\r\n SendMessage(name, 'model::request');\r\n\r\n // Register for updates\r\n console.log(`Registering model '${name}'`);\r\n RegisterChannel(`${name}`, (payload: Message) => {\r\n const msg = payload as ModelSyncMsg<T>;\r\n if (msg.command === 'model::update') {\r\n onUpdate(msg.payload);\r\n } else if (msg.command === 'model::delete') {\r\n onDelete(msg.payload.id);\r\n } else {\r\n console.error(`Unknown model command: ${msg.command}`);\r\n }\r\n });\r\n\r\n // Handle updates\r\n const models = new BehaviorSubject<T[]>([]);\r\n\r\n const onUpdate = (modelData: Partial<T>) => {\r\n const model = models.value.find((m) => m.id === modelData.id);\r\n if (model) {\r\n // Update existing model\r\n model.update(modelData);\r\n models.next([...models.value]);\r\n } else if (modelData.id) {\r\n const newModel = new registration.type(modelData.id);\r\n\r\n newModel.modelChanges$.subscribe((changes) => {\r\n // If we did the changes, we'll ignore it\r\n if (!newModel.ignoreNextChange) {\r\n SendMessage(\r\n `${name}`,\r\n 'model::update',\r\n newModel.toJson(changes)\r\n );\r\n }\r\n newModel.ignoreNextChange = false;\r\n models.next([...models.value]);\r\n });\r\n\r\n newModel.update(modelData);\r\n models.next([...models.value, newModel]);\r\n }\r\n };\r\n\r\n const onDelete = (id: string) => {\r\n const model = models.value.find((m) => m.id === id);\r\n model?.delete();\r\n models.next(models.value.filter((m) => m.id !== id));\r\n };\r\n\r\n const registerModel = (model: T) => {\r\n model.modelChanges$.subscribe((changes) => {\r\n // If we did the changes, we'll ignore it\r\n if (!model.ignoreNextChange) {\r\n SendMessage(`${name}`, 'model::update', model.toJson(changes));\r\n }\r\n model.ignoreNextChange = false;\r\n });\r\n\r\n // send initial model\r\n SendMessage(`${name}`, 'model::update', model.toJson());\r\n models.next([...models.value, model]);\r\n };\r\n\r\n return [models.asObservable(), registerModel];\r\n};\r\n","import { Subject, bufferTime, filter, map, share } from 'rxjs';\r\n\r\nexport abstract class SyncModel<T> {\r\n public readonly id: string;\r\n\r\n // FIXME: this is set in 'registerSyncedProperty' but the decorator is called after the constructor\r\n // FIXME: -> therefore only available via prototype!\r\n private syncedProperties!: { [key: string]: (string | symbol) };\r\n\r\n // TODO: move to ModelSynchronization or use a much better structure or something like that?\r\n public ignoreNextChange = false;\r\n\r\n public readonly modelChanges = new Subject<string>();\r\n public readonly modelChanges$ = this.modelChanges.pipe(\r\n bufferTime(1),\r\n map(changes => changes.filter(this.uniq)),\r\n filter(changes => changes.length > 0),\r\n share());\r\n\r\n public constructor(id: string) {\r\n this.id = id;\r\n }\r\n\r\n private uniq(value: string, index: number, array: string[]) {\r\n return array.indexOf(value) === index;\r\n }\r\n\r\n protected onModelChanges(prop: string): void {\r\n this.modelChanges.next(prop);\r\n }\r\n\r\n public delete(): void {\r\n this.modelChanges.complete();\r\n }\r\n\r\n public registerSyncedProperty(name: string, prop: string | symbol): void {\r\n // workaround since decorators are called before constructors\r\n if (!this.syncedProperties) {\r\n this.syncedProperties = {};\r\n }\r\n\r\n this.syncedProperties[name] = prop;\r\n }\r\n\r\n public update(updates: Partial<T>): void {\r\n const syncedProps = Object.getPrototypeOf(this).syncedProperties;\r\n\r\n for (const key of Object.keys(updates)) {\r\n if (key !== 'id') {\r\n const localKey = syncedProps[key];\r\n if (localKey) {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n (this as any)[localKey as keyof SyncModel<T>] = updates[key as keyof T];\r\n this.ignoreNextChange = true;\r\n } else {\r\n console.warn(`Unknown property ${key} in ${this.constructor.name}`);\r\n }\r\n }\r\n }\r\n }\r\n\r\n public toJson(attributes: string[] = []): Partial<T> {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const json: any = { id: this.id };\r\n\r\n // TODO: only changed properties\r\n const syncedProps = Object.getPrototypeOf(this).syncedProperties;\r\n for (const key of Object.keys(syncedProps)) {\r\n const localKey = syncedProps[key] as keyof SyncModel<T>;\r\n if (attributes.length === 0 || attributes.includes(localKey)) {\r\n json[key] = this[localKey];\r\n }\r\n }\r\n\r\n return json;\r\n }\r\n}","import { SyncModel } from './SyncModel';\r\n\r\n/**\r\n * This decorator can be attached to any property or accessor of a SyncModel. \r\n * It will cause the property to be synchronized with the server.\r\n * Refer to https://www.typescriptlang.org/docs/handbook/decorators.html\r\n */\r\nexport const Synced = <T>(syncedName: string = ''): PropertyDecorator => {\r\n\r\n return function (target: unknown, key: string | symbol, descriptor?: PropertyDescriptor): void {\r\n if (!syncedName)\r\n syncedName = key.toString();\r\n syncedName = syncedName.toLowerCase();\r\n\r\n if (!(target instanceof SyncModel)) {\r\n console.error('Synced decorator can only be used on SyncModel properties');\r\n return;\r\n }\r\n\r\n let original = target[key as keyof Object];\r\n // FIXME: this sets the property on the prototype, not the instance!\r\n target.registerSyncedProperty(syncedName, key);\r\n\r\n if (!descriptor?.get) {\r\n /**\r\n * TODO: This (i.e., for properties) doesn't work in react for some reason!\r\n */\r\n Reflect.deleteProperty(target, key);\r\n Reflect.defineProperty(target, key, {\r\n get: () => original,\r\n set: newVal => {\r\n console.log(`Set ${key.toString()} to ${newVal}`);\r\n original = newVal;\r\n },\r\n enumerable: true,\r\n configurable: true\r\n });\r\n } else {\r\n const originalAccessors = {\r\n get: descriptor.get,\r\n set: descriptor.set\r\n };\r\n\r\n descriptor.get = function (): T {\r\n const ret: T = originalAccessors.get.call(this);\r\n return ret;\r\n };\r\n\r\n descriptor.set = function (newval: T) {\r\n const model = this as SyncModel<T>;\r\n model.modelChanges.next(String(key));\r\n\r\n if (originalAccessors.set) {\r\n originalAccessors.set.call(this, newval);\r\n }\r\n };\r\n }\r\n };\r\n};\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hcikn/colibri",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "Communication Library for Research Prototyping - Allows object synchronization, networking, and voice chat with little to no code required",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"rxjs": "^7.8.1",
|
|
29
29
|
"socket.io-client": "^4.7.1",
|
|
30
|
+
"ts-custom-error": "^3.3.1",
|
|
30
31
|
"typescript": "^5.1.6"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|