@estiva-app/interop 0.10.1 → 0.11.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/CHANGELOG.md +30 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/objectUrl.d.ts +26 -0
- package/dist/objectUrl.d.ts.map +1 -1
- package/dist/objectUrl.js +39 -2
- package/dist/objectUrl.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/objectUrl.ts +51 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,36 @@ how a declaration is read, is a MAJOR — in `0.x`, a MINOR — even when no
|
|
|
6
6
|
TypeScript signature moved. A consumer upgrading must be able to tell whether
|
|
7
7
|
manifests already published still mean what they meant.
|
|
8
8
|
|
|
9
|
+
## 0.11.0 — 2026-09-04
|
|
10
|
+
|
|
11
|
+
**A second identity shape: an event id, for an object that has no `d`.** No
|
|
12
|
+
existing manifest changes meaning; a pattern declaring `<id>` instead of `<d>`
|
|
13
|
+
is new, and `MatchedObjectUrl` gains `by` so a consumer knows which query to
|
|
14
|
+
make.
|
|
15
|
+
|
|
16
|
+
RFC 0.5 §7.6 recorded a message as *addressed by `nevent`, outside this
|
|
17
|
+
grammar*. PEE-17 needed a link to one, and following that literally would have
|
|
18
|
+
put bech32 in a URL — which §7.3 argues against on two grounds that apply to
|
|
19
|
+
`nevent` exactly as they do to `naddr`: a slice of it is checksum bytes rather
|
|
20
|
+
than the identity, and relay hints are part of the encoding, so one object has
|
|
21
|
+
more than one spelling.
|
|
22
|
+
|
|
23
|
+
**A raw event id has neither problem**, and resolves with `{ids: ["<id>"]}` —
|
|
24
|
+
one query, no index, the same property §7.2 chose the bare uuid for. So the rule
|
|
25
|
+
generalises rather than gaining an exception: the identity goes in the path,
|
|
26
|
+
unencoded, and everything before it is decoration.
|
|
27
|
+
|
|
28
|
+
- **`eventIdFromRef`** reads a 64-character id off a ref's tail.
|
|
29
|
+
- **`MatchedObjectUrl.by`** is `'d'` or `'id'` — the difference between
|
|
30
|
+
`{"#d": […]}` and `{ids: […]}`.
|
|
31
|
+
- **The pattern decides which**, by carrying `<id>` or `<d>`. Not sniffed from
|
|
32
|
+
the value: a uuid and a 64-hex string are distinguishable today, and a
|
|
33
|
+
consumer relying on that would be inferring an app's addressing model from a
|
|
34
|
+
character class.
|
|
35
|
+
|
|
36
|
+
Exactly 64 hex is required. A shorter run is a truncated id, which resolves to
|
|
37
|
+
nothing or — worse — to something nobody intended.
|
|
38
|
+
|
|
9
39
|
## 0.10.1 — 2026-09-03
|
|
10
40
|
|
|
11
41
|
**`matchObjectUrl` matched no fragment route at all.** The parser's path capture
|
package/dist/index.d.ts
CHANGED
|
@@ -33,5 +33,5 @@ export { resolveManifest, resolveForeignObject, resolveForeignEvent, resolveFold
|
|
|
33
33
|
* question: those resolve an object you already hold, these turn a string
|
|
34
34
|
* somebody pasted into one you can hold.
|
|
35
35
|
*/
|
|
36
|
-
export { slugify, objectRef, identifierFromRef, urlPatternsOf, matchObjectUrl, type UrlPattern, type MatchedObjectUrl, } from './objectUrl.js';
|
|
36
|
+
export { slugify, objectRef, identifierFromRef, eventIdFromRef, urlPatternsOf, matchObjectUrl, type UrlPattern, type MatchedObjectUrl, } from './objectUrl.js';
|
|
37
37
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,SAAS,EACT,kBAAkB,EAClB,qBAAqB,EACrB,KAAK,aAAa,EAClB,cAAc,EACd,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,cAAc,EACd,KAAK,mBAAmB,GACzB,MAAM,iBAAiB,CAAA;AAExB;;;;;;;GAOG;AACH,OAAO,EACL,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,KAAK,UAAU,EACf,KAAK,gBAAgB,GACtB,MAAM,gBAAgB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,SAAS,EACT,kBAAkB,EAClB,qBAAqB,EACrB,KAAK,aAAa,EAClB,cAAc,EACd,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,cAAc,EACd,KAAK,mBAAmB,GACzB,MAAM,iBAAiB,CAAA;AAExB;;;;;;;GAOG;AACH,OAAO,EACL,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,cAAc,EACd,KAAK,UAAU,EACf,KAAK,gBAAgB,GACtB,MAAM,gBAAgB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -33,5 +33,5 @@ export { resolveManifest, resolveForeignObject, resolveForeignEvent, resolveFold
|
|
|
33
33
|
* question: those resolve an object you already hold, these turn a string
|
|
34
34
|
* somebody pasted into one you can hold.
|
|
35
35
|
*/
|
|
36
|
-
export { slugify, objectRef, identifierFromRef, urlPatternsOf, matchObjectUrl, } from './objectUrl.js';
|
|
36
|
+
export { slugify, objectRef, identifierFromRef, eventIdFromRef, urlPatternsOf, matchObjectUrl, } from './objectUrl.js';
|
|
37
37
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,SAAS,EACT,kBAAkB,EAClB,qBAAqB,EAErB,cAAc,EACd,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EAenB,cAAc,GAEf,MAAM,iBAAiB,CAAA;AAExB;;;;;;;GAOG;AACH,OAAO,EACL,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,aAAa,EACb,cAAc,GAGf,MAAM,gBAAgB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,SAAS,EACT,kBAAkB,EAClB,qBAAqB,EAErB,cAAc,EACd,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EAenB,cAAc,GAEf,MAAM,iBAAiB,CAAA;AAExB;;;;;;;GAOG;AACH,OAAO,EACL,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,cAAc,GAGf,MAAM,gBAAgB,CAAA"}
|
package/dist/objectUrl.d.ts
CHANGED
|
@@ -48,6 +48,22 @@ export declare function objectRef(identifier: string, title?: string): string;
|
|
|
48
48
|
* rather than silently miss.
|
|
49
49
|
*/
|
|
50
50
|
export declare function identifierFromRef(ref: string): string | null;
|
|
51
|
+
/**
|
|
52
|
+
* The event id out of a ref, or null.
|
|
53
|
+
*
|
|
54
|
+
* **Why an event id in a URL rather than an `nevent`.** §7.3 rejects bech32 in
|
|
55
|
+
* a URL on two grounds, and both apply to `nevent` exactly as they do to
|
|
56
|
+
* `naddr`: a slice of it is checksum bytes rather than the identity, and relay
|
|
57
|
+
* hints are part of the encoding, so one object has more than one spelling.
|
|
58
|
+
*
|
|
59
|
+
* A raw event id has neither problem. It is the whole identity of an event that
|
|
60
|
+
* has no `d`, it is stable because nothing optional is encoded into it, and it
|
|
61
|
+
* resolves with `{ids: ["<id>"]}` — one query, no index, exactly the property
|
|
62
|
+
* §7.2 chose the bare uuid for. So the rule generalises rather than gaining an
|
|
63
|
+
* exception: **the identity goes in the path, unencoded, and everything before
|
|
64
|
+
* it is decoration.**
|
|
65
|
+
*/
|
|
66
|
+
export declare function eventIdFromRef(ref: string): string | null;
|
|
51
67
|
/**
|
|
52
68
|
* A URL shape an app says it serves — RFC 0.5 §7.5's `urls`, read off the
|
|
53
69
|
* manifest event the way `web` is.
|
|
@@ -78,7 +94,17 @@ export declare function urlPatternsOf(event: {
|
|
|
78
94
|
}): UrlPattern[];
|
|
79
95
|
/** What a pasted URL turned out to name. */
|
|
80
96
|
export interface MatchedObjectUrl {
|
|
97
|
+
/** A `d` for an addressable object, or an event id for one without. */
|
|
81
98
|
identifier: string;
|
|
99
|
+
/**
|
|
100
|
+
* How to resolve `identifier`: by `#d` for an addressable object, by `ids`
|
|
101
|
+
* for an event that has none.
|
|
102
|
+
*
|
|
103
|
+
* Declared by the pattern's placeholder — `<d>` or `<id>` — rather than
|
|
104
|
+
* guessed from the string, because a consumer that inspected the value would
|
|
105
|
+
* be deciding a kind's addressing model from the shape of a hex string.
|
|
106
|
+
*/
|
|
107
|
+
by: 'd' | 'id';
|
|
82
108
|
/** Present only when the matched pattern named one. */
|
|
83
109
|
kind?: number;
|
|
84
110
|
}
|
package/dist/objectUrl.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"objectUrl.d.ts","sourceRoot":"","sources":["../src/objectUrl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;
|
|
1
|
+
{"version":3,"file":"objectUrl.d.ts","sourceRoot":"","sources":["../src/objectUrl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AA2BH;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS7C;AAED,qFAAqF;AACrF,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGpE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG5D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGzD;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAA;IACf;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,mDAAmD;AACnD,wBAAgB,aAAa,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAA;CAAE,GAAG,UAAU,EAAE,CAQvE;AAED,4CAA4C;AAC5C,MAAM,WAAW,gBAAgB;IAC/B,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;;;OAOG;IACH,EAAE,EAAE,GAAG,GAAG,IAAI,CAAA;IACd,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,gBAAgB,GAAG,IAAI,CAmC3F"}
|
package/dist/objectUrl.js
CHANGED
|
@@ -40,6 +40,14 @@
|
|
|
40
40
|
* label that may itself contain hyphens, digits and hex.
|
|
41
41
|
*/
|
|
42
42
|
const TRAILING_UUID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
43
|
+
/**
|
|
44
|
+
* A 64-character event id at the very end of a ref.
|
|
45
|
+
*
|
|
46
|
+
* The second identity shape, for an object that has no `d` — a `kind:9`
|
|
47
|
+
* message is the case in hand, and §7.6 recorded it as outside the grammar
|
|
48
|
+
* until this. Anchored at the end for the same positional reason as the uuid.
|
|
49
|
+
*/
|
|
50
|
+
const TRAILING_EVENT_ID_RE = /(?:^|[^0-9a-f])([0-9a-f]{64})$/i;
|
|
43
51
|
/**
|
|
44
52
|
* Slugs are cut here. Long enough to stay recognisable in a chat client's link
|
|
45
53
|
* preview, short enough that the uuid is not pushed off the end of a rendered
|
|
@@ -77,6 +85,25 @@ export function identifierFromRef(ref) {
|
|
|
77
85
|
const match = TRAILING_UUID_RE.exec(ref);
|
|
78
86
|
return match ? match[0].toLowerCase() : null;
|
|
79
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* The event id out of a ref, or null.
|
|
90
|
+
*
|
|
91
|
+
* **Why an event id in a URL rather than an `nevent`.** §7.3 rejects bech32 in
|
|
92
|
+
* a URL on two grounds, and both apply to `nevent` exactly as they do to
|
|
93
|
+
* `naddr`: a slice of it is checksum bytes rather than the identity, and relay
|
|
94
|
+
* hints are part of the encoding, so one object has more than one spelling.
|
|
95
|
+
*
|
|
96
|
+
* A raw event id has neither problem. It is the whole identity of an event that
|
|
97
|
+
* has no `d`, it is stable because nothing optional is encoded into it, and it
|
|
98
|
+
* resolves with `{ids: ["<id>"]}` — one query, no index, exactly the property
|
|
99
|
+
* §7.2 chose the bare uuid for. So the rule generalises rather than gaining an
|
|
100
|
+
* exception: **the identity goes in the path, unencoded, and everything before
|
|
101
|
+
* it is decoration.**
|
|
102
|
+
*/
|
|
103
|
+
export function eventIdFromRef(ref) {
|
|
104
|
+
const match = TRAILING_EVENT_ID_RE.exec(ref);
|
|
105
|
+
return match ? match[1].toLowerCase() : null;
|
|
106
|
+
}
|
|
80
107
|
/** The `urls` shapes a manifest event declares. */
|
|
81
108
|
export function urlPatternsOf(event) {
|
|
82
109
|
const out = [];
|
|
@@ -122,10 +149,20 @@ export function matchObjectUrl(url, patterns) {
|
|
|
122
149
|
.every((segment, i) => segment === target.segments[i]);
|
|
123
150
|
if (!prefixMatches)
|
|
124
151
|
continue;
|
|
125
|
-
const
|
|
152
|
+
const tail = target.segments[target.segments.length - 1] ?? '';
|
|
153
|
+
/*
|
|
154
|
+
The pattern says which identity it carries. `<id>` is an event id, for a
|
|
155
|
+
kind with no `d`; anything else is the `<d>` uuid this grammar started
|
|
156
|
+
with. Read from the declaration rather than sniffed from the value: a
|
|
157
|
+
64-hex string and a uuid are distinguishable today, and a consumer that
|
|
158
|
+
relied on that would be inferring an app's addressing model from a
|
|
159
|
+
character class.
|
|
160
|
+
*/
|
|
161
|
+
const wantsEventId = shape.segments[shape.segments.length - 1]?.includes('<id>') ?? false;
|
|
162
|
+
const identifier = wantsEventId ? eventIdFromRef(tail) : identifierFromRef(tail);
|
|
126
163
|
if (!identifier)
|
|
127
164
|
continue;
|
|
128
|
-
return { identifier, ...(kind === undefined ? {} : { kind }) };
|
|
165
|
+
return { identifier, by: wantsEventId ? 'id' : 'd', ...(kind === undefined ? {} : { kind }) };
|
|
129
166
|
}
|
|
130
167
|
return null;
|
|
131
168
|
}
|
package/dist/objectUrl.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"objectUrl.js","sourceRoot":"","sources":["../src/objectUrl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH;;;;;;GAMG;AACH,MAAM,gBAAgB,GAAG,gEAAgE,CAAA;AAEzF;;;;GAIG;AACH,MAAM,eAAe,GAAG,EAAE,CAAA;AAE1B;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,OAAO,KAAK;SACT,SAAS,CAAC,MAAM,CAAC;SACjB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC;SACzB,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;AACxB,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,SAAS,CAAC,UAAkB,EAAE,KAAc;IAC1D,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IACxC,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC,UAAU,CAAA;AACpD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACxC,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AAC9C,CAAC;AA2BD,mDAAmD;AACnD,MAAM,UAAU,aAAa,CAAC,KAA2B;IACvD,MAAM,GAAG,GAAiB,EAAE,CAAA;IAC5B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,SAAQ;QAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QAChD,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;IAC5E,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;
|
|
1
|
+
{"version":3,"file":"objectUrl.js","sourceRoot":"","sources":["../src/objectUrl.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@estiva-app/interop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Render and act on another app's objects from its published NIP-89 manifest. The owner defines the projection; the consumer decides how it looks.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/src/index.ts
CHANGED
package/src/objectUrl.ts
CHANGED
|
@@ -42,6 +42,15 @@
|
|
|
42
42
|
*/
|
|
43
43
|
const TRAILING_UUID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* A 64-character event id at the very end of a ref.
|
|
47
|
+
*
|
|
48
|
+
* The second identity shape, for an object that has no `d` — a `kind:9`
|
|
49
|
+
* message is the case in hand, and §7.6 recorded it as outside the grammar
|
|
50
|
+
* until this. Anchored at the end for the same positional reason as the uuid.
|
|
51
|
+
*/
|
|
52
|
+
const TRAILING_EVENT_ID_RE = /(?:^|[^0-9a-f])([0-9a-f]{64})$/i
|
|
53
|
+
|
|
45
54
|
/**
|
|
46
55
|
* Slugs are cut here. Long enough to stay recognisable in a chat client's link
|
|
47
56
|
* preview, short enough that the uuid is not pushed off the end of a rendered
|
|
@@ -83,6 +92,26 @@ export function identifierFromRef(ref: string): string | null {
|
|
|
83
92
|
return match ? match[0].toLowerCase() : null
|
|
84
93
|
}
|
|
85
94
|
|
|
95
|
+
/**
|
|
96
|
+
* The event id out of a ref, or null.
|
|
97
|
+
*
|
|
98
|
+
* **Why an event id in a URL rather than an `nevent`.** §7.3 rejects bech32 in
|
|
99
|
+
* a URL on two grounds, and both apply to `nevent` exactly as they do to
|
|
100
|
+
* `naddr`: a slice of it is checksum bytes rather than the identity, and relay
|
|
101
|
+
* hints are part of the encoding, so one object has more than one spelling.
|
|
102
|
+
*
|
|
103
|
+
* A raw event id has neither problem. It is the whole identity of an event that
|
|
104
|
+
* has no `d`, it is stable because nothing optional is encoded into it, and it
|
|
105
|
+
* resolves with `{ids: ["<id>"]}` — one query, no index, exactly the property
|
|
106
|
+
* §7.2 chose the bare uuid for. So the rule generalises rather than gaining an
|
|
107
|
+
* exception: **the identity goes in the path, unencoded, and everything before
|
|
108
|
+
* it is decoration.**
|
|
109
|
+
*/
|
|
110
|
+
export function eventIdFromRef(ref: string): string | null {
|
|
111
|
+
const match = TRAILING_EVENT_ID_RE.exec(ref)
|
|
112
|
+
return match ? match[1].toLowerCase() : null
|
|
113
|
+
}
|
|
114
|
+
|
|
86
115
|
/**
|
|
87
116
|
* A URL shape an app says it serves — RFC 0.5 §7.5's `urls`, read off the
|
|
88
117
|
* manifest event the way `web` is.
|
|
@@ -121,7 +150,17 @@ export function urlPatternsOf(event: { tags: string[][] }): UrlPattern[] {
|
|
|
121
150
|
|
|
122
151
|
/** What a pasted URL turned out to name. */
|
|
123
152
|
export interface MatchedObjectUrl {
|
|
153
|
+
/** A `d` for an addressable object, or an event id for one without. */
|
|
124
154
|
identifier: string
|
|
155
|
+
/**
|
|
156
|
+
* How to resolve `identifier`: by `#d` for an addressable object, by `ids`
|
|
157
|
+
* for an event that has none.
|
|
158
|
+
*
|
|
159
|
+
* Declared by the pattern's placeholder — `<d>` or `<id>` — rather than
|
|
160
|
+
* guessed from the string, because a consumer that inspected the value would
|
|
161
|
+
* be deciding a kind's addressing model from the shape of a hex string.
|
|
162
|
+
*/
|
|
163
|
+
by: 'd' | 'id'
|
|
125
164
|
/** Present only when the matched pattern named one. */
|
|
126
165
|
kind?: number
|
|
127
166
|
}
|
|
@@ -157,9 +196,19 @@ export function matchObjectUrl(url: string, patterns: UrlPattern[]): MatchedObje
|
|
|
157
196
|
.every((segment, i) => segment === target.segments[i])
|
|
158
197
|
if (!prefixMatches) continue
|
|
159
198
|
|
|
160
|
-
const
|
|
199
|
+
const tail = target.segments[target.segments.length - 1] ?? ''
|
|
200
|
+
/*
|
|
201
|
+
The pattern says which identity it carries. `<id>` is an event id, for a
|
|
202
|
+
kind with no `d`; anything else is the `<d>` uuid this grammar started
|
|
203
|
+
with. Read from the declaration rather than sniffed from the value: a
|
|
204
|
+
64-hex string and a uuid are distinguishable today, and a consumer that
|
|
205
|
+
relied on that would be inferring an app's addressing model from a
|
|
206
|
+
character class.
|
|
207
|
+
*/
|
|
208
|
+
const wantsEventId = shape.segments[shape.segments.length - 1]?.includes('<id>') ?? false
|
|
209
|
+
const identifier = wantsEventId ? eventIdFromRef(tail) : identifierFromRef(tail)
|
|
161
210
|
if (!identifier) continue
|
|
162
|
-
return { identifier, ...(kind === undefined ? {} : { kind }) }
|
|
211
|
+
return { identifier, by: wantsEventId ? 'id' : 'd', ...(kind === undefined ? {} : { kind }) }
|
|
163
212
|
}
|
|
164
213
|
return null
|
|
165
214
|
}
|