@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/common/constants.ts","../src/common/strings.ts","../src/common/address.ts","../src/common/tags.ts","../src/common/json.ts","../src/kinds/game-item-definition/address.ts","../src/kinds/game-item-definition/validate.ts","../src/common/result.ts","../src/kinds/game-item-definition/parse.ts","../src/kinds/game-item-definition/build.ts","../src/kinds/game-inventory/address.ts","../src/kinds/game-inventory/quantity.ts","../src/kinds/game-inventory/validate.ts","../src/kinds/game-inventory/quantity.internal.ts","../src/kinds/game-inventory/parse.ts","../src/kinds/game-inventory/build.ts","../src/kinds/game-inventory/helpers.ts"],"names":["isBlank","HEX64","MANAGED_TAG_NAMES","assertExtraTagAllowed"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Kind numbers defined by this library.\n *\n * - {@link KIND_GAME_ITEM_DEFINITION} (31632): Game Item Definition.\n * - {@link KIND_GAME_INVENTORY} (31633): Game Inventory.\n */\nexport const KIND_GAME_ITEM_DEFINITION = 31632 as const;\nexport const KIND_GAME_INVENTORY = 31633 as const;\n\nexport type KindGameItemDefinition = typeof KIND_GAME_ITEM_DEFINITION;\nexport type KindGameInventory = typeof KIND_GAME_INVENTORY;\n","/**\n * Shared string helpers.\n */\n\n/**\n * A string is \"blank\" if it is not a string, is empty, or contains only\n * whitespace. Callers must never trim otherwise-valid values based on this\n * check; it only decides whether a value counts as absent.\n */\nexport function isBlank(value: unknown): boolean {\n return typeof value !== \"string\" || value.trim() === \"\";\n}\n","/**\n * Helpers for NIP-01 addressable event coordinates of the form:\n *\n * ```text\n * <kind>:<pubkey>:<d-tag>\n * ```\n *\n * IMPORTANT: the `d-tag` may itself contain `:` characters. The recommended\n * `d` format in kind:31632 is `<namespace>:<category>:<slug>` (for example\n * `blobbi:food:carrot`), which means a full address such as\n * `31632:pubkey123:blobbi:food:carrot` has more than three colon-separated\n * segments. Address parsing therefore splits only on the first two colons and\n * treats everything after the second colon as the `d-tag`.\n */\n\nimport { isBlank } from \"./strings.js\";\n\nexport interface AddressableEventAddress {\n kind: number;\n pubkey: string;\n identifier: string;\n}\n\n/**\n\n * Options controlling how strict pubkey validation is.\n *\n * Placeholder pubkeys are accepted by default to support fixtures, drafts,\n * and permissive parsing. Consumers may enable canonical 64-character hex\n * validation.\n *\n * - By default, only a non-empty pubkey segment is required.\n * - When `requireHexPubkey` is `true`, the pubkey must be a 64-character\n * lowercase hexadecimal string, which is the canonical Nostr pubkey form.\n */\nexport interface AddressParseOptions {\n requireHexPubkey?: boolean;\n}\n\nconst HEX64 = /^[0-9a-f]{64}$/;\n\n/**\n * Build an addressable event coordinate string.\n *\n * @throws if any component is invalid (empty pubkey/identifier or non-integer\n * kind). Builders in this library validate their inputs before calling this,\n * so a throw here indicates a programming error rather than bad user data.\n */\nexport function buildAddressableEventAddress(\n kind: number,\n pubkey: string,\n identifier: string,\n): string {\n if (!Number.isInteger(kind) || kind < 0) {\n throw new Error(`Invalid kind for address: ${String(kind)}`);\n }\n if (pubkey.trim() === \"\") {\n throw new Error(\"Cannot build address with empty pubkey\");\n }\n if (identifier.trim() === \"\") {\n throw new Error(\"Cannot build address with empty identifier\");\n }\n return `${kind}:${pubkey}:${identifier}`;\n}\n\n/**\n * Parse an addressable event coordinate string.\n *\n * Returns `null` when the string is not a well-formed `<kind>:<pubkey>:<d>`\n * coordinate. Everything after the second colon is treated as the identifier\n * (`d` tag), preserving any colons it contains.\n */\nexport function parseAddressableEventAddress(\n address: string,\n options: AddressParseOptions = {},\n): AddressableEventAddress | null {\n if (typeof address !== \"string\") {\n return null;\n }\n\n const firstColon = address.indexOf(\":\");\n if (firstColon <= 0) {\n return null;\n }\n const secondColon = address.indexOf(\":\", firstColon + 1);\n if (secondColon < 0) {\n return null;\n }\n\n const kindStr = address.slice(0, firstColon);\n const pubkey = address.slice(firstColon + 1, secondColon);\n const identifier = address.slice(secondColon + 1);\n\n if (!/^\\d+$/.test(kindStr)) {\n return null;\n }\n const kind = Number.parseInt(kindStr, 10);\n if (!Number.isSafeInteger(kind)) {\n return null;\n }\n\n // Reject blank (empty or whitespace-only) segments. Valid values are never\n // trimmed or otherwise mutated.\n if (isBlank(pubkey) || isBlank(identifier)) {\n return null;\n }\n\n if (options.requireHexPubkey && !HEX64.test(pubkey)) {\n return null;\n }\n\n return { kind, pubkey, identifier };\n}\n","import type { NostrEvent } from \"../nostr/event.js\";\n\n/**\n * Return the value of the first tag with the given name, or `undefined` if no\n * such tag exists or its value slot is missing.\n */\nexport function getTagValue(\n tags: string[][],\n name: string,\n): string | undefined {\n for (const tag of tags) {\n if (tag[0] === name) {\n return tag[1];\n }\n }\n return undefined;\n}\n\n/**\n * Return all values of tags with the given name (index 1), skipping tags whose\n * value slot is missing.\n */\nexport function getTagValues(tags: string[][], name: string): string[] {\n const values: string[] = [];\n for (const tag of tags) {\n if (tag[0] === name && typeof tag[1] === \"string\") {\n values.push(tag[1]);\n }\n }\n return values;\n}\n\n/**\n * Return the `d` tag value of an event or its raw tags.\n *\n * Returns `undefined` if there is no `d` tag or its value is missing. Note that\n * an empty-string `d` value is returned as `\"\"` (the caller decides whether an\n * empty `d` is acceptable; per both specs it is not).\n */\nexport function getDTag(source: NostrEvent | string[][]): string | undefined {\n const tags = Array.isArray(source) ? source : source.tags;\n return getTagValue(tags, \"d\");\n}\n","/**\n * Result of attempting to parse a `content` string as JSON.\n */\nexport type ContentParseResult =\n | { kind: \"empty\" }\n | { kind: \"json\"; value: unknown }\n | { kind: \"invalid\"; raw: string };\n\n/**\n * Attempt to parse an event `content` string as JSON without throwing.\n *\n * An empty string is treated as {@link ContentParseResult} `empty`, matching\n * both specifications which allow `content` to be an empty string.\n *\n * This never throws: invalid JSON is reported as `invalid` so callers can\n * decide how to react based on their parse mode.\n */\nexport function parseContentJson(content: string): ContentParseResult {\n if (content === \"\") {\n return { kind: \"empty\" };\n }\n try {\n return { kind: \"json\", value: JSON.parse(content) as unknown };\n } catch {\n return { kind: \"invalid\", raw: content };\n }\n}\n\n/**\n * Normalize a `content` input that may be either a preserialized string or a\n * value to be JSON-serialized.\n *\n * - `undefined` -> `\"\"` (the spec default for both kinds).\n * - `string` -> returned as-is (the caller is responsible for its validity).\n * - anything else -> `JSON.stringify`.\n *\n * This function always either returns a valid string or throws; it never\n * returns `undefined`. `JSON.stringify` returns `undefined` for values that\n * are not JSON-serializable (for example `function`, `symbol`, or a top-level\n * `undefined` reached via another path), and throws on circular references. In\n * both cases a clear error is raised.\n *\n * @throws {Error} when the value is not JSON-serializable (including circular\n * references).\n */\nexport function serializeContent(content: unknown): string {\n if (content === undefined) {\n return \"\";\n }\n if (typeof content === \"string\") {\n return content;\n }\n\n let serialized: string | undefined;\n try {\n serialized = JSON.stringify(content);\n } catch (cause) {\n const detail = cause instanceof Error ? `: ${cause.message}` : \"\";\n throw new Error(\n `serializeContent: value is not JSON-serializable (circular reference?)${detail}`,\n );\n }\n\n if (serialized === undefined) {\n throw new Error(\n \"serializeContent: value is not JSON-serializable (produced `undefined`, e.g. a function or symbol)\",\n );\n }\n\n return serialized;\n}\n","import {\n KIND_GAME_ITEM_DEFINITION,\n type KindGameItemDefinition,\n} from \"../../common/constants.js\";\nimport {\n buildAddressableEventAddress,\n parseAddressableEventAddress,\n type AddressParseOptions,\n} from \"../../common/address.js\";\n\nexport { KIND_GAME_ITEM_DEFINITION, type KindGameItemDefinition };\n\n/**\n * The marker used on an `a` tag to indicate a derivation reference.\n */\nexport const BASED_ON_MARKER = \"based_on\" as const;\n\nexport interface GameItemAddress {\n kind: KindGameItemDefinition;\n pubkey: string;\n /** The item `d` tag / identifier. */\n itemId: string;\n}\n\n/**\n * Build the addressable coordinate for a game item definition:\n * `31632:<pubkey>:<item-d-tag>`.\n */\nexport function buildGameItemAddress(pubkey: string, itemId: string): string {\n return buildAddressableEventAddress(\n KIND_GAME_ITEM_DEFINITION,\n pubkey,\n itemId,\n );\n}\n\n/**\n * Parse a `31632:<pubkey>:<item-d-tag>` coordinate.\n *\n * Returns `null` if the string is malformed or does not reference kind 31632.\n */\nexport function parseGameItemAddress(\n address: string,\n options: AddressParseOptions = {},\n): GameItemAddress | null {\n const parsed = parseAddressableEventAddress(address, options);\n if (parsed === null || parsed.kind !== KIND_GAME_ITEM_DEFINITION) {\n return null;\n }\n return {\n kind: KIND_GAME_ITEM_DEFINITION,\n pubkey: parsed.pubkey,\n itemId: parsed.identifier,\n };\n}\n","import type { NostrEvent } from \"../../nostr/event.js\";\nimport { KIND_GAME_ITEM_DEFINITION } from \"../../common/constants.js\";\nimport { getTagValue } from \"../../common/tags.js\";\nimport { parseContentJson } from \"../../common/json.js\";\n\n/**\n * A structured validation issue for a kind:31632 event.\n */\nexport interface ItemDefinitionValidationIssue {\n code:\n | \"wrong-kind\"\n | \"missing-d\"\n | \"empty-d\"\n | \"missing-name\"\n | \"empty-name\"\n | \"missing-type\"\n | \"empty-type\"\n | \"invalid-json-content\";\n message: string;\n}\n\nexport interface ItemDefinitionValidationOptions {\n /**\n * When `true`, `content` must be empty or valid JSON. This mirrors the spec:\n * an item definition is rejected for invalid JSON only when the client\n * \"requires strict JSON content\". Defaults to `false`.\n */\n requireJsonContent?: boolean;\n}\n\nexport interface ItemDefinitionValidationResult {\n valid: boolean;\n issues: ItemDefinitionValidationIssue[];\n}\n\n/** A string is \"blank\" if it is empty or contains only whitespace. */\nfunction isBlank(value: string): boolean {\n return value.trim() === \"\";\n}\n\n/**\n * Validate that an event is a well-formed kind:31632 Game Item Definition.\n *\n * Per the spec, an event is rejected as an item definition if:\n * - `kind` is not 31632\n * - the `d` tag is missing or empty\n * - the `name` tag is missing or empty\n * - the `type` tag is missing or empty\n * - (only when {@link ItemDefinitionValidationOptions.requireJsonContent})\n * `content` is not valid JSON\n *\n * A required value that contains only whitespace is treated as empty. Values\n * are never trimmed or otherwise mutated.\n */\nexport function validateGameItemDefinition(\n event: NostrEvent,\n options: ItemDefinitionValidationOptions = {},\n): ItemDefinitionValidationResult {\n const issues: ItemDefinitionValidationIssue[] = [];\n\n if (event.kind !== KIND_GAME_ITEM_DEFINITION) {\n issues.push({\n code: \"wrong-kind\",\n message: `Expected kind ${KIND_GAME_ITEM_DEFINITION}, got ${event.kind}`,\n });\n }\n\n const d = getTagValue(event.tags, \"d\");\n if (d === undefined) {\n issues.push({ code: \"missing-d\", message: \"Missing required `d` tag\" });\n } else if (isBlank(d)) {\n issues.push({ code: \"empty-d\", message: \"`d` tag value is empty\" });\n }\n\n const name = getTagValue(event.tags, \"name\");\n if (name === undefined) {\n issues.push({\n code: \"missing-name\",\n message: \"Missing required `name` tag\",\n });\n } else if (isBlank(name)) {\n issues.push({ code: \"empty-name\", message: \"`name` tag value is empty\" });\n }\n\n const type = getTagValue(event.tags, \"type\");\n if (type === undefined) {\n issues.push({\n code: \"missing-type\",\n message: \"Missing required `type` tag\",\n });\n } else if (isBlank(type)) {\n issues.push({ code: \"empty-type\", message: \"`type` tag value is empty\" });\n }\n\n if (options.requireJsonContent) {\n const parsed = parseContentJson(event.content);\n if (parsed.kind === \"invalid\") {\n issues.push({\n code: \"invalid-json-content\",\n message: \"`content` is not valid JSON\",\n });\n }\n }\n\n return { valid: issues.length === 0, issues };\n}\n","/**\n * Parsing modes shared across the library.\n *\n * - `permissive` (default): follow the \"SHOULD tolerate\" rules of the specs.\n * Invalid tags are ignored, invalid JSON content does not block tag parsing,\n * duplicate inventory items resolve using the recommended default strategy.\n * - `strict`: reject the event when the specs allow rejection (for example\n * duplicate inventory item references, or invalid JSON content when JSON is\n * required).\n *\n * \"MUST reject\" rules (wrong kind, missing/empty `d`, missing required item\n * definition tags) apply in both modes.\n */\nexport type ParseMode = \"permissive\" | \"strict\";\n\n/**\n * A recoverable, non-fatal issue detected while parsing.\n *\n * Warnings never prevent parsing in permissive mode. They are surfaced so\n * callers can audit what was ignored or coerced.\n */\nexport interface ParseWarning {\n /** Machine-readable warning code. */\n code: ParseWarningCode;\n /** Human-readable explanation. */\n message: string;\n /** The offending tag, when the warning relates to a specific tag. */\n tag?: string[];\n}\n\nexport type ParseWarningCode =\n | \"invalid-json-content\"\n | \"invalid-item-tag\"\n | \"invalid-quantity\"\n | \"wrong-referenced-kind\"\n | \"malformed-address\"\n | \"duplicate-item\"\n | \"invalid-grant-tag\"\n | \"empty-required-value\";\n\n/**\n * Structured parse result.\n *\n * `ok: true` means a usable value was produced (possibly with warnings about\n * ignored tags). `ok: false` means the event was rejected as invalid for this\n * kind.\n */\nexport type ParseResult<T> =\n | { ok: true; value: T; warnings: ParseWarning[] }\n | { ok: false; error: string; warnings: ParseWarning[] };\n\nexport function ok<T>(value: T, warnings: ParseWarning[] = []): ParseResult<T> {\n return { ok: true, value, warnings };\n}\n\nexport function fail<T = never>(\n error: string,\n warnings: ParseWarning[] = [],\n): ParseResult<T> {\n return { ok: false, error, warnings };\n}\n","import type { NostrEvent } from \"../../nostr/event.js\";\nimport {\n type ParseMode,\n type ParseResult,\n type ParseWarning,\n ok,\n fail,\n} from \"../../common/result.js\";\nimport { getTagValue, getTagValues } from \"../../common/tags.js\";\nimport { parseContentJson } from \"../../common/json.js\";\nimport { KIND_GAME_ITEM_DEFINITION } from \"../../common/constants.js\";\nimport {\n buildGameItemAddress,\n parseGameItemAddress,\n BASED_ON_MARKER,\n} from \"./address.js\";\nimport { validateGameItemDefinition } from \"./validate.js\";\nimport type { GameItemDefinition, GameItemBasedOnReference } from \"./types.js\";\n\nexport interface ParseGameItemDefinitionOptions {\n /**\n * `permissive` (default) tolerates invalid JSON content and unknown tags.\n * `strict` additionally rejects the event when `content` is non-empty and\n * not valid JSON (i.e. it implies `requireJsonContent`).\n */\n mode?: ParseMode;\n /**\n * Explicitly require valid JSON content, independent of mode. When left\n * `undefined`, this defaults to `true` in strict mode and `false` in\n * permissive mode.\n */\n requireJsonContent?: boolean;\n}\n\n/**\n * Parse a kind:31632 event into a {@link GameItemDefinition}, returning a\n * structured {@link ParseResult}.\n *\n * The event is rejected (`ok: false`) only for the MUST-reject conditions\n * (wrong kind, missing/empty `d`/`name`/`type`, and invalid JSON when\n * required). Unknown tags are preserved on the event and tolerated. Invalid\n * JSON content in permissive mode produces a warning, not a rejection. Invalid\n * `based_on` references (malformed address or wrong referenced kind) are\n * ignored and reported as warnings.\n */\nexport function parseGameItemDefinitionResult(\n event: NostrEvent,\n options: ParseGameItemDefinitionOptions = {},\n): ParseResult<GameItemDefinition> {\n const mode: ParseMode = options.mode ?? \"permissive\";\n const requireJsonContent = options.requireJsonContent ?? mode === \"strict\";\n const warnings: ParseWarning[] = [];\n\n const validation = validateGameItemDefinition(event, {\n requireJsonContent,\n });\n if (!validation.valid) {\n return fail(validation.issues.map((i) => i.message).join(\"; \"), warnings);\n }\n\n // Validation guarantees these exist and are non-empty.\n const id = getTagValue(event.tags, \"d\") as string;\n const name = getTagValue(event.tags, \"name\") as string;\n const type = getTagValue(event.tags, \"type\") as string;\n\n const contentParsed = parseContentJson(event.content);\n let contentJson: unknown;\n if (contentParsed.kind === \"json\") {\n contentJson = contentParsed.value;\n } else if (contentParsed.kind === \"invalid\") {\n // requireJsonContent already rejected in validation; here we only warn.\n warnings.push({\n code: \"invalid-json-content\",\n message: \"`content` is not valid JSON; ignored\",\n });\n }\n\n const basedOn = parseBasedOnReferences(event.tags, warnings);\n\n const definition: GameItemDefinition = {\n id,\n address: buildGameItemAddress(event.pubkey, id),\n issuer: event.pubkey,\n kind: KIND_GAME_ITEM_DEFINITION,\n name,\n type,\n contexts: getTagValues(event.tags, \"context\"),\n topics: getTagValues(event.tags, \"t\"),\n basedOn,\n content: event.content,\n event,\n };\n\n assignOptional(definition, \"category\", getTagValue(event.tags, \"category\"));\n assignOptional(definition, \"image\", getTagValue(event.tags, \"image\"));\n assignOptional(definition, \"model3d\", getTagValue(event.tags, \"model_3d\"));\n assignOptional(definition, \"audio\", getTagValue(event.tags, \"audio\"));\n assignOptional(definition, \"symbol\", getTagValue(event.tags, \"symbol\"));\n assignOptional(definition, \"rarity\", getTagValue(event.tags, \"rarity\"));\n assignOptional(definition, \"maxStack\", getTagValue(event.tags, \"max_stack\"));\n assignOptional(definition, \"version\", getTagValue(event.tags, \"version\"));\n assignOptional(definition, \"alt\", getTagValue(event.tags, \"alt\"));\n\n if (contentJson !== undefined) {\n definition.contentJson = contentJson;\n }\n\n return ok(definition, warnings);\n}\n\n/**\n * Convenience wrapper returning the parsed definition or `null`.\n *\n * Prefer {@link parseGameItemDefinitionResult} when you need warnings or the\n * rejection reason.\n */\nexport function parseGameItemDefinition(\n event: NostrEvent,\n options: ParseGameItemDefinitionOptions = {},\n): GameItemDefinition | null {\n const result = parseGameItemDefinitionResult(event, options);\n return result.ok ? result.value : null;\n}\n\n/**\n * Parse `a` tags carrying the `based_on` marker into derivation references.\n *\n * Each reference must be a well-formed addressable coordinate that references\n * kind 31632. References that are malformed or point at a different kind are\n * ignored and reported as warnings (`malformed-address` /\n * `wrong-referenced-kind`). Valid references keep their relay hint.\n */\nfunction parseBasedOnReferences(\n tags: string[][],\n warnings: ParseWarning[],\n): GameItemBasedOnReference[] {\n const refs: GameItemBasedOnReference[] = [];\n for (const tag of tags) {\n if (tag[0] !== \"a\") {\n continue;\n }\n if (tag[3] !== BASED_ON_MARKER) {\n continue;\n }\n const address = tag[1];\n if (typeof address !== \"string\" || address === \"\") {\n warnings.push({\n code: \"malformed-address\",\n message: \"`based_on` `a` tag is missing an address; ignored\",\n tag,\n });\n continue;\n }\n\n // A coordinate that starts with the 31632 kind prefix but still fails to\n // parse is malformed; otherwise it references a different kind.\n const parsed = parseGameItemAddress(address);\n if (parsed === null) {\n const isMalformed = address.startsWith(`${KIND_GAME_ITEM_DEFINITION}:`);\n warnings.push({\n code: isMalformed ? \"malformed-address\" : \"wrong-referenced-kind\",\n message: isMalformed\n ? `Malformed \\`based_on\\` address; ignored: ${address}`\n : `\\`based_on\\` address does not reference kind ${KIND_GAME_ITEM_DEFINITION}; ignored: ${address}`,\n tag,\n });\n continue;\n }\n\n refs.push({ address, relay: tag[2] ?? \"\" });\n }\n return refs;\n}\n\nfunction assignOptional<T, K extends keyof T>(\n target: T,\n key: K,\n value: T[K] | undefined,\n): void {\n if (value !== undefined) {\n target[key] = value;\n }\n}\n","import type { UnsignedEventTemplate } from \"../../nostr/event.js\";\nimport {\n KIND_GAME_ITEM_DEFINITION,\n type KindGameItemDefinition,\n} from \"../../common/constants.js\";\nimport { serializeContent } from \"../../common/json.js\";\nimport { BASED_ON_MARKER, parseGameItemAddress } from \"./address.js\";\nimport type { BuildGameItemDefinitionInput } from \"./types.js\";\n\n/**\n * Tag names fully managed by this builder. They MUST NOT appear in\n * `extraTags`: supplying one is a conflict and causes the builder to throw,\n * rather than silently emitting a duplicate or being ignored.\n */\nconst MANAGED_TAG_NAMES = new Set<string>([\n \"d\",\n \"name\",\n \"type\",\n \"category\",\n \"image\",\n \"model_3d\",\n \"audio\",\n \"symbol\",\n \"rarity\",\n \"max_stack\",\n \"version\",\n \"context\",\n \"t\",\n \"alt\",\n]);\n\n/**\n * Build an unsigned kind:31632 event template.\n *\n * The builder:\n * - validates required input (`id`, `name`, `type` must be non-empty and not\n * whitespace-only);\n * - validates `maxStack` (positive decimal integer) and numeric `version`\n * (finite);\n * - validates every `basedOn` reference (well-formed kind:31632 coordinate);\n * - never creates `id` or `sig`;\n * - emits tags in a stable, deterministic order to simplify testing;\n * - rejects `extraTags` that conflict with builder-managed tags (see\n * {@link MANAGED_TAG_NAMES}); an `a` tag is only treated as conflicting when\n * it carries the `based_on` marker, so other future `a` usages remain\n * possible;\n * - appends the remaining `extraTags` verbatim after the managed tags.\n *\n * Valid values are never trimmed or otherwise mutated.\n *\n * @throws {Error} if a required field is missing/blank, an optional numeric\n * field is invalid, a `basedOn` reference is invalid, or an `extraTags` entry\n * conflicts with a builder-managed tag.\n */\nexport function buildGameItemDefinitionEvent(\n input: BuildGameItemDefinitionInput,\n): UnsignedEventTemplate<KindGameItemDefinition> {\n const id = requireNonBlank(input.id, \"id\");\n const name = requireNonBlank(input.name, \"name\");\n const type = requireNonBlank(input.type, \"type\");\n\n const tags: string[][] = [\n [\"d\", id],\n [\"name\", name],\n [\"type\", type],\n ];\n\n pushOptional(tags, \"category\", input.category);\n pushOptional(tags, \"image\", input.image);\n pushOptional(tags, \"model_3d\", input.model3d);\n pushOptional(tags, \"audio\", input.audio);\n pushOptional(tags, \"symbol\", input.symbol);\n pushOptional(tags, \"rarity\", input.rarity);\n pushOptional(tags, \"max_stack\", validateMaxStack(input.maxStack));\n pushOptional(tags, \"version\", validateVersion(input.version));\n\n for (const context of input.contexts ?? []) {\n // Do not emit repeatable values that are blank; do not normalize others.\n if (!isBlank(context)) {\n tags.push([\"context\", context]);\n }\n }\n for (const topic of input.topics ?? []) {\n if (!isBlank(topic)) {\n tags.push([\"t\", topic]);\n }\n }\n for (const ref of input.basedOn ?? []) {\n if (parseGameItemAddress(ref.address) === null) {\n throw new Error(\n `buildGameItemDefinitionEvent: invalid \\`basedOn.address\\` (must be a kind:${KIND_GAME_ITEM_DEFINITION} coordinate): ${String(ref.address)}`,\n );\n }\n tags.push([\"a\", ref.address, ref.relay ?? \"\", BASED_ON_MARKER]);\n }\n\n pushOptional(tags, \"alt\", input.alt);\n\n for (const tag of input.extraTags ?? []) {\n assertExtraTagAllowed(tag);\n tags.push([...tag]);\n }\n\n return {\n kind: KIND_GAME_ITEM_DEFINITION,\n content: serializeContent(input.content),\n tags,\n };\n}\n\n/** A string is \"blank\" if it is empty or contains only whitespace. */\nfunction isBlank(value: string): boolean {\n return typeof value !== \"string\" || value.trim() === \"\";\n}\n\nfunction requireNonBlank(value: string, field: string): string {\n if (isBlank(value)) {\n throw new Error(\n `buildGameItemDefinitionEvent: \\`${field}\\` is required and must be non-empty`,\n );\n }\n return value;\n}\n\n/**\n * Emit an optional string tag unless the value is missing or blank. Non-empty\n * values are never normalized.\n */\nfunction pushOptional(\n tags: string[][],\n name: string,\n value: string | undefined,\n): void {\n if (value !== undefined && !isBlank(value)) {\n tags.push([name, value]);\n }\n}\n\n/**\n * Validate `maxStack`. When provided it must be a positive decimal integer\n * (per the spec: \"SHOULD be a positive integer\"). `0`, negatives, decimals,\n * NaN, Infinity and non-numeric strings are rejected. Returns the canonical\n * string form, or `undefined` when not provided.\n */\nfunction validateMaxStack(\n value: string | number | undefined,\n): string | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n if (typeof value === \"number\") {\n if (!Number.isInteger(value) || value <= 0) {\n throw new Error(\n `buildGameItemDefinitionEvent: \\`maxStack\\` must be a positive integer: ${String(value)}`,\n );\n }\n return String(value);\n }\n\n // String form: strictly a positive decimal integer (no sign, decimal point,\n // whitespace, exponent, or leading zeros).\n if (!/^[1-9]\\d*$/.test(value)) {\n throw new Error(\n `buildGameItemDefinitionEvent: \\`maxStack\\` must be a positive integer string: ${value}`,\n );\n }\n return value;\n}\n\n/**\n * Validate `version`. String versions are free-form and returned unchanged.\n * Numeric versions must be finite (NaN/Infinity are rejected and never\n * serialized). Returns the string form, or `undefined` when not provided.\n */\nfunction validateVersion(\n value: string | number | undefined,\n): string | undefined {\n if (value === undefined) {\n return undefined;\n }\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) {\n throw new Error(\n `buildGameItemDefinitionEvent: numeric \\`version\\` must be finite: ${String(value)}`,\n );\n }\n return String(value);\n }\n return value;\n}\n\n/**\n * Reject `extraTags` that conflict with builder-managed tags.\n *\n * A tag conflicts when its name is in {@link MANAGED_TAG_NAMES}. The `a` tag is\n * special: it only conflicts when it carries the `based_on` marker at index 3,\n * because that is the specific `a` usage the builder manages. Other `a` tag\n * usages (for example future markers) are allowed through.\n */\nfunction assertExtraTagAllowed(tag: string[]): void {\n const name = tag[0];\n if (name === undefined) {\n return;\n }\n\n if (name === \"a\") {\n if (tag[3] === BASED_ON_MARKER) {\n throw new Error(\n \"buildGameItemDefinitionEvent: `extraTags` may not contain a builder-managed `a`+`based_on` tag; pass it via `basedOn` instead\",\n );\n }\n return;\n }\n\n if (MANAGED_TAG_NAMES.has(name)) {\n throw new Error(\n `buildGameItemDefinitionEvent: \\`extraTags\\` may not contain the builder-managed tag \\`${name}\\``,\n );\n }\n}\n","import {\n KIND_GAME_INVENTORY,\n type KindGameInventory,\n} from \"../../common/constants.js\";\nimport {\n buildAddressableEventAddress,\n parseAddressableEventAddress,\n type AddressParseOptions,\n} from \"../../common/address.js\";\n\nexport { KIND_GAME_INVENTORY, type KindGameInventory };\n\n/**\n * The marker used on an `e` tag to indicate a grant / receipt reference.\n */\nexport const GRANT_MARKER = \"grant\" as const;\n\nexport interface GameInventoryAddress {\n kind: KindGameInventory;\n pubkey: string;\n /** The inventory `d` tag / context identifier. */\n inventoryId: string;\n}\n\n/**\n * Build the addressable coordinate for a game inventory:\n * `31633:<owner-pubkey>:<inventory-d-tag>`.\n */\nexport function buildGameInventoryAddress(\n ownerPubkey: string,\n inventoryId: string,\n): string {\n return buildAddressableEventAddress(\n KIND_GAME_INVENTORY,\n ownerPubkey,\n inventoryId,\n );\n}\n\n/**\n * Parse a `31633:<owner-pubkey>:<inventory-d-tag>` coordinate.\n *\n * Returns `null` if the string is malformed or does not reference kind 31633.\n */\nexport function parseGameInventoryAddress(\n address: string,\n options: AddressParseOptions = {},\n): GameInventoryAddress | null {\n const parsed = parseAddressableEventAddress(address, options);\n if (parsed === null || parsed.kind !== KIND_GAME_INVENTORY) {\n return null;\n }\n return {\n kind: KIND_GAME_INVENTORY,\n pubkey: parsed.pubkey,\n inventoryId: parsed.identifier,\n };\n}\n","/**\n * Public quantity helpers for kind:31633 inventory `a` tags.\n *\n * Per the spec, quantity MUST be encoded as a decimal integer string. Parsers\n * SHOULD ignore item references whose quantity is missing, `0`, negative,\n * decimal, or non-numeric. A \"valid\" stored inventory quantity is therefore a\n * positive integer.\n *\n * - {@link parseInventoryQuantity} parses an untrusted tag string, tolerantly\n * returning `null` for anything that is not a canonical positive decimal\n * integer string.\n * - {@link encodeInventoryQuantity} encodes a positive integer back into its\n * canonical decimal string form.\n *\n * Strict validation of public numeric inputs (used by the builder and the\n * inventory helpers) lives in `quantity.internal.ts` and is not part of the\n * public API.\n */\n\n/**\n * Parse an inventory quantity *string* into a positive integer.\n *\n * Returns `null` for any value that is not a strictly positive canonical\n * decimal integer string. This intentionally rejects:\n * - `undefined` / `null` / missing\n * - `\"0\"` and `\"-1\"` (not positive)\n * - `\"1.5\"` (decimal)\n * - `\"abc\"`, `\"1e3\"`, `\"0x1\"`, `\" 1 \"`, `\"+1\"` (not a plain decimal integer)\n * - `\"03\"` (leading zeros; a well-formed publisher never emits them)\n */\nexport function parseInventoryQuantity(\n raw: string | undefined | null,\n): number | null {\n if (typeof raw !== \"string\") {\n return null;\n }\n if (!/^[1-9]\\d*$/.test(raw)) {\n return null;\n }\n const value = Number.parseInt(raw, 10);\n if (!Number.isSafeInteger(value) || value <= 0) {\n return null;\n }\n return value;\n}\n\n/**\n * Encode a positive integer quantity as its canonical decimal string.\n *\n * @throws if the value is not a positive safe integer.\n */\nexport function encodeInventoryQuantity(value: number): string {\n if (!Number.isSafeInteger(value) || value <= 0) {\n throw new Error(\n `Cannot encode non-positive-integer quantity: ${String(value)}`,\n );\n }\n return String(value);\n}\n","import type { NostrEvent } from \"../../nostr/event.js\";\nimport { KIND_GAME_INVENTORY } from \"../../common/constants.js\";\nimport { getTagValue } from \"../../common/tags.js\";\nimport { parseContentJson } from \"../../common/json.js\";\nimport { isBlank } from \"../../common/strings.js\";\n\nexport interface InventoryValidationIssue {\n code: \"wrong-kind\" | \"missing-d\" | \"empty-d\" | \"invalid-json-content\";\n message: string;\n}\n\nexport interface InventoryValidationOptions {\n /**\n * When `true`, non-empty invalid JSON content is treated as invalid. The\n * spec only allows rejecting invalid JSON when NOT in permissive mode, so\n * this defaults to `false`.\n */\n requireJsonContent?: boolean;\n}\n\nexport interface InventoryValidationResult {\n valid: boolean;\n issues: InventoryValidationIssue[];\n}\n\n/**\n * Validate the MUST-reject conditions for a kind:31633 inventory.\n *\n * An event MUST be rejected if:\n * - `kind` is not 31633\n * - the `d` tag is missing\n * - the `d` tag is empty or contains only whitespace\n *\n * A whitespace-only `d` value is treated as empty; values are never trimmed.\n * Item-level problems (bad quantity, wrong referenced kind, malformed address)\n * do NOT invalidate the inventory; those items are ignored during parsing.\n */\nexport function validateGameInventory(\n event: NostrEvent,\n options: InventoryValidationOptions = {},\n): InventoryValidationResult {\n const issues: InventoryValidationIssue[] = [];\n\n if (event.kind !== KIND_GAME_INVENTORY) {\n issues.push({\n code: \"wrong-kind\",\n message: `Expected kind ${KIND_GAME_INVENTORY}, got ${event.kind}`,\n });\n }\n\n const d = getTagValue(event.tags, \"d\");\n if (d === undefined) {\n issues.push({ code: \"missing-d\", message: \"Missing required `d` tag\" });\n } else if (isBlank(d)) {\n issues.push({ code: \"empty-d\", message: \"`d` tag value is empty\" });\n }\n\n if (options.requireJsonContent) {\n const parsed = parseContentJson(event.content);\n if (parsed.kind === \"invalid\") {\n issues.push({\n code: \"invalid-json-content\",\n message: \"`content` is not valid JSON\",\n });\n }\n }\n\n return { valid: issues.length === 0, issues };\n}\n","/**\n * Internal quantity validation helpers for kind:31633.\n *\n * These are implementation details shared between the builder, parser, and\n * inventory helpers. They are intentionally NOT re-exported from the package's\n * public API (see `index.ts`, which only re-exports `quantity.ts`).\n */\n\n/** Upper bound for safe integer quantities. */\nexport const MAX_QUANTITY = Number.MAX_SAFE_INTEGER;\n\n/**\n * Assert that a public numeric input is a non-negative safe integer.\n *\n * `0` is accepted (it means \"omit\"/\"remove\" the item). Negative values,\n * decimals, `NaN`, `Infinity`, and integers beyond\n * {@link Number.MAX_SAFE_INTEGER} are rejected by throwing. The value is never\n * coerced, floored, or clamped.\n *\n * @throws {Error} when `value` is not a non-negative safe integer.\n */\nexport function assertNonNegativeInteger(value: number, label: string): number {\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n throw new Error(`${label} must be a number, got: ${String(value)}`);\n }\n if (!Number.isFinite(value)) {\n throw new Error(`${label} must be finite, got: ${String(value)}`);\n }\n if (!Number.isInteger(value)) {\n throw new Error(`${label} must be an integer, got: ${String(value)}`);\n }\n if (value < 0) {\n throw new Error(`${label} must be non-negative, got: ${String(value)}`);\n }\n if (!Number.isSafeInteger(value)) {\n throw new Error(\n `${label} exceeds Number.MAX_SAFE_INTEGER, got: ${String(value)}`,\n );\n }\n return value;\n}\n\n/**\n * Add two non-negative integers, throwing if the result would exceed\n * {@link Number.MAX_SAFE_INTEGER}. Both operands are assumed already validated\n * as non-negative safe integers.\n *\n * @throws {Error} on overflow.\n */\nexport function addQuantitiesChecked(\n a: number,\n b: number,\n label: string,\n): number {\n const sum = a + b;\n if (sum > MAX_QUANTITY || !Number.isSafeInteger(sum)) {\n throw new Error(\n `${label} overflow: ${String(a)} + ${String(b)} exceeds Number.MAX_SAFE_INTEGER`,\n );\n }\n return sum;\n}\n","import type { NostrEvent } from \"../../nostr/event.js\";\nimport {\n type ParseMode,\n type ParseResult,\n type ParseWarning,\n ok,\n fail,\n} from \"../../common/result.js\";\nimport { getTagValue, getTagValues } from \"../../common/tags.js\";\nimport { parseContentJson } from \"../../common/json.js\";\nimport { isBlank } from \"../../common/strings.js\";\nimport { KIND_GAME_INVENTORY } from \"../../common/constants.js\";\nimport {\n parseGameItemAddress,\n KIND_GAME_ITEM_DEFINITION,\n} from \"../game-item-definition/address.js\";\nimport { parseAddressableEventAddress } from \"../../common/address.js\";\nimport { parseInventoryQuantity } from \"./quantity.js\";\nimport { MAX_QUANTITY } from \"./quantity.internal.js\";\nimport { validateGameInventory } from \"./validate.js\";\nimport { GRANT_MARKER, buildGameInventoryAddress } from \"./address.js\";\nimport type {\n GameInventory,\n GameInventoryItem,\n GameInventoryGrantReference,\n DuplicateStrategy,\n} from \"./types.js\";\n\nconst HEX64 = /^[0-9a-f]{64}$/;\n\nexport interface ParseGameInventoryOptions {\n /**\n * `permissive` (default) tolerates invalid JSON content and ignores invalid\n * item tags. `strict` additionally:\n * - rejects the event when `content` is non-empty and not valid JSON;\n * - defaults the duplicate strategy to `strict` (reject on duplicates).\n */\n mode?: ParseMode;\n /**\n * Duplicate item address handling. When omitted, defaults to `strict` in\n * strict mode and `last` in permissive mode (the spec's recommended\n * default).\n */\n duplicateStrategy?: DuplicateStrategy;\n /**\n * Require valid JSON content independent of mode. Defaults to `true` in\n * strict mode, `false` in permissive mode.\n */\n requireJsonContent?: boolean;\n /**\n * Require item addresses to use 64-char hex pubkeys. Defaults to `false`\n * (the spec examples use placeholder pubkeys).\n */\n requireHexPubkey?: boolean;\n /**\n * Require grant `e` tag event ids to be canonical 64-char lowercase hex.\n * Defaults to `false` so fixture placeholders remain supported.\n */\n requireHexEventId?: boolean;\n}\n\n/**\n * Parse a kind:31633 event into a {@link GameInventory}, returning a structured\n * {@link ParseResult}.\n *\n * The event is rejected (`ok: false`) only when: kind is wrong, `d` is\n * missing/empty, JSON content is required but invalid, duplicate item\n * references are present under the `strict` strategy, or a `sum` duplicate\n * resolution would overflow Number.MAX_SAFE_INTEGER. Item references that are\n * malformed, reference a non-31632 kind, or carry an invalid quantity are\n * ignored and reported as warnings. Malformed grant tags are ignored with an\n * `invalid-grant-tag` warning.\n */\nexport function parseGameInventoryResult(\n event: NostrEvent,\n options: ParseGameInventoryOptions = {},\n): ParseResult<GameInventory> {\n const mode: ParseMode = options.mode ?? \"permissive\";\n const requireJsonContent = options.requireJsonContent ?? mode === \"strict\";\n const duplicateStrategy: DuplicateStrategy =\n options.duplicateStrategy ?? (mode === \"strict\" ? \"strict\" : \"last\");\n const requireHexPubkey = options.requireHexPubkey ?? false;\n const requireHexEventId = options.requireHexEventId ?? false;\n const warnings: ParseWarning[] = [];\n\n const validation = validateGameInventory(event, { requireJsonContent });\n if (!validation.valid) {\n return fail(validation.issues.map((i) => i.message).join(\"; \"), warnings);\n }\n\n const id = getTagValue(event.tags, \"d\") as string;\n\n // Collect valid item references in tag order.\n const collected: GameInventoryItem[] = [];\n for (const tag of event.tags) {\n if (tag[0] !== \"a\") {\n continue;\n }\n const address = tag[1];\n if (typeof address !== \"string\") {\n warnings.push({\n code: \"invalid-item-tag\",\n message: \"`a` tag missing address\",\n tag,\n });\n continue;\n }\n\n // Distinguish malformed vs. wrong-kind by first parsing with the generic\n // addressable-event parser (not by string prefix).\n const generic = parseAddressableEventAddress(address, { requireHexPubkey });\n if (generic === null) {\n warnings.push({\n code: \"malformed-address\",\n message: `Malformed item address: ${address}`,\n tag,\n });\n continue;\n }\n if (generic.kind !== KIND_GAME_ITEM_DEFINITION) {\n warnings.push({\n code: \"wrong-referenced-kind\",\n message: `Item \\`a\\` tag does not reference kind ${KIND_GAME_ITEM_DEFINITION}: ${address}`,\n tag,\n });\n continue;\n }\n // Confirm with the kind-specific parser (honours requireHexPubkey too).\n if (parseGameItemAddress(address, { requireHexPubkey }) === null) {\n warnings.push({\n code: \"malformed-address\",\n message: `Malformed item address: ${address}`,\n tag,\n });\n continue;\n }\n\n const quantity = parseInventoryQuantity(tag[3]);\n if (quantity === null) {\n warnings.push({\n code: \"invalid-quantity\",\n message: `Invalid or missing quantity for ${address}: ${String(tag[3])}`,\n tag,\n });\n continue;\n }\n\n collected.push({ address, relay: tag[2] ?? \"\", quantity });\n }\n\n const resolved = resolveDuplicates(collected, duplicateStrategy, warnings);\n if (!resolved.ok) {\n return fail(resolved.error, warnings);\n }\n\n const grants = parseGrants(event.tags, requireHexEventId, warnings);\n\n const contentParsed = parseContentJson(event.content);\n let contentJson: unknown;\n if (contentParsed.kind === \"json\") {\n contentJson = contentParsed.value;\n } else if (contentParsed.kind === \"invalid\") {\n warnings.push({\n code: \"invalid-json-content\",\n message: \"`content` is not valid JSON; ignored\",\n });\n }\n\n const inventory: GameInventory = {\n id,\n address: buildGameInventoryAddress(event.pubkey, id),\n owner: event.pubkey,\n kind: KIND_GAME_INVENTORY,\n contexts: getTagValues(event.tags, \"context\"),\n items: resolved.value,\n grants,\n grantEventIds: grants.map((g) => g.eventId),\n content: event.content,\n event,\n };\n\n const name = getTagValue(event.tags, \"name\");\n if (name !== undefined) {\n inventory.name = name;\n }\n const alt = getTagValue(event.tags, \"alt\");\n if (alt !== undefined) {\n inventory.alt = alt;\n }\n if (contentJson !== undefined) {\n inventory.contentJson = contentJson;\n }\n\n return ok(inventory, warnings);\n}\n\n/**\n * Convenience wrapper returning the parsed inventory or `null`.\n */\nexport function parseGameInventory(\n event: NostrEvent,\n options: ParseGameInventoryOptions = {},\n): GameInventory | null {\n const result = parseGameInventoryResult(event, options);\n return result.ok ? result.value : null;\n}\n\ntype DuplicateResolution =\n { ok: true; value: GameInventoryItem[] } | { ok: false; error: string };\n\nfunction resolveDuplicates(\n items: GameInventoryItem[],\n strategy: DuplicateStrategy,\n warnings: ParseWarning[],\n): DuplicateResolution {\n // Identify which addresses actually occur more than once.\n const counts = new Map<string, number>();\n for (const item of items) {\n counts.set(item.address, (counts.get(item.address) ?? 0) + 1);\n }\n const hasDuplicate = [...counts.values()].some((n) => n > 1);\n\n if (!hasDuplicate) {\n return { ok: true, value: items.map((item) => ({ ...item })) };\n }\n\n if (strategy === \"strict\") {\n return {\n ok: false,\n error:\n \"Duplicate item references present and duplicate strategy is `strict`\",\n };\n }\n\n // Warn once per duplicated address (not once per item).\n for (const [address, count] of counts) {\n if (count > 1) {\n warnings.push({\n code: \"duplicate-item\",\n message: `Duplicate item reference (${count}x) resolved with strategy \\`${strategy}\\`: ${address}`,\n });\n }\n }\n\n // Preserve first-seen order; `last` overwrites quantity, `sum` accumulates.\n const order: string[] = [];\n const byAddress = new Map<string, GameInventoryItem>();\n for (const item of items) {\n const existing = byAddress.get(item.address);\n if (existing === undefined) {\n order.push(item.address);\n byAddress.set(item.address, { ...item });\n } else if (strategy === \"sum\") {\n const sum = existing.quantity + item.quantity;\n if (sum > MAX_QUANTITY || !Number.isSafeInteger(sum)) {\n return {\n ok: false,\n error: `Summed quantity for ${item.address} exceeds Number.MAX_SAFE_INTEGER`,\n };\n }\n existing.quantity = sum;\n existing.relay = item.relay;\n } else {\n // last\n existing.quantity = item.quantity;\n existing.relay = item.relay;\n }\n }\n\n return {\n ok: true,\n value: order.map((addr) => byAddress.get(addr) as GameInventoryItem),\n };\n}\n\nfunction parseGrants(\n tags: string[][],\n requireHexEventId: boolean,\n warnings: ParseWarning[],\n): GameInventoryGrantReference[] {\n const grants: GameInventoryGrantReference[] = [];\n for (const tag of tags) {\n if (tag[0] !== \"e\") {\n continue;\n }\n if (tag[3] !== GRANT_MARKER) {\n // Not a grant reference; leave for other consumers.\n continue;\n }\n const eventId = tag[1];\n if (typeof eventId !== \"string\" || isBlank(eventId)) {\n warnings.push({\n code: \"invalid-grant-tag\",\n message: \"Grant `e` tag is missing an event id; ignored\",\n tag,\n });\n continue;\n }\n if (requireHexEventId && !HEX64.test(eventId)) {\n warnings.push({\n code: \"invalid-grant-tag\",\n message: `Grant \\`e\\` tag event id is not canonical 64-char hex; ignored: ${eventId}`,\n tag,\n });\n continue;\n }\n grants.push({ eventId, relay: tag[2] ?? \"\" });\n }\n return grants;\n}\n","import type { UnsignedEventTemplate } from \"../../nostr/event.js\";\nimport {\n KIND_GAME_INVENTORY,\n type KindGameInventory,\n} from \"../../common/constants.js\";\nimport { serializeContent } from \"../../common/json.js\";\nimport { isBlank } from \"../../common/strings.js\";\nimport {\n parseGameItemAddress,\n KIND_GAME_ITEM_DEFINITION,\n} from \"../game-item-definition/address.js\";\nimport {\n assertNonNegativeInteger,\n addQuantitiesChecked,\n} from \"./quantity.internal.js\";\nimport { encodeInventoryQuantity } from \"./quantity.js\";\nimport { GRANT_MARKER } from \"./address.js\";\nimport type {\n BuildGameInventoryInput,\n BuildGameInventoryItemInput,\n DuplicateStrategy,\n} from \"./types.js\";\n\n/**\n * Tag names fully managed by this builder. They MUST NOT appear in\n * `extraTags`; supplying one throws rather than emitting a duplicate.\n *\n * `a` is intentionally excluded from this set because it needs the marker-aware\n * check in {@link assertExtraTagAllowed}: in kind:31633 *every* `a` tag is the\n * inventory item representation, so all `a` tags are rejected.\n */\nconst MANAGED_TAG_NAMES = new Set<string>([\"d\", \"context\", \"name\", \"alt\"]);\n\n/**\n * Build an unsigned kind:31633 event template.\n *\n * The builder:\n * - validates the required `d` id (non-empty, not whitespace-only);\n * - validates each item address (must be a kind:31632 coordinate);\n * - requires each item quantity to be a non-negative safe integer; `0` is\n * omitted, and negatives/decimals/NaN/Infinity/unsafe integers throw (they\n * are never floored, clamped, or silently dropped);\n * - resolves duplicate addresses per `duplicateStrategy` (default `last`), and\n * throws if a `sum` overflows Number.MAX_SAFE_INTEGER;\n * - rejects grants with an empty/whitespace-only event id;\n * - emits tags in a stable, deterministic order;\n * - never creates `id` or `sig`;\n * - rejects `extraTags` that conflict with builder-managed tags and appends the\n * rest verbatim after the managed tags.\n *\n * Non-empty display values are never trimmed or otherwise normalized.\n *\n * @throws {Error} for a blank `id`, invalid item address, invalid quantity,\n * duplicate under the `strict` strategy, `sum` overflow, a blank grant event\n * id, or an `extraTags` entry that conflicts with a managed tag.\n */\nexport function buildGameInventoryEvent(\n input: BuildGameInventoryInput,\n): UnsignedEventTemplate<KindGameInventory> {\n if (isBlank(input.id)) {\n throw new Error(\n \"buildGameInventoryEvent: `id` is required and must be non-empty\",\n );\n }\n\n const strategy: DuplicateStrategy = input.duplicateStrategy ?? \"last\";\n const items = normalizeItems(input.items ?? [], strategy);\n\n const tags: string[][] = [[\"d\", input.id]];\n\n for (const context of input.contexts ?? []) {\n // Repeatable display metadata: omit when blank, never normalize otherwise.\n if (!isBlank(context)) {\n tags.push([\"context\", context]);\n }\n }\n if (input.name !== undefined && !isBlank(input.name)) {\n tags.push([\"name\", input.name]);\n }\n\n for (const item of items) {\n tags.push([\n \"a\",\n item.address,\n item.relay,\n encodeInventoryQuantity(item.quantity),\n ]);\n }\n\n for (const grant of input.grants ?? []) {\n if (isBlank(grant.eventId)) {\n throw new Error(\n \"buildGameInventoryEvent: grant `eventId` must be non-empty\",\n );\n }\n tags.push([\"e\", grant.eventId, grant.relay ?? \"\", GRANT_MARKER]);\n }\n\n if (input.alt !== undefined && !isBlank(input.alt)) {\n tags.push([\"alt\", input.alt]);\n }\n\n for (const tag of input.extraTags ?? []) {\n assertExtraTagAllowed(tag);\n tags.push([...tag]);\n }\n\n return {\n kind: KIND_GAME_INVENTORY,\n content: serializeContent(input.content),\n tags,\n };\n}\n\ninterface NormalizedItem {\n address: string;\n relay: string;\n quantity: number;\n}\n\nfunction normalizeItems(\n inputs: BuildGameInventoryItemInput[],\n strategy: DuplicateStrategy,\n): NormalizedItem[] {\n const order: string[] = [];\n const byAddress = new Map<string, NormalizedItem>();\n\n for (const raw of inputs) {\n if (parseGameItemAddress(raw.address) === null) {\n throw new Error(\n `buildGameInventoryEvent: invalid item address (must be a kind:${KIND_GAME_ITEM_DEFINITION} coordinate): ${String(raw.address)}`,\n );\n }\n // Public input: strictly validate; do not floor/clamp/omit-invalid.\n const quantity = assertNonNegativeInteger(raw.quantity, \"item quantity\");\n if (quantity === 0) {\n // `0` means omit the item per the spec.\n continue;\n }\n const relay = raw.relay ?? \"\";\n\n const existing = byAddress.get(raw.address);\n if (existing === undefined) {\n order.push(raw.address);\n byAddress.set(raw.address, { address: raw.address, relay, quantity });\n continue;\n }\n\n if (strategy === \"strict\") {\n throw new Error(\n `buildGameInventoryEvent: duplicate item address under strict strategy: ${raw.address}`,\n );\n } else if (strategy === \"sum\") {\n existing.quantity = addQuantitiesChecked(\n existing.quantity,\n quantity,\n \"summed item quantity\",\n );\n existing.relay = relay;\n } else {\n existing.quantity = quantity;\n existing.relay = relay;\n }\n }\n\n return order.map((addr) => byAddress.get(addr) as NormalizedItem);\n}\n\n/**\n * Reject `extraTags` that conflict with builder-managed tags.\n *\n * Rejected: `d`, `context`, `name`, `alt`; every `a` tag (all `a` tags are the\n * inventory item representation in kind:31633); and `e` tags carrying the\n * `grant` marker at index 3. Unrelated forward-compatible tags — including\n * non-grant `e` tags — are allowed.\n */\nfunction assertExtraTagAllowed(tag: string[]): void {\n const name = tag[0];\n if (name === undefined) {\n return;\n }\n\n if (name === \"a\") {\n throw new Error(\n \"buildGameInventoryEvent: `extraTags` may not contain an `a` tag; pass inventory items via `items` instead\",\n );\n }\n\n if (name === \"e\") {\n if (tag[3] === GRANT_MARKER) {\n throw new Error(\n \"buildGameInventoryEvent: `extraTags` may not contain a grant `e` tag; pass it via `grants` instead\",\n );\n }\n return;\n }\n\n if (MANAGED_TAG_NAMES.has(name)) {\n throw new Error(\n `buildGameInventoryEvent: \\`extraTags\\` may not contain the builder-managed tag \\`${name}\\``,\n );\n }\n}\n","import type { GameInventory, GameInventoryItem } from \"./types.js\";\nimport {\n assertNonNegativeInteger,\n addQuantitiesChecked,\n} from \"./quantity.internal.js\";\nimport { parseGameItemAddress } from \"../game-item-definition/address.js\";\nimport { KIND_GAME_ITEM_DEFINITION } from \"../../common/constants.js\";\n\n/**\n * Assert that `itemAddress` is a well-formed kind:31632 coordinate.\n *\n * @throws {Error} when the address is malformed or references another kind.\n */\nfunction assertItemAddress(itemAddress: string): void {\n if (parseGameItemAddress(itemAddress) === null) {\n throw new Error(\n `itemAddress must be a valid kind:${KIND_GAME_ITEM_DEFINITION} coordinate, got: ${String(itemAddress)}`,\n );\n }\n}\n\n/**\n * Read the quantity of an item in an inventory.\n *\n * Returns `0` when the item is not present. This mirrors inventory semantics:\n * a missing item is equivalent to holding zero of it.\n */\nexport function getInventoryItemQuantity(\n inventory: GameInventory,\n itemAddress: string,\n): number {\n for (const item of inventory.items) {\n if (item.address === itemAddress) {\n return item.quantity;\n }\n }\n return 0;\n}\n\n/**\n * Return a new inventory with the given item's quantity set to an exact value.\n *\n * - `itemAddress` must be a valid kind:31632 coordinate (throws otherwise).\n * - `quantity` must be a non-negative safe integer (throws otherwise); it is\n * never floored, clamped, or silently dropped.\n * - Setting a quantity of `0` removes the item.\n * - The input inventory is never mutated (pure / immutable). When the\n * operation is rejected it throws before producing any value, so the input\n * is untouched.\n */\nexport function setInventoryItemQuantity(\n inventory: GameInventory,\n itemAddress: string,\n quantity: number,\n relay?: string,\n): GameInventory {\n assertItemAddress(itemAddress);\n assertNonNegativeInteger(quantity, \"quantity\");\n\n const existing = inventory.items.find((item) => item.address === itemAddress);\n\n if (quantity === 0) {\n // Remove the item (if present). Always return a fresh object.\n return {\n ...inventory,\n items: inventory.items\n .filter((item) => item.address !== itemAddress)\n .map((item) => ({ ...item })),\n };\n }\n\n const resolvedRelay = relay ?? existing?.relay ?? \"\";\n\n if (existing) {\n // Preserve original position.\n return {\n ...inventory,\n items: inventory.items.map((item) =>\n item.address === itemAddress\n ? { address: itemAddress, relay: resolvedRelay, quantity }\n : { ...item },\n ),\n };\n }\n\n return {\n ...inventory,\n items: [\n ...inventory.items.map((item) => ({ ...item })),\n { address: itemAddress, relay: resolvedRelay, quantity },\n ],\n };\n}\n\n/**\n * Return a new inventory with `amount` added to the item's quantity.\n *\n * - `itemAddress` must be a valid kind:31632 coordinate (throws otherwise).\n * - `amount` must be a non-negative safe integer (throws otherwise). An amount\n * of `0` is a no-op that still returns a fresh inventory object.\n * - Throws if the resulting quantity would exceed Number.MAX_SAFE_INTEGER.\n * - The input inventory is never mutated.\n */\nexport function addInventoryItemQuantity(\n inventory: GameInventory,\n itemAddress: string,\n amount: number,\n relay?: string,\n): GameInventory {\n assertItemAddress(itemAddress);\n assertNonNegativeInteger(amount, \"amount\");\n\n const current = getInventoryItemQuantity(inventory, itemAddress);\n const next = addQuantitiesChecked(current, amount, \"quantity\");\n return setInventoryItemQuantity(inventory, itemAddress, next, relay);\n}\n\n/**\n * Return a new inventory with `amount` removed from the item's quantity.\n *\n * - `itemAddress` must be a valid kind:31632 coordinate (throws otherwise).\n * - `amount` must be a non-negative safe integer (throws otherwise).\n * - The result is clamped at zero; reaching zero removes the item. (Clamping a\n * removal *result* to zero is allowed; the requested `amount` itself must be\n * valid and is never silently corrected.)\n * - The input inventory is never mutated.\n */\nexport function removeInventoryItemQuantity(\n inventory: GameInventory,\n itemAddress: string,\n amount: number,\n): GameInventory {\n assertItemAddress(itemAddress);\n assertNonNegativeInteger(amount, \"amount\");\n\n const current = getInventoryItemQuantity(inventory, itemAddress);\n const next = Math.max(0, current - amount);\n return setInventoryItemQuantity(inventory, itemAddress, next);\n}\n\n/**\n * Return a shallow copy of the inventory's items array.\n *\n * Useful for callers that want the item list without exposing the internal\n * reference.\n */\nexport function getInventoryItems(\n inventory: GameInventory,\n): GameInventoryItem[] {\n return inventory.items.map((item) => ({ ...item }));\n}\n"]}
|