Skip to content

Commit d5d8c2d

Browse files
committed
prototype implementation
1 parent 9a02db4 commit d5d8c2d

File tree

1 file changed

+195
-0
lines changed
  • src/js/packages/event-to-object/new

1 file changed

+195
-0
lines changed
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
// Note, the build command is: bun build index.ts --outfile=index.js
2+
// TODO: Test this with file uploads, form events, and other complex event types.
3+
// TODO: Investigate these potentially useful libraries:
4+
// https://www.npmjs.com/package/superserial#circular-reference
5+
// https://www.npmjs.com/package/@badcafe/jsonizer
6+
// https://www.npmjs.com/package/class-transformer#how-does-it-handle-circular-references
7+
// https://www.npmjs.com/package/compress-json
8+
9+
const stopSignal = { __stop__: true };
10+
11+
/**
12+
* Convert any object to a plain object.
13+
*/
14+
function convert(object: object, maxDepth: number = 3): object {
15+
// Begin conversion
16+
const convertedObj = {};
17+
for (const key in object) {
18+
// Skip keys that cannot be converted
19+
if (shouldIgnore(object[key], key)) {
20+
continue;
21+
}
22+
// Handle objects (potentially cyclical)
23+
else if (typeof object[key] === "object") {
24+
const result = deepClone(object[key], maxDepth);
25+
convertedObj[key] = result;
26+
}
27+
// Handle simple types (non-cyclical)
28+
else {
29+
convertedObj[key] = object[key];
30+
}
31+
}
32+
return convertedObj;
33+
}
34+
35+
/**
36+
* Recursively convert a complex object to a plain object.
37+
*/
38+
function deepClone(x: any, _maxDepth: number): object {
39+
const maxDepth = _maxDepth - 1;
40+
41+
// Return an indicator if maxDepth is reached
42+
if (maxDepth <= 0 && typeof x === "object") {
43+
return stopSignal;
44+
}
45+
46+
// Return early if already a plain object
47+
if (isPlainObject(x)) {
48+
return x;
49+
}
50+
51+
// Convert array-like class (e.g., NodeList, ClassList, HTMLCollection)
52+
if (
53+
Array.isArray(x) ||
54+
(typeof x?.length === "number" &&
55+
typeof x[Symbol.iterator] === "function" &&
56+
!Object.prototype.toString.call(x).includes("Map") &&
57+
!(x instanceof CSSStyleDeclaration))
58+
) {
59+
return classToArray(x, maxDepth);
60+
}
61+
62+
// Convert mapping-like class (e.g., Node, Map, Set)
63+
return classToObject(x, maxDepth);
64+
}
65+
66+
/**
67+
* Convert an array-like class to a plain array.
68+
*/
69+
function classToArray(x: any, maxDepth: number): Array<any> {
70+
const result: Array<any> = [];
71+
for (let i = 0; i < x.length; i++) {
72+
// Skip keys that cannot be converted
73+
if (shouldIgnore(x[i])) {
74+
continue;
75+
}
76+
// Only push objects as if we haven't reached max depth
77+
else if (typeof x[i] === "object") {
78+
const convertedItem = deepClone(x[i], maxDepth);
79+
if (convertedItem !== stopSignal) {
80+
result.push(convertedItem);
81+
}
82+
}
83+
// Add plain values if not skippable
84+
else {
85+
result.push(x[i]);
86+
}
87+
}
88+
return result;
89+
}
90+
91+
/**
92+
* Convert a mapping-like class to a plain JSON object.
93+
*/
94+
function classToObject(x: any, maxDepth: number): object {
95+
const result = {};
96+
for (const key in x) {
97+
// Skip keys that cannot be converted
98+
if (shouldIgnore(x[key], key, x)) {
99+
continue;
100+
}
101+
// Add objects as a property if we haven't reached max depth
102+
else if (typeof x[key] === "object") {
103+
const convertedObj = deepClone(x[key], maxDepth);
104+
if (convertedObj !== stopSignal) {
105+
result[key] = convertedObj;
106+
}
107+
}
108+
// Add plain values if not skippable
109+
else {
110+
result[key] = x[key];
111+
}
112+
}
113+
return result;
114+
}
115+
116+
/**
117+
* Check if a value is non-convertible or holds minimal value.
118+
*/
119+
function shouldIgnore(
120+
value: any,
121+
keyName: string = "",
122+
parent: any = undefined,
123+
): boolean {
124+
return (
125+
value === null ||
126+
value === undefined ||
127+
// value === "" ||
128+
typeof value === "function" ||
129+
value instanceof CSSStyleSheet ||
130+
value instanceof Window ||
131+
value instanceof Document ||
132+
keyName === "view" ||
133+
keyName === "size" ||
134+
keyName === "length" ||
135+
keyName.toUpperCase() === keyName ||
136+
(parent instanceof CSSStyleDeclaration && value === "")
137+
);
138+
}
139+
140+
/**
141+
* Check if an object is a plain object.
142+
* A plain object is an object created by `Object()` or squiggly braces `{}`. Additionally,
143+
* `null` is treated as a plain object.
144+
*/
145+
function isPlainObject(x: any): boolean {
146+
if (typeof x !== "object" || x === null) {
147+
return false;
148+
}
149+
const proto = Object.getPrototypeOf(x);
150+
return proto === Object.prototype || proto === null;
151+
}
152+
153+
// /**
154+
// * Get the class name of an object.
155+
// */
156+
// function getClassName(obj: object): string {
157+
// return Function.prototype.call.bind(Object.prototype.toString)(obj);
158+
// }
159+
//
160+
// /**
161+
// * Get the index of an object in a set, or -1 if not found.
162+
// */
163+
// function getObjectIndex(obj: object, set: Set<object>): number {
164+
// // Try to find the object by comparing JSON representation
165+
// let index = 0;
166+
// for (const item of set) {
167+
// if (
168+
// typeof item === "object" &&
169+
// getClassName(item) === getClassName(obj) &&
170+
// JSON.stringify(item) == JSON.stringify(obj)
171+
// ) {
172+
// return index;
173+
// }
174+
// index++;
175+
// }
176+
//
177+
// // If the object is not found in the set, return -1
178+
// return -1;
179+
// }
180+
181+
// Example usage of the convert function
182+
var my_event = null;
183+
function my_click(event) {
184+
my_event = event;
185+
console.log("Original Event:", event);
186+
const jsonEvent = convert(event);
187+
console.log("Converted Event:", JSON.stringify(jsonEvent, null, 2));
188+
console.log(
189+
"Byte Length:",
190+
new TextEncoder().encode(JSON.stringify(jsonEvent)).length,
191+
);
192+
}
193+
document
194+
.getElementById("fetchData")
195+
?.addEventListener("click", my_click, false);

0 commit comments

Comments
 (0)