@braidlabs/core 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +123 -0
  2. package/package.json +37 -0
  3. package/src/adapters/adapter.d.ts +75 -0
  4. package/src/adapters/adapter.js +35 -0
  5. package/src/adapters/adapter.js.map +1 -0
  6. package/src/adapters/compat-adapter.d.ts +27 -0
  7. package/src/adapters/compat-adapter.js +113 -0
  8. package/src/adapters/compat-adapter.js.map +1 -0
  9. package/src/adapters/custom-element-adapter.d.ts +21 -0
  10. package/src/adapters/custom-element-adapter.js +91 -0
  11. package/src/adapters/custom-element-adapter.js.map +1 -0
  12. package/src/compat/born-inert-scripts.d.ts +30 -0
  13. package/src/compat/born-inert-scripts.js +85 -0
  14. package/src/compat/born-inert-scripts.js.map +1 -0
  15. package/src/compat/document-facade.d.ts +44 -0
  16. package/src/compat/document-facade.js +104 -0
  17. package/src/compat/document-facade.js.map +1 -0
  18. package/src/compat/document-member-classification.d.ts +30 -0
  19. package/src/compat/document-member-classification.js +64 -0
  20. package/src/compat/document-member-classification.js.map +1 -0
  21. package/src/compat/document-overrides.d.ts +44 -0
  22. package/src/compat/document-overrides.js +225 -0
  23. package/src/compat/document-overrides.js.map +1 -0
  24. package/src/compat/document-surface.generated.d.ts +21 -0
  25. package/src/compat/document-surface.generated.js +292 -0
  26. package/src/compat/document-surface.generated.js.map +1 -0
  27. package/src/compat/fragment-boundary.d.ts +14 -0
  28. package/src/compat/fragment-boundary.js +335 -0
  29. package/src/compat/fragment-boundary.js.map +1 -0
  30. package/src/compat/metadata.d.ts +15 -0
  31. package/src/compat/metadata.js +21 -0
  32. package/src/compat/metadata.js.map +1 -0
  33. package/src/compat/navigation-bus.d.ts +29 -0
  34. package/src/compat/navigation-bus.js +92 -0
  35. package/src/compat/navigation-bus.js.map +1 -0
  36. package/src/compat/realm-patches.d.ts +21 -0
  37. package/src/compat/realm-patches.js +573 -0
  38. package/src/compat/realm-patches.js.map +1 -0
  39. package/src/compat/register-worker.d.ts +45 -0
  40. package/src/compat/register-worker.js +57 -0
  41. package/src/compat/register-worker.js.map +1 -0
  42. package/src/compat/script-execution.d.ts +65 -0
  43. package/src/compat/script-execution.js +361 -0
  44. package/src/compat/script-execution.js.map +1 -0
  45. package/src/compat/service-worker.d.ts +22 -0
  46. package/src/compat/service-worker.js +104 -0
  47. package/src/compat/service-worker.js.map +1 -0
  48. package/src/config.d.ts +37 -0
  49. package/src/config.js +26 -0
  50. package/src/config.js.map +1 -0
  51. package/src/context/context-bus.d.ts +67 -0
  52. package/src/context/context-bus.js +117 -0
  53. package/src/context/context-bus.js.map +1 -0
  54. package/src/elements/fragment-slot.d.ts +39 -0
  55. package/src/elements/fragment-slot.js +281 -0
  56. package/src/elements/fragment-slot.js.map +1 -0
  57. package/src/env/create-env.d.ts +31 -0
  58. package/src/env/create-env.js +91 -0
  59. package/src/env/create-env.js.map +1 -0
  60. package/src/env/fragment-env.d.ts +56 -0
  61. package/src/env/fragment-env.js +14 -0
  62. package/src/env/fragment-env.js.map +1 -0
  63. package/src/errors.d.ts +21 -0
  64. package/src/errors.js +21 -0
  65. package/src/errors.js.map +1 -0
  66. package/src/index.d.ts +36 -0
  67. package/src/index.js +40 -0
  68. package/src/index.js.map +1 -0
  69. package/src/protocol.d.ts +38 -0
  70. package/src/protocol.js +45 -0
  71. package/src/protocol.js.map +1 -0
  72. package/src/realm/realm-manager.d.ts +60 -0
  73. package/src/realm/realm-manager.js +296 -0
  74. package/src/realm/realm-manager.js.map +1 -0
  75. package/src/utils/assert.d.ts +7 -0
  76. package/src/utils/assert.js +10 -0
  77. package/src/utils/assert.js.map +1 -0
  78. package/src/utils/selector-helpers.d.ts +10 -0
  79. package/src/utils/selector-helpers.js +13 -0
  80. package/src/utils/selector-helpers.js.map +1 -0
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Library-wide configuration for the Braid client runtime, set via `initBraid(options)`.
3
+ *
4
+ * Note there is no host-isolation mode switch: host purity is an invariant, not a mode.
5
+ * Braid never mutates a host-page global or prototype — in any mode, ever. The compat adapter
6
+ * achieves its interception with fragment-boundary techniques confined to each fragment's own
7
+ * realm and shadow DOM subtree.
8
+ */
9
+ export interface BraidOptions {
10
+ /**
11
+ * Enables development-mode diagnostics: unaudited-API warnings from the compat document
12
+ * facade, boundary-bypass reports, and verbose boot logging. Defaults to false.
13
+ */
14
+ dev?: boolean;
15
+ /**
16
+ * Host navigation adapter. Bound fragments need to know when the host application navigates
17
+ * (e.g. the host router calls `history.pushState`). Braid never patches the host History API,
18
+ * so wire your router's after-navigation hook to the provided `notify` callback:
19
+ *
20
+ * ```ts
21
+ * initBraid({ onHostNavigation: (notify) => router.afterEach(() => notify()) });
22
+ * ```
23
+ *
24
+ * Where the Navigation API is available, host navigations are additionally observed
25
+ * automatically. Back/forward navigations (`popstate`) are always observed natively and need
26
+ * no adapter.
27
+ */
28
+ onHostNavigation?: (notify: () => void) => void;
29
+ }
30
+ interface ResolvedBraidConfig {
31
+ dev: boolean;
32
+ onHostNavigation?: (notify: () => void) => void;
33
+ }
34
+ export declare function setBraidConfig(options?: BraidOptions): void;
35
+ export declare function getBraidConfig(): Readonly<ResolvedBraidConfig>;
36
+ export declare function isDevMode(): boolean;
37
+ export {};
package/src/config.js ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Library-wide configuration for the Braid client runtime, set via `initBraid(options)`.
3
+ *
4
+ * Note there is no host-isolation mode switch: host purity is an invariant, not a mode.
5
+ * Braid never mutates a host-page global or prototype — in any mode, ever. The compat adapter
6
+ * achieves its interception with fragment-boundary techniques confined to each fragment's own
7
+ * realm and shadow DOM subtree.
8
+ */
9
+ const config = {
10
+ dev: false,
11
+ };
12
+ export function setBraidConfig(options = {}) {
13
+ if (options.dev !== undefined) {
14
+ config.dev = options.dev;
15
+ }
16
+ if (options.onHostNavigation) {
17
+ config.onHostNavigation = options.onHostNavigation;
18
+ }
19
+ }
20
+ export function getBraidConfig() {
21
+ return config;
22
+ }
23
+ export function isDevMode() {
24
+ return config.dev;
25
+ }
26
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../libs/core/src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA8BH,MAAM,MAAM,GAAwB;IAClC,GAAG,EAAE,KAAK;CACX,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,gBAAgB,EAAE,CAAC;QAC7B,MAAM,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IACrD,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"}
@@ -0,0 +1,67 @@
1
+ import type { VersionedSchema } from '@braidlabs/skew';
2
+ /**
3
+ * Context bus — host-published shared state, delivered at each subscriber's own contract version.
4
+ *
5
+ * Values are structured-cloned at the boundary: sharing live objects across realms creates
6
+ * cross-realm retention (GC leaks) and accidental coupling, and clone-at-the-boundary keeps fragment
7
+ * instances disposable.
8
+ *
9
+ * **Versioning is what makes this a bus rather than an event emitter.** A page composes fragments
10
+ * built at different times, so the publisher and a subscriber routinely disagree about what a
11
+ * context payload looks like. Register a `@braidlabs/skew` schema for a key and every delivery is
12
+ * projected to the version that subscriber asked for — older subscribers get the older shape, and
13
+ * neither side has to know the other exists.
14
+ *
15
+ * Two decisions worth stating, because both were tempting to get wrong:
16
+ *
17
+ * 1. **Reachability is checked when a fragment subscribes, not when a value is broadcast.**
18
+ * Projecting down needs a `down` on every intervening step, and a chain missing one is a
19
+ * programming error in your own contract rather than data skew. Discovering it mid-broadcast
20
+ * would fail one delivery deep inside a fan-out with the other subscribers already served;
21
+ * refusing the subscription makes it a start-up error with a fragment's name on it.
22
+ * 2. **An unregistered key passes through untouched.** A bus that demanded a schema for every value
23
+ * is a bus nobody adopts incrementally. Untyped contexts are cloned and delivered as they are,
24
+ * exactly as the v0 bus did.
25
+ */
26
+ type ContextListener = (value: unknown) => void;
27
+ export interface ContextSubscribeOptions {
28
+ signal?: AbortSignal;
29
+ /**
30
+ * The contract version this subscriber speaks. Defaults to the registered schema's current
31
+ * version — right for a fragment built from the same source, wrong for every fragment that was
32
+ * not, so a fragment that knows it is behind should say so.
33
+ */
34
+ as?: number;
35
+ /** Names the fragment in any error raised. Falls back to the context key. */
36
+ fragmentId?: string;
37
+ }
38
+ export interface ContextReadOptions {
39
+ as?: number;
40
+ fragmentId?: string;
41
+ }
42
+ export declare const braidContext: {
43
+ /**
44
+ * Declares the contract a context key carries.
45
+ *
46
+ * Registering after values have been published is allowed, and deliberately so: the host learns a
47
+ * fragment's contract when that fragment mounts, which is later than the first broadcast on every
48
+ * real page.
49
+ */
50
+ register<T>(key: string, schema: VersionedSchema<T>): void;
51
+ /** The schema registered for a key, if any. */
52
+ schemaFor(key: string): VersionedSchema<unknown> | undefined;
53
+ /**
54
+ * Publishes a context value to every fragment (and host code) subscribed to the key.
55
+ *
56
+ * **Every subscriber is isolated.** One fragment throwing in its listener — or declaring a version
57
+ * that became unreachable after it subscribed — must not stop delivery to the fragments after it
58
+ * in the set. Without that, a bad deploy of one app presents as a *different* app silently
59
+ * failing to update, which is the least debuggable shape a bug can take on a composed page.
60
+ */
61
+ set(key: string, value: unknown): void;
62
+ get(key: string, options?: ContextReadOptions): unknown;
63
+ subscribe(key: string, listener: ContextListener, options?: ContextSubscribeOptions): () => void;
64
+ /** Exposed for tests. */
65
+ clear(): void;
66
+ };
67
+ export {};
@@ -0,0 +1,117 @@
1
+ import { BraidError } from '../errors.js';
2
+ const values = new Map();
3
+ const subscriptions = new Map();
4
+ const schemas = new Map();
5
+ export const braidContext = {
6
+ /**
7
+ * Declares the contract a context key carries.
8
+ *
9
+ * Registering after values have been published is allowed, and deliberately so: the host learns a
10
+ * fragment's contract when that fragment mounts, which is later than the first broadcast on every
11
+ * real page.
12
+ */
13
+ register(key, schema) {
14
+ schemas.set(key, schema);
15
+ },
16
+ /** The schema registered for a key, if any. */
17
+ schemaFor(key) {
18
+ return schemas.get(key);
19
+ },
20
+ /**
21
+ * Publishes a context value to every fragment (and host code) subscribed to the key.
22
+ *
23
+ * **Every subscriber is isolated.** One fragment throwing in its listener — or declaring a version
24
+ * that became unreachable after it subscribed — must not stop delivery to the fragments after it
25
+ * in the set. Without that, a bad deploy of one app presents as a *different* app silently
26
+ * failing to update, which is the least debuggable shape a bug can take on a composed page.
27
+ */
28
+ set(key, value) {
29
+ values.set(key, value);
30
+ for (const subscription of [...(subscriptions.get(key) ?? [])]) {
31
+ try {
32
+ subscription.listener(project(key, value, subscription.as));
33
+ }
34
+ catch (error) {
35
+ // Reported rather than swallowed: a subscriber that never hears anything again is worth
36
+ // knowing about, and this is the only place that can see it happen.
37
+ console.error(`braid: a "${key}" context subscriber threw; other subscribers were unaffected`, error);
38
+ }
39
+ }
40
+ },
41
+ get(key, options) {
42
+ const value = values.get(key);
43
+ if (value === undefined)
44
+ return undefined;
45
+ // A read at an unreachable version is the same error as a subscription at one, and is raised
46
+ // rather than quietly answered at the publisher's version: handing a fragment a shape it cannot
47
+ // parse is how a context bus produces a stack trace in someone else's code.
48
+ assertReachable(key, options?.as, options?.fragmentId);
49
+ return project(key, value, options?.as);
50
+ },
51
+ subscribe(key, listener, options) {
52
+ assertReachable(key, options?.as, options?.fragmentId);
53
+ let keySubscriptions = subscriptions.get(key);
54
+ if (!keySubscriptions) {
55
+ keySubscriptions = new Set();
56
+ subscriptions.set(key, keySubscriptions);
57
+ }
58
+ const subscription = {
59
+ listener,
60
+ ...(options?.as === undefined ? {} : { as: options.as }),
61
+ };
62
+ keySubscriptions.add(subscription);
63
+ const unsubscribe = () => void keySubscriptions.delete(subscription);
64
+ options?.signal?.addEventListener('abort', unsubscribe, { once: true });
65
+ return unsubscribe;
66
+ },
67
+ /** Exposed for tests. */
68
+ clear() {
69
+ values.clear();
70
+ subscriptions.clear();
71
+ schemas.clear();
72
+ },
73
+ };
74
+ /**
75
+ * Projects a published value to one subscriber's version.
76
+ *
77
+ * `write({ as })` down-migrates and returns the older envelope — the polite move when the reader is
78
+ * known to be older than the publisher. Subscribers receive the payload rather than the envelope:
79
+ * the version they asked for is the version they get, so handing them a wrapper to unwrap would be
80
+ * ceremony without information.
81
+ */
82
+ function project(key, value, as) {
83
+ const schema = schemas.get(key);
84
+ if (!schema)
85
+ return structuredClone(value);
86
+ const envelope = as === undefined || as === schema.version ? schema.write(value) : schema.write(value, { as });
87
+ return structuredClone(envelope.payload);
88
+ }
89
+ /**
90
+ * Refuses a subscriber whose version cannot be reached from the publisher's.
91
+ *
92
+ * Decided from the declared chain rather than by attempting a migration, so the answer does not
93
+ * depend on a value happening to have been published yet — a fragment that mounts before the first
94
+ * broadcast deserves the same error as one that mounts after it.
95
+ */
96
+ function assertReachable(key, as, fragmentId) {
97
+ const schema = schemas.get(key);
98
+ if (!schema || as === undefined || as === schema.version)
99
+ return;
100
+ const raise = (message, fixHint) => {
101
+ throw new BraidError(message, { fragmentId: fragmentId ?? key, stage: 'context-version', fixHint });
102
+ };
103
+ if (as > schema.version) {
104
+ raise(`context "${key}" is published at v${schema.version}, and this subscriber asked for v${as}`, 'the publisher is older than the subscriber — deploy the host, or subscribe at a version it can produce');
105
+ }
106
+ if (as < 1)
107
+ raise(`context "${key}" has no v${as}`, 'contract versions start at 1');
108
+ // Every step between the subscriber's version and the publisher's has to walk back down.
109
+ const missing = schema.steps
110
+ .filter((step) => step.to > as && step.to <= schema.version && !step.down)
111
+ .map((step) => `v${step.to} (${step.description})`);
112
+ if (missing.length > 0) {
113
+ raise(`context "${key}" cannot be projected from v${schema.version} down to v${as}: ` +
114
+ `${missing.join(', ')} ${missing.length === 1 ? 'declares' : 'declare'} no down migration`, `add a down migration to the step that introduced ${missing[0]}, or subscribe at v${schema.version}`);
115
+ }
116
+ }
117
+ //# sourceMappingURL=context-bus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-bus.js","sourceRoot":"","sources":["../../../../../libs/core/src/context/context-bus.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAmD1C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAmB,CAAC;AAC1C,MAAM,aAAa,GAAG,IAAI,GAAG,EAA6B,CAAC;AAC3D,MAAM,OAAO,GAAG,IAAI,GAAG,EAAoC,CAAC;AAE5D,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAI,GAAW,EAAE,MAA0B;QACjD,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,MAAkC,CAAC,CAAC;IACvD,CAAC;IAED,+CAA+C;IAC/C,SAAS,CAAC,GAAW;QACnB,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAED;;;;;;;OAOG;IACH,GAAG,CAAC,GAAW,EAAE,KAAc;QAC7B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAEvB,KAAK,MAAM,YAAY,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;YAC/D,IAAI,CAAC;gBACH,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC;YAC9D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,wFAAwF;gBACxF,oEAAoE;gBACpE,OAAO,CAAC,KAAK,CAAC,aAAa,GAAG,+DAA+D,EAAE,KAAK,CAAC,CAAC;YACxG,CAAC;QACH,CAAC;IACH,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,OAA4B;QAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAE1C,6FAA6F;QAC7F,gGAAgG;QAChG,4EAA4E;QAC5E,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;QACvD,OAAO,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,SAAS,CAAC,GAAW,EAAE,QAAyB,EAAE,OAAiC;QACjF,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;QAEvD,IAAI,gBAAgB,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,gBAAgB,GAAG,IAAI,GAAG,EAAE,CAAC;YAC7B,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;QAC3C,CAAC;QACD,MAAM,YAAY,GAAiB;YACjC,QAAQ;YACR,GAAG,CAAC,OAAO,EAAE,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;SACzD,CAAC;QACF,gBAAgB,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAEnC,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC,KAAK,gBAAgB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QACrE,OAAO,EAAE,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACxE,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,yBAAyB;IACzB,KAAK;QACH,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,aAAa,CAAC,KAAK,EAAE,CAAC;QACtB,OAAO,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;CACF,CAAC;AAEF;;;;;;;GAOG;AACH,SAAS,OAAO,CAAC,GAAW,EAAE,KAAc,EAAE,EAAsB;IAClE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,CAAC,MAAM;QAAE,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;IAE3C,MAAM,QAAQ,GAAG,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAC/G,OAAO,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;GAMG;AACH,SAAS,eAAe,CAAC,GAAW,EAAE,EAAsB,EAAE,UAA8B;IAC1F,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,CAAC,MAAM,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,MAAM,CAAC,OAAO;QAAE,OAAO;IAEjE,MAAM,KAAK,GAAG,CAAC,OAAe,EAAE,OAAe,EAAS,EAAE;QACxD,MAAM,IAAI,UAAU,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,UAAU,IAAI,GAAG,EAAE,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,CAAC,CAAC;IACtG,CAAC,CAAC;IAEF,IAAI,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;QACxB,KAAK,CACH,YAAY,GAAG,sBAAsB,MAAM,CAAC,OAAO,oCAAoC,EAAE,EAAE,EAC3F,wGAAwG,CACzG,CAAC;IACJ,CAAC;IAED,IAAI,EAAE,GAAG,CAAC;QAAE,KAAK,CAAC,YAAY,GAAG,aAAa,EAAE,EAAE,EAAE,8BAA8B,CAAC,CAAC;IAEpF,yFAAyF;IACzF,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK;SACzB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,IAAI,CAAC,EAAE,IAAI,MAAM,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;SACzE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;IAEtD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,CACH,YAAY,GAAG,+BAA+B,MAAM,CAAC,OAAO,aAAa,EAAE,IAAI;YAC7E,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,oBAAoB,EAC5F,oDAAoD,OAAO,CAAC,CAAC,CAAC,sBAAsB,MAAM,CAAC,OAAO,EAAE,CACrG,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,39 @@
1
+ export type FragmentSlotState = 'idle' | 'loading' | 'ready' | 'error';
2
+ /**
3
+ * Finds server-rendered fragment content the gateway pierced into a slot.
4
+ *
5
+ * Deliberately a direct-children scan rather than `querySelector(':scope > braid-document')`:
6
+ * `:scope` has no element to match against on a ShadowRoot, so that selector silently never
7
+ * matches. The failure mode is invisible from inside the page — the slot just quietly re-fetches
8
+ * and replaces identical content — so it is worth a named helper and a test.
9
+ */
10
+ export declare function findPiercedContentRoot(shadowRoot: ShadowRoot | null): HTMLElement | null;
11
+ /**
12
+ * `<fragment-slot>` — the custom element a host renders to mount a fragment.
13
+ *
14
+ * ```html
15
+ * <fragment-slot name="checkout"></fragment-slot>
16
+ * ```
17
+ *
18
+ * - `name` — the fragment id in the gateway registry (required).
19
+ * - `src` — optional route url for the fragment. When absent, the fragment is *bound*: it
20
+ * follows the host page's location and participates in host navigation.
21
+ * - `props` — JSON attribute (or the `props` property) passed to the fragment instance.
22
+ *
23
+ * Events: `braid:ready`, `braid:error` (detail includes stage + fix hint), `braid:event`
24
+ * (fragment → host).
25
+ *
26
+ * The slot uses only its own shadow root and its own elements — the host page is never patched.
27
+ */
28
+ export declare class FragmentSlot extends HTMLElement {
29
+ #private;
30
+ static get observedAttributes(): string[];
31
+ get state(): FragmentSlotState;
32
+ get name(): string;
33
+ get props(): Record<string, unknown>;
34
+ set props(value: Record<string, unknown>);
35
+ attributeChangedCallback(attribute: string, oldValue: string | null, newValue: string | null): void;
36
+ connectedCallback(): void;
37
+ disconnectedCallback(): void;
38
+ reload(): Promise<void>;
39
+ }
@@ -0,0 +1,281 @@
1
+ import { BraidError } from '../errors.js';
2
+ import { braidDocumentUrl, BRAID_FRAGMENT_ID_HEADER } from '../protocol.js';
3
+ import { createRealm } from '../realm/realm-manager.js';
4
+ import { resolveAdapter } from '../adapters/adapter.js';
5
+ import { createFragmentEnv } from '../env/create-env.js';
6
+ import { isDevMode } from '../config.js';
7
+ /**
8
+ * Finds server-rendered fragment content the gateway pierced into a slot.
9
+ *
10
+ * Deliberately a direct-children scan rather than `querySelector(':scope > braid-document')`:
11
+ * `:scope` has no element to match against on a ShadowRoot, so that selector silently never
12
+ * matches. The failure mode is invisible from inside the page — the slot just quietly re-fetches
13
+ * and replaces identical content — so it is worth a named helper and a test.
14
+ */
15
+ export function findPiercedContentRoot(shadowRoot) {
16
+ if (!shadowRoot)
17
+ return null;
18
+ for (const child of shadowRoot.children) {
19
+ if (child.tagName === 'BRAID-DOCUMENT')
20
+ return child;
21
+ }
22
+ return null;
23
+ }
24
+ /**
25
+ * `<fragment-slot>` — the custom element a host renders to mount a fragment.
26
+ *
27
+ * ```html
28
+ * <fragment-slot name="checkout"></fragment-slot>
29
+ * ```
30
+ *
31
+ * - `name` — the fragment id in the gateway registry (required).
32
+ * - `src` — optional route url for the fragment. When absent, the fragment is *bound*: it
33
+ * follows the host page's location and participates in host navigation.
34
+ * - `props` — JSON attribute (or the `props` property) passed to the fragment instance.
35
+ *
36
+ * Events: `braid:ready`, `braid:error` (detail includes stage + fix hint), `braid:event`
37
+ * (fragment → host).
38
+ *
39
+ * The slot uses only its own shadow root and its own elements — the host page is never patched.
40
+ */
41
+ export class FragmentSlot extends HTMLElement {
42
+ static get observedAttributes() {
43
+ return ['name', 'src', 'props'];
44
+ }
45
+ #state = 'idle';
46
+ #props = {};
47
+ #propsListeners = new Set();
48
+ #abortController;
49
+ #booted = false;
50
+ #bootScheduled = false;
51
+ /** Set by reload(): a reload must re-fetch, never re-adopt the pierced content. */
52
+ #forceFetch = false;
53
+ get state() {
54
+ return this.#state;
55
+ }
56
+ get name() {
57
+ return this.getAttribute('name') ?? '';
58
+ }
59
+ get props() {
60
+ return this.#props;
61
+ }
62
+ set props(value) {
63
+ this.#props = structuredClone(value ?? {});
64
+ this.#propsListeners.forEach((listener) => listener(this.#props));
65
+ }
66
+ attributeChangedCallback(attribute, oldValue, newValue) {
67
+ if (attribute === 'props') {
68
+ try {
69
+ this.props = newValue ? JSON.parse(newValue) : {};
70
+ }
71
+ catch (error) {
72
+ console.warn(`[braid:${this.name}] ignoring unparsable props attribute`, error);
73
+ }
74
+ }
75
+ else if (attribute === 'name' && oldValue !== newValue) {
76
+ if (this.isConnected && this.#booted) {
77
+ void this.reload();
78
+ }
79
+ else if (this.isConnected && !this.#booted && !this.#bootScheduled) {
80
+ this.#bootScheduled = true;
81
+ queueMicrotask(() => {
82
+ this.#bootScheduled = false;
83
+ if (this.isConnected && !this.#booted) {
84
+ this.#booted = true;
85
+ void this.#boot();
86
+ }
87
+ });
88
+ }
89
+ }
90
+ }
91
+ connectedCallback() {
92
+ if (this.#booted || this.#bootScheduled) {
93
+ return;
94
+ }
95
+ this.#bootScheduled = true;
96
+ queueMicrotask(() => {
97
+ this.#bootScheduled = false;
98
+ if (!this.isConnected || this.#booted) {
99
+ return;
100
+ }
101
+ this.#booted = true;
102
+ void this.#boot();
103
+ });
104
+ }
105
+ disconnectedCallback() {
106
+ this.#bootScheduled = false;
107
+ this.#teardown();
108
+ this.#booted = false;
109
+ }
110
+ async reload() {
111
+ this.#teardown();
112
+ this.#forceFetch = true;
113
+ await this.#boot();
114
+ }
115
+ #teardown() {
116
+ this.#abortController?.abort();
117
+ this.#abortController = undefined;
118
+ this.shadowRoot?.replaceChildren();
119
+ this.#state = 'idle';
120
+ }
121
+ async #boot() {
122
+ const fragmentId = this.name;
123
+ try {
124
+ if (!fragmentId) {
125
+ throw new BraidError('the <fragment-slot> element is missing its name attribute', {
126
+ fragmentId: '<unnamed>',
127
+ stage: 'slot-config',
128
+ fixHint: '<fragment-slot name="..."> must name a fragment registered in the gateway',
129
+ });
130
+ }
131
+ if (this.getAttribute('trust') === 'untrusted') {
132
+ throw new BraidError('the untrusted tier is not available in this build', {
133
+ fragmentId,
134
+ stage: 'slot-config',
135
+ fixHint: 'this build of @braidlabs/core ships the trusted compat tier only',
136
+ });
137
+ }
138
+ this.#state = 'loading';
139
+ const abortController = new AbortController();
140
+ this.#abortController = abortController;
141
+ const signal = abortController.signal;
142
+ // src present → standalone fragment pinned to that route; absent → bound to host location
143
+ const src = this.getAttribute('src');
144
+ const bound = !src;
145
+ const routeUrl = src ?? location.pathname + location.search;
146
+ const routeSrcUrl = new URL(routeUrl, document.baseURI);
147
+ /**
148
+ * Pierced fragments arrive with their content already in the DOM: the gateway wrote a
149
+ * declarative shadow root into this element, and the browser parsed it at the same time
150
+ * as the rest of the page. Adopting it is strictly better than fetching — the
151
+ * content is already painted, and re-fetching it would replace live DOM with identical
152
+ * DOM. `#forceFetch` is set by reload(), which must go back to the network.
153
+ */
154
+ const piercedContentRoot = this.#forceFetch ? null : findPiercedContentRoot(this.shadowRoot);
155
+ let shadowRoot;
156
+ let contentRoot;
157
+ if (piercedContentRoot) {
158
+ shadowRoot = this.shadowRoot;
159
+ contentRoot = piercedContentRoot;
160
+ }
161
+ else {
162
+ shadowRoot = this.shadowRoot ?? this.attachShadow({ mode: 'open' });
163
+ const styleSheet = document.createElement('style');
164
+ styleSheet.textContent =
165
+ ':host, braid-document, braid-html, braid-body { display: block; } braid-head { display: none; }';
166
+ contentRoot = document.createElement('braid-document');
167
+ shadowRoot.replaceChildren(styleSheet, contentRoot);
168
+ }
169
+ /**
170
+ * Boot the realm and fetch the fragment's document at the same time, unless it was
171
+ * already pierced in. Both go through the gateway's namespaces, addressed by id.
172
+ *
173
+ * Which adapter a fragment uses is only known once the realm stub has loaded, so the
174
+ * document request is started optimistically and its failure is held rather than thrown:
175
+ * an adapter that builds its own UI from an entry module (a lone custom element, say) has
176
+ * no document to fetch, and must not be reported as broken for not serving one.
177
+ */
178
+ const [htmlResult, realm] = await Promise.all([
179
+ piercedContentRoot
180
+ ? Promise.resolve({ ok: true, html: null })
181
+ : this.#fetchFragmentHtml(fragmentId, routeSrcUrl, signal).then((html) => ({ ok: true, html }), (error) => ({ ok: false, error })),
182
+ createRealm('compat-http', { fragmentId, routeUrl, bound, signal }),
183
+ ]);
184
+ // The gateway stamps the manifest-declared adapter onto the realm stub; unknown adapters
185
+ // fail as a named error, and an undeclared adapter resolves to the default (compat).
186
+ const adapter = resolveAdapter(realm.manifestAdapter, fragmentId);
187
+ if (!htmlResult.ok && adapter.needsDocument !== false)
188
+ throw htmlResult.error;
189
+ const html = htmlResult.ok ? htmlResult.html : null;
190
+ const env = createFragmentEnv({
191
+ contentRoot,
192
+ shadowRoot,
193
+ routeUrl,
194
+ getProps: () => this.#props,
195
+ onPropsChanged: (listener) => {
196
+ this.#propsListeners.add(listener);
197
+ const unsubscribe = () => this.#propsListeners.delete(listener);
198
+ signal.addEventListener('abort', unsubscribe, { once: true });
199
+ return unsubscribe;
200
+ },
201
+ emit: (type, detail) => {
202
+ this.dispatchEvent(new CustomEvent('braid:event', { detail: { type, detail }, bubbles: true }));
203
+ },
204
+ signal,
205
+ });
206
+ await adapter.boot({
207
+ fragmentId,
208
+ shadowRoot,
209
+ contentRoot,
210
+ realm,
211
+ html,
212
+ pierced: Boolean(piercedContentRoot),
213
+ routeUrl,
214
+ bound,
215
+ env,
216
+ signal,
217
+ });
218
+ if (signal.aborted)
219
+ return;
220
+ this.#state = 'ready';
221
+ this.dispatchEvent(new CustomEvent('braid:ready', { detail: { fragmentId } }));
222
+ if (isDevMode()) {
223
+ console.debug(`[braid:${fragmentId}] ready`, { slot: this });
224
+ }
225
+ }
226
+ catch (error) {
227
+ if (this.#abortController?.signal.aborted)
228
+ return;
229
+ this.#state = 'error';
230
+ const braidError = error instanceof BraidError
231
+ ? error
232
+ : new BraidError(error instanceof Error ? error.message : String(error), {
233
+ fragmentId,
234
+ stage: 'adapter-mount',
235
+ cause: error,
236
+ });
237
+ console.error(braidError);
238
+ this.dispatchEvent(new CustomEvent('braid:error', {
239
+ detail: {
240
+ fragmentId: braidError.fragmentId,
241
+ stage: braidError.stage,
242
+ fixHint: braidError.fixHint,
243
+ error: braidError,
244
+ },
245
+ }));
246
+ }
247
+ }
248
+ async #fetchFragmentHtml(fragmentId, routeSrcUrl, signal) {
249
+ // the document namespace: the gateway prepares this exactly as it prepares pierced content
250
+ const documentUrl = braidDocumentUrl(fragmentId, routeSrcUrl.pathname, routeSrcUrl.search);
251
+ let response;
252
+ try {
253
+ response = await fetch(documentUrl, {
254
+ headers: {
255
+ accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
256
+ [BRAID_FRAGMENT_ID_HEADER]: fragmentId,
257
+ },
258
+ signal,
259
+ });
260
+ }
261
+ catch (error) {
262
+ throw new BraidError(`fetching the fragment's html from "${documentUrl}" failed`, {
263
+ fragmentId,
264
+ stage: 'fragment-fetch',
265
+ cause: error,
266
+ fixHint: 'ensure the braid gateway is mounted in front of this app and reachable from the browser',
267
+ });
268
+ }
269
+ if (!response.ok) {
270
+ throw new BraidError(`the gateway responded with HTTP ${response.status} for "${documentUrl}"`, {
271
+ fragmentId,
272
+ stage: 'fragment-fetch',
273
+ fixHint: response.status === 404
274
+ ? `register a manifest for fragment id "${fragmentId}" in the gateway registry`
275
+ : `check the gateway logs for fragment id "${fragmentId}"`,
276
+ });
277
+ }
278
+ return response.text();
279
+ }
280
+ }
281
+ //# sourceMappingURL=fragment-slot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragment-slot.js","sourceRoot":"","sources":["../../../../../libs/core/src/elements/fragment-slot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { FragmentEnv } from './fragment-env.js';
2
+ export interface CreateEnvOptions {
3
+ contentRoot: HTMLElement;
4
+ shadowRoot: ShadowRoot;
5
+ routeUrl: string;
6
+ /** Reads the slot's current props. */
7
+ getProps(): Readonly<Record<string, unknown>>;
8
+ /** Registers a props-change listener; returns an unsubscribe function. */
9
+ onPropsChanged(listener: (props: Readonly<Record<string, unknown>>) => void): () => void;
10
+ /** Fragment → host event dispatch (surfaced as `braid:event` on the slot element). */
11
+ emit(type: string, detail?: unknown): void;
12
+ signal: AbortSignal;
13
+ /** Names this fragment in context-version errors. */
14
+ fragmentId?: string;
15
+ /**
16
+ * The contract version this fragment speaks for each context key, from its manifest.
17
+ *
18
+ * A fragment built months ago reads a context published today, so it declares what it can parse
19
+ * and the bus projects each delivery down to it. Absent, a fragment is assumed current — right for
20
+ * one built from the same source, and the reason a fragment that knows it is behind must say so.
21
+ */
22
+ contextVersions?: Readonly<Record<string, number>>;
23
+ }
24
+ /**
25
+ * Builds the FragmentEnv for a fragment instance.
26
+ *
27
+ * Compat fragments never consume the env — the compat adapter installs the full illusion
28
+ * instead — but the runtime constructs one uniformly so the adapter interface is the same
29
+ * for every adapter, and contract adapters can land later without touching the slot.
30
+ */
31
+ export declare function createFragmentEnv(options: CreateEnvOptions): FragmentEnv;