Skip to content

Commit

Permalink
fix: support "infinitely" large payloads (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
jacob-ebey authored Aug 10, 2024
1 parent 6606948 commit 8b602a3
Show file tree
Hide file tree
Showing 2 changed files with 331 additions and 209 deletions.
225 changes: 115 additions & 110 deletions src/flatten.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,132 +42,137 @@ function stringify(this: ThisEncode, input: unknown, index: number) {
const { deferred, plugins } = this;
const str = this.stringified;

const partsForObj = (obj: any) =>
Object.keys(obj)
.map((k) => `"${flatten.call(this, k)}":${flatten.call(this, obj[k])}`)
.join(",");
const stack: [unknown, number][] = [[input, index]];
while (stack.length > 0) {
const [input, index] = stack.pop()!;

switch (typeof input) {
case "boolean":
case "number":
case "string":
str[index] = JSON.stringify(input);
break;
case "bigint":
str[index] = `["${TYPE_BIGINT}","${input}"]`;
break;
case "symbol": {
const keyFor = Symbol.keyFor(input);
if (!keyFor)
throw new Error(
"Cannot encode symbol unless created with Symbol.for()"
);
str[index] = `["${TYPE_SYMBOL}",${JSON.stringify(keyFor)}]`;
break;
}
case "object": {
if (!input) {
str[index] = `${NULL}`;
const partsForObj = (obj: any) =>
Object.keys(obj)
.map((k) => `"${flatten.call(this, k)}":${flatten.call(this, obj[k])}`)
.join(",");

switch (typeof input) {
case "boolean":
case "number":
case "string":
str[index] = JSON.stringify(input);
break;
case "bigint":
str[index] = `["${TYPE_BIGINT}","${input}"]`;
break;
case "symbol": {
const keyFor = Symbol.keyFor(input);
if (!keyFor)
throw new Error(
"Cannot encode symbol unless created with Symbol.for()"
);
str[index] = `["${TYPE_SYMBOL}",${JSON.stringify(keyFor)}]`;
break;
}
case "object": {
if (!input) {
str[index] = `${NULL}`;
break;
}

const isArray = Array.isArray(input);
let pluginHandled = false;
if (!isArray && plugins) {
for (const plugin of plugins) {
const pluginResult = plugin(input);
if (Array.isArray(pluginResult)) {
pluginHandled = true;
const [pluginIdentifier, ...rest] = pluginResult;
str[index] = `[${JSON.stringify(pluginIdentifier)}`;
if (rest.length > 0) {
str[index] += `,${rest
.map((v) => flatten.call(this, v))
.join(",")}`;
const isArray = Array.isArray(input);
let pluginHandled = false;
if (!isArray && plugins) {
for (const plugin of plugins) {
const pluginResult = plugin(input);
if (Array.isArray(pluginResult)) {
pluginHandled = true;
const [pluginIdentifier, ...rest] = pluginResult;
str[index] = `[${JSON.stringify(pluginIdentifier)}`;
if (rest.length > 0) {
str[index] += `,${rest
.map((v) => flatten.call(this, v))
.join(",")}`;
}
str[index] += "]";
break;
}
str[index] += "]";
break;
}
}
}

if (!pluginHandled) {
let result = isArray ? "[" : "{";
if (isArray) {
for (let i = 0; i < input.length; i++)
result +=
(i ? "," : "") +
(i in input ? flatten.call(this, input[i]) : HOLE);
str[index] = `${result}]`;
} else if (input instanceof Date) {
str[index] = `["${TYPE_DATE}",${input.getTime()}]`;
} else if (input instanceof URL) {
str[index] = `["${TYPE_URL}",${JSON.stringify(input.href)}]`;
} else if (input instanceof RegExp) {
str[index] = `["${TYPE_REGEXP}",${JSON.stringify(
input.source
)},${JSON.stringify(input.flags)}]`;
} else if (input instanceof Set) {
if (input.size > 0) {
str[index] = `["${TYPE_SET}",${[...input]
.map((val) => flatten.call(this, val))
.join(",")}]`;
} else {
str[index] = `["${TYPE_SET}"]`;
}
} else if (input instanceof Map) {
if (input.size > 0) {
str[index] = `["${TYPE_MAP}",${[...input]
.flatMap(([k, v]) => [
flatten.call(this, k),
flatten.call(this, v),
])
.join(",")}]`;
if (!pluginHandled) {
let result = isArray ? "[" : "{";
if (isArray) {
for (let i = 0; i < input.length; i++)
result +=
(i ? "," : "") +
(i in input ? flatten.call(this, input[i]) : HOLE);
str[index] = `${result}]`;
} else if (input instanceof Date) {
str[index] = `["${TYPE_DATE}",${input.getTime()}]`;
} else if (input instanceof URL) {
str[index] = `["${TYPE_URL}",${JSON.stringify(input.href)}]`;
} else if (input instanceof RegExp) {
str[index] = `["${TYPE_REGEXP}",${JSON.stringify(
input.source
)},${JSON.stringify(input.flags)}]`;
} else if (input instanceof Set) {
if (input.size > 0) {
str[index] = `["${TYPE_SET}",${[...input]
.map((val) => flatten.call(this, val))
.join(",")}]`;
} else {
str[index] = `["${TYPE_SET}"]`;
}
} else if (input instanceof Map) {
if (input.size > 0) {
str[index] = `["${TYPE_MAP}",${[...input]
.flatMap(([k, v]) => [
flatten.call(this, k),
flatten.call(this, v),
])
.join(",")}]`;
} else {
str[index] = `["${TYPE_MAP}"]`;
}
} else if (input instanceof Promise) {
str[index] = `["${TYPE_PROMISE}",${index}]`;
deferred[index] = input;
} else if (input instanceof Error) {
str[index] = `["${TYPE_ERROR}",${JSON.stringify(input.message)}`;
if (input.name !== "Error") {
str[index] += `,${JSON.stringify(input.name)}`;
}
str[index] += "]";
} else if (Object.getPrototypeOf(input) === null) {
str[index] = `["${TYPE_NULL_OBJECT}",{${partsForObj(input)}}]`;
} else if (isPlainObject(input)) {
str[index] = `{${partsForObj(input)}}`;
} else {
str[index] = `["${TYPE_MAP}"]`;
throw new Error("Cannot encode object with prototype");
}
} else if (input instanceof Promise) {
str[index] = `["${TYPE_PROMISE}",${index}]`;
deferred[index] = input;
} else if (input instanceof Error) {
str[index] = `["${TYPE_ERROR}",${JSON.stringify(input.message)}`;
if (input.name !== "Error") {
str[index] += `,${JSON.stringify(input.name)}`;
}
str[index] += "]";
} else if (Object.getPrototypeOf(input) === null) {
str[index] = `["${TYPE_NULL_OBJECT}",{${partsForObj(input)}}]`;
} else if (isPlainObject(input)) {
str[index] = `{${partsForObj(input)}}`;
} else {
throw new Error("Cannot encode object with prototype");
}
break;
}
break;
}
default: {
const isArray = Array.isArray(input);
let pluginHandled = false;
if (!isArray && plugins) {
for (const plugin of plugins) {
const pluginResult = plugin(input);
if (Array.isArray(pluginResult)) {
pluginHandled = true;
const [pluginIdentifier, ...rest] = pluginResult;
str[index] = `[${JSON.stringify(pluginIdentifier)}`;
if (rest.length > 0) {
str[index] += `,${rest
.map((v) => flatten.call(this, v))
.join(",")}`;
default: {
const isArray = Array.isArray(input);
let pluginHandled = false;
if (!isArray && plugins) {
for (const plugin of plugins) {
const pluginResult = plugin(input);
if (Array.isArray(pluginResult)) {
pluginHandled = true;
const [pluginIdentifier, ...rest] = pluginResult;
str[index] = `[${JSON.stringify(pluginIdentifier)}`;
if (rest.length > 0) {
str[index] += `,${rest
.map((v) => flatten.call(this, v))
.join(",")}`;
}
str[index] += "]";
break;
}
str[index] += "]";
break;
}
}
}

if (!pluginHandled) {
throw new Error("Cannot encode function or unexpected type");
if (!pluginHandled) {
throw new Error("Cannot encode function or unexpected type");
}
}
}
}
Expand Down
Loading

0 comments on commit 8b602a3

Please sign in to comment.