Initial commit of my folder

This commit is contained in:
Timothy
2026-03-10 20:41:46 +00:00
parent 37becee76a
commit 7ad88804f3
1225 changed files with 238706 additions and 0 deletions

View File

@ -0,0 +1,5 @@
{
"name": "node-osc",
"image": "mcr.microsoft.com/devcontainers/javascript-node:22",
"postCreateCommand": "npm install"
}

11
backend/node_modules/node-osc/.gitattributes generated vendored Normal file
View File

@ -0,0 +1,11 @@
# Auto detect text files and perform LF normalization
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.js text eol=lf
*.mjs text eol=lf
*.json text eol=lf
*.md text eol=lf
*.yml text eol=lf
*.yaml text eol=lf

View File

@ -0,0 +1,62 @@
# This is a basic workflow that is manually triggered
name: Bump version
permissions:
contents: read
# Controls when the action will run. Workflow runs when manually triggered using the UI
# or API.
on:
workflow_dispatch:
# Inputs the workflow accepts.
inputs:
version:
# Friendly description to be shown in the UI instead of 'name'
description: 'Semver type of new version (major / minor / patch)'
# Input has to be provided for the workflow to run
required: true
type: choice
options:
- patch
- minor
- major
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "bump-version"
bump-version:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Check out the content (source branch). Use a deploy key so that
# when we push changes, it will trigger the release workflow
# run that runs on: tag. (Using the GitHub token would
# not run the workflow to prevent infinite recursion.)
- name: Check out source
uses: actions/checkout@v6
with:
ssh-key: ${{ secrets.DEPLOY_KEY }}
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: 'npm'
- name: Install npm packages
run: npm ci
- name: Setup Git
run: |
git config user.name 'Myles Borins'
git config user.email 'myles.borins@gmail.com'
# git config gpg.format ssh
# git config user.signingKey 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFtQmrz647zOGumjiqGirj1G9brj/QbwJQ5S3gHRmcfl myles.borins@gmail.com'
- name: bump version
run: npm version ${{ github.event.inputs.version }} # --sign-git-tag
- name: Push latest version
run: git push origin main --follow-tags

View File

@ -0,0 +1,46 @@
on:
push:
tags:
- 'v*'
name: Create Release
permissions:
id-token: write
contents: read
jobs:
publish-to-npm:
name: Publish to npm
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- name: Checkout source
uses: actions/checkout@v6
- name: Setup node
uses: actions/setup-node@v6
with:
node-version: 24
registry-url: 'https://registry.npmjs.org'
cache: npm
- name: Install latest npm
run: npm i -g npm
- name: Install dependencies and build
run: npm ci
- name: Publish package
run: npm publish
create-github-release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: publish-to-npm
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Create Release
run: gh release create ${{ github.ref }} --generate-notes
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -0,0 +1,35 @@
name: Node.js CI
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
permissions:
contents: read
jobs:
run-tests:
strategy:
matrix:
node-version: ['25', '24', '22', '20']
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
cache: ${{ runner.os != 'Windows' && 'npm' || '' }}
- name: Install Dependencies
run: npm ci
- name: Test
run: npm run test

201
backend/node_modules/node-osc/LICENSE generated vendored Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

207
backend/node_modules/node-osc/README.md generated vendored Normal file
View File

@ -0,0 +1,207 @@
# node-osc
A no frills [Open Sound Control](http://opensoundcontrol.org) client and server.
Heavily inspired by [pyOSC](https://trac.v2.nl/wiki/pyOSC).
## Installation
Install using npm
```bash
npm install node-osc
```
## Features
- 🚀 Simple and intuitive API
- 🔄 Both callback and async/await support
- 📦 Send and receive OSC messages and bundles
- 🌐 Works with both ESM and CommonJS
- 📘 TypeScript type definitions included (generated from JSDoc)
- 📝 Comprehensive documentation and examples
- ✅ Well tested and actively maintained
## Quick Start
### Sending Messages
```js
import { Client } from 'node-osc';
const client = new Client('127.0.0.1', 3333);
await client.send('/oscAddress', 200);
await client.close();
```
### Receiving Messages
```js
import { Server } from 'node-osc';
const server = new Server(3333, '0.0.0.0');
server.on('message', (msg) => {
console.log(`Message: ${msg}`);
});
```
## Documentation
- 📂 **[Documentation Hub](./docs/)** - Complete documentation with navigation guide
- 📚 **[API Reference](./docs/API.md)** - Complete API reference generated from source code
- 📘 **[Usage Guide](./docs/GUIDE.md)** - Best practices, error handling, and troubleshooting
- 📖 **[Examples](./examples/)** - Working examples for various use cases
## Compatibility
Written using ESM, supports CJS.
Supports the latest versions of Node.js 20, 22, and 24 in both ESM + CJS.
## TypeScript
TypeScript type definitions are included! No need to install `@types/node-osc`.
The types are automatically generated from JSDoc comments during the build process and included with the package. A single `.d.mts` type definition format is provided that works for both ESM and CommonJS consumers.
**Note:** If you previously installed `@types/node-osc`, you should uninstall it to avoid conflicts:
```bash
npm uninstall @types/node-osc
```
## More Examples
### Sending with async/await
```js
import { Client } from 'node-osc';
const client = new Client('127.0.0.1', 3333);
await client.send('/oscAddress', 200);
await client.close();
```
### Sending with callbacks
```js
import { Client } from 'node-osc';
const client = new Client('127.0.0.1', 3333);
client.send('/oscAddress', 200, () => {
client.close();
});
```
### Listening for OSC messages
```js
import { Server } from 'node-osc';
const oscServer = new Server(3333, '0.0.0.0', () => {
console.log('OSC Server is listening');
});
oscServer.on('message', function (msg) {
console.log(`Message: ${msg}`);
});
```
### Sending OSC bundles
```js
import { Bundle, Client } from 'node-osc';
const bundle = new Bundle(['/one', 1], ['/two', 2], ['/three', 3]);
const client = new Client('127.0.0.1', 3333);
await client.send(bundle);
await client.close();
```
### Listening for OSC bundles
```js
import { Server } from 'node-osc';
const oscServer = new Server(3333, '0.0.0.0', () => {
console.log('OSC Server is listening');
});
oscServer.on('bundle', function (bundle) {
bundle.elements.forEach((element) => {
console.log(`Timestamp: ${bundle.timetag}`);
console.log(`Message: ${element}`);
});
});
```
### Low-Level Encoding and Decoding
For advanced use cases, you can directly encode and decode OSC messages:
```js
import { Message, encode, decode } from 'node-osc';
// Encode a message to binary
const message = new Message('/oscillator/frequency', 440);
const buffer = encode(message);
// Decode binary data back to a message
const decoded = decode(buffer);
console.log('Address:', decoded.address);
console.log('Value:', decoded.args[0].value);
```
This is useful for:
- Sending OSC over non-UDP transports (WebSocket, TCP, HTTP)
- Storing OSC messages to files or databases
- Testing and debugging OSC implementations
- Building custom OSC routers or processors
See the **[API Documentation](./docs/API.md)** for complete details.
## CommonJS
Both callback and promise-based APIs work with CommonJS!
```js
const { Client, Server } = require('node-osc');
async function main() {
const server = new Server(3333, '0.0.0.0');
const client = new Client('127.0.0.1', 3333);
await new Promise((resolve) => {
server.on('listening', resolve);
});
server.on('message', (msg) => {
console.log(`Message: ${msg}`);
});
await client.send('/hello', 'world');
await client.close();
await server.close();
}
main();
```
## Examples
See the [examples](./examples/) directory for more usage examples:
- [client.js](./examples/client.js) - CommonJS client example
- [server.js](./examples/server.js) - CommonJS server example
- [esm.mjs](./examples/esm.mjs) - ESM example with callbacks
- [async-await.mjs](./examples/async-await.mjs) - ESM example with async/await
- [bundle-example.mjs](./examples/bundle-example.mjs) - Working with bundles
- [error-handling.mjs](./examples/error-handling.mjs) - Error handling patterns
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
Apache-2.0
**Note:** This project was relicensed from LGPL-3.0-or-later to Apache-2.0 in December 2025.

6
backend/node_modules/node-osc/SECURITY.md generated vendored Normal file
View File

@ -0,0 +1,6 @@
# Security Policy
## Reporting a Vulnerability
If you find a vulnerability please report directly to the maintainer of this repostiory
at [myles.borins@gmail.com](mailto:myles.borins@gmail.com).

330
backend/node_modules/node-osc/agent.md generated vendored Normal file
View File

@ -0,0 +1,330 @@
# Agent Instructions for node-osc
This document provides context and instructions for AI agents (GitHub Copilot, Cursor, and other agentic platforms) working on the node-osc project.
## Project Overview
**node-osc** is a Node.js library for sending and receiving [Open Sound Control (OSC)](http://opensoundcontrol.org) messages over UDP. It provides a simple, no-frills API inspired by pyOSC.
### Key Features
- Send and receive OSC messages and bundles
- Dual module support (ESM and CommonJS)
- Both callback and async/await APIs
- TypeScript type definitions generated from JSDoc
- Well-tested with comprehensive test coverage
- Supports Node.js 20, 22, and 24
## Architecture
### Core Components
1. **Server** (`lib/Server.mjs`) - EventEmitter-based OSC server for receiving messages
- Listens on UDP socket
- Emits events: `listening`, `message`, `bundle`, `error`, and address-specific events
2. **Client** (`lib/Client.mjs`) - OSC client for sending messages
- Sends messages over UDP
- Supports both callbacks and async/await
3. **Message** (`lib/Message.mjs`) - Represents a single OSC message
- Contains address (string) and arguments (array)
- Can append additional arguments
4. **Bundle** (`lib/Bundle.mjs`) - Represents a collection of OSC messages
- Contains timetag and array of elements (messages or nested bundles)
- Used for sending multiple messages together
5. **Low-level encoding/decoding** (`lib/osc.mjs`, `lib/internal/`) - Binary OSC protocol implementation
- `encode()` - Converts Message/Bundle objects to binary Buffer
- `decode()` - Parses binary Buffer into Message/Bundle objects
### Module System
The project uses **ESM as the source format** but provides **dual ESM/CommonJS support**:
- Source files: `lib/**/*.mjs` (ESM)
- Built CommonJS files: `dist/lib/**/*.js` (transpiled via Rollup)
- TypeScript definitions: `types/index.d.mts` (generated from JSDoc)
**Important:** The single `.d.mts` type definition file works for both ESM and CommonJS consumers.
### Package Exports
```json
{
"exports": {
"types": "./types/index.d.mts",
"require": "./dist/lib/index.js",
"import": "./lib/index.mjs",
"default": "./lib/index.mjs"
}
}
```
## Development Workflow
### Essential Commands
```bash
# Install dependencies
npm install
# Run linter (ESLint)
npm run lint
# Build the project (clean, transpile to CJS, generate types)
npm run build
# Run all tests (lint + build + ESM tests + CJS tests)
npm test
# Run only ESM tests
npm run test:esm
# Run only CJS tests
npm run test:cjs
# Generate API documentation from JSDoc
npm run docs
# Clean build artifacts
npm run clean
```
### Testing Strategy
- Tests are written in ESM format in `test/test-*.mjs`
- Tests are run against both ESM source (`lib/`) and transpiled CJS (`dist/`)
- Uses `tap` test framework
- Test utilities in `test/util.mjs` provide helpers like `getPort()` for getting available ports
- Always run `npm run build` before running CJS tests
- **100% test coverage is required** - All lines, branches, functions, and statements must be covered
### Build Process
1. **Clean**: Removes `dist/` and `types/` directories
2. **Rollup**: Transpiles ESM to CommonJS in `dist/` directory
3. **TypeScript**: Generates type definitions from JSDoc in `types/` directory
The build is automatically run before publishing (`prepublishOnly` script).
## Coding Standards
### JavaScript Style
- **ES Modules**: Use ESM syntax (`import`/`export`)
- **File extension**: Use `.mjs` for ESM files
- **Linting**: Follow ESLint rules in `eslint.config.mjs`
- **Modern JavaScript**: Use async/await, arrow functions, destructuring
- **Error handling**: Always handle errors in async operations
### Documentation
- **JSDoc comments**: All public APIs must have JSDoc comments
- **Type annotations**: Use JSDoc types for TypeScript generation
- **Examples**: Include code examples in JSDoc comments
- **Auto-generated docs**: Run `npm run docs` after changing JSDoc comments
Example JSDoc pattern:
```javascript
/**
* Sends an OSC message or bundle.
*
* @param {Message|Bundle|string} msg - The message, bundle, or address to send.
* @param {...*} args - Additional arguments (used when first param is a string address).
* @returns {Promise<void>}
*
* @example
* await client.send('/test', 123);
*
* @example
* const message = new Message('/test', 123);
* await client.send(message);
*/
async send(msg, ...args) { ... }
```
### Type System
- TypeScript definitions are **generated** from JSDoc comments
- Do not manually edit `types/*.d.mts` files
- Update JSDoc comments in source files instead
- Run `npm run build:types` to regenerate types
### Naming Conventions
- **Classes**: PascalCase (e.g., `Client`, `Server`, `Message`, `Bundle`)
- **Functions**: camelCase (e.g., `encode`, `decode`, `toBuffer`)
- **Private functions**: Prefix with underscore (e.g., `_oscType`)
- **Constants**: UPPER_SNAKE_CASE for true constants
- **Files**: Match class names or use descriptive kebab-case
### Dual Module Support Patterns
When writing code that needs to work in both ESM and CJS:
1. **Imports**: Use ESM imports in source (Rollup handles conversion)
2. **Exports**: Use named exports for all public APIs
3. **Testing**: Test both ESM and CJS builds
4. **Package imports**: Use `#decode` subpath import for internal modules (defined in `package.json` imports field)
## Important Files and Directories
### Source Files
- `lib/` - ESM source code (the canonical source)
- `lib/index.mjs` - Main entry point, exports all public APIs
- `lib/internal/` - Internal utilities (decode, encode, helpers)
- `lib/osc.mjs` - Low-level encode/decode functions
### Build Artifacts
- `dist/` - Transpiled CommonJS files (generated, do not edit)
- `types/` - TypeScript type definitions (generated, do not edit)
### Tests
- `test/test-*.mjs` - Test files using tap framework
- `test/util.mjs` - Test utilities and helpers
- `test/fixtures/` - Test data and fixtures
### Documentation
- `README.md` - Main documentation with quick start guide
- `docs/API.md` - Auto-generated API reference (do not edit manually)
- `docs/GUIDE.md` - Best practices, error handling, troubleshooting
- `examples/` - Working example code for various use cases
### Configuration
- `package.json` - Package configuration, scripts, exports
- `eslint.config.mjs` - ESLint configuration
- `rollup.config.mjs` - Rollup build configuration (ESM to CJS)
- `tsconfig.json` - TypeScript compiler options for type generation
- `jsdoc.json` - JSDoc configuration for documentation generation
## Making Changes
### Adding a New Feature
1. **Write ESM source** in `lib/`
2. **Add JSDoc comments** with types and examples
3. **Export** from `lib/index.mjs` if it's a public API
4. **Write tests** in `test/test-*.mjs` - **must achieve 100% coverage** (lines, branches, functions, statements)
5. **Run tests**: `npm test` (tests both ESM and CJS)
6. **Update docs**: `npm run docs` to regenerate API.md
7. **Update README.md** if adding user-facing functionality
### Fixing a Bug
1. **Write a failing test** that demonstrates the bug
2. **Fix the bug** in the ESM source files
3. **Run tests**: `npm test` to verify fix works in both ESM and CJS
4. **Verify coverage**: Ensure 100% test coverage is maintained
5. **Check no regressions**: Ensure all tests pass
### Modifying the API
1. **Update JSDoc** in source files
2. **Regenerate types**: `npm run build:types`
3. **Update tests** to cover new behavior - **must maintain 100% coverage**
4. **Regenerate docs**: `npm run docs`
5. **Update README.md** and `docs/GUIDE.md` as appropriate
## Common Patterns
### Creating a Server
```javascript
import { Server } from 'node-osc';
const server = new Server(3333, '0.0.0.0');
server.on('message', (msg, rinfo) => {
console.log('Message:', msg);
});
```
### Creating a Client
```javascript
import { Client } from 'node-osc';
const client = new Client('127.0.0.1', 3333);
await client.send('/test', 123);
await client.close();
```
### Working with Bundles
```javascript
import { Bundle } from 'node-osc';
const bundle = new Bundle(['/one', 1], ['/two', 2]);
await client.send(bundle);
```
### Low-level Encoding/Decoding
```javascript
import { Message, encode, decode } from 'node-osc';
const message = new Message('/test', 123);
const buffer = encode(message);
const decoded = decode(buffer);
```
## Troubleshooting
### Build Issues
- **"Cannot find module"**: Run `npm install` to install dependencies
- **Type generation fails**: Check JSDoc syntax in source files
- **CJS tests fail but ESM pass**: Run `npm run build` before testing
### Test Issues
- **Port conflicts**: Tests use dynamic port allocation via `getPort()` utility
- **Timing issues**: Use async/await and proper event handling
- **ESM/CJS differences**: Ensure code works in both environments
### Module Resolution
- **Dual package hazard**: The package exports both ESM and CJS - don't mix them
- **Type imports**: TypeScript consumers get types automatically from `types/index.d.mts`
- **Internal imports**: Use `#decode` subpath for internal modules
## Dependencies
### Runtime Dependencies
- **None** - This is a zero-dependency library for production use
### Development Dependencies
- **eslint** - Code linting
- **tap** - Test framework
- **rollup** - Module bundler for ESM → CJS transpilation
- **typescript** - Type definition generation from JSDoc
- **jsdoc** - Documentation generation
- **globals** - ESLint globals configuration
## OSC Protocol Knowledge
When working with OSC message encoding/decoding:
- OSC addresses start with `/` (e.g., `/oscillator/frequency`)
- OSC types: integer (i), float (f), string (s), blob (b), time tag (t)
- Messages are null-padded to 4-byte boundaries
- Bundles have time tags (when to execute) and can contain nested bundles
- See [OSC Specification](http://opensoundcontrol.org/spec-1_0) for protocol details
## Security Considerations
- Always validate input data when decoding OSC messages
- Be careful with buffer operations to avoid out-of-bounds access
- Limit message and bundle sizes to prevent DoS attacks
- Sanitize OSC addresses before using them as event names
- Handle malformed OSC data gracefully (emit errors, don't crash)
## License
This project uses the Apache-2.0 license. When contributing code:
- Ensure all new code is compatible with Apache-2.0
- Do not introduce dependencies with incompatible licenses
- Include proper attribution for any third-party code
## Getting Help
- **API Documentation**: See `docs/API.md`
- **Usage Guide**: See `docs/GUIDE.md`
- **Examples**: See `examples/` directory
- **Issues**: Check existing GitHub issues for similar problems
- **OSC Protocol**: Refer to http://opensoundcontrol.org for protocol details

92
backend/node_modules/node-osc/dist/lib/Bundle.js generated vendored Normal file
View File

@ -0,0 +1,92 @@
'use strict';
var Message = require('./Message.js');
/**
* Convert array notation to Message object.
* @private
* @param {Array|Message|Bundle} element - The element to sanitize.
* @returns {Message|Bundle} The sanitized element.
*/
function sanitize(element) {
if (element instanceof Array) element = new Message(element[0], ...element.slice(1));
return element;
}
/**
* Represents an OSC bundle containing multiple messages or nested bundles.
*
* OSC bundles allow multiple messages to be sent together, optionally with
* a timetag indicating when the bundle should be processed.
*
* @class
*
* @example
* // Create a bundle without a timetag
* const bundle = new Bundle(['/one', 1], ['/two', 2]);
*
* @example
* // Create a bundle with a timetag
* const bundle = new Bundle(10, ['/one', 1], ['/two', 2]);
*
* @example
* // Nest bundles
* const bundle1 = new Bundle(['/one', 1]);
* const bundle2 = new Bundle(['/two', 2]);
* bundle1.append(bundle2);
*/
class Bundle {
/**
* Create an OSC Bundle.
*
* @param {number|Message|Bundle|Array} [timetagOrElement=0] - Timetag, or if not a number, the first element and timetag will default to 0.
* @param {...(Message|Bundle|Array)} elements - Messages or bundles to include.
* Arrays will be automatically converted to Message objects.
*
* @example
* // Bundle without timetag
* const bundle = new Bundle(['/test', 1], ['/test2', 2]);
*
* @example
* // Bundle with timetag of 10
* const bundle = new Bundle(10, ['/test', 1]);
*
* @example
* // Bundle with Message objects
* const msg1 = new Message('/one', 1);
* const msg2 = new Message('/two', 2);
* const bundle = new Bundle(msg1, msg2);
*/
constructor(timetag, ...elements) {
if (!(typeof timetag === 'number')) {
elements.unshift(timetag);
timetag = 0;
}
this.oscType = 'bundle';
this.timetag = timetag;
this.elements = elements.map(sanitize);
}
/**
* Append a message or bundle to this bundle.
*
* @param {Message|Bundle|Array} element - The message or bundle to append.
* Arrays will be automatically converted to Message objects.
*
* @example
* const bundle = new Bundle();
* bundle.append(['/test', 1]);
* bundle.append(new Message('/test2', 2));
*
* @example
* // Append a nested bundle
* const bundle1 = new Bundle(['/one', 1]);
* const bundle2 = new Bundle(['/two', 2]);
* bundle1.append(bundle2);
*/
append(element) {
this.elements.push(sanitize(element));
}
}
module.exports = Bundle;

180
backend/node_modules/node-osc/dist/lib/Client.js generated vendored Normal file
View File

@ -0,0 +1,180 @@
'use strict';
var node_dgram = require('node:dgram');
var node_events = require('node:events');
var osc = require('./osc.js');
var Message = require('./Message.js');
/**
* OSC Client for sending messages and bundles over UDP.
*
* Extends EventEmitter and emits the following events:
* - 'error': Emitted when a socket error occurs
*
* @class
* @extends EventEmitter
* @example
* // Create a client
* const client = new Client('127.0.0.1', 3333);
*
* // Send a message with callback
* client.send('/oscAddress', 200, (err) => {
* if (err) console.error(err);
* client.close();
* });
*
* @example
* // Send a message with async/await
* const client = new Client('127.0.0.1', 3333);
* await client.send('/oscAddress', 200);
* await client.close();
*/
class Client extends node_events.EventEmitter {
/**
* Create an OSC Client.
*
* @param {string} host - The hostname or IP address of the OSC server.
* @param {number} port - The port number of the OSC server.
*
* @example
* const client = new Client('127.0.0.1', 3333);
*/
constructor(host, port) {
super();
this.host = host;
this.port = port;
this._sock = node_dgram.createSocket({
type: 'udp4',
reuseAddr: true
});
this._sock.on('error', (err) => {
this.emit('error', err);
});
}
/**
* Close the client socket.
*
* This method can be used with either a callback or as a Promise.
*
* @param {Function} [cb] - Optional callback function called when socket is closed.
* @returns {Promise<void>|undefined} Returns a Promise if no callback is provided.
*
* @example
* // With callback
* client.close((err) => {
* if (err) console.error(err);
* });
*
* @example
* // With async/await
* await client.close();
*/
close(cb) {
if (cb) {
this._sock.close(cb);
} else {
return new Promise((resolve, reject) => {
this._sock.close((err) => {
if (err) reject(err);
else resolve();
});
});
}
}
_performSend(message, args, callback) {
let mes;
let buf;
try {
switch (typeof message) {
case 'object':
buf = osc.encode(message);
this._sock.send(buf, 0, buf.length, this.port, this.host, callback);
break;
case 'string':
mes = new Message(args[0]);
for (let i = 1; i < args.length; i++) {
mes.append(args[i]);
}
buf = osc.encode(mes);
this._sock.send(buf, 0, buf.length, this.port, this.host, callback);
break;
default:
throw new TypeError('That Message Just Doesn\'t Seem Right');
}
}
catch (e) {
if (e.code !== 'ERR_SOCKET_DGRAM_NOT_RUNNING') throw e;
const error = new ReferenceError('Cannot send message on closed socket.');
error.code = e.code;
callback(error);
}
}
/**
* Send an OSC message or bundle to the server.
*
* This method can be used with either a callback or as a Promise.
* Messages can be sent in several formats:
* - As separate arguments: address followed by values
* - As a Message or Bundle object
* - As an array: [address, ...values]
*
* @param {...*} args - The message to send. Can be:
* - (address: string, ...values: any[], callback?: Function)
* - (message: Message|Bundle, callback?: Function)
* - (array: Array, callback?: Function)
* @returns {Promise<void>|undefined} Returns a Promise if no callback is provided.
*
* @throws {TypeError} If the message format is invalid.
* @throws {ReferenceError} If attempting to send on a closed socket.
*
* @example
* // Send with address and arguments
* client.send('/oscAddress', 200, 'hello', (err) => {
* if (err) console.error(err);
* });
*
* @example
* // Send with async/await
* await client.send('/oscAddress', 200, 'hello');
*
* @example
* // Send a Message object
* const msg = new Message('/test', 1, 2, 3);
* await client.send(msg);
*
* @example
* // Send a Bundle object
* const bundle = new Bundle(['/one', 1], ['/two', 2]);
* await client.send(bundle);
*/
send(...args) {
let message = args[0];
let callback;
// Convert array syntax to message object
if (message instanceof Array) {
message = {
address: message[0],
args: message.slice(1)
};
}
if (typeof args[args.length - 1] === 'function') {
callback = args.pop();
this._performSend(message, args, callback);
}
else {
// No callback provided, return a Promise
return new Promise((resolve, reject) => {
callback = (err) => {
if (err) reject(err);
else resolve();
};
this._performSend(message, args, callback);
});
}
}
}
module.exports = Client;

145
backend/node_modules/node-osc/dist/lib/Message.js generated vendored Normal file
View File

@ -0,0 +1,145 @@
'use strict';
const typeTags = {
s: 'string',
f: 'float',
i: 'integer',
b: 'blob',
m: 'midi'
};
/**
* Represents a typed argument for an OSC message.
*
* @class
* @private
*/
class Argument {
/**
* @param {string} type - The type of the argument (string, float, integer, blob, boolean).
* @param {*} value - The value of the argument.
*/
constructor(type, value) {
this.type = type;
this.value = value;
}
}
/**
* Represents an OSC message with an address and arguments.
*
* OSC messages consist of an address pattern (string starting with '/')
* and zero or more arguments of various types.
*
* @class
*
* @example
* // Create a message with constructor arguments
* const msg = new Message('/test', 1, 2, 'hello');
*
* @example
* // Create a message and append arguments
* const msg = new Message('/test');
* msg.append(1);
* msg.append('hello');
* msg.append(3.14);
*/
class Message {
/**
* Create an OSC Message.
*
* @param {string} address - The OSC address pattern (e.g., '/oscillator/frequency').
* @param {...*} args - Optional arguments to include in the message.
*
* @example
* const msg = new Message('/test');
*
* @example
* const msg = new Message('/test', 1, 2, 3);
*
* @example
* const msg = new Message('/synth', 'note', 60, 0.5);
*/
constructor(address, ...args) {
this.oscType = 'message';
this.address = address;
this.args = args;
}
/**
* Append an argument to the message.
*
* Automatically detects the type based on the JavaScript type:
* - Integers are encoded as OSC integers
* - Floats are encoded as OSC floats
* - Strings are encoded as OSC strings
* - Booleans are encoded as OSC booleans
* - Buffers are encoded as OSC blobs
* - Arrays are recursively appended
* - Objects with a 'type' property are used as-is
*
* @param {*} arg - The argument to append. Can be:
* - A primitive value (number, string, boolean)
* - A Buffer (encoded as blob)
* - An array of values (will be recursively appended)
* - An object with 'type' and 'value' properties for explicit type control
*
* @throws {Error} If the argument type cannot be encoded.
*
* @example
* const msg = new Message('/test');
* msg.append(42); // Integer
* msg.append(3.14); // Float
* msg.append('hello'); // String
* msg.append(true); // Boolean
*
* @example
* // Append multiple values at once
* msg.append([1, 2, 3]);
*
* @example
* // Explicitly specify type
* msg.append({ type: 'float', value: 42 });
* msg.append({ type: 'blob', value: Buffer.from('data') });
*
* @example
* // MIDI messages (4 bytes: port, status, data1, data2)
* msg.append({ type: 'midi', value: { port: 0, status: 144, data1: 60, data2: 127 } });
* msg.append({ type: 'm', value: Buffer.from([0, 144, 60, 127]) });
*/
append(arg) {
let argOut;
switch (typeof arg) {
case 'object':
if (Buffer.isBuffer(arg)) {
this.args.push(arg);
} else if (arg instanceof Array) {
arg.forEach(a => this.append(a));
} else if (arg.type) {
if (typeTags[arg.type]) arg.type = typeTags[arg.type];
this.args.push(arg);
} else {
throw new Error(`don't know how to encode object ${arg}`);
}
break;
case 'number':
if (Math.floor(arg) === arg) {
argOut = new Argument('integer', arg);
} else {
argOut = new Argument('float', arg);
}
break;
case 'string':
argOut = new Argument('string', arg);
break;
case 'boolean':
argOut = new Argument('boolean', arg);
break;
default:
throw new Error(`don't know how to encode ${arg}`);
}
if (argOut) this.args.push(argOut);
}
}
module.exports = Message;

156
backend/node_modules/node-osc/dist/lib/Server.js generated vendored Normal file
View File

@ -0,0 +1,156 @@
'use strict';
var node_dgram = require('node:dgram');
var node_events = require('node:events');
var decode = require('#decode');
/**
* OSC Server for receiving messages and bundles over UDP.
*
* Emits the following events:
* - 'listening': Emitted when the server starts listening
* - 'message': Emitted when an OSC message is received (receives msg array and rinfo object)
* - 'bundle': Emitted when an OSC bundle is received (receives bundle object and rinfo object)
* - 'error': Emitted when a socket error or decoding error occurs (receives error and rinfo)
* - Address-specific events: Emitted for each message address (e.g., '/test')
*
* @class
* @extends EventEmitter
*
* @fires Server#listening
* @fires Server#message
* @fires Server#bundle
* @fires Server#error
*
* @example
* // Create and listen for messages
* const server = new Server(3333, '0.0.0.0', () => {
* console.log('Server is listening');
* });
*
* server.on('message', (msg, rinfo) => {
* console.log('Message:', msg);
* console.log('From:', rinfo.address, rinfo.port);
* });
*
* @example
* // Using async/await with events.once
* import { once } from 'node:events';
*
* const server = new Server(3333, '0.0.0.0');
* await once(server, 'listening');
*
* server.on('message', (msg) => {
* console.log('Message:', msg);
* });
*
* @example
* // Listen for specific OSC addresses
* server.on('/note', (msg) => {
* const [address, pitch, velocity] = msg;
* console.log(`Note: ${pitch}, Velocity: ${velocity}`);
* });
*/
class Server extends node_events.EventEmitter {
/**
* Create an OSC Server.
*
* @param {number} port - The port to listen on.
* @param {string} [host='127.0.0.1'] - The host address to bind to. Use '0.0.0.0' to listen on all interfaces.
* @param {Function} [cb] - Optional callback function called when server starts listening.
*
* @example
* // Basic server
* const server = new Server(3333);
*
* @example
* // Server on all interfaces with callback
* const server = new Server(3333, '0.0.0.0', () => {
* console.log('Server started');
* });
*
* @example
* // Host parameter can be omitted, callback as second parameter
* const server = new Server(3333, () => {
* console.log('Server started on 127.0.0.1');
* });
*/
constructor(port, host='127.0.0.1', cb) {
super();
if (typeof host === 'function') {
cb = host;
host = '127.0.0.1';
}
let decoded;
this.port = port;
this.host = host;
this._sock = node_dgram.createSocket({
type: 'udp4',
reuseAddr: true
});
this._sock.bind(port, host);
// Update port and emit listening event when socket is ready
this._sock.on('listening', () => {
// Update port with actual bound port (important when using port 0)
this.port = this._sock.address().port;
this.emit('listening');
if (cb) cb();
});
this._sock.on('message', (msg, rinfo) => {
try {
decoded = decode(msg);
}
catch (e) {
const error = new Error(`can't decode incoming message: ${e.message}`);
this.emit('error', error, rinfo);
return;
}
if (decoded.elements) {
this.emit('bundle', decoded, rinfo);
}
else if (decoded) {
this.emit('message', decoded, rinfo);
this.emit(decoded[0], decoded, rinfo);
}
});
this._sock.on('error', (err) => {
this.emit('error', err);
});
}
/**
* Close the server socket.
*
* This method can be used with either a callback or as a Promise.
*
* @param {Function} [cb] - Optional callback function called when socket is closed.
* @returns {Promise<void>|undefined} Returns a Promise if no callback is provided.
*
* @example
* // With callback
* server.close((err) => {
* if (err) console.error(err);
* });
*
* @example
* // With async/await
* await server.close();
*/
close(cb) {
if (cb) {
this._sock.close(cb);
} else {
return new Promise((resolve, reject) => {
this._sock.close((err) => {
if (err) reject(err);
else resolve();
});
});
}
}
}
module.exports = Server;

16
backend/node_modules/node-osc/dist/lib/index.js generated vendored Normal file
View File

@ -0,0 +1,16 @@
'use strict';
var Message = require('./Message.js');
var Bundle = require('./Bundle.js');
var Server = require('./Server.js');
var Client = require('./Client.js');
var osc = require('./osc.js');
exports.Message = Message;
exports.Bundle = Bundle;
exports.Server = Server;
exports.Client = Client;
exports.decode = osc.decode;
exports.encode = osc.encode;

View File

@ -0,0 +1,37 @@
'use strict';
var osc = require('../osc.js');
function sanitizeMessage(decoded) {
const message = [];
message.push(decoded.address);
const args = decoded.args ?? [];
args.forEach(arg => {
message.push(arg.value);
});
return message;
}
function sanitizeBundle(decoded) {
decoded.elements = decoded.elements.map(element => {
if (element.oscType === 'bundle') return sanitizeBundle(element);
else if (element.oscType === 'message') return sanitizeMessage(element);
throw new Error('Malformed Packet');
});
return decoded;
}
function decodeAndSanitize(data, customDecode = osc.decode) {
const decoded = customDecode(data);
if (decoded.oscType === 'bundle') {
return sanitizeBundle(decoded);
}
else if (decoded.oscType === 'message') {
return sanitizeMessage(decoded);
}
else {
throw new Error ('Malformed Packet');
}
}
module.exports = decodeAndSanitize;

424
backend/node_modules/node-osc/dist/lib/osc.js generated vendored Normal file
View File

@ -0,0 +1,424 @@
'use strict';
var node_buffer = require('node:buffer');
// OSC 1.0 Protocol Implementation
// Based on http://opensoundcontrol.org/spec-1_0
function padString(str) {
const nullTerminated = str + '\0';
const byteLength = node_buffer.Buffer.byteLength(nullTerminated);
const padding = (4 - (byteLength % 4)) % 4;
return nullTerminated + '\0'.repeat(padding);
}
function readString(buffer, offset) {
let end = offset;
while (end < buffer.length && buffer[end] !== 0) {
end++;
}
if (end >= buffer.length) {
throw new Error('Malformed Packet: Missing null terminator for string');
}
const str = buffer.subarray(offset, end).toString('utf8');
// Find next 4-byte boundary
const paddedLength = Math.ceil((end - offset + 1) / 4) * 4;
return { value: str, offset: offset + paddedLength };
}
function writeInt32(value) {
const buffer = node_buffer.Buffer.alloc(4);
buffer.writeInt32BE(value, 0);
return buffer;
}
function readInt32(buffer, offset) {
if (offset + 4 > buffer.length) {
throw new Error('Malformed Packet: Not enough bytes for int32');
}
const value = buffer.readInt32BE(offset);
return { value, offset: offset + 4 };
}
function writeFloat32(value) {
const buffer = node_buffer.Buffer.alloc(4);
buffer.writeFloatBE(value, 0);
return buffer;
}
function readFloat32(buffer, offset) {
if (offset + 4 > buffer.length) {
throw new Error('Malformed Packet: Not enough bytes for float32');
}
const value = buffer.readFloatBE(offset);
return { value, offset: offset + 4 };
}
function writeBlob(value) {
const length = value.length;
const lengthBuffer = writeInt32(length);
const padding = 4 - (length % 4);
const paddingBuffer = node_buffer.Buffer.alloc(padding === 4 ? 0 : padding);
return node_buffer.Buffer.concat([lengthBuffer, value, paddingBuffer]);
}
function readBlob(buffer, offset) {
const lengthResult = readInt32(buffer, offset);
const length = lengthResult.value;
if (length < 0) {
throw new Error('Malformed Packet: Invalid blob length');
}
if (lengthResult.offset + length > buffer.length) {
throw new Error('Malformed Packet: Not enough bytes for blob');
}
const data = buffer.subarray(lengthResult.offset, lengthResult.offset + length);
const padding = 4 - (length % 4);
const nextOffset = lengthResult.offset + length + (padding === 4 ? 0 : padding);
if (nextOffset > buffer.length) {
throw new Error('Malformed Packet: Not enough bytes for blob padding');
}
return { value: data, offset: nextOffset };
}
function writeTimeTag(value) {
// For now, treat timetag as a double (8 bytes)
// OSC timetag is 64-bit: 32-bit seconds since 1900, 32-bit fractional
const buffer = node_buffer.Buffer.alloc(8);
if (value === 0 || value === null || value === undefined) {
// Immediate execution
buffer.writeUInt32BE(0, 0);
buffer.writeUInt32BE(1, 4);
} else if (typeof value === 'number') {
// Convert to OSC timetag format
const seconds = Math.floor(value);
const fraction = Math.floor((value - seconds) * 0x100000000);
buffer.writeUInt32BE(seconds + 2208988800, 0); // Add epoch offset (1900 vs 1970)
buffer.writeUInt32BE(fraction, 4);
} else {
// If not a number, write zeros (immediate execution)
buffer.writeUInt32BE(0, 0);
buffer.writeUInt32BE(1, 4);
}
return buffer;
}
function readTimeTag(buffer, offset) {
if (offset + 8 > buffer.length) {
throw new Error('Malformed Packet: Not enough bytes for timetag');
}
const seconds = buffer.readUInt32BE(offset);
const fraction = buffer.readUInt32BE(offset + 4);
let value;
if (seconds === 0 && fraction === 1) {
// Immediate execution
value = 0;
} else {
// Convert from OSC epoch (1900) to Unix epoch (1970)
const unixSeconds = seconds - 2208988800;
const fractionalSeconds = fraction / 0x100000000;
value = unixSeconds + fractionalSeconds;
}
return { value, offset: offset + 8 };
}
function writeMidi(value) {
// MIDI message is 4 bytes: port id, status byte, data1, data2
const buffer = node_buffer.Buffer.alloc(4);
if (node_buffer.Buffer.isBuffer(value)) {
if (value.length !== 4) {
throw new Error('MIDI message must be exactly 4 bytes');
}
value.copy(buffer);
} else if (typeof value === 'object' && value !== null) {
// Allow object format: { port: 0, status: 144, data1: 60, data2: 127 }
buffer.writeUInt8(value.port || 0, 0);
buffer.writeUInt8(value.status || 0, 1);
buffer.writeUInt8(value.data1 || 0, 2);
buffer.writeUInt8(value.data2 || 0, 3);
} else {
throw new Error('MIDI value must be a 4-byte Buffer or object with port, status, data1, data2 properties');
}
return buffer;
}
function readMidi(buffer, offset) {
if (offset + 4 > buffer.length) {
throw new Error('Not enough bytes for MIDI message');
}
const value = buffer.subarray(offset, offset + 4);
return { value, offset: offset + 4 };
}
function encodeArgument(arg) {
if (typeof arg === 'object' && arg.type && arg.value !== undefined) {
// Explicit type specification
switch (arg.type) {
case 'i':
case 'integer':
return { tag: 'i', data: writeInt32(arg.value) };
case 'f':
case 'float':
return { tag: 'f', data: writeFloat32(arg.value) };
case 's':
case 'string':
return { tag: 's', data: node_buffer.Buffer.from(padString(arg.value)) };
case 'b':
case 'blob':
return { tag: 'b', data: writeBlob(arg.value) };
case 'd':
case 'double':
// For doubles, use float for now (OSC 1.0 doesn't have double)
return { tag: 'f', data: writeFloat32(arg.value) };
case 'T':
return { tag: 'T', data: node_buffer.Buffer.alloc(0) };
case 'F':
return { tag: 'F', data: node_buffer.Buffer.alloc(0) };
case 'boolean':
return arg.value ? { tag: 'T', data: node_buffer.Buffer.alloc(0) } : { tag: 'F', data: node_buffer.Buffer.alloc(0) };
case 'm':
case 'midi':
return { tag: 'm', data: writeMidi(arg.value) };
default:
throw new Error(`Unknown argument type: ${arg.type}`);
}
}
// Infer type from JavaScript type
switch (typeof arg) {
case 'number':
if (Number.isInteger(arg)) {
return { tag: 'i', data: writeInt32(arg) };
} else {
return { tag: 'f', data: writeFloat32(arg) };
}
case 'string':
return { tag: 's', data: node_buffer.Buffer.from(padString(arg)) };
case 'boolean':
return arg ? { tag: 'T', data: node_buffer.Buffer.alloc(0) } : { tag: 'F', data: node_buffer.Buffer.alloc(0) };
default:
if (node_buffer.Buffer.isBuffer(arg)) {
return { tag: 'b', data: writeBlob(arg) };
}
throw new Error(`Don't know how to encode argument: ${arg}`);
}
}
function decodeArgument(tag, buffer, offset) {
switch (tag) {
case 'i':
return readInt32(buffer, offset);
case 'f':
return readFloat32(buffer, offset);
case 's':
return readString(buffer, offset);
case 'b':
return readBlob(buffer, offset);
case 'T':
return { value: true, offset };
case 'F':
return { value: false, offset };
case 'N':
return { value: null, offset };
case 'm':
return readMidi(buffer, offset);
default:
throw new Error(`I don't understand the argument code ${tag}`);
}
}
/**
* Encode an OSC message or bundle to a Buffer.
*
* This low-level function converts OSC messages and bundles into binary format
* for transmission or storage. Useful for sending OSC over custom transports
* (WebSocket, TCP, HTTP), storing to files, or implementing custom OSC routers.
*
* @param {Object} message - OSC message or bundle object with oscType property
* @returns {Buffer} The encoded OSC data ready for transmission
*
* @example
* // Encode a message
* import { Message, encode } from 'node-osc';
*
* const message = new Message('/oscillator/frequency', 440);
* const buffer = encode(message);
* console.log('Encoded bytes:', buffer.length);
*
* @example
* // Encode a bundle
* import { Bundle, encode } from 'node-osc';
*
* const bundle = new Bundle(['/one', 1], ['/two', 2]);
* const buffer = encode(bundle);
*
* @example
* // Send over WebSocket
* const buffer = encode(message);
* websocket.send(buffer);
*/
function encode(message) {
if (message.oscType === 'bundle') {
return encodeBundleToBuffer(message);
} else {
return encodeMessageToBuffer(message);
}
}
function encodeMessageToBuffer(message) {
// OSC Message format:
// Address pattern (padded string)
// Type tag string (padded string starting with ,)
// Arguments (encoded according to type tags)
const address = padString(message.address);
const addressBuffer = node_buffer.Buffer.from(address);
const encodedArgs = message.args.map(encodeArgument);
const typeTags = ',' + encodedArgs.map(arg => arg.tag).join('');
const typeTagsBuffer = node_buffer.Buffer.from(padString(typeTags));
const argumentBuffers = encodedArgs.map(arg => arg.data);
return node_buffer.Buffer.concat([addressBuffer, typeTagsBuffer, ...argumentBuffers]);
}
function encodeBundleToBuffer(bundle) {
// OSC Bundle format:
// "#bundle" (padded string)
// Timetag (8 bytes)
// Elements (each prefixed with size)
const bundleString = padString('#bundle');
const bundleStringBuffer = node_buffer.Buffer.from(bundleString);
const timetagBuffer = writeTimeTag(bundle.timetag);
const elementBuffers = bundle.elements.map(element => {
let elementBuffer;
if (element.oscType === 'bundle') {
elementBuffer = encodeBundleToBuffer(element);
} else {
elementBuffer = encodeMessageToBuffer(element);
}
const sizeBuffer = writeInt32(elementBuffer.length);
return node_buffer.Buffer.concat([sizeBuffer, elementBuffer]);
});
return node_buffer.Buffer.concat([bundleStringBuffer, timetagBuffer, ...elementBuffers]);
}
/**
* Decode a Buffer containing OSC data into a message or bundle object.
*
* This low-level function parses binary OSC data back into JavaScript objects.
* Useful for receiving OSC over custom transports, reading from files,
* or implementing custom OSC routers.
*
* @param {Buffer} buffer - The Buffer containing OSC data
* @returns {Object} The decoded OSC message or bundle. Messages have
* {oscType: 'message', address: string, args: Array}, bundles have
* {oscType: 'bundle', timetag: number, elements: Array}
* @throws {Error} If the buffer contains malformed OSC data
*
* @example
* // Decode received data
* import { decode } from 'node-osc';
*
* const decoded = decode(buffer);
* if (decoded.oscType === 'message') {
* console.log('Address:', decoded.address);
* console.log('Arguments:', decoded.args);
* }
*
* @example
* // Round-trip encode/decode
* import { Message, encode, decode } from 'node-osc';
*
* const original = new Message('/test', 42, 'hello');
* const buffer = encode(original);
* const decoded = decode(buffer);
* console.log(decoded.address); // '/test'
*/
function decode(buffer) {
// Check if it's a bundle or message
if (buffer.length >= 8 && buffer.subarray(0, 8).toString() === '#bundle\0') {
return decodeBundleFromBuffer(buffer);
} else {
return decodeMessageFromBuffer(buffer);
}
}
function decodeMessageFromBuffer(buffer) {
let offset = 0;
// Read address pattern
const addressResult = readString(buffer, offset);
const address = addressResult.value;
offset = addressResult.offset;
// Read type tag string
const typeTagsResult = readString(buffer, offset);
const typeTags = typeTagsResult.value;
offset = typeTagsResult.offset;
if (!typeTags.startsWith(',')) {
throw new Error('Malformed Packet');
}
const tags = typeTags.slice(1); // Remove leading comma
const args = [];
for (const tag of tags) {
const argResult = decodeArgument(tag, buffer, offset);
args.push({ value: argResult.value });
offset = argResult.offset;
}
return {
oscType: 'message',
address,
args
};
}
function decodeBundleFromBuffer(buffer) {
let offset = 8; // Skip "#bundle\0"
// Read timetag
const timetagResult = readTimeTag(buffer, offset);
const timetag = timetagResult.value;
offset = timetagResult.offset;
const elements = [];
while (offset < buffer.length) {
// Read element size
const sizeResult = readInt32(buffer, offset);
const size = sizeResult.value;
offset = sizeResult.offset;
if (size <= 0 || offset + size > buffer.length) {
throw new Error('Malformed Packet');
}
// Read element data
const elementBuffer = buffer.subarray(offset, offset + size);
const element = decode(elementBuffer);
elements.push(element);
offset += size;
}
return {
oscType: 'bundle',
timetag,
elements
};
}
exports.decode = decode;
exports.encode = encode;

424
backend/node_modules/node-osc/dist/test/lib/osc.js generated vendored Normal file
View File

@ -0,0 +1,424 @@
'use strict';
var node_buffer = require('node:buffer');
// OSC 1.0 Protocol Implementation
// Based on http://opensoundcontrol.org/spec-1_0
function padString(str) {
const nullTerminated = str + '\0';
const byteLength = node_buffer.Buffer.byteLength(nullTerminated);
const padding = (4 - (byteLength % 4)) % 4;
return nullTerminated + '\0'.repeat(padding);
}
function readString(buffer, offset) {
let end = offset;
while (end < buffer.length && buffer[end] !== 0) {
end++;
}
if (end >= buffer.length) {
throw new Error('Malformed Packet: Missing null terminator for string');
}
const str = buffer.subarray(offset, end).toString('utf8');
// Find next 4-byte boundary
const paddedLength = Math.ceil((end - offset + 1) / 4) * 4;
return { value: str, offset: offset + paddedLength };
}
function writeInt32(value) {
const buffer = node_buffer.Buffer.alloc(4);
buffer.writeInt32BE(value, 0);
return buffer;
}
function readInt32(buffer, offset) {
if (offset + 4 > buffer.length) {
throw new Error('Malformed Packet: Not enough bytes for int32');
}
const value = buffer.readInt32BE(offset);
return { value, offset: offset + 4 };
}
function writeFloat32(value) {
const buffer = node_buffer.Buffer.alloc(4);
buffer.writeFloatBE(value, 0);
return buffer;
}
function readFloat32(buffer, offset) {
if (offset + 4 > buffer.length) {
throw new Error('Malformed Packet: Not enough bytes for float32');
}
const value = buffer.readFloatBE(offset);
return { value, offset: offset + 4 };
}
function writeBlob(value) {
const length = value.length;
const lengthBuffer = writeInt32(length);
const padding = 4 - (length % 4);
const paddingBuffer = node_buffer.Buffer.alloc(padding === 4 ? 0 : padding);
return node_buffer.Buffer.concat([lengthBuffer, value, paddingBuffer]);
}
function readBlob(buffer, offset) {
const lengthResult = readInt32(buffer, offset);
const length = lengthResult.value;
if (length < 0) {
throw new Error('Malformed Packet: Invalid blob length');
}
if (lengthResult.offset + length > buffer.length) {
throw new Error('Malformed Packet: Not enough bytes for blob');
}
const data = buffer.subarray(lengthResult.offset, lengthResult.offset + length);
const padding = 4 - (length % 4);
const nextOffset = lengthResult.offset + length + (padding === 4 ? 0 : padding);
if (nextOffset > buffer.length) {
throw new Error('Malformed Packet: Not enough bytes for blob padding');
}
return { value: data, offset: nextOffset };
}
function writeTimeTag(value) {
// For now, treat timetag as a double (8 bytes)
// OSC timetag is 64-bit: 32-bit seconds since 1900, 32-bit fractional
const buffer = node_buffer.Buffer.alloc(8);
if (value === 0 || value === null || value === undefined) {
// Immediate execution
buffer.writeUInt32BE(0, 0);
buffer.writeUInt32BE(1, 4);
} else if (typeof value === 'number') {
// Convert to OSC timetag format
const seconds = Math.floor(value);
const fraction = Math.floor((value - seconds) * 0x100000000);
buffer.writeUInt32BE(seconds + 2208988800, 0); // Add epoch offset (1900 vs 1970)
buffer.writeUInt32BE(fraction, 4);
} else {
// If not a number, write zeros (immediate execution)
buffer.writeUInt32BE(0, 0);
buffer.writeUInt32BE(1, 4);
}
return buffer;
}
function readTimeTag(buffer, offset) {
if (offset + 8 > buffer.length) {
throw new Error('Malformed Packet: Not enough bytes for timetag');
}
const seconds = buffer.readUInt32BE(offset);
const fraction = buffer.readUInt32BE(offset + 4);
let value;
if (seconds === 0 && fraction === 1) {
// Immediate execution
value = 0;
} else {
// Convert from OSC epoch (1900) to Unix epoch (1970)
const unixSeconds = seconds - 2208988800;
const fractionalSeconds = fraction / 0x100000000;
value = unixSeconds + fractionalSeconds;
}
return { value, offset: offset + 8 };
}
function writeMidi(value) {
// MIDI message is 4 bytes: port id, status byte, data1, data2
const buffer = node_buffer.Buffer.alloc(4);
if (node_buffer.Buffer.isBuffer(value)) {
if (value.length !== 4) {
throw new Error('MIDI message must be exactly 4 bytes');
}
value.copy(buffer);
} else if (typeof value === 'object' && value !== null) {
// Allow object format: { port: 0, status: 144, data1: 60, data2: 127 }
buffer.writeUInt8(value.port || 0, 0);
buffer.writeUInt8(value.status || 0, 1);
buffer.writeUInt8(value.data1 || 0, 2);
buffer.writeUInt8(value.data2 || 0, 3);
} else {
throw new Error('MIDI value must be a 4-byte Buffer or object with port, status, data1, data2 properties');
}
return buffer;
}
function readMidi(buffer, offset) {
if (offset + 4 > buffer.length) {
throw new Error('Not enough bytes for MIDI message');
}
const value = buffer.subarray(offset, offset + 4);
return { value, offset: offset + 4 };
}
function encodeArgument(arg) {
if (typeof arg === 'object' && arg.type && arg.value !== undefined) {
// Explicit type specification
switch (arg.type) {
case 'i':
case 'integer':
return { tag: 'i', data: writeInt32(arg.value) };
case 'f':
case 'float':
return { tag: 'f', data: writeFloat32(arg.value) };
case 's':
case 'string':
return { tag: 's', data: node_buffer.Buffer.from(padString(arg.value)) };
case 'b':
case 'blob':
return { tag: 'b', data: writeBlob(arg.value) };
case 'd':
case 'double':
// For doubles, use float for now (OSC 1.0 doesn't have double)
return { tag: 'f', data: writeFloat32(arg.value) };
case 'T':
return { tag: 'T', data: node_buffer.Buffer.alloc(0) };
case 'F':
return { tag: 'F', data: node_buffer.Buffer.alloc(0) };
case 'boolean':
return arg.value ? { tag: 'T', data: node_buffer.Buffer.alloc(0) } : { tag: 'F', data: node_buffer.Buffer.alloc(0) };
case 'm':
case 'midi':
return { tag: 'm', data: writeMidi(arg.value) };
default:
throw new Error(`Unknown argument type: ${arg.type}`);
}
}
// Infer type from JavaScript type
switch (typeof arg) {
case 'number':
if (Number.isInteger(arg)) {
return { tag: 'i', data: writeInt32(arg) };
} else {
return { tag: 'f', data: writeFloat32(arg) };
}
case 'string':
return { tag: 's', data: node_buffer.Buffer.from(padString(arg)) };
case 'boolean':
return arg ? { tag: 'T', data: node_buffer.Buffer.alloc(0) } : { tag: 'F', data: node_buffer.Buffer.alloc(0) };
default:
if (node_buffer.Buffer.isBuffer(arg)) {
return { tag: 'b', data: writeBlob(arg) };
}
throw new Error(`Don't know how to encode argument: ${arg}`);
}
}
function decodeArgument(tag, buffer, offset) {
switch (tag) {
case 'i':
return readInt32(buffer, offset);
case 'f':
return readFloat32(buffer, offset);
case 's':
return readString(buffer, offset);
case 'b':
return readBlob(buffer, offset);
case 'T':
return { value: true, offset };
case 'F':
return { value: false, offset };
case 'N':
return { value: null, offset };
case 'm':
return readMidi(buffer, offset);
default:
throw new Error(`I don't understand the argument code ${tag}`);
}
}
/**
* Encode an OSC message or bundle to a Buffer.
*
* This low-level function converts OSC messages and bundles into binary format
* for transmission or storage. Useful for sending OSC over custom transports
* (WebSocket, TCP, HTTP), storing to files, or implementing custom OSC routers.
*
* @param {Object} message - OSC message or bundle object with oscType property
* @returns {Buffer} The encoded OSC data ready for transmission
*
* @example
* // Encode a message
* import { Message, encode } from 'node-osc';
*
* const message = new Message('/oscillator/frequency', 440);
* const buffer = encode(message);
* console.log('Encoded bytes:', buffer.length);
*
* @example
* // Encode a bundle
* import { Bundle, encode } from 'node-osc';
*
* const bundle = new Bundle(['/one', 1], ['/two', 2]);
* const buffer = encode(bundle);
*
* @example
* // Send over WebSocket
* const buffer = encode(message);
* websocket.send(buffer);
*/
function encode(message) {
if (message.oscType === 'bundle') {
return encodeBundleToBuffer(message);
} else {
return encodeMessageToBuffer(message);
}
}
function encodeMessageToBuffer(message) {
// OSC Message format:
// Address pattern (padded string)
// Type tag string (padded string starting with ,)
// Arguments (encoded according to type tags)
const address = padString(message.address);
const addressBuffer = node_buffer.Buffer.from(address);
const encodedArgs = message.args.map(encodeArgument);
const typeTags = ',' + encodedArgs.map(arg => arg.tag).join('');
const typeTagsBuffer = node_buffer.Buffer.from(padString(typeTags));
const argumentBuffers = encodedArgs.map(arg => arg.data);
return node_buffer.Buffer.concat([addressBuffer, typeTagsBuffer, ...argumentBuffers]);
}
function encodeBundleToBuffer(bundle) {
// OSC Bundle format:
// "#bundle" (padded string)
// Timetag (8 bytes)
// Elements (each prefixed with size)
const bundleString = padString('#bundle');
const bundleStringBuffer = node_buffer.Buffer.from(bundleString);
const timetagBuffer = writeTimeTag(bundle.timetag);
const elementBuffers = bundle.elements.map(element => {
let elementBuffer;
if (element.oscType === 'bundle') {
elementBuffer = encodeBundleToBuffer(element);
} else {
elementBuffer = encodeMessageToBuffer(element);
}
const sizeBuffer = writeInt32(elementBuffer.length);
return node_buffer.Buffer.concat([sizeBuffer, elementBuffer]);
});
return node_buffer.Buffer.concat([bundleStringBuffer, timetagBuffer, ...elementBuffers]);
}
/**
* Decode a Buffer containing OSC data into a message or bundle object.
*
* This low-level function parses binary OSC data back into JavaScript objects.
* Useful for receiving OSC over custom transports, reading from files,
* or implementing custom OSC routers.
*
* @param {Buffer} buffer - The Buffer containing OSC data
* @returns {Object} The decoded OSC message or bundle. Messages have
* {oscType: 'message', address: string, args: Array}, bundles have
* {oscType: 'bundle', timetag: number, elements: Array}
* @throws {Error} If the buffer contains malformed OSC data
*
* @example
* // Decode received data
* import { decode } from 'node-osc';
*
* const decoded = decode(buffer);
* if (decoded.oscType === 'message') {
* console.log('Address:', decoded.address);
* console.log('Arguments:', decoded.args);
* }
*
* @example
* // Round-trip encode/decode
* import { Message, encode, decode } from 'node-osc';
*
* const original = new Message('/test', 42, 'hello');
* const buffer = encode(original);
* const decoded = decode(buffer);
* console.log(decoded.address); // '/test'
*/
function decode(buffer) {
// Check if it's a bundle or message
if (buffer.length >= 8 && buffer.subarray(0, 8).toString() === '#bundle\0') {
return decodeBundleFromBuffer(buffer);
} else {
return decodeMessageFromBuffer(buffer);
}
}
function decodeMessageFromBuffer(buffer) {
let offset = 0;
// Read address pattern
const addressResult = readString(buffer, offset);
const address = addressResult.value;
offset = addressResult.offset;
// Read type tag string
const typeTagsResult = readString(buffer, offset);
const typeTags = typeTagsResult.value;
offset = typeTagsResult.offset;
if (!typeTags.startsWith(',')) {
throw new Error('Malformed Packet');
}
const tags = typeTags.slice(1); // Remove leading comma
const args = [];
for (const tag of tags) {
const argResult = decodeArgument(tag, buffer, offset);
args.push({ value: argResult.value });
offset = argResult.offset;
}
return {
oscType: 'message',
address,
args
};
}
function decodeBundleFromBuffer(buffer) {
let offset = 8; // Skip "#bundle\0"
// Read timetag
const timetagResult = readTimeTag(buffer, offset);
const timetag = timetagResult.value;
offset = timetagResult.offset;
const elements = [];
while (offset < buffer.length) {
// Read element size
const sizeResult = readInt32(buffer, offset);
const size = sizeResult.value;
offset = sizeResult.offset;
if (size <= 0 || offset + size > buffer.length) {
throw new Error('Malformed Packet');
}
// Read element data
const elementBuffer = buffer.subarray(offset, offset + size);
const element = decode(elementBuffer);
elements.push(element);
offset += size;
}
return {
oscType: 'bundle',
timetag,
elements
};
}
exports.decode = decode;
exports.encode = encode;

90
backend/node_modules/node-osc/dist/test/test-bundle.js generated vendored Normal file
View File

@ -0,0 +1,90 @@
'use strict';
var node_events = require('node:events');
var tap = require('tap');
var nodeOsc = require('node-osc');
tap.test('bundle: verbose bundle', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
const client = new nodeOsc.Client('127.0.0.1', server.port);
t.plan(2);
t.teardown(() => {
server.close();
client.close();
});
server.on('bundle', (bundle) => {
t.same(bundle.elements[0], ['/one', 1]);
t.same(bundle.elements[1], ['/two', 2]);
});
client.send(new nodeOsc.Bundle(1, {
address: '/one',
args: [
1
]
}, {
address: '/two',
args: [
2
]
}));
});
tap.test('bundle: array syntax', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
const client = new nodeOsc.Client('127.0.0.1', server.port);
t.plan(2);
t.teardown(() => {
server.close();
client.close();
});
server.on('bundle', (bundle) => {
t.same(bundle.elements[0], ['/one', 1]);
t.same(bundle.elements[1], ['/two', 2]);
});
client.send(new nodeOsc.Bundle(
['/one', 1],
['/two', 2]
));
});
tap.test('bundle: nested bundle', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
const client = new nodeOsc.Client('127.0.0.1', server.port);
t.plan(4);
t.teardown(() => {
server.close();
client.close();
});
const payload = new nodeOsc.Bundle(
['/one', 1],
['/two', 2],
['/three', 3]
);
payload.append(new nodeOsc.Bundle(10,
['/four', 4]
));
server.on('bundle', (bundle) => {
t.same(bundle.elements[0], ['/one', 1]);
t.same(bundle.elements[1], ['/two', 2]);
t.same(bundle.elements[2], ['/three', 3]);
t.same(bundle.elements[3].elements[0], ['/four', 4]);
});
client.send(payload);
});

292
backend/node_modules/node-osc/dist/test/test-client.js generated vendored Normal file
View File

@ -0,0 +1,292 @@
'use strict';
var node_events = require('node:events');
var tap = require('tap');
var nodeOsc = require('node-osc');
tap.test('client: with array', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test', 0, 1, 'testing', true], 'We should receive expected payload');
});
client.send(['/test', 0, 1, 'testing', true], (err) => {
t.error(err, 'there should be no error');
client.close();
});
});
tap.test('client: array is not mutated when sent', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(3);
const originalArray = ['/test', 0, 1, 'testing', true];
const expectedArray = ['/test', 0, 1, 'testing', true];
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test', 0, 1, 'testing', true], 'We should receive expected payload');
// Verify the original array was not mutated
t.same(originalArray, expectedArray, 'Original array should not be mutated');
});
client.send(originalArray, (err) => {
t.error(err, 'there should be no error');
client.close();
});
});
tap.test('client: with string', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test'], `We should receive expected payload: ${msg}`);
});
client.send('/test', (err) => {
t.error(err, 'there should be no error');
client.close();
});
});
tap.test('client: with Message object', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test', 1, 2, 3, 'lol', false], `we received the payload: ${msg}`);
});
client.send({
address: '/test',
args: [
1,
2,
3,
'lol',
false
]
}, (err) => {
t.error(err, 'there should be no error');
client.close();
});
});
tap.test('client: with Bundle object', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test', 1, 2, 3, 'lol', false], `we received the payload: ${msg}`);
});
client.send({
address: '/test',
args: [
1,
2,
3,
'lol',
false
]
}, (err) => {
t.error(err, 'there should be no error');
client.close();
});
});
tap.test('client: failure', async (t) => {
const client = new nodeOsc.Client('127.0.0.1', 9999);
t.plan(2);
t.throws(() => {
client.send(123, (err) => {
t.error(err, 'there should be no error');
});
});
client.close();
client.send('/boom', (err) => {
t.equal(err.code, 'ERR_SOCKET_DGRAM_NOT_RUNNING');
});
});
tap.test('client: close with callback', async (t) => {
const client = new nodeOsc.Client('127.0.0.1', 9999);
t.plan(1);
client.close((err) => {
t.error(err, 'close should not error');
});
});
tap.test('client: send bundle with non-numeric timetag', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('bundle', (bundle) => {
oscServer.close();
t.equal(bundle.timetag, 0, 'should receive immediate execution timetag as 0');
t.ok(bundle.elements.length > 0, 'should have elements');
client.close();
});
// Send bundle with non-numeric timetag (will be encoded as immediate execution)
const bundle = {
oscType: 'bundle',
timetag: 'immediate', // Non-numeric, will trigger the else branch in writeTimeTag
elements: [
{
oscType: 'message',
address: '/test1',
args: [{ type: 'i', value: 42 }]
}
]
};
client.send(bundle);
});
tap.test('client: send bundle with null timetag', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('bundle', (bundle) => {
oscServer.close();
t.equal(bundle.timetag, 0, 'should receive immediate execution timetag as 0');
t.ok(bundle.elements.length > 0, 'should have elements');
client.close();
});
// Send bundle with null timetag (will be encoded as immediate execution)
const bundle = {
oscType: 'bundle',
timetag: null, // Null, will trigger the else branch in writeTimeTag
elements: [
{
oscType: 'message',
address: '/test2',
args: [{ type: 's', value: 'hello' }]
}
]
};
client.send(bundle);
});
tap.test('client: send message with float type arg', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('message', (msg) => {
oscServer.close();
t.equal(msg[0], '/float-test', 'should receive address');
t.ok(Math.abs(msg[1] - 9.876) < 0.001, 'should receive float value');
client.close();
});
// Send raw message with 'float' type to hit that case label
client.send({
oscType: 'message',
address: '/float-test',
args: [{ type: 'float', value: 9.876 }]
});
});
tap.test('client: send message with blob type arg', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('message', (msg) => {
oscServer.close();
t.equal(msg[0], '/blob-test', 'should receive address');
t.ok(Buffer.isBuffer(msg[1]), 'should receive blob as buffer');
client.close();
});
// Send raw message with 'blob' type to hit that case label
client.send({
oscType: 'message',
address: '/blob-test',
args: [{ type: 'blob', value: Buffer.from([0xAA, 0xBB]) }]
});
});
tap.test('client: send message with double type arg', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('message', (msg) => {
oscServer.close();
t.equal(msg[0], '/double-test', 'should receive address');
t.ok(Math.abs(msg[1] - 1.23456789) < 0.001, 'should receive double value as float');
client.close();
});
// Send raw message with 'double' type to hit that case label
client.send({
oscType: 'message',
address: '/double-test',
args: [{ type: 'double', value: 1.23456789 }]
});
});
tap.test('client: send message with midi type arg', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('message', (msg) => {
oscServer.close();
t.equal(msg[0], '/midi-test', 'should receive address');
t.ok(Buffer.isBuffer(msg[1]), 'should receive MIDI as buffer');
client.close();
});
// Send raw message with 'midi' type to hit that case label
client.send({
oscType: 'message',
address: '/midi-test',
args: [{ type: 'midi', value: Buffer.from([0x00, 0x90, 0x40, 0x60]) }]
});
});

144
backend/node_modules/node-osc/dist/test/test-decode.js generated vendored Normal file
View File

@ -0,0 +1,144 @@
'use strict';
var tap = require('tap');
var decode = require('#decode');
tap.test('decode: valid', (t) => {
const buf = Buffer.from('/test\0\0\0,s\0,testing\0');
t.same(decode(buf), ['/test', 'testing'], 'should be empty array');
t.end();
});
tap.test('decode: valid', (t) => {
const buf = Buffer.from('/test\0\0\0,s\0,testing\0');
t.same(decode(buf), ['/test', 'testing'], 'should be empty array');
t.end();
});
tap.test('decode: malformed packet', (t) => {
t.throws(() => {
const buf = Buffer.from('/test\0\0');
decode(buf);
}, /Malformed Packet/);
t.end();
});
tap.test('decode: invalid typetags', (t) => {
t.throws(() => {
const buf = Buffer.from('/test\0\0\0,R\0');
decode(buf);
}, /I don't understand the argument code R/);
t.end();
});
tap.test('decode: malformed OSC structure', (t) => {
// Try to create a scenario that might trigger the "else" case in decode
// This tests an edge case where the buffer might be parsed but not create a valid OSC structure
t.throws(() => {
// Create a buffer that's too short to be valid
const buf = Buffer.from('\0\0\0\0');
decode(buf);
}, /Malformed Packet/);
t.end();
});
tap.test('decode: corrupted buffer', (t) => {
// Test with a buffer that could potentially cause fromBuffer to return unexpected results
t.throws(() => {
// Create a malformed buffer that might not parse correctly
const buf = Buffer.from('invalid');
decode(buf);
}, /(Malformed Packet|Cannot read|out of range)/);
t.end();
});
// This test attempts to exercise edge cases in the decode function
tap.test('decode: edge case with manually crafted invalid structure', (t) => {
// Since the decode function has a defensive else clause, let's try to trigger it
// by creating a buffer that might result in an unexpected object structure
// Try with an empty buffer
t.throws(() => {
const buf = Buffer.alloc(0);
decode(buf);
}, /(Malformed Packet|Cannot read|out of range)/);
// Try with a buffer containing only null bytes
t.throws(() => {
const buf = Buffer.alloc(16, 0);
decode(buf);
}, /(Malformed Packet|Cannot read|out of range)/);
t.end();
});
tap.test('decode: malformed structure with unexpected oscType', async (t) => {
// Test the defensive else clause by providing a custom fromBuffer function
// that returns an object with an invalid oscType
const mockFromBuffer = () => ({
oscType: 'invalid',
data: 'test'
});
t.throws(() => {
decode(Buffer.from('test'), mockFromBuffer);
}, /Malformed Packet/, 'should throw for invalid oscType');
// Test with undefined oscType
const mockFromBufferUndefined = () => ({
data: 'test'
// missing oscType property
});
t.throws(() => {
decode(Buffer.from('test'), mockFromBufferUndefined);
}, /Malformed Packet/, 'should throw for undefined oscType');
// Test with null oscType
const mockFromBufferNull = () => ({
oscType: null,
data: 'test'
});
t.throws(() => {
decode(Buffer.from('test'), mockFromBufferNull);
}, /Malformed Packet/, 'should throw for null oscType');
t.end();
});
tap.test('decode: message without args defaults to empty array', (t) => {
const mockFromBuffer = () => ({
oscType: 'message',
address: '/test'
});
t.same(
decode(Buffer.from('test'), mockFromBuffer),
['/test'],
'should default args to empty array'
);
t.end();
});
tap.test('decode: bundle element must be message or bundle', (t) => {
const mockFromBuffer = () => ({
oscType: 'bundle',
elements: [
{
oscType: 'message',
address: '/ok',
args: []
},
{
oscType: 'nope'
}
]
});
t.throws(() => {
decode(Buffer.from('test'), mockFromBuffer);
}, /Malformed Packet/, 'should throw for invalid bundle element');
t.end();
});

58
backend/node_modules/node-osc/dist/test/test-e2e.js generated vendored Normal file
View File

@ -0,0 +1,58 @@
'use strict';
var node_events = require('node:events');
var tap = require('tap');
var nodeOsc = require('node-osc');
function flaky() {
return process.release.lts === 'Dubnium' && process.platform === 'win32';
}
function skip(t) {
t.skip(`flaky ~ ${t.name}`);
t.end();
}
tap.test('osc: argument message no callback', async (t) => {
if (flaky()) return skip(t);
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(1);
t.teardown(() => {
oscServer.close();
client.close();
});
oscServer.on('message', (msg) => {
t.same(msg, ['/test', 1, 2, 'testing'], 'We should receive expected payload');
});
client.send('/test', 1, 2, 'testing');
});
tap.test('osc: client with callback and message as arguments', async (t) => {
if (flaky()) return skip(t);
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(2);
t.teardown(() => {
oscServer.close();
client.close();
});
oscServer.on('message', (msg) => {
t.same(msg, ['/test', 1, 2, 'testing'], 'We should receive expected payload');
});
client.send('/test', 1, 2, 'testing', (err) => {
t.error(err, 'there should be no error');
});
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,117 @@
'use strict';
var node_events = require('node:events');
var tap = require('tap');
var nodeOsc = require('node-osc');
tap.test('server: socket error event is emitted', async (t) => {
t.plan(1);
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
oscServer.on('error', (err) => {
t.ok(err, 'error event should be emitted');
oscServer.close();
});
// Simulate a socket error
oscServer._sock.emit('error', new Error('test socket error'));
});
tap.test('server: error listener can be added before listening', async (t) => {
t.plan(2);
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
oscServer.on('error', (err) => {
t.ok(err, 'error event should be emitted');
t.equal(err.message, 'socket test error', 'error message should match');
});
t.teardown(() => {
oscServer.close();
});
// Simulate a socket error
oscServer._sock.emit('error', new Error('socket test error'));
});
tap.test('client: socket error event is emitted', (t) => {
t.plan(1);
const client = new nodeOsc.Client('127.0.0.1', 9999);
client.on('error', (err) => {
t.ok(err, 'error event should be emitted');
client.close();
});
// Simulate a socket error
client._sock.emit('error', new Error('test client error'));
});
tap.test('client: error listener can be added at construction', (t) => {
t.plan(2);
const client = new nodeOsc.Client('127.0.0.1', 9999);
client.on('error', (err) => {
t.ok(err, 'error event should be emitted');
t.equal(err.message, 'client socket error', 'error message should match');
});
t.teardown(() => {
client.close();
});
// Simulate a socket error
client._sock.emit('error', new Error('client socket error'));
});
tap.test('client: is an EventEmitter instance', (t) => {
t.plan(1);
const client = new nodeOsc.Client('127.0.0.1', 9999);
t.ok(typeof client.on === 'function', 'client should have EventEmitter methods');
client.close();
});
tap.test('server: multiple error listeners can be attached', async (t) => {
t.plan(2);
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
oscServer.on('error', (err) => {
t.ok(err, 'first listener should receive error');
});
oscServer.on('error', (err) => {
t.ok(err, 'second listener should receive error');
});
t.teardown(() => {
oscServer.close();
});
// Simulate a socket error
oscServer._sock.emit('error', new Error('multi listener test'));
});
tap.test('client: multiple error listeners can be attached', (t) => {
t.plan(2);
const client = new nodeOsc.Client('127.0.0.1', 9999);
client.on('error', (err) => {
t.ok(err, 'first listener should receive error');
});
client.on('error', (err) => {
t.ok(err, 'second listener should receive error');
});
t.teardown(() => {
client.close();
});
// Simulate a socket error
client._sock.emit('error', new Error('multi listener test'));
});

403
backend/node_modules/node-osc/dist/test/test-message.js generated vendored Normal file
View File

@ -0,0 +1,403 @@
'use strict';
var node_events = require('node:events');
var tap = require('tap');
var nodeOsc = require('node-osc');
function round(num) {
return Math.round(num * 100) / 100;
}
tap.test('message: basic usage', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
const client = new nodeOsc.Client('127.0.0.1', server.port);
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new nodeOsc.Message('/address');
m.append('testing');
m.append(123);
m.append([456, 789]);
server.on('message', (msg) => {
const expected = ['/address', 'testing', 123, 456, 789];
t.same(msg, expected, `We reveived the payload: ${msg}`);
});
client.send(m);
});
tap.test('message: multiple args', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
const client = new nodeOsc.Client('127.0.0.1', server.port);
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new nodeOsc.Message('/address', 'testing', 123, true);
server.on('message', (msg) => {
const expected = ['/address', 'testing', 123, true];
t.same(msg, expected, `We reveived the payload: ${msg}`);
});
client.send(m);
});
tap.test('message: object', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
const client = new nodeOsc.Client('127.0.0.1', server.port);
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new nodeOsc.Message('/address');
m.append({
type: 'string',
value: 'test'
});
m.append({
type: 'double',
value: 100
});
server.on('message', (msg) => {
const expected = ['/address', 'test', 100];
t.same(msg, expected, `We reveived the payload: ${msg}`);
});
client.send(m);
});
tap.test('message: float', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
const client = new nodeOsc.Client('127.0.0.1', server.port);
t.plan(2);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new nodeOsc.Message('/address');
m.append(3.14);
server.on('message', (msg) => {
const expected = [
'/address',
3.14
];
t.equal(msg[0], expected[0], `We reveived the payload: ${msg}`);
t.equal(round(msg[1]), expected[1], 'pie please');
});
client.send(m);
});
tap.test('message: alias messages', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
const client = new nodeOsc.Client('127.0.0.1', server.port);
t.plan(5);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new nodeOsc.Message('/address');
m.append({
type: 'i',
value: 123
});
m.append({
type: 'f',
value: 3.14
});
server.on('message', (msg) => {
const expected = [
'/address',
123,
3.14
];
t.equal(msg[0], expected[0], `We reveived the payload: ${msg}`);
t.equal(msg[1], expected[1], 'easy as abc');
t.ok(Number.isInteger(msg[1]), 'the first value is an int');
t.equal(round(msg[2]), expected[2], 'pie please');
t.ok(msg[2] % 1 !== 0, 'the second value is a float');
});
client.send(m);
});
tap.test('message: boolean', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const client = new nodeOsc.Client('127.0.0.1', server.port);
const m = new nodeOsc.Message('/address');
m.append(true);
server.on('message', (msg) => {
const expected = [
'/address',
true
];
t.same(msg, expected, `We reveived the payload: ${msg}`);
});
client.send(m);
});
tap.test('message: blob', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
const client = new nodeOsc.Client('127.0.0.1', server.port);
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new nodeOsc.Message('/address');
const buf = Buffer.from('test');
m.append({
type: 'blob',
value: buf
});
server.on('message', (msg) => {
const expected = [
'/address',
buf
];
t.same(msg, expected, `We reveived the payload: ${msg}`);
});
client.send(m);
});
tap.test('message: Buffer as blob', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
const client = new nodeOsc.Client('127.0.0.1', server.port);
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new nodeOsc.Message('/address');
const buf = Buffer.from('test buffer data');
// Directly append Buffer without wrapping in object
m.append(buf);
server.on('message', (msg) => {
const expected = [
'/address',
buf
];
t.same(msg, expected, `We received the buffer payload: ${msg}`);
});
client.send(m);
});
// test('message: timetag', (t) => {
// const oscServer = new osc.Server(3333, '127.0.0.1');
// const client = new osc.Client('127.0.0.1', 3333);
// const m = new osc.Message('/address');
//
// oscServer.on('message', (msg) => {
// const expected = [
// '/address'
// ];
// t.same(msg, expected, `We reveived the payload: ${msg}`);
// oscServer.close();
// t.end();
// });
//
// client.send(m, () => {
// client.close();
// });
// });
tap.test('message: Buffer with multiple arguments', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
const client = new nodeOsc.Client('127.0.0.1', server.port);
t.plan(6);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new nodeOsc.Message('/address');
const buf1 = Buffer.from('first');
const buf2 = Buffer.from('second');
m.append('string');
m.append(42);
m.append(buf1);
m.append(3.14);
m.append(buf2);
server.on('message', (msg) => {
t.equal(msg[0], '/address', 'Address matches');
t.equal(msg[1], 'string', 'String matches');
t.equal(msg[2], 42, 'Integer matches');
t.same(msg[3], buf1, 'First buffer matches');
t.equal(round(msg[4]), 3.14, 'Float matches');
t.same(msg[5], buf2, 'Second buffer matches');
});
client.send(m);
});
tap.test('message: Buffer in constructor', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
const client = new nodeOsc.Client('127.0.0.1', server.port);
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const buf = Buffer.from('constructor buffer');
const m = new nodeOsc.Message('/address', 'test', buf, 123);
server.on('message', (msg) => {
const expected = [
'/address',
'test',
buf,
123
];
t.same(msg, expected, `We received the constructor buffer payload: ${msg}`);
});
client.send(m);
});
tap.test('message: Buffer in array', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
const client = new nodeOsc.Client('127.0.0.1', server.port);
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new nodeOsc.Message('/address');
const buf1 = Buffer.from('array1');
const buf2 = Buffer.from('array2');
m.append([buf1, 'string', buf2, 456]);
server.on('message', (msg) => {
const expected = [
'/address',
buf1,
'string',
buf2,
456
];
t.same(msg, expected, `We received the array with buffers: ${msg}`);
});
client.send(m);
});
tap.test('message: empty Buffer', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
const client = new nodeOsc.Client('127.0.0.1', server.port);
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new nodeOsc.Message('/address');
const buf = Buffer.from('');
m.append(buf);
server.on('message', (msg) => {
const expected = [
'/address',
buf
];
t.same(msg, expected, `We received the empty buffer: ${msg}`);
});
client.send(m);
});
tap.test('message: large Buffer', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
const client = new nodeOsc.Client('127.0.0.1', server.port);
t.plan(4);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new nodeOsc.Message('/address');
const buf = Buffer.alloc(1024, 'x');
m.append(buf);
server.on('message', (msg) => {
t.equal(msg[0], '/address', 'Address matches');
t.ok(Buffer.isBuffer(msg[1]), 'Second element is a Buffer');
t.equal(msg[1].length, 1024, 'Buffer size matches');
t.same(msg[1], buf, 'Buffer content matches');
});
client.send(m);
});
tap.test('message: error', (t) => {
const m = new nodeOsc.Message('/address');
t.plan(2);
t.throws(() => {
m.append({
lol: 'it broken'
});
}, /don't know how to encode object/);
t.throws(() => {
m.append(undefined);
}, /don't know how to encode/);
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,314 @@
'use strict';
var node_events = require('node:events');
var tap = require('tap');
var nodeOsc = require('node-osc');
tap.test('client: send with promise - array', async (t) => {
const server = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(server, 'listening');
const client = new nodeOsc.Client('127.0.0.1', server.port);
t.plan(1);
server.on('message', (msg) => {
server.close();
t.same(msg, ['/test', 0, 1, 'testing', true], 'We should receive expected payload');
});
await client.send(['/test', 0, 1, 'testing', true]);
await client.close();
});
tap.test('client: array is not mutated when sent with promise', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(2);
const originalArray = ['/test', 0, 1, 'testing', true];
const expectedArray = ['/test', 0, 1, 'testing', true];
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test', 0, 1, 'testing', true], 'We should receive expected payload');
});
await client.send(originalArray);
// Verify the original array was not mutated
t.same(originalArray, expectedArray, 'Original array should not be mutated');
await client.close();
});
tap.test('client: send with promise - string', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(1);
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test'], 'We should receive expected payload');
});
await client.send('/test');
await client.close();
});
tap.test('client: send with promise - message object', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(1);
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test', 1, 2, 3, 'lol', false], 'we received the payload');
});
await client.send({
address: '/test',
args: [1, 2, 3, 'lol', false]
});
await client.close();
});
tap.test('client: send with promise - multiple args', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(1);
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test', 1, 2, 'testing'], 'We should receive expected payload');
});
await client.send('/test', 1, 2, 'testing');
await client.close();
});
tap.test('client: send promise rejection on closed socket', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(1);
await client.close();
await oscServer.close();
try {
await client.send('/boom');
t.fail('Should have thrown an error');
} catch (err) {
t.equal(err.code, 'ERR_SOCKET_DGRAM_NOT_RUNNING', 'Should reject with correct error code');
}
});
tap.test('client: async/await usage', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(1);
const messagePromise = node_events.once(oscServer, 'message');
await client.send('/async-test', 42, 'hello');
const [receivedMessage] = await messagePromise;
t.same(receivedMessage, ['/async-test', 42, 'hello'], 'Message received via async/await');
await client.close();
await oscServer.close();
});
tap.test('server: close with promise', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
t.plan(1);
await node_events.once(oscServer, 'listening');
await oscServer.close();
t.pass('Server closed successfully with promise');
});
tap.test('server: no callback still emits listening event', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
t.plan(1);
await node_events.once(oscServer, 'listening');
t.pass('listening event emitted');
await oscServer.close();
});
tap.test('client and server: full async/await workflow', async (t) => {
t.plan(3);
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
// Wait for server to be ready
await node_events.once(oscServer, 'listening');
t.pass('Server started');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.pass('Client created');
// Set up message handler
const messageReceived = node_events.once(oscServer, 'message');
// Send message and wait for it to be received
await client.send('/workflow', 'test', 123);
const [msg] = await messageReceived;
t.same(msg, ['/workflow', 'test', 123], 'Message received correctly');
// Clean up
await client.close();
await oscServer.close();
});
tap.test('client: multiple sends with promises', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(3);
const messages = [];
oscServer.on('message', (msg) => {
messages.push(msg);
});
await client.send('/msg1', 1);
await client.send('/msg2', 2);
await client.send('/msg3', 3);
// Give a little time for all messages to be received
await new Promise((resolve) => setTimeout(resolve, 100));
t.equal(messages.length, 3, 'Received all three messages');
t.same(messages[0], ['/msg1', 1], 'First message correct');
t.same(messages[2], ['/msg3', 3], 'Last message correct');
await client.close();
await oscServer.close();
});
tap.test('client: close promise rejection on error', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', t.context.port);
t.plan(1);
// Mock the socket's close method to simulate an error
const originalClose = client._sock.close.bind(client._sock);
// Set up teardown to ensure socket is properly closed
t.teardown(() => {
// Restore original close method first
client._sock.close = originalClose;
// Then close the socket
try {
client._sock.close(() => {});
} catch {
// Socket might already be closed, that's ok
}
});
client._sock.close = function(cb) {
// Simulate an error being passed to callback
if (cb) {
const err = new Error('Mock close error');
err.code = 'MOCK_ERROR';
setImmediate(() => cb(err));
}
};
try {
await oscServer.close();
await client.close();
t.fail('Should have thrown an error');
} catch (err) {
t.equal(err.code, 'MOCK_ERROR', 'Should reject with mock error');
}
});
tap.test('server: close promise rejection on error', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
t.plan(1);
await node_events.once(oscServer, 'listening');
// Mock the socket's close method to simulate an error
const originalClose = oscServer._sock.close.bind(oscServer._sock);
// Set up teardown to ensure socket is properly closed
t.teardown(() => {
// Restore original close method first
oscServer._sock.close = originalClose;
// Then close the socket
try {
oscServer._sock.close(() => {});
} catch {
// Socket might already be closed, that's ok
}
});
oscServer._sock.close = function(cb) {
// Simulate an error being passed to callback
if (cb) {
const err = new Error('Mock close error');
err.code = 'MOCK_ERROR';
setImmediate(() => cb(err));
}
};
try {
await oscServer.close();
t.fail('Should have thrown an error');
} catch (err) {
t.equal(err.code, 'MOCK_ERROR', 'Should reject with mock error');
}
});
tap.test('client: send promise rejection on send error', async (t) => {
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(1);
// Mock the socket's send method to simulate an error
const originalSend = client._sock.send;
client._sock.send = function(msg, offset, length, port, address, callback) {
// Simulate an error being passed to callback
const err = new Error('Mock send error');
err.code = 'MOCK_SEND_ERROR';
if (callback) {
setImmediate(() => callback(err));
}
};
t.teardown(async () => {
client._sock.send = originalSend;
await client.close();
await oscServer.close();
});
try {
await client.send('/test', 'data');
t.fail('Should have thrown an error');
} catch (err) {
t.equal(err.code, 'MOCK_SEND_ERROR', 'Should reject with mock send error');
}
});

102
backend/node_modules/node-osc/dist/test/test-server.js generated vendored Normal file
View File

@ -0,0 +1,102 @@
'use strict';
var node_events = require('node:events');
var tap = require('tap');
var nodeOsc = require('node-osc');
tap.test('server: create and close', async (t) => {
t.plan(1);
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
oscServer.close((err) => {
t.error(err);
});
});
tap.test('server: listen to message', async (t) => {
const oscServer = new nodeOsc.Server(0);
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(3);
t.teardown(() => {
oscServer.close();
client.close();
});
oscServer.on('message', (msg) => {
t.same(msg, ['/test'], 'We should receive expected payload');
});
oscServer.on('/test', (msg) => {
t.same(msg, ['/test'], 'We should receive expected payload');
});
client.send('/test', (err) => {
t.error(err, 'there should be no error');
});
});
tap.test('server: no defined host', async (t) => {
const oscServer = new nodeOsc.Server(0);
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.plan(3);
t.teardown(() => {
oscServer.close();
client.close();
});
oscServer.on('message', (msg) => {
t.same(msg, ['/test'], 'We should receive expected payload');
});
oscServer.on('/test', (msg) => {
t.same(msg, ['/test'], 'We should receive expected payload');
});
client.send('/test', (err) => {
t.error(err, 'there should be no error');
});
});
tap.test('server: callback as second arg', async (t) => {
t.plan(4);
const oscServer = new nodeOsc.Server(0, () => {
t.ok('callback called');
});
await node_events.once(oscServer, 'listening');
const client = new nodeOsc.Client('127.0.0.1', oscServer.port);
t.teardown(() => {
oscServer.close();
client.close();
});
oscServer.on('message', (msg) => {
t.same(msg, ['/test'], 'We should receive expected payload');
});
oscServer.on('/test', (msg) => {
t.same(msg, ['/test'], 'We should receive expected payload');
});
client.send('/test', (err) => {
t.error(err, 'there should be no error');
});
});
tap.test('server: bad message', async (t) => {
t.plan(2);
const oscServer = new nodeOsc.Server(0, '127.0.0.1');
await node_events.once(oscServer, 'listening');
t.throws(() => {
oscServer._sock.emit('message', 'whoops');
}, /can't decode incoming message:/);
oscServer.close((err) => {
t.error(err);
});
});

42
backend/node_modules/node-osc/dist/test/test-types.js generated vendored Normal file
View File

@ -0,0 +1,42 @@
'use strict';
var tap = require('tap');
var node_child_process = require('node:child_process');
var node_path = require('node:path');
var node_url = require('node:url');
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
const __dirname$1 = node_url.fileURLToPath(new URL('.', (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('test-types.js', document.baseURI).href))));
// Only run in ESM mode (not when transpiled to CJS in dist/)
// Normalize path separators for cross-platform compatibility
const normalizedPath = __dirname$1.replace(/\\/g, '/');
const isESM = !normalizedPath.includes('/dist/');
tap.test('types: TypeScript compilation', (t) => {
let tsconfigPath;
const testRoot = node_path.resolve(__dirname$1, isESM ? '.': '../../test');
if (isESM) {
tsconfigPath = node_path.join(testRoot, 'fixtures', 'types', 'tsconfig-esm.test.json');
}
else {
tsconfigPath = node_path.join(testRoot, 'fixtures', 'types', 'tsconfig-cjs.test.json');
}
try {
// Run TypeScript compiler
const cmd = 'npx tsc --project "' + tsconfigPath + '"';
node_child_process.execSync(cmd, {
encoding: 'utf-8',
stdio: 'pipe',
cwd: node_path.join(testRoot, 'fixtures', 'types')
});
t.pass('TypeScript types compile successfully');
} catch (error) {
t.fail('TypeScript compilation failed: ' + error.message);
if (error.stdout) console.log('STDOUT:', error.stdout);
if (error.stderr) console.log('STDERR:', error.stderr);
}
t.end();
});

477
backend/node_modules/node-osc/docs/API.md generated vendored Normal file
View File

@ -0,0 +1,477 @@
<!-- Generated by JSDoc. Update this documentation by updating the source code. -->
# API Reference
> **⚠️ This file is auto-generated from JSDoc comments in the source code.**
> To update this documentation, edit the JSDoc comments in the source files and run `npm run docs`.
This document provides detailed API reference for all classes, methods, and functions in node-osc.
For usage guides, best practices, and troubleshooting, see the **[Guide](./GUIDE.md)**.
## Table of Contents
- [Server](#server)
- [Constructor](#server-constructor)
- [close()](#server-close)
- [Client](#client)
- [Constructor](#client-constructor)
- [close()](#client-close)
- [send()](#client-send)
- [Message](#message)
- [Constructor](#message-constructor)
- [append()](#message-append)
- [Bundle](#bundle)
- [Constructor](#bundle-constructor)
- [append()](#bundle-append)
- [Low Level Functions](#low-level-functions)
- [encode()](#encode)
- [decode()](#decode)
---
## Server
**Extends:** EventEmitter
OSC Server for receiving messages and bundles over UDP.
Emits the following events:
- 'listening': Emitted when the server starts listening
- 'message': Emitted when an OSC message is received (receives msg array and rinfo object)
- 'bundle': Emitted when an OSC bundle is received (receives bundle object and rinfo object)
- 'error': Emitted when a socket error or decoding error occurs (receives error and rinfo)
- Address-specific events: Emitted for each message address (e.g., '/test')
### Server Constructor
Creates a new Server instance.
**Parameters:**
- `port` *{number}* - The port to listen on.
- `host` *{string}* (optional) - Default: `'127.0.0.1'` - The host address to bind to. Use '0.0.0.0' to listen on all interfaces.
- `cb` *{function}* (optional) - Optional callback function called when server starts listening.
**Examples:**
```javascript
// Create and listen for messages
const server = new Server(3333, '0.0.0.0', () => {
console.log('Server is listening');
});
server.on('message', (msg, rinfo) => {
console.log('Message:', msg);
console.log('From:', rinfo.address, rinfo.port);
});
```
```javascript
// Using async/await with events.once
import { once } from 'node:events';
const server = new Server(3333, '0.0.0.0');
await once(server, 'listening');
server.on('message', (msg) => {
console.log('Message:', msg);
});
```
```javascript
// Listen for specific OSC addresses
server.on('/note', (msg) => {
const [address, pitch, velocity] = msg;
console.log(`Note: ${pitch}, Velocity: ${velocity}`);
});
```
### Server.close()
Close the server socket.
This method can be used with either a callback or as a Promise.
**Parameters:**
- `cb` *{function}* (optional) - Optional callback function called when socket is closed.
**Returns:** *{Promise.<void> | undefined}* - Returns a Promise if no callback is provided.
**Examples:**
```javascript
// With callback
server.close((err) => {
if (err) console.error(err);
});
```
```javascript
// With async/await
await server.close();
```
---
## Client
**Extends:** EventEmitter
OSC Client for sending messages and bundles over UDP.
Extends EventEmitter and emits the following events:
- 'error': Emitted when a socket error occurs
### Client Constructor
Creates a new Client instance.
**Parameters:**
- `host` *{string}* - The hostname or IP address of the OSC server.
- `port` *{number}* - The port number of the OSC server.
**Examples:**
```javascript
// Create a client
const client = new Client('127.0.0.1', 3333);
// Send a message with callback
client.send('/oscAddress', 200, (err) => {
if (err) console.error(err);
client.close();
});
```
```javascript
// Send a message with async/await
const client = new Client('127.0.0.1', 3333);
await client.send('/oscAddress', 200);
await client.close();
```
### Client.close()
Close the client socket.
This method can be used with either a callback or as a Promise.
**Parameters:**
- `cb` *{function}* (optional) - Optional callback function called when socket is closed.
**Returns:** *{Promise.<void> | undefined}* - Returns a Promise if no callback is provided.
**Examples:**
```javascript
// With callback
client.close((err) => {
if (err) console.error(err);
});
```
```javascript
// With async/await
await client.close();
```
### Client.send()
Send an OSC message or bundle to the server.
This method can be used with either a callback or as a Promise.
Messages can be sent in several formats:
- As separate arguments: address followed by values
- As a Message or Bundle object
- As an array: [address, ...values]
**Parameters:**
- `args` *{*}* - The message to send. Can be:
- (address: string, ...values: any[], callback?: Function)
- (message: Message|Bundle, callback?: Function)
- (array: Array, callback?: Function)
**Returns:** *{Promise.<void> | undefined}* - Returns a Promise if no callback is provided.
**Throws:**
- *{TypeError}* - If the message format is invalid.
- *{ReferenceError}* - If attempting to send on a closed socket.
**Examples:**
```javascript
// Send with address and arguments
client.send('/oscAddress', 200, 'hello', (err) => {
if (err) console.error(err);
});
```
```javascript
// Send with async/await
await client.send('/oscAddress', 200, 'hello');
```
```javascript
// Send a Message object
const msg = new Message('/test', 1, 2, 3);
await client.send(msg);
```
```javascript
// Send a Bundle object
const bundle = new Bundle(['/one', 1], ['/two', 2]);
await client.send(bundle);
```
---
## Message
Represents an OSC message with an address and arguments.
OSC messages consist of an address pattern (string starting with '/')
and zero or more arguments of various types.
### Message Constructor
Creates a new Message instance.
**Parameters:**
- `address` *{string}* - The OSC address pattern (e.g., '/oscillator/frequency').
- `args` *{*}* - Optional arguments to include in the message.
**Examples:**
```javascript
// Create a message with constructor arguments
const msg = new Message('/test', 1, 2, 'hello');
```
```javascript
// Create a message and append arguments
const msg = new Message('/test');
msg.append(1);
msg.append('hello');
msg.append(3.14);
```
### Message.append()
Append an argument to the message.
Automatically detects the type based on the JavaScript type:
- Integers are encoded as OSC integers
- Floats are encoded as OSC floats
- Strings are encoded as OSC strings
- Booleans are encoded as OSC booleans
- Buffers are encoded as OSC blobs
- Arrays are recursively appended
- Objects with a 'type' property are used as-is
**Parameters:**
- `arg` *{*}* - The argument to append. Can be:
- A primitive value (number, string, boolean)
- A Buffer (encoded as blob)
- An array of values (will be recursively appended)
- An object with 'type' and 'value' properties for explicit type control
**Throws:**
- *{Error}* - If the argument type cannot be encoded.
**Examples:**
```javascript
const msg = new Message('/test');
msg.append(42); // Integer
msg.append(3.14); // Float
msg.append('hello'); // String
msg.append(true); // Boolean
```
```javascript
// Append multiple values at once
msg.append([1, 2, 3]);
```
```javascript
// Explicitly specify type
msg.append({ type: 'float', value: 42 });
msg.append({ type: 'blob', value: Buffer.from('data') });
```
```javascript
// MIDI messages (4 bytes: port, status, data1, data2)
msg.append({ type: 'midi', value: { port: 0, status: 144, data1: 60, data2: 127 } });
msg.append({ type: 'm', value: Buffer.from([0, 144, 60, 127]) });
```
---
## Bundle
Represents an OSC bundle containing multiple messages or nested bundles.
OSC bundles allow multiple messages to be sent together, optionally with
a timetag indicating when the bundle should be processed.
### Bundle Constructor
Creates a new Bundle instance.
**Parameters:**
- `timetagOrElement` *{number | Message | Bundle | Array}* (optional) - Timetag, or if not a number, the first element and timetag will default to 0.
- `elements` *{Message | Bundle | Array}* - Messages or bundles to include.
Arrays will be automatically converted to Message objects.
**Examples:**
```javascript
// Create a bundle without a timetag
const bundle = new Bundle(['/one', 1], ['/two', 2]);
```
```javascript
// Create a bundle with a timetag
const bundle = new Bundle(10, ['/one', 1], ['/two', 2]);
```
```javascript
// Nest bundles
const bundle1 = new Bundle(['/one', 1]);
const bundle2 = new Bundle(['/two', 2]);
bundle1.append(bundle2);
```
### Bundle.append()
Append a message or bundle to this bundle.
**Parameters:**
- `element` *{Message | Bundle | Array}* - The message or bundle to append.
Arrays will be automatically converted to Message objects.
**Examples:**
```javascript
const bundle = new Bundle();
bundle.append(['/test', 1]);
bundle.append(new Message('/test2', 2));
```
```javascript
// Append a nested bundle
const bundle1 = new Bundle(['/one', 1]);
const bundle2 = new Bundle(['/two', 2]);
bundle1.append(bundle2);
```
---
## Low Level Functions
These functions provide low-level access to OSC encoding and decoding for advanced use cases.
### encode()
Encode an OSC message or bundle to a Buffer.
This low-level function converts OSC messages and bundles into binary format
for transmission or storage. Useful for sending OSC over custom transports
(WebSocket, TCP, HTTP), storing to files, or implementing custom OSC routers.
**Parameters:**
- `message` *{Object}* - OSC message or bundle object with oscType property
**Returns:** *{Buffer}* - The encoded OSC data ready for transmission
**Examples:**
```javascript
// Encode a message
import { Message, encode } from 'node-osc';
const message = new Message('/oscillator/frequency', 440);
const buffer = encode(message);
console.log('Encoded bytes:', buffer.length);
```
```javascript
// Encode a bundle
import { Bundle, encode } from 'node-osc';
const bundle = new Bundle(['/one', 1], ['/two', 2]);
const buffer = encode(bundle);
```
```javascript
// Send over WebSocket
const buffer = encode(message);
websocket.send(buffer);
```
### decode()
Decode a Buffer containing OSC data into a message or bundle object.
This low-level function parses binary OSC data back into JavaScript objects.
Useful for receiving OSC over custom transports, reading from files,
or implementing custom OSC routers.
**Parameters:**
- `buffer` *{Buffer}* - The Buffer containing OSC data
**Returns:** *{Object}* - The decoded OSC message or bundle. Messages have
{oscType: 'message', address: string, args: Array}, bundles have
{oscType: 'bundle', timetag: number, elements: Array}
**Throws:**
- *{Error}* - If the buffer contains malformed OSC data
**Examples:**
```javascript
// Decode received data
import { decode } from 'node-osc';
const decoded = decode(buffer);
if (decoded.oscType === 'message') {
console.log('Address:', decoded.address);
console.log('Arguments:', decoded.args);
}
```
```javascript
// Round-trip encode/decode
import { Message, encode, decode } from 'node-osc';
const original = new Message('/test', 42, 'hello');
const buffer = encode(original);
const decoded = decode(buffer);
console.log(decoded.address); // '/test'
```

605
backend/node_modules/node-osc/docs/GUIDE.md generated vendored Normal file
View File

@ -0,0 +1,605 @@
# node-osc Guide
This guide provides best practices, patterns, and detailed information for using node-osc effectively.
## Table of Contents
- [Events](#events)
- [Error Handling](#error-handling)
- [Type System](#type-system)
- [Best Practices](#best-practices)
- [Troubleshooting](#troubleshooting)
## Events
The `Server` class extends `EventEmitter` and emits several events for different scenarios.
### Server Events
#### `listening`
Emitted when the server starts listening for messages.
```javascript
server.on('listening', () => {
console.log('Server is ready to receive messages');
});
```
#### `message`
Emitted when an OSC message is received.
**Parameters:**
- `msg` (Array): The message as an array where the first element is the address and subsequent elements are arguments
- `rinfo` (Object): Remote address information
- `address` (string): The sender's IP address
- `port` (number): The sender's port number
```javascript
server.on('message', (msg, rinfo) => {
const [address, ...args] = msg;
console.log(`Received ${address} from ${rinfo.address}:${rinfo.port}`);
console.log('Arguments:', args);
});
```
#### `bundle`
Emitted when an OSC bundle is received.
**Parameters:**
- `bundle` (Object): The bundle object
- `timetag` (number): The bundle's timetag
- `elements` (Array): Array of messages or nested bundles
- `rinfo` (Object): Remote address information
```javascript
server.on('bundle', (bundle, rinfo) => {
console.log(`Received bundle with timetag ${bundle.timetag}`);
bundle.elements.forEach((element) => {
console.log('Element:', element);
});
});
```
#### Address-Specific Events
The server also emits events for each message address received. This allows you to listen for specific OSC addresses without filtering in your code.
```javascript
// Listen specifically for messages to /note
server.on('/note', (msg, rinfo) => {
const [address, pitch, velocity] = msg;
console.log(`Note: ${pitch}, Velocity: ${velocity}`);
});
// Listen for /oscillator/frequency
server.on('/oscillator/frequency', (msg) => {
const [address, freq] = msg;
console.log(`Frequency set to ${freq} Hz`);
});
```
#### `error`
Emitted when there's an error decoding an incoming message or a socket error.
**Parameters:**
- `error` (Error): The error object
- `rinfo` (Object): Remote address information (for decode errors)
```javascript
server.on('error', (error, rinfo) => {
if (rinfo) {
console.error(`Error from ${rinfo.address}:${rinfo.port}: ${error.message}`);
} else {
console.error('Socket error:', error.message);
}
});
```
### Client Events
#### `error`
Emitted when a socket error occurs.
```javascript
client.on('error', (error) => {
console.error('Client error:', error.message);
});
```
## Error Handling
Proper error handling is essential for robust OSC applications.
### Client Errors
#### Sending on Closed Socket
If you try to send a message after closing the client, a `ReferenceError` will be thrown:
```javascript
const client = new Client('127.0.0.1', 3333);
await client.close();
try {
await client.send('/test', 123);
} catch (err) {
console.error(err.message); // "Cannot send message on closed socket."
console.error(err.code); // "ERR_SOCKET_DGRAM_NOT_RUNNING"
}
```
**Prevention:** Always ensure the client is open before sending:
```javascript
const client = new Client('127.0.0.1', 3333);
try {
await client.send('/test', 123);
} finally {
await client.close(); // Close after sending
}
```
#### Invalid Message Format
Passing an invalid message format will throw a `TypeError`:
```javascript
try {
await client.send(12345); // Not a valid message format
} catch (err) {
console.error(err.message); // "That Message Just Doesn't Seem Right"
}
```
### Server Errors
#### Decoding Errors
When the server receives malformed OSC data, it emits an `'error'` event rather than throwing:
```javascript
server.on('error', (err, rinfo) => {
console.error(`Decode error from ${rinfo.address}: ${err.message}`);
});
```
### Error Handling Patterns
#### With Callbacks
```javascript
client.send('/test', 123, (err) => {
if (err) {
console.error('Send failed:', err);
return;
}
console.log('Message sent successfully');
});
```
#### With Async/Await
```javascript
try {
await client.send('/test', 123);
console.log('Message sent successfully');
} catch (err) {
console.error('Send failed:', err);
}
```
#### Always Close Resources
Use try/finally to ensure resources are cleaned up even if errors occur:
```javascript
const client = new Client('127.0.0.1', 3333);
try {
await client.send('/test', 123);
await client.send('/test', 456);
} catch (err) {
console.error('Error sending:', err);
} finally {
await client.close(); // Always executes
}
```
## Type System
OSC supports several data types. node-osc automatically detects types for common JavaScript values:
| JavaScript Type | OSC Type | Description |
|----------------|----------|-------------|
| Integer number | `integer` | Whole numbers (e.g., 42, -10, 0) |
| Float number | `float` | Decimal numbers (e.g., 3.14, -0.5) |
| String | `string` | Text values (e.g., "hello") |
| Boolean | `boolean` | true or false |
| Buffer | `blob` | Binary data |
| MIDI object/Buffer | `midi` | MIDI messages (4 bytes: port, status, data1, data2) |
### Automatic Type Detection
```javascript
const msg = new Message('/test');
msg.append(42); // → integer
msg.append(3.14); // → float
msg.append('hello'); // → string
msg.append(true); // → boolean
```
### Explicit Type Control
For advanced use cases, you can explicitly specify types:
```javascript
const msg = new Message('/test');
// Force a whole number to be sent as float
msg.append({ type: 'float', value: 42 });
// Use shorthand type notation
msg.append({ type: 'f', value: 42 }); // 'f' = float
msg.append({ type: 'i', value: 3.14 }); // 'i' = integer (truncates)
msg.append({ type: 's', value: 'text' }); // 's' = string
msg.append({ type: 'b', value: Buffer.from('data') }); // 'b' = blob
msg.append({ type: 'm', value: { port: 0, status: 144, data1: 60, data2: 127 } }); // 'm' = MIDI
```
### Supported Type Tags
- `'i'` or `'integer'` - 32-bit integer
- `'f'` or `'float'` - 32-bit float
- `'s'` or `'string'` - OSC string
- `'b'` or `'blob'` - Binary blob
- `'m'` or `'midi'` - MIDI message (4 bytes)
- `'boolean'` - Boolean value (true/false)
- `'T'` - True
- `'F'` - False
## Best Practices
### 1. Use Async/Await for Cleaner Code
Prefer async/await over callbacks for more readable code:
```javascript
// ✅ Good - Clean and readable
async function sendMessages() {
const client = new Client('127.0.0.1', 3333);
try {
await client.send('/test', 1);
await client.send('/test', 2);
await client.send('/test', 3);
} finally {
await client.close();
}
}
// ❌ Less ideal - Callback pyramid
function sendMessages() {
const client = new Client('127.0.0.1', 3333);
client.send('/test', 1, (err) => {
if (err) return console.error(err);
client.send('/test', 2, (err) => {
if (err) return console.error(err);
client.send('/test', 3, (err) => {
if (err) return console.error(err);
client.close();
});
});
});
}
```
### 2. Always Close Resources
Always close clients and servers when done to prevent resource leaks:
```javascript
const client = new Client('127.0.0.1', 3333);
try {
await client.send('/test', 123);
} finally {
await client.close(); // Always close
}
```
### 3. Use Address-Specific Event Listeners
For better code organization, use address-specific event listeners:
```javascript
// ✅ Good - Clear and organized
server.on('/note', (msg) => {
handleNote(msg);
});
server.on('/control', (msg) => {
handleControl(msg);
});
// ❌ Less ideal - Manual routing
server.on('message', (msg) => {
const [address] = msg;
if (address === '/note') handleNote(msg);
else if (address === '/control') handleControl(msg);
});
```
### 4. Handle Errors Gracefully
Always implement error handling for both clients and servers:
```javascript
// Client
try {
await client.send('/test', 123);
} catch (err) {
console.error('Failed to send:', err.message);
}
// Server
server.on('error', (err, rinfo) => {
console.error(`Server error from ${rinfo?.address}:`, err.message);
});
```
### 5. Use Bundles for Related Messages
When sending multiple related messages, use bundles for atomic operations:
```javascript
// ✅ Good - Atomic operation
const bundle = new Bundle(
['/synth/freq', 440],
['/synth/amp', 0.5],
['/synth/gate', 1]
);
await client.send(bundle);
// ❌ Less ideal - Separate messages (not atomic)
await client.send('/synth/freq', 440);
await client.send('/synth/amp', 0.5);
await client.send('/synth/gate', 1);
```
### 6. Listen on All Interfaces for Network Access
If you need to receive messages from other machines:
```javascript
// Listen on all interfaces (accessible from network)
const server = new Server(3333, '0.0.0.0');
// Only localhost (default, more secure)
const server = new Server(3333, '127.0.0.1');
```
### 7. Use Descriptive OSC Addresses
Follow OSC naming conventions with hierarchical addresses:
```javascript
// ✅ Good - Hierarchical and descriptive
await client.send('/synth/oscillator/1/frequency', 440);
await client.send('/mixer/channel/3/volume', 0.8);
// ❌ Less ideal - Flat and unclear
await client.send('/freq1', 440);
await client.send('/vol3', 0.8);
```
### 8. Validate Input Data
Validate data before sending to avoid runtime errors:
```javascript
function sendNote(pitch, velocity) {
if (typeof pitch !== 'number' || pitch < 0 || pitch > 127) {
throw new Error('Invalid pitch: must be 0-127');
}
if (typeof velocity !== 'number' || velocity < 0 || velocity > 127) {
throw new Error('Invalid velocity: must be 0-127');
}
return client.send('/note', pitch, velocity);
}
```
### 9. Wait for Server Ready
Always wait for the server to be listening before sending messages:
```javascript
import { once } from 'node:events';
const server = new Server(3333, '0.0.0.0');
// Wait for server to be ready
await once(server, 'listening');
// Now safe to send messages
console.log('Server ready!');
```
### 10. Use Parallel Sends When Appropriate
When sending multiple independent messages, use `Promise.all` for better performance:
```javascript
// Send multiple messages in parallel
await Promise.all([
client.send('/track/1/volume', 0.8),
client.send('/track/2/volume', 0.6),
client.send('/track/3/volume', 0.9)
]);
```
## Troubleshooting
### Messages Not Being Received
**Possible causes and solutions:**
1. **Firewall blocking UDP traffic**
- Check your firewall settings
- Ensure the UDP port is open
- Try with localhost first (`127.0.0.1`)
2. **Wrong host binding**
- Server: Use `'0.0.0.0'` to listen on all interfaces
- Server: Use `'127.0.0.1'` for localhost only
- Client: Match the server's IP address
3. **Port mismatch**
- Ensure client and server use the same port number
- Check if another process is using the port
4. **Network connectivity**
- Test with localhost first (`127.0.0.1`)
- Verify network connectivity between machines
- Check if devices are on the same network
### "Cannot send message on closed socket"
This error occurs when trying to send after closing the client:
```javascript
// ❌ Wrong - Sending after close
await client.close();
await client.send('/test', 123); // Error!
// ✅ Correct - Send before close
await client.send('/test', 123);
await client.close();
```
### Server Not Listening
Ensure you wait for the server to start before sending messages:
```javascript
const server = new Server(3333, '0.0.0.0');
// Wait for server to be ready
await new Promise(resolve => server.on('listening', resolve));
// Now safe to send messages
console.log('Server ready!');
```
### Messages Lost or Dropped
UDP is unreliable by design and messages can be lost:
**Solutions:**
1. Use TCP-based OSC if reliability is critical (requires custom implementation)
2. Implement acknowledgment messages
3. Add retry logic for critical messages
4. Use bundles to ensure related messages arrive together
### High CPU Usage
If you're seeing high CPU usage:
1. **Check for infinite loops** in event handlers
2. **Rate limit** message sending if sending many messages
3. **Use bundles** instead of many individual messages
4. **Close unused connections** to free resources
### Memory Leaks
To prevent memory leaks:
1. **Always close** clients and servers when done
2. **Remove event listeners** when no longer needed
3. **Avoid** creating new clients/servers in loops
4. **Reuse** client/server instances when possible
```javascript
// ✅ Good - Proper cleanup
const server = new Server(3333);
const handler = (msg) => console.log(msg);
server.on('message', handler);
// Later, clean up
server.removeListener('message', handler);
await server.close();
```
## Advanced Topics
### Custom Transports
The `encode` and `decode` functions allow you to use OSC over custom transports:
```javascript
import { encode, decode, Message } from 'node-osc';
import WebSocket from 'ws';
// WebSocket server
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
ws.on('message', (data) => {
const oscMsg = decode(data);
console.log('Received:', oscMsg);
});
});
// WebSocket client
const ws = new WebSocket('ws://localhost:8080');
const message = new Message('/test', 123);
ws.send(encode(message));
```
### Timetags in Bundles
OSC bundles support timetags for scheduling:
```javascript
// Immediate execution (timetag = 0)
const bundle = new Bundle(['/test', 1], ['/test', 2]);
// Scheduled execution (timetag in OSC time)
const futureTime = Date.now() / 1000 + 5; // 5 seconds from now
const scheduledBundle = new Bundle(futureTime, ['/test', 1]);
```
**Note:** The server receives the timetag but does not automatically schedule execution. You must implement scheduling logic if needed.
### Performance Optimization
For high-throughput applications:
1. **Reuse client instances** instead of creating new ones
2. **Use bundles** to send multiple messages together
3. **Batch messages** and send periodically rather than immediately
4. **Use binary blobs** for large data instead of many arguments
5. **Profile your code** to identify bottlenecks
```javascript
// ✅ Good - Reuse client
const client = new Client('127.0.0.1', 3333);
for (let i = 0; i < 1000; i++) {
await client.send('/test', i);
}
await client.close();
// ❌ Bad - Creating many clients
for (let i = 0; i < 1000; i++) {
const client = new Client('127.0.0.1', 3333);
await client.send('/test', i);
await client.close();
}
```
## Further Reading
- [API Documentation](./API.md) - Complete API reference
- [OSC Specification](http://opensoundcontrol.org/spec-1_0) - Official OSC 1.0 specification
- [Examples](../examples/) - Working code examples
- [Main README](../README.md) - Quick start guide

81
backend/node_modules/node-osc/docs/README.md generated vendored Normal file
View File

@ -0,0 +1,81 @@
# node-osc Documentation
Welcome to the node-osc documentation! This directory contains comprehensive documentation for the node-osc library.
## Documentation Overview
### 📚 [API Reference](./API.md)
**Complete API documentation for all classes and functions**
Auto-generated from JSDoc comments in the source code. This is your reference for:
- All classes: `Server`, `Client`, `Message`, `Bundle`
- All methods and their parameters
- Low-level functions: `encode()` and `decode()`
- Return types and error conditions
- Code examples for each API
> **Note:** This file is automatically generated. To update it, edit the JSDoc comments in the source code and run `npm run docs`.
### 📘 [Usage Guide](./GUIDE.md)
**Best practices, patterns, and troubleshooting**
A comprehensive guide covering:
- Event handling patterns
- Error handling strategies
- OSC type system details
- Best practices for production use
- Troubleshooting common issues
- Advanced topics like custom transports and performance optimization
## Quick Navigation
**New to node-osc?**
1. Start with the [main README](../README.md) for a quick introduction and installation
2. Try the [examples](../examples/) to see working code
3. Read the [Usage Guide](./GUIDE.md) to learn best practices
4. Reference the [API documentation](./API.md) as needed
**Looking for something specific?**
- **How to send/receive messages** → [API Reference](./API.md) (Server and Client sections)
- **How to handle errors** → [Usage Guide](./GUIDE.md#error-handling)
- **Type system and data types** → [Usage Guide](./GUIDE.md#type-system)
- **Working with bundles** → [API Reference](./API.md#bundle)
- **Troubleshooting** → [Usage Guide](./GUIDE.md#troubleshooting)
- **Code examples** → [Examples directory](../examples/)
- **Advanced use cases** → [Usage Guide](./GUIDE.md#advanced-topics)
## Additional Resources
- **[Examples](../examples/)** - Working code examples for various use cases
- **[Main README](../README.md)** - Quick start and project overview
- **[OSC Specification](http://opensoundcontrol.org/spec-1_0)** - Official OSC protocol documentation
## Contributing to Documentation
### Updating API Documentation
The API documentation is automatically generated from JSDoc comments:
1. Edit JSDoc comments in the source files (`lib/**/*.mjs`)
2. Run `npm run docs` to regenerate `API.md`
3. Review the changes and commit
### Updating the Usage Guide
The Usage Guide (`GUIDE.md`) is manually maintained. When editing:
- Keep it focused on patterns, best practices, and how-to content
- Avoid duplicating API details (link to API.md instead)
- Include practical code examples
- Update the table of contents if adding new sections
## Documentation Structure Philosophy
Our documentation is organized to minimize duplication while maximizing usefulness:
- **README.md** (main) → Quick start, basic examples, installation
- **API.md** → Complete API reference with all technical details
- **GUIDE.md** → How to use the library effectively, patterns, and troubleshooting
- **examples/** → Working code you can run and learn from
This structure ensures you can find what you need without reading through repeated content.

8
backend/node_modules/node-osc/eslint.config.mjs generated vendored Normal file
View File

@ -0,0 +1,8 @@
import globals from "globals";
import pluginJs from "@eslint/js";
export default [
{languageOptions: { globals: globals.node }},
pluginJs.configs.recommended,
];

121
backend/node_modules/node-osc/examples/README.md generated vendored Normal file
View File

@ -0,0 +1,121 @@
# node-osc Examples
This directory contains working examples demonstrating various ways to use the node-osc library.
## Running the Examples
All examples can be run directly with Node.js. Some examples require a server and client running simultaneously.
### Server/Client Examples
Run the server in one terminal:
```bash
node examples/server.js
```
Run the client in another terminal:
```bash
node examples/client.js
```
### Standalone Examples
These examples run both client and server in the same process:
```bash
# Callback-based example
node examples/esm.mjs
# Async/await example (recommended)
node examples/async-await.mjs
# Bundle example
node examples/bundle-example.mjs
# Error handling example
node examples/error-handling.mjs
```
## Example Files
### [server.js](./server.js)
**CommonJS Server Example**
Demonstrates:
- Creating an OSC server with CommonJS
- Listening for messages
- Displaying remote client information
- Closing the server after receiving a message
### [client.js](./client.js)
**CommonJS Client Example**
Demonstrates:
- Creating an OSC client with CommonJS
- Building messages with the Message class
- Sending messages with callbacks
### [esm.mjs](./esm.mjs)
**ESM with Callbacks Example**
Demonstrates:
- Using node-osc with ES modules
- Callback-based API
- Server event listeners
- Client sending with callbacks
### [async-await.mjs](./async-await.mjs)
**ESM with Async/Await Example** (Recommended Pattern)
Demonstrates:
- Modern async/await patterns
- Using `node:events.once()` to wait for server ready
- Sending multiple messages in parallel with `Promise.all()`
- Clean shutdown of both client and server
- Complete end-to-end workflow
Expected output:
```
OSC server listening on port 3333
Sent /hello
Sent counters
Received: /hello [ 'world' ]
Received: /counter [ 1 ]
Received: /counter [ 2 ]
Received: /counter [ 3 ]
Client and server closed
```
### [bundle-example.mjs](./bundle-example.mjs)
**OSC Bundles Example**
Demonstrates:
- Creating OSC bundles with multiple messages
- Sending bundles atomically
- Receiving and processing bundles
- Using timetags
### [error-handling.mjs](./error-handling.mjs)
**Error Handling Example**
Demonstrates:
- Proper error handling with try/catch
- Server error events
- Resource cleanup with finally blocks
- Handling closed socket errors
## Tips
1. **Always close resources**: Use try/finally or ensure you call `.close()` on clients and servers
2. **Wait for server ready**: Use the 'listening' event before sending messages
3. **Use async/await**: The async/await pattern is cleaner than callbacks for most use cases
4. **Handle errors**: Always implement error handling in production code
5. **Test locally first**: Start with `127.0.0.1` before trying network communication
## Further Reading
- [Main README](../README.md) - Quick start guide
- [Documentation Hub](../docs/) - Complete documentation with navigation guide
- [API Reference](../docs/API.md) - Complete API reference
- [Usage Guide](../docs/GUIDE.md) - Best practices and troubleshooting
- [OSC Specification](http://opensoundcontrol.org/spec-1_0) - Learn about the OSC protocol

57
backend/node_modules/node-osc/examples/async-await.mjs generated vendored Normal file
View File

@ -0,0 +1,57 @@
/**
* OSC Client and Server Example (ESM with Async/Await)
*
* This is the recommended pattern for modern Node.js applications.
* It demonstrates:
* - Using async/await for cleaner async code
* - Properly waiting for server to be ready
* - Sending multiple messages in parallel
* - Clean shutdown of resources
*
* To run this example:
* node examples/async-await.mjs
*/
// Example: Using async/await with node-osc Client and Server (ESM)
import { once } from "node:events";
import { setImmediate } from "node:timers/promises";
import { Client, Server } from "node-osc";
// Create server on all interfaces, port 3333
const server = new Server(3333, "0.0.0.0");
// Wait for server to be ready using once() - cleaner than event listeners
await once(server, "listening");
console.log("OSC server listening on port 3333");
// Set up message handler
// Messages arrive as arrays: [address, ...arguments]
server.on("message", (msg) => {
const [address, ...args] = msg;
console.log("Received:", address, args);
});
// Create client pointing to localhost
const client = new Client("127.0.0.1", 3333);
// Send a simple message
await client.send("/hello", "world");
console.log("Sent /hello");
// Send multiple messages in parallel using Promise.all()
await Promise.all([
client.send("/counter", 1),
client.send("/counter", 2),
client.send("/counter", 3),
]);
console.log("Sent counters");
// Allow socket I/O to be processed before shutting down
await setImmediate();
// Clean shutdown - always close resources
await client.close();
await server.close();
console.log("Client and server closed");

View File

@ -0,0 +1,92 @@
/**
* OSC Bundle Example
*
* This example demonstrates how to create and send OSC bundles.
* Bundles allow you to group multiple messages together, optionally
* with a timetag for synchronized processing.
*
* To run this example:
* node examples/bundle-example.mjs
*/
import { once } from "node:events";
import { setImmediate } from "node:timers/promises";
import { Bundle, Client, Message, Server } from "node-osc";
// Start server
const server = new Server(3333, "0.0.0.0");
await once(server, "listening");
console.log("Server listening on port 3333\n");
// Handle bundles specifically
server.on("bundle", (bundle, rinfo) => {
console.log(`📦 Received bundle from ${rinfo.address}:${rinfo.port}`);
console.log(` Timetag: ${bundle.timetag}`);
console.log(` Elements: ${bundle.elements.length}`);
// Process each element in the bundle
bundle.elements.forEach((element, i) => {
if (element.oscType === 'message') {
const [address, ...args] = element;
console.log(` ${i + 1}. ${address}: ${args.join(', ')}`);
} else if (element.oscType === 'bundle') {
console.log(` ${i + 1}. [Nested Bundle]`);
}
});
console.log();
});
// Create client
const client = new Client("127.0.0.1", 3333);
// Example 1: Bundle without timetag (array notation)
console.log("Sending bundle without timetag...");
const bundle1 = new Bundle(
["/synth/freq", 440],
["/synth/amp", 0.5],
["/synth/gate", 1]
);
await client.send(bundle1);
await setImmediate();
// Example 2: Bundle with timetag
console.log("Sending bundle with timetag...");
const bundle2 = new Bundle(
10, // timetag
["/oscillator/1/freq", 220],
["/oscillator/2/freq", 330]
);
await client.send(bundle2);
await setImmediate();
// Example 3: Bundle with Message objects
console.log("Sending bundle with Message objects...");
const msg1 = new Message("/note", 60, 127);
const msg2 = new Message("/note", 64, 127);
const msg3 = new Message("/note", 67, 127);
const bundle3 = new Bundle(msg1, msg2, msg3);
await client.send(bundle3);
await setImmediate();
// Example 4: Nested bundles
console.log("Sending nested bundles...");
const innerBundle = new Bundle(["/inner/message", 123]);
const outerBundle = new Bundle(["/outer/message", 456]);
outerBundle.append(innerBundle);
await client.send(outerBundle);
await setImmediate();
// Example 5: Building a bundle incrementally
console.log("Sending incrementally built bundle...");
// Create bundle with initial element, then append more
const bundle5 = new Bundle(["/initial", 0]);
bundle5.append(["/control/1", 10]);
bundle5.append(["/control/2", 20]);
bundle5.append(["/control/3", 30]);
await client.send(bundle5);
await setImmediate();
// Clean shutdown
await client.close();
await server.close();
console.log("Done!");

40
backend/node_modules/node-osc/examples/client.js generated vendored Normal file
View File

@ -0,0 +1,40 @@
/**
* OSC Client Example (CommonJS)
*
* This example demonstrates how to create an OSC client and send messages
* using the Message class with callbacks.
*
* To run this example:
* 1. Start the server: node examples/server.js
* 2. Run this client: node examples/client.js
*/
'use strict';
const { Client, Message } = require('node-osc');
// Create a client connected to localhost on port 3333
const client = new Client('127.0.0.1', 3333);
// Create a message using the Message class
const message = new Message('/address');
message.append('testing'); // Append a string
message.append('testing'); // Append the same string again (for demonstration)
message.append(123); // Append an integer
// Send the message with a callback
client.send(message, (err) => {
if (err) {
console.error(new Error(err));
}
// Always close the client when done
client.close();
});
// Alternative ways to send messages:
// Method 1: Send address and arguments directly
// client.send('/address', 'testing', 'testing', 123);
// Method 2: Create message with constructor arguments
// const msg = new Message('/address', 1, 2, 3);
// client.send(msg);

View File

@ -0,0 +1,152 @@
/**
* OSC Error Handling Example
*
* This example demonstrates proper error handling patterns with node-osc,
* including handling server errors, client errors, and ensuring cleanup.
*
* To run this example:
* node examples/error-handling.mjs
*/
import { once } from "node:events";
import { Client, Server } from "node-osc";
console.log("=== OSC Error Handling Examples ===\n");
// Example 1: Server decode errors
console.log("1. Testing server decode error handling...");
const server = new Server(3333, "0.0.0.0");
// Set up error handler for server
server.on("error", (err, rinfo) => {
console.error(`❌ Server error from ${rinfo.address}:${rinfo.port}`);
console.error(` ${err.message}`);
});
await once(server, "listening");
console.log("✅ Server started successfully\n");
// Example 2: Try/catch with async/await
console.log("2. Testing client send with try/catch...");
const client = new Client("127.0.0.1", 3333);
try {
await client.send("/test", 123, "hello", true);
console.log("✅ Message sent successfully\n");
} catch (err) {
console.error(`❌ Failed to send message: ${err.message}\n`);
}
// Example 3: Error when sending on closed socket
console.log("3. Testing send on closed socket...");
await client.close();
console.log(" Client closed");
try {
await client.send("/test", 456);
console.log("✅ Message sent (this shouldn't happen)\n");
} catch (err) {
console.log(`✅ Caught expected error: ${err.message}`);
console.log(` Error code: ${err.code}\n`);
}
// Example 4: Try/finally for cleanup
console.log("4. Testing try/finally for guaranteed cleanup...");
const client2 = new Client("127.0.0.1", 3333);
try {
await client2.send("/cleanup/test", 789);
console.log("✅ Message sent");
// Simulate an error
throw new Error("Simulated error");
} catch (err) {
console.log(`⚠️ Caught error: ${err.message}`);
} finally {
// This always runs, even if there was an error
await client2.close();
console.log("✅ Client closed in finally block\n");
}
// Example 5: Callback-based error handling
console.log("5. Testing callback-based error handling...");
const client3 = new Client("127.0.0.1", 3333);
await new Promise((resolve) => {
client3.send("/callback/test", 999, (err) => {
if (err) {
console.error(`❌ Send error: ${err}`);
} else {
console.log("✅ Message sent via callback");
}
client3.close((err) => {
if (err) {
console.error(`❌ Close error: ${err}`);
} else {
console.log("✅ Client closed via callback\n");
}
resolve();
});
});
});
// Example 6: Multiple operations with proper error handling
console.log("6. Testing multiple operations with error handling...");
const client4 = new Client("127.0.0.1", 3333);
try {
// Send multiple messages
const results = await Promise.allSettled([
client4.send("/multi/1", 1),
client4.send("/multi/2", 2),
client4.send("/multi/3", 3),
]);
// Check results
results.forEach((result, i) => {
if (result.status === "fulfilled") {
console.log(`✅ Message ${i + 1} sent successfully`);
} else {
console.error(`❌ Message ${i + 1} failed: ${result.reason}`);
}
});
} catch (err) {
console.error(`❌ Unexpected error: ${err.message}`);
} finally {
await client4.close();
console.log("✅ Client closed\n");
}
// Example 7: Server error event
console.log("7. Testing server message handling with error check...");
let messageReceived = false;
server.on("message", (msg) => {
messageReceived = true;
const [address, ...args] = msg;
console.log(`✅ Received: ${address} with args: ${args.join(", ")}`);
});
const client5 = new Client("127.0.0.1", 3333);
await client5.send("/final/test", "done");
await client5.close();
// Wait a bit for message to be received
await new Promise(resolve => setTimeout(resolve, 100));
if (!messageReceived) {
console.log("⚠️ Warning: Message was not received");
}
// Clean shutdown
await server.close();
console.log("\n✅ All tests complete, server closed");
console.log("\n=== Key Takeaways ===");
console.log("1. Always use try/catch with async/await");
console.log("2. Use try/finally to ensure cleanup");
console.log("3. Listen for 'error' events on servers");
console.log("4. Check for errors in callbacks");
console.log("5. Don't send on closed sockets");
console.log("6. Use Promise.allSettled for multiple operations");

39
backend/node_modules/node-osc/examples/esm.mjs generated vendored Normal file
View File

@ -0,0 +1,39 @@
/**
* OSC Client and Server Example (ESM with Callbacks)
*
* This example demonstrates using node-osc with ES modules and callback-based API.
* It shows how to create both a client and server, send messages, and handle events.
*
* To run this example:
* node examples/esm.mjs
*/
import { Client, Server } from 'node-osc';
// Create a client connected to localhost on port 3333
const client = new Client('127.0.0.1', 3333);
// Create a server listening on port 3333, bound to all interfaces
var server = new Server(3333, '0.0.0.0');
// Listen for when the server is ready
server.on('listening', () => {
console.log('OSC Server is Listening');
});
// Listen for incoming messages
server.on('message', (msg, rinfo) => {
// msg is an array: [address, ...arguments]
console.log(`Message: ${msg}\nReceived from: ${rinfo.address}:${rinfo.port}`);
// Close the server after receiving a message
server.close();
});
// Send a message with callback-based API
client.send('/hello', 'world', (err) => {
if (err) console.error(err);
// Close the client after sending
client.close();
});

25
backend/node_modules/node-osc/examples/server.js generated vendored Normal file
View File

@ -0,0 +1,25 @@
/**
* OSC Server Example (CommonJS)
*
* This example demonstrates how to create an OSC server that listens for
* incoming messages and displays them along with sender information.
*
* To run this example:
* 1. Start this server: node examples/server.js
* 2. Send messages from client: node examples/client.js
*/
'use strict';
var { Server } = require('node-osc');
// Create a server listening on port 3333, bound to all interfaces
var oscServer = new Server(3333, '0.0.0.0');
// Listen for incoming OSC messages
oscServer.on('message', function (msg, rinfo) {
// msg is an array: [address, ...arguments]
console.log(`Message: ${msg}\nReceived from: ${rinfo.address}:${rinfo.port}`);
// Close the server after receiving one message (for demo purposes)
oscServer.close();
});

16
backend/node_modules/node-osc/jsdoc.json generated vendored Normal file
View File

@ -0,0 +1,16 @@
{
"source": {
"include": [
"lib/Server.mjs",
"lib/Client.mjs",
"lib/Bundle.mjs",
"lib/Message.mjs",
"lib/osc.mjs"
],
"includePattern": ".+\\.mjs$"
},
"opts": {
"encoding": "utf8",
"recurse": false
}
}

90
backend/node_modules/node-osc/lib/Bundle.mjs generated vendored Normal file
View File

@ -0,0 +1,90 @@
import Message from './Message.mjs';
/**
* Convert array notation to Message object.
* @private
* @param {Array|Message|Bundle} element - The element to sanitize.
* @returns {Message|Bundle} The sanitized element.
*/
function sanitize(element) {
if (element instanceof Array) element = new Message(element[0], ...element.slice(1));
return element;
}
/**
* Represents an OSC bundle containing multiple messages or nested bundles.
*
* OSC bundles allow multiple messages to be sent together, optionally with
* a timetag indicating when the bundle should be processed.
*
* @class
*
* @example
* // Create a bundle without a timetag
* const bundle = new Bundle(['/one', 1], ['/two', 2]);
*
* @example
* // Create a bundle with a timetag
* const bundle = new Bundle(10, ['/one', 1], ['/two', 2]);
*
* @example
* // Nest bundles
* const bundle1 = new Bundle(['/one', 1]);
* const bundle2 = new Bundle(['/two', 2]);
* bundle1.append(bundle2);
*/
class Bundle {
/**
* Create an OSC Bundle.
*
* @param {number|Message|Bundle|Array} [timetagOrElement=0] - Timetag, or if not a number, the first element and timetag will default to 0.
* @param {...(Message|Bundle|Array)} elements - Messages or bundles to include.
* Arrays will be automatically converted to Message objects.
*
* @example
* // Bundle without timetag
* const bundle = new Bundle(['/test', 1], ['/test2', 2]);
*
* @example
* // Bundle with timetag of 10
* const bundle = new Bundle(10, ['/test', 1]);
*
* @example
* // Bundle with Message objects
* const msg1 = new Message('/one', 1);
* const msg2 = new Message('/two', 2);
* const bundle = new Bundle(msg1, msg2);
*/
constructor(timetag, ...elements) {
if (!(typeof timetag === 'number')) {
elements.unshift(timetag);
timetag = 0;
}
this.oscType = 'bundle';
this.timetag = timetag;
this.elements = elements.map(sanitize);
}
/**
* Append a message or bundle to this bundle.
*
* @param {Message|Bundle|Array} element - The message or bundle to append.
* Arrays will be automatically converted to Message objects.
*
* @example
* const bundle = new Bundle();
* bundle.append(['/test', 1]);
* bundle.append(new Message('/test2', 2));
*
* @example
* // Append a nested bundle
* const bundle1 = new Bundle(['/one', 1]);
* const bundle2 = new Bundle(['/two', 2]);
* bundle1.append(bundle2);
*/
append(element) {
this.elements.push(sanitize(element));
}
}
export default Bundle;

178
backend/node_modules/node-osc/lib/Client.mjs generated vendored Normal file
View File

@ -0,0 +1,178 @@
import { createSocket } from 'node:dgram';
import { EventEmitter } from 'node:events';
import { encode } from './osc.mjs';
import Message from './Message.mjs';
/**
* OSC Client for sending messages and bundles over UDP.
*
* Extends EventEmitter and emits the following events:
* - 'error': Emitted when a socket error occurs
*
* @class
* @extends EventEmitter
* @example
* // Create a client
* const client = new Client('127.0.0.1', 3333);
*
* // Send a message with callback
* client.send('/oscAddress', 200, (err) => {
* if (err) console.error(err);
* client.close();
* });
*
* @example
* // Send a message with async/await
* const client = new Client('127.0.0.1', 3333);
* await client.send('/oscAddress', 200);
* await client.close();
*/
class Client extends EventEmitter {
/**
* Create an OSC Client.
*
* @param {string} host - The hostname or IP address of the OSC server.
* @param {number} port - The port number of the OSC server.
*
* @example
* const client = new Client('127.0.0.1', 3333);
*/
constructor(host, port) {
super();
this.host = host;
this.port = port;
this._sock = createSocket({
type: 'udp4',
reuseAddr: true
});
this._sock.on('error', (err) => {
this.emit('error', err);
});
}
/**
* Close the client socket.
*
* This method can be used with either a callback or as a Promise.
*
* @param {Function} [cb] - Optional callback function called when socket is closed.
* @returns {Promise<void>|undefined} Returns a Promise if no callback is provided.
*
* @example
* // With callback
* client.close((err) => {
* if (err) console.error(err);
* });
*
* @example
* // With async/await
* await client.close();
*/
close(cb) {
if (cb) {
this._sock.close(cb);
} else {
return new Promise((resolve, reject) => {
this._sock.close((err) => {
if (err) reject(err);
else resolve();
});
});
}
}
_performSend(message, args, callback) {
let mes;
let buf;
try {
switch (typeof message) {
case 'object':
buf = encode(message);
this._sock.send(buf, 0, buf.length, this.port, this.host, callback);
break;
case 'string':
mes = new Message(args[0]);
for (let i = 1; i < args.length; i++) {
mes.append(args[i]);
}
buf = encode(mes);
this._sock.send(buf, 0, buf.length, this.port, this.host, callback);
break;
default:
throw new TypeError('That Message Just Doesn\'t Seem Right');
}
}
catch (e) {
if (e.code !== 'ERR_SOCKET_DGRAM_NOT_RUNNING') throw e;
const error = new ReferenceError('Cannot send message on closed socket.');
error.code = e.code;
callback(error);
}
}
/**
* Send an OSC message or bundle to the server.
*
* This method can be used with either a callback or as a Promise.
* Messages can be sent in several formats:
* - As separate arguments: address followed by values
* - As a Message or Bundle object
* - As an array: [address, ...values]
*
* @param {...*} args - The message to send. Can be:
* - (address: string, ...values: any[], callback?: Function)
* - (message: Message|Bundle, callback?: Function)
* - (array: Array, callback?: Function)
* @returns {Promise<void>|undefined} Returns a Promise if no callback is provided.
*
* @throws {TypeError} If the message format is invalid.
* @throws {ReferenceError} If attempting to send on a closed socket.
*
* @example
* // Send with address and arguments
* client.send('/oscAddress', 200, 'hello', (err) => {
* if (err) console.error(err);
* });
*
* @example
* // Send with async/await
* await client.send('/oscAddress', 200, 'hello');
*
* @example
* // Send a Message object
* const msg = new Message('/test', 1, 2, 3);
* await client.send(msg);
*
* @example
* // Send a Bundle object
* const bundle = new Bundle(['/one', 1], ['/two', 2]);
* await client.send(bundle);
*/
send(...args) {
let message = args[0];
let callback;
// Convert array syntax to message object
if (message instanceof Array) {
message = {
address: message[0],
args: message.slice(1)
};
}
if (typeof args[args.length - 1] === 'function') {
callback = args.pop();
this._performSend(message, args, callback);
}
else {
// No callback provided, return a Promise
return new Promise((resolve, reject) => {
callback = (err) => {
if (err) reject(err);
else resolve();
};
this._performSend(message, args, callback);
});
}
}
}
export default Client;

143
backend/node_modules/node-osc/lib/Message.mjs generated vendored Normal file
View File

@ -0,0 +1,143 @@
const typeTags = {
s: 'string',
f: 'float',
i: 'integer',
b: 'blob',
m: 'midi'
};
/**
* Represents a typed argument for an OSC message.
*
* @class
* @private
*/
class Argument {
/**
* @param {string} type - The type of the argument (string, float, integer, blob, boolean).
* @param {*} value - The value of the argument.
*/
constructor(type, value) {
this.type = type;
this.value = value;
}
}
/**
* Represents an OSC message with an address and arguments.
*
* OSC messages consist of an address pattern (string starting with '/')
* and zero or more arguments of various types.
*
* @class
*
* @example
* // Create a message with constructor arguments
* const msg = new Message('/test', 1, 2, 'hello');
*
* @example
* // Create a message and append arguments
* const msg = new Message('/test');
* msg.append(1);
* msg.append('hello');
* msg.append(3.14);
*/
class Message {
/**
* Create an OSC Message.
*
* @param {string} address - The OSC address pattern (e.g., '/oscillator/frequency').
* @param {...*} args - Optional arguments to include in the message.
*
* @example
* const msg = new Message('/test');
*
* @example
* const msg = new Message('/test', 1, 2, 3);
*
* @example
* const msg = new Message('/synth', 'note', 60, 0.5);
*/
constructor(address, ...args) {
this.oscType = 'message';
this.address = address;
this.args = args;
}
/**
* Append an argument to the message.
*
* Automatically detects the type based on the JavaScript type:
* - Integers are encoded as OSC integers
* - Floats are encoded as OSC floats
* - Strings are encoded as OSC strings
* - Booleans are encoded as OSC booleans
* - Buffers are encoded as OSC blobs
* - Arrays are recursively appended
* - Objects with a 'type' property are used as-is
*
* @param {*} arg - The argument to append. Can be:
* - A primitive value (number, string, boolean)
* - A Buffer (encoded as blob)
* - An array of values (will be recursively appended)
* - An object with 'type' and 'value' properties for explicit type control
*
* @throws {Error} If the argument type cannot be encoded.
*
* @example
* const msg = new Message('/test');
* msg.append(42); // Integer
* msg.append(3.14); // Float
* msg.append('hello'); // String
* msg.append(true); // Boolean
*
* @example
* // Append multiple values at once
* msg.append([1, 2, 3]);
*
* @example
* // Explicitly specify type
* msg.append({ type: 'float', value: 42 });
* msg.append({ type: 'blob', value: Buffer.from('data') });
*
* @example
* // MIDI messages (4 bytes: port, status, data1, data2)
* msg.append({ type: 'midi', value: { port: 0, status: 144, data1: 60, data2: 127 } });
* msg.append({ type: 'm', value: Buffer.from([0, 144, 60, 127]) });
*/
append(arg) {
let argOut;
switch (typeof arg) {
case 'object':
if (Buffer.isBuffer(arg)) {
this.args.push(arg);
} else if (arg instanceof Array) {
arg.forEach(a => this.append(a));
} else if (arg.type) {
if (typeTags[arg.type]) arg.type = typeTags[arg.type];
this.args.push(arg);
} else {
throw new Error(`don't know how to encode object ${arg}`);
}
break;
case 'number':
if (Math.floor(arg) === arg) {
argOut = new Argument('integer', arg);
} else {
argOut = new Argument('float', arg);
}
break;
case 'string':
argOut = new Argument('string', arg);
break;
case 'boolean':
argOut = new Argument('boolean', arg);
break;
default:
throw new Error(`don't know how to encode ${arg}`);
}
if (argOut) this.args.push(argOut);
}
}
export default Message;

155
backend/node_modules/node-osc/lib/Server.mjs generated vendored Normal file
View File

@ -0,0 +1,155 @@
import { createSocket } from 'node:dgram';
import { EventEmitter } from 'node:events';
import decode from '#decode';
/**
* OSC Server for receiving messages and bundles over UDP.
*
* Emits the following events:
* - 'listening': Emitted when the server starts listening
* - 'message': Emitted when an OSC message is received (receives msg array and rinfo object)
* - 'bundle': Emitted when an OSC bundle is received (receives bundle object and rinfo object)
* - 'error': Emitted when a socket error or decoding error occurs (receives error and rinfo)
* - Address-specific events: Emitted for each message address (e.g., '/test')
*
* @class
* @extends EventEmitter
*
* @fires Server#listening
* @fires Server#message
* @fires Server#bundle
* @fires Server#error
*
* @example
* // Create and listen for messages
* const server = new Server(3333, '0.0.0.0', () => {
* console.log('Server is listening');
* });
*
* server.on('message', (msg, rinfo) => {
* console.log('Message:', msg);
* console.log('From:', rinfo.address, rinfo.port);
* });
*
* @example
* // Using async/await with events.once
* import { once } from 'node:events';
*
* const server = new Server(3333, '0.0.0.0');
* await once(server, 'listening');
*
* server.on('message', (msg) => {
* console.log('Message:', msg);
* });
*
* @example
* // Listen for specific OSC addresses
* server.on('/note', (msg) => {
* const [address, pitch, velocity] = msg;
* console.log(`Note: ${pitch}, Velocity: ${velocity}`);
* });
*/
class Server extends EventEmitter {
/**
* Create an OSC Server.
*
* @param {number} port - The port to listen on.
* @param {string} [host='127.0.0.1'] - The host address to bind to. Use '0.0.0.0' to listen on all interfaces.
* @param {Function} [cb] - Optional callback function called when server starts listening.
*
* @example
* // Basic server
* const server = new Server(3333);
*
* @example
* // Server on all interfaces with callback
* const server = new Server(3333, '0.0.0.0', () => {
* console.log('Server started');
* });
*
* @example
* // Host parameter can be omitted, callback as second parameter
* const server = new Server(3333, () => {
* console.log('Server started on 127.0.0.1');
* });
*/
constructor(port, host='127.0.0.1', cb) {
super();
if (typeof host === 'function') {
cb = host;
host = '127.0.0.1';
}
let decoded;
this.port = port;
this.host = host;
this._sock = createSocket({
type: 'udp4',
reuseAddr: true
});
this._sock.bind(port, host);
// Update port and emit listening event when socket is ready
this._sock.on('listening', () => {
// Update port with actual bound port (important when using port 0)
this.port = this._sock.address().port;
this.emit('listening');
if (cb) cb();
});
this._sock.on('message', (msg, rinfo) => {
try {
decoded = decode(msg);
}
catch (e) {
const error = new Error(`can't decode incoming message: ${e.message}`);
this.emit('error', error, rinfo);
return;
}
if (decoded.elements) {
this.emit('bundle', decoded, rinfo);
}
else if (decoded) {
this.emit('message', decoded, rinfo);
this.emit(decoded[0], decoded, rinfo);
}
});
this._sock.on('error', (err) => {
this.emit('error', err);
});
}
/**
* Close the server socket.
*
* This method can be used with either a callback or as a Promise.
*
* @param {Function} [cb] - Optional callback function called when socket is closed.
* @returns {Promise<void>|undefined} Returns a Promise if no callback is provided.
*
* @example
* // With callback
* server.close((err) => {
* if (err) console.error(err);
* });
*
* @example
* // With async/await
* await server.close();
*/
close(cb) {
if (cb) {
this._sock.close(cb);
} else {
return new Promise((resolve, reject) => {
this._sock.close((err) => {
if (err) reject(err);
else resolve();
});
});
}
}
}
export default Server;

5
backend/node_modules/node-osc/lib/index.mjs generated vendored Normal file
View File

@ -0,0 +1,5 @@
export { default as Message } from './Message.mjs';
export { default as Bundle } from './Bundle.mjs';
export { default as Server } from './Server.mjs';
export { default as Client } from './Client.mjs';
export { encode, decode } from './osc.mjs';

35
backend/node_modules/node-osc/lib/internal/decode.mjs generated vendored Normal file
View File

@ -0,0 +1,35 @@
import { decode } from '../osc.mjs';
function sanitizeMessage(decoded) {
const message = [];
message.push(decoded.address);
const args = decoded.args ?? [];
args.forEach(arg => {
message.push(arg.value);
});
return message;
}
function sanitizeBundle(decoded) {
decoded.elements = decoded.elements.map(element => {
if (element.oscType === 'bundle') return sanitizeBundle(element);
else if (element.oscType === 'message') return sanitizeMessage(element);
throw new Error('Malformed Packet');
});
return decoded;
}
function decodeAndSanitize(data, customDecode = decode) {
const decoded = customDecode(data);
if (decoded.oscType === 'bundle') {
return sanitizeBundle(decoded);
}
else if (decoded.oscType === 'message') {
return sanitizeMessage(decoded);
}
else {
throw new Error ('Malformed Packet');
}
}
export default decodeAndSanitize;

422
backend/node_modules/node-osc/lib/osc.mjs generated vendored Normal file
View File

@ -0,0 +1,422 @@
// OSC 1.0 Protocol Implementation
// Based on http://opensoundcontrol.org/spec-1_0
// Helper functions for OSC encoding/decoding
import { Buffer } from 'node:buffer';
function padString(str) {
const nullTerminated = str + '\0';
const byteLength = Buffer.byteLength(nullTerminated);
const padding = (4 - (byteLength % 4)) % 4;
return nullTerminated + '\0'.repeat(padding);
}
function readString(buffer, offset) {
let end = offset;
while (end < buffer.length && buffer[end] !== 0) {
end++;
}
if (end >= buffer.length) {
throw new Error('Malformed Packet: Missing null terminator for string');
}
const str = buffer.subarray(offset, end).toString('utf8');
// Find next 4-byte boundary
const paddedLength = Math.ceil((end - offset + 1) / 4) * 4;
return { value: str, offset: offset + paddedLength };
}
function writeInt32(value) {
const buffer = Buffer.alloc(4);
buffer.writeInt32BE(value, 0);
return buffer;
}
function readInt32(buffer, offset) {
if (offset + 4 > buffer.length) {
throw new Error('Malformed Packet: Not enough bytes for int32');
}
const value = buffer.readInt32BE(offset);
return { value, offset: offset + 4 };
}
function writeFloat32(value) {
const buffer = Buffer.alloc(4);
buffer.writeFloatBE(value, 0);
return buffer;
}
function readFloat32(buffer, offset) {
if (offset + 4 > buffer.length) {
throw new Error('Malformed Packet: Not enough bytes for float32');
}
const value = buffer.readFloatBE(offset);
return { value, offset: offset + 4 };
}
function writeBlob(value) {
const length = value.length;
const lengthBuffer = writeInt32(length);
const padding = 4 - (length % 4);
const paddingBuffer = Buffer.alloc(padding === 4 ? 0 : padding);
return Buffer.concat([lengthBuffer, value, paddingBuffer]);
}
function readBlob(buffer, offset) {
const lengthResult = readInt32(buffer, offset);
const length = lengthResult.value;
if (length < 0) {
throw new Error('Malformed Packet: Invalid blob length');
}
if (lengthResult.offset + length > buffer.length) {
throw new Error('Malformed Packet: Not enough bytes for blob');
}
const data = buffer.subarray(lengthResult.offset, lengthResult.offset + length);
const padding = 4 - (length % 4);
const nextOffset = lengthResult.offset + length + (padding === 4 ? 0 : padding);
if (nextOffset > buffer.length) {
throw new Error('Malformed Packet: Not enough bytes for blob padding');
}
return { value: data, offset: nextOffset };
}
function writeTimeTag(value) {
// For now, treat timetag as a double (8 bytes)
// OSC timetag is 64-bit: 32-bit seconds since 1900, 32-bit fractional
const buffer = Buffer.alloc(8);
if (value === 0 || value === null || value === undefined) {
// Immediate execution
buffer.writeUInt32BE(0, 0);
buffer.writeUInt32BE(1, 4);
} else if (typeof value === 'number') {
// Convert to OSC timetag format
const seconds = Math.floor(value);
const fraction = Math.floor((value - seconds) * 0x100000000);
buffer.writeUInt32BE(seconds + 2208988800, 0); // Add epoch offset (1900 vs 1970)
buffer.writeUInt32BE(fraction, 4);
} else {
// If not a number, write zeros (immediate execution)
buffer.writeUInt32BE(0, 0);
buffer.writeUInt32BE(1, 4);
}
return buffer;
}
function readTimeTag(buffer, offset) {
if (offset + 8 > buffer.length) {
throw new Error('Malformed Packet: Not enough bytes for timetag');
}
const seconds = buffer.readUInt32BE(offset);
const fraction = buffer.readUInt32BE(offset + 4);
let value;
if (seconds === 0 && fraction === 1) {
// Immediate execution
value = 0;
} else {
// Convert from OSC epoch (1900) to Unix epoch (1970)
const unixSeconds = seconds - 2208988800;
const fractionalSeconds = fraction / 0x100000000;
value = unixSeconds + fractionalSeconds;
}
return { value, offset: offset + 8 };
}
function writeMidi(value) {
// MIDI message is 4 bytes: port id, status byte, data1, data2
const buffer = Buffer.alloc(4);
if (Buffer.isBuffer(value)) {
if (value.length !== 4) {
throw new Error('MIDI message must be exactly 4 bytes');
}
value.copy(buffer);
} else if (typeof value === 'object' && value !== null) {
// Allow object format: { port: 0, status: 144, data1: 60, data2: 127 }
buffer.writeUInt8(value.port || 0, 0);
buffer.writeUInt8(value.status || 0, 1);
buffer.writeUInt8(value.data1 || 0, 2);
buffer.writeUInt8(value.data2 || 0, 3);
} else {
throw new Error('MIDI value must be a 4-byte Buffer or object with port, status, data1, data2 properties');
}
return buffer;
}
function readMidi(buffer, offset) {
if (offset + 4 > buffer.length) {
throw new Error('Not enough bytes for MIDI message');
}
const value = buffer.subarray(offset, offset + 4);
return { value, offset: offset + 4 };
}
function encodeArgument(arg) {
if (typeof arg === 'object' && arg.type && arg.value !== undefined) {
// Explicit type specification
switch (arg.type) {
case 'i':
case 'integer':
return { tag: 'i', data: writeInt32(arg.value) };
case 'f':
case 'float':
return { tag: 'f', data: writeFloat32(arg.value) };
case 's':
case 'string':
return { tag: 's', data: Buffer.from(padString(arg.value)) };
case 'b':
case 'blob':
return { tag: 'b', data: writeBlob(arg.value) };
case 'd':
case 'double':
// For doubles, use float for now (OSC 1.0 doesn't have double)
return { tag: 'f', data: writeFloat32(arg.value) };
case 'T':
return { tag: 'T', data: Buffer.alloc(0) };
case 'F':
return { tag: 'F', data: Buffer.alloc(0) };
case 'boolean':
return arg.value ? { tag: 'T', data: Buffer.alloc(0) } : { tag: 'F', data: Buffer.alloc(0) };
case 'm':
case 'midi':
return { tag: 'm', data: writeMidi(arg.value) };
default:
throw new Error(`Unknown argument type: ${arg.type}`);
}
}
// Infer type from JavaScript type
switch (typeof arg) {
case 'number':
if (Number.isInteger(arg)) {
return { tag: 'i', data: writeInt32(arg) };
} else {
return { tag: 'f', data: writeFloat32(arg) };
}
case 'string':
return { tag: 's', data: Buffer.from(padString(arg)) };
case 'boolean':
return arg ? { tag: 'T', data: Buffer.alloc(0) } : { tag: 'F', data: Buffer.alloc(0) };
default:
if (Buffer.isBuffer(arg)) {
return { tag: 'b', data: writeBlob(arg) };
}
throw new Error(`Don't know how to encode argument: ${arg}`);
}
}
function decodeArgument(tag, buffer, offset) {
switch (tag) {
case 'i':
return readInt32(buffer, offset);
case 'f':
return readFloat32(buffer, offset);
case 's':
return readString(buffer, offset);
case 'b':
return readBlob(buffer, offset);
case 'T':
return { value: true, offset };
case 'F':
return { value: false, offset };
case 'N':
return { value: null, offset };
case 'm':
return readMidi(buffer, offset);
default:
throw new Error(`I don't understand the argument code ${tag}`);
}
}
/**
* Encode an OSC message or bundle to a Buffer.
*
* This low-level function converts OSC messages and bundles into binary format
* for transmission or storage. Useful for sending OSC over custom transports
* (WebSocket, TCP, HTTP), storing to files, or implementing custom OSC routers.
*
* @param {Object} message - OSC message or bundle object with oscType property
* @returns {Buffer} The encoded OSC data ready for transmission
*
* @example
* // Encode a message
* import { Message, encode } from 'node-osc';
*
* const message = new Message('/oscillator/frequency', 440);
* const buffer = encode(message);
* console.log('Encoded bytes:', buffer.length);
*
* @example
* // Encode a bundle
* import { Bundle, encode } from 'node-osc';
*
* const bundle = new Bundle(['/one', 1], ['/two', 2]);
* const buffer = encode(bundle);
*
* @example
* // Send over WebSocket
* const buffer = encode(message);
* websocket.send(buffer);
*/
function encode(message) {
if (message.oscType === 'bundle') {
return encodeBundleToBuffer(message);
} else {
return encodeMessageToBuffer(message);
}
}
function encodeMessageToBuffer(message) {
// OSC Message format:
// Address pattern (padded string)
// Type tag string (padded string starting with ,)
// Arguments (encoded according to type tags)
const address = padString(message.address);
const addressBuffer = Buffer.from(address);
const encodedArgs = message.args.map(encodeArgument);
const typeTags = ',' + encodedArgs.map(arg => arg.tag).join('');
const typeTagsBuffer = Buffer.from(padString(typeTags));
const argumentBuffers = encodedArgs.map(arg => arg.data);
return Buffer.concat([addressBuffer, typeTagsBuffer, ...argumentBuffers]);
}
function encodeBundleToBuffer(bundle) {
// OSC Bundle format:
// "#bundle" (padded string)
// Timetag (8 bytes)
// Elements (each prefixed with size)
const bundleString = padString('#bundle');
const bundleStringBuffer = Buffer.from(bundleString);
const timetagBuffer = writeTimeTag(bundle.timetag);
const elementBuffers = bundle.elements.map(element => {
let elementBuffer;
if (element.oscType === 'bundle') {
elementBuffer = encodeBundleToBuffer(element);
} else {
elementBuffer = encodeMessageToBuffer(element);
}
const sizeBuffer = writeInt32(elementBuffer.length);
return Buffer.concat([sizeBuffer, elementBuffer]);
});
return Buffer.concat([bundleStringBuffer, timetagBuffer, ...elementBuffers]);
}
/**
* Decode a Buffer containing OSC data into a message or bundle object.
*
* This low-level function parses binary OSC data back into JavaScript objects.
* Useful for receiving OSC over custom transports, reading from files,
* or implementing custom OSC routers.
*
* @param {Buffer} buffer - The Buffer containing OSC data
* @returns {Object} The decoded OSC message or bundle. Messages have
* {oscType: 'message', address: string, args: Array}, bundles have
* {oscType: 'bundle', timetag: number, elements: Array}
* @throws {Error} If the buffer contains malformed OSC data
*
* @example
* // Decode received data
* import { decode } from 'node-osc';
*
* const decoded = decode(buffer);
* if (decoded.oscType === 'message') {
* console.log('Address:', decoded.address);
* console.log('Arguments:', decoded.args);
* }
*
* @example
* // Round-trip encode/decode
* import { Message, encode, decode } from 'node-osc';
*
* const original = new Message('/test', 42, 'hello');
* const buffer = encode(original);
* const decoded = decode(buffer);
* console.log(decoded.address); // '/test'
*/
function decode(buffer) {
// Check if it's a bundle or message
if (buffer.length >= 8 && buffer.subarray(0, 8).toString() === '#bundle\0') {
return decodeBundleFromBuffer(buffer);
} else {
return decodeMessageFromBuffer(buffer);
}
}
function decodeMessageFromBuffer(buffer) {
let offset = 0;
// Read address pattern
const addressResult = readString(buffer, offset);
const address = addressResult.value;
offset = addressResult.offset;
// Read type tag string
const typeTagsResult = readString(buffer, offset);
const typeTags = typeTagsResult.value;
offset = typeTagsResult.offset;
if (!typeTags.startsWith(',')) {
throw new Error('Malformed Packet');
}
const tags = typeTags.slice(1); // Remove leading comma
const args = [];
for (const tag of tags) {
const argResult = decodeArgument(tag, buffer, offset);
args.push({ value: argResult.value });
offset = argResult.offset;
}
return {
oscType: 'message',
address,
args
};
}
function decodeBundleFromBuffer(buffer) {
let offset = 8; // Skip "#bundle\0"
// Read timetag
const timetagResult = readTimeTag(buffer, offset);
const timetag = timetagResult.value;
offset = timetagResult.offset;
const elements = [];
while (offset < buffer.length) {
// Read element size
const sizeResult = readInt32(buffer, offset);
const size = sizeResult.value;
offset = sizeResult.offset;
if (size <= 0 || offset + size > buffer.length) {
throw new Error('Malformed Packet');
}
// Read element data
const elementBuffer = buffer.subarray(offset, offset + size);
const element = decode(elementBuffer);
elements.push(element);
offset += size;
}
return {
oscType: 'bundle',
timetag,
elements
};
}
export { encode, decode };

58
backend/node_modules/node-osc/package.json generated vendored Normal file
View File

@ -0,0 +1,58 @@
{
"name": "node-osc",
"description": "pyOSC inspired library for sending and receiving OSC messages",
"version": "11.2.2",
"exports": {
"types": "./types/index.d.mts",
"require": "./dist/lib/index.js",
"import": "./lib/index.mjs",
"default": "./lib/index.mjs"
},
"imports": {
"#decode": {
"require": "./dist/lib/internal/decode.js",
"default": "./lib/internal/decode.mjs"
}
},
"author": {
"name": "Myles Borins",
"email": "myles.borins@gmail.com"
},
"engines": {
"node": "^20.9.0 || ^22.11.0 || >=24.0.0"
},
"license": "Apache-2.0",
"scripts": {
"clean": "rm -rf dist/ types/",
"build": "npm run clean && rollup --config rollup.config.mjs && npm run build:types",
"build:types": "tsc",
"docs": "node scripts/generate-docs.mjs",
"prepublishOnly": "npm run build",
"lint": "eslint \"lib/**/*.mjs\" \"test/test-*.mjs\" \"examples/*.js\" \"examples/*.mjs\" \"scripts/*.mjs\" rollup.config.mjs",
"test": "npm run lint && npm run build && npm run test:esm && npm run test:cjs",
"test:esm": "tap test/test-*.mjs",
"test:cjs": "tap dist/test/test-*.js"
},
"contributors": [
"Hans Hübner <hans.huebner@gmail.com>",
"Andy Smith <jabber@term.ie>",
"Myles Borins <myles.borins@gmail.com>"
],
"keywords": [
"osc",
"udp"
],
"repository": {
"type": "git",
"url": "git+https://github.com/MylesBorins/node-osc.git"
},
"devDependencies": {
"@eslint/js": "^9.32.0",
"eslint": "^9.32.0",
"globals": "^16.3.0",
"jsdoc": "^4.0.5",
"rollup": "^4.46.2",
"tap": "^21.1.0",
"typescript": "^5.9.3"
}
}

85
backend/node_modules/node-osc/rollup.config.mjs generated vendored Normal file
View File

@ -0,0 +1,85 @@
import { readdirSync as readdir, statSync as stat } from 'fs';
import { join } from 'path';
function walk(root, result=[]) {
const rootURL = new URL(root, import.meta.url);
const paths = readdir(rootURL);
for (const path of paths) {
const stats = stat(new URL(path, rootURL));
if (stats.isDirectory()) {
walk(`${root}${path}/`, result);
}
else {
result.push({
input: join(root, path),
dir: join('dist/', root)
});
}
}
return result;
}
function walkLib(config) {
// Build all lib files in a single pass
const files = walk('./lib/');
config.push({
input: files.map(f => f.input),
output: {
entryFileNames: '[name].js',
dir: 'dist/lib',
format: 'cjs',
preserveModules: true,
preserveModulesRoot: 'lib',
exports: 'auto'
},
external: [
'node:dgram',
'node:events',
'node:buffer',
'jspack',
'#decode'
]
});
}
function walkTest(config) {
// Build all test files in a single pass, excluding fixtures
const tests = walk('./test/').filter(t => {
// Normalize path separators to work on both Unix and Windows
const normalizedPath = t.input.replace(/\\/g, '/');
return !normalizedPath.includes('/fixtures/');
});
config.push({
input: tests.map(t => t.input),
plugins: [],
output: {
entryFileNames: '[name].js',
dir: 'dist/test',
format: 'cjs',
exports: 'auto',
preserveModules: true,
preserveModulesRoot: 'test'
},
external: [
'node:dgram',
'node:net',
'node:buffer',
'node:events',
'node:child_process',
'node:fs',
'node:path',
'node:url',
'node:timers/promises',
'node-osc',
'tap',
'#decode'
]
});
}
const config = [];
walkLib(config);
walkTest(config);
export default config;

229
backend/node_modules/node-osc/scripts/generate-docs.mjs generated vendored Executable file
View File

@ -0,0 +1,229 @@
#!/usr/bin/env node
/**
* Converts JSDoc JSON output to Markdown documentation.
* This script reads JSDoc JSON data and generates a formatted Markdown file.
*/
import { writeFileSync } from 'node:fs';
import { execSync } from 'node:child_process';
// Generate JSDoc JSON
let jsdocJson;
try {
jsdocJson = execSync('npx jsdoc -X -c jsdoc.json', {
encoding: 'utf8',
maxBuffer: 10 * 1024 * 1024
});
} catch (error) {
console.error('❌ Failed to run JSDoc:');
console.error(error.message);
process.exit(1);
}
let docs;
try {
docs = JSON.parse(jsdocJson);
} catch (error) {
console.error('❌ Failed to parse JSDoc JSON output:');
console.error(error.message);
process.exit(1);
}
// Filter and organize documentation
const classes = {};
const functions = {};
docs.forEach(item => {
if (item.undocumented || item.ignore) return;
if (item.kind === 'class' && item.classdesc) {
if (!classes[item.name]) {
classes[item.name] = {
desc: item.classdesc,
constructor: null,
methods: [],
examples: item.examples || [],
augments: item.augments || []
};
}
// Look for constructor params
if (item.params) {
classes[item.name].constructor = {
params: item.params,
examples: item.examples || []
};
}
} else if (item.kind === 'function' && item.memberof) {
// Method of a class
const className = item.memberof;
if (!classes[className]) {
classes[className] = {
desc: '',
constructor: null,
methods: [],
examples: []
};
}
classes[className].methods.push(item);
} else if (item.kind === 'function' && !item.memberof && item.scope === 'global') {
// Top-level function
functions[item.name] = item;
}
});
// Generate Markdown
let markdown = `<!-- Generated by JSDoc. Update this documentation by updating the source code. -->
# API Reference
> **⚠️ This file is auto-generated from JSDoc comments in the source code.**
> To update this documentation, edit the JSDoc comments in the source files and run \`npm run docs\`.
This document provides detailed API reference for all classes, methods, and functions in node-osc.
For usage guides, best practices, and troubleshooting, see the **[Guide](./GUIDE.md)**.
## Table of Contents
`;
// Define order: Server → Client → Message → Bundle → Low Level
const classOrder = ['Server', 'Client', 'Message', 'Bundle'];
const functionOrder = ['encode', 'decode'];
// Add classes to TOC
classOrder.forEach(name => {
if (classes[name]) {
markdown += `- [${name}](#${name.toLowerCase()})\n`;
if (classes[name].constructor) {
markdown += ` - [Constructor](#${name.toLowerCase()}-constructor)\n`;
}
classes[name].methods.forEach(method => {
markdown += ` - [${method.name}()](#${name.toLowerCase()}-${method.name.toLowerCase()})\n`;
});
}
});
// Add functions to TOC
markdown += `- [Low Level Functions](#low-level-functions)\n`;
functionOrder.forEach(name => {
if (functions[name]) {
markdown += ` - [${name}()](#${name.toLowerCase()})\n`;
}
});
markdown += `\n---\n\n`;
// Helper function to format parameters
function formatParams(params) {
if (!params || params.length === 0) return '';
let result = '\n**Parameters:**\n\n';
params.forEach(param => {
const optional = param.optional ? ' (optional)' : '';
const defaultVal = param.defaultvalue ? ` - Default: \`${param.defaultvalue}\`` : '';
const types = param.type ? param.type.names.join(' | ') : 'any';
result += `- \`${param.name}\` *{${types}}*${optional}${defaultVal} - ${param.description || ''}\n`;
});
return result;
}
// Helper function to format examples
function formatExamples(examples) {
if (!examples || examples.length === 0) return '';
let result = '\n**Examples:**\n\n';
examples.forEach(example => {
result += '```javascript\n' + example + '\n```\n\n';
});
return result;
}
// Helper function to format returns
function formatReturns(returns) {
if (!returns || returns.length === 0) return '';
const ret = returns[0];
const types = ret.type ? ret.type.names.join(' | ') : 'any';
return `\n**Returns:** *{${types}}* - ${ret.description || ''}\n`;
}
// Helper function to format throws
function formatThrows(exceptions) {
if (!exceptions || exceptions.length === 0) return '';
let result = '\n**Throws:**\n\n';
exceptions.forEach(ex => {
const types = ex.type ? ex.type.names.join(' | ') : 'Error';
result += `- *{${types}}* - ${ex.description || ''}\n`;
});
return result;
}
// Generate class documentation
classOrder.forEach(className => {
const classInfo = classes[className];
if (!classInfo) return;
markdown += `## ${className}\n\n`;
// Add extends info
if (classInfo.augments && classInfo.augments.length > 0) {
markdown += `**Extends:** ${classInfo.augments.join(', ')}\n\n`;
}
markdown += `${classInfo.desc}\n`;
// Class-level examples
if (classInfo.examples.length > 0 && !classInfo.constructor) {
markdown += formatExamples(classInfo.examples);
}
// Constructor
if (classInfo.constructor) {
markdown += `\n### ${className} Constructor\n\n`;
markdown += `Creates a new ${className} instance.\n`;
markdown += formatParams(classInfo.constructor.params);
markdown += formatExamples(classInfo.constructor.examples);
}
// Methods
classInfo.methods.forEach(method => {
markdown += `\n### ${className}.${method.name}()\n\n`;
markdown += `${method.description || ''}\n`;
markdown += formatParams(method.params);
markdown += formatReturns(method.returns);
markdown += formatThrows(method.exceptions);
markdown += formatExamples(method.examples);
});
markdown += `\n---\n\n`;
});
// Generate function documentation
markdown += `## Low Level Functions\n\n`;
markdown += `These functions provide low-level access to OSC encoding and decoding for advanced use cases.\n\n`;
functionOrder.forEach(funcName => {
const func = functions[funcName];
if (!func) return;
markdown += `### ${funcName}()\n\n`;
markdown += `${func.description || ''}\n`;
markdown += formatParams(func.params);
markdown += formatReturns(func.returns);
markdown += formatThrows(func.exceptions);
markdown += formatExamples(func.examples);
markdown += `\n`;
});
// Write output
try {
writeFileSync('docs/API.md', markdown, 'utf8');
console.log('✅ API documentation generated: docs/API.md');
} catch (error) {
console.error('❌ Failed to write API.md:');
console.error(error.message);
process.exit(1);
}

View File

@ -0,0 +1,19 @@
// Test CJS-style TypeScript imports
import type { Client, Server, Message, Bundle } from 'node-osc';
const osc = require('node-osc');
// Create server first (typical usage pattern)
const server: Server = new osc.Server(3333, '0.0.0.0');
// Create client after server
const client: Client = new osc.Client('127.0.0.1', 3333);
// Test Message type
const message: Message = new osc.Message('/test', 1, 2, 3);
// Test Bundle type
const bundle: Bundle = new osc.Bundle(['/one', 1]);
// Test encode/decode with consistent type annotations
const encoded: Buffer = osc.encode(message);
const decoded: Object = osc.decode(encoded);

View File

@ -0,0 +1,35 @@
// Test ESM TypeScript imports with Top-Level Await
import { once } from 'node:events';
import { Client, Server, Message, Bundle, encode, decode } from 'node-osc';
// Create server first (typical usage pattern)
const server: Server = new Server(3333, '0.0.0.0');
// Wait for server to be ready (pattern from examples)
await once(server, 'listening');
server.on('message', (msg) => {
console.log('Received message:', msg);
});
// Create client after server
const client: Client = new Client('127.0.0.1', 3333);
// Test async usage with Top-Level Await (ESM feature)
await client.send('/test', 1, 2, 3);
await client.close();
await server.close();
// Test Message type
const message: Message = new Message('/oscillator/frequency', 440);
message.append(3.14);
message.append('hello');
message.append(true);
// Test Bundle type
const bundle: Bundle = new Bundle(['/one', 1], ['/two', 2]);
bundle.append(['/three', 3]);
// Test encode/decode with consistent type annotations
const encoded: Buffer = encode(message);
const decoded: Object = decode(encoded);

View File

@ -0,0 +1,17 @@
{
"compilerOptions": {
"noEmit": true,
"skipLibCheck": true,
"module": "commonjs",
"esModuleInterop": true,
"target": "ES2022",
"moduleResolution": "node",
"baseUrl": ".",
"paths": {
"node-osc": ["../../../types/index.d.mts"]
}
},
"include": [
"test-cjs-types.ts"
]
}

View File

@ -0,0 +1,17 @@
{
"compilerOptions": {
"noEmit": true,
"skipLibCheck": true,
"esModuleInterop": true,
"module": "ES2022",
"target": "ES2022",
"moduleResolution": "node",
"baseUrl": ".",
"paths": {
"node-osc": ["../../../types/index.d.mts"]
}
},
"include": [
"test-esm-types.ts"
]
}

89
backend/node_modules/node-osc/test/test-bundle.mjs generated vendored Normal file
View File

@ -0,0 +1,89 @@
import { once } from 'node:events';
import { test } from 'tap';
import { Client, Server, Bundle } from 'node-osc';
test('bundle: verbose bundle', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
const client = new Client('127.0.0.1', server.port);
t.plan(2);
t.teardown(() => {
server.close();
client.close();
});
server.on('bundle', (bundle) => {
t.same(bundle.elements[0], ['/one', 1]);
t.same(bundle.elements[1], ['/two', 2]);
});
client.send(new Bundle(1, {
address: '/one',
args: [
1
]
}, {
address: '/two',
args: [
2
]
}));
});
test('bundle: array syntax', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
const client = new Client('127.0.0.1', server.port);
t.plan(2);
t.teardown(() => {
server.close();
client.close();
});
server.on('bundle', (bundle) => {
t.same(bundle.elements[0], ['/one', 1]);
t.same(bundle.elements[1], ['/two', 2]);
});
client.send(new Bundle(
['/one', 1],
['/two', 2]
));
});
test('bundle: nested bundle', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
const client = new Client('127.0.0.1', server.port);
t.plan(4);
t.teardown(() => {
server.close();
client.close();
});
const payload = new Bundle(
['/one', 1],
['/two', 2],
['/three', 3]
);
payload.append(new Bundle(10,
['/four', 4]
));
server.on('bundle', (bundle) => {
t.same(bundle.elements[0], ['/one', 1]);
t.same(bundle.elements[1], ['/two', 2]);
t.same(bundle.elements[2], ['/three', 3]);
t.same(bundle.elements[3].elements[0], ['/four', 4]);
});
client.send(payload);
});

291
backend/node_modules/node-osc/test/test-client.mjs generated vendored Normal file
View File

@ -0,0 +1,291 @@
import { once } from 'node:events';
import { test } from 'tap';
import { Server, Client } from 'node-osc';
test('client: with array', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test', 0, 1, 'testing', true], 'We should receive expected payload');
});
client.send(['/test', 0, 1, 'testing', true], (err) => {
t.error(err, 'there should be no error');
client.close();
});
});
test('client: array is not mutated when sent', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(3);
const originalArray = ['/test', 0, 1, 'testing', true];
const expectedArray = ['/test', 0, 1, 'testing', true];
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test', 0, 1, 'testing', true], 'We should receive expected payload');
// Verify the original array was not mutated
t.same(originalArray, expectedArray, 'Original array should not be mutated');
});
client.send(originalArray, (err) => {
t.error(err, 'there should be no error');
client.close();
});
});
test('client: with string', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test'], `We should receive expected payload: ${msg}`);
});
client.send('/test', (err) => {
t.error(err, 'there should be no error');
client.close();
});
});
test('client: with Message object', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test', 1, 2, 3, 'lol', false], `we received the payload: ${msg}`);
});
client.send({
address: '/test',
args: [
1,
2,
3,
'lol',
false
]
}, (err) => {
t.error(err, 'there should be no error');
client.close();
});
});
test('client: with Bundle object', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test', 1, 2, 3, 'lol', false], `we received the payload: ${msg}`);
});
client.send({
address: '/test',
args: [
1,
2,
3,
'lol',
false
]
}, (err) => {
t.error(err, 'there should be no error');
client.close();
});
});
test('client: failure', async (t) => {
const client = new Client('127.0.0.1', 9999);
t.plan(2);
t.throws(() => {
client.send(123, (err) => {
t.error(err, 'there should be no error');
});
});
client.close();
client.send('/boom', (err) => {
t.equal(err.code, 'ERR_SOCKET_DGRAM_NOT_RUNNING');
});
});
test('client: close with callback', async (t) => {
const client = new Client('127.0.0.1', 9999);
t.plan(1);
client.close((err) => {
t.error(err, 'close should not error');
});
});
test('client: send bundle with non-numeric timetag', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('bundle', (bundle) => {
oscServer.close();
t.equal(bundle.timetag, 0, 'should receive immediate execution timetag as 0');
t.ok(bundle.elements.length > 0, 'should have elements');
client.close();
});
// Send bundle with non-numeric timetag (will be encoded as immediate execution)
const bundle = {
oscType: 'bundle',
timetag: 'immediate', // Non-numeric, will trigger the else branch in writeTimeTag
elements: [
{
oscType: 'message',
address: '/test1',
args: [{ type: 'i', value: 42 }]
}
]
};
client.send(bundle);
});
test('client: send bundle with null timetag', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('bundle', (bundle) => {
oscServer.close();
t.equal(bundle.timetag, 0, 'should receive immediate execution timetag as 0');
t.ok(bundle.elements.length > 0, 'should have elements');
client.close();
});
// Send bundle with null timetag (will be encoded as immediate execution)
const bundle = {
oscType: 'bundle',
timetag: null, // Null, will trigger the else branch in writeTimeTag
elements: [
{
oscType: 'message',
address: '/test2',
args: [{ type: 's', value: 'hello' }]
}
]
};
client.send(bundle);
});
test('client: send message with float type arg', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('message', (msg) => {
oscServer.close();
t.equal(msg[0], '/float-test', 'should receive address');
t.ok(Math.abs(msg[1] - 9.876) < 0.001, 'should receive float value');
client.close();
});
// Send raw message with 'float' type to hit that case label
client.send({
oscType: 'message',
address: '/float-test',
args: [{ type: 'float', value: 9.876 }]
});
});
test('client: send message with blob type arg', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('message', (msg) => {
oscServer.close();
t.equal(msg[0], '/blob-test', 'should receive address');
t.ok(Buffer.isBuffer(msg[1]), 'should receive blob as buffer');
client.close();
});
// Send raw message with 'blob' type to hit that case label
client.send({
oscType: 'message',
address: '/blob-test',
args: [{ type: 'blob', value: Buffer.from([0xAA, 0xBB]) }]
});
});
test('client: send message with double type arg', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('message', (msg) => {
oscServer.close();
t.equal(msg[0], '/double-test', 'should receive address');
t.ok(Math.abs(msg[1] - 1.23456789) < 0.001, 'should receive double value as float');
client.close();
});
// Send raw message with 'double' type to hit that case label
client.send({
oscType: 'message',
address: '/double-test',
args: [{ type: 'double', value: 1.23456789 }]
});
});
test('client: send message with midi type arg', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(2);
oscServer.on('message', (msg) => {
oscServer.close();
t.equal(msg[0], '/midi-test', 'should receive address');
t.ok(Buffer.isBuffer(msg[1]), 'should receive MIDI as buffer');
client.close();
});
// Send raw message with 'midi' type to hit that case label
client.send({
oscType: 'message',
address: '/midi-test',
args: [{ type: 'midi', value: Buffer.from([0x00, 0x90, 0x40, 0x60]) }]
});
});

143
backend/node_modules/node-osc/test/test-decode.mjs generated vendored Normal file
View File

@ -0,0 +1,143 @@
import { test } from 'tap';
import decode from '#decode';
test('decode: valid', (t) => {
const buf = Buffer.from('/test\0\0\0,s\0,testing\0');
t.same(decode(buf), ['/test', 'testing'], 'should be empty array');
t.end();
});
test('decode: valid', (t) => {
const buf = Buffer.from('/test\0\0\0,s\0,testing\0');
t.same(decode(buf), ['/test', 'testing'], 'should be empty array');
t.end();
});
test('decode: malformed packet', (t) => {
t.throws(() => {
const buf = Buffer.from('/test\0\0');
decode(buf);
}, /Malformed Packet/);
t.end();
});
test('decode: invalid typetags', (t) => {
t.throws(() => {
const buf = Buffer.from('/test\0\0\0,R\0');
decode(buf);
}, /I don't understand the argument code R/);
t.end();
});
test('decode: malformed OSC structure', (t) => {
// Try to create a scenario that might trigger the "else" case in decode
// This tests an edge case where the buffer might be parsed but not create a valid OSC structure
t.throws(() => {
// Create a buffer that's too short to be valid
const buf = Buffer.from('\0\0\0\0');
decode(buf);
}, /Malformed Packet/);
t.end();
});
test('decode: corrupted buffer', (t) => {
// Test with a buffer that could potentially cause fromBuffer to return unexpected results
t.throws(() => {
// Create a malformed buffer that might not parse correctly
const buf = Buffer.from('invalid');
decode(buf);
}, /(Malformed Packet|Cannot read|out of range)/);
t.end();
});
// This test attempts to exercise edge cases in the decode function
test('decode: edge case with manually crafted invalid structure', (t) => {
// Since the decode function has a defensive else clause, let's try to trigger it
// by creating a buffer that might result in an unexpected object structure
// Try with an empty buffer
t.throws(() => {
const buf = Buffer.alloc(0);
decode(buf);
}, /(Malformed Packet|Cannot read|out of range)/);
// Try with a buffer containing only null bytes
t.throws(() => {
const buf = Buffer.alloc(16, 0);
decode(buf);
}, /(Malformed Packet|Cannot read|out of range)/);
t.end();
});
test('decode: malformed structure with unexpected oscType', async (t) => {
// Test the defensive else clause by providing a custom fromBuffer function
// that returns an object with an invalid oscType
const mockFromBuffer = () => ({
oscType: 'invalid',
data: 'test'
});
t.throws(() => {
decode(Buffer.from('test'), mockFromBuffer);
}, /Malformed Packet/, 'should throw for invalid oscType');
// Test with undefined oscType
const mockFromBufferUndefined = () => ({
data: 'test'
// missing oscType property
});
t.throws(() => {
decode(Buffer.from('test'), mockFromBufferUndefined);
}, /Malformed Packet/, 'should throw for undefined oscType');
// Test with null oscType
const mockFromBufferNull = () => ({
oscType: null,
data: 'test'
});
t.throws(() => {
decode(Buffer.from('test'), mockFromBufferNull);
}, /Malformed Packet/, 'should throw for null oscType');
t.end();
});
test('decode: message without args defaults to empty array', (t) => {
const mockFromBuffer = () => ({
oscType: 'message',
address: '/test'
});
t.same(
decode(Buffer.from('test'), mockFromBuffer),
['/test'],
'should default args to empty array'
);
t.end();
});
test('decode: bundle element must be message or bundle', (t) => {
const mockFromBuffer = () => ({
oscType: 'bundle',
elements: [
{
oscType: 'message',
address: '/ok',
args: []
},
{
oscType: 'nope'
}
]
});
t.throws(() => {
decode(Buffer.from('test'), mockFromBuffer);
}, /Malformed Packet/, 'should throw for invalid bundle element');
t.end();
});

57
backend/node_modules/node-osc/test/test-e2e.mjs generated vendored Normal file
View File

@ -0,0 +1,57 @@
import { once } from 'node:events';
import { test } from 'tap';
import { Server, Client } from 'node-osc';
function flaky() {
return process.release.lts === 'Dubnium' && process.platform === 'win32';
}
function skip(t) {
t.skip(`flaky ~ ${t.name}`);
t.end();
}
test('osc: argument message no callback', async (t) => {
if (flaky()) return skip(t);
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(1);
t.teardown(() => {
oscServer.close();
client.close();
});
oscServer.on('message', (msg) => {
t.same(msg, ['/test', 1, 2, 'testing'], 'We should receive expected payload');
});
client.send('/test', 1, 2, 'testing');
});
test('osc: client with callback and message as arguments', async (t) => {
if (flaky()) return skip(t);
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(2);
t.teardown(() => {
oscServer.close();
client.close();
});
oscServer.on('message', (msg) => {
t.same(msg, ['/test', 1, 2, 'testing'], 'We should receive expected payload');
});
client.send('/test', 1, 2, 'testing', (err) => {
t.error(err, 'there should be no error');
});
});

1302
backend/node_modules/node-osc/test/test-encode-decode.mjs generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,116 @@
import { once } from 'node:events';
import { test } from 'tap';
import { Server, Client } from 'node-osc';
test('server: socket error event is emitted', async (t) => {
t.plan(1);
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
oscServer.on('error', (err) => {
t.ok(err, 'error event should be emitted');
oscServer.close();
});
// Simulate a socket error
oscServer._sock.emit('error', new Error('test socket error'));
});
test('server: error listener can be added before listening', async (t) => {
t.plan(2);
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
oscServer.on('error', (err) => {
t.ok(err, 'error event should be emitted');
t.equal(err.message, 'socket test error', 'error message should match');
});
t.teardown(() => {
oscServer.close();
});
// Simulate a socket error
oscServer._sock.emit('error', new Error('socket test error'));
});
test('client: socket error event is emitted', (t) => {
t.plan(1);
const client = new Client('127.0.0.1', 9999);
client.on('error', (err) => {
t.ok(err, 'error event should be emitted');
client.close();
});
// Simulate a socket error
client._sock.emit('error', new Error('test client error'));
});
test('client: error listener can be added at construction', (t) => {
t.plan(2);
const client = new Client('127.0.0.1', 9999);
client.on('error', (err) => {
t.ok(err, 'error event should be emitted');
t.equal(err.message, 'client socket error', 'error message should match');
});
t.teardown(() => {
client.close();
});
// Simulate a socket error
client._sock.emit('error', new Error('client socket error'));
});
test('client: is an EventEmitter instance', (t) => {
t.plan(1);
const client = new Client('127.0.0.1', 9999);
t.ok(typeof client.on === 'function', 'client should have EventEmitter methods');
client.close();
});
test('server: multiple error listeners can be attached', async (t) => {
t.plan(2);
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
oscServer.on('error', (err) => {
t.ok(err, 'first listener should receive error');
});
oscServer.on('error', (err) => {
t.ok(err, 'second listener should receive error');
});
t.teardown(() => {
oscServer.close();
});
// Simulate a socket error
oscServer._sock.emit('error', new Error('multi listener test'));
});
test('client: multiple error listeners can be attached', (t) => {
t.plan(2);
const client = new Client('127.0.0.1', 9999);
client.on('error', (err) => {
t.ok(err, 'first listener should receive error');
});
client.on('error', (err) => {
t.ok(err, 'second listener should receive error');
});
t.teardown(() => {
client.close();
});
// Simulate a socket error
client._sock.emit('error', new Error('multi listener test'));
});

401
backend/node_modules/node-osc/test/test-message.mjs generated vendored Normal file
View File

@ -0,0 +1,401 @@
import { once } from 'node:events';
import { test } from 'tap';
import { Server, Client, Message } from 'node-osc';
function round(num) {
return Math.round(num * 100) / 100;
}
test('message: basic usage', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
const client = new Client('127.0.0.1', server.port);
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new Message('/address');
m.append('testing');
m.append(123);
m.append([456, 789]);
server.on('message', (msg) => {
const expected = ['/address', 'testing', 123, 456, 789];
t.same(msg, expected, `We reveived the payload: ${msg}`);
});
client.send(m);
});
test('message: multiple args', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
const client = new Client('127.0.0.1', server.port);
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new Message('/address', 'testing', 123, true);
server.on('message', (msg) => {
const expected = ['/address', 'testing', 123, true];
t.same(msg, expected, `We reveived the payload: ${msg}`);
});
client.send(m);
});
test('message: object', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
const client = new Client('127.0.0.1', server.port);
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new Message('/address');
m.append({
type: 'string',
value: 'test'
});
m.append({
type: 'double',
value: 100
});
server.on('message', (msg) => {
const expected = ['/address', 'test', 100];
t.same(msg, expected, `We reveived the payload: ${msg}`);
});
client.send(m);
});
test('message: float', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
const client = new Client('127.0.0.1', server.port);
t.plan(2);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new Message('/address');
m.append(3.14);
server.on('message', (msg) => {
const expected = [
'/address',
3.14
];
t.equal(msg[0], expected[0], `We reveived the payload: ${msg}`);
t.equal(round(msg[1]), expected[1], 'pie please');
});
client.send(m);
});
test('message: alias messages', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
const client = new Client('127.0.0.1', server.port);
t.plan(5);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new Message('/address');
m.append({
type: 'i',
value: 123
});
m.append({
type: 'f',
value: 3.14
});
server.on('message', (msg) => {
const expected = [
'/address',
123,
3.14
];
t.equal(msg[0], expected[0], `We reveived the payload: ${msg}`);
t.equal(msg[1], expected[1], 'easy as abc');
t.ok(Number.isInteger(msg[1]), 'the first value is an int');
t.equal(round(msg[2]), expected[2], 'pie please');
t.ok(msg[2] % 1 !== 0, 'the second value is a float');
});
client.send(m);
});
test('message: boolean', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const client = new Client('127.0.0.1', server.port);
const m = new Message('/address');
m.append(true);
server.on('message', (msg) => {
const expected = [
'/address',
true
];
t.same(msg, expected, `We reveived the payload: ${msg}`);
});
client.send(m);
});
test('message: blob', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
const client = new Client('127.0.0.1', server.port);
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new Message('/address');
const buf = Buffer.from('test');
m.append({
type: 'blob',
value: buf
});
server.on('message', (msg) => {
const expected = [
'/address',
buf
];
t.same(msg, expected, `We reveived the payload: ${msg}`);
});
client.send(m);
});
test('message: Buffer as blob', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
const client = new Client('127.0.0.1', server.port);
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new Message('/address');
const buf = Buffer.from('test buffer data');
// Directly append Buffer without wrapping in object
m.append(buf);
server.on('message', (msg) => {
const expected = [
'/address',
buf
];
t.same(msg, expected, `We received the buffer payload: ${msg}`);
});
client.send(m);
});
// test('message: timetag', (t) => {
// const oscServer = new osc.Server(3333, '127.0.0.1');
// const client = new osc.Client('127.0.0.1', 3333);
// const m = new osc.Message('/address');
//
// oscServer.on('message', (msg) => {
// const expected = [
// '/address'
// ];
// t.same(msg, expected, `We reveived the payload: ${msg}`);
// oscServer.close();
// t.end();
// });
//
// client.send(m, () => {
// client.close();
// });
// });
test('message: Buffer with multiple arguments', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
const client = new Client('127.0.0.1', server.port);
t.plan(6);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new Message('/address');
const buf1 = Buffer.from('first');
const buf2 = Buffer.from('second');
m.append('string');
m.append(42);
m.append(buf1);
m.append(3.14);
m.append(buf2);
server.on('message', (msg) => {
t.equal(msg[0], '/address', 'Address matches');
t.equal(msg[1], 'string', 'String matches');
t.equal(msg[2], 42, 'Integer matches');
t.same(msg[3], buf1, 'First buffer matches');
t.equal(round(msg[4]), 3.14, 'Float matches');
t.same(msg[5], buf2, 'Second buffer matches')
});
client.send(m);
});
test('message: Buffer in constructor', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
const client = new Client('127.0.0.1', server.port);
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const buf = Buffer.from('constructor buffer');
const m = new Message('/address', 'test', buf, 123);
server.on('message', (msg) => {
const expected = [
'/address',
'test',
buf,
123
];
t.same(msg, expected, `We received the constructor buffer payload: ${msg}`);
});
client.send(m);
});
test('message: Buffer in array', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
const client = new Client('127.0.0.1', server.port);
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new Message('/address');
const buf1 = Buffer.from('array1');
const buf2 = Buffer.from('array2');
m.append([buf1, 'string', buf2, 456]);
server.on('message', (msg) => {
const expected = [
'/address',
buf1,
'string',
buf2,
456
];
t.same(msg, expected, `We received the array with buffers: ${msg}`);
});
client.send(m);
});
test('message: empty Buffer', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
const client = new Client('127.0.0.1', server.port);
t.plan(1);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new Message('/address');
const buf = Buffer.from('');
m.append(buf);
server.on('message', (msg) => {
const expected = [
'/address',
buf
];
t.same(msg, expected, `We received the empty buffer: ${msg}`);
});
client.send(m);
});
test('message: large Buffer', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
const client = new Client('127.0.0.1', server.port);
t.plan(4);
t.teardown(async () => {
await server.close();
await client.close();
});
const m = new Message('/address');
const buf = Buffer.alloc(1024, 'x');
m.append(buf);
server.on('message', (msg) => {
t.equal(msg[0], '/address', 'Address matches');
t.ok(Buffer.isBuffer(msg[1]), 'Second element is a Buffer');
t.equal(msg[1].length, 1024, 'Buffer size matches');
t.same(msg[1], buf, 'Buffer content matches');
});
client.send(m);
});
test('message: error', (t) => {
const m = new Message('/address');
t.plan(2);
t.throws(() => {
m.append({
lol: 'it broken'
});
}, /don't know how to encode object/);
t.throws(() => {
m.append(undefined);
}, /don't know how to encode/);
});

1005
backend/node_modules/node-osc/test/test-osc-internal.mjs generated vendored Normal file

File diff suppressed because it is too large Load Diff

313
backend/node_modules/node-osc/test/test-promises.mjs generated vendored Normal file
View File

@ -0,0 +1,313 @@
import { once } from 'node:events';
import { test } from 'tap';
import { Server, Client } from 'node-osc';
test('client: send with promise - array', async (t) => {
const server = new Server(0, '127.0.0.1');
await once(server, 'listening');
const client = new Client('127.0.0.1', server.port);
t.plan(1);
server.on('message', (msg) => {
server.close();
t.same(msg, ['/test', 0, 1, 'testing', true], 'We should receive expected payload');
});
await client.send(['/test', 0, 1, 'testing', true]);
await client.close();
});
test('client: array is not mutated when sent with promise', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(2);
const originalArray = ['/test', 0, 1, 'testing', true];
const expectedArray = ['/test', 0, 1, 'testing', true];
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test', 0, 1, 'testing', true], 'We should receive expected payload');
});
await client.send(originalArray);
// Verify the original array was not mutated
t.same(originalArray, expectedArray, 'Original array should not be mutated');
await client.close();
});
test('client: send with promise - string', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(1);
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test'], 'We should receive expected payload');
});
await client.send('/test');
await client.close();
});
test('client: send with promise - message object', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(1);
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test', 1, 2, 3, 'lol', false], 'we received the payload');
});
await client.send({
address: '/test',
args: [1, 2, 3, 'lol', false]
});
await client.close();
});
test('client: send with promise - multiple args', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(1);
oscServer.on('message', (msg) => {
oscServer.close();
t.same(msg, ['/test', 1, 2, 'testing'], 'We should receive expected payload');
});
await client.send('/test', 1, 2, 'testing');
await client.close();
});
test('client: send promise rejection on closed socket', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(1);
await client.close();
await oscServer.close();
try {
await client.send('/boom');
t.fail('Should have thrown an error');
} catch (err) {
t.equal(err.code, 'ERR_SOCKET_DGRAM_NOT_RUNNING', 'Should reject with correct error code');
}
});
test('client: async/await usage', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(1);
const messagePromise = once(oscServer, 'message');
await client.send('/async-test', 42, 'hello');
const [receivedMessage] = await messagePromise;
t.same(receivedMessage, ['/async-test', 42, 'hello'], 'Message received via async/await');
await client.close();
await oscServer.close();
});
test('server: close with promise', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
t.plan(1);
await once(oscServer, 'listening');
await oscServer.close();
t.pass('Server closed successfully with promise');
});
test('server: no callback still emits listening event', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
t.plan(1);
await once(oscServer, 'listening');
t.pass('listening event emitted');
await oscServer.close();
});
test('client and server: full async/await workflow', async (t) => {
t.plan(3);
const oscServer = new Server(0, '127.0.0.1');
// Wait for server to be ready
await once(oscServer, 'listening');
t.pass('Server started');
const client = new Client('127.0.0.1', oscServer.port);
t.pass('Client created');
// Set up message handler
const messageReceived = once(oscServer, 'message');
// Send message and wait for it to be received
await client.send('/workflow', 'test', 123);
const [msg] = await messageReceived;
t.same(msg, ['/workflow', 'test', 123], 'Message received correctly');
// Clean up
await client.close();
await oscServer.close();
});
test('client: multiple sends with promises', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(3);
const messages = [];
oscServer.on('message', (msg) => {
messages.push(msg);
});
await client.send('/msg1', 1);
await client.send('/msg2', 2);
await client.send('/msg3', 3);
// Give a little time for all messages to be received
await new Promise((resolve) => setTimeout(resolve, 100));
t.equal(messages.length, 3, 'Received all three messages');
t.same(messages[0], ['/msg1', 1], 'First message correct');
t.same(messages[2], ['/msg3', 3], 'Last message correct');
await client.close();
await oscServer.close();
});
test('client: close promise rejection on error', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', t.context.port);
t.plan(1);
// Mock the socket's close method to simulate an error
const originalClose = client._sock.close.bind(client._sock);
// Set up teardown to ensure socket is properly closed
t.teardown(() => {
// Restore original close method first
client._sock.close = originalClose;
// Then close the socket
try {
client._sock.close(() => {});
} catch {
// Socket might already be closed, that's ok
}
});
client._sock.close = function(cb) {
// Simulate an error being passed to callback
if (cb) {
const err = new Error('Mock close error');
err.code = 'MOCK_ERROR';
setImmediate(() => cb(err));
}
};
try {
await oscServer.close();
await client.close();
t.fail('Should have thrown an error');
} catch (err) {
t.equal(err.code, 'MOCK_ERROR', 'Should reject with mock error');
}
});
test('server: close promise rejection on error', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
t.plan(1);
await once(oscServer, 'listening');
// Mock the socket's close method to simulate an error
const originalClose = oscServer._sock.close.bind(oscServer._sock);
// Set up teardown to ensure socket is properly closed
t.teardown(() => {
// Restore original close method first
oscServer._sock.close = originalClose;
// Then close the socket
try {
oscServer._sock.close(() => {});
} catch {
// Socket might already be closed, that's ok
}
});
oscServer._sock.close = function(cb) {
// Simulate an error being passed to callback
if (cb) {
const err = new Error('Mock close error');
err.code = 'MOCK_ERROR';
setImmediate(() => cb(err));
}
};
try {
await oscServer.close();
t.fail('Should have thrown an error');
} catch (err) {
t.equal(err.code, 'MOCK_ERROR', 'Should reject with mock error');
}
});
test('client: send promise rejection on send error', async (t) => {
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(1);
// Mock the socket's send method to simulate an error
const originalSend = client._sock.send;
client._sock.send = function(msg, offset, length, port, address, callback) {
// Simulate an error being passed to callback
const err = new Error('Mock send error');
err.code = 'MOCK_SEND_ERROR';
if (callback) {
setImmediate(() => callback(err));
}
};
t.teardown(async () => {
client._sock.send = originalSend;
await client.close();
await oscServer.close();
});
try {
await client.send('/test', 'data');
t.fail('Should have thrown an error');
} catch (err) {
t.equal(err.code, 'MOCK_SEND_ERROR', 'Should reject with mock send error');
}
});

101
backend/node_modules/node-osc/test/test-server.mjs generated vendored Normal file
View File

@ -0,0 +1,101 @@
import { once } from 'node:events';
import { test } from 'tap';
import { Server, Client } from 'node-osc';
test('server: create and close', async (t) => {
t.plan(1);
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
oscServer.close((err) => {
t.error(err);
});
});
test('server: listen to message', async (t) => {
const oscServer = new Server(0);
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(3);
t.teardown(() => {
oscServer.close();
client.close();
});
oscServer.on('message', (msg) => {
t.same(msg, ['/test'], 'We should receive expected payload');
});
oscServer.on('/test', (msg) => {
t.same(msg, ['/test'], 'We should receive expected payload');
});
client.send('/test', (err) => {
t.error(err, 'there should be no error');
});
});
test('server: no defined host', async (t) => {
const oscServer = new Server(0);
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.plan(3);
t.teardown(() => {
oscServer.close();
client.close();
});
oscServer.on('message', (msg) => {
t.same(msg, ['/test'], 'We should receive expected payload');
});
oscServer.on('/test', (msg) => {
t.same(msg, ['/test'], 'We should receive expected payload');
});
client.send('/test', (err) => {
t.error(err, 'there should be no error');
});
});
test('server: callback as second arg', async (t) => {
t.plan(4);
const oscServer = new Server(0, () => {
t.ok('callback called');
});
await once(oscServer, 'listening');
const client = new Client('127.0.0.1', oscServer.port);
t.teardown(() => {
oscServer.close();
client.close();
});
oscServer.on('message', (msg) => {
t.same(msg, ['/test'], 'We should receive expected payload');
});
oscServer.on('/test', (msg) => {
t.same(msg, ['/test'], 'We should receive expected payload');
});
client.send('/test', (err) => {
t.error(err, 'there should be no error');
});
});
test('server: bad message', async (t) => {
t.plan(2);
const oscServer = new Server(0, '127.0.0.1');
await once(oscServer, 'listening');
t.throws(() => {
oscServer._sock.emit('message', 'whoops');
}, /can't decode incoming message:/);
oscServer.close((err) => {
t.error(err);
});
});

39
backend/node_modules/node-osc/test/test-types.mjs generated vendored Normal file
View File

@ -0,0 +1,39 @@
import { test } from 'tap';
import { execSync } from 'node:child_process';
import { join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
// Only run in ESM mode (not when transpiled to CJS in dist/)
// Normalize path separators for cross-platform compatibility
const normalizedPath = __dirname.replace(/\\/g, '/');
const isESM = !normalizedPath.includes('/dist/');
test('types: TypeScript compilation', (t) => {
let tsconfigPath;
const testRoot = resolve(__dirname, isESM ? '.': '../../test');
if (isESM) {
tsconfigPath = join(testRoot, 'fixtures', 'types', 'tsconfig-esm.test.json');
}
else {
tsconfigPath = join(testRoot, 'fixtures', 'types', 'tsconfig-cjs.test.json');
}
try {
// Run TypeScript compiler
const cmd = 'npx tsc --project "' + tsconfigPath + '"';
execSync(cmd, {
encoding: 'utf-8',
stdio: 'pipe',
cwd: join(testRoot, 'fixtures', 'types')
});
t.pass('TypeScript types compile successfully');
} catch (error) {
t.fail('TypeScript compilation failed: ' + error.message);
if (error.stdout) console.log('STDOUT:', error.stdout);
if (error.stderr) console.log('STDERR:', error.stderr);
}
t.end();
});

45
backend/node_modules/node-osc/tsconfig.json generated vendored Normal file
View File

@ -0,0 +1,45 @@
{
"compilerOptions": {
// Enable JSDoc type checking
"allowJs": true,
"checkJs": false,
// Generate declaration files only
"declaration": true,
"emitDeclarationOnly": true,
"declarationMap": true,
// Output directory for declarations
"outDir": "./types",
// Module settings for ESM (will generate .d.mts files)
"module": "ES2022",
"moduleResolution": "node",
"target": "ES2022",
// Allow default imports from modules with no default export
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
// Strict options for better type inference
"strict": false,
"skipLibCheck": true,
// Resolve JSON modules
"resolveJsonModule": true,
// Base URL for paths
"baseUrl": ".",
"paths": {
"#decode": ["./lib/internal/decode.mjs"]
}
},
"include": [
"lib/**/*.mjs"
],
"exclude": [
"node_modules",
"dist",
"test"
]
}

70
backend/node_modules/node-osc/types/Bundle.d.mts generated vendored Normal file
View File

@ -0,0 +1,70 @@
export default Bundle;
/**
* Represents an OSC bundle containing multiple messages or nested bundles.
*
* OSC bundles allow multiple messages to be sent together, optionally with
* a timetag indicating when the bundle should be processed.
*
* @class
*
* @example
* // Create a bundle without a timetag
* const bundle = new Bundle(['/one', 1], ['/two', 2]);
*
* @example
* // Create a bundle with a timetag
* const bundle = new Bundle(10, ['/one', 1], ['/two', 2]);
*
* @example
* // Nest bundles
* const bundle1 = new Bundle(['/one', 1]);
* const bundle2 = new Bundle(['/two', 2]);
* bundle1.append(bundle2);
*/
declare class Bundle {
/**
* Create an OSC Bundle.
*
* @param {number|Message|Bundle|Array} [timetagOrElement=0] - Timetag, or if not a number, the first element and timetag will default to 0.
* @param {...(Message|Bundle|Array)} elements - Messages or bundles to include.
* Arrays will be automatically converted to Message objects.
*
* @example
* // Bundle without timetag
* const bundle = new Bundle(['/test', 1], ['/test2', 2]);
*
* @example
* // Bundle with timetag of 10
* const bundle = new Bundle(10, ['/test', 1]);
*
* @example
* // Bundle with Message objects
* const msg1 = new Message('/one', 1);
* const msg2 = new Message('/two', 2);
* const bundle = new Bundle(msg1, msg2);
*/
constructor(timetag: any, ...elements: (Message | Bundle | any[])[]);
oscType: string;
timetag: any;
elements: (Message | Bundle)[];
/**
* Append a message or bundle to this bundle.
*
* @param {Message|Bundle|Array} element - The message or bundle to append.
* Arrays will be automatically converted to Message objects.
*
* @example
* const bundle = new Bundle();
* bundle.append(['/test', 1]);
* bundle.append(new Message('/test2', 2));
*
* @example
* // Append a nested bundle
* const bundle1 = new Bundle(['/one', 1]);
* const bundle2 = new Bundle(['/two', 2]);
* bundle1.append(bundle2);
*/
append(element: Message | Bundle | any[]): void;
}
import Message from './Message.mjs';
//# sourceMappingURL=Bundle.d.mts.map

1
backend/node_modules/node-osc/types/Bundle.d.mts.map generated vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"Bundle.d.mts","sourceRoot":"","sources":["../lib/Bundle.mjs"],"names":[],"mappings":";AAaA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH;IACE;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,uCAjBc,CAAC,OAAO,GAAC,MAAM,QAAM,CAAC,EAAA,EAyBnC;IAHC,gBAAuB;IACvB,aAAsB;IACtB,+BAAsC;IAGxC;;;;;;;;;;;;;;;;OAgBG;IACH,gBAdW,OAAO,GAAC,MAAM,QAAM,QAgB9B;CACF;oBAvFmB,eAAe"}

101
backend/node_modules/node-osc/types/Client.d.mts generated vendored Normal file
View File

@ -0,0 +1,101 @@
export default Client;
/**
* OSC Client for sending messages and bundles over UDP.
*
* Extends EventEmitter and emits the following events:
* - 'error': Emitted when a socket error occurs
*
* @class
* @extends EventEmitter
* @example
* // Create a client
* const client = new Client('127.0.0.1', 3333);
*
* // Send a message with callback
* client.send('/oscAddress', 200, (err) => {
* if (err) console.error(err);
* client.close();
* });
*
* @example
* // Send a message with async/await
* const client = new Client('127.0.0.1', 3333);
* await client.send('/oscAddress', 200);
* await client.close();
*/
declare class Client extends EventEmitter<[never]> {
/**
* Create an OSC Client.
*
* @param {string} host - The hostname or IP address of the OSC server.
* @param {number} port - The port number of the OSC server.
*
* @example
* const client = new Client('127.0.0.1', 3333);
*/
constructor(host: string, port: number);
host: string;
port: number;
_sock: import("dgram").Socket;
/**
* Close the client socket.
*
* This method can be used with either a callback or as a Promise.
*
* @param {Function} [cb] - Optional callback function called when socket is closed.
* @returns {Promise<void>|undefined} Returns a Promise if no callback is provided.
*
* @example
* // With callback
* client.close((err) => {
* if (err) console.error(err);
* });
*
* @example
* // With async/await
* await client.close();
*/
close(cb?: Function): Promise<void> | undefined;
_performSend(message: any, args: any, callback: any): void;
/**
* Send an OSC message or bundle to the server.
*
* This method can be used with either a callback or as a Promise.
* Messages can be sent in several formats:
* - As separate arguments: address followed by values
* - As a Message or Bundle object
* - As an array: [address, ...values]
*
* @param {...*} args - The message to send. Can be:
* - (address: string, ...values: any[], callback?: Function)
* - (message: Message|Bundle, callback?: Function)
* - (array: Array, callback?: Function)
* @returns {Promise<void>|undefined} Returns a Promise if no callback is provided.
*
* @throws {TypeError} If the message format is invalid.
* @throws {ReferenceError} If attempting to send on a closed socket.
*
* @example
* // Send with address and arguments
* client.send('/oscAddress', 200, 'hello', (err) => {
* if (err) console.error(err);
* });
*
* @example
* // Send with async/await
* await client.send('/oscAddress', 200, 'hello');
*
* @example
* // Send a Message object
* const msg = new Message('/test', 1, 2, 3);
* await client.send(msg);
*
* @example
* // Send a Bundle object
* const bundle = new Bundle(['/one', 1], ['/two', 2]);
* await client.send(bundle);
*/
send(...args: any[]): Promise<void> | undefined;
}
import { EventEmitter } from 'node:events';
//# sourceMappingURL=Client.d.mts.map

1
backend/node_modules/node-osc/types/Client.d.mts.map generated vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"Client.d.mts","sourceRoot":"","sources":["../lib/Client.mjs"],"names":[],"mappings":";AAKA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH;IACE;;;;;;;;OAQG;IACH,kBANW,MAAM,QACN,MAAM,EAiBhB;IAVC,aAAgB;IAChB,aAAgB;IAChB,8BAGE;IAMJ;;;;;;;;;;;;;;;;;OAiBG;IACH,sBAZa,OAAO,CAAC,IAAI,CAAC,GAAC,SAAS,CAuBnC;IACD,2DA2BC;IACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IACH,cA7Bc,GAAC,EAAA,GAIF,OAAO,CAAC,IAAI,CAAC,GAAC,SAAS,CAmDnC;CACF;6BA9K4B,aAAa"}

84
backend/node_modules/node-osc/types/Message.d.mts generated vendored Normal file
View File

@ -0,0 +1,84 @@
export default Message;
/**
* Represents an OSC message with an address and arguments.
*
* OSC messages consist of an address pattern (string starting with '/')
* and zero or more arguments of various types.
*
* @class
*
* @example
* // Create a message with constructor arguments
* const msg = new Message('/test', 1, 2, 'hello');
*
* @example
* // Create a message and append arguments
* const msg = new Message('/test');
* msg.append(1);
* msg.append('hello');
* msg.append(3.14);
*/
declare class Message {
/**
* Create an OSC Message.
*
* @param {string} address - The OSC address pattern (e.g., '/oscillator/frequency').
* @param {...*} args - Optional arguments to include in the message.
*
* @example
* const msg = new Message('/test');
*
* @example
* const msg = new Message('/test', 1, 2, 3);
*
* @example
* const msg = new Message('/synth', 'note', 60, 0.5);
*/
constructor(address: string, ...args: any[]);
oscType: string;
address: string;
args: any[];
/**
* Append an argument to the message.
*
* Automatically detects the type based on the JavaScript type:
* - Integers are encoded as OSC integers
* - Floats are encoded as OSC floats
* - Strings are encoded as OSC strings
* - Booleans are encoded as OSC booleans
* - Buffers are encoded as OSC blobs
* - Arrays are recursively appended
* - Objects with a 'type' property are used as-is
*
* @param {*} arg - The argument to append. Can be:
* - A primitive value (number, string, boolean)
* - A Buffer (encoded as blob)
* - An array of values (will be recursively appended)
* - An object with 'type' and 'value' properties for explicit type control
*
* @throws {Error} If the argument type cannot be encoded.
*
* @example
* const msg = new Message('/test');
* msg.append(42); // Integer
* msg.append(3.14); // Float
* msg.append('hello'); // String
* msg.append(true); // Boolean
*
* @example
* // Append multiple values at once
* msg.append([1, 2, 3]);
*
* @example
* // Explicitly specify type
* msg.append({ type: 'float', value: 42 });
* msg.append({ type: 'blob', value: Buffer.from('data') });
*
* @example
* // MIDI messages (4 bytes: port, status, data1, data2)
* msg.append({ type: 'midi', value: { port: 0, status: 144, data1: 60, data2: 127 } });
* msg.append({ type: 'm', value: Buffer.from([0, 144, 60, 127]) });
*/
append(arg: any): void;
}
//# sourceMappingURL=Message.d.mts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Message.d.mts","sourceRoot":"","sources":["../lib/Message.mjs"],"names":[],"mappings":";AAyBA;;;;;;;;;;;;;;;;;;GAkBG;AACH;IACE;;;;;;;;;;;;;;OAcG;IACH,qBAZW,MAAM,WACH,GAAC,EAAA,EAed;IAHC,gBAAwB;IACxB,gBAAsB;IACtB,YAAgB;IAGlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwCG;IACH,YA7BW,GAAC,QA6DX;CACF"}

98
backend/node_modules/node-osc/types/Server.d.mts generated vendored Normal file
View File

@ -0,0 +1,98 @@
export default Server;
/**
* OSC Server for receiving messages and bundles over UDP.
*
* Emits the following events:
* - 'listening': Emitted when the server starts listening
* - 'message': Emitted when an OSC message is received (receives msg array and rinfo object)
* - 'bundle': Emitted when an OSC bundle is received (receives bundle object and rinfo object)
* - 'error': Emitted when a socket error or decoding error occurs (receives error and rinfo)
* - Address-specific events: Emitted for each message address (e.g., '/test')
*
* @class
* @extends EventEmitter
*
* @fires Server#listening
* @fires Server#message
* @fires Server#bundle
* @fires Server#error
*
* @example
* // Create and listen for messages
* const server = new Server(3333, '0.0.0.0', () => {
* console.log('Server is listening');
* });
*
* server.on('message', (msg, rinfo) => {
* console.log('Message:', msg);
* console.log('From:', rinfo.address, rinfo.port);
* });
*
* @example
* // Using async/await with events.once
* import { once } from 'node:events';
*
* const server = new Server(3333, '0.0.0.0');
* await once(server, 'listening');
*
* server.on('message', (msg) => {
* console.log('Message:', msg);
* });
*
* @example
* // Listen for specific OSC addresses
* server.on('/note', (msg) => {
* const [address, pitch, velocity] = msg;
* console.log(`Note: ${pitch}, Velocity: ${velocity}`);
* });
*/
declare class Server extends EventEmitter<[never]> {
/**
* Create an OSC Server.
*
* @param {number} port - The port to listen on.
* @param {string} [host='127.0.0.1'] - The host address to bind to. Use '0.0.0.0' to listen on all interfaces.
* @param {Function} [cb] - Optional callback function called when server starts listening.
*
* @example
* // Basic server
* const server = new Server(3333);
*
* @example
* // Server on all interfaces with callback
* const server = new Server(3333, '0.0.0.0', () => {
* console.log('Server started');
* });
*
* @example
* // Host parameter can be omitted, callback as second parameter
* const server = new Server(3333, () => {
* console.log('Server started on 127.0.0.1');
* });
*/
constructor(port: number, host?: string, cb?: Function);
port: number;
host: string;
_sock: import("dgram").Socket;
/**
* Close the server socket.
*
* This method can be used with either a callback or as a Promise.
*
* @param {Function} [cb] - Optional callback function called when socket is closed.
* @returns {Promise<void>|undefined} Returns a Promise if no callback is provided.
*
* @example
* // With callback
* server.close((err) => {
* if (err) console.error(err);
* });
*
* @example
* // With async/await
* await server.close();
*/
close(cb?: Function): Promise<void> | undefined;
}
import { EventEmitter } from 'node:events';
//# sourceMappingURL=Server.d.mts.map

1
backend/node_modules/node-osc/types/Server.d.mts.map generated vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"Server.d.mts","sourceRoot":"","sources":["../lib/Server.mjs"],"names":[],"mappings":";AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH;IACE;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,kBApBW,MAAM,SACN,MAAM,iBAgEhB;IArCC,aAAgB;IAChB,aAAgB;IAChB,8BAGE;IAiCJ;;;;;;;;;;;;;;;;;OAiBG;IACH,sBAZa,OAAO,CAAC,IAAI,CAAC,GAAC,SAAS,CAuBnC;CACF;6BAvJ4B,aAAa"}

6
backend/node_modules/node-osc/types/index.d.mts generated vendored Normal file
View File

@ -0,0 +1,6 @@
export { default as Message } from "./Message.mjs";
export { default as Bundle } from "./Bundle.mjs";
export { default as Server } from "./Server.mjs";
export { default as Client } from "./Client.mjs";
export { encode, decode } from "./osc.mjs";
//# sourceMappingURL=index.d.mts.map

1
backend/node_modules/node-osc/types/index.d.mts.map generated vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../lib/index.mjs"],"names":[],"mappings":""}

View File

@ -0,0 +1,4 @@
export default decodeAndSanitize;
declare function decodeAndSanitize(data: any, customDecode?: typeof decode): any;
import { decode } from '../osc.mjs';
//# sourceMappingURL=decode.d.mts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"decode.d.mts","sourceRoot":"","sources":["../../lib/internal/decode.mjs"],"names":[],"mappings":";AAqBA,iFAWC;uBAhCsB,YAAY"}

66
backend/node_modules/node-osc/types/osc.d.mts generated vendored Normal file
View File

@ -0,0 +1,66 @@
/**
* Encode an OSC message or bundle to a Buffer.
*
* This low-level function converts OSC messages and bundles into binary format
* for transmission or storage. Useful for sending OSC over custom transports
* (WebSocket, TCP, HTTP), storing to files, or implementing custom OSC routers.
*
* @param {Object} message - OSC message or bundle object with oscType property
* @returns {Buffer} The encoded OSC data ready for transmission
*
* @example
* // Encode a message
* import { Message, encode } from 'node-osc';
*
* const message = new Message('/oscillator/frequency', 440);
* const buffer = encode(message);
* console.log('Encoded bytes:', buffer.length);
*
* @example
* // Encode a bundle
* import { Bundle, encode } from 'node-osc';
*
* const bundle = new Bundle(['/one', 1], ['/two', 2]);
* const buffer = encode(bundle);
*
* @example
* // Send over WebSocket
* const buffer = encode(message);
* websocket.send(buffer);
*/
export function encode(message: any): Buffer;
/**
* Decode a Buffer containing OSC data into a message or bundle object.
*
* This low-level function parses binary OSC data back into JavaScript objects.
* Useful for receiving OSC over custom transports, reading from files,
* or implementing custom OSC routers.
*
* @param {Buffer} buffer - The Buffer containing OSC data
* @returns {Object} The decoded OSC message or bundle. Messages have
* {oscType: 'message', address: string, args: Array}, bundles have
* {oscType: 'bundle', timetag: number, elements: Array}
* @throws {Error} If the buffer contains malformed OSC data
*
* @example
* // Decode received data
* import { decode } from 'node-osc';
*
* const decoded = decode(buffer);
* if (decoded.oscType === 'message') {
* console.log('Address:', decoded.address);
* console.log('Arguments:', decoded.args);
* }
*
* @example
* // Round-trip encode/decode
* import { Message, encode, decode } from 'node-osc';
*
* const original = new Message('/test', 42, 'hello');
* const buffer = encode(original);
* const decoded = decode(buffer);
* console.log(decoded.address); // '/test'
*/
export function decode(buffer: Buffer): any;
import { Buffer } from 'node:buffer';
//# sourceMappingURL=osc.d.mts.map

1
backend/node_modules/node-osc/types/osc.d.mts.map generated vendored Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"osc.d.mts","sourceRoot":"","sources":["../lib/osc.mjs"],"names":[],"mappings":"AAyOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,sCAtBa,MAAM,CA4BlB;AA6CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,+BAzBW,MAAM,OAgChB;uBA5VsB,aAAa"}