@compilacion/colleciones-clientos 2.0.28 → 2.0.30
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/dist/browser.domainModel.gtm.config.sandbox.js +6 -2
- package/dist/browser.domainModel.gtm.config.sandbox.js.map +1 -1
- package/dist/browser.domainModel.gtm.js +151 -22
- package/dist/browser.domainModel.gtm.js.map +1 -1
- package/dist/browser.domainModel.gtm.min.js +1 -1
- package/dist/browser.domainModel.gtm.min.js.map +1 -1
- package/dist/browser.domainModel.gtm.sandbox.js.map +1 -1
- package/dist/browser.domainModel.gtm.sharedWindowNames.js.map +1 -1
- package/dist/gtm/Colleciones Client/303/250s Configurationes.tpl" +18 -1
- package/docs/gtm/domain-model.md +63 -0
- package/package.json +1 -1
|
@@ -10,12 +10,13 @@ var collecionesClientosGtmConfigSandbox = (function (exports) {
|
|
|
10
10
|
'trackerName': 'trackerName',
|
|
11
11
|
'appName': 'appName',
|
|
12
12
|
'emitterEndpoint': 'emitterEndpoint',
|
|
13
|
+
'errorLoggingEndpoint': 'errorLoggingEndpoint',
|
|
13
14
|
'typeName': 'type'
|
|
14
15
|
},
|
|
15
16
|
'typeCastGtmVariableConfig': 'compilacionCollecionesClientosGtmTypeCastConfig'
|
|
16
17
|
};
|
|
17
18
|
|
|
18
|
-
function getConfigObject(endpoint, flushInterval, flushSize, trackerName, appName, jsLibSource, jsLibUrlInput) {
|
|
19
|
+
function getConfigObject(endpoint, flushInterval, flushSize, trackerName, appName, jsLibSource, jsLibUrlInput, errorLoggingEndpoint) {
|
|
19
20
|
let returnObject = {};
|
|
20
21
|
returnObject[constants.config.typeName] = constants.typeCastGtmVariableConfig;
|
|
21
22
|
returnObject[constants.config.emitterEndpoint] = endpoint;
|
|
@@ -25,9 +26,12 @@ var collecionesClientosGtmConfigSandbox = (function (exports) {
|
|
|
25
26
|
returnObject[constants.config.appName] = appName;
|
|
26
27
|
returnObject[constants.config.sourceName] = jsLibSource;
|
|
27
28
|
returnObject[constants.config.jsLibUrlVariable] = jsLibUrlInput;
|
|
29
|
+
// Optional: providing the URL of the server-side error-logging endpoint is what
|
|
30
|
+
// turns frontend error forwarding on. When left empty the runtime stays silent.
|
|
31
|
+
returnObject[constants.config.errorLoggingEndpoint] = errorLoggingEndpoint;
|
|
28
32
|
return returnObject;
|
|
29
33
|
}
|
|
30
|
-
/* return getConfigObject(endpoint, flushInterval, flushSize, trackerName, appName, jsLibSource, jsLibUrl);*/
|
|
34
|
+
/* return getConfigObject(endpoint, flushInterval, flushSize, trackerName, appName, jsLibSource, jsLibUrl, errorLoggingEndpoint);*/
|
|
31
35
|
|
|
32
36
|
exports.getConfigObject = getConfigObject;
|
|
33
37
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.domainModel.gtm.config.sandbox.js","sources":["../src/browser.domainModel.gtm.constants.js","../src/browser.domainModel.gtm.config.sandbox.js"],"sourcesContent":["var constants = {\n 'sharedWindowObjectNames': {\n 'queue': 'compilacionCollecionesClientosGtmQueue',\n 'configVariable': 'compilacionCollecionesClientosGtmTypeCastConfig'\n },\n 'config': {\n 'sourceName': 'jsLibSource',\n 'jsLibUrlVariable': 'jsLibSource_selfHostedUrl',\n 'selfHostedValue': 'SELF',\n 'unpkgHostedValue': 'UNPKG',\n 'flushInterval': 'flushInterval',\n 'flushSize': 'flushSize',\n 'trackerName': 'trackerName',\n 'appName': 'appName',\n 'emitterEndpoint': 'emitterEndpoint',\n 'typeName': 'type'\n },\n 'collecionesObject': 'compilacionCollecionesClientosGtm',\n 'typeCastGtmVariableConfig': 'compilacionCollecionesClientosGtmTypeCastConfig'\n};\n\nexport default constants;","import constants from './browser.domainModel.gtm.constants.js';\n\nexport function getConfigObject(endpoint, flushInterval, flushSize, trackerName, appName, jsLibSource, jsLibUrlInput) {
|
|
1
|
+
{"version":3,"file":"browser.domainModel.gtm.config.sandbox.js","sources":["../src/browser.domainModel.gtm.constants.js","../src/browser.domainModel.gtm.config.sandbox.js"],"sourcesContent":["var constants = {\n 'sharedWindowObjectNames': {\n 'queue': 'compilacionCollecionesClientosGtmQueue',\n 'configVariable': 'compilacionCollecionesClientosGtmTypeCastConfig'\n },\n 'config': {\n 'sourceName': 'jsLibSource',\n 'jsLibUrlVariable': 'jsLibSource_selfHostedUrl',\n 'selfHostedValue': 'SELF',\n 'unpkgHostedValue': 'UNPKG',\n 'flushInterval': 'flushInterval',\n 'flushSize': 'flushSize',\n 'trackerName': 'trackerName',\n 'appName': 'appName',\n 'emitterEndpoint': 'emitterEndpoint',\n 'errorLoggingEndpoint': 'errorLoggingEndpoint',\n 'typeName': 'type'\n },\n 'collecionesObject': 'compilacionCollecionesClientosGtm',\n 'typeCastGtmVariableConfig': 'compilacionCollecionesClientosGtmTypeCastConfig'\n};\n\nexport default constants;","import constants from './browser.domainModel.gtm.constants.js';\n\nexport function getConfigObject(endpoint, flushInterval, flushSize, trackerName, appName, jsLibSource, jsLibUrlInput, errorLoggingEndpoint) {\n let jsLibUrl = null;\n if(jsLibSource === constants.config.unpkgHostedValue) {\n jsLibUrl = \"https://unpkg.com/@compilacion/colleciones-clientos@latest/dist/browser.gtm.min.js\";\n } else if (jsLibSource === constants.config.selfHostedValue) {\n jsLibUrl = jsLibUrlInput\n }\n let returnObject = {};\n returnObject[constants.config.typeName] = constants.typeCastGtmVariableConfig;\n returnObject[constants.config.emitterEndpoint] = endpoint;\n returnObject[constants.config.flushInterval] = flushInterval;\n returnObject[constants.config.flushSize] = flushSize;\n returnObject[constants.config.trackerName] = trackerName;\n returnObject[constants.config.appName] = appName;\n returnObject[constants.config.sourceName] = jsLibSource;\n returnObject[constants.config.jsLibUrlVariable] = jsLibUrlInput;\n // Optional: providing the URL of the server-side error-logging endpoint is what\n // turns frontend error forwarding on. When left empty the runtime stays silent.\n returnObject[constants.config.errorLoggingEndpoint] = errorLoggingEndpoint;\n return returnObject;\n}\n/* return getConfigObject(endpoint, flushInterval, flushSize, trackerName, appName, jsLibSource, jsLibUrl, errorLoggingEndpoint);*/"],"names":[],"mappings":";;;IAAA,IAAI,SAAS,GAAG;IAChB,IAII,QAAQ,EAAE;IACd,QAAQ,YAAY,EAAE,aAAa;IACnC,QAAQ,kBAAkB,EAAE,2BAA2B;IACvD,QAEQ,eAAe,EAAE,eAAe;IACxC,QAAQ,WAAW,EAAE,WAAW;IAChC,QAAQ,aAAa,EAAE,aAAa;IACpC,QAAQ,SAAS,EAAE,SAAS;IAC5B,QAAQ,iBAAiB,EAAE,iBAAiB;IAC5C,QAAQ,sBAAsB,EAAE,sBAAsB;IACtD,QAAQ,UAAU,EAAE;IACpB,KAAK;IACL,IACI,2BAA2B,EAAE;IACjC,CAAC;;IClBM,SAAS,eAAe,CAAC,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,oBAAoB,EAAE;IAO5I,IAAI,IAAI,YAAY,GAAG,EAAE;IACzB,IAAI,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC,yBAAyB;IACjF,IAAI,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,eAAe,CAAC,GAAG,QAAQ;IAC7D,IAAI,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,aAAa;IAChE,IAAI,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,SAAS;IACxD,IAAI,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,WAAW;IAC5D,IAAI,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,OAAO;IACpD,IAAI,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,WAAW;IAC3D,IAAI,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,gBAAgB,CAAC,GAAG,aAAa;IACnE;IACA;IACA,IAAI,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,oBAAoB,CAAC,GAAG,oBAAoB;IAC9E,IAAI,OAAO,YAAY;IACvB;IACA;;;;;;;;;;"}
|
|
@@ -791,7 +791,8 @@
|
|
|
791
791
|
'flushSize': 'flushSize',
|
|
792
792
|
'trackerName': 'trackerName',
|
|
793
793
|
'appName': 'appName',
|
|
794
|
-
'emitterEndpoint': 'emitterEndpoint'
|
|
794
|
+
'emitterEndpoint': 'emitterEndpoint',
|
|
795
|
+
'errorLoggingEndpoint': 'errorLoggingEndpoint'},
|
|
795
796
|
'collecionesObject': 'compilacionCollecionesClientosGtm',
|
|
796
797
|
'typeCastGtmVariableConfig': 'compilacionCollecionesClientosGtmTypeCastConfig'
|
|
797
798
|
};
|
|
@@ -808,6 +809,62 @@
|
|
|
808
809
|
let queueReference = null;
|
|
809
810
|
let processedQueueLength = 0;
|
|
810
811
|
|
|
812
|
+
// Identity/destination details captured from the GTM config so a caught error
|
|
813
|
+
// can be attributed (tracker/app) and forwarded to the right endpoint.
|
|
814
|
+
let appName = null;
|
|
815
|
+
let trackerName = null;
|
|
816
|
+
let errorLoggingEndpoint = null;
|
|
817
|
+
|
|
818
|
+
const safeStringify = (value) => {
|
|
819
|
+
if (value === undefined) return '';
|
|
820
|
+
if (typeof value === 'string') return value;
|
|
821
|
+
try {
|
|
822
|
+
return JSON.stringify(value);
|
|
823
|
+
} catch (e) {
|
|
824
|
+
return String(value);
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
|
|
828
|
+
// Forward a caught client-side error to the configured bad-event endpoint.
|
|
829
|
+
// The whole point of this is to make the try/catch fallbacks visible server-side,
|
|
830
|
+
// so it is strictly best-effort and must NEVER throw: a failure to report an
|
|
831
|
+
// error can not be allowed to break the tracking flow it is meant to protect.
|
|
832
|
+
// Reporting only happens when an errorLoggingEndpoint URL is configured.
|
|
833
|
+
const reportError = (error, details = {}) => {
|
|
834
|
+
if (!errorLoggingEndpoint) return;
|
|
835
|
+
if (typeof fetch !== 'function') return;
|
|
836
|
+
try {
|
|
837
|
+
const payload = {
|
|
838
|
+
errorName: (error && error.name) ? String(error.name) : 'Error',
|
|
839
|
+
errorMessage: (error && error.message) ? String(error.message) : String(error),
|
|
840
|
+
tracker: trackerName != null ? String(trackerName) : '',
|
|
841
|
+
appName: appName != null ? String(appName) : '',
|
|
842
|
+
entity: details.entity != null ? String(details.entity) : '',
|
|
843
|
+
action: details.action != null ? String(details.action) : '',
|
|
844
|
+
rawEvent: safeStringify(details.rawEvent),
|
|
845
|
+
context: {
|
|
846
|
+
// phase identifies which fallback caught the error: init, flush or group.
|
|
847
|
+
phase: details.phase || '',
|
|
848
|
+
stack: (error && error.stack) ? String(error.stack) : '',
|
|
849
|
+
url: (typeof window !== 'undefined' && window.location) ? window.location.href : '',
|
|
850
|
+
userAgent: (typeof navigator !== 'undefined' && navigator.userAgent) ? navigator.userAgent : '',
|
|
851
|
+
language: (typeof navigator !== 'undefined' && navigator.language) ? navigator.language : '',
|
|
852
|
+
clientTimestamp: new Date().toISOString()
|
|
853
|
+
}
|
|
854
|
+
};
|
|
855
|
+
// keepalive lets a report fired during page unload still have a chance to land.
|
|
856
|
+
fetch(errorLoggingEndpoint, {
|
|
857
|
+
method: 'POST',
|
|
858
|
+
credentials: 'include',
|
|
859
|
+
keepalive: true,
|
|
860
|
+
headers: { 'Content-Type': 'application/json' },
|
|
861
|
+
body: JSON.stringify(payload)
|
|
862
|
+
}).catch(() => {});
|
|
863
|
+
} catch (e) {
|
|
864
|
+
// Swallow: error reporting can never be allowed to surface its own exception.
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
|
|
811
868
|
let init = () => {
|
|
812
869
|
if (initialized) return true;
|
|
813
870
|
if (window[constants.sharedWindowObjectNames.configVariable] === undefined) return false;
|
|
@@ -815,8 +872,11 @@
|
|
|
815
872
|
if (config.type === undefined || config.type !== constants.typeCastGtmVariableConfig) return false;
|
|
816
873
|
let flushInterval = config[constants.config.flushInterval];
|
|
817
874
|
let flushSize = config[constants.config.flushSize];
|
|
818
|
-
|
|
819
|
-
|
|
875
|
+
trackerName = config[constants.config.trackerName];
|
|
876
|
+
appName = config[constants.config.appName];
|
|
877
|
+
// Capture the error endpoint as soon as a valid config is available so even an
|
|
878
|
+
// emitter-init failure below can already be reported.
|
|
879
|
+
errorLoggingEndpoint = config[constants.config.errorLoggingEndpoint] ?? null;
|
|
820
880
|
let emitterEndpoint = config[constants.config.emitterEndpoint] ?? config.endpoint;
|
|
821
881
|
try {
|
|
822
882
|
emitter = new CollecionesEmitter(emitterEndpoint, flushSize, flushInterval);
|
|
@@ -825,6 +885,7 @@
|
|
|
825
885
|
return true;
|
|
826
886
|
} catch (e) {
|
|
827
887
|
console.error('Error initializing CollecionesEmitter:', e);
|
|
888
|
+
reportError(e, { phase: 'init' });
|
|
828
889
|
return false;
|
|
829
890
|
}
|
|
830
891
|
};
|
|
@@ -835,6 +896,15 @@
|
|
|
835
896
|
if (!tracker) return false;
|
|
836
897
|
if (!arg || typeof arg !== 'object') return false;
|
|
837
898
|
|
|
899
|
+
// GTM variables can resolve to numbers/booleans or be left empty. Coerce
|
|
900
|
+
// entity/action/actor names to a string so a mistyped or empty config value
|
|
901
|
+
// cannot throw inside CollecionesEvent (formatToCamelCase) and drop the event.
|
|
902
|
+
const toEventName = (value) => {
|
|
903
|
+
if (typeof value === 'string') return value;
|
|
904
|
+
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
|
|
905
|
+
return '';
|
|
906
|
+
};
|
|
907
|
+
|
|
838
908
|
const normalizeIdentifiers = (identifiers) => {
|
|
839
909
|
if (Array.isArray(identifiers)) {
|
|
840
910
|
return identifiers.flatMap(identifier => {
|
|
@@ -970,24 +1040,27 @@
|
|
|
970
1040
|
};
|
|
971
1041
|
|
|
972
1042
|
const populateEvent = (eventArg, event) => {
|
|
973
|
-
event.setEntity(eventArg.entity);
|
|
974
|
-
event.setAction(eventArg.action);
|
|
1043
|
+
event.setEntity(toEventName(eventArg.entity));
|
|
1044
|
+
event.setAction(toEventName(eventArg.action));
|
|
975
1045
|
|
|
976
1046
|
normalizeIdentifiers(eventArg.identifiers).forEach(identifier => {
|
|
977
1047
|
if (typeof identifier !== 'object' || identifier === null) return;
|
|
978
|
-
|
|
1048
|
+
// Reject only an absent name or a null/undefined value; falsy-but-valid
|
|
1049
|
+
// values such as 0 or '' must still be kept (matches addCollectionItem).
|
|
1050
|
+
if (!identifier.name || identifier.value == null) return;
|
|
979
1051
|
event.setIdentifier(identifier.name, identifier.value);
|
|
980
1052
|
});
|
|
981
1053
|
|
|
982
|
-
|
|
983
|
-
|
|
1054
|
+
const actorName = toEventName(eventArg.actor?.entity ?? eventArg.actor?.name);
|
|
1055
|
+
if (actorName) {
|
|
1056
|
+
event.setActor(actorName);
|
|
984
1057
|
let identifiers = eventArg.actor?.identifiers;
|
|
985
1058
|
if (!Array.isArray(identifiers)) {
|
|
986
|
-
identifiers = identifiers?.[
|
|
1059
|
+
identifiers = identifiers?.[actorName] ?? identifiers;
|
|
987
1060
|
}
|
|
988
1061
|
normalizeIdentifiers(identifiers).forEach(identifier => {
|
|
989
1062
|
if (typeof identifier !== 'object' || identifier === null) return;
|
|
990
|
-
if (!identifier.name ||
|
|
1063
|
+
if (!identifier.name || identifier.value == null) return;
|
|
991
1064
|
event.setActorIdentifier(identifier.name, identifier.value);
|
|
992
1065
|
});
|
|
993
1066
|
}
|
|
@@ -995,15 +1068,30 @@
|
|
|
995
1068
|
const relations = eventArg?.relations ?? eventArg?.rerelations;
|
|
996
1069
|
if (relations && typeof relations === 'object') {
|
|
997
1070
|
Object.keys(relations).forEach(relationName => {
|
|
1071
|
+
const relationIdentifiers = relations[relationName];
|
|
998
1072
|
event.setReference(relationName);
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1073
|
+
// A relation can be declared while its identifier object is left empty
|
|
1074
|
+
// (an unfilled GTM variable resolves to undefined/null). Guard against
|
|
1075
|
+
// non-object values so Object.keys cannot throw and drop the event.
|
|
1076
|
+
if (relationIdentifiers && typeof relationIdentifiers === 'object') {
|
|
1077
|
+
Object.keys(relationIdentifiers).forEach(identifierName => {
|
|
1078
|
+
event.setReferenceIdentifier(relationName, identifierName, relationIdentifiers[identifierName]);
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1002
1081
|
});
|
|
1003
1082
|
}
|
|
1004
|
-
|
|
1083
|
+
// Adjectives may arrive as an array of values or as an object keyed by adjective.
|
|
1084
|
+
if (Array.isArray(eventArg?.adjectives)) {
|
|
1085
|
+
eventArg.adjectives.forEach(adjective => {
|
|
1086
|
+
if (typeof adjective === 'string' && adjective.length > 0) {
|
|
1087
|
+
event.addAdjective(adjective);
|
|
1088
|
+
}
|
|
1089
|
+
});
|
|
1090
|
+
} else if (eventArg?.adjectives && typeof eventArg.adjectives === 'object') {
|
|
1005
1091
|
Object.keys(eventArg.adjectives).forEach(adjectiveName => {
|
|
1006
|
-
|
|
1092
|
+
if (adjectiveName.length > 0) {
|
|
1093
|
+
event.addAdjective(adjectiveName);
|
|
1094
|
+
}
|
|
1007
1095
|
});
|
|
1008
1096
|
}
|
|
1009
1097
|
|
|
@@ -1022,10 +1110,23 @@
|
|
|
1022
1110
|
let added = 0;
|
|
1023
1111
|
arg.events.forEach(eventArg => {
|
|
1024
1112
|
if (!eventArg || typeof eventArg !== 'object') return;
|
|
1025
|
-
if (!eventArg.entity || !eventArg.action) return;
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1113
|
+
if (!toEventName(eventArg.entity) || !toEventName(eventArg.action)) return;
|
|
1114
|
+
// Build the event before registering it so a single malformed event cannot
|
|
1115
|
+
// throw and take the whole group (including its valid siblings) down with it.
|
|
1116
|
+
try {
|
|
1117
|
+
const event = new CollecionesEvent();
|
|
1118
|
+
populateEvent(eventArg, event);
|
|
1119
|
+
group.addEvent(event);
|
|
1120
|
+
added++;
|
|
1121
|
+
} catch (e) {
|
|
1122
|
+
console.error('Colleciones: skipped malformed grouped event', e);
|
|
1123
|
+
reportError(e, {
|
|
1124
|
+
phase: 'group',
|
|
1125
|
+
rawEvent: eventArg,
|
|
1126
|
+
entity: eventArg?.entity,
|
|
1127
|
+
action: eventArg?.action
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1029
1130
|
});
|
|
1030
1131
|
if (added === 0) return false;
|
|
1031
1132
|
// Apply the semantic group name AFTER addEvent calls: each addEvent invalidates
|
|
@@ -1037,14 +1138,14 @@
|
|
|
1037
1138
|
return true;
|
|
1038
1139
|
}
|
|
1039
1140
|
|
|
1040
|
-
if (!arg.entity || !arg.action) return false;
|
|
1141
|
+
if (!toEventName(arg.entity) || !toEventName(arg.action)) return false;
|
|
1041
1142
|
const event = new CollecionesEvent();
|
|
1042
1143
|
populateEvent(arg, event);
|
|
1043
1144
|
tracker.track(event);
|
|
1044
1145
|
return true;
|
|
1045
1146
|
};
|
|
1046
1147
|
|
|
1047
|
-
let
|
|
1148
|
+
let flushInternal = () => {
|
|
1048
1149
|
if (!init()) {
|
|
1049
1150
|
return;
|
|
1050
1151
|
}
|
|
@@ -1063,7 +1164,35 @@
|
|
|
1063
1164
|
while (processedQueueLength < queue.length) {
|
|
1064
1165
|
const args = queue[processedQueueLength];
|
|
1065
1166
|
processedQueueLength += 1;
|
|
1066
|
-
|
|
1167
|
+
// Never let one bad queue entry abort the flush loop: the index has already
|
|
1168
|
+
// advanced, so an uncaught throw here would permanently skip the rest of the batch.
|
|
1169
|
+
try {
|
|
1170
|
+
track(args);
|
|
1171
|
+
} catch (e) {
|
|
1172
|
+
console.error('Colleciones: failed to process queued event', e);
|
|
1173
|
+
reportError(e, {
|
|
1174
|
+
phase: 'flush',
|
|
1175
|
+
rawEvent: args,
|
|
1176
|
+
entity: args?.entity,
|
|
1177
|
+
action: args?.action
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
// Overarching safety net. The per-event / per-group catches above exist for
|
|
1184
|
+
// RECOVERY (they keep the loop alive), so they intentionally sit inside the loop.
|
|
1185
|
+
// This wrapper catches everything else our own runtime can throw — init, queue
|
|
1186
|
+
// bookkeeping, or any path we forgot to guard — so nothing fails silently again.
|
|
1187
|
+
// It only fires for genuinely unhandled throws (the inner catches swallow theirs),
|
|
1188
|
+
// so there is no double reporting. Scope is our runtime only: page-level errors
|
|
1189
|
+
// from other scripts are deliberately not captured here.
|
|
1190
|
+
let flush = () => {
|
|
1191
|
+
try {
|
|
1192
|
+
flushInternal();
|
|
1193
|
+
} catch (e) {
|
|
1194
|
+
console.error('Colleciones: unexpected runtime failure', e);
|
|
1195
|
+
reportError(e, { phase: 'runtime' });
|
|
1067
1196
|
}
|
|
1068
1197
|
};
|
|
1069
1198
|
|