{"_id":"@aurabx/jmix-js","_rev":"4-3ceeb20205c5e6005d0cfe9b0cd0fce7","name":"@aurabx/jmix-js","dist-tags":{"latest":"0.2.2"},"versions":{"0.1.0":{"name":"@aurabx/jmix-js","version":"0.1.0","keywords":["jmix"],"author":{"name":"aurabox"},"license":"MIT","_id":"@aurabx/jmix-js@0.1.0","maintainers":[{"name":"xtfer","email":"chris.skene@gmail.com"}],"homepage":"https://github.com/aurabx/jmix-js#readme","bugs":{"url":"https://github.com/aurabx/jmix-js/issues"},"dist":{"shasum":"29e4e73507762f16a1010047f2863e625ca2e245","tarball":"https://registry.npmjs.org/@aurabx/jmix-js/-/jmix-js-0.1.0.tgz","fileCount":73,"integrity":"sha512-ps0LK5zDe3+xYhaDNKMLq5YeU+aSR9XbGKaWbX+VvZx9vMBD6DwV1FvjhPS9fI2rI0AdfOo9sWbVQCbljgG/KQ==","signatures":[{"sig":"MEUCIAaEgzubTfEnyjdpb8yUdQ+8Eo46m0OE00O/qKeZ0TL6AiEAlBXaoMEr7K2lM4IhpQ3ni2IGu1rrlANtQ0IZjBOQBTw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":217828},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"9c5402be6ab9269d68aaa8b0e7eebd2063b3e5bb","scripts":{"lint":"echo 'Linting skipped - TypeScript checking handled by tsc'","test":"jest","build":"tsc","clean":"rimraf dist ./tmp","format":"prettier --check .","keys:gen":"node -e \"const n=require('tweetnacl');const kp=n.box.keyPair();console.log(Buffer.from(kp.publicKey).toString('base64'))\"","lint:fix":"echo 'Lint fix skipped - use prettier for formatting'","typecheck":"tsc --noEmit","format:fix":"prettier --write .","test:watch":"jest --watch","demo:decrypt":"node dist/demo-decrypt.js ./samples/study_1 ./tmp","demo:package":"node dist/demo-package.js ./samples/study_1 ./tmp","test:coverage":"jest --coverage","demo:verify:hash":"node dist/demo-verify-hash.js","demo:decrypt:existing":"node dist/demo-decrypt-existing.js","demo:package:encrypted":"node dist/demo-package-encrypted.js"},"_npmUser":{"name":"xtfer","email":"chris.skene@gmail.com"},"repository":{"url":"git+https://github.com/aurabx/jmix-js.git","type":"git"},"_npmVersion":"10.9.3","description":"TypeScript implementation of JMIX (JSON Medical Interchange) format for secure medical data exchange","directories":{"test":"tests"},"_nodeVersion":"20.16.0","dependencies":{"ajv":"^8.12.0","tar":"^6.2.1","daikon":"^1.2.0","tweetnacl":"^1.0.3","tar-stream":"^3.1.7","ajv-formats":"^2.1.1"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.0.0","eslint":"^8.0.0","rimraf":"^5.0.0","ts-jest":"^29.0.0","ts-node":"^10.9.2","prettier":"^3.0.0","@types/tar":"^6.1.5","typescript":"^5.5.3","@types/jest":"^29.5.0","@types/node":"^20.0.0","@types/tar-stream":"^2.2.2","@typescript-eslint/parser":"^6.0.0","@typescript-eslint/eslint-plugin":"^6.0.0"},"_npmOperationalInternal":{"tmp":"tmp/jmix-js_0.1.0_1759561751427_0.3445687795249084","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@aurabx/jmix-js","version":"0.2.0","keywords":["jmix"],"author":{"name":"aurabox"},"license":"MIT","_id":"@aurabx/jmix-js@0.2.0","maintainers":[{"name":"xtfer","email":"chris.skene@gmail.com"}],"homepage":"https://github.com/aurabx/jmix-js#readme","bugs":{"url":"https://github.com/aurabx/jmix-js/issues"},"dist":{"shasum":"ceac15f1cbaf6950d99ecbafcde26b9bf3b549db","tarball":"https://registry.npmjs.org/@aurabx/jmix-js/-/jmix-js-0.2.0.tgz","fileCount":74,"integrity":"sha512-hCS67nUKD62cCGQILopQCKlGK4Ytab8Q07P9IMIV2EP2ktUse8hhN5zJfBuEKMKIVXrFwnEjwWbM/IV4t1RWBw==","signatures":[{"sig":"MEUCIQCPYPRPLp9+8X1Ksd62ZabrdrjlxoW8gil0jjp1f4cueQIgMBVj09gNZOXGUKFJlpG4RkaP8TpOOMn7FDuhxg6GLas=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":225366},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"7bc4bad54e2fe1a2e61c1bee1f336a5f1c567810","scripts":{"lint":"echo 'Linting skipped - TypeScript checking handled by tsc'","test":"jest","build":"tsc","clean":"rimraf dist ./tmp","format":"prettier --check .","keys:gen":"node -e \"const n=require('tweetnacl');const kp=n.box.keyPair();console.log(Buffer.from(kp.publicKey).toString('base64'))\"","lint:fix":"echo 'Lint fix skipped - use prettier for formatting'","typecheck":"tsc --noEmit","format:fix":"prettier --write .","test:watch":"jest --watch","demo:decrypt":"node dist/demo-decrypt.js ./samples/study_1 ./tmp","demo:package":"node dist/demo-package.js ./samples/study_1 ./tmp","test:coverage":"jest --coverage","demo:verify:hash":"node dist/demo-verify-hash.js","demo:decrypt:existing":"node dist/demo-decrypt-existing.js","demo:package:encrypted":"node dist/demo-package-encrypted.js"},"_npmUser":{"name":"xtfer","email":"chris.skene@gmail.com"},"repository":{"url":"git+https://github.com/aurabx/jmix-js.git","type":"git"},"_npmVersion":"10.9.3","description":"TypeScript implementation of JMIX (JSON Medical Interchange) format for secure medical data exchange","directories":{"test":"tests"},"_nodeVersion":"20.16.0","dependencies":{"ajv":"^8.12.0","tar":"^6.2.1","daikon":"^1.2.0","tweetnacl":"^1.0.3","tar-stream":"^3.1.7","ajv-formats":"^2.1.1"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.0.0","eslint":"^8.0.0","rimraf":"^5.0.0","ts-jest":"^29.0.0","ts-node":"^10.9.2","prettier":"^3.0.0","@types/tar":"^6.1.5","typescript":"^5.5.3","@types/jest":"^29.5.0","@types/node":"^20.0.0","@types/tar-stream":"^2.2.2","@typescript-eslint/parser":"^6.0.0","@typescript-eslint/eslint-plugin":"^6.0.0"},"_npmOperationalInternal":{"tmp":"tmp/jmix-js_0.2.0_1761264499232_0.22086770776385678","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@aurabx/jmix-js","version":"0.2.1","keywords":["jmix"],"author":{"name":"aurabox"},"license":"MIT","_id":"@aurabx/jmix-js@0.2.1","maintainers":[{"name":"xtfer","email":"chris.skene@gmail.com"}],"homepage":"https://github.com/aurabx/jmix-js#readme","bugs":{"url":"https://github.com/aurabx/jmix-js/issues"},"dist":{"shasum":"3fdf198607579c9bfefeb3c16870d5508e4b3a3c","tarball":"https://registry.npmjs.org/@aurabx/jmix-js/-/jmix-js-0.2.1.tgz","fileCount":74,"integrity":"sha512-eXx41WhY3s7/9r/Fmgkq1MsqRYLUzFPs2fMaI2mtSi5bI0MxlugV2Fwvo8OSB0LK83PwwFIc11Op1ITP8+k0Cg==","signatures":[{"sig":"MEQCIEGKQWbFJtfIvu6blP4iU+CHE+dzTRiIikbLIhmXgFBpAiBqe8GXBZjHuPIrQ6fgno654EbvgttEBEDIaxLhi6PPXQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":225366},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"c31ed396ca6056a86a04e748818b8d445d0484a3","scripts":{"lint":"echo 'Linting skipped - TypeScript checking handled by tsc'","test":"jest","build":"tsc","clean":"rimraf dist ./tmp","format":"prettier --check .","keys:gen":"node -e \"const n=require('tweetnacl');const kp=n.box.keyPair();console.log(Buffer.from(kp.publicKey).toString('base64'))\"","lint:fix":"echo 'Lint fix skipped - use prettier for formatting'","typecheck":"tsc --noEmit","format:fix":"prettier --write .","test:watch":"jest --watch","demo:decrypt":"node dist/demo-decrypt.js ./samples/study_1 ./tmp","demo:package":"node dist/demo-package.js ./samples/study_1 ./tmp","test:coverage":"jest --coverage","demo:verify:hash":"node dist/demo-verify-hash.js","demo:decrypt:existing":"node dist/demo-decrypt-existing.js","demo:package:encrypted":"node dist/demo-package-encrypted.js"},"_npmUser":{"name":"xtfer","email":"chris.skene@gmail.com"},"repository":{"url":"git+https://github.com/aurabx/jmix-js.git","type":"git"},"_npmVersion":"10.9.3","description":"TypeScript implementation of JMIX (JSON Medical Interchange) format for secure medical data exchange","directories":{"test":"tests"},"_nodeVersion":"20.16.0","dependencies":{"ajv":"^8.12.0","tar":"^6.2.1","daikon":"^1.2.0","tweetnacl":"^1.0.3","tar-stream":"^3.1.7","ajv-formats":"^2.1.1"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.0.0","eslint":"^8.0.0","rimraf":"^5.0.0","ts-jest":"^29.0.0","ts-node":"^10.9.2","prettier":"^3.0.0","@types/tar":"^6.1.5","typescript":"^5.5.3","@types/jest":"^29.5.0","@types/node":"^20.0.0","@types/tar-stream":"^2.2.2","@typescript-eslint/parser":"^6.0.0","@typescript-eslint/eslint-plugin":"^6.0.0"},"_npmOperationalInternal":{"tmp":"tmp/jmix-js_0.2.1_1761265036233_0.20433991831493903","host":"s3://npm-registry-packages-npm-production"}},"0.2.2":{"_id":"@aurabx/jmix-js@0.2.2","bugs":{"url":"https://github.com/aurabx/jmix-js/issues"},"dist":{"shasum":"9117bc7dc2c5e568256bedd6c31b8981431fc7d7","tarball":"https://registry.npmjs.org/@aurabx/jmix-js/-/jmix-js-0.2.2.tgz","fileCount":74,"integrity":"sha512-RLREXap+1FCFyYePgUmElbkQyR/AODXnGaaNpu93o1NkZ6PhzUvi7KQiJApY8bGqpwJWGaPfghNibGdjJMhBZQ==","signatures":[{"sig":"MEYCIQDxr04S2nxKbbWcnE34Cwm64AVQZtSExWjuYju7esaNIAIhAJf/Y3aG5e1hiE66ud9b6dGQZ3Z6nLeeDfY0g4ny3tgr","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCiMRLKT/ae5TTzHilCxp98Ots2uFk/J0cgaQZK/94YpgIgebZ9HM0oONWpuGCXjIOwpWGF8ji4EsLwgZ5+eyU+skI="}],"unpackedSize":225998},"main":"dist/index.js","name":"@aurabx/jmix-js","type":"module","types":"dist/index.d.ts","author":{"name":"aurabox"},"engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"gitHead":"8e337a23837b84bc62c0d3b1c91f4f3bf4f41840","license":"MIT","scripts":{"lint":"echo 'Linting skipped - TypeScript checking handled by tsc'","test":"jest","build":"tsc","clean":"rimraf dist ./tmp","format":"prettier --check .","keys:gen":"node -e \"const n=require('tweetnacl');const kp=n.box.keyPair();console.log(Buffer.from(kp.publicKey).toString('base64'))\"","lint:fix":"echo 'Lint fix skipped - use prettier for formatting'","typecheck":"tsc --noEmit","format:fix":"prettier --write .","test:watch":"jest --watch","demo:decrypt":"node dist/demo-decrypt.js ./samples/study_1 ./tmp","demo:package":"node dist/demo-package.js ./samples/study_1 ./tmp","test:coverage":"jest --coverage","prepublishOnly":"npm run build","demo:verify:hash":"node dist/demo-verify-hash.js","demo:decrypt:existing":"node dist/demo-decrypt-existing.js","demo:package:encrypted":"node dist/demo-package-encrypted.js"},"version":"0.2.2","_npmUser":{"name":"xtfer","email":"chris.skene@gmail.com"},"homepage":"https://github.com/aurabx/jmix-js#readme","keywords":["jmix"],"repository":{"url":"git+https://github.com/aurabx/jmix-js.git","type":"git"},"_npmVersion":"11.12.1","description":"TypeScript implementation of JMIX (JSON Medical Interchange) format for secure medical data exchange","directories":{"test":"tests"},"maintainers":[{"name":"xtfer","email":"chris.skene@gmail.com"}],"_nodeVersion":"25.9.0","dependencies":{"ajv":"^8.12.0","tar":"^6.2.1","daikon":"^1.2.0","tweetnacl":"^1.0.3","tar-stream":"^3.1.7","ajv-formats":"^2.1.1"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.0.0","eslint":"^8.0.0","rimraf":"^5.0.0","ts-jest":"^29.0.0","ts-node":"^10.9.2","prettier":"^3.0.0","@types/tar":"^6.1.5","typescript":"^5.5.3","@types/jest":"^29.5.0","@types/node":"^20.0.0","@types/tar-stream":"^2.2.2","@typescript-eslint/parser":"^6.0.0","@typescript-eslint/eslint-plugin":"^6.0.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/jmix-js_0.2.2_1789648221980_0.6293718098302141"}}},"time":{"created":"2025-10-04T07:09:11.340Z","modified":"2026-09-17T12:30:22.259Z","0.1.0":"2025-10-04T07:09:11.694Z","0.2.0":"2025-10-24T00:08:19.420Z","0.2.1":"2025-10-24T00:17:16.434Z","0.2.2":"2026-09-17T12:30:22.086Z"},"bugs":{"url":"https://github.com/aurabx/jmix-js/issues"},"author":{"name":"aurabox"},"license":"MIT","homepage":"https://github.com/aurabx/jmix-js#readme","keywords":["jmix"],"repository":{"url":"git+https://github.com/aurabx/jmix-js.git","type":"git"},"description":"TypeScript implementation of JMIX (JSON Medical Interchange) format for secure medical data exchange","maintainers":[{"name":"xtfer","email":"chris.skene@gmail.com"}],"readme":"# JMIX TypeScript Library\n\nTypeScript implementation of the JMIX (JSON Medical Interchange) format for secure medical data exchange.\n\n## Installation\n\n```bash\nnpm install\nnpm run build\n```\n\n## Prerequisites\n\n- Node.js 20+ LTS\n- npm\n- TypeScript 5+\n\n## Quick Start\n\n```typescript\nimport { JmixBuilder } from 'jmix-ts';\n\n// Create a builder\nconst builder = new JmixBuilder({\n  outputPath: './tmp'\n});\n\n// Create or load configuration\nconst config = await JmixBuilder.loadConfig('./samples/sample_config.json');\n\n// Build envelope from DICOM directory\nconst envelope = await builder.buildFromDicom('./samples/study_1', config);\n\n// Save to files\nawait builder.saveToFiles(envelope, './output');\n```\n\n## Demo\n\nRun the demo to see the library in action:\n\n```bash\nnpm run build\nnode demo-no-validation.js\n```\n\nThis creates a complete JMIX envelope with:\n- `manifest.json` - Security, routing, and patient information\n- `metadata.json` - Clinical data and DICOM metadata\n- `audit.json` - Audit trail and transmission details\nckaging (folder-based JMIX envelope)\n\nCreate a proper JMIX folder that includes the original DICOM files under `payload/dicom` and computes a deterministic SHA-256 payload hash stored at `manifest.security.payload_hash`.\n\nCLI-like demo:\n\n```sh\nnode demo-package.js ./samples/study_1 ./tmp\n```\n\nProgrammatic API:\n\n```ts\nimport { JmixBuilder } from './dist/index.js';\n\nconst builder = new JmixBuilder();\nconst config = await JmixBuilder.loadConfig('./samples/sample_config.json');\nconst packagePath = await builder.packageToDirectory('./samples/study_1', config, './tmp');\nconsole.log(packagePath); // => ./tmp/<envelope_id>.JMIX\n```\n\nResulting layout:\n\n```text\n<outputRoot>/<envelope_id>.JMIX/\n├── manifest.json\n├── audit.json\n└── payload/\n    ├── metadata.json\n    └── dicom/\n        └── ... (original DICOM files, structure preserved)\n```\n\n### Encryption (AES-256-GCM with X25519 + HKDF)\n\nThe library can produce an encrypted JMIX envelope. It tars the plaintext `payload/` directory, encrypts it with AES-256-GCM using a key derived from X25519 ECDH and HKDF-SHA256, then writes `payload.encrypted` and removes the plaintext `payload/`.\n\n- Key agreement: Curve25519 (X25519)\n- KDF: HKDF-SHA256 (info = \"JMIX-Payload-Encryption\")\n- Cipher: AES-256-GCM\n- Manifest fields:\n  - `manifest.security.payload_hash` — SHA-256 over the plaintext `payload/` (deterministic, path+newline+bytes per file)\n  - `manifest.security.encryption`:\n    - `algorithm`: \"AES-256-GCM\"\n    - `ephemeral_public_key`: base64\n    - `iv`: base64 (12 bytes)\n    - `auth_tag`: base64 (16 bytes)\n\nDemo:\n\n```sh\n# recipientPublicKeyBase64 must be a 32-byte Curve25519 key in base64\nnode demo-package-encrypted.js <recipientPublicKeyBase64> ./samples/study_1 ./tmp\n```\n\nProgrammatic API:\n\n```ts\nimport { JmixBuilder } from './dist/index.js';\n\nconst builder = new JmixBuilder();\nconst config = await JmixBuilder.loadConfig('./samples/sample_config.json');\nconst packagePath = await builder.packageEncryptedToDirectory(\n  './samples/study_1',\n  config,\n  './tmp',\n  '<recipientPublicKeyBase64>'\n);\nconsole.log(packagePath);\n```\n\nEncrypted layout:\n\n```text\n<outputRoot>/<envelope_id>.JMIX/\n├── manifest.json               # includes security.payload_hash and security.encryption\n├── audit.json\n└── payload.encrypted           # AES-256-GCM encrypted TAR of plaintext payload/\n```\n\n### Decryption\n\nYou can decrypt an encrypted JMIX envelope and restore a plaintext `payload/` directory. The payload hash is verified against `manifest.security.payload_hash`.\n\nSelf-contained demo (encrypt + decrypt with a fresh keypair):\n\n```sh\nnode demo-decrypt.js ./samples/study_1 ./tmp\n```\n\nDecrypt an existing folder (requires the matching Curve25519 private key in base64):\n\n```sh\nnode demo-decrypt-existing.js ./tmp/<envelope_id>.JMIX <recipientPrivateKeyBase64>\n```\n\nProgrammatic API:\n\n```ts\nimport { JmixBuilder } from './dist/index.js';\n\nconst builder = new JmixBuilder();\nconst payloadPath = await builder.decryptEnvelope(\n  './tmp/<envelope_id>.JMIX',\n  '<recipientPrivateKeyBase64>'\n);\nconsole.log(payloadPath);\n```\n\n### Verify Payload Hash\n\nVerify the payload hash listed in `manifest.security.payload_hash`.\n\n- Plaintext payload/: compare directly\n- Encrypted payload.encrypted: requires the recipient private key to decrypt to a temp dir before verifying\n\nCLI demo:\n\n```sh\n# Plaintext envelope\nnpm run demo:verify:hash -- ./tmp/<envelope_id>.JMIX\n\n# Encrypted envelope (requires private key)\nnpm run demo:verify:hash -- ./tmp/<envelope_id>.JMIX <recipientPrivateKeyBase64>\n```\n\nProgrammatic API:\n\n```ts\nimport { JmixBuilder } from './dist/index.js';\n\nconst builder = new JmixBuilder();\nconst result = await builder.verifyPayloadHash(\n  './tmp/<envelope_id>.JMIX',\n  { recipientPrivateKeyBase64: '<privKey-if-encrypted>' }\n);\nconsole.log(result.ok, result.expected, result.computed);\n```\n\n## Scripts\n\n```bash\n# Development\nnpm run build          # Compile TypeScript\nnpm run typecheck      # Type checking only\nnpm test               # Run all tests\nnpm run test:watch     # Run tests in watch mode\nnpm run test:coverage  # Generate coverage report\n\n# Code Quality\nnpm run format         # Check code formatting\nnpm run format:fix     # Fix code formatting\nnpm run clean          # Clean build artifacts\n```\n\n## Architecture\n\n```\nsrc/\n├── JmixBuilder.ts           # Main orchestrator\n├── types/index.ts           # TypeScript interfaces\n├── validation/              # Ajv schema validation\n├── dicom/                   # DICOM file processing\n├── crypto/                  # AES-256-GCM encryption (future)\n└── errors/                  # Error types\n\ntests/                       # Jest test suite\nsamples/                     # Sample JSON files\n```\n\n## Configuration\n\n### Schema Validation\n\n** This is Alpha quality code and has not yet been fully tested **\n\nThe library uses configurable schema validation:\n\n```typescript\nconst builder = new JmixBuilder({\n  schemaValidatorOptions: {\n    schemaPath: '../jmix/schemas',  // Default: ../jmix/schemas\n    strictMode: true                // Default: true\n  }\n});\n\n// Or via environment variable\nprocess.env.JMIX_SCHEMA_PATH = '/path/to/schemas';\n```\n\n### Output Directory\n\nFollows user rules for temp file output:\n\n```typescript\nconst builder = new JmixBuilder({\n  outputPath: './tmp'  // Default: ./tmp (not /tmp)\n});\n```\n\n## DICOM Processing\n\nThe library automatically:\n\n1. Recursively scans directories for DICOM files\n2. Validates files using DICM magic number at byte offset 128\n3. Extracts metadata (patient info, study details, series information)\n4. Falls back gracefully to configuration data when DICOM parsing fails\n5. Supports empty directories for testing\n\n## Schema Validation\n\n- **Default**: Looks for schemas in `../jmix/schemas`\n- **Configurable**: Override via constructor or environment variable\n- **Graceful Degradation**: Skips validation when schemas not found\n- **Comprehensive**: Validates manifest, metadata, and audit components\n\n## Testing\n\nRun the comprehensive test suite:\n\n```bash\n# All tests\nnpm test\n\n# Specific test files\nnpm test JmixBuilder.test.ts\nnpm test SchemaValidator.test.ts\n\n# With coverage\nnpm run test:coverage\n```\n\nThe test suite includes:\n- ✅ JmixBuilder integration tests\n- ✅ Schema validator tests\n- ✅ Type definition tests\n- ✅ DICOM processing tests\n- ✅ Configuration loading tests\n- ✅ File I/O tests\n\nAll tests use the `/samples` directory for realistic test data and output to `./tmp` for temporary files.\n\n## Security Model\n\nImplements the JMIX security whitepaper specifications:\n- **AES-256-GCM** encryption with ephemeral public keys\n- **Base64 encoding** for all cryptographic material (ephemeral_public_key, iv, auth_tag)\n- **Forward secrecy** through ephemeral key usage\n- **Optional governance** via Aurabox directory services\n\nSee `.ai/security.md` for the complete security model.\n\n## Development\n\nThe library follows TypeScript best practices:\n- **ESM modules** with .js imports in source\n- **Strict TypeScript** configuration\n- **Comprehensive type definitions** for all JMIX components\n- **Jest testing** with ts-jest for ESM support\n- **Prettier formatting** for consistent code style\n\n## Files Generated\n\nA complete JMIX envelope consists of:\n\n- **`manifest.json`** - Routing, security classification, patient info, sender/receiver details\n- **`metadata.json`** - Clinical metadata, DICOM study information, custom metadata\n- **`audit.json`** - Audit trail, status, and events timeline\n- **`files.json`** - (Optional) File manifest with hashes and sizes\n\n## Compatibility\n\n- **JMIX Specification**: Implements JMIX v1.0 format\n- **Schema Compatibility**: Works with JMIX JSON Schema Draft 2020-12\n- **DICOM Support**: Basic DICOM file detection and metadata extraction\n- **Framework Agnostic**: Pure TypeScript, works with any Node.js framework\n\n## License\n\nMatches the licensing of the JMIX specification and related implementations.\n\n---\n\nFor detailed API documentation, see the TypeScript declarations in `dist/` after building.","readmeFilename":"README.md"}