lambda-live-debugger
Version:
Debug Lambda functions locally like it is running in the cloud
177 lines (176 loc) • 7.07 kB
JavaScript
import { determineTimestampFormat } from "@smithy/core/protocols";
import { NormalizedSchema } from "@smithy/core/schema";
import { dateToUtcString, generateIdempotencyToken, LazyJsonString, NumericValue } from "@smithy/core/serde";
import { toBase64 } from "@smithy/util-base64";
import { SerdeContextConfig } from "../ConfigurableSerdeContext";
import { JsonReplacer } from "./jsonReplacer";
export class JsonShapeSerializer extends SerdeContextConfig {
settings;
buffer;
useReplacer = false;
rootSchema;
constructor(settings) {
super();
this.settings = settings;
}
write(schema, value) {
this.rootSchema = NormalizedSchema.of(schema);
this.buffer = this._write(this.rootSchema, value);
}
writeDiscriminatedDocument(schema, value) {
this.write(schema, value);
if (typeof this.buffer === "object") {
this.buffer.__type = NormalizedSchema.of(schema).getName(true);
}
}
flush() {
const { rootSchema, useReplacer } = this;
this.rootSchema = undefined;
this.useReplacer = false;
if (rootSchema?.isStructSchema() || rootSchema?.isDocumentSchema()) {
if (!useReplacer) {
return JSON.stringify(this.buffer);
}
const replacer = new JsonReplacer();
return replacer.replaceInJson(JSON.stringify(this.buffer, replacer.createReplacer(), 0));
}
return this.buffer;
}
_write(schema, value, container) {
const isObject = value !== null && typeof value === "object";
const ns = NormalizedSchema.of(schema);
if (isObject) {
if (ns.isStructSchema()) {
const record = value;
const out = {};
const { jsonName } = this.settings;
let nameMap = void 0;
if (jsonName) {
nameMap = {};
}
for (const [memberName, memberSchema] of ns.structIterator()) {
const serializableValue = this._write(memberSchema, record[memberName], ns);
if (serializableValue !== undefined) {
let targetKey = memberName;
if (jsonName) {
targetKey = memberSchema.getMergedTraits().jsonName ?? memberName;
nameMap[memberName] = targetKey;
}
out[targetKey] = serializableValue;
}
}
if (ns.isUnionSchema() && Object.keys(out).length === 0) {
const { $unknown } = record;
if (Array.isArray($unknown)) {
const [k, v] = $unknown;
out[k] = this._write(15, v);
}
}
else if (typeof record.__type === "string") {
for (const [k, v] of Object.entries(record)) {
const targetKey = jsonName ? nameMap[k] ?? k : k;
if (!(targetKey in out)) {
out[targetKey] = this._write(15, v);
}
}
}
return out;
}
if (Array.isArray(value) && ns.isListSchema()) {
const listMember = ns.getValueSchema();
const out = [];
const sparse = !!ns.getMergedTraits().sparse;
for (const item of value) {
if (sparse || item != null) {
out.push(this._write(listMember, item));
}
}
return out;
}
if (ns.isMapSchema()) {
const mapMember = ns.getValueSchema();
const out = {};
const sparse = !!ns.getMergedTraits().sparse;
for (const [_k, _v] of Object.entries(value)) {
if (sparse || _v != null) {
out[_k] = this._write(mapMember, _v);
}
}
return out;
}
if (value instanceof Uint8Array && (ns.isBlobSchema() || ns.isDocumentSchema())) {
if (ns === this.rootSchema) {
return value;
}
return (this.serdeContext?.base64Encoder ?? toBase64)(value);
}
if (value instanceof Date && (ns.isTimestampSchema() || ns.isDocumentSchema())) {
const format = determineTimestampFormat(ns, this.settings);
switch (format) {
case 5:
return value.toISOString().replace(".000Z", "Z");
case 6:
return dateToUtcString(value);
case 7:
return value.getTime() / 1000;
default:
console.warn("Missing timestamp format, using epoch seconds", value);
return value.getTime() / 1000;
}
}
if (value instanceof NumericValue) {
this.useReplacer = true;
}
}
if (value === null && container?.isStructSchema()) {
return void 0;
}
if (ns.isStringSchema()) {
if (typeof value === "undefined" && ns.isIdempotencyToken()) {
return generateIdempotencyToken();
}
const mediaType = ns.getMergedTraits().mediaType;
if (value != null && mediaType) {
const isJson = mediaType === "application/json" || mediaType.endsWith("+json");
if (isJson) {
return LazyJsonString.from(value);
}
}
return value;
}
if (typeof value === "number" && ns.isNumericSchema()) {
if (Math.abs(value) === Infinity || isNaN(value)) {
return String(value);
}
return value;
}
if (typeof value === "string" && ns.isBlobSchema()) {
if (ns === this.rootSchema) {
return value;
}
return (this.serdeContext?.base64Encoder ?? toBase64)(value);
}
if (typeof value === "bigint") {
this.useReplacer = true;
}
if (ns.isDocumentSchema()) {
if (isObject) {
const out = Array.isArray(value) ? [] : {};
for (const [k, v] of Object.entries(value)) {
if (v instanceof NumericValue) {
this.useReplacer = true;
out[k] = v;
}
else {
out[k] = this._write(ns, v);
}
}
return out;
}
else {
return structuredClone(value);
}
}
return value;
}
}