@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 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
@@ -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"}
@@ -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
  }
@@ -1 +1 @@
1
- {"version":3,"file":"objectUrl.d.ts","sourceRoot":"","sources":["../src/objectUrl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAkBH;;;;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;;;;;;;;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,UAAU,EAAE,MAAM,CAAA;IAClB,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,CAyB3F"}
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 identifier = identifierFromRef(target.segments[target.segments.length - 1] ?? '');
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
  }
@@ -1 +1 @@
1
- {"version":3,"file":"objectUrl.js","sourceRoot":"","sources":["../src/objectUrl.ts"],"names":[],"mappings":"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"}
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.10.1",
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
@@ -75,6 +75,7 @@ export {
75
75
  slugify,
76
76
  objectRef,
77
77
  identifierFromRef,
78
+ eventIdFromRef,
78
79
  urlPatternsOf,
79
80
  matchObjectUrl,
80
81
  type UrlPattern,
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 identifier = identifierFromRef(target.segments[target.segments.length - 1] ?? '')
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
  }