@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.
- package/README.md +123 -0
- package/package.json +37 -0
- package/src/adapters/adapter.d.ts +75 -0
- package/src/adapters/adapter.js +35 -0
- package/src/adapters/adapter.js.map +1 -0
- package/src/adapters/compat-adapter.d.ts +27 -0
- package/src/adapters/compat-adapter.js +113 -0
- package/src/adapters/compat-adapter.js.map +1 -0
- package/src/adapters/custom-element-adapter.d.ts +21 -0
- package/src/adapters/custom-element-adapter.js +91 -0
- package/src/adapters/custom-element-adapter.js.map +1 -0
- package/src/compat/born-inert-scripts.d.ts +30 -0
- package/src/compat/born-inert-scripts.js +85 -0
- package/src/compat/born-inert-scripts.js.map +1 -0
- package/src/compat/document-facade.d.ts +44 -0
- package/src/compat/document-facade.js +104 -0
- package/src/compat/document-facade.js.map +1 -0
- package/src/compat/document-member-classification.d.ts +30 -0
- package/src/compat/document-member-classification.js +64 -0
- package/src/compat/document-member-classification.js.map +1 -0
- package/src/compat/document-overrides.d.ts +44 -0
- package/src/compat/document-overrides.js +225 -0
- package/src/compat/document-overrides.js.map +1 -0
- package/src/compat/document-surface.generated.d.ts +21 -0
- package/src/compat/document-surface.generated.js +292 -0
- package/src/compat/document-surface.generated.js.map +1 -0
- package/src/compat/fragment-boundary.d.ts +14 -0
- package/src/compat/fragment-boundary.js +335 -0
- package/src/compat/fragment-boundary.js.map +1 -0
- package/src/compat/metadata.d.ts +15 -0
- package/src/compat/metadata.js +21 -0
- package/src/compat/metadata.js.map +1 -0
- package/src/compat/navigation-bus.d.ts +29 -0
- package/src/compat/navigation-bus.js +92 -0
- package/src/compat/navigation-bus.js.map +1 -0
- package/src/compat/realm-patches.d.ts +21 -0
- package/src/compat/realm-patches.js +573 -0
- package/src/compat/realm-patches.js.map +1 -0
- package/src/compat/register-worker.d.ts +45 -0
- package/src/compat/register-worker.js +57 -0
- package/src/compat/register-worker.js.map +1 -0
- package/src/compat/script-execution.d.ts +65 -0
- package/src/compat/script-execution.js +361 -0
- package/src/compat/script-execution.js.map +1 -0
- package/src/compat/service-worker.d.ts +22 -0
- package/src/compat/service-worker.js +104 -0
- package/src/compat/service-worker.js.map +1 -0
- package/src/config.d.ts +37 -0
- package/src/config.js +26 -0
- package/src/config.js.map +1 -0
- package/src/context/context-bus.d.ts +67 -0
- package/src/context/context-bus.js +117 -0
- package/src/context/context-bus.js.map +1 -0
- package/src/elements/fragment-slot.d.ts +39 -0
- package/src/elements/fragment-slot.js +281 -0
- package/src/elements/fragment-slot.js.map +1 -0
- package/src/env/create-env.d.ts +31 -0
- package/src/env/create-env.js +91 -0
- package/src/env/create-env.js.map +1 -0
- package/src/env/fragment-env.d.ts +56 -0
- package/src/env/fragment-env.js +14 -0
- package/src/env/fragment-env.js.map +1 -0
- package/src/errors.d.ts +21 -0
- package/src/errors.js +21 -0
- package/src/errors.js.map +1 -0
- package/src/index.d.ts +36 -0
- package/src/index.js +40 -0
- package/src/index.js.map +1 -0
- package/src/protocol.d.ts +38 -0
- package/src/protocol.js +45 -0
- package/src/protocol.js.map +1 -0
- package/src/realm/realm-manager.d.ts +60 -0
- package/src/realm/realm-manager.js +296 -0
- package/src/realm/realm-manager.js.map +1 -0
- package/src/utils/assert.d.ts +7 -0
- package/src/utils/assert.js +10 -0
- package/src/utils/assert.js.map +1 -0
- package/src/utils/selector-helpers.d.ts +10 -0
- package/src/utils/selector-helpers.js +13 -0
- 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":"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"}
|
|
@@ -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":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAqB;IACpD,IAAI,EAAE,gBAAgB;IAEtB;;;;;OAKG;IACH,SAAS,EAAE,aAAa;IAExB;;;OAGG;IACH,aAAa,EAAE,KAAK;IAEpB,KAAK,CAAC,IAAI,CAAC,GAAuB;QAChC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC;QAC/C,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAE9D,qDAAqD;QACrD,MAAM,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAEpC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACtD,MAAM,IAAI,UAAU,CAClB,2DAA2D,OAAO,CAAC,OAAO,GAAG,EAC7E;gBACE,UAAU;gBACV,KAAK,EAAE,eAAe;gBACtB,OAAO,EACL,wFAAwF;oBACxF,wCAAwC;aAC3C,CACF,CAAC;QACJ,CAAC;QAED,yFAAyF;QACzF,uFAAuF;QACvF,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9D,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QAC/B,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAEzB,GAAG,CAAC,cAAc,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;QAE1D,6EAA6E;QAC7E,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YAClC,OAAO,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAG,KAAqB,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QACvG,CAAC;QAED,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAEzE,IAAI,SAAS,EAAE,EAAE,CAAC;YAChB,OAAO,CAAC,KAAK,CAAC,UAAU,UAAU,cAAc,OAAO,CAAC,OAAO,GAAG,EAAE;gBAClE,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,UAAU,EAAE,OAAO,CAAC,MAAM;aAC3B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;CACF,CAAC;AAQF,SAAS,WAAW,CAAC,GAAsC,EAAE,UAAkB;IAC7E,MAAM,KAAK,GAAG,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,SAAS,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEzE,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;QACvB,MAAM,IAAI,UAAU,CAClB,oFAAoF,EACpF;YACE,UAAU;YACV,KAAK,EAAE,oBAAoB;YAC3B,OAAO,EAAE,iHAAiH;SAC3H,CACF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,KAAK;QACL,OAAO;QACP,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,QAAQ,CAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE;KAC3H,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU,CAAC,OAAgB,EAAE,KAAwC;IAC5E,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -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;
|