{"_id":"@belteshazzar/binjson","_rev":"4-72d019b7a287ceac9e85c177f7951f40","name":"@belteshazzar/binjson","dist-tags":{"latest":"1.1.0"},"versions":{"1.0.0":{"name":"@belteshazzar/binjson","version":"1.0.0","keywords":["binary","json","encoding","opfs","storage","mongodb","objectid","serialization"],"author":"","license":"BSD-2-Clause","_id":"@belteshazzar/binjson@1.0.0","maintainers":[{"name":"belteshazzar","email":"daniel@belteshazzar.com"}],"bin":{"binjson-decode":"bin/binjson-decode.js"},"dist":{"shasum":"9bbed47895f2a8df36d66dadd9f2e11f22dbaaec","tarball":"https://registry.npmjs.org/@belteshazzar/binjson/-/binjson-1.0.0.tgz","fileCount":14,"integrity":"sha512-RJ/+pGkyMmrpR3PoJK+th+N6WLqMVUvs6Vxa08OBJA9ElJ+PXuB6mcUVistBxNuvZoN6Fys7hyzaADIs2zFREQ==","signatures":[{"sig":"MEYCIQCbuY+QxmDscHTHbhyGzkUsg3vzTbmyXNT7CpBu2gv8ZQIhAN8pFFk2v9Fklemby95WlF+7wQAWSjdBxMRs43l86TNm","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":120762},"main":"src/binjson.js","type":"module","exports":{".":"./src/binjson.js","./rtree":"./src/rtree.js","./textlog":"./src/textlog.js","./bplustree":"./src/bplustree.js","./textindex":"./src/textindex.js"},"gitHead":"34a010510a1f15008e1a360ef39a9906110d79d3","scripts":{"test":"vitest run","serve":"http-server -c-1 . -p 8080","test:ui":"vitest --ui","test:watch":"vitest","build:worker":"rollup -c rollup.config.js","test:coverage":"vitest --coverage"},"_npmUser":{"name":"belteshazzar","email":"daniel@belteshazzar.com"},"_npmVersion":"11.7.0","description":"A compact binary encoding format for JSON data with support for Origin Private File System (OPFS) in browsers","directories":{},"_nodeVersion":"23.11.0","dependencies":{"diff":"^8.0.2","stemmer":"^2.0.1"},"_hasShrinkwrap":false,"devDependencies":{"rollup":"^4.31.0","vitest":"^3.2.4","node-opfs":"^1.0.0","@vitest/ui":"^3.2.4","playwright":"^1.49.1","typescript":"^5.9.3","http-server":"^14.1.1","@vitest/browser":"^3.2.4","@vitest/coverage-v8":"^3.2.4","@rollup/plugin-commonjs":"^28.0.2","@rollup/plugin-node-resolve":"^16.0.0"},"_npmOperationalInternal":{"tmp":"tmp/binjson_1.0.0_1769237402417_0.09661131772174869","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@belteshazzar/binjson","version":"1.0.1","keywords":["binary","json","encoding","opfs","storage","mongodb","objectid","serialization"],"author":"","license":"BSD-2-Clause","_id":"@belteshazzar/binjson@1.0.1","maintainers":[{"name":"belteshazzar","email":"daniel@belteshazzar.com"}],"bin":{"binjson-decode":"bin/binjson-decode.js"},"dist":{"shasum":"8a6a191212a20043588e05b6a0be46d7466e2973","tarball":"https://registry.npmjs.org/@belteshazzar/binjson/-/binjson-1.0.1.tgz","fileCount":14,"integrity":"sha512-mQ6e2d3f/aqIHamt9665BosSqH+eZHvdceViRT32cRttQXTi9G9U2i0+6CLtNhJD3zzxdLz5V3hRYaAkxkJznw==","signatures":[{"sig":"MEQCIFWOTrkujYWJG5J5uRXbJP0nOXpjgv2/uraaVNnvRHpPAiABpoVMhVKDR701ZvWRF98JRPvuwCLLje23VI9aIOBHVQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":120792},"main":"src/binjson.js","type":"module","exports":{".":"./src/binjson.js","./rtree":"./src/rtree.js","./textlog":"./src/textlog.js","./bplustree":"./src/bplustree.js","./textindex":"./src/textindex.js"},"gitHead":"824a6b10072712d23925d72823d5dd6667686e08","scripts":{"test":"vitest run","serve":"http-server -c-1 . -p 8080","test:ui":"vitest --ui","test:watch":"vitest","build:worker":"rollup -c rollup.config.js","test:coverage":"vitest --coverage"},"_npmUser":{"name":"belteshazzar","email":"daniel@belteshazzar.com"},"_npmVersion":"11.7.0","description":"A compact binary encoding format for JSON data with support for Origin Private File System (OPFS) in browsers","directories":{},"_nodeVersion":"23.11.0","dependencies":{"diff":"^8.0.2","stemmer":"^2.0.1"},"_hasShrinkwrap":false,"devDependencies":{"rollup":"^4.31.0","vitest":"^3.2.4","node-opfs":"^1.0.0","@vitest/ui":"^3.2.4","playwright":"^1.49.1","typescript":"^5.9.3","http-server":"^14.1.1","@vitest/browser":"^3.2.4","@vitest/coverage-v8":"^3.2.4","@rollup/plugin-commonjs":"^28.0.2","@rollup/plugin-node-resolve":"^16.0.0"},"_npmOperationalInternal":{"tmp":"tmp/binjson_1.0.1_1769238286158_0.6245251539648145","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@belteshazzar/binjson","version":"1.0.2","keywords":["binary","json","encoding","opfs","storage","mongodb","objectid","serialization"],"author":"","license":"BSD-2-Clause","_id":"@belteshazzar/binjson@1.0.2","maintainers":[{"name":"belteshazzar","email":"daniel@belteshazzar.com"}],"bin":{"rtree":"bin/rtree.js","binjson":"bin/binjson.js","textlog":"bin/textlog.js","bplustree":"bin/bplustree.js","textindex":"bin/textindex.js","binjson-decode":"bin/binjson.js"},"dist":{"shasum":"76bbbc5756f703c898020f14f1a0d686e4abb5bb","tarball":"https://registry.npmjs.org/@belteshazzar/binjson/-/binjson-1.0.2.tgz","fileCount":43,"integrity":"sha512-zDxF8Xj08SBQ61G6TLn1b/eNCOBocE0JEHC32xaPSPuFV48Hcu4Kd0nmuSDbD9H8VIdeYuKY//2DA+X+CGbaZQ==","signatures":[{"sig":"MEUCIEakv/8sXnt5bhteQUPtIZlg/PMtKj3s+nWHOwByiWujAiEA/VNnKYirJRpCOKcdxtZ5qGZB6cwbANLKL9GbTEcpb40=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":569327},"main":"src/binjson.js","type":"module","exports":{".":"./src/binjson.js","./wasm":"./src/binjson-wasm.js","./rtree":"./src/rtree.js","./textlog":"./src/textlog.js","./bplustree":"./src/bplustree.js","./textindex":"./src/textindex.js","./rtree-wasm":"./src/binjson-wasm.js","./bplustree-wasm":"./src/binjson-wasm.js"},"gitHead":"6a4b1e406311d9ed264d716deac84a25e65f2e58","scripts":{"test":"vitest run","serve":"http-server -c-1 . -p 8083","test:c":"cc -std=c11 -Wall -Wextra -Werror c/binjson.c c/test_binjson.c -o /tmp/bjtest && /tmp/bjtest","prepack":"npm run build:wasm","test:ui":"vitest --ui","test:wasm":"node c/test-wasm.mjs","build:wasm":"./c/build-wasm.sh","test:watch":"vitest","build:worker":"rollup -c rollup.config.js && cp lib/binjson.wasm site/binjson.wasm","test:coverage":"vitest --coverage"},"_npmUser":{"name":"belteshazzar","email":"daniel@belteshazzar.com"},"_npmVersion":"11.16.0","description":"A compact binary encoding format for JSON data with support for Origin Private File System (OPFS) in browsers","directories":{},"_nodeVersion":"26.3.0","dependencies":{"diff":"^8.0.2","stemmer":"^2.0.1"},"_hasShrinkwrap":false,"devDependencies":{"rollup":"^4.31.0","vitest":"^3.2.4","node-opfs":"^1.0.0","@vitest/ui":"^3.2.4","playwright":"^1.49.1","typescript":"^5.9.3","http-server":"^14.1.1","@vitest/browser":"^3.2.4","@vitest/coverage-v8":"^3.2.4","@rollup/plugin-commonjs":"^28.0.2","@rollup/plugin-node-resolve":"^16.0.0"},"_npmOperationalInternal":{"tmp":"tmp/binjson_1.0.2_1783591574177_0.9654907114208193","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@belteshazzar/binjson","version":"1.1.0","description":"A compact binary encoding format for JSON data with support for Origin Private File System (OPFS) in browsers","main":"src/binjson.js","type":"module","exports":{".":"./src/binjson.js","./wasm":"./src/binjson-wasm.js"},"bin":{"binjson":"bin/binjson.js","bplustree":"bin/bplustree.js","rtree":"bin/rtree.js","textindex":"bin/textindex.js","textlog":"bin/textlog.js"},"scripts":{"test":"vitest run","test:watch":"vitest","test:ui":"vitest --ui","test:coverage":"vitest --coverage","build:worker":"rollup -c rollup.config.js && cp lib/binjson.wasm site/binjson.wasm","build:wasm":"./c/build-wasm.sh","prepack":"npm run build:wasm","test:wasm":"node c/test-wasm.mjs","test:c":"cc -std=c11 -Wall -Wextra -Werror c/binjson.c c/test_binjson.c -o /tmp/bjtest && /tmp/bjtest","serve":"http-server -c-1 . -p 8083"},"keywords":["binary","json","encoding","opfs","storage","mongodb","objectid","serialization"],"author":"","license":"BSD-2-Clause","devDependencies":{"@rollup/plugin-commonjs":"^28.0.2","@rollup/plugin-node-resolve":"^16.0.0","@vitest/browser":"^3.2.4","@vitest/coverage-v8":"^3.2.4","@vitest/ui":"^3.2.4","http-server":"^14.1.1","node-opfs":"^1.0.0","playwright":"^1.49.1","rollup":"^4.31.0","typescript":"^5.9.3","vitest":"^3.2.4"},"dependencies":{"diff":"^8.0.2","stemmer":"^2.0.1"},"gitHead":"12c12d47f8345b82230ba4a3b7ea5d2baef7f318","_id":"@belteshazzar/binjson@1.1.0","_nodeVersion":"26.3.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-8ZpFdOtNCu9auqDoqSEb6grFT2PanDiipcnldU6GQFSIH7IhAWwsdE6RFGkKjNiq1KnQyb3Y04abKw2o+zE+DA==","shasum":"a6d355ca0d697235e038d30e252a2e1269a2a13a","tarball":"https://registry.npmjs.org/@belteshazzar/binjson/-/binjson-1.1.0.tgz","fileCount":48,"unpackedSize":709699,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEcHo2oM0RwKi/4qpD+ATflV/LawQumLpjEiEJO1PnxCAiAwa0ot2ME3qnThhKgpIacNkVZWPgi6NaQg0xab0tdL4w=="}]},"_npmUser":{"name":"belteshazzar","email":"daniel@belteshazzar.com"},"directories":{},"maintainers":[{"name":"belteshazzar","email":"daniel@belteshazzar.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/binjson_1.1.0_1783834019116_0.49907229177062074"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-24T06:50:02.343Z","modified":"2026-07-12T05:26:59.410Z","1.0.0":"2026-01-24T06:50:02.599Z","1.0.1":"2026-01-24T07:04:46.307Z","1.0.2":"2026-07-09T10:06:14.372Z","1.1.0":"2026-07-12T05:26:59.314Z"},"license":"BSD-2-Clause","keywords":["binary","json","encoding","opfs","storage","mongodb","objectid","serialization"],"description":"A compact binary encoding format for JSON data with support for Origin Private File System (OPFS) in browsers","maintainers":[{"name":"belteshazzar","email":"daniel@belteshazzar.com"}],"readme":"# Binary JSON (binjson)\n\nA compact binary encoding format for JSON data with support for Origin Private File System (OPFS) in browsers. This library enables efficient storage and retrieval of JSON data in a binary format.\n\n## Features\n\n- 🚀 **Compact Binary Format**: Efficient binary encoding of JSON data\n- 💾 **OPFS Support**: Read and write binary JSON files using Origin Private File System\n- 🔄 **Full Round-trip**: Encoding and decoding of all JSON types\n- 📦 **MongoDB ObjectId Support**: Native support for MongoDB ObjectIds (24-character hex strings)\n- 🎯 **File Pointer Support**: Built-in Pointer type for storing 64-bit file offsets for indexed data access\n- 🔍 **File Scanning**: Ability to scan through files and read records sequentially\n- ➕ **Append Operations**: Append new records to existing files\n- 🌐 **Browser & Node.js**: Works in both browser and Node.js environments\n\n## Type Encoding\n\nThe library uses the following byte values for encoding JSON types:\n\n| Type    | Byte Value | Data Format                                          |\n|---------|-----------|------------------------------------------------------|\n| NULL    | 0x00      | No additional data                                   |\n| FALSE   | 0x01      | No additional data                                   |\n| TRUE    | 0x02      | No additional data                                   |\n| INT     | 0x03      | 4 bytes (32-bit signed integer, little-endian)      |\n| FLOAT   | 0x04      | 8 bytes (64-bit float, little-endian)               |\n| STRING  | 0x05      | 4-byte length + UTF-8 encoded bytes                 |\n| OID     | 0x06      | 12 bytes (MongoDB ObjectId)                         |\n| DATE    | 0x07      | 8 bytes (64-bit signed integer milliseconds, little-endian) |\n| POINTER | 0x08      | 8 bytes (64-bit non-negative integer file offset, little-endian) |\n| BINARY  | 0x09      | 4-byte length + raw binary bytes                    |\n| ARRAY   | 0x10      | 4-byte length + encoded elements                    |\n| OBJECT  | 0x11      | 4-byte count + key-value pairs                      |\n\n## Installation\n\n### Node.js\n\n```bash\nnpm install @belteshazzar/binjson\n```\n\n### Browser\n\nInclude the script in your HTML:\n\n```html\n<script src=\"binjson.js\"></script>\n```\n\n## Usage\n\n### Basic Encoding and Decoding\n\n```javascript\nconst { encode, decode } = require('./binjson.js');\n\n// Encode data to binary\nconst data = { name: 'John', age: 30, active: true };\nconst binary = encode(data);\n\n// Decode binary back to data\nconst decoded = decode(binary);\nconsole.log(decoded); // { name: 'John', age: 30, active: true }\n```\n\n### Using MongoDB ObjectId\n\n```javascript\nconst { ObjectId, encode, decode } = require('./binjson.js');\n\nconst data = {\n  _id: new ObjectId('507f1f77bcf86cd799439011'),\n  name: 'Document'\n};\n\nconst binary = encode(data);\nconst decoded = decode(binary);\n\nconsole.log(decoded._id.toString()); // '507f1f77bcf86cd799439011'\n```\n\n### Using Date Objects\n\n```javascript\nconst { encode, decode } = require('./binjson.js');\n\nconst data = {\n  timestamp: new Date('2023-01-15T12:30:45Z'),\n  message: 'Hello'\n};\n\nconst binary = encode(data);\nconst decoded = decode(binary);\n\nconsole.log(decoded.timestamp); // Date object: 2023-01-15T12:30:45.000Z\nconsole.log(decoded.message); // 'Hello'\n```\n\n### Using Binary Data (Uint8Array)\n\n```javascript\nconst { encode, decode } = require('./binjson.js');\n\n// Encode binary data\nconst binaryData = new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF]);\nconst encoded = encode(binaryData);\nconst decoded = decode(encoded);\n\nconsole.log(decoded); // Uint8Array [0xDE, 0xAD, 0xBE, 0xEF]\n\n// Binary data in objects\nconst data = {\n  name: 'image.png',\n  content: new Uint8Array([0x89, 0x50, 0x4E, 0x47]), // PNG header\n  size: 4\n};\n\nconst binaryEncoded = encode(data);\nconst binaryDecoded = decode(binaryEncoded);\n\nconsole.log(binaryDecoded.content); // Uint8Array [0x89, 0x50, 0x4E, 0x47]\n```\n\n### Using Pointer for File Offsets\n\n```javascript\nconst { Pointer, encode, decode } = require('./binjson.js');\n\n// Create an index record with a pointer to data at offset 2048\nconst indexRecord = {\n  key: 'user_123',\n  dataOffset: new Pointer(2048)\n};\n\nconst binary = encode(indexRecord);\nconst decoded = decode(binary);\n\nconsole.log(decoded.dataOffset.valueOf()); // 2048\n// Use the pointer to seek to that position in a file and read data\n```\n\n### OPFS File Operations (Web Worker Only)\n\n**Important**: \n- `BinJsonFile` uses `FileSystemSyncAccessHandle` which is **only available in Web Workers**\n- Files must be explicitly opened before use and closed when done\n- A single file handle is used for all read/write operations, improving performance\n- Changes are automatically flushed to disk after each write/append operation\n- Files can be opened in two modes:\n  - `'r'` - Read-only mode (file must exist)\n  - `'rw'` - Read-write mode (file created if it doesn't exist)\n\nSee [FILE-MODE-API.md](FILE-MODE-API.md) for detailed documentation.\n\n#### Write to File\n\n```javascript\n// In a Web Worker:\nconst { BinJsonFile } = self.BJson;\n\nconst file = new BinJsonFile('data.bj');\nawait file.open('rw');  // Open in read-write mode\n\nconst data = { name: 'John', age: 30 };\nawait file.write(data);  // Automatically flushed to disk\n\nawait file.close();\n```\n\n#### Read from File\n\n```javascript\n// In a Web Worker:\nconst file = new BinJsonFile('data.bj');\nawait file.open('r');  // Open in read-only mode\n\nconst data = await file.read();\nconsole.log(data); // { name: 'John', age: 30 }\n\nawait file.close();\n```\n\n#### Append to File\n\n```javascript\n// In a Web Worker:\nconst file = new BinJsonFile('data.bj');\nawait file.open('rw');  // Open in read-write mode\n\n// Write initial record\nawait file.write({ id: 1, name: 'Record 1' });\n\n// Append more records (same file handle - efficient!)\nawait file.append({ id: 2, name: 'Record 2' });\nawait file.append({ id: 3, name: 'Record 3' });\n\nawait file.close();\n```\n\n#### Scan File (Read All Records)\n\n```javascript\n// In a Web Worker:\nconst file = new BinJsonFile('data.bj');\nawait file.open('r');  // Open in read-only mode\n\nfor (const record of file.scan()) {\n  console.log(record);\n}\n// Output:\n// { id: 1, name: 'Record 1' }\n// { id: 2, name: 'Record 2' }\n// { id: 3, name: 'Record 3' }\n\nawait file.close();\n```\n\n#### Other File Operations\n\n```javascript\n// In a Web Worker:\nconst file = new BinJsonFile('data.bj');\n\n// Check if file exists (doesn't require opening)\nconst exists = await file.exists();\nconsole.log(exists); // true or false\n\n// Delete file (requires read-write mode)\nawait file.open('rw');\nawait file.delete();  // Automatically closes after deletion\n\n// Explicit flush (write/append already auto-flush)\nawait file.open('rw');\nawait file.write({ data: 'example' });\n// ... more operations ...\nawait file.flush();  // Ensure all writes are persisted\nawait file.close();\n```\n\n#### Reusing File Handles\n\nA single `FileSystemSyncAccessHandle` is used for all operations once opened, making multiple operations very efficient:\n\n```javascript\n// In a Web Worker:\nconst file = new BinJsonFile('data.bj');\nawait file.open('rw');  // Opens once with a single sync access handle\n\n// Multiple operations on the same handle - no need to reopen!\nawait file.write({ id: 1 });\nawait file.append({ id: 2 });\nawait file.append({ id: 3 });\n\n// Read back from same handle\nconst data = await file.read();\n\n// Scan without re-opening\nfor (const record of file.scan()) {\n  console.log(record);\n}\n\nawait file.close();  // Flushes and closes the single handle\n```\n\n## Browser Support\n\nThe OPFS functionality with `FileSystemSyncAccessHandle` requires:\n\n- **Web Worker context** (not available on main thread)\n- Chrome 102+\n- Edge 102+\n- Opera 88+\n- Other Chromium-based browsers\n\nFor other browsers or main thread usage, the encoding and decoding functions work normally, but file operations are not available.\n\n## Demo\n\nOpen `demo.html` in a supported browser to see an interactive demonstration of all features.\n\n## API Reference\n\n### `encode(value)`\n\nEncodes a JavaScript value to binary format.\n\n- **Parameters**: `value` - Any JSON-serializable value (including ObjectId)\n- **Returns**: `Uint8Array` - Binary encoded data\n\n### `decode(data)`\n\nDecodes binary data to a JavaScript value.\n\n- **Parameters**: `data` - `Uint8Array` containing binary encoded data\n- **Returns**: Decoded JavaScript value\n\n### `ObjectId`\n\nClass representing a MongoDB ObjectId.\n\n#### Constructor\n\n```javascript\nnew ObjectId(value)\n```\n\n- **Parameters**: \n  - `value` - Either a 24-character hex string or 12-byte Uint8Array\n\n#### Methods\n\n- `toString()` - Returns the ObjectId as a 24-character hex string\n- `toBytes()` - Returns the ObjectId as a 12-byte Uint8Array\n- `static isValid(value)` - Checks if a string is a valid ObjectId format\n\n### `Pointer`\n\nClass representing a file offset pointer.\n\n#### Constructor\n\n```javascript\nnew Pointer(offset)\n```\n\n- **Parameters**: \n  - `offset` - A non-negative integer representing a file byte offset (must be within Number.MAX_SAFE_INTEGER)\n\n#### Methods\n\n- `valueOf()` - Returns the offset as a number\n- `toString()` - Returns the offset as a string\n- `toJSON()` - Returns the offset as a number for JSON serialization\n- `equals(other)` - Compares this Pointer with another for equality\n\n### `BinJsonFile`\n\nClass for OPFS file operations using `FileSystemSyncAccessHandle`.\n\n**Important**: \n- Must be used in a **Web Worker context** only\n- Uses a single persistent file handle for all operations\n- Files must be opened before use and closed when done\n- All write/append operations automatically flush to disk\n\nSee [FILE-MODE-API.md](FILE-MODE-API.md) for detailed documentation.\n\n#### Constructor\n\n```javascript\nnew BinJsonFile(filename)\n```\n\n- **Parameters**: `filename` - Name of the file in OPFS\n\n#### Methods\n\n- `async open(mode)` - Open file in specified mode and create sync access handle\n  - `mode` - `'r'` for read-only (file must exist) or `'rw'` for read-write (creates if needed)\n  - Throws error if file already open, if file not found in read mode, or if not in a Web Worker\n  - Creates a single `FileSystemSyncAccessHandle` for all subsequent operations\n- `async close()` - Flush pending writes, close the sync access handle, and release resources\n- `async write(data)` - Write data to file (overwrites existing, requires 'rw' mode, auto-flushes)\n- `async read(pointer)` - Read and decode data from file (requires file to be open)\n  - `pointer` - Optional Pointer offset to start reading from (default: 0)\n- `async append(data)` - Append data to existing file (requires 'rw' mode, auto-flushes)\n- `async flush()` - Explicitly flush any pending writes to disk (write/append already auto-flush)\n- `async *scan()` - Async generator to scan through all records (requires file to be open)\n- `async delete()` - Delete the file (requires 'rw' mode, auto-closes after)\n- `async exists()` - Check if file exists (can be called without opening)\n\n#### Access Control\n\n- **Read-only mode (`'r'`)**:\n  - Can use: `read()`, `scan()`, `getFileSize()`, `readRange()`\n  - Cannot use: `write()`, `append()`, `delete()` (will throw errors)\n  \n- **Read-write mode (`'rw'`)**:\n  - Can use all methods\n  - File is created if it doesn't exist\n\n## Examples\n\n### Complex Data Structure\n\n```javascript\nconst { ObjectId, encode, decode } = require('./binjson.js');\n\nconst document = {\n  _id: new ObjectId('507f1f77bcf86cd799439011'),\n  name: 'Product',\n  price: 99.99,\n  inStock: true,\n  tags: ['electronics', 'featured'],\n  metadata: {\n    created: 1234567890,\n    updated: null,\n    stats: {\n      views: 1500,\n      purchases: 42\n    }\n  },\n  reviews: [\n    { rating: 5, comment: 'Great product!' },\n    { rating: 4, comment: 'Good value' }\n  ]\n};\n\nconst binary = encode(document);\nconsole.log(`Encoded size: ${binary.length} bytes`);\n\nconst decoded = decode(binary);\nconsole.log(decoded);\n```\n\n### Batch File Operations\n\n```javascript\nconst file = new BinJsonFile('products.bj');\nawait file.open('rw');  // Open once for multiple operations\n\n// Write multiple products\nconst products = [\n  { id: 1, name: 'Product 1', price: 10.00 },\n  { id: 2, name: 'Product 2', price: 20.00 },\n  { id: 3, name: 'Product 3', price: 30.00 }\n];\n\n// Write first product\nawait file.write(products[0]);\n\n// Append remaining products (same file handle)\nfor (let i = 1; i < products.length; i++) {\n  await file.append(products[i]);\n}\n\n// Read all products (same file handle)\nconst allProducts = [];\nfor (const product of file.scan()) {\n  allProducts.push(product);\n}\n\nconsole.log(allProducts);\n\nawait file.close();\n```\n\n### Using Pointers for File Seeking\n\n```javascript\nconst { Pointer, encode, decode } = require('./binjson.js');\nconst fs = require('fs');\n\n// Scenario: Build an index of records with pointers to actual data locations\n\n// Step 1: Write data records and track their offsets\nconst records = [\n  { id: 1, name: 'Alice', email: 'alice@example.com' },\n  { id: 2, name: 'Bob', email: 'bob@example.com' },\n  { id: 3, name: 'Charlie', email: 'charlie@example.com' }\n];\n\nconst dataFile = 'data.bj';\nconst indexFile = 'index.bj';\nconst index = [];\n\n// Write records and build index\nlet currentOffset = 0;\nconst dataBuffer = [];\n\nfor (const record of records) {\n  const encoded = encode(record);\n  dataBuffer.push(encoded);\n  \n  // Store index entry with pointer to data location\n  index.push({\n    id: record.id,\n    name: record.name,\n    dataPointer: new Pointer(currentOffset)\n  });\n  \n  currentOffset += encoded.length;\n}\n\n// Write data file\nconst allData = new Uint8Array(currentOffset);\nlet writeOffset = 0;\nfor (const buf of dataBuffer) {\n  allData.set(buf, writeOffset);\n  writeOffset += buf.length;\n}\nfs.writeFileSync(dataFile, allData);\n\n// Write index file\nconst indexEncoded = encode(index);\nfs.writeFileSync(indexFile, indexEncoded);\n\n// Step 2: Use the index to seek and read specific records\nconst indexData = decode(fs.readFileSync(indexFile));\nconst fullDataFile = fs.readFileSync(dataFile);\n\n// Find and read record with id: 2\nconst indexEntry = indexData.find(entry => entry.id === 2);\nconst offset = indexEntry.dataPointer.valueOf();\n\n// Seek to offset and decode the record\n// Note: In a real implementation, you'd need to determine the record size\n// For this example, we'll read a known-size chunk\nconst recordData = fullDataFile.slice(offset); // In practice, you'd know the size\nconst record = decode(recordData);\n\nconsole.log(record); // { id: 2, name: 'Bob', email: 'bob@example.com' }\n```\n\n## Browser Demos\n\nThe `public/` directory contains interactive HTML demos that showcase OPFS integration. These demos use Web Workers with `FileSystemSyncAccessHandle` for efficient file operations.\n\n### Building\n\nThe demos require a build step to bundle the worker with its dependencies (including the `stemmer` npm package):\n\n```bash\nnpm run build:worker\n```\n\nThis creates `site/worker.js` which bundles all necessary dependencies.\n\n### Running Demos\n\n1. Build the worker: `npm run build:worker`\n2. Start the dev server: `npm run serve`\n3. Open http://localhost:8080/public/ in your browser (Chrome 102+ or Edge 102+ required for OPFS support)\n\nAvailable demos:\n- **binjson.html**: Basic encoding, file operations (write, read, append, scan, delete)\n- **bplustree-compact.html**: B+ tree index with compaction\n- **rtree-compact.html**: R-tree spatial index with geographic queries\n- **textindex-compact.html**: Full-text search index with BM25 ranking\n- **diff.html**: Live 3-column diff of two text versions, computed by the WASM port of `diff` (no worker build required)\n\n## WebAssembly Codec\n\nA drop-in WebAssembly build of the codec is available at `@belteshazzar/binjson/wasm`.\nThe byte-level encode/decode (and record sizing used by `BinJsonFile.scan()`) run\nin C compiled to WASM; the value types (`ObjectId`, `Pointer`) and the OPFS layer\nare unchanged. The **wire format is identical**, so the WASM and pure-JS codecs\ninteroperate freely — data written by one reads back with the other.\n\nThe only API difference: the WASM module loads asynchronously, so you must\n`await ready()` once before the (synchronous) `encode`/`decode`:\n\n```javascript\nimport { ready, encode, decode } from '@belteshazzar/binjson/wasm';\n\nawait ready(); // instantiate the WASM module once (e.g. at worker startup)\n\nconst binary = encode({ name: 'John', age: 30 });\nconst decoded = decode(binary); // { name: 'John', age: 30 }\n```\n\n`ObjectId`, `Pointer`, `TYPE`, `BinJsonFile`, `exists`, `deleteFile`, and\n`getFileHandle` are all exported with the same signatures as the main module.\n`isReady()` reports whether initialization has completed.\n\n### Building the WASM codec\n\nThe C sources live in `c/`. The prebuilt artifacts ship in `lib/` (rebuilt\nautomatically on `prepack`), so no toolchain is needed to *use* the package. To\nrebuild them you need the [Emscripten SDK](https://emscripten.org/) (`emcc`) on\nyour `PATH`:\n\n```bash\nnpm run build:wasm   # emits lib/binjson.wasm.mjs + lib/binjson.wasm\n```\n\nThe wire format is specified in [FORMAT.md](FORMAT.md). The C codec has its own\nnative conformance test (`npm run test:c`) and an end-to-end parity/fuzz suite\nagainst the reference (`npm run test:wasm`, plus `test/binjson-wasm*.test.js`).\n\n## Testing\n\nRun the test suite (covers both the JS and WASM codecs):\n\n```bash\nnpm test\n```\n\n## License\n\nBSD 2-Clause License\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.","readmeFilename":"README.md"}