@schukai/monster 4.71.0 → 4.73.0
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.md +19 -0
- package/package.json +1 -1
- package/source/components/datatable/datasource/rest.mjs +426 -1
- package/test/util/jsdom.mjs +20 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
## [4.73.0] - 2026-01-03
|
|
6
|
+
|
|
7
|
+
### Add Features
|
|
8
|
+
|
|
9
|
+
- Enhance lookup column functionality with templating and debugging options
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## [4.72.0] - 2026-01-03
|
|
14
|
+
|
|
15
|
+
### Add Features
|
|
16
|
+
|
|
17
|
+
- Implement new fetch columns feature for issue [#360](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/360)
|
|
18
|
+
### Changes
|
|
19
|
+
|
|
20
|
+
- update tests
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
5
24
|
## [4.71.0] - 2026-01-03
|
|
6
25
|
|
|
7
26
|
### Add Features
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.4","@popperjs/core":"^2.11.8"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.
|
|
1
|
+
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.4","@popperjs/core":"^2.11.8"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.73.0"}
|
|
@@ -30,7 +30,7 @@ import { findElementWithIdUpwards } from "../../../dom/util.mjs";
|
|
|
30
30
|
import { Observer } from "../../../types/observer.mjs";
|
|
31
31
|
import { Pathfinder } from "../../../data/pathfinder.mjs";
|
|
32
32
|
import { fireCustomEvent } from "../../../dom/events.mjs";
|
|
33
|
-
import { isObject } from "../../../types/is.mjs";
|
|
33
|
+
import { isArray, isFunction, isObject, isString } from "../../../types/is.mjs";
|
|
34
34
|
|
|
35
35
|
export { Rest };
|
|
36
36
|
|
|
@@ -64,6 +64,8 @@ const intersectionObserverObserverSymbol = Symbol(
|
|
|
64
64
|
const filterObserverSymbol = Symbol("filterObserver");
|
|
65
65
|
const readRequestIdSymbol = Symbol("readRequestId");
|
|
66
66
|
const writeRequestIdSymbol = Symbol("writeRequestId");
|
|
67
|
+
const lookupCacheSymbol = Symbol("lookupCache");
|
|
68
|
+
const lookupPendingSymbol = Symbol("lookupPending");
|
|
67
69
|
|
|
68
70
|
/**
|
|
69
71
|
* A rest api datasource
|
|
@@ -174,6 +176,28 @@ class Rest extends Datasource {
|
|
|
174
176
|
validation: {
|
|
175
177
|
map: {},
|
|
176
178
|
},
|
|
179
|
+
|
|
180
|
+
lookups: {
|
|
181
|
+
enabled: false,
|
|
182
|
+
debug: false,
|
|
183
|
+
sourcePath: "dataset",
|
|
184
|
+
request: {
|
|
185
|
+
idsParam: "ids",
|
|
186
|
+
idsSeparator: ",",
|
|
187
|
+
},
|
|
188
|
+
response: {
|
|
189
|
+
path: "dataset",
|
|
190
|
+
id: "id",
|
|
191
|
+
},
|
|
192
|
+
format: {
|
|
193
|
+
template: "${name}",
|
|
194
|
+
marker: {
|
|
195
|
+
open: ["${"],
|
|
196
|
+
close: ["}"],
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
columns: {},
|
|
200
|
+
},
|
|
177
201
|
});
|
|
178
202
|
}
|
|
179
203
|
|
|
@@ -315,6 +339,7 @@ class Rest extends Datasource {
|
|
|
315
339
|
return;
|
|
316
340
|
}
|
|
317
341
|
this[dataSourceSymbol].set(transformedPayload);
|
|
342
|
+
applyLookups.call(this, requestId).catch(() => {});
|
|
318
343
|
};
|
|
319
344
|
this[dataSourceSymbol].setOption("read", opt);
|
|
320
345
|
|
|
@@ -708,6 +733,406 @@ function handleValidationError(error) {
|
|
|
708
733
|
.catch(() => {});
|
|
709
734
|
}
|
|
710
735
|
|
|
736
|
+
/**
|
|
737
|
+
* @private
|
|
738
|
+
* @return {boolean}
|
|
739
|
+
*/
|
|
740
|
+
function lookupDebugEnabled() {
|
|
741
|
+
return this.getOption("lookups.debug", false) === true;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* @private
|
|
746
|
+
* @param {number} requestId
|
|
747
|
+
* @return {Promise<void>}
|
|
748
|
+
*/
|
|
749
|
+
async function applyLookups(requestId) {
|
|
750
|
+
if (!this.getOption("lookups.enabled", false)) {
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
const columns = this.getOption("lookups.columns", {});
|
|
755
|
+
if (!isObject(columns)) {
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
const columnEntries = Object.entries(columns).filter(([, cfg]) =>
|
|
760
|
+
isObject(cfg),
|
|
761
|
+
);
|
|
762
|
+
if (columnEntries.length === 0) {
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
const resolverColumns = [];
|
|
767
|
+
const remoteColumns = [];
|
|
768
|
+
for (const [name, cfg] of columnEntries) {
|
|
769
|
+
if (isFunction(cfg.resolve)) {
|
|
770
|
+
resolverColumns.push([name, cfg]);
|
|
771
|
+
} else if (isString(cfg.url) && cfg.url !== "") {
|
|
772
|
+
remoteColumns.push([name, cfg]);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
if (resolverColumns.length > 0) {
|
|
777
|
+
updateLookupRows.call(this, requestId, (rows) => {
|
|
778
|
+
for (const [, cfg] of resolverColumns) {
|
|
779
|
+
const key = cfg.key;
|
|
780
|
+
const target = cfg.target;
|
|
781
|
+
if (!isString(key) || !isString(target)) {
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
for (const row of rows) {
|
|
786
|
+
const entry = cfg.resolve(row[key], row);
|
|
787
|
+
if (!entry) {
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
row[target] = formatLookupValue.call(this, cfg, entry, row);
|
|
791
|
+
if (isString(cfg.loadingKey)) {
|
|
792
|
+
row[cfg.loadingKey] = false;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
if (remoteColumns.length === 0) {
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
await Promise.all(
|
|
804
|
+
remoteColumns.map(([name, cfg]) =>
|
|
805
|
+
resolveRemoteLookup.call(this, requestId, name, cfg),
|
|
806
|
+
),
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* @private
|
|
812
|
+
* @param {number} requestId
|
|
813
|
+
* @param {string} name
|
|
814
|
+
* @param {object} cfg
|
|
815
|
+
* @return {Promise<void>}
|
|
816
|
+
*/
|
|
817
|
+
async function resolveRemoteLookup(requestId, name, cfg) {
|
|
818
|
+
const key = cfg.key;
|
|
819
|
+
const target = cfg.target;
|
|
820
|
+
if (!isString(key) || !isString(target)) {
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
const cache = getLookupCache.call(this, name);
|
|
825
|
+
const pending = getLookupPending.call(this, name);
|
|
826
|
+
|
|
827
|
+
const ids = collectLookupIds.call(this, key);
|
|
828
|
+
const missingIds = ids.filter((id) => !cache.has(id) && !pending.has(id));
|
|
829
|
+
|
|
830
|
+
if (missingIds.length === 0) {
|
|
831
|
+
applyLookupCache.call(this, requestId, cfg, cache);
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
updateLookupRows.call(this, requestId, (rows) => {
|
|
836
|
+
if (!isString(cfg.loadingKey)) return;
|
|
837
|
+
for (const row of rows) {
|
|
838
|
+
if (missingIds.includes(String(row[key]))) {
|
|
839
|
+
row[cfg.loadingKey] = true;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
missingIds.forEach((id) => pending.add(id));
|
|
845
|
+
|
|
846
|
+
const response = await fetchLookupEntries.call(this, cfg, missingIds);
|
|
847
|
+
response.forEach((entry, id) => cache.set(id, entry));
|
|
848
|
+
missingIds.forEach((id) => pending.delete(id));
|
|
849
|
+
|
|
850
|
+
applyLookupCache.call(this, requestId, cfg, cache);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* @private
|
|
855
|
+
* @param {number} requestId
|
|
856
|
+
* @param {object} cfg
|
|
857
|
+
* @param {Map} cache
|
|
858
|
+
*/
|
|
859
|
+
function applyLookupCache(requestId, cfg, cache) {
|
|
860
|
+
const key = cfg.key;
|
|
861
|
+
const target = cfg.target;
|
|
862
|
+
|
|
863
|
+
updateLookupRows.call(this, requestId, (rows) => {
|
|
864
|
+
for (const row of rows) {
|
|
865
|
+
const entry = cache.get(String(row[key]));
|
|
866
|
+
if (entry) {
|
|
867
|
+
row[target] = formatLookupValue.call(this, cfg, entry, row);
|
|
868
|
+
}
|
|
869
|
+
if (isString(cfg.loadingKey)) {
|
|
870
|
+
row[cfg.loadingKey] = false;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* @private
|
|
878
|
+
* @param {string} key
|
|
879
|
+
* @return {string[]}
|
|
880
|
+
*/
|
|
881
|
+
function collectLookupIds(key) {
|
|
882
|
+
const rows = resolveLookupRows.call(this);
|
|
883
|
+
if (!isArray(rows)) {
|
|
884
|
+
return [];
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
const ids = new Set();
|
|
888
|
+
for (const row of rows) {
|
|
889
|
+
const value = row?.[key];
|
|
890
|
+
if (value === undefined || value === null || value === "") {
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
if (isArray(value)) {
|
|
894
|
+
value.forEach((entry) => ids.add(String(entry)));
|
|
895
|
+
} else {
|
|
896
|
+
ids.add(String(value));
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
return Array.from(ids);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* @private
|
|
905
|
+
* @param {object} cfg
|
|
906
|
+
* @param {string[]} ids
|
|
907
|
+
* @return {Promise<Map<string, object>>}
|
|
908
|
+
*/
|
|
909
|
+
async function fetchLookupEntries(cfg, ids) {
|
|
910
|
+
const requestDefaults = this.getOption("lookups.request", {});
|
|
911
|
+
const request = {
|
|
912
|
+
...requestDefaults,
|
|
913
|
+
...(cfg.request || {}),
|
|
914
|
+
};
|
|
915
|
+
|
|
916
|
+
const init = isObject(request.init) ? request.init : {};
|
|
917
|
+
const url = buildLookupUrl(cfg.url, ids, request);
|
|
918
|
+
const debug = lookupDebugEnabled.call(this);
|
|
919
|
+
if (debug) {
|
|
920
|
+
console.debug("[monster-datasource-rest] lookup fetch", {
|
|
921
|
+
url,
|
|
922
|
+
ids,
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
const response = await fetch(url, init);
|
|
927
|
+
if (!response.ok) {
|
|
928
|
+
if (debug) {
|
|
929
|
+
console.debug("[monster-datasource-rest] lookup failed", {
|
|
930
|
+
url,
|
|
931
|
+
status: response.status,
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
return new Map();
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
let payload;
|
|
938
|
+
try {
|
|
939
|
+
payload = await response.json();
|
|
940
|
+
} catch (_error) {
|
|
941
|
+
if (debug) {
|
|
942
|
+
console.debug("[monster-datasource-rest] lookup invalid json", { url });
|
|
943
|
+
}
|
|
944
|
+
return new Map();
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
const responseDefaults = this.getOption("lookups.response", {});
|
|
948
|
+
const responseConfig = {
|
|
949
|
+
...responseDefaults,
|
|
950
|
+
...(cfg.response || {}),
|
|
951
|
+
};
|
|
952
|
+
|
|
953
|
+
let entries = payload;
|
|
954
|
+
if (isString(responseConfig.path) && responseConfig.path !== "") {
|
|
955
|
+
entries = new Pathfinder(payload).getVia(responseConfig.path);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
if (!isArray(entries)) {
|
|
959
|
+
if (debug) {
|
|
960
|
+
console.debug("[monster-datasource-rest] lookup no entries", {
|
|
961
|
+
url,
|
|
962
|
+
path: responseConfig.path,
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
return new Map();
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
const idKey = responseConfig.id || "id";
|
|
969
|
+
const result = new Map();
|
|
970
|
+
for (const entry of entries) {
|
|
971
|
+
if (!entry || entry[idKey] === undefined || entry[idKey] === null) {
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
result.set(String(entry[idKey]), entry);
|
|
975
|
+
}
|
|
976
|
+
if (debug) {
|
|
977
|
+
console.debug("[monster-datasource-rest] lookup resolved", {
|
|
978
|
+
url,
|
|
979
|
+
entries: result.size,
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
return result;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* @private
|
|
988
|
+
* @param {string} url
|
|
989
|
+
* @param {string[]} ids
|
|
990
|
+
* @param {object} request
|
|
991
|
+
* @return {string}
|
|
992
|
+
*/
|
|
993
|
+
function buildLookupUrl(url, ids, request) {
|
|
994
|
+
const idsParam = request.idsParam || "ids";
|
|
995
|
+
const idsValue = buildLookupIdsValue(ids, request);
|
|
996
|
+
|
|
997
|
+
if (url.includes("${")) {
|
|
998
|
+
const formatter = new Formatter({ ids: idsValue });
|
|
999
|
+
return formatter.format(url);
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
const separator = url.includes("?") ? "&" : "?";
|
|
1003
|
+
return `${url}${separator}${encodeURIComponent(idsParam)}=${encodeURIComponent(
|
|
1004
|
+
idsValue,
|
|
1005
|
+
)}`;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* @private
|
|
1010
|
+
* @param {string[]} ids
|
|
1011
|
+
* @param {object} request
|
|
1012
|
+
* @return {string}
|
|
1013
|
+
*/
|
|
1014
|
+
function buildLookupIdsValue(ids, request) {
|
|
1015
|
+
if (isFunction(request.queryBuilder)) {
|
|
1016
|
+
return request.queryBuilder(ids);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
const idsTemplate = request.idsTemplate;
|
|
1020
|
+
const idsSeparator = request.idsSeparator || ",";
|
|
1021
|
+
const wrapOpen = request.wrapOpen || "";
|
|
1022
|
+
const wrapClose = request.wrapClose || "";
|
|
1023
|
+
|
|
1024
|
+
let values = ids;
|
|
1025
|
+
if (isString(idsTemplate) && idsTemplate !== "") {
|
|
1026
|
+
values = ids.map((id) => {
|
|
1027
|
+
const formatter = new Formatter({ id });
|
|
1028
|
+
return formatter.format(idsTemplate);
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
return `${wrapOpen}${values.join(idsSeparator)}${wrapClose}`;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
/**
|
|
1036
|
+
* @private
|
|
1037
|
+
* @param {object} cfg
|
|
1038
|
+
* @param {object} entry
|
|
1039
|
+
* @param {object} row
|
|
1040
|
+
* @return {string}
|
|
1041
|
+
*/
|
|
1042
|
+
function formatLookupValue(cfg, entry, row) {
|
|
1043
|
+
if (isFunction(cfg.format)) {
|
|
1044
|
+
return cfg.format(entry, row);
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
const formatDefaults = this.getOption("lookups.format", {});
|
|
1048
|
+
const format = isObject(cfg.format) ? cfg.format : {};
|
|
1049
|
+
const template = format.template || formatDefaults.template || "${name}";
|
|
1050
|
+
if (!isString(template)) {
|
|
1051
|
+
return "";
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
const formatter = new Formatter({ ...entry, row });
|
|
1055
|
+
const marker = format.marker || formatDefaults.marker;
|
|
1056
|
+
if (marker?.open) {
|
|
1057
|
+
formatter.setMarker(marker.open, marker.close);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
return formatter.format(template);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* @private
|
|
1065
|
+
* @param {number} requestId
|
|
1066
|
+
* @param {Function} update
|
|
1067
|
+
*/
|
|
1068
|
+
function updateLookupRows(requestId, update) {
|
|
1069
|
+
if (this[readRequestIdSymbol] !== requestId) {
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
const data = this[dataSourceSymbol].get();
|
|
1074
|
+
const sourcePath = this.getOption("lookups.sourcePath", "dataset");
|
|
1075
|
+
const next = clone(data);
|
|
1076
|
+
const rows = resolveLookupRows.call(this, next, sourcePath);
|
|
1077
|
+
|
|
1078
|
+
if (!isArray(rows)) {
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
update(rows);
|
|
1083
|
+
this[dataSourceSymbol].set(next);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* @private
|
|
1088
|
+
* @param {object} [data]
|
|
1089
|
+
* @param {string} [sourcePath]
|
|
1090
|
+
* @return {Array|undefined}
|
|
1091
|
+
*/
|
|
1092
|
+
function resolveLookupRows(data, sourcePath) {
|
|
1093
|
+
const source = data || this[dataSourceSymbol].get();
|
|
1094
|
+
if (isString(sourcePath) && sourcePath !== "") {
|
|
1095
|
+
return new Pathfinder(source).getVia(sourcePath);
|
|
1096
|
+
}
|
|
1097
|
+
if (isArray(source)) {
|
|
1098
|
+
return source;
|
|
1099
|
+
}
|
|
1100
|
+
if (isObject(source) && isArray(source.dataset)) {
|
|
1101
|
+
return source.dataset;
|
|
1102
|
+
}
|
|
1103
|
+
return undefined;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
/**
|
|
1107
|
+
* @private
|
|
1108
|
+
* @param {string} name
|
|
1109
|
+
* @return {Map<string, object>}
|
|
1110
|
+
*/
|
|
1111
|
+
function getLookupCache(name) {
|
|
1112
|
+
if (!this[lookupCacheSymbol]) {
|
|
1113
|
+
this[lookupCacheSymbol] = new Map();
|
|
1114
|
+
}
|
|
1115
|
+
if (!this[lookupCacheSymbol].has(name)) {
|
|
1116
|
+
this[lookupCacheSymbol].set(name, new Map());
|
|
1117
|
+
}
|
|
1118
|
+
return this[lookupCacheSymbol].get(name);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* @private
|
|
1123
|
+
* @param {string} name
|
|
1124
|
+
* @return {Set<string>}
|
|
1125
|
+
*/
|
|
1126
|
+
function getLookupPending(name) {
|
|
1127
|
+
if (!this[lookupPendingSymbol]) {
|
|
1128
|
+
this[lookupPendingSymbol] = new Map();
|
|
1129
|
+
}
|
|
1130
|
+
if (!this[lookupPendingSymbol].has(name)) {
|
|
1131
|
+
this[lookupPendingSymbol].set(name, new Set());
|
|
1132
|
+
}
|
|
1133
|
+
return this[lookupPendingSymbol].get(name);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
711
1136
|
/**
|
|
712
1137
|
* @private
|
|
713
1138
|
* @return {string}
|
package/test/util/jsdom.mjs
CHANGED
|
@@ -34,9 +34,13 @@ function initJSDOM(options) {
|
|
|
34
34
|
const {window} = new JSDOM(`<!DOCTYPE html><html lang="en"><head><title>Test</title></head><body><div id="mocks"></div></body></html>`, options);
|
|
35
35
|
|
|
36
36
|
g['window'] = window;
|
|
37
|
-
|
|
38
|
-
return new Promise((resolve, reject) =>
|
|
39
|
-
|
|
37
|
+
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
let settled = false;
|
|
40
|
+
|
|
41
|
+
const finalize = () => {
|
|
42
|
+
if (settled) return;
|
|
43
|
+
settled = true;
|
|
40
44
|
|
|
41
45
|
[
|
|
42
46
|
'Blob',
|
|
@@ -78,8 +82,6 @@ function initJSDOM(options) {
|
|
|
78
82
|
'XMLSerializer',
|
|
79
83
|
].forEach(key => {
|
|
80
84
|
try {
|
|
81
|
-
console.log("setting key", key);
|
|
82
|
-
|
|
83
85
|
g[key] = window[key]
|
|
84
86
|
} catch(e) {
|
|
85
87
|
console.error("Error setting key", key, e);
|
|
@@ -128,16 +130,26 @@ function initJSDOM(options) {
|
|
|
128
130
|
ensureStorage("sessionStorage");
|
|
129
131
|
|
|
130
132
|
resolve(g);
|
|
131
|
-
|
|
133
|
+
|
|
132
134
|
}).catch(e => {
|
|
133
135
|
console.error("Error loading dom-storage", e);
|
|
134
136
|
reject(e);
|
|
135
137
|
});
|
|
138
|
+
};
|
|
136
139
|
|
|
137
|
-
|
|
138
|
-
|
|
140
|
+
const onLoad = () => {
|
|
141
|
+
window.removeEventListener("load", onLoad);
|
|
142
|
+
finalize();
|
|
143
|
+
};
|
|
139
144
|
|
|
145
|
+
window.addEventListener("load", onLoad);
|
|
140
146
|
|
|
147
|
+
if (window.document.readyState === "complete") {
|
|
148
|
+
queueMicrotask(finalize);
|
|
149
|
+
} else {
|
|
150
|
+
setTimeout(finalize, 50);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
141
153
|
});
|
|
142
154
|
}
|
|
143
155
|
|