@private.me/xbind 3.0.1 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -14
- package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1920 -1
- package/dist-standalone/_deps/shared/cjs/errors.js +729 -1
- package/dist-standalone/_deps/shared/cjs/index.js +463 -1
- package/dist-standalone/_deps/shared/cjs/types.js +315 -1
- package/dist-standalone/_deps/shared/errors.js +244 -1
- package/dist-standalone/_deps/shared/index.js +72 -1
- package/dist-standalone/_deps/shared/types.js +86 -1
- package/dist-standalone/_deps/ux-helpers/cjs/errors.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/index.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/progress.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/search.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/types.js +1 -1
- package/dist-standalone/_deps/ux-helpers/errors.js +1 -1
- package/dist-standalone/_deps/ux-helpers/index.js +1 -1
- package/dist-standalone/_deps/ux-helpers/pagination.js +1 -1
- package/dist-standalone/_deps/ux-helpers/progress.js +1 -1
- package/dist-standalone/_deps/ux-helpers/search.js +1 -1
- package/dist-standalone/_deps/xchange/auto-accept.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/auto-accept.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/errors.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/index.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/invite-client.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/lazy-init.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/trust-integration.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/xchange.js +1 -1
- package/dist-standalone/_deps/xchange/errors.js +1 -1
- package/dist-standalone/_deps/xchange/index.js +1 -1
- package/dist-standalone/_deps/xchange/invite-client.js +1 -1
- package/dist-standalone/_deps/xchange/lazy-init.js +1 -1
- package/dist-standalone/_deps/xchange/trust-integration.js +1 -1
- package/dist-standalone/_deps/xchange/xchange.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/discovery.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/errors.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/index.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/registry.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/schema.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/types.js +1 -1
- package/dist-standalone/_deps/xregistry/discovery.js +1 -1
- package/dist-standalone/_deps/xregistry/errors.js +1 -1
- package/dist-standalone/_deps/xregistry/index.js +1 -1
- package/dist-standalone/_deps/xregistry/registry.js +1 -1
- package/dist-standalone/_deps/xregistry/schema.js +1 -1
- package/dist-standalone/_deps/xregistry/types.js +1 -1
- package/dist-standalone/agent-call.js +659 -1
- package/dist-standalone/agent-sdk.js +328 -1
- package/dist-standalone/agent.js +1800 -1
- package/dist-standalone/approval.js +193 -1
- package/dist-standalone/async-iterators.js +382 -1
- package/dist-standalone/auth.js +219 -1
- package/dist-standalone/auto-accept.js +229 -1
- package/dist-standalone/backup-config.js +201 -1
- package/dist-standalone/backup.js +326 -1
- package/dist-standalone/batch-operations.js +388 -1
- package/dist-standalone/cancellation.js +477 -1
- package/dist-standalone/checkpoint.js +186 -1
- package/dist-standalone/circuit-breaker.js +468 -1
- package/dist-standalone/cjs/agent-call.js +701 -1
- package/dist-standalone/cjs/agent-sdk.js +332 -1
- package/dist-standalone/cjs/agent.js +1837 -1
- package/dist-standalone/cjs/approval.js +199 -1
- package/dist-standalone/cjs/async-iterators.js +392 -1
- package/dist-standalone/cjs/auth.js +225 -1
- package/dist-standalone/cjs/auto-accept.js +233 -1
- package/dist-standalone/cjs/backup-config.js +207 -1
- package/dist-standalone/cjs/backup.js +330 -1
- package/dist-standalone/cjs/batch-operations.js +397 -1
- package/dist-standalone/cjs/cancellation.js +490 -1
- package/dist-standalone/cjs/checkpoint.js +193 -1
- package/dist-standalone/cjs/circuit-breaker.js +476 -1
- package/dist-standalone/cjs/cli/init.js +492 -1
- package/dist-standalone/cjs/config-validation.js +522 -1
- package/dist-standalone/cjs/connect.js +312 -1
- package/dist-standalone/cjs/connection-pool.js +506 -1
- package/dist-standalone/cjs/correlation-id.js +339 -1
- package/dist-standalone/cjs/crypto-utils.js +176 -1
- package/dist-standalone/cjs/debug-mode.js +534 -1
- package/dist-standalone/cjs/did-document.js +101 -1
- package/dist-standalone/cjs/did-privateme.js +130 -1
- package/dist-standalone/cjs/did-web.js +201 -1
- package/dist-standalone/cjs/discovery.js +462 -1
- package/dist-standalone/cjs/dual-mode.js +251 -1
- package/dist-standalone/cjs/email-templates.js +313 -1
- package/dist-standalone/cjs/email-transport.js +239 -1
- package/dist-standalone/cjs/envelope.js +538 -1
- package/dist-standalone/cjs/errors.js +913 -1
- package/dist-standalone/cjs/event-emitter.js +461 -1
- package/dist-standalone/cjs/gateway-state.js +55 -1
- package/dist-standalone/cjs/gateway-transport.js +120 -1
- package/dist-standalone/cjs/graceful-degradation.js +403 -1
- package/dist-standalone/cjs/guardrails.js +223 -1
- package/dist-standalone/cjs/health-check.js +336 -1
- package/dist-standalone/cjs/http-compat.js +272 -1
- package/dist-standalone/cjs/http-status-map.js +571 -1
- package/dist-standalone/cjs/identity.js +645 -1
- package/dist-standalone/cjs/index.js +406 -1
- package/dist-standalone/cjs/invitation.js +421 -1
- package/dist-standalone/cjs/invite.js +328 -1
- package/dist-standalone/cjs/key-agreement.js +335 -1
- package/dist-standalone/cjs/lazy-init.js +300 -1
- package/dist-standalone/cjs/logger.js +291 -1
- package/dist-standalone/cjs/mdns-discovery.js +202 -1
- package/dist-standalone/cjs/nonce-store.js +80 -1
- package/dist-standalone/cjs/pairing-manager.js +223 -1
- package/dist-standalone/cjs/plugin-system.js +264 -1
- package/dist-standalone/cjs/plugins/logging.js +168 -1
- package/dist-standalone/cjs/plugins/metrics.js +181 -1
- package/dist-standalone/cjs/plugins/validation.js +302 -1
- package/dist-standalone/cjs/policy.js +320 -1
- package/dist-standalone/cjs/progress-callbacks.js +583 -1
- package/dist-standalone/cjs/redis-nonce-store.js +76 -1
- package/dist-standalone/cjs/registry-middleware.js +50 -1
- package/dist-standalone/cjs/retry-strategies.js +544 -1
- package/dist-standalone/cjs/retry-transport.js +102 -1
- package/dist-standalone/cjs/runtime/browser.js +533 -1
- package/dist-standalone/cjs/runtime/edge.js +526 -1
- package/dist-standalone/cjs/runtime/react-native.js +394 -1
- package/dist-standalone/cjs/security-policy.js +245 -1
- package/dist-standalone/cjs/serialization.js +1040 -1
- package/dist-standalone/cjs/split-channel.js +225 -1
- package/dist-standalone/cjs/subscription-proof.js +230 -1
- package/dist-standalone/cjs/succession.js +148 -1
- package/dist-standalone/cjs/timeouts.js +412 -1
- package/dist-standalone/cjs/trace-context.js +424 -1
- package/dist-standalone/cjs/trace-spans.js +495 -1
- package/dist-standalone/cjs/transport.js +63 -1
- package/dist-standalone/cjs/trust-registry.js +991 -1
- package/dist-standalone/cjs/types/error-response.js +56 -1
- package/dist-standalone/cjs/vault-auth.js +178 -1
- package/dist-standalone/cjs/vault-store-loader.js +194 -1
- package/dist-standalone/cjs/verify.js +25 -1
- package/dist-standalone/cjs/version-info.js +543 -1
- package/dist-standalone/cjs/xfetch.js +340 -1
- package/dist-standalone/cli/init.js +455 -1
- package/dist-standalone/cli/setup.js +514 -1
- package/dist-standalone/cli/types.js +27 -1
- package/dist-standalone/cli/xbind.js +148 -1
- package/dist-standalone/config-validation.js +513 -1
- package/dist-standalone/connect.js +274 -1
- package/dist-standalone/connection-pool.js +500 -1
- package/dist-standalone/correlation-id.js +326 -1
- package/dist-standalone/crypto-utils.js +157 -1
- package/dist-standalone/debug-mode.js +510 -1
- package/dist-standalone/did-document.js +96 -1
- package/dist-standalone/did-privateme.js +121 -1
- package/dist-standalone/did-web.js +196 -1
- package/dist-standalone/discovery.js +458 -1
- package/dist-standalone/dual-mode.js +247 -1
- package/dist-standalone/email-templates.js +309 -1
- package/dist-standalone/email-transport.js +232 -1
- package/dist-standalone/envelope.js +525 -1
- package/dist-standalone/errors.js +896 -1
- package/dist-standalone/event-emitter.js +456 -1
- package/dist-standalone/gateway-state.js +51 -1
- package/dist-standalone/gateway-transport.js +116 -1
- package/dist-standalone/graceful-degradation.js +396 -1
- package/dist-standalone/guardrails.js +216 -1
- package/dist-standalone/health-check.js +332 -1
- package/dist-standalone/http-compat.js +267 -1
- package/dist-standalone/http-status-map.js +561 -1
- package/dist-standalone/identity.js +619 -1
- package/dist-standalone/index.js +78 -1
- package/dist-standalone/invitation.js +415 -1
- package/dist-standalone/invite.js +324 -1
- package/dist-standalone/key-agreement.js +325 -1
- package/dist-standalone/lazy-init.js +295 -1
- package/dist-standalone/logger.js +285 -1
- package/dist-standalone/mdns-discovery.js +195 -1
- package/dist-standalone/nonce-store.js +76 -1
- package/dist-standalone/pairing-manager.js +219 -1
- package/dist-standalone/plugin-system.js +257 -1
- package/dist-standalone/plugins/logging.js +163 -1
- package/dist-standalone/plugins/metrics.js +176 -1
- package/dist-standalone/plugins/validation.js +297 -1
- package/dist-standalone/policy.js +315 -1
- package/dist-standalone/progress-callbacks.js +576 -1
- package/dist-standalone/redis-nonce-store.js +72 -1
- package/dist-standalone/registry-middleware.js +47 -1
- package/dist-standalone/retry-strategies.js +534 -1
- package/dist-standalone/retry-transport.js +98 -1
- package/dist-standalone/runtime/browser.js +516 -1
- package/dist-standalone/runtime/edge.js +511 -1
- package/dist-standalone/runtime/react-native.js +383 -1
- package/dist-standalone/security-policy.js +239 -1
- package/dist-standalone/serialization.js +1031 -1
- package/dist-standalone/split-channel.js +219 -1
- package/dist-standalone/subscription-proof.js +224 -1
- package/dist-standalone/succession.js +142 -1
- package/dist-standalone/timeouts.js +398 -1
- package/dist-standalone/trace-context.js +414 -1
- package/dist-standalone/trace-spans.js +488 -1
- package/dist-standalone/transport.js +59 -1
- package/dist-standalone/trust-registry.js +950 -1
- package/dist-standalone/types/error-response.js +52 -1
- package/dist-standalone/vault-auth.js +174 -1
- package/dist-standalone/vault-store-loader.js +187 -1
- package/dist-standalone/verify.js +16 -1
- package/dist-standalone/version-info.js +530 -1
- package/dist-standalone/xfetch.js +335 -1
- package/package.json +4 -13
- package/share1.dat +0 -0
- package/dist-standalone/_deps/mldsa-wasm/LICENSE +0 -24
- package/dist-standalone/_deps/mldsa-wasm/package.json +0 -46
- package/dist-standalone/_deps/shared/cjs/package.json +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/package.json +0 -1
- package/dist-standalone/_deps/xchange/cjs/package.json +0 -1
- package/dist-standalone/_deps/xregistry/cjs/package.json +0 -1
- package/dist-standalone/cjs/package.json +0 -3
- package/dist-standalone/package.json +0 -10
|
@@ -1 +1,1031 @@
|
|
|
1
|
-
import{ok,err}from"./_deps/shared/index.js";class JSONSerializer{format="json";contentType="application/json";serialize(e,t){try{const r=t?.pretty?JSON.stringify(e,null,2):JSON.stringify(e);return ok((new TextEncoder).encode(r))}catch(e){return err({code:"SERIALIZATION_FAILED",reason:e instanceof Error?e.message:String(e),format:"json"})}}deserialize(e){try{const t=(new TextDecoder).decode(e);return ok(JSON.parse(t))}catch(e){return err({code:"DESERIALIZATION_FAILED",reason:e instanceof Error?e.message:String(e)})}}detect(e){if(0===e.length)return!1;const t=e[0];if(void 0===t)return!1;if(123===t||91===t||34===t)return!0;if(32===t||9===t||10===t||13===t)for(let t=1;t<Math.min(e.length,10);t++){const r=e[t];if(void 0===r)return!1;if(123===r||91===r||34===r)return!0;if(32!==r&&9!==r&&10!==r&&13!==r)return!1}return!1}}class MessagePackSerializer{format="msgpack";contentType="application/msgpack";serialize(e,t){try{return ok(this.encode(e))}catch(e){return err({code:"SERIALIZATION_FAILED",reason:e instanceof Error?e.message:String(e),format:"msgpack"})}}deserialize(e){try{return ok(this.decode(e))}catch(e){return err({code:"DESERIALIZATION_FAILED",reason:e instanceof Error?e.message:String(e)})}}detect(e){if(0===e.length)return!1;const t=e[0];if(void 0===t)return!1;if(t>=224&&t<=255&&e.length<2)return!1;try{const t={offset:0};return this.decodeValue(e,t),!0}catch{return!1}}encode(e){const t=[];this.encodeValue(e,t);const r=t.reduce((e,t)=>e+t.length,0),n=new Uint8Array(r);let s=0;for(const e of t)n.set(e,s),s+=e.length;return n}encodeValue(e,t){if(null==e)t.push(new Uint8Array([192]));else if("boolean"==typeof e)t.push(new Uint8Array([e?195:194]));else if("number"==typeof e)this.encodeNumber(e,t);else if("string"==typeof e)this.encodeString(e,t);else if(e instanceof Uint8Array)this.encodeBinary(e,t);else if(Array.isArray(e))this.encodeArray(e,t);else{if("object"!=typeof e)throw new Error("Unsupported type: "+typeof e);this.encodeObject(e,t)}}encodeNumber(e,t){if(Number.isInteger(e))if(e>=0&&e<=127)t.push(new Uint8Array([e]));else if(e>=-32&&e<0)t.push(new Uint8Array([224|31&e]));else if(e>=0&&e<=255)t.push(new Uint8Array([204,e]));else if(e>=0&&e<=65535)t.push(new Uint8Array([205,e>>8,255&e]));else if(e>=0&&e<=4294967295){const r=new Uint8Array(5);r[0]=206,new DataView(r.buffer).setUint32(1,e,!1),t.push(r)}else{const r=new Uint8Array(9);r[0]=203,new DataView(r.buffer).setFloat64(1,e,!1),t.push(r)}else{const r=new Uint8Array(9);r[0]=203,new DataView(r.buffer).setFloat64(1,e,!1),t.push(r)}}encodeString(e,t){const r=(new TextEncoder).encode(e),n=r.length;if(n<=31)t.push(new Uint8Array([160|n]));else if(n<=255)t.push(new Uint8Array([217,n]));else if(n<=65535)t.push(new Uint8Array([218,n>>8,255&n]));else{const e=new Uint8Array(5);e[0]=219,new DataView(e.buffer).setUint32(1,n,!1),t.push(e)}t.push(r)}encodeBinary(e,t){const r=e.length;if(r<=255)t.push(new Uint8Array([196,r]));else if(r<=65535)t.push(new Uint8Array([197,r>>8,255&r]));else{const e=new Uint8Array(5);e[0]=198,new DataView(e.buffer).setUint32(1,r,!1),t.push(e)}t.push(e)}encodeArray(e,t){const r=e.length;if(r<=15)t.push(new Uint8Array([144|r]));else if(r<=65535)t.push(new Uint8Array([220,r>>8,255&r]));else{const e=new Uint8Array(5);e[0]=221,new DataView(e.buffer).setUint32(1,r,!1),t.push(e)}for(const r of e)this.encodeValue(r,t)}encodeObject(e,t){const r=Object.keys(e),n=r.length;if(n<=15)t.push(new Uint8Array([128|n]));else if(n<=65535)t.push(new Uint8Array([222,n>>8,255&n]));else{const e=new Uint8Array(5);e[0]=223,new DataView(e.buffer).setUint32(1,n,!1),t.push(e)}for(const n of r)this.encodeValue(n,t),this.encodeValue(e[n],t)}decode(e){return this.decodeValue(e,{offset:0})}decodeValue(e,t){if(t.offset>=e.length)throw new Error("Unexpected end of data");const r=e[t.offset++];if(void 0===r)throw new Error("Unexpected end of data");if(r<=127)return r;if(r>=128&&r<=143){const n=15&r;return this.decodeMap(e,t,n)}if(r>=144&&r<=159){const n=15&r;return this.decodeArray(e,t,n)}if(r>=160&&r<=191){const n=31&r;return this.decodeString(e,t,n)}if(r>=224)return(31&r)-32;switch(r){case 192:return null;case 194:return!1;case 195:return!0;case 196:{const r=e[t.offset++];if(void 0===r)throw new Error("Unexpected end of data");return this.decodeBinary(e,t,r)}case 197:return this.decodeBinary(e,t,this.readUint16(e,t));case 198:return this.decodeBinary(e,t,this.readUint32(e,t));case 202:return this.readFloat32(e,t);case 203:return this.readFloat64(e,t);case 204:{const r=e[t.offset++];if(void 0===r)throw new Error("Unexpected end of data");return r}case 205:return this.readUint16(e,t);case 206:return this.readUint32(e,t);case 208:return this.readInt8(e,t);case 209:return this.readInt16(e,t);case 210:return this.readInt32(e,t);case 217:{const r=e[t.offset++];if(void 0===r)throw new Error("Unexpected end of data");return this.decodeString(e,t,r)}case 218:return this.decodeString(e,t,this.readUint16(e,t));case 219:return this.decodeString(e,t,this.readUint32(e,t));case 220:return this.decodeArray(e,t,this.readUint16(e,t));case 221:return this.decodeArray(e,t,this.readUint32(e,t));case 222:return this.decodeMap(e,t,this.readUint16(e,t));case 223:return this.decodeMap(e,t,this.readUint32(e,t));default:throw new Error(`Unknown MessagePack type: 0x${r.toString(16)}`)}}decodeString(e,t,r){const n=e.slice(t.offset,t.offset+r);return t.offset+=r,(new TextDecoder).decode(n)}decodeBinary(e,t,r){const n=e.slice(t.offset,t.offset+r);return t.offset+=r,n}decodeArray(e,t,r){const n=[];for(let s=0;s<r;s++)n.push(this.decodeValue(e,t));return n}decodeMap(e,t,r){const n={};for(let s=0;s<r;s++){const r=this.decodeValue(e,t),s=this.decodeValue(e,t);n[String(r)]=s}return n}readUint16(e,t){const r=new DataView(e.buffer,e.byteOffset).getUint16(t.offset,!1);return t.offset+=2,r}readUint32(e,t){const r=new DataView(e.buffer,e.byteOffset).getUint32(t.offset,!1);return t.offset+=4,r}readInt8(e,t){if(t.offset>=e.length)throw new Error("Unexpected end of data");const r=new DataView(e.buffer,e.byteOffset).getInt8(t.offset);return t.offset+=1,r}readInt16(e,t){const r=new DataView(e.buffer,e.byteOffset).getInt16(t.offset,!1);return t.offset+=2,r}readInt32(e,t){const r=new DataView(e.buffer,e.byteOffset).getInt32(t.offset,!1);return t.offset+=4,r}readFloat32(e,t){const r=new DataView(e.buffer,e.byteOffset).getFloat32(t.offset,!1);return t.offset+=4,r}readFloat64(e,t){const r=new DataView(e.buffer,e.byteOffset).getFloat64(t.offset,!1);return t.offset+=8,r}}class CBORSerializer{format="cbor";contentType="application/cbor";serialize(e,t){try{return ok(this.encode(e))}catch(e){return err({code:"SERIALIZATION_FAILED",reason:e instanceof Error?e.message:String(e),format:"cbor"})}}deserialize(e){try{return ok(this.decode(e))}catch(e){return err({code:"DESERIALIZATION_FAILED",reason:e instanceof Error?e.message:String(e)})}}detect(e){if(0===e.length)return!1;e[0];try{const t={offset:0};return this.decodeValue(e,t),!0}catch{return!1}}encode(e){const t=[];this.encodeValue(e,t);const r=t.reduce((e,t)=>e+t.length,0),n=new Uint8Array(r);let s=0;for(const e of t)n.set(e,s),s+=e.length;return n}encodeValue(e,t){if(null==e)t.push(new Uint8Array([246]));else if("boolean"==typeof e)t.push(new Uint8Array([e?245:244]));else if("number"==typeof e)this.encodeNumber(e,t);else if("string"==typeof e)this.encodeString(e,t);else if(e instanceof Uint8Array)this.encodeBinary(e,t);else if(Array.isArray(e))this.encodeArray(e,t);else{if("object"!=typeof e)throw new Error("Unsupported type: "+typeof e);this.encodeObject(e,t)}}encodeNumber(e,t){if(Number.isInteger(e))if(e>=0)if(e<=23)t.push(new Uint8Array([e]));else if(e<=255)t.push(new Uint8Array([24,e]));else if(e<=65535)t.push(new Uint8Array([25,e>>8,255&e]));else if(e<=4294967295){const r=new Uint8Array(5);r[0]=26,new DataView(r.buffer).setUint32(1,e,!1),t.push(r)}else{const r=new Uint8Array(9);r[0]=251,new DataView(r.buffer).setFloat64(1,e,!1),t.push(r)}else{const r=-1-e;if(r<=23)t.push(new Uint8Array([32|r]));else if(r<=255)t.push(new Uint8Array([56,r]));else if(r<=65535)t.push(new Uint8Array([57,r>>8,255&r]));else{const e=new Uint8Array(5);e[0]=58,new DataView(e.buffer).setUint32(1,r,!1),t.push(e)}}else{const r=new Uint8Array(9);r[0]=251,new DataView(r.buffer).setFloat64(1,e,!1),t.push(r)}}encodeString(e,t){const r=(new TextEncoder).encode(e),n=r.length;if(n<=23)t.push(new Uint8Array([96|n]));else if(n<=255)t.push(new Uint8Array([120,n]));else if(n<=65535)t.push(new Uint8Array([121,n>>8,255&n]));else{const e=new Uint8Array(5);e[0]=122,new DataView(e.buffer).setUint32(1,n,!1),t.push(e)}t.push(r)}encodeBinary(e,t){const r=e.length;if(r<=23)t.push(new Uint8Array([64|r]));else if(r<=255)t.push(new Uint8Array([88,r]));else if(r<=65535)t.push(new Uint8Array([89,r>>8,255&r]));else{const e=new Uint8Array(5);e[0]=90,new DataView(e.buffer).setUint32(1,r,!1),t.push(e)}t.push(e)}encodeArray(e,t){const r=e.length;if(r<=23)t.push(new Uint8Array([128|r]));else if(r<=255)t.push(new Uint8Array([152,r]));else if(r<=65535)t.push(new Uint8Array([153,r>>8,255&r]));else{const e=new Uint8Array(5);e[0]=154,new DataView(e.buffer).setUint32(1,r,!1),t.push(e)}for(const r of e)this.encodeValue(r,t)}encodeObject(e,t){const r=Object.keys(e),n=r.length;if(n<=23)t.push(new Uint8Array([160|n]));else if(n<=255)t.push(new Uint8Array([184,n]));else if(n<=65535)t.push(new Uint8Array([185,n>>8,255&n]));else{const e=new Uint8Array(5);e[0]=186,new DataView(e.buffer).setUint32(1,n,!1),t.push(e)}for(const n of r)this.encodeValue(n,t),this.encodeValue(e[n],t)}decode(e){return this.decodeValue(e,{offset:0})}decodeValue(e,t){if(t.offset>=e.length)throw new Error("Unexpected end of data");const r=e[t.offset++];if(void 0===r)throw new Error("Unexpected end of data");const n=r>>5&7,s=31&r;switch(n){case 0:return this.decodeInteger(e,t,s);case 1:return-1-this.decodeInteger(e,t,s);case 2:return this.decodeBinary(e,t,s);case 3:return this.decodeString(e,t,s);case 4:return this.decodeArray(e,t,s);case 5:return this.decodeMap(e,t,s);case 7:return this.decodeSpecial(e,t,s);default:throw new Error(`Unsupported CBOR major type: ${n}`)}}decodeInteger(e,t,r){if(r<=23)return r;if(24===r){const r=e[t.offset++];if(void 0===r)throw new Error("Unexpected end of data");return r}if(25===r)return this.readUint16(e,t);if(26===r)return this.readUint32(e,t);throw new Error("Unsupported integer size")}decodeString(e,t,r){let n;n=r<=23?r:this.decodeInteger(e,t,r);const s=e.slice(t.offset,t.offset+n);return t.offset+=n,(new TextDecoder).decode(s)}decodeBinary(e,t,r){let n;n=r<=23?r:this.decodeInteger(e,t,r);const s=e.slice(t.offset,t.offset+n);return t.offset+=n,s}decodeArray(e,t,r){let n;n=r<=23?r:this.decodeInteger(e,t,r);const s=[];for(let r=0;r<n;r++)s.push(this.decodeValue(e,t));return s}decodeMap(e,t,r){let n;n=r<=23?r:this.decodeInteger(e,t,r);const s={};for(let r=0;r<n;r++){const r=this.decodeValue(e,t),n=this.decodeValue(e,t);s[String(r)]=n}return s}decodeSpecial(e,t,r){switch(r){case 20:return!1;case 21:return!0;case 22:return null;case 23:return;case 27:return this.readFloat64(e,t);default:throw new Error(`Unsupported CBOR special: ${r}`)}}readUint16(e,t){const r=new DataView(e.buffer,e.byteOffset).getUint16(t.offset,!1);return t.offset+=2,r}readUint32(e,t){const r=new DataView(e.buffer,e.byteOffset).getUint32(t.offset,!1);return t.offset+=4,r}readFloat64(e,t){const r=new DataView(e.buffer,e.byteOffset).getFloat64(t.offset,!1);return t.offset+=8,r}}const serializers=new Map([["json",new JSONSerializer],["cbor",new CBORSerializer],["msgpack",new MessagePackSerializer]]);export function serialize(e,t={}){const r=t.format??"json",n=serializers.get(r);if(!n)return err({code:"UNSUPPORTED_FORMAT",format:r});const s=t.collectMetrics?performance.now():0,o=n.serialize(e,t);if(!o.ok)return o;const i=t.collectMetrics?{serializeDuration:performance.now()-s,size:o.value.length,format:r,timestamp:Date.now()}:void 0;return ok({data:o.value,format:r,size:o.value.length,contentType:n.contentType,metrics:i})}export function deserialize(e,t={}){const r=t.collectMetrics?performance.now():0;let n,s;if(t.format){if(n=t.format,s=serializers.get(n),!s)return err({code:"UNSUPPORTED_FORMAT",format:n})}else{const t=detectFormat(e);if(!t.ok)return t;if(n=t.value,s=serializers.get(n),!s)return err({code:"UNSUPPORTED_FORMAT",format:n})}const o=s.deserialize(e);if(!o.ok)return o;const i=t.collectMetrics?{serializeDuration:performance.now()-r,size:e.length,format:n,timestamp:Date.now()}:void 0;return ok({value:o.value,format:n,metrics:i})}export function detectFormat(e){const t=serializers.get("json");if(t?.detect(e))return ok("json");if(0===e.length)return err({code:"FORMAT_DETECTION_FAILED",reason:"Empty data"});const r=serializers.get("cbor");if(r?.detect(e))return ok("cbor");const n=serializers.get("msgpack");return n?.detect(e)?ok("msgpack"):err({code:"FORMAT_DETECTION_FAILED",reason:"No serializer recognized the data format"})}export function negotiateFormat(e,t){for(const r of e)if(t.includes(r))return ok(r);return err({code:"UNSUPPORTED_FORMAT",format:`No common format (client: ${e.join(",")}, server: ${t.join(",")})`})}export function compareFormats(e,t=["json","msgpack","cbor"]){const r=[];for(const n of t){const t=serialize(e,{format:n,collectMetrics:!0});if(!t.ok)return t;r.push({format:n,size:t.value.size,duration:t.value.metrics?.serializeDuration??0})}return ok(r)}export function getContentType(e){const t=serializers.get(e);return t?.contentType??"application/octet-stream"}export function parseContentType(e){const t=e.toLowerCase().split(";")[0]?.trim()??"";for(const e of serializers.values())if(e.contentType===t)return e.format}
|
|
1
|
+
/**
|
|
2
|
+
* Alternative Serialization Formats (API-14)
|
|
3
|
+
*
|
|
4
|
+
* Pluggable serialization system supporting JSON, MessagePack, and CBOR.
|
|
5
|
+
* Provides format negotiation, backward compatibility, and performance benchmarks.
|
|
6
|
+
*
|
|
7
|
+
* @module serialization
|
|
8
|
+
*/
|
|
9
|
+
import { ok, err } from"./_deps/shared/index.js";
|
|
10
|
+
/* ── JSON Serializer ── */
|
|
11
|
+
class JSONSerializer {
|
|
12
|
+
format = 'json';
|
|
13
|
+
contentType = 'application/json';
|
|
14
|
+
serialize(value, options) {
|
|
15
|
+
try {
|
|
16
|
+
const json = options?.pretty
|
|
17
|
+
? JSON.stringify(value, null, 2)
|
|
18
|
+
: JSON.stringify(value);
|
|
19
|
+
return ok(new TextEncoder().encode(json));
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return err({
|
|
23
|
+
code: 'SERIALIZATION_FAILED',
|
|
24
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
25
|
+
format: 'json',
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
deserialize(data) {
|
|
30
|
+
try {
|
|
31
|
+
const text = new TextDecoder().decode(data);
|
|
32
|
+
return ok(JSON.parse(text));
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
return err({
|
|
36
|
+
code: 'DESERIALIZATION_FAILED',
|
|
37
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
detect(data) {
|
|
42
|
+
if (data.length === 0)
|
|
43
|
+
return false;
|
|
44
|
+
// JSON starts with { [ " or whitespace + one of those
|
|
45
|
+
const first = data[0];
|
|
46
|
+
if (first === undefined)
|
|
47
|
+
return false;
|
|
48
|
+
if (first === 0x7B || first === 0x5B || first === 0x22)
|
|
49
|
+
return true; // { [ "
|
|
50
|
+
if (first === 0x20 || first === 0x09 || first === 0x0A || first === 0x0D) {
|
|
51
|
+
// Whitespace - scan for first non-whitespace
|
|
52
|
+
for (let i = 1; i < Math.min(data.length, 10); i++) {
|
|
53
|
+
const byte = data[i];
|
|
54
|
+
if (byte === undefined)
|
|
55
|
+
return false;
|
|
56
|
+
if (byte === 0x7B || byte === 0x5B || byte === 0x22)
|
|
57
|
+
return true;
|
|
58
|
+
if (byte !== 0x20 && byte !== 0x09 && byte !== 0x0A && byte !== 0x0D)
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/* ── MessagePack Serializer ── */
|
|
66
|
+
class MessagePackSerializer {
|
|
67
|
+
format = 'msgpack';
|
|
68
|
+
contentType = 'application/msgpack';
|
|
69
|
+
serialize(value, _options) {
|
|
70
|
+
try {
|
|
71
|
+
return ok(this.encode(value));
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return err({
|
|
75
|
+
code: 'SERIALIZATION_FAILED',
|
|
76
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
77
|
+
format: 'msgpack',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
deserialize(data) {
|
|
82
|
+
try {
|
|
83
|
+
return ok(this.decode(data));
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return err({
|
|
87
|
+
code: 'DESERIALIZATION_FAILED',
|
|
88
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
detect(data) {
|
|
93
|
+
if (data.length === 0)
|
|
94
|
+
return false;
|
|
95
|
+
const first = data[0];
|
|
96
|
+
if (first === undefined)
|
|
97
|
+
return false;
|
|
98
|
+
// MessagePack format markers
|
|
99
|
+
// fixmap: 0x80-0x8F, fixarray: 0x90-0x9F, fixstr: 0xA0-0xBF
|
|
100
|
+
// nil: 0xC0, false: 0xC2, true: 0xC3
|
|
101
|
+
// bin8/16/32: 0xC4-0xC6, ext8/16/32: 0xC7-0xC9
|
|
102
|
+
// float32/64: 0xCA-0xCB, uint8/16/32/64: 0xCC-0xCF
|
|
103
|
+
// int8/16/32/64: 0xD0-0xD3, fixext1/2/4/8/16: 0xD4-0xD8
|
|
104
|
+
// str8/16/32: 0xD9-0xDB, array16/32: 0xDC-0xDD, map16/32: 0xDE-0xDF
|
|
105
|
+
// Reject invalid patterns that would cause decode errors
|
|
106
|
+
if (first >= 0xE0 && first <= 0xFF) {
|
|
107
|
+
// Negative fixint: valid only for single-byte integers
|
|
108
|
+
// But we need more data to validate structure
|
|
109
|
+
if (data.length < 2)
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
// Try to validate structure
|
|
113
|
+
try {
|
|
114
|
+
const state = { offset: 0 };
|
|
115
|
+
this.decodeValue(data, state);
|
|
116
|
+
return true; // Successfully decoded
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return false; // Decode failed
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Minimal MessagePack encoder (subset implementation)
|
|
123
|
+
encode(value) {
|
|
124
|
+
const chunks = [];
|
|
125
|
+
this.encodeValue(value, chunks);
|
|
126
|
+
// Concatenate chunks
|
|
127
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
128
|
+
const result = new Uint8Array(totalLength);
|
|
129
|
+
let offset = 0;
|
|
130
|
+
for (const chunk of chunks) {
|
|
131
|
+
result.set(chunk, offset);
|
|
132
|
+
offset += chunk.length;
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
encodeValue(value, chunks) {
|
|
137
|
+
if (value === null || value === undefined) {
|
|
138
|
+
chunks.push(new Uint8Array([0xC0])); // nil
|
|
139
|
+
}
|
|
140
|
+
else if (typeof value === 'boolean') {
|
|
141
|
+
chunks.push(new Uint8Array([value ? 0xC3 : 0xC2])); // true/false
|
|
142
|
+
}
|
|
143
|
+
else if (typeof value === 'number') {
|
|
144
|
+
this.encodeNumber(value, chunks);
|
|
145
|
+
}
|
|
146
|
+
else if (typeof value === 'string') {
|
|
147
|
+
this.encodeString(value, chunks);
|
|
148
|
+
}
|
|
149
|
+
else if (value instanceof Uint8Array) {
|
|
150
|
+
this.encodeBinary(value, chunks);
|
|
151
|
+
}
|
|
152
|
+
else if (Array.isArray(value)) {
|
|
153
|
+
this.encodeArray(value, chunks);
|
|
154
|
+
}
|
|
155
|
+
else if (typeof value === 'object') {
|
|
156
|
+
this.encodeObject(value, chunks);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
throw new Error(`Unsupported type: ${typeof value}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
encodeNumber(value, chunks) {
|
|
163
|
+
if (Number.isInteger(value)) {
|
|
164
|
+
if (value >= 0 && value <= 127) {
|
|
165
|
+
chunks.push(new Uint8Array([value])); // positive fixint
|
|
166
|
+
}
|
|
167
|
+
else if (value >= -32 && value < 0) {
|
|
168
|
+
chunks.push(new Uint8Array([0xE0 | (value & 0x1F)])); // negative fixint
|
|
169
|
+
}
|
|
170
|
+
else if (value >= 0 && value <= 0xFF) {
|
|
171
|
+
chunks.push(new Uint8Array([0xCC, value])); // uint8
|
|
172
|
+
}
|
|
173
|
+
else if (value >= 0 && value <= 0xFFFF) {
|
|
174
|
+
chunks.push(new Uint8Array([0xCD, value >> 8, value & 0xFF])); // uint16
|
|
175
|
+
}
|
|
176
|
+
else if (value >= 0 && value <= 0xFFFFFFFF) {
|
|
177
|
+
const bytes = new Uint8Array(5);
|
|
178
|
+
bytes[0] = 0xCE;
|
|
179
|
+
new DataView(bytes.buffer).setUint32(1, value, false);
|
|
180
|
+
chunks.push(bytes);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
// Use float64 for large integers or negative integers
|
|
184
|
+
const bytes = new Uint8Array(9);
|
|
185
|
+
bytes[0] = 0xCB;
|
|
186
|
+
new DataView(bytes.buffer).setFloat64(1, value, false);
|
|
187
|
+
chunks.push(bytes);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
// Float
|
|
192
|
+
const bytes = new Uint8Array(9);
|
|
193
|
+
bytes[0] = 0xCB; // float64
|
|
194
|
+
new DataView(bytes.buffer).setFloat64(1, value, false);
|
|
195
|
+
chunks.push(bytes);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
encodeString(value, chunks) {
|
|
199
|
+
const utf8 = new TextEncoder().encode(value);
|
|
200
|
+
const len = utf8.length;
|
|
201
|
+
if (len <= 31) {
|
|
202
|
+
chunks.push(new Uint8Array([0xA0 | len])); // fixstr
|
|
203
|
+
}
|
|
204
|
+
else if (len <= 0xFF) {
|
|
205
|
+
chunks.push(new Uint8Array([0xD9, len])); // str8
|
|
206
|
+
}
|
|
207
|
+
else if (len <= 0xFFFF) {
|
|
208
|
+
chunks.push(new Uint8Array([0xDA, len >> 8, len & 0xFF])); // str16
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
const header = new Uint8Array(5);
|
|
212
|
+
header[0] = 0xDB;
|
|
213
|
+
new DataView(header.buffer).setUint32(1, len, false);
|
|
214
|
+
chunks.push(header);
|
|
215
|
+
}
|
|
216
|
+
chunks.push(utf8);
|
|
217
|
+
}
|
|
218
|
+
encodeBinary(value, chunks) {
|
|
219
|
+
const len = value.length;
|
|
220
|
+
if (len <= 0xFF) {
|
|
221
|
+
chunks.push(new Uint8Array([0xC4, len])); // bin8
|
|
222
|
+
}
|
|
223
|
+
else if (len <= 0xFFFF) {
|
|
224
|
+
chunks.push(new Uint8Array([0xC5, len >> 8, len & 0xFF])); // bin16
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
const header = new Uint8Array(5);
|
|
228
|
+
header[0] = 0xC6;
|
|
229
|
+
new DataView(header.buffer).setUint32(1, len, false);
|
|
230
|
+
chunks.push(header);
|
|
231
|
+
}
|
|
232
|
+
chunks.push(value);
|
|
233
|
+
}
|
|
234
|
+
encodeArray(value, chunks) {
|
|
235
|
+
const len = value.length;
|
|
236
|
+
if (len <= 15) {
|
|
237
|
+
chunks.push(new Uint8Array([0x90 | len])); // fixarray
|
|
238
|
+
}
|
|
239
|
+
else if (len <= 0xFFFF) {
|
|
240
|
+
chunks.push(new Uint8Array([0xDC, len >> 8, len & 0xFF])); // array16
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
const header = new Uint8Array(5);
|
|
244
|
+
header[0] = 0xDD;
|
|
245
|
+
new DataView(header.buffer).setUint32(1, len, false);
|
|
246
|
+
chunks.push(header);
|
|
247
|
+
}
|
|
248
|
+
for (const item of value) {
|
|
249
|
+
this.encodeValue(item, chunks);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
encodeObject(value, chunks) {
|
|
253
|
+
const keys = Object.keys(value);
|
|
254
|
+
const len = keys.length;
|
|
255
|
+
if (len <= 15) {
|
|
256
|
+
chunks.push(new Uint8Array([0x80 | len])); // fixmap
|
|
257
|
+
}
|
|
258
|
+
else if (len <= 0xFFFF) {
|
|
259
|
+
chunks.push(new Uint8Array([0xDE, len >> 8, len & 0xFF])); // map16
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
const header = new Uint8Array(5);
|
|
263
|
+
header[0] = 0xDF;
|
|
264
|
+
new DataView(header.buffer).setUint32(1, len, false);
|
|
265
|
+
chunks.push(header);
|
|
266
|
+
}
|
|
267
|
+
for (const key of keys) {
|
|
268
|
+
this.encodeValue(key, chunks);
|
|
269
|
+
this.encodeValue(value[key], chunks);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Minimal MessagePack decoder (subset implementation)
|
|
273
|
+
decode(data) {
|
|
274
|
+
const state = { offset: 0 };
|
|
275
|
+
return this.decodeValue(data, state);
|
|
276
|
+
}
|
|
277
|
+
decodeValue(data, state) {
|
|
278
|
+
if (state.offset >= data.length) {
|
|
279
|
+
throw new Error('Unexpected end of data');
|
|
280
|
+
}
|
|
281
|
+
const byte = data[state.offset++];
|
|
282
|
+
if (byte === undefined) {
|
|
283
|
+
throw new Error('Unexpected end of data');
|
|
284
|
+
}
|
|
285
|
+
// Positive fixint (0x00 - 0x7F)
|
|
286
|
+
if (byte <= 0x7F)
|
|
287
|
+
return byte;
|
|
288
|
+
// Fixmap (0x80 - 0x8F)
|
|
289
|
+
if (byte >= 0x80 && byte <= 0x8F) {
|
|
290
|
+
const len = byte & 0x0F;
|
|
291
|
+
return this.decodeMap(data, state, len);
|
|
292
|
+
}
|
|
293
|
+
// Fixarray (0x90 - 0x9F)
|
|
294
|
+
if (byte >= 0x90 && byte <= 0x9F) {
|
|
295
|
+
const len = byte & 0x0F;
|
|
296
|
+
return this.decodeArray(data, state, len);
|
|
297
|
+
}
|
|
298
|
+
// Fixstr (0xA0 - 0xBF)
|
|
299
|
+
if (byte >= 0xA0 && byte <= 0xBF) {
|
|
300
|
+
const len = byte & 0x1F;
|
|
301
|
+
return this.decodeString(data, state, len);
|
|
302
|
+
}
|
|
303
|
+
// Negative fixint (0xE0 - 0xFF)
|
|
304
|
+
if (byte >= 0xE0)
|
|
305
|
+
return (byte & 0x1F) - 32;
|
|
306
|
+
// Other types
|
|
307
|
+
switch (byte) {
|
|
308
|
+
case 0xC0: return null; // nil
|
|
309
|
+
case 0xC2: return false;
|
|
310
|
+
case 0xC3: return true;
|
|
311
|
+
case 0xC4: { // bin8
|
|
312
|
+
const len = data[state.offset++];
|
|
313
|
+
if (len === undefined)
|
|
314
|
+
throw new Error('Unexpected end of data');
|
|
315
|
+
return this.decodeBinary(data, state, len);
|
|
316
|
+
}
|
|
317
|
+
case 0xC5: return this.decodeBinary(data, state, this.readUint16(data, state)); // bin16
|
|
318
|
+
case 0xC6: return this.decodeBinary(data, state, this.readUint32(data, state)); // bin32
|
|
319
|
+
case 0xCA: return this.readFloat32(data, state);
|
|
320
|
+
case 0xCB: return this.readFloat64(data, state);
|
|
321
|
+
case 0xCC: { // uint8
|
|
322
|
+
const value = data[state.offset++];
|
|
323
|
+
if (value === undefined)
|
|
324
|
+
throw new Error('Unexpected end of data');
|
|
325
|
+
return value;
|
|
326
|
+
}
|
|
327
|
+
case 0xCD: return this.readUint16(data, state); // uint16
|
|
328
|
+
case 0xCE: return this.readUint32(data, state); // uint32
|
|
329
|
+
case 0xD0: return this.readInt8(data, state); // int8
|
|
330
|
+
case 0xD1: return this.readInt16(data, state); // int16
|
|
331
|
+
case 0xD2: return this.readInt32(data, state); // int32
|
|
332
|
+
case 0xD9: { // str8
|
|
333
|
+
const len = data[state.offset++];
|
|
334
|
+
if (len === undefined)
|
|
335
|
+
throw new Error('Unexpected end of data');
|
|
336
|
+
return this.decodeString(data, state, len);
|
|
337
|
+
}
|
|
338
|
+
case 0xDA: return this.decodeString(data, state, this.readUint16(data, state)); // str16
|
|
339
|
+
case 0xDB: return this.decodeString(data, state, this.readUint32(data, state)); // str32
|
|
340
|
+
case 0xDC: return this.decodeArray(data, state, this.readUint16(data, state)); // array16
|
|
341
|
+
case 0xDD: return this.decodeArray(data, state, this.readUint32(data, state)); // array32
|
|
342
|
+
case 0xDE: return this.decodeMap(data, state, this.readUint16(data, state)); // map16
|
|
343
|
+
case 0xDF: return this.decodeMap(data, state, this.readUint32(data, state)); // map32
|
|
344
|
+
default:
|
|
345
|
+
throw new Error(`Unknown MessagePack type: 0x${byte.toString(16)}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
decodeString(data, state, len) {
|
|
349
|
+
const bytes = data.slice(state.offset, state.offset + len);
|
|
350
|
+
state.offset += len;
|
|
351
|
+
return new TextDecoder().decode(bytes);
|
|
352
|
+
}
|
|
353
|
+
decodeBinary(data, state, len) {
|
|
354
|
+
const bytes = data.slice(state.offset, state.offset + len);
|
|
355
|
+
state.offset += len;
|
|
356
|
+
return bytes;
|
|
357
|
+
}
|
|
358
|
+
decodeArray(data, state, len) {
|
|
359
|
+
const result = [];
|
|
360
|
+
for (let i = 0; i < len; i++) {
|
|
361
|
+
result.push(this.decodeValue(data, state));
|
|
362
|
+
}
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
365
|
+
decodeMap(data, state, len) {
|
|
366
|
+
const result = {};
|
|
367
|
+
for (let i = 0; i < len; i++) {
|
|
368
|
+
const key = this.decodeValue(data, state);
|
|
369
|
+
const value = this.decodeValue(data, state);
|
|
370
|
+
result[String(key)] = value;
|
|
371
|
+
}
|
|
372
|
+
return result;
|
|
373
|
+
}
|
|
374
|
+
readUint16(data, state) {
|
|
375
|
+
const value = new DataView(data.buffer, data.byteOffset).getUint16(state.offset, false);
|
|
376
|
+
state.offset += 2;
|
|
377
|
+
return value;
|
|
378
|
+
}
|
|
379
|
+
readUint32(data, state) {
|
|
380
|
+
const value = new DataView(data.buffer, data.byteOffset).getUint32(state.offset, false);
|
|
381
|
+
state.offset += 4;
|
|
382
|
+
return value;
|
|
383
|
+
}
|
|
384
|
+
readInt8(data, state) {
|
|
385
|
+
if (state.offset >= data.length)
|
|
386
|
+
throw new Error('Unexpected end of data');
|
|
387
|
+
const value = new DataView(data.buffer, data.byteOffset).getInt8(state.offset);
|
|
388
|
+
state.offset += 1;
|
|
389
|
+
return value;
|
|
390
|
+
}
|
|
391
|
+
readInt16(data, state) {
|
|
392
|
+
const value = new DataView(data.buffer, data.byteOffset).getInt16(state.offset, false);
|
|
393
|
+
state.offset += 2;
|
|
394
|
+
return value;
|
|
395
|
+
}
|
|
396
|
+
readInt32(data, state) {
|
|
397
|
+
const value = new DataView(data.buffer, data.byteOffset).getInt32(state.offset, false);
|
|
398
|
+
state.offset += 4;
|
|
399
|
+
return value;
|
|
400
|
+
}
|
|
401
|
+
readFloat32(data, state) {
|
|
402
|
+
const value = new DataView(data.buffer, data.byteOffset).getFloat32(state.offset, false);
|
|
403
|
+
state.offset += 4;
|
|
404
|
+
return value;
|
|
405
|
+
}
|
|
406
|
+
readFloat64(data, state) {
|
|
407
|
+
const value = new DataView(data.buffer, data.byteOffset).getFloat64(state.offset, false);
|
|
408
|
+
state.offset += 8;
|
|
409
|
+
return value;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/* ── CBOR Serializer ── */
|
|
413
|
+
class CBORSerializer {
|
|
414
|
+
format = 'cbor';
|
|
415
|
+
contentType = 'application/cbor';
|
|
416
|
+
serialize(value, _options) {
|
|
417
|
+
try {
|
|
418
|
+
return ok(this.encode(value));
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
return err({
|
|
422
|
+
code: 'SERIALIZATION_FAILED',
|
|
423
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
424
|
+
format: 'cbor',
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
deserialize(data) {
|
|
429
|
+
try {
|
|
430
|
+
return ok(this.decode(data));
|
|
431
|
+
}
|
|
432
|
+
catch (error) {
|
|
433
|
+
return err({
|
|
434
|
+
code: 'DESERIALIZATION_FAILED',
|
|
435
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
detect(data) {
|
|
440
|
+
if (data.length === 0)
|
|
441
|
+
return false;
|
|
442
|
+
const first = data[0];
|
|
443
|
+
// CBOR major types:
|
|
444
|
+
// 0: unsigned integer (0x00-0x1B)
|
|
445
|
+
// 1: negative integer (0x20-0x3B)
|
|
446
|
+
// 2: byte string (0x40-0x5B)
|
|
447
|
+
// 3: text string (0x60-0x7B)
|
|
448
|
+
// 4: array (0x80-0x9B)
|
|
449
|
+
// 5: map (0xA0-0xBB)
|
|
450
|
+
// 6: tag (0xC0-0xDB)
|
|
451
|
+
// 7: float/special (0xE0-0xFB, 0xF4-0xF7 for false/true/null/undefined)
|
|
452
|
+
// Try to validate structure
|
|
453
|
+
try {
|
|
454
|
+
const state = { offset: 0 };
|
|
455
|
+
this.decodeValue(data, state);
|
|
456
|
+
return true; // Successfully decoded
|
|
457
|
+
}
|
|
458
|
+
catch {
|
|
459
|
+
return false; // Decode failed
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
// Minimal CBOR encoder (subset implementation)
|
|
463
|
+
encode(value) {
|
|
464
|
+
const chunks = [];
|
|
465
|
+
this.encodeValue(value, chunks);
|
|
466
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
467
|
+
const result = new Uint8Array(totalLength);
|
|
468
|
+
let offset = 0;
|
|
469
|
+
for (const chunk of chunks) {
|
|
470
|
+
result.set(chunk, offset);
|
|
471
|
+
offset += chunk.length;
|
|
472
|
+
}
|
|
473
|
+
return result;
|
|
474
|
+
}
|
|
475
|
+
encodeValue(value, chunks) {
|
|
476
|
+
if (value === null || value === undefined) {
|
|
477
|
+
chunks.push(new Uint8Array([0xF6])); // null
|
|
478
|
+
}
|
|
479
|
+
else if (typeof value === 'boolean') {
|
|
480
|
+
chunks.push(new Uint8Array([value ? 0xF5 : 0xF4])); // true/false
|
|
481
|
+
}
|
|
482
|
+
else if (typeof value === 'number') {
|
|
483
|
+
this.encodeNumber(value, chunks);
|
|
484
|
+
}
|
|
485
|
+
else if (typeof value === 'string') {
|
|
486
|
+
this.encodeString(value, chunks);
|
|
487
|
+
}
|
|
488
|
+
else if (value instanceof Uint8Array) {
|
|
489
|
+
this.encodeBinary(value, chunks);
|
|
490
|
+
}
|
|
491
|
+
else if (Array.isArray(value)) {
|
|
492
|
+
this.encodeArray(value, chunks);
|
|
493
|
+
}
|
|
494
|
+
else if (typeof value === 'object') {
|
|
495
|
+
this.encodeObject(value, chunks);
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
throw new Error(`Unsupported type: ${typeof value}`);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
encodeNumber(value, chunks) {
|
|
502
|
+
if (Number.isInteger(value)) {
|
|
503
|
+
if (value >= 0) {
|
|
504
|
+
if (value <= 23) {
|
|
505
|
+
chunks.push(new Uint8Array([value])); // 0-23 direct
|
|
506
|
+
}
|
|
507
|
+
else if (value <= 0xFF) {
|
|
508
|
+
chunks.push(new Uint8Array([0x18, value])); // uint8
|
|
509
|
+
}
|
|
510
|
+
else if (value <= 0xFFFF) {
|
|
511
|
+
chunks.push(new Uint8Array([0x19, value >> 8, value & 0xFF])); // uint16
|
|
512
|
+
}
|
|
513
|
+
else if (value <= 0xFFFFFFFF) {
|
|
514
|
+
const bytes = new Uint8Array(5);
|
|
515
|
+
bytes[0] = 0x1A;
|
|
516
|
+
new DataView(bytes.buffer).setUint32(1, value, false);
|
|
517
|
+
chunks.push(bytes);
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
// Large integer as float64
|
|
521
|
+
const bytes = new Uint8Array(9);
|
|
522
|
+
bytes[0] = 0xFB;
|
|
523
|
+
new DataView(bytes.buffer).setFloat64(1, value, false);
|
|
524
|
+
chunks.push(bytes);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
else {
|
|
528
|
+
// Negative integer
|
|
529
|
+
const absMinusOne = -1 - value;
|
|
530
|
+
if (absMinusOne <= 23) {
|
|
531
|
+
chunks.push(new Uint8Array([0x20 | absMinusOne]));
|
|
532
|
+
}
|
|
533
|
+
else if (absMinusOne <= 0xFF) {
|
|
534
|
+
chunks.push(new Uint8Array([0x38, absMinusOne]));
|
|
535
|
+
}
|
|
536
|
+
else if (absMinusOne <= 0xFFFF) {
|
|
537
|
+
chunks.push(new Uint8Array([0x39, absMinusOne >> 8, absMinusOne & 0xFF]));
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
const bytes = new Uint8Array(5);
|
|
541
|
+
bytes[0] = 0x3A;
|
|
542
|
+
new DataView(bytes.buffer).setUint32(1, absMinusOne, false);
|
|
543
|
+
chunks.push(bytes);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
// Float
|
|
549
|
+
const bytes = new Uint8Array(9);
|
|
550
|
+
bytes[0] = 0xFB; // float64
|
|
551
|
+
new DataView(bytes.buffer).setFloat64(1, value, false);
|
|
552
|
+
chunks.push(bytes);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
encodeString(value, chunks) {
|
|
556
|
+
const utf8 = new TextEncoder().encode(value);
|
|
557
|
+
const len = utf8.length;
|
|
558
|
+
if (len <= 23) {
|
|
559
|
+
chunks.push(new Uint8Array([0x60 | len])); // text string 0-23
|
|
560
|
+
}
|
|
561
|
+
else if (len <= 0xFF) {
|
|
562
|
+
chunks.push(new Uint8Array([0x78, len])); // text string uint8
|
|
563
|
+
}
|
|
564
|
+
else if (len <= 0xFFFF) {
|
|
565
|
+
chunks.push(new Uint8Array([0x79, len >> 8, len & 0xFF])); // text string uint16
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
const header = new Uint8Array(5);
|
|
569
|
+
header[0] = 0x7A;
|
|
570
|
+
new DataView(header.buffer).setUint32(1, len, false);
|
|
571
|
+
chunks.push(header);
|
|
572
|
+
}
|
|
573
|
+
chunks.push(utf8);
|
|
574
|
+
}
|
|
575
|
+
encodeBinary(value, chunks) {
|
|
576
|
+
const len = value.length;
|
|
577
|
+
if (len <= 23) {
|
|
578
|
+
chunks.push(new Uint8Array([0x40 | len])); // byte string 0-23
|
|
579
|
+
}
|
|
580
|
+
else if (len <= 0xFF) {
|
|
581
|
+
chunks.push(new Uint8Array([0x58, len])); // byte string uint8
|
|
582
|
+
}
|
|
583
|
+
else if (len <= 0xFFFF) {
|
|
584
|
+
chunks.push(new Uint8Array([0x59, len >> 8, len & 0xFF])); // byte string uint16
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
const header = new Uint8Array(5);
|
|
588
|
+
header[0] = 0x5A;
|
|
589
|
+
new DataView(header.buffer).setUint32(1, len, false);
|
|
590
|
+
chunks.push(header);
|
|
591
|
+
}
|
|
592
|
+
chunks.push(value);
|
|
593
|
+
}
|
|
594
|
+
encodeArray(value, chunks) {
|
|
595
|
+
const len = value.length;
|
|
596
|
+
if (len <= 23) {
|
|
597
|
+
chunks.push(new Uint8Array([0x80 | len])); // array 0-23
|
|
598
|
+
}
|
|
599
|
+
else if (len <= 0xFF) {
|
|
600
|
+
chunks.push(new Uint8Array([0x98, len])); // array uint8
|
|
601
|
+
}
|
|
602
|
+
else if (len <= 0xFFFF) {
|
|
603
|
+
chunks.push(new Uint8Array([0x99, len >> 8, len & 0xFF])); // array uint16
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
const header = new Uint8Array(5);
|
|
607
|
+
header[0] = 0x9A;
|
|
608
|
+
new DataView(header.buffer).setUint32(1, len, false);
|
|
609
|
+
chunks.push(header);
|
|
610
|
+
}
|
|
611
|
+
for (const item of value) {
|
|
612
|
+
this.encodeValue(item, chunks);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
encodeObject(value, chunks) {
|
|
616
|
+
const keys = Object.keys(value);
|
|
617
|
+
const len = keys.length;
|
|
618
|
+
if (len <= 23) {
|
|
619
|
+
chunks.push(new Uint8Array([0xA0 | len])); // map 0-23
|
|
620
|
+
}
|
|
621
|
+
else if (len <= 0xFF) {
|
|
622
|
+
chunks.push(new Uint8Array([0xB8, len])); // map uint8
|
|
623
|
+
}
|
|
624
|
+
else if (len <= 0xFFFF) {
|
|
625
|
+
chunks.push(new Uint8Array([0xB9, len >> 8, len & 0xFF])); // map uint16
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
const header = new Uint8Array(5);
|
|
629
|
+
header[0] = 0xBA;
|
|
630
|
+
new DataView(header.buffer).setUint32(1, len, false);
|
|
631
|
+
chunks.push(header);
|
|
632
|
+
}
|
|
633
|
+
for (const key of keys) {
|
|
634
|
+
this.encodeValue(key, chunks);
|
|
635
|
+
this.encodeValue(value[key], chunks);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
// Minimal CBOR decoder
|
|
639
|
+
decode(data) {
|
|
640
|
+
const state = { offset: 0 };
|
|
641
|
+
return this.decodeValue(data, state);
|
|
642
|
+
}
|
|
643
|
+
decodeValue(data, state) {
|
|
644
|
+
if (state.offset >= data.length) {
|
|
645
|
+
throw new Error('Unexpected end of data');
|
|
646
|
+
}
|
|
647
|
+
const byte = data[state.offset++];
|
|
648
|
+
if (byte === undefined) {
|
|
649
|
+
throw new Error('Unexpected end of data');
|
|
650
|
+
}
|
|
651
|
+
const majorType = (byte >> 5) & 0x07;
|
|
652
|
+
const additionalInfo = byte & 0x1F;
|
|
653
|
+
switch (majorType) {
|
|
654
|
+
case 0: // unsigned integer
|
|
655
|
+
return this.decodeInteger(data, state, additionalInfo);
|
|
656
|
+
case 1: // negative integer
|
|
657
|
+
return -1 - this.decodeInteger(data, state, additionalInfo);
|
|
658
|
+
case 2: // byte string
|
|
659
|
+
return this.decodeBinary(data, state, additionalInfo);
|
|
660
|
+
case 3: // text string
|
|
661
|
+
return this.decodeString(data, state, additionalInfo);
|
|
662
|
+
case 4: // array
|
|
663
|
+
return this.decodeArray(data, state, additionalInfo);
|
|
664
|
+
case 5: // map
|
|
665
|
+
return this.decodeMap(data, state, additionalInfo);
|
|
666
|
+
case 7: // float/special
|
|
667
|
+
return this.decodeSpecial(data, state, additionalInfo);
|
|
668
|
+
default:
|
|
669
|
+
throw new Error(`Unsupported CBOR major type: ${majorType}`);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
decodeInteger(data, state, additionalInfo) {
|
|
673
|
+
if (additionalInfo <= 23) {
|
|
674
|
+
return additionalInfo;
|
|
675
|
+
}
|
|
676
|
+
else if (additionalInfo === 24) {
|
|
677
|
+
const value = data[state.offset++];
|
|
678
|
+
if (value === undefined)
|
|
679
|
+
throw new Error('Unexpected end of data');
|
|
680
|
+
return value;
|
|
681
|
+
}
|
|
682
|
+
else if (additionalInfo === 25) {
|
|
683
|
+
return this.readUint16(data, state);
|
|
684
|
+
}
|
|
685
|
+
else if (additionalInfo === 26) {
|
|
686
|
+
return this.readUint32(data, state);
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
throw new Error('Unsupported integer size');
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
decodeString(data, state, additionalInfo) {
|
|
693
|
+
let len;
|
|
694
|
+
if (additionalInfo <= 23) {
|
|
695
|
+
len = additionalInfo;
|
|
696
|
+
}
|
|
697
|
+
else {
|
|
698
|
+
len = this.decodeInteger(data, state, additionalInfo);
|
|
699
|
+
}
|
|
700
|
+
const bytes = data.slice(state.offset, state.offset + len);
|
|
701
|
+
state.offset += len;
|
|
702
|
+
return new TextDecoder().decode(bytes);
|
|
703
|
+
}
|
|
704
|
+
decodeBinary(data, state, additionalInfo) {
|
|
705
|
+
let len;
|
|
706
|
+
if (additionalInfo <= 23) {
|
|
707
|
+
len = additionalInfo;
|
|
708
|
+
}
|
|
709
|
+
else {
|
|
710
|
+
len = this.decodeInteger(data, state, additionalInfo);
|
|
711
|
+
}
|
|
712
|
+
const bytes = data.slice(state.offset, state.offset + len);
|
|
713
|
+
state.offset += len;
|
|
714
|
+
return bytes;
|
|
715
|
+
}
|
|
716
|
+
decodeArray(data, state, additionalInfo) {
|
|
717
|
+
let len;
|
|
718
|
+
if (additionalInfo <= 23) {
|
|
719
|
+
len = additionalInfo;
|
|
720
|
+
}
|
|
721
|
+
else {
|
|
722
|
+
len = this.decodeInteger(data, state, additionalInfo);
|
|
723
|
+
}
|
|
724
|
+
const result = [];
|
|
725
|
+
for (let i = 0; i < len; i++) {
|
|
726
|
+
result.push(this.decodeValue(data, state));
|
|
727
|
+
}
|
|
728
|
+
return result;
|
|
729
|
+
}
|
|
730
|
+
decodeMap(data, state, additionalInfo) {
|
|
731
|
+
let len;
|
|
732
|
+
if (additionalInfo <= 23) {
|
|
733
|
+
len = additionalInfo;
|
|
734
|
+
}
|
|
735
|
+
else {
|
|
736
|
+
len = this.decodeInteger(data, state, additionalInfo);
|
|
737
|
+
}
|
|
738
|
+
const result = {};
|
|
739
|
+
for (let i = 0; i < len; i++) {
|
|
740
|
+
const key = this.decodeValue(data, state);
|
|
741
|
+
const value = this.decodeValue(data, state);
|
|
742
|
+
result[String(key)] = value;
|
|
743
|
+
}
|
|
744
|
+
return result;
|
|
745
|
+
}
|
|
746
|
+
decodeSpecial(data, state, additionalInfo) {
|
|
747
|
+
switch (additionalInfo) {
|
|
748
|
+
case 20: return false;
|
|
749
|
+
case 21: return true;
|
|
750
|
+
case 22: return null;
|
|
751
|
+
case 23: return undefined;
|
|
752
|
+
case 27: { // float64
|
|
753
|
+
return this.readFloat64(data, state);
|
|
754
|
+
}
|
|
755
|
+
default:
|
|
756
|
+
throw new Error(`Unsupported CBOR special: ${additionalInfo}`);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
readUint16(data, state) {
|
|
760
|
+
const value = new DataView(data.buffer, data.byteOffset).getUint16(state.offset, false);
|
|
761
|
+
state.offset += 2;
|
|
762
|
+
return value;
|
|
763
|
+
}
|
|
764
|
+
readUint32(data, state) {
|
|
765
|
+
const value = new DataView(data.buffer, data.byteOffset).getUint32(state.offset, false);
|
|
766
|
+
state.offset += 4;
|
|
767
|
+
return value;
|
|
768
|
+
}
|
|
769
|
+
readFloat64(data, state) {
|
|
770
|
+
const value = new DataView(data.buffer, data.byteOffset).getFloat64(state.offset, false);
|
|
771
|
+
state.offset += 8;
|
|
772
|
+
return value;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
/* ── Serializer Registry ── */
|
|
776
|
+
// Order matters for format detection: JSON (most specific), CBOR, MessagePack
|
|
777
|
+
const serializers = new Map([
|
|
778
|
+
['json', new JSONSerializer()],
|
|
779
|
+
['cbor', new CBORSerializer()],
|
|
780
|
+
['msgpack', new MessagePackSerializer()],
|
|
781
|
+
]);
|
|
782
|
+
/* ── Public API ── */
|
|
783
|
+
/**
|
|
784
|
+
* Serialize a value using the specified format.
|
|
785
|
+
*
|
|
786
|
+
* @param value - Value to serialize
|
|
787
|
+
* @param options - Serialization options
|
|
788
|
+
* @returns Serialized data with metadata
|
|
789
|
+
*
|
|
790
|
+
* @example
|
|
791
|
+
* ```typescript
|
|
792
|
+
* const data = { message: 'Hello', timestamp: Date.now() };
|
|
793
|
+
*
|
|
794
|
+
* // JSON (default, human-readable)
|
|
795
|
+
* const json = serialize(data);
|
|
796
|
+
*
|
|
797
|
+
* // MessagePack (compact binary)
|
|
798
|
+
* const msgpack = serialize(data, { format: 'msgpack' });
|
|
799
|
+
*
|
|
800
|
+
* // CBOR (standard binary)
|
|
801
|
+
* const cbor = serialize(data, { format: 'cbor' });
|
|
802
|
+
*
|
|
803
|
+
* console.log('JSON:', json.value.size, 'bytes');
|
|
804
|
+
* console.log('MessagePack:', msgpack.value.size, 'bytes');
|
|
805
|
+
* console.log('CBOR:', cbor.value.size, 'bytes');
|
|
806
|
+
* ```
|
|
807
|
+
*/
|
|
808
|
+
export function serialize(value, options = {}) {
|
|
809
|
+
const format = options.format ?? 'json';
|
|
810
|
+
const serializer = serializers.get(format);
|
|
811
|
+
if (!serializer) {
|
|
812
|
+
return err({ code: 'UNSUPPORTED_FORMAT', format });
|
|
813
|
+
}
|
|
814
|
+
const startTime = options.collectMetrics ? performance.now() : 0;
|
|
815
|
+
const result = serializer.serialize(value, options);
|
|
816
|
+
if (!result.ok)
|
|
817
|
+
return result;
|
|
818
|
+
const metrics = options.collectMetrics ? {
|
|
819
|
+
serializeDuration: performance.now() - startTime,
|
|
820
|
+
size: result.value.length,
|
|
821
|
+
format,
|
|
822
|
+
timestamp: Date.now(),
|
|
823
|
+
} : undefined;
|
|
824
|
+
return ok({
|
|
825
|
+
data: result.value,
|
|
826
|
+
format,
|
|
827
|
+
size: result.value.length,
|
|
828
|
+
contentType: serializer.contentType,
|
|
829
|
+
metrics,
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Deserialize bytes using format auto-detection or explicit format.
|
|
834
|
+
*
|
|
835
|
+
* @param data - Serialized bytes
|
|
836
|
+
* @param options - Deserialization options
|
|
837
|
+
* @returns Deserialized value with format metadata
|
|
838
|
+
*
|
|
839
|
+
* @example
|
|
840
|
+
* ```typescript
|
|
841
|
+
* // Auto-detect format
|
|
842
|
+
* const result = deserialize(bytes);
|
|
843
|
+
* if (result.ok) {
|
|
844
|
+
* console.log('Format:', result.value.format);
|
|
845
|
+
* console.log('Data:', result.value.value);
|
|
846
|
+
* }
|
|
847
|
+
*
|
|
848
|
+
* // Explicit format (faster, skips detection)
|
|
849
|
+
* const json = deserialize(bytes, { format: 'json' });
|
|
850
|
+
* ```
|
|
851
|
+
*/
|
|
852
|
+
export function deserialize(data, options = {}) {
|
|
853
|
+
const startTime = options.collectMetrics ? performance.now() : 0;
|
|
854
|
+
let format;
|
|
855
|
+
let serializer;
|
|
856
|
+
if (options.format) {
|
|
857
|
+
// Explicit format
|
|
858
|
+
format = options.format;
|
|
859
|
+
serializer = serializers.get(format);
|
|
860
|
+
if (!serializer) {
|
|
861
|
+
return err({ code: 'UNSUPPORTED_FORMAT', format });
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
else {
|
|
865
|
+
// Auto-detect format
|
|
866
|
+
const detected = detectFormat(data);
|
|
867
|
+
if (!detected.ok)
|
|
868
|
+
return detected;
|
|
869
|
+
format = detected.value;
|
|
870
|
+
serializer = serializers.get(format);
|
|
871
|
+
if (!serializer) {
|
|
872
|
+
return err({ code: 'UNSUPPORTED_FORMAT', format });
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
const result = serializer.deserialize(data);
|
|
876
|
+
if (!result.ok)
|
|
877
|
+
return result;
|
|
878
|
+
const metrics = options.collectMetrics ? {
|
|
879
|
+
serializeDuration: performance.now() - startTime,
|
|
880
|
+
size: data.length,
|
|
881
|
+
format,
|
|
882
|
+
timestamp: Date.now(),
|
|
883
|
+
} : undefined;
|
|
884
|
+
return ok({
|
|
885
|
+
value: result.value,
|
|
886
|
+
format,
|
|
887
|
+
metrics,
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Detect serialization format from bytes.
|
|
892
|
+
*
|
|
893
|
+
* @param data - Serialized bytes
|
|
894
|
+
* @returns Detected format
|
|
895
|
+
*
|
|
896
|
+
* @example
|
|
897
|
+
* ```typescript
|
|
898
|
+
* const format = detectFormat(bytes);
|
|
899
|
+
* if (format.ok) {
|
|
900
|
+
* console.log('Detected format:', format.value); // 'json' | 'msgpack' | 'cbor'
|
|
901
|
+
* }
|
|
902
|
+
* ```
|
|
903
|
+
*/
|
|
904
|
+
export function detectFormat(data) {
|
|
905
|
+
// Try JSON first (most specific - must start with { [ " or whitespace + those)
|
|
906
|
+
const jsonSerializer = serializers.get('json');
|
|
907
|
+
if (jsonSerializer?.detect(data)) {
|
|
908
|
+
return ok('json');
|
|
909
|
+
}
|
|
910
|
+
if (data.length === 0) {
|
|
911
|
+
return err({
|
|
912
|
+
code: 'FORMAT_DETECTION_FAILED',
|
|
913
|
+
reason: 'Empty data',
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
// CBOR and MessagePack have overlapping byte ranges
|
|
917
|
+
// Both formats can successfully decode data but with different interpretations
|
|
918
|
+
// Priority: Try CBOR first, then MessagePack
|
|
919
|
+
// This matches the serializers Map insertion order
|
|
920
|
+
const cborSerializer = serializers.get('cbor');
|
|
921
|
+
if (cborSerializer?.detect(data)) {
|
|
922
|
+
return ok('cbor');
|
|
923
|
+
}
|
|
924
|
+
const msgpackSerializer = serializers.get('msgpack');
|
|
925
|
+
if (msgpackSerializer?.detect(data)) {
|
|
926
|
+
return ok('msgpack');
|
|
927
|
+
}
|
|
928
|
+
return err({
|
|
929
|
+
code: 'FORMAT_DETECTION_FAILED',
|
|
930
|
+
reason: 'No serializer recognized the data format',
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* Negotiate format between client and server.
|
|
935
|
+
*
|
|
936
|
+
* @param clientFormats - Formats accepted by client (in preference order)
|
|
937
|
+
* @param serverFormats - Formats supported by server
|
|
938
|
+
* @returns Best matching format
|
|
939
|
+
*
|
|
940
|
+
* @example
|
|
941
|
+
* ```typescript
|
|
942
|
+
* const client = ['msgpack', 'cbor', 'json'];
|
|
943
|
+
* const server = ['json', 'cbor'];
|
|
944
|
+
* const format = negotiateFormat(client, server);
|
|
945
|
+
* console.log(format.value); // 'cbor' (first match)
|
|
946
|
+
* ```
|
|
947
|
+
*/
|
|
948
|
+
export function negotiateFormat(clientFormats, serverFormats) {
|
|
949
|
+
for (const format of clientFormats) {
|
|
950
|
+
if (serverFormats.includes(format)) {
|
|
951
|
+
return ok(format);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return err({
|
|
955
|
+
code: 'UNSUPPORTED_FORMAT',
|
|
956
|
+
format: `No common format (client: ${clientFormats.join(',')}, server: ${serverFormats.join(',')})`,
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Compare serialization performance across formats.
|
|
961
|
+
*
|
|
962
|
+
* @param value - Value to benchmark
|
|
963
|
+
* @param formats - Formats to compare
|
|
964
|
+
* @returns Performance comparison
|
|
965
|
+
*
|
|
966
|
+
* @example
|
|
967
|
+
* ```typescript
|
|
968
|
+
* const data = { message: 'Hello', items: [1, 2, 3, 4, 5] };
|
|
969
|
+
* const comparison = compareFormats(data);
|
|
970
|
+
*
|
|
971
|
+
* if (comparison.ok) {
|
|
972
|
+
* for (const result of comparison.value) {
|
|
973
|
+
* console.log(`${result.format}: ${result.size} bytes, ${result.duration}ms`);
|
|
974
|
+
* }
|
|
975
|
+
* }
|
|
976
|
+
* ```
|
|
977
|
+
*/
|
|
978
|
+
export function compareFormats(value, formats = ['json', 'msgpack', 'cbor']) {
|
|
979
|
+
const results = [];
|
|
980
|
+
for (const format of formats) {
|
|
981
|
+
const result = serialize(value, { format, collectMetrics: true });
|
|
982
|
+
if (!result.ok)
|
|
983
|
+
return result;
|
|
984
|
+
results.push({
|
|
985
|
+
format,
|
|
986
|
+
size: result.value.size,
|
|
987
|
+
duration: result.value.metrics?.serializeDuration ?? 0,
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
return ok(results);
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Get Content-Type header for a serialization format.
|
|
994
|
+
*
|
|
995
|
+
* @param format - Serialization format
|
|
996
|
+
* @returns Content-Type header value
|
|
997
|
+
*
|
|
998
|
+
* @example
|
|
999
|
+
* ```typescript
|
|
1000
|
+
* const contentType = getContentType('msgpack');
|
|
1001
|
+
* console.log(contentType); // 'application/msgpack'
|
|
1002
|
+
* ```
|
|
1003
|
+
*/
|
|
1004
|
+
export function getContentType(format) {
|
|
1005
|
+
const serializer = serializers.get(format);
|
|
1006
|
+
return serializer?.contentType ?? 'application/octet-stream';
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Parse Content-Type header to serialization format.
|
|
1010
|
+
*
|
|
1011
|
+
* @param contentType - Content-Type header value
|
|
1012
|
+
* @returns Serialization format or undefined
|
|
1013
|
+
*
|
|
1014
|
+
* @example
|
|
1015
|
+
* ```typescript
|
|
1016
|
+
* const format = parseContentType('application/json');
|
|
1017
|
+
* console.log(format); // 'json'
|
|
1018
|
+
*
|
|
1019
|
+
* const format2 = parseContentType('application/msgpack');
|
|
1020
|
+
* console.log(format2); // 'msgpack'
|
|
1021
|
+
* ```
|
|
1022
|
+
*/
|
|
1023
|
+
export function parseContentType(contentType) {
|
|
1024
|
+
const normalized = contentType.toLowerCase().split(';')[0]?.trim() ?? '';
|
|
1025
|
+
for (const serializer of serializers.values()) {
|
|
1026
|
+
if (serializer.contentType === normalized) {
|
|
1027
|
+
return serializer.format;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
return undefined;
|
|
1031
|
+
}
|