diff --git a/packages/bundle-size/README.md b/packages/bundle-size/README.md index de379c6dd..ce1b64853 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,862 b | 64,285 b | 15,000 b | -| protobuf-es | 4 | 126,057 b | 65,789 b | 15,698 b | -| protobuf-es | 8 | 128,835 b | 67,560 b | 16,188 b | -| protobuf-es | 16 | 139,343 b | 75,541 b | 18,515 b | -| protobuf-es | 32 | 167,238 b | 97,563 b | 23,959 b | +| 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-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 64b68c7aa..43934a6d3 100644 --- a/packages/bundle-size/chart.svg +++ b/packages/bundle-size/chart.svg @@ -43,14 +43,14 @@ 0 KiB - + protobuf-es -protobuf-es 14.65 KiB for 1 files -protobuf-es 15.33 KiB for 4 files -protobuf-es 15.81 KiB for 8 files -protobuf-es 18.08 KiB for 16 files -protobuf-es 23.4 KiB for 32 files +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 diff --git a/packages/protobuf-test/src/reflect/reflect.test.ts b/packages/protobuf-test/src/reflect/reflect.test.ts index 6e67e9786..f9307016f 100644 --- a/packages/protobuf-test/src/reflect/reflect.test.ts +++ b/packages/protobuf-test/src/reflect/reflect.test.ts @@ -608,205 +608,4 @@ describe("ReflectMessage", () => { ); }); }); - describe("addListItem()", () => { - const desc = proto3_ts.Proto3MessageDesc; - let msg: proto3_ts.Proto3Message; - let r: ReflectMessage; - beforeEach(() => { - msg = create(desc); - r = reflect(desc, msg); - }); - test("adds valid item to repeatedStringField", () => { - const f = desc.field.repeatedStringField; - assert(f.fieldKind == "list"); - const err = r.addListItem(f, "abc"); - expect(err).toBeUndefined(); - expect(msg.repeatedStringField).toStrictEqual(["abc"]); - }); - test("adds unknown value for open enum", () => { - const f = desc.field.repeatedEnumField; - assert(f.fieldKind == "list"); - const err = r.addListItem(f, 99); - expect(err).toBeUndefined(); - expect(msg.repeatedEnumField).toStrictEqual([99]); - }); - test("adds bigint, number, and string as bigint", () => { - const f = desc.field.repeatedInt64Field; - assert(f.fieldKind == "list"); - r.addListItem(f, protoInt64.parse(1)); - r.addListItem(f, 2); - r.addListItem(f, "3"); - expect(msg.repeatedInt64Field).toStrictEqual([ - protoInt64.parse(1), - protoInt64.parse(2), - protoInt64.parse(3), - ]); - }); - test("adds bigint, number, and string as string for jstype=JS_STRING", () => { - const f = desc.field.repeatedInt64JsStringField; - assert(f.fieldKind == "list"); - r.addListItem(f, protoInt64.parse(1)); - r.addListItem(f, 2); - r.addListItem(f, "3"); - expect(msg.repeatedInt64JsStringField).toStrictEqual(["1", "2", "3"]); - }); - test("throws error on foreign field", async () => { - const foreignMessage = await compileMessage(` - syntax="proto3"; - message Foreign { repeated string foreign = 1;} - `); - const foreignField = foreignMessage.fields[0]; - assert(foreignField.fieldKind == "list"); - expect(() => r.addListItem(foreignField, "value")).toThrow( - /^cannot use field Foreign.foreign with message spec.Proto3Message$/, - ); - }); - describe("returns error on invalid item", () => { - test("bool for repeatedStringField", () => { - const f = desc.field.repeatedStringField; - assert(f.fieldKind == "list"); - const err = r.addListItem(f, true); - expect(err?.message).toMatch( - /^list item #1: expected string, got true$/, - ); - expect(err?.name).toMatch("FieldValueInvalidError"); - }); - test("number out of range for repeatedInt32Field", () => { - const f = desc.field.repeatedInt32Field; - assert(f.fieldKind == "list"); - const err = r.addListItem(f, Number.MAX_SAFE_INTEGER); - expect(err?.message).toMatch( - /^list item #1: expected number \(int32\): 9007199254740991 out of range/, - ); - expect(err?.name).toMatch("FieldValueInvalidError"); - }); - test("message for repeatedMessageField", () => { - const f = desc.field.repeatedMessageField; - assert(f.fieldKind == "list"); - // @ts-expect-error ignore to test runtime behavior - const err = r.addListItem(f, create(example_ts.UserDesc)); - expect(err?.message).toMatch( - /^list item #1: expected ReflectMessage \(spec.Proto3Message\), got message docs.User/, - ); - expect(err?.name).toMatch("FieldValueInvalidError"); - }); - test("wrong ReflectMessage for repeatedMessageField", () => { - const f = desc.field.repeatedMessageField; - assert(f.fieldKind == "list"); - const testMessage = reflect(example_ts.UserDesc); - const err = r.addListItem(f, testMessage); - expect(err?.message).toMatch( - /^list item #1: expected ReflectMessage \(spec.Proto3Message\), got ReflectMessage \(docs.User\)/, - ); - expect(err?.name).toMatch("FieldValueInvalidError"); - }); - test("true for repeatedMessageField", () => { - const f = desc.field.repeatedMessageField; - assert(f.fieldKind == "list"); - const err = r.addListItem(f, true); - expect(err?.message).toMatch( - /^list item #1: expected ReflectMessage \(spec.Proto3Message\), got true/, - ); - expect(err?.name).toMatch("FieldValueInvalidError"); - }); - }); - }); - describe("setMapEntry()", () => { - const desc = proto3_ts.Proto3MessageDesc; - let msg: proto3_ts.Proto3Message; - let r: ReflectMessage; - beforeEach(() => { - msg = create(desc); - r = reflect(desc, msg); - }); - test("adds valid entry to mapStringStringField", () => { - const f = desc.field.mapStringStringField; - assert(f.fieldKind == "map"); - const err = r.setMapEntry(f, "key", "value"); - expect(err).toBeUndefined(); - expect(msg.mapStringStringField).toStrictEqual({ key: "value" }); - }); - test("throws error on foreign field", async () => { - const foreignMessage = await compileMessage(` - syntax="proto3"; - message Foreign { map foreign = 1;} - `); - const foreignField = foreignMessage.fields[0]; - if (foreignField.fieldKind != "map") { - throw new Error(); - } - expect(() => r.setMapEntry(foreignField, "key", "value")).toThrow( - /^cannot use field Foreign.foreign with message spec.Proto3Message$/, - ); - }); - test("adds bigint, number, and string value as bigint", () => { - const f = desc.field.mapInt64Int64Field; - assert(f.fieldKind == "map"); - expect( - r.setMapEntry(f, protoInt64.parse(1), protoInt64.parse(1)), - ).toBeUndefined(); - expect(r.setMapEntry(f, protoInt64.parse(2), 2)).toBeUndefined(); - expect(r.setMapEntry(f, protoInt64.parse(3), "3")).toBeUndefined(); - expect(Object.values(msg.mapInt64Int64Field)).toStrictEqual([ - protoInt64.parse(1), - protoInt64.parse(2), - protoInt64.parse(3), - ]); - }); - test("adds bigint, number, and string key as string", () => { - const f = desc.field.mapInt64Int64Field; - assert(f.fieldKind == "map"); - expect( - r.setMapEntry(f, protoInt64.parse(1), protoInt64.parse(1)), - ).toBeUndefined(); - expect(r.setMapEntry(f, 2, protoInt64.parse(1))).toBeUndefined(); - expect(r.setMapEntry(f, "3", protoInt64.parse(1))).toBeUndefined(); - expect(Object.keys(msg.mapInt64Int64Field)).toStrictEqual([ - "1", - "2", - "3", - ]); - }); - test("adds bool key as string", () => { - const f = desc.field.mapBoolBoolField; - assert(f.fieldKind == "map"); - expect(r.setMapEntry(f, true, true)).toBeUndefined(); - expect(r.setMapEntry(f, false, false)).toBeUndefined(); - expect(Object.keys(msg.mapBoolBoolField)).toStrictEqual([ - "true", - "false", - ]); - }); - describe("returns error on invalid value", () => { - test("wrong message", () => { - const f = desc.field.mapInt32MessageField; - assert(f.fieldKind == "map"); - const err = r.setMapEntry(f, 123, reflect(example_ts.UserDesc)); - expect(err?.message).toMatch( - /^map entry 123: expected ReflectMessage \(spec.Proto3Message\), got ReflectMessage \(docs.User\)/, - ); - expect(err?.name).toMatch("FieldValueInvalidError"); - }); - test("number out of range", () => { - const f = desc.field.mapInt32Int32Field; - assert(f.fieldKind == "map"); - const err = r.setMapEntry(f, 123, Number.MAX_SAFE_INTEGER); - expect(err?.message).toMatch( - /^map entry 123: expected number \(int32\): 9007199254740991 out of range/, - ); - expect(err?.name).toMatch("FieldValueInvalidError"); - }); - }); - describe("returns error on invalid key", () => { - test("number out of range", () => { - const f = desc.field.mapInt32Int32Field; - assert(f.fieldKind == "map"); - const err = r.setMapEntry(f, Number.MAX_SAFE_INTEGER, 123); - expect(err?.message).toMatch( - /^invalid map key: expected number \(int32\): 9007199254740991 out of range/, - ); - expect(err?.name).toMatch("FieldValueInvalidError"); - }); - }); - }); }); diff --git a/packages/protobuf/src/clone.ts b/packages/protobuf/src/clone.ts index 6acb4c193..cc842a187 100644 --- a/packages/protobuf/src/clone.ts +++ b/packages/protobuf/src/clone.ts @@ -44,18 +44,21 @@ function cloneReflect(i: ReflectMessage): ReflectMessage { break; } case "list": + // eslint-disable-next-line no-case-declarations + const list = o.get(f); for (const item of i.get(f)) { - // TODO fix type error - // @ts-expect-error TODO - const err = o.addListItem(f, cloneSingular(f, item)); + const err = list.add(cloneSingular(f, item)); if (err) { throw err; } } break; case "map": + // eslint-disable-next-line no-case-declarations + const map = o.get(f); for (const entry of i.get(f).entries()) { - const err = o.setMapEntry(f, entry[0], cloneSingular(f, entry[1])); + // @ts-expect-error TODO fix type error + const err = map.set(entry[0], cloneSingular(f, entry[1])); if (err) { throw err; } diff --git a/packages/protobuf/src/from-binary.ts b/packages/protobuf/src/from-binary.ts index 9d5153e0c..083c890e1 100644 --- a/packages/protobuf/src/from-binary.ts +++ b/packages/protobuf/src/from-binary.ts @@ -14,7 +14,12 @@ import { type DescField, type DescMessage, ScalarType } from "./descriptors.js"; import type { MessageShape } from "./types.js"; -import type { MapEntryKey, ReflectMessage } from "./reflect/index.js"; +import type { + MapEntryKey, + ReflectList, + ReflectMap, + ReflectMessage, +} from "./reflect/index.js"; import { scalarZeroValue } from "./reflect/scalar.js"; import type { ScalarValue } from "./reflect/scalar.js"; import { reflect } from "./reflect/reflect.js"; @@ -77,7 +82,7 @@ export function mergeFromBinary( target: MessageShape, bytes: Uint8Array, options?: Partial, -): void { +): MessageShape { readMessage( reflect(messageDesc, target), new BinaryReader(bytes), @@ -85,6 +90,7 @@ export function mergeFromBinary( false, bytes.byteLength, ); + return target; } /** @@ -154,21 +160,21 @@ export function readField( ); break; case "list": - readListField(message, reader, options, field, wireType); + readListField(reader, wireType, message.get(field), options); break; case "map": - readMapEntry(message, field, reader, options); + readMapEntry(reader, message.get(field), options); break; } } // Read a map field, expecting key field = 1, value field = 2 function readMapEntry( - message: ReflectMessage, - field: DescField & { fieldKind: "map" }, reader: BinaryReader, + map: ReflectMap, options: BinaryReadOptions, ): void { + const field = map.field(); let key: MapEntryKey | undefined, val: ScalarValue | ReflectMessage | undefined; const end = reader.pos + reader.uint32(); @@ -209,21 +215,18 @@ function readMapEntry( break; } } - message.setMapEntry(field, key, val); + map.set(key, val); } function readListField( - message: ReflectMessage, reader: BinaryReader, - options: BinaryReadOptions, - field: DescField & { fieldKind: "list" }, wireType: WireType, + list: ReflectList, + options: BinaryReadOptions, ) { + const field = list.field(); if (field.listKind === "message") { - message.addListItem( - field, - readMessageField(reader, options, field) as never, // TODO: Investigate the type issue. - ); + list.add(readMessageField(reader, options, field)); return; } const scalarType = field.scalar ?? ScalarType.INT32; @@ -232,12 +235,12 @@ function readListField( scalarType != ScalarType.STRING && scalarType != ScalarType.BYTES; if (!packed) { - message.addListItem(field, readScalar(reader, scalarType)); + list.add(readScalar(reader, scalarType)); return; } const e = reader.uint32() + reader.pos; while (reader.pos < e) { - message.addListItem(field, readScalar(reader, scalarType)); + list.add(readScalar(reader, scalarType)); } } diff --git a/packages/protobuf/src/from-json.ts b/packages/protobuf/src/from-json.ts index e1fdb86c1..3d12fa5df 100644 --- a/packages/protobuf/src/from-json.ts +++ b/packages/protobuf/src/from-json.ts @@ -26,7 +26,11 @@ import type { JsonValue } from "./json-value.js"; import { protoInt64 } from "./proto-int64.js"; import { create } from "./create.js"; import type { Registry } from "./registry.js"; -import type { ReflectMessage } from "./reflect/reflect-types.js"; +import type { + ReflectList, + ReflectMap, + ReflectMessage, +} from "./reflect/reflect-types.js"; import { reflect } from "./reflect/reflect.js"; import { FieldError, isFieldError } from "./reflect/error.js"; import { formatVal } from "./reflect/reflect-check.js"; @@ -110,8 +114,8 @@ export function mergeFromJsonString( target: MessageShape, json: string, options?: Partial, -): void { - mergeFromJson( +): MessageShape { + return mergeFromJson( messageDesc, target, parseJsonString(json, messageDesc.typeName), @@ -156,7 +160,7 @@ export function mergeFromJson( target: MessageShape, json: JsonValue, options?: Partial, -): void { +): MessageShape { try { readMessage(reflect(messageDesc, target), json, makeReadOptions(options)); } catch (e) { @@ -168,6 +172,7 @@ export function mergeFromJson( } throw e; } + return target; } function readMessage( @@ -244,23 +249,19 @@ function readField( readMessageField(msg, field, json, opts); break; case "list": - readListField(msg, field, json, opts); + readListField(msg.get(field), json, opts); break; case "map": - readMapField(msg, field, json, opts); + readMapField(msg.get(field), json, opts); break; } } -function readMapField( - msg: ReflectMessage, - field: DescField & { fieldKind: "map" }, - json: JsonValue, - opts: JsonReadOptions, -) { +function readMapField(map: ReflectMap, json: JsonValue, opts: JsonReadOptions) { if (json === null) { return; } + const field = map.field(); if (typeof json != "object" || Array.isArray(json)) { throw new FieldError(field, "expected object, got " + formatVal(json)); } @@ -268,59 +269,47 @@ function readMapField( if (jsonMapValue === null) { throw new FieldError(field, "map value must not be null"); } - const key = mapKeyFromJson(field.mapKey, jsonMapKey); + let value: unknown; switch (field.mapKind) { case "message": const msgValue = reflect(field.message); readMessage(msgValue, jsonMapValue, opts); - // TODO fix types - // @ts-expect-error TODO - const err = msg.setMapEntry(field, key, msgValue); - if (err) { - throw err; - } + value = msgValue; break; case "enum": - const enumValue = readEnum( + value = readEnum( field.enum, jsonMapValue, opts.ignoreUnknownFields, true, ); - if (enumValue !== tokenIgnoredUnknownEnum) { - // TODO fix types - // @ts-expect-error TODO - const err = msg.setMapEntry(field, key, enumValue); - if (err) { - throw err; - } + if (value === tokenIgnoredUnknownEnum) { + return; } break; case "scalar": - const err2 = msg.setMapEntry( - field, - // TODO fix types - // @ts-expect-error TODO - key, - scalarFromJson(field, jsonMapValue, true), - ); - if (err2) { - throw err2; - } + value = scalarFromJson(field, jsonMapValue, true); break; } + const key = mapKeyFromJson(field.mapKey, jsonMapKey); + // TODO fix types + // @ts-expect-error TODO + const err = map.set(key, value); + if (err) { + throw err; + } } } function readListField( - msg: ReflectMessage, - field: DescField & { fieldKind: "list" }, + list: ReflectList, json: JsonValue, opts: JsonReadOptions, ) { if (json === null) { return; } + const field = list.field(); if (!Array.isArray(json)) { throw new FieldError(field, "expected Array, got " + formatVal(json)); } @@ -332,7 +321,7 @@ function readListField( case "message": const msgValue = reflect(field.message); readMessage(msgValue, jsonItem, opts); - msg.addListItem(field, msgValue); + list.add(msgValue); break; case "enum": const enumValue = readEnum( @@ -342,14 +331,11 @@ function readListField( true, ); if (enumValue !== tokenIgnoredUnknownEnum) { - msg.addListItem(field, enumValue); + list.add(enumValue); } break; case "scalar": - const err = msg.addListItem( - field, - scalarFromJson(field, jsonItem, true), - ); + const err = list.add(scalarFromJson(field, jsonItem, true)); if (err) { throw err; } diff --git a/packages/protobuf/src/reflect/reflect-types.ts b/packages/protobuf/src/reflect/reflect-types.ts index 3afd80671..32b958202 100644 --- a/packages/protobuf/src/reflect/reflect-types.ts +++ b/packages/protobuf/src/reflect/reflect-types.ts @@ -143,23 +143,6 @@ export interface ReflectMessage { value: ReflectSetValue, ): FieldError | undefined; - /** - * Add an item to a list field. - */ - addListItem( - field: Field, - value: ReflectAddListItemValue, - ): FieldError | undefined; - - /** - * Set a map entry. - */ - setMapEntry( - field: Field, - key: MapEntryKey, - value: ReflectSetMapEntryValue, - ): FieldError | undefined; - /** * Returns the unknown fields of the message. */ @@ -305,25 +288,3 @@ export type ReflectSetValue = ( Field extends { fieldKind: "scalar"; scalar: infer T } ? ScalarValue : never ); - -/** - * The type of the "value" argument of ReflectMessage.addListItem() - */ -// prettier-ignore -export type ReflectAddListItemValue = ( - Field extends { listKind: "scalar"; scalar: infer T } ? ScalarValue : - Field extends { listKind: "enum" } ? enumVal : - Field extends { listKind: "message" } ? ReflectMessage : - never -); - -/** - * The type of the "value" argument of ReflectMessage.setMapEntry() - */ -// prettier-ignore -export type ReflectSetMapEntryValue = ( - Field extends { mapKind: "enum" } ? enumVal : - Field extends { mapKind: "message" } ? ReflectMessage : - Field extends { mapKind: "scalar"; scalar: infer T } ? ScalarValue : - never -); diff --git a/packages/protobuf/src/reflect/reflect.ts b/packages/protobuf/src/reflect/reflect.ts index 7fc8054e7..d22b0452f 100644 --- a/packages/protobuf/src/reflect/reflect.ts +++ b/packages/protobuf/src/reflect/reflect.ts @@ -23,8 +23,6 @@ import { checkField, checkListItem, checkMapEntry } from "./reflect-check.js"; import { FieldError } from "./error.js"; import type { MapEntryKey, - ReflectAddListItemValue, - ReflectSetMapEntryValue, ReflectGetValue, ReflectList, ReflectMap, @@ -32,14 +30,12 @@ import type { ReflectSetValue, } from "./reflect-types.js"; import { - unsafeAddListItem, unsafeClear, unsafeGet, unsafeIsSet, unsafeLocal, unsafeOneofCase, unsafeSet, - unsafeSetMapEntry, } from "./unsafe.js"; import { create } from "../create.js"; import { isWrapper, isWrapperDesc } from "../wkt/wrappers.js"; @@ -94,6 +90,8 @@ class ReflectMessageImpl implements ReflectMessage { private readonly check: boolean; private _fieldsByNumber: Map | undefined; private _sortedFields: DescField[] | undefined; + private lists = new Map(); + private maps = new Map(); constructor( messageDesc: Desc, @@ -138,19 +136,29 @@ class ReflectMessageImpl implements ReflectMessage { let value = unsafeGet(this.message, field); switch (field.fieldKind) { case "list": - return new ReflectListImpl( - field, - value as unknown[], - this.check, - ) as ReflectGetValue; + // eslint-disable-next-line no-case-declarations + let list = this.lists.get(field); + if (!list || list[unsafeLocal] !== value) { + this.lists.set( + field, + (list = new ReflectListImpl(field, value as unknown[], this.check)), + ); + } + return list as ReflectGetValue; case "map": - // TODO fix types - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return reflectMap( - field, - value as Record, - this.check, - ) as any; // eslint-disable-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return + // eslint-disable-next-line no-case-declarations + let map = this.maps.get(field); + if (!map || map[unsafeLocal] !== value) { + this.maps.set( + field, + (map = new ReflectMapImpl( + field, + value as Record, + this.check, + )), + ); + } + return map as ReflectGetValue; case "message": if ( value !== undefined && @@ -202,52 +210,6 @@ class ReflectMessageImpl implements ReflectMessage { return undefined; } - addListItem< - Field extends DescField & { - fieldKind: "list"; - }, - >( - field: Field, - value: ReflectAddListItemValue, - ): FieldError | undefined { - assertOwn(this.message, field); - assertKind(field, "list"); - if (this.check) { - if (checkListItem(field, 0, value)) { - const arr = unsafeGet(this.message, field) as unknown[]; - return checkListItem(field, arr.length, value); - } - } - unsafeAddListItem(this.message, field, listItemToLocal(field, value)); - return undefined; - } - - setMapEntry< - Field extends DescField & { - fieldKind: "map"; - }, - >( - field: Field, - key: MapEntryKey, - value: ReflectSetMapEntryValue, - ): FieldError | undefined { - assertOwn(this.message, field); - assertKind(field, "map"); - if (this.check) { - const err = checkMapEntry(field, key, value); - if (err) { - return err; - } - } - unsafeSetMapEntry( - this.message, - field, - mapKeyToLocal(key), - mapValueToLocal(field, value), - ); - return undefined; - } - getUnknown(): UnknownField[] | undefined { return this.message.$unknown; } @@ -257,16 +219,6 @@ class ReflectMessageImpl implements ReflectMessage { } } -function assertKind(field: DescField, kind: DescField["fieldKind"]) { - if (field.fieldKind != kind) { - throw new FieldError( - field, - `${field.toString()} is ${field.fieldKind}`, - "ForeignFieldError", - ); - } -} - function assertOwn(owner: Message, member: DescField | DescOneof) { if (member.parent.typeName !== owner.$typeName) { throw new FieldError( diff --git a/packages/protobuf/src/reflect/unsafe.ts b/packages/protobuf/src/reflect/unsafe.ts index 6f0cf0c75..4f2936a22 100644 --- a/packages/protobuf/src/reflect/unsafe.ts +++ b/packages/protobuf/src/reflect/unsafe.ts @@ -163,30 +163,3 @@ export function unsafeClear( } } } - -/** - * Add an item to a list field. - * - * @private - */ -export function unsafeAddListItem( - target: Record, - field: DescField & { fieldKind: "list" }, - value: unknown, -) { - (target[field.localName] as unknown[]).push(value); -} - -/** - * Set a map entry. - * - * @private - */ -export function unsafeSetMapEntry( - target: Record, - field: DescField & { fieldKind: "map" }, - key: string | number, - value: unknown, -) { - (target[field.localName] as Record)[key] = value; -}