@singi-labs/sifa-sdk 0.19.62 → 0.19.64

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/dist/index.cjs CHANGED
@@ -7197,7 +7197,7 @@ function describeSifaRecord(collection, value) {
7197
7197
  }
7198
7198
 
7199
7199
  // src/index.ts
7200
- var SIFA_SDK_VERSION = "0.19.62";
7200
+ var SIFA_SDK_VERSION = "0.19.64";
7201
7201
 
7202
7202
  exports.ACTIVITY_TIERS = ACTIVITY_TIERS;
7203
7203
  exports.ACTIVITY_VERBS = ACTIVITY_VERBS;
package/dist/index.js CHANGED
@@ -353,7 +353,7 @@ import {
353
353
  roadmapIssueUrl
354
354
  } from "./roadmap/index.js";
355
355
  import { SIFA_REPO_GROUPS, describeSifaRecord, repoGroupForCollection } from "./repo/index.js";
356
- var SIFA_SDK_VERSION = "0.19.62";
356
+ var SIFA_SDK_VERSION = "0.19.64";
357
357
  export {
358
358
  ACTIVITY_TIERS,
359
359
  ACTIVITY_VERBS,
@@ -0,0 +1,59 @@
1
+ // src/rpg/catalog-drift.ts
2
+ var SIFA_RPG_PROVIDER_DID = "did:plc:2f2ahswozqy4v5lvu676375y";
3
+ var COMPARED_FIELDS = ["title", "kind", "category", "assetCid", "iconCid"];
4
+ function compareRpgCatalog(ours, theirs, expectedProvider = SIFA_RPG_PROVIDER_DID) {
5
+ const remote = new Map(theirs.items.map((i) => [i.item, i]));
6
+ const mismatches = [];
7
+ for (const item of ours) {
8
+ if (!item.enabled) continue;
9
+ const r = remote.get(item.id);
10
+ if (!r) {
11
+ mismatches.push({ item: item.id, field: "listed", ours: "yes", theirs: "missing" });
12
+ continue;
13
+ }
14
+ for (const field of COMPARED_FIELDS) {
15
+ if (item[field] !== r[field]) {
16
+ mismatches.push({
17
+ item: item.id,
18
+ field,
19
+ ours: item[field],
20
+ theirs: r[field] ?? "(absent)"
21
+ });
22
+ }
23
+ }
24
+ }
25
+ const ourIds = new Set(ours.map((i) => i.id));
26
+ const extra = theirs.items.map((i) => i.item).filter((id) => !ourIds.has(id));
27
+ const providerMismatch = theirs.provider === expectedProvider ? null : { expected: expectedProvider, actual: String(theirs.provider) };
28
+ return { ok: !providerMismatch && mismatches.length === 0, providerMismatch, mismatches, extra };
29
+ }
30
+ function renderRpgCatalogDriftMarkdown(result) {
31
+ const lines = ["## rpg.actor item catalog drift", ""];
32
+ if (result.ok) lines.push("No drift: every enabled Sifa item matches rpg.actor.", "");
33
+ if (result.providerMismatch) {
34
+ lines.push(
35
+ `**Provider mismatch:** expected \`${result.providerMismatch.expected}\`, got \`${result.providerMismatch.actual}\`.`,
36
+ ""
37
+ );
38
+ }
39
+ if (result.mismatches.length > 0) {
40
+ lines.push("| Item | Field | Ours | rpg.actor |", "| --- | --- | --- | --- |");
41
+ for (const m of result.mismatches) {
42
+ lines.push(`| ${m.item} | ${m.field} | ${m.ours} | ${m.theirs} |`);
43
+ }
44
+ lines.push("");
45
+ }
46
+ if (result.extra.length > 0) {
47
+ lines.push(
48
+ `Notice: rpg.actor lists items not in our catalog: ${result.extra.map((id) => `\`${id}\``).join(", ")}.`,
49
+ ""
50
+ );
51
+ }
52
+ return lines.join("\n");
53
+ }
54
+ export {
55
+ SIFA_RPG_PROVIDER_DID,
56
+ compareRpgCatalog,
57
+ renderRpgCatalogDriftMarkdown
58
+ };
59
+ //# sourceMappingURL=catalog-drift.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/rpg/catalog-drift.ts"],"sourcesContent":["/**\n * Pure comparison between our rpg.actor item catalog (`RPG_ITEMS`) and the\n * catalog rpg.actor serves at `GET /api/creator/items`. Used by\n * scripts/check-rpg-catalog.mjs (weekly drift check). Internal: deliberately\n * not re-exported from the `/rpg` barrel.\n */\nimport type { RpgItem } from './catalog.js';\n\n/** The sifa.id DID that owns the Sifa items on rpg.actor. */\nexport const SIFA_RPG_PROVIDER_DID = 'did:plc:2f2ahswozqy4v5lvu676375y';\n\nexport interface RpgActorItem {\n item: string;\n title?: string;\n description?: string;\n kind?: string;\n category?: string;\n channels?: string[];\n assetCid?: string;\n iconCid?: string;\n}\n\nexport interface RpgActorCatalog {\n provider: string;\n count?: number;\n items: RpgActorItem[];\n}\n\n/**\n * Fields that must match. `description` is left out on purpose: it is flavor\n * text that may be reworded on either side without affecting gifts.\n */\nconst COMPARED_FIELDS = ['title', 'kind', 'category', 'assetCid', 'iconCid'] as const;\n\nexport interface RpgCatalogMismatch {\n item: string;\n field: (typeof COMPARED_FIELDS)[number] | 'listed';\n ours: string;\n theirs: string;\n}\n\nexport interface RpgCatalogDriftResult {\n ok: boolean;\n providerMismatch: { expected: string; actual: string } | null;\n mismatches: RpgCatalogMismatch[];\n /** Items rpg.actor lists that we do not have. Informational only. */\n extra: string[];\n}\n\nexport function compareRpgCatalog(\n ours: readonly RpgItem[],\n theirs: RpgActorCatalog,\n expectedProvider: string = SIFA_RPG_PROVIDER_DID,\n): RpgCatalogDriftResult {\n const remote = new Map(theirs.items.map((i) => [i.item, i]));\n const mismatches: RpgCatalogMismatch[] = [];\n\n for (const item of ours) {\n if (!item.enabled) continue;\n const r = remote.get(item.id);\n if (!r) {\n mismatches.push({ item: item.id, field: 'listed', ours: 'yes', theirs: 'missing' });\n continue;\n }\n for (const field of COMPARED_FIELDS) {\n if (item[field] !== r[field]) {\n mismatches.push({\n item: item.id,\n field,\n ours: item[field],\n theirs: r[field] ?? '(absent)',\n });\n }\n }\n }\n\n const ourIds = new Set(ours.map((i) => i.id));\n const extra = theirs.items.map((i) => i.item).filter((id) => !ourIds.has(id));\n\n const providerMismatch =\n theirs.provider === expectedProvider\n ? null\n : { expected: expectedProvider, actual: String(theirs.provider) };\n\n return { ok: !providerMismatch && mismatches.length === 0, providerMismatch, mismatches, extra };\n}\n\nexport function renderRpgCatalogDriftMarkdown(result: RpgCatalogDriftResult): string {\n const lines = ['## rpg.actor item catalog drift', ''];\n if (result.ok) lines.push('No drift: every enabled Sifa item matches rpg.actor.', '');\n if (result.providerMismatch) {\n lines.push(\n `**Provider mismatch:** expected \\`${result.providerMismatch.expected}\\`, got \\`${result.providerMismatch.actual}\\`.`,\n '',\n );\n }\n if (result.mismatches.length > 0) {\n lines.push('| Item | Field | Ours | rpg.actor |', '| --- | --- | --- | --- |');\n for (const m of result.mismatches) {\n lines.push(`| ${m.item} | ${m.field} | ${m.ours} | ${m.theirs} |`);\n }\n lines.push('');\n }\n if (result.extra.length > 0) {\n lines.push(\n `Notice: rpg.actor lists items not in our catalog: ${result.extra.map((id) => `\\`${id}\\``).join(', ')}.`,\n '',\n );\n }\n return lines.join('\\n');\n}\n"],"mappings":";AASO,IAAM,wBAAwB;AAuBrC,IAAM,kBAAkB,CAAC,SAAS,QAAQ,YAAY,YAAY,SAAS;AAiBpE,SAAS,kBACd,MACA,QACA,mBAA2B,uBACJ;AACvB,QAAM,SAAS,IAAI,IAAI,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAC3D,QAAM,aAAmC,CAAC;AAE1C,aAAW,QAAQ,MAAM;AACvB,QAAI,CAAC,KAAK,QAAS;AACnB,UAAM,IAAI,OAAO,IAAI,KAAK,EAAE;AAC5B,QAAI,CAAC,GAAG;AACN,iBAAW,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,UAAU,MAAM,OAAO,QAAQ,UAAU,CAAC;AAClF;AAAA,IACF;AACA,eAAW,SAAS,iBAAiB;AACnC,UAAI,KAAK,KAAK,MAAM,EAAE,KAAK,GAAG;AAC5B,mBAAW,KAAK;AAAA,UACd,MAAM,KAAK;AAAA,UACX;AAAA,UACA,MAAM,KAAK,KAAK;AAAA,UAChB,QAAQ,EAAE,KAAK,KAAK;AAAA,QACtB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAC5C,QAAM,QAAQ,OAAO,MAAM,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;AAE5E,QAAM,mBACJ,OAAO,aAAa,mBAChB,OACA,EAAE,UAAU,kBAAkB,QAAQ,OAAO,OAAO,QAAQ,EAAE;AAEpE,SAAO,EAAE,IAAI,CAAC,oBAAoB,WAAW,WAAW,GAAG,kBAAkB,YAAY,MAAM;AACjG;AAEO,SAAS,8BAA8B,QAAuC;AACnF,QAAM,QAAQ,CAAC,mCAAmC,EAAE;AACpD,MAAI,OAAO,GAAI,OAAM,KAAK,wDAAwD,EAAE;AACpF,MAAI,OAAO,kBAAkB;AAC3B,UAAM;AAAA,MACJ,qCAAqC,OAAO,iBAAiB,QAAQ,aAAa,OAAO,iBAAiB,MAAM;AAAA,MAChH;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,WAAW,SAAS,GAAG;AAChC,UAAM,KAAK,uCAAuC,2BAA2B;AAC7E,eAAW,KAAK,OAAO,YAAY;AACjC,YAAM,KAAK,KAAK,EAAE,IAAI,MAAM,EAAE,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,MAAM,IAAI;AAAA,IACnE;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AACA,MAAI,OAAO,MAAM,SAAS,GAAG;AAC3B,UAAM;AAAA,MACJ,qDAAqD,OAAO,MAAM,IAAI,CAAC,OAAO,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA,MACrG;AAAA,IACF;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;","names":[]}
@@ -14,86 +14,130 @@ var RpgUnlockSchema = z.discriminatedUnion("kind", [
14
14
  z.object({ kind: z.literal("usesApp"), appId: z.string().min(1) }),
15
15
  z.object({ kind: z.literal("hasDoctorate") })
16
16
  ]);
17
+ var RPG_CID_REGEX = /^bafkrei[a-z2-7]{52}$/;
17
18
  var RpgItemSchema = z.object({
18
19
  /** Lowercase `[a-z0-9_]` only: the id is embedded in an AT Protocol record key. */
19
20
  id: z.string().min(1).max(50).regex(/^[a-z0-9_]+$/),
20
21
  title: z.string().max(100),
21
22
  description: z.string().max(500),
22
- kind: z.enum(["layer", "held"]),
23
+ kind: z.enum(["layer", "held", "overlay"]),
23
24
  category: z.string().max(30),
25
+ /** rpg.actor channels the item is offered in (optional). */
26
+ channels: z.array(z.string().min(1)).optional(),
27
+ /** Full-size sprite asset (CIDv1, raw codec, base32). */
28
+ assetCid: z.string().regex(RPG_CID_REGEX),
29
+ /** Inventory icon (CIDv1, raw codec, base32). */
30
+ iconCid: z.string().regex(RPG_CID_REGEX),
24
31
  unlock: z.array(RpgUnlockSchema).min(1),
25
32
  // ANY of these unlocks the item
26
33
  enabled: z.boolean()
27
34
  });
28
35
  var RPG_ITEMS = [
29
36
  {
30
- id: "sifa_power_suit",
31
- title: "Power Suit",
32
- description: "",
33
- kind: "layer",
34
- category: "tops",
37
+ id: "leather_briefcase",
38
+ title: "Leather Briefcase",
39
+ description: "A trusty briefcase, for your daily commute",
40
+ kind: "held",
41
+ category: "righthand",
42
+ channels: ["main"],
43
+ assetCid: "bafkreibjtgvetkpzwkliq4o4qnf7atbui6nxgyas3fyk6sjgd5zrl52swa",
44
+ iconCid: "bafkreifmyglopjtbkiuocmcjcbjhtr34vjumkzbnspbnlsezbdphbkfvu4",
35
45
  enabled: true,
36
46
  unlock: [{ kind: "hasRecord", collection: "id.sifa.profile.position" }]
37
47
  },
38
48
  {
39
- id: "sifa_weekend_shirt",
40
- title: "Weekend Shirt",
41
- description: "",
42
- kind: "layer",
43
- category: "tops",
44
- enabled: true,
45
- unlock: [{ kind: "hasRecord", collection: "id.sifa.profile.volunteering" }]
49
+ id: "sifa_suit",
50
+ title: "Sifa Suit",
51
+ description: "Sharp power suit for confirmed professionals",
52
+ kind: "overlay",
53
+ category: "costume",
54
+ channels: ["main"],
55
+ assetCid: "bafkreibortlffvfzkdcuqdpoubczuno7ohnvfwqhefe4h4s36eooe4njgu",
56
+ iconCid: "bafkreifo7vojbr4i6uvsthj2hbczoqwmrn3ovysrskvxlxw6fvkbswm2ae",
57
+ enabled: false,
58
+ // Reserved for the confirmed tier (a colleague confirms the job and both
59
+ // have a confirmed work email). The unlock kind for that is added once work
60
+ // email confirmation exists; until then the item stays disabled.
61
+ unlock: [{ kind: "hasRecord", collection: "id.sifa.profile.position" }]
46
62
  },
47
63
  {
48
- id: "sifa_speaker_mic",
64
+ id: "speaker_mic",
49
65
  title: "Speaker Mic",
50
- description: "",
66
+ description: "A presentation mic, so you can drop it on 'em",
51
67
  kind: "held",
52
68
  category: "righthand",
69
+ channels: ["main"],
70
+ assetCid: "bafkreibr4kgeijc5naecf7l4awfv3omph4ngjmjb36qkfnk2sdo6l45ck4",
71
+ iconCid: "bafkreidnkirbw4nwiro3ocrn5nk66uxf3dhcoy5pojunisvs7so55u3j3q",
53
72
  enabled: true,
54
73
  unlock: [{ kind: "hasRecord", collection: "id.sifa.profile.presentation" }]
55
74
  },
56
75
  {
57
- id: "sifa_school_books",
58
- title: "School Books",
59
- description: "",
76
+ id: "strapped_books",
77
+ title: "Strapped Books",
78
+ description: "Knowledge is power, better come strapped",
60
79
  kind: "held",
61
- category: "lefthand",
80
+ category: "righthand",
81
+ channels: ["main"],
82
+ assetCid: "bafkreieofltl6427jy3n6dfv63phublalv6nq2hcs36qnyxz33ta6u6yqi",
83
+ iconCid: "bafkreiavywpv4sgw7gnkzq5gw3nq6yihaucdexpsvgko26q6jq6u6grjpi",
62
84
  enabled: true,
63
85
  unlock: [{ kind: "hasRecord", collection: "id.sifa.profile.education" }]
64
86
  },
65
87
  {
66
- id: "sifa_doctoral_cap",
88
+ id: "dev_hoodie",
89
+ title: "Dev Hoodie",
90
+ description: "Snug hoodie for when you enter total code mode",
91
+ kind: "layer",
92
+ category: "tops",
93
+ channels: ["main", "sub1"],
94
+ assetCid: "bafkreigmrqp5kd6lytqcen2mypuv6dqonrdfbi2ngxhq3uqozk63yi7hoy",
95
+ iconCid: "bafkreigjwti3w43ta6gy2c75qtdnuvovfohjtv2febbd6hdhpri4tupbem",
96
+ enabled: true,
97
+ unlock: [
98
+ { kind: "hasExternalAccount", platforms: ["github"] },
99
+ { kind: "usesApp", appId: "tangled" }
100
+ ]
101
+ },
102
+ {
103
+ id: "doctoral_cap",
67
104
  title: "Doctoral Cap",
68
- description: "",
105
+ description: "Took so many long years to earn this hat",
69
106
  kind: "layer",
70
107
  category: "headwear",
108
+ channels: ["main"],
109
+ assetCid: "bafkreic7pvfpb7fjgooiaianrc27mh7fwj5vkicre63l6x5hrzb4w36tcq",
110
+ iconCid: "bafkreigqt6lmx5crj3w7vc3bvuewarzdreg2doxyeerbxmhljhpeshjewm",
71
111
  enabled: true,
72
112
  unlock: [{ kind: "hasDoctorate" }]
73
113
  },
74
114
  {
75
- id: "sifa_lab_coat",
76
- title: "Lab Coat",
77
- description: "",
115
+ id: "weekend_shirt",
116
+ title: "Weekend Shirt",
117
+ description: "Fun shirt to wear when work is all finished",
78
118
  kind: "layer",
79
119
  category: "tops",
120
+ channels: ["main", "sub1", "sub2"],
121
+ assetCid: "bafkreigifgpwkxvftiuzbjc6c2xz4xf2iv4bznbhu364e7rhvfmhosbak4",
122
+ iconCid: "bafkreieguco74wkabuyhs4sz6ec35nyywr7ikaw6et4coeezphnsnqqjui",
80
123
  enabled: true,
81
- unlock: [{ kind: "hasExternalAccount", platforms: ["orcid"] }]
124
+ unlock: [{ kind: "hasRecord", collection: "id.sifa.profile.volunteering" }]
82
125
  },
83
126
  {
84
- id: "sifa_dev_hoodie",
85
- title: "Dev Hoodie",
86
- description: "",
127
+ id: "lab_coat",
128
+ title: "Lab Coat",
129
+ description: "Scientifically proven attire",
87
130
  kind: "layer",
88
131
  category: "tops",
132
+ channels: ["main"],
133
+ assetCid: "bafkreiby4g6fgkmi65myv7tdblgb4jvqa3kgnrxvtkxlrtme55unuie4he",
134
+ iconCid: "bafkreifoddxk5tobgwadib5rxwjn67iind6me3swpvw4t5rbcauc5r4syy",
89
135
  enabled: true,
90
- unlock: [
91
- { kind: "hasExternalAccount", platforms: ["github"] },
92
- { kind: "usesApp", appId: "tangled" }
93
- ]
136
+ unlock: [{ kind: "hasExternalAccount", platforms: ["orcid"] }]
94
137
  }
95
138
  ];
96
139
  export {
140
+ RPG_CID_REGEX,
97
141
  RPG_ITEMS,
98
142
  RpgItemSchema,
99
143
  RpgUnlockSchema
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/rpg/catalog.ts"],"sourcesContent":["import { z } from 'zod';\n\nexport const RpgUnlockSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('hasRecord'),\n collection: z.enum([\n 'id.sifa.profile.position',\n 'id.sifa.profile.volunteering',\n 'id.sifa.profile.presentation',\n 'id.sifa.profile.education',\n ]),\n }),\n z.object({ kind: z.literal('hasExternalAccount'), platforms: z.array(z.string()).min(1) }),\n z.object({ kind: z.literal('usesApp'), appId: z.string().min(1) }),\n z.object({ kind: z.literal('hasDoctorate') }),\n]);\n\nexport const RpgItemSchema = z.object({\n /** Lowercase `[a-z0-9_]` only: the id is embedded in an AT Protocol record key. */\n id: z\n .string()\n .min(1)\n .max(50)\n .regex(/^[a-z0-9_]+$/),\n title: z.string().max(100),\n description: z.string().max(500),\n kind: z.enum(['layer', 'held']),\n category: z.string().max(30),\n unlock: z.array(RpgUnlockSchema).min(1), // ANY of these unlocks the item\n enabled: z.boolean(),\n});\nexport type RpgItem = z.infer<typeof RpgItemSchema>;\nexport type RpgUnlock = z.infer<typeof RpgUnlockSchema>;\n\n/**\n * The Sifa item catalog for rpg.actor. PLACEHOLDER titles/categories until\n * rpg.actor transfers the real item cores.\n */\nexport const RPG_ITEMS: readonly RpgItem[] = [\n {\n id: 'sifa_power_suit',\n title: 'Power Suit',\n description: '',\n kind: 'layer',\n category: 'tops',\n enabled: true,\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.position' }],\n },\n {\n id: 'sifa_weekend_shirt',\n title: 'Weekend Shirt',\n description: '',\n kind: 'layer',\n category: 'tops',\n enabled: true,\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.volunteering' }],\n },\n {\n id: 'sifa_speaker_mic',\n title: 'Speaker Mic',\n description: '',\n kind: 'held',\n category: 'righthand',\n enabled: true,\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.presentation' }],\n },\n {\n id: 'sifa_school_books',\n title: 'School Books',\n description: '',\n kind: 'held',\n category: 'lefthand',\n enabled: true,\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.education' }],\n },\n {\n id: 'sifa_doctoral_cap',\n title: 'Doctoral Cap',\n description: '',\n kind: 'layer',\n category: 'headwear',\n enabled: true,\n unlock: [{ kind: 'hasDoctorate' }],\n },\n {\n id: 'sifa_lab_coat',\n title: 'Lab Coat',\n description: '',\n kind: 'layer',\n category: 'tops',\n enabled: true,\n unlock: [{ kind: 'hasExternalAccount', platforms: ['orcid'] }],\n },\n {\n id: 'sifa_dev_hoodie',\n title: 'Dev Hoodie',\n description: '',\n kind: 'layer',\n category: 'tops',\n enabled: true,\n unlock: [\n { kind: 'hasExternalAccount', platforms: ['github'] },\n { kind: 'usesApp', appId: 'tangled' },\n ],\n },\n];\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/rpg/catalog.ts"],"sourcesContent":["import { z } from 'zod';\n\nexport const RpgUnlockSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('hasRecord'),\n collection: z.enum([\n 'id.sifa.profile.position',\n 'id.sifa.profile.volunteering',\n 'id.sifa.profile.presentation',\n 'id.sifa.profile.education',\n ]),\n }),\n z.object({ kind: z.literal('hasExternalAccount'), platforms: z.array(z.string()).min(1) }),\n z.object({ kind: z.literal('usesApp'), appId: z.string().min(1) }),\n z.object({ kind: z.literal('hasDoctorate') }),\n]);\n\n/** CIDv1 (raw codec, sha2-256, base32) as used for rpg.actor image blobs. */\nexport const RPG_CID_REGEX = /^bafkrei[a-z2-7]{52}$/;\n\nexport const RpgItemSchema = z.object({\n /** Lowercase `[a-z0-9_]` only: the id is embedded in an AT Protocol record key. */\n id: z\n .string()\n .min(1)\n .max(50)\n .regex(/^[a-z0-9_]+$/),\n title: z.string().max(100),\n description: z.string().max(500),\n kind: z.enum(['layer', 'held', 'overlay']),\n category: z.string().max(30),\n /** rpg.actor channels the item is offered in (optional). */\n channels: z.array(z.string().min(1)).optional(),\n /** Full-size sprite asset (CIDv1, raw codec, base32). */\n assetCid: z.string().regex(RPG_CID_REGEX),\n /** Inventory icon (CIDv1, raw codec, base32). */\n iconCid: z.string().regex(RPG_CID_REGEX),\n unlock: z.array(RpgUnlockSchema).min(1), // ANY of these unlocks the item\n enabled: z.boolean(),\n});\nexport type RpgItem = z.infer<typeof RpgItemSchema>;\nexport type RpgUnlock = z.infer<typeof RpgUnlockSchema>;\n\n/**\n * The Sifa item catalog for rpg.actor. Ids, titles, descriptions, kinds,\n * categories, channels and image CIDs come from rpg.actor's item cores.\n */\nexport const RPG_ITEMS: readonly RpgItem[] = [\n {\n id: 'leather_briefcase',\n title: 'Leather Briefcase',\n description: 'A trusty briefcase, for your daily commute',\n kind: 'held',\n category: 'righthand',\n channels: ['main'],\n assetCid: 'bafkreibjtgvetkpzwkliq4o4qnf7atbui6nxgyas3fyk6sjgd5zrl52swa',\n iconCid: 'bafkreifmyglopjtbkiuocmcjcbjhtr34vjumkzbnspbnlsezbdphbkfvu4',\n enabled: true,\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.position' }],\n },\n {\n id: 'sifa_suit',\n title: 'Sifa Suit',\n description: 'Sharp power suit for confirmed professionals',\n kind: 'overlay',\n category: 'costume',\n channels: ['main'],\n assetCid: 'bafkreibortlffvfzkdcuqdpoubczuno7ohnvfwqhefe4h4s36eooe4njgu',\n iconCid: 'bafkreifo7vojbr4i6uvsthj2hbczoqwmrn3ovysrskvxlxw6fvkbswm2ae',\n enabled: false,\n // Reserved for the confirmed tier (a colleague confirms the job and both\n // have a confirmed work email). The unlock kind for that is added once work\n // email confirmation exists; until then the item stays disabled.\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.position' }],\n },\n {\n id: 'speaker_mic',\n title: 'Speaker Mic',\n description: \"A presentation mic, so you can drop it on 'em\",\n kind: 'held',\n category: 'righthand',\n channels: ['main'],\n assetCid: 'bafkreibr4kgeijc5naecf7l4awfv3omph4ngjmjb36qkfnk2sdo6l45ck4',\n iconCid: 'bafkreidnkirbw4nwiro3ocrn5nk66uxf3dhcoy5pojunisvs7so55u3j3q',\n enabled: true,\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.presentation' }],\n },\n {\n id: 'strapped_books',\n title: 'Strapped Books',\n description: 'Knowledge is power, better come strapped',\n kind: 'held',\n category: 'righthand',\n channels: ['main'],\n assetCid: 'bafkreieofltl6427jy3n6dfv63phublalv6nq2hcs36qnyxz33ta6u6yqi',\n iconCid: 'bafkreiavywpv4sgw7gnkzq5gw3nq6yihaucdexpsvgko26q6jq6u6grjpi',\n enabled: true,\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.education' }],\n },\n {\n id: 'dev_hoodie',\n title: 'Dev Hoodie',\n description: 'Snug hoodie for when you enter total code mode',\n kind: 'layer',\n category: 'tops',\n channels: ['main', 'sub1'],\n assetCid: 'bafkreigmrqp5kd6lytqcen2mypuv6dqonrdfbi2ngxhq3uqozk63yi7hoy',\n iconCid: 'bafkreigjwti3w43ta6gy2c75qtdnuvovfohjtv2febbd6hdhpri4tupbem',\n enabled: true,\n unlock: [\n { kind: 'hasExternalAccount', platforms: ['github'] },\n { kind: 'usesApp', appId: 'tangled' },\n ],\n },\n {\n id: 'doctoral_cap',\n title: 'Doctoral Cap',\n description: 'Took so many long years to earn this hat',\n kind: 'layer',\n category: 'headwear',\n channels: ['main'],\n assetCid: 'bafkreic7pvfpb7fjgooiaianrc27mh7fwj5vkicre63l6x5hrzb4w36tcq',\n iconCid: 'bafkreigqt6lmx5crj3w7vc3bvuewarzdreg2doxyeerbxmhljhpeshjewm',\n enabled: true,\n unlock: [{ kind: 'hasDoctorate' }],\n },\n {\n id: 'weekend_shirt',\n title: 'Weekend Shirt',\n description: 'Fun shirt to wear when work is all finished',\n kind: 'layer',\n category: 'tops',\n channels: ['main', 'sub1', 'sub2'],\n assetCid: 'bafkreigifgpwkxvftiuzbjc6c2xz4xf2iv4bznbhu364e7rhvfmhosbak4',\n iconCid: 'bafkreieguco74wkabuyhs4sz6ec35nyywr7ikaw6et4coeezphnsnqqjui',\n enabled: true,\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.volunteering' }],\n },\n {\n id: 'lab_coat',\n title: 'Lab Coat',\n description: 'Scientifically proven attire',\n kind: 'layer',\n category: 'tops',\n channels: ['main'],\n assetCid: 'bafkreiby4g6fgkmi65myv7tdblgb4jvqa3kgnrxvtkxlrtme55unuie4he',\n iconCid: 'bafkreifoddxk5tobgwadib5rxwjn67iind6me3swpvw4t5rbcauc5r4syy',\n enabled: true,\n unlock: [{ kind: 'hasExternalAccount', platforms: ['orcid'] }],\n },\n];\n"],"mappings":"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","names":[]}
@@ -17,83 +17,126 @@ var RpgUnlockSchema = zod.z.discriminatedUnion("kind", [
17
17
  zod.z.object({ kind: zod.z.literal("usesApp"), appId: zod.z.string().min(1) }),
18
18
  zod.z.object({ kind: zod.z.literal("hasDoctorate") })
19
19
  ]);
20
+ var RPG_CID_REGEX = /^bafkrei[a-z2-7]{52}$/;
20
21
  var RpgItemSchema = zod.z.object({
21
22
  /** Lowercase `[a-z0-9_]` only: the id is embedded in an AT Protocol record key. */
22
23
  id: zod.z.string().min(1).max(50).regex(/^[a-z0-9_]+$/),
23
24
  title: zod.z.string().max(100),
24
25
  description: zod.z.string().max(500),
25
- kind: zod.z.enum(["layer", "held"]),
26
+ kind: zod.z.enum(["layer", "held", "overlay"]),
26
27
  category: zod.z.string().max(30),
28
+ /** rpg.actor channels the item is offered in (optional). */
29
+ channels: zod.z.array(zod.z.string().min(1)).optional(),
30
+ /** Full-size sprite asset (CIDv1, raw codec, base32). */
31
+ assetCid: zod.z.string().regex(RPG_CID_REGEX),
32
+ /** Inventory icon (CIDv1, raw codec, base32). */
33
+ iconCid: zod.z.string().regex(RPG_CID_REGEX),
27
34
  unlock: zod.z.array(RpgUnlockSchema).min(1),
28
35
  // ANY of these unlocks the item
29
36
  enabled: zod.z.boolean()
30
37
  });
31
38
  var RPG_ITEMS = [
32
39
  {
33
- id: "sifa_power_suit",
34
- title: "Power Suit",
35
- description: "",
36
- kind: "layer",
37
- category: "tops",
40
+ id: "leather_briefcase",
41
+ title: "Leather Briefcase",
42
+ description: "A trusty briefcase, for your daily commute",
43
+ kind: "held",
44
+ category: "righthand",
45
+ channels: ["main"],
46
+ assetCid: "bafkreibjtgvetkpzwkliq4o4qnf7atbui6nxgyas3fyk6sjgd5zrl52swa",
47
+ iconCid: "bafkreifmyglopjtbkiuocmcjcbjhtr34vjumkzbnspbnlsezbdphbkfvu4",
38
48
  enabled: true,
39
49
  unlock: [{ kind: "hasRecord", collection: "id.sifa.profile.position" }]
40
50
  },
41
51
  {
42
- id: "sifa_weekend_shirt",
43
- title: "Weekend Shirt",
44
- description: "",
45
- kind: "layer",
46
- category: "tops",
47
- enabled: true,
48
- unlock: [{ kind: "hasRecord", collection: "id.sifa.profile.volunteering" }]
52
+ id: "sifa_suit",
53
+ title: "Sifa Suit",
54
+ description: "Sharp power suit for confirmed professionals",
55
+ kind: "overlay",
56
+ category: "costume",
57
+ channels: ["main"],
58
+ assetCid: "bafkreibortlffvfzkdcuqdpoubczuno7ohnvfwqhefe4h4s36eooe4njgu",
59
+ iconCid: "bafkreifo7vojbr4i6uvsthj2hbczoqwmrn3ovysrskvxlxw6fvkbswm2ae",
60
+ enabled: false,
61
+ // Reserved for the confirmed tier (a colleague confirms the job and both
62
+ // have a confirmed work email). The unlock kind for that is added once work
63
+ // email confirmation exists; until then the item stays disabled.
64
+ unlock: [{ kind: "hasRecord", collection: "id.sifa.profile.position" }]
49
65
  },
50
66
  {
51
- id: "sifa_speaker_mic",
67
+ id: "speaker_mic",
52
68
  title: "Speaker Mic",
53
- description: "",
69
+ description: "A presentation mic, so you can drop it on 'em",
54
70
  kind: "held",
55
71
  category: "righthand",
72
+ channels: ["main"],
73
+ assetCid: "bafkreibr4kgeijc5naecf7l4awfv3omph4ngjmjb36qkfnk2sdo6l45ck4",
74
+ iconCid: "bafkreidnkirbw4nwiro3ocrn5nk66uxf3dhcoy5pojunisvs7so55u3j3q",
56
75
  enabled: true,
57
76
  unlock: [{ kind: "hasRecord", collection: "id.sifa.profile.presentation" }]
58
77
  },
59
78
  {
60
- id: "sifa_school_books",
61
- title: "School Books",
62
- description: "",
79
+ id: "strapped_books",
80
+ title: "Strapped Books",
81
+ description: "Knowledge is power, better come strapped",
63
82
  kind: "held",
64
- category: "lefthand",
83
+ category: "righthand",
84
+ channels: ["main"],
85
+ assetCid: "bafkreieofltl6427jy3n6dfv63phublalv6nq2hcs36qnyxz33ta6u6yqi",
86
+ iconCid: "bafkreiavywpv4sgw7gnkzq5gw3nq6yihaucdexpsvgko26q6jq6u6grjpi",
65
87
  enabled: true,
66
88
  unlock: [{ kind: "hasRecord", collection: "id.sifa.profile.education" }]
67
89
  },
68
90
  {
69
- id: "sifa_doctoral_cap",
91
+ id: "dev_hoodie",
92
+ title: "Dev Hoodie",
93
+ description: "Snug hoodie for when you enter total code mode",
94
+ kind: "layer",
95
+ category: "tops",
96
+ channels: ["main", "sub1"],
97
+ assetCid: "bafkreigmrqp5kd6lytqcen2mypuv6dqonrdfbi2ngxhq3uqozk63yi7hoy",
98
+ iconCid: "bafkreigjwti3w43ta6gy2c75qtdnuvovfohjtv2febbd6hdhpri4tupbem",
99
+ enabled: true,
100
+ unlock: [
101
+ { kind: "hasExternalAccount", platforms: ["github"] },
102
+ { kind: "usesApp", appId: "tangled" }
103
+ ]
104
+ },
105
+ {
106
+ id: "doctoral_cap",
70
107
  title: "Doctoral Cap",
71
- description: "",
108
+ description: "Took so many long years to earn this hat",
72
109
  kind: "layer",
73
110
  category: "headwear",
111
+ channels: ["main"],
112
+ assetCid: "bafkreic7pvfpb7fjgooiaianrc27mh7fwj5vkicre63l6x5hrzb4w36tcq",
113
+ iconCid: "bafkreigqt6lmx5crj3w7vc3bvuewarzdreg2doxyeerbxmhljhpeshjewm",
74
114
  enabled: true,
75
115
  unlock: [{ kind: "hasDoctorate" }]
76
116
  },
77
117
  {
78
- id: "sifa_lab_coat",
79
- title: "Lab Coat",
80
- description: "",
118
+ id: "weekend_shirt",
119
+ title: "Weekend Shirt",
120
+ description: "Fun shirt to wear when work is all finished",
81
121
  kind: "layer",
82
122
  category: "tops",
123
+ channels: ["main", "sub1", "sub2"],
124
+ assetCid: "bafkreigifgpwkxvftiuzbjc6c2xz4xf2iv4bznbhu364e7rhvfmhosbak4",
125
+ iconCid: "bafkreieguco74wkabuyhs4sz6ec35nyywr7ikaw6et4coeezphnsnqqjui",
83
126
  enabled: true,
84
- unlock: [{ kind: "hasExternalAccount", platforms: ["orcid"] }]
127
+ unlock: [{ kind: "hasRecord", collection: "id.sifa.profile.volunteering" }]
85
128
  },
86
129
  {
87
- id: "sifa_dev_hoodie",
88
- title: "Dev Hoodie",
89
- description: "",
130
+ id: "lab_coat",
131
+ title: "Lab Coat",
132
+ description: "Scientifically proven attire",
90
133
  kind: "layer",
91
134
  category: "tops",
135
+ channels: ["main"],
136
+ assetCid: "bafkreiby4g6fgkmi65myv7tdblgb4jvqa3kgnrxvtkxlrtme55unuie4he",
137
+ iconCid: "bafkreifoddxk5tobgwadib5rxwjn67iind6me3swpvw4t5rbcauc5r4syy",
92
138
  enabled: true,
93
- unlock: [
94
- { kind: "hasExternalAccount", platforms: ["github"] },
95
- { kind: "usesApp", appId: "tangled" }
96
- ]
139
+ unlock: [{ kind: "hasExternalAccount", platforms: ["orcid"] }]
97
140
  }
98
141
  ];
99
142
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/rpg/catalog.ts","../../src/rpg/unlocks.ts","../../src/rpg/links.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["import { z } from 'zod';\n\nexport const RpgUnlockSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('hasRecord'),\n collection: z.enum([\n 'id.sifa.profile.position',\n 'id.sifa.profile.volunteering',\n 'id.sifa.profile.presentation',\n 'id.sifa.profile.education',\n ]),\n }),\n z.object({ kind: z.literal('hasExternalAccount'), platforms: z.array(z.string()).min(1) }),\n z.object({ kind: z.literal('usesApp'), appId: z.string().min(1) }),\n z.object({ kind: z.literal('hasDoctorate') }),\n]);\n\nexport const RpgItemSchema = z.object({\n /** Lowercase `[a-z0-9_]` only: the id is embedded in an AT Protocol record key. */\n id: z\n .string()\n .min(1)\n .max(50)\n .regex(/^[a-z0-9_]+$/),\n title: z.string().max(100),\n description: z.string().max(500),\n kind: z.enum(['layer', 'held']),\n category: z.string().max(30),\n unlock: z.array(RpgUnlockSchema).min(1), // ANY of these unlocks the item\n enabled: z.boolean(),\n});\nexport type RpgItem = z.infer<typeof RpgItemSchema>;\nexport type RpgUnlock = z.infer<typeof RpgUnlockSchema>;\n\n/**\n * The Sifa item catalog for rpg.actor. PLACEHOLDER titles/categories until\n * rpg.actor transfers the real item cores.\n */\nexport const RPG_ITEMS: readonly RpgItem[] = [\n {\n id: 'sifa_power_suit',\n title: 'Power Suit',\n description: '',\n kind: 'layer',\n category: 'tops',\n enabled: true,\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.position' }],\n },\n {\n id: 'sifa_weekend_shirt',\n title: 'Weekend Shirt',\n description: '',\n kind: 'layer',\n category: 'tops',\n enabled: true,\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.volunteering' }],\n },\n {\n id: 'sifa_speaker_mic',\n title: 'Speaker Mic',\n description: '',\n kind: 'held',\n category: 'righthand',\n enabled: true,\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.presentation' }],\n },\n {\n id: 'sifa_school_books',\n title: 'School Books',\n description: '',\n kind: 'held',\n category: 'lefthand',\n enabled: true,\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.education' }],\n },\n {\n id: 'sifa_doctoral_cap',\n title: 'Doctoral Cap',\n description: '',\n kind: 'layer',\n category: 'headwear',\n enabled: true,\n unlock: [{ kind: 'hasDoctorate' }],\n },\n {\n id: 'sifa_lab_coat',\n title: 'Lab Coat',\n description: '',\n kind: 'layer',\n category: 'tops',\n enabled: true,\n unlock: [{ kind: 'hasExternalAccount', platforms: ['orcid'] }],\n },\n {\n id: 'sifa_dev_hoodie',\n title: 'Dev Hoodie',\n description: '',\n kind: 'layer',\n category: 'tops',\n enabled: true,\n unlock: [\n { kind: 'hasExternalAccount', platforms: ['github'] },\n { kind: 'usesApp', appId: 'tangled' },\n ],\n },\n];\n","import { RPG_ITEMS, type RpgItem, type RpgUnlock } from './catalog.js';\n\n/**\n * Profile signals the unlock rules are checked against. Callers must pass\n * lowercase platform codes (e.g. 'github', 'orcid') and registry app ids (e.g. 'tangled').\n */\nexport interface RpgUnlockSignals {\n collections: readonly string[];\n externalPlatforms: readonly string[];\n activeAppIds: readonly string[];\n hasDoctorate: boolean;\n}\n\n/** One enabled catalog item and whether the signals earn it. */\nexport interface RpgUnlockResult {\n item: RpgItem;\n earned: boolean;\n}\n\nfunction matches(u: RpgUnlock, s: RpgUnlockSignals): boolean {\n switch (u.kind) {\n case 'hasRecord':\n return s.collections.includes(u.collection);\n case 'hasExternalAccount':\n return u.platforms.some((p) => s.externalPlatforms.includes(p));\n case 'usesApp':\n return s.activeAppIds.includes(u.appId);\n case 'hasDoctorate':\n return s.hasDoctorate;\n }\n}\n\n/** Evaluate every enabled item (ANY matching unlock earns it); disabled items are omitted. */\nexport function evaluateRpgUnlocks(\n signals: RpgUnlockSignals,\n items: readonly RpgItem[] = RPG_ITEMS,\n): RpgUnlockResult[] {\n return items\n .filter((item) => item.enabled)\n .map((item) => ({ item, earned: item.unlock.some((u) => matches(u, signals)) }));\n}\n","/** Base URL of rpg.actor. */\nexport const RPG_ACTOR_URL = 'https://rpg.actor';\n\n/**\n * Record key of the `equipment.rpg.give` Sifa writes for an item. rpg.actor\n * stores the accepted `equipment.rpg.item` under the same key, so this also\n * identifies the item in the player's own account.\n *\n * The DID is used as-is: AT Protocol record keys allow `A-Za-z0-9 . - _ : ~`\n * (https://atproto.com/specs/record-key), so the colons in `did:plc:` are\n * valid. The format is already live in written gift records; do not change it.\n */\nexport function rpgGiveRkey(itemId: string, recipientDid: string): string {\n return `sifa-${itemId}-${recipientDid}`;\n}\n\n/** The player's character page on rpg.actor, where pending gifts can be accepted. */\nexport function rpgActorCharacterUrl(handleOrDid: string): string {\n return `${RPG_ACTOR_URL}/${encodeURIComponent(handleOrDid.replace(/^@+/, ''))}`;\n}\n\n/** Deep link that opens the Wear action for an accepted Sifa item on the character page. */\nexport function rpgActorWearUrl(handleOrDid: string, itemId: string, recipientDid: string): string {\n return `${rpgActorCharacterUrl(handleOrDid)}?wear=${encodeURIComponent(rpgGiveRkey(itemId, recipientDid))}`;\n}\n"]}
1
+ {"version":3,"sources":["../../src/rpg/catalog.ts","../../src/rpg/unlocks.ts","../../src/rpg/links.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["import { z } from 'zod';\n\nexport const RpgUnlockSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('hasRecord'),\n collection: z.enum([\n 'id.sifa.profile.position',\n 'id.sifa.profile.volunteering',\n 'id.sifa.profile.presentation',\n 'id.sifa.profile.education',\n ]),\n }),\n z.object({ kind: z.literal('hasExternalAccount'), platforms: z.array(z.string()).min(1) }),\n z.object({ kind: z.literal('usesApp'), appId: z.string().min(1) }),\n z.object({ kind: z.literal('hasDoctorate') }),\n]);\n\n/** CIDv1 (raw codec, sha2-256, base32) as used for rpg.actor image blobs. */\nexport const RPG_CID_REGEX = /^bafkrei[a-z2-7]{52}$/;\n\nexport const RpgItemSchema = z.object({\n /** Lowercase `[a-z0-9_]` only: the id is embedded in an AT Protocol record key. */\n id: z\n .string()\n .min(1)\n .max(50)\n .regex(/^[a-z0-9_]+$/),\n title: z.string().max(100),\n description: z.string().max(500),\n kind: z.enum(['layer', 'held', 'overlay']),\n category: z.string().max(30),\n /** rpg.actor channels the item is offered in (optional). */\n channels: z.array(z.string().min(1)).optional(),\n /** Full-size sprite asset (CIDv1, raw codec, base32). */\n assetCid: z.string().regex(RPG_CID_REGEX),\n /** Inventory icon (CIDv1, raw codec, base32). */\n iconCid: z.string().regex(RPG_CID_REGEX),\n unlock: z.array(RpgUnlockSchema).min(1), // ANY of these unlocks the item\n enabled: z.boolean(),\n});\nexport type RpgItem = z.infer<typeof RpgItemSchema>;\nexport type RpgUnlock = z.infer<typeof RpgUnlockSchema>;\n\n/**\n * The Sifa item catalog for rpg.actor. Ids, titles, descriptions, kinds,\n * categories, channels and image CIDs come from rpg.actor's item cores.\n */\nexport const RPG_ITEMS: readonly RpgItem[] = [\n {\n id: 'leather_briefcase',\n title: 'Leather Briefcase',\n description: 'A trusty briefcase, for your daily commute',\n kind: 'held',\n category: 'righthand',\n channels: ['main'],\n assetCid: 'bafkreibjtgvetkpzwkliq4o4qnf7atbui6nxgyas3fyk6sjgd5zrl52swa',\n iconCid: 'bafkreifmyglopjtbkiuocmcjcbjhtr34vjumkzbnspbnlsezbdphbkfvu4',\n enabled: true,\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.position' }],\n },\n {\n id: 'sifa_suit',\n title: 'Sifa Suit',\n description: 'Sharp power suit for confirmed professionals',\n kind: 'overlay',\n category: 'costume',\n channels: ['main'],\n assetCid: 'bafkreibortlffvfzkdcuqdpoubczuno7ohnvfwqhefe4h4s36eooe4njgu',\n iconCid: 'bafkreifo7vojbr4i6uvsthj2hbczoqwmrn3ovysrskvxlxw6fvkbswm2ae',\n enabled: false,\n // Reserved for the confirmed tier (a colleague confirms the job and both\n // have a confirmed work email). The unlock kind for that is added once work\n // email confirmation exists; until then the item stays disabled.\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.position' }],\n },\n {\n id: 'speaker_mic',\n title: 'Speaker Mic',\n description: \"A presentation mic, so you can drop it on 'em\",\n kind: 'held',\n category: 'righthand',\n channels: ['main'],\n assetCid: 'bafkreibr4kgeijc5naecf7l4awfv3omph4ngjmjb36qkfnk2sdo6l45ck4',\n iconCid: 'bafkreidnkirbw4nwiro3ocrn5nk66uxf3dhcoy5pojunisvs7so55u3j3q',\n enabled: true,\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.presentation' }],\n },\n {\n id: 'strapped_books',\n title: 'Strapped Books',\n description: 'Knowledge is power, better come strapped',\n kind: 'held',\n category: 'righthand',\n channels: ['main'],\n assetCid: 'bafkreieofltl6427jy3n6dfv63phublalv6nq2hcs36qnyxz33ta6u6yqi',\n iconCid: 'bafkreiavywpv4sgw7gnkzq5gw3nq6yihaucdexpsvgko26q6jq6u6grjpi',\n enabled: true,\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.education' }],\n },\n {\n id: 'dev_hoodie',\n title: 'Dev Hoodie',\n description: 'Snug hoodie for when you enter total code mode',\n kind: 'layer',\n category: 'tops',\n channels: ['main', 'sub1'],\n assetCid: 'bafkreigmrqp5kd6lytqcen2mypuv6dqonrdfbi2ngxhq3uqozk63yi7hoy',\n iconCid: 'bafkreigjwti3w43ta6gy2c75qtdnuvovfohjtv2febbd6hdhpri4tupbem',\n enabled: true,\n unlock: [\n { kind: 'hasExternalAccount', platforms: ['github'] },\n { kind: 'usesApp', appId: 'tangled' },\n ],\n },\n {\n id: 'doctoral_cap',\n title: 'Doctoral Cap',\n description: 'Took so many long years to earn this hat',\n kind: 'layer',\n category: 'headwear',\n channels: ['main'],\n assetCid: 'bafkreic7pvfpb7fjgooiaianrc27mh7fwj5vkicre63l6x5hrzb4w36tcq',\n iconCid: 'bafkreigqt6lmx5crj3w7vc3bvuewarzdreg2doxyeerbxmhljhpeshjewm',\n enabled: true,\n unlock: [{ kind: 'hasDoctorate' }],\n },\n {\n id: 'weekend_shirt',\n title: 'Weekend Shirt',\n description: 'Fun shirt to wear when work is all finished',\n kind: 'layer',\n category: 'tops',\n channels: ['main', 'sub1', 'sub2'],\n assetCid: 'bafkreigifgpwkxvftiuzbjc6c2xz4xf2iv4bznbhu364e7rhvfmhosbak4',\n iconCid: 'bafkreieguco74wkabuyhs4sz6ec35nyywr7ikaw6et4coeezphnsnqqjui',\n enabled: true,\n unlock: [{ kind: 'hasRecord', collection: 'id.sifa.profile.volunteering' }],\n },\n {\n id: 'lab_coat',\n title: 'Lab Coat',\n description: 'Scientifically proven attire',\n kind: 'layer',\n category: 'tops',\n channels: ['main'],\n assetCid: 'bafkreiby4g6fgkmi65myv7tdblgb4jvqa3kgnrxvtkxlrtme55unuie4he',\n iconCid: 'bafkreifoddxk5tobgwadib5rxwjn67iind6me3swpvw4t5rbcauc5r4syy',\n enabled: true,\n unlock: [{ kind: 'hasExternalAccount', platforms: ['orcid'] }],\n },\n];\n","import { RPG_ITEMS, type RpgItem, type RpgUnlock } from './catalog.js';\n\n/**\n * Profile signals the unlock rules are checked against. Callers must pass\n * lowercase platform codes (e.g. 'github', 'orcid') and registry app ids (e.g. 'tangled').\n */\nexport interface RpgUnlockSignals {\n collections: readonly string[];\n externalPlatforms: readonly string[];\n activeAppIds: readonly string[];\n hasDoctorate: boolean;\n}\n\n/** One enabled catalog item and whether the signals earn it. */\nexport interface RpgUnlockResult {\n item: RpgItem;\n earned: boolean;\n}\n\nfunction matches(u: RpgUnlock, s: RpgUnlockSignals): boolean {\n switch (u.kind) {\n case 'hasRecord':\n return s.collections.includes(u.collection);\n case 'hasExternalAccount':\n return u.platforms.some((p) => s.externalPlatforms.includes(p));\n case 'usesApp':\n return s.activeAppIds.includes(u.appId);\n case 'hasDoctorate':\n return s.hasDoctorate;\n }\n}\n\n/** Evaluate every enabled item (ANY matching unlock earns it); disabled items are omitted. */\nexport function evaluateRpgUnlocks(\n signals: RpgUnlockSignals,\n items: readonly RpgItem[] = RPG_ITEMS,\n): RpgUnlockResult[] {\n return items\n .filter((item) => item.enabled)\n .map((item) => ({ item, earned: item.unlock.some((u) => matches(u, signals)) }));\n}\n","/** Base URL of rpg.actor. */\nexport const RPG_ACTOR_URL = 'https://rpg.actor';\n\n/**\n * Record key of the `equipment.rpg.give` Sifa writes for an item. rpg.actor\n * stores the accepted `equipment.rpg.item` under the same key, so this also\n * identifies the item in the player's own account.\n *\n * The DID is used as-is: AT Protocol record keys allow `A-Za-z0-9 . - _ : ~`\n * (https://atproto.com/specs/record-key), so the colons in `did:plc:` are\n * valid. The format is already live in written gift records; do not change it.\n */\nexport function rpgGiveRkey(itemId: string, recipientDid: string): string {\n return `sifa-${itemId}-${recipientDid}`;\n}\n\n/** The player's character page on rpg.actor, where pending gifts can be accepted. */\nexport function rpgActorCharacterUrl(handleOrDid: string): string {\n return `${RPG_ACTOR_URL}/${encodeURIComponent(handleOrDid.replace(/^@+/, ''))}`;\n}\n\n/** Deep link that opens the Wear action for an accepted Sifa item on the character page. */\nexport function rpgActorWearUrl(handleOrDid: string, itemId: string, recipientDid: string): string {\n return `${rpgActorCharacterUrl(handleOrDid)}?wear=${encodeURIComponent(rpgGiveRkey(itemId, recipientDid))}`;\n}\n"]}
@@ -24,8 +24,12 @@ declare const RpgItemSchema: z.ZodObject<{
24
24
  kind: z.ZodEnum<{
25
25
  layer: "layer";
26
26
  held: "held";
27
+ overlay: "overlay";
27
28
  }>;
28
29
  category: z.ZodString;
30
+ channels: z.ZodOptional<z.ZodArray<z.ZodString>>;
31
+ assetCid: z.ZodString;
32
+ iconCid: z.ZodString;
29
33
  unlock: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
30
34
  kind: z.ZodLiteral<"hasRecord">;
31
35
  collection: z.ZodEnum<{
@@ -48,8 +52,8 @@ declare const RpgItemSchema: z.ZodObject<{
48
52
  type RpgItem = z.infer<typeof RpgItemSchema>;
49
53
  type RpgUnlock = z.infer<typeof RpgUnlockSchema>;
50
54
  /**
51
- * The Sifa item catalog for rpg.actor. PLACEHOLDER titles/categories until
52
- * rpg.actor transfers the real item cores.
55
+ * The Sifa item catalog for rpg.actor. Ids, titles, descriptions, kinds,
56
+ * categories, channels and image CIDs come from rpg.actor's item cores.
53
57
  */
54
58
  declare const RPG_ITEMS: readonly RpgItem[];
55
59
 
@@ -24,8 +24,12 @@ declare const RpgItemSchema: z.ZodObject<{
24
24
  kind: z.ZodEnum<{
25
25
  layer: "layer";
26
26
  held: "held";
27
+ overlay: "overlay";
27
28
  }>;
28
29
  category: z.ZodString;
30
+ channels: z.ZodOptional<z.ZodArray<z.ZodString>>;
31
+ assetCid: z.ZodString;
32
+ iconCid: z.ZodString;
29
33
  unlock: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
30
34
  kind: z.ZodLiteral<"hasRecord">;
31
35
  collection: z.ZodEnum<{
@@ -48,8 +52,8 @@ declare const RpgItemSchema: z.ZodObject<{
48
52
  type RpgItem = z.infer<typeof RpgItemSchema>;
49
53
  type RpgUnlock = z.infer<typeof RpgUnlockSchema>;
50
54
  /**
51
- * The Sifa item catalog for rpg.actor. PLACEHOLDER titles/categories until
52
- * rpg.actor transfers the real item cores.
55
+ * The Sifa item catalog for rpg.actor. Ids, titles, descriptions, kinds,
56
+ * categories, channels and image CIDs come from rpg.actor's item cores.
53
57
  */
54
58
  declare const RPG_ITEMS: readonly RpgItem[];
55
59
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@singi-labs/sifa-sdk",
3
- "version": "0.19.62",
3
+ "version": "0.19.64",
4
4
  "description": "Sifa SDK — public client library for the Sifa AppView on AT Protocol. Shared by sifa-web and sifa-app.",
5
5
  "license": "MIT",
6
6
  "type": "module",