diff --git a/packages/bundle-size/README.md b/packages/bundle-size/README.md
index ce1b64853..80f5654eb 100644
--- a/packages/bundle-size/README.md
+++ b/packages/bundle-size/README.md
@@ -16,11 +16,11 @@ usually do. We repeat this for an increasing number of files.
| code generator | files | bundle size | minified | compressed |
|-----------------|----------|------------------------:|-----------------------:|-------------------:|
-| protobuf-es | 1 | 123,019 b | 63,933 b | 14,930 b |
-| protobuf-es | 4 | 125,214 b | 65,443 b | 15,592 b |
-| protobuf-es | 8 | 127,992 b | 67,214 b | 16,104 b |
-| protobuf-es | 16 | 138,500 b | 75,195 b | 18,432 b |
-| protobuf-es | 32 | 166,395 b | 97,210 b | 23,848 b |
+| protobuf-es | 1 | 123,350 b | 64,136 b | 14,970 b |
+| protobuf-es | 4 | 125,545 b | 65,646 b | 15,662 b |
+| protobuf-es | 8 | 128,323 b | 67,417 b | 16,196 b |
+| protobuf-es | 16 | 138,831 b | 75,398 b | 18,504 b |
+| protobuf-es | 32 | 166,726 b | 97,413 b | 23,953 b |
| protobuf-javascript | 1 | 339,613 b | 255,820 b | 42,481 b |
| protobuf-javascript | 4 | 366,281 b | 271,092 b | 43,912 b |
| protobuf-javascript | 8 | 388,324 b | 283,409 b | 45,038 b |
diff --git a/packages/bundle-size/chart.svg b/packages/bundle-size/chart.svg
index 43934a6d3..183492679 100644
--- a/packages/bundle-size/chart.svg
+++ b/packages/bundle-size/chart.svg
@@ -43,14 +43,14 @@
0 KiB
-
+
protobuf-es
-protobuf-es 14.58 KiB for 1 files
-protobuf-es 15.23 KiB for 4 files
-protobuf-es 15.73 KiB for 8 files
-protobuf-es 18 KiB for 16 files
-protobuf-es 23.29 KiB for 32 files
+protobuf-es 14.62 KiB for 1 files
+protobuf-es 15.29 KiB for 4 files
+protobuf-es 15.82 KiB for 8 files
+protobuf-es 18.07 KiB for 16 files
+protobuf-es 23.39 KiB for 32 files
diff --git a/packages/protobuf-test/src/wire/binary-encoding.test.ts b/packages/protobuf-test/src/wire/binary-encoding.test.ts
index c6aaa04ae..4e430c2d1 100644
--- a/packages/protobuf-test/src/wire/binary-encoding.test.ts
+++ b/packages/protobuf-test/src/wire/binary-encoding.test.ts
@@ -31,7 +31,7 @@ describe("BinaryWriter", () => {
expect(user.firstName).toBe("Homer");
expect(user.active).toBe(true);
});
- describe("float32", () => {
+ describe("float32()", () => {
it.each([
1024,
3.142,
@@ -41,13 +41,20 @@ describe("BinaryWriter", () => {
Number.NEGATIVE_INFINITY,
Number.NaN,
])("should encode %s", (val) => {
- expect(new BinaryWriter().float(val).finish().length).toBeGreaterThan(0);
+ const bytes = new BinaryWriter().float(val).finish();
+ expect(bytes.length).toBeGreaterThan(0);
+ // @ts-expect-error test string
+ const bytesStr = new BinaryWriter().float(val.toString()).finish();
+ expect(bytesStr.length).toBeGreaterThan(0);
+ expect(bytesStr).toStrictEqual(bytes);
});
it.each([
- { val: "123", err: /^invalid float32: string/ },
- { val: true, err: /^invalid float32: bool/ },
+ { val: null, err: "invalid float32: object" },
+ { val: new Date(), err: "invalid float32: object" },
+ { val: undefined, err: "invalid float32: undefined" },
+ { val: true, err: "invalid float32: bool" },
])("should error for wrong type $val", ({ val, err }) => {
- // @ts-expect-error TS2345
+ // @ts-expect-error test wrong type
expect(() => new BinaryWriter().float(val)).toThrow(err);
});
it.each([Number.MAX_VALUE, -Number.MAX_VALUE])(
@@ -56,40 +63,76 @@ describe("BinaryWriter", () => {
expect(() => new BinaryWriter().float(val)).toThrow(
/^invalid float32: .*/,
);
+ // @ts-expect-error test string
+ expect(() => new BinaryWriter().float(val.toString())).toThrow(
+ /^invalid float32: .*/,
+ );
},
);
});
- describe("int32", () => {
+ // sfixed32, sint32, and int32 are signed 32-bit integers, just with different encoding
+ describe.each(["sfixed32", "sint32", "int32"] as const)("%s()", (type) => {
it.each([-0x80000000, 1024, 0x7fffffff])("should encode %s", (val) => {
- expect(new BinaryWriter().int32(val).finish().length).toBeGreaterThan(0);
+ const bytes = new BinaryWriter()[type](val).finish();
+ expect(bytes.length).toBeGreaterThan(0);
+ // @ts-expect-error test string
+ const bytesStr = new BinaryWriter()[type](val.toString()).finish();
+ expect(bytesStr.length).toBeGreaterThan(0);
+ expect(bytesStr).toStrictEqual(bytes);
});
it.each([
- { val: "123", err: /^invalid int32: string/ },
- { val: true, err: /^invalid int32: bool/ },
+ { val: null, err: "invalid int32: object" },
+ { val: new Date(), err: "invalid int32: object" },
+ { val: undefined, err: "invalid int32: undefined" },
+ { val: true, err: "invalid int32: bool" },
])("should error for wrong type $val", ({ val, err }) => {
// @ts-expect-error TS2345
- expect(() => new BinaryWriter().int32(val)).toThrow(err);
+ expect(() => new BinaryWriter()[type](val)).toThrow(err);
});
- it.each([0x7fffffff + 1, -0x80000000 - 1])(
+ it.each([0x7fffffff + 1, -0x80000000 - 1, 3.142])(
"should error for value out of range %s",
(val) => {
- expect(() => new BinaryWriter().int32(val)).toThrow(
+ expect(() => new BinaryWriter()[type](val)).toThrow(
+ /^invalid int32: .*/,
+ );
+ // @ts-expect-error test string
+ expect(() => new BinaryWriter()[type](val.toString())).toThrow(
/^invalid int32: .*/,
);
},
);
});
- describe("uint32", () => {
+ // fixed32 and uint32 are unsigned 32-bit integers, just with different encoding
+ describe.each(["fixed32", "uint32"] as const)("%s()", (type) => {
it.each([0, 1024, 0xffffffff])("should encode %s", (val) => {
- expect(new BinaryWriter().uint32(val).finish().length).toBeGreaterThan(0);
+ const bytes = new BinaryWriter()[type](val).finish();
+ expect(bytes.length).toBeGreaterThan(0);
+ // @ts-expect-error test string
+ const bytesStr = new BinaryWriter()[type](val.toString()).finish();
+ expect(bytesStr.length).toBeGreaterThan(0);
+ expect(bytesStr).toStrictEqual(bytes);
});
it.each([
- { val: "123", err: /^invalid uint32: string/ },
- { val: true, err: /^invalid uint32: bool/ },
- ])("should error for wrong type$val", ({ val, err }) => {
+ { val: null, err: `invalid uint32: object` },
+ { val: new Date(), err: `invalid uint32: object` },
+ { val: undefined, err: `invalid uint32: undefined` },
+ { val: true, err: `invalid uint32: bool` },
+ ])("should error for wrong type $val", ({ val, err }) => {
// @ts-expect-error TS2345
- expect(() => new BinaryWriter().uint32(val)).toThrow(err);
+ expect(() => new BinaryWriter()[type](val)).toThrow(err);
});
+ it.each([0xffffffff + 1, -1, 3.142])(
+ "should error for value out of range %s",
+ (val) => {
+ expect(() => new BinaryWriter()[type](val)).toThrow(
+ /^invalid uint32: .*/,
+ );
+ // @ts-expect-error test string
+ expect(() => new BinaryWriter()[type](val.toString())).toThrow(
+ /^invalid uint32: .*/,
+ );
+ },
+ );
});
});
diff --git a/packages/protobuf/src/reflect/guard.ts b/packages/protobuf/src/reflect/guard.ts
index a3e7180d3..2115a2d58 100644
--- a/packages/protobuf/src/reflect/guard.ts
+++ b/packages/protobuf/src/reflect/guard.ts
@@ -21,7 +21,6 @@ import type {
} from "./reflect-types.js";
import { unsafeLocal } from "./unsafe.js";
import type { DescField, DescMessage } from "../descriptors.js";
-import { isMessage } from "../is-message.js";
export function isObject(arg: unknown): arg is Record {
return arg !== null && typeof arg == "object" && !Array.isArray(arg);
@@ -102,8 +101,9 @@ export function isReflectMessage(
return (
isObject(arg) &&
unsafeLocal in arg &&
- "message" in arg &&
- isMessage(arg.message) &&
- (messageDesc === undefined || arg.message.$typeName == messageDesc.typeName)
+ "desc" in arg &&
+ isObject(arg.desc) &&
+ arg.desc.kind === "message" &&
+ (messageDesc === undefined || arg.desc.typeName == messageDesc.typeName)
);
}
diff --git a/packages/protobuf/src/wire/binary-encoding.ts b/packages/protobuf/src/wire/binary-encoding.ts
index d9fb30a35..29d57d11d 100644
--- a/packages/protobuf/src/wire/binary-encoding.ts
+++ b/packages/protobuf/src/wire/binary-encoding.ts
@@ -568,29 +568,55 @@ export class BinaryReader {
}
/**
- * Assert a valid signed protobuf 32-bit integer.
+ * Assert a valid signed protobuf 32-bit integer as a number or string.
*/
function assertInt32(arg: unknown): asserts arg is number {
- if (typeof arg !== "number") throw new Error("invalid int32: " + typeof arg);
- if (!Number.isInteger(arg) || arg > INT32_MAX || arg < INT32_MIN)
+ if (typeof arg == "string") {
+ arg = Number(arg);
+ } else if (typeof arg != "number") {
+ throw new Error("invalid int32: " + typeof arg);
+ }
+ if (
+ !Number.isInteger(arg) ||
+ (arg as number) > INT32_MAX ||
+ (arg as number) < INT32_MIN
+ )
throw new Error("invalid int32: " + arg);
}
/**
- * Assert a valid unsigned protobuf 32-bit integer.
+ * Assert a valid unsigned protobuf 32-bit integer as a number or string.
*/
function assertUInt32(arg: unknown): asserts arg is number {
- if (typeof arg !== "number") throw new Error("invalid uint32: " + typeof arg);
- if (!Number.isInteger(arg) || arg > UINT32_MAX || arg < 0)
+ if (typeof arg == "string") {
+ arg = Number(arg);
+ } else if (typeof arg != "number") {
+ throw new Error("invalid uint32: " + typeof arg);
+ }
+ if (
+ !Number.isInteger(arg) ||
+ (arg as number) > UINT32_MAX ||
+ (arg as number) < 0
+ )
throw new Error("invalid uint32: " + arg);
}
/**
- * Assert a valid protobuf float value.
+ * Assert a valid protobuf float value as a number or string.
*/
function assertFloat32(arg: unknown): asserts arg is number {
- if (typeof arg !== "number")
+ if (typeof arg == "string") {
+ const o = arg;
+ arg = Number(arg);
+ if (isNaN(arg as number) && o !== "NaN") {
+ throw new Error("invalid float32: " + o);
+ }
+ } else if (typeof arg != "number") {
throw new Error("invalid float32: " + typeof arg);
- if (Number.isFinite(arg) && (arg > FLOAT32_MAX || arg < FLOAT32_MIN))
+ }
+ if (
+ Number.isFinite(arg) &&
+ ((arg as number) > FLOAT32_MAX || (arg as number) < FLOAT32_MIN)
+ )
throw new Error("invalid float32: " + arg);
}