diff --git a/package.json b/package.json index bf1bd324..b197577c 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "test:cover:wasm": "npx nyc --no-clean npm run test:wasm", "test:cover:td": "npx nyc --no-clean npm run test:td", "cover:clean": "rimraf .nyc_output coverage/", - "cover:report": "nyc report --reporter=text-summary --reporter=html --reporter=json", + "cover:report": "npx nyc report --reporter=text-summary --reporter=html --reporter=json", "test:browser": "karma start --single-run", "test:browser:firefox": "karma start --single-run --browsers FirefoxHeadless", "test:browser:chrome": "karma start --single-run --browsers ChromeHeadless", diff --git a/src/ExtensionCodec.ts b/src/ExtensionCodec.ts index 3f12cb3b..ece0e9c2 100644 --- a/src/ExtensionCodec.ts +++ b/src/ExtensionCodec.ts @@ -50,25 +50,25 @@ export class ExtensionCodec implements ExtensionCodecType { } public tryToEncode(object: unknown): ExtData | null { - // built-in extensions - for (let i = 0; i < this.builtInEncoders.length; i++) { - const encoder = this.builtInEncoders[i]; + // custom extensions + for (let i = 0; i < this.encoders.length; i++) { + const encoder = this.encoders[i]; if (encoder != null) { const data = encoder(object); if (data != null) { - const type = -1 - i; + const type = i; return new ExtData(type, data); } } } - // custom extensions - for (let i = 0; i < this.encoders.length; i++) { - const encoder = this.encoders[i]; + // built-in extensions + for (let i = 0; i < this.builtInEncoders.length; i++) { + const encoder = this.builtInEncoders[i]; if (encoder != null) { const data = encoder(object); if (data != null) { - const type = i; + const type = -1 - i; return new ExtData(type, data); } } diff --git a/src/JavaScriptCodec.ts b/src/JavaScriptCodec.ts new file mode 100644 index 00000000..04fbf5d1 --- /dev/null +++ b/src/JavaScriptCodec.ts @@ -0,0 +1,69 @@ +import { ExtensionCodec, ExtensionCodecType } from "./ExtensionCodec"; +import { encode } from "./encode"; +import { decode } from "./decode"; + +export const EXT_JAVASCRIPT = 0; + +const enum JSData { + Map, + Set, + Date, + RegExp, + BigInt, +} + +export function encodeJavaScriptData(input: unknown): Uint8Array | null { + if (input instanceof Map) { + return encode([JSData.Map, [...input]]); + } else if (input instanceof Set) { + return encode([JSData.Set, [...input]]); + } else if (input instanceof Date) { + // Not a MessagePack timestamp because + // it may be overrided by users + return encode([JSData.Date, input.getTime()]); + } else if (input instanceof RegExp) { + return encode([JSData.RegExp, [input.source, input.flags]]); + } else if (typeof input === "bigint") { + return encode([JSData.BigInt, input.toString()]); + } else { + return null; + } +} + +export function decodeJavaScriptData(data: Uint8Array) { + const [jsDataType, source] = decode(data) as [JSData, any]; + + switch (jsDataType) { + case JSData.Map: { + return new Map(source); + } + case JSData.Set: { + return new Set(source); + } + case JSData.Date: { + return new Date(source); + } + case JSData.RegExp: { + const [pattern, flags] = source; + return new RegExp(pattern, flags); + } + case JSData.BigInt: { + return BigInt(source); + } + default: { + throw new Error(`Unknown data type: ${jsDataType}`); + } + } +} + +export const JavaScriptCodec: ExtensionCodecType = (() => { + const ext = new ExtensionCodec(); + + ext.register({ + type: EXT_JAVASCRIPT, + encode: encodeJavaScriptData, + decode: decodeJavaScriptData, + }); + + return ext; +})(); diff --git a/src/index.ts b/src/index.ts index 50b50813..b1eb94a0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,4 +20,6 @@ export { decodeTimestampExtension, } from "./timestamp"; +export { JavaScriptCodec, EXT_JAVASCRIPT, encodeJavaScriptData, decodeJavaScriptData } from "./JavaScriptCodec"; + export { WASM_AVAILABLE as __WASM_AVAILABLE } from "./wasmFunctions"; diff --git a/test/javascript-codec.test.ts b/test/javascript-codec.test.ts new file mode 100644 index 00000000..2769ec80 --- /dev/null +++ b/test/javascript-codec.test.ts @@ -0,0 +1,28 @@ +import assert from "assert"; +import { encode, decode, JavaScriptCodec } from "@msgpack/msgpack"; + +describe("JavaScriptCodec", () => { + context("mixed", () => { + // this data comes from https://github.com/yahoo/serialize-javascript + + it("encodes and decodes the object", () => { + const object = { + str: "string", + num: 0, + obj: { foo: "foo", bar: "bar" }, + arr: [1, 2, 3], + bool: true, + nil: null, + // undef: undefined, // not supported + date: new Date("Thu, 28 Apr 2016 22:02:17 GMT"), + map: new Map([["foo", 10], ["bar", 20]]), + set: new Set([123, 456]), + regexp: /foo\n/i, + bigint: typeof(BigInt) !== "undefined" ? BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1) : null, + }; + const encoded = encode(object, { extensionCodec: JavaScriptCodec }); + + assert.deepStrictEqual(decode(encoded, { extensionCodec: JavaScriptCodec }), object); + }); + }); +}); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy