penambahan web socket

This commit is contained in:
2025-09-18 19:01:22 +07:00
parent 1d053646a9
commit d7bb2eb5bb
15070 changed files with 2402916 additions and 0 deletions

View File

@@ -0,0 +1,152 @@
import path from './path.js';
/**
@class Cache
@private
*/
export default class Cache {
constructor(equals) {
this._equals = equals;
this._proxyCache = new WeakMap();
this._pathCache = new WeakMap();
this.isUnsubscribed = false;
}
_getDescriptorCache() {
if (this._descriptorCache === undefined) {
this._descriptorCache = new WeakMap();
}
return this._descriptorCache;
}
_getProperties(target) {
const descriptorCache = this._getDescriptorCache();
let properties = descriptorCache.get(target);
if (properties === undefined) {
properties = {};
descriptorCache.set(target, properties);
}
return properties;
}
_getOwnPropertyDescriptor(target, property) {
if (this.isUnsubscribed) {
return Reflect.getOwnPropertyDescriptor(target, property);
}
const properties = this._getProperties(target);
let descriptor = properties[property];
if (descriptor === undefined) {
descriptor = Reflect.getOwnPropertyDescriptor(target, property);
properties[property] = descriptor;
}
return descriptor;
}
getProxy(target, path, handler, proxyTarget) {
if (this.isUnsubscribed) {
return target;
}
const reflectTarget = target[proxyTarget];
const source = reflectTarget ?? target;
this._pathCache.set(source, path);
let proxy = this._proxyCache.get(source);
if (proxy === undefined) {
proxy = reflectTarget === undefined
? new Proxy(target, handler)
: target;
this._proxyCache.set(source, proxy);
}
return proxy;
}
getPath(target) {
return this.isUnsubscribed ? undefined : this._pathCache.get(target);
}
isDetached(target, object) {
return !Object.is(target, path.get(object, this.getPath(target)));
}
defineProperty(target, property, descriptor) {
if (!Reflect.defineProperty(target, property, descriptor)) {
return false;
}
if (!this.isUnsubscribed) {
this._getProperties(target)[property] = descriptor;
}
return true;
}
setProperty(target, property, value, receiver, previous) { // eslint-disable-line max-params
if (!this._equals(previous, value) || !(property in target)) {
const descriptor = this._getOwnPropertyDescriptor(target, property);
if (descriptor !== undefined && 'set' in descriptor) {
return Reflect.set(target, property, value, receiver);
}
return Reflect.set(target, property, value);
}
return true;
}
deleteProperty(target, property, previous) {
if (Reflect.deleteProperty(target, property)) {
if (!this.isUnsubscribed) {
const properties = this._getDescriptorCache().get(target);
if (properties) {
delete properties[property];
this._pathCache.delete(previous);
}
}
return true;
}
return false;
}
isSameDescriptor(a, target, property) {
const b = this._getOwnPropertyDescriptor(target, property);
return a !== undefined
&& b !== undefined
&& Object.is(a.value, b.value)
&& (a.writable || false) === (b.writable || false)
&& (a.enumerable || false) === (b.enumerable || false)
&& (a.configurable || false) === (b.configurable || false)
&& a.get === b.get
&& a.set === b.set;
}
isGetInvariant(target, property) {
const descriptor = this._getOwnPropertyDescriptor(target, property);
return descriptor !== undefined
&& descriptor.configurable !== true
&& descriptor.writable !== true;
}
unsubscribe() {
this._descriptorCache = null;
this._pathCache = null;
this._proxyCache = null;
this.isUnsubscribed = true;
}
}

View File

@@ -0,0 +1,3 @@
export const PATH_SEPARATOR = '.';
export const TARGET = Symbol('target');
export const UNSUBSCRIBE = Symbol('unsubscribe');

View File

@@ -0,0 +1,8 @@
import isSymbol from './is-symbol.js';
export default function ignoreProperty(cache, options, property) {
return cache.isUnsubscribed
|| (options.ignoreSymbols && isSymbol(property))
|| (options.ignoreUnderscores && property.charAt(0) === '_')
|| ('ignoreKeys' in options && options.ignoreKeys.includes(property));
}

View File

@@ -0,0 +1 @@
export default Array.isArray;

View File

@@ -0,0 +1,12 @@
export function isBuiltinWithMutableMethods(value) {
return value instanceof Date
|| value instanceof Set
|| value instanceof Map
|| value instanceof WeakSet
|| value instanceof WeakMap
|| ArrayBuffer.isView(value);
}
export function isBuiltinWithoutMutableMethods(value) {
return (typeof value === 'object' ? value === null : typeof value !== 'function') || value instanceof RegExp;
}

View File

@@ -0,0 +1,3 @@
export default function isIterator(value) {
return typeof value === 'object' && typeof value.next === 'function';
}

View File

@@ -0,0 +1,3 @@
export default function isObject(value) {
return toString.call(value) === '[object Object]';
}

View File

@@ -0,0 +1,3 @@
export default function isSymbol(value) {
return typeof value === 'symbol';
}

View File

@@ -0,0 +1,149 @@
import {PATH_SEPARATOR} from './constants.js';
import isArray from './is-array.js';
import isSymbol from './is-symbol.js';
const path = {
after(path, subPath) {
if (isArray(path)) {
return path.slice(subPath.length);
}
if (subPath === '') {
return path;
}
return path.slice(subPath.length + 1);
},
concat(path, key) {
if (isArray(path)) {
path = [...path];
if (key) {
path.push(key);
}
return path;
}
if (key && key.toString !== undefined) {
if (path !== '') {
path += PATH_SEPARATOR;
}
if (isSymbol(key)) {
return path + key.toString();
}
return path + key;
}
return path;
},
initial(path) {
if (isArray(path)) {
return path.slice(0, -1);
}
if (path === '') {
return path;
}
const index = path.lastIndexOf(PATH_SEPARATOR);
if (index === -1) {
return '';
}
return path.slice(0, index);
},
last(path) {
if (isArray(path)) {
return path.at(-1) ?? '';
}
if (path === '') {
return path;
}
const index = path.lastIndexOf(PATH_SEPARATOR);
if (index === -1) {
return path;
}
return path.slice(index + 1);
},
walk(path, callback) {
if (isArray(path)) {
for (const key of path) {
callback(key);
}
} else if (path !== '') {
let position = 0;
let index = path.indexOf(PATH_SEPARATOR);
if (index === -1) {
callback(path);
} else {
while (position < path.length) {
if (index === -1) {
index = path.length;
}
callback(path.slice(position, index));
position = index + 1;
index = path.indexOf(PATH_SEPARATOR, position);
}
}
}
},
get(object, path) {
this.walk(path, key => {
if (object) {
object = object[key];
}
});
return object;
},
isSubPath(path, subPath) {
if (isArray(path)) {
if (path.length < subPath.length) {
return false;
}
// eslint-disable-next-line unicorn/no-for-loop
for (let i = 0; i < subPath.length; i++) {
if (path[i] !== subPath[i]) {
return false;
}
}
return true;
}
if (path.length < subPath.length) {
return false;
}
if (path === subPath) {
return true;
}
if (path.startsWith(subPath)) {
return path[subPath.length] === PATH_SEPARATOR;
}
return false;
},
isRootPath(path) {
if (isArray(path)) {
return path.length === 0;
}
return path === '';
},
};
export default path;

View File

@@ -0,0 +1,8 @@
import {HANDLED_ARRAY_METHODS} from '../methods/array.js';
import CloneObject from './clone-object.js';
export default class CloneArray extends CloneObject {
static isHandledMethod(name) {
return HANDLED_ARRAY_METHODS.has(name);
}
}

View File

@@ -0,0 +1,11 @@
import CloneObject from './clone-object.js';
export default class CloneDate extends CloneObject {
undo(object) {
object.setTime(this.clone.getTime());
}
isChanged(value, equals) {
return !equals(this.clone.valueOf(), value.valueOf());
}
}

View File

@@ -0,0 +1,20 @@
import {HANDLED_MAP_METHODS} from '../methods/map.js';
import CloneObject from './clone-object.js';
export default class CloneMap extends CloneObject {
static isHandledMethod(name) {
return HANDLED_MAP_METHODS.has(name);
}
undo(object) {
for (const [key, value] of this.clone.entries()) {
object.set(key, value);
}
for (const key of object.keys()) {
if (!this.clone.has(key)) {
object.delete(key);
}
}
}
}

View File

@@ -0,0 +1,115 @@
import path from '../../path.js';
import isArray from '../../is-array.js';
import isObject from '../../is-object.js';
import {MUTABLE_ARRAY_METHODS} from '../methods/array.js';
import {MUTABLE_SET_METHODS} from '../methods/set.js';
import {MUTABLE_MAP_METHODS} from '../methods/map.js';
import {IMMUTABLE_OBJECT_METHODS} from '../methods/object.js';
export default class CloneObject {
constructor(value, path, argumentsList, hasOnValidate) {
this._path = path;
this._isChanged = false;
this._clonedCache = new Set();
this._hasOnValidate = hasOnValidate;
this._changes = hasOnValidate ? [] : null;
this.clone = path === undefined ? value : this._shallowClone(value);
}
static isHandledMethod(name) {
return IMMUTABLE_OBJECT_METHODS.has(name);
}
_shallowClone(value) {
let clone = value;
if (isObject(value)) {
clone = {...value};
} else if (isArray(value) || ArrayBuffer.isView(value)) {
clone = [...value];
} else if (value instanceof Date) {
clone = new Date(value);
} else if (value instanceof Set) {
clone = new Set([...value].map(item => this._shallowClone(item)));
} else if (value instanceof Map) {
clone = new Map();
for (const [key, item] of value.entries()) {
clone.set(key, this._shallowClone(item));
}
}
this._clonedCache.add(clone);
return clone;
}
preferredThisArg(isHandledMethod, name, thisArgument, thisProxyTarget) {
if (isHandledMethod) {
if (isArray(thisProxyTarget)) {
this._onIsChanged = MUTABLE_ARRAY_METHODS[name];
} else if (thisProxyTarget instanceof Set) {
this._onIsChanged = MUTABLE_SET_METHODS[name];
} else if (thisProxyTarget instanceof Map) {
this._onIsChanged = MUTABLE_MAP_METHODS[name];
}
return thisProxyTarget;
}
return thisArgument;
}
update(fullPath, property, value) {
const changePath = path.after(fullPath, this._path);
if (property !== 'length') {
let object = this.clone;
path.walk(changePath, key => {
if (object?.[key]) {
if (!this._clonedCache.has(object[key])) {
object[key] = this._shallowClone(object[key]);
}
object = object[key];
}
});
if (this._hasOnValidate) {
this._changes.push({
path: changePath,
property,
previous: value,
});
}
if (object?.[property]) {
object[property] = value;
}
}
this._isChanged = true;
}
undo(object) {
let change;
for (let index = this._changes.length - 1; index !== -1; index--) {
change = this._changes[index];
path.get(object, change.path)[change.property] = change.previous;
}
}
isChanged(value) {
return this._onIsChanged === undefined
? this._isChanged
: this._onIsChanged(this.clone, value);
}
isPathApplicable(changePath) {
return path.isRootPath(this._path) || path.isSubPath(changePath, this._path);
}
}

View File

@@ -0,0 +1,20 @@
import {HANDLED_SET_METHODS} from '../methods/set.js';
import CloneObject from './clone-object.js';
export default class CloneSet extends CloneObject {
static isHandledMethod(name) {
return HANDLED_SET_METHODS.has(name);
}
undo(object) {
for (const value of this.clone) {
object.add(value);
}
for (const value of object) {
if (!this.clone.has(value)) {
object.delete(value);
}
}
}
}

View File

@@ -0,0 +1,27 @@
import CloneObject from './clone-object.js';
export default class CloneWeakMap extends CloneObject {
constructor(value, path, argumentsList, hasOnValidate) {
super(undefined, path, argumentsList, hasOnValidate);
this._weakKey = argumentsList[0];
this._weakHas = value.has(this._weakKey);
this._weakValue = value.get(this._weakKey);
}
isChanged(value) {
return this._weakValue !== value.get(this._weakKey);
}
undo(object) {
const weakHas = object.has(this._weakKey);
if (this._weakHas && !weakHas) {
object.set(this._weakKey, this._weakValue);
} else if (!this._weakHas && weakHas) {
object.delete(this._weakKey);
} else if (this._weakValue !== object.get(this._weakKey)) {
object.set(this._weakKey, this._weakValue);
}
}
}

View File

@@ -0,0 +1,22 @@
import CloneObject from './clone-object.js';
export default class CloneWeakSet extends CloneObject {
constructor(value, path, argumentsList, hasOnValidate) {
super(undefined, path, argumentsList, hasOnValidate);
this._argument1 = argumentsList[0];
this._weakValue = value.has(this._argument1);
}
isChanged(value) {
return this._weakValue !== value.has(this._argument1);
}
undo(object) {
if (this._weakValue && !object.has(this._argument1)) {
object.add(this._argument1);
} else {
object.delete(this._argument1);
}
}
}

View File

@@ -0,0 +1,3 @@
export default function isDiffArrays(clone, value) {
return clone.length !== value.length || clone.some((item, index) => value[index] !== item);
}

View File

@@ -0,0 +1,3 @@
export default function isDiffCertain() {
return true;
}

View File

@@ -0,0 +1,16 @@
export default function isDiffMaps(clone, value) {
if (clone.size !== value.size) {
return true;
}
let bValue;
for (const [key, aValue] of clone) {
bValue = value.get(key);
if (bValue !== aValue || (bValue === undefined && !value.has(key))) {
return true;
}
}
return false;
}

View File

@@ -0,0 +1,13 @@
export default function isDiffSets(clone, value) {
if (clone.size !== value.size) {
return true;
}
for (const element of clone) {
if (!value.has(element)) {
return true;
}
}
return false;
}

View File

@@ -0,0 +1,31 @@
import isDiffCertain from '../diff/is-diff-certain.js';
import isDiffArrays from '../diff/is-diff-arrays.js';
import {IMMUTABLE_OBJECT_METHODS} from './object.js';
const IMMUTABLE_ARRAY_METHODS = new Set([
'concat',
'includes',
'indexOf',
'join',
'keys',
'lastIndexOf',
]);
export const MUTABLE_ARRAY_METHODS = {
push: isDiffCertain,
pop: isDiffCertain,
shift: isDiffCertain,
unshift: isDiffCertain,
copyWithin: isDiffArrays,
reverse: isDiffArrays,
sort: isDiffArrays,
splice: isDiffArrays,
flat: isDiffArrays,
fill: isDiffArrays,
};
export const HANDLED_ARRAY_METHODS = new Set([
...IMMUTABLE_OBJECT_METHODS,
...IMMUTABLE_ARRAY_METHODS,
...Object.keys(MUTABLE_ARRAY_METHODS),
]);

View File

@@ -0,0 +1,17 @@
import isDiffMaps from '../diff/is-diff-maps.js';
import {IMMUTABLE_SET_METHODS, COLLECTION_ITERATOR_METHODS} from './set.js';
const IMMUTABLE_MAP_METHODS = new Set([...IMMUTABLE_SET_METHODS, 'get']);
export const MUTABLE_MAP_METHODS = {
set: isDiffMaps,
clear: isDiffMaps,
delete: isDiffMaps,
forEach: isDiffMaps,
};
export const HANDLED_MAP_METHODS = new Set([
...IMMUTABLE_MAP_METHODS,
...Object.keys(MUTABLE_MAP_METHODS),
...COLLECTION_ITERATOR_METHODS,
]);

View File

@@ -0,0 +1,8 @@
export const IMMUTABLE_OBJECT_METHODS = new Set([
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'toLocaleString',
'toString',
'valueOf',
]);

View File

@@ -0,0 +1,25 @@
import isDiffSets from '../diff/is-diff-sets.js';
export const COLLECTION_ITERATOR_METHODS = [
'keys',
'values',
'entries',
];
export const IMMUTABLE_SET_METHODS = new Set([
'has',
'toString',
]);
export const MUTABLE_SET_METHODS = {
add: isDiffSets,
clear: isDiffSets,
delete: isDiffSets,
forEach: isDiffSets,
};
export const HANDLED_SET_METHODS = new Set([
...IMMUTABLE_SET_METHODS,
...Object.keys(MUTABLE_SET_METHODS),
...COLLECTION_ITERATOR_METHODS,
]);

View File

@@ -0,0 +1,99 @@
import isArray from '../is-array.js';
import {isBuiltinWithMutableMethods} from '../is-builtin.js';
import isObject from '../is-object.js';
import CloneObject from './clone/clone-object.js';
import CloneArray from './clone/clone-array.js';
import CloneDate from './clone/clone-date.js';
import CloneSet from './clone/clone-set.js';
import CloneMap from './clone/clone-map.js';
import CloneWeakSet from './clone/clone-weakset.js';
import CloneWeakMap from './clone/clone-weakmap.js';
export default class SmartClone {
constructor(hasOnValidate) {
this._stack = [];
this._hasOnValidate = hasOnValidate;
}
static isHandledType(value) {
return isObject(value)
|| isArray(value)
|| isBuiltinWithMutableMethods(value);
}
static isHandledMethod(target, name) {
if (isObject(target)) {
return CloneObject.isHandledMethod(name);
}
if (isArray(target)) {
return CloneArray.isHandledMethod(name);
}
if (target instanceof Set) {
return CloneSet.isHandledMethod(name);
}
if (target instanceof Map) {
return CloneMap.isHandledMethod(name);
}
return isBuiltinWithMutableMethods(target);
}
get isCloning() {
return this._stack.length > 0;
}
start(value, path, argumentsList) {
let CloneClass = CloneObject;
if (isArray(value)) {
CloneClass = CloneArray;
} else if (value instanceof Date) {
CloneClass = CloneDate;
} else if (value instanceof Set) {
CloneClass = CloneSet;
} else if (value instanceof Map) {
CloneClass = CloneMap;
} else if (value instanceof WeakSet) {
CloneClass = CloneWeakSet;
} else if (value instanceof WeakMap) {
CloneClass = CloneWeakMap;
}
this._stack.push(new CloneClass(value, path, argumentsList, this._hasOnValidate));
}
update(fullPath, property, value) {
this._stack.at(-1).update(fullPath, property, value);
}
preferredThisArg(target, thisArgument, thisProxyTarget) {
const {name} = target;
const isHandledMethod = SmartClone.isHandledMethod(thisProxyTarget, name);
return this._stack.at(-1)
.preferredThisArg(isHandledMethod, name, thisArgument, thisProxyTarget);
}
isChanged(isMutable, value, equals) {
return this._stack.at(-1).isChanged(isMutable, value, equals);
}
isPartOfClone(changePath) {
return this._stack.at(-1).isPathApplicable(changePath);
}
undo(object) {
if (this._previousClone !== undefined) {
this._previousClone.undo(object);
}
}
stop() {
this._previousClone = this._stack.pop();
return this._previousClone.clone;
}
}

View File

@@ -0,0 +1,63 @@
import {TARGET} from './constants.js';
// eslint-disable-next-line max-params
export default function wrapIterator(iterator, target, thisArgument, applyPath, prepareValue) {
const originalNext = iterator.next;
if (target.name === 'entries') {
iterator.next = function () {
const result = originalNext.call(this);
if (result.done === false) {
result.value[0] = prepareValue(
result.value[0],
target,
result.value[0],
applyPath,
);
result.value[1] = prepareValue(
result.value[1],
target,
result.value[0],
applyPath,
);
}
return result;
};
} else if (target.name === 'values') {
const keyIterator = thisArgument[TARGET].keys();
iterator.next = function () {
const result = originalNext.call(this);
if (result.done === false) {
result.value = prepareValue(
result.value,
target,
keyIterator.next().value,
applyPath,
);
}
return result;
};
} else {
iterator.next = function () {
const result = originalNext.call(this);
if (result.done === false) {
result.value = prepareValue(
result.value,
target,
result.value,
applyPath,
);
}
return result;
};
}
return iterator;
}