Prettier
This commit is contained in:
@@ -39,7 +39,7 @@ steps:
|
|||||||
- test
|
- test
|
||||||
- build
|
- build
|
||||||
when:
|
when:
|
||||||
event:
|
event:
|
||||||
- tag
|
- tag
|
||||||
settings:
|
settings:
|
||||||
base_url: https://git.jacknet.io
|
base_url: https://git.jacknet.io
|
||||||
@@ -49,4 +49,4 @@ steps:
|
|||||||
- bennc-m.zip
|
- bennc-m.zip
|
||||||
checksum:
|
checksum:
|
||||||
- md5
|
- md5
|
||||||
- sha256
|
- sha256
|
||||||
|
|||||||
6
.vscode/extensions.json
vendored
6
.vscode/extensions.json
vendored
@@ -1,5 +1,3 @@
|
|||||||
{
|
{
|
||||||
"recommendations": [
|
"recommendations": ["orta.vscode-jest"]
|
||||||
"orta.vscode-jest"
|
}
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|||||||
34
.vscode/launch.json
vendored
34
.vscode/launch.json
vendored
@@ -1,20 +1,16 @@
|
|||||||
{
|
{
|
||||||
// Use IntelliSense to learn about possible attributes.
|
// Use IntelliSense to learn about possible attributes.
|
||||||
// Hover to view descriptions of existing attributes.
|
// Hover to view descriptions of existing attributes.
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"type": "pwa-node",
|
"type": "pwa-node",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Launch Program",
|
"name": "Launch Program",
|
||||||
"skipFiles": [
|
"skipFiles": ["<node_internals>/**"],
|
||||||
"<node_internals>/**"
|
"program": "${workspaceFolder}/dist/index.js",
|
||||||
],
|
"outFiles": ["${workspaceFolder}/**/*.js"]
|
||||||
"program": "${workspaceFolder}/dist/index.js",
|
}
|
||||||
"outFiles": [
|
]
|
||||||
"${workspaceFolder}/**/*.js"
|
}
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|||||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"editor.tabSize": 2
|
"editor.tabSize": 2
|
||||||
}
|
}
|
||||||
|
|||||||
32
.vscode/tasks.json
vendored
32
.vscode/tasks.json
vendored
@@ -1,18 +1,16 @@
|
|||||||
{
|
{
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"type": "typescript",
|
"type": "typescript",
|
||||||
"tsconfig": "tsconfig.json",
|
"tsconfig": "tsconfig.json",
|
||||||
"option": "watch",
|
"option": "watch",
|
||||||
"problemMatcher": [
|
"problemMatcher": ["$tsc-watch"],
|
||||||
"$tsc-watch"
|
"group": {
|
||||||
],
|
"kind": "build",
|
||||||
"group": {
|
"isDefault": true
|
||||||
"kind": "build",
|
},
|
||||||
"isDefault": true
|
"label": "tsc: watch - tsconfig.json"
|
||||||
},
|
}
|
||||||
"label": "tsc: watch - tsconfig.json"
|
]
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,11 +12,11 @@
|
|||||||
|
|
||||||
## Connection Endpoints
|
## Connection Endpoints
|
||||||
|
|
||||||
| Protocol | Endpoint |
|
| Protocol | Endpoint |
|
||||||
|----------|----------|
|
| --------------- | --------------------------------- |
|
||||||
| TCP | `chat.3t.network:10009` |
|
| TCP | `chat.3t.network:10009` |
|
||||||
| WebSocket (TLS) | `wss://chat.3t.network:443/BENNC` |
|
| WebSocket (TLS) | `wss://chat.3t.network:443/BENNC` |
|
||||||
| WebSocket | `ws://chat.3t.network:80/BENNC` |
|
| WebSocket | `ws://chat.3t.network:80/BENNC` |
|
||||||
|
|
||||||
## Core Requirements
|
## Core Requirements
|
||||||
|
|
||||||
@@ -30,6 +30,7 @@
|
|||||||
## Message Structure
|
## Message Structure
|
||||||
|
|
||||||
**Client → Server:**
|
**Client → Server:**
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────┬─────────────┬─────────────────────────┐
|
┌─────────────┬─────────────┬─────────────────────────┐
|
||||||
│ Message Type│ Length │ Data │
|
│ Message Type│ Length │ Data │
|
||||||
@@ -38,6 +39,7 @@
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Server → Client:**
|
**Server → Client:**
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────┬─────────────┬─────────────┬─────────────────────┐
|
┌─────────────┬─────────────┬─────────────┬─────────────────────┐
|
||||||
│ Message Type│ Sender ID │ Length │ Data │
|
│ Message Type│ Sender ID │ Length │ Data │
|
||||||
@@ -45,7 +47,7 @@
|
|||||||
└─────────────┴─────────────┴─────────────┴─────────────────────┘
|
└─────────────┴─────────────┴─────────────┴─────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
*Sender ID is randomly generated per connection to identify message source.*
|
_Sender ID is randomly generated per connection to identify message source._
|
||||||
|
|
||||||
## Encryption Implementation
|
## Encryption Implementation
|
||||||
|
|
||||||
@@ -67,22 +69,23 @@
|
|||||||
|
|
||||||
## Message Types Reference
|
## Message Types Reference
|
||||||
|
|
||||||
| ID | Name | Subscribable | Encrypted | Compressed | Purpose |
|
| ID | Name | Subscribable | Encrypted | Compressed | Purpose |
|
||||||
|----|------|--------------|-----------|------------|---------|
|
| ------ | ------------------ | ------------ | --------- | ---------- | ----------------------------- |
|
||||||
| 0x0000 | Subscribe | ❌ | ❌ | ❌ | Subscribe to message type |
|
| 0x0000 | Subscribe | ❌ | ❌ | ❌ | Subscribe to message type |
|
||||||
| 0x0001 | Basic Message | ✅ | ✅ | ❌ | Chat messages |
|
| 0x0001 | Basic Message | ✅ | ✅ | ❌ | Chat messages |
|
||||||
| 0x0002 | Request User Data | ✅ | ✅ | ❌ | Request user info |
|
| 0x0002 | Request User Data | ✅ | ✅ | ❌ | Request user info |
|
||||||
| 0x0003 | User Data Response | ✅ | ✅ | ❌ | Respond with user info |
|
| 0x0003 | User Data Response | ✅ | ✅ | ❌ | Respond with user info |
|
||||||
| 0x0005 | Keepalive | ❌ | ❌ | ❌ | Prevent connection timeout |
|
| 0x0005 | Keepalive | ❌ | ❌ | ❌ | Prevent connection timeout |
|
||||||
| 0x0006 | Advanced Text | ✅ | ✅ | ✅ | Long/rich text messages |
|
| 0x0006 | Advanced Text | ✅ | ✅ | ✅ | Long/rich text messages |
|
||||||
| 0x0007 | Edit Advanced Text | ✅ | ✅ | ✅ | Edit/delete advanced text |
|
| 0x0007 | Edit Advanced Text | ✅ | ✅ | ✅ | Edit/delete advanced text |
|
||||||
| 0xFFFF | Unsubscribe | ❌ | ❌ | ❌ | Unsubscribe from message type |
|
| 0xFFFF | Unsubscribe | ❌ | ❌ | ❌ | Unsubscribe from message type |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Message Type Specifications
|
## Message Type Specifications
|
||||||
|
|
||||||
### Subscribe (0x0000)
|
### Subscribe (0x0000)
|
||||||
|
|
||||||
Subscribe to receive messages of specified type. Must resubscribe after disconnect.
|
Subscribe to receive messages of specified type. Must resubscribe after disconnect.
|
||||||
|
|
||||||
**Data:** Message type to subscribe to (2 bytes, big-endian)
|
**Data:** Message type to subscribe to (2 bytes, big-endian)
|
||||||
@@ -90,6 +93,7 @@ Subscribe to receive messages of specified type. Must resubscribe after disconne
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Basic Message (0x0001)
|
### Basic Message (0x0001)
|
||||||
|
|
||||||
UTF-8 chat messages. 16-byte nonce + encrypted data ≤ 1000 bytes total.
|
UTF-8 chat messages. 16-byte nonce + encrypted data ≤ 1000 bytes total.
|
||||||
|
|
||||||
**Data:** Encrypted UTF-8 string
|
**Data:** Encrypted UTF-8 string
|
||||||
@@ -97,9 +101,11 @@ UTF-8 chat messages. 16-byte nonce + encrypted data ≤ 1000 bytes total.
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Request User Data (0x0002)
|
### Request User Data (0x0002)
|
||||||
|
|
||||||
Request user information from all users. Clients should respond with User Data Response (0x0003).
|
Request user information from all users. Clients should respond with User Data Response (0x0003).
|
||||||
|
|
||||||
**Data Structure:**
|
**Data Structure:**
|
||||||
|
|
||||||
- Username length (2 bytes, big-endian)
|
- Username length (2 bytes, big-endian)
|
||||||
- Username (up to 32 bytes, UTF-8)
|
- Username (up to 32 bytes, UTF-8)
|
||||||
- Color RGB (3 bytes: R, G, B values)
|
- Color RGB (3 bytes: R, G, B values)
|
||||||
@@ -109,9 +115,11 @@ Request user information from all users. Clients should respond with User Data R
|
|||||||
---
|
---
|
||||||
|
|
||||||
### User Data Response (0x0003)
|
### User Data Response (0x0003)
|
||||||
|
|
||||||
Send user information in response to Request User Data (0x0002).
|
Send user information in response to Request User Data (0x0002).
|
||||||
|
|
||||||
**Data Structure:** Same as Request User Data
|
**Data Structure:** Same as Request User Data
|
||||||
|
|
||||||
- Username length (2 bytes, big-endian)
|
- Username length (2 bytes, big-endian)
|
||||||
- Username (up to 32 bytes, UTF-8)
|
- Username (up to 32 bytes, UTF-8)
|
||||||
- Color RGB (3 bytes: R, G, B values)
|
- Color RGB (3 bytes: R, G, B values)
|
||||||
@@ -121,6 +129,7 @@ Send user information in response to Request User Data (0x0002).
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Keepalive (0x0005)
|
### Keepalive (0x0005)
|
||||||
|
|
||||||
Prevent connection timeout when idle. Send every 30 seconds when no other traffic. Not forwarded to other clients and cannot be subscribed to.
|
Prevent connection timeout when idle. Send every 30 seconds when no other traffic. Not forwarded to other clients and cannot be subscribed to.
|
||||||
|
|
||||||
**Data:** None - empty message
|
**Data:** None - empty message
|
||||||
@@ -128,6 +137,7 @@ Prevent connection timeout when idle. Send every 30 seconds when no other traffi
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Unsubscribe (0xFFFF)
|
### Unsubscribe (0xFFFF)
|
||||||
|
|
||||||
Stop receiving messages of specified type.
|
Stop receiving messages of specified type.
|
||||||
|
|
||||||
**Data:** Message type to unsubscribe from (2 bytes, big-endian)
|
**Data:** Message type to unsubscribe from (2 bytes, big-endian)
|
||||||
@@ -135,9 +145,11 @@ Stop receiving messages of specified type.
|
|||||||
---
|
---
|
||||||
|
|
||||||
### Advanced Text (0x0006)
|
### Advanced Text (0x0006)
|
||||||
|
|
||||||
Multi-packet messages for long text with markdown formatting and ZSTD compression.
|
Multi-packet messages for long text with markdown formatting and ZSTD compression.
|
||||||
|
|
||||||
**Implementation Notes:**
|
**Implementation Notes:**
|
||||||
|
|
||||||
- Text is compressed **before** splitting into packets
|
- Text is compressed **before** splitting into packets
|
||||||
- Header is not compressed
|
- Header is not compressed
|
||||||
- Packets may arrive out of order - use packet numbers to reconstruct
|
- Packets may arrive out of order - use packet numbers to reconstruct
|
||||||
@@ -145,12 +157,14 @@ Multi-packet messages for long text with markdown formatting and ZSTD compressio
|
|||||||
- Warn users before displaying very large messages
|
- Warn users before displaying very large messages
|
||||||
|
|
||||||
**Data Structure:**
|
**Data Structure:**
|
||||||
|
|
||||||
- Message ID (4 bytes, big-endian)
|
- Message ID (4 bytes, big-endian)
|
||||||
- Packet number (2 bytes, big-endian, 0-indexed)
|
- Packet number (2 bytes, big-endian, 0-indexed)
|
||||||
- Final packet number (2 bytes, big-endian, 0-indexed)
|
- Final packet number (2 bytes, big-endian, 0-indexed)
|
||||||
- Compressed markdown text (remaining bytes, ZSTD)
|
- Compressed markdown text (remaining bytes, ZSTD)
|
||||||
|
|
||||||
### Edit Advanced Text (0x0007)
|
### Edit Advanced Text (0x0007)
|
||||||
|
|
||||||
Edit or delete existing Advanced Text messages. Use same Message ID to replace existing message. Empty compressed text deletes the message. Client replaces original when final packet received.
|
Edit or delete existing Advanced Text messages. Use same Message ID to replace existing message. Empty compressed text deletes the message. Client replaces original when final packet received.
|
||||||
|
|
||||||
**Data Structure:** Same as Advanced Text (0x0006)
|
**Data Structure:** Same as Advanced Text (0x0006)
|
||||||
@@ -160,12 +174,14 @@ Edit or delete existing Advanced Text messages. Use same Message ID to replace e
|
|||||||
## Implementation Guidelines
|
## Implementation Guidelines
|
||||||
|
|
||||||
### Validation Requirements
|
### Validation Requirements
|
||||||
|
|
||||||
- All length fields must not exceed their specified maximums
|
- All length fields must not exceed their specified maximums
|
||||||
- Username/Client ID lengths must match actual string lengths
|
- Username/Client ID lengths must match actual string lengths
|
||||||
- Message data must not exceed 1000 bytes (including nonce)
|
- Message data must not exceed 1000 bytes (including nonce)
|
||||||
- Packet numbers must be sequential and within final packet range
|
- Packet numbers must be sequential and within final packet range
|
||||||
|
|
||||||
### Connection Lifecycle
|
### Connection Lifecycle
|
||||||
|
|
||||||
1. **Connect** to server (TCP/WebSocket)
|
1. **Connect** to server (TCP/WebSocket)
|
||||||
2. **Subscribe** to required message types
|
2. **Subscribe** to required message types
|
||||||
3. **Send keepalive** every 30 seconds when idle
|
3. **Send keepalive** every 30 seconds when idle
|
||||||
@@ -173,6 +189,7 @@ Edit or delete existing Advanced Text messages. Use same Message ID to replace e
|
|||||||
5. **Handle** out-of-order packets for Advanced Text
|
5. **Handle** out-of-order packets for Advanced Text
|
||||||
|
|
||||||
### Common Issues
|
### Common Issues
|
||||||
|
|
||||||
- **Encryption fails**: Ensure message type in additional data matches packet header
|
- **Encryption fails**: Ensure message type in additional data matches packet header
|
||||||
- **Messages dropped**: Check total size ≤ 1000 bytes including nonce
|
- **Messages dropped**: Check total size ≤ 1000 bytes including nonce
|
||||||
- **Connection timeout**: Verify keepalive frequency
|
- **Connection timeout**: Verify keepalive frequency
|
||||||
@@ -188,4 +205,4 @@ Edit or delete existing Advanced Text messages. Use same Message ID to replace e
|
|||||||
- **Multi-packet support** for long messages
|
- **Multi-packet support** for long messages
|
||||||
- **TCP and WebSocket** transport options
|
- **TCP and WebSocket** transport options
|
||||||
|
|
||||||
This is a documentation-only repository. The protocol specification is final and changes are not permitted.
|
This is a documentation-only repository. The protocol specification is final and changes are not permitted.
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ The codebase implements the BENNC protocol specification with the following stru
|
|||||||
Each BENNC message type has its own module in `src/messages/`:
|
Each BENNC message type has its own module in `src/messages/`:
|
||||||
|
|
||||||
- `subscribe.ts` (0x0000) - Subscribe to message types
|
- `subscribe.ts` (0x0000) - Subscribe to message types
|
||||||
- `basic.ts` (0x0001) - Basic encrypted chat messages
|
- `basic.ts` (0x0001) - Basic encrypted chat messages
|
||||||
- `userDataRequest.ts` (0x0002) - Request user information
|
- `userDataRequest.ts` (0x0002) - Request user information
|
||||||
- `userDataResponse.ts` (0x0003) - Respond with user information
|
- `userDataResponse.ts` (0x0003) - Respond with user information
|
||||||
- `keepalive.ts` (0x0005) - Connection keepalive messages
|
- `keepalive.ts` (0x0005) - Connection keepalive messages
|
||||||
@@ -47,7 +47,7 @@ Each BENNC message type has its own module in `src/messages/`:
|
|||||||
|
|
||||||
### Utilities
|
### Utilities
|
||||||
|
|
||||||
- `src/utilities/number.ts` - Big-endian number conversion functions (`numberToUint16BE`, `numberToUint32BE`)
|
- `src/utilities/number.ts` - Big-endian number conversion functions (`numberToUint16BE`, `numberToUint32BE`)
|
||||||
- `src/utilities/smart-buffer.ts` - Buffer manipulation utility for packet construction/parsing
|
- `src/utilities/smart-buffer.ts` - Buffer manipulation utility for packet construction/parsing
|
||||||
|
|
||||||
### Key Architecture Patterns
|
### Key Architecture Patterns
|
||||||
@@ -57,4 +57,4 @@ Each BENNC message type has its own module in `src/messages/`:
|
|||||||
- **Encryption Integration**: Encrypted message types automatically handle nonce generation and Romulus-M encryption
|
- **Encryption Integration**: Encrypted message types automatically handle nonce generation and Romulus-M encryption
|
||||||
- **Protocol Compliance**: Strict adherence to BENNC specification including reserved sender IDs (0xFFFFFF00-0xFFFFFFFF)
|
- **Protocol Compliance**: Strict adherence to BENNC specification including reserved sender IDs (0xFFFFFF00-0xFFFFFFFF)
|
||||||
|
|
||||||
The codebase serves as a complete client-side implementation for connecting to BENNC chat servers via TCP or WebSocket protocols.
|
The codebase serves as a complete client-side implementation for connecting to BENNC chat servers via TCP or WebSocket protocols.
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
# BENNC-JS
|
# BENNC-JS
|
||||||
|
|
||||||
[](https://drone.jacknet.io/TerribleCodeClub/bennc-js)
|
[](https://drone.jacknet.io/TerribleCodeClub/bennc-js)
|
||||||
|
|
||||||
An implementation of the [BENNC](https://wiki.jacknet.io/books/simontech/chapter/bennc) client specification.
|
An implementation of the [BENNC](https://wiki.jacknet.io/books/simontech/chapter/bennc) client specification.
|
||||||
@@ -8,15 +9,18 @@ An implementation of the [BENNC](https://wiki.jacknet.io/books/simontech/chapter
|
|||||||
To build the BENNC-JS library, first clone this repository.
|
To build the BENNC-JS library, first clone this repository.
|
||||||
|
|
||||||
Run the following commands from the root of the repository:
|
Run the following commands from the root of the repository:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ npm install
|
$ npm install
|
||||||
$ npm run build
|
$ npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
The build output will be saved to the `dist` directory.
|
The build output will be saved to the `dist` directory.
|
||||||
|
|
||||||
## Development instructions
|
## Development instructions
|
||||||
|
|
||||||
Requirements:
|
Requirements:
|
||||||
|
|
||||||
- The latest LTS builds of Node and npm.
|
- The latest LTS builds of Node and npm.
|
||||||
|
|
||||||
Follow the instructions below to lint, test and build BENNC-JS.
|
Follow the instructions below to lint, test and build BENNC-JS.
|
||||||
@@ -34,6 +38,7 @@ $ npm run lint
|
|||||||
$ npm install
|
$ npm install
|
||||||
$ npm run test
|
$ npm run test
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Build
|
#### Build
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -43,7 +48,7 @@ $ npm run build
|
|||||||
|
|
||||||
### Visual Studio Code
|
### Visual Studio Code
|
||||||
|
|
||||||
This repository contains the necessary configuration files to debug, test and build BENNC-JS using only Visual Studio Code.
|
This repository contains the necessary configuration files to debug, test and build BENNC-JS using only Visual Studio Code.
|
||||||
|
|
||||||
Run the build task (`Ctrl+Shift+B` or `⇧⌘B`) to automatically compile the Typescript source files in the background.
|
Run the build task (`Ctrl+Shift+B` or `⇧⌘B`) to automatically compile the Typescript source files in the background.
|
||||||
|
|
||||||
|
|||||||
18
src/index.ts
18
src/index.ts
@@ -1,9 +1,9 @@
|
|||||||
export { numberToUint16BE, numberToUint32BE } from './utilities/number'
|
export { numberToUint16BE, numberToUint32BE } from "./utilities/number";
|
||||||
export { unpackIncomingPacket } from './messages/packet'
|
export { unpackIncomingPacket } from "./messages/packet";
|
||||||
export { packers, unpackers } from './mapping'
|
export { packers, unpackers } from "./mapping";
|
||||||
export { MessageTypes } from './common'
|
export { MessageTypes } from "./common";
|
||||||
export { IncomingPacket, OutgoingPacket } from './messages/packet'
|
export { IncomingPacket, OutgoingPacket } from "./messages/packet";
|
||||||
export { SubscribeMessage } from './messages/subscribe'
|
export { SubscribeMessage } from "./messages/subscribe";
|
||||||
export { BasicMessage } from './messages/basic'
|
export { BasicMessage } from "./messages/basic";
|
||||||
export { UserDataRequestMessage } from './messages/userDataRequest'
|
export { UserDataRequestMessage } from "./messages/userDataRequest";
|
||||||
export { UserDataResponseMessage } from './messages/userDataResponse'
|
export { UserDataResponseMessage } from "./messages/userDataResponse";
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { encrypt } from 'romulus-js'
|
import { encrypt } from "romulus-js";
|
||||||
import { DEFAULT_KEY, MessageTypes } from '../common'
|
import { DEFAULT_KEY, MessageTypes } from "../common";
|
||||||
import { numberToUint16BE } from '../utilities/number'
|
import { numberToUint16BE } from "../utilities/number";
|
||||||
import { packOutgoingPacket } from './packet'
|
import { packOutgoingPacket } from "./packet";
|
||||||
|
|
||||||
const MESSAGE_TYPE = numberToUint16BE(MessageTypes.Basic)
|
const MESSAGE_TYPE = numberToUint16BE(MessageTypes.Basic);
|
||||||
|
|
||||||
export interface BasicMessage {
|
export interface BasicMessage {
|
||||||
data: Uint8Array
|
data: Uint8Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,12 +15,15 @@ export interface BasicMessage {
|
|||||||
* @param key The key to encrypt the data with.
|
* @param key The key to encrypt the data with.
|
||||||
* @returns An encrypted outgoing basic message (0x0001) packet.
|
* @returns An encrypted outgoing basic message (0x0001) packet.
|
||||||
*/
|
*/
|
||||||
export function packBasicMessage (message: Uint8Array, key: Uint8Array = DEFAULT_KEY): Uint8Array {
|
export function packBasicMessage(
|
||||||
const data = encrypt(message, MESSAGE_TYPE, key)
|
message: Uint8Array,
|
||||||
|
key: Uint8Array = DEFAULT_KEY,
|
||||||
|
): Uint8Array {
|
||||||
|
const data = encrypt(message, MESSAGE_TYPE, key);
|
||||||
return packOutgoingPacket({
|
return packOutgoingPacket({
|
||||||
messageType: MESSAGE_TYPE,
|
messageType: MESSAGE_TYPE,
|
||||||
data: data
|
data: data,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,6 +31,6 @@ export function packBasicMessage (message: Uint8Array, key: Uint8Array = DEFAULT
|
|||||||
* @param data The data section of an incoming basic message (0x0001) message.
|
* @param data The data section of an incoming basic message (0x0001) message.
|
||||||
* @returns An encrypted unpacked basic message (0x0001) message.
|
* @returns An encrypted unpacked basic message (0x0001) message.
|
||||||
*/
|
*/
|
||||||
export function unpackBasicMessage (data: Uint8Array): Uint8Array {
|
export function unpackBasicMessage(data: Uint8Array): Uint8Array {
|
||||||
return data
|
return data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { MessageTypes } from '../common'
|
import { MessageTypes } from "../common";
|
||||||
import { numberToUint16BE } from '../utilities/number'
|
import { numberToUint16BE } from "../utilities/number";
|
||||||
import { packOutgoingPacket } from './packet'
|
import { packOutgoingPacket } from "./packet";
|
||||||
|
|
||||||
const MESSAGE_TYPE = numberToUint16BE(MessageTypes.Keepalive)
|
const MESSAGE_TYPE = numberToUint16BE(MessageTypes.Keepalive);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an outgoing keepalive (0x0005) packet.
|
* Create an outgoing keepalive (0x0005) packet.
|
||||||
* @returns An outgoing keepalive (0x0005) packet.
|
* @returns An outgoing keepalive (0x0005) packet.
|
||||||
*/
|
*/
|
||||||
export function packKeepaliveMessage (): Uint8Array {
|
export function packKeepaliveMessage(): Uint8Array {
|
||||||
return packOutgoingPacket({
|
return packOutgoingPacket({
|
||||||
messageType: MESSAGE_TYPE,
|
messageType: MESSAGE_TYPE,
|
||||||
data: new Uint8Array(0)
|
data: new Uint8Array(0),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { MAX_DATA_LENGTH } from '../common'
|
import { MAX_DATA_LENGTH } from "../common";
|
||||||
import { numberToUint16BE } from '../utilities/number'
|
import { numberToUint16BE } from "../utilities/number";
|
||||||
import { SmartBuffer } from '../utilities/smart-buffer'
|
import { SmartBuffer } from "../utilities/smart-buffer";
|
||||||
|
|
||||||
export interface IncomingPacket {
|
export interface IncomingPacket {
|
||||||
messageType: number
|
messageType: number;
|
||||||
senderId: number
|
senderId: number;
|
||||||
data: Uint8Array
|
data: Uint8Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OutgoingPacket {
|
export interface OutgoingPacket {
|
||||||
messageType: Uint8Array
|
messageType: Uint8Array;
|
||||||
data: Uint8Array
|
data: Uint8Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,19 +18,21 @@ export interface OutgoingPacket {
|
|||||||
* @param outgoingPacket The message type and data to send.
|
* @param outgoingPacket The message type and data to send.
|
||||||
* @returns A buffer containing the ready-to-send packet.
|
* @returns A buffer containing the ready-to-send packet.
|
||||||
*/
|
*/
|
||||||
export function packOutgoingPacket (outgoingPacket: OutgoingPacket): Uint8Array {
|
export function packOutgoingPacket(outgoingPacket: OutgoingPacket): Uint8Array {
|
||||||
// Verify that the data does not exceed the maximum data length.
|
// Verify that the data does not exceed the maximum data length.
|
||||||
if (outgoingPacket.data.length > MAX_DATA_LENGTH) {
|
if (outgoingPacket.data.length > MAX_DATA_LENGTH) {
|
||||||
throw RangeError(`Specified data of length ${outgoingPacket.data.length} exceeds max data length ${MAX_DATA_LENGTH}.`)
|
throw RangeError(
|
||||||
|
`Specified data of length ${outgoingPacket.data.length} exceeds max data length ${MAX_DATA_LENGTH}.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the outgoing packet.
|
// Prepare the outgoing packet.
|
||||||
const buffer = new SmartBuffer()
|
const buffer = new SmartBuffer();
|
||||||
buffer.writeBytes(outgoingPacket.messageType)
|
buffer.writeBytes(outgoingPacket.messageType);
|
||||||
buffer.writeBytes(numberToUint16BE(outgoingPacket.data.length))
|
buffer.writeBytes(numberToUint16BE(outgoingPacket.data.length));
|
||||||
buffer.writeBytes(outgoingPacket.data)
|
buffer.writeBytes(outgoingPacket.data);
|
||||||
|
|
||||||
return buffer.data
|
return buffer.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,17 +40,19 @@ export function packOutgoingPacket (outgoingPacket: OutgoingPacket): Uint8Array
|
|||||||
* @param incomingPacket The incoming buffer from a WebSocket to unpack.
|
* @param incomingPacket The incoming buffer from a WebSocket to unpack.
|
||||||
* @returns The unpacked data.
|
* @returns The unpacked data.
|
||||||
*/
|
*/
|
||||||
export function unpackIncomingPacket (incomingPacket: ArrayBuffer): IncomingPacket {
|
export function unpackIncomingPacket(
|
||||||
const buffer = SmartBuffer.from(incomingPacket)
|
incomingPacket: ArrayBuffer,
|
||||||
|
): IncomingPacket {
|
||||||
|
const buffer = SmartBuffer.from(incomingPacket);
|
||||||
|
|
||||||
const messageType = buffer.readUInt16()
|
const messageType = buffer.readUInt16();
|
||||||
const senderId = buffer.readUInt32()
|
const senderId = buffer.readUInt32();
|
||||||
const length = buffer.readUInt16()
|
const length = buffer.readUInt16();
|
||||||
const data = buffer.readBytes(length)
|
const data = buffer.readBytes(length);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
messageType: messageType,
|
messageType: messageType,
|
||||||
senderId: senderId,
|
senderId: senderId,
|
||||||
data: data
|
data: data,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { MessageTypes } from '../common'
|
import { MessageTypes } from "../common";
|
||||||
import { numberToUint16BE } from '../utilities/number'
|
import { numberToUint16BE } from "../utilities/number";
|
||||||
import { packOutgoingPacket } from './packet'
|
import { packOutgoingPacket } from "./packet";
|
||||||
|
|
||||||
const MESSAGE_TYPE = numberToUint16BE(MessageTypes.Subscribe)
|
const MESSAGE_TYPE = numberToUint16BE(MessageTypes.Subscribe);
|
||||||
|
|
||||||
export interface SubscribeMessage {
|
export interface SubscribeMessage {
|
||||||
messageType: number
|
messageType: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,10 +13,10 @@ export interface SubscribeMessage {
|
|||||||
* @param properties The properties for the message.
|
* @param properties The properties for the message.
|
||||||
* @returns An outgoing subscribe (0x0000) packet.
|
* @returns An outgoing subscribe (0x0000) packet.
|
||||||
*/
|
*/
|
||||||
export function packSubscribeMessage (properties: SubscribeMessage): Uint8Array {
|
export function packSubscribeMessage(properties: SubscribeMessage): Uint8Array {
|
||||||
const data = numberToUint16BE(properties.messageType)
|
const data = numberToUint16BE(properties.messageType);
|
||||||
return packOutgoingPacket({
|
return packOutgoingPacket({
|
||||||
messageType: MESSAGE_TYPE,
|
messageType: MESSAGE_TYPE,
|
||||||
data: data
|
data: data,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { MessageTypes } from '../common'
|
import { MessageTypes } from "../common";
|
||||||
import { numberToUint16BE } from '../utilities/number'
|
import { numberToUint16BE } from "../utilities/number";
|
||||||
import { packOutgoingPacket } from './packet'
|
import { packOutgoingPacket } from "./packet";
|
||||||
|
|
||||||
const MESSAGE_TYPE = numberToUint16BE(MessageTypes.Unsubscribe)
|
const MESSAGE_TYPE = numberToUint16BE(MessageTypes.Unsubscribe);
|
||||||
|
|
||||||
export interface UnsubscribeMessage {
|
export interface UnsubscribeMessage {
|
||||||
messageType: number
|
messageType: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,10 +13,12 @@ export interface UnsubscribeMessage {
|
|||||||
* @param properties The properties for the message.
|
* @param properties The properties for the message.
|
||||||
* @returns An outgoing unsubscribe (0xFFFF) packet.
|
* @returns An outgoing unsubscribe (0xFFFF) packet.
|
||||||
*/
|
*/
|
||||||
export function packUnsubscribeMessage (properties: UnsubscribeMessage): Uint8Array {
|
export function packUnsubscribeMessage(
|
||||||
const data = numberToUint16BE(properties.messageType)
|
properties: UnsubscribeMessage,
|
||||||
|
): Uint8Array {
|
||||||
|
const data = numberToUint16BE(properties.messageType);
|
||||||
return packOutgoingPacket({
|
return packOutgoingPacket({
|
||||||
messageType: MESSAGE_TYPE,
|
messageType: MESSAGE_TYPE,
|
||||||
data: data
|
data: data,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import Color from 'color'
|
import Color from "color";
|
||||||
import { encrypt } from 'romulus-js'
|
import { encrypt } from "romulus-js";
|
||||||
import { DEFAULT_KEY, MessageTypes } from '../common'
|
import { DEFAULT_KEY, MessageTypes } from "../common";
|
||||||
import { numberToUint16BE } from '../utilities/number'
|
import { numberToUint16BE } from "../utilities/number";
|
||||||
import { SmartBuffer } from '../utilities/smart-buffer'
|
import { SmartBuffer } from "../utilities/smart-buffer";
|
||||||
import { packOutgoingPacket } from './packet'
|
import { packOutgoingPacket } from "./packet";
|
||||||
|
|
||||||
const MESSAGE_TYPE = numberToUint16BE(MessageTypes.UserDataRequest)
|
const MESSAGE_TYPE = numberToUint16BE(MessageTypes.UserDataRequest);
|
||||||
|
|
||||||
export interface UserDataRequestMessage {
|
export interface UserDataRequestMessage {
|
||||||
username: string
|
username: string;
|
||||||
colour: Color
|
colour: Color;
|
||||||
clientId: string
|
clientId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,33 +19,36 @@ export interface UserDataRequestMessage {
|
|||||||
* @param key The key to encrypt the data with.
|
* @param key The key to encrypt the data with.
|
||||||
* @returns An outgoing user data request (0x0002) packet.
|
* @returns An outgoing user data request (0x0002) packet.
|
||||||
*/
|
*/
|
||||||
export function packUserDataRequestMessage (properties: UserDataRequestMessage, key: Uint8Array = DEFAULT_KEY): Uint8Array {
|
export function packUserDataRequestMessage(
|
||||||
const encoder = new TextEncoder()
|
properties: UserDataRequestMessage,
|
||||||
|
key: Uint8Array = DEFAULT_KEY,
|
||||||
|
): Uint8Array {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
|
||||||
// Prepare data in correct format.
|
// Prepare data in correct format.
|
||||||
const username = encoder.encode(properties.username)
|
const username = encoder.encode(properties.username);
|
||||||
const usernameLength = numberToUint16BE(username.length)
|
const usernameLength = numberToUint16BE(username.length);
|
||||||
const colour = new Uint8Array(properties.colour.array())
|
const colour = new Uint8Array(properties.colour.array());
|
||||||
const clientId = encoder.encode(properties.clientId)
|
const clientId = encoder.encode(properties.clientId);
|
||||||
const clientIdLength = numberToUint16BE(clientId.length)
|
const clientIdLength = numberToUint16BE(clientId.length);
|
||||||
|
|
||||||
// Pack data.
|
// Pack data.
|
||||||
const packedData = new SmartBuffer()
|
const packedData = new SmartBuffer();
|
||||||
packedData.writeBytes(usernameLength)
|
packedData.writeBytes(usernameLength);
|
||||||
packedData.writeBytes(username)
|
packedData.writeBytes(username);
|
||||||
packedData.pad(32 - username.length)
|
packedData.pad(32 - username.length);
|
||||||
packedData.writeBytes(colour)
|
packedData.writeBytes(colour);
|
||||||
packedData.writeBytes(clientIdLength)
|
packedData.writeBytes(clientIdLength);
|
||||||
packedData.writeBytes(clientId)
|
packedData.writeBytes(clientId);
|
||||||
packedData.pad(32 - clientId.length)
|
packedData.pad(32 - clientId.length);
|
||||||
|
|
||||||
// Encrypt the data.
|
// Encrypt the data.
|
||||||
const data = encrypt(packedData.data, MESSAGE_TYPE, key)
|
const data = encrypt(packedData.data, MESSAGE_TYPE, key);
|
||||||
|
|
||||||
return packOutgoingPacket({
|
return packOutgoingPacket({
|
||||||
messageType: MESSAGE_TYPE,
|
messageType: MESSAGE_TYPE,
|
||||||
data: data
|
data: data,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,23 +56,25 @@ export function packUserDataRequestMessage (properties: UserDataRequestMessage,
|
|||||||
* @param data The decrypted data section of an incoming user data request (0x0002) message.
|
* @param data The decrypted data section of an incoming user data request (0x0002) message.
|
||||||
* @returns An unpacked user data request (0x0002) message.
|
* @returns An unpacked user data request (0x0002) message.
|
||||||
*/
|
*/
|
||||||
export function unpackUserDataRequestMessage (data: Uint8Array): UserDataRequestMessage {
|
export function unpackUserDataRequestMessage(
|
||||||
|
data: Uint8Array,
|
||||||
|
): UserDataRequestMessage {
|
||||||
// Unpack and read data in correct format.
|
// Unpack and read data in correct format.
|
||||||
const packedData = SmartBuffer.from(data)
|
const packedData = SmartBuffer.from(data);
|
||||||
|
|
||||||
const usernameLength = packedData.readUInt16()
|
const usernameLength = packedData.readUInt16();
|
||||||
const username = packedData.readBytes(usernameLength)
|
const username = packedData.readBytes(usernameLength);
|
||||||
packedData.cursor = 34
|
packedData.cursor = 34;
|
||||||
const colour = packedData.readBytes(3)
|
const colour = packedData.readBytes(3);
|
||||||
const clientIdLength = packedData.readUInt16()
|
const clientIdLength = packedData.readUInt16();
|
||||||
const clientId = packedData.readBytes(clientIdLength)
|
const clientId = packedData.readBytes(clientIdLength);
|
||||||
|
|
||||||
const decoder = new TextDecoder()
|
const decoder = new TextDecoder();
|
||||||
|
|
||||||
// Return data in correct format.
|
// Return data in correct format.
|
||||||
return {
|
return {
|
||||||
username: decoder.decode(username),
|
username: decoder.decode(username),
|
||||||
colour: Color.rgb(colour),
|
colour: Color.rgb(colour),
|
||||||
clientId: decoder.decode(clientId)
|
clientId: decoder.decode(clientId),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import Color from 'color'
|
import Color from "color";
|
||||||
import { encrypt } from 'romulus-js'
|
import { encrypt } from "romulus-js";
|
||||||
import { DEFAULT_KEY, MessageTypes } from '../common'
|
import { DEFAULT_KEY, MessageTypes } from "../common";
|
||||||
import { numberToUint16BE } from '../utilities/number'
|
import { numberToUint16BE } from "../utilities/number";
|
||||||
import { SmartBuffer } from '../utilities/smart-buffer'
|
import { SmartBuffer } from "../utilities/smart-buffer";
|
||||||
import { packOutgoingPacket } from './packet'
|
import { packOutgoingPacket } from "./packet";
|
||||||
|
|
||||||
const MESSAGE_TYPE = numberToUint16BE(MessageTypes.UserDataResponse)
|
const MESSAGE_TYPE = numberToUint16BE(MessageTypes.UserDataResponse);
|
||||||
|
|
||||||
export interface UserDataResponseMessage {
|
export interface UserDataResponseMessage {
|
||||||
username: string
|
username: string;
|
||||||
colour: Color
|
colour: Color;
|
||||||
clientId: string
|
clientId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,32 +19,35 @@ export interface UserDataResponseMessage {
|
|||||||
* @param key The key to encrypt the data with.
|
* @param key The key to encrypt the data with.
|
||||||
* @returns The data section of an outgoing user data response (0x0003) message.
|
* @returns The data section of an outgoing user data response (0x0003) message.
|
||||||
*/
|
*/
|
||||||
export function packUserDataResponseMessage (properties: UserDataResponseMessage, key: Uint8Array = DEFAULT_KEY): Uint8Array {
|
export function packUserDataResponseMessage(
|
||||||
const encoder = new TextEncoder()
|
properties: UserDataResponseMessage,
|
||||||
|
key: Uint8Array = DEFAULT_KEY,
|
||||||
|
): Uint8Array {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
|
||||||
// Prepare data in correct format.
|
// Prepare data in correct format.
|
||||||
const username = encoder.encode(properties.username)
|
const username = encoder.encode(properties.username);
|
||||||
const usernameLength = numberToUint16BE(username.length)
|
const usernameLength = numberToUint16BE(username.length);
|
||||||
const colour = new Uint8Array(properties.colour.array())
|
const colour = new Uint8Array(properties.colour.array());
|
||||||
const clientId = encoder.encode(properties.clientId)
|
const clientId = encoder.encode(properties.clientId);
|
||||||
const clientIdLength = numberToUint16BE(clientId.length)
|
const clientIdLength = numberToUint16BE(clientId.length);
|
||||||
|
|
||||||
// Pack data.
|
// Pack data.
|
||||||
const packedData = new SmartBuffer()
|
const packedData = new SmartBuffer();
|
||||||
packedData.writeBytes(usernameLength)
|
packedData.writeBytes(usernameLength);
|
||||||
packedData.writeBytes(username)
|
packedData.writeBytes(username);
|
||||||
packedData.pad(32 - username.length)
|
packedData.pad(32 - username.length);
|
||||||
packedData.writeBytes(colour)
|
packedData.writeBytes(colour);
|
||||||
packedData.writeBytes(clientIdLength)
|
packedData.writeBytes(clientIdLength);
|
||||||
packedData.writeBytes(clientId)
|
packedData.writeBytes(clientId);
|
||||||
packedData.pad(32 - clientId.length)
|
packedData.pad(32 - clientId.length);
|
||||||
|
|
||||||
// Return encrypted data.
|
// Return encrypted data.
|
||||||
const data = encrypt(packedData.data, MESSAGE_TYPE, key)
|
const data = encrypt(packedData.data, MESSAGE_TYPE, key);
|
||||||
return packOutgoingPacket({
|
return packOutgoingPacket({
|
||||||
messageType: MESSAGE_TYPE,
|
messageType: MESSAGE_TYPE,
|
||||||
data: data
|
data: data,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,23 +55,25 @@ export function packUserDataResponseMessage (properties: UserDataResponseMessage
|
|||||||
* @param data The decrypted data section of an incoming user data response (0x0003) message.
|
* @param data The decrypted data section of an incoming user data response (0x0003) message.
|
||||||
* @returns A unpacked user data response (0x0003) message.
|
* @returns A unpacked user data response (0x0003) message.
|
||||||
*/
|
*/
|
||||||
export function unpackUserDataResponseMessage (data: Uint8Array): UserDataResponseMessage {
|
export function unpackUserDataResponseMessage(
|
||||||
|
data: Uint8Array,
|
||||||
|
): UserDataResponseMessage {
|
||||||
// Unpack and read data in correct format.
|
// Unpack and read data in correct format.
|
||||||
const packedData = SmartBuffer.from(data)
|
const packedData = SmartBuffer.from(data);
|
||||||
|
|
||||||
const usernameLength = packedData.readUInt16()
|
const usernameLength = packedData.readUInt16();
|
||||||
const username = packedData.readBytes(usernameLength)
|
const username = packedData.readBytes(usernameLength);
|
||||||
packedData.cursor = 34
|
packedData.cursor = 34;
|
||||||
const colour = packedData.readBytes(3)
|
const colour = packedData.readBytes(3);
|
||||||
const clientIdLength = packedData.readUInt16()
|
const clientIdLength = packedData.readUInt16();
|
||||||
const clientId = packedData.readBytes(clientIdLength)
|
const clientId = packedData.readBytes(clientIdLength);
|
||||||
|
|
||||||
const decoder = new TextDecoder()
|
const decoder = new TextDecoder();
|
||||||
|
|
||||||
// Return data in correct format.
|
// Return data in correct format.
|
||||||
return {
|
return {
|
||||||
username: decoder.decode(username),
|
username: decoder.decode(username),
|
||||||
colour: Color.rgb(colour),
|
colour: Color.rgb(colour),
|
||||||
clientId: decoder.decode(clientId)
|
clientId: decoder.decode(clientId),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
* @param number The number to pack.
|
* @param number The number to pack.
|
||||||
* @returns The packed buffer.
|
* @returns The packed buffer.
|
||||||
*/
|
*/
|
||||||
export function numberToUint16BE (number: number): Uint8Array {
|
export function numberToUint16BE(number: number): Uint8Array {
|
||||||
const ret = new Uint8Array(2)
|
const ret = new Uint8Array(2);
|
||||||
ret[0] = (number & 0xFF00) >> 8
|
ret[0] = (number & 0xff00) >> 8;
|
||||||
ret[1] = (number & 0x00FF) >> 0
|
ret[1] = (number & 0x00ff) >> 0;
|
||||||
return ret
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,11 +15,11 @@ export function numberToUint16BE (number: number): Uint8Array {
|
|||||||
* @param number The number to pack.
|
* @param number The number to pack.
|
||||||
* @returns The packed buffer.
|
* @returns The packed buffer.
|
||||||
*/
|
*/
|
||||||
export function numberToUint32BE (number: number): Uint8Array {
|
export function numberToUint32BE(number: number): Uint8Array {
|
||||||
const ret = new Uint8Array(4)
|
const ret = new Uint8Array(4);
|
||||||
ret[0] = (number & 0xFF000000) >> 24
|
ret[0] = (number & 0xff000000) >> 24;
|
||||||
ret[1] = (number & 0x00FF0000) >> 16
|
ret[1] = (number & 0x00ff0000) >> 16;
|
||||||
ret[2] = (number & 0x0000FF00) >> 8
|
ret[2] = (number & 0x0000ff00) >> 8;
|
||||||
ret[3] = (number & 0x000000FF) >> 0
|
ret[3] = (number & 0x000000ff) >> 0;
|
||||||
return ret
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,62 +1,64 @@
|
|||||||
import { numberToUint16BE, numberToUint32BE } from './number'
|
import { numberToUint16BE, numberToUint32BE } from "./number";
|
||||||
|
|
||||||
export class SmartBuffer {
|
export class SmartBuffer {
|
||||||
private _data: number[]
|
private _data: number[];
|
||||||
private _cursor: number
|
private _cursor: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap a buffer to track position and provide useful read / write functionality.
|
* Wrap a buffer to track position and provide useful read / write functionality.
|
||||||
* @param data Buffer to wrap (optional).
|
* @param data Buffer to wrap (optional).
|
||||||
*/
|
*/
|
||||||
constructor (length: number = 0) {
|
constructor(length: number = 0) {
|
||||||
this._data = new Array<number>(length)
|
this._data = new Array<number>(length);
|
||||||
this._cursor = 0
|
this._cursor = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a regular buffer.
|
* Return a regular buffer.
|
||||||
*/
|
*/
|
||||||
get data (): Uint8Array {
|
get data(): Uint8Array {
|
||||||
return new Uint8Array(this._data)
|
return new Uint8Array(this._data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the smart buffer to wrap new data.
|
* Update the smart buffer to wrap new data.
|
||||||
*/
|
*/
|
||||||
set data (data: Uint8Array) {
|
set data(data: Uint8Array) {
|
||||||
this._data = Array.from(data)
|
this._data = Array.from(data);
|
||||||
this.cursor = 0
|
this.cursor = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the length of the smart buffer data.
|
* Get the length of the smart buffer data.
|
||||||
*/
|
*/
|
||||||
get length (): number {
|
get length(): number {
|
||||||
return this._data.length
|
return this._data.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the length of the smart buffer data.
|
* Set the length of the smart buffer data.
|
||||||
*/
|
*/
|
||||||
set length (length: number) {
|
set length(length: number) {
|
||||||
this._data.length = length
|
this._data.length = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current cursor position of the smart buffer.
|
* Get the current cursor position of the smart buffer.
|
||||||
*/
|
*/
|
||||||
get cursor (): number {
|
get cursor(): number {
|
||||||
return this._cursor
|
return this._cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the cursor position of the smart buffer.
|
* Set the cursor position of the smart buffer.
|
||||||
*/
|
*/
|
||||||
set cursor (position: number) {
|
set cursor(position: number) {
|
||||||
if (position < 0) {
|
if (position < 0) {
|
||||||
throw RangeError(`Cannot seek to ${this.cursor} of ${this.length} bytes.`)
|
throw RangeError(
|
||||||
|
`Cannot seek to ${this.cursor} of ${this.length} bytes.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this._cursor = position
|
this._cursor = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,33 +66,33 @@ export class SmartBuffer {
|
|||||||
* @param data The object to convert to a new SmartBuffer.
|
* @param data The object to convert to a new SmartBuffer.
|
||||||
* @returns A new SmartBuffer.
|
* @returns A new SmartBuffer.
|
||||||
*/
|
*/
|
||||||
static from (data: number[] | ArrayBuffer): SmartBuffer {
|
static from(data: number[] | ArrayBuffer): SmartBuffer {
|
||||||
const smartBuffer = new SmartBuffer()
|
const smartBuffer = new SmartBuffer();
|
||||||
if (data instanceof ArrayBuffer) {
|
if (data instanceof ArrayBuffer) {
|
||||||
smartBuffer._data = Array.from(new Uint8Array(data))
|
smartBuffer._data = Array.from(new Uint8Array(data));
|
||||||
} else {
|
} else {
|
||||||
smartBuffer._data = Array.from(data)
|
smartBuffer._data = Array.from(data);
|
||||||
}
|
}
|
||||||
return smartBuffer
|
return smartBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pads bytes to the end of the smart buffer.
|
* Pads bytes to the end of the smart buffer.
|
||||||
* @param length The number of bytes to pad.
|
* @param length The number of bytes to pad.
|
||||||
*/
|
*/
|
||||||
pad (length: number): void {
|
pad(length: number): void {
|
||||||
this._data.push(...Array<number>(length).fill(0))
|
this._data.push(...Array<number>(length).fill(0));
|
||||||
this.cursor += length
|
this.cursor += length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the data from the specified range.
|
* Return the data from the specified range.
|
||||||
* @param start The start position.
|
* @param start The start position.
|
||||||
* @param end The end position.
|
* @param end The end position.
|
||||||
* @returns A new buffer containing data from the specified range.
|
* @returns A new buffer containing data from the specified range.
|
||||||
*/
|
*/
|
||||||
slice (start: number, end: number): Uint8Array {
|
slice(start: number, end: number): Uint8Array {
|
||||||
return new Uint8Array(this._data.slice(start, end))
|
return new Uint8Array(this._data.slice(start, end));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,66 +101,66 @@ export class SmartBuffer {
|
|||||||
* @param deleteCount The number of items to remove before inserting new data.
|
* @param deleteCount The number of items to remove before inserting new data.
|
||||||
* @param items The items to insert at the specified position.
|
* @param items The items to insert at the specified position.
|
||||||
*/
|
*/
|
||||||
splice (start: number, deleteCount: number, ...items: number[]): void {
|
splice(start: number, deleteCount: number, ...items: number[]): void {
|
||||||
if (this.length < start) {
|
if (this.length < start) {
|
||||||
this._data.push(...Array<number>(start))
|
this._data.push(...Array<number>(start));
|
||||||
}
|
}
|
||||||
this._data.splice(this.cursor, deleteCount, ...items)
|
this._data.splice(this.cursor, deleteCount, ...items);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a UInt16 number from the smart buffer at the current cursor position, and increment the cursor.
|
* Read a UInt16 number from the smart buffer at the current cursor position, and increment the cursor.
|
||||||
* @returns A number represented by the bytes at the current cursor position.
|
* @returns A number represented by the bytes at the current cursor position.
|
||||||
*/
|
*/
|
||||||
readUInt16 (): number {
|
readUInt16(): number {
|
||||||
const num = this.slice(this.cursor, this.cursor + 2)
|
const num = this.slice(this.cursor, this.cursor + 2);
|
||||||
this.cursor += 2
|
this.cursor += 2;
|
||||||
return (num[0] << 8 | num[1]) >>> 0
|
return ((num[0] << 8) | num[1]) >>> 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a UInt32 number from the smart buffer at the current cursor position, and increment the cursor.
|
* Read a UInt32 number from the smart buffer at the current cursor position, and increment the cursor.
|
||||||
* @returns A number represented by the bytes at the current cursor position.
|
* @returns A number represented by the bytes at the current cursor position.
|
||||||
*/
|
*/
|
||||||
readUInt32 (): number {
|
readUInt32(): number {
|
||||||
const num = this.slice(this.cursor, this.cursor + 4)
|
const num = this.slice(this.cursor, this.cursor + 4);
|
||||||
this.cursor += 4
|
this.cursor += 4;
|
||||||
return (num[0] << 24 | num[1] << 16 | num[2] << 8 | num[3]) >>> 0
|
return ((num[0] << 24) | (num[1] << 16) | (num[2] << 8) | num[3]) >>> 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the specified number of bytes from the smart buffer at the current cursor position, and increment the cursor.
|
* Read the specified number of bytes from the smart buffer at the current cursor position, and increment the cursor.
|
||||||
* @returns A buffer containing the bytes read from the current cursor position.
|
* @returns A buffer containing the bytes read from the current cursor position.
|
||||||
*/
|
*/
|
||||||
readBytes (length: number): Uint8Array {
|
readBytes(length: number): Uint8Array {
|
||||||
this.cursor += length
|
this.cursor += length;
|
||||||
return this.slice(this.cursor - length, this.cursor)
|
return this.slice(this.cursor - length, this.cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a UInt16 number to the smart buffer at the current cursor position, and increment the cursor.
|
* Write a UInt16 number to the smart buffer at the current cursor position, and increment the cursor.
|
||||||
* @param number The number to write in UInt16 format at the current cursor position.
|
* @param number The number to write in UInt16 format at the current cursor position.
|
||||||
*/
|
*/
|
||||||
writeUInt16 (number: number): void {
|
writeUInt16(number: number): void {
|
||||||
this.splice(this.cursor, 2, ...numberToUint16BE(number))
|
this.splice(this.cursor, 2, ...numberToUint16BE(number));
|
||||||
this.cursor += 2
|
this.cursor += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a UInt32 number to the smart buffer at the current cursor position, and increment the cursor.
|
* Write a UInt32 number to the smart buffer at the current cursor position, and increment the cursor.
|
||||||
* @param number The number to write in UInt32 format at the current cursor position.
|
* @param number The number to write in UInt32 format at the current cursor position.
|
||||||
*/
|
*/
|
||||||
writeUInt32 (number: number): void {
|
writeUInt32(number: number): void {
|
||||||
this.splice(this.cursor, 4, ...numberToUint32BE(number))
|
this.splice(this.cursor, 4, ...numberToUint32BE(number));
|
||||||
this.cursor += 4
|
this.cursor += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the specified bytes to the smart buffer at the current cursor position, and increment the cursor.
|
* Write the specified bytes to the smart buffer at the current cursor position, and increment the cursor.
|
||||||
* @param buffer The bytes to write at the current cursor position.
|
* @param buffer The bytes to write at the current cursor position.
|
||||||
*/
|
*/
|
||||||
writeBytes (buffer: number[] | Uint8Array): void {
|
writeBytes(buffer: number[] | Uint8Array): void {
|
||||||
this.splice(this.cursor, buffer.length, ...buffer)
|
this.splice(this.cursor, buffer.length, ...buffer);
|
||||||
this.cursor += buffer.length
|
this.cursor += buffer.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,37 @@
|
|||||||
import { MessageTypes } from '../../src/common'
|
import { MessageTypes } from "../../src/common";
|
||||||
import { packers, unpackers } from '../../src/mapping'
|
import { packers, unpackers } from "../../src/mapping";
|
||||||
|
|
||||||
const KEY = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
|
const KEY = new Uint8Array([
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
]);
|
||||||
|
|
||||||
test('Create a basic message (0x0001) packet.', () => {
|
test("Create a basic message (0x0001) packet.", () => {
|
||||||
// Given
|
// Given
|
||||||
const encoder = new TextEncoder()
|
const encoder = new TextEncoder();
|
||||||
const message = encoder.encode('Hello, World!')
|
const message = encoder.encode("Hello, World!");
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const packedPacket = packers[MessageTypes.Basic](
|
const packedPacket = packers[MessageTypes.Basic](message, KEY);
|
||||||
message,
|
|
||||||
KEY
|
|
||||||
)
|
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
// We can't check the contents of the data as it's encrypted with a random nonce.
|
// We can't check the contents of the data as it's encrypted with a random nonce.
|
||||||
// Check the message type and length.
|
// Check the message type and length.
|
||||||
expect(packedPacket.slice(0, 4)).toMatchObject(new Uint8Array([0x00, 0x01, 0x00, 0x2D]))
|
expect(packedPacket.slice(0, 4)).toMatchObject(
|
||||||
|
new Uint8Array([0x00, 0x01, 0x00, 0x2d]),
|
||||||
|
);
|
||||||
|
|
||||||
// Check the total length is as expected.
|
// Check the total length is as expected.
|
||||||
expect(packedPacket.length).toBe(49)
|
expect(packedPacket.length).toBe(49);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Parse a basic message (0x0001).', () => {
|
test("Parse a basic message (0x0001).", () => {
|
||||||
// Given
|
// Given
|
||||||
const data = new Uint8Array([1, 2, 3, 4])
|
const data = new Uint8Array([1, 2, 3, 4]);
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const unpackedPacket = unpackers[MessageTypes.Basic](data)
|
const unpackedPacket = unpackers[MessageTypes.Basic](data);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(unpackedPacket)
|
expect(unpackedPacket);
|
||||||
expect(unpackedPacket).toMatchObject(data)
|
expect(unpackedPacket).toMatchObject(data);
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { MessageTypes } from '../../src/common'
|
import { MessageTypes } from "../../src/common";
|
||||||
import { packers } from '../../src/mapping'
|
import { packers } from "../../src/mapping";
|
||||||
|
|
||||||
test('Create a keepalive (0x0005) packet.', () => {
|
test("Create a keepalive (0x0005) packet.", () => {
|
||||||
// When
|
// When
|
||||||
const packedPacket = packers[MessageTypes.Keepalive]()
|
const packedPacket = packers[MessageTypes.Keepalive]();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
const expectedResult = new Uint8Array([0x00, 0x05, 0x00, 0x00])
|
const expectedResult = new Uint8Array([0x00, 0x05, 0x00, 0x00]);
|
||||||
expect(packedPacket).toMatchObject(expectedResult)
|
expect(packedPacket).toMatchObject(expectedResult);
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
import { packOutgoingPacket, unpackIncomingPacket } from '../../src/messages/packet'
|
import {
|
||||||
|
packOutgoingPacket,
|
||||||
|
unpackIncomingPacket,
|
||||||
|
} from "../../src/messages/packet";
|
||||||
|
|
||||||
test('Pack an outgoing packet.', () => {
|
test("Pack an outgoing packet.", () => {
|
||||||
// Given
|
// Given
|
||||||
const messageType = new Uint8Array([0x12, 0x34])
|
const messageType = new Uint8Array([0x12, 0x34]);
|
||||||
const data = new Uint8Array([0x12, 0x34, 0x56, 0x78])
|
const data = new Uint8Array([0x12, 0x34, 0x56, 0x78]);
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const packedPacket = packOutgoingPacket({
|
const packedPacket = packOutgoingPacket({
|
||||||
messageType: messageType,
|
messageType: messageType,
|
||||||
data: data
|
data: data,
|
||||||
})
|
});
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
const expectedResult = new Uint8Array([
|
const expectedResult = new Uint8Array([
|
||||||
@@ -18,12 +21,12 @@ test('Pack an outgoing packet.', () => {
|
|||||||
// Data length
|
// Data length
|
||||||
0x00, 0x04,
|
0x00, 0x04,
|
||||||
// Data
|
// Data
|
||||||
0x12, 0x34, 0x56, 0x78
|
0x12, 0x34, 0x56, 0x78,
|
||||||
])
|
]);
|
||||||
expect(packedPacket).toMatchObject(expectedResult)
|
expect(packedPacket).toMatchObject(expectedResult);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Unpack an incoming packet.', () => {
|
test("Unpack an incoming packet.", () => {
|
||||||
// Given
|
// Given
|
||||||
const incomingPacket = new Uint8Array([
|
const incomingPacket = new Uint8Array([
|
||||||
// Message type
|
// Message type
|
||||||
@@ -33,14 +36,16 @@ test('Unpack an incoming packet.', () => {
|
|||||||
// Data length
|
// Data length
|
||||||
0x00, 0x04,
|
0x00, 0x04,
|
||||||
// Data
|
// Data
|
||||||
0x12, 0x34, 0x56, 0x78
|
0x12, 0x34, 0x56, 0x78,
|
||||||
])
|
]);
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const unpackedResult = unpackIncomingPacket(incomingPacket)
|
const unpackedResult = unpackIncomingPacket(incomingPacket);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(unpackedResult.messageType).toBe(0x1234)
|
expect(unpackedResult.messageType).toBe(0x1234);
|
||||||
expect(unpackedResult.senderId).toBe(0xaabbccdd)
|
expect(unpackedResult.senderId).toBe(0xaabbccdd);
|
||||||
expect(unpackedResult.data).toMatchObject(new Uint8Array([0x12, 0x34, 0x56, 0x78]))
|
expect(unpackedResult.data).toMatchObject(
|
||||||
})
|
new Uint8Array([0x12, 0x34, 0x56, 0x78]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { MessageTypes } from '../../src/common'
|
import { MessageTypes } from "../../src/common";
|
||||||
import { packers } from '../../src/mapping'
|
import { packers } from "../../src/mapping";
|
||||||
|
|
||||||
test('Create a subscribe (0x0000) packet.', () => {
|
test("Create a subscribe (0x0000) packet.", () => {
|
||||||
// Given
|
// Given
|
||||||
const messageType = 0xabcd
|
const messageType = 0xabcd;
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const packedPacket = packers[MessageTypes.Subscribe]({ messageType: messageType })
|
const packedPacket = packers[MessageTypes.Subscribe]({
|
||||||
|
messageType: messageType,
|
||||||
|
});
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
const expectedResult = new Uint8Array([0x00, 0x00, 0x00, 0x02, 0xab, 0xcd])
|
const expectedResult = new Uint8Array([0x00, 0x00, 0x00, 0x02, 0xab, 0xcd]);
|
||||||
expect(packedPacket).toMatchObject(expectedResult)
|
expect(packedPacket).toMatchObject(expectedResult);
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { MessageTypes } from '../../src/common'
|
import { MessageTypes } from "../../src/common";
|
||||||
import { packers } from '../../src/mapping'
|
import { packers } from "../../src/mapping";
|
||||||
|
|
||||||
test('Create an unsubscribe (0xffff) packet.', () => {
|
test("Create an unsubscribe (0xffff) packet.", () => {
|
||||||
// Given
|
// Given
|
||||||
const messageType = 0xabcd
|
const messageType = 0xabcd;
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const packedPacket = packers[MessageTypes.Unsubscribe]({ messageType: messageType })
|
const packedPacket = packers[MessageTypes.Unsubscribe]({
|
||||||
|
messageType: messageType,
|
||||||
|
});
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
const expectedResult = new Uint8Array([0xff, 0xff, 0x00, 0x02, 0xab, 0xcd])
|
const expectedResult = new Uint8Array([0xff, 0xff, 0x00, 0x02, 0xab, 0xcd]);
|
||||||
expect(packedPacket).toMatchObject(expectedResult)
|
expect(packedPacket).toMatchObject(expectedResult);
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,52 +1,56 @@
|
|||||||
import Color from 'color'
|
import Color from "color";
|
||||||
import { MessageTypes } from '../../src/common'
|
import { MessageTypes } from "../../src/common";
|
||||||
import { packers, unpackers } from '../../src/mapping'
|
import { packers, unpackers } from "../../src/mapping";
|
||||||
|
|
||||||
const KEY = new Uint8Array([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F])
|
const KEY = new Uint8Array([
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
|
||||||
|
0x0d, 0x0e, 0x0f,
|
||||||
|
]);
|
||||||
|
|
||||||
test('Create a user data request (0x0002) packet.', () => {
|
test("Create a user data request (0x0002) packet.", () => {
|
||||||
// Given
|
// Given
|
||||||
const username = 'Butlersaurus'
|
const username = "Butlersaurus";
|
||||||
const colour = Color('#FF4000')
|
const colour = Color("#FF4000");
|
||||||
const clientId = 'Mercury'
|
const clientId = "Mercury";
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const packedPacket = packers[MessageTypes.UserDataRequest](
|
const packedPacket = packers[MessageTypes.UserDataRequest](
|
||||||
{
|
{
|
||||||
username: username,
|
username: username,
|
||||||
colour: colour,
|
colour: colour,
|
||||||
clientId: clientId
|
clientId: clientId,
|
||||||
},
|
},
|
||||||
KEY
|
KEY,
|
||||||
)
|
);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
// We can't check the contents of the data as it's encrypted with a random nonce.
|
// We can't check the contents of the data as it's encrypted with a random nonce.
|
||||||
// Check the message type and length.
|
// Check the message type and length.
|
||||||
expect(packedPacket.slice(0, 4)).toMatchObject(new Uint8Array([0x00, 0x02, 0x00, 0x67]))
|
expect(packedPacket.slice(0, 4)).toMatchObject(
|
||||||
|
new Uint8Array([0x00, 0x02, 0x00, 0x67]),
|
||||||
|
);
|
||||||
|
|
||||||
// Check the total length is as expected.
|
// Check the total length is as expected.
|
||||||
expect(packedPacket.length).toBe(107)
|
expect(packedPacket.length).toBe(107);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Parse a user data request (0x0002).', () => {
|
test("Parse a user data request (0x0002).", () => {
|
||||||
// Given
|
// Given
|
||||||
const data = new Uint8Array([
|
const data = new Uint8Array([
|
||||||
0, 12,
|
0, 12, 66, 117, 116, 108, 101, 114, 115, 97, 117, 114, 117, 115, 0, 0, 0, 0,
|
||||||
66, 117, 116, 108, 101, 114, 115, 97, 117, 114, 117, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 64, 0, 0, 7, 77, 101,
|
||||||
255, 64, 0,
|
114, 99, 117, 114, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 7,
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
77, 101, 114, 99, 117, 114, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
]);
|
||||||
])
|
const username = "Butlersaurus";
|
||||||
const username = 'Butlersaurus'
|
const colour = Color("#FF4000");
|
||||||
const colour = Color('#FF4000')
|
const clientId = "Mercury";
|
||||||
const clientId = 'Mercury'
|
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const unpackedPacket = unpackers[MessageTypes.UserDataRequest](data)
|
const unpackedPacket = unpackers[MessageTypes.UserDataRequest](data);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(unpackedPacket.username).toBe(username)
|
expect(unpackedPacket.username).toBe(username);
|
||||||
expect(unpackedPacket.colour).toMatchObject(colour)
|
expect(unpackedPacket.colour).toMatchObject(colour);
|
||||||
expect(unpackedPacket.clientId).toBe(clientId)
|
expect(unpackedPacket.clientId).toBe(clientId);
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,52 +1,56 @@
|
|||||||
import Color from 'color'
|
import Color from "color";
|
||||||
import { MessageTypes } from '../../src/common'
|
import { MessageTypes } from "../../src/common";
|
||||||
import { packers, unpackers } from '../../src/mapping'
|
import { packers, unpackers } from "../../src/mapping";
|
||||||
|
|
||||||
const KEY = new Uint8Array([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F])
|
const KEY = new Uint8Array([
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
|
||||||
|
0x0d, 0x0e, 0x0f,
|
||||||
|
]);
|
||||||
|
|
||||||
test('Create a user data response (0x0003) packet.', () => {
|
test("Create a user data response (0x0003) packet.", () => {
|
||||||
// Given
|
// Given
|
||||||
const username = 'Butlersaurus'
|
const username = "Butlersaurus";
|
||||||
const colour = Color('#FF4000')
|
const colour = Color("#FF4000");
|
||||||
const clientId = 'Mercury'
|
const clientId = "Mercury";
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const packedPacket = packers[MessageTypes.UserDataResponse](
|
const packedPacket = packers[MessageTypes.UserDataResponse](
|
||||||
{
|
{
|
||||||
username: username,
|
username: username,
|
||||||
colour: colour,
|
colour: colour,
|
||||||
clientId: clientId
|
clientId: clientId,
|
||||||
},
|
},
|
||||||
KEY
|
KEY,
|
||||||
)
|
);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
// We can't check the contents of the data as it's encrypted with a random nonce.
|
// We can't check the contents of the data as it's encrypted with a random nonce.
|
||||||
// Check the message type and length.
|
// Check the message type and length.
|
||||||
expect(packedPacket.slice(0, 4)).toMatchObject(new Uint8Array([0x00, 0x03, 0x00, 0x67]))
|
expect(packedPacket.slice(0, 4)).toMatchObject(
|
||||||
|
new Uint8Array([0x00, 0x03, 0x00, 0x67]),
|
||||||
|
);
|
||||||
|
|
||||||
// Check the total length is as expected.
|
// Check the total length is as expected.
|
||||||
expect(packedPacket.length).toBe(107)
|
expect(packedPacket.length).toBe(107);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Parse a user data response (0x0003).', () => {
|
test("Parse a user data response (0x0003).", () => {
|
||||||
// Given
|
// Given
|
||||||
const data = new Uint8Array([
|
const data = new Uint8Array([
|
||||||
0, 12,
|
0, 12, 66, 117, 116, 108, 101, 114, 115, 97, 117, 114, 117, 115, 0, 0, 0, 0,
|
||||||
66, 117, 116, 108, 101, 114, 115, 97, 117, 114, 117, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 64, 0, 0, 7, 77, 101,
|
||||||
255, 64, 0,
|
114, 99, 117, 114, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 7,
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
77, 101, 114, 99, 117, 114, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
]);
|
||||||
])
|
const username = "Butlersaurus";
|
||||||
const username = 'Butlersaurus'
|
const colour = Color("#FF4000");
|
||||||
const colour = Color('#FF4000')
|
const clientId = "Mercury";
|
||||||
const clientId = 'Mercury'
|
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const unpackedPacket = unpackers[MessageTypes.UserDataResponse](data)
|
const unpackedPacket = unpackers[MessageTypes.UserDataResponse](data);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(unpackedPacket.username).toBe(username)
|
expect(unpackedPacket.username).toBe(username);
|
||||||
expect(unpackedPacket.colour).toMatchObject(colour)
|
expect(unpackedPacket.colour).toMatchObject(colour);
|
||||||
expect(unpackedPacket.clientId).toBe(clientId)
|
expect(unpackedPacket.clientId).toBe(clientId);
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import { numberToUint16BE, numberToUint32BE } from '../../src/utilities/number'
|
import { numberToUint16BE, numberToUint32BE } from "../../src/utilities/number";
|
||||||
|
|
||||||
test('Test number conversion to Uint16 big endian buffer.', () => {
|
test("Test number conversion to Uint16 big endian buffer.", () => {
|
||||||
// When
|
// When
|
||||||
const result = numberToUint16BE(1234)
|
const result = numberToUint16BE(1234);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
const expectedResult = new Uint8Array([0x04, 0xd2])
|
const expectedResult = new Uint8Array([0x04, 0xd2]);
|
||||||
expect(result).toMatchObject(expectedResult)
|
expect(result).toMatchObject(expectedResult);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Test number conversion to Uint32 big endian buffer.', () => {
|
test("Test number conversion to Uint32 big endian buffer.", () => {
|
||||||
// When
|
// When
|
||||||
const result = numberToUint32BE(123456)
|
const result = numberToUint32BE(123456);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
const expectedResult = new Uint8Array([0x00, 0x01, 0xE2, 0x40])
|
const expectedResult = new Uint8Array([0x00, 0x01, 0xe2, 0x40]);
|
||||||
expect(result).toMatchObject(expectedResult)
|
expect(result).toMatchObject(expectedResult);
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,230 +1,240 @@
|
|||||||
import { SmartBuffer } from '../../src/utilities/smart-buffer'
|
import { SmartBuffer } from "../../src/utilities/smart-buffer";
|
||||||
|
|
||||||
test('Read a UInt16.', () => {
|
test("Read a UInt16.", () => {
|
||||||
// Given
|
// Given
|
||||||
const buffer = [0x30, 0x39]
|
const buffer = [0x30, 0x39];
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const smartBuffer = SmartBuffer.from(buffer)
|
const smartBuffer = SmartBuffer.from(buffer);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(smartBuffer.readUInt16()).toBe(12345)
|
expect(smartBuffer.readUInt16()).toBe(12345);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Read a UInt32.', () => {
|
test("Read a UInt32.", () => {
|
||||||
// Given
|
// Given
|
||||||
const buffer = [0x49, 0x96, 0x02, 0xD2]
|
const buffer = [0x49, 0x96, 0x02, 0xd2];
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const smartBuffer = SmartBuffer.from(buffer)
|
const smartBuffer = SmartBuffer.from(buffer);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(smartBuffer.readUInt32()).toBe(1234567890)
|
expect(smartBuffer.readUInt32()).toBe(1234567890);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Read a buffer.', () => {
|
test("Read a buffer.", () => {
|
||||||
// Given
|
// Given
|
||||||
const buffer = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
const buffer = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const smartBuffer = SmartBuffer.from(buffer)
|
const smartBuffer = SmartBuffer.from(buffer);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
const result = smartBuffer.readBytes(4)
|
const result = smartBuffer.readBytes(4);
|
||||||
expect(result).toMatchObject(new Uint8Array([0, 1, 2, 3]))
|
expect(result).toMatchObject(new Uint8Array([0, 1, 2, 3]));
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Read a UInt16 from an offset.', () => {
|
test("Read a UInt16 from an offset.", () => {
|
||||||
// Given
|
// Given
|
||||||
const buffer = [0x00, 0x00, 0x30, 0x39]
|
const buffer = [0x00, 0x00, 0x30, 0x39];
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const smartBuffer = SmartBuffer.from(buffer)
|
const smartBuffer = SmartBuffer.from(buffer);
|
||||||
smartBuffer.cursor = 2
|
smartBuffer.cursor = 2;
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(smartBuffer.readUInt16()).toBe(12345)
|
expect(smartBuffer.readUInt16()).toBe(12345);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Read a UInt32 from an offset.', () => {
|
test("Read a UInt32 from an offset.", () => {
|
||||||
// Given
|
// Given
|
||||||
const buffer = [0x00, 0x00, 0x49, 0x96, 0x02, 0xD2]
|
const buffer = [0x00, 0x00, 0x49, 0x96, 0x02, 0xd2];
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const smartBuffer = SmartBuffer.from(buffer)
|
const smartBuffer = SmartBuffer.from(buffer);
|
||||||
smartBuffer.cursor = 2
|
smartBuffer.cursor = 2;
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(smartBuffer.readUInt32()).toBe(1234567890)
|
expect(smartBuffer.readUInt32()).toBe(1234567890);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Read a buffer from an offset.', () => {
|
test("Read a buffer from an offset.", () => {
|
||||||
// Given
|
// Given
|
||||||
const buffer = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
const buffer = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const smartBuffer = SmartBuffer.from(buffer)
|
const smartBuffer = SmartBuffer.from(buffer);
|
||||||
smartBuffer.cursor = 2
|
smartBuffer.cursor = 2;
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(smartBuffer.readBytes(4)).toMatchObject(new Uint8Array([2, 3, 4, 5]))
|
expect(smartBuffer.readBytes(4)).toMatchObject(new Uint8Array([2, 3, 4, 5]));
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Write a UInt16.', () => {
|
test("Write a UInt16.", () => {
|
||||||
// Given
|
// Given
|
||||||
const smartBuffer = new SmartBuffer()
|
const smartBuffer = new SmartBuffer();
|
||||||
|
|
||||||
// When
|
// When
|
||||||
smartBuffer.writeUInt16(12345)
|
smartBuffer.writeUInt16(12345);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(smartBuffer.data).toMatchObject(new Uint8Array([0x30, 0x39]))
|
expect(smartBuffer.data).toMatchObject(new Uint8Array([0x30, 0x39]));
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Write a UInt32.', () => {
|
test("Write a UInt32.", () => {
|
||||||
// Given
|
// Given
|
||||||
const smartBuffer = new SmartBuffer()
|
const smartBuffer = new SmartBuffer();
|
||||||
|
|
||||||
// When
|
// When
|
||||||
smartBuffer.writeUInt32(1234567890)
|
smartBuffer.writeUInt32(1234567890);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(smartBuffer.data).toMatchObject(new Uint8Array([0x49, 0x96, 0x02, 0xD2]))
|
expect(smartBuffer.data).toMatchObject(
|
||||||
})
|
new Uint8Array([0x49, 0x96, 0x02, 0xd2]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('Write a buffer.', () => {
|
test("Write a buffer.", () => {
|
||||||
// Given
|
// Given
|
||||||
const smartBuffer = new SmartBuffer()
|
const smartBuffer = new SmartBuffer();
|
||||||
|
|
||||||
// When
|
// When
|
||||||
smartBuffer.writeBytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
|
smartBuffer.writeBytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(smartBuffer.data).toMatchObject(new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
|
expect(smartBuffer.data).toMatchObject(
|
||||||
})
|
new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('Write a UInt16 at an offset.', () => {
|
test("Write a UInt16 at an offset.", () => {
|
||||||
// Given
|
// Given
|
||||||
const smartBuffer = new SmartBuffer()
|
const smartBuffer = new SmartBuffer();
|
||||||
|
|
||||||
// When
|
// When
|
||||||
smartBuffer.cursor = 2
|
smartBuffer.cursor = 2;
|
||||||
smartBuffer.writeUInt16(12345)
|
smartBuffer.writeUInt16(12345);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(smartBuffer.data).toMatchObject(new Uint8Array([0x00, 0x00, 0x30, 0x39]))
|
expect(smartBuffer.data).toMatchObject(
|
||||||
})
|
new Uint8Array([0x00, 0x00, 0x30, 0x39]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('Write a UInt32 at an offset.', () => {
|
test("Write a UInt32 at an offset.", () => {
|
||||||
// Given
|
// Given
|
||||||
const smartBuffer = new SmartBuffer()
|
const smartBuffer = new SmartBuffer();
|
||||||
|
|
||||||
// When
|
// When
|
||||||
smartBuffer.cursor = 2
|
smartBuffer.cursor = 2;
|
||||||
smartBuffer.writeUInt32(1234567890)
|
smartBuffer.writeUInt32(1234567890);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(smartBuffer.data).toMatchObject(new Uint8Array([0x00, 0x00, 0x49, 0x96, 0x02, 0xD2]))
|
expect(smartBuffer.data).toMatchObject(
|
||||||
})
|
new Uint8Array([0x00, 0x00, 0x49, 0x96, 0x02, 0xd2]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('Write a buffer at an offset.', () => {
|
test("Write a buffer at an offset.", () => {
|
||||||
// Given
|
// Given
|
||||||
const smartBuffer = new SmartBuffer()
|
const smartBuffer = new SmartBuffer();
|
||||||
|
|
||||||
// When
|
// When
|
||||||
smartBuffer.cursor = 2
|
smartBuffer.cursor = 2;
|
||||||
smartBuffer.writeBytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
|
smartBuffer.writeBytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(smartBuffer.data).toMatchObject(new Uint8Array([0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
|
expect(smartBuffer.data).toMatchObject(
|
||||||
})
|
new Uint8Array([0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('Cursor is correctly incremented after reading a UInt16.', () => {
|
test("Cursor is correctly incremented after reading a UInt16.", () => {
|
||||||
// Given
|
// Given
|
||||||
const buffer = new Uint8Array(4)
|
const buffer = new Uint8Array(4);
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const smartBuffer = SmartBuffer.from(buffer)
|
const smartBuffer = SmartBuffer.from(buffer);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
smartBuffer.readUInt16()
|
smartBuffer.readUInt16();
|
||||||
expect(smartBuffer.cursor).toBe(2)
|
expect(smartBuffer.cursor).toBe(2);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Cursor is correctly incremented after reading a UInt32.', () => {
|
test("Cursor is correctly incremented after reading a UInt32.", () => {
|
||||||
// Given
|
// Given
|
||||||
const buffer = new Uint8Array(4)
|
const buffer = new Uint8Array(4);
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const smartBuffer = SmartBuffer.from(buffer)
|
const smartBuffer = SmartBuffer.from(buffer);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
smartBuffer.readUInt32()
|
smartBuffer.readUInt32();
|
||||||
expect(smartBuffer.cursor).toBe(4)
|
expect(smartBuffer.cursor).toBe(4);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Cursor is correctly incremented after reading a buffer.', () => {
|
test("Cursor is correctly incremented after reading a buffer.", () => {
|
||||||
// Given
|
// Given
|
||||||
const buffer = new Uint8Array(8)
|
const buffer = new Uint8Array(8);
|
||||||
|
|
||||||
// When
|
// When
|
||||||
const smartBuffer = SmartBuffer.from(buffer)
|
const smartBuffer = SmartBuffer.from(buffer);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
smartBuffer.readBytes(4)
|
smartBuffer.readBytes(4);
|
||||||
expect(smartBuffer.cursor).toBe(4)
|
expect(smartBuffer.cursor).toBe(4);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Cursor is correctly incremented after writing a UInt16.', () => {
|
test("Cursor is correctly incremented after writing a UInt16.", () => {
|
||||||
// Given
|
// Given
|
||||||
const smartBuffer = new SmartBuffer()
|
const smartBuffer = new SmartBuffer();
|
||||||
|
|
||||||
// When
|
// When
|
||||||
smartBuffer.writeUInt16(12345)
|
smartBuffer.writeUInt16(12345);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(smartBuffer.cursor).toBe(2)
|
expect(smartBuffer.cursor).toBe(2);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Cursor is correctly incremented after writing a UInt32.', () => {
|
test("Cursor is correctly incremented after writing a UInt32.", () => {
|
||||||
// Given
|
// Given
|
||||||
const smartBuffer = new SmartBuffer()
|
const smartBuffer = new SmartBuffer();
|
||||||
|
|
||||||
// When
|
// When
|
||||||
smartBuffer.writeUInt32(1234567890)
|
smartBuffer.writeUInt32(1234567890);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(smartBuffer.cursor).toBe(4)
|
expect(smartBuffer.cursor).toBe(4);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Cursor is correctly incremented after writing a buffer.', () => {
|
test("Cursor is correctly incremented after writing a buffer.", () => {
|
||||||
// Given
|
// Given
|
||||||
const smartBuffer = new SmartBuffer()
|
const smartBuffer = new SmartBuffer();
|
||||||
|
|
||||||
// When
|
// When
|
||||||
smartBuffer.writeBytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
|
smartBuffer.writeBytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(smartBuffer.cursor).toBe(10)
|
expect(smartBuffer.cursor).toBe(10);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Seek to position below 0 throws range error.', () => {
|
test("Seek to position below 0 throws range error.", () => {
|
||||||
// When
|
// When
|
||||||
const smartBuffer = new SmartBuffer()
|
const smartBuffer = new SmartBuffer();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(() => {
|
expect(() => {
|
||||||
smartBuffer.cursor = -1
|
smartBuffer.cursor = -1;
|
||||||
}).toThrow(RangeError)
|
}).toThrow(RangeError);
|
||||||
})
|
});
|
||||||
|
|
||||||
test('Pad some data.', () => {
|
test("Pad some data.", () => {
|
||||||
// Given
|
// Given
|
||||||
const smartBuffer = new SmartBuffer()
|
const smartBuffer = new SmartBuffer();
|
||||||
|
|
||||||
// When
|
// When
|
||||||
smartBuffer.pad(10)
|
smartBuffer.pad(10);
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
expect(smartBuffer.length).toBe(10)
|
expect(smartBuffer.length).toBe(10);
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"extends": "./tsconfig.json",
|
"extends": "./tsconfig.json",
|
||||||
"exclude": []
|
"exclude": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||||
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||||
"module": "commonjs", /* Specify what module code is generated. */
|
"module": "commonjs" /* Specify what module code is generated. */,
|
||||||
"rootDir": "src", /* Specify the root folder within your source files. */
|
"rootDir": "src" /* Specify the root folder within your source files. */,
|
||||||
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
"sourceMap": true /* Create source map files for emitted JavaScript files. */,
|
||||||
"outDir": "dist", /* Specify an output folder for all emitted files. */
|
"outDir": "dist" /* Specify an output folder for all emitted files. */,
|
||||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
|
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,
|
||||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||||
"strict": true, /* Enable all strict type-checking options. */
|
"strict": true /* Enable all strict type-checking options. */,
|
||||||
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
|
"skipLibCheck": true /* Skip type checking all .d.ts files. */,
|
||||||
"declaration": true
|
"declaration": true
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": ["tests", "dist"]
|
||||||
"tests",
|
|
||||||
"dist"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user