@braidlabs/core 0.1.1

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.
Files changed (80) hide show
  1. package/README.md +123 -0
  2. package/package.json +37 -0
  3. package/src/adapters/adapter.d.ts +75 -0
  4. package/src/adapters/adapter.js +35 -0
  5. package/src/adapters/adapter.js.map +1 -0
  6. package/src/adapters/compat-adapter.d.ts +27 -0
  7. package/src/adapters/compat-adapter.js +113 -0
  8. package/src/adapters/compat-adapter.js.map +1 -0
  9. package/src/adapters/custom-element-adapter.d.ts +21 -0
  10. package/src/adapters/custom-element-adapter.js +91 -0
  11. package/src/adapters/custom-element-adapter.js.map +1 -0
  12. package/src/compat/born-inert-scripts.d.ts +30 -0
  13. package/src/compat/born-inert-scripts.js +85 -0
  14. package/src/compat/born-inert-scripts.js.map +1 -0
  15. package/src/compat/document-facade.d.ts +44 -0
  16. package/src/compat/document-facade.js +104 -0
  17. package/src/compat/document-facade.js.map +1 -0
  18. package/src/compat/document-member-classification.d.ts +30 -0
  19. package/src/compat/document-member-classification.js +64 -0
  20. package/src/compat/document-member-classification.js.map +1 -0
  21. package/src/compat/document-overrides.d.ts +44 -0
  22. package/src/compat/document-overrides.js +225 -0
  23. package/src/compat/document-overrides.js.map +1 -0
  24. package/src/compat/document-surface.generated.d.ts +21 -0
  25. package/src/compat/document-surface.generated.js +292 -0
  26. package/src/compat/document-surface.generated.js.map +1 -0
  27. package/src/compat/fragment-boundary.d.ts +14 -0
  28. package/src/compat/fragment-boundary.js +335 -0
  29. package/src/compat/fragment-boundary.js.map +1 -0
  30. package/src/compat/metadata.d.ts +15 -0
  31. package/src/compat/metadata.js +21 -0
  32. package/src/compat/metadata.js.map +1 -0
  33. package/src/compat/navigation-bus.d.ts +29 -0
  34. package/src/compat/navigation-bus.js +92 -0
  35. package/src/compat/navigation-bus.js.map +1 -0
  36. package/src/compat/realm-patches.d.ts +21 -0
  37. package/src/compat/realm-patches.js +573 -0
  38. package/src/compat/realm-patches.js.map +1 -0
  39. package/src/compat/register-worker.d.ts +45 -0
  40. package/src/compat/register-worker.js +57 -0
  41. package/src/compat/register-worker.js.map +1 -0
  42. package/src/compat/script-execution.d.ts +65 -0
  43. package/src/compat/script-execution.js +361 -0
  44. package/src/compat/script-execution.js.map +1 -0
  45. package/src/compat/service-worker.d.ts +22 -0
  46. package/src/compat/service-worker.js +104 -0
  47. package/src/compat/service-worker.js.map +1 -0
  48. package/src/config.d.ts +37 -0
  49. package/src/config.js +26 -0
  50. package/src/config.js.map +1 -0
  51. package/src/context/context-bus.d.ts +67 -0
  52. package/src/context/context-bus.js +117 -0
  53. package/src/context/context-bus.js.map +1 -0
  54. package/src/elements/fragment-slot.d.ts +39 -0
  55. package/src/elements/fragment-slot.js +281 -0
  56. package/src/elements/fragment-slot.js.map +1 -0
  57. package/src/env/create-env.d.ts +31 -0
  58. package/src/env/create-env.js +91 -0
  59. package/src/env/create-env.js.map +1 -0
  60. package/src/env/fragment-env.d.ts +56 -0
  61. package/src/env/fragment-env.js +14 -0
  62. package/src/env/fragment-env.js.map +1 -0
  63. package/src/errors.d.ts +21 -0
  64. package/src/errors.js +21 -0
  65. package/src/errors.js.map +1 -0
  66. package/src/index.d.ts +36 -0
  67. package/src/index.js +40 -0
  68. package/src/index.js.map +1 -0
  69. package/src/protocol.d.ts +38 -0
  70. package/src/protocol.js +45 -0
  71. package/src/protocol.js.map +1 -0
  72. package/src/realm/realm-manager.d.ts +60 -0
  73. package/src/realm/realm-manager.js +296 -0
  74. package/src/realm/realm-manager.js.map +1 -0
  75. package/src/utils/assert.d.ts +7 -0
  76. package/src/utils/assert.js +10 -0
  77. package/src/utils/assert.js.map +1 -0
  78. package/src/utils/selector-helpers.d.ts +10 -0
  79. package/src/utils/selector-helpers.js +13 -0
  80. package/src/utils/selector-helpers.js.map +1 -0
@@ -0,0 +1,57 @@
1
+ import { BraidError } from '../errors.js';
2
+ /**
3
+ * Registers the worker and introduces the page to it.
4
+ *
5
+ * ```ts
6
+ * import { registerBraidServiceWorker } from '@braidlabs/core';
7
+ * await registerBraidServiceWorker({ buildId: BUILD_ID });
8
+ * ```
9
+ *
10
+ * Resolves to the registration, or `null` where service workers are unavailable — a non-secure
11
+ * origin, a browser without them, or SSR. That is a `null` rather than a throw because a worker is
12
+ * an enhancement: a page that works without one should not fail to start because it could not have
13
+ * one.
14
+ */
15
+ export async function registerBraidServiceWorker(options = {}) {
16
+ const container = globalThis.navigator
17
+ ?.serviceWorker;
18
+ if (!container)
19
+ return null;
20
+ const url = options.url ?? '/__braid/sw.js';
21
+ const scope = options.scope ?? '/';
22
+ let registration;
23
+ try {
24
+ registration = await container.register(url, { scope, type: 'module' });
25
+ }
26
+ catch (cause) {
27
+ // The overwhelmingly common cause is the scope: a script at /__braid/sw.js may only claim '/'
28
+ // if its response carried Service-Worker-Allowed, and a shell serving the script itself
29
+ // (rather than through the gateway) usually has not configured that.
30
+ throw new BraidError(`registering the Braid service worker at ${url} failed`, {
31
+ fragmentId: 'braid-sw',
32
+ stage: 'realm-boot',
33
+ cause,
34
+ fixHint: `the script response must send "Service-Worker-Allowed: ${scope}" — set serviceWorker: true ` +
35
+ 'on the gateway, which is the component that can send it',
36
+ });
37
+ }
38
+ if (options.onVersion) {
39
+ const listener = (event) => {
40
+ const data = event.data;
41
+ if (data?.type === 'braid-sw:version')
42
+ options.onVersion?.(data);
43
+ };
44
+ container.addEventListener('message', listener);
45
+ }
46
+ // The controller is what receives messages; a first-load registration has none until it activates
47
+ // and takes over, so there is nobody to introduce the page to yet. The next navigation does it.
48
+ container.controller?.postMessage({
49
+ type: 'braid-sw:hello',
50
+ page: {
51
+ ...(options.buildId === undefined ? {} : { buildId: options.buildId }),
52
+ ...(options.snapshotId === undefined ? {} : { snapshotId: options.snapshotId }),
53
+ },
54
+ });
55
+ return registration;
56
+ }
57
+ //# sourceMappingURL=register-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-worker.js","sourceRoot":"","sources":["../../../../../libs/core/src/compat/register-worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAmC1C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,UAA6C,EAAE;IAE/C,MAAM,SAAS,GAAI,UAAyE,CAAC,SAAS;QACpG,EAAE,aAAa,CAAC;IAClB,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAE5B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,gBAAgB,CAAC;IAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,GAAG,CAAC;IAEnC,IAAI,YAAuC,CAAC;IAC5C,IAAI,CAAC;QACH,YAAY,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC1E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,8FAA8F;QAC9F,wFAAwF;QACxF,qEAAqE;QACrE,MAAM,IAAI,UAAU,CAAC,2CAA2C,GAAG,SAAS,EAAE;YAC5E,UAAU,EAAE,UAAU;YACtB,KAAK,EAAE,YAAY;YACnB,KAAK;YACL,OAAO,EACL,0DAA0D,KAAK,8BAA8B;gBAC7F,yDAAyD;SAC5D,CAAC,CAAC;IACL,CAAC;IAED,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,MAAM,QAAQ,GAAG,CAAC,KAAmB,EAAE,EAAE;YACvC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAqC,CAAC;YACzD,IAAI,IAAI,EAAE,IAAI,KAAK,kBAAkB;gBAAE,OAAO,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC;QACnE,CAAC,CAAC;QACF,SAAS,CAAC,gBAAgB,CAAC,SAAS,EAAE,QAAyB,CAAC,CAAC;IACnE,CAAC;IAED,kGAAkG;IAClG,gGAAgG;IAChG,SAAS,CAAC,UAAU,EAAE,WAAW,CAAC;QAChC,IAAI,EAAE,gBAAgB;QACtB,IAAI,EAAE;YACJ,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;YACtE,GAAG,CAAC,OAAO,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;SAChF;KACF,CAAC,CAAC;IAEH,OAAO,YAAY,CAAC;AACtB,CAAC"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Script execution machinery for compat fragments: any script that enters a fragment's DOM is
3
+ * neutralized in the main document (an inert placeholder keeps the DOM shape the app expects)
4
+ * and executed in the fragment's realm instead.
5
+ */
6
+ export declare function compatDomInsertion<T extends Node>(nodeToInsert: T, doInsertTheNode: Function, realmDocument?: Document): T;
7
+ /**
8
+ * Handles a multi-node insertion (append, prepend, replaceChildren, replaceWith) into a fragment
9
+ * DOM.
10
+ *
11
+ * All scripts and preload links across all node arguments are neutralized first, then the actual
12
+ * insertion runs exactly once, and only then are the scripts restored and executed in the
13
+ * fragment's realm, in argument order. (Neutralizing/restoring per-node around a shared deferred
14
+ * insertion would restore earlier scripts to their live form before the insertion actually
15
+ * happens, causing them to execute in the main JS context.)
16
+ */
17
+ export declare function compatMultiNodeDomInsertion(nodes: (Node | string)[], doInsertTheNodes: () => void, realmDocument?: Document): void;
18
+ /**
19
+ * Activates the initial content of a fragment: patches braid-html/head/body stand-ins and
20
+ * executes every neutralized script/preload-link in document order in the fragment's realm.
21
+ *
22
+ * Used for both server-pierced content and content fetched client-side by the slot (which the
23
+ * compat adapter neutralizes before insertion).
24
+ */
25
+ export declare function executeScriptsInFragmentContent(contentRoot: ParentNode, realmDocument: Document): void;
26
+ /**
27
+ * Weak map of scripts running in the realm to their inert clones in the fragment DOM.
28
+ */
29
+ export declare const execToInertScriptMap: WeakMap<HTMLScriptElement, HTMLScriptElement>;
30
+ export declare const alreadyExecutedScriptsAndLinks: WeakSet<HTMLScriptElement | HTMLLinkElement>;
31
+ /**
32
+ * Executes a script in the fragment's realm.
33
+ * @param inertScript inert script already appended to a document within the fragment DOM
34
+ * @param realmDocument realm document in which the script should execute
35
+ * @returns true if the script executed, false if it was ignored
36
+ */
37
+ export declare function executeInertScript(inertScript: HTMLScriptElement, realmDocument: Document): boolean;
38
+ export declare function executeInertPreloadLink(inertLink: HTMLLinkElement, realmDocument: Document): boolean;
39
+ /**
40
+ * Attaches exec scripts to the realm and keeps a record of already evaluated scripts.
41
+ * @param inertScript inert script already appended to a document within the fragment DOM
42
+ * @param execScript exec script to be attached to the realm document
43
+ * @param realmDocument realm document in which the script should execute
44
+ */
45
+ export declare function attachScriptToRealm({ inertScript, execScript, realmDocument, }: {
46
+ inertScript: HTMLScriptElement;
47
+ execScript?: HTMLScriptElement;
48
+ realmDocument: Document;
49
+ }): void;
50
+ /**
51
+ * Set the type attribute of a script element to "inert".
52
+ *
53
+ * This prevents the script from executing in the main JS context when the script is attached to
54
+ * the main document.
55
+ */
56
+ export declare function setInertScriptType(script: HTMLScriptElement): void;
57
+ /**
58
+ * Restore the original script type and erase any signs of us making the script inert.
59
+ */
60
+ export declare function restoreScriptType(script: HTMLScriptElement): void;
61
+ /**
62
+ * Neutralizes the original link, so that it doesn't result in a duplicate network call.
63
+ */
64
+ export declare function setInertLinkRel(link: HTMLLinkElement): void;
65
+ export declare function patchSpecialHtmlElement(node: Element, realmDocument: Document): void;
@@ -0,0 +1,361 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-function-type --
2
+ * ported from the matrix-tested web-fragments fork: the deferred-insertion callbacks are
3
+ * arbitrary pre-bound native method wrappers. */
4
+ import { assert } from '../utils/assert.js';
5
+ import { getInternalReference } from './metadata.js';
6
+ /**
7
+ * Script execution machinery for compat fragments: any script that enters a fragment's DOM is
8
+ * neutralized in the main document (an inert placeholder keeps the DOM shape the app expects)
9
+ * and executed in the fragment's realm instead.
10
+ */
11
+ export function compatDomInsertion(nodeToInsert, doInsertTheNode, realmDocument) {
12
+ // if we are operating outside of a fragment DOM or the appended child is neither an element nor
13
+ // a document fragment (which can carry scripts, e.g. cloned template contents), then just append
14
+ if (!realmDocument || !(nodeToInsert instanceof HTMLElement || nodeToInsert instanceof DocumentFragment)) {
15
+ return doInsertTheNode();
16
+ }
17
+ // if the child's prototype is directly HTMLElement, then check if this is a HTML/BODY/HEAD element and rewrite it
18
+ if (nodeToInsert instanceof HTMLElement && Object.getPrototypeOf(nodeToInsert) === HTMLElement.prototype) {
19
+ patchSpecialHtmlElement(nodeToInsert, realmDocument);
20
+ }
21
+ // if the child is a script, then append and execute it in the fragment's realm
22
+ if (nodeToInsert instanceof HTMLScriptElement) {
23
+ // if the child is an unattached inline script that doesn't yet have any text content, then we need to treat it in a special way
24
+ if (!nodeToInsert.src && !nodeToInsert.firstChild && !nodeToInsert.parentNode) {
25
+ prepareUnattachedInlineScript(nodeToInsert, realmDocument);
26
+ return doInsertTheNode();
27
+ }
28
+ setInertScriptType(nodeToInsert);
29
+ const returnVal = doInsertTheNode();
30
+ restoreScriptType(nodeToInsert);
31
+ executeInertScript(nodeToInsert, realmDocument);
32
+ return returnVal;
33
+ }
34
+ // if it's a preload or prefetch link for a script then append it to the realm and leave an inert node in the fragment DOM
35
+ if (nodeToInsert instanceof HTMLLinkElement &&
36
+ (nodeToInsert.rel === 'preload' || nodeToInsert.rel === 'prefetch' || nodeToInsert.rel === 'modulepreload')) {
37
+ setInertLinkRel(nodeToInsert);
38
+ const returnVal = doInsertTheNode();
39
+ executeInertPreloadLink(nodeToInsert, realmDocument);
40
+ return returnVal;
41
+ }
42
+ const nestedScriptsAndLinks = nodeToInsert.querySelectorAll?.('script,link[rel=preload],link[rel=prefetch],link[rel=modulepreload]');
43
+ // if the child doesn't contain nested scripts or links, then just append it
44
+ if (nestedScriptsAndLinks.length === 0) {
45
+ return doInsertTheNode();
46
+ }
47
+ // otherwise, append the child and execute all nested scripts in the fragment's realm
48
+ nestedScriptsAndLinks.forEach((element) => makeElementInert(element));
49
+ const returnVal = doInsertTheNode();
50
+ nestedScriptsAndLinks.forEach((element) => restoreElement(element));
51
+ nestedScriptsAndLinks.forEach((element) => executeElement(element, realmDocument));
52
+ return returnVal;
53
+ }
54
+ /**
55
+ * Handles a multi-node insertion (append, prepend, replaceChildren, replaceWith) into a fragment
56
+ * DOM.
57
+ *
58
+ * All scripts and preload links across all node arguments are neutralized first, then the actual
59
+ * insertion runs exactly once, and only then are the scripts restored and executed in the
60
+ * fragment's realm, in argument order. (Neutralizing/restoring per-node around a shared deferred
61
+ * insertion would restore earlier scripts to their live form before the insertion actually
62
+ * happens, causing them to execute in the main JS context.)
63
+ */
64
+ export function compatMultiNodeDomInsertion(nodes, doInsertTheNodes, realmDocument) {
65
+ if (!realmDocument) {
66
+ return doInsertTheNodes();
67
+ }
68
+ const scriptsAndLinks = [];
69
+ for (const node of nodes) {
70
+ if (typeof node === 'string') {
71
+ console.warn('Braid compat: string arguments to append/prepend/replaceChildren/replaceWith are not supported and could result in incorrect script execution. Inserted string: ', node);
72
+ continue;
73
+ }
74
+ if (node instanceof HTMLElement && Object.getPrototypeOf(node) === HTMLElement.prototype) {
75
+ patchSpecialHtmlElement(node, realmDocument);
76
+ }
77
+ if (node instanceof HTMLScriptElement) {
78
+ if (!node.src && !node.firstChild && !node.parentNode) {
79
+ prepareUnattachedInlineScript(node, realmDocument);
80
+ }
81
+ else {
82
+ scriptsAndLinks.push(node);
83
+ }
84
+ }
85
+ else if (node instanceof HTMLLinkElement &&
86
+ (node.rel === 'preload' || node.rel === 'prefetch' || node.rel === 'modulepreload')) {
87
+ scriptsAndLinks.push(node);
88
+ }
89
+ else if (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
90
+ const nested = node.querySelectorAll?.('script,link[rel=preload],link[rel=prefetch],link[rel=modulepreload]');
91
+ nested?.forEach((element) => scriptsAndLinks.push(element));
92
+ }
93
+ }
94
+ scriptsAndLinks.forEach((element) => makeElementInert(element));
95
+ doInsertTheNodes();
96
+ scriptsAndLinks.forEach((element) => restoreElement(element));
97
+ scriptsAndLinks.forEach((element) => executeElement(element, realmDocument));
98
+ }
99
+ function makeElementInert(element) {
100
+ if (element instanceof HTMLScriptElement) {
101
+ setInertScriptType(element);
102
+ }
103
+ else {
104
+ setInertLinkRel(element);
105
+ }
106
+ }
107
+ function restoreElement(element) {
108
+ if (element instanceof HTMLScriptElement) {
109
+ restoreScriptType(element);
110
+ }
111
+ else {
112
+ // don't restore links as we currently don't have another way of keeping them inert
113
+ }
114
+ }
115
+ function executeElement(element, realmDocument) {
116
+ if (element instanceof HTMLScriptElement) {
117
+ executeInertScript(element, realmDocument);
118
+ }
119
+ else {
120
+ executeInertPreloadLink(element, realmDocument);
121
+ }
122
+ }
123
+ /**
124
+ * Activates the initial content of a fragment: patches braid-html/head/body stand-ins and
125
+ * executes every neutralized script/preload-link in document order in the fragment's realm.
126
+ *
127
+ * Used for both server-pierced content and content fetched client-side by the slot (which the
128
+ * compat adapter neutralizes before insertion).
129
+ */
130
+ export function executeScriptsInFragmentContent(contentRoot, realmDocument) {
131
+ // In addition to executing scripts, we also need to patch braid- tags if they are present so
132
+ // that the scripts see them without the prefix.
133
+ [...contentRoot.querySelectorAll('braid-html, braid-body, braid-head')].forEach((element) => patchSpecialHtmlElement(element, realmDocument));
134
+ const scriptsAndLinks = [
135
+ ...contentRoot.querySelectorAll('link[rel=inert-preload],link[rel=inert-prefetch],link[rel=inert-modulepreload],script'),
136
+ ];
137
+ scriptsAndLinks.forEach((inertElement) => {
138
+ if (inertElement instanceof HTMLLinkElement) {
139
+ executeInertPreloadLink(inertElement, realmDocument);
140
+ }
141
+ else {
142
+ restoreScriptType(inertElement);
143
+ executeInertScript(inertElement, realmDocument);
144
+ }
145
+ });
146
+ }
147
+ /**
148
+ * Weak map of scripts running in the realm to their inert clones in the fragment DOM.
149
+ */
150
+ export const execToInertScriptMap = new WeakMap();
151
+ export const alreadyExecutedScriptsAndLinks = new WeakSet();
152
+ /**
153
+ * Executes a script in the fragment's realm.
154
+ * @param inertScript inert script already appended to a document within the fragment DOM
155
+ * @param realmDocument realm document in which the script should execute
156
+ * @returns true if the script executed, false if it was ignored
157
+ */
158
+ export function executeInertScript(inertScript, realmDocument) {
159
+ // If the inert script has already been evaluated but later re-added to the DOM via any DOM
160
+ // insertion method (i.e. insertBefore() and appendChild()), do not evaluate the script again
161
+ if (alreadyExecutedScriptsAndLinks.has(inertScript)) {
162
+ return false;
163
+ }
164
+ // If the script does not have a valid type attribute, treat the script node as a data block.
165
+ // Data blocks live in the main document as-is.
166
+ const validScriptTypes = ['module', 'text/javascript', 'importmap', 'speculationrules', '', null];
167
+ if (!validScriptTypes.includes(inertScript.getAttribute('type'))) {
168
+ return false;
169
+ }
170
+ assert(!!(inertScript.src || inertScript.textContent), `Can't execute script without src or textContent!`);
171
+ attachScriptToRealm({ inertScript, realmDocument });
172
+ return true;
173
+ }
174
+ export function executeInertPreloadLink(inertLink, realmDocument) {
175
+ // If the inert link has already been evaluated but later re-added to the DOM via any DOM
176
+ // insertion method, do not evaluate the link again
177
+ if (alreadyExecutedScriptsAndLinks.has(inertLink)) {
178
+ return false;
179
+ }
180
+ // If the link does not have a valid href attribute then skip
181
+ if (!inertLink.href)
182
+ return false;
183
+ const execLink = realmDocument.importNode(inertLink, true);
184
+ restoreLinkRel(execLink);
185
+ // Grab the Event constructor from the realm
186
+ const Event = realmDocument.defaultView.Event;
187
+ // Redispatch the load event onto the inert link in the fragment DOM
188
+ execLink.addEventListener('load', () => {
189
+ inertLink.dispatchEvent(new Event('load', { cancelable: false }));
190
+ });
191
+ // Redispatch the error event onto the inert link in the fragment DOM
192
+ execLink.addEventListener('error', () => {
193
+ inertLink.dispatchEvent(new Event('error', { cancelable: false }));
194
+ });
195
+ getInternalReference(realmDocument, 'body').appendChild(execLink);
196
+ alreadyExecutedScriptsAndLinks.add(inertLink);
197
+ return true;
198
+ }
199
+ /**
200
+ * Attaches exec scripts to the realm and keeps a record of already evaluated scripts.
201
+ * @param inertScript inert script already appended to a document within the fragment DOM
202
+ * @param execScript exec script to be attached to the realm document
203
+ * @param realmDocument realm document in which the script should execute
204
+ */
205
+ export function attachScriptToRealm({ inertScript, execScript, realmDocument, }) {
206
+ if (!execScript) {
207
+ // Build a FRESH script element rather than cloning: a script that descends from a parser
208
+ // document (DOMParser, fragment parsing) carries the spec's "already started" flag, and
209
+ // cloning preserves the flag — a clone would silently never execute.
210
+ //
211
+ // The fresh element must be created realm-side with the realm document's NATIVE
212
+ // createElement: the facade virtualizes `realmDocument.createElement` into the main
213
+ // document, and a script adopted across documents does not execute.
214
+ execScript = Document.prototype.createElement.call(realmDocument, 'script');
215
+ for (const attribute of inertScript.attributes) {
216
+ execScript.setAttribute(attribute.name, attribute.value);
217
+ }
218
+ execScript.textContent = inertScript.textContent;
219
+ }
220
+ // the following line will append the executable script to the realm
221
+ // - inline scripts (script with textContent) will be executed synchronously when attached
222
+ // - external scripts (with src attribute) will execute once the current turn of the event loop unwinds
223
+ execToInertScriptMap.set(execScript, inertScript);
224
+ // redispatch the load event onto the inert script in the fragment DOM
225
+ execScript.addEventListener('load', () => {
226
+ inertScript.dispatchEvent(new Event('load', { cancelable: false }));
227
+ });
228
+ // redispatch the error event onto the inert script in the fragment DOM
229
+ execScript.addEventListener('error', () => {
230
+ inertScript.dispatchEvent(new Event('error', { cancelable: false }));
231
+ });
232
+ getInternalReference(realmDocument, 'body').appendChild(execScript);
233
+ alreadyExecutedScriptsAndLinks.add(inertScript);
234
+ }
235
+ /**
236
+ * Set the type attribute of a script element to "inert".
237
+ *
238
+ * This prevents the script from executing in the main JS context when the script is attached to
239
+ * the main document.
240
+ */
241
+ export function setInertScriptType(script) {
242
+ const scriptType = script.type;
243
+ if (scriptType) {
244
+ script.setAttribute('data-script-type', scriptType);
245
+ }
246
+ script.setAttribute('type', 'inert');
247
+ }
248
+ /**
249
+ * Restore the original script type and erase any signs of us making the script inert.
250
+ */
251
+ export function restoreScriptType(script) {
252
+ const scriptType = script.getAttribute('data-script-type');
253
+ script.removeAttribute('data-script-type');
254
+ script.removeAttribute('type');
255
+ if (scriptType) {
256
+ script.setAttribute('type', scriptType);
257
+ }
258
+ }
259
+ /**
260
+ * Neutralizes the original link, so that it doesn't result in a duplicate network call.
261
+ */
262
+ export function setInertLinkRel(link) {
263
+ const linkRel = link.rel;
264
+ if (linkRel) {
265
+ link.rel = 'inert-' + linkRel;
266
+ }
267
+ }
268
+ /**
269
+ * Reverses the `setInertLinkRel` operation.
270
+ */
271
+ function restoreLinkRel(link) {
272
+ const linkRel = link.rel;
273
+ if (linkRel && linkRel.startsWith('inert-')) {
274
+ // strip the 'inert-' prefix
275
+ link.rel = linkRel.slice(6);
276
+ }
277
+ }
278
+ /**
279
+ * Inline scripts that do not have textContent set are not evaluated until the first time
280
+ * textContent is set.
281
+ *
282
+ * We prepare these scripts for future execution by:
283
+ * - appending a non-neutralized clone of the script to the realm document
284
+ * - neutralizing the original script so that it doesn't execute in the main JS context in the future
285
+ * - patching the script's appendChild method to also append to the clone which will execute the
286
+ * script in the fragment's realm
287
+ *
288
+ * If text content is added later, the script then executes in the fragment's realm.
289
+ */
290
+ function prepareUnattachedInlineScript(script, realmDocument) {
291
+ // We must clone the script before neutralizing it, otherwise the clone will also be neutralized
292
+ const execScript = realmDocument.importNode(script, true);
293
+ const inertScript = script;
294
+ // neutralize the script so that it doesn't execute in the main JS context
295
+ inertScript.textContent = '//inert';
296
+ document.body.appendChild(inertScript);
297
+ // now restore the inert script so the code using it doesn't see what we did
298
+ inertScript.remove();
299
+ inertScript.firstChild.remove();
300
+ // We have already cloned the inertScript so we don't need to clone it again.
301
+ // Cloning again would cause the execScript to be neutralized.
302
+ attachScriptToRealm({ inertScript, execScript, realmDocument });
303
+ const origScriptAppendChild = inertScript.appendChild;
304
+ inertScript.appendChild = function (node) {
305
+ origScriptAppendChild.call(inertScript, node);
306
+ execScript.appendChild.call(execScript, realmDocument.importNode(node, true));
307
+ // restore appendChild since only the first invocation should execute a script
308
+ inertScript.appendChild = origScriptAppendChild;
309
+ return node;
310
+ };
311
+ }
312
+ /**
313
+ * Rewrite the tagName for the special braid-* stand-in elements so fragment code sees plain
314
+ * HTML/HEAD/BODY, and map their sizing reads onto the host page's real elements.
315
+ */
316
+ let lazyBraidCustomElements;
317
+ function getBraidCustomElements() {
318
+ lazyBraidCustomElements ??= new Map([
319
+ ['BRAID-HTML', document.documentElement],
320
+ ['BRAID-HEAD', document.head],
321
+ ['BRAID-BODY', document.body],
322
+ ]);
323
+ return lazyBraidCustomElements;
324
+ }
325
+ export function patchSpecialHtmlElement(node, realmDocument) {
326
+ const originalTagName = node.tagName;
327
+ const mappedElement = getBraidCustomElements().get(originalTagName);
328
+ if (mappedElement) {
329
+ Object.defineProperties(node, {
330
+ clientWidth: {
331
+ get() {
332
+ return mappedElement.clientWidth;
333
+ },
334
+ },
335
+ clientHeight: {
336
+ get() {
337
+ return mappedElement.clientHeight;
338
+ },
339
+ },
340
+ nodeName: {
341
+ get() {
342
+ return originalTagName.replace(/^BRAID-/i, '');
343
+ },
344
+ },
345
+ tagName: {
346
+ get() {
347
+ return originalTagName.replace(/^BRAID-/i, '');
348
+ },
349
+ },
350
+ });
351
+ // if the node is BRAID-HEAD, we are done
352
+ if (node.tagName === 'HEAD') {
353
+ return;
354
+ }
355
+ // otherwise we need to patch addEventListener and removeEventListener to support retargeting
356
+ // to the main <html> and <body> elements
357
+ node.addEventListener = realmDocument.addEventListener;
358
+ node.removeEventListener = realmDocument.removeEventListener;
359
+ }
360
+ }
361
+ //# sourceMappingURL=script-execution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script-execution.js","sourceRoot":"","sources":["../../../../../libs/core/src/compat/script-execution.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Service worker containment for compat realms.
3
+ *
4
+ * A realm's `navigator` is aliased to the host window's, so without this a fragment reaches the
5
+ * host page's real `ServiceWorkerContainer` — and `register()` installs a **persistent worker on
6
+ * the host's origin** that outlives the fragment's unmount, because `env.signal` teardown has
7
+ * nothing to unregister it with.
8
+ *
9
+ * The invariants had a hole here worth naming. Host purity says *Braid* never patches the host;
10
+ * nothing said anything about a *fragment* installing origin-wide persistent state. Realm isolation
11
+ * covers JavaScript, and a service worker is neither JavaScript state nor scoped to a realm — it is
12
+ * infrastructure attached to the origin.
13
+ *
14
+ * So registration is refused, loudly. A fragment that wants offline behavior should get it from the
15
+ * host's worker rather than installing a second one, and two workers racing for one scope is a
16
+ * debugging problem nobody should inherit by accident.
17
+ *
18
+ * The container stays *present* rather than being deleted: `'serviceWorker' in navigator` is how
19
+ * every app feature-detects, and answering false would make an app that genuinely needs a worker
20
+ * degrade in silence instead of saying so.
21
+ */
22
+ export declare function createRealmNavigator(hostNavigator: Navigator, fragmentId: string): Navigator;
@@ -0,0 +1,104 @@
1
+ import { BraidError } from '../errors.js';
2
+ import { isDevMode } from '../config.js';
3
+ /**
4
+ * Service worker containment for compat realms.
5
+ *
6
+ * A realm's `navigator` is aliased to the host window's, so without this a fragment reaches the
7
+ * host page's real `ServiceWorkerContainer` — and `register()` installs a **persistent worker on
8
+ * the host's origin** that outlives the fragment's unmount, because `env.signal` teardown has
9
+ * nothing to unregister it with.
10
+ *
11
+ * The invariants had a hole here worth naming. Host purity says *Braid* never patches the host;
12
+ * nothing said anything about a *fragment* installing origin-wide persistent state. Realm isolation
13
+ * covers JavaScript, and a service worker is neither JavaScript state nor scoped to a realm — it is
14
+ * infrastructure attached to the origin.
15
+ *
16
+ * So registration is refused, loudly. A fragment that wants offline behavior should get it from the
17
+ * host's worker rather than installing a second one, and two workers racing for one scope is a
18
+ * debugging problem nobody should inherit by accident.
19
+ *
20
+ * The container stays *present* rather than being deleted: `'serviceWorker' in navigator` is how
21
+ * every app feature-detects, and answering false would make an app that genuinely needs a worker
22
+ * degrade in silence instead of saying so.
23
+ */
24
+ export function createRealmNavigator(hostNavigator, fragmentId) {
25
+ const container = createContainmentContainer(fragmentId);
26
+ return new Proxy(hostNavigator, {
27
+ get(target, property, receiver) {
28
+ if (property === 'serviceWorker')
29
+ return container;
30
+ const value = Reflect.get(target, property, target);
31
+ // Navigator members are natively bound to the real navigator; handing back an unbound
32
+ // function would make `navigator.sendBeacon(...)` throw an illegal-invocation error.
33
+ return typeof value === 'function' ? value.bind(target) : value;
34
+ },
35
+ // `navigator.serviceWorker = x` is silently ignored by the platform (the property has no
36
+ // setter); mirroring that is more predictable than throwing.
37
+ set(target, property, value, receiver) {
38
+ if (property === 'serviceWorker')
39
+ return true;
40
+ return Reflect.set(target, property, value, receiver);
41
+ },
42
+ has(target, property) {
43
+ return property === 'serviceWorker' || Reflect.has(target, property);
44
+ },
45
+ });
46
+ }
47
+ function createContainmentContainer(fragmentId) {
48
+ const refuse = (operation) => new BraidError(`a fragment may not ${operation} a service worker`, {
49
+ fragmentId,
50
+ stage: 'realm-boot',
51
+ fixHint: 'a service worker attaches to the whole origin, outlives this fragment, and is not scoped ' +
52
+ 'by the realm — so the shell must own it. Register it from the host and let this fragment ' +
53
+ "share it, or drop the registration if the fragment's offline behavior is not needed here.",
54
+ });
55
+ const report = (operation) => {
56
+ if (isDevMode()) {
57
+ console.warn(`[braid:${fragmentId}] refused to ${operation} a service worker. Registration would install ` +
58
+ `origin-wide persistent state that survives this fragment's unmount; the shell owns the worker.`);
59
+ }
60
+ };
61
+ const target = new EventTarget();
62
+ const container = {
63
+ async register() {
64
+ report('register');
65
+ throw refuse('register');
66
+ },
67
+ /**
68
+ * Empty rather than the host's registrations.
69
+ *
70
+ * Reporting them would tell a fragment what else is installed on the origin — the host's
71
+ * infrastructure is not the fragment's business, and a fragment acting on a registration it
72
+ * did not create is a worse outcome than one that sees none.
73
+ */
74
+ async getRegistration() {
75
+ return undefined;
76
+ },
77
+ async getRegistrations() {
78
+ return [];
79
+ },
80
+ /**
81
+ * Rejected, which is a deliberate deviation: the spec leaves `ready` pending forever when there
82
+ * is no registration. A promise that never settles is indistinguishable from a hang, and in an
83
+ * emulation layer an honest error beats a spec-accurate silence.
84
+ */
85
+ get ready() {
86
+ report('await readiness of');
87
+ return Promise.reject(refuse('use'));
88
+ },
89
+ get controller() {
90
+ return null;
91
+ },
92
+ startMessages() {
93
+ // no worker, so no messages to start delivering
94
+ },
95
+ addEventListener: target.addEventListener.bind(target),
96
+ removeEventListener: target.removeEventListener.bind(target),
97
+ dispatchEvent: target.dispatchEvent.bind(target),
98
+ oncontrollerchange: null,
99
+ onmessage: null,
100
+ onmessageerror: null,
101
+ };
102
+ return container;
103
+ }
104
+ //# sourceMappingURL=service-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-worker.js","sourceRoot":"","sources":["../../../../../libs/core/src/compat/service-worker.ts"],"names":[],"mappings":"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"}