@braidlabs/gateway 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/src/protocol.d.ts CHANGED
@@ -49,6 +49,17 @@ export declare const BRAID_ADAPTER_META = "braid-adapter";
49
49
  export declare const BRAID_ADAPTER_OPTIONS_META = "braid-adapter-options";
50
50
  /** Response/request header carrying a fragment id for diagnostics. */
51
51
  export declare const BRAID_FRAGMENT_ID_HEADER = "x-braid-fragment-id";
52
+ /**
53
+ * Response header on a `409` from the document namespace: where the fragment tried to send the
54
+ * request. A browser hides the target of a redirect it was told not to follow, so the gateway
55
+ * carries it here instead — only for fragments that opt in with `redirect: 'navigate'`.
56
+ */
57
+ export declare const BRAID_REDIRECT_LOCATION_HEADER = "x-braid-redirect-location";
58
+ /**
59
+ * Request header on a document fetch: the page the fragment is being shown on. The gateway uses it
60
+ * to point a login's return URL at that page instead of at the fragment's own document URL.
61
+ */
62
+ export declare const BRAID_RETURN_URL_HEADER = "x-braid-return-url";
52
63
  /** What a braid-namespaced URL addresses. */
53
64
  export type BraidRouteKind =
54
65
  /** The fragment's own endpoint: assets, data, anything it serves. Forwarded verbatim. */
@@ -75,5 +86,10 @@ export declare function parseNamespacePathname(pathname: string): {
75
86
  fragmentId: string;
76
87
  pathname: string;
77
88
  } | null;
78
- /** Builds a URL in the fragment's asset namespace. */
79
- export declare function braidFragmentUrl(fragmentId: string, pathname: string, search?: string): string;
89
+ /**
90
+ * Builds a URL in the fragment's asset namespace.
91
+ *
92
+ * `basePath` is the gateway's mount (`/manage`), already normalized: empty, or a path with no
93
+ * trailing slash.
94
+ */
95
+ export declare function braidFragmentUrl(fragmentId: string, pathname: string, search?: string, basePath?: string): string;
package/src/protocol.js CHANGED
@@ -49,6 +49,17 @@ export const BRAID_ADAPTER_META = 'braid-adapter';
49
49
  export const BRAID_ADAPTER_OPTIONS_META = 'braid-adapter-options';
50
50
  /** Response/request header carrying a fragment id for diagnostics. */
51
51
  export const BRAID_FRAGMENT_ID_HEADER = 'x-braid-fragment-id';
52
+ /**
53
+ * Response header on a `409` from the document namespace: where the fragment tried to send the
54
+ * request. A browser hides the target of a redirect it was told not to follow, so the gateway
55
+ * carries it here instead — only for fragments that opt in with `redirect: 'navigate'`.
56
+ */
57
+ export const BRAID_REDIRECT_LOCATION_HEADER = 'x-braid-redirect-location';
58
+ /**
59
+ * Request header on a document fetch: the page the fragment is being shown on. The gateway uses it
60
+ * to point a login's return URL at that page instead of at the fragment's own document URL.
61
+ */
62
+ export const BRAID_RETURN_URL_HEADER = 'x-braid-return-url';
52
63
  const PREFIXES = [
53
64
  [BRAID_FRAGMENT_PREFIX, 'fragment'],
54
65
  [BRAID_REALM_PREFIX, 'realm'],
@@ -82,8 +93,13 @@ export function parseNamespacePathname(pathname) {
82
93
  const route = parseBraidPathname(pathname);
83
94
  return route && route.kind === 'fragment' ? { fragmentId: route.fragmentId, pathname: route.pathname } : null;
84
95
  }
85
- /** Builds a URL in the fragment's asset namespace. */
86
- export function braidFragmentUrl(fragmentId, pathname, search = '') {
87
- return `${BRAID_FRAGMENT_PREFIX}${encodeURIComponent(fragmentId)}${pathname}${search}`;
96
+ /**
97
+ * Builds a URL in the fragment's asset namespace.
98
+ *
99
+ * `basePath` is the gateway's mount (`/manage`), already normalized: empty, or a path with no
100
+ * trailing slash.
101
+ */
102
+ export function braidFragmentUrl(fragmentId, pathname, search = '', basePath = '') {
103
+ return `${basePath}${BRAID_FRAGMENT_PREFIX}${encodeURIComponent(fragmentId)}${pathname}${search}`;
88
104
  }
89
105
  //# sourceMappingURL=protocol.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../../libs/braid-gateway/src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,gBAAgB,CAAC;AACtD,MAAM,CAAC,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AACpD,MAAM,CAAC,MAAM,qBAAqB,GAAG,eAAe,CAAC;AAErD,4FAA4F;AAC5F,MAAM,CAAC,MAAM,sBAAsB,GAAG,qBAAqB,CAAC;AAE5D;;;;;;GAMG;AACH,4EAA4E;AAC5E,MAAM,CAAC,MAAM,yBAAyB,GAAG,gBAAgB,CAAC;AAE1D,gFAAgF;AAChF,MAAM,CAAC,MAAM,wBAAwB,GAAG,oBAAoB,CAAC;AAC7D,+CAA+C;AAC/C,MAAM,CAAC,MAAM,wBAAwB,GAAG,iBAAiB,CAAC;AAE1D,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAE1C,6FAA6F;AAC7F,MAAM,CAAC,MAAM,mBAAmB,GAAG,gBAAgB,CAAC;AAEpD,wGAAwG;AACxG,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,uBAAuB,CAAC;AAElE,sEAAsE;AACtE,MAAM,CAAC,MAAM,wBAAwB,GAAG,qBAAqB,CAAC;AAkB9D,MAAM,QAAQ,GAAqD;IACjE,CAAC,qBAAqB,EAAE,UAAU,CAAC;IACnC,CAAC,kBAAkB,EAAE,OAAO,CAAC;IAC7B,CAAC,qBAAqB,EAAE,UAAU,CAAC;CACpC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IACjD,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,QAAQ,EAAE,CAAC;QACtC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,SAAS;QAE3C,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,iBAAiB,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QAE/E,IAAI,CAAC,iBAAiB;YAAE,OAAO,IAAI,CAAC;QAEpC,OAAO;YACL,IAAI;YACJ,UAAU,EAAE,kBAAkB,CAAC,iBAAiB,CAAC;YACjD,QAAQ,EAAE,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;SAC3D,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,sBAAsB,CAAC,QAAgB;IACrD,MAAM,KAAK,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC3C,OAAO,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAChH,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,gBAAgB,CAAC,UAAkB,EAAE,QAAgB,EAAE,MAAM,GAAG,EAAE;IAChF,OAAO,GAAG,qBAAqB,GAAG,kBAAkB,CAAC,UAAU,CAAC,GAAG,QAAQ,GAAG,MAAM,EAAE,CAAC;AACzF,CAAC"}
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../../libs/braid-gateway/src/protocol.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Turning a fragment's redirect into something a browser can act on.
3
+ *
4
+ * A browser that was told not to follow a redirect hides where it was going, so for fragments that
5
+ * opt in (`redirect: 'navigate'`) the gateway answers the document request with a `409` carrying the
6
+ * target in a header. Everything here is pure: what the target may be, and how a login's return URL
7
+ * is pointed back at the page the user was on.
8
+ *
9
+ * The target is chosen by the fragment's endpoint, and it ends up as a top-level navigation, so it
10
+ * is only ever one of three things: a path on the host's own origin, a path inside the fragment's
11
+ * own endpoint (mapped back to the host), or an origin the host operator listed.
12
+ */
13
+ /** An origin the host trusts as a redirect destination — `https://login.example.com`, or `https://*.example.com`. */
14
+ export interface OriginRule {
15
+ protocol: string;
16
+ /** The exact hostname, or the suffix after `*.` when `wildcard` is set. */
17
+ host: string;
18
+ wildcard: boolean;
19
+ port: string;
20
+ }
21
+ /** Parses `redirectOrigins`, failing at construction so a typo is not a silent refusal at 2am. */
22
+ export declare function parseOriginRules(origins: readonly string[]): OriginRule[];
23
+ export interface RedirectContext {
24
+ /** The raw `Location` the fragment answered with. */
25
+ location: string;
26
+ /** The URL the fragment's endpoint was asked for — what a relative `Location` is relative to. */
27
+ fragmentRequestUrl: URL;
28
+ /** The fragment's endpoint, when it is a URL (a function endpoint has no origin of its own). */
29
+ endpoint: string | undefined;
30
+ /** The request that reached the gateway: the host's origin. */
31
+ requestUrl: URL;
32
+ allowedOrigins: readonly OriginRule[];
33
+ }
34
+ export type RedirectResolution = {
35
+ ok: true;
36
+ target: string;
37
+ } | {
38
+ ok: false;
39
+ reason: string;
40
+ };
41
+ /** `origin + path` only. A login's query string is where its tokens and return URLs live. */
42
+ export declare function redactUrl(url: URL): string;
43
+ /**
44
+ * A path that can only mean a path on this origin.
45
+ *
46
+ * `//evil.net` is a valid *path* to a URL parser (`https://host//evil.net`, or `/\evil.net`, whose
47
+ * backslash becomes a slash) and a valid *host* to whoever reads it back as a relative URL — the
48
+ * client does, with `new URL(target, location.href)`. Every leading slash or backslash collapses to
49
+ * one, so nothing this module emits as a path can be read as anything else.
50
+ */
51
+ export declare function hostPath(pathname: string, search?: string, hash?: string): string;
52
+ export declare function resolveRedirectTarget(context: RedirectContext): RedirectResolution;
53
+ /**
54
+ * The page the user is on, if the request says so and it is one the gateway may send them back to.
55
+ *
56
+ * Same origin as the gateway only: this value is client-supplied and ends up inside a redirect, so
57
+ * it must not be able to name anywhere else. A braid URL is not a page.
58
+ */
59
+ export declare function resolveReturnUrl(requestUrl: URL, headerValue: string | null, referer: string | null): URL | null;
60
+ /**
61
+ * Points a login's return-URL parameters at the page instead of at the fragment.
62
+ *
63
+ * A fragment builds `?next=` from the request it saw — its own URL, which means nothing to the
64
+ * user. Any query parameter whose value is that URL (same path, on the fragment's origin or the
65
+ * host's) is replaced with the page the user was on, in the same form (absolute stays absolute, a
66
+ * path stays a path). Anything else is left exactly as it was.
67
+ */
68
+ export declare function rewriteReturnUrls(target: string, returnUrl: URL | null, context: Pick<RedirectContext, 'fragmentRequestUrl' | 'requestUrl'> & {
69
+ strippedUrl: URL;
70
+ }): string;
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Turning a fragment's redirect into something a browser can act on.
3
+ *
4
+ * A browser that was told not to follow a redirect hides where it was going, so for fragments that
5
+ * opt in (`redirect: 'navigate'`) the gateway answers the document request with a `409` carrying the
6
+ * target in a header. Everything here is pure: what the target may be, and how a login's return URL
7
+ * is pointed back at the page the user was on.
8
+ *
9
+ * The target is chosen by the fragment's endpoint, and it ends up as a top-level navigation, so it
10
+ * is only ever one of three things: a path on the host's own origin, a path inside the fragment's
11
+ * own endpoint (mapped back to the host), or an origin the host operator listed.
12
+ */
13
+ /** Parses `redirectOrigins`, failing at construction so a typo is not a silent refusal at 2am. */
14
+ export function parseOriginRules(origins) {
15
+ return origins.map((origin) => {
16
+ const wildcard = /^[a-z][a-z0-9+.-]*:\/\/\*\./i.test(origin);
17
+ let url;
18
+ try {
19
+ url = new URL(wildcard ? origin.replace('*.', '') : origin);
20
+ }
21
+ catch {
22
+ throw new Error(`braid-gateway: redirectOrigins entry "${origin}" is not a valid origin`);
23
+ }
24
+ if ((url.protocol !== 'https:' && url.protocol !== 'http:') || url.pathname !== '/' || url.search || url.hash) {
25
+ throw new Error(`braid-gateway: redirectOrigins entry "${origin}" must be a bare http(s) origin such as ` +
26
+ `"https://login.example.com" or "https://*.example.com"`);
27
+ }
28
+ return { protocol: url.protocol, host: url.hostname, wildcard, port: url.port };
29
+ });
30
+ }
31
+ function originAllowed(url, rules) {
32
+ return rules.some((rule) => rule.protocol === url.protocol &&
33
+ rule.port === url.port &&
34
+ // a wildcard is subdomains only — the apex is a different thing and has to be listed
35
+ (rule.wildcard ? url.hostname.endsWith(`.${rule.host}`) : url.hostname === rule.host));
36
+ }
37
+ /** `origin + path` only. A login's query string is where its tokens and return URLs live. */
38
+ export function redactUrl(url) {
39
+ return `${url.origin}${url.pathname}`;
40
+ }
41
+ /**
42
+ * A path that can only mean a path on this origin.
43
+ *
44
+ * `//evil.net` is a valid *path* to a URL parser (`https://host//evil.net`, or `/\evil.net`, whose
45
+ * backslash becomes a slash) and a valid *host* to whoever reads it back as a relative URL — the
46
+ * client does, with `new URL(target, location.href)`. Every leading slash or backslash collapses to
47
+ * one, so nothing this module emits as a path can be read as anything else.
48
+ */
49
+ export function hostPath(pathname, search = '', hash = '') {
50
+ return `/${pathname.replace(/^[\\/]+/, '')}${search}${hash}`;
51
+ }
52
+ export function resolveRedirectTarget(context) {
53
+ let target;
54
+ try {
55
+ target = new URL(context.location, context.fragmentRequestUrl);
56
+ }
57
+ catch {
58
+ return { ok: false, reason: 'its Location is not a valid URL' };
59
+ }
60
+ // `javascript:` and `data:` would run in the page that follows them
61
+ if (target.protocol !== 'https:' && target.protocol !== 'http:') {
62
+ return { ok: false, reason: `its Location uses the "${target.protocol}" scheme` };
63
+ }
64
+ // credentials in a URL are for phishing, and nothing a login sends the user to needs them
65
+ if (target.username || target.password) {
66
+ return { ok: false, reason: 'its Location carries credentials in the URL' };
67
+ }
68
+ // Inside the fragment's own endpoint: the same path on the host. The mirror image of how the
69
+ // gateway forwards requests, which is what makes `/apps/billing/login` mean `/login` to a user.
70
+ if (context.endpoint) {
71
+ const endpointUrl = new URL(context.endpoint);
72
+ const basePath = endpointUrl.pathname.endsWith('/') ? endpointUrl.pathname.slice(0, -1) : endpointUrl.pathname;
73
+ if (target.origin === endpointUrl.origin &&
74
+ (!basePath || target.pathname === basePath || target.pathname.startsWith(`${basePath}/`))) {
75
+ return { ok: true, target: hostPath(target.pathname.slice(basePath.length), target.search, target.hash) };
76
+ }
77
+ }
78
+ // The host's own origin — including a path under /__braid/, which is how a login that is itself
79
+ // a fragment is reached.
80
+ if (target.origin === context.requestUrl.origin) {
81
+ return { ok: true, target: hostPath(target.pathname, target.search, target.hash) };
82
+ }
83
+ if (originAllowed(target, context.allowedOrigins))
84
+ return { ok: true, target: target.href };
85
+ return {
86
+ ok: false,
87
+ reason: `its Location (${redactUrl(target)}) is not on this host, inside the fragment's endpoint, or in ` +
88
+ `redirectOrigins`,
89
+ };
90
+ }
91
+ /**
92
+ * The page the user is on, if the request says so and it is one the gateway may send them back to.
93
+ *
94
+ * Same origin as the gateway only: this value is client-supplied and ends up inside a redirect, so
95
+ * it must not be able to name anywhere else. A braid URL is not a page.
96
+ */
97
+ export function resolveReturnUrl(requestUrl, headerValue, referer) {
98
+ for (const candidate of [headerValue, referer]) {
99
+ if (!candidate)
100
+ continue;
101
+ try {
102
+ const url = new URL(candidate);
103
+ // a braid URL is not a page — wherever the gateway is mounted (`/manage/__braid/…` with a basePath)
104
+ if (url.origin === requestUrl.origin && !/(^|\/)__braid\//.test(url.pathname))
105
+ return url;
106
+ }
107
+ catch {
108
+ // try the next one
109
+ }
110
+ }
111
+ return null;
112
+ }
113
+ /**
114
+ * Points a login's return-URL parameters at the page instead of at the fragment.
115
+ *
116
+ * A fragment builds `?next=` from the request it saw — its own URL, which means nothing to the
117
+ * user. Any query parameter whose value is that URL (same path, on the fragment's origin or the
118
+ * host's) is replaced with the page the user was on, in the same form (absolute stays absolute, a
119
+ * path stays a path). Anything else is left exactly as it was.
120
+ */
121
+ export function rewriteReturnUrls(target, returnUrl, context) {
122
+ if (!returnUrl)
123
+ return target;
124
+ const url = new URL(target, context.requestUrl);
125
+ if (!url.search)
126
+ return target;
127
+ const origins = new Set([context.fragmentRequestUrl.origin, context.requestUrl.origin]);
128
+ const paths = new Set([
129
+ context.fragmentRequestUrl.pathname,
130
+ context.strippedUrl.pathname,
131
+ context.requestUrl.pathname,
132
+ ]);
133
+ const pagePath = hostPath(returnUrl.pathname, returnUrl.search, returnUrl.hash);
134
+ let rewritten = false;
135
+ // Pair by pair on the raw query, so a parameter that is not being rewritten keeps its exact bytes
136
+ // — re-serializing the whole query would change `~`, bare flags and invalid UTF-8 for an IdP that
137
+ // never asked for it.
138
+ const pairs = url.search.slice(1).split('&').map((pair) => {
139
+ const eq = pair.indexOf('=');
140
+ if (eq < 0)
141
+ return pair;
142
+ let value;
143
+ try {
144
+ value = decodeURIComponent(pair.slice(eq + 1).replace(/\+/g, ' '));
145
+ }
146
+ catch {
147
+ return pair;
148
+ }
149
+ const isPath = value.startsWith('/') && !value.startsWith('//');
150
+ if (!isPath && !/^https?:\/\//i.test(value))
151
+ return pair;
152
+ let candidate;
153
+ try {
154
+ candidate = new URL(value, context.fragmentRequestUrl);
155
+ }
156
+ catch {
157
+ return pair;
158
+ }
159
+ // A fragment served at `/` would match every `redirect_uri=<its root>`, which is an OAuth
160
+ // callback registration and not a place to return to.
161
+ if (candidate.pathname === '/' || !origins.has(candidate.origin) || !paths.has(candidate.pathname))
162
+ return pair;
163
+ rewritten = true;
164
+ return `${pair.slice(0, eq + 1)}${encodeURIComponent(isPath ? pagePath : returnUrl.href)}`;
165
+ });
166
+ if (!rewritten)
167
+ return target;
168
+ url.search = `?${pairs.join('&')}`;
169
+ // keep the form the target arrived in: host-relative stays host-relative
170
+ return /^https?:\/\//i.test(target) ? url.href : hostPath(url.pathname, url.search, url.hash);
171
+ }
172
+ //# sourceMappingURL=redirect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redirect.js","sourceRoot":"","sources":["../../../../libs/braid-gateway/src/redirect.ts"],"names":[],"mappings":"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"}
package/src/registry.d.ts CHANGED
@@ -24,6 +24,54 @@ export interface FragmentManifest {
24
24
  adapter?: string;
25
25
  /** Contract version for contract-mode fragments (unused by the compat adapter). */
26
26
  contractVersion?: string;
27
+ /**
28
+ * What this fragment requires of a host, and what it provides back.
29
+ *
30
+ * Compared at the boundary handshake, before any state crosses. Recorded here rather than taken
31
+ * only from the fragment's own ACCEPT because the registry is host-side configuration: on the
32
+ * untrusted tier a fragment stating its own requirements could state them away.
33
+ *
34
+ * ```jsonc
35
+ * "contract": {
36
+ * "version": "2.1.0",
37
+ * "requires": { "host": ">=1.4.0", "context": { "cart": 2 } },
38
+ * "provides": { "events": ["checkout:complete"] }
39
+ * }
40
+ * ```
41
+ */
42
+ contract?: {
43
+ version: string;
44
+ requires?: {
45
+ host?: string;
46
+ context?: Record<string, number>;
47
+ };
48
+ provides?: {
49
+ events?: string[];
50
+ actions?: string[];
51
+ };
52
+ };
53
+ /**
54
+ * What this fragment is allowed to do. Host-owned: never describable by the fragment itself,
55
+ * because a grant a fragment can widen is not a grant.
56
+ *
57
+ * ```jsonc
58
+ * "capabilities": {
59
+ * "context": { "read": ["user", "cart"] },
60
+ * "sandbox": ["allow-popups"],
61
+ * "permissions": ["clipboard-write"]
62
+ * }
63
+ * ```
64
+ */
65
+ capabilities?: {
66
+ context?: {
67
+ read?: string[];
68
+ write?: string[];
69
+ };
70
+ sandbox?: string[];
71
+ permissions?: string[];
72
+ network?: string[];
73
+ storage?: 'partitioned' | 'shared';
74
+ };
27
75
  /** Module entry for contract-mode fragments (unused by the compat adapter). */
28
76
  entry?: string;
29
77
  /**
@@ -57,6 +105,15 @@ export interface FragmentManifest {
57
105
  * 404 on every page the widget appears on.
58
106
  */
59
107
  src?: string;
108
+ /**
109
+ * Where a bound fragment's document lives, as a path on its own endpoint (`/index.html`).
110
+ *
111
+ * For a single-page app on a static origin — a bucket behind a CDN — that has one document and
112
+ * 404s every route. Without it, a deep link asks that origin for `/accounts/123`. The fragment's
113
+ * router still sees the page's real route; only the request for its document changes. Assets
114
+ * are unaffected and are always fetched at their own paths.
115
+ */
116
+ documentPath?: string;
60
117
  /** Typed event surface for hosts. Reserved; not enforced by this build. */
61
118
  events?: Record<string, {
62
119
  detail: string;
@@ -117,6 +174,22 @@ export interface FragmentManifest {
117
174
  * holding a deep link but kept out of listings.
118
175
  */
119
176
  access?: FragmentAccess;
177
+ /**
178
+ * What happens when the fragment answers its document request with a redirect — almost always an
179
+ * unauthenticated request being sent to a login page.
180
+ *
181
+ * - `'error'` (default) — the client reports a named `fragment-fetch` error. A browser hides where
182
+ * a redirect it was told not to follow was going, so nothing more is possible without an opt-in.
183
+ * - `'navigate'` — the gateway hands the target to the client, which navigates the whole page
184
+ * there, exactly as the shell would have done had the redirect hit the top-level request.
185
+ *
186
+ * Opt-in because it lets the fragment's endpoint move the top-level page. The target must be on
187
+ * the host's origin, inside the fragment's own endpoint path, or on an origin listed in the
188
+ * gateway's `redirectOrigins`; anything else is refused. It only works end to end once the
189
+ * fragment can see who the user is (`forwardCredentials` or `additionalHeaders`) — otherwise it
190
+ * redirects again on the way back, and the client stops after one round trip.
191
+ */
192
+ redirect?: 'error' | 'navigate';
120
193
  }
121
194
  export interface FragmentAccess {
122
195
  /** Who may see this fragment in the discovery registry. Public when omitted. */
package/src/registry.js CHANGED
@@ -45,12 +45,21 @@ export function normalizeManifest(manifest) {
45
45
  if (!manifest.endpoint) {
46
46
  throw new Error(`braid-gateway: manifest "${manifest.id}" is missing its endpoint`);
47
47
  }
48
+ if (manifest.documentPath !== undefined && !isPlainPath(manifest.documentPath)) {
49
+ throw new Error(`braid-gateway: manifest "${manifest.id}" has documentPath "${manifest.documentPath}" — it must be ` +
50
+ `a plain path on the fragment's endpoint, e.g. documentPath: "/index.html" (no "//", "\\", ` +
51
+ `dot segments, query, or fragment)`);
52
+ }
48
53
  // Warned rather than thrown: the fragment still composes, at the page path, which is wrong in a
49
54
  // way that shows up as an empty widget rather than as an error anyone can trace back to here.
50
55
  if (manifest.bound === false && !manifest.src) {
51
56
  console.warn(`braid-gateway: fragment "${manifest.id}" declares bound: false without a src, so it will be ` +
52
57
  `fetched at each page's own path — declare the path its content lives at, e.g. src: "/panel"`);
53
58
  }
59
+ if (manifest.redirect !== undefined && manifest.redirect !== 'error' && manifest.redirect !== 'navigate') {
60
+ throw new Error(`braid-gateway: manifest "${manifest.id}" declares redirect: "${String(manifest.redirect)}" — ` +
61
+ `use "error" or "navigate"`);
62
+ }
54
63
  return {
55
64
  ...manifest,
56
65
  adapter: manifest.adapter ?? DEFAULT_ADAPTER,
@@ -59,6 +68,13 @@ export function normalizeManifest(manifest) {
59
68
  bound: manifest.bound ?? true,
60
69
  };
61
70
  }
71
+ /**
72
+ * An absolute path that means the same thing however it is joined onto an endpoint: one leading
73
+ * slash, no backslashes (which URL parsers treat as slashes), no dot segments, no query or hash.
74
+ */
75
+ function isPlainPath(path) {
76
+ return /^\/(?!\/)[^?#\\]*$/.test(path) && !path.split('/').some((segment) => segment === '.' || segment === '..');
77
+ }
62
78
  export class Registry {
63
79
  #source;
64
80
  #manifests;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../../libs/braid-gateway/src/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../../libs/braid-gateway/src/registry.ts"],"names":[],"mappings":"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"}
@@ -44,9 +44,14 @@ export interface EndTag {
44
44
  /** Emits content immediately before this end tag. */
45
45
  before(content: Injection): void;
46
46
  }
47
+ /**
48
+ * A handler may return a promise, and the stream waits on it at that tag — everything before the
49
+ * tag has already been emitted. That is how a pierced slot waits for its fragment without holding
50
+ * back the shell ahead of it. Handlers that return nothing cost nothing extra.
51
+ */
47
52
  export interface ElementHandler {
48
- element?(tag: StartTag): void;
49
- endTag?(tag: EndTag): void;
53
+ element?(tag: StartTag): void | Promise<void>;
54
+ endTag?(tag: EndTag): void | Promise<void>;
50
55
  }
51
56
  export interface RewriteOptions {
52
57
  /**
@@ -61,7 +66,7 @@ export interface RewriteOptions {
61
66
  * Emitted once the input ends. Used as the safety net for injections anchored to an end tag
62
67
  * that never arrives — `</body>` is optional in HTML and frequently omitted.
63
68
  */
64
- onEnd?(): Injection | undefined;
69
+ onEnd?(): Injection | undefined | Promise<Injection | undefined>;
65
70
  }
66
71
  export declare function rewriteHtmlStream(input: ReadableStream<Uint8Array>, options: RewriteOptions): ReadableStream<Uint8Array>;
67
72
  /** Adapts an async generator of chunks into a ReadableStream. */
@@ -74,7 +74,7 @@ async function* rewrite(input, options) {
74
74
  finally {
75
75
  reader.releaseLock();
76
76
  }
77
- const trailing = options.onEnd?.();
77
+ const trailing = await options.onEnd?.();
78
78
  if (trailing !== undefined) {
79
79
  yield* emit(trailing);
80
80
  }
@@ -206,7 +206,11 @@ async function* rewrite(input, options) {
206
206
  },
207
207
  };
208
208
  for (const element of elementHandlers) {
209
- element(tag);
209
+ // awaited only when it is a promise: this runs for every element of every document, and an
210
+ // unconditional await would be a microtask per tag
211
+ const pending = element(tag);
212
+ if (pending)
213
+ await pending;
210
214
  }
211
215
  if (name !== originalName && !token.selfClosing && !VOID_ELEMENTS.has(originalName)) {
212
216
  openRenames.push({ original: originalName, renamed: name });
@@ -234,12 +238,14 @@ async function* rewrite(input, options) {
234
238
  return;
235
239
  }
236
240
  const before = [];
237
- handler.endTag({
241
+ const pending = handler.endTag({
238
242
  tagName: token.name,
239
243
  before(content) {
240
244
  before.push(content);
241
245
  },
242
246
  });
247
+ if (pending)
248
+ await pending;
243
249
  for (const content of before) {
244
250
  yield* emit(content);
245
251
  }
@@ -330,10 +336,17 @@ function scanToken(text, start) {
330
336
  index++;
331
337
  continue;
332
338
  }
339
+ // Attribute names end where the HTML tokenizer ends them — at `/` too, which separates
340
+ // attributes — and a leading `=` is the name's first character, not a separator. A name split
341
+ // differently here than in the browser is an attribute the transforms never see.
333
342
  const attributeNameStart = index;
334
- while (index < length && !isWhitespace(text[index]) && text[index] !== '=' && text[index] !== '>') {
335
- if (text[index] === '/' && text[index + 1] === '>')
336
- break;
343
+ if (text[index] === '=')
344
+ index++;
345
+ while (index < length &&
346
+ !isWhitespace(text[index]) &&
347
+ text[index] !== '=' &&
348
+ text[index] !== '>' &&
349
+ text[index] !== '/') {
337
350
  index++;
338
351
  }
339
352
  if (index >= length)
@@ -374,7 +387,12 @@ function scanToken(text, start) {
374
387
  }
375
388
  index = cursor;
376
389
  }
377
- attributes.push({ name: rawName.toLowerCase(), rawName, value });
390
+ // The browser keeps the first of a repeated attribute and drops the rest, so the same goes
391
+ // here: otherwise removing the first would promote a second the transforms never looked at.
392
+ const attributeName = rawName.toLowerCase();
393
+ if (!attributes.some((attribute) => attribute.name === attributeName)) {
394
+ attributes.push({ name: attributeName, rawName, value });
395
+ }
378
396
  }
379
397
  }
380
398
  /** Finds the end of a markup declaration, honoring quoted strings (doctypes may contain `>`). */