feat: add NIP-89 app address to client tag

- Add GRIMOIRE_APP_ADDRESS and GRIMOIRE_CLIENT_TAG constants
- Update all client tag usages to include the 31990 app definition address
- Update tests to verify the app address is included
- Update spell.ts documentation
This commit is contained in:
Claude
2026-01-21 11:05:54 +00:00
parent 814d7e72ab
commit 46209ae0fe
8 changed files with 39 additions and 17 deletions

View File

@@ -3,6 +3,7 @@ import { PublishSpellbook } from "./publish-spellbook";
import type { ActionContext } from "applesauce-actions";
import type { GrimoireState } from "@/types/app";
import type { NostrEvent } from "nostr-tools/core";
import { GRIMOIRE_APP_ADDRESS } from "@/constants/app";
// Mock accountManager
vi.mock("@/services/accounts", () => ({
@@ -192,6 +193,7 @@ describe("PublishSpellbook action", () => {
expect(descTag?.[1]).toBe("Test description");
expect(clientTag).toBeDefined();
expect(clientTag?.[1]).toBe("grimoire");
expect(clientTag?.[2]).toBe(GRIMOIRE_APP_ADDRESS);
expect(altTag).toBeDefined();
expect(altTag?.[1]).toBe("Grimoire Spellbook: Test Spellbook");
});

View File

@@ -2,6 +2,7 @@ import { createSpellbook, slugify } from "@/lib/spellbook-manager";
import { SpellbookEvent } from "@/types/spell";
import { GrimoireState } from "@/types/app";
import { SpellbookContent } from "@/types/spell";
import { GRIMOIRE_CLIENT_TAG } from "@/constants/app";
import accountManager from "@/services/accounts";
import type { ActionContext } from "applesauce-actions";
@@ -74,7 +75,7 @@ export function PublishSpellbook(options: PublishSpellbookOptions) {
tags: [
["d", slugify(title)],
["title", title],
["client", "grimoire"],
GRIMOIRE_CLIENT_TAG,
] as [string, string, ...string[]][],
};
if (description) {

20
src/constants/app.ts Normal file
View File

@@ -0,0 +1,20 @@
/**
* Grimoire app constants
*/
/**
* Grimoire NIP-89 app definition address (kind 31990)
* Format: "kind:pubkey:identifier"
*/
export const GRIMOIRE_APP_ADDRESS =
"31990:7fa56f5d6962ab1e3cd424e758c3002b8665f7b0d8dcee9fe9e288d7751ac194:k50nvf8d85";
/**
* Client tag for events published by Grimoire
* Format: ["client", "<name>", "<31990:pubkey:d-tag>"]
*/
export const GRIMOIRE_CLIENT_TAG: [string, string, string] = [
"client",
"grimoire",
GRIMOIRE_APP_ADDRESS,
];

View File

@@ -1,6 +1,7 @@
import { describe, it, expect } from "vitest";
import { encodeSpell, decodeSpell } from "./spell-conversion";
import type { SpellEvent } from "@/types/spell";
import { GRIMOIRE_CLIENT_TAG } from "@/constants/app";
describe("Spell Conversion", () => {
describe("encodeSpell", () => {
@@ -11,7 +12,7 @@ describe("Spell Conversion", () => {
});
expect(result.tags).toContainEqual(["cmd", "REQ"]);
expect(result.tags).toContainEqual(["client", "grimoire"]);
expect(result.tags).toContainEqual(GRIMOIRE_CLIENT_TAG);
expect(result.tags).toContainEqual(["k", "1"]);
expect(result.tags).toContainEqual(["k", "3"]);
expect(result.tags).toContainEqual(["k", "7"]);
@@ -253,12 +254,7 @@ describe("Spell Conversion", () => {
pubkey: "test-pubkey",
created_at: 1234567890,
kind: 777,
tags: [
["cmd", "REQ"],
["client", "grimoire"],
["k", "1"],
["k", "3"],
],
tags: [["cmd", "REQ"], GRIMOIRE_CLIENT_TAG, ["k", "1"], ["k", "3"]],
content: "Test spell",
sig: "test-sig",
};
@@ -360,7 +356,7 @@ describe("Spell Conversion", () => {
kind: 777,
tags: [
["cmd", "REQ"],
["client", "grimoire"],
GRIMOIRE_CLIENT_TAG,
["k", "1"],
["authors", "abc123", "def456"],
],
@@ -382,7 +378,7 @@ describe("Spell Conversion", () => {
kind: 777,
tags: [
["cmd", "REQ"],
["client", "grimoire"],
GRIMOIRE_CLIENT_TAG,
["k", "1"],
["tag", "t", "bitcoin", "nostr"],
["tag", "p", "abc123"],
@@ -410,7 +406,7 @@ describe("Spell Conversion", () => {
kind: 777,
tags: [
["cmd", "REQ"],
["client", "grimoire"],
GRIMOIRE_CLIENT_TAG,
["k", "1"],
["since", "7d"],
["until", "now"],
@@ -433,7 +429,7 @@ describe("Spell Conversion", () => {
kind: 777,
tags: [
["cmd", "REQ"],
["client", "grimoire"],
GRIMOIRE_CLIENT_TAG,
["k", "1"],
["t", "bitcoin"],
["t", "news"],
@@ -456,7 +452,7 @@ describe("Spell Conversion", () => {
kind: 777,
tags: [
["cmd", "REQ"],
["client", "grimoire"],
GRIMOIRE_CLIENT_TAG,
["k", "1"],
["e", "abc123def456"],
],

View File

@@ -6,6 +6,7 @@ import type {
SpellEvent,
} from "@/types/spell";
import type { NostrFilter } from "@/types/nostr";
import { GRIMOIRE_CLIENT_TAG } from "@/constants/app";
/**
* Simple tokenization that doesn't expand shell variables
@@ -116,7 +117,7 @@ export function encodeSpell(options: CreateSpellOptions): EncodedSpell {
// Start with required tags
const tags: [string, string, ...string[]][] = [
["cmd", cmdType],
["client", "grimoire"],
GRIMOIRE_CLIENT_TAG,
];
// Add name tag if provided

View File

@@ -7,6 +7,7 @@ import {
} from "./spellbook-manager";
import { GrimoireState, WindowInstance, Workspace } from "@/types/app";
import { SPELLBOOK_KIND, SpellbookEvent } from "@/types/spell";
import { GRIMOIRE_CLIENT_TAG } from "@/constants/app";
// Mock Data
const mockWindow1: WindowInstance = {
@@ -138,7 +139,7 @@ describe("Spellbook Manager", () => {
"description",
"Test description",
]);
expect(eventProps.tags).toContainEqual(["client", "grimoire"]);
expect(eventProps.tags).toContainEqual(GRIMOIRE_CLIENT_TAG);
// Check referenced spells (e tags)
expect(referencedSpells).toContain("spell-1");

View File

@@ -2,6 +2,7 @@ import { v4 as uuidv4 } from "uuid";
import type { MosaicNode } from "react-mosaic-component";
import type { GrimoireState, WindowInstance, Workspace } from "@/types/app";
import { SPELLBOOK_KIND } from "@/constants/kinds";
import { GRIMOIRE_CLIENT_TAG } from "@/constants/app";
import {
type SpellbookContent,
type SpellbookEvent,
@@ -127,7 +128,7 @@ export function createSpellbook(
const tags: [string, string, ...string[]][] = [
["d", slugify(title)],
["title", title],
["client", "grimoire"],
GRIMOIRE_CLIENT_TAG,
];
if (description) {

View File

@@ -13,7 +13,7 @@ export { SPELL_KIND, SPELLBOOK_KIND };
* - ["cmd", "REQ"] - Command type
*
* METADATA:
* - ["client", "grimoire"] - Client identifier
* - ["client", "grimoire", "<31990:pubkey:d-tag>"] - Client identifier with NIP-89 app address
* - ["alt", "description"] - NIP-31 human-readable description
* - ["name", "My Spell"] - Optional spell name (metadata only, not unique identifier)
* - ["t", "bitcoin"], ["t", "news"] - Topic tags for categorization