@braidlabs/core 0.1.1 → 0.2.0
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/package.json +2 -2
- package/src/adapters/adapter.d.ts +13 -0
- package/src/adapters/adapter.js.map +1 -1
- package/src/adapters/compat-adapter.d.ts +7 -0
- package/src/adapters/compat-adapter.js +24 -16
- package/src/adapters/compat-adapter.js.map +1 -1
- package/src/adapters/contract-adapter.d.ts +21 -0
- package/src/adapters/contract-adapter.js +71 -0
- package/src/adapters/contract-adapter.js.map +1 -0
- package/src/config.d.ts +29 -0
- package/src/config.js +16 -0
- package/src/config.js.map +1 -1
- package/src/context/context-bus.d.ts +63 -3
- package/src/context/context-bus.js +147 -61
- package/src/context/context-bus.js.map +1 -1
- package/src/elements/fragment-slot.d.ts +114 -1
- package/src/elements/fragment-slot.js +743 -60
- package/src/elements/fragment-slot.js.map +1 -1
- package/src/env/create-env.d.ts +20 -8
- package/src/env/create-env.js +48 -16
- package/src/env/create-env.js.map +1 -1
- package/src/env/define-fragment.d.ts +43 -0
- package/src/env/define-fragment.js +23 -0
- package/src/env/define-fragment.js.map +1 -0
- package/src/env/fragment-env.d.ts +20 -1
- package/src/env/fragment-env.js +0 -12
- package/src/env/fragment-env.js.map +1 -1
- package/src/errors.d.ts +1 -1
- package/src/errors.js.map +1 -1
- package/src/index.d.ts +14 -1
- package/src/index.js +7 -1
- package/src/index.js.map +1 -1
- package/src/protocol.d.ts +20 -4
- package/src/protocol.js +23 -7
- package/src/protocol.js.map +1 -1
- package/src/realm/realm-manager.d.ts +19 -2
- package/src/realm/realm-manager.js +32 -7
- package/src/realm/realm-manager.js.map +1 -1
- package/src/realm/sandbox-realm.d.ts +95 -0
- package/src/realm/sandbox-realm.js +194 -0
- package/src/realm/sandbox-realm.js.map +1 -0
- package/src/weave/capabilities.d.ts +71 -0
- package/src/weave/capabilities.js +61 -0
- package/src/weave/capabilities.js.map +1 -0
- package/src/weave/channel.d.ts +63 -0
- package/src/weave/channel.js +189 -0
- package/src/weave/channel.js.map +1 -0
- package/src/weave/closing.d.ts +99 -0
- package/src/weave/closing.js +130 -0
- package/src/weave/closing.js.map +1 -0
- package/src/weave/context-bridge.d.ts +81 -0
- package/src/weave/context-bridge.js +145 -0
- package/src/weave/context-bridge.js.map +1 -0
- package/src/weave/contract.d.ts +89 -0
- package/src/weave/contract.js +143 -0
- package/src/weave/contract.js.map +1 -0
- package/src/weave/envelope.d.ts +80 -0
- package/src/weave/envelope.js +49 -0
- package/src/weave/envelope.js.map +1 -0
- package/src/weave/guest.d.ts +59 -0
- package/src/weave/guest.js +120 -0
- package/src/weave/guest.js.map +1 -0
- package/src/weave/handshake.d.ts +124 -0
- package/src/weave/handshake.js +109 -0
- package/src/weave/handshake.js.map +1 -0
- package/src/weave/liveness.d.ts +90 -0
- package/src/weave/liveness.js +189 -0
- package/src/weave/liveness.js.map +1 -0
- package/src/weave/messages.d.ts +27 -0
- package/src/weave/messages.js +13 -0
- package/src/weave/messages.js.map +1 -0
- package/src/weave/port-backing.d.ts +19 -0
- package/src/weave/port-backing.js +54 -0
- package/src/weave/port-backing.js.map +1 -0
- package/src/weave/same-realm-backing.d.ts +21 -0
- package/src/weave/same-realm-backing.js +44 -0
- package/src/weave/same-realm-backing.js.map +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@braidlabs/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
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
5
|
"keywords": [
|
|
6
6
|
"micro-frontends",
|
|
@@ -34,4 +34,4 @@
|
|
|
34
34
|
"url": "https://github.com/braidjs/braid/issues"
|
|
35
35
|
},
|
|
36
36
|
"module": "./src/index.js"
|
|
37
|
-
}
|
|
37
|
+
}
|
|
@@ -25,6 +25,10 @@ export interface AdapterBootContext {
|
|
|
25
25
|
/**
|
|
26
26
|
* The fragment's HTML, fetched from the gateway namespace, or null when {@link pierced} —
|
|
27
27
|
* a pierced fragment's content is already in `contentRoot`, put there by the parser.
|
|
28
|
+
*
|
|
29
|
+
* A client-fetched document is usually in `contentRoot` already too, painted the moment it
|
|
30
|
+
* arrived, and then arrives here as `pierced` with `html: null`. Adapters should treat `pierced`
|
|
31
|
+
* as "the content is in place", not as "the server rendered it".
|
|
28
32
|
*/
|
|
29
33
|
html: string | null;
|
|
30
34
|
/**
|
|
@@ -38,6 +42,15 @@ export interface AdapterBootContext {
|
|
|
38
42
|
/** Whether the fragment's navigation is bound to the host window's navigation. */
|
|
39
43
|
bound: boolean;
|
|
40
44
|
env: FragmentEnv;
|
|
45
|
+
/**
|
|
46
|
+
* The entry module, when the host supplies it directly rather than through the gateway.
|
|
47
|
+
*
|
|
48
|
+
* Gateway-composed fragments carry their entry on the realm stub, stamped from the manifest, and
|
|
49
|
+
* an adapter reads it from `realm.adapterOptions`. A gateway-free contract fragment has no stub —
|
|
50
|
+
* that is the point — so the host declares the entry in markup and it arrives here instead.
|
|
51
|
+
* Adapters that need an entry should prefer this and fall back to the stub.
|
|
52
|
+
*/
|
|
53
|
+
entry?: string;
|
|
41
54
|
/** Aborts when the fragment is being torn down. */
|
|
42
55
|
signal: AbortSignal;
|
|
43
56
|
}
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../../../libs/core/src/adapters/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AA8E1C;;;;;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"}
|
|
@@ -25,3 +25,10 @@ export declare const compatAdapter: InstalledAdapter;
|
|
|
25
25
|
* saw a bare markup snippet.
|
|
26
26
|
*/
|
|
27
27
|
export declare function parseFragmentContent(html: string, mainDocument: Document): HTMLElement;
|
|
28
|
+
/**
|
|
29
|
+
* Resolves once every image that holds a document's load event has loaded or failed.
|
|
30
|
+
*
|
|
31
|
+
* Lazy images are skipped, as browsers skip them: one below the fold does not load until it is
|
|
32
|
+
* scrolled to, and waiting for it would mean the fragment's load event never fires.
|
|
33
|
+
*/
|
|
34
|
+
export declare function imagesSettled(contentRoot: HTMLElement): Promise<void>;
|
|
@@ -50,19 +50,24 @@ export const compatAdapter = {
|
|
|
50
50
|
fragmentShadowRoot[compatMetadataSymbol].documentReadyState = 'interactive';
|
|
51
51
|
contentRoot.dispatchEvent(new Event('readystatechange', { bubbles: false, cancelable: false }));
|
|
52
52
|
contentRoot.dispatchEvent(new Event('DOMContentLoaded', { bubbles: true, cancelable: false }));
|
|
53
|
-
//
|
|
54
|
-
// all styles and scripts have loaded, so images are the main kind of resource to wait for.
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
53
|
+
// The window load event waits for the fragment's images, as a standalone document's would. By
|
|
54
|
+
// now all styles and scripts have loaded, so images are the main kind of resource to wait for.
|
|
55
|
+
//
|
|
56
|
+
// Not awaited: boot is done once the fragment's code has run, and the slot reports ready on
|
|
57
|
+
// boot. Holding that for every image — a hero banner, or a lazy one that may never load —
|
|
58
|
+
// delays readiness for something the fragment observes on its own timeline anyway.
|
|
59
|
+
void imagesSettled(contentRoot).then(() => {
|
|
60
|
+
// Wrap the event into a task so we don't execute too early in case there are no images.
|
|
61
|
+
setTimeout(() => {
|
|
62
|
+
if (signal.aborted)
|
|
63
|
+
return;
|
|
64
|
+
fragmentShadowRoot[compatMetadataSymbol].documentReadyState = 'complete';
|
|
65
|
+
contentRoot.dispatchEvent(new Event('readystatechange', { bubbles: false, cancelable: false }));
|
|
66
|
+
realm.window.dispatchEvent(new Event('load', { bubbles: false, cancelable: false }));
|
|
67
|
+
if (isDevMode()) {
|
|
68
|
+
console.debug(`[braid:${fragmentId}] compat fragment loaded`, { shadowRoot: fragmentShadowRoot });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
66
71
|
});
|
|
67
72
|
},
|
|
68
73
|
};
|
|
@@ -90,10 +95,13 @@ export function parseFragmentContent(html, mainDocument) {
|
|
|
90
95
|
return wrapper;
|
|
91
96
|
}
|
|
92
97
|
/**
|
|
93
|
-
*
|
|
98
|
+
* Resolves once every image that holds a document's load event has loaded or failed.
|
|
99
|
+
*
|
|
100
|
+
* Lazy images are skipped, as browsers skip them: one below the fold does not load until it is
|
|
101
|
+
* scrolled to, and waiting for it would mean the fragment's load event never fires.
|
|
94
102
|
*/
|
|
95
|
-
function
|
|
96
|
-
const images = contentRoot.querySelectorAll('img');
|
|
103
|
+
export function imagesSettled(contentRoot) {
|
|
104
|
+
const images = contentRoot.querySelectorAll('img:not([loading="lazy" i])');
|
|
97
105
|
if (images.length === 0) {
|
|
98
106
|
return Promise.resolve();
|
|
99
107
|
}
|
|
@@ -1 +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,
|
|
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 contract adapter: a fragment that asks for a `FragmentEnv` instead of pretending to own a
|
|
4
|
+
* browser.
|
|
5
|
+
*
|
|
6
|
+
* This is the mode `braid-architecture.md` has described as the end state since before there was
|
|
7
|
+
* anything to run it — `FragmentEnv` was defined, documented as "the heart of the project", and
|
|
8
|
+
* consumed by nothing a framework application would reach for. Every normal path ran through compat
|
|
9
|
+
* mode, which meant Braid's production surface was its most expensive and most browser-quirk-
|
|
10
|
+
* dependent one: the mode whose failure modes fill `braid-failure-modes.md`.
|
|
11
|
+
*
|
|
12
|
+
* What a contract fragment gives up: the illusion. No `document` facade, no `window` patches, no
|
|
13
|
+
* history virtualisation, no script neutralisation, no singleton renaming. What it gets back is the
|
|
14
|
+
* whole of that list as *not-your-problem*, plus one structural bonus that is easy to undersell:
|
|
15
|
+
*
|
|
16
|
+
* **A contract fragment needs no gateway.** Its realm boots from a `blob:` URL the runtime writes
|
|
17
|
+
* itself, so there is no realm stub to serve, no namespace to route, and no host-origin middleware
|
|
18
|
+
* to install. A statically hosted SPA can be a Braid host for contract fragments. That was the
|
|
19
|
+
* sharpest structural criticism of the architecture, and this is the answer to it.
|
|
20
|
+
*/
|
|
21
|
+
export declare const contractAdapter: InstalledAdapter;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { BraidError } from '../errors.js';
|
|
2
|
+
import { readFragmentDefinition } from '../env/define-fragment.js';
|
|
3
|
+
/**
|
|
4
|
+
* The contract adapter: a fragment that asks for a `FragmentEnv` instead of pretending to own a
|
|
5
|
+
* browser.
|
|
6
|
+
*
|
|
7
|
+
* This is the mode `braid-architecture.md` has described as the end state since before there was
|
|
8
|
+
* anything to run it — `FragmentEnv` was defined, documented as "the heart of the project", and
|
|
9
|
+
* consumed by nothing a framework application would reach for. Every normal path ran through compat
|
|
10
|
+
* mode, which meant Braid's production surface was its most expensive and most browser-quirk-
|
|
11
|
+
* dependent one: the mode whose failure modes fill `braid-failure-modes.md`.
|
|
12
|
+
*
|
|
13
|
+
* What a contract fragment gives up: the illusion. No `document` facade, no `window` patches, no
|
|
14
|
+
* history virtualisation, no script neutralisation, no singleton renaming. What it gets back is the
|
|
15
|
+
* whole of that list as *not-your-problem*, plus one structural bonus that is easy to undersell:
|
|
16
|
+
*
|
|
17
|
+
* **A contract fragment needs no gateway.** Its realm boots from a `blob:` URL the runtime writes
|
|
18
|
+
* itself, so there is no realm stub to serve, no namespace to route, and no host-origin middleware
|
|
19
|
+
* to install. A statically hosted SPA can be a Braid host for contract fragments. That was the
|
|
20
|
+
* sharpest structural criticism of the architecture, and this is the answer to it.
|
|
21
|
+
*/
|
|
22
|
+
export const contractAdapter = {
|
|
23
|
+
name: 'contract',
|
|
24
|
+
/**
|
|
25
|
+
* A blob realm: no network round trip, no realm stub, and — because the realm never navigates —
|
|
26
|
+
* zero interaction with the joint session history, which is the entire class of back/forward
|
|
27
|
+
* corruption that compat mode has to work around.
|
|
28
|
+
*/
|
|
29
|
+
realmKind: 'contract-blob',
|
|
30
|
+
/** A contract fragment builds its own UI from its entry module; it serves no document. */
|
|
31
|
+
needsDocument: false,
|
|
32
|
+
async boot(ctx) {
|
|
33
|
+
const { fragmentId, realm, env } = ctx;
|
|
34
|
+
// Markup first, stub second: the gateway-free path has no stub to read, and a host that named
|
|
35
|
+
// an entry explicitly means it.
|
|
36
|
+
const entry = ctx.entry ?? readEntry(realm.adapterOptions, fragmentId);
|
|
37
|
+
await realm.evaluate(entry);
|
|
38
|
+
/**
|
|
39
|
+
* The entry module registers itself on the realm's global during evaluation, rather than
|
|
40
|
+
* exporting something the host reads back.
|
|
41
|
+
*
|
|
42
|
+
* `realm.evaluate()` is a dynamic import in another realm, and its module namespace object is
|
|
43
|
+
* not reachable from here in a way that survives every bundler's output shape. A registration
|
|
44
|
+
* call is boring, explicit, and works the same whether the fragment ships an ES module, a
|
|
45
|
+
* bundled IIFE, or something a build tool invented last week.
|
|
46
|
+
*/
|
|
47
|
+
const definition = readFragmentDefinition(realm.window);
|
|
48
|
+
if (!definition || typeof definition.mount !== 'function') {
|
|
49
|
+
throw new BraidError(`the entry module "${entry}" did not define a fragment`, {
|
|
50
|
+
fragmentId,
|
|
51
|
+
stage: 'adapter-mount',
|
|
52
|
+
fixHint: 'the entry module must call defineFragment({ mount }) from @braidlabs/core at import time — ' +
|
|
53
|
+
'a default export is not enough, because the host reads the registration, not the module',
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
await definition.mount(env);
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
function readEntry(options, fragmentId) {
|
|
60
|
+
const entry = options['entry'];
|
|
61
|
+
if (typeof entry !== 'string' || entry.length === 0) {
|
|
62
|
+
throw new BraidError('the contract adapter needs an entry module', {
|
|
63
|
+
fragmentId,
|
|
64
|
+
stage: 'adapter-resolution',
|
|
65
|
+
fixHint: `set "entry" on the fragment's manifest ("entry": "/main.js"), or declare it on the slot ` +
|
|
66
|
+
`for a gateway-free fragment: <fragment-slot adapter="contract" entry="https://…/main.js">`,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return entry;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=contract-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-adapter.js","sourceRoot":"","sources":["../../../../../libs/core/src/adapters/contract-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAEnE;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAqB;IAC/C,IAAI,EAAE,UAAU;IAEhB;;;;OAIG;IACH,SAAS,EAAE,eAAe;IAE1B,0FAA0F;IAC1F,aAAa,EAAE,KAAK;IAEpB,KAAK,CAAC,IAAI,CAAC,GAAuB;QAChC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;QACvC,8FAA8F;QAC9F,gCAAgC;QAChC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,IAAI,SAAS,CAAC,KAAK,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAEvE,MAAM,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAE5B;;;;;;;;WAQG;QACH,MAAM,UAAU,GAAG,sBAAsB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAExD,IAAI,CAAC,UAAU,IAAI,OAAO,UAAU,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YAC1D,MAAM,IAAI,UAAU,CAAC,qBAAqB,KAAK,6BAA6B,EAAE;gBAC5E,UAAU;gBACV,KAAK,EAAE,eAAe;gBACtB,OAAO,EACL,6FAA6F;oBAC7F,yFAAyF;aAC5F,CAAC,CAAC;QACL,CAAC;QAED,MAAM,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;CACF,CAAC;AAEF,SAAS,SAAS,CAAC,OAA0C,EAAE,UAAkB;IAC/E,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,UAAU,CAAC,4CAA4C,EAAE;YACjE,UAAU;YACV,KAAK,EAAE,oBAAoB;YAC3B,OAAO,EACL,0FAA0F;gBAC1F,2FAA2F;SAC9F,CAAC,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/src/config.d.ts
CHANGED
|
@@ -6,7 +6,26 @@
|
|
|
6
6
|
* achieves its interception with fragment-boundary techniques confined to each fragment's own
|
|
7
7
|
* realm and shadow DOM subtree.
|
|
8
8
|
*/
|
|
9
|
+
import type { HostContract } from './weave/contract.js';
|
|
10
|
+
import type { FragmentCapabilities } from './weave/capabilities.js';
|
|
9
11
|
export interface BraidOptions {
|
|
12
|
+
/**
|
|
13
|
+
* What this host offers fragments, as a contract they can require a range of.
|
|
14
|
+
*
|
|
15
|
+
* Optional, and its absence means "no contract declared" rather than "version 0" — a host that
|
|
16
|
+
* declares nothing composes every fragment that requires nothing, which is every fragment that
|
|
17
|
+
* exists today.
|
|
18
|
+
*/
|
|
19
|
+
contract?: HostContract;
|
|
20
|
+
/**
|
|
21
|
+
* Per-fragment capability grants, by fragment id.
|
|
22
|
+
*
|
|
23
|
+
* The registry is the usual source — the gateway stamps a trusted fragment's capabilities onto
|
|
24
|
+
* its realm stub — but the untrusted tier has no gateway, so a host composing cross-origin
|
|
25
|
+
* fragments declares them here instead. Both sources are host-controlled, which is the invariant
|
|
26
|
+
* that matters; where both speak, the registry wins.
|
|
27
|
+
*/
|
|
28
|
+
capabilities?: Record<string, FragmentCapabilities>;
|
|
10
29
|
/**
|
|
11
30
|
* Enables development-mode diagnostics: unaudited-API warnings from the compat document
|
|
12
31
|
* facade, boundary-bypass reports, and verbose boot logging. Defaults to false.
|
|
@@ -26,9 +45,19 @@ export interface BraidOptions {
|
|
|
26
45
|
* no adapter.
|
|
27
46
|
*/
|
|
28
47
|
onHostNavigation?: (notify: () => void) => void;
|
|
48
|
+
/**
|
|
49
|
+
* The path the Braid gateway is mounted under (`/manage`), when the host shares its domain and
|
|
50
|
+
* does not own the root. Must match the gateway's `basePath`: the client fetches realm stubs and
|
|
51
|
+
* fragment documents from `/manage/__braid/…`. Call `initBraid` before any slot connects.
|
|
52
|
+
*/
|
|
53
|
+
basePath?: string;
|
|
29
54
|
}
|
|
30
55
|
interface ResolvedBraidConfig {
|
|
31
56
|
dev: boolean;
|
|
57
|
+
/** Empty, or an absolute path with no trailing slash. */
|
|
58
|
+
basePath: string;
|
|
59
|
+
contract?: HostContract;
|
|
60
|
+
capabilities?: Record<string, FragmentCapabilities>;
|
|
32
61
|
onHostNavigation?: (notify: () => void) => void;
|
|
33
62
|
}
|
|
34
63
|
export declare function setBraidConfig(options?: BraidOptions): void;
|
package/src/config.js
CHANGED
|
@@ -8,14 +8,30 @@
|
|
|
8
8
|
*/
|
|
9
9
|
const config = {
|
|
10
10
|
dev: false,
|
|
11
|
+
basePath: '',
|
|
11
12
|
};
|
|
12
13
|
export function setBraidConfig(options = {}) {
|
|
13
14
|
if (options.dev !== undefined) {
|
|
14
15
|
config.dev = options.dev;
|
|
15
16
|
}
|
|
17
|
+
if (options.contract) {
|
|
18
|
+
config.contract = options.contract;
|
|
19
|
+
}
|
|
20
|
+
if (options.capabilities) {
|
|
21
|
+
config.capabilities = options.capabilities;
|
|
22
|
+
}
|
|
16
23
|
if (options.onHostNavigation) {
|
|
17
24
|
config.onHostNavigation = options.onHostNavigation;
|
|
18
25
|
}
|
|
26
|
+
// Kept in sync with the gateway's normalizeBasePath: a path the URL parser would rewrite (`/café`,
|
|
27
|
+
// a dot segment) could never match the parsed pathnames the gateway compares it to.
|
|
28
|
+
if (options.basePath !== undefined) {
|
|
29
|
+
const basePath = options.basePath.replace(/\/+$/, '');
|
|
30
|
+
if (basePath && (!/^(\/[^/?#\s]+)+$/.test(basePath) || new URL(basePath, 'http://braid.invalid').pathname !== basePath)) {
|
|
31
|
+
throw new Error(`braid: basePath "${options.basePath}" must be an absolute path such as "/manage"`);
|
|
32
|
+
}
|
|
33
|
+
config.basePath = basePath;
|
|
34
|
+
}
|
|
19
35
|
}
|
|
20
36
|
export function getBraidConfig() {
|
|
21
37
|
return config;
|
package/src/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../libs/core/src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../libs/core/src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA+DH,MAAM,MAAM,GAAwB;IAClC,GAAG,EAAE,KAAK;IACV,QAAQ,EAAE,EAAE;CACb,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,UAAwB,EAAE;IACvD,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAC3B,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,MAAM,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IACrC,CAAC;IACD,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;QACzB,MAAM,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IAC7C,CAAC;IACD,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;QAC7B,MAAM,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IACrD,CAAC;IACD,mGAAmG;IACnG,oFAAoF;IACpF,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACtD,IAAI,QAAQ,IAAI,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,GAAG,CAAC,QAAQ,EAAE,sBAAsB,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,EAAE,CAAC;YACxH,MAAM,IAAI,KAAK,CAAC,oBAAoB,OAAO,CAAC,QAAQ,8CAA8C,CAAC,CAAC;QACtG,CAAC;QACD,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,MAAM,CAAC,GAAG,CAAC;AACpB,CAAC"}
|
|
@@ -39,7 +39,15 @@ export interface ContextReadOptions {
|
|
|
39
39
|
as?: number;
|
|
40
40
|
fragmentId?: string;
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
/**
|
|
43
|
+
* A context bus.
|
|
44
|
+
*
|
|
45
|
+
* State moved from module scope to instance fields so that a page can hold more than one — see
|
|
46
|
+
* `createContextBus`. Every method body below is unchanged by that move; the semantics that make
|
|
47
|
+
* this a bus rather than an event emitter are the versioning ones, and they are per-bus by nature.
|
|
48
|
+
*/
|
|
49
|
+
declare class ContextBus {
|
|
50
|
+
#private;
|
|
43
51
|
/**
|
|
44
52
|
* Declares the contract a context key carries.
|
|
45
53
|
*
|
|
@@ -61,7 +69,59 @@ export declare const braidContext: {
|
|
|
61
69
|
set(key: string, value: unknown): void;
|
|
62
70
|
get(key: string, options?: ContextReadOptions): unknown;
|
|
63
71
|
subscribe(key: string, listener: ContextListener, options?: ContextSubscribeOptions): () => void;
|
|
72
|
+
/** Every key that currently holds a value. Used to seed a fragment's mirror at handshake. */
|
|
73
|
+
keys(): string[];
|
|
74
|
+
/**
|
|
75
|
+
* Observes *which* keys change, including keys that do not exist yet.
|
|
76
|
+
*
|
|
77
|
+
* `subscribe` cannot serve this: it binds to one key by name, so a mirror built from it would
|
|
78
|
+
* silently miss every key first published after the fragment mounted.
|
|
79
|
+
*/
|
|
80
|
+
observe(listener: (key: string) => void): () => void;
|
|
81
|
+
/**
|
|
82
|
+
* Raises if `as` cannot be projected to from the published version.
|
|
83
|
+
*
|
|
84
|
+
* The check `subscribe` runs, exposed so it can be run *earlier* — at the boundary handshake,
|
|
85
|
+
* before a fragment has been handed any state. Decided from the declared chain rather than from
|
|
86
|
+
* a stored value, so a fragment that mounts before the first broadcast gets the same answer as
|
|
87
|
+
* one that mounts after it.
|
|
88
|
+
*/
|
|
89
|
+
assertReachable(key: string, as?: number, fragmentId?: string): void;
|
|
64
90
|
/** Exposed for tests. */
|
|
65
91
|
clear(): void;
|
|
66
|
-
}
|
|
67
|
-
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The page-level context bus: what `<fragment-slot>` routes to unless a fragment is scoped elsewhere.
|
|
95
|
+
*
|
|
96
|
+
* Still a singleton, still the default, and every existing import of it behaves exactly as before.
|
|
97
|
+
* What changed is that it is now *an instance of something*, which is what makes a second bus
|
|
98
|
+
* possible at all.
|
|
99
|
+
*/
|
|
100
|
+
export declare const braidContext: ContextBus;
|
|
101
|
+
/**
|
|
102
|
+
* Creates a bus a fragment owns, rather than one it merely subscribes to.
|
|
103
|
+
*
|
|
104
|
+
* Keys published here never reach the page bus and are never visible to any other fragment — not
|
|
105
|
+
* their values, not their names, not the timing of their changes. Two uses, and they are the two
|
|
106
|
+
* readings the plan left open:
|
|
107
|
+
*
|
|
108
|
+
* - **A private namespace.** A fragment that wants durable, versioned, cross-realm state that is
|
|
109
|
+
* nobody else's business gets it without inventing a second storage mechanism, and without the
|
|
110
|
+
* host having to grant or withhold anything.
|
|
111
|
+
* - **A subtree bus**, once nested fragments exist: a parent fragment hands its own bus to the
|
|
112
|
+
* fragments it composes, and they coordinate below the host's notice.
|
|
113
|
+
*
|
|
114
|
+
* The first works today. The second needs nested fragments to be a first-class concept, which is a
|
|
115
|
+
* larger commitment and is not made here — but nothing about this signature has to change when it
|
|
116
|
+
* is, because the routing already addresses buses by `scope` rather than by position.
|
|
117
|
+
*/
|
|
118
|
+
export declare function createContextBus(): ContextBus;
|
|
119
|
+
export type { ContextBus };
|
|
120
|
+
/**
|
|
121
|
+
* The surface a boundary router needs from a bus.
|
|
122
|
+
*
|
|
123
|
+
* Named so the router can take its bus as a constructor argument rather than importing the
|
|
124
|
+
* singleton. That was not indirection for its own sake: it is why `createContextBus()` above is a
|
|
125
|
+
* new routing target rather than a refactor of everything that touches context.
|
|
126
|
+
*/
|
|
127
|
+
export type ContextBusLike = Pick<ContextBus, 'get' | 'keys' | 'observe' | 'assertReachable' | 'schemaFor'>;
|