From f0f1537315bd25793ad8ebc749e95c7ad2dd1fa4 Mon Sep 17 00:00:00 2001 From: Jacob Ebey Date: Mon, 12 Aug 2024 13:21:34 -0700 Subject: [PATCH] feat: maintain property order (#45) --- src/flatten.ts | 2 +- src/turbo-stream.spec.ts | 24 ++++++++++++++++++++++++ src/unflatten.ts | 8 ++++---- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/flatten.ts b/src/flatten.ts index 5112d12..66abf99 100644 --- a/src/flatten.ts +++ b/src/flatten.ts @@ -48,7 +48,7 @@ function stringify(this: ThisEncode, input: unknown, index: number) { const partsForObj = (obj: any) => Object.keys(obj) - .map((k) => `"${flatten.call(this, k)}":${flatten.call(this, obj[k])}`) + .map((k) => `"_${flatten.call(this, k)}":${flatten.call(this, obj[k])}`) .join(","); switch (typeof input) { diff --git a/src/turbo-stream.spec.ts b/src/turbo-stream.spec.ts index 319fdba..a255b23 100644 --- a/src/turbo-stream.spec.ts +++ b/src/turbo-stream.spec.ts @@ -175,6 +175,30 @@ test("should encode and decode large payload", async () => { expect(output).toEqual(input); }); +test("should encode and decode object maintaining property order for re-used keys", async () => { + const input = [ + { a: "a value 1", b: "b value" }, + { c: "c value", a: "a value 2" }, + ]; + const output = await quickDecode(encode(input)); + expect(JSON.stringify(output)).toEqual(JSON.stringify(input)); +}); + +test("should encode and decode null prototype object maintaining property order for re-used keys", async () => { + const input = Object.create(null); + const test1 = Object.create(null); + test1.a = "a value 1"; + test1.b = "b value"; + input.test1 = test1; + const test2 = Object.create(null); + test2.c = "c value"; + test2.a = "a value 2"; + input.test2 = test2; + + const output = await quickDecode(encode(input)); + expect(JSON.stringify(output)).toEqual(JSON.stringify(input)); +}); + test("should encode and decode object and dedupe object key, value, and promise value", async () => { const input = { foo: "bar", bar: "bar", baz: Promise.resolve("bar") }; const output = await quickDecode(encode(input)); diff --git a/src/unflatten.ts b/src/unflatten.ts index 3f10fa6..5dbb7b2 100644 --- a/src/unflatten.ts +++ b/src/unflatten.ts @@ -152,7 +152,7 @@ function hydrate(this: ThisDecode, index: number): any { case TYPE_NULL_OBJECT: const obj = Object.create(null); hydrated[index] = obj; - for (const key in b) { + for (const key of Object.keys(b).reverse()) { const r: any[] = []; stack.push([ b[key], @@ -161,7 +161,7 @@ function hydrate(this: ThisDecode, index: number): any { }, ]); stack.push([ - Number(key), + Number(key.slice(1)), (k) => { r[0] = k; }, @@ -244,7 +244,7 @@ function hydrate(this: ThisDecode, index: number): any { const object: Record = {}; hydrated[index] = object; - for (const key in value) { + for (const key of Object.keys(value).reverse()) { const r: any[] = []; stack.push([ (value as Record)[key], @@ -253,7 +253,7 @@ function hydrate(this: ThisDecode, index: number): any { }, ]); stack.push([ - Number(key), + Number(key.slice(1)), (k) => { r[0] = k; },