diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6d3aaf4..939a092 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,6 +47,18 @@ jobs: - run: npm ci - run: npm run test:browser -- --browsers ${{ matrix.browser }} + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + cache: npm + node-version: '22' + - run: npm ci + - run: npx tsc + - run: npm run lint deno: runs-on: ubuntu-latest @@ -68,3 +80,16 @@ jobs: - run: bun install - run: npm run test:bun + timeline: + runs-on: ubuntu-latest + permissions: + actions: read + needs: + - nodejs + - browser + - lint + - deno + - bun + steps: + - uses: Kesin11/actions-timeline@v2 + diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ecd37f..c38d2ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # This is the revision history of @msgpack/msgpack + +## 3.1.0 2025-02-21 + +https://github.com/msgpack/msgpack-javascript/compare/v3.0.1...v3.1.0 + +* Added support for nonstandard map keys in the decoder ([#266](https://github.com/msgpack/msgpack-javascript/pull/266) by @PejmanNik) + +## 3.0.1 2025-02-11 + +https://github.com/msgpack/msgpack-javascript/compare/v3.0.0...v3.0.1 + +* Implement a tiny polyfill to Symbol.dispose ([#261](https://github.com/msgpack/msgpack-javascript/pull/261) to fix #260) + + ## 3.0.0 2025-02-07 https://github.com/msgpack/msgpack-javascript/compare/v2.8.0...v3.0.0 diff --git a/README.md b/README.md index 6ba5681..f7c36cc 100644 --- a/README.md +++ b/README.md @@ -145,17 +145,20 @@ NodeJS `Buffer` is also acceptable because it is a subclass of `Uint8Array`. #### `DecoderOptions` -| Name | Type | Default | -| -------------- | -------------- | ----------------------------- | -| extensionCodec | ExtensionCodec | `ExtensionCodec.defaultCodec` | -| context | user-defined | - | -| useBigInt64 | boolean | false | -| rawStrings | boolean | false | -| maxStrLength | number | `4_294_967_295` (UINT32_MAX) | -| maxBinLength | number | `4_294_967_295` (UINT32_MAX) | -| maxArrayLength | number | `4_294_967_295` (UINT32_MAX) | -| maxMapLength | number | `4_294_967_295` (UINT32_MAX) | -| maxExtLength | number | `4_294_967_295` (UINT32_MAX) | +| Name | Type | Default | +| --------------- | ------------------- | ---------------------------------------------- | +| extensionCodec | ExtensionCodec | `ExtensionCodec.defaultCodec` | +| context | user-defined | - | +| useBigInt64 | boolean | false | +| rawStrings | boolean | false | +| maxStrLength | number | `4_294_967_295` (UINT32_MAX) | +| maxBinLength | number | `4_294_967_295` (UINT32_MAX) | +| maxArrayLength | number | `4_294_967_295` (UINT32_MAX) | +| maxMapLength | number | `4_294_967_295` (UINT32_MAX) | +| maxExtLength | number | `4_294_967_295` (UINT32_MAX) | +| mapKeyConverter | MapKeyConverterType | throw exception if key is not string or number | + +`MapKeyConverterType` is defined as `(key: unknown) => string | number`. To skip UTF-8 decoding of strings, `rawStrings` can be set to `true`. In this case, strings are decoded into `Uint8Array`. diff --git a/eslint.config.mjs b/eslint.config.mjs index 7303492..3d5529e 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -17,7 +17,7 @@ const compat = new FlatCompat({ }); export default [{ - ignores: ["**/*.js"], + ignores: ["**/*.js", "test/deno*", "test/bun*"], }, ...fixupConfigRules(compat.extends( "eslint:recommended", "plugin:@typescript-eslint/recommended", diff --git a/package-lock.json b/package-lock.json index 65407f8..7268a8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@msgpack/msgpack", - "version": "3.0.0", + "version": "3.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@msgpack/msgpack", - "version": "3.0.0", + "version": "3.1.0", "license": "ISC", "devDependencies": { "@eslint/compat": "latest", diff --git a/package.json b/package.json index 5d03ab5..b1b2e2a 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "@msgpack/msgpack", - "version": "3.0.0", + "version": "3.1.0", "description": "MessagePack for ECMA-262/JavaScript/TypeScript", "author": "The MessagePack community", "license": "ISC", - "main": "./dist.cjs/index.js", + "main": "./dist.cjs/index.cjs", "module": "./dist.esm/index.mjs", "cdn": "./dist.umd/msgpack.min.js", "unpkg": "./dist.umd/msgpack.min.js", @@ -12,13 +12,13 @@ "sideEffects": false, "scripts": { "build": "npm publish --dry-run", - "prepare": "npm run clean && webpack --bail && tsc --build tsconfig.dist.cjs.json tsconfig.dist.esm.json && ts-node tools/esmify.ts dist.esm/*.js dist.esm/*/*.js", + "prepare": "npm run clean && webpack --bail && tsc --build tsconfig.dist.cjs.json tsconfig.dist.esm.json && tsimp tools/fix-ext.mts --mjs dist.esm/*.js dist.esm/*/*.js && tsimp tools/fix-ext.mts --cjs dist.cjs/*.js dist.cjs/*/*.js", "prepublishOnly": "npm run test:dist", "clean": "rimraf build dist dist.*", "test": "mocha 'test/**/*.test.ts'", "test:dist": "npm run lint && npm run test && npm run test:deno", "test:cover": "npm run cover:clean && npx nyc --no-clean npm run 'test' && npm run cover:report", - "test:deno": "deno test test/deno_test.ts", + "test:deno": "deno test --allow-read test/deno_*.ts", "test:bun": "bun test test/bun.spec.ts", "test:fuzz": "npm exec --yes -- jsfuzz@git+https://gitlab.com/gitlab-org/security-products/analyzers/fuzzers/jsfuzz.git#39e6cf16613a0e30c7a7953f62e64292dbd5d3f3 --fuzzTime 60 --no-versifier test/decode.jsfuzz.js corpus", "cover:clean": "rimraf .nyc_output coverage/", @@ -89,10 +89,10 @@ "webpack-cli": "latest" }, "files": [ - "mod.ts", "src/**/*.*", - "dist/**/*.*", + "dist.cjs/**/*.*", + "dist.esm/**/*.*", "dist.umd/**/*.*", - "dist.esm/**/*.*" + "mod.ts" ] } diff --git a/src/CachedKeyDecoder.ts b/src/CachedKeyDecoder.ts index f410854..73524f0 100644 --- a/src/CachedKeyDecoder.ts +++ b/src/CachedKeyDecoder.ts @@ -1,4 +1,4 @@ -import { utf8DecodeJs } from "./utils/utf8"; +import { utf8DecodeJs } from "./utils/utf8.ts"; const DEFAULT_MAX_KEY_LENGTH = 16; const DEFAULT_MAX_LENGTH_PER_KEY = 16; diff --git a/src/Decoder.ts b/src/Decoder.ts index 3a40f9e..e9257a9 100644 --- a/src/Decoder.ts +++ b/src/Decoder.ts @@ -1,12 +1,12 @@ -import "./utils/symbol.dispose"; -import { prettyByte } from "./utils/prettyByte"; -import { ExtensionCodec, ExtensionCodecType } from "./ExtensionCodec"; -import { getInt64, getUint64, UINT32_MAX } from "./utils/int"; -import { utf8Decode } from "./utils/utf8"; -import { ensureUint8Array } from "./utils/typedArrays"; -import { CachedKeyDecoder, KeyDecoder } from "./CachedKeyDecoder"; -import { DecodeError } from "./DecodeError"; -import type { ContextOf } from "./context"; +import "./utils/symbol.dispose.ts"; +import { prettyByte } from "./utils/prettyByte.ts"; +import { ExtensionCodec, ExtensionCodecType } from "./ExtensionCodec.ts"; +import { getInt64, getUint64, UINT32_MAX } from "./utils/int.ts"; +import { utf8Decode } from "./utils/utf8.ts"; +import { ensureUint8Array } from "./utils/typedArrays.ts"; +import { CachedKeyDecoder, KeyDecoder } from "./CachedKeyDecoder.ts"; +import { DecodeError } from "./DecodeError.ts"; +import type { ContextOf } from "./context.ts"; export type DecoderOptions = Readonly< Partial<{ @@ -68,6 +68,13 @@ export type DecoderOptions = Readonly< * `null` is a special value to disable the use of the key decoder at all. */ keyDecoder: KeyDecoder | null; + + /** + * A function to convert decoded map key to a valid JS key type. + * + * Defaults to a function that throws an error if the key is not a string or a number. + */ + mapKeyConverter: (key: unknown) => MapKeyType; }> > & ContextOf; @@ -78,8 +85,11 @@ const STATE_MAP_VALUE = "map_value"; type MapKeyType = string | number; -const isValidMapKeyType = (key: unknown): key is MapKeyType => { - return typeof key === "string" || typeof key === "number"; +const mapKeyConverter = (key: unknown): MapKeyType => { + if (typeof key === "string" || typeof key === "number") { + return key; + } + throw new DecodeError("The type of key must be string or number but " + typeof key); }; type StackMapState = { @@ -213,6 +223,7 @@ export class Decoder { private readonly maxMapLength: number; private readonly maxExtLength: number; private readonly keyDecoder: KeyDecoder | null; + private readonly mapKeyConverter: (key: unknown) => MapKeyType; private totalPos = 0; private pos = 0; @@ -236,6 +247,7 @@ export class Decoder { this.maxMapLength = options?.maxMapLength ?? UINT32_MAX; this.maxExtLength = options?.maxExtLength ?? UINT32_MAX; this.keyDecoder = options?.keyDecoder !== undefined ? options.keyDecoder : sharedCachedKeyDecoder; + this.mapKeyConverter = options?.mapKeyConverter ?? mapKeyConverter; } private clone(): Decoder { @@ -631,14 +643,11 @@ export class Decoder { continue DECODE; } } else if (state.type === STATE_MAP_KEY) { - if (!isValidMapKeyType(object)) { - throw new DecodeError("The type of key must be string or number but " + typeof object); - } if (object === "__proto__") { throw new DecodeError("The key __proto__ is not allowed"); } - state.key = object; + state.key = this.mapKeyConverter(object); state.type = STATE_MAP_VALUE; continue DECODE; } else { diff --git a/src/Encoder.ts b/src/Encoder.ts index 393c275..27d8ded 100644 --- a/src/Encoder.ts +++ b/src/Encoder.ts @@ -1,10 +1,10 @@ -import "./utils/symbol.dispose"; -import { utf8Count, utf8Encode } from "./utils/utf8"; -import { ExtensionCodec, ExtensionCodecType } from "./ExtensionCodec"; -import { setInt64, setUint64 } from "./utils/int"; -import { ensureUint8Array } from "./utils/typedArrays"; -import type { ExtData } from "./ExtData"; -import type { ContextOf } from "./context"; +import "./utils/symbol.dispose.ts"; +import { utf8Count, utf8Encode } from "./utils/utf8.ts"; +import { ExtensionCodec, ExtensionCodecType } from "./ExtensionCodec.ts"; +import { setInt64, setUint64 } from "./utils/int.ts"; +import { ensureUint8Array } from "./utils/typedArrays.ts"; +import type { ExtData } from "./ExtData.ts"; +import type { ContextOf } from "./context.ts"; export const DEFAULT_MAX_DEPTH = 100; export const DEFAULT_INITIAL_BUFFER_SIZE = 2048; diff --git a/src/ExtensionCodec.ts b/src/ExtensionCodec.ts index 5bd2200..5691579 100644 --- a/src/ExtensionCodec.ts +++ b/src/ExtensionCodec.ts @@ -1,7 +1,7 @@ // ExtensionCodec to handle MessagePack extensions -import { ExtData } from "./ExtData"; -import { timestampExtension } from "./timestamp"; +import { ExtData } from "./ExtData.ts"; +import { timestampExtension } from "./timestamp.ts"; export type ExtensionDecoderType = ( data: Uint8Array, diff --git a/src/context.ts b/src/context.ts index 1f75665..1edaaa5 100644 --- a/src/context.ts +++ b/src/context.ts @@ -1,4 +1,4 @@ -export type SplitTypes = U extends T ? (Exclude extends never ? T : Exclude) : T; +type SplitTypes = U extends T ? (Exclude extends never ? T : Exclude) : T; export type SplitUndefined = SplitTypes; diff --git a/src/decode.ts b/src/decode.ts index e4857ae..685fc10 100644 --- a/src/decode.ts +++ b/src/decode.ts @@ -1,6 +1,6 @@ -import { Decoder } from "./Decoder"; -import type { DecoderOptions } from "./Decoder"; -import type { SplitUndefined } from "./context"; +import { Decoder } from "./Decoder.ts"; +import type { DecoderOptions } from "./Decoder.ts"; +import type { SplitUndefined } from "./context.ts"; /** * It decodes a single MessagePack object in a buffer. diff --git a/src/decodeAsync.ts b/src/decodeAsync.ts index d4f91dc..9534f07 100644 --- a/src/decodeAsync.ts +++ b/src/decodeAsync.ts @@ -1,8 +1,8 @@ -import { Decoder } from "./Decoder"; -import { ensureAsyncIterable } from "./utils/stream"; -import type { DecoderOptions } from "./Decoder"; -import type { ReadableStreamLike } from "./utils/stream"; -import type { SplitUndefined } from "./context"; +import { Decoder } from "./Decoder.ts"; +import { ensureAsyncIterable } from "./utils/stream.ts"; +import type { DecoderOptions } from "./Decoder.ts"; +import type { ReadableStreamLike } from "./utils/stream.ts"; +import type { SplitUndefined } from "./context.ts"; /** * @throws {@link RangeError} if the buffer is incomplete, including the case where the buffer is empty. diff --git a/src/encode.ts b/src/encode.ts index 392b7fe..9a9d07a 100644 --- a/src/encode.ts +++ b/src/encode.ts @@ -1,6 +1,6 @@ -import { Encoder } from "./Encoder"; -import type { EncoderOptions } from "./Encoder"; -import type { SplitUndefined } from "./context"; +import { Encoder } from "./Encoder.ts"; +import type { EncoderOptions } from "./Encoder.ts"; +import type { SplitUndefined } from "./context.ts"; /** * It encodes `value` in the MessagePack format and diff --git a/src/index.ts b/src/index.ts index 840f25d..f4550e2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,33 +1,33 @@ // Main Functions: -import { encode } from "./encode"; +import { encode } from "./encode.ts"; export { encode }; -import { decode, decodeMulti } from "./decode"; +import { decode, decodeMulti } from "./decode.ts"; export { decode, decodeMulti }; -import { decodeAsync, decodeArrayStream, decodeMultiStream } from "./decodeAsync"; +import { decodeAsync, decodeArrayStream, decodeMultiStream } from "./decodeAsync.ts"; export { decodeAsync, decodeArrayStream, decodeMultiStream }; -import { Decoder } from "./Decoder"; +import { Decoder } from "./Decoder.ts"; export { Decoder }; -import type { DecoderOptions } from "./Decoder"; +import type { DecoderOptions } from "./Decoder.ts"; export type { DecoderOptions }; -import { DecodeError } from "./DecodeError"; +import { DecodeError } from "./DecodeError.ts"; export { DecodeError }; -import { Encoder } from "./Encoder"; +import { Encoder } from "./Encoder.ts"; export { Encoder }; -import type { EncoderOptions } from "./Encoder"; +import type { EncoderOptions } from "./Encoder.ts"; export type { EncoderOptions }; // Utilities for Extension Types: -import { ExtensionCodec } from "./ExtensionCodec"; +import { ExtensionCodec } from "./ExtensionCodec.ts"; export { ExtensionCodec }; -import type { ExtensionCodecType, ExtensionDecoderType, ExtensionEncoderType } from "./ExtensionCodec"; +import type { ExtensionCodecType, ExtensionDecoderType, ExtensionEncoderType } from "./ExtensionCodec.ts"; export type { ExtensionCodecType, ExtensionDecoderType, ExtensionEncoderType }; -import { ExtData } from "./ExtData"; +import { ExtData } from "./ExtData.ts"; export { ExtData }; import { @@ -37,7 +37,7 @@ import { decodeTimestampToTimeSpec, encodeTimestampExtension, decodeTimestampExtension, -} from "./timestamp"; +} from "./timestamp.ts"; export { EXT_TIMESTAMP, encodeDateToTimeSpec, diff --git a/src/timestamp.ts b/src/timestamp.ts index e3fe015..3c1e9db 100644 --- a/src/timestamp.ts +++ b/src/timestamp.ts @@ -1,6 +1,6 @@ // https://github.com/msgpack/msgpack/blob/master/spec.md#timestamp-extension-type -import { DecodeError } from "./DecodeError"; -import { getInt64, setInt64 } from "./utils/int"; +import { DecodeError } from "./DecodeError.ts"; +import { getInt64, setInt64 } from "./utils/int.ts"; export const EXT_TIMESTAMP = -1; diff --git a/src/utils/stream.ts b/src/utils/stream.ts index b7549e1..75bc1d2 100644 --- a/src/utils/stream.ts +++ b/src/utils/stream.ts @@ -10,12 +10,6 @@ export function isAsyncIterable(object: ReadableStreamLike): object is Asy return (object as any)[Symbol.asyncIterator] != null; } -function assertNonNull(value: T | null | undefined): asserts value is T { - if (value == null) { - throw new Error("Assertion Failure: value must not be null nor undefined"); - } -} - export async function* asyncIterableFromStream(stream: ReadableStream): AsyncIterable { const reader = stream.getReader(); @@ -25,7 +19,6 @@ export async function* asyncIterableFromStream(stream: ReadableStream): As if (done) { return; } - assertNonNull(value); yield value; } } finally { diff --git a/test/CachedKeyDecoder.test.ts b/test/CachedKeyDecoder.test.ts index 4e9dd06..8cb16f0 100644 --- a/test/CachedKeyDecoder.test.ts +++ b/test/CachedKeyDecoder.test.ts @@ -1,7 +1,7 @@ import assert from "assert"; -import { CachedKeyDecoder } from "../src/CachedKeyDecoder"; -import { utf8EncodeJs, utf8Count } from "../src/utils/utf8"; -import type { KeyDecoder } from "../src/CachedKeyDecoder"; +import { CachedKeyDecoder } from "../src/CachedKeyDecoder.ts"; +import { utf8EncodeJs, utf8Count } from "../src/utils/utf8.ts"; +import type { KeyDecoder } from "../src/CachedKeyDecoder.ts"; function tryDecode(keyDecoder: KeyDecoder, str: string): string { const byteLength = utf8Count(str); diff --git a/test/ExtensionCodec.test.ts b/test/ExtensionCodec.test.ts index 5d9f5e3..543171b 100644 --- a/test/ExtensionCodec.test.ts +++ b/test/ExtensionCodec.test.ts @@ -1,6 +1,6 @@ import assert from "assert"; import util from "util"; -import { encode, decode, ExtensionCodec, decodeAsync } from "../src/index"; +import { encode, decode, ExtensionCodec, decodeAsync } from "../src/index.ts"; describe("ExtensionCodec", () => { context("timestamp", () => { diff --git a/test/bigint64.test.ts b/test/bigint64.test.ts index e4ab68a..e2bf08f 100644 --- a/test/bigint64.test.ts +++ b/test/bigint64.test.ts @@ -1,5 +1,5 @@ import assert from "assert"; -import { encode, decode } from "../src/index"; +import { encode, decode } from "../src/index.ts"; describe("useBigInt64: true", () => { before(function () { diff --git a/test/bun.spec.ts b/test/bun.spec.ts index 92e202d..fca79ea 100644 --- a/test/bun.spec.ts +++ b/test/bun.spec.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { encode, decode } from "../src/index"; +import { encode, decode } from "../src/index.ts"; test("Hello, world!", () => { const encoded = encode("Hello, world!"); diff --git a/test/codec-bigint.test.ts b/test/codec-bigint.test.ts index 524bbf7..b0ef7aa 100644 --- a/test/codec-bigint.test.ts +++ b/test/codec-bigint.test.ts @@ -1,5 +1,5 @@ import assert from "assert"; -import { encode, decode, ExtensionCodec, DecodeError } from "../src/index"; +import { encode, decode, ExtensionCodec, DecodeError } from "../src/index.ts"; // There's a built-in `useBigInt64: true` option, but a custom codec might be // better if you'd like to encode bigint to reduce the size of binaries. diff --git a/test/codec-float.test.ts b/test/codec-float.test.ts index 3b690dc..accc590 100644 --- a/test/codec-float.test.ts +++ b/test/codec-float.test.ts @@ -1,6 +1,6 @@ import assert from "assert"; import * as ieee754 from "ieee754"; -import { decode } from "../src/index"; +import { decode } from "../src/index.ts"; const FLOAT32_TYPE = 0xca; const FLOAT64_TYPE = 0xcb; diff --git a/test/codec-int.test.ts b/test/codec-int.test.ts index 486f93b..7e5a822 100644 --- a/test/codec-int.test.ts +++ b/test/codec-int.test.ts @@ -1,5 +1,5 @@ import assert from "assert"; -import { setInt64, getInt64, getUint64, setUint64 } from "../src/utils/int"; +import { setInt64, getInt64, getUint64, setUint64 } from "../src/utils/int.ts"; const INT64SPECS = { ZERO: 0, diff --git a/test/codec-timestamp.test.ts b/test/codec-timestamp.test.ts index 1ee1432..a309872 100644 --- a/test/codec-timestamp.test.ts +++ b/test/codec-timestamp.test.ts @@ -7,7 +7,7 @@ import { decodeTimestampExtension, decodeTimestampToTimeSpec, encodeTimestampExtension, -} from "../src/index"; +} from "../src/index.ts"; const TIME = 1556636810389; diff --git a/test/decode-blob.test.ts b/test/decode-blob.test.ts index 5fdb6c9..0c8c5b4 100644 --- a/test/decode-blob.test.ts +++ b/test/decode-blob.test.ts @@ -1,5 +1,5 @@ import assert from "assert"; -import { encode, decode, decodeAsync } from "../src/index"; +import { encode, decode, decodeAsync } from "../src/index.ts"; (typeof Blob !== "undefined" ? describe : describe.skip)("Blob", () => { it("decodes it with `decode()`", async function () { diff --git a/test/decode-max-length.test.ts b/test/decode-max-length.test.ts index 8a2eb56..8dd87f9 100644 --- a/test/decode-max-length.test.ts +++ b/test/decode-max-length.test.ts @@ -1,6 +1,6 @@ import assert from "assert"; -import { encode, decode, decodeAsync } from "../src/index"; -import type { DecoderOptions } from "../src/index"; +import { encode, decode, decodeAsync } from "../src/index.ts"; +import type { DecoderOptions } from "../src/index.ts"; describe("decode with max${Type}Length specified", () => { async function* createStream(input: T) { diff --git a/test/decode-raw-strings.test.ts b/test/decode-raw-strings.test.ts index 9608f5d..c50ff15 100644 --- a/test/decode-raw-strings.test.ts +++ b/test/decode-raw-strings.test.ts @@ -1,6 +1,6 @@ import assert from "assert"; -import { encode, decode } from "../src/index"; -import type { DecoderOptions } from "../src/index"; +import { encode, decode } from "../src/index.ts"; +import type { DecoderOptions } from "../src/index.ts"; describe("decode with rawStrings specified", () => { const options = { rawStrings: true } satisfies DecoderOptions; diff --git a/test/decodeArrayStream.test.ts b/test/decodeArrayStream.test.ts index f91b12e..a8f7ed5 100644 --- a/test/decodeArrayStream.test.ts +++ b/test/decodeArrayStream.test.ts @@ -1,5 +1,5 @@ import assert from "assert"; -import { encode, decodeArrayStream } from "../src/index"; +import { encode, decodeArrayStream } from "../src/index.ts"; describe("decodeArrayStream", () => { const generateSampleObject = () => { diff --git a/test/decodeAsync.test.ts b/test/decodeAsync.test.ts index 99b46f6..6af6c19 100644 --- a/test/decodeAsync.test.ts +++ b/test/decodeAsync.test.ts @@ -1,5 +1,5 @@ import assert from "assert"; -import { encode, decodeAsync } from "../src/index"; +import { encode, decodeAsync } from "../src/index.ts"; describe("decodeAsync", () => { function wrapWithNoisyBuffer(byte: number) { @@ -36,6 +36,21 @@ describe("decodeAsync", () => { assert.deepStrictEqual(object, { "foo": "bar" }); }); + it("decodes fixmap {'[1, 2]': 'baz'} with custom map key converter", async () => { + const createStream = async function* () { + yield [0x81]; // fixmap size=1 + yield encode([1, 2]); + yield encode("baz"); + }; + + const object = await decodeAsync(createStream(), { + mapKeyConverter: (key) => JSON.stringify(key), + }); + + const key = JSON.stringify([1, 2]); + assert.deepStrictEqual(object, { [key]: "baz" }); + }); + it("decodes multi-byte integer byte-by-byte", async () => { const createStream = async function* () { yield [0xcd]; // uint 16 diff --git a/test/decodeMulti.test.ts b/test/decodeMulti.test.ts index f5b96e1..eb661b8 100644 --- a/test/decodeMulti.test.ts +++ b/test/decodeMulti.test.ts @@ -1,5 +1,5 @@ import assert from "assert"; -import { encode, decodeMulti } from "../src/index"; +import { encode, decodeMulti } from "../src/index.ts"; describe("decodeMulti", () => { it("decodes multiple objects in a single binary", () => { diff --git a/test/decodeMultiStream.test.ts b/test/decodeMultiStream.test.ts index b88dd12..4da31e5 100644 --- a/test/decodeMultiStream.test.ts +++ b/test/decodeMultiStream.test.ts @@ -1,5 +1,5 @@ import assert from "assert"; -import { encode, decodeMultiStream } from "../src/index"; +import { encode, decodeMultiStream } from "../src/index.ts"; describe("decodeStream", () => { it("decodes stream", async () => { diff --git a/test/deno_cjs_test.ts b/test/deno_cjs_test.ts new file mode 100755 index 0000000..7293f0a --- /dev/null +++ b/test/deno_cjs_test.ts @@ -0,0 +1,12 @@ +#!/usr/bin/env deno test --allow-read + +/* eslint-disable */ +import { deepStrictEqual } from "node:assert"; +import { test } from "node:test"; +import * as msgpack from "../dist.cjs/index.cjs"; + +test("Hello, world!", () => { + const encoded = msgpack.encode("Hello, world!"); + const decoded = msgpack.decode(encoded); + deepStrictEqual(decoded, "Hello, world!"); +}); diff --git a/test/edge-cases.test.ts b/test/edge-cases.test.ts index 43cc9e5..797f6be 100644 --- a/test/edge-cases.test.ts +++ b/test/edge-cases.test.ts @@ -1,7 +1,7 @@ // kind of hand-written fuzzing data // any errors should not break Encoder/Decoder instance states import assert from "assert"; -import { encode, decodeAsync, decode, Encoder, Decoder, decodeMulti, decodeMultiStream } from "../src/index"; +import { encode, decodeAsync, decode, Encoder, Decoder, decodeMulti, decodeMultiStream } from "../src/index.ts"; function testEncoder(encoder: Encoder): void { const object = { diff --git a/test/encode.test.ts b/test/encode.test.ts index 2167276..f1b6ffe 100644 --- a/test/encode.test.ts +++ b/test/encode.test.ts @@ -1,5 +1,5 @@ import assert from "assert"; -import { encode, decode } from "../src/index"; +import { encode, decode } from "../src/index.ts"; describe("encode", () => { context("sortKeys", () => { diff --git a/test/msgpack-ext.test.ts b/test/msgpack-ext.test.ts index a884f5c..15244cb 100644 --- a/test/msgpack-ext.test.ts +++ b/test/msgpack-ext.test.ts @@ -1,5 +1,5 @@ import assert from "assert"; -import { encode, decode, ExtData } from "../src/index"; +import { encode, decode, ExtData } from "../src/index.ts"; function seq(n: number) { const a: Array = []; diff --git a/test/msgpack-test-suite.test.ts b/test/msgpack-test-suite.test.ts index 94c841e..d42b0db 100644 --- a/test/msgpack-test-suite.test.ts +++ b/test/msgpack-test-suite.test.ts @@ -2,7 +2,7 @@ import assert from "assert"; import util from "util"; import { Exam } from "msgpack-test-js"; import { MsgTimestamp } from "msg-timestamp"; -import { encode, decode, ExtensionCodec, EXT_TIMESTAMP, encodeTimeSpecToTimestamp } from "../src/index"; +import { encode, decode, ExtensionCodec, EXT_TIMESTAMP, encodeTimeSpecToTimestamp } from "../src/index.ts"; const extensionCodec = new ExtensionCodec(); extensionCodec.register({ diff --git a/test/prototype-pollution.test.ts b/test/prototype-pollution.test.ts index 78238c0..46e293e 100644 --- a/test/prototype-pollution.test.ts +++ b/test/prototype-pollution.test.ts @@ -1,5 +1,5 @@ import { throws } from "assert"; -import { encode, decode, DecodeError } from "../src/index"; +import { encode, decode, DecodeError } from "../src/index.ts"; describe("prototype pollution", () => { context("__proto__ exists as a map key", () => { diff --git a/test/reuse-instances-with-extensions.test.ts b/test/reuse-instances-with-extensions.test.ts index 39cb456..f539625 100644 --- a/test/reuse-instances-with-extensions.test.ts +++ b/test/reuse-instances-with-extensions.test.ts @@ -1,7 +1,7 @@ // https://github.com/msgpack/msgpack-javascript/issues/195 import { deepStrictEqual } from "assert"; -import { Encoder, Decoder, ExtensionCodec } from "../src/index"; +import { Encoder, Decoder, ExtensionCodec } from "../src/index.ts"; const MSGPACK_EXT_TYPE_BIGINT = 0; diff --git a/test/reuse-instances.test.ts b/test/reuse-instances.test.ts index 8f1cee1..2c971cd 100644 --- a/test/reuse-instances.test.ts +++ b/test/reuse-instances.test.ts @@ -1,5 +1,5 @@ import { deepStrictEqual } from "assert"; -import { Encoder, Decoder, decode } from "../src/index"; +import { Encoder, Decoder, decode } from "../src/index.ts"; const createStream = async function* (...args: any) { for (const item of args) { diff --git a/test/whatwg-streams.test.ts b/test/whatwg-streams.test.ts index ac2ae6a..2b32493 100644 --- a/test/whatwg-streams.test.ts +++ b/test/whatwg-streams.test.ts @@ -1,5 +1,5 @@ import { deepStrictEqual } from "assert"; -import { decodeAsync, encode, decodeArrayStream } from "../src/index"; +import { decodeAsync, encode, decodeArrayStream } from "../src/index.ts"; const isReadableStreamConstructorAvailable: boolean = (() => { try { diff --git a/tools/esmify.ts b/tools/esmify.ts deleted file mode 100644 index 8447c10..0000000 --- a/tools/esmify.ts +++ /dev/null @@ -1,25 +0,0 @@ -#!ts-node -/* eslint-disable no-console */ - -import fs from "fs"; - -const files = process.argv.slice(2); - -for (const file of files) { - const fileMjs = file.replace(/\.js$/, ".mjs"); - console.info(`Processing ${file} => ${fileMjs}`); - // .js => .mjs - const content = fs.readFileSync(file).toString("utf-8"); - const newContent = content.replace(/\bfrom "(\.\.?\/[^"]+)";/g, 'from "$1.mjs";') - .replace(/\bimport "(\.\.?\/[^"]+)";/g, 'import "$1.mjs";') - .replace(/\/\/# sourceMappingURL=(.+)\.js\.map$/, - "//# sourceMappingURL=$1.mjs.map"); - fs.writeFileSync(fileMjs, newContent); - fs.unlinkSync(file); - - // .js.map => .mjs.map - const mapping = JSON.parse(fs.readFileSync(`${file}.map`).toString("utf-8")); - mapping.file = mapping.file.replace(/\.js$/, ".mjs"); - fs.writeFileSync(`${fileMjs}.map`, JSON.stringify(mapping)); - fs.unlinkSync(`${file}.map`); -} diff --git a/tools/fix-ext.mts b/tools/fix-ext.mts new file mode 100644 index 0000000..bd6f737 --- /dev/null +++ b/tools/fix-ext.mts @@ -0,0 +1,28 @@ +import fs from "node:fs"; + +const mode = process.argv[2]; // --cjs or --mjs +const files = process.argv.slice(3); + +const ext = mode === "--cjs" ? "cjs" : "mjs"; + +console.info(`Fixing ${mode} files with extension ${ext}`); + +for (const file of files) { + const fileMjs = file.replace(/\.js$/, `.${ext}`); + console.info(`Processing ${file} => ${fileMjs}`); + // .js => .mjs + const content = fs.readFileSync(file).toString("utf-8"); + const newContent = content + .replace(/\bfrom "(\.\.?\/[^"]+)\.js";/g, `from "$1.${ext}";`) + .replace(/\bimport "(\.\.?\/[^"]+)\.js";/g, `import "$1.${ext}";`) + .replace(/\brequire\("(\.\.?\/[^"]+)\.js"\)/g, `require("$1.${ext}");`) + .replace(/\/\/# sourceMappingURL=(.+)\.js\.map$/, `//# sourceMappingURL=$1.${ext}.map`); + fs.writeFileSync(fileMjs, newContent); + fs.unlinkSync(file); + + // .js.map => .mjs.map + const mapping = JSON.parse(fs.readFileSync(`${file}.map`).toString("utf-8")); + mapping.file = mapping.file.replace(/\.js$/, ext); + fs.writeFileSync(`${fileMjs}.map`, JSON.stringify(mapping)); + fs.unlinkSync(`${file}.map`); +} diff --git a/tsconfig.dist.cjs.json b/tsconfig.dist.cjs.json index bb97412..8225a84 100644 --- a/tsconfig.dist.cjs.json +++ b/tsconfig.dist.cjs.json @@ -6,6 +6,7 @@ "declaration": false, "noEmitOnError": true, "noEmit": false, + "rewriteRelativeImportExtensions": true, "incremental": false }, "include": ["src/**/*.ts"] diff --git a/tsconfig.dist.esm.json b/tsconfig.dist.esm.json index 42aeff6..f8d47a0 100644 --- a/tsconfig.dist.esm.json +++ b/tsconfig.dist.esm.json @@ -6,6 +6,7 @@ "declaration": true, "noEmitOnError": true, "noEmit": false, + "rewriteRelativeImportExtensions": true, "incremental": false }, "include": ["src/**/*.ts"] diff --git a/tsconfig.dist.webpack.json b/tsconfig.dist.webpack.json index 687889a..c7fb1ea 100644 --- a/tsconfig.dist.webpack.json +++ b/tsconfig.dist.webpack.json @@ -4,6 +4,7 @@ "module": "ESNext", "noEmitOnError": true, "noEmit": false, + "allowImportingTsExtensions": false, "outDir": "./build/webpack" }, "include": ["src/**/*.ts"] diff --git a/tsconfig.json b/tsconfig.json index 60e468c..1c7ef93 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -41,7 +41,7 @@ "noPropertyAccessFromIndexSignature": true, "noImplicitOverride": true, "verbatimModuleSyntax": false, - // "allowImportingTsExtensions": true, + "allowImportingTsExtensions": true, "noEmit": true, /* Module Resolution Options */ diff --git a/tsconfig.test-karma.json b/tsconfig.test-karma.json index 560ff84..4a3bdec 100644 --- a/tsconfig.test-karma.json +++ b/tsconfig.test-karma.json @@ -2,8 +2,10 @@ "extends": "./tsconfig.json", "compilerOptions": { "module": "ESNext", - "outDir": "./build/karma", + "noEmitOnError": true, "noEmit": false, + "allowImportingTsExtensions": false, + "outDir": "./build/karma", "incremental": false } }