@fluid-app/widget-runtime 0.1.4 → 0.1.6
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/README.md +36 -0
- package/dist/index.cjs +3 -2
- package/dist/index.d.cts +3 -2
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +3 -3
- package/dist/{remote-dom-renderer-CKvnUH8G.d.mts → remote-dom-renderer-BGIZ0FRi.d.mts} +35 -1
- package/dist/remote-dom-renderer-BGIZ0FRi.d.mts.map +1 -0
- package/dist/{remote-dom-renderer-f7OWlvvv.cjs → remote-dom-renderer-BlZKfMAA.cjs} +340 -172
- package/dist/remote-dom-renderer-BlZKfMAA.cjs.map +1 -0
- package/dist/{remote-dom-renderer-CeYSItkU.d.cts → remote-dom-renderer-BoB9jd3B.d.cts} +35 -1
- package/dist/remote-dom-renderer-BoB9jd3B.d.cts.map +1 -0
- package/dist/{remote-dom-renderer-CpdKWANy.mjs → remote-dom-renderer-Dn3pfVsl.mjs} +340 -172
- package/dist/remote-dom-renderer-Dn3pfVsl.mjs.map +1 -0
- package/dist/remote-dom-ui-contract-B140dMly.d.mts +632 -0
- package/dist/remote-dom-ui-contract-B140dMly.d.mts.map +1 -0
- package/dist/remote-dom-ui-contract-BLOyWbT2.d.cts +632 -0
- package/dist/remote-dom-ui-contract-BLOyWbT2.d.cts.map +1 -0
- package/dist/remote-dom-values-B5UZPDRy.cjs +849 -0
- package/dist/remote-dom-values-B5UZPDRy.cjs.map +1 -0
- package/dist/remote-dom-values-D4AgBNWr.d.cts.map +1 -1
- package/dist/remote-dom-values-EwoLXnSn.d.mts.map +1 -1
- package/dist/remote-dom-values-wEbrw5iW.mjs +790 -0
- package/dist/remote-dom-values-wEbrw5iW.mjs.map +1 -0
- package/dist/remote-dom.cjs +2 -2
- package/dist/remote-dom.d.cts +1 -1
- package/dist/remote-dom.d.mts +1 -1
- package/dist/remote-dom.mjs +2 -2
- package/dist/{worker-react-renderer-BKzig_Vy.mjs → worker-react-renderer-BV6ExKCS.mjs} +17 -40
- package/dist/worker-react-renderer-BV6ExKCS.mjs.map +1 -0
- package/dist/{worker-react-renderer-BvHZ2o0B.cjs → worker-react-renderer-CR-TvErh.cjs} +18 -41
- package/dist/worker-react-renderer-CR-TvErh.cjs.map +1 -0
- package/dist/worker.cjs +18 -3
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.cts +17 -2
- package/dist/worker.d.cts.map +1 -1
- package/dist/worker.d.mts +17 -1
- package/dist/worker.d.mts.map +1 -1
- package/dist/worker.mjs +17 -3
- package/dist/worker.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/remote-dom-renderer-CKvnUH8G.d.mts.map +0 -1
- package/dist/remote-dom-renderer-CeYSItkU.d.cts.map +0 -1
- package/dist/remote-dom-renderer-CpdKWANy.mjs.map +0 -1
- package/dist/remote-dom-renderer-f7OWlvvv.cjs.map +0 -1
- package/dist/remote-dom-values-BSAxrOE7.mjs +0 -238
- package/dist/remote-dom-values-BSAxrOE7.mjs.map +0 -1
- package/dist/remote-dom-values-DbgIedSg.cjs +0 -297
- package/dist/remote-dom-values-DbgIedSg.cjs.map +0 -1
- package/dist/worker-react-renderer-BKzig_Vy.mjs.map +0 -1
- package/dist/worker-react-renderer-BvHZ2o0B.cjs.map +0 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { n as release, r as retain, t as Thread } from "./Thread-BiW7rXts.mjs";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as isRemoteDomSerializableValue, c as REMOTE_DOM_SUPPORTED_UI_CONTRACT_VERSIONS, i as createRemoteDomUrlOriginPolicy, l as REMOTE_DOM_UI_COMPONENTS, o as isSafeRemoteDomPropertyKey, r as SUPPORTED_REMOTE_DOM_EVENTS, t as REMOTE_DOM_ALLOWED_TAG_NAMES, u as getRemoteDomUiComponent } from "./remote-dom-values-wEbrw5iW.mjs";
|
|
3
3
|
import { MUTATION_TYPE_INSERT_CHILD, MUTATION_TYPE_REMOVE_CHILD, MUTATION_TYPE_UPDATE_PROPERTY, MUTATION_TYPE_UPDATE_TEXT, NODE_TYPE_ELEMENT, NODE_TYPE_TEXT, ROOT_ID, UPDATE_PROPERTY_TYPE_ATTRIBUTE, UPDATE_PROPERTY_TYPE_EVENT_LISTENER, UPDATE_PROPERTY_TYPE_PROPERTY } from "@remote-dom/core";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { RemoteFragmentRenderer, RemoteReceiver, RemoteRootRenderer, renderRemoteNode, useRemoteReceived } from "@remote-dom/react/host";
|
|
5
|
+
import { createElement, useEffect, useLayoutEffect, useRef } from "react";
|
|
6
|
+
import { flushSync } from "react-dom";
|
|
7
|
+
import { createRoot } from "react-dom/client";
|
|
6
8
|
import postcssParse from "postcss/lib/parse";
|
|
7
9
|
import valueParser from "postcss-value-parser";
|
|
8
10
|
//#region src/remote-dom-sanitizer.ts
|
|
@@ -306,7 +308,6 @@ const MEDIA_SOURCE_TAGS = new Set([
|
|
|
306
308
|
"video"
|
|
307
309
|
]);
|
|
308
310
|
const SUPPORTED_REMOTE_DOM_EVENT_SET = new Set(SUPPORTED_REMOTE_DOM_EVENTS);
|
|
309
|
-
const REMOTE_DOM_SEARCH_SORT_EVENT_SET = new Set(REMOTE_DOM_SEARCH_SORT_EVENTS);
|
|
310
311
|
function sanitizeRemoteDomTagName(tagName) {
|
|
311
312
|
if (typeof tagName !== "string") return void 0;
|
|
312
313
|
const normalizedTagName = tagName.trim().toLowerCase();
|
|
@@ -322,7 +323,7 @@ function sanitizeRemoteDomAttributes(tagName, attributes, options = {}) {
|
|
|
322
323
|
}
|
|
323
324
|
return sanitizedAttributes;
|
|
324
325
|
}
|
|
325
|
-
function sanitizeRemoteDomProperty(tagName, rawName, rawValue) {
|
|
326
|
+
function sanitizeRemoteDomProperty(tagName, rawName, rawValue, options = {}) {
|
|
326
327
|
if (typeof rawName !== "string") return void 0;
|
|
327
328
|
const normalizedTagName = tagName.trim().toLowerCase();
|
|
328
329
|
const name = rawName.trim().toLowerCase();
|
|
@@ -337,45 +338,54 @@ function sanitizeRemoteDomProperty(tagName, rawName, rawValue) {
|
|
|
337
338
|
value: rawValue
|
|
338
339
|
};
|
|
339
340
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if (
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
341
|
+
const definition = getRemoteDomUiComponent(normalizedTagName);
|
|
342
|
+
if (definition) {
|
|
343
|
+
const propertyEntry = Object.entries(definition.properties).find(([propertyName]) => propertyName.toLowerCase() === name);
|
|
344
|
+
if (!propertyEntry) return void 0;
|
|
345
|
+
const [propertyName, codec] = propertyEntry;
|
|
346
|
+
if (codec === "navigation-url" && rawValue !== void 0) {
|
|
347
|
+
const value = sanitizeRemoteDomUrl(rawValue, {
|
|
348
|
+
allowHashOnly: true,
|
|
349
|
+
allowMailTo: false,
|
|
350
|
+
allowRelative: true,
|
|
351
|
+
allowTel: false,
|
|
352
|
+
allowedHttpOriginGroups: ["navigation"],
|
|
353
|
+
...options.allowedAnchorUrls ? { allowedHttpUrls: options.allowedAnchorUrls } : {},
|
|
354
|
+
originPolicy: options.urlOriginPolicy,
|
|
355
|
+
relativeUrlBase: "navigation"
|
|
356
|
+
});
|
|
357
|
+
return value === void 0 ? void 0 : {
|
|
358
|
+
name: propertyName,
|
|
353
359
|
value
|
|
354
360
|
};
|
|
355
361
|
}
|
|
356
|
-
if (
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
362
|
+
if (codec === "url" && rawValue !== void 0) {
|
|
363
|
+
const value = sanitizeRemoteDomUrl(rawValue, {
|
|
364
|
+
allowHashOnly: false,
|
|
365
|
+
allowMailTo: false,
|
|
366
|
+
allowRelative: true,
|
|
367
|
+
allowTel: false,
|
|
368
|
+
allowedHttpOriginGroups: ["artifact", "media"],
|
|
369
|
+
originPolicy: options.urlOriginPolicy,
|
|
370
|
+
relativeUrlBase: "artifact"
|
|
371
|
+
});
|
|
372
|
+
return value === void 0 ? void 0 : {
|
|
373
|
+
name: propertyName,
|
|
374
|
+
value
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
return isRemoteDomUiPropertyValue(rawValue, codec) ? {
|
|
378
|
+
name: propertyName,
|
|
364
379
|
value: rawValue
|
|
365
380
|
} : void 0;
|
|
366
|
-
if (name === "previewmode" && typeof rawValue === "boolean") return {
|
|
367
|
-
name: "previewMode",
|
|
368
|
-
value: rawValue
|
|
369
|
-
};
|
|
370
381
|
}
|
|
371
382
|
}
|
|
372
383
|
function isRemoteDomPropertyName(tagName, rawName) {
|
|
373
384
|
const normalizedTagName = tagName.trim().toLowerCase();
|
|
374
385
|
const name = rawName.trim().toLowerCase();
|
|
375
386
|
if (normalizedTagName === "input") return name === "checked" || name === "value";
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
return false;
|
|
387
|
+
const definition = getRemoteDomUiComponent(normalizedTagName);
|
|
388
|
+
return Boolean(definition && Object.keys(definition.properties).some((propertyName) => propertyName.toLowerCase() === name));
|
|
379
389
|
}
|
|
380
390
|
function sanitizeRemoteDomAttribute(tagName, rawName, rawValue, options = {}) {
|
|
381
391
|
if (typeof rawName !== "string") return void 0;
|
|
@@ -465,7 +475,8 @@ function sanitizeRemoteDomCssUrlReferences(value, options = {}) {
|
|
|
465
475
|
}
|
|
466
476
|
function isSupportedRemoteDomEventName(eventName, tagName) {
|
|
467
477
|
if (SUPPORTED_REMOTE_DOM_EVENT_SET.has(eventName)) return true;
|
|
468
|
-
|
|
478
|
+
const definition = tagName ? getRemoteDomUiComponent(tagName.trim().toLowerCase()) : void 0;
|
|
479
|
+
return Boolean(definition?.events.some((event) => event.eventName === eventName));
|
|
469
480
|
}
|
|
470
481
|
function serializeRemoteDomEvent(event, targetId, currentTargetId, currentTargetTagName) {
|
|
471
482
|
if (!isSupportedRemoteDomEventName(event.type, currentTargetTagName)) return;
|
|
@@ -484,20 +495,22 @@ function serializeRemoteDomEvent(event, targetId, currentTargetId, currentTarget
|
|
|
484
495
|
...readCustomEventDetail(event)
|
|
485
496
|
};
|
|
486
497
|
}
|
|
487
|
-
function
|
|
488
|
-
if (
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
498
|
+
function isRemoteDomUiPropertyValue(value, codec) {
|
|
499
|
+
if (value === void 0) return true;
|
|
500
|
+
switch (codec) {
|
|
501
|
+
case "boolean": return typeof value === "boolean";
|
|
502
|
+
case "finite-number-array": return Array.isArray(value) && value.every((item) => typeof item === "number" && Number.isFinite(item));
|
|
503
|
+
case "finite-number": return typeof value === "number" && Number.isFinite(value);
|
|
504
|
+
case "navigation-url": return false;
|
|
505
|
+
case "option-array": return Array.isArray(value) && value.every((item) => isObjectRecord$1(item) && typeof item.label === "string" && typeof item.value === "string" && Object.keys(item).every((key) => key === "label" || key === "value"));
|
|
506
|
+
case "string": return typeof value === "string";
|
|
507
|
+
case "string-array": return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
508
|
+
case "string-or-string-array": return typeof value === "string" || Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
509
|
+
case "url": return false;
|
|
496
510
|
}
|
|
497
|
-
return options;
|
|
498
511
|
}
|
|
499
512
|
function readCustomEventDetail(event) {
|
|
500
|
-
if (!(event instanceof CustomEvent)
|
|
513
|
+
if (!(event instanceof CustomEvent)) return {};
|
|
501
514
|
if (!isRemoteDomSerializableValue(event.detail)) return {};
|
|
502
515
|
try {
|
|
503
516
|
const detailJson = JSON.stringify(event.detail);
|
|
@@ -561,6 +574,7 @@ function hasControlCharacter(value) {
|
|
|
561
574
|
}
|
|
562
575
|
function isAllowedAttributeName(tagName, name) {
|
|
563
576
|
if (name.length === 0 || name.startsWith("on") || name === "style" || name === "srcdoc" || name.includes(":") || !/^[a-z][a-z0-9_.-]*$/u.test(name)) return false;
|
|
577
|
+
if (name === "data-fluid-remote-dom-node-id") return false;
|
|
564
578
|
if (name.startsWith("data-")) return true;
|
|
565
579
|
if (GLOBAL_ALLOWED_ATTRIBUTES.has(name)) return true;
|
|
566
580
|
return TAG_ALLOWED_ATTRIBUTES[tagName]?.has(name) === true;
|
|
@@ -995,14 +1009,26 @@ function normalizeUpdatePropertyRecordForHost(options, state, mutation, pendingE
|
|
|
995
1009
|
eventName: property,
|
|
996
1010
|
listener
|
|
997
1011
|
});
|
|
998
|
-
return {
|
|
1012
|
+
return {
|
|
1013
|
+
ok: true,
|
|
1014
|
+
record: [
|
|
1015
|
+
MUTATION_TYPE_UPDATE_PROPERTY,
|
|
1016
|
+
id,
|
|
1017
|
+
property,
|
|
1018
|
+
listener,
|
|
1019
|
+
UPDATE_PROPERTY_TYPE_EVENT_LISTENER
|
|
1020
|
+
]
|
|
1021
|
+
};
|
|
999
1022
|
}
|
|
1000
1023
|
if (value !== void 0 && !isRemoteDomSerializableValue(value)) return {
|
|
1001
1024
|
ok: false,
|
|
1002
1025
|
message: `Remote DOM mutation property "${property}" was not serializable.`
|
|
1003
1026
|
};
|
|
1004
1027
|
if (updateType === UPDATE_PROPERTY_TYPE_PROPERTY) {
|
|
1005
|
-
const sanitizedProperty = sanitizeRemoteDomProperty(node.localName, property, value
|
|
1028
|
+
const sanitizedProperty = sanitizeRemoteDomProperty(node.localName, property, value, {
|
|
1029
|
+
...options.allowedAnchorUrls ? { allowedAnchorUrls: options.allowedAnchorUrls } : {},
|
|
1030
|
+
urlOriginPolicy: options.urlOriginPolicy
|
|
1031
|
+
});
|
|
1006
1032
|
if (sanitizedProperty) return {
|
|
1007
1033
|
ok: true,
|
|
1008
1034
|
record: [
|
|
@@ -1082,7 +1108,7 @@ function normalizeRemoteNodeForHost(options, state, value, seen, pendingEventLis
|
|
|
1082
1108
|
ok: false,
|
|
1083
1109
|
message: `Remote DOM element "${value.id}" used an unsupported tag name.`
|
|
1084
1110
|
};
|
|
1085
|
-
|
|
1111
|
+
const eventListeners = sanitizeRemoteNodeEventListeners(value.id, tagName, value.eventListeners, pendingEventListenerUpdates);
|
|
1086
1112
|
if (!Array.isArray(value.children)) return {
|
|
1087
1113
|
ok: false,
|
|
1088
1114
|
message: `Remote DOM element "${value.id}" did not contain a valid children array.`
|
|
@@ -1101,7 +1127,10 @@ function normalizeRemoteNodeForHost(options, state, value, seen, pendingEventLis
|
|
|
1101
1127
|
const mutableAttributeProperties = { ...properties };
|
|
1102
1128
|
attributeProperties = mutableAttributeProperties;
|
|
1103
1129
|
for (const [rawName, rawValue] of Object.entries(value.properties)) {
|
|
1104
|
-
const property = sanitizeRemoteDomProperty(tagName, rawName, rawValue
|
|
1130
|
+
const property = sanitizeRemoteDomProperty(tagName, rawName, rawValue, {
|
|
1131
|
+
...options.allowedAnchorUrls ? { allowedAnchorUrls: options.allowedAnchorUrls } : {},
|
|
1132
|
+
urlOriginPolicy: options.urlOriginPolicy
|
|
1133
|
+
});
|
|
1105
1134
|
if (property) {
|
|
1106
1135
|
remoteProperties[property.name] = property.value;
|
|
1107
1136
|
delete mutableAttributeProperties[rawName];
|
|
@@ -1128,13 +1157,15 @@ function normalizeRemoteNodeForHost(options, state, value, seen, pendingEventLis
|
|
|
1128
1157
|
element: tagName,
|
|
1129
1158
|
attributes: remoteAttributes,
|
|
1130
1159
|
properties: remoteProperties,
|
|
1160
|
+
eventListeners,
|
|
1131
1161
|
children
|
|
1132
1162
|
},
|
|
1133
1163
|
nodeCount
|
|
1134
1164
|
};
|
|
1135
1165
|
}
|
|
1136
|
-
function
|
|
1137
|
-
|
|
1166
|
+
function sanitizeRemoteNodeEventListeners(id, tagName, eventListeners, pendingEventListenerUpdates) {
|
|
1167
|
+
const sanitized = {};
|
|
1168
|
+
if (!isObjectRecord(eventListeners)) return sanitized;
|
|
1138
1169
|
for (const [eventName, listener] of Object.entries(eventListeners)) {
|
|
1139
1170
|
if (!isSafeRemoteDomPropertyKey(eventName) || !isSupportedRemoteDomEventName(eventName, tagName)) continue;
|
|
1140
1171
|
pendingEventListenerUpdates.push({
|
|
@@ -1142,7 +1173,9 @@ function queueRemoteNodeEventListeners(id, tagName, eventListeners, pendingEvent
|
|
|
1142
1173
|
eventName,
|
|
1143
1174
|
listener: typeof listener === "function" ? listener : null
|
|
1144
1175
|
});
|
|
1176
|
+
if (typeof listener === "function") sanitized[eventName] = listener;
|
|
1145
1177
|
}
|
|
1178
|
+
return sanitized;
|
|
1146
1179
|
}
|
|
1147
1180
|
function validateRemoteNodeId(id, seen) {
|
|
1148
1181
|
if (id === ROOT_ID) return {
|
|
@@ -1680,8 +1713,207 @@ function mirrorClasses({ host, sourceElement, classNames, managedClasses, origin
|
|
|
1680
1713
|
}
|
|
1681
1714
|
//#endregion
|
|
1682
1715
|
//#region src/remote-dom-renderer.ts
|
|
1683
|
-
const DEFAULT_MAX_MUTATION_BATCH_SIZE =
|
|
1716
|
+
const DEFAULT_MAX_MUTATION_BATCH_SIZE = 1e3;
|
|
1684
1717
|
const DEFAULT_MAX_DOM_NODE_COUNT = 1e3;
|
|
1718
|
+
const BOOLEAN_ATTRIBUTE_REACT_PROPERTIES = new Map([
|
|
1719
|
+
["allowfullscreen", "allowFullScreen"],
|
|
1720
|
+
["async", "async"],
|
|
1721
|
+
["autofocus", "autoFocus"],
|
|
1722
|
+
["autoplay", "autoPlay"],
|
|
1723
|
+
["checked", "checked"],
|
|
1724
|
+
["controls", "controls"],
|
|
1725
|
+
["default", "default"],
|
|
1726
|
+
["defer", "defer"],
|
|
1727
|
+
["disabled", "disabled"],
|
|
1728
|
+
["disablepictureinpicture", "disablePictureInPicture"],
|
|
1729
|
+
["disableremoteplayback", "disableRemotePlayback"],
|
|
1730
|
+
["formnovalidate", "formNoValidate"],
|
|
1731
|
+
["hidden", "hidden"],
|
|
1732
|
+
["inert", "inert"],
|
|
1733
|
+
["itemscope", "itemScope"],
|
|
1734
|
+
["loop", "loop"],
|
|
1735
|
+
["multiple", "multiple"],
|
|
1736
|
+
["muted", "muted"],
|
|
1737
|
+
["nomodule", "noModule"],
|
|
1738
|
+
["novalidate", "noValidate"],
|
|
1739
|
+
["open", "open"],
|
|
1740
|
+
["playsinline", "playsInline"],
|
|
1741
|
+
["readonly", "readOnly"],
|
|
1742
|
+
["required", "required"],
|
|
1743
|
+
["reversed", "reversed"],
|
|
1744
|
+
["selected", "selected"]
|
|
1745
|
+
]);
|
|
1746
|
+
function createRemoteComponentRenderers(options, isDisposed) {
|
|
1747
|
+
const renderers = /* @__PURE__ */ new Map();
|
|
1748
|
+
const fluidTags = new Set(REMOTE_DOM_UI_COMPONENTS.map(({ tagName }) => tagName));
|
|
1749
|
+
for (const tagName of REMOTE_DOM_ALLOWED_TAG_NAMES) {
|
|
1750
|
+
if (fluidTags.has(tagName)) continue;
|
|
1751
|
+
renderers.set(tagName, createNativeRemoteComponentRenderer(tagName, options, isDisposed));
|
|
1752
|
+
}
|
|
1753
|
+
for (const definition of REMOTE_DOM_UI_COMPONENTS) {
|
|
1754
|
+
if (definition.contractVersion !== "1") continue;
|
|
1755
|
+
renderers.set(definition.tagName, createNativeRemoteComponentRenderer(definition.tagName, options, isDisposed));
|
|
1756
|
+
}
|
|
1757
|
+
renderers.set("remote-fragment", RemoteFragmentRenderer);
|
|
1758
|
+
for (const [tagName, renderer] of options.componentRenderers ?? []) {
|
|
1759
|
+
if (!fluidTags.has(tagName)) continue;
|
|
1760
|
+
renderers.set(tagName, renderer);
|
|
1761
|
+
}
|
|
1762
|
+
return renderers;
|
|
1763
|
+
}
|
|
1764
|
+
function createNativeRemoteComponentRenderer(tagName, options, isDisposed) {
|
|
1765
|
+
function NativeRemoteComponent({ element, receiver, components }) {
|
|
1766
|
+
const attachedElement = useRemoteReceived(element, receiver);
|
|
1767
|
+
const elementRef = useRef(null);
|
|
1768
|
+
useEffect(() => {
|
|
1769
|
+
const hostElement = elementRef.current;
|
|
1770
|
+
if (!hostElement || !attachedElement) return;
|
|
1771
|
+
const cleanup = [];
|
|
1772
|
+
for (const [eventName, listener] of Object.entries(attachedElement.eventListeners)) {
|
|
1773
|
+
if (typeof listener !== "function") continue;
|
|
1774
|
+
const handleEvent = (event) => {
|
|
1775
|
+
if (hasCloserRemoteEventListener(receiver, event, attachedElement.id, eventName)) return;
|
|
1776
|
+
deliverRemoteDomEvent({
|
|
1777
|
+
event,
|
|
1778
|
+
eventName,
|
|
1779
|
+
currentTargetId: attachedElement.id,
|
|
1780
|
+
currentTargetTagName: tagName,
|
|
1781
|
+
listener,
|
|
1782
|
+
options,
|
|
1783
|
+
isDisposed
|
|
1784
|
+
});
|
|
1785
|
+
};
|
|
1786
|
+
hostElement.addEventListener(eventName, handleEvent);
|
|
1787
|
+
cleanup.push(() => hostElement.removeEventListener(eventName, handleEvent));
|
|
1788
|
+
}
|
|
1789
|
+
return () => {
|
|
1790
|
+
for (const dispose of cleanup) dispose();
|
|
1791
|
+
};
|
|
1792
|
+
}, [
|
|
1793
|
+
attachedElement,
|
|
1794
|
+
attachedElement?.version,
|
|
1795
|
+
receiver
|
|
1796
|
+
]);
|
|
1797
|
+
useLayoutEffect(() => {
|
|
1798
|
+
const hostElement = elementRef.current;
|
|
1799
|
+
if (!(hostElement instanceof HTMLInputElement) || !attachedElement) return;
|
|
1800
|
+
const value = attachedElement.properties.value;
|
|
1801
|
+
if (typeof value === "string" && hostElement.value !== value) hostElement.value = value;
|
|
1802
|
+
const checked = attachedElement.properties.checked;
|
|
1803
|
+
if (typeof checked === "boolean" && hostElement.checked !== checked) hostElement.checked = checked;
|
|
1804
|
+
}, [attachedElement, attachedElement?.version]);
|
|
1805
|
+
if (!attachedElement) return null;
|
|
1806
|
+
const domProps = {};
|
|
1807
|
+
for (const [name, value] of Object.entries({
|
|
1808
|
+
...attachedElement.attributes,
|
|
1809
|
+
...attachedElement.properties
|
|
1810
|
+
})) {
|
|
1811
|
+
if (tagName === "input" && (name === "checked" || name === "value")) continue;
|
|
1812
|
+
const booleanProperty = BOOLEAN_ATTRIBUTE_REACT_PROPERTIES.get(name);
|
|
1813
|
+
domProps[booleanProperty ?? toReactDomPropertyName(name)] = booleanProperty && typeof value === "string" ? true : value;
|
|
1814
|
+
}
|
|
1815
|
+
domProps["data-fluid-remote-dom-node-id"] = attachedElement.id;
|
|
1816
|
+
domProps.ref = elementRef;
|
|
1817
|
+
const renderOptions = {
|
|
1818
|
+
receiver,
|
|
1819
|
+
components
|
|
1820
|
+
};
|
|
1821
|
+
return createElement(tagName, domProps, ...attachedElement.children.map((child) => renderRemoteNode(child, renderOptions)));
|
|
1822
|
+
}
|
|
1823
|
+
NativeRemoteComponent.displayName = `RemoteDomNative(${tagName})`;
|
|
1824
|
+
return NativeRemoteComponent;
|
|
1825
|
+
}
|
|
1826
|
+
function RemoteRootEventBoundary({ receiver, container, options, isDisposed, children }) {
|
|
1827
|
+
const root = useRemoteReceived(receiver.root, receiver);
|
|
1828
|
+
useEffect(() => {
|
|
1829
|
+
if (!root) return;
|
|
1830
|
+
const cleanup = [];
|
|
1831
|
+
for (const [eventName, listener] of Object.entries(root.eventListeners)) {
|
|
1832
|
+
if (typeof listener !== "function") continue;
|
|
1833
|
+
const handleEvent = (event) => {
|
|
1834
|
+
if (hasCloserRemoteEventListener(receiver, event, ROOT_ID, eventName)) return;
|
|
1835
|
+
deliverRemoteDomEvent({
|
|
1836
|
+
event,
|
|
1837
|
+
eventName,
|
|
1838
|
+
currentTargetId: ROOT_ID,
|
|
1839
|
+
listener,
|
|
1840
|
+
options,
|
|
1841
|
+
isDisposed
|
|
1842
|
+
});
|
|
1843
|
+
};
|
|
1844
|
+
container.addEventListener(eventName, handleEvent, { capture: true });
|
|
1845
|
+
cleanup.push(() => container.removeEventListener(eventName, handleEvent, { capture: true }));
|
|
1846
|
+
}
|
|
1847
|
+
return () => {
|
|
1848
|
+
for (const dispose of cleanup) dispose();
|
|
1849
|
+
};
|
|
1850
|
+
}, [
|
|
1851
|
+
container,
|
|
1852
|
+
isDisposed,
|
|
1853
|
+
options,
|
|
1854
|
+
receiver,
|
|
1855
|
+
root,
|
|
1856
|
+
root?.version
|
|
1857
|
+
]);
|
|
1858
|
+
return children;
|
|
1859
|
+
}
|
|
1860
|
+
function deliverRemoteDomEvent({ event, eventName, currentTargetId, currentTargetTagName, listener, options, isDisposed }) {
|
|
1861
|
+
if (eventName === "submit" && event.cancelable) event.preventDefault();
|
|
1862
|
+
const targetId = resolveRenderedRemoteNodeId(event) ?? currentTargetId;
|
|
1863
|
+
const payload = serializeRemoteDomEvent(event, targetId, currentTargetId, currentTargetTagName);
|
|
1864
|
+
if (!payload) return;
|
|
1865
|
+
try {
|
|
1866
|
+
const result = listener({
|
|
1867
|
+
targetId,
|
|
1868
|
+
detail: toSerializableEventPayload(payload)
|
|
1869
|
+
});
|
|
1870
|
+
if (isPromiseLike(result)) result.catch((error) => {
|
|
1871
|
+
if (!isDisposed()) reportError(options, "Remote DOM event delivery failed", error);
|
|
1872
|
+
});
|
|
1873
|
+
} catch (error) {
|
|
1874
|
+
if (!isDisposed()) reportError(options, "Remote DOM event delivery failed", error);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
function hasCloserRemoteEventListener(receiver, event, currentTargetId, eventName) {
|
|
1878
|
+
for (const pathTarget of event.composedPath()) {
|
|
1879
|
+
if (!(pathTarget instanceof Element)) continue;
|
|
1880
|
+
const id = pathTarget.getAttribute("data-fluid-remote-dom-node-id");
|
|
1881
|
+
if (!id) continue;
|
|
1882
|
+
if (id === currentTargetId) return false;
|
|
1883
|
+
const remoteNode = receiver.get({ id });
|
|
1884
|
+
if (remoteNode && "eventListeners" in remoteNode && typeof remoteNode.eventListeners[eventName] === "function") return true;
|
|
1885
|
+
}
|
|
1886
|
+
return false;
|
|
1887
|
+
}
|
|
1888
|
+
function resolveRenderedRemoteNodeId(event) {
|
|
1889
|
+
for (const pathTarget of event.composedPath()) {
|
|
1890
|
+
if (!(pathTarget instanceof Element)) continue;
|
|
1891
|
+
const id = pathTarget.getAttribute("data-fluid-remote-dom-node-id");
|
|
1892
|
+
if (id) return id;
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
function toReactDomPropertyName(name) {
|
|
1896
|
+
switch (name) {
|
|
1897
|
+
case "class": return "className";
|
|
1898
|
+
case "for": return "htmlFor";
|
|
1899
|
+
case "readonly": return "readOnly";
|
|
1900
|
+
case "tabindex": return "tabIndex";
|
|
1901
|
+
default: return name;
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
function getSupportedUiContractVersions(componentRenderers) {
|
|
1905
|
+
return REMOTE_DOM_UI_COMPONENTS.every(({ contractVersion, tagName }) => contractVersion !== "2" || componentRenderers.has(tagName)) ? REMOTE_DOM_SUPPORTED_UI_CONTRACT_VERSIONS : REMOTE_DOM_SUPPORTED_UI_CONTRACT_VERSIONS.filter((version) => version !== "2");
|
|
1906
|
+
}
|
|
1907
|
+
/**
|
|
1908
|
+
* Mounts one Remote DOM widget worker into an isolated shadow root.
|
|
1909
|
+
*
|
|
1910
|
+
* The low-level runtime supports native Remote DOM elements by default. A host
|
|
1911
|
+
* that supports worker-facing Fluid UI components must also provide the full
|
|
1912
|
+
* reviewed {@link MountRemoteDomWidgetOptions.componentRenderers} map, a
|
|
1913
|
+
* containment-aware {@link MountRemoteDomWidgetOptions.wrapRemoteRoot}, and
|
|
1914
|
+
* the matching Fluid component CSS. The runtime advertises Fluid UI contract
|
|
1915
|
+
* v2 only when every v2 renderer is present.
|
|
1916
|
+
*/
|
|
1685
1917
|
function mountRemoteDomWidget(options) {
|
|
1686
1918
|
const initialProps = options.props ?? null;
|
|
1687
1919
|
assertSerializable(initialProps, "Initial widget props");
|
|
@@ -1728,35 +1960,25 @@ function mountRemoteDomWidget(options) {
|
|
|
1728
1960
|
throw error;
|
|
1729
1961
|
}
|
|
1730
1962
|
let state;
|
|
1731
|
-
const
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
root: shadowRoot,
|
|
1735
|
-
call: () => {
|
|
1736
|
-
throw new RemoteDomConnectionCallDeniedError("remote element method");
|
|
1737
|
-
}
|
|
1738
|
-
}).connection,
|
|
1739
|
-
...options.allowedAnchorUrls ? { allowedAnchorUrls: options.allowedAnchorUrls } : {},
|
|
1740
|
-
urlOriginPolicy,
|
|
1741
|
-
maxMutationBatchSize: options.maxMutationBatchSize ?? DEFAULT_MAX_MUTATION_BATCH_SIZE,
|
|
1742
|
-
maxDomNodeCount: options.maxDomNodeCount ?? DEFAULT_MAX_DOM_NODE_COUNT,
|
|
1743
|
-
onEventListenerUpdate: (targetId, eventName, listener) => {
|
|
1744
|
-
pendingListenerUpdates.set(subscriptionKey(targetId, eventName), {
|
|
1745
|
-
targetId,
|
|
1746
|
-
eventName,
|
|
1747
|
-
listener
|
|
1748
|
-
});
|
|
1749
|
-
},
|
|
1750
|
-
onNodeLimitExceeded: () => {
|
|
1751
|
-
reportError(options, "Remote DOM widget exceeded its DOM node limit");
|
|
1752
|
-
},
|
|
1753
|
-
onInvalidRecord: (message) => reportError(options, message),
|
|
1754
|
-
onMutateError: (error) => reportError(options, "Remote DOM mutation could not be applied", error)
|
|
1963
|
+
const receiver = new RemoteReceiver({
|
|
1964
|
+
retain,
|
|
1965
|
+
release
|
|
1755
1966
|
});
|
|
1967
|
+
const renderContainer = document.createElement("div");
|
|
1968
|
+
renderContainer.setAttribute("data-fluid-remote-dom-render-root", "");
|
|
1969
|
+
const portalContainer = document.createElement("div");
|
|
1970
|
+
portalContainer.setAttribute("data-fluid-remote-dom-portal-container", "");
|
|
1971
|
+
shadowRoot.append(renderContainer, portalContainer);
|
|
1756
1972
|
const submitListener = createSubmitPreventionListener();
|
|
1757
1973
|
const navigationListener = (event) => {
|
|
1758
1974
|
handleNavigationIntent(state, event);
|
|
1759
1975
|
};
|
|
1976
|
+
const reactRoot = createRoot(renderContainer, { onUncaughtError(error) {
|
|
1977
|
+
reportError(options, "Remote DOM host rendering failed", error);
|
|
1978
|
+
queueMicrotask(() => {
|
|
1979
|
+
if (!state.disposed) disposeAfterFailure(state, false);
|
|
1980
|
+
});
|
|
1981
|
+
} });
|
|
1760
1982
|
state = {
|
|
1761
1983
|
options,
|
|
1762
1984
|
shadowRoot,
|
|
@@ -1765,14 +1987,40 @@ function mountRemoteDomWidget(options) {
|
|
|
1765
1987
|
styles,
|
|
1766
1988
|
urlOriginPolicy,
|
|
1767
1989
|
navigationOrigin,
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1990
|
+
receiver,
|
|
1991
|
+
reactRoot,
|
|
1992
|
+
renderContainer,
|
|
1993
|
+
portalContainer,
|
|
1771
1994
|
submitListener,
|
|
1772
1995
|
navigationListener,
|
|
1773
1996
|
disposed: false,
|
|
1774
1997
|
cleanupStarted: false
|
|
1775
1998
|
};
|
|
1999
|
+
const componentRenderers = createRemoteComponentRenderers(options, () => state.disposed);
|
|
2000
|
+
const remoteRoot = createElement(RemoteRootEventBoundary, {
|
|
2001
|
+
receiver,
|
|
2002
|
+
container: renderContainer,
|
|
2003
|
+
options,
|
|
2004
|
+
isDisposed: () => state?.disposed ?? false
|
|
2005
|
+
}, createElement(RemoteRootRenderer, {
|
|
2006
|
+
receiver,
|
|
2007
|
+
components: componentRenderers
|
|
2008
|
+
}));
|
|
2009
|
+
flushSync(() => {
|
|
2010
|
+
reactRoot.render(options.wrapRemoteRoot ? options.wrapRemoteRoot(remoteRoot, portalContainer) : remoteRoot);
|
|
2011
|
+
});
|
|
2012
|
+
const connection = createSanitizingRemoteConnection({
|
|
2013
|
+
connection: receiver.connection,
|
|
2014
|
+
...options.allowedAnchorUrls ? { allowedAnchorUrls: options.allowedAnchorUrls } : {},
|
|
2015
|
+
urlOriginPolicy,
|
|
2016
|
+
maxMutationBatchSize: options.maxMutationBatchSize ?? DEFAULT_MAX_MUTATION_BATCH_SIZE,
|
|
2017
|
+
maxDomNodeCount: options.maxDomNodeCount ?? DEFAULT_MAX_DOM_NODE_COUNT,
|
|
2018
|
+
onNodeLimitExceeded: () => {
|
|
2019
|
+
reportError(options, "Remote DOM widget exceeded its DOM node limit");
|
|
2020
|
+
},
|
|
2021
|
+
onInvalidRecord: (message) => reportError(options, message),
|
|
2022
|
+
onMutateError: (error) => reportError(options, "Remote DOM mutation could not be applied", error)
|
|
2023
|
+
});
|
|
1776
2024
|
shadowRoot.addEventListener("submit", submitListener, { capture: true });
|
|
1777
2025
|
shadowRoot.addEventListener("click", navigationListener, { capture: true });
|
|
1778
2026
|
worker.onerror = (event) => {
|
|
@@ -1784,10 +2032,8 @@ function mountRemoteDomWidget(options) {
|
|
|
1784
2032
|
const hostApi = {
|
|
1785
2033
|
mutate(records) {
|
|
1786
2034
|
if (state.disposed) return;
|
|
1787
|
-
connection.mutate(records);
|
|
1788
|
-
rebuildNodeIndex(state);
|
|
2035
|
+
flushSync(() => connection.mutate(records));
|
|
1789
2036
|
enforceAllowedAnchorSafety(state);
|
|
1790
|
-
flushPendingListenerUpdates(state);
|
|
1791
2037
|
},
|
|
1792
2038
|
callCapability(capability, method, payload) {
|
|
1793
2039
|
return callCapability(state, capability, method, payload);
|
|
@@ -1799,6 +2045,7 @@ function mountRemoteDomWidget(options) {
|
|
|
1799
2045
|
const ready = Promise.all([styles.ready, thread.imports.mount({
|
|
1800
2046
|
widgetType: options.artifact.widgetType,
|
|
1801
2047
|
props: initialProps,
|
|
2048
|
+
supportedUiContractVersions: getSupportedUiContractVersions(componentRenderers),
|
|
1802
2049
|
...options.artifact.networkAccessGranted === true ? {
|
|
1803
2050
|
networkAccessGranted: true,
|
|
1804
2051
|
hostOrigin: options.hostOrigin
|
|
@@ -1848,82 +2095,6 @@ function assertSerializableCapabilityResult(result) {
|
|
|
1848
2095
|
if (!isRemoteDomSerializableValue(result)) throw new Error("Remote DOM capability result was not serializable");
|
|
1849
2096
|
return result;
|
|
1850
2097
|
}
|
|
1851
|
-
function rebuildNodeIndex(state) {
|
|
1852
|
-
state.nodesById.clear();
|
|
1853
|
-
state.nodesById.set(ROOT_ID, state.shadowRoot);
|
|
1854
|
-
const visit = (node) => {
|
|
1855
|
-
state.nodesById.set(remoteId(node), node);
|
|
1856
|
-
for (const child of node.childNodes) visit(child);
|
|
1857
|
-
};
|
|
1858
|
-
for (const child of state.shadowRoot.childNodes) if (!isWidgetStyleNode(child)) visit(child);
|
|
1859
|
-
for (const subscription of Array.from(state.subscriptions.values())) if (!state.nodesById.has(subscription.targetId)) unsubscribe(state, subscription.targetId, subscription.eventName);
|
|
1860
|
-
}
|
|
1861
|
-
function flushPendingListenerUpdates(state) {
|
|
1862
|
-
for (const update of state.pendingListenerUpdates.values()) if (update.listener === null) unsubscribe(state, update.targetId, update.eventName);
|
|
1863
|
-
else subscribe(state, update.targetId, update.eventName, update.listener);
|
|
1864
|
-
state.pendingListenerUpdates.clear();
|
|
1865
|
-
}
|
|
1866
|
-
function subscribe(state, targetId, eventName, proxy) {
|
|
1867
|
-
const target = state.nodesById.get(targetId);
|
|
1868
|
-
const tagName = target instanceof Element ? target.localName : void 0;
|
|
1869
|
-
if (!target || !isSupportedRemoteDomEventName(eventName, tagName)) return;
|
|
1870
|
-
unsubscribe(state, targetId, eventName);
|
|
1871
|
-
const capture = targetId === ROOT_ID;
|
|
1872
|
-
const listener = (event) => {
|
|
1873
|
-
if (!capture && hasCloserEventSubscription(state, event, target, eventName)) return;
|
|
1874
|
-
if (eventName === "submit" && event.cancelable) event.preventDefault();
|
|
1875
|
-
const actualTargetId = resolveEventTargetId(state, event);
|
|
1876
|
-
if (!actualTargetId) return;
|
|
1877
|
-
const eventPayload = serializeRemoteDomEvent(event, actualTargetId, targetId, tagName);
|
|
1878
|
-
if (!eventPayload) return;
|
|
1879
|
-
try {
|
|
1880
|
-
const result = proxy({
|
|
1881
|
-
targetId: actualTargetId,
|
|
1882
|
-
detail: toSerializableEventPayload(eventPayload)
|
|
1883
|
-
});
|
|
1884
|
-
if (isPromiseLike(result)) result.catch((error) => {
|
|
1885
|
-
reportEventDeliveryFailure(state, error);
|
|
1886
|
-
});
|
|
1887
|
-
} catch (error) {
|
|
1888
|
-
reportEventDeliveryFailure(state, error);
|
|
1889
|
-
}
|
|
1890
|
-
};
|
|
1891
|
-
target.addEventListener(eventName, listener, { capture });
|
|
1892
|
-
retain(proxy);
|
|
1893
|
-
state.subscriptions.set(subscriptionKey(targetId, eventName), {
|
|
1894
|
-
targetId,
|
|
1895
|
-
eventName,
|
|
1896
|
-
target,
|
|
1897
|
-
listener,
|
|
1898
|
-
capture,
|
|
1899
|
-
proxy
|
|
1900
|
-
});
|
|
1901
|
-
}
|
|
1902
|
-
function hasCloserEventSubscription(state, event, subscriptionTarget, eventName) {
|
|
1903
|
-
for (const pathTarget of event.composedPath()) {
|
|
1904
|
-
if (pathTarget === subscriptionTarget) return false;
|
|
1905
|
-
if (!(pathTarget instanceof Node)) continue;
|
|
1906
|
-
const pathTargetId = remoteId(pathTarget);
|
|
1907
|
-
if (state.nodesById.get(pathTargetId) === pathTarget && state.subscriptions.has(subscriptionKey(pathTargetId, eventName))) return true;
|
|
1908
|
-
}
|
|
1909
|
-
return false;
|
|
1910
|
-
}
|
|
1911
|
-
function unsubscribe(state, targetId, eventName) {
|
|
1912
|
-
const key = subscriptionKey(targetId, eventName);
|
|
1913
|
-
const subscription = state.subscriptions.get(key);
|
|
1914
|
-
if (!subscription) return;
|
|
1915
|
-
subscription.target.removeEventListener(eventName, subscription.listener, { capture: subscription.capture });
|
|
1916
|
-
release(subscription.proxy);
|
|
1917
|
-
state.subscriptions.delete(key);
|
|
1918
|
-
}
|
|
1919
|
-
function resolveEventTargetId(state, event) {
|
|
1920
|
-
for (const target of event.composedPath()) {
|
|
1921
|
-
if (target === state.shadowRoot) return ROOT_ID;
|
|
1922
|
-
if (!(target instanceof Node)) continue;
|
|
1923
|
-
const id = remoteId(target);
|
|
1924
|
-
if (state.nodesById.get(id) === target) return id;
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
2098
|
function handleNavigationIntent(state, event) {
|
|
1928
2099
|
if (!(event instanceof MouseEvent)) return;
|
|
1929
2100
|
const anchor = (event.target instanceof Element ? event.target : event.target instanceof Text ? event.target.parentElement : null)?.closest("a[href]");
|
|
@@ -1978,10 +2149,7 @@ function disposeMount(state, notifyWorker = true) {
|
|
|
1978
2149
|
state.shadowRoot.removeEventListener("click", state.navigationListener, { capture: true });
|
|
1979
2150
|
}
|
|
1980
2151
|
];
|
|
1981
|
-
|
|
1982
|
-
unsubscribe(state, subscription.targetId, subscription.eventName);
|
|
1983
|
-
});
|
|
1984
|
-
cleanupSteps.push(() => state.pendingListenerUpdates.clear(), () => state.styles.dispose(), () => clearRenderedRemoteDomNodes(state.shadowRoot), () => state.nodesById.clear());
|
|
2152
|
+
cleanupSteps.push(() => flushSync(() => state.reactRoot.unmount()), () => clearRemoteReceiver(state.receiver), () => state.styles.dispose(), () => clearRenderedRemoteDomNodes(state.shadowRoot));
|
|
1985
2153
|
const cleanupErrors = collectCleanupErrors(cleanupSteps);
|
|
1986
2154
|
if (notifyWorker) Promise.resolve(state.thread.imports.dispose()).catch(() => void 0);
|
|
1987
2155
|
queueMicrotask(() => {
|
|
@@ -1996,10 +2164,6 @@ function disposeAfterFailure(state, notifyWorker = true) {
|
|
|
1996
2164
|
reportError(state.options, "Remote DOM widget cleanup failed after an earlier error", cleanupError);
|
|
1997
2165
|
}
|
|
1998
2166
|
}
|
|
1999
|
-
function reportEventDeliveryFailure(state, error) {
|
|
2000
|
-
if (state.disposed) return;
|
|
2001
|
-
reportError(state.options, "Remote DOM event delivery failed", error);
|
|
2002
|
-
}
|
|
2003
2167
|
function createShadowRoot(host) {
|
|
2004
2168
|
if (host.shadowRoot) {
|
|
2005
2169
|
host.shadowRoot.replaceChildren();
|
|
@@ -2065,8 +2229,8 @@ function resolveInternalNavigationUrl(href, navigationOrigin) {
|
|
|
2065
2229
|
return;
|
|
2066
2230
|
}
|
|
2067
2231
|
}
|
|
2068
|
-
function findNodeId(
|
|
2069
|
-
|
|
2232
|
+
function findNodeId(_state, node) {
|
|
2233
|
+
return (node instanceof Element ? node : node.parentElement)?.closest("[data-fluid-remote-dom-node-id]")?.getAttribute("data-fluid-remote-dom-node-id") ?? void 0;
|
|
2070
2234
|
}
|
|
2071
2235
|
function isWidgetStyleNode(node) {
|
|
2072
2236
|
return node instanceof HTMLStyleElement && node.hasAttribute("data-fluid-widget-style");
|
|
@@ -2074,6 +2238,13 @@ function isWidgetStyleNode(node) {
|
|
|
2074
2238
|
function clearRenderedRemoteDomNodes(shadowRoot) {
|
|
2075
2239
|
for (const child of Array.from(shadowRoot.childNodes)) if (!isWidgetStyleNode(child)) child.remove();
|
|
2076
2240
|
}
|
|
2241
|
+
function clearRemoteReceiver(receiver) {
|
|
2242
|
+
while (receiver.root.children.length > 0) receiver.connection.mutate([[
|
|
2243
|
+
MUTATION_TYPE_REMOVE_CHILD,
|
|
2244
|
+
ROOT_ID,
|
|
2245
|
+
0
|
|
2246
|
+
]]);
|
|
2247
|
+
}
|
|
2077
2248
|
function toSerializableEventPayload(payload) {
|
|
2078
2249
|
const result = {
|
|
2079
2250
|
bubbles: payload.bubbles,
|
|
@@ -2178,13 +2349,10 @@ function reportCleanupErrors(options, message, errors) {
|
|
|
2178
2349
|
if (errors.length === 0) return;
|
|
2179
2350
|
reportError(options, message, errors.length === 1 ? errors[0] : new AggregateError(errors, "Multiple cleanup operations failed"));
|
|
2180
2351
|
}
|
|
2181
|
-
function subscriptionKey(targetId, eventName) {
|
|
2182
|
-
return `${targetId}:${eventName}`;
|
|
2183
|
-
}
|
|
2184
2352
|
function isPromiseLike(value) {
|
|
2185
2353
|
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
2186
2354
|
}
|
|
2187
2355
|
//#endregion
|
|
2188
2356
|
export { mountRemoteDomWidget as t };
|
|
2189
2357
|
|
|
2190
|
-
//# sourceMappingURL=remote-dom-renderer-
|
|
2358
|
+
//# sourceMappingURL=remote-dom-renderer-Dn3pfVsl.mjs.map
|