nostr-attestations 1.0.0 → 2.0.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/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # nostr-attestations
2
2
 
3
- One Nostr event kind for all attestations — credentials, endorsements, vouches, provenance, licensing, and trust.
3
+ One Nostr event kind for all attestations — credentials, endorsements, vouches, provenance, licensing, and trust. Supports both direct attestation and assertion-first patterns within a single kind.
4
+
5
+ **Nostr:** [`npub1mgvlrnf5hm9yf0n5mf9nqmvarhvxkc6remu5ec3vf8r0txqkuk7su0e7q2`](https://njump.me/npub1mgvlrnf5hm9yf0n5mf9nqmvarhvxkc6remu5ec3vf8r0txqkuk7su0e7q2)
4
6
 
5
7
  ## Install
6
8
 
@@ -29,12 +31,34 @@ const event = createAttestation({
29
31
 
30
32
  No relay client, no signing library, no crypto. Bring your own. Works with nostr-tools, any Nostr SDK, or bare WebSocket code.
31
33
 
34
+ ### Assertion-First Pattern
35
+
36
+ Attest to someone else's claim — the individual makes an assertion, you verify it:
37
+
38
+ ```typescript
39
+ import { createAttestation } from 'nostr-attestations'
40
+
41
+ // Attest to a first-person assertion event
42
+ const event = createAttestation({
43
+ assertion: { id: '<assertion-event-id>', relay: 'wss://relay.example.com' },
44
+ subject: '<subject-pubkey>',
45
+ content: 'Identity verified in person',
46
+ })
47
+ // type is inferred from the referenced assertion — no type tag needed
48
+ ```
49
+
32
50
  ## Why This?
33
51
 
34
52
  Nostr has several ways to label or badge identities, but none designed for verifiable attestations. [NIP-58](https://github.com/nostr-protocol/nips/blob/master/58.md) badges are display-only — no expiry, no revocation, no structured claims. [NIP-85](https://github.com/nostr-protocol/nips/blob/master/85.md) covers social graph metrics, not arbitrary claims. [NIP-32](https://github.com/nostr-protocol/nips/blob/master/32.md) labels are lightweight but not individually replaceable per subject.
35
53
 
36
54
  nostr-attestations uses **one kind (31000) with a `type` tag** instead of inventing a new event kind for every attestation use case. Credentials, endorsements, vouches, licensing, provenance, and revocations all share the same event structure. New attestation types need zero protocol changes — just define a new `type` value.
37
55
 
56
+ It supports two attestation patterns within the same kind:
57
+ - **Direct attestation** — the attestor defines the type and makes a claim about a subject
58
+ - **Assertion-first** — the subject makes their own claim, and the attestor references and verifies it
59
+
60
+ The base layer is **semantically neutral** — it carries attestations but does not interpret them. Application profiles (identity verification, professional licensing, service reputation) are built downstream, not in the library.
61
+
38
62
  ## Revocation
39
63
 
40
64
  ```typescript
@@ -62,13 +86,20 @@ import { parseAttestation } from 'nostr-attestations'
62
86
  const attestation = parseAttestation(event)
63
87
  // {
64
88
  // kind: 31000,
65
- // type: 'credential',
89
+ // type: 'credential', // "assertion" for assertion-only attestations
66
90
  // pubkey: '<attestor-pubkey>',
67
91
  // createdAt: 1700000000,
68
92
  // identifier: '<subject-pubkey>',
69
93
  // subject: '<subject-pubkey>',
94
+ // assertionId: null, // event ID if assertion-first
95
+ // assertionAddress: null, // addressable coord if assertion-first
96
+ // assertionRelay: null, // relay hint for assertion
70
97
  // summary: 'Professional credential verified',
71
98
  // expiration: 1735689600,
99
+ // validFrom: null,
100
+ // validTo: null, // validity window end
101
+ // request: null, // what prompted this attestation
102
+ // schema: null, // machine-readable schema URI
72
103
  // revoked: false,
73
104
  // reason: null,
74
105
  // tags: [...],
@@ -103,9 +134,10 @@ const attestation = parseAttestation(event)
103
134
  | Function | Signature | Returns |
104
135
  |----------|-----------|---------|
105
136
  | `attestationFilter` | `(params: FilterParams) => NostrFilter` | Relay query filter |
106
- | `revocationFilter` | `(type: string, identifier: string) => NostrFilter` | Revocation check filter |
137
+ | `revocationFilter` | `(type, identifier)` or `({ assertionId \| assertionAddress })` | Revocation check filter |
107
138
  | `buildDTag` | `(type: string, identifier: string) => string` | `"type:identifier"` string |
108
- | `parseDTag` | `(dTag: string) => { type: string; identifier: string } \| null` | Parsed d-tag |
139
+ | `buildAssertionDTag` | `(ref: string) => string` | `"assertion:ref"` string |
140
+ | `parseDTag` | `(dTag: string) => { type: string; identifier: string } \| null` | Parsed d-tag (type is `"assertion"` for assertion-only) |
109
141
 
110
142
  ### Constants
111
143
 
@@ -116,15 +148,71 @@ const attestation = parseAttestation(event)
116
148
 
117
149
  ### Types
118
150
 
119
- `AttestationParams`, `RevocationParams`, `Attestation`, `ValidationResult`, `FilterParams`, `NostrFilter`, `NostrEvent`, `EventTemplate` — all exported from the package root.
151
+ `AssertionRef`, `AttestationParams`, `RevocationParams`, `Attestation`, `ValidationResult`, `FilterParams`, `NostrFilter`, `NostrEvent`, `EventTemplate` — all exported from the package root and from `nostr-attestations/types` (zero-runtime import).
120
152
 
121
153
  ## Test Vectors
122
154
 
123
- `vectors/attestations.json` contains 10 frozen conformance test vectors covering the full range of attestation types (credential, endorsement, vouch, verifier, provenance) and states (active, revoked, self-attestation). Any conformant implementation must produce identical parse results from these inputs. The vectors are pinned — if tests against them fail, the implementation is broken, not the vector.
155
+ `vectors/attestations.json` contains 16 frozen conformance test vectors covering the full range of attestation types (credential, endorsement, vouch, verifier, provenance) and states (active, revoked, self-attestation). Any conformant implementation must produce identical parse results from these inputs. The vectors are pinned — if tests against them fail, the implementation is broken, not the vector.
156
+
157
+ ## Attested on Nostr
158
+
159
+ This library's authorship is claimed on Nostr using the very protocol it implements — NIP-VA eating its own dog food.
160
+
161
+ A self-attestation alone only proves that the holder of a private key *claims* authorship — not that the claim is true. The real value comes from **third-party attestations**: other pubkeys independently publishing `type: endorsement` events that reference this repo.
162
+
163
+ But counting endorsements isn't enough either — anyone can create 50 throwaway npubs and endorse themselves. What matters is **who** endorses, not how many. A single endorsement from a pubkey with a verified NIP-05 domain, a history of notes, and followers you recognise is worth more than a thousand from anonymous keys. This is a web of trust, not a vote count. When verifying, ask: do I know this endorser? Do people I trust follow them? That's how you resist Sybil attacks without a centralised authority.
164
+
165
+ All verification uses [nak](https://github.com/fiatjaf/nak) (the Nostr Army Knife). Install with `go install github.com/fiatjaf/nak@latest` or `brew install fiatjaf/tap/nak`.
166
+
167
+ **1. GitHub → Nostr** — this README claims `npub1mgv...` (see header above)
168
+
169
+ **2. Nostr → GitHub** — the repo announcement points back here:
170
+
171
+ ```bash
172
+ nak req -k 30617 \
173
+ -a $(nak decode npub1mgvlrnf5hm9yf0n5mf9nqmvarhvxkc6remu5ec3vf8r0txqkuk7su0e7q2) \
174
+ -d nostr-attestations \
175
+ wss://relay.damus.io
176
+ # Look for the "web" tag → github.com/forgesworn/nostr-attestations
177
+ ```
178
+
179
+ **3. Verify the authorship claim** — signed by the same key:
180
+
181
+ ```bash
182
+ nak req -k 31000 \
183
+ -a $(nak decode npub1mgvlrnf5hm9yf0n5mf9nqmvarhvxkc6remu5ec3vf8r0txqkuk7su0e7q2) \
184
+ -d authorship:nostr-attestations \
185
+ wss://relay.damus.io 2>/dev/null | nak verify \
186
+ && echo "✓ Signature valid"
187
+ ```
188
+
189
+ **4. Check for third-party endorsements:**
190
+
191
+ ```bash
192
+ nak req -k 31000 \
193
+ -t a=30617:da19f1cd34beca44be74da4b306d9d1dd86b6343cef94ce22c49c6f59816e5bd:nostr-attestations \
194
+ wss://relay.damus.io
195
+ # Returns all attestations referencing this repo — filter by pubkey or type client-side
196
+ ```
197
+
198
+ Same npub on both sides — you'd need to control both GitHub and the private key to fake it. Third-party endorsements add independent signatures that can't be faked by one person.
199
+
200
+ **Endorse it yourself:**
201
+
202
+ ```bash
203
+ nak event -k 31000 \
204
+ --prompt-sec \
205
+ -d endorsement:da19f1cd34beca44be74da4b306d9d1dd86b6343cef94ce22c49c6f59816e5bd \
206
+ -t type=endorsement \
207
+ -p da19f1cd34beca44be74da4b306d9d1dd86b6343cef94ce22c49c6f59816e5bd \
208
+ -t a=30617:da19f1cd34beca44be74da4b306d9d1dd86b6343cef94ce22c49c6f59816e5bd:nostr-attestations \
209
+ -t summary="Reviewed and endorsed nostr-attestations" \
210
+ wss://relay.damus.io wss://nos.lol wss://relay.nostr.band
211
+ ```
124
212
 
125
213
  ## NIP-VA
126
214
 
127
- Full protocol specification: [NIP-VA.md](./NIP-VA.md)
215
+ Full protocol specification: [NIP-VA.md](./NIP-VA.md) | [NostrHub](https://nostrhub.io/npub1mgvlrnf5hm9yf0n5mf9nqmvarhvxkc6remu5ec3vf8r0txqkuk7su0e7q2)
128
216
 
129
217
  ## Licence
130
218
 
@@ -2,11 +2,16 @@ import type { AttestationParams, RevocationParams, EventTemplate } from './types
2
2
  /**
3
3
  * Create an unsigned attestation event template.
4
4
  * The caller is responsible for signing (adding pubkey, id, sig, created_at).
5
+ *
6
+ * At least one of `type` or `assertion` must be provided.
5
7
  */
6
8
  export declare function createAttestation(params: AttestationParams): EventTemplate;
7
9
  /**
8
10
  * Create an unsigned revocation event template.
9
11
  * When published, this replaces the original attestation via addressable event semantics.
12
+ *
13
+ * For typed attestations: provide `type` + `identifier`.
14
+ * For assertion-only attestations: provide `assertionId` or `assertionAddress`.
10
15
  */
11
16
  export declare function createRevocation(params: RevocationParams): EventTemplate;
12
17
  //# sourceMappingURL=builders.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../src/builders.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAEpF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,aAAa,CA6C1E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,aAAa,CAyBxE"}
1
+ {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../src/builders.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAEpF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,aAAa,CAyG1E;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,aAAa,CA0CxE"}
package/dist/builders.js CHANGED
@@ -1,27 +1,80 @@
1
1
  import { ATTESTATION_KIND } from './constants.js';
2
- import { buildDTag } from './filters.js';
2
+ import { buildDTag, buildAssertionDTag } from './filters.js';
3
3
  /**
4
4
  * Create an unsigned attestation event template.
5
5
  * The caller is responsible for signing (adding pubkey, id, sig, created_at).
6
+ *
7
+ * At least one of `type` or `assertion` must be provided.
6
8
  */
7
9
  export function createAttestation(params) {
8
- if (!params.type)
9
- throw new Error('type must not be empty');
10
- if (params.type.includes(':'))
11
- throw new Error('type must not contain colons');
10
+ const hasType = !!params.type;
11
+ const hasAssertion = !!params.assertion;
12
+ if (!hasType && !hasAssertion) {
13
+ throw new Error('at least one of type or assertion must be provided');
14
+ }
15
+ if (hasType) {
16
+ if (params.type.includes(':'))
17
+ throw new Error('type must not contain colons');
18
+ }
19
+ if (hasAssertion) {
20
+ const a = params.assertion;
21
+ if (a.id && a.address)
22
+ throw new Error('assertion must have id or address, not both');
23
+ if (!a.id && !a.address)
24
+ throw new Error('assertion must have id or address');
25
+ }
26
+ if (params.validTo != null) {
27
+ if (!Number.isFinite(params.validTo))
28
+ throw new Error('validTo must be a finite number');
29
+ if (params.validFrom != null && params.validTo <= params.validFrom) {
30
+ throw new Error('validTo must be greater than validFrom');
31
+ }
32
+ }
33
+ if (params.schema != null && !params.schema.trim()) {
34
+ throw new Error('schema must not be empty');
35
+ }
12
36
  const tags = [];
13
- // d-tag: type:identifier, or type:subject if no identifier, or just type
14
- const identifier = params.identifier ?? params.subject;
15
- if (identifier) {
16
- tags.push(['d', buildDTag(params.type, identifier)]);
37
+ // d-tag construction
38
+ if (hasType) {
39
+ const identifier = params.identifier ?? params.subject;
40
+ if (identifier) {
41
+ tags.push(['d', buildDTag(params.type, identifier)]);
42
+ }
43
+ else {
44
+ tags.push(['d', params.type]);
45
+ }
46
+ tags.push(['type', params.type]);
17
47
  }
18
48
  else {
19
- tags.push(['d', params.type]);
49
+ // assertion-only: d-tag uses assertion: prefix
50
+ const ref = params.assertion.id ?? params.assertion.address;
51
+ tags.push(['d', buildAssertionDTag(ref)]);
20
52
  }
21
- tags.push(['type', params.type]);
22
53
  if (params.subject) {
23
54
  tags.push(['p', params.subject]);
24
55
  }
56
+ // Assertion reference tags
57
+ if (hasAssertion) {
58
+ const a = params.assertion;
59
+ if (a.id) {
60
+ const eTag = ['e', a.id];
61
+ if (a.relay)
62
+ eTag.push(a.relay);
63
+ else
64
+ eTag.push('');
65
+ eTag.push('assertion');
66
+ tags.push(eTag);
67
+ }
68
+ else if (a.address) {
69
+ const aTag = ['a', a.address];
70
+ if (a.relay)
71
+ aTag.push(a.relay);
72
+ else
73
+ aTag.push('');
74
+ aTag.push('assertion');
75
+ tags.push(aTag);
76
+ }
77
+ }
25
78
  if (params.summary) {
26
79
  tags.push(['summary', params.summary]);
27
80
  }
@@ -35,6 +88,15 @@ export function createAttestation(params) {
35
88
  throw new Error('validFrom must be a finite number');
36
89
  tags.push(['valid_from', String(params.validFrom)]);
37
90
  }
91
+ if (params.validTo != null) {
92
+ tags.push(['valid_to', String(params.validTo)]);
93
+ }
94
+ if (params.request) {
95
+ tags.push(['request', params.request]);
96
+ }
97
+ if (params.schema) {
98
+ tags.push(['schema', params.schema]);
99
+ }
38
100
  if (params.tags) {
39
101
  for (const tag of params.tags) {
40
102
  tags.push(tag);
@@ -49,13 +111,29 @@ export function createAttestation(params) {
49
111
  /**
50
112
  * Create an unsigned revocation event template.
51
113
  * When published, this replaces the original attestation via addressable event semantics.
114
+ *
115
+ * For typed attestations: provide `type` + `identifier`.
116
+ * For assertion-only attestations: provide `assertionId` or `assertionAddress`.
52
117
  */
53
118
  export function createRevocation(params) {
54
- const tags = [
55
- ['d', buildDTag(params.type, params.identifier)],
56
- ['type', params.type],
57
- ['status', 'revoked'],
58
- ];
119
+ const hasTyped = !!params.type && params.identifier != null;
120
+ const hasAssertion = !!params.assertionId || !!params.assertionAddress;
121
+ if (!hasTyped && !hasAssertion) {
122
+ throw new Error('provide (type + identifier) or (assertionId | assertionAddress)');
123
+ }
124
+ if (params.assertionId && params.assertionAddress) {
125
+ throw new Error('provide assertionId or assertionAddress, not both');
126
+ }
127
+ const tags = [];
128
+ if (hasTyped) {
129
+ tags.push(['d', buildDTag(params.type, params.identifier)]);
130
+ tags.push(['type', params.type]);
131
+ }
132
+ else {
133
+ const ref = (params.assertionId ?? params.assertionAddress);
134
+ tags.push(['d', buildAssertionDTag(ref)]);
135
+ }
136
+ tags.push(['status', 'revoked']);
59
137
  if (params.subject) {
60
138
  tags.push(['p', params.subject]);
61
139
  }
@@ -1 +1 @@
1
- {"version":3,"file":"builders.js","sourceRoot":"","sources":["../src/builders.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"builders.js","sourceRoot":"","sources":["../src/builders.ts"],"names":[],"mappings":"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"}
@@ -10,5 +10,7 @@ export declare const TYPES: {
10
10
  readonly VOUCH: "vouch";
11
11
  readonly VERIFIER: "verifier";
12
12
  readonly PROVENANCE: "provenance";
13
+ /** Synthetic type for assertion-only attestations (no explicit type tag). */
14
+ readonly ASSERTION: "assertion";
13
15
  };
14
16
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,eAAO,MAAM,gBAAgB,EAAG,KAAc,CAAA;AAE9C;;;GAGG;AACH,eAAO,MAAM,KAAK;;;;;;CAMR,CAAA"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,eAAO,MAAM,gBAAgB,EAAG,KAAc,CAAA;AAE9C;;;GAGG;AACH,eAAO,MAAM,KAAK;;;;;;IAMhB,6EAA6E;;CAErE,CAAA"}
package/dist/constants.js CHANGED
@@ -10,5 +10,7 @@ export const TYPES = {
10
10
  VOUCH: 'vouch',
11
11
  VERIFIER: 'verifier',
12
12
  PROVENANCE: 'provenance',
13
+ /** Synthetic type for assertion-only attestations (no explicit type tag). */
14
+ ASSERTION: 'assertion',
13
15
  };
14
16
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAc,CAAA;AAE9C;;;GAGG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,UAAU,EAAE,YAAY;IACxB,WAAW,EAAE,aAAa;IAC1B,KAAK,EAAE,OAAO;IACd,QAAQ,EAAE,UAAU;IACpB,UAAU,EAAE,YAAY;CAChB,CAAA"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAc,CAAA;AAE9C;;;GAGG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,UAAU,EAAE,YAAY;IACxB,WAAW,EAAE,aAAa;IAC1B,KAAK,EAAE,OAAO;IACd,QAAQ,EAAE,UAAU;IACpB,UAAU,EAAE,YAAY;IACxB,6EAA6E;IAC7E,SAAS,EAAE,WAAW;CACd,CAAA"}
package/dist/filters.d.ts CHANGED
@@ -4,9 +4,15 @@ import type { FilterParams, NostrFilter } from './types.js';
4
4
  * Type values must not contain colons — the first colon is the delimiter.
5
5
  */
6
6
  export declare function buildDTag(type: string, identifier: string): string;
7
+ /**
8
+ * Build a d-tag for an assertion-only attestation.
9
+ * Uses the `assertion:` prefix followed by the assertion reference.
10
+ */
11
+ export declare function buildAssertionDTag(ref: string): string;
7
12
  /**
8
13
  * Parse a d-tag into type and identifier.
9
14
  * Returns null if the d-tag does not contain a colon.
15
+ * For assertion-only attestations (prefix `assertion:`), returns type as null.
10
16
  */
11
17
  export declare function parseDTag(dTag: string): {
12
18
  type: string;
@@ -16,4 +22,9 @@ export declare function parseDTag(dTag: string): {
16
22
  export declare function attestationFilter(params: FilterParams): NostrFilter;
17
23
  /** Build a Nostr relay filter for fetching the latest version of a specific attestation (for revocation checking). */
18
24
  export declare function revocationFilter(type: string, identifier: string): NostrFilter;
25
+ /** Build a Nostr relay filter for an assertion-only attestation by its reference. */
26
+ export declare function revocationFilter(options: {
27
+ assertionId?: string;
28
+ assertionAddress?: string;
29
+ }): NostrFilter;
19
30
  //# sourceMappingURL=filters.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"filters.d.ts","sourceRoot":"","sources":["../src/filters.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE3D;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAIlE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAOnF;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW,CAMnE;AAED,sHAAsH;AACtH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,CAK9E"}
1
+ {"version":3,"file":"filters.d.ts","sourceRoot":"","sources":["../src/filters.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAK3D;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAIlE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAMnF;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW,CAOnE;AAED,sHAAsH;AACtH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,CAAA;AAC/E,qFAAqF;AACrF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,CAAA"}
package/dist/filters.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { ATTESTATION_KIND } from './constants.js';
2
+ /** Assertion d-tag prefix for assertion-only attestations. */
3
+ const ASSERTION_PREFIX = 'assertion';
2
4
  /**
3
5
  * Build a d-tag from type and identifier.
4
6
  * Type values must not contain colons — the first colon is the delimiter.
@@ -10,18 +12,27 @@ export function buildDTag(type, identifier) {
10
12
  throw new Error('type must not contain colons');
11
13
  return `${type}:${identifier}`;
12
14
  }
15
+ /**
16
+ * Build a d-tag for an assertion-only attestation.
17
+ * Uses the `assertion:` prefix followed by the assertion reference.
18
+ */
19
+ export function buildAssertionDTag(ref) {
20
+ if (!ref)
21
+ throw new Error('assertion reference must not be empty');
22
+ return `${ASSERTION_PREFIX}:${ref}`;
23
+ }
13
24
  /**
14
25
  * Parse a d-tag into type and identifier.
15
26
  * Returns null if the d-tag does not contain a colon.
27
+ * For assertion-only attestations (prefix `assertion:`), returns type as null.
16
28
  */
17
29
  export function parseDTag(dTag) {
18
30
  const idx = dTag.indexOf(':');
19
31
  if (idx <= 0)
20
32
  return null;
21
- return {
22
- type: dTag.slice(0, idx),
23
- identifier: dTag.slice(idx + 1),
24
- };
33
+ const prefix = dTag.slice(0, idx);
34
+ const rest = dTag.slice(idx + 1);
35
+ return { type: prefix, identifier: rest };
25
36
  }
26
37
  /** Build a Nostr relay filter for querying attestation events. */
27
38
  export function attestationFilter(params) {
@@ -32,13 +43,23 @@ export function attestationFilter(params) {
32
43
  filter['#p'] = [params.subject];
33
44
  if (params.type)
34
45
  filter['#type'] = [params.type];
46
+ if (params.schema)
47
+ filter['#schema'] = [params.schema];
35
48
  return filter;
36
49
  }
37
- /** Build a Nostr relay filter for fetching the latest version of a specific attestation (for revocation checking). */
38
- export function revocationFilter(type, identifier) {
50
+ export function revocationFilter(typeOrOptions, identifier) {
51
+ if (typeof typeOrOptions === 'string') {
52
+ return {
53
+ kinds: [ATTESTATION_KIND],
54
+ '#d': [buildDTag(typeOrOptions, identifier)],
55
+ };
56
+ }
57
+ const ref = typeOrOptions.assertionId ?? typeOrOptions.assertionAddress;
58
+ if (!ref)
59
+ throw new Error('assertionId or assertionAddress required');
39
60
  return {
40
61
  kinds: [ATTESTATION_KIND],
41
- '#d': [buildDTag(type, identifier)],
62
+ '#d': [buildAssertionDTag(ref)],
42
63
  };
43
64
  }
44
65
  //# sourceMappingURL=filters.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"filters.js","sourceRoot":"","sources":["../src/filters.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAGjD;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,UAAkB;IACxD,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAA;IACpD,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAA;IACvE,OAAO,GAAG,IAAI,IAAI,UAAU,EAAE,CAAA;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAC7B,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IACzB,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;QACxB,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;KAChC,CAAA;AACH,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,iBAAiB,CAAC,MAAoB;IACpD,MAAM,MAAM,GAAgB,EAAE,KAAK,EAAE,CAAC,gBAAgB,CAAC,EAAE,CAAA;IACzD,IAAI,MAAM,CAAC,OAAO,EAAE,MAAM;QAAE,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAA;IAC3D,IAAI,MAAM,CAAC,OAAO;QAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IACnD,IAAI,MAAM,CAAC,IAAI;QAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAChD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,sHAAsH;AACtH,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,UAAkB;IAC/D,OAAO;QACL,KAAK,EAAE,CAAC,gBAAgB,CAAC;QACzB,IAAI,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;KACpC,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"filters.js","sourceRoot":"","sources":["../src/filters.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAGjD,8DAA8D;AAC9D,MAAM,gBAAgB,GAAG,WAAW,CAAA;AAEpC;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,UAAkB;IACxD,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAA;IACpD,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAA;IACvE,OAAO,GAAG,IAAI,IAAI,UAAU,EAAE,CAAA;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAA;IAClE,OAAO,GAAG,gBAAgB,IAAI,GAAG,EAAE,CAAA;AACrC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAC7B,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IACzB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAA;IAChC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;AAC3C,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,iBAAiB,CAAC,MAAoB;IACpD,MAAM,MAAM,GAAgB,EAAE,KAAK,EAAE,CAAC,gBAAgB,CAAC,EAAE,CAAA;IACzD,IAAI,MAAM,CAAC,OAAO,EAAE,MAAM;QAAE,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAA;IAC3D,IAAI,MAAM,CAAC,OAAO;QAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IACnD,IAAI,MAAM,CAAC,IAAI;QAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAChD,IAAI,MAAM,CAAC,MAAM;QAAE,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACtD,OAAO,MAAM,CAAA;AACf,CAAC;AAMD,MAAM,UAAU,gBAAgB,CAC9B,aAA2E,EAC3E,UAAmB;IAEnB,IAAI,OAAO,aAAa,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO;YACL,KAAK,EAAE,CAAC,gBAAgB,CAAC;YACzB,IAAI,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,UAAW,CAAC,CAAC;SAC9C,CAAA;IACH,CAAC;IACD,MAAM,GAAG,GAAG,aAAa,CAAC,WAAW,IAAI,aAAa,CAAC,gBAAgB,CAAA;IACvE,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;IACrE,OAAO;QACL,KAAK,EAAE,CAAC,gBAAgB,CAAC;QACzB,IAAI,EAAE,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;KAChC,CAAA;AACH,CAAC"}
package/dist/index.d.ts CHANGED
@@ -2,6 +2,6 @@ export { ATTESTATION_KIND, TYPES } from './constants.js';
2
2
  export { createAttestation, createRevocation } from './builders.js';
3
3
  export { parseAttestation, isRevoked } from './parsers.js';
4
4
  export { validateAttestation } from './validators.js';
5
- export { buildDTag, parseDTag, attestationFilter, revocationFilter } from './filters.js';
6
- export type { AttestationParams, RevocationParams, Attestation, ValidationResult, FilterParams, NostrFilter, NostrEvent, EventTemplate, } from './types.js';
5
+ export { buildDTag, buildAssertionDTag, parseDTag, attestationFilter, revocationFilter } from './filters.js';
6
+ export type { AssertionRef, AttestationParams, RevocationParams, Attestation, ValidationResult, FilterParams, NostrFilter, NostrEvent, EventTemplate, } from './types.js';
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAExD,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAEnE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAErD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAExF,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,UAAU,EACV,aAAa,GACd,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAExD,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAEnE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAErD,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,SAAS,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAE5G,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,UAAU,EACV,aAAa,GACd,MAAM,YAAY,CAAA"}
package/dist/index.js CHANGED
@@ -2,5 +2,5 @@ export { ATTESTATION_KIND, TYPES } from './constants.js';
2
2
  export { createAttestation, createRevocation } from './builders.js';
3
3
  export { parseAttestation, isRevoked } from './parsers.js';
4
4
  export { validateAttestation } from './validators.js';
5
- export { buildDTag, parseDTag, attestationFilter, revocationFilter } from './filters.js';
5
+ export { buildDTag, buildAssertionDTag, parseDTag, attestationFilter, revocationFilter } from './filters.js';
6
6
  //# 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,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAExD,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAEnE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAErD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAExD,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAEnE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAErD,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,SAAS,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA"}
package/dist/parsers.d.ts CHANGED
@@ -2,6 +2,8 @@ import type { NostrEvent, Attestation } from './types.js';
2
2
  /**
3
3
  * Parse a NostrEvent into a typed Attestation.
4
4
  * Returns null if the event is not a valid kind 31000 attestation.
5
+ *
6
+ * An attestation must have either a type tag or an assertion reference.
5
7
  */
6
8
  export declare function parseAttestation(event: NostrEvent): Attestation | null;
7
9
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"parsers.d.ts","sourceRoot":"","sources":["../src/parsers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAEzD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI,CA2BtE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAGpD"}
1
+ {"version":3,"file":"parsers.d.ts","sourceRoot":"","sources":["../src/parsers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAUzD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI,CAqCtE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAGpD"}
package/dist/parsers.js CHANGED
@@ -1,30 +1,48 @@
1
- import { ATTESTATION_KIND } from './constants.js';
1
+ import { ATTESTATION_KIND, TYPES } from './constants.js';
2
2
  import { findTag } from './helpers.js';
3
3
  import { parseDTag } from './filters.js';
4
+ /**
5
+ * Find an e or a tag with the "assertion" marker (fourth element).
6
+ * Returns the tag array or null.
7
+ */
8
+ function findAssertionTag(tags) {
9
+ return tags.find(t => (t[0] === 'e' || t[0] === 'a') && t[3] === 'assertion') ?? null;
10
+ }
4
11
  /**
5
12
  * Parse a NostrEvent into a typed Attestation.
6
13
  * Returns null if the event is not a valid kind 31000 attestation.
14
+ *
15
+ * An attestation must have either a type tag or an assertion reference.
7
16
  */
8
17
  export function parseAttestation(event) {
9
18
  if (event.kind !== ATTESTATION_KIND)
10
19
  return null;
11
20
  const type = findTag(event.tags, 'type');
12
- if (!type)
21
+ const assertionTag = findAssertionTag(event.tags);
22
+ // Must have type or assertion reference
23
+ if (!type && !assertionTag)
13
24
  return null;
14
25
  const dTag = findTag(event.tags, 'd');
15
26
  const parsed = dTag ? parseDTag(dTag) : null;
16
27
  const expirationStr = findTag(event.tags, 'expiration');
17
28
  const validFromStr = findTag(event.tags, 'valid_from');
29
+ const validToStr = findTag(event.tags, 'valid_to');
18
30
  return {
19
31
  kind: ATTESTATION_KIND,
20
- type,
32
+ type: type ?? TYPES.ASSERTION,
21
33
  pubkey: event.pubkey,
22
34
  createdAt: event.created_at,
23
35
  identifier: parsed?.identifier ?? null,
24
36
  subject: findTag(event.tags, 'p'),
37
+ assertionId: assertionTag !== null && assertionTag[0] === 'e' ? (assertionTag[1] ?? null) : null,
38
+ assertionAddress: assertionTag !== null && assertionTag[0] === 'a' ? (assertionTag[1] ?? null) : null,
39
+ assertionRelay: (assertionTag !== null && assertionTag[2]) ? assertionTag[2] : null,
25
40
  summary: findTag(event.tags, 'summary'),
26
41
  expiration: expirationStr ? Number(expirationStr) : null,
27
42
  validFrom: validFromStr ? Number(validFromStr) : null,
43
+ validTo: validToStr ? Number(validToStr) : null,
44
+ request: findTag(event.tags, 'request'),
45
+ schema: findTag(event.tags, 'schema'),
28
46
  revoked: findTag(event.tags, 'status') === 'revoked',
29
47
  reason: findTag(event.tags, 'reason'),
30
48
  tags: event.tags,
@@ -1 +1 @@
1
- {"version":3,"file":"parsers.js","sourceRoot":"","sources":["../src/parsers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAGxC;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAChD,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;QAAE,OAAO,IAAI,CAAA;IAEhD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IAEtB,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAE5C,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;IACvD,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;IAEtD,OAAO;QACL,IAAI,EAAE,gBAAyB;QAC/B,IAAI;QACJ,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,SAAS,EAAE,KAAK,CAAC,UAAU;QAC3B,UAAU,EAAE,MAAM,EAAE,UAAU,IAAI,IAAI;QACtC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC;QACjC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC;QACvC,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI;QACxD,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI;QACrD,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,SAAS;QACpD,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC;QACrC,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,KAAiB;IACzC,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;QAAE,OAAO,KAAK,CAAA;IACjD,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,SAAS,CAAA;AACpD,CAAC"}
1
+ {"version":3,"file":"parsers.js","sourceRoot":"","sources":["../src/parsers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAGxC;;;GAGG;AACH,SAAS,gBAAgB,CAAC,IAAgB;IACxC,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,IAAI,IAAI,CAAA;AACvF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAChD,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;QAAE,OAAO,IAAI,CAAA;IAEhD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACxC,MAAM,YAAY,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAEjD,wCAAwC;IACxC,IAAI,CAAC,IAAI,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAA;IAEvC,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAE5C,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;IACvD,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;IACtD,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;IAElD,OAAO;QACL,IAAI,EAAE,gBAAyB;QAC/B,IAAI,EAAE,IAAI,IAAI,KAAK,CAAC,SAAS;QAC7B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,SAAS,EAAE,KAAK,CAAC,UAAU;QAC3B,UAAU,EAAE,MAAM,EAAE,UAAU,IAAI,IAAI;QACtC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC;QACjC,WAAW,EAAE,YAAY,KAAK,IAAI,IAAI,YAAY,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QAChG,gBAAgB,EAAE,YAAY,KAAK,IAAI,IAAI,YAAY,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QACrG,cAAc,EAAE,CAAC,YAAY,KAAK,IAAI,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI;QACnF,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC;QACvC,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI;QACxD,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI;QACrD,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI;QAC/C,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC;QACvC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC;QACrC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,SAAS;QACpD,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC;QACrC,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,KAAiB;IACzC,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;QAAE,OAAO,KAAK,CAAA;IACjD,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,SAAS,CAAA;AACpD,CAAC"}