@dwk/host-meta 0.1.0-beta.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/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 David W. Keith
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # `@dwk/host-meta`
2
+
3
+ > Web Host Metadata (RFC 6415) host-meta discovery endpoint. Endpoint package.
4
+
5
+ Part of the [`@dwk` IndieWeb + Solid cohort](../../README.md). See the
6
+ [package specification](../../spec/packages/host-meta.md) for the full
7
+ requirements.
8
+
9
+ host-meta serves a host-wide resource-discovery document at
10
+ `/.well-known/host-meta` (XRD) and `/.well-known/host-meta.json` (JRD): a set of
11
+ top-level **`Link`** relations — at minimum an **`lrdd`** template pointing at
12
+ the site's WebFinger endpoint. It is largely **superseded by**
13
+ [`@dwk/webfinger`](../webfinger) for account discovery, but some fediverse and
14
+ OpenID software still fetch host-meta to find the `lrdd` template before falling
15
+ back to WebFinger.
16
+
17
+ ## Worker vs. static (why this package exists)
18
+
19
+ host-meta is **borderline static**. A single-identity site can have its static
20
+ site generator emit fixed `host-meta` + `host-meta.json` files and skip this
21
+ package entirely. A Worker is justified only by:
22
+
23
+ - **Content negotiation** between the XRD (`application/xrd+xml`) and JRD
24
+ (`application/jrd+json`) representations from the one `/.well-known/host-meta`
25
+ URL (RFC 6415 §3), plus a `?format=` override; and
26
+ - a **dynamic `lrdd` template** pointing at this Worker's WebFinger endpoint.
27
+
28
+ If neither is needed, prefer the static output and do not mount this package.
29
+
30
+ ## Usage
31
+
32
+ ```ts
33
+ import { createHostMeta } from "@dwk/host-meta";
34
+
35
+ const hostMeta = createHostMeta({
36
+ // Seeds an `lrdd` link templated to ...?resource={uri}
37
+ webfingerUrl: "https://example.com/.well-known/webfinger",
38
+ // Any additional static top-level links
39
+ links: [
40
+ {
41
+ rel: "author",
42
+ type: "text/html",
43
+ href: "https://example.com/about",
44
+ },
45
+ ],
46
+ });
47
+
48
+ // In your Worker's fetch handler, mount at both well-known paths:
49
+ // GET /.well-known/host-meta → XRD by default
50
+ // GET /.well-known/host-meta.json → JRD
51
+ return hostMeta(request, env, ctx);
52
+ ```
53
+
54
+ - The document is **request-invariant** — it is built once at construction from
55
+ config and served verbatim. `createHostMeta` **fails loudly** if neither
56
+ `webfingerUrl` nor any `links` is supplied; a host-meta that advertises
57
+ nothing is always a misconfiguration.
58
+ - The two representations are **information-equivalent** (RFC 6415 §3): the XRD
59
+ and JRD render the same `subject`, `properties`, and `links`.
60
+
61
+ ### Content negotiation
62
+
63
+ The representation is chosen, in priority order:
64
+
65
+ 1. an explicit `?format=json` / `?format=xml` query override;
66
+ 2. the request path — `/.well-known/host-meta.json` always returns JRD
67
+ (RFC 7033 §10.1);
68
+ 3. the `Accept` header — JRD only when `application/jrd+json` is *strictly*
69
+ preferred over `application/xrd+xml`; otherwise **XRD is the default** (for
70
+ ties, wildcards, and a missing header).
71
+
72
+ Every response — success or error — carries `Access-Control-Allow-Origin: *`
73
+ (discovery data is public) and `Vary: Accept`. `OPTIONS` returns a CORS
74
+ preflight; non-`GET`/`HEAD` methods get **405**.
75
+
76
+ ## Design
77
+
78
+ Pure and **stateless**: no Durable Object, no D1, and no required Cloudflare
79
+ bindings — the document is config-supplied (composition contract), never read
80
+ from the global environment. The serializers unit-test under Node without a
81
+ Workers runtime. The JRD link shaping is reused from
82
+ [`@dwk/webfinger`](../webfinger); the XRD serializer is the only surface new to
83
+ this package.
84
+
85
+ ## Observability
86
+
87
+ Discovery events are emitted through the injected `@dwk/log` `Logger`/`Metrics`
88
+ seams (default no-op): `host-meta.served` with the negotiated `format`,
89
+ `host-meta.rejected` for a `405`. The document is host-wide, so no request
90
+ carries user data to redact.
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Configuration for {@link createHostMeta}: the WebFinger endpoint URL that
3
+ * seeds the `lrdd` template and/or a set of static top-level links, plus the
4
+ * optional logging/metrics seams. Per the composition contract the package
5
+ * never reads the global environment — every link arrives here, so a handler
6
+ * can be instantiated multiple times and tested in isolation.
7
+ */
8
+ import { type Logger, type Metrics } from "@dwk/log";
9
+ import type { Link } from "@dwk/webfinger";
10
+ import { type HostMetaDocument } from "./document";
11
+ /** Configuration passed to {@link createHostMeta}. */
12
+ export interface HostMetaConfig {
13
+ /**
14
+ * The site's WebFinger endpoint URL (e.g.
15
+ * `https://example.com/.well-known/webfinger`). When supplied, an `lrdd` link
16
+ * templated to it (`…?resource={uri}`) is emitted first — the relation
17
+ * fediverse/OpenID software fetches host-meta to find before falling back to
18
+ * WebFinger. A URL already containing `{uri}` is used verbatim.
19
+ */
20
+ readonly webfingerUrl?: string;
21
+ /**
22
+ * Additional static top-level `Link` entries to advertise (e.g. `author`,
23
+ * `license`). Appended after the auto-generated `lrdd` link. At least one of
24
+ * {@link webfingerUrl} or `links` MUST be supplied, or {@link createHostMeta}
25
+ * throws at construction time.
26
+ */
27
+ readonly links?: readonly Link[];
28
+ /** Optional subject the metadata describes — the host itself (XRD `Subject`). */
29
+ readonly subject?: string;
30
+ /** Optional host-level properties; a `null` value means "present but absent". */
31
+ readonly properties?: Readonly<Record<string, string | null>>;
32
+ /**
33
+ * Logger for discovery events; defaults to a no-op. Wire a real logger (see
34
+ * `@dwk/log`) to surface which representation was served and why a request was
35
+ * rejected instead of swallowing them.
36
+ */
37
+ readonly logger?: Logger;
38
+ /**
39
+ * Metrics sink for discovery counters; defaults to a no-op. Wire an adapter
40
+ * (e.g. `analyticsEngineMetrics` from `@dwk/log`) to chart the same events the
41
+ * logger names — "served/min" by format, "rejection rate".
42
+ */
43
+ readonly metrics?: Metrics;
44
+ }
45
+ /** Configuration after defaults are filled and the document is pre-computed. */
46
+ export interface ResolvedConfig {
47
+ /** The host-meta document, built once at construction (it never varies per request). */
48
+ readonly document: HostMetaDocument;
49
+ /** The XRD representation, serialized once at construction. */
50
+ readonly xrdBody: string;
51
+ /** The JRD representation, serialized once at construction. */
52
+ readonly jrdBody: string;
53
+ readonly logger: Logger;
54
+ readonly metrics: Metrics;
55
+ }
56
+ /**
57
+ * Validate and normalise a {@link HostMetaConfig}. Fails loudly when neither a
58
+ * WebFinger URL nor any static link is configured — a host-meta document that
59
+ * advertises nothing is always a misconfiguration, not a silently empty `XRD`.
60
+ * The document is computed once here (it is request-invariant) and reused for
61
+ * every response.
62
+ */
63
+ export declare function resolveConfig(config: HostMetaConfig): ResolvedConfig;
64
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAA2B,KAAK,MAAM,EAAE,KAAK,OAAO,EAAE,MAAM,UAAU,CAAC;AAC9E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAiB,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAIlE,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;IACjC,iFAAiF;IACjF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,iFAAiF;IACjF,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IAC9D;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,gFAAgF;AAChF,MAAM,WAAW,cAAc;IAC7B,wFAAwF;IACxF,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IACpC,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CA0BpE"}
package/dist/config.js ADDED
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Configuration for {@link createHostMeta}: the WebFinger endpoint URL that
3
+ * seeds the `lrdd` template and/or a set of static top-level links, plus the
4
+ * optional logging/metrics seams. Per the composition contract the package
5
+ * never reads the global environment — every link arrives here, so a handler
6
+ * can be instantiated multiple times and tested in isolation.
7
+ */
8
+ import { noopLogger, noopMetrics } from "@dwk/log";
9
+ import { buildDocument } from "./document";
10
+ import { serializeJrd } from "./jrd";
11
+ import { serializeXrd } from "./xrd";
12
+ /**
13
+ * Validate and normalise a {@link HostMetaConfig}. Fails loudly when neither a
14
+ * WebFinger URL nor any static link is configured — a host-meta document that
15
+ * advertises nothing is always a misconfiguration, not a silently empty `XRD`.
16
+ * The document is computed once here (it is request-invariant) and reused for
17
+ * every response.
18
+ */
19
+ export function resolveConfig(config) {
20
+ const hasLinks = (config.links?.length ?? 0) > 0;
21
+ if (config.webfingerUrl === undefined && !hasLinks) {
22
+ throw new Error("@dwk/host-meta: configure `webfingerUrl` and/or `links` — a host-meta " +
23
+ "document must advertise at least one link.");
24
+ }
25
+ // The document is request-invariant, so both representations are serialized
26
+ // once here and reused verbatim for every response — no per-request XML
27
+ // serialization or JSON.stringify.
28
+ const document = buildDocument({
29
+ webfingerUrl: config.webfingerUrl,
30
+ links: config.links,
31
+ subject: config.subject,
32
+ properties: config.properties,
33
+ });
34
+ return {
35
+ document,
36
+ xrdBody: serializeXrd(document),
37
+ jrdBody: serializeJrd(document),
38
+ logger: config.logger ?? noopLogger,
39
+ metrics: config.metrics ?? noopMetrics,
40
+ };
41
+ }
42
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAA6B,MAAM,UAAU,CAAC;AAG9E,OAAO,EAAE,aAAa,EAAyB,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAiDrC;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,MAAsB;IAClD,MAAM,QAAQ,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IACjD,IAAI,MAAM,CAAC,YAAY,KAAK,SAAS,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CACb,wEAAwE;YACtE,4CAA4C,CAC/C,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,wEAAwE;IACxE,mCAAmC;IACnC,MAAM,QAAQ,GAAG,aAAa,CAAC;QAC7B,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,UAAU,EAAE,MAAM,CAAC,UAAU;KAC9B,CAAC,CAAC;IAEH,OAAO;QACL,QAAQ;QACR,OAAO,EAAE,YAAY,CAAC,QAAQ,CAAC;QAC/B,OAAO,EAAE,YAAY,CAAC,QAAQ,CAAC;QAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,UAAU;QACnC,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,WAAW;KACvC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The host-meta document model (RFC 6415 §2): a host-wide set of top-level
3
+ * `Link`s plus optional `Subject`/`Property` metadata. The model is a single
4
+ * plain-data shape that **both** representations render from — the XRD
5
+ * (`application/xrd+xml`) serializer in `xrd.ts` and the JRD
6
+ * (`application/jrd+json`) serializer in `jrd.ts` — so the two are guaranteed
7
+ * information-equivalent (RFC 6415 §3).
8
+ *
9
+ * The `Link` type is reused verbatim from `@dwk/webfinger`: host-meta links are
10
+ * the same `rel`/`href`/`template`/`type`/`titles`/`properties` shape WebFinger
11
+ * already defines, so the JRD link-shaping is shared and only the XRD serializer
12
+ * is new (see the package spec).
13
+ */
14
+ import type { Link } from "@dwk/webfinger";
15
+ export type { Link };
16
+ /**
17
+ * A resolved host-meta document. `links` is always present (it is the point of
18
+ * the document); `subject` and `properties` appear only when the operator
19
+ * configured them. This is what both serializers consume.
20
+ */
21
+ export interface HostMetaDocument {
22
+ /** Optional subject the metadata describes — the host itself (XRD `Subject`). */
23
+ readonly subject?: string;
24
+ /** Optional host-level properties; a `null` value means "present but absent". */
25
+ readonly properties?: Readonly<Record<string, string | null>>;
26
+ /** The top-level link relations advertised for the host. */
27
+ readonly links: readonly Link[];
28
+ }
29
+ /**
30
+ * Build the `lrdd` `template` from a WebFinger endpoint URL. The Link-based
31
+ * Resource Descriptor Document template is the URL clients expand with the
32
+ * queried resource (`{uri}`) to reach per-account discovery (RFC 6415 §3,
33
+ * RFC 7033 §10.1). If the supplied URL already contains a `{uri}` placeholder it
34
+ * is used as-is; otherwise a `resource={uri}` query parameter is appended.
35
+ */
36
+ export declare function lrddTemplate(webfingerUrl: string): string;
37
+ /**
38
+ * Assemble the host-meta {@link HostMetaDocument} from the operator's inputs.
39
+ * When a WebFinger endpoint URL is supplied, an `lrdd` link templated to it is
40
+ * emitted **first** (the relation fediverse/OpenID software probes for before
41
+ * falling back to WebFinger), followed by any static operator-configured links.
42
+ */
43
+ export declare function buildDocument(opts: {
44
+ readonly webfingerUrl?: string;
45
+ readonly links?: readonly Link[];
46
+ readonly subject?: string;
47
+ readonly properties?: Readonly<Record<string, string | null>>;
48
+ }): HostMetaDocument;
49
+ //# sourceMappingURL=document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAE3C,YAAY,EAAE,IAAI,EAAE,CAAC;AAErB;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,iFAAiF;IACjF,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IAC9D,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;CACjC;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAIzD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;CAC/D,GAAG,gBAAgB,CAyBnB"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The host-meta document model (RFC 6415 §2): a host-wide set of top-level
3
+ * `Link`s plus optional `Subject`/`Property` metadata. The model is a single
4
+ * plain-data shape that **both** representations render from — the XRD
5
+ * (`application/xrd+xml`) serializer in `xrd.ts` and the JRD
6
+ * (`application/jrd+json`) serializer in `jrd.ts` — so the two are guaranteed
7
+ * information-equivalent (RFC 6415 §3).
8
+ *
9
+ * The `Link` type is reused verbatim from `@dwk/webfinger`: host-meta links are
10
+ * the same `rel`/`href`/`template`/`type`/`titles`/`properties` shape WebFinger
11
+ * already defines, so the JRD link-shaping is shared and only the XRD serializer
12
+ * is new (see the package spec).
13
+ */
14
+ /**
15
+ * Build the `lrdd` `template` from a WebFinger endpoint URL. The Link-based
16
+ * Resource Descriptor Document template is the URL clients expand with the
17
+ * queried resource (`{uri}`) to reach per-account discovery (RFC 6415 §3,
18
+ * RFC 7033 §10.1). If the supplied URL already contains a `{uri}` placeholder it
19
+ * is used as-is; otherwise a `resource={uri}` query parameter is appended.
20
+ */
21
+ export function lrddTemplate(webfingerUrl) {
22
+ if (webfingerUrl.includes("{uri}"))
23
+ return webfingerUrl;
24
+ const separator = webfingerUrl.includes("?") ? "&" : "?";
25
+ return `${webfingerUrl}${separator}resource={uri}`;
26
+ }
27
+ /**
28
+ * Assemble the host-meta {@link HostMetaDocument} from the operator's inputs.
29
+ * When a WebFinger endpoint URL is supplied, an `lrdd` link templated to it is
30
+ * emitted **first** (the relation fediverse/OpenID software probes for before
31
+ * falling back to WebFinger), followed by any static operator-configured links.
32
+ */
33
+ export function buildDocument(opts) {
34
+ const links = [];
35
+ if (opts.webfingerUrl !== undefined) {
36
+ links.push({
37
+ rel: "lrdd",
38
+ type: "application/jrd+json",
39
+ template: lrddTemplate(opts.webfingerUrl),
40
+ });
41
+ }
42
+ if (opts.links) {
43
+ links.push(...opts.links);
44
+ }
45
+ const document = { links };
46
+ if (opts.subject !== undefined) {
47
+ document.subject = opts.subject;
48
+ }
49
+ if (opts.properties && Object.keys(opts.properties).length > 0) {
50
+ document.properties = opts.properties;
51
+ }
52
+ return document;
53
+ }
54
+ //# sourceMappingURL=document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.js","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAoBH;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,YAAoB;IAC/C,IAAI,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,YAAY,CAAC;IACxD,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACzD,OAAO,GAAG,YAAY,GAAG,SAAS,gBAAgB,CAAC;AACrD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAK7B;IACC,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,IAAI,IAAI,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACpC,KAAK,CAAC,IAAI,CAAC;YACT,GAAG,EAAE,MAAM;YACX,IAAI,EAAE,sBAAsB;YAC5B,QAAQ,EAAE,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC;SAC1C,CAAC,CAAC;IACL,CAAC;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IAED,MAAM,QAAQ,GAIV,EAAE,KAAK,EAAE,CAAC;IACd,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC/B,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAClC,CAAC;IACD,IAAI,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/D,QAAQ,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;IACxC,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The host-meta fetch handler (RFC 6415): a stateless `GET` endpoint, mountable
3
+ * at both `/.well-known/host-meta` and `/.well-known/host-meta.json`, that
4
+ * serves one request-invariant document in either the XRD (`application/xrd+xml`,
5
+ * the default) or JRD (`application/jrd+json`) representation depending on
6
+ * content negotiation. Discovery data is public, so every response carries
7
+ * permissive CORS (mirroring RFC 7033 §10.2).
8
+ */
9
+ import { type HostMetaConfig } from "./config";
10
+ /**
11
+ * Cloudflare bindings required by the host-meta handler: **none**. The document
12
+ * is config-supplied (composition contract), so this fragment is empty and
13
+ * contributes nothing to the composed Worker's `Env`.
14
+ */
15
+ export type HostMetaEnv = Record<never, never>;
16
+ /** A `fetch`-compatible Worker handler. */
17
+ export type HostMetaHandler = (request: Request, env: HostMetaEnv, ctx: ExecutionContext) => Promise<Response>;
18
+ /**
19
+ * Build the host-meta handler from configuration.
20
+ *
21
+ * The returned handler is mountable at `/.well-known/host-meta` and
22
+ * `/.well-known/host-meta.json`. It accepts `GET` (and `HEAD`) and returns
23
+ * `200` with the host-meta document: XRD by default, JRD when the client
24
+ * prefers it (`?format=json`, the `host-meta.json` path, or an Accept header
25
+ * favouring `application/jrd+json`). `OPTIONS` returns a CORS preflight; other
26
+ * methods get `405`. Fails loudly at construction if no link source is
27
+ * configured.
28
+ */
29
+ export declare function createHostMeta(config: HostMetaConfig): HostMetaHandler;
30
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,UAAU,CAAC;AAIlB;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAE/C,2CAA2C;AAC3C,MAAM,MAAM,eAAe,GAAG,CAC5B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,gBAAgB,KAClB,OAAO,CAAC,QAAQ,CAAC,CAAC;AAqEvB;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,CA+CtE"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * The host-meta fetch handler (RFC 6415): a stateless `GET` endpoint, mountable
3
+ * at both `/.well-known/host-meta` and `/.well-known/host-meta.json`, that
4
+ * serves one request-invariant document in either the XRD (`application/xrd+xml`,
5
+ * the default) or JRD (`application/jrd+json`) representation depending on
6
+ * content negotiation. Discovery data is public, so every response carries
7
+ * permissive CORS (mirroring RFC 7033 §10.2).
8
+ */
9
+ import {} from "@dwk/log";
10
+ import { resolveConfig, } from "./config";
11
+ import { HostMetaLogEvent } from "./log";
12
+ import { negotiateFormat } from "./negotiation";
13
+ /** Media type for an XRD document (RFC 6415 §2). */
14
+ const XRD_CONTENT_TYPE = "application/xrd+xml; charset=utf-8";
15
+ /** Media type for a JSON Resource Descriptor (RFC 7033 §10.2). */
16
+ const JRD_CONTENT_TYPE = "application/jrd+json; charset=utf-8";
17
+ const TEXT_CONTENT_TYPE = "text/plain; charset=utf-8";
18
+ /**
19
+ * host-meta serves public discovery data to any origin, so every response —
20
+ * success or error — advertises permissive CORS, mirroring the WebFinger policy
21
+ * (RFC 7033 §10.2) it links to.
22
+ */
23
+ const CORS_HEADERS = {
24
+ "access-control-allow-origin": "*",
25
+ };
26
+ /**
27
+ * Emit a structured event on both the logger and the metrics seam, which share
28
+ * one event vocabulary (see `@dwk/log`): `warn` for rejections, `info` for a
29
+ * served document.
30
+ */
31
+ function emit(config, level, event, fields) {
32
+ config.logger[level](event, fields);
33
+ config.metrics.count(event, fields);
34
+ }
35
+ function documentResponse(body, contentLength, format, method) {
36
+ return new Response(method === "HEAD" ? null : body, {
37
+ status: 200,
38
+ headers: {
39
+ "content-type": format === "jrd" ? JRD_CONTENT_TYPE : XRD_CONTENT_TYPE,
40
+ // RFC 9110 §9.3.2: a HEAD response carries the same Content-Length the
41
+ // corresponding GET would, so it is set explicitly (the body is dropped
42
+ // for HEAD, which would otherwise omit the header).
43
+ "content-length": contentLength,
44
+ // The representation varies on the negotiated content type; advertise it
45
+ // so shared caches key XRD and JRD responses separately.
46
+ vary: "Accept",
47
+ ...CORS_HEADERS,
48
+ },
49
+ });
50
+ }
51
+ function errorResponse(status, message, extraHeaders) {
52
+ return new Response(message, {
53
+ status,
54
+ headers: {
55
+ "content-type": TEXT_CONTENT_TYPE,
56
+ ...CORS_HEADERS,
57
+ ...extraHeaders,
58
+ },
59
+ });
60
+ }
61
+ /**
62
+ * Build the host-meta handler from configuration.
63
+ *
64
+ * The returned handler is mountable at `/.well-known/host-meta` and
65
+ * `/.well-known/host-meta.json`. It accepts `GET` (and `HEAD`) and returns
66
+ * `200` with the host-meta document: XRD by default, JRD when the client
67
+ * prefers it (`?format=json`, the `host-meta.json` path, or an Accept header
68
+ * favouring `application/jrd+json`). `OPTIONS` returns a CORS preflight; other
69
+ * methods get `405`. Fails loudly at construction if no link source is
70
+ * configured.
71
+ */
72
+ export function createHostMeta(config) {
73
+ const resolved = resolveConfig(config);
74
+ // Pre-serialized bodies and their byte lengths, computed once: nothing about
75
+ // the document varies per request, so a response is pure header assembly.
76
+ const byteLength = (body) => String(new TextEncoder().encode(body).length);
77
+ const representations = {
78
+ xrd: { body: resolved.xrdBody, length: byteLength(resolved.xrdBody) },
79
+ jrd: { body: resolved.jrdBody, length: byteLength(resolved.jrdBody) },
80
+ };
81
+ return async (request, _env, _ctx) => {
82
+ const method = request.method;
83
+ if (method === "OPTIONS") {
84
+ return new Response(null, {
85
+ status: 204,
86
+ headers: {
87
+ ...CORS_HEADERS,
88
+ "access-control-allow-methods": "GET, HEAD, OPTIONS",
89
+ "access-control-allow-headers": "*",
90
+ },
91
+ });
92
+ }
93
+ if (method !== "GET" && method !== "HEAD") {
94
+ emit(resolved, "warn", HostMetaLogEvent.Rejected, {
95
+ reason: "method_not_allowed",
96
+ });
97
+ return errorResponse(405, "method_not_allowed: use GET", {
98
+ allow: "GET, HEAD, OPTIONS",
99
+ });
100
+ }
101
+ const url = new URL(request.url);
102
+ const isJsonPath = url.pathname.endsWith(".json");
103
+ const format = negotiateFormat(url, request.headers.get("accept"), isJsonPath);
104
+ const { body, length } = representations[format];
105
+ emit(resolved, "info", HostMetaLogEvent.Served, { format });
106
+ return documentResponse(body, length, format, method);
107
+ };
108
+ }
109
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * `@dwk/host-meta` — Web Host Metadata (RFC 6415) host-meta discovery endpoint.
3
+ *
4
+ * Endpoint package: exports a factory returning a `fetch`-compatible handler,
5
+ * mountable at `/.well-known/host-meta` and `/.well-known/host-meta.json` so it
6
+ * composes with other `@dwk` packages in one Worker. It serves one host-wide
7
+ * resource-discovery document — a set of top-level `Link`s, at minimum an
8
+ * `lrdd` template pointing at the site's WebFinger endpoint — in either the XRD
9
+ * (`application/xrd+xml`, the default) or JRD (`application/jrd+json`)
10
+ * representation, chosen by content negotiation (RFC 6415 §3, RFC 7033 §10.1).
11
+ *
12
+ * It exists because host-meta is **borderline static**: a single-identity site
13
+ * can emit fixed files, but only request logic can negotiate XRD ⇄ JRD from the
14
+ * one URL and template a dynamic `lrdd` link. The package is pure and stateless
15
+ * — the document is config-supplied (a WebFinger URL and/or static links),
16
+ * never read from the global environment — so it ships no Durable Object and
17
+ * needs no bindings, and the serializers unit-test under Node without a Workers
18
+ * runtime. The XRD serializer is the only surface new to this package; the JRD
19
+ * link shaping is reused from `@dwk/webfinger`.
20
+ *
21
+ * @see spec/packages/host-meta.md
22
+ * @packageDocumentation
23
+ */
24
+ export { createHostMeta } from "./handler";
25
+ export type { HostMetaEnv, HostMetaHandler } from "./handler";
26
+ export { resolveConfig, type HostMetaConfig, type ResolvedConfig, } from "./config";
27
+ export { buildDocument, lrddTemplate, type HostMetaDocument, type Link, } from "./document";
28
+ export { buildHostMetaJrd, serializeJrd, type HostMetaJrd } from "./jrd";
29
+ export { serializeXrd, escapeXml, XRD_NAMESPACE } from "./xrd";
30
+ export { negotiateFormat, type Format } from "./negotiation";
31
+ export { HostMetaLogEvent } from "./log";
32
+ export type { Logger, Metrics } from "@dwk/log";
33
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE9D,OAAO,EACL,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,aAAa,EACb,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,IAAI,GACV,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,OAAO,CAAC;AAEzE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE/D,OAAO,EAAE,eAAe,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAE7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AACzC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `@dwk/host-meta` — Web Host Metadata (RFC 6415) host-meta discovery endpoint.
3
+ *
4
+ * Endpoint package: exports a factory returning a `fetch`-compatible handler,
5
+ * mountable at `/.well-known/host-meta` and `/.well-known/host-meta.json` so it
6
+ * composes with other `@dwk` packages in one Worker. It serves one host-wide
7
+ * resource-discovery document — a set of top-level `Link`s, at minimum an
8
+ * `lrdd` template pointing at the site's WebFinger endpoint — in either the XRD
9
+ * (`application/xrd+xml`, the default) or JRD (`application/jrd+json`)
10
+ * representation, chosen by content negotiation (RFC 6415 §3, RFC 7033 §10.1).
11
+ *
12
+ * It exists because host-meta is **borderline static**: a single-identity site
13
+ * can emit fixed files, but only request logic can negotiate XRD ⇄ JRD from the
14
+ * one URL and template a dynamic `lrdd` link. The package is pure and stateless
15
+ * — the document is config-supplied (a WebFinger URL and/or static links),
16
+ * never read from the global environment — so it ships no Durable Object and
17
+ * needs no bindings, and the serializers unit-test under Node without a Workers
18
+ * runtime. The XRD serializer is the only surface new to this package; the JRD
19
+ * link shaping is reused from `@dwk/webfinger`.
20
+ *
21
+ * @see spec/packages/host-meta.md
22
+ * @packageDocumentation
23
+ */
24
+ export { createHostMeta } from "./handler";
25
+ export { resolveConfig, } from "./config";
26
+ export { buildDocument, lrddTemplate, } from "./document";
27
+ export { buildHostMetaJrd, serializeJrd } from "./jrd";
28
+ export { serializeXrd, escapeXml, XRD_NAMESPACE } from "./xrd";
29
+ export { negotiateFormat } from "./negotiation";
30
+ export { HostMetaLogEvent } from "./log";
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,EACL,aAAa,GAGd,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,aAAa,EACb,YAAY,GAGb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAoB,MAAM,OAAO,CAAC;AAEzE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE/D,OAAO,EAAE,eAAe,EAAe,MAAM,eAAe,CAAC;AAE7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC"}
package/dist/jrd.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * JRD (JSON Resource Descriptor) rendering of a host-meta document
3
+ * (RFC 7033 §10.1, the `host-meta.json` variant of RFC 6415). The host-meta
4
+ * `Link` objects are already the WebFinger JRD link shape — reused verbatim
5
+ * from `@dwk/webfinger` — so rendering is just assembling the top-level members
6
+ * and dropping any the document did not supply, mirroring `@dwk/webfinger`'s
7
+ * `buildJrd`. The XRD serializer (`xrd.ts`) renders the *same* document model,
8
+ * keeping the two representations information-equivalent.
9
+ */
10
+ import type { HostMetaDocument, Link } from "./document";
11
+ /**
12
+ * A fully-rendered host-meta JRD. `links` is always present (possibly empty);
13
+ * `subject` and `properties` appear only when the document supplied them, so the
14
+ * JSON stays minimal.
15
+ */
16
+ export interface HostMetaJrd {
17
+ /** The subject the metadata describes, when configured. */
18
+ readonly subject?: string;
19
+ /** Host-level properties, when configured. */
20
+ readonly properties?: Readonly<Record<string, string | null>>;
21
+ /** The top-level link set. */
22
+ readonly links: readonly Link[];
23
+ }
24
+ /**
25
+ * Shape a {@link HostMetaDocument} into its {@link HostMetaJrd} object: carry
26
+ * through `subject`/`properties` only when present, and emit the links as-is
27
+ * (they are already JRD-shaped). Exposed so callers can embed the JRD object
28
+ * rather than re-parse the serialized string.
29
+ */
30
+ export declare function buildHostMetaJrd(document: HostMetaDocument): HostMetaJrd;
31
+ /** Serialize a host-meta document to a JRD (`application/jrd+json`) body. */
32
+ export declare function serializeJrd(document: HostMetaDocument): string;
33
+ //# sourceMappingURL=jrd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jrd.d.ts","sourceRoot":"","sources":["../src/jrd.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEzD;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,8CAA8C;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IAC9D,8BAA8B;IAC9B,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;CACjC;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,WAAW,CAaxE;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAE/D"}
package/dist/jrd.js ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * JRD (JSON Resource Descriptor) rendering of a host-meta document
3
+ * (RFC 7033 §10.1, the `host-meta.json` variant of RFC 6415). The host-meta
4
+ * `Link` objects are already the WebFinger JRD link shape — reused verbatim
5
+ * from `@dwk/webfinger` — so rendering is just assembling the top-level members
6
+ * and dropping any the document did not supply, mirroring `@dwk/webfinger`'s
7
+ * `buildJrd`. The XRD serializer (`xrd.ts`) renders the *same* document model,
8
+ * keeping the two representations information-equivalent.
9
+ */
10
+ /**
11
+ * Shape a {@link HostMetaDocument} into its {@link HostMetaJrd} object: carry
12
+ * through `subject`/`properties` only when present, and emit the links as-is
13
+ * (they are already JRD-shaped). Exposed so callers can embed the JRD object
14
+ * rather than re-parse the serialized string.
15
+ */
16
+ export function buildHostMetaJrd(document) {
17
+ const jrd = { links: document.links };
18
+ if (document.subject !== undefined) {
19
+ jrd.subject = document.subject;
20
+ }
21
+ if (document.properties && Object.keys(document.properties).length > 0) {
22
+ jrd.properties = document.properties;
23
+ }
24
+ return jrd;
25
+ }
26
+ /** Serialize a host-meta document to a JRD (`application/jrd+json`) body. */
27
+ export function serializeJrd(document) {
28
+ return JSON.stringify(buildHostMetaJrd(document));
29
+ }
30
+ //# sourceMappingURL=jrd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jrd.js","sourceRoot":"","sources":["../src/jrd.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAkBH;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAA0B;IACzD,MAAM,GAAG,GAIL,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;IAC9B,IAAI,QAAQ,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACnC,GAAG,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;IACjC,CAAC;IACD,IAAI,QAAQ,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvE,GAAG,CAAC,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC;IACvC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,YAAY,CAAC,QAA0B;IACrD,OAAO,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC;AACpD,CAAC"}