@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 +15 -0
- package/README.md +90 -0
- package/dist/config.d.ts +64 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +42 -0
- package/dist/config.js.map +1 -0
- package/dist/document.d.ts +49 -0
- package/dist/document.d.ts.map +1 -0
- package/dist/document.js +54 -0
- package/dist/document.js.map +1 -0
- package/dist/handler.d.ts +30 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +109 -0
- package/dist/handler.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/jrd.d.ts +33 -0
- package/dist/jrd.d.ts.map +1 -0
- package/dist/jrd.js +30 -0
- package/dist/jrd.js.map +1 -0
- package/dist/log.d.ts +30 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +28 -0
- package/dist/log.js.map +1 -0
- package/dist/negotiation.d.ts +21 -0
- package/dist/negotiation.d.ts.map +1 -0
- package/dist/negotiation.js +62 -0
- package/dist/negotiation.js.map +1 -0
- package/dist/xrd.d.ts +24 -0
- package/dist/xrd.d.ts.map +1 -0
- package/dist/xrd.js +100 -0
- package/dist/xrd.js.map +1 -0
- package/package.json +49 -0
- package/src/config.ts +96 -0
- package/src/document.ts +82 -0
- package/src/handler.ts +159 -0
- package/src/index.ts +48 -0
- package/src/jrd.ts +51 -0
- package/src/log.ts +32 -0
- package/src/negotiation.ts +65 -0
- package/src/xrd.ts +114 -0
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.
|
package/dist/config.d.ts
ADDED
|
@@ -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"}
|
package/dist/document.js
ADDED
|
@@ -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"}
|
package/dist/handler.js
ADDED
|
@@ -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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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
package/dist/jrd.js.map
ADDED
|
@@ -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"}
|