@nostr-games/inventory 0.1.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 +281 -0
- package/dist/index.cjs +913 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +744 -0
- package/dist/index.d.ts +744 -0
- package/dist/index.js +882 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal structural type compatible with a signed Nostr event.
|
|
3
|
+
*
|
|
4
|
+
* We intentionally define this locally instead of depending on a full Nostr
|
|
5
|
+
* library. Any object that structurally matches this shape (for example, the
|
|
6
|
+
* event type from `nostr-tools`) is accepted.
|
|
7
|
+
*/
|
|
8
|
+
interface NostrEvent {
|
|
9
|
+
pubkey: string;
|
|
10
|
+
created_at: number;
|
|
11
|
+
kind: number;
|
|
12
|
+
tags: string[][];
|
|
13
|
+
content: string;
|
|
14
|
+
sig?: string;
|
|
15
|
+
id?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* An unsigned event template as produced by the builders in this library.
|
|
19
|
+
*
|
|
20
|
+
* Builders never create `id` or `sig` and never sign anything. The template is
|
|
21
|
+
* intended to be passed to a signer / relay client by the consuming
|
|
22
|
+
* application.
|
|
23
|
+
*
|
|
24
|
+
* `pubkey` and `created_at` are intentionally omitted: they are added by the
|
|
25
|
+
* signing/publishing layer, which is out of scope for this library.
|
|
26
|
+
*/
|
|
27
|
+
interface UnsignedEventTemplate<K extends number = number> {
|
|
28
|
+
kind: K;
|
|
29
|
+
content: string;
|
|
30
|
+
tags: string[][];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Kind numbers defined by this library.
|
|
35
|
+
*
|
|
36
|
+
* - {@link KIND_GAME_ITEM_DEFINITION} (31632): Game Item Definition.
|
|
37
|
+
* - {@link KIND_GAME_INVENTORY} (31633): Game Inventory.
|
|
38
|
+
*/
|
|
39
|
+
declare const KIND_GAME_ITEM_DEFINITION: 31632;
|
|
40
|
+
declare const KIND_GAME_INVENTORY: 31633;
|
|
41
|
+
type KindGameItemDefinition = typeof KIND_GAME_ITEM_DEFINITION;
|
|
42
|
+
type KindGameInventory = typeof KIND_GAME_INVENTORY;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Parsing modes shared across the library.
|
|
46
|
+
*
|
|
47
|
+
* - `permissive` (default): follow the "SHOULD tolerate" rules of the specs.
|
|
48
|
+
* Invalid tags are ignored, invalid JSON content does not block tag parsing,
|
|
49
|
+
* duplicate inventory items resolve using the recommended default strategy.
|
|
50
|
+
* - `strict`: reject the event when the specs allow rejection (for example
|
|
51
|
+
* duplicate inventory item references, or invalid JSON content when JSON is
|
|
52
|
+
* required).
|
|
53
|
+
*
|
|
54
|
+
* "MUST reject" rules (wrong kind, missing/empty `d`, missing required item
|
|
55
|
+
* definition tags) apply in both modes.
|
|
56
|
+
*/
|
|
57
|
+
type ParseMode = "permissive" | "strict";
|
|
58
|
+
/**
|
|
59
|
+
* A recoverable, non-fatal issue detected while parsing.
|
|
60
|
+
*
|
|
61
|
+
* Warnings never prevent parsing in permissive mode. They are surfaced so
|
|
62
|
+
* callers can audit what was ignored or coerced.
|
|
63
|
+
*/
|
|
64
|
+
interface ParseWarning {
|
|
65
|
+
/** Machine-readable warning code. */
|
|
66
|
+
code: ParseWarningCode;
|
|
67
|
+
/** Human-readable explanation. */
|
|
68
|
+
message: string;
|
|
69
|
+
/** The offending tag, when the warning relates to a specific tag. */
|
|
70
|
+
tag?: string[];
|
|
71
|
+
}
|
|
72
|
+
type ParseWarningCode = "invalid-json-content" | "invalid-item-tag" | "invalid-quantity" | "wrong-referenced-kind" | "malformed-address" | "duplicate-item" | "invalid-grant-tag" | "empty-required-value";
|
|
73
|
+
/**
|
|
74
|
+
* Structured parse result.
|
|
75
|
+
*
|
|
76
|
+
* `ok: true` means a usable value was produced (possibly with warnings about
|
|
77
|
+
* ignored tags). `ok: false` means the event was rejected as invalid for this
|
|
78
|
+
* kind.
|
|
79
|
+
*/
|
|
80
|
+
type ParseResult<T> = {
|
|
81
|
+
ok: true;
|
|
82
|
+
value: T;
|
|
83
|
+
warnings: ParseWarning[];
|
|
84
|
+
} | {
|
|
85
|
+
ok: false;
|
|
86
|
+
error: string;
|
|
87
|
+
warnings: ParseWarning[];
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Helpers for NIP-01 addressable event coordinates of the form:
|
|
92
|
+
*
|
|
93
|
+
* ```text
|
|
94
|
+
* <kind>:<pubkey>:<d-tag>
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* IMPORTANT: the `d-tag` may itself contain `:` characters. The recommended
|
|
98
|
+
* `d` format in kind:31632 is `<namespace>:<category>:<slug>` (for example
|
|
99
|
+
* `blobbi:food:carrot`), which means a full address such as
|
|
100
|
+
* `31632:pubkey123:blobbi:food:carrot` has more than three colon-separated
|
|
101
|
+
* segments. Address parsing therefore splits only on the first two colons and
|
|
102
|
+
* treats everything after the second colon as the `d-tag`.
|
|
103
|
+
*/
|
|
104
|
+
interface AddressableEventAddress {
|
|
105
|
+
kind: number;
|
|
106
|
+
pubkey: string;
|
|
107
|
+
identifier: string;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
|
|
111
|
+
* Options controlling how strict pubkey validation is.
|
|
112
|
+
*
|
|
113
|
+
* Placeholder pubkeys are accepted by default to support fixtures, drafts,
|
|
114
|
+
* and permissive parsing. Consumers may enable canonical 64-character hex
|
|
115
|
+
* validation.
|
|
116
|
+
*
|
|
117
|
+
* - By default, only a non-empty pubkey segment is required.
|
|
118
|
+
* - When `requireHexPubkey` is `true`, the pubkey must be a 64-character
|
|
119
|
+
* lowercase hexadecimal string, which is the canonical Nostr pubkey form.
|
|
120
|
+
*/
|
|
121
|
+
interface AddressParseOptions {
|
|
122
|
+
requireHexPubkey?: boolean;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Build an addressable event coordinate string.
|
|
126
|
+
*
|
|
127
|
+
* @throws if any component is invalid (empty pubkey/identifier or non-integer
|
|
128
|
+
* kind). Builders in this library validate their inputs before calling this,
|
|
129
|
+
* so a throw here indicates a programming error rather than bad user data.
|
|
130
|
+
*/
|
|
131
|
+
declare function buildAddressableEventAddress(kind: number, pubkey: string, identifier: string): string;
|
|
132
|
+
/**
|
|
133
|
+
* Parse an addressable event coordinate string.
|
|
134
|
+
*
|
|
135
|
+
* Returns `null` when the string is not a well-formed `<kind>:<pubkey>:<d>`
|
|
136
|
+
* coordinate. Everything after the second colon is treated as the identifier
|
|
137
|
+
* (`d` tag), preserving any colons it contains.
|
|
138
|
+
*/
|
|
139
|
+
declare function parseAddressableEventAddress(address: string, options?: AddressParseOptions): AddressableEventAddress | null;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Return the value of the first tag with the given name, or `undefined` if no
|
|
143
|
+
* such tag exists or its value slot is missing.
|
|
144
|
+
*/
|
|
145
|
+
declare function getTagValue(tags: string[][], name: string): string | undefined;
|
|
146
|
+
/**
|
|
147
|
+
* Return all values of tags with the given name (index 1), skipping tags whose
|
|
148
|
+
* value slot is missing.
|
|
149
|
+
*/
|
|
150
|
+
declare function getTagValues(tags: string[][], name: string): string[];
|
|
151
|
+
/**
|
|
152
|
+
* Return the `d` tag value of an event or its raw tags.
|
|
153
|
+
*
|
|
154
|
+
* Returns `undefined` if there is no `d` tag or its value is missing. Note that
|
|
155
|
+
* an empty-string `d` value is returned as `""` (the caller decides whether an
|
|
156
|
+
* empty `d` is acceptable; per both specs it is not).
|
|
157
|
+
*/
|
|
158
|
+
declare function getDTag(source: NostrEvent | string[][]): string | undefined;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Result of attempting to parse a `content` string as JSON.
|
|
162
|
+
*/
|
|
163
|
+
type ContentParseResult = {
|
|
164
|
+
kind: "empty";
|
|
165
|
+
} | {
|
|
166
|
+
kind: "json";
|
|
167
|
+
value: unknown;
|
|
168
|
+
} | {
|
|
169
|
+
kind: "invalid";
|
|
170
|
+
raw: string;
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Attempt to parse an event `content` string as JSON without throwing.
|
|
174
|
+
*
|
|
175
|
+
* An empty string is treated as {@link ContentParseResult} `empty`, matching
|
|
176
|
+
* both specifications which allow `content` to be an empty string.
|
|
177
|
+
*
|
|
178
|
+
* This never throws: invalid JSON is reported as `invalid` so callers can
|
|
179
|
+
* decide how to react based on their parse mode.
|
|
180
|
+
*/
|
|
181
|
+
declare function parseContentJson(content: string): ContentParseResult;
|
|
182
|
+
/**
|
|
183
|
+
* Normalize a `content` input that may be either a preserialized string or a
|
|
184
|
+
* value to be JSON-serialized.
|
|
185
|
+
*
|
|
186
|
+
* - `undefined` -> `""` (the spec default for both kinds).
|
|
187
|
+
* - `string` -> returned as-is (the caller is responsible for its validity).
|
|
188
|
+
* - anything else -> `JSON.stringify`.
|
|
189
|
+
*
|
|
190
|
+
* This function always either returns a valid string or throws; it never
|
|
191
|
+
* returns `undefined`. `JSON.stringify` returns `undefined` for values that
|
|
192
|
+
* are not JSON-serializable (for example `function`, `symbol`, or a top-level
|
|
193
|
+
* `undefined` reached via another path), and throws on circular references. In
|
|
194
|
+
* both cases a clear error is raised.
|
|
195
|
+
*
|
|
196
|
+
* @throws {Error} when the value is not JSON-serializable (including circular
|
|
197
|
+
* references).
|
|
198
|
+
*/
|
|
199
|
+
declare function serializeContent(content: unknown): string;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* A derivation reference declared with an `a` tag marked `based_on`.
|
|
203
|
+
*/
|
|
204
|
+
interface GameItemBasedOnReference {
|
|
205
|
+
/** The referenced `31632:<pubkey>:<d>` address (as written in the tag). */
|
|
206
|
+
address: string;
|
|
207
|
+
/** Relay URL hint, or `""` when unknown. */
|
|
208
|
+
relay: string;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* A parsed kind:31632 Game Item Definition.
|
|
212
|
+
*
|
|
213
|
+
* Tags are the source of truth. `content` holds the raw content string; parsed
|
|
214
|
+
* JSON is exposed separately via {@link GameItemDefinition.contentJson}.
|
|
215
|
+
*/
|
|
216
|
+
interface GameItemDefinition {
|
|
217
|
+
/** The `d` tag value (item identifier). */
|
|
218
|
+
id: string;
|
|
219
|
+
/** The full addressable coordinate `31632:<pubkey>:<d>`. */
|
|
220
|
+
address: string;
|
|
221
|
+
/** The event author / item issuer. */
|
|
222
|
+
issuer: string;
|
|
223
|
+
kind: KindGameItemDefinition;
|
|
224
|
+
/** Required `name` tag. */
|
|
225
|
+
name: string;
|
|
226
|
+
/** Required `type` tag. */
|
|
227
|
+
type: string;
|
|
228
|
+
/** Optional `category` tag. */
|
|
229
|
+
category?: string;
|
|
230
|
+
/** Optional `image` tag. */
|
|
231
|
+
image?: string;
|
|
232
|
+
/** Optional `model_3d` tag. */
|
|
233
|
+
model3d?: string;
|
|
234
|
+
/** Optional `audio` tag. */
|
|
235
|
+
audio?: string;
|
|
236
|
+
/** Optional `symbol` tag. */
|
|
237
|
+
symbol?: string;
|
|
238
|
+
/** Optional `rarity` tag. */
|
|
239
|
+
rarity?: string;
|
|
240
|
+
/** Optional `max_stack` tag, kept as the raw string per the spec. */
|
|
241
|
+
maxStack?: string;
|
|
242
|
+
/** Optional `version` tag. */
|
|
243
|
+
version?: string;
|
|
244
|
+
/** Optional `alt` tag. */
|
|
245
|
+
alt?: string;
|
|
246
|
+
/** All `context` tag values (repeatable). */
|
|
247
|
+
contexts: string[];
|
|
248
|
+
/** All `t` topic tag values (repeatable). */
|
|
249
|
+
topics: string[];
|
|
250
|
+
/** All `based_on` derivation references (repeatable). */
|
|
251
|
+
basedOn: GameItemBasedOnReference[];
|
|
252
|
+
/** Raw `content` string, preserved exactly as received. */
|
|
253
|
+
content: string;
|
|
254
|
+
/**
|
|
255
|
+
* Parsed content JSON, when `content` was non-empty valid JSON. `undefined`
|
|
256
|
+
* when content is empty or (in permissive mode) invalid JSON.
|
|
257
|
+
*/
|
|
258
|
+
contentJson?: unknown;
|
|
259
|
+
/** The original event this definition was parsed from. */
|
|
260
|
+
event: NostrEvent;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Input for building a kind:31632 event template.
|
|
264
|
+
*
|
|
265
|
+
* `content` accepts either a preserialized string or any JSON-serializable
|
|
266
|
+
* value. Tag-owned fields (name, type, etc.) MUST NOT be duplicated in content
|
|
267
|
+
* by the library; that is left to the caller's discretion per the spec.
|
|
268
|
+
*/
|
|
269
|
+
interface BuildGameItemDefinitionInput {
|
|
270
|
+
/** The `d` item identifier. Required, non-empty. */
|
|
271
|
+
id: string;
|
|
272
|
+
/** The `name` tag. Required, non-empty. */
|
|
273
|
+
name: string;
|
|
274
|
+
/** The `type` tag. Required, non-empty. */
|
|
275
|
+
type: string;
|
|
276
|
+
category?: string;
|
|
277
|
+
image?: string;
|
|
278
|
+
model3d?: string;
|
|
279
|
+
audio?: string;
|
|
280
|
+
symbol?: string;
|
|
281
|
+
rarity?: string;
|
|
282
|
+
/** Suggested max stack; coerced to a string if provided as a number. */
|
|
283
|
+
maxStack?: string | number;
|
|
284
|
+
version?: string | number;
|
|
285
|
+
/**
|
|
286
|
+
* Human-readable fallback. If omitted, the builder does NOT auto-generate an
|
|
287
|
+
* `alt` tag (it is only RECOMMENDED by the spec). Pass a string to include it.
|
|
288
|
+
*/
|
|
289
|
+
alt?: string;
|
|
290
|
+
contexts?: string[];
|
|
291
|
+
topics?: string[];
|
|
292
|
+
basedOn?: GameItemBasedOnReference[];
|
|
293
|
+
/** Optional content; string or JSON-serializable value. Defaults to `""`. */
|
|
294
|
+
content?: unknown;
|
|
295
|
+
/**
|
|
296
|
+
* Extra tags to append verbatim after the managed tags. Use for
|
|
297
|
+
* forward-compatible or game-specific tags.
|
|
298
|
+
*
|
|
299
|
+
* Tags that conflict with builder-managed tags are REJECTED (the builder
|
|
300
|
+
* throws), not silently ignored or duplicated. Managed tags are: `d`,
|
|
301
|
+
* `name`, `type`, `category`, `image`, `model_3d`, `audio`, `symbol`,
|
|
302
|
+
* `rarity`, `max_stack`, `version`, `context`, `t`, `alt`. An `a` tag only
|
|
303
|
+
* conflicts when it carries the `based_on` marker (pass such references via
|
|
304
|
+
* `basedOn` instead); other `a` usages remain allowed.
|
|
305
|
+
*/
|
|
306
|
+
extraTags?: string[][];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* The marker used on an `a` tag to indicate a derivation reference.
|
|
311
|
+
*/
|
|
312
|
+
declare const BASED_ON_MARKER: "based_on";
|
|
313
|
+
interface GameItemAddress {
|
|
314
|
+
kind: KindGameItemDefinition;
|
|
315
|
+
pubkey: string;
|
|
316
|
+
/** The item `d` tag / identifier. */
|
|
317
|
+
itemId: string;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Build the addressable coordinate for a game item definition:
|
|
321
|
+
* `31632:<pubkey>:<item-d-tag>`.
|
|
322
|
+
*/
|
|
323
|
+
declare function buildGameItemAddress(pubkey: string, itemId: string): string;
|
|
324
|
+
/**
|
|
325
|
+
* Parse a `31632:<pubkey>:<item-d-tag>` coordinate.
|
|
326
|
+
*
|
|
327
|
+
* Returns `null` if the string is malformed or does not reference kind 31632.
|
|
328
|
+
*/
|
|
329
|
+
declare function parseGameItemAddress(address: string, options?: AddressParseOptions): GameItemAddress | null;
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* A structured validation issue for a kind:31632 event.
|
|
333
|
+
*/
|
|
334
|
+
interface ItemDefinitionValidationIssue {
|
|
335
|
+
code: "wrong-kind" | "missing-d" | "empty-d" | "missing-name" | "empty-name" | "missing-type" | "empty-type" | "invalid-json-content";
|
|
336
|
+
message: string;
|
|
337
|
+
}
|
|
338
|
+
interface ItemDefinitionValidationOptions {
|
|
339
|
+
/**
|
|
340
|
+
* When `true`, `content` must be empty or valid JSON. This mirrors the spec:
|
|
341
|
+
* an item definition is rejected for invalid JSON only when the client
|
|
342
|
+
* "requires strict JSON content". Defaults to `false`.
|
|
343
|
+
*/
|
|
344
|
+
requireJsonContent?: boolean;
|
|
345
|
+
}
|
|
346
|
+
interface ItemDefinitionValidationResult {
|
|
347
|
+
valid: boolean;
|
|
348
|
+
issues: ItemDefinitionValidationIssue[];
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Validate that an event is a well-formed kind:31632 Game Item Definition.
|
|
352
|
+
*
|
|
353
|
+
* Per the spec, an event is rejected as an item definition if:
|
|
354
|
+
* - `kind` is not 31632
|
|
355
|
+
* - the `d` tag is missing or empty
|
|
356
|
+
* - the `name` tag is missing or empty
|
|
357
|
+
* - the `type` tag is missing or empty
|
|
358
|
+
* - (only when {@link ItemDefinitionValidationOptions.requireJsonContent})
|
|
359
|
+
* `content` is not valid JSON
|
|
360
|
+
*
|
|
361
|
+
* A required value that contains only whitespace is treated as empty. Values
|
|
362
|
+
* are never trimmed or otherwise mutated.
|
|
363
|
+
*/
|
|
364
|
+
declare function validateGameItemDefinition(event: NostrEvent, options?: ItemDefinitionValidationOptions): ItemDefinitionValidationResult;
|
|
365
|
+
|
|
366
|
+
interface ParseGameItemDefinitionOptions {
|
|
367
|
+
/**
|
|
368
|
+
* `permissive` (default) tolerates invalid JSON content and unknown tags.
|
|
369
|
+
* `strict` additionally rejects the event when `content` is non-empty and
|
|
370
|
+
* not valid JSON (i.e. it implies `requireJsonContent`).
|
|
371
|
+
*/
|
|
372
|
+
mode?: ParseMode;
|
|
373
|
+
/**
|
|
374
|
+
* Explicitly require valid JSON content, independent of mode. When left
|
|
375
|
+
* `undefined`, this defaults to `true` in strict mode and `false` in
|
|
376
|
+
* permissive mode.
|
|
377
|
+
*/
|
|
378
|
+
requireJsonContent?: boolean;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Parse a kind:31632 event into a {@link GameItemDefinition}, returning a
|
|
382
|
+
* structured {@link ParseResult}.
|
|
383
|
+
*
|
|
384
|
+
* The event is rejected (`ok: false`) only for the MUST-reject conditions
|
|
385
|
+
* (wrong kind, missing/empty `d`/`name`/`type`, and invalid JSON when
|
|
386
|
+
* required). Unknown tags are preserved on the event and tolerated. Invalid
|
|
387
|
+
* JSON content in permissive mode produces a warning, not a rejection. Invalid
|
|
388
|
+
* `based_on` references (malformed address or wrong referenced kind) are
|
|
389
|
+
* ignored and reported as warnings.
|
|
390
|
+
*/
|
|
391
|
+
declare function parseGameItemDefinitionResult(event: NostrEvent, options?: ParseGameItemDefinitionOptions): ParseResult<GameItemDefinition>;
|
|
392
|
+
/**
|
|
393
|
+
* Convenience wrapper returning the parsed definition or `null`.
|
|
394
|
+
*
|
|
395
|
+
* Prefer {@link parseGameItemDefinitionResult} when you need warnings or the
|
|
396
|
+
* rejection reason.
|
|
397
|
+
*/
|
|
398
|
+
declare function parseGameItemDefinition(event: NostrEvent, options?: ParseGameItemDefinitionOptions): GameItemDefinition | null;
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Build an unsigned kind:31632 event template.
|
|
402
|
+
*
|
|
403
|
+
* The builder:
|
|
404
|
+
* - validates required input (`id`, `name`, `type` must be non-empty and not
|
|
405
|
+
* whitespace-only);
|
|
406
|
+
* - validates `maxStack` (positive decimal integer) and numeric `version`
|
|
407
|
+
* (finite);
|
|
408
|
+
* - validates every `basedOn` reference (well-formed kind:31632 coordinate);
|
|
409
|
+
* - never creates `id` or `sig`;
|
|
410
|
+
* - emits tags in a stable, deterministic order to simplify testing;
|
|
411
|
+
* - rejects `extraTags` that conflict with builder-managed tags (see
|
|
412
|
+
* {@link MANAGED_TAG_NAMES}); an `a` tag is only treated as conflicting when
|
|
413
|
+
* it carries the `based_on` marker, so other future `a` usages remain
|
|
414
|
+
* possible;
|
|
415
|
+
* - appends the remaining `extraTags` verbatim after the managed tags.
|
|
416
|
+
*
|
|
417
|
+
* Valid values are never trimmed or otherwise mutated.
|
|
418
|
+
*
|
|
419
|
+
* @throws {Error} if a required field is missing/blank, an optional numeric
|
|
420
|
+
* field is invalid, a `basedOn` reference is invalid, or an `extraTags` entry
|
|
421
|
+
* conflicts with a builder-managed tag.
|
|
422
|
+
*/
|
|
423
|
+
declare function buildGameItemDefinitionEvent(input: BuildGameItemDefinitionInput): UnsignedEventTemplate<KindGameItemDefinition>;
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* A single item reference inside an inventory.
|
|
427
|
+
*/
|
|
428
|
+
interface GameInventoryItem {
|
|
429
|
+
/** The referenced `31632:<issuer-pubkey>:<item-d-tag>` address. */
|
|
430
|
+
address: string;
|
|
431
|
+
/** Relay URL hint, or `""` when unknown. */
|
|
432
|
+
relay: string;
|
|
433
|
+
/** Positive integer quantity. */
|
|
434
|
+
quantity: number;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* A grant / receipt reference declared with an `e` tag marked `grant`.
|
|
438
|
+
*/
|
|
439
|
+
interface GameInventoryGrantReference {
|
|
440
|
+
/** The referenced grant event id. */
|
|
441
|
+
eventId: string;
|
|
442
|
+
/** Relay URL hint, or `""` when unknown. */
|
|
443
|
+
relay: string;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* A parsed kind:31633 Game Inventory.
|
|
447
|
+
*
|
|
448
|
+
* Tags are the source of truth. `content` holds the raw content string; parsed
|
|
449
|
+
* JSON is exposed separately via {@link GameInventory.contentJson}.
|
|
450
|
+
*/
|
|
451
|
+
interface GameInventory {
|
|
452
|
+
/** The `d` tag value (inventory context identifier). */
|
|
453
|
+
id: string;
|
|
454
|
+
/** The full addressable coordinate `31633:<owner>:<d>`. */
|
|
455
|
+
address: string;
|
|
456
|
+
/** The event author / inventory owner. */
|
|
457
|
+
owner: string;
|
|
458
|
+
kind: KindGameInventory;
|
|
459
|
+
/** All `context` tag values (repeatable). */
|
|
460
|
+
contexts: string[];
|
|
461
|
+
/** Optional `name` tag. */
|
|
462
|
+
name?: string;
|
|
463
|
+
/** Optional `alt` tag. */
|
|
464
|
+
alt?: string;
|
|
465
|
+
/** Valid item references, in tag order (after duplicate resolution). */
|
|
466
|
+
items: GameInventoryItem[];
|
|
467
|
+
/** Grant references from `e` tags marked `grant`. */
|
|
468
|
+
grants: GameInventoryGrantReference[];
|
|
469
|
+
/** Convenience: grant event ids only. */
|
|
470
|
+
grantEventIds: string[];
|
|
471
|
+
/** Raw `content` string, preserved exactly as received. */
|
|
472
|
+
content: string;
|
|
473
|
+
/** Parsed content JSON, when content was non-empty valid JSON. */
|
|
474
|
+
contentJson?: unknown;
|
|
475
|
+
/** The original event this inventory was parsed from. */
|
|
476
|
+
event: NostrEvent;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Input for a single item when building an inventory event.
|
|
480
|
+
*
|
|
481
|
+
* `quantity` must be a non-negative safe integer. `0` omits the item; negative,
|
|
482
|
+
* decimal, NaN, Infinity, and unsafe-integer values throw. Quantities are never
|
|
483
|
+
* floored, clamped, or silently dropped.
|
|
484
|
+
*/
|
|
485
|
+
interface BuildGameInventoryItemInput {
|
|
486
|
+
address: string;
|
|
487
|
+
relay?: string;
|
|
488
|
+
quantity: number;
|
|
489
|
+
}
|
|
490
|
+
interface BuildGameInventoryGrantInput {
|
|
491
|
+
eventId: string;
|
|
492
|
+
relay?: string;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Strategy for handling duplicate item addresses when building or parsing.
|
|
496
|
+
*
|
|
497
|
+
* - `last` (spec default): keep the last valid quantity.
|
|
498
|
+
* - `sum`: sum all valid quantities for the address.
|
|
499
|
+
* - `strict`: reject (build throws / parse returns error).
|
|
500
|
+
*/
|
|
501
|
+
type DuplicateStrategy = "last" | "sum" | "strict";
|
|
502
|
+
/**
|
|
503
|
+
* Input for building a kind:31633 event template.
|
|
504
|
+
*/
|
|
505
|
+
interface BuildGameInventoryInput {
|
|
506
|
+
/** The `d` inventory identifier. Required, non-empty. */
|
|
507
|
+
id: string;
|
|
508
|
+
items?: BuildGameInventoryItemInput[];
|
|
509
|
+
contexts?: string[];
|
|
510
|
+
name?: string;
|
|
511
|
+
alt?: string;
|
|
512
|
+
grants?: BuildGameInventoryGrantInput[];
|
|
513
|
+
/** Optional content; string or JSON-serializable value. Defaults to `""`. */
|
|
514
|
+
content?: unknown;
|
|
515
|
+
/**
|
|
516
|
+
* How to handle duplicate item addresses in the input. Defaults to `last`
|
|
517
|
+
* (the spec's recommended default). `strict` throws on duplicates. `sum`
|
|
518
|
+
* throws if the summed quantity exceeds Number.MAX_SAFE_INTEGER.
|
|
519
|
+
*/
|
|
520
|
+
duplicateStrategy?: DuplicateStrategy;
|
|
521
|
+
/**
|
|
522
|
+
* Extra tags appended verbatim after managed tags.
|
|
523
|
+
*
|
|
524
|
+
* Tags that conflict with builder-managed tags are REJECTED (the builder
|
|
525
|
+
* throws), not silently duplicated. Rejected: `d`, `context`, `name`, `alt`;
|
|
526
|
+
* every `a` tag (all `a` tags represent inventory items in kind:31633 — pass
|
|
527
|
+
* items via `items`); and `e` tags carrying the `grant` marker (pass grants
|
|
528
|
+
* via `grants`). Unrelated forward-compatible tags, including non-grant `e`
|
|
529
|
+
* tags, are allowed.
|
|
530
|
+
*/
|
|
531
|
+
extraTags?: string[][];
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* The marker used on an `e` tag to indicate a grant / receipt reference.
|
|
536
|
+
*/
|
|
537
|
+
declare const GRANT_MARKER: "grant";
|
|
538
|
+
interface GameInventoryAddress {
|
|
539
|
+
kind: KindGameInventory;
|
|
540
|
+
pubkey: string;
|
|
541
|
+
/** The inventory `d` tag / context identifier. */
|
|
542
|
+
inventoryId: string;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Build the addressable coordinate for a game inventory:
|
|
546
|
+
* `31633:<owner-pubkey>:<inventory-d-tag>`.
|
|
547
|
+
*/
|
|
548
|
+
declare function buildGameInventoryAddress(ownerPubkey: string, inventoryId: string): string;
|
|
549
|
+
/**
|
|
550
|
+
* Parse a `31633:<owner-pubkey>:<inventory-d-tag>` coordinate.
|
|
551
|
+
*
|
|
552
|
+
* Returns `null` if the string is malformed or does not reference kind 31633.
|
|
553
|
+
*/
|
|
554
|
+
declare function parseGameInventoryAddress(address: string, options?: AddressParseOptions): GameInventoryAddress | null;
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Public quantity helpers for kind:31633 inventory `a` tags.
|
|
558
|
+
*
|
|
559
|
+
* Per the spec, quantity MUST be encoded as a decimal integer string. Parsers
|
|
560
|
+
* SHOULD ignore item references whose quantity is missing, `0`, negative,
|
|
561
|
+
* decimal, or non-numeric. A "valid" stored inventory quantity is therefore a
|
|
562
|
+
* positive integer.
|
|
563
|
+
*
|
|
564
|
+
* - {@link parseInventoryQuantity} parses an untrusted tag string, tolerantly
|
|
565
|
+
* returning `null` for anything that is not a canonical positive decimal
|
|
566
|
+
* integer string.
|
|
567
|
+
* - {@link encodeInventoryQuantity} encodes a positive integer back into its
|
|
568
|
+
* canonical decimal string form.
|
|
569
|
+
*
|
|
570
|
+
* Strict validation of public numeric inputs (used by the builder and the
|
|
571
|
+
* inventory helpers) lives in `quantity.internal.ts` and is not part of the
|
|
572
|
+
* public API.
|
|
573
|
+
*/
|
|
574
|
+
/**
|
|
575
|
+
* Parse an inventory quantity *string* into a positive integer.
|
|
576
|
+
*
|
|
577
|
+
* Returns `null` for any value that is not a strictly positive canonical
|
|
578
|
+
* decimal integer string. This intentionally rejects:
|
|
579
|
+
* - `undefined` / `null` / missing
|
|
580
|
+
* - `"0"` and `"-1"` (not positive)
|
|
581
|
+
* - `"1.5"` (decimal)
|
|
582
|
+
* - `"abc"`, `"1e3"`, `"0x1"`, `" 1 "`, `"+1"` (not a plain decimal integer)
|
|
583
|
+
* - `"03"` (leading zeros; a well-formed publisher never emits them)
|
|
584
|
+
*/
|
|
585
|
+
declare function parseInventoryQuantity(raw: string | undefined | null): number | null;
|
|
586
|
+
/**
|
|
587
|
+
* Encode a positive integer quantity as its canonical decimal string.
|
|
588
|
+
*
|
|
589
|
+
* @throws if the value is not a positive safe integer.
|
|
590
|
+
*/
|
|
591
|
+
declare function encodeInventoryQuantity(value: number): string;
|
|
592
|
+
|
|
593
|
+
interface InventoryValidationIssue {
|
|
594
|
+
code: "wrong-kind" | "missing-d" | "empty-d" | "invalid-json-content";
|
|
595
|
+
message: string;
|
|
596
|
+
}
|
|
597
|
+
interface InventoryValidationOptions {
|
|
598
|
+
/**
|
|
599
|
+
* When `true`, non-empty invalid JSON content is treated as invalid. The
|
|
600
|
+
* spec only allows rejecting invalid JSON when NOT in permissive mode, so
|
|
601
|
+
* this defaults to `false`.
|
|
602
|
+
*/
|
|
603
|
+
requireJsonContent?: boolean;
|
|
604
|
+
}
|
|
605
|
+
interface InventoryValidationResult {
|
|
606
|
+
valid: boolean;
|
|
607
|
+
issues: InventoryValidationIssue[];
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Validate the MUST-reject conditions for a kind:31633 inventory.
|
|
611
|
+
*
|
|
612
|
+
* An event MUST be rejected if:
|
|
613
|
+
* - `kind` is not 31633
|
|
614
|
+
* - the `d` tag is missing
|
|
615
|
+
* - the `d` tag is empty or contains only whitespace
|
|
616
|
+
*
|
|
617
|
+
* A whitespace-only `d` value is treated as empty; values are never trimmed.
|
|
618
|
+
* Item-level problems (bad quantity, wrong referenced kind, malformed address)
|
|
619
|
+
* do NOT invalidate the inventory; those items are ignored during parsing.
|
|
620
|
+
*/
|
|
621
|
+
declare function validateGameInventory(event: NostrEvent, options?: InventoryValidationOptions): InventoryValidationResult;
|
|
622
|
+
|
|
623
|
+
interface ParseGameInventoryOptions {
|
|
624
|
+
/**
|
|
625
|
+
* `permissive` (default) tolerates invalid JSON content and ignores invalid
|
|
626
|
+
* item tags. `strict` additionally:
|
|
627
|
+
* - rejects the event when `content` is non-empty and not valid JSON;
|
|
628
|
+
* - defaults the duplicate strategy to `strict` (reject on duplicates).
|
|
629
|
+
*/
|
|
630
|
+
mode?: ParseMode;
|
|
631
|
+
/**
|
|
632
|
+
* Duplicate item address handling. When omitted, defaults to `strict` in
|
|
633
|
+
* strict mode and `last` in permissive mode (the spec's recommended
|
|
634
|
+
* default).
|
|
635
|
+
*/
|
|
636
|
+
duplicateStrategy?: DuplicateStrategy;
|
|
637
|
+
/**
|
|
638
|
+
* Require valid JSON content independent of mode. Defaults to `true` in
|
|
639
|
+
* strict mode, `false` in permissive mode.
|
|
640
|
+
*/
|
|
641
|
+
requireJsonContent?: boolean;
|
|
642
|
+
/**
|
|
643
|
+
* Require item addresses to use 64-char hex pubkeys. Defaults to `false`
|
|
644
|
+
* (the spec examples use placeholder pubkeys).
|
|
645
|
+
*/
|
|
646
|
+
requireHexPubkey?: boolean;
|
|
647
|
+
/**
|
|
648
|
+
* Require grant `e` tag event ids to be canonical 64-char lowercase hex.
|
|
649
|
+
* Defaults to `false` so fixture placeholders remain supported.
|
|
650
|
+
*/
|
|
651
|
+
requireHexEventId?: boolean;
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Parse a kind:31633 event into a {@link GameInventory}, returning a structured
|
|
655
|
+
* {@link ParseResult}.
|
|
656
|
+
*
|
|
657
|
+
* The event is rejected (`ok: false`) only when: kind is wrong, `d` is
|
|
658
|
+
* missing/empty, JSON content is required but invalid, duplicate item
|
|
659
|
+
* references are present under the `strict` strategy, or a `sum` duplicate
|
|
660
|
+
* resolution would overflow Number.MAX_SAFE_INTEGER. Item references that are
|
|
661
|
+
* malformed, reference a non-31632 kind, or carry an invalid quantity are
|
|
662
|
+
* ignored and reported as warnings. Malformed grant tags are ignored with an
|
|
663
|
+
* `invalid-grant-tag` warning.
|
|
664
|
+
*/
|
|
665
|
+
declare function parseGameInventoryResult(event: NostrEvent, options?: ParseGameInventoryOptions): ParseResult<GameInventory>;
|
|
666
|
+
/**
|
|
667
|
+
* Convenience wrapper returning the parsed inventory or `null`.
|
|
668
|
+
*/
|
|
669
|
+
declare function parseGameInventory(event: NostrEvent, options?: ParseGameInventoryOptions): GameInventory | null;
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Build an unsigned kind:31633 event template.
|
|
673
|
+
*
|
|
674
|
+
* The builder:
|
|
675
|
+
* - validates the required `d` id (non-empty, not whitespace-only);
|
|
676
|
+
* - validates each item address (must be a kind:31632 coordinate);
|
|
677
|
+
* - requires each item quantity to be a non-negative safe integer; `0` is
|
|
678
|
+
* omitted, and negatives/decimals/NaN/Infinity/unsafe integers throw (they
|
|
679
|
+
* are never floored, clamped, or silently dropped);
|
|
680
|
+
* - resolves duplicate addresses per `duplicateStrategy` (default `last`), and
|
|
681
|
+
* throws if a `sum` overflows Number.MAX_SAFE_INTEGER;
|
|
682
|
+
* - rejects grants with an empty/whitespace-only event id;
|
|
683
|
+
* - emits tags in a stable, deterministic order;
|
|
684
|
+
* - never creates `id` or `sig`;
|
|
685
|
+
* - rejects `extraTags` that conflict with builder-managed tags and appends the
|
|
686
|
+
* rest verbatim after the managed tags.
|
|
687
|
+
*
|
|
688
|
+
* Non-empty display values are never trimmed or otherwise normalized.
|
|
689
|
+
*
|
|
690
|
+
* @throws {Error} for a blank `id`, invalid item address, invalid quantity,
|
|
691
|
+
* duplicate under the `strict` strategy, `sum` overflow, a blank grant event
|
|
692
|
+
* id, or an `extraTags` entry that conflicts with a managed tag.
|
|
693
|
+
*/
|
|
694
|
+
declare function buildGameInventoryEvent(input: BuildGameInventoryInput): UnsignedEventTemplate<KindGameInventory>;
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Read the quantity of an item in an inventory.
|
|
698
|
+
*
|
|
699
|
+
* Returns `0` when the item is not present. This mirrors inventory semantics:
|
|
700
|
+
* a missing item is equivalent to holding zero of it.
|
|
701
|
+
*/
|
|
702
|
+
declare function getInventoryItemQuantity(inventory: GameInventory, itemAddress: string): number;
|
|
703
|
+
/**
|
|
704
|
+
* Return a new inventory with the given item's quantity set to an exact value.
|
|
705
|
+
*
|
|
706
|
+
* - `itemAddress` must be a valid kind:31632 coordinate (throws otherwise).
|
|
707
|
+
* - `quantity` must be a non-negative safe integer (throws otherwise); it is
|
|
708
|
+
* never floored, clamped, or silently dropped.
|
|
709
|
+
* - Setting a quantity of `0` removes the item.
|
|
710
|
+
* - The input inventory is never mutated (pure / immutable). When the
|
|
711
|
+
* operation is rejected it throws before producing any value, so the input
|
|
712
|
+
* is untouched.
|
|
713
|
+
*/
|
|
714
|
+
declare function setInventoryItemQuantity(inventory: GameInventory, itemAddress: string, quantity: number, relay?: string): GameInventory;
|
|
715
|
+
/**
|
|
716
|
+
* Return a new inventory with `amount` added to the item's quantity.
|
|
717
|
+
*
|
|
718
|
+
* - `itemAddress` must be a valid kind:31632 coordinate (throws otherwise).
|
|
719
|
+
* - `amount` must be a non-negative safe integer (throws otherwise). An amount
|
|
720
|
+
* of `0` is a no-op that still returns a fresh inventory object.
|
|
721
|
+
* - Throws if the resulting quantity would exceed Number.MAX_SAFE_INTEGER.
|
|
722
|
+
* - The input inventory is never mutated.
|
|
723
|
+
*/
|
|
724
|
+
declare function addInventoryItemQuantity(inventory: GameInventory, itemAddress: string, amount: number, relay?: string): GameInventory;
|
|
725
|
+
/**
|
|
726
|
+
* Return a new inventory with `amount` removed from the item's quantity.
|
|
727
|
+
*
|
|
728
|
+
* - `itemAddress` must be a valid kind:31632 coordinate (throws otherwise).
|
|
729
|
+
* - `amount` must be a non-negative safe integer (throws otherwise).
|
|
730
|
+
* - The result is clamped at zero; reaching zero removes the item. (Clamping a
|
|
731
|
+
* removal *result* to zero is allowed; the requested `amount` itself must be
|
|
732
|
+
* valid and is never silently corrected.)
|
|
733
|
+
* - The input inventory is never mutated.
|
|
734
|
+
*/
|
|
735
|
+
declare function removeInventoryItemQuantity(inventory: GameInventory, itemAddress: string, amount: number): GameInventory;
|
|
736
|
+
/**
|
|
737
|
+
* Return a shallow copy of the inventory's items array.
|
|
738
|
+
*
|
|
739
|
+
* Useful for callers that want the item list without exposing the internal
|
|
740
|
+
* reference.
|
|
741
|
+
*/
|
|
742
|
+
declare function getInventoryItems(inventory: GameInventory): GameInventoryItem[];
|
|
743
|
+
|
|
744
|
+
export { type AddressParseOptions, type AddressableEventAddress, BASED_ON_MARKER, type BuildGameInventoryGrantInput, type BuildGameInventoryInput, type BuildGameInventoryItemInput, type BuildGameItemDefinitionInput, type ContentParseResult, type DuplicateStrategy, GRANT_MARKER, type GameInventory, type GameInventoryAddress, type GameInventoryGrantReference, type GameInventoryItem, type GameItemAddress, type GameItemBasedOnReference, type GameItemDefinition, type InventoryValidationIssue, type InventoryValidationOptions, type InventoryValidationResult, type ItemDefinitionValidationIssue, type ItemDefinitionValidationOptions, type ItemDefinitionValidationResult, KIND_GAME_INVENTORY, KIND_GAME_ITEM_DEFINITION, type KindGameInventory, type KindGameItemDefinition, type NostrEvent, type ParseGameInventoryOptions, type ParseGameItemDefinitionOptions, type ParseMode, type ParseResult, type ParseWarning, type ParseWarningCode, type UnsignedEventTemplate, addInventoryItemQuantity, buildAddressableEventAddress, buildGameInventoryAddress, buildGameInventoryEvent, buildGameItemAddress, buildGameItemDefinitionEvent, encodeInventoryQuantity, getDTag, getInventoryItemQuantity, getInventoryItems, getTagValue, getTagValues, parseAddressableEventAddress, parseContentJson, parseGameInventory, parseGameInventoryAddress, parseGameInventoryResult, parseGameItemAddress, parseGameItemDefinition, parseGameItemDefinitionResult, parseInventoryQuantity, removeInventoryItemQuantity, serializeContent, setInventoryItemQuantity, validateGameInventory, validateGameItemDefinition };
|