@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
package/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # @braidlabs/core
2
+
3
+ The Braid client runtime — compose independently deployed frontend apps into one cohesive page:
4
+ one origin, one DOM, one accessibility tree, while each app keeps its own JavaScript world, its
5
+ own dependencies, and its own release train.
6
+
7
+ Skew handling — versioned envelopes, migration chains, and negotiation — lives alongside this
8
+ runtime in [`@braidlabs/skew`](../skew).
9
+
10
+ **Start with [Braid, explained](../../docs/braid-explained.md)** if you are new to this: it defines
11
+ every term this README uses and walks one page load end to end. The founding architecture and its
12
+ rationale live in [`docs/braid-architecture.md`](../../docs/braid-architecture.md).
13
+
14
+ Also see: [failure modes](../../docs/braid-failure-modes.md) ·
15
+ [working POC](../../docs/braid-poc.md) ·
16
+ [using Braid without the gateway](../../docs/braid-without-gateway.md)
17
+
18
+ ## Adapters in this build
19
+
20
+ | Adapter | Manifest | For | Needs a document? |
21
+ | --- | --- | --- | --- |
22
+ | `compat` | omit `adapter` — this is the default | whole apps that cannot be modified | yes |
23
+ | `custom-element` | `"adapter": "custom-element"` | a fragment that *is* a web component | no |
24
+
25
+ The **compat adapter** is the contained emulation layer for apps that cannot be told anything
26
+ (a legacy monolith mid-migration), and it is the default: a manifest that doesn't declare an
27
+ adapter gets it. Being a fragment requires zero app-code changes; config only.
28
+
29
+ The **custom-element adapter** is the other end of the spectrum, and the first adapter to use the
30
+ contract surface. Registering a fragment that already ships a custom element takes three manifest
31
+ fields and no emulation at all:
32
+
33
+ ```ts
34
+ { id: 'rating', endpoint: 'http://localhost:4503',
35
+ adapter: 'custom-element', entry: '/star-rating.js', element: 'star-rating',
36
+ events: ['rating:change'] }
37
+ ```
38
+
39
+ The element is created **inside the realm**, where it upgrades against the fragment's own
40
+ definition, then moved into the host's DOM — adoption across documents preserves the element's
41
+ class, so the fragment's implementation keeps running while its custom element registry stays out
42
+ of the host's. `env.props` are applied as DOM properties; the listed `events` are republished as
43
+ `braid:event` on the slot. Such a fragment serves no document at all, which is what
44
+ `needsDocument: false` on the adapter declares.
45
+
46
+ The rest of the contract surface (`FragmentEnv`, `BraidAdapter`) is defined and exported, and
47
+ **contract-blob realms work** (see below), but no framework contract adapters (react/angular/vue)
48
+ ship yet, so nothing routes to them automatically.
49
+
50
+ ## Realms
51
+
52
+ | Kind | URL | History involvement | Used by |
53
+ | --------------- | ---------------- | ------------------------------------------ | --------------- |
54
+ | `compat-http` | `/__braid/frag/…`| initial load only (`replaceState` after) | compat adapter |
55
+ | `contract-blob` | `blob:` | none, ever | contract adapters (none yet) |
56
+ | `sandbox` | third-party | — | not in this build |
57
+
58
+ Contract-blob realms boot from a `blob:` URL the runtime authors: no network round trip, no
59
+ gateway stub, and zero interaction with the joint session history. Each carries a `<base>` into
60
+ the fragment's namespace and its own import map, which is how two fragments ship different
61
+ majors of the same dependency without a shared resolution namespace.
62
+
63
+ Verified empirically in Chromium: same-origin and DOM-capable, five realms add
64
+ **zero** history entries, per-realm import maps resolve, module evaluation works, and globals do
65
+ not leak between realms. Firefox and WebKit remain unverified here.
66
+
67
+ **Known platform limitation.** `window.location` and `document.location` are
68
+ `[LegacyUnforgeable]` — own, non-configurable properties that cannot be intercepted in any
69
+ realm, including one we authored. So the dev-mode guidance toward `env.*`
70
+ covers `document` members but *cannot* cover `location`: a contract fragment reading `location`
71
+ silently gets the blob URL. This is the same platform rule that stops a blob document from
72
+ faking an http URL, and it is why compat mode keeps http realms.
73
+
74
+ ## Piercing
75
+
76
+ When the gateway server-renders a fragment into the page, `<fragment-slot>` **adopts** that
77
+ content instead of fetching: the slot detects the declarative shadow root the gateway wrote,
78
+ and the compat adapter activates the already-neutralized content in place. `reload()` always
79
+ goes back to the network.
80
+
81
+ ## Usage
82
+
83
+ Host page:
84
+
85
+ ```ts
86
+ import { initBraid } from '@braidlabs/core';
87
+
88
+ initBraid({
89
+ // wire your router so bound fragments observe host navigations (Braid never patches
90
+ // the host History API — host purity is an invariant, not a mode):
91
+ onHostNavigation: (notify) => router.afterEach(() => notify()),
92
+ });
93
+ ```
94
+
95
+ ```html
96
+ <fragment-slot name="legacy-billing"></fragment-slot>
97
+ ```
98
+
99
+ Server: mount [`@braidlabs/gateway`](../braid-gateway) in front of your app and register a
100
+ manifest for `legacy-billing`. No fragment code is required.
101
+
102
+ ## What the compat adapter does
103
+
104
+ - Boots each fragment's JS in a hidden same-origin iframe realm loaded from the gateway
105
+ namespace (`/__braid/frag/:id/…`), restoring the fragment's route-url illusion via
106
+ `history.replaceState`.
107
+ - Installs the document facade: a Proxy spliced into the realm document's prototype chain,
108
+ driven by a generated WebIDL audit manifest — unaudited API use surfaces as loud diagnostics
109
+ in dev mode.
110
+ - Confines all main-realm interception to the fragment boundary: per-node prototype stamping,
111
+ born-inert scripts, and a shadow-root-scoped observer safety net.
112
+ - **Never patches a host-page global or prototype. In any mode. Ever.**
113
+
114
+ ## Isolation boundaries
115
+
116
+ | Scope | Isolation mechanism |
117
+ | ----------------------- | ---------------------------------------------------------------------------------------- |
118
+ | compat realm (window) | constructor `hasInstance`, observer/CSSOM constructor swaps, size getters, history proxy |
119
+ | compat realm (document) | proxy facade over the document prototype chain (WebIDL-audited) |
120
+ | fragment DOM nodes | per-node prototype stamping (insertion/clone/ownerDocument/getRootNode) |
121
+ | fragment scripts | born-inert `type` shadowing traps until activation |
122
+
123
+ All interception is strictly confined to the fragment's own realm and shadow DOM boundary.
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@braidlabs/core",
3
+ "version": "0.1.1",
4
+ "description": "Braid client runtime — compose independently deployed frontend apps into one page. This build ships the compat adapter (the contained web-fragments-style emulation layer) as the default adapter.",
5
+ "keywords": [
6
+ "micro-frontends",
7
+ "composition",
8
+ "fragments",
9
+ "version-skew",
10
+ "web-fragments"
11
+ ],
12
+ "license": "MIT",
13
+ "sideEffects": false,
14
+ "type": "module",
15
+ "main": "./src/index.js",
16
+ "types": "./src/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./src/index.d.ts",
20
+ "default": "./src/index.js"
21
+ },
22
+ "./package.json": "./package.json"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/braidjs/braid.git",
30
+ "directory": "libs/core"
31
+ },
32
+ "homepage": "https://github.com/braidjs/braid#readme",
33
+ "bugs": {
34
+ "url": "https://github.com/braidjs/braid/issues"
35
+ },
36
+ "module": "./src/index.js"
37
+ }
@@ -0,0 +1,75 @@
1
+ import { FragmentEnv } from '../env/fragment-env.js';
2
+ import { RealmHandle, RealmKind } from '../realm/realm-manager.js';
3
+ /**
4
+ * The adapter authoring surface: framework adapters map a `FragmentEnv` into the
5
+ * framework's own extension points (Angular's `DOCUMENT` DI token, React's `createRoot`, …).
6
+ * Teardown is signal-driven via `env.signal`.
7
+ */
8
+ export interface BraidAdapter {
9
+ mount(env: FragmentEnv, entry: unknown): Promise<void> | void;
10
+ }
11
+ /**
12
+ * Everything the runtime hands an installed adapter to boot a fragment instance. This is the
13
+ * internal counterpart of {@link BraidAdapter}: contract adapters only need `env` + `entry`,
14
+ * while the compat adapter — which owns the full emulation layer — additionally drives the
15
+ * realm and the fragment's DOM directly.
16
+ */
17
+ export interface AdapterBootContext {
18
+ fragmentId: string;
19
+ /** The fragment's shadow root (owned by the <fragment-slot> element). */
20
+ shadowRoot: ShadowRoot;
21
+ /** The <braid-document> element acting as the fragment's virtual document element. */
22
+ contentRoot: HTMLElement;
23
+ /** The fragment's already-created realm. */
24
+ realm: RealmHandle;
25
+ /**
26
+ * The fragment's HTML, fetched from the gateway namespace, or null when {@link pierced} —
27
+ * a pierced fragment's content is already in `contentRoot`, put there by the parser.
28
+ */
29
+ html: string | null;
30
+ /**
31
+ * Whether the fragment's content was server-rendered into the page by the gateway. It is
32
+ * already neutralized (scripts inert, singletons renamed) and already in the DOM; the adapter
33
+ * activates it in place instead of building it.
34
+ */
35
+ pierced: boolean;
36
+ /** The fragment's logical route URL (pathname + search). */
37
+ routeUrl: string;
38
+ /** Whether the fragment's navigation is bound to the host window's navigation. */
39
+ bound: boolean;
40
+ env: FragmentEnv;
41
+ /** Aborts when the fragment is being torn down. */
42
+ signal: AbortSignal;
43
+ }
44
+ /**
45
+ * An installed adapter: which realm kind its fragments boot in, and the boot routine itself.
46
+ */
47
+ export interface InstalledAdapter {
48
+ readonly name: string;
49
+ readonly realmKind: RealmKind;
50
+ /**
51
+ * Whether the adapter needs the fragment's *document* — the markup the gateway prepares for
52
+ * the host's DOM. True unless stated otherwise.
53
+ *
54
+ * Contract adapters generally build their own UI from an entry module and never look at
55
+ * fragment HTML. Saying so here means a fragment that serves no document (a lone custom
56
+ * element, say) is not reported as broken for failing to serve one.
57
+ */
58
+ readonly needsDocument?: boolean;
59
+ boot(ctx: AdapterBootContext): Promise<void>;
60
+ }
61
+ /**
62
+ * The adapter every fragment runs under when its manifest does not declare one.
63
+ *
64
+ * This build ships only the compat adapter — the contained emulation
65
+ * layer — and makes it the default, so legacy apps compose with zero app-code changes.
66
+ */
67
+ export declare const DEFAULT_ADAPTER = "compat";
68
+ export declare function installAdapter(adapter: InstalledAdapter): void;
69
+ /**
70
+ * Resolves an adapter by its manifest-declared name, defaulting to {@link DEFAULT_ADAPTER}.
71
+ * An unknown adapter is a named error, not a silent fallback.
72
+ */
73
+ export declare function resolveAdapter(name: string | null | undefined, fragmentId: string): InstalledAdapter;
74
+ /** Exposed for tests. */
75
+ export declare function clearInstalledAdapters(): void;
@@ -0,0 +1,35 @@
1
+ import { BraidError } from '../errors.js';
2
+ /**
3
+ * The adapter every fragment runs under when its manifest does not declare one.
4
+ *
5
+ * This build ships only the compat adapter — the contained emulation
6
+ * layer — and makes it the default, so legacy apps compose with zero app-code changes.
7
+ */
8
+ export const DEFAULT_ADAPTER = 'compat';
9
+ const installedAdapters = new Map();
10
+ export function installAdapter(adapter) {
11
+ installedAdapters.set(adapter.name, adapter);
12
+ }
13
+ /**
14
+ * Resolves an adapter by its manifest-declared name, defaulting to {@link DEFAULT_ADAPTER}.
15
+ * An unknown adapter is a named error, not a silent fallback.
16
+ */
17
+ export function resolveAdapter(name, fragmentId) {
18
+ const adapterName = name || DEFAULT_ADAPTER;
19
+ const adapter = installedAdapters.get(adapterName);
20
+ if (!adapter) {
21
+ throw new BraidError(`no adapter named "${adapterName}" is installed in this client`, {
22
+ fragmentId,
23
+ stage: 'adapter-resolution',
24
+ fixHint: adapterName === DEFAULT_ADAPTER
25
+ ? 'call initBraid() before any <fragment-slot> connects, so the default compat adapter is installed'
26
+ : `this build of @braidlabs/core ships only the "compat" adapter; either set "adapter": "compat" in the fragment's manifest (or omit it — compat is the default), or install a client build that provides "${adapterName}"`,
27
+ });
28
+ }
29
+ return adapter;
30
+ }
31
+ /** Exposed for tests. */
32
+ export function clearInstalledAdapters() {
33
+ installedAdapters.clear();
34
+ }
35
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../../../libs/core/src/adapters/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAiE1C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC;AAExC,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAA4B,CAAC;AAE9D,MAAM,UAAU,cAAc,CAAC,OAAyB;IACtD,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAA+B,EAAE,UAAkB;IAChF,MAAM,WAAW,GAAG,IAAI,IAAI,eAAe,CAAC;IAC5C,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACnD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,UAAU,CAAC,qBAAqB,WAAW,+BAA+B,EAAE;YACpF,UAAU;YACV,KAAK,EAAE,oBAAoB;YAC3B,OAAO,EACL,WAAW,KAAK,eAAe;gBAC7B,CAAC,CAAC,kGAAkG;gBACpG,CAAC,CAAC,2MAA2M,WAAW,GAAG;SAChO,CAAC,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,yBAAyB;AACzB,MAAM,UAAU,sBAAsB;IACpC,iBAAiB,CAAC,KAAK,EAAE,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { InstalledAdapter } from './adapter.js';
2
+ /**
3
+ * The compat adapter — the emulation layer, contained.
4
+ *
5
+ * For apps that cannot be told anything (the legacy monolith mid-migration), this adapter
6
+ * provides the full web-fragments-style illusion: the fragment's code believes it owns the whole
7
+ * browser, while its DOM lives in the host page's shadow root and its JS runs in a hidden
8
+ * same-origin realm iframe. All interception is confined to the fragment's own realm and
9
+ * boundary; the host page is never patched.
10
+ *
11
+ * This is the only adapter shipped in this build, and the default (see `DEFAULT_ADAPTER`):
12
+ * a manifest that doesn't declare an adapter gets this one, so being a fragment requires zero
13
+ * app-code changes — config only.
14
+ */
15
+ export declare const compatAdapter: InstalledAdapter;
16
+ /**
17
+ * Parses gateway-prepared fragment markup into nodes owned by the host document.
18
+ *
19
+ * The markup arrives already transformed (`braid-html`/`braid-head`/`braid-body` stand-ins,
20
+ * inert scripts, namespaced subresource URLs), so this only parses and adopts it — parsing
21
+ * happens in a detached `DOMParser` document, where nothing executes.
22
+ *
23
+ * The stand-ins are unknown elements, so the parser leaves them in `body`; the fragment's root
24
+ * is `braid-html` when the gateway saw a full document, and a wrapper is synthesized when it
25
+ * saw a bare markup snippet.
26
+ */
27
+ export declare function parseFragmentContent(html: string, mainDocument: Document): HTMLElement;
@@ -0,0 +1,113 @@
1
+ import { compatMetadataSymbol } from '../compat/metadata.js';
2
+ import { initializeRealmContext } from '../compat/realm-patches.js';
3
+ import { executeScriptsInFragmentContent } from '../compat/script-execution.js';
4
+ import { isDevMode } from '../config.js';
5
+ /**
6
+ * The compat adapter — the emulation layer, contained.
7
+ *
8
+ * For apps that cannot be told anything (the legacy monolith mid-migration), this adapter
9
+ * provides the full web-fragments-style illusion: the fragment's code believes it owns the whole
10
+ * browser, while its DOM lives in the host page's shadow root and its JS runs in a hidden
11
+ * same-origin realm iframe. All interception is confined to the fragment's own realm and
12
+ * boundary; the host page is never patched.
13
+ *
14
+ * This is the only adapter shipped in this build, and the default (see `DEFAULT_ADAPTER`):
15
+ * a manifest that doesn't declare an adapter gets this one, so being a fragment requires zero
16
+ * app-code changes — config only.
17
+ */
18
+ export const compatAdapter = {
19
+ name: 'compat',
20
+ realmKind: 'compat-http',
21
+ async boot(ctx) {
22
+ const { fragmentId, shadowRoot, contentRoot, realm, html, pierced, bound, signal } = ctx;
23
+ const fragmentShadowRoot = Object.assign(shadowRoot, {
24
+ [compatMetadataSymbol]: { documentReadyState: 'loading' },
25
+ });
26
+ if (!pierced) {
27
+ // The gateway prepared this markup exactly as it prepares pierced content — singletons
28
+ // renamed, scripts neutralized, subresource URLs re-rooted — so the client only has to
29
+ // parse it. Re-applying those transforms here would be actively harmful: neutralizing an
30
+ // already-inert script would record `inert` as its "real" type and it would never run.
31
+ const content = parseFragmentContent(html ?? '', contentRoot.ownerDocument);
32
+ // Insert the (fully inert) content before the boundary exists; the boundary stamps the
33
+ // whole tree when it is created below — same order as the pierced flow.
34
+ contentRoot.appendChild(content);
35
+ }
36
+ // The abort controller tears down every listener/observer the compat machinery installs;
37
+ // it aborts when the fragment unmounts (slot disconnect) or the realm unloads.
38
+ const fragmentAbortController = new AbortController();
39
+ signal.addEventListener('abort', () => fragmentAbortController.abort(), { once: true });
40
+ // Install the full compat illusion onto the realm (window patches, document facade,
41
+ // boundary stamping, born-inert scripts).
42
+ const realmContext = initializeRealmContext(realm, fragmentShadowRoot, contentRoot, bound, fragmentAbortController);
43
+ // Now activate the fragment's scripts, in document order, in the realm's JS context.
44
+ executeScriptsInFragmentContent(contentRoot, realm.document);
45
+ // The fragment's own code has run, so its router has resolved whatever initial route it was
46
+ // going to resolve. From here a navigation is the user's, and a bound fragment may drive the
47
+ // host URL with it.
48
+ realmContext.bootComplete();
49
+ // Replicate the document lifecycle the fragment would observe standalone.
50
+ fragmentShadowRoot[compatMetadataSymbol].documentReadyState = 'interactive';
51
+ contentRoot.dispatchEvent(new Event('readystatechange', { bubbles: false, cancelable: false }));
52
+ contentRoot.dispatchEvent(new Event('DOMContentLoaded', { bubbles: true, cancelable: false }));
53
+ // In order to fire the window load event we need to wait for all the images to load. By now
54
+ // all styles and scripts have loaded, so images are the main kind of resource to wait for.
55
+ await allImagesLoaded(contentRoot);
56
+ // Wrap the event into a task so we don't execute too early in case there are no images.
57
+ setTimeout(() => {
58
+ if (signal.aborted)
59
+ return;
60
+ fragmentShadowRoot[compatMetadataSymbol].documentReadyState = 'complete';
61
+ contentRoot.dispatchEvent(new Event('readystatechange', { bubbles: false, cancelable: false }));
62
+ realm.window.dispatchEvent(new Event('load', { bubbles: false, cancelable: false }));
63
+ if (isDevMode()) {
64
+ console.debug(`[braid:${fragmentId}] compat fragment loaded`, { shadowRoot: fragmentShadowRoot });
65
+ }
66
+ });
67
+ },
68
+ };
69
+ /**
70
+ * Parses gateway-prepared fragment markup into nodes owned by the host document.
71
+ *
72
+ * The markup arrives already transformed (`braid-html`/`braid-head`/`braid-body` stand-ins,
73
+ * inert scripts, namespaced subresource URLs), so this only parses and adopts it — parsing
74
+ * happens in a detached `DOMParser` document, where nothing executes.
75
+ *
76
+ * The stand-ins are unknown elements, so the parser leaves them in `body`; the fragment's root
77
+ * is `braid-html` when the gateway saw a full document, and a wrapper is synthesized when it
78
+ * saw a bare markup snippet.
79
+ */
80
+ export function parseFragmentContent(html, mainDocument) {
81
+ const parsed = new DOMParser().parseFromString(html, 'text/html');
82
+ const braidHtml = parsed.body.querySelector('braid-html');
83
+ if (braidHtml) {
84
+ return mainDocument.importNode(braidHtml, true);
85
+ }
86
+ const wrapper = mainDocument.createElement('braid-html');
87
+ const braidBody = mainDocument.createElement('braid-body');
88
+ braidBody.append(...[...parsed.body.childNodes].map((node) => mainDocument.importNode(node, true)));
89
+ wrapper.append(braidBody);
90
+ return wrapper;
91
+ }
92
+ /**
93
+ * Returns a promise resolving when all images in the fragment's DOM have loaded (or errored).
94
+ */
95
+ function allImagesLoaded(contentRoot) {
96
+ const images = contentRoot.querySelectorAll('img');
97
+ if (images.length === 0) {
98
+ return Promise.resolve();
99
+ }
100
+ const imagePromises = [...images].map((image) => {
101
+ return new Promise((resolve) => {
102
+ if (image.complete) {
103
+ resolve();
104
+ }
105
+ else {
106
+ image.addEventListener('load', () => resolve());
107
+ image.addEventListener('error', () => resolve());
108
+ }
109
+ });
110
+ });
111
+ return Promise.all(imagePromises).then(() => undefined);
112
+ }
113
+ //# sourceMappingURL=compat-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compat-adapter.js","sourceRoot":"","sources":["../../../../../libs/core/src/adapters/compat-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,aAAa,GAAqB;IAC7C,IAAI,EAAE,QAAQ;IACd,SAAS,EAAE,aAAa;IAExB,KAAK,CAAC,IAAI,CAAC,GAAuB;QAChC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC;QAEzF,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE;YACnD,CAAC,oBAAoB,CAAC,EAAE,EAAE,kBAAkB,EAAE,SAA+B,EAAE;SAChF,CAAqB,CAAC;QAEvB,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,uFAAuF;YACvF,uFAAuF;YACvF,yFAAyF;YACzF,uFAAuF;YACvF,MAAM,OAAO,GAAG,oBAAoB,CAAC,IAAI,IAAI,EAAE,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC;YAE5E,uFAAuF;YACvF,wEAAwE;YACxE,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;QAED,yFAAyF;QACzF,+EAA+E;QAC/E,MAAM,uBAAuB,GAAG,IAAI,eAAe,EAAE,CAAC;QACtD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,uBAAuB,CAAC,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAExF,oFAAoF;QACpF,0CAA0C;QAC1C,MAAM,YAAY,GAAG,sBAAsB,CACzC,KAAK,EACL,kBAAkB,EAClB,WAAW,EACX,KAAK,EACL,uBAAuB,CACxB,CAAC;QAEF,qFAAqF;QACrF,+BAA+B,CAAC,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAE7D,4FAA4F;QAC5F,6FAA6F;QAC7F,oBAAoB;QACpB,YAAY,CAAC,YAAY,EAAE,CAAC;QAE5B,0EAA0E;QAC1E,kBAAkB,CAAC,oBAAoB,CAAC,CAAC,kBAAkB,GAAG,aAAa,CAAC;QAC5E,WAAW,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,kBAAkB,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAChG,WAAW,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,kBAAkB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAE/F,4FAA4F;QAC5F,2FAA2F;QAC3F,MAAM,eAAe,CAAC,WAAW,CAAC,CAAC;QAEnC,wFAAwF;QACxF,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAO;YAC3B,kBAAkB,CAAC,oBAAoB,CAAC,CAAC,kBAAkB,GAAG,UAAU,CAAC;YACzE,WAAW,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,kBAAkB,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;YAChG,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;YAErF,IAAI,SAAS,EAAE,EAAE,CAAC;gBAChB,OAAO,CAAC,KAAK,CAAC,UAAU,UAAU,0BAA0B,EAAE,EAAE,UAAU,EAAE,kBAAkB,EAAE,CAAC,CAAC;YACpG,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAE,YAAsB;IACvE,MAAM,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAElE,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IAC1D,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,YAAY,CAAC,UAAU,CAAC,SAAS,EAAE,IAAI,CAAgB,CAAC;IACjE,CAAC;IAED,MAAM,OAAO,GAAG,YAAY,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IACzD,MAAM,SAAS,GAAG,YAAY,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IAC3D,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACpG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC1B,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,WAAwB;IAC/C,MAAM,MAAM,GAAG,WAAW,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACnD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,MAAM,aAAa,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAC9C,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACnC,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACnB,OAAO,EAAE,CAAC;YACZ,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;gBAChD,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YACnD,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { InstalledAdapter } from './adapter.js';
2
+ /**
3
+ * The custom-element adapter — a fragment that ships an ordinary custom element.
4
+ *
5
+ * This is contract-mode: no document facade, no window patches, no boundary stamping. A
6
+ * well-behaved custom element does not need to believe it owns the browser; it needs a mount
7
+ * point, its props, and a teardown signal — which is exactly what `FragmentEnv` provides. The
8
+ * whole adapter is the code below.
9
+ *
10
+ * **How the element gets into the host page.** Custom element registries are per-window, so an
11
+ * element defined in the fragment's realm only upgrades elements created by *that* realm's
12
+ * document. So the element is created in the realm — where it upgrades against the fragment's
13
+ * own definition — and then moved into the host's DOM. Adoption preserves the element's class
14
+ * and behavior (the spec calls `adoptedCallback` and the instance keeps its definition), which
15
+ * is what lets a fragment's element live in the host page while its code stays isolated.
16
+ *
17
+ * The isolation this buys is real: two fragments may define the same tag name differently, or
18
+ * ship different majors of the same component library, and nothing collides — each definition
19
+ * lives in its own registry.
20
+ */
21
+ export declare const customElementAdapter: InstalledAdapter;
@@ -0,0 +1,91 @@
1
+ import { BraidError } from '../errors.js';
2
+ import { isDevMode } from '../config.js';
3
+ /**
4
+ * The custom-element adapter — a fragment that ships an ordinary custom element.
5
+ *
6
+ * This is contract-mode: no document facade, no window patches, no boundary stamping. A
7
+ * well-behaved custom element does not need to believe it owns the browser; it needs a mount
8
+ * point, its props, and a teardown signal — which is exactly what `FragmentEnv` provides. The
9
+ * whole adapter is the code below.
10
+ *
11
+ * **How the element gets into the host page.** Custom element registries are per-window, so an
12
+ * element defined in the fragment's realm only upgrades elements created by *that* realm's
13
+ * document. So the element is created in the realm — where it upgrades against the fragment's
14
+ * own definition — and then moved into the host's DOM. Adoption preserves the element's class
15
+ * and behavior (the spec calls `adoptedCallback` and the instance keeps its definition), which
16
+ * is what lets a fragment's element live in the host page while its code stays isolated.
17
+ *
18
+ * The isolation this buys is real: two fragments may define the same tag name differently, or
19
+ * ship different majors of the same component library, and nothing collides — each definition
20
+ * lives in its own registry.
21
+ */
22
+ export const customElementAdapter = {
23
+ name: 'custom-element',
24
+ /**
25
+ * An http realm, not a blob realm, only because the client learns which adapter a fragment
26
+ * uses by reading the realm stub — so the stub has to exist before the adapter is chosen.
27
+ * Nothing else here needs it, and moving to `contract-blob` (no round trip, no history
28
+ * interaction) is a change to the realm manager alone.
29
+ */
30
+ realmKind: 'compat-http',
31
+ /**
32
+ * The element is built from the entry module, so the fragment's HTML is never read. A widget
33
+ * that ships only a script is a complete fragment.
34
+ */
35
+ needsDocument: false,
36
+ async boot(ctx) {
37
+ const { fragmentId, realm, env, signal } = ctx;
38
+ const options = readOptions(realm.adapterOptions, fragmentId);
39
+ // define the element inside the fragment's own realm
40
+ await realm.evaluate(options.entry);
41
+ if (!realm.window.customElements.get(options.element)) {
42
+ throw new BraidError(`the entry module did not define a custom element named "${options.element}"`, {
43
+ fragmentId,
44
+ stage: 'adapter-mount',
45
+ fixHint: `check the "element" in this fragment's manifest against the tag name its entry module ` +
46
+ `registers with customElements.define()`,
47
+ });
48
+ }
49
+ // created in the realm so it upgrades against the fragment's definition, then moved into
50
+ // the host's DOM — adoption keeps the class, so it behaves the same in either document
51
+ const element = realm.document.createElement(options.element);
52
+ applyProps(element, env.props);
53
+ env.root.append(element);
54
+ env.onPropsChanged((props) => applyProps(element, props));
55
+ // the fragment's own events, republished on the host boundary as braid:event
56
+ for (const type of options.events) {
57
+ element.addEventListener(type, (event) => env.emit(type, event.detail), { signal });
58
+ }
59
+ signal.addEventListener('abort', () => element.remove(), { once: true });
60
+ if (isDevMode()) {
61
+ console.debug(`[braid:${fragmentId}] mounted <${options.element}>`, {
62
+ entry: options.entry,
63
+ forwarding: options.events,
64
+ });
65
+ }
66
+ },
67
+ };
68
+ function readOptions(raw, fragmentId) {
69
+ const entry = typeof raw['entry'] === 'string' ? raw['entry'] : '';
70
+ const element = typeof raw['element'] === 'string' ? raw['element'] : '';
71
+ if (!entry || !element) {
72
+ throw new BraidError(`the custom-element adapter needs "entry" and "element" in this fragment's manifest`, {
73
+ fragmentId,
74
+ stage: 'adapter-resolution',
75
+ fixHint: `add them to the manifest, e.g. { "adapter": "custom-element", "entry": "/widget.js", "element": "star-rating" }`,
76
+ });
77
+ }
78
+ return {
79
+ entry,
80
+ element,
81
+ events: Array.isArray(raw['events']) ? raw['events'].filter((e) => typeof e === 'string') : [],
82
+ };
83
+ }
84
+ /**
85
+ * Props are assigned as element *properties*, which is the custom-element convention for
86
+ * structured values — attributes would stringify them.
87
+ */
88
+ function applyProps(element, props) {
89
+ Object.assign(element, props);
90
+ }
91
+ //# sourceMappingURL=custom-element-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-element-adapter.js","sourceRoot":"","sources":["../../../../../libs/core/src/adapters/custom-element-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Born-inert scripts.
3
+ *
4
+ * Every script element created by fragment code (via the document facade's
5
+ * `createElement`/`createElementNS`) is neutralized at creation: its `type` attribute is set to
6
+ * `inert` before fragment code can attach content or insert it. This guarantees that no script
7
+ * reachable by fragment code can ever execute in the main JS context, no matter how it is
8
+ * inserted — even through code paths the boundary does not intercept synchronously.
9
+ *
10
+ * So that fragment code doesn't observe the neutralization, the script's `type` property and its
11
+ * `getAttribute`/`setAttribute` methods are shadowed to read and write the real type from/to the
12
+ * `data-script-type` attribute (the same attribute the gateway uses when neutralizing SSR
13
+ * content). The shadowing own-properties are removed before the script is activated.
14
+ */
15
+ export declare function makeScriptBornInert(script: HTMLScriptElement): void;
16
+ export declare function isBornInertScript(script: HTMLScriptElement): boolean;
17
+ /**
18
+ * Removes the type-shadowing own-properties, leaving the script in the standard inert form
19
+ * (`type="inert"` plus the real type in `data-script-type`) that the script execution machinery
20
+ * understands. Used on the asynchronous (boundary observer) activation path, where the script is
21
+ * already attached to the fragment's DOM in inert form.
22
+ */
23
+ export declare function removeBornInertShadowing(script: HTMLScriptElement): void;
24
+ /**
25
+ * Fully reverts a born-inert script to the live form the application intended (real `type`
26
+ * attribute, no `data-script-type`). Used on the synchronous (stamped insertion) activation
27
+ * path, which re-neutralizes the script itself before performing the actual DOM insertion — the
28
+ * script is never attached to the DOM in live form.
29
+ */
30
+ export declare function unwrapBornInertScriptToLive(script: HTMLScriptElement): void;