UNPKG

osc-mcp-server

Version:

Model Context Protocol server for OSC (Open Sound Control) endpoint management

246 lines 9.19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseOSCMessage = parseOSCMessage; exports.extractAddressPattern = extractAddressPattern; exports.extractTypeTags = extractTypeTags; exports.extractArguments = extractArguments; exports.isValidOSCMessage = isValidOSCMessage; const index_1 = require("../types/index"); function parseOSCMessage(data, sourceIp, sourcePort) { try { if (data.length < 8) { return { error: { code: index_1.ErrorCode.INVALID_OSC_MESSAGE, message: 'OSC message too short (minimum 8 bytes required)', details: { messageLength: data.length }, }, }; } let offset = 0; const addressResult = extractAddressPattern(data, offset); if (addressResult.error) { return { error: addressResult.error }; } const address = addressResult.value; offset = addressResult.nextOffset; const typeTagsResult = extractTypeTags(data, offset); if (typeTagsResult.error) { return { error: typeTagsResult.error }; } const typeTags = typeTagsResult.value; offset = typeTagsResult.nextOffset; const argumentsResult = extractArguments(data, offset, typeTags); if (argumentsResult.error) { return { error: argumentsResult.error }; } const arguments_ = argumentsResult.value; const message = { timestamp: new Date(), address, typeTags, arguments: arguments_, sourceIp, sourcePort, }; return { message }; } catch (error) { return { error: { code: index_1.ErrorCode.MESSAGE_PARSE_ERROR, message: `Failed to parse OSC message: ${error instanceof Error ? error.message : 'Unknown error'}`, details: { originalError: error }, }, }; } } function extractAddressPattern(data, offset) { try { const nullIndex = data.indexOf(0, offset); if (nullIndex === -1) { return { error: { code: index_1.ErrorCode.INVALID_OSC_MESSAGE, message: 'OSC address pattern not null-terminated', }, }; } const address = data.subarray(offset, nullIndex).toString('utf8'); if (!address.startsWith('/')) { return { error: { code: index_1.ErrorCode.INVALID_OSC_MESSAGE, message: 'OSC address pattern must start with "/"', details: { address }, }, }; } const nextOffset = alignTo4Bytes(nullIndex + 1); return { value: address, nextOffset, }; } catch (error) { return { error: { code: index_1.ErrorCode.MESSAGE_PARSE_ERROR, message: `Failed to extract address pattern: ${error instanceof Error ? error.message : 'Unknown error'}`, }, }; } } function extractTypeTags(data, offset) { try { if (offset >= data.length) { return { error: { code: index_1.ErrorCode.INVALID_OSC_MESSAGE, message: 'Insufficient data for type tags', }, }; } if (data[offset] !== 0x2c) { return { error: { code: index_1.ErrorCode.INVALID_OSC_MESSAGE, message: 'OSC type tags must start with ","', }, }; } const nullIndex = data.indexOf(0, offset); if (nullIndex === -1) { return { error: { code: index_1.ErrorCode.INVALID_OSC_MESSAGE, message: 'OSC type tags not null-terminated', }, }; } const typeTags = data.subarray(offset + 1, nullIndex).toString('utf8'); const nextOffset = alignTo4Bytes(nullIndex + 1); return { value: typeTags, nextOffset, }; } catch (error) { return { error: { code: index_1.ErrorCode.MESSAGE_PARSE_ERROR, message: `Failed to extract type tags: ${error instanceof Error ? error.message : 'Unknown error'}`, }, }; } } function extractArguments(data, offset, typeTags) { try { const arguments_ = []; let currentOffset = offset; for (const typeTag of typeTags) { if (currentOffset >= data.length) { return { error: { code: index_1.ErrorCode.INVALID_OSC_MESSAGE, message: `Insufficient data for argument of type '${typeTag}'`, }, }; } switch (typeTag) { case index_1.OSCType.INT32: { if (currentOffset + 4 > data.length) { return { error: { code: index_1.ErrorCode.INVALID_OSC_MESSAGE, message: 'Insufficient data for int32 argument', }, }; } const value = data.readInt32BE(currentOffset); arguments_.push(value); currentOffset += 4; } break; case index_1.OSCType.FLOAT32: { if (currentOffset + 4 > data.length) { return { error: { code: index_1.ErrorCode.INVALID_OSC_MESSAGE, message: 'Insufficient data for float32 argument', }, }; } const value = data.readFloatBE(currentOffset); arguments_.push(value); currentOffset += 4; } break; case index_1.OSCType.STRING: { const nullIndex = data.indexOf(0, currentOffset); if (nullIndex === -1) { return { error: { code: index_1.ErrorCode.INVALID_OSC_MESSAGE, message: 'String argument not null-terminated', }, }; } const value = data.subarray(currentOffset, nullIndex).toString('utf8'); arguments_.push(value); currentOffset = alignTo4Bytes(nullIndex + 1); } break; case index_1.OSCType.BLOB: { if (currentOffset + 4 > data.length) { return { error: { code: index_1.ErrorCode.INVALID_OSC_MESSAGE, message: 'Insufficient data for blob size', }, }; } const blobSize = data.readInt32BE(currentOffset); currentOffset += 4; if (currentOffset + blobSize > data.length) { return { error: { code: index_1.ErrorCode.INVALID_OSC_MESSAGE, message: 'Insufficient data for blob content', }, }; } const value = data.subarray(currentOffset, currentOffset + blobSize); arguments_.push(value); currentOffset = alignTo4Bytes(currentOffset + blobSize); } break; default: console.warn(`Unsupported OSC type tag '${typeTag}', skipping argument`); break; } } return { value: arguments_, }; } catch (error) { return { error: { code: index_1.ErrorCode.MESSAGE_PARSE_ERROR, message: `Failed to extract arguments: ${error instanceof Error ? error.message : 'Unknown error'}`, }, }; } } function alignTo4Bytes(offset) { return (offset + 3) & ~3; } function isValidOSCMessage(data) { return data.length >= 8 && data[0] === 0x2f; } //# sourceMappingURL=parser.js.map