UNPKG

nestjs-protobuf-es

Version:

Protobuf-ES integration for NestJS

146 lines (145 loc) 5.72 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.initMessage = initMessage; exports.populate = populate; exports.populateInPlace = populateInPlace; const protobuf_1 = require("@bufbuild/protobuf"); const reflect_1 = require("@bufbuild/protobuf/reflect"); const wkt_1 = require("@bufbuild/protobuf/wkt"); const valid_types_1 = require("./valid-types"); /** * Create a new message instance. * * This is similar to `create` from `@bufbuild/protobuf`, with the difference that it handles JS dates and converts them to `Timestamp`. */ function initMessage(schema, init) { if ((0, protobuf_1.isMessage)(init, schema)) { return init; } if (init == null) { return (0, protobuf_1.create)(schema); } if (schema.typeName === 'google.protobuf.Timestamp') { if (init instanceof Date) { return (0, wkt_1.timestampFromDate)(init); } return (0, protobuf_1.create)(schema, init); } // biome-ignore lint/suspicious/noExplicitAny: `any` is the best choice for dynamic access const target = init; for (const member of schema.members) { let field; // biome-ignore lint/suspicious/noExplicitAny: `any` is the best choice for dynamic access let value; if (member.kind === 'field') { field = member; value = target[field.localName]; } else { const caseName = target[member.localName]?.case; if (caseName === undefined) continue; field = member.fields.find((f) => f.localName === caseName); value = target[member.localName].value; if (!field) continue; } if (value === undefined) continue; if (field.fieldKind === 'message') { const msg = initMessage(field.message, value); if (field.oneof) { target[field.oneof.localName].value = msg; } else { target[field.localName] = msg; } } else if (field.fieldKind === 'map') { if (field.mapKind !== 'message' || typeof value !== 'object' || value === null) continue; for (const key of Object.keys(value)) { value[key] = initMessage(field.message, value[key]); } } else if (field.fieldKind === 'list') { if (field.listKind !== 'message' || !Array.isArray(value) || value.length === 0) continue; for (let i = 0; i < value.length; i++) { value[i] = initMessage(field.message, value[i]); } } } return (0, protobuf_1.create)(schema, target); } /** * Create a deep copy of the message and populate it and/or convert `Timestamp` fields into JS `Date`. * * This method may be slow because of the cloning. * If you don't worry about modifying the original message, you can use `populateInPlace` instead. */ function populate(schema, message, options) { return populateInPlace(schema, (0, protobuf_1.clone)(schema, message), options); } /** * Deep populate the message and/or convert `Timestamp` fields into JS `Date`. * * This method is more performant, but mutates the original message. */ function populateInPlace(schema, message, options) { const r = (0, reflect_1.isReflectMessage)(message) ? message : (0, reflect_1.reflect)(schema, message, false); if (schema.typeName === 'google.protobuf.Timestamp') { if (options?.jsDates) { return (0, wkt_1.timestampDate)(r.message); } // Timestamp already has every field populated. return r.message; } for (const member of r.members) { let field; if (member.kind === 'field') { field = member; } else { const oneofField = r.oneofCase(member); if (!oneofField) continue; field = oneofField; } if (field.fieldKind === 'message') { if (options?.messages || (options?.jsDates && r.isSet(field)) || (options?.validTypes !== false && (0, valid_types_1.isFieldRequired)(field))) { r.set(field, populateInPlace(field.message, r.get(field), options)); } } else { // biome-ignore lint/suspicious/noExplicitAny: `any` is the best choice for dynamic access const target = r.message; const value = field.oneof ? target[field.oneof.localName].value : target[field.localName]; if (field.fieldKind === 'scalar') { if (value === undefined && options?.scalars) { r.set(field, (0, reflect_1.scalarZeroValue)(field.scalar, field.longAsString)); } } else if (field.fieldKind === 'enum') { if (value === undefined && options?.scalars) { r.set(field, field.enum.values[0].number); } } else if ((field.fieldKind === 'map' && field.mapKind === 'message') || (field.fieldKind === 'list' && field.listKind === 'message')) { if (options?.validTypes !== false || options.messages || options.jsDates) { const mapOrList = r.get(field); for (const entry of mapOrList.entries()) { const msg = populateInPlace(field.message, entry[1], options); mapOrList.set(entry[0], msg); } } } } } return r.message; }