tiny-essentials 1.25.2 → 1.25.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog/1/25/3.md +6 -0
- package/changelog/1/25/4.md +3 -0
- package/dist/v1/TinyArrayComparator.min.js +1 -0
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/TinyIframeEvents.min.js +1 -1
- package/dist/v1/build/TinyArrayComparator.cjs +7 -0
- package/dist/v1/build/TinyArrayComparator.d.mts +3 -0
- package/dist/v1/build/TinyArrayComparator.mjs +2 -0
- package/dist/v1/index.cjs +2 -0
- package/dist/v1/index.d.mts +2 -1
- package/dist/v1/index.mjs +2 -1
- package/dist/v1/libs/TinyArrayComparator.cjs +130 -0
- package/dist/v1/libs/TinyArrayComparator.d.mts +79 -0
- package/dist/v1/libs/TinyArrayComparator.mjs +112 -0
- package/dist/v1/libs/TinyIframeEvents.cjs +266 -94
- package/dist/v1/libs/TinyIframeEvents.d.mts +119 -26
- package/dist/v1/libs/TinyIframeEvents.mjs +224 -86
- package/docs/v1/README.md +1 -0
- package/docs/v1/libs/TinyArrayComparator.md +101 -0
- package/docs/v1/libs/TinyIframeEvents.md +61 -32
- package/package.json +5 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{"use strict";var e={d:(t,n)=>{for(var s in n)e.o(n,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:n[s]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{TinyIframeEvents:()=>
|
|
1
|
+
(()=>{"use strict";var e={d:(t,n)=>{for(var s in n)e.o(n,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:n[s]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};function n(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)&&"[object Object]"===Object.prototype.toString.call(e)}e.d(t,{TinyIframeEvents:()=>i});const s=class{#e=new Map;#t=10;#n=!1;#s(e,t){if("string"==typeof e)return[e];if(Array.isArray(e)&&e.every(e=>"string"==typeof e))return e;throw new TypeError(`${t}(event): event must be a string or string[]`)}setThrowOnMaxListeners(e){if("boolean"!=typeof e)throw new TypeError("setThrowOnMaxListeners(value): value must be a boolean");this.#n=e}getThrowOnMaxListeners(){return this.#n}#r(e,t,{once:n=!1}={}){let s=this.#e.get(e);Array.isArray(s)||(s=[],this.#e.set(e,s)),s.unshift({handler:t,config:{once:n}});const r=this.#t;if(r>0&&s.length>r){const t=`Possible memory leak detected. ${s.length} "${e}" listeners added. Use setMaxListeners() to increase limit.`;if(this.#n)throw new Error(t);console.warn(t)}}prependListener(e,t){const n=this.#s(e,"prependListener");if("function"!=typeof t)throw new TypeError("prependListener(event, handler): handler must be a function");for(const e of n)this.#r(e,t)}prependListenerOnce(e,t){const n=this.#s(e,"prependListenerOnce");if("function"!=typeof t)throw new TypeError("prependListenerOnce(event, handler): handler must be a function");const s=[];for(const e of n){const n=(...s)=>{this.off(e,n),t(...s)};this.#r(e,n,{once:!0}),s.push(n)}return s}#i(e,t,{once:n=!1}={}){const s=this.#s(e,"on");for(const e of s){let s=this.#e.get(e);Array.isArray(s)||(s=[],this.#e.set(e,s)),s.push({handler:t,config:{once:n}});const r=this.#t;if(r>0&&s.length>r){const t=`Possible memory leak detected. ${s.length} "${e}" listeners added. Use setMaxListeners() to increase limit.`;if(this.#n)throw new Error(t);console.warn(t)}}}on(e,t){if("function"!=typeof t)throw new TypeError("on(event, handler): handler must be a function");return this.#i(e,t)}once(e,t){const n=this.#s(e,"once");if("function"!=typeof t)throw new TypeError("once(event, handler): handler must be a function");const s=[];for(const e of n){const n=(...s)=>{this.off(e,n),t(...s)};this.#i(e,n,{once:!0}),s.push(n)}return s}appendListener(e,t){return this.on(e,t)}appendListenerOnce(e,t){return this.once(e,t)}off(e,t){const n=this.#s(e,"off");if("function"!=typeof t)throw new TypeError("off(event, handler): handler must be a function");for(const e of n){const n=this.#e.get(e);if(!Array.isArray(n))continue;const s=n.findIndex(e=>e.handler===t);-1!==s&&n.splice(s,1),0===n.length&&this.#e.delete(e)}}offAll(e){const t=this.#s(e,"offAll");for(const e of t)this.#e.delete(e)}offAllTypes(){this.#e.clear()}listenerCount(e){if("string"!=typeof e)throw new TypeError("listenerCount(event): event name must be a string");const t=this.#e.get(e);return Array.isArray(t)?t.length:0}listeners(e){if("string"!=typeof e)throw new TypeError("listeners(event): event name must be a string");const t=this.#e.get(e);return Array.isArray(t)?[...t].filter(e=>!e.config.once).map(e=>e.handler):[]}onceListeners(e){if("string"!=typeof e)throw new TypeError("onceListeners(event): event name must be a string");const t=this.#e.get(e);return Array.isArray(t)?[...t].filter(e=>e.config.once).map(e=>e.handler):[]}allListeners(e){if("string"!=typeof e)throw new TypeError("allListeners(event): event name must be a string");const t=this.#e.get(e);return Array.isArray(t)?[...t].map(e=>e.handler):[]}eventNames(){return[...this.#e.keys()]}emit(e,...t){const n=this.#s(e,"emit"),s=[];for(const e of n){const n=this.#e.get(e);Array.isArray(n)&&0!==n.length?(n.forEach(e=>e.handler(...t)),s.push(!0)):s.push(!1)}return s}setMaxListeners(e){if(!Number.isInteger(e)||e<0)throw new TypeError("setMaxListeners(n): n must be a non-negative integer");this.#t=e}getMaxListeners(){return this.#t}},r=new WeakMap,i=class{#o=new s;setThrowOnMaxListeners(e){return this.#o.setThrowOnMaxListeners(e)}getThrowOnMaxListeners(){return this.#o.getThrowOnMaxListeners()}prependListener(e,t){return this.#o.prependListener(e,t)}prependListenerOnce(e,t){return this.#o.prependListenerOnce(e,t)}appendListener(e,t){return this.#o.appendListener(e,t)}appendListenerOnce(e,t){return this.#o.appendListenerOnce(e,t)}on(e,t){return this.#o.on(e,t)}once(e,t){return this.#o.once(e,t)}off(e,t){return this.#o.off(e,t)}offAll(e){return this.#o.offAll(e)}offAllTypes(){return this.#o.offAllTypes()}listenerCount(e){return this.#o.listenerCount(e)}listeners(e){return this.#o.listeners(e)}onceListeners(e){return this.#o.onceListeners(e)}allListeners(e){return this.#o.allListeners(e)}eventNames(){return this.#o.eventNames()}setMaxListeners(e){return this.#o.setMaxListeners(e)}getMaxListeners(){return this.#o.getMaxListeners()}#a;get targetWindow(){return this.#a}#h;get targetIframeElement(){return this.#h}#d;get targetOrigin(){return this.#d}#f;get selfType(){return this.#f}#l=!1;#p=!1;get ready(){return this.#p}onReady(e){this.#p?e():this.#o.once(this.#m,e)}#c=null;#y=[];#u;#g;#m;#v=null;constructor({targetIframe:e,targetOrigin:t=window.location.origin,secretEventName:n="__tinyIframeEvent__",handshakeEventName:s="__tinyIframeHandshake__",readyEventName:i="__tinyIframeReady__"}={}){if(!(void 0===e||e instanceof HTMLIFrameElement&&e.contentWindow))throw new TypeError('[TinyIframeEvents] Invalid "targetIframe": expected HTMLIFrameElement, received '+typeof e);if("string"!=typeof t)throw new TypeError('[TinyIframeEvents] Invalid "targetOrigin": expected string, received '+typeof t);if(this.#h=e,this.#a=e?.contentWindow??window.parent,this.#d=t,this.#f=e?"parent":"iframe",this.#u=n,this.#g=s,this.#m=i,r.has(this.#a))throw new Error("Duplicate window reference.");this._boundWindowMessage=this.#w.bind(this),this._boundPortMessage=this.#E.bind(this),this.#L(),r.set(this.#a,this)}get secretEventName(){return this.#u}set secretEventName(e){if("string"!=typeof e)throw new TypeError("TinyIframeEvents: secretEventName must be a string.");this.#u=e}get handshakeEventName(){return this.#g}set handshakeEventName(e){if("string"!=typeof e)throw new TypeError("TinyIframeEvents: handshakeEventName must be a string.");this.#g=e}get readyEventName(){return this.#m}set readyEventName(e){if("string"!=typeof e)throw new TypeError("TinyIframeEvents: readyEventName must be a string.");this.#m=e}#L(){"parent"===this.#f?(this.#v=()=>{if(this.#l)return;this.#c&&this.#c.close();const e=new MessageChannel;this.#c=e.port1,this.#c.onmessage=this._boundPortMessage,this.#p=!1,this.#a.postMessage({type:this.#g},this.#d,[e.port2])},this.#h&&(this.#h.addEventListener("load",this.#v),this.#v())):window.addEventListener("message",this._boundWindowMessage,!1)}#w(e){const t=e.data,s=e.ports;n(t)&&t.type===this.#g&&0!==s.length&&(this.#c=s[0],this.#c.onmessage=this._boundPortMessage,window.removeEventListener("message",this._boundWindowMessage),this.#c.postMessage({type:`${this.#g}_ACK`}),this.#T())}#E(e){const t=e.data;if(n(t)&&t.type===`${this.#g}_ACK`)return void this.#T();if(!n(t)||!t[this.#u])return;const s=t.eventName,r=t.payload,i=t.direction;"string"!=typeof s||"iframe"===this.#f&&"iframe"!==i||"parent"===this.#f&&"parent"!==i||this.#o.emit(s,r,e)}#T(){this.#p||(this.#p=!0,this.#b(),this.#o.emit(this.#m))}#b(){for(;this.#y.length>0;){const e=this.#y.shift();e&&this.#c&&this.#c.postMessage(e)}}emit(e,t){if("string"!=typeof e)throw new TypeError("Event name must be a string.");if(this.#l)throw new Error("Cannot emit: instance has been destroyed.");const n={[this.#u]:!0,eventName:e,payload:t,direction:"parent"===this.#f?"iframe":"parent"};this.#p&&this.#c?this.#c.postMessage(n):this.#y.push(n)}isDestroyed(){return this.#l}destroy(){this.#l=!0,this.#p=!1,window.removeEventListener("message",this._boundWindowMessage),this.#h&&this.#v&&this.#h.removeEventListener("load",this.#v),this.#c&&(this.#c.close(),this.#c=null),this.#o.offAllTypes(),this.#y=[],r.delete(this.#a)}};window.TinyIframeEvents=t.TinyIframeEvents})();
|
package/dist/v1/index.cjs
CHANGED
|
@@ -49,6 +49,7 @@ var TinyLoadingScreen = require('./libs/TinyLoadingScreen.cjs');
|
|
|
49
49
|
var TinyColorValidator = require('./libs/TinyColorValidator.cjs');
|
|
50
50
|
var TinyAnalogClock = require('./libs/TinyAnalogClock.cjs');
|
|
51
51
|
var TinyTextDiffer = require('./libs/TinyTextDiffer.cjs');
|
|
52
|
+
var TinyArrayComparator = require('./libs/TinyArrayComparator.cjs');
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
|
|
@@ -179,3 +180,4 @@ exports.TinyLoadingScreen = TinyLoadingScreen;
|
|
|
179
180
|
exports.TinyColorValidator = TinyColorValidator;
|
|
180
181
|
exports.TinyAnalogClock = TinyAnalogClock;
|
|
181
182
|
exports.TinyTextDiffer = TinyTextDiffer;
|
|
183
|
+
exports.TinyArrayComparator = TinyArrayComparator;
|
package/dist/v1/index.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import TinyArrayComparator from './libs/TinyArrayComparator.mjs';
|
|
1
2
|
import TinyTextDiffer from './libs/TinyTextDiffer.mjs';
|
|
2
3
|
import TinyAnalogClock from './libs/TinyAnalogClock.mjs';
|
|
3
4
|
import TinyColorValidator from './libs/TinyColorValidator.mjs';
|
|
@@ -125,5 +126,5 @@ import { getTimeDuration } from './basics/clock.mjs';
|
|
|
125
126
|
import { shuffleArray } from './basics/array.mjs';
|
|
126
127
|
import { toTitleCase } from './basics/text.mjs';
|
|
127
128
|
import { toTitleCaseLowerFirst } from './basics/text.mjs';
|
|
128
|
-
export { TinyTextDiffer, TinyAnalogClock, TinyColorValidator, TinyLoadingScreen, TinyElementObserver, TinySimpleDice, TinyNeedBar, TinyI18, TinyCookieConsent, TinyInventory, TinyInventoryTrader, TinyArrayPaginator, TinyAdvancedRaffle, TinyDayNightCycle, TinyGamepad, TinyTextarea, TinyNewWinEvents, TinyIframeEvents, TinyLocalStorage, TinyEvents, TinyTimeout, TinyColorConverter, TinyClipboard, TinyTextRangeEditor, TinySmartScroller, UltraRandomMsgGen, TinyAfterScrollWatcher, TinyHtml, TinyNotifications, TinyDomReadyManager, TinyDragger, TinyDragDropDetector, TinyToastNotify, TinyNotifyCenter, TinyRateLimiter, ColorSafeStringify, TinyPromiseQueue, TinyLevelUp, diffArrayList, diffStrings, breakdownDuration, calculateMarketcap, compareMarketcap, getPercentage, areElsCollTop, areElsCollBottom, areElsCollLeft, areElsCollRight, areElsCollPerfTop, areElsCollPerfBottom, areElsCollPerfLeft, areElsCollPerfRight, areElsColliding, areElsPerfColliding, getElsColliding, getElsPerfColliding, getElsCollOverlap, getElsCollOverlapPos, getRectCenter, getElsRelativeCenterOffset, getElsCollDirDepth, getElsCollDetails, safeTextTrim, installWindowHiddenScript, genFibonacciSeq, isDirEmptyAsync, fileSizeAsync, dirSizeAsync, listFilesAsync, listDirsAsync, getLatestBackupPath, fetchJson, fetchText, readJsonBlob, readFileBlob, readBase64Blob, saveJsonFile, readJsonFile, writeJsonFile, ensureDirectory, clearDirectoryAsync, clearDirectory, fileExists, dirExists, isDirEmpty, ensureCopyFile, tryDeleteFile, writeTextFile, listFiles, listDirs, fileSize, dirSize, backupFile, restoreLatestBackup, renameFileBatch, renameFileRegex, renameFileAddPrefixSuffix, renameFileNormalizeCase, renameFilePadNumbers, documentIsFullScreen, isScreenFilled, requestFullScreen, exitFullScreen, isFullScreenMode, onFullScreenChange, offFullScreenChange, isJsonObject, arraySortPositions, formatBytes, addAiMarkerShortcut, extendObjType, reorderObjTypeOrder, cloneObjTypeOrder, countObj, checkObj, objType, ruleOfThree, getSimplePerc, asyncReplace, getAge, formatCustomTimer, formatDayTimer, formatTimer, getTimeDuration, shuffleArray, toTitleCase, toTitleCaseLowerFirst };
|
|
129
|
+
export { TinyArrayComparator, TinyTextDiffer, TinyAnalogClock, TinyColorValidator, TinyLoadingScreen, TinyElementObserver, TinySimpleDice, TinyNeedBar, TinyI18, TinyCookieConsent, TinyInventory, TinyInventoryTrader, TinyArrayPaginator, TinyAdvancedRaffle, TinyDayNightCycle, TinyGamepad, TinyTextarea, TinyNewWinEvents, TinyIframeEvents, TinyLocalStorage, TinyEvents, TinyTimeout, TinyColorConverter, TinyClipboard, TinyTextRangeEditor, TinySmartScroller, UltraRandomMsgGen, TinyAfterScrollWatcher, TinyHtml, TinyNotifications, TinyDomReadyManager, TinyDragger, TinyDragDropDetector, TinyToastNotify, TinyNotifyCenter, TinyRateLimiter, ColorSafeStringify, TinyPromiseQueue, TinyLevelUp, diffArrayList, diffStrings, breakdownDuration, calculateMarketcap, compareMarketcap, getPercentage, areElsCollTop, areElsCollBottom, areElsCollLeft, areElsCollRight, areElsCollPerfTop, areElsCollPerfBottom, areElsCollPerfLeft, areElsCollPerfRight, areElsColliding, areElsPerfColliding, getElsColliding, getElsPerfColliding, getElsCollOverlap, getElsCollOverlapPos, getRectCenter, getElsRelativeCenterOffset, getElsCollDirDepth, getElsCollDetails, safeTextTrim, installWindowHiddenScript, genFibonacciSeq, isDirEmptyAsync, fileSizeAsync, dirSizeAsync, listFilesAsync, listDirsAsync, getLatestBackupPath, fetchJson, fetchText, readJsonBlob, readFileBlob, readBase64Blob, saveJsonFile, readJsonFile, writeJsonFile, ensureDirectory, clearDirectoryAsync, clearDirectory, fileExists, dirExists, isDirEmpty, ensureCopyFile, tryDeleteFile, writeTextFile, listFiles, listDirs, fileSize, dirSize, backupFile, restoreLatestBackup, renameFileBatch, renameFileRegex, renameFileAddPrefixSuffix, renameFileNormalizeCase, renameFilePadNumbers, documentIsFullScreen, isScreenFilled, requestFullScreen, exitFullScreen, isFullScreenMode, onFullScreenChange, offFullScreenChange, isJsonObject, arraySortPositions, formatBytes, addAiMarkerShortcut, extendObjType, reorderObjTypeOrder, cloneObjTypeOrder, countObj, checkObj, objType, ruleOfThree, getSimplePerc, asyncReplace, getAge, formatCustomTimer, formatDayTimer, formatTimer, getTimeDuration, shuffleArray, toTitleCase, toTitleCaseLowerFirst };
|
|
129
130
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/v1/index.mjs
CHANGED
|
@@ -47,7 +47,8 @@ import TinyLoadingScreen from './libs/TinyLoadingScreen.mjs';
|
|
|
47
47
|
import TinyColorValidator from './libs/TinyColorValidator.mjs';
|
|
48
48
|
import TinyAnalogClock from './libs/TinyAnalogClock.mjs';
|
|
49
49
|
import TinyTextDiffer from './libs/TinyTextDiffer.mjs';
|
|
50
|
+
import TinyArrayComparator from './libs/TinyArrayComparator.mjs';
|
|
50
51
|
// import TinyHtmlElems from './libs/TinyHtml/index.mjs';
|
|
51
|
-
export { TinyTextDiffer, TinyAnalogClock,
|
|
52
|
+
export { TinyArrayComparator, TinyTextDiffer, TinyAnalogClock,
|
|
52
53
|
// TinyHtmlElems,
|
|
53
54
|
TinyColorValidator, TinyLoadingScreen, TinyElementObserver, TinySimpleDice, TinyNeedBar, TinyI18, TinyCookieConsent, TinyInventory, TinyInventoryTrader, TinyArrayPaginator, TinyAdvancedRaffle, TinyDayNightCycle, TinyGamepad, TinyTextarea, TinyNewWinEvents, TinyIframeEvents, TinyLocalStorage, TinyEvents, TinyTimeout, TinyColorConverter, TinyClipboard, TinyTextRangeEditor, TinySmartScroller, UltraRandomMsgGen, TinyAfterScrollWatcher, TinyHtml, TinyNotifications, TinyDomReadyManager, TinyDragger, TinyDragDropDetector, TinyToastNotify, TinyNotifyCenter, TinyRateLimiter, ColorSafeStringify, TinyPromiseQueue, TinyLevelUp, diffArrayList, diffStrings, breakdownDuration, calculateMarketcap, compareMarketcap, getPercentage, areElsCollTop, areElsCollBottom, areElsCollLeft, areElsCollRight, areElsCollPerfTop, areElsCollPerfBottom, areElsCollPerfLeft, areElsCollPerfRight, areElsColliding, areElsPerfColliding, getElsColliding, getElsPerfColliding, getElsCollOverlap, getElsCollOverlapPos, getRectCenter, getElsRelativeCenterOffset, getElsCollDirDepth, getElsCollDetails, safeTextTrim, installWindowHiddenScript, genFibonacciSeq, isDirEmptyAsync, fileSizeAsync, dirSizeAsync, listFilesAsync, listDirsAsync, getLatestBackupPath, fetchJson, fetchText, readJsonBlob, readFileBlob, readBase64Blob, saveJsonFile, readJsonFile, writeJsonFile, ensureDirectory, clearDirectoryAsync, clearDirectory, fileExists, dirExists, isDirEmpty, ensureCopyFile, tryDeleteFile, writeTextFile, listFiles, listDirs, fileSize, dirSize, backupFile, restoreLatestBackup, renameFileBatch, renameFileRegex, renameFileAddPrefixSuffix, renameFileNormalizeCase, renameFilePadNumbers, documentIsFullScreen, isScreenFilled, requestFullScreen, exitFullScreen, isFullScreenMode, onFullScreenChange, offFullScreenChange, isJsonObject, arraySortPositions, formatBytes, addAiMarkerShortcut, extendObjType, reorderObjTypeOrder, cloneObjTypeOrder, countObj, checkObj, objType, ruleOfThree, getSimplePerc, asyncReplace, getAge, formatCustomTimer, formatDayTimer, formatTimer, getTimeDuration, shuffleArray, toTitleCase, toTitleCaseLowerFirst, };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Array comparison utility.
|
|
5
|
+
* @module TinyArrayComparator
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {Object} InternalState
|
|
10
|
+
* Holds the internal tracking variables during the comparison process.
|
|
11
|
+
* @property {Map<string, any>} oldItemsMap - A map storing the hash as the key and the original item as the value.
|
|
12
|
+
* @property {AffectedItems} affectedItems - The final array that collects added or deleted items.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The final array that collects added or deleted items.
|
|
17
|
+
* @typedef {Array<{item: any, status: 'added'|'deleted'}>} AffectedItems
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Compares two arrays efficiently by hashing their items to detect additions and deletions.
|
|
22
|
+
*/
|
|
23
|
+
class TinyArrayComparator {
|
|
24
|
+
/**
|
|
25
|
+
* Generates a simple 32-bit integer hash converted to a base36 string.
|
|
26
|
+
* @param {any} item - The item to be hashed (can be an object, array, string, or number).
|
|
27
|
+
* @returns {string} The unique hash representing the item's value.
|
|
28
|
+
*/
|
|
29
|
+
static generateHash(item) {
|
|
30
|
+
const stringified = typeof item === 'object' ? JSON.stringify(item) : String(item);
|
|
31
|
+
let hash = 0;
|
|
32
|
+
|
|
33
|
+
for (let i = 0, len = stringified.length; i < len; i++) {
|
|
34
|
+
const char = stringified.charCodeAt(i);
|
|
35
|
+
hash = (hash << 5) - hash + char;
|
|
36
|
+
hash |= 0; // Convert to 32bit integer
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return hash.toString(36);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Internal storage for the base array.
|
|
44
|
+
* @type {any[]}
|
|
45
|
+
*/
|
|
46
|
+
#oldArray = [];
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Gets the current base array used for comparisons.
|
|
50
|
+
* @returns {any[]} The current initial state of the array.
|
|
51
|
+
*/
|
|
52
|
+
get oldArray() {
|
|
53
|
+
return this.#oldArray;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Sets a new base array for future comparisons.
|
|
58
|
+
* @param {Array<any>} oldArray - The initial state of the array.
|
|
59
|
+
* @throws {TypeError} Throws an error if the provided value is not an array.
|
|
60
|
+
*/
|
|
61
|
+
set oldArray(oldArray) {
|
|
62
|
+
if (!Array.isArray(oldArray))
|
|
63
|
+
throw new TypeError('The provided oldArray must be a valid Array.');
|
|
64
|
+
this.#oldArray = oldArray;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Initializes the comparator with an optional base array.
|
|
69
|
+
* @param {Array<any>} [oldArray] - The initial state of the array to be stored.
|
|
70
|
+
*/
|
|
71
|
+
constructor(oldArray) {
|
|
72
|
+
if (typeof oldArray !== 'undefined') this.oldArray = oldArray;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Proxy to generates a hash converted to a string.
|
|
77
|
+
* @param {any} item - The item to be hashed (can be an object, array, string, or number).
|
|
78
|
+
* @returns {string} The unique hash representing the item's value.
|
|
79
|
+
* @private
|
|
80
|
+
*/
|
|
81
|
+
_generateHash(item) {
|
|
82
|
+
return TinyArrayComparator.generateHash(item);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Compares the stored older array with a newer array and identifies missing or new items.
|
|
87
|
+
*
|
|
88
|
+
* @param {Array<any>} newArray - The modified state of the array.
|
|
89
|
+
* @returns {AffectedItems} An array containing the affected items and their status.
|
|
90
|
+
* @throws {TypeError} Throws an error if the provided value is not an array.
|
|
91
|
+
*/
|
|
92
|
+
compare(newArray) {
|
|
93
|
+
if (!Array.isArray(newArray))
|
|
94
|
+
throw new TypeError('The provided newArray must be a valid Array.');
|
|
95
|
+
|
|
96
|
+
/** @type {InternalState} */
|
|
97
|
+
const state = {
|
|
98
|
+
oldItemsMap: new Map(),
|
|
99
|
+
affectedItems: [],
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Step 1: Hash and store all items from the first array
|
|
103
|
+
for (const item of this.#oldArray) {
|
|
104
|
+
const hashKey = this._generateHash(item);
|
|
105
|
+
state.oldItemsMap.set(hashKey, item);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Step 2: Iterate through the second array to find matches or additions
|
|
109
|
+
for (const item of newArray) {
|
|
110
|
+
const hashKey = this._generateHash(item);
|
|
111
|
+
|
|
112
|
+
if (state.oldItemsMap.has(hashKey)) {
|
|
113
|
+
// Item exists in both, so it is unchanged. Remove from map.
|
|
114
|
+
state.oldItemsMap.delete(hashKey);
|
|
115
|
+
} else {
|
|
116
|
+
// Item was not in the old array, so it is an addition.
|
|
117
|
+
state.affectedItems.push({ item, status: 'added' });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Step 3: Any item left in the oldItemsMap was not present in the new array (deleted)
|
|
122
|
+
for (const item of state.oldItemsMap.values()) {
|
|
123
|
+
state.affectedItems.push({ item, status: 'deleted' });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return state.affectedItems;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = TinyArrayComparator;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export default TinyArrayComparator;
|
|
2
|
+
/**
|
|
3
|
+
* Holds the internal tracking variables during the comparison process.
|
|
4
|
+
*/
|
|
5
|
+
export type InternalState = {
|
|
6
|
+
/**
|
|
7
|
+
* - A map storing the hash as the key and the original item as the value.
|
|
8
|
+
*/
|
|
9
|
+
oldItemsMap: Map<string, any>;
|
|
10
|
+
/**
|
|
11
|
+
* - The final array that collects added or deleted items.
|
|
12
|
+
*/
|
|
13
|
+
affectedItems: AffectedItems;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The final array that collects added or deleted items.
|
|
17
|
+
*/
|
|
18
|
+
export type AffectedItems = Array<{
|
|
19
|
+
item: any;
|
|
20
|
+
status: "added" | "deleted";
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* @fileoverview Array comparison utility.
|
|
24
|
+
* @module TinyArrayComparator
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {Object} InternalState
|
|
28
|
+
* Holds the internal tracking variables during the comparison process.
|
|
29
|
+
* @property {Map<string, any>} oldItemsMap - A map storing the hash as the key and the original item as the value.
|
|
30
|
+
* @property {AffectedItems} affectedItems - The final array that collects added or deleted items.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* The final array that collects added or deleted items.
|
|
34
|
+
* @typedef {Array<{item: any, status: 'added'|'deleted'}>} AffectedItems
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Compares two arrays efficiently by hashing their items to detect additions and deletions.
|
|
38
|
+
*/
|
|
39
|
+
declare class TinyArrayComparator {
|
|
40
|
+
/**
|
|
41
|
+
* Generates a simple 32-bit integer hash converted to a base36 string.
|
|
42
|
+
* @param {any} item - The item to be hashed (can be an object, array, string, or number).
|
|
43
|
+
* @returns {string} The unique hash representing the item's value.
|
|
44
|
+
*/
|
|
45
|
+
static generateHash(item: any): string;
|
|
46
|
+
/**
|
|
47
|
+
* Initializes the comparator with an optional base array.
|
|
48
|
+
* @param {Array<any>} [oldArray] - The initial state of the array to be stored.
|
|
49
|
+
*/
|
|
50
|
+
constructor(oldArray?: Array<any>);
|
|
51
|
+
/**
|
|
52
|
+
* Sets a new base array for future comparisons.
|
|
53
|
+
* @param {Array<any>} oldArray - The initial state of the array.
|
|
54
|
+
* @throws {TypeError} Throws an error if the provided value is not an array.
|
|
55
|
+
*/
|
|
56
|
+
set oldArray(oldArray: Array<any>);
|
|
57
|
+
/**
|
|
58
|
+
* Gets the current base array used for comparisons.
|
|
59
|
+
* @returns {any[]} The current initial state of the array.
|
|
60
|
+
*/
|
|
61
|
+
get oldArray(): any[];
|
|
62
|
+
/**
|
|
63
|
+
* Proxy to generates a hash converted to a string.
|
|
64
|
+
* @param {any} item - The item to be hashed (can be an object, array, string, or number).
|
|
65
|
+
* @returns {string} The unique hash representing the item's value.
|
|
66
|
+
* @private
|
|
67
|
+
*/
|
|
68
|
+
private _generateHash;
|
|
69
|
+
/**
|
|
70
|
+
* Compares the stored older array with a newer array and identifies missing or new items.
|
|
71
|
+
*
|
|
72
|
+
* @param {Array<any>} newArray - The modified state of the array.
|
|
73
|
+
* @returns {AffectedItems} An array containing the affected items and their status.
|
|
74
|
+
* @throws {TypeError} Throws an error if the provided value is not an array.
|
|
75
|
+
*/
|
|
76
|
+
compare(newArray: Array<any>): AffectedItems;
|
|
77
|
+
#private;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=TinyArrayComparator.d.mts.map
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Array comparison utility.
|
|
3
|
+
* @module TinyArrayComparator
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} InternalState
|
|
7
|
+
* Holds the internal tracking variables during the comparison process.
|
|
8
|
+
* @property {Map<string, any>} oldItemsMap - A map storing the hash as the key and the original item as the value.
|
|
9
|
+
* @property {AffectedItems} affectedItems - The final array that collects added or deleted items.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* The final array that collects added or deleted items.
|
|
13
|
+
* @typedef {Array<{item: any, status: 'added'|'deleted'}>} AffectedItems
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Compares two arrays efficiently by hashing their items to detect additions and deletions.
|
|
17
|
+
*/
|
|
18
|
+
class TinyArrayComparator {
|
|
19
|
+
/**
|
|
20
|
+
* Generates a simple 32-bit integer hash converted to a base36 string.
|
|
21
|
+
* @param {any} item - The item to be hashed (can be an object, array, string, or number).
|
|
22
|
+
* @returns {string} The unique hash representing the item's value.
|
|
23
|
+
*/
|
|
24
|
+
static generateHash(item) {
|
|
25
|
+
const stringified = typeof item === 'object' ? JSON.stringify(item) : String(item);
|
|
26
|
+
let hash = 0;
|
|
27
|
+
for (let i = 0, len = stringified.length; i < len; i++) {
|
|
28
|
+
const char = stringified.charCodeAt(i);
|
|
29
|
+
hash = (hash << 5) - hash + char;
|
|
30
|
+
hash |= 0; // Convert to 32bit integer
|
|
31
|
+
}
|
|
32
|
+
return hash.toString(36);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Internal storage for the base array.
|
|
36
|
+
* @type {any[]}
|
|
37
|
+
*/
|
|
38
|
+
#oldArray = [];
|
|
39
|
+
/**
|
|
40
|
+
* Gets the current base array used for comparisons.
|
|
41
|
+
* @returns {any[]} The current initial state of the array.
|
|
42
|
+
*/
|
|
43
|
+
get oldArray() {
|
|
44
|
+
return this.#oldArray;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Sets a new base array for future comparisons.
|
|
48
|
+
* @param {Array<any>} oldArray - The initial state of the array.
|
|
49
|
+
* @throws {TypeError} Throws an error if the provided value is not an array.
|
|
50
|
+
*/
|
|
51
|
+
set oldArray(oldArray) {
|
|
52
|
+
if (!Array.isArray(oldArray))
|
|
53
|
+
throw new TypeError('The provided oldArray must be a valid Array.');
|
|
54
|
+
this.#oldArray = oldArray;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Initializes the comparator with an optional base array.
|
|
58
|
+
* @param {Array<any>} [oldArray] - The initial state of the array to be stored.
|
|
59
|
+
*/
|
|
60
|
+
constructor(oldArray) {
|
|
61
|
+
if (typeof oldArray !== 'undefined')
|
|
62
|
+
this.oldArray = oldArray;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Proxy to generates a hash converted to a string.
|
|
66
|
+
* @param {any} item - The item to be hashed (can be an object, array, string, or number).
|
|
67
|
+
* @returns {string} The unique hash representing the item's value.
|
|
68
|
+
* @private
|
|
69
|
+
*/
|
|
70
|
+
_generateHash(item) {
|
|
71
|
+
return TinyArrayComparator.generateHash(item);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Compares the stored older array with a newer array and identifies missing or new items.
|
|
75
|
+
*
|
|
76
|
+
* @param {Array<any>} newArray - The modified state of the array.
|
|
77
|
+
* @returns {AffectedItems} An array containing the affected items and their status.
|
|
78
|
+
* @throws {TypeError} Throws an error if the provided value is not an array.
|
|
79
|
+
*/
|
|
80
|
+
compare(newArray) {
|
|
81
|
+
if (!Array.isArray(newArray))
|
|
82
|
+
throw new TypeError('The provided newArray must be a valid Array.');
|
|
83
|
+
/** @type {InternalState} */
|
|
84
|
+
const state = {
|
|
85
|
+
oldItemsMap: new Map(),
|
|
86
|
+
affectedItems: [],
|
|
87
|
+
};
|
|
88
|
+
// Step 1: Hash and store all items from the first array
|
|
89
|
+
for (const item of this.#oldArray) {
|
|
90
|
+
const hashKey = this._generateHash(item);
|
|
91
|
+
state.oldItemsMap.set(hashKey, item);
|
|
92
|
+
}
|
|
93
|
+
// Step 2: Iterate through the second array to find matches or additions
|
|
94
|
+
for (const item of newArray) {
|
|
95
|
+
const hashKey = this._generateHash(item);
|
|
96
|
+
if (state.oldItemsMap.has(hashKey)) {
|
|
97
|
+
// Item exists in both, so it is unchanged. Remove from map.
|
|
98
|
+
state.oldItemsMap.delete(hashKey);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
// Item was not in the old array, so it is an addition.
|
|
102
|
+
state.affectedItems.push({ item, status: 'added' });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Step 3: Any item left in the oldItemsMap was not present in the new array (deleted)
|
|
106
|
+
for (const item of state.oldItemsMap.values()) {
|
|
107
|
+
state.affectedItems.push({ item, status: 'deleted' });
|
|
108
|
+
}
|
|
109
|
+
return state.affectedItems;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export default TinyArrayComparator;
|