@braidlabs/core 0.1.1 → 0.3.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.
Files changed (77) hide show
  1. package/package.json +2 -2
  2. package/src/adapters/adapter.d.ts +13 -0
  3. package/src/adapters/adapter.js.map +1 -1
  4. package/src/adapters/compat-adapter.d.ts +7 -0
  5. package/src/adapters/compat-adapter.js +24 -16
  6. package/src/adapters/compat-adapter.js.map +1 -1
  7. package/src/adapters/contract-adapter.d.ts +21 -0
  8. package/src/adapters/contract-adapter.js +71 -0
  9. package/src/adapters/contract-adapter.js.map +1 -0
  10. package/src/config.d.ts +29 -0
  11. package/src/config.js +16 -0
  12. package/src/config.js.map +1 -1
  13. package/src/context/context-bus.d.ts +63 -3
  14. package/src/context/context-bus.js +147 -61
  15. package/src/context/context-bus.js.map +1 -1
  16. package/src/elements/fragment-slot.d.ts +114 -1
  17. package/src/elements/fragment-slot.js +743 -60
  18. package/src/elements/fragment-slot.js.map +1 -1
  19. package/src/env/create-env.d.ts +20 -8
  20. package/src/env/create-env.js +48 -16
  21. package/src/env/create-env.js.map +1 -1
  22. package/src/env/define-fragment.d.ts +43 -0
  23. package/src/env/define-fragment.js +23 -0
  24. package/src/env/define-fragment.js.map +1 -0
  25. package/src/env/fragment-env.d.ts +20 -1
  26. package/src/env/fragment-env.js +0 -12
  27. package/src/env/fragment-env.js.map +1 -1
  28. package/src/errors.d.ts +1 -1
  29. package/src/errors.js.map +1 -1
  30. package/src/index.d.ts +14 -1
  31. package/src/index.js +7 -1
  32. package/src/index.js.map +1 -1
  33. package/src/protocol.d.ts +20 -4
  34. package/src/protocol.js +23 -7
  35. package/src/protocol.js.map +1 -1
  36. package/src/realm/realm-manager.d.ts +19 -2
  37. package/src/realm/realm-manager.js +32 -7
  38. package/src/realm/realm-manager.js.map +1 -1
  39. package/src/realm/sandbox-realm.d.ts +95 -0
  40. package/src/realm/sandbox-realm.js +194 -0
  41. package/src/realm/sandbox-realm.js.map +1 -0
  42. package/src/weave/capabilities.d.ts +71 -0
  43. package/src/weave/capabilities.js +61 -0
  44. package/src/weave/capabilities.js.map +1 -0
  45. package/src/weave/channel.d.ts +63 -0
  46. package/src/weave/channel.js +189 -0
  47. package/src/weave/channel.js.map +1 -0
  48. package/src/weave/closing.d.ts +99 -0
  49. package/src/weave/closing.js +130 -0
  50. package/src/weave/closing.js.map +1 -0
  51. package/src/weave/context-bridge.d.ts +81 -0
  52. package/src/weave/context-bridge.js +145 -0
  53. package/src/weave/context-bridge.js.map +1 -0
  54. package/src/weave/contract.d.ts +89 -0
  55. package/src/weave/contract.js +143 -0
  56. package/src/weave/contract.js.map +1 -0
  57. package/src/weave/envelope.d.ts +80 -0
  58. package/src/weave/envelope.js +49 -0
  59. package/src/weave/envelope.js.map +1 -0
  60. package/src/weave/guest.d.ts +59 -0
  61. package/src/weave/guest.js +120 -0
  62. package/src/weave/guest.js.map +1 -0
  63. package/src/weave/handshake.d.ts +124 -0
  64. package/src/weave/handshake.js +109 -0
  65. package/src/weave/handshake.js.map +1 -0
  66. package/src/weave/liveness.d.ts +90 -0
  67. package/src/weave/liveness.js +189 -0
  68. package/src/weave/liveness.js.map +1 -0
  69. package/src/weave/messages.d.ts +27 -0
  70. package/src/weave/messages.js +13 -0
  71. package/src/weave/messages.js.map +1 -0
  72. package/src/weave/port-backing.d.ts +19 -0
  73. package/src/weave/port-backing.js +54 -0
  74. package/src/weave/port-backing.js.map +1 -0
  75. package/src/weave/same-realm-backing.d.ts +21 -0
  76. package/src/weave/same-realm-backing.js +44 -0
  77. package/src/weave/same-realm-backing.js.map +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-bridge.js","sourceRoot":"","sources":["../../../../../libs/core/src/weave/context-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ import type { VersionedSchema } from '@braidlabs/skew';
2
+ /**
3
+ * Contract negotiation at connect time.
4
+ *
5
+ * Skew is the strongest thing Braid has, and it answers the *second* question about version
6
+ * disagreement: given a mismatch, what can be salvaged? This module asks the first one — should
7
+ * these two have been connected at all — and it exists because Braid could previously answer only
8
+ * by failing later, inside whichever interaction happened to touch the missing term.
9
+ *
10
+ * Three outcomes, and the third is the one only Braid can offer:
11
+ *
12
+ * 1. **compatible** — terms agree; open.
13
+ * 2. **incompatible** — refuse at mount, naming both versions and the failing term. Failing here is
14
+ * strictly better than failing at first interaction: an empty slot with a fallback beats a
15
+ * rendered UI that throws when someone clicks it.
16
+ * 3. **bridged** — the terms disagree but a registered Skew migration spans the gap. Open, and
17
+ * report what will be derived or discarded. Detecting skew and reconciling it are not the same
18
+ * capability, and this is where the difference shows up.
19
+ */
20
+ export interface ContractProvisions {
21
+ events?: string[];
22
+ actions?: string[];
23
+ }
24
+ export interface ContractRequirements {
25
+ /** Range the fragment requires of the host's contract version. See `satisfiesRange`. */
26
+ host?: string;
27
+ /**
28
+ * Skew chain version per context key, not a semver range.
29
+ *
30
+ * Deliberately the same integers `contextVersions` already uses rather than a parallel
31
+ * string-range vocabulary: a second way to say "which shape of `cart` do you speak" would need
32
+ * its own mapping onto the migration chain, and the two would disagree the first time anyone
33
+ * edited one of them.
34
+ */
35
+ context?: Record<string, number>;
36
+ }
37
+ export interface FragmentContract {
38
+ version: string;
39
+ requires?: ContractRequirements;
40
+ provides?: ContractProvisions;
41
+ }
42
+ export interface HostContract {
43
+ version: string;
44
+ provides?: ContractProvisions;
45
+ }
46
+ /** One context key the fragment will receive at a version other than the published one. */
47
+ export interface ContextBridge {
48
+ key: string;
49
+ /** The version the host publishes. */
50
+ from: number;
51
+ /** The version the fragment declared it speaks. */
52
+ to: number;
53
+ /** Fields the projection discards on the way down. Empty when the bridge is lossless. */
54
+ discards: string[];
55
+ }
56
+ export type NegotiationOutcome = 'compatible' | 'bridged' | 'incompatible';
57
+ export interface Negotiation {
58
+ outcome: NegotiationOutcome;
59
+ bridges: ContextBridge[];
60
+ /** Set when incompatible: what disagreed, in terms a developer can act on. */
61
+ reason?: string;
62
+ fixHint?: string;
63
+ }
64
+ export interface NegotiateOptions {
65
+ host: HostContract | undefined;
66
+ fragment: FragmentContract | undefined;
67
+ /** Resolves a context key's registered schema. Injected — see `ContextBusLike`. */
68
+ schemaFor(key: string): VersionedSchema<unknown> | undefined;
69
+ }
70
+ /**
71
+ * Compares what a fragment requires against what the host offers.
72
+ *
73
+ * A fragment that declares no contract is compatible with everything, and must stay that way:
74
+ * compat mode's whole promise is that being composed requires no change to the app, and a
75
+ * negotiation that refused undeclared fragments would revoke it.
76
+ */
77
+ export declare function negotiateContract(options: NegotiateOptions): Negotiation;
78
+ /**
79
+ * A deliberately narrow subset of semver range syntax.
80
+ *
81
+ * `semver` is not a dependency of `@braidlabs/core` and should not become one — it is a browser
82
+ * runtime bundle, and the ranges that appear in a fragment manifest are the simple ones. The subset
83
+ * is documented, and anything outside it returns `'unparseable'` rather than a guess, so a range
84
+ * this build cannot read fails loudly at mount instead of quietly never being enforced.
85
+ *
86
+ * Supported: `*`, `1.x` / `1.*`, `1.2.3`, `>=`, `>`, `<=`, `<`, `^`, `~`.
87
+ * Not supported: hyphen ranges, `||` unions, pre-release precedence.
88
+ */
89
+ export declare function satisfiesRange(version: string, range: string): boolean | 'unparseable';
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Compares what a fragment requires against what the host offers.
3
+ *
4
+ * A fragment that declares no contract is compatible with everything, and must stay that way:
5
+ * compat mode's whole promise is that being composed requires no change to the app, and a
6
+ * negotiation that refused undeclared fragments would revoke it.
7
+ */
8
+ export function negotiateContract(options) {
9
+ const { host, fragment, schemaFor } = options;
10
+ if (!fragment)
11
+ return { outcome: 'compatible', bridges: [] };
12
+ const requiredHost = fragment.requires?.host;
13
+ if (requiredHost !== undefined) {
14
+ if (!host) {
15
+ return {
16
+ outcome: 'incompatible',
17
+ bridges: [],
18
+ reason: `the fragment requires a host contract matching "${requiredHost}", and this host declares none`,
19
+ fixHint: 'declare the host contract: initBraid({ contract: { version: "1.0.0" } })',
20
+ };
21
+ }
22
+ const satisfied = satisfiesRange(host.version, requiredHost);
23
+ if (satisfied === 'unparseable') {
24
+ // Refused rather than waved through. A range nobody can read is a requirement nobody is
25
+ // checking, and the failure mode of silently passing it is a fragment that mounts against a
26
+ // host it was never meant to run on.
27
+ return {
28
+ outcome: 'incompatible',
29
+ bridges: [],
30
+ reason: `the fragment requires host "${requiredHost}", which is not a range this build understands`,
31
+ fixHint: `use one of: "1.2.3", ">=1.2.3", ">1.2.3", "<=1.2.3", "<1.2.3", "^1.2.3", "~1.2.3", "1.x", "*"`,
32
+ };
33
+ }
34
+ if (!satisfied) {
35
+ return {
36
+ outcome: 'incompatible',
37
+ bridges: [],
38
+ reason: `the fragment requires host "${requiredHost}", and this host is "${host.version}"`,
39
+ fixHint: 'deploy a host that satisfies the range, or relax the fragment’s requires.host',
40
+ };
41
+ }
42
+ }
43
+ const bridges = [];
44
+ for (const [key, declared] of Object.entries(fragment.requires?.context ?? {})) {
45
+ const schema = schemaFor(key);
46
+ // No schema registered means no projection happens and none is needed — the value crosses as
47
+ // published, exactly as an untyped context always has.
48
+ if (!schema || declared === schema.version)
49
+ continue;
50
+ if (declared > schema.version) {
51
+ return {
52
+ outcome: 'incompatible',
53
+ bridges: [],
54
+ reason: `the fragment reads context "${key}" at v${declared}, and this host publishes v${schema.version}`,
55
+ fixHint: 'the fragment is newer than the host — deploy the host, or pin the fragment to a version it can produce',
56
+ };
57
+ }
58
+ const spanned = schema.steps.filter((step) => step.to > declared && step.to <= schema.version);
59
+ const missing = spanned.filter((step) => !step.down);
60
+ if (missing.length > 0) {
61
+ return {
62
+ outcome: 'incompatible',
63
+ bridges: [],
64
+ reason: `context "${key}" cannot be bridged from v${schema.version} down to v${declared}: ` +
65
+ `${missing.map((step) => `v${step.to} (${step.description})`).join(', ')} ` +
66
+ `${missing.length === 1 ? 'declares' : 'declare'} no down migration`,
67
+ fixHint: `add a down migration to the step that introduced v${missing[0]?.to}`,
68
+ };
69
+ }
70
+ bridges.push({
71
+ key,
72
+ from: schema.version,
73
+ to: declared,
74
+ // What the fragment will not see. Reported rather than merely permitted: a bridge that
75
+ // silently drops a field is how a fragment ends up rendering a confidently wrong screen.
76
+ discards: [...new Set(spanned.flatMap((step) => step.lossy ?? []))],
77
+ });
78
+ }
79
+ return { outcome: bridges.length > 0 ? 'bridged' : 'compatible', bridges };
80
+ }
81
+ /**
82
+ * A deliberately narrow subset of semver range syntax.
83
+ *
84
+ * `semver` is not a dependency of `@braidlabs/core` and should not become one — it is a browser
85
+ * runtime bundle, and the ranges that appear in a fragment manifest are the simple ones. The subset
86
+ * is documented, and anything outside it returns `'unparseable'` rather than a guess, so a range
87
+ * this build cannot read fails loudly at mount instead of quietly never being enforced.
88
+ *
89
+ * Supported: `*`, `1.x` / `1.*`, `1.2.3`, `>=`, `>`, `<=`, `<`, `^`, `~`.
90
+ * Not supported: hyphen ranges, `||` unions, pre-release precedence.
91
+ */
92
+ export function satisfiesRange(version, range) {
93
+ const trimmed = range.trim();
94
+ if (trimmed === '*' || trimmed === '')
95
+ return true;
96
+ const target = parseVersion(version);
97
+ if (!target)
98
+ return 'unparseable';
99
+ const wildcard = /^(\d+)\.(x|\*)$/.exec(trimmed);
100
+ if (wildcard)
101
+ return target[0] === Number(wildcard[1]);
102
+ const operatorMatch = /^(>=|<=|>|<|\^|~)?\s*(\d+\.\d+\.\d+)$/.exec(trimmed);
103
+ if (!operatorMatch)
104
+ return 'unparseable';
105
+ const bound = parseVersion(operatorMatch[2]);
106
+ if (!bound)
107
+ return 'unparseable';
108
+ const order = compareVersions(target, bound);
109
+ switch (operatorMatch[1]) {
110
+ case undefined:
111
+ return order === 0;
112
+ case '>=':
113
+ return order >= 0;
114
+ case '>':
115
+ return order > 0;
116
+ case '<=':
117
+ return order <= 0;
118
+ case '<':
119
+ return order < 0;
120
+ case '^':
121
+ // Caret: same major, and not older. Major 0 is treated as every minor being breaking, which
122
+ // is the convention pre-1.0 packages actually rely on.
123
+ return order >= 0 && target[0] === bound[0] && (bound[0] !== 0 || target[1] === bound[1]);
124
+ case '~':
125
+ return order >= 0 && target[0] === bound[0] && target[1] === bound[1];
126
+ default:
127
+ return 'unparseable';
128
+ }
129
+ }
130
+ function parseVersion(value) {
131
+ const match = /^(\d+)\.(\d+)\.(\d+)/.exec(value.trim());
132
+ if (!match)
133
+ return undefined;
134
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
135
+ }
136
+ function compareVersions(a, b) {
137
+ for (let i = 0; i < 3; i++) {
138
+ if (a[i] !== b[i])
139
+ return a[i] < b[i] ? -1 : 1;
140
+ }
141
+ return 0;
142
+ }
143
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../../../../../libs/core/src/weave/contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The Weave envelope — the unit that crosses a fragment boundary.
3
+ *
4
+ * Braid already had a *bus*: `braidContext` publishes with structured cloning, per-subscriber
5
+ * Skew projection and subscriber isolation. What it did not have was a **wire**. A fragment
6
+ * reached the bus because the host built `env.context` as closures over `braidContext` and handed
7
+ * that object into the realm — a synchronous cross-realm call on a shared object graph. That works
8
+ * for a same-origin realm and for nothing else: a closure cannot be handed to another origin, which
9
+ * is the real reason the untrusted tier throws rather than degrades.
10
+ *
11
+ * This module is the addressing layer that was missing underneath. The bus's semantics are
12
+ * unchanged and are not re-implemented here — they remain the specification this format serves.
13
+ */
14
+ /**
15
+ * Version of the Weave envelope format.
16
+ *
17
+ * Deliberately distinct from `BRAID_PROTOCOL_VERSION`, which versions the *client ↔ gateway*
18
+ * composition protocol. The two move for unrelated reasons: a change to how the gateway namespaces
19
+ * fragment assets has nothing to do with how a mounted fragment talks to its host, and a single
20
+ * number covering both would force one side to break for the other's benefit.
21
+ */
22
+ export declare const WEAVE_VERSION = 1;
23
+ /** A failure delivered as a reply, rather than thrown into a realm that cannot catch it. */
24
+ export interface WeaveErrorPayload {
25
+ message: string;
26
+ /** Mirrors `BraidErrorStage` where the failure had one; absent for transport-level faults. */
27
+ stage?: string;
28
+ fixHint?: string;
29
+ }
30
+ export interface Envelope {
31
+ v: typeof WEAVE_VERSION;
32
+ /** Unique per message. Correlates a reply to its request; also useful in a trace. */
33
+ id: string;
34
+ /**
35
+ * Set only on a reply, to the `id` being answered.
36
+ *
37
+ * A reply is not a message type — it is any message answering another. Keeping that in a field
38
+ * rather than in `type` means a handler for `ctx/get` never has to know whether the value it
39
+ * returns will be delivered as a reply or dropped; the channel decides from `reply` below.
40
+ */
41
+ re?: string;
42
+ /** Set on a request to mean "a reply to this id is expected". */
43
+ reply?: true;
44
+ type: string;
45
+ fragmentId: string;
46
+ /**
47
+ * Document-instance id: unique to one boot of one fragment, surviving nothing — not a reload,
48
+ * not a re-mount of the same fragment into the same slot.
49
+ *
50
+ * Cheap to carry now and expensive to add later, because it is the only thing that distinguishes
51
+ * "the fragment reloaded" from "this is a different fragment". Liveness (Phase 2) needs it to
52
+ * avoid crediting a dead instance's beats to its replacement, and negotiated teardown (Phase 3)
53
+ * needs it to avoid ACKing a close on behalf of an instance that never received it.
54
+ */
55
+ instance: string;
56
+ /**
57
+ * Which bus this addresses. `undefined` means the host bus — which is every message today.
58
+ *
59
+ * Reserved, unused, and deliberately not removed: the fragment-local bus is planned last, and
60
+ * without this field a context message addresses a *key* but not a *bus*. Introducing a second
61
+ * bus later would then mean either bumping `WEAVE_VERSION` or smuggling the scope into the key
62
+ * string, and both are worse than one optional field nothing currently sets.
63
+ */
64
+ scope?: string;
65
+ payload?: unknown;
66
+ /** Set on a reply that failed. Mutually exclusive with a meaningful `payload`. */
67
+ error?: WeaveErrorPayload;
68
+ }
69
+ /**
70
+ * Identity generator for message and instance ids.
71
+ *
72
+ * `crypto.randomUUID` is required to be present in a secure context and is available in every
73
+ * environment Braid targets, but it is absent over plain http on a LAN address — a configuration
74
+ * developers hit constantly when testing on a phone — so the fallback exists to keep the dev loop
75
+ * working rather than to be cryptographically interesting. Ids here are correlation handles, never
76
+ * capabilities: nothing is authorized by holding one.
77
+ */
78
+ export declare function weaveId(): string;
79
+ /** Narrows an unknown message — anything can arrive on a port — to a well-formed envelope. */
80
+ export declare function isEnvelope(value: unknown): value is Envelope;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The Weave envelope — the unit that crosses a fragment boundary.
3
+ *
4
+ * Braid already had a *bus*: `braidContext` publishes with structured cloning, per-subscriber
5
+ * Skew projection and subscriber isolation. What it did not have was a **wire**. A fragment
6
+ * reached the bus because the host built `env.context` as closures over `braidContext` and handed
7
+ * that object into the realm — a synchronous cross-realm call on a shared object graph. That works
8
+ * for a same-origin realm and for nothing else: a closure cannot be handed to another origin, which
9
+ * is the real reason the untrusted tier throws rather than degrades.
10
+ *
11
+ * This module is the addressing layer that was missing underneath. The bus's semantics are
12
+ * unchanged and are not re-implemented here — they remain the specification this format serves.
13
+ */
14
+ /**
15
+ * Version of the Weave envelope format.
16
+ *
17
+ * Deliberately distinct from `BRAID_PROTOCOL_VERSION`, which versions the *client ↔ gateway*
18
+ * composition protocol. The two move for unrelated reasons: a change to how the gateway namespaces
19
+ * fragment assets has nothing to do with how a mounted fragment talks to its host, and a single
20
+ * number covering both would force one side to break for the other's benefit.
21
+ */
22
+ export const WEAVE_VERSION = 1;
23
+ /**
24
+ * Identity generator for message and instance ids.
25
+ *
26
+ * `crypto.randomUUID` is required to be present in a secure context and is available in every
27
+ * environment Braid targets, but it is absent over plain http on a LAN address — a configuration
28
+ * developers hit constantly when testing on a phone — so the fallback exists to keep the dev loop
29
+ * working rather than to be cryptographically interesting. Ids here are correlation handles, never
30
+ * capabilities: nothing is authorized by holding one.
31
+ */
32
+ export function weaveId() {
33
+ const cryptoApi = globalThis.crypto;
34
+ if (cryptoApi && typeof cryptoApi.randomUUID === 'function')
35
+ return cryptoApi.randomUUID();
36
+ return `w-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
37
+ }
38
+ /** Narrows an unknown message — anything can arrive on a port — to a well-formed envelope. */
39
+ export function isEnvelope(value) {
40
+ if (typeof value !== 'object' || value === null)
41
+ return false;
42
+ const candidate = value;
43
+ return (candidate.v === WEAVE_VERSION &&
44
+ typeof candidate.id === 'string' &&
45
+ typeof candidate.type === 'string' &&
46
+ typeof candidate.fragmentId === 'string' &&
47
+ typeof candidate.instance === 'string');
48
+ }
49
+ //# sourceMappingURL=envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../../../../libs/core/src/weave/envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC;AAkD/B;;;;;;;;GAQG;AACH,MAAM,UAAU,OAAO;IACrB,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC;IACpC,IAAI,SAAS,IAAI,OAAO,SAAS,CAAC,UAAU,KAAK,UAAU;QAAE,OAAO,SAAS,CAAC,UAAU,EAAE,CAAC;IAC3F,OAAO,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AACnF,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,SAAS,GAAG,KAA0B,CAAC;IAC7C,OAAO,CACL,SAAS,CAAC,CAAC,KAAK,aAAa;QAC7B,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ;QAChC,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ;QAClC,OAAO,SAAS,CAAC,UAAU,KAAK,QAAQ;QACxC,OAAO,SAAS,CAAC,QAAQ,KAAK,QAAQ,CACvC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,59 @@
1
+ import { BoundaryChannel } from './channel.js';
2
+ import { ClosingCoordinator } from './closing.js';
3
+ import { FragmentContract } from './contract.js';
4
+ /**
5
+ * The guest half of the untrusted tier: what a cross-origin fragment runs to join a host page.
6
+ *
7
+ * ```ts
8
+ * const session = await connectToBraidHost({ hostOrigin: 'https://portal.example.com' });
9
+ * session.context.subscribe('user', renderUser);
10
+ * session.onClosing(() => flushOutbox());
11
+ * ```
12
+ *
13
+ * Note what a session **does not** have, compared with `FragmentEnv`: no `root`, no `document`, no
14
+ * `location`, no `history`. A cross-origin fragment already owns all four — it is a whole document
15
+ * with its own URL — and handing it host-shaped substitutes would be re-creating exactly the
16
+ * illusion this tier exists to refuse. What crosses is state and lifecycle, nothing more.
17
+ *
18
+ * This is intentionally small. The framework adapters that make it ergonomic are Phase 7's job; this
19
+ * is the primitive they will be written against, shipped now because the untrusted tier is not
20
+ * testable without a guest and a tier with no guest is a tier nobody can use.
21
+ */
22
+ export interface GuestSession {
23
+ readonly channel: BoundaryChannel;
24
+ readonly context: {
25
+ get(key: string): unknown;
26
+ subscribe(key: string, listener: (value: unknown) => void, options?: {
27
+ signal?: AbortSignal;
28
+ }): () => void;
29
+ };
30
+ readonly props: Readonly<Record<string, unknown>>;
31
+ onPropsChanged(listener: (props: Readonly<Record<string, unknown>>) => void): () => void;
32
+ /** Fragment → host. Surfaced on the slot element as `braid:event`. */
33
+ emit(type: string, detail?: unknown): void;
34
+ onClosing: ClosingCoordinator['onClosing'];
35
+ setDirty: ClosingCoordinator['setDirty'];
36
+ /** Aborts when the host closes the session. */
37
+ readonly signal: AbortSignal;
38
+ disconnect(): void;
39
+ }
40
+ export interface ConnectOptions {
41
+ /**
42
+ * The exact origin of the host page this fragment agrees to be embedded by.
43
+ *
44
+ * Required, and deliberately not defaulted to "whoever framed me". A fragment that accepted a
45
+ * channel from any parent could be framed by an attacker's page, handed a plausible-looking
46
+ * context, and induced to render a user's data inside a document that can screenshot it. The
47
+ * fragment decides who may embed it, and this is where it says so.
48
+ */
49
+ hostOrigin: string;
50
+ /** Skew chain versions this fragment can parse, per context key. */
51
+ contextVersions?: Readonly<Record<string, number>>;
52
+ contract?: FragmentContract;
53
+ /** How long to wait for a host to offer a connection. */
54
+ timeoutMs?: number;
55
+ /** Beat interval; defaults to the shared two seconds. */
56
+ beatIntervalMs?: number;
57
+ }
58
+ /** Waits for a host to offer a port, then completes the Weave handshake over it. */
59
+ export declare function connectToBraidHost(options: ConnectOptions): Promise<GuestSession>;
@@ -0,0 +1,120 @@
1
+ import { BraidError } from '../errors.js';
2
+ import { SANDBOX_CONNECT, SANDBOX_READY, } from '../realm/sandbox-realm.js';
3
+ import { createBoundaryChannel } from './channel.js';
4
+ import { createPortBacking } from './port-backing.js';
5
+ import { createContextMirror } from './context-bridge.js';
6
+ import { createClosingCoordinator } from './closing.js';
7
+ import { answerHandshake } from './handshake.js';
8
+ import { startFragmentBeats } from './liveness.js';
9
+ import { FRAGMENT_EVENT, PROPS_CHANGED } from './messages.js';
10
+ const CONNECT_TIMEOUT_MS = 15_000;
11
+ /** Waits for a host to offer a port, then completes the Weave handshake over it. */
12
+ export async function connectToBraidHost(options) {
13
+ const { hostOrigin } = options;
14
+ const { port, fragmentId, instance } = await acceptConnection(hostOrigin, options.timeoutMs ?? CONNECT_TIMEOUT_MS);
15
+ const controller = new AbortController();
16
+ const channel = createBoundaryChannel({
17
+ backing: createPortBacking(port),
18
+ fragmentId,
19
+ instance,
20
+ signal: controller.signal,
21
+ });
22
+ const context = createContextMirror(channel);
23
+ const closing = createClosingCoordinator(channel);
24
+ let props = {};
25
+ const propsListeners = new Set();
26
+ const announceProps = () => {
27
+ for (const listener of [...propsListeners]) {
28
+ try {
29
+ listener(props);
30
+ }
31
+ catch (error) {
32
+ console.error('braid: a props listener threw', error);
33
+ }
34
+ }
35
+ };
36
+ channel.on(PROPS_CHANGED, (payload) => {
37
+ props = (payload ?? {}).props ?? {};
38
+ announceProps();
39
+ });
40
+ const opened = answerHandshake({
41
+ channel,
42
+ ...(options.contextVersions === undefined ? {} : { contextVersions: options.contextVersions }),
43
+ ...(options.contract === undefined ? {} : { contract: options.contract }),
44
+ onOpen(open) {
45
+ context.seed(open);
46
+ props = open.props ?? {};
47
+ announceProps();
48
+ },
49
+ });
50
+ await opened;
51
+ /**
52
+ * Beats run on this document's own timers, which is what makes them meaningful here — more so
53
+ * than on the trusted tier, where the "realm" is a hidden frame. A cross-origin fragment that
54
+ * hangs is a whole application hanging, and its beats stop because its event loop stopped.
55
+ */
56
+ startFragmentBeats({
57
+ channel,
58
+ scheduler: window,
59
+ signal: controller.signal,
60
+ ...(options.beatIntervalMs === undefined ? {} : { beatIntervalMs: options.beatIntervalMs }),
61
+ });
62
+ return {
63
+ channel,
64
+ context: { get: (key) => context.get(key), subscribe: (key, listener, subscribeOptions) => context.subscribe(key, listener, { signal: subscribeOptions?.signal ?? controller.signal }) },
65
+ get props() {
66
+ return props;
67
+ },
68
+ onPropsChanged(listener) {
69
+ propsListeners.add(listener);
70
+ return () => void propsListeners.delete(listener);
71
+ },
72
+ emit: (type, detail) => channel.send(FRAGMENT_EVENT, { type, detail }),
73
+ onClosing: closing.onClosing,
74
+ setDirty: closing.setDirty,
75
+ signal: controller.signal,
76
+ disconnect: () => controller.abort(),
77
+ };
78
+ }
79
+ /** Announces readiness, then takes the first port a pinned host offers. */
80
+ function acceptConnection(hostOrigin, timeoutMs) {
81
+ const { promise, resolve, reject } = Promise.withResolvers();
82
+ const onMessage = (event) => {
83
+ // Origin pinning, the whole point. `event.origin` is set by the browser and cannot be forged by
84
+ // the sender, which is what makes it worth checking and `event.data` worth distrusting.
85
+ if (event.origin !== hostOrigin)
86
+ return;
87
+ const data = event.data;
88
+ if (data?.braid !== SANDBOX_CONNECT)
89
+ return;
90
+ const port = event.ports[0];
91
+ if (!port)
92
+ return;
93
+ cleanup();
94
+ resolve({ port, fragmentId: String(data.fragmentId ?? 'fragment'), instance: String(data.instance ?? 'instance') });
95
+ };
96
+ const timer = setTimeout(() => {
97
+ cleanup();
98
+ reject(new BraidError(`no braid host offered a connection within ${timeoutMs}ms`, {
99
+ fragmentId: '<unconnected>',
100
+ stage: 'handshake',
101
+ fixHint: `check that this page is framed by "${hostOrigin}" and that the host mounts it with trust="untrusted"`,
102
+ }));
103
+ }, timeoutMs);
104
+ function cleanup() {
105
+ clearTimeout(timer);
106
+ window.removeEventListener('message', onMessage);
107
+ }
108
+ window.addEventListener('message', onMessage);
109
+ /**
110
+ * Announced *after* the listener is attached, so the two orderings converge.
111
+ *
112
+ * The host posts on the frame's `load` event, which may fire before this module has even been
113
+ * evaluated. Announcing tells a host that already gave up waiting to post again; attaching first
114
+ * means a host that posted immediately is not missed either.
115
+ */
116
+ const ready = { braid: SANDBOX_READY, v: 1 };
117
+ window.parent?.postMessage(ready, hostOrigin);
118
+ return promise;
119
+ }
120
+ //# sourceMappingURL=guest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guest.js","sourceRoot":"","sources":["../../../../../libs/core/src/weave/guest.ts"],"names":[],"mappings":"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"}