Initial commit of my folder
This commit is contained in:
5
backend/node_modules/node-osc/.devcontainer/devcontainer.json
generated
vendored
Normal file
5
backend/node_modules/node-osc/.devcontainer/devcontainer.json
generated
vendored
Normal 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
11
backend/node_modules/node-osc/.gitattributes
generated
vendored
Normal 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
|
||||
62
backend/node_modules/node-osc/.github/workflows/bump-version.yml
generated
vendored
Normal file
62
backend/node_modules/node-osc/.github/workflows/bump-version.yml
generated
vendored
Normal 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
|
||||
46
backend/node_modules/node-osc/.github/workflows/create-release.yml
generated
vendored
Normal file
46
backend/node_modules/node-osc/.github/workflows/create-release.yml
generated
vendored
Normal 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 }}
|
||||
35
backend/node_modules/node-osc/.github/workflows/nodejs.yml
generated
vendored
Normal file
35
backend/node_modules/node-osc/.github/workflows/nodejs.yml
generated
vendored
Normal 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
201
backend/node_modules/node-osc/LICENSE
generated
vendored
Normal 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
207
backend/node_modules/node-osc/README.md
generated
vendored
Normal 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
6
backend/node_modules/node-osc/SECURITY.md
generated
vendored
Normal 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
330
backend/node_modules/node-osc/agent.md
generated
vendored
Normal 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
92
backend/node_modules/node-osc/dist/lib/Bundle.js
generated
vendored
Normal 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
180
backend/node_modules/node-osc/dist/lib/Client.js
generated
vendored
Normal 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
145
backend/node_modules/node-osc/dist/lib/Message.js
generated
vendored
Normal 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
156
backend/node_modules/node-osc/dist/lib/Server.js
generated
vendored
Normal 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
16
backend/node_modules/node-osc/dist/lib/index.js
generated
vendored
Normal 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;
|
||||
37
backend/node_modules/node-osc/dist/lib/internal/decode.js
generated
vendored
Normal file
37
backend/node_modules/node-osc/dist/lib/internal/decode.js
generated
vendored
Normal 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
424
backend/node_modules/node-osc/dist/lib/osc.js
generated
vendored
Normal 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
424
backend/node_modules/node-osc/dist/test/lib/osc.js
generated
vendored
Normal 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
90
backend/node_modules/node-osc/dist/test/test-bundle.js
generated
vendored
Normal 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
292
backend/node_modules/node-osc/dist/test/test-client.js
generated
vendored
Normal 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
144
backend/node_modules/node-osc/dist/test/test-decode.js
generated
vendored
Normal 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
58
backend/node_modules/node-osc/dist/test/test-e2e.js
generated
vendored
Normal 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');
|
||||
});
|
||||
});
|
||||
1304
backend/node_modules/node-osc/dist/test/test-encode-decode.js
generated
vendored
Normal file
1304
backend/node_modules/node-osc/dist/test/test-encode-decode.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
117
backend/node_modules/node-osc/dist/test/test-error-handling.js
generated
vendored
Normal file
117
backend/node_modules/node-osc/dist/test/test-error-handling.js
generated
vendored
Normal 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
403
backend/node_modules/node-osc/dist/test/test-message.js
generated
vendored
Normal 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/);
|
||||
});
|
||||
1007
backend/node_modules/node-osc/dist/test/test-osc-internal.js
generated
vendored
Normal file
1007
backend/node_modules/node-osc/dist/test/test-osc-internal.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
314
backend/node_modules/node-osc/dist/test/test-promises.js
generated
vendored
Normal file
314
backend/node_modules/node-osc/dist/test/test-promises.js
generated
vendored
Normal 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
102
backend/node_modules/node-osc/dist/test/test-server.js
generated
vendored
Normal 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
42
backend/node_modules/node-osc/dist/test/test-types.js
generated
vendored
Normal 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
477
backend/node_modules/node-osc/docs/API.md
generated
vendored
Normal 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
605
backend/node_modules/node-osc/docs/GUIDE.md
generated
vendored
Normal 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
81
backend/node_modules/node-osc/docs/README.md
generated
vendored
Normal 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
8
backend/node_modules/node-osc/eslint.config.mjs
generated
vendored
Normal 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
121
backend/node_modules/node-osc/examples/README.md
generated
vendored
Normal 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
57
backend/node_modules/node-osc/examples/async-await.mjs
generated
vendored
Normal 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");
|
||||
92
backend/node_modules/node-osc/examples/bundle-example.mjs
generated
vendored
Normal file
92
backend/node_modules/node-osc/examples/bundle-example.mjs
generated
vendored
Normal 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
40
backend/node_modules/node-osc/examples/client.js
generated
vendored
Normal 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);
|
||||
152
backend/node_modules/node-osc/examples/error-handling.mjs
generated
vendored
Normal file
152
backend/node_modules/node-osc/examples/error-handling.mjs
generated
vendored
Normal 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
39
backend/node_modules/node-osc/examples/esm.mjs
generated
vendored
Normal 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
25
backend/node_modules/node-osc/examples/server.js
generated
vendored
Normal 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
16
backend/node_modules/node-osc/jsdoc.json
generated
vendored
Normal 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
90
backend/node_modules/node-osc/lib/Bundle.mjs
generated
vendored
Normal 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
178
backend/node_modules/node-osc/lib/Client.mjs
generated
vendored
Normal 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
143
backend/node_modules/node-osc/lib/Message.mjs
generated
vendored
Normal 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
155
backend/node_modules/node-osc/lib/Server.mjs
generated
vendored
Normal 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
5
backend/node_modules/node-osc/lib/index.mjs
generated
vendored
Normal 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
35
backend/node_modules/node-osc/lib/internal/decode.mjs
generated
vendored
Normal 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
422
backend/node_modules/node-osc/lib/osc.mjs
generated
vendored
Normal 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
58
backend/node_modules/node-osc/package.json
generated
vendored
Normal 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
85
backend/node_modules/node-osc/rollup.config.mjs
generated
vendored
Normal 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
229
backend/node_modules/node-osc/scripts/generate-docs.mjs
generated
vendored
Executable 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);
|
||||
}
|
||||
19
backend/node_modules/node-osc/test/fixtures/types/test-cjs-types.ts
generated
vendored
Normal file
19
backend/node_modules/node-osc/test/fixtures/types/test-cjs-types.ts
generated
vendored
Normal 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);
|
||||
35
backend/node_modules/node-osc/test/fixtures/types/test-esm-types.ts
generated
vendored
Normal file
35
backend/node_modules/node-osc/test/fixtures/types/test-esm-types.ts
generated
vendored
Normal 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);
|
||||
17
backend/node_modules/node-osc/test/fixtures/types/tsconfig-cjs.test.json
generated
vendored
Normal file
17
backend/node_modules/node-osc/test/fixtures/types/tsconfig-cjs.test.json
generated
vendored
Normal 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"
|
||||
]
|
||||
}
|
||||
17
backend/node_modules/node-osc/test/fixtures/types/tsconfig-esm.test.json
generated
vendored
Normal file
17
backend/node_modules/node-osc/test/fixtures/types/tsconfig-esm.test.json
generated
vendored
Normal 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
89
backend/node_modules/node-osc/test/test-bundle.mjs
generated
vendored
Normal 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
291
backend/node_modules/node-osc/test/test-client.mjs
generated
vendored
Normal 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
143
backend/node_modules/node-osc/test/test-decode.mjs
generated
vendored
Normal 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
57
backend/node_modules/node-osc/test/test-e2e.mjs
generated
vendored
Normal 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
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
116
backend/node_modules/node-osc/test/test-error-handling.mjs
generated
vendored
Normal file
116
backend/node_modules/node-osc/test/test-error-handling.mjs
generated
vendored
Normal 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
401
backend/node_modules/node-osc/test/test-message.mjs
generated
vendored
Normal 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
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
313
backend/node_modules/node-osc/test/test-promises.mjs
generated
vendored
Normal 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
101
backend/node_modules/node-osc/test/test-server.mjs
generated
vendored
Normal 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
39
backend/node_modules/node-osc/test/test-types.mjs
generated
vendored
Normal 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
45
backend/node_modules/node-osc/tsconfig.json
generated
vendored
Normal 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
70
backend/node_modules/node-osc/types/Bundle.d.mts
generated
vendored
Normal 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
1
backend/node_modules/node-osc/types/Bundle.d.mts.map
generated
vendored
Normal 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
101
backend/node_modules/node-osc/types/Client.d.mts
generated
vendored
Normal 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
1
backend/node_modules/node-osc/types/Client.d.mts.map
generated
vendored
Normal 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
84
backend/node_modules/node-osc/types/Message.d.mts
generated
vendored
Normal 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
|
||||
1
backend/node_modules/node-osc/types/Message.d.mts.map
generated
vendored
Normal file
1
backend/node_modules/node-osc/types/Message.d.mts.map
generated
vendored
Normal 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
98
backend/node_modules/node-osc/types/Server.d.mts
generated
vendored
Normal 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
1
backend/node_modules/node-osc/types/Server.d.mts.map
generated
vendored
Normal 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
6
backend/node_modules/node-osc/types/index.d.mts
generated
vendored
Normal 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
1
backend/node_modules/node-osc/types/index.d.mts.map
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../lib/index.mjs"],"names":[],"mappings":""}
|
||||
4
backend/node_modules/node-osc/types/internal/decode.d.mts
generated
vendored
Normal file
4
backend/node_modules/node-osc/types/internal/decode.d.mts
generated
vendored
Normal 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
|
||||
1
backend/node_modules/node-osc/types/internal/decode.d.mts.map
generated
vendored
Normal file
1
backend/node_modules/node-osc/types/internal/decode.d.mts.map
generated
vendored
Normal 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
66
backend/node_modules/node-osc/types/osc.d.mts
generated
vendored
Normal 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
1
backend/node_modules/node-osc/types/osc.d.mts.map
generated
vendored
Normal 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"}
|
||||
Reference in New Issue
Block a user