Odoo GraphQL Subscription using Node, Express JS for Sample
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

181 lines
8.8 KiB

4 months ago
import { __assign } from "tslib";
import { Trie } from "@wry/trie";
import { canUseWeakMap, canUseWeakSet, isNonNullObject as isObjectOrArray, } from "../../utilities/index.js";
import { isArray } from "./helpers.js";
function shallowCopy(value) {
if (isObjectOrArray(value)) {
return isArray(value) ?
value.slice(0)
: __assign({ __proto__: Object.getPrototypeOf(value) }, value);
}
return value;
}
// When programmers talk about the "canonical form" of an object, they
// usually have the following meaning in mind, which I've copied from
// https://en.wiktionary.org/wiki/canonical_form:
//
// 1. A standard or normal presentation of a mathematical entity [or
// object]. A canonical form is an element of a set of representatives
// of equivalence classes of forms such that there is a function or
// procedure which projects every element of each equivalence class
// onto that one element, the canonical form of that equivalence
// class. The canonical form is expected to be simpler than the rest of
// the forms in some way.
//
// That's a long-winded way of saying any two objects that have the same
// canonical form may be considered equivalent, even if they are !==,
// which usually means the objects are structurally equivalent (deeply
// equal), but don't necessarily use the same memory.
//
// Like a literary or musical canon, this ObjectCanon class represents a
// collection of unique canonical items (JavaScript objects), with the
// important property that canon.admit(a) === canon.admit(b) if a and b
// are deeply equal to each other. In terms of the definition above, the
// canon.admit method is the "function or procedure which projects every"
// object "onto that one element, the canonical form."
//
// In the worst case, the canonicalization process may involve looking at
// every property in the provided object tree, so it takes the same order
// of time as deep equality checking. Fortunately, already-canonicalized
// objects are returned immediately from canon.admit, so the presence of
// canonical subtrees tends to speed up canonicalization.
//
// Since consumers of canonical objects can check for deep equality in
// constant time, canonicalizing cache results can massively improve the
// performance of application code that skips re-rendering unchanged
// results, such as "pure" UI components in a framework like React.
//
// Of course, since canonical objects may be shared widely between
// unrelated consumers, it's important to think of them as immutable, even
// though they are not actually frozen with Object.freeze in production,
// due to the extra performance overhead that comes with frozen objects.
//
// Custom scalar objects whose internal class name is neither Array nor
// Object can be included safely in the admitted tree, but they will not
// be replaced with a canonical version (to put it another way, they are
// assumed to be canonical already).
//
// If we ignore custom objects, no detection of cycles or repeated object
// references is currently required by the StoreReader class, since
// GraphQL result objects are JSON-serializable trees (and thus contain
// neither cycles nor repeated subtrees), so we can avoid the complexity
// of keeping track of objects we've already seen during the recursion of
// the admit method.
//
// In the future, we may consider adding additional cases to the switch
// statement to handle other common object types, such as "[object Date]"
// objects, as needed.
var ObjectCanon = /** @class */ (function () {
function ObjectCanon() {
// Set of all canonical objects this ObjectCanon has admitted, allowing
// canon.admit to return previously-canonicalized objects immediately.
this.known = new (canUseWeakSet ? WeakSet : Set)();
// Efficient storage/lookup structure for canonical objects.
this.pool = new Trie(canUseWeakMap);
// Make the ObjectCanon assume this value has already been
// canonicalized.
this.passes = new WeakMap();
// Arrays that contain the same elements in a different order can share
// the same SortedKeysInfo object, to save memory.
this.keysByJSON = new Map();
// This has to come last because it depends on keysByJSON.
this.empty = this.admit({});
}
ObjectCanon.prototype.isKnown = function (value) {
return isObjectOrArray(value) && this.known.has(value);
};
ObjectCanon.prototype.pass = function (value) {
if (isObjectOrArray(value)) {
var copy = shallowCopy(value);
this.passes.set(copy, value);
return copy;
}
return value;
};
ObjectCanon.prototype.admit = function (value) {
var _this = this;
if (isObjectOrArray(value)) {
var original = this.passes.get(value);
if (original)
return original;
var proto = Object.getPrototypeOf(value);
switch (proto) {
case Array.prototype: {
if (this.known.has(value))
return value;
var array = value.map(this.admit, this);
// Arrays are looked up in the Trie using their recursively
// canonicalized elements, and the known version of the array is
// preserved as node.array.
var node = this.pool.lookupArray(array);
if (!node.array) {
this.known.add((node.array = array));
// Since canonical arrays may be shared widely between
// unrelated consumers, it's important to regard them as
// immutable, even if they are not frozen in production.
if (globalThis.__DEV__ !== false) {
Object.freeze(array);
}
}
return node.array;
}
case null:
case Object.prototype: {
if (this.known.has(value))
return value;
var proto_1 = Object.getPrototypeOf(value);
var array_1 = [proto_1];
var keys = this.sortedKeys(value);
array_1.push(keys.json);
var firstValueIndex_1 = array_1.length;
keys.sorted.forEach(function (key) {
array_1.push(_this.admit(value[key]));
});
// Objects are looked up in the Trie by their prototype (which
// is *not* recursively canonicalized), followed by a JSON
// representation of their (sorted) keys, followed by the
// sequence of recursively canonicalized values corresponding to
// those keys. To keep the final results unambiguous with other
// sequences (such as arrays that just happen to contain [proto,
// keys.json, value1, value2, ...]), the known version of the
// object is stored as node.object.
var node = this.pool.lookupArray(array_1);
if (!node.object) {
var obj_1 = (node.object = Object.create(proto_1));
this.known.add(obj_1);
keys.sorted.forEach(function (key, i) {
obj_1[key] = array_1[firstValueIndex_1 + i];
});
// Since canonical objects may be shared widely between
// unrelated consumers, it's important to regard them as
// immutable, even if they are not frozen in production.
if (globalThis.__DEV__ !== false) {
Object.freeze(obj_1);
}
}
return node.object;
}
}
}
return value;
};
// It's worthwhile to cache the sorting of arrays of strings, since the
// same initial unsorted arrays tend to be encountered many times.
// Fortunately, we can reuse the Trie machinery to look up the sorted
// arrays in linear time (which is faster than sorting large arrays).
ObjectCanon.prototype.sortedKeys = function (obj) {
var keys = Object.keys(obj);
var node = this.pool.lookupArray(keys);
if (!node.keys) {
keys.sort();
var json = JSON.stringify(keys);
if (!(node.keys = this.keysByJSON.get(json))) {
this.keysByJSON.set(json, (node.keys = { sorted: keys, json: json }));
}
}
return node.keys;
};
return ObjectCanon;
}());
export { ObjectCanon };
//# sourceMappingURL=object-canon.js.map