Skip to content

Commit

Permalink
fix: ajusta lib
Browse files Browse the repository at this point in the history
  • Loading branch information
drusco committed Aug 18, 2023
1 parent 0d6d1e3 commit 60372ff
Show file tree
Hide file tree
Showing 17 changed files with 207 additions and 62 deletions.
41 changes: 28 additions & 13 deletions src/Emulator.spec.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
import Emulator from "./Emulator";
import Exotic from "./types/Exotic";

describe("(lib) Emulator", () => {
it("Can simulate a browser window's context", async () => {
const nodejs = new Emulator();
const external = new Emulator();
const browser = new Emulator();

const win = {
innerWidth: 600,
innerHeight: 1000,
innerWidth: 1000,
innerHeight: 600,
};

// the browser's emulator sets the 'window' ref
// with the actual window object
browser.useRef("window", win);

// the emulator informs the browser context to create a new ref 'window'
// since the 'window' ref exists in the browser already it will use the existing proxy
// every handler trap call on the api will correspond to the real window target in the browser
const window = nodejs.useRef("window", global);

expect(window).toBeTruthy();
external.addListener(
"proxy",
(origin: Exotic.proxy.origin, target: any) => {
browser.include(origin, target);
},
);

browser.addListener(
"reference",
(ref: string, use: Exotic.FunctionLike) => {
let value: any;
if (ref === "window") {
value = win;
}
use(value);
},
);

const window = external.useRef("window", global);

window.test = true;

expect(browser.target(browser.useRef("window").test)).toBe(true);
//expect(true).toBeTruthy();
});

it("Cannot leak memory with proper use", () => {
Expand Down
12 changes: 11 additions & 1 deletion src/Emulator.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import lib from "./lib";
import Exotic from "./types/Exotic";
import EventEmitter from "events";

export default class Emulator implements Exotic.Emulator {
export default class Emulator extends EventEmitter implements Exotic.Emulator {
constructor(options: Exotic.emulator.options = {}) {
super();
lib.constructor(this, options);
}

Expand Down Expand Up @@ -66,11 +68,19 @@ export default class Emulator implements Exotic.Emulator {
return lib.methods.encode(value);
}

decode(value: any): any {
return lib.methods.decode(this, value);
}

get(value?: any): Promise<any> {
return lib.methods.get(this, value);
}

revokeAll(): void {
return lib.methods.revokeAll(this);
}

include(origin: Exotic.proxy.origin, target?: any): void {
return lib.methods.include(this, origin, target);
}
}
6 changes: 6 additions & 0 deletions src/lib/methods/decode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Exotic from "../../types/Exotic";
import { decode as decodeValue } from "../../utils";

export default function decode(scope: Exotic.Emulator, value: any): any {
return decodeValue(scope, value);
}
51 changes: 51 additions & 0 deletions src/lib/methods/include.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import Exotic from "../../types/Exotic";
import { map, traps, createProxy, decode } from "../../utils";

export default function include(
scope: Exotic.Emulator,
origin: Exotic.proxy.origin,
target?: any,
): void {
const decodedOrigin = decode(scope, origin);
const { action, proxy, key, value, that, args, ref } = decodedOrigin;

if (ref) {
new Promise((resolve) => {
scope.emit("reference", ref, (target: any) => {
createProxy(scope, decodedOrigin, target);
resolve(undefined);
});
});
return;
}

if (!action) {
createProxy(scope, decodedOrigin, target);
return;
}

const originProxy = map.proxies.get(proxy);

if (!originProxy) {
return;
}

const { mock } = originProxy;

switch (action) {
case "get":
if (!key) return;
traps.get(mock, key);
break;
case "set":
if (!key) return;
traps.set(mock, key, value);
break;
case "apply":
traps.apply(mock, that, args);
break;
case "construct":
traps.construct(mock, args);
break;
}
}
4 changes: 4 additions & 0 deletions src/lib/methods/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import revokeAll from "./revokeAll";
import isRevoked from "./isRevoked";
import entries from "./entries";
import encode from "./encode";
import decode from "./decode";
import get from "./get";
import include from "./include";

export default {
useRef,
Expand All @@ -23,5 +25,7 @@ export default {
isRevoked,
entries,
encode,
decode,
get,
include,
};
2 changes: 1 addition & 1 deletion src/lib/methods/use.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import { createProxy } from "../../utils";

export default function use(scope: Exotic.Emulator, value?: any): Exotic.Proxy {
// create a proxy by target reference
return createProxy(scope, value);
return createProxy(scope, undefined, value);
}
2 changes: 1 addition & 1 deletion src/lib/methods/useRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export default function useRef(
value?: any,
): Exotic.Proxy {
// create a proxy by reference key
return createProxy(scope, value, undefined, key);
return createProxy(scope, { ref: key }, value);
}
11 changes: 8 additions & 3 deletions src/types/Exotic.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import EventEmitter from "events";

declare namespace Exotic {
type traceable = object;
type traceable = object | FunctionLike;
type key = string | symbol;
type FunctionLike = (...args: any[]) => any;

interface Emulator {
interface Emulator extends EventEmitter {
use(value?: any): Proxy;
useRef(ref: key, value?: any): Proxy;
include(origin: proxy.origin, target?: any): void;
target(value?: any): any;
parent(value?: traceable): undefined | Proxy;
values(value?: traceable): Proxy[];
Expand All @@ -15,6 +18,7 @@ declare namespace Exotic {
isRevoked(value: traceable): boolean;
entries(): IterableIterator<Proxy>;
encode(value: any): any;
decode(value: any): any;
get(value?: any): Promise<any>;
refs: key[];
active: number;
Expand Down Expand Up @@ -49,12 +53,13 @@ declare namespace Exotic {
type payload = [noBreak: "⁠", proxyId: number];

interface origin {
action: "get" | "set" | "construct" | "apply";
action?: "get" | "set" | "construct" | "apply";
proxy?: Proxy;
key?: key;
value?: any;
that?: any;
args?: any[];
ref?: key;
}

interface data {
Expand Down
16 changes: 12 additions & 4 deletions src/utils/createProxy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@ const $ = new Emulator();
describe("(function) createProxy", () => {
it("Returns an existing proxy", () => {
const proxy = $.use();
const sameProxy = createProxy($, proxy);
const sameProxy = createProxy($, undefined, proxy);
expect(proxy).toBe(sameProxy);
});

it("Returns an existing proxy by reference key", () => {
const refKey = "test";
const proxy = $.useRef(refKey);
const sameProxy = createProxy($, proxy, undefined, refKey);
const sameProxy = createProxy(
$,
{
ref: refKey,
},
proxy,
);

expect(proxy).toBe(sameProxy);
});
Expand All @@ -22,15 +28,17 @@ describe("(function) createProxy", () => {
const refKey = "new";
const refKeyExisted = $.refs.includes(refKey);

createProxy($, undefined, undefined, refKey);
createProxy($, {
ref: refKey,
});

expect(refKeyExisted).toBe(false);
expect($.refs.includes(refKey)).toBe(true);
});

it("Adds any target value to a proxy", () => {
const target = "target";
const proxy = createProxy($, target);
const proxy = createProxy($, undefined, target);

expect($.target(proxy)).toBe(target);
});
Expand Down
10 changes: 7 additions & 3 deletions src/utils/createProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import findProxy from "./findProxy";
import isTraceable from "./isTraceable";
import mockPrototype from "./mockPrototype";
import traps from "./traps";
import encode from "./encode";

const createProxy = (
scope: Exotic.Emulator,
origin: Exotic.proxy.origin = {},
target?: any,
origin?: Exotic.proxy.origin,
refKey?: Exotic.key,
): Exotic.Proxy => {
const usableProxy = findProxy(target);

Expand All @@ -20,6 +20,8 @@ const createProxy = (

const data: Exotic.emulator.data = map.emulators.get(scope);
const { refs } = data;
const encodedOrigin = encode(origin);
const refKey = origin?.ref;
const validRefKey = refKey !== undefined;
const reference = validRefKey ? refKey : undefined;

Expand Down Expand Up @@ -58,7 +60,7 @@ const createProxy = (
const proxyData: Exotic.proxy.data = {
id,
mock,
origin,
origin: encodedOrigin,
target,
revoke: revokeFunction,
scope,
Expand All @@ -77,6 +79,8 @@ const createProxy = (
map.targets.set(target, proxy);
}

scope.emit("proxy", encodedOrigin, encode(target));

return proxy;
};

Expand Down
51 changes: 38 additions & 13 deletions src/utils/decode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,53 @@ import Exotic from "../types/Exotic";
import findProxy from "./findProxy";
import findProxyById from "./findProxyById";
import isPayload from "./isPayload";
import isTraceable from "./isTraceable";

export default function decode(scope: Exotic.Emulator, value: any): any {
const isObject = value !== null && typeof value === "object";
const isArray = Array.isArray(value);
export default function decode(
scope: Exotic.Emulator,
value: any,
visited: WeakSet<Exotic.traceable> = new WeakSet(),
): any {
const traceable = isTraceable(value);
const proxy = findProxy(value);
const payload = isPayload(value);

let result = value;

if (proxy) {
result = proxy;
} else if (payload) {
result = findProxyById(scope, value[1]);
} else if (isObject) {
return proxy;
}

if (payload) {
return findProxyById(scope, value[1]);
}

if (traceable) {
if (typeof value === "function") {
return value;
}

if (visited.has(value)) {
// Handle circular reference by returning the original value
return value;
}

visited.add(value);

const isArray = Array.isArray(value);
const copy = isArray ? [] : {};
const keys = Object.keys(value);

for (const key in value) {
copy[key] = decode(scope, value[key]);
if (isArray) {
for (let i = 0; i < value.length; i++) {
(copy as any[]).push(decode(scope, value[i], visited));
}
} else {
for (const key of keys) {
copy[key] = decode(scope, value[key], visited);
}
}

result = copy;
return copy;
}

return result;
return value;
}
Loading

0 comments on commit 60372ff

Please sign in to comment.