Website Structure
This commit is contained in:
parent
62812f2090
commit
71f0676a62
22365 changed files with 4265753 additions and 791 deletions
199
Frontend-Learner/node_modules/on-change/source/cache.js
generated
vendored
Normal file
199
Frontend-Learner/node_modules/on-change/source/cache.js
generated
vendored
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
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._allPathsCache = new WeakMap();
|
||||
this.isUnsubscribed = false;
|
||||
}
|
||||
|
||||
_pathsEqual(pathA, pathB) {
|
||||
if (!Array.isArray(pathA) || !Array.isArray(pathB)) {
|
||||
return pathA === pathB;
|
||||
}
|
||||
|
||||
return pathA.length === pathB.length
|
||||
&& pathA.every((part, index) => part === pathB[index]);
|
||||
}
|
||||
|
||||
_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 = proxyTarget === undefined ? undefined : target[proxyTarget];
|
||||
const source = reflectTarget ?? target;
|
||||
|
||||
// Always set the primary path (for backward compatibility)
|
||||
this._pathCache.set(source, path);
|
||||
|
||||
// Track all paths for this object
|
||||
let allPaths = this._allPathsCache.get(source);
|
||||
if (!allPaths) {
|
||||
allPaths = [];
|
||||
this._allPathsCache.set(source, allPaths);
|
||||
}
|
||||
|
||||
// Add path if it doesn't already exist
|
||||
const pathExists = allPaths.some(existingPath => this._pathsEqual(existingPath, path));
|
||||
if (!pathExists) {
|
||||
allPaths.push(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);
|
||||
}
|
||||
|
||||
getAllPaths(target) {
|
||||
if (this.isUnsubscribed) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this._allPathsCache.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)) {
|
||||
// Check if there's a setter anywhere in the prototype chain
|
||||
let hasSetterInChain = false;
|
||||
let current = target;
|
||||
while (current) {
|
||||
const descriptor = Reflect.getOwnPropertyDescriptor(current, property);
|
||||
|
||||
if (descriptor && 'set' in descriptor) {
|
||||
hasSetterInChain = true;
|
||||
break;
|
||||
}
|
||||
|
||||
current = Object.getPrototypeOf(current);
|
||||
}
|
||||
|
||||
if (hasSetterInChain) {
|
||||
// Use receiver to ensure setter gets proxy as 'this'
|
||||
return Reflect.set(target, property, value, receiver);
|
||||
}
|
||||
|
||||
// For simple properties, don't use receiver to maintain existing behavior
|
||||
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._allPathsCache = null;
|
||||
this.isUnsubscribed = true;
|
||||
}
|
||||
}
|
||||
3
Frontend-Learner/node_modules/on-change/source/constants.js
generated
vendored
Normal file
3
Frontend-Learner/node_modules/on-change/source/constants.js
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export const PATH_SEPARATOR = '.';
|
||||
export const TARGET = Symbol('target');
|
||||
export const UNSUBSCRIBE = Symbol('unsubscribe');
|
||||
23
Frontend-Learner/node_modules/on-change/source/ignore-property.js
generated
vendored
Normal file
23
Frontend-Learner/node_modules/on-change/source/ignore-property.js
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import isSymbol from './is-symbol.js';
|
||||
|
||||
export default function ignoreProperty(cache, options, property) {
|
||||
if (cache.isUnsubscribed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (options.ignoreSymbols && isSymbol(property)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only strings can be prefixed with "_"
|
||||
if (options.ignoreUnderscores && typeof property === 'string' && property.charAt(0) === '_') {
|
||||
return true;
|
||||
}
|
||||
|
||||
const keys = options.ignoreKeys;
|
||||
if (keys) {
|
||||
return Array.isArray(keys) ? keys.includes(property) : (keys instanceof Set ? keys.has(property) : false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
298
Frontend-Learner/node_modules/on-change/source/index.d.ts
generated
vendored
Normal file
298
Frontend-Learner/node_modules/on-change/source/index.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
export type Options = {
|
||||
/**
|
||||
Deep changes will not trigger the callback. Only changes to the immediate properties of the original object.
|
||||
|
||||
@default false
|
||||
|
||||
@example
|
||||
```
|
||||
import onChange from 'on-change';
|
||||
|
||||
const object = {
|
||||
a: {
|
||||
b: false
|
||||
}
|
||||
};
|
||||
|
||||
let index = 0;
|
||||
const watchedObject = onChange(object, () => {
|
||||
console.log('Object changed:', ++index);
|
||||
}, {isShallow: true});
|
||||
|
||||
watchedObject.a.b = true;
|
||||
// Nothing happens
|
||||
|
||||
watchedObject.a = true;
|
||||
//=> 'Object changed: 1'
|
||||
```
|
||||
*/
|
||||
readonly isShallow?: boolean;
|
||||
|
||||
/**
|
||||
The function receives two arguments to be compared for equality. Should return `true` if the two values are determined to be equal.
|
||||
|
||||
@default Object.is
|
||||
|
||||
@example
|
||||
```
|
||||
import onChange from 'on-change';
|
||||
|
||||
const object = {
|
||||
a: {
|
||||
b: false
|
||||
}
|
||||
};
|
||||
|
||||
let index = 0;
|
||||
const watchedObject = onChange(object, () => {
|
||||
console.log('Object changed:', ++index);
|
||||
}, {equals: (a, b) => a === b});
|
||||
|
||||
watchedObject.a.b = 0;
|
||||
// Nothing happens
|
||||
|
||||
watchedObject.a = true;
|
||||
//=> 'Object changed: 1'
|
||||
```
|
||||
*/
|
||||
equals?: (a: unknown, b: unknown) => boolean;
|
||||
|
||||
/**
|
||||
Setting properties as `Symbol` won't trigger the callback.
|
||||
|
||||
@default false
|
||||
*/
|
||||
readonly ignoreSymbols?: boolean;
|
||||
|
||||
/**
|
||||
Setting properties in this array won't trigger the callback.
|
||||
|
||||
@default undefined
|
||||
*/
|
||||
readonly ignoreKeys?: ReadonlyArray<string | symbol>;
|
||||
|
||||
/**
|
||||
Setting properties with an underscore as the first character won't trigger the callback.
|
||||
|
||||
@default false
|
||||
*/
|
||||
readonly ignoreUnderscores?: boolean;
|
||||
|
||||
/**
|
||||
The path will be provided as an array of keys instead of a delimited string. Recommended when working with Sets, Maps, or property keys that are Symbols.
|
||||
|
||||
@default false
|
||||
*/
|
||||
readonly pathAsArray?: boolean;
|
||||
|
||||
/**
|
||||
Ignore changes to objects that become detached from the watched object.
|
||||
|
||||
@default false
|
||||
*/
|
||||
readonly ignoreDetached?: boolean;
|
||||
|
||||
/**
|
||||
Trigger callbacks for each change within specified method calls or all method calls.
|
||||
|
||||
@default false
|
||||
*/
|
||||
readonly details?: boolean | readonly string[];
|
||||
|
||||
/**
|
||||
The function receives the same arguments and context as the [onChange callback](#onchange). The function is called whenever a change is attempted. Returning true will allow the change to be made and the onChange callback to execute, returning anything else will prevent the change from being made and the onChange callback will not trigger.
|
||||
|
||||
@example
|
||||
```
|
||||
import onChange from 'on-change';
|
||||
|
||||
const object = {a: 0};
|
||||
let index = 0;
|
||||
const watchedObject = onChange(object, () => {
|
||||
console.log('Object changed:', ++index);
|
||||
}, {onValidate: () => false});
|
||||
|
||||
watchedObject.a = true;
|
||||
// watchedObject.a still equals 0
|
||||
```
|
||||
*/
|
||||
onValidate?: (
|
||||
this: unknown,
|
||||
path: string,
|
||||
value: unknown,
|
||||
previousValue: unknown,
|
||||
applyData: ApplyData
|
||||
) => boolean;
|
||||
};
|
||||
|
||||
export type ApplyData = {
|
||||
/**
|
||||
The name of the method that produced the change.
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
The arguments provided to the method that produced the change.
|
||||
*/
|
||||
readonly args: unknown[];
|
||||
|
||||
/**
|
||||
The result returned from the method that produced the change.
|
||||
*/
|
||||
readonly result: unknown;
|
||||
};
|
||||
|
||||
declare const onChange: {
|
||||
/**
|
||||
Watch an object or array for changes. It works recursively, so it will even detect if you modify a deep property like `obj.a.b[0].c = true`.
|
||||
|
||||
@param object - Object to watch for changes.
|
||||
@param onChange - Function that gets called anytime the object changes.
|
||||
@param [options] - Options for altering the behavior of onChange.
|
||||
@returns A version of `object` that is watched. It's the exact same object, just with some `Proxy` traps.
|
||||
|
||||
@example
|
||||
```
|
||||
import onChange from 'on-change';
|
||||
|
||||
const object = {
|
||||
foo: false,
|
||||
a: {
|
||||
b: [
|
||||
{
|
||||
c: false
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let index = 0;
|
||||
const watchedObject = onChange(object, function (path, value, previousValue, applyData) {
|
||||
console.log('Object changed:', ++index);
|
||||
console.log('this:', this);
|
||||
console.log('path:', path);
|
||||
console.log('value:', value);
|
||||
console.log('previousValue:', previousValue);
|
||||
console.log('applyData:', applyData);
|
||||
});
|
||||
|
||||
watchedObject.foo = true;
|
||||
//=> 'Object changed: 1'
|
||||
//=> 'this: {
|
||||
// foo: true,
|
||||
// a: {
|
||||
// b: [
|
||||
// {
|
||||
// c: false
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// }'
|
||||
//=> 'path: "foo"'
|
||||
//=> 'value: true'
|
||||
//=> 'previousValue: false'
|
||||
//=> 'applyData: undefined'
|
||||
|
||||
watchedObject.a.b[0].c = true;
|
||||
//=> 'Object changed: 2'
|
||||
//=> 'this: {
|
||||
// foo: true,
|
||||
// a: {
|
||||
// b: [
|
||||
// {
|
||||
// c: true
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// }'
|
||||
//=> 'path: "a.b.0.c"'
|
||||
//=> 'value: true'
|
||||
//=> 'previousValue: false'
|
||||
//=> 'applyData: undefined'
|
||||
|
||||
watchedObject.a.b.push(3);
|
||||
//=> 'Object changed: 3'
|
||||
//=> 'this: {
|
||||
// foo: true,
|
||||
// a: {
|
||||
// b: [
|
||||
// {
|
||||
// c: true
|
||||
// },
|
||||
// 3
|
||||
// ]
|
||||
// }
|
||||
// }'
|
||||
//=> 'path: "a.b"'
|
||||
//=> 'value: [{c: true}, 3]'
|
||||
//=> 'previousValue: [{c: true}]'
|
||||
//=> 'applyData: {
|
||||
// name: "push",
|
||||
// args: [3],
|
||||
// result: 2,
|
||||
// }'
|
||||
|
||||
// Access the original object
|
||||
onChange.target(watchedObject).foo = false;
|
||||
// Callback isn't called
|
||||
|
||||
// Unsubscribe
|
||||
onChange.unsubscribe(watchedObject);
|
||||
watchedObject.foo = 'bar';
|
||||
// Callback isn't called
|
||||
```
|
||||
*/
|
||||
<ObjectType extends Record<string, any>>(
|
||||
object: ObjectType,
|
||||
onChange: (
|
||||
this: ObjectType,
|
||||
path: string,
|
||||
value: unknown,
|
||||
previousValue: unknown,
|
||||
applyData: ApplyData
|
||||
) => void,
|
||||
options?: Options & {pathAsArray?: false}
|
||||
): ObjectType;
|
||||
|
||||
// Overload that returns a string array as path when `ignoreSymbols` and `pathAsArray` options are true.
|
||||
<ObjectType extends Record<string, any>>(
|
||||
object: ObjectType,
|
||||
onChange: (
|
||||
this: ObjectType,
|
||||
path: string[],
|
||||
value: unknown,
|
||||
previousValue: unknown,
|
||||
applyData: ApplyData
|
||||
) => void,
|
||||
options: Options & {ignoreSymbols: true; pathAsArray: true}
|
||||
): ObjectType;
|
||||
|
||||
// Overload that returns an array as path when `pathAsArray` option is true.
|
||||
<ObjectType extends Record<string, any>>(
|
||||
object: ObjectType,
|
||||
onChange: (
|
||||
this: ObjectType,
|
||||
path: Array<string | symbol>,
|
||||
value: unknown,
|
||||
previousValue: unknown,
|
||||
applyData: ApplyData
|
||||
) => void,
|
||||
options: Options & {pathAsArray: true}
|
||||
): ObjectType;
|
||||
|
||||
/**
|
||||
@param object - Object that is already being watched for changes.
|
||||
@returns The original unwatched object.
|
||||
*/
|
||||
target<ObjectType extends Record<string, any>>(object: ObjectType): ObjectType;
|
||||
|
||||
/**
|
||||
Cancels all future callbacks on a watched object.
|
||||
|
||||
@param object - Object that is already being watched for changes.
|
||||
@returns The original unwatched object.
|
||||
*/
|
||||
unsubscribe<ObjectType extends Record<string, any>>(object: ObjectType): ObjectType;
|
||||
};
|
||||
|
||||
export default onChange;
|
||||
438
Frontend-Learner/node_modules/on-change/source/index.js
generated
vendored
Normal file
438
Frontend-Learner/node_modules/on-change/source/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,438 @@
|
|||
/* eslint-disable unicorn/prefer-spread */
|
||||
import {TARGET, UNSUBSCRIBE, PATH_SEPARATOR} from './constants.js';
|
||||
import {isBuiltinWithMutableMethods, isBuiltinWithoutMutableMethods} from './is-builtin.js';
|
||||
import path from './path.js';
|
||||
import isObject from './is-object.js';
|
||||
import isSymbol from './is-symbol.js';
|
||||
import isIterator from './is-iterator.js';
|
||||
import wrapIterator from './wrap-iterator.js';
|
||||
import ignoreProperty from './ignore-property.js';
|
||||
import Cache from './cache.js';
|
||||
import SmartClone from './smart-clone/smart-clone.js';
|
||||
|
||||
// Constant set of iterator method names for efficient lookup
|
||||
const ITERATOR_METHOD_NAMES = new Set(['values', 'keys', 'entries']);
|
||||
|
||||
// Constant set of array search methods for efficient lookup
|
||||
const ARRAY_SEARCH_METHODS = new Set(['indexOf', 'lastIndexOf', 'includes']);
|
||||
|
||||
const defaultOptions = {
|
||||
equals: Object.is,
|
||||
isShallow: false,
|
||||
pathAsArray: false,
|
||||
ignoreSymbols: false,
|
||||
ignoreUnderscores: false,
|
||||
ignoreDetached: false,
|
||||
details: false,
|
||||
};
|
||||
|
||||
const shouldProvideApplyData = (details, methodName) => details === false
|
||||
|| details === true
|
||||
|| (Array.isArray(details) && details.includes(methodName));
|
||||
|
||||
const onChange = (object, onChange, options = {}) => {
|
||||
options = {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
};
|
||||
|
||||
const proxyTarget = Symbol('ProxyTarget');
|
||||
const {equals, isShallow, ignoreDetached, details} = options;
|
||||
const cache = new Cache(equals);
|
||||
const hasOnValidate = typeof options.onValidate === 'function';
|
||||
const smartClone = new SmartClone(hasOnValidate);
|
||||
|
||||
// eslint-disable-next-line max-params
|
||||
const validate = (target, property, value, previous, applyData) => !hasOnValidate
|
||||
|| smartClone.isCloning
|
||||
|| options.onValidate(path.concat(cache.getPath(target), property), value, previous, applyData) === true;
|
||||
|
||||
// eslint-disable-next-line max-params
|
||||
const handleChangeOnTarget = (target, property, value, previous, applyData) => {
|
||||
if (
|
||||
ignoreProperty(cache, options, property)
|
||||
|| (ignoreDetached && cache.isDetached(target, object))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine which paths to notify
|
||||
const allPaths = cache.getAllPaths(target);
|
||||
const pathsToNotify = !smartClone.isCloning && allPaths && allPaths.length > 1
|
||||
? allPaths
|
||||
: [cache.getPath(target)];
|
||||
|
||||
// Notify all relevant paths
|
||||
for (const changePath of pathsToNotify) {
|
||||
handleChange(changePath, property, value, previous, applyData);
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line max-params
|
||||
const handleChange = (changePath, property, value, previous, applyData) => {
|
||||
if (smartClone.isCloning && smartClone.isPartOfClone(changePath)) {
|
||||
smartClone.update(changePath, property, previous);
|
||||
} else {
|
||||
onChange(path.concat(changePath, property), value, previous, applyData);
|
||||
}
|
||||
};
|
||||
|
||||
const getProxyTarget = value =>
|
||||
(value !== null && (typeof value === 'object' || typeof value === 'function'))
|
||||
? (value[proxyTarget] ?? value)
|
||||
: value;
|
||||
|
||||
const prepareValue = (value, target, property, basePath) => {
|
||||
if (
|
||||
isBuiltinWithoutMutableMethods(value)
|
||||
|| property === 'constructor'
|
||||
|| (isShallow && !SmartClone.isHandledMethod(target, property))
|
||||
|| ignoreProperty(cache, options, property)
|
||||
|| cache.isGetInvariant(target, property)
|
||||
|| (ignoreDetached && cache.isDetached(target, object))
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (basePath === undefined) {
|
||||
basePath = cache.getPath(target);
|
||||
}
|
||||
|
||||
/*
|
||||
Check for circular references.
|
||||
|
||||
If the value already has a corresponding path/proxy,
|
||||
and if the path corresponds to one of the parents,
|
||||
then we are on a circular case, where the child is pointing to their parent.
|
||||
In this case we return the proxy object with the shortest path.
|
||||
*/
|
||||
const childPath = path.concat(basePath, property);
|
||||
const existingPath = cache.getPath(value);
|
||||
|
||||
if (existingPath && isSameObjectTree(childPath, existingPath)) {
|
||||
// We are on the same object tree but deeper, so we use the parent path.
|
||||
return cache.getProxy(value, existingPath, handler, proxyTarget);
|
||||
}
|
||||
|
||||
return cache.getProxy(value, childPath, handler, proxyTarget);
|
||||
};
|
||||
|
||||
/*
|
||||
Returns true if `childPath` is a subpath of `existingPath`
|
||||
(if childPath starts with existingPath). Otherwise, it returns false.
|
||||
|
||||
It also returns false if the 2 paths are identical.
|
||||
|
||||
For example:
|
||||
- childPath = group.layers.0.parent.layers.0.value
|
||||
- existingPath = group.layers.0.parent
|
||||
*/
|
||||
const isSameObjectTree = (childPath, existingPath) => {
|
||||
if (isSymbol(childPath) || childPath.length <= existingPath.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(existingPath) && existingPath.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const childParts = Array.isArray(childPath) ? childPath : childPath.split(PATH_SEPARATOR);
|
||||
const existingParts = Array.isArray(existingPath) ? existingPath : existingPath.split(PATH_SEPARATOR);
|
||||
|
||||
if (childParts.length <= existingParts.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !(existingParts.some((part, index) => part !== childParts[index]));
|
||||
};
|
||||
|
||||
// Unified handler for SmartClone-based method execution
|
||||
const handleMethodExecution = (target, thisArgument, thisProxyTarget, argumentsList) => {
|
||||
// Standard SmartClone path for all handled types including Date
|
||||
let applyPath = path.initial(cache.getPath(target));
|
||||
const isHandledMethod = SmartClone.isHandledMethod(thisProxyTarget, target.name);
|
||||
|
||||
smartClone.start(thisProxyTarget, applyPath, argumentsList);
|
||||
|
||||
let result;
|
||||
// Special handling for array search methods that need proxy-aware comparison
|
||||
if (Array.isArray(thisProxyTarget) && ARRAY_SEARCH_METHODS.has(target.name)) {
|
||||
result = performProxyAwareArraySearch({
|
||||
proxyArray: thisProxyTarget,
|
||||
methodName: target.name,
|
||||
searchElement: argumentsList[0],
|
||||
fromIndex: argumentsList[1],
|
||||
getProxyTarget,
|
||||
});
|
||||
} else {
|
||||
result = Reflect.apply(
|
||||
target,
|
||||
smartClone.preferredThisArg(target, thisArgument, thisProxyTarget),
|
||||
isHandledMethod
|
||||
? argumentsList.map(argument => getProxyTarget(argument))
|
||||
: argumentsList,
|
||||
);
|
||||
}
|
||||
|
||||
const isChanged = smartClone.isChanged(thisProxyTarget, equals);
|
||||
const previous = smartClone.stop();
|
||||
|
||||
if (SmartClone.isHandledType(result) && isHandledMethod) {
|
||||
if (thisArgument instanceof Map && target.name === 'get') {
|
||||
applyPath = path.concat(applyPath, argumentsList[0]);
|
||||
}
|
||||
|
||||
result = cache.getProxy(result, applyPath, handler);
|
||||
}
|
||||
|
||||
if (isChanged) {
|
||||
// Provide applyData based on details configuration
|
||||
const applyData = shouldProvideApplyData(details, target.name)
|
||||
? {
|
||||
name: target.name,
|
||||
args: argumentsList,
|
||||
result,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const changePath = smartClone.isCloning
|
||||
? path.initial(applyPath)
|
||||
: applyPath;
|
||||
const property = smartClone.isCloning
|
||||
? path.last(applyPath)
|
||||
: '';
|
||||
|
||||
if (validate(path.get(object, changePath), property, thisProxyTarget, previous, applyData)) {
|
||||
handleChange(changePath, property, thisProxyTarget, previous, applyData);
|
||||
} else {
|
||||
smartClone.undo(thisProxyTarget);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(thisArgument instanceof Map || thisArgument instanceof Set)
|
||||
&& isIterator(result)
|
||||
) {
|
||||
return wrapIterator(result, target, thisArgument, applyPath, prepareValue);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const handler = {
|
||||
get(target, property, receiver) {
|
||||
if (isSymbol(property)) {
|
||||
if (property === proxyTarget || property === TARGET) {
|
||||
return target;
|
||||
}
|
||||
|
||||
if (
|
||||
property === UNSUBSCRIBE
|
||||
&& !cache.isUnsubscribed
|
||||
&& cache.getPath(target).length === 0
|
||||
) {
|
||||
cache.unsubscribe();
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
const value = isBuiltinWithMutableMethods(target)
|
||||
? Reflect.get(target, property)
|
||||
: Reflect.get(target, property, receiver);
|
||||
|
||||
return prepareValue(value, target, property);
|
||||
},
|
||||
|
||||
set(target, property, value, receiver) {
|
||||
value = getProxyTarget(value);
|
||||
|
||||
const reflectTarget = target[proxyTarget] ?? target;
|
||||
const previous = reflectTarget[property];
|
||||
|
||||
if (equals(previous, value) && property in target) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const isValid = validate(target, property, value, previous);
|
||||
|
||||
if (
|
||||
isValid
|
||||
&& cache.setProperty(reflectTarget, property, value, receiver, previous)
|
||||
) {
|
||||
handleChangeOnTarget(target, property, target[property], previous);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return !isValid;
|
||||
},
|
||||
|
||||
defineProperty(target, property, descriptor) {
|
||||
if (!cache.isSameDescriptor(descriptor, target, property)) {
|
||||
const previous = target[property];
|
||||
|
||||
if (
|
||||
validate(target, property, descriptor.value, previous)
|
||||
&& cache.defineProperty(target, property, descriptor)
|
||||
) {
|
||||
// For accessor descriptors (getters/setters), descriptor.value is undefined
|
||||
// We need to get the actual value after the property is defined
|
||||
const hasValue = Object.hasOwn(descriptor, 'value');
|
||||
const value = hasValue
|
||||
? descriptor.value
|
||||
: (() => {
|
||||
try {
|
||||
// Read the actual value through the getter
|
||||
return target[property];
|
||||
} catch {
|
||||
// If the getter throws, use undefined
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
|
||||
handleChangeOnTarget(target, property, value, previous);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
deleteProperty(target, property) {
|
||||
if (!Reflect.has(target, property)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const previous = Reflect.get(target, property);
|
||||
const isValid = validate(target, property, undefined, previous);
|
||||
|
||||
if (
|
||||
isValid
|
||||
&& cache.deleteProperty(target, property, previous)
|
||||
) {
|
||||
handleChangeOnTarget(target, property, undefined, previous);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return !isValid;
|
||||
},
|
||||
|
||||
apply(target, thisArgument, argumentsList) {
|
||||
// Handle case where thisArgument is undefined/null (e.g., extracted method calls)
|
||||
const thisProxyTarget = thisArgument?.[proxyTarget] ?? thisArgument;
|
||||
|
||||
if (cache.isUnsubscribed) {
|
||||
return Reflect.apply(target, thisProxyTarget, argumentsList);
|
||||
}
|
||||
|
||||
// Check if SmartClone should be used for aggregate change tracking
|
||||
if (SmartClone.isHandledType(thisProxyTarget)) {
|
||||
// Skip SmartClone for custom methods on plain objects to enable property-level tracking
|
||||
// Note: This approach doesn't support private fields (#field) which require the original instance
|
||||
const isPlainObjectCustomMethod = isObject(thisProxyTarget)
|
||||
&& !SmartClone.isHandledMethod(thisProxyTarget, target.name);
|
||||
|
||||
if (!isPlainObjectCustomMethod) {
|
||||
// Use SmartClone for internal methods or based on details configuration
|
||||
const isInternalMethod = typeof target.name === 'symbol'
|
||||
|| ITERATOR_METHOD_NAMES.has(target.name);
|
||||
|
||||
const shouldUseSmartClone = isInternalMethod
|
||||
|| details === false
|
||||
|| (Array.isArray(details) && !details.includes(target.name));
|
||||
|
||||
if (shouldUseSmartClone) {
|
||||
return handleMethodExecution(target, thisArgument, thisProxyTarget, argumentsList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Special handling for Date mutations when details option is used
|
||||
// This allows tracking Date method calls with apply data
|
||||
if (thisProxyTarget instanceof Date && SmartClone.isHandledMethod(thisProxyTarget, target.name)) {
|
||||
const previousTime = thisProxyTarget.getTime();
|
||||
const result = Reflect.apply(target, thisProxyTarget, argumentsList);
|
||||
const currentTime = thisProxyTarget.getTime();
|
||||
|
||||
if (!equals(previousTime, currentTime)) {
|
||||
const applyPath = cache.getPath(thisProxyTarget);
|
||||
|
||||
if (shouldProvideApplyData(details, target.name)) {
|
||||
const applyData = {
|
||||
name: target.name,
|
||||
args: argumentsList,
|
||||
result,
|
||||
};
|
||||
const previousDate = new Date(previousTime);
|
||||
|
||||
if (validate(path.get(object, applyPath), '', thisProxyTarget, previousDate, applyData)) {
|
||||
handleChange(applyPath, '', thisProxyTarget, previousDate, applyData);
|
||||
} else {
|
||||
// Undo the change if validation fails
|
||||
thisProxyTarget.setTime(previousTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// For plain object custom methods or when SmartClone is not used,
|
||||
// use the proxy as 'this' to ensure property mutations go through proxy traps
|
||||
return Reflect.apply(target, thisArgument, argumentsList);
|
||||
},
|
||||
};
|
||||
|
||||
const proxy = cache.getProxy(object, options.pathAsArray ? [] : '', handler);
|
||||
onChange = onChange.bind(proxy);
|
||||
|
||||
if (hasOnValidate) {
|
||||
options.onValidate = options.onValidate.bind(proxy);
|
||||
}
|
||||
|
||||
return proxy;
|
||||
};
|
||||
|
||||
// Helper function for array search methods that need proxy-aware comparison
|
||||
const performProxyAwareArraySearch = options => {
|
||||
const {proxyArray, methodName, searchElement, fromIndex, getProxyTarget} = options;
|
||||
const {length} = proxyArray;
|
||||
|
||||
if (length === 0) {
|
||||
return methodName === 'includes' ? false : -1;
|
||||
}
|
||||
|
||||
// Parse fromIndex according to ECMAScript specification
|
||||
const isLastIndexOf = methodName === 'lastIndexOf';
|
||||
let startIndex = fromIndex === undefined
|
||||
? (isLastIndexOf ? length - 1 : 0)
|
||||
: Math.trunc(Number(fromIndex)) || 0;
|
||||
|
||||
if (startIndex < 0) {
|
||||
startIndex = Math.max(0, length + startIndex);
|
||||
} else if (isLastIndexOf) {
|
||||
startIndex = Math.min(startIndex, length - 1);
|
||||
}
|
||||
|
||||
// Cache the search element's target for efficiency
|
||||
const searchTarget = getProxyTarget(searchElement);
|
||||
|
||||
// Search with both proxy and target comparison
|
||||
const searchBackward = methodName === 'lastIndexOf';
|
||||
const endIndex = searchBackward ? -1 : length;
|
||||
const step = searchBackward ? -1 : 1;
|
||||
|
||||
for (let index = startIndex; searchBackward ? index > endIndex : index < endIndex; index += step) {
|
||||
const element = proxyArray[index];
|
||||
if (element === searchElement || getProxyTarget(element) === searchTarget) {
|
||||
return methodName === 'includes' ? true : index;
|
||||
}
|
||||
}
|
||||
|
||||
return methodName === 'includes' ? false : -1;
|
||||
};
|
||||
|
||||
onChange.target = proxy => proxy?.[TARGET] ?? proxy;
|
||||
onChange.unsubscribe = proxy => proxy?.[UNSUBSCRIBE] ?? proxy;
|
||||
|
||||
export default onChange;
|
||||
1
Frontend-Learner/node_modules/on-change/source/is-array.js
generated
vendored
Normal file
1
Frontend-Learner/node_modules/on-change/source/is-array.js
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
export default Array.isArray;
|
||||
15
Frontend-Learner/node_modules/on-change/source/is-builtin.js
generated
vendored
Normal file
15
Frontend-Learner/node_modules/on-change/source/is-builtin.js
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
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) {
|
||||
// Primitives and null → true. Functions → false. RegExp → true.
|
||||
return value === null
|
||||
|| (typeof value !== 'object' && typeof value !== 'function')
|
||||
|| value instanceof RegExp;
|
||||
}
|
||||
5
Frontend-Learner/node_modules/on-change/source/is-iterator.js
generated
vendored
Normal file
5
Frontend-Learner/node_modules/on-change/source/is-iterator.js
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export default function isIterator(value) {
|
||||
return value !== null
|
||||
&& typeof value === 'object'
|
||||
&& typeof value.next === 'function';
|
||||
}
|
||||
3
Frontend-Learner/node_modules/on-change/source/is-object.js
generated
vendored
Normal file
3
Frontend-Learner/node_modules/on-change/source/is-object.js
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export default function isObject(value) {
|
||||
return Object.prototype.toString.call(value) === '[object Object]';
|
||||
}
|
||||
3
Frontend-Learner/node_modules/on-change/source/is-symbol.js
generated
vendored
Normal file
3
Frontend-Learner/node_modules/on-change/source/is-symbol.js
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export default function isSymbol(value) {
|
||||
return typeof value === 'symbol';
|
||||
}
|
||||
146
Frontend-Learner/node_modules/on-change/source/path.js
generated
vendored
Normal file
146
Frontend-Learner/node_modules/on-change/source/path.js
generated
vendored
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
import {PATH_SEPARATOR} from './constants.js';
|
||||
import isSymbol from './is-symbol.js';
|
||||
|
||||
const path = {
|
||||
after(path, subPath) {
|
||||
if (Array.isArray(path)) {
|
||||
return path.slice(subPath.length);
|
||||
}
|
||||
|
||||
if (subPath === '') {
|
||||
return path;
|
||||
}
|
||||
|
||||
return path.slice(subPath.length + 1);
|
||||
},
|
||||
concat(path, key) {
|
||||
if (Array.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 (Array.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 (Array.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 (Array.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 => {
|
||||
object &&= object[key];
|
||||
});
|
||||
|
||||
return object;
|
||||
},
|
||||
isSubPath(path, subPath) {
|
||||
if (Array.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 (Array.isArray(path)) {
|
||||
return path.length === 0;
|
||||
}
|
||||
|
||||
return path === '';
|
||||
},
|
||||
};
|
||||
|
||||
export default path;
|
||||
8
Frontend-Learner/node_modules/on-change/source/smart-clone/clone/clone-array.js
generated
vendored
Normal file
8
Frontend-Learner/node_modules/on-change/source/smart-clone/clone/clone-array.js
generated
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
11
Frontend-Learner/node_modules/on-change/source/smart-clone/clone/clone-date.js
generated
vendored
Normal file
11
Frontend-Learner/node_modules/on-change/source/smart-clone/clone/clone-date.js
generated
vendored
Normal 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());
|
||||
}
|
||||
}
|
||||
20
Frontend-Learner/node_modules/on-change/source/smart-clone/clone/clone-map.js
generated
vendored
Normal file
20
Frontend-Learner/node_modules/on-change/source/smart-clone/clone/clone-map.js
generated
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
115
Frontend-Learner/node_modules/on-change/source/smart-clone/clone/clone-object.js
generated
vendored
Normal file
115
Frontend-Learner/node_modules/on-change/source/smart-clone/clone/clone-object.js
generated
vendored
Normal 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, _equals) {
|
||||
return this._onIsChanged === undefined
|
||||
? this._isChanged
|
||||
: this._onIsChanged(this.clone, value);
|
||||
}
|
||||
|
||||
isPathApplicable(changePath) {
|
||||
return path.isRootPath(this._path) || path.isSubPath(changePath, this._path);
|
||||
}
|
||||
}
|
||||
20
Frontend-Learner/node_modules/on-change/source/smart-clone/clone/clone-set.js
generated
vendored
Normal file
20
Frontend-Learner/node_modules/on-change/source/smart-clone/clone/clone-set.js
generated
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
27
Frontend-Learner/node_modules/on-change/source/smart-clone/clone/clone-weakmap.js
generated
vendored
Normal file
27
Frontend-Learner/node_modules/on-change/source/smart-clone/clone/clone-weakmap.js
generated
vendored
Normal 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, _equals) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
Frontend-Learner/node_modules/on-change/source/smart-clone/clone/clone-weakset.js
generated
vendored
Normal file
22
Frontend-Learner/node_modules/on-change/source/smart-clone/clone/clone-weakset.js
generated
vendored
Normal 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, _equals) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
8
Frontend-Learner/node_modules/on-change/source/smart-clone/diff/is-diff-arrays.js
generated
vendored
Normal file
8
Frontend-Learner/node_modules/on-change/source/smart-clone/diff/is-diff-arrays.js
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export default function isDiffArrays(clone, value) {
|
||||
if (clone === value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return clone.length !== value.length
|
||||
|| clone.some((item, index) => value[index] !== item);
|
||||
}
|
||||
3
Frontend-Learner/node_modules/on-change/source/smart-clone/diff/is-diff-certain.js
generated
vendored
Normal file
3
Frontend-Learner/node_modules/on-change/source/smart-clone/diff/is-diff-certain.js
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export default function isDiffCertain() {
|
||||
return true;
|
||||
}
|
||||
19
Frontend-Learner/node_modules/on-change/source/smart-clone/diff/is-diff-maps.js
generated
vendored
Normal file
19
Frontend-Learner/node_modules/on-change/source/smart-clone/diff/is-diff-maps.js
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
export default function isDiffMaps(clone, value) {
|
||||
if (clone === value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (clone.size !== value.size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const [key, aValue] of clone) {
|
||||
const bValue = value.get(key);
|
||||
// Distinguish missing vs undefined and catch strict inequality
|
||||
if (bValue !== aValue || (bValue === undefined && !value.has(key))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
17
Frontend-Learner/node_modules/on-change/source/smart-clone/diff/is-diff-sets.js
generated
vendored
Normal file
17
Frontend-Learner/node_modules/on-change/source/smart-clone/diff/is-diff-sets.js
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
export default function isDiffSets(clone, value) {
|
||||
if (clone === value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (clone.size !== value.size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const element of clone) {
|
||||
if (!value.has(element)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
31
Frontend-Learner/node_modules/on-change/source/smart-clone/methods/array.js
generated
vendored
Normal file
31
Frontend-Learner/node_modules/on-change/source/smart-clone/methods/array.js
generated
vendored
Normal 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),
|
||||
]);
|
||||
17
Frontend-Learner/node_modules/on-change/source/smart-clone/methods/map.js
generated
vendored
Normal file
17
Frontend-Learner/node_modules/on-change/source/smart-clone/methods/map.js
generated
vendored
Normal 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,
|
||||
]);
|
||||
8
Frontend-Learner/node_modules/on-change/source/smart-clone/methods/object.js
generated
vendored
Normal file
8
Frontend-Learner/node_modules/on-change/source/smart-clone/methods/object.js
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export const IMMUTABLE_OBJECT_METHODS = new Set([
|
||||
'hasOwnProperty',
|
||||
'isPrototypeOf',
|
||||
'propertyIsEnumerable',
|
||||
'toLocaleString',
|
||||
'toString',
|
||||
'valueOf',
|
||||
]);
|
||||
25
Frontend-Learner/node_modules/on-change/source/smart-clone/methods/set.js
generated
vendored
Normal file
25
Frontend-Learner/node_modules/on-change/source/smart-clone/methods/set.js
generated
vendored
Normal 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,
|
||||
]);
|
||||
99
Frontend-Learner/node_modules/on-change/source/smart-clone/smart-clone.js
generated
vendored
Normal file
99
Frontend-Learner/node_modules/on-change/source/smart-clone/smart-clone.js
generated
vendored
Normal 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(value, equals) {
|
||||
return this._stack.at(-1).isChanged(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;
|
||||
}
|
||||
}
|
||||
69
Frontend-Learner/node_modules/on-change/source/wrap-iterator.js
generated
vendored
Normal file
69
Frontend-Learner/node_modules/on-change/source/wrap-iterator.js
generated
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import {TARGET} from './constants.js';
|
||||
|
||||
/**
|
||||
Wraps an iterator's `next()` so yielded values (or [key, value] pairs) are passed through `prepareValue` with the correct owner and path.
|
||||
*/
|
||||
// eslint-disable-next-line max-params
|
||||
export default function wrapIterator(iterator, target, thisArgument, applyPath, prepareValue) {
|
||||
const originalNext = iterator?.next;
|
||||
if (typeof originalNext !== 'function') {
|
||||
return iterator;
|
||||
}
|
||||
|
||||
if (target.name === 'entries') {
|
||||
iterator.next = function () {
|
||||
const result = originalNext.call(this);
|
||||
|
||||
if (result && 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 && 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 && result.done === false) {
|
||||
result.value = prepareValue(
|
||||
result.value,
|
||||
target,
|
||||
result.value,
|
||||
applyPath,
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
return iterator;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue