🌿 Collaborative wiki on ATProto lichen.wiki
atproto
14
fork

Configure Feed

Select the types of activity you want to include in your feed.

Rename the project to lichen.wiki

juprodh 56d33040 4112e704

+195 -180
+6 -4
CONTRIBUTING.md
··· 18 18 bun run typecheck # tsc --noEmit 19 19 ``` 20 20 21 - The database auto-seeds with sample data on first run. To reset, delete `atwiki.db` and restart. 21 + The database auto-seeds with sample data on first run. To reset, delete `lichen.db` and restart. 22 22 23 23 ## Development commands 24 24 ··· 60 60 server/ # Elysia routes, database (schema, queries/, seed) 61 61 atproto/ # ATProto OAuth (client, routes, session, env) + PDS write functions 62 62 firehose/ # standalone firehose subscriber 63 - lib/ # shared logic (at-uri, diff, markdown, slugs, viz, image, blob refs, ws-polyfill) 63 + lib/ # shared logic (at-uri, diff, markdown, slugs, viz, image, blob refs, ws-polyfill, i18n, urls, response) 64 64 shared/ # shared type definitions (viz-types) 65 65 views/ # HTML templates 66 66 lexicons/ # ATProto lexicon schemas ··· 82 82 - PDS is canonical: PDS write must succeed before DB write. If PDS fails, the request fails (no partial state) 83 83 - No ORMs — raw SQL with prepared statements 84 84 - Route param names must be consistent across all routes sharing a path segment 85 + - UI strings must use the i18n system (`t(locale).section.key`) — no hardcoded English strings in views 86 + - Tailwind class names must come from `src/views/theme.ts` (`THEME.*`) — no hardcoded class strings in views. Client-side hex colors come from `THEME_HEX` 85 87 86 88 ## Database 87 89 ··· 96 98 97 99 ## ATProto 98 100 99 - **Lexicon namespace:** `pub.coral`. Lexicon JSON files live in `lexicons/`. 101 + **Lexicon namespace:** `wiki.lichen` (domain: `lichen.wiki`). Lexicon JSON files live in `lexicons/`. 100 102 101 - Record types: `pub.coral.wiki`, `pub.coral.note`, `pub.coral.noteRevision`, `pub.coral.memberRequest`, `pub.coral.membership`, `pub.coral.bookmark` 103 + Record types: `wiki.lichen.wiki`, `wiki.lichen.note`, `wiki.lichen.noteRevision`, `wiki.lichen.memberRequest`, `wiki.lichen.membership`, `wiki.lichen.bookmark` 102 104 103 105 In dev mode (no OAuth configured), PDS writes are skipped and a mock DID (`did:plc:mock123`) is used. All data lives in SQLite only. 104 106
+6
README.md
··· 1 + # Lichen 2 + 1 3 A wiki built on [ATProto](https://atproto.com). Sign in with your Bluesky account, create public or private wikis, and write in plain markdown with `[[wikilinks]]`. 2 4 3 5 Your data lives on your own PDS: records persist independently of this app, and your Bluesky identity works everywhere on ATProto. 6 + 7 + **Features:** wikilinks · backlinks · revision history · access control (public/private wikis, roles) · D3 visualizations in fenced code blocks · image upload · EN/FR UI 8 + 9 + **Lexicon namespace:** `wiki.lichen` — domain: `lichen.wiki` 4 10 5 11 ## Quick start 6 12 ```bash
+1 -1
lexicons/pub.coral.bookmark.json lexicons/wiki.lichen.bookmark.json
··· 1 1 { 2 2 "lexicon": 1, 3 - "id": "pub.coral.bookmark", 3 + "id": "wiki.lichen.bookmark", 4 4 "defs": { 5 5 "main": { 6 6 "type": "record",
+1 -1
lexicons/pub.coral.memberRequest.json lexicons/wiki.lichen.memberRequest.json
··· 1 1 { 2 2 "lexicon": 1, 3 - "id": "pub.coral.memberRequest", 3 + "id": "wiki.lichen.memberRequest", 4 4 "defs": { 5 5 "main": { 6 6 "type": "record",
+1 -1
lexicons/pub.coral.membership.json lexicons/wiki.lichen.membership.json
··· 1 1 { 2 2 "lexicon": 1, 3 - "id": "pub.coral.membership", 3 + "id": "wiki.lichen.membership", 4 4 "defs": { 5 5 "main": { 6 6 "type": "record",
+1 -1
lexicons/pub.coral.note.json lexicons/wiki.lichen.note.json
··· 1 1 { 2 2 "lexicon": 1, 3 - "id": "pub.coral.note", 3 + "id": "wiki.lichen.note", 4 4 "defs": { 5 5 "main": { 6 6 "type": "record",
+1 -1
lexicons/pub.coral.noteRevision.json lexicons/wiki.lichen.noteRevision.json
··· 1 1 { 2 2 "lexicon": 1, 3 - "id": "pub.coral.noteRevision", 3 + "id": "wiki.lichen.noteRevision", 4 4 "defs": { 5 5 "main": { 6 6 "type": "record",
+1 -1
lexicons/pub.coral.wiki.json lexicons/wiki.lichen.wiki.json
··· 1 1 { 2 2 "lexicon": 1, 3 - "id": "pub.coral.wiki", 3 + "id": "wiki.lichen.wiki", 4 4 "defs": { 5 5 "main": { 6 6 "type": "record",
+1 -1
package.json
··· 1 1 { 2 - "name": "atwiki", 2 + "name": "lichen", 3 3 "module": "src/server/index.ts", 4 4 "type": "module", 5 5 "private": true,
+1 -1
scripts/dev-full.ts
··· 76 76 DEV_PDS_URL: pdsUrl, 77 77 DEV_PLC_URL: plcUrl, 78 78 DEV_ACCOUNTS: JSON.stringify(accounts), 79 - DB_PATH: "atwiki-dev.db", 79 + DB_PATH: "lichen-dev.db", 80 80 }; 81 81 82 82 console.log("Starting appview...");
+1 -1
src/atproto/client.ts
··· 71 71 const client = new NodeOAuthClient({ 72 72 clientMetadata: { 73 73 client_id: `${url}/client-metadata.json`, 74 - client_name: "atwiki", 74 + client_name: "Lichen", 75 75 client_uri: url, 76 76 jwks_uri: `${url}/jwks.json`, 77 77 redirect_uris: [`${url}/atproto-oauth-callback`],
+6 -6
src/lib/constants.ts
··· 1 1 export const COLLECTIONS = { 2 - wiki: "pub.coral.wiki", 3 - note: "pub.coral.note", 4 - noteRevision: "pub.coral.noteRevision", 5 - membership: "pub.coral.membership", 6 - memberRequest: "pub.coral.memberRequest", 7 - bookmark: "pub.coral.bookmark", 2 + wiki: "wiki.lichen.wiki", 3 + note: "wiki.lichen.note", 4 + noteRevision: "wiki.lichen.noteRevision", 5 + membership: "wiki.lichen.membership", 6 + memberRequest: "wiki.lichen.memberRequest", 7 + bookmark: "wiki.lichen.bookmark", 8 8 } as const; 9 9 10 10 export type MemberRole = "admin" | "contributor" | "viewer";
+4 -4
src/lib/orchestrators/membership.ts
··· 38 38 39 39 const now = currentTimestamp(); 40 40 const tid = generateTid(); 41 - const atUri = `at://${ctx.session.did}/pub.coral.memberRequest/${tid}`; 41 + const atUri = `at://${ctx.session.did}/wiki.lichen.memberRequest/${tid}`; 42 42 43 43 try { 44 44 const agent = await getAgent(ctx.session); ··· 69 69 ): Promise<void> { 70 70 const now = currentTimestamp(); 71 71 const membershipTid = generateTid(); 72 - const membershipAtUri = `at://${ctx.effectiveDid}/pub.coral.membership/${membershipTid}`; 72 + const membershipAtUri = `at://${ctx.effectiveDid}/wiki.lichen.membership/${membershipTid}`; 73 73 74 74 if (ctx.session) { 75 75 const session = ctx.session; ··· 112 112 } 113 113 114 114 const newTid = generateTid(); 115 - const newAtUri = `at://${ctx.effectiveDid}/pub.coral.membership/${newTid}`; 115 + const newAtUri = `at://${ctx.effectiveDid}/wiki.lichen.membership/${newTid}`; 116 116 117 117 if (ctx.session) { 118 118 const session = ctx.session; ··· 167 167 ): Promise<void> { 168 168 const now = currentTimestamp(); 169 169 const tid = generateTid(); 170 - const atUri = `at://${ctx.effectiveDid}/pub.coral.membership/${tid}`; 170 + const atUri = `at://${ctx.effectiveDid}/wiki.lichen.membership/${tid}`; 171 171 172 172 if (ctx.session) { 173 173 const session = ctx.session;
+3 -3
src/lib/orchestrators/note.ts
··· 89 89 // Generate TIDs once — shared between PDS and DB writes 90 90 const noteTid = generateTid(); 91 91 const revisionTid = generateTid(); 92 - const noteAtUri = `at://${ctx.effectiveDid}/pub.coral.note/${noteTid}`; 93 - const revisionAtUri = `at://${ctx.effectiveDid}/pub.coral.noteRevision/${revisionTid}`; 92 + const noteAtUri = `at://${ctx.effectiveDid}/wiki.lichen.note/${noteTid}`; 93 + const revisionAtUri = `at://${ctx.effectiveDid}/wiki.lichen.noteRevision/${revisionTid}`; 94 94 95 95 if (ctx.session) { 96 96 const agent = await getAgent(ctx.session); ··· 159 159 160 160 // Generate revision TID once — shared between PDS and DB writes 161 161 const revisionTid = generateTid(); 162 - const revisionAtUri = `at://${ctx.effectiveDid}/pub.coral.noteRevision/${revisionTid}`; 162 + const revisionAtUri = `at://${ctx.effectiveDid}/wiki.lichen.noteRevision/${revisionTid}`; 163 163 const currentContent = currentNote?.content ?? ""; 164 164 165 165 if (ctx.session) {
+4 -4
src/lib/orchestrators/wiki.ts
··· 67 67 const now = currentTimestamp(); 68 68 const did = ctx.effectiveDid; 69 69 70 - let atUri = `at://${did}/pub.coral.wiki/${slug}`; 70 + let atUri = `at://${did}/wiki.lichen.wiki/${slug}`; 71 71 const agent = ctx.session ? await getAgent(ctx.session) : null; 72 72 73 73 if (agent) { ··· 97 97 98 98 // Auto-admin: make creator an admin member 99 99 const membershipTid = generateTid(); 100 - const membershipAtUri = `at://${did}/pub.coral.membership/${membershipTid}`; 100 + const membershipAtUri = `at://${did}/wiki.lichen.membership/${membershipTid}`; 101 101 upsertMembership(slug, did, "admin", membershipAtUri, now); 102 102 103 103 if (agent) { ··· 118 118 const homeContent = `# Welcome to ${fields.name}\n\nThis is the home page of your wiki. Edit it to get started.`; 119 119 const noteTid = generateTid(); 120 120 const revisionTid = generateTid(); 121 - const noteAtUri = `at://${did}/pub.coral.note/${noteTid}`; 122 - const revisionAtUri = `at://${did}/pub.coral.noteRevision/${revisionTid}`; 121 + const noteAtUri = `at://${did}/wiki.lichen.note/${noteTid}`; 122 + const revisionAtUri = `at://${did}/wiki.lichen.noteRevision/${revisionTid}`; 123 123 124 124 if (agent) { 125 125 const diff = createDiff("", homeContent);
+1 -1
src/server/db/index.ts
··· 2 2 import { initSchema } from "./schema.ts"; 3 3 import { seedIfEmpty } from "./seed.ts"; 4 4 5 - const DB_PATH = process.env.DB_PATH ?? "atwiki.db"; 5 + const DB_PATH = process.env.DB_PATH ?? "lichen.db"; 6 6 7 7 let db: Database | null = null; 8 8
+6 -6
src/server/db/seed.ts
··· 16 16 const mockDid = DEV_DID; 17 17 18 18 const noteAtUris = { 19 - home: `at://${mockDid}/pub.coral.note/${homeTid}`, 20 - hello: `at://${mockDid}/pub.coral.note/${helloTid}`, 21 - gettingStarted: `at://${mockDid}/pub.coral.note/${gettingStartedTid}`, 19 + home: `at://${mockDid}/wiki.lichen.note/${homeTid}`, 20 + hello: `at://${mockDid}/wiki.lichen.note/${helloTid}`, 21 + gettingStarted: `at://${mockDid}/wiki.lichen.note/${gettingStartedTid}`, 22 22 }; 23 23 24 24 const noteContents: Record<string, string> = { ··· 29 29 ## Quick Links 30 30 31 31 - [[hello|Hello World]] — a sample note 32 - - [[getting-started|Getting Started]] — how to use atwiki 32 + - [[getting-started|Getting Started]] — how to use Lichen 33 33 34 34 ## About 35 35 ··· 46 46 - Code blocks work too: 47 47 48 48 \`\`\`js 49 - console.log("hello from atwiki"); 49 + console.log("hello from Lichen"); 50 50 \`\`\` 51 51 `, 52 52 [noteAtUris.gettingStarted]: `# Getting Started ··· 63 63 try { 64 64 db.run(` 65 65 INSERT INTO wikis (slug, did, name, visibility, at_uri) VALUES 66 - ('test', '${mockDid}', 'Test Wiki', 'public', 'at://${mockDid}/pub.coral.wiki/test') 66 + ('test', '${mockDid}', 'Test Wiki', 'public', 'at://${mockDid}/wiki.lichen.wiki/test') 67 67 `); 68 68 69 69 db.run(
+1 -1
src/server/index.ts
··· 40 40 .use(wikiRoutes) 41 41 .listen(3000); 42 42 43 - console.log(`atwiki running at http://localhost:${app.server?.port}`); 43 + console.log(`Lichen running at http://localhost:${app.server?.port}`);
+3 -3
src/views/layout.ts
··· 117 117 <head> 118 118 <meta charset="UTF-8"> 119 119 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 120 - <title>${title} — atwiki</title> 120 + <title>${title} — Lichen</title> 121 121 <link rel="stylesheet" href="/public/dist.css"> 122 122 <script src="https://unpkg.com/htmx.org@2.0.4"></script> 123 123 ${extraScripts} 124 124 </head> 125 125 <body class="${THEME.bgPage} ${THEME.textPrimary} min-h-screen"> 126 126 <nav class="${THEME.bgSurface} border-b ${THEME.borderDefault} px-6 py-3 flex items-center gap-4"> 127 - <a href="/" class="text-lg font-semibold ${THEME.accentText} shrink-0">atwiki</a> 127 + <a href="/" class="flex items-center gap-2 shrink-0"><span class="inline-block w-3 h-3 rounded-full bg-teal-700"></span><span class="text-lg font-semibold ${THEME.accentText}">Lichen</span></a> 128 128 <div class="flex-1 text-center">${options?.wikiName ? `<a href="/wiki/${options.wikiSlug}" class="text-lg font-medium ${THEME.textSecondary} ${THEME.accentHoverText}">${escapeHtml(options.wikiName)}</a>` : ""}</div> 129 - <a href="https://tangled.org/juprodh.bsky.social/atwiki" target="_blank" rel="noopener noreferrer" class="shrink-0" title="View source on tangled"> 129 + <a href="https://tangled.org/juprodh.bsky.social/lichen.wiki" target="_blank" rel="noopener noreferrer" class="shrink-0" title="View source on tangled"> 130 130 <img src="/public/tangled.svg" alt="tangled" class="w-5 h-5"> 131 131 </a> 132 132 <div class="flex items-center gap-3 shrink-0">
+49 -46
src/views/theme.ts
··· 1 1 /** 2 - * Design tokens for the UI. All color/font/spacing values used across views 3 - * and client-side code should reference these constants. 2 + * Lichen theme — design tokens for the UI. 3 + * 4 + * Palette inspired by lichen: muted sage/teal greens with warm stone neutrals. 5 + * All color/font/spacing values used across views and client-side code should 6 + * reference these constants. 4 7 * 5 8 * Future: user-selectable themes swap these values (e.g. accent color, font). 6 9 */ 7 10 8 11 export const THEME = { 9 - // Accent / brand (indigo-600) 10 - accentBg: "bg-indigo-600", 11 - accentText: "text-indigo-600", 12 - accentBorder: "border-indigo-600", 13 - accentHoverText: "hover:text-indigo-600", 12 + // Accent / brand (teal-700) 13 + accentBg: "bg-teal-700", 14 + accentText: "text-teal-700", 15 + accentBorder: "border-teal-700", 16 + accentHoverText: "hover:text-teal-700", 14 17 15 - // Accent dark (indigo-700) 16 - accentDarkHoverBg: "hover:bg-indigo-700", 17 - accentDarkText: "text-indigo-700", 18 + // Accent dark (teal-800) 19 + accentDarkHoverBg: "hover:bg-teal-800", 20 + accentDarkText: "text-teal-800", 18 21 19 - // Accent light (indigo-50) 20 - accentLightHoverBg: "hover:bg-indigo-50", 21 - accentLightFocusBg: "focus:bg-indigo-50", 22 + // Accent light (teal-50) 23 + accentLightHoverBg: "hover:bg-teal-50", 24 + accentLightFocusBg: "focus:bg-teal-50", 22 25 23 26 // Focus / ring states 24 - accentFocusRing: "focus:ring-indigo-500", 25 - accentFocusBorder: "focus:border-indigo-500", 26 - accentInputFocusBorder: "focus:border-indigo-400", 27 - accentSubtleRing: "focus:ring-indigo-200", 27 + accentFocusRing: "focus:ring-teal-600", 28 + accentFocusBorder: "focus:border-teal-600", 29 + accentInputFocusBorder: "focus:border-teal-500", 30 + accentSubtleRing: "focus:ring-teal-200", 28 31 29 - // Text hierarchy 30 - textPrimary: "text-gray-900", 31 - textSecondary: "text-gray-700", 32 - textMuted: "text-gray-500", 33 - textSecondaryHover: "hover:text-gray-700", 32 + // Text hierarchy (warm stone tones) 33 + textPrimary: "text-stone-900", 34 + textSecondary: "text-stone-700", 35 + textMuted: "text-stone-500", 36 + textSecondaryHover: "hover:text-stone-700", 34 37 35 - // Backgrounds 36 - bgPage: "bg-gray-50", 38 + // Backgrounds (warm stone tones) 39 + bgPage: "bg-stone-50", 37 40 bgSurface: "bg-white", 38 - bgPlaceholder: "bg-gray-200", 41 + bgPlaceholder: "bg-stone-200", 39 42 40 - // Borders 41 - borderDefault: "border-gray-200", 42 - borderInput: "border-gray-300", 43 - borderSubtle: "border-gray-100", 43 + // Borders (warm stone tones) 44 + borderDefault: "border-stone-200", 45 + borderInput: "border-stone-300", 46 + borderSubtle: "border-stone-100", 44 47 45 48 // Status 46 49 statusPublicBg: "bg-green-100", ··· 62 65 // --- Hex values for client-side JS (editor, viz renderers) --- 63 66 64 67 export const THEME_HEX = { 65 - // Editor toolbar 66 - toolbarBg: "#f3f4f6", 67 - toolbarBorder: "#d1d5db", 68 + // Editor toolbar (stone tones) 69 + toolbarBg: "#f5f5f4", // stone-100 70 + toolbarBorder: "#d6d3d1", // stone-300 68 71 buttonBg: "#ffffff", 69 - buttonBorder: "#d1d5db", 72 + buttonBorder: "#d6d3d1", // stone-300 70 73 71 74 // Editor pane 72 - paneBorder: "#d1d5db", 75 + paneBorder: "#d6d3d1", // stone-300 73 76 previewBg: "#ffffff", 74 77 75 78 // Viz: text labels 76 - vizText: "#374151", 79 + vizText: "#44403c", // stone-700 77 80 78 - // Viz: graph 79 - graphLinkStroke: "#999999", 81 + // Viz: graph (nature/lichen palette) 82 + graphLinkStroke: "#a8a29e", // stone-400 80 83 graphNodeStroke: "#ffffff", 81 - graphDefaultColor: "#6366f1", 84 + graphDefaultColor: "#0f766e", // teal-700 82 85 graphGroupColors: [ 83 - "#6366f1", // indigo 84 - "#f59e0b", // amber 85 - "#10b981", // emerald 86 - "#ef4444", // red 87 - "#8b5cf6", // violet 88 - "#06b6d4", // cyan 89 - "#f97316", // orange 90 - "#ec4899", // pink 86 + "#0f766e", // teal-700 87 + "#b45309", // amber-700 88 + "#047857", // emerald-700 89 + "#dc2626", // red-600 90 + "#7c3aed", // violet-600 91 + "#0e7490", // cyan-700 92 + "#c2410c", // orange-700 93 + "#be185d", // pink-700 91 94 ], 92 95 } as const;
+33 -33
tests/firehose/handlers.test.ts
··· 14 14 15 15 const ALICE_DID = "did:plc:alice"; 16 16 const BOB_DID = "did:plc:bob"; 17 - const WIKI_AT_URI = `at://${ALICE_DID}/pub.coral.wiki/test-wiki`; 17 + const WIKI_AT_URI = `at://${ALICE_DID}/wiki.lichen.wiki/test-wiki`; 18 18 19 19 interface CommitEvtInput { 20 20 event: string; ··· 88 88 handleCommitEvent( 89 89 makeCommitEvt({ 90 90 event: "create", 91 - collection: "pub.coral.wiki", 91 + collection: "wiki.lichen.wiki", 92 92 rkey: "test-wiki", 93 93 did: ALICE_DID, 94 94 record: { ··· 111 111 handleCommitEvent( 112 112 makeCommitEvt({ 113 113 event: "update", 114 - collection: "pub.coral.wiki", 114 + collection: "wiki.lichen.wiki", 115 115 rkey: "test-wiki", 116 116 did: ALICE_DID, 117 117 record: { ··· 131 131 handleCommitEvent( 132 132 makeCommitEvt({ 133 133 event: "create", 134 - collection: "pub.coral.wiki", 134 + collection: "wiki.lichen.wiki", 135 135 rkey: "evil-wiki", 136 136 did: BOB_DID, 137 137 uri: { 138 - toString: () => `at://${ALICE_DID}/pub.coral.wiki/evil-wiki`, 138 + toString: () => `at://${ALICE_DID}/wiki.lichen.wiki/evil-wiki`, 139 139 }, 140 140 record: { 141 141 name: "Evil Wiki", ··· 153 153 handleCommitEvent( 154 154 makeCommitEvt({ 155 155 event: "create", 156 - collection: "pub.coral.wiki", 156 + collection: "wiki.lichen.wiki", 157 157 rkey: "lang-wiki", 158 158 did: ALICE_DID, 159 159 record: { ··· 174 174 handleCommitEvent( 175 175 makeCommitEvt({ 176 176 event: "create", 177 - collection: "pub.coral.wiki", 177 + collection: "wiki.lichen.wiki", 178 178 rkey: "nolang-wiki", 179 179 did: ALICE_DID, 180 180 record: { ··· 194 194 handleCommitEvent( 195 195 makeCommitEvt({ 196 196 event: "create", 197 - collection: "pub.coral.wiki", 197 + collection: "wiki.lichen.wiki", 198 198 rkey: "incomplete-wiki", 199 199 did: ALICE_DID, 200 200 record: { ··· 211 211 handleCommitEvent( 212 212 makeCommitEvt({ 213 213 event: "create", 214 - collection: "pub.coral.wiki", 214 + collection: "wiki.lichen.wiki", 215 215 rkey: "delete-me", 216 216 did: ALICE_DID, 217 217 record: { ··· 226 226 handleCommitEvent( 227 227 makeCommitEvt({ 228 228 event: "delete", 229 - collection: "pub.coral.wiki", 229 + collection: "wiki.lichen.wiki", 230 230 rkey: "delete-me", 231 231 did: ALICE_DID, 232 232 }), ··· 237 237 238 238 describe("note handler", () => { 239 239 const NOTE_TID = "362pbqd3tgb7z"; 240 - const NOTE_AT_URI = `at://${ALICE_DID}/pub.coral.note/${NOTE_TID}`; 240 + const NOTE_AT_URI = `at://${ALICE_DID}/wiki.lichen.note/${NOTE_TID}`; 241 241 242 242 test("creates note linked to existing wiki", () => { 243 243 handleCommitEvent( 244 244 makeCommitEvt({ 245 245 event: "create", 246 - collection: "pub.coral.note", 246 + collection: "wiki.lichen.note", 247 247 rkey: NOTE_TID, 248 248 did: ALICE_DID, 249 249 record: { ··· 265 265 handleCommitEvent( 266 266 makeCommitEvt({ 267 267 event: "create", 268 - collection: "pub.coral.note", 268 + collection: "wiki.lichen.note", 269 269 rkey: "362pbqd3tgb8a", 270 270 did: ALICE_DID, 271 271 record: { 272 272 slug: "orphan-note", 273 273 title: "Orphan", 274 - wikiRef: `at://${ALICE_DID}/pub.coral.wiki/nonexistent`, 274 + wikiRef: `at://${ALICE_DID}/wiki.lichen.wiki/nonexistent`, 275 275 createdAt: "2026-01-01T00:00:00.000Z", 276 276 }, 277 277 }), ··· 286 286 handleCommitEvent( 287 287 makeCommitEvt({ 288 288 event: "create", 289 - collection: "pub.coral.note", 289 + collection: "wiki.lichen.note", 290 290 rkey: delNoteTid, 291 291 did: ALICE_DID, 292 292 record: { ··· 302 302 handleCommitEvent( 303 303 makeCommitEvt({ 304 304 event: "delete", 305 - collection: "pub.coral.note", 305 + collection: "wiki.lichen.note", 306 306 rkey: delNoteTid, 307 307 did: ALICE_DID, 308 308 }), ··· 313 313 314 314 describe("revision handler", () => { 315 315 const NOTE_TID = "362pbqd3tgb7z"; 316 - const NOTE_AT_URI = `at://${ALICE_DID}/pub.coral.note/${NOTE_TID}`; 316 + const NOTE_AT_URI = `at://${ALICE_DID}/wiki.lichen.note/${NOTE_TID}`; 317 317 318 318 test("applies diff and updates current_note", () => { 319 319 const revTid = "362pbqd3tgc2a"; 320 320 handleCommitEvent( 321 321 makeCommitEvt({ 322 322 event: "create", 323 - collection: "pub.coral.noteRevision", 323 + collection: "wiki.lichen.noteRevision", 324 324 rkey: revTid, 325 325 did: ALICE_DID, 326 326 record: { ··· 339 339 340 340 test("chains revisions correctly", () => { 341 341 const rev2Tid = "362pbqd3tgc3b"; 342 - const prevRevUri = `at://${ALICE_DID}/pub.coral.noteRevision/362pbqd3tgc2a`; 342 + const prevRevUri = `at://${ALICE_DID}/wiki.lichen.noteRevision/362pbqd3tgc2a`; 343 343 344 344 handleCommitEvent( 345 345 makeCommitEvt({ 346 346 event: "create", 347 - collection: "pub.coral.noteRevision", 347 + collection: "wiki.lichen.noteRevision", 348 348 rkey: rev2Tid, 349 349 did: ALICE_DID, 350 350 record: { ··· 366 366 handleCommitEvent( 367 367 makeCommitEvt({ 368 368 event: "create", 369 - collection: "pub.coral.note", 369 + collection: "wiki.lichen.note", 370 370 rkey: linkNoteTid, 371 371 did: ALICE_DID, 372 372 record: { ··· 382 382 handleCommitEvent( 383 383 makeCommitEvt({ 384 384 event: "create", 385 - collection: "pub.coral.noteRevision", 385 + collection: "wiki.lichen.noteRevision", 386 386 rkey: linkRevTid, 387 387 did: ALICE_DID, 388 388 record: { 389 - noteRef: `at://${ALICE_DID}/pub.coral.note/${linkNoteTid}`, 389 + noteRef: `at://${ALICE_DID}/wiki.lichen.note/${linkNoteTid}`, 390 390 diff: "@@ -0,0 +1,25 @@\n+Check out %5B%5Bhello-world%5D%5D\n", 391 391 diffFormat: "diff-match-patch", 392 392 createdAt: "2026-01-01T00:00:00.000Z", ··· 403 403 }[]; 404 404 expect(backlinks).toHaveLength(1); 405 405 expect(backlinks[0]?.source_note_uri).toBe( 406 - `at://${ALICE_DID}/pub.coral.note/${linkNoteTid}`, 406 + `at://${ALICE_DID}/wiki.lichen.note/${linkNoteTid}`, 407 407 ); 408 408 }); 409 409 ··· 412 412 handleCommitEvent( 413 413 makeCommitEvt({ 414 414 event: "create", 415 - collection: "pub.coral.noteRevision", 415 + collection: "wiki.lichen.noteRevision", 416 416 rkey: orphanRevTid, 417 417 did: ALICE_DID, 418 418 record: { 419 - noteRef: `at://${ALICE_DID}/pub.coral.note/nonexistent`, 419 + noteRef: `at://${ALICE_DID}/wiki.lichen.note/nonexistent`, 420 420 diff: "@@ -0,0 +1,6 @@\n+orphan\n", 421 421 diffFormat: "diff-match-patch", 422 422 createdAt: "2026-01-01T00:00:00.000Z", ··· 426 426 427 427 const rev = db 428 428 .query("SELECT * FROM revisions WHERE at_uri = ?") 429 - .get(`at://${ALICE_DID}/pub.coral.noteRevision/${orphanRevTid}`); 429 + .get(`at://${ALICE_DID}/wiki.lichen.noteRevision/${orphanRevTid}`); 430 430 expect(rev).toBeNull(); 431 431 }); 432 432 }); ··· 437 437 handleCommitEvent( 438 438 makeCommitEvt({ 439 439 event: "create", 440 - collection: "pub.coral.membership", 440 + collection: "wiki.lichen.membership", 441 441 rkey: memberTid, 442 442 did: ALICE_DID, 443 443 record: { ··· 461 461 handleCommitEvent( 462 462 makeCommitEvt({ 463 463 event: "create", 464 - collection: "pub.coral.membership", 464 + collection: "wiki.lichen.membership", 465 465 rkey: evilTid, 466 466 did: BOB_DID, 467 467 record: { ··· 481 481 482 482 test("deletes membership on delete event", () => { 483 483 const memberTid = "362pbqd3tgf1a"; 484 - const memberAtUri = `at://${ALICE_DID}/pub.coral.membership/${memberTid}`; 484 + const memberAtUri = `at://${ALICE_DID}/wiki.lichen.membership/${memberTid}`; 485 485 486 486 handleCommitEvent( 487 487 makeCommitEvt({ 488 488 event: "delete", 489 - collection: "pub.coral.membership", 489 + collection: "wiki.lichen.membership", 490 490 rkey: memberTid, 491 491 did: ALICE_DID, 492 492 }), ··· 505 505 handleCommitEvent( 506 506 makeCommitEvt({ 507 507 event: "create", 508 - collection: "pub.coral.memberRequest", 508 + collection: "wiki.lichen.memberRequest", 509 509 rkey: requestTid, 510 510 did: BOB_DID, 511 511 record: { ··· 527 527 handleCommitEvent( 528 528 makeCommitEvt({ 529 529 event: "delete", 530 - collection: "pub.coral.memberRequest", 530 + collection: "wiki.lichen.memberRequest", 531 531 rkey: requestTid, 532 532 did: BOB_DID, 533 533 }),
+1 -1
tests/integration/roundtrip.test.ts
··· 453 453 454 454 describe("integration: orphan note rejection", () => { 455 455 test("note referencing non-existent wiki is rejected", async () => { 456 - const fakeWikiUri = `at://${aliceDid}/pub.coral.wiki/nonexistent`; 456 + const fakeWikiUri = `at://${aliceDid}/wiki.lichen.wiki/nonexistent`; 457 457 458 458 const { uri: noteUri } = await putNoteRecord( 459 459 aliceAgent,
+13 -13
tests/lib/orchestrators/membership.test.ts
··· 8 8 const realSession = await import("../../../src/atproto/session.ts"); 9 9 10 10 const mockWriteMemberRequestRecord = mock(async () => ({ 11 - uri: "at://did:plc:user/pub.coral.memberRequest/abc", 11 + uri: "at://did:plc:user/wiki.lichen.memberRequest/abc", 12 12 cid: "bafyrei123", 13 13 })); 14 14 const mockWriteMembershipRecord = mock(async () => ({ 15 - uri: "at://did:plc:orchowner/pub.coral.membership/def", 15 + uri: "at://did:plc:orchowner/wiki.lichen.membership/def", 16 16 cid: "bafyrei456", 17 17 })); 18 18 const mockDeleteRecord = mock(async () => {}); ··· 52 52 name: "Orchestrator Membership Test Wiki", 53 53 visibility: "public", 54 54 language: "en", 55 - at_uri: `at://${OWNER_DID}/pub.coral.wiki/${WIKI_SLUG}`, 55 + at_uri: `at://${OWNER_DID}/wiki.lichen.wiki/${WIKI_SLUG}`, 56 56 created_at: "2026-01-01T00:00:00.000Z", 57 57 updated_at: "2026-01-01T00:00:00.000Z", 58 58 }; ··· 195 195 WIKI_SLUG, 196 196 "did:plc:role-change", 197 197 "contributor", 198 - `at://${ADMIN_DID}/pub.coral.membership/role-change-tid`, 198 + `at://${ADMIN_DID}/wiki.lichen.membership/role-change-tid`, 199 199 "2026-01-01T00:00:00.000Z", 200 200 ], 201 201 ); ··· 222 222 WIKI_SLUG, 223 223 "did:plc:role-change-pds", 224 224 "contributor", 225 - `at://${ADMIN_DID}/pub.coral.membership/owned-tid`, 225 + `at://${ADMIN_DID}/wiki.lichen.membership/owned-tid`, 226 226 "2026-01-01T00:00:00.000Z", 227 227 ], 228 228 ); ··· 250 250 WIKI_SLUG, 251 251 "did:plc:role-change-other", 252 252 "contributor", 253 - `at://did:plc:other-admin/pub.coral.membership/other-admin-tid`, 253 + `at://did:plc:other-admin/wiki.lichen.membership/other-admin-tid`, 254 254 "2026-01-01T00:00:00.000Z", 255 255 ], 256 256 ); ··· 287 287 WIKI_SLUG, 288 288 "did:plc:role-pds-fail", 289 289 "contributor", 290 - `at://${ADMIN_DID}/pub.coral.membership/role-pds-fail-tid`, 290 + `at://${ADMIN_DID}/wiki.lichen.membership/role-pds-fail-tid`, 291 291 "2026-01-01T00:00:00.000Z", 292 292 ], 293 293 ); ··· 310 310 WIKI_SLUG, 311 311 "did:plc:role-no-session", 312 312 "contributor", 313 - `at://${ADMIN_DID}/pub.coral.membership/role-no-session-tid`, 313 + `at://${ADMIN_DID}/wiki.lichen.membership/role-no-session-tid`, 314 314 "2026-01-01T00:00:00.000Z", 315 315 ], 316 316 ); ··· 376 376 WIKI_SLUG, 377 377 "did:plc:readd-member", 378 378 "viewer", 379 - `at://${ADMIN_DID}/pub.coral.membership/readd-tid`, 379 + `at://${ADMIN_DID}/wiki.lichen.membership/readd-tid`, 380 380 "2026-01-01T00:00:00.000Z", 381 381 ], 382 382 ); ··· 415 415 test("deletes from DB and PDS", async () => { 416 416 // Setup: add a member to remove 417 417 const db = getDb(); 418 - const memberAtUri = `at://${ADMIN_DID}/pub.coral.membership/remove-test-tid`; 418 + const memberAtUri = `at://${ADMIN_DID}/wiki.lichen.membership/remove-test-tid`; 419 419 db.run( 420 420 `INSERT OR REPLACE INTO memberships (wiki_slug, did, role, at_uri, created_at) 421 421 VALUES (?, ?, ?, ?, ?)`, ··· 436 436 437 437 // Verify deleteRecord was called with correct collection and rkey 438 438 const args = mockDeleteRecord.mock.calls[0] as unknown[]; 439 - expect(args[2]).toBe("pub.coral.membership"); 439 + expect(args[2]).toBe("wiki.lichen.membership"); 440 440 expect(args[3]).toBe("remove-test-tid"); 441 441 442 442 // Verify DB deletion ··· 468 468 WIKI_SLUG, 469 469 "did:plc:pdsfailremove", 470 470 "admin", 471 - `at://${ADMIN_DID}/pub.coral.membership/pds-fail-tid`, 471 + `at://${ADMIN_DID}/wiki.lichen.membership/pds-fail-tid`, 472 472 "2026-01-01T00:00:00.000Z", 473 473 ], 474 474 ); ··· 492 492 WIKI_SLUG, 493 493 "did:plc:nosessionremove", 494 494 "admin", 495 - `at://${ADMIN_DID}/pub.coral.membership/no-session-tid`, 495 + `at://${ADMIN_DID}/wiki.lichen.membership/no-session-tid`, 496 496 "2026-01-01T00:00:00.000Z", 497 497 ], 498 498 );
+4 -4
tests/lib/orchestrators/note.test.ts
··· 8 8 const realSession = await import("../../../src/atproto/session.ts"); 9 9 10 10 const mockWriteNoteRecord = mock(async () => ({ 11 - uri: "at://did:plc:test/pub.coral.note/abc", 11 + uri: "at://did:plc:test/wiki.lichen.note/abc", 12 12 cid: "bafyrei123", 13 13 })); 14 14 const mockWriteRevisionRecord = mock(async () => ({ 15 - uri: "at://did:plc:test/pub.coral.noteRevision/def", 15 + uri: "at://did:plc:test/wiki.lichen.noteRevision/def", 16 16 cid: "bafyrei456", 17 17 })); 18 18 const mockGetAgent = mock(async () => ({}) as never); ··· 49 49 name: "Orchestrator Note Test Wiki", 50 50 visibility: "public", 51 51 language: "en", 52 - at_uri: `at://${WIKI_DID}/pub.coral.wiki/${WIKI_SLUG}`, 52 + at_uri: `at://${WIKI_DID}/wiki.lichen.wiki/${WIKI_SLUG}`, 53 53 created_at: "2026-01-01T00:00:00.000Z", 54 54 updated_at: "2026-01-01T00:00:00.000Z", 55 55 }; ··· 75 75 WIKI_DID, 76 76 "Orchestrator Note Test Wiki", 77 77 "public", 78 - `at://${WIKI_DID}/pub.coral.wiki/${WIKI_SLUG}`, 78 + `at://${WIKI_DID}/wiki.lichen.wiki/${WIKI_SLUG}`, 79 79 "2026-01-01T00:00:00.000Z", 80 80 "2026-01-01T00:00:00.000Z", 81 81 "en",
+6 -6
tests/lib/orchestrators/wiki.test.ts
··· 11 11 const realSession = await import("../../../src/atproto/session.ts"); 12 12 13 13 const mockWriteWikiRecord = mock(async () => ({ 14 - uri: "at://did:plc:wikitest/pub.coral.wiki/test", 14 + uri: "at://did:plc:wikitest/wiki.lichen.wiki/test", 15 15 cid: "bafyrei123", 16 16 })); 17 17 const mockWriteMembershipRecord = mock(async () => ({ 18 - uri: "at://did:plc:wikitest/pub.coral.membership/abc", 18 + uri: "at://did:plc:wikitest/wiki.lichen.membership/abc", 19 19 cid: "bafyrei456", 20 20 })); 21 21 const mockWriteNoteRecord = mock(async () => ({ 22 - uri: "at://did:plc:wikitest/pub.coral.note/note1", 22 + uri: "at://did:plc:wikitest/wiki.lichen.note/note1", 23 23 cid: "bafyrei789", 24 24 })); 25 25 const mockWriteRevisionRecord = mock(async () => ({ 26 - uri: "at://did:plc:wikitest/pub.coral.noteRevision/rev1", 26 + uri: "at://did:plc:wikitest/wiki.lichen.noteRevision/rev1", 27 27 cid: "bafyreirev", 28 28 })); 29 29 const mockDeleteRecord = mock(async () => {}); ··· 96 96 const db = getDb(); 97 97 for (const slug of createdSlugs) { 98 98 db.run("DELETE FROM current_note WHERE note_at_uri LIKE ?", [ 99 - `at://${TEST_DID}/pub.coral.note/%`, 99 + `at://${TEST_DID}/wiki.lichen.note/%`, 100 100 ]); 101 101 db.run("DELETE FROM revisions WHERE note_at_uri LIKE ?", [ 102 - `at://${TEST_DID}/pub.coral.note/%`, 102 + `at://${TEST_DID}/wiki.lichen.note/%`, 103 103 ]); 104 104 db.run("DELETE FROM notes WHERE wiki_slug = ?", [slug]); 105 105 db.run("DELETE FROM memberships WHERE wiki_slug = ?", [slug]);
+5 -5
tests/server/db/queries/blob.test.ts
··· 24 24 test("inserts a blob row", () => { 25 25 insertBlob( 26 26 "bafytest-blob-1", 27 - "at://did:plc:test/pub.coral.noteRevision/abc", 27 + "at://did:plc:test/wiki.lichen.noteRevision/abc", 28 28 "image/png", 29 29 "data/blobs/test1.png", 30 30 ); ··· 40 40 test("INSERT OR IGNORE on duplicate CID", () => { 41 41 insertBlob( 42 42 "bafytest-blob-dup", 43 - "at://did:plc:test/pub.coral.noteRevision/first", 43 + "at://did:plc:test/wiki.lichen.noteRevision/first", 44 44 "image/jpeg", 45 45 "data/blobs/first.jpg", 46 46 ); 47 47 // Insert again with different values — should be ignored 48 48 insertBlob( 49 49 "bafytest-blob-dup", 50 - "at://did:plc:test/pub.coral.noteRevision/second", 50 + "at://did:plc:test/wiki.lichen.noteRevision/second", 51 51 "image/png", 52 52 "data/blobs/second.png", 53 53 ); ··· 69 69 test("returns matching blobs", () => { 70 70 insertBlob( 71 71 "bafytest-blob-2", 72 - "at://did:plc:test/pub.coral.noteRevision/xyz", 72 + "at://did:plc:test/wiki.lichen.noteRevision/xyz", 73 73 "image/webp", 74 74 "data/blobs/test2.webp", 75 75 ); 76 76 insertBlob( 77 77 "bafytest-blob-3", 78 - "at://did:plc:test/pub.coral.noteRevision/xyz", 78 + "at://did:plc:test/wiki.lichen.noteRevision/xyz", 79 79 "image/gif", 80 80 "data/blobs/test3.gif", 81 81 );
+11 -9
tests/server/db/queries/membership.test.ts
··· 20 20 WIKI_SLUG, 21 21 ADMIN_DID, 22 22 "admin", 23 - "at://did:plc:member-admin/pub.coral.membership/m1", 23 + "at://did:plc:member-admin/wiki.lichen.membership/m1", 24 24 "2026-01-01T00:00:00.000Z", 25 25 ); 26 26 upsertMembership( 27 27 WIKI_SLUG, 28 28 VIEWER_DID, 29 29 "viewer", 30 - "at://did:plc:member-viewer/pub.coral.membership/m2", 30 + "at://did:plc:member-viewer/wiki.lichen.membership/m2", 31 31 "2026-01-02T00:00:00.000Z", 32 32 ); 33 33 upsertRequest( 34 34 WIKI_SLUG, 35 35 REQUESTER_DID, 36 - "at://did:plc:requester/pub.coral.memberRequest/r1", 36 + "at://did:plc:requester/wiki.lichen.memberRequest/r1", 37 37 "2026-01-03T00:00:00.000Z", 38 38 ); 39 39 ··· 53 53 expect(m?.did).toBe(ADMIN_DID); 54 54 expect(m?.role).toBe("admin"); 55 55 expect(m?.wiki_slug).toBe(WIKI_SLUG); 56 - expect(m?.at_uri).toBe("at://did:plc:member-admin/pub.coral.membership/m1"); 56 + expect(m?.at_uri).toBe( 57 + "at://did:plc:member-admin/wiki.lichen.membership/m1", 58 + ); 57 59 }); 58 60 59 61 test("returns null for non-member", () => { ··· 71 73 WIKI_SLUG, 72 74 VIEWER_DID, 73 75 "contributor", 74 - "at://did:plc:member-viewer/pub.coral.membership/m2", 76 + "at://did:plc:member-viewer/wiki.lichen.membership/m2", 75 77 "2026-01-02T00:00:00.000Z", 76 78 ); 77 79 expect(getMemberRole(WIKI_SLUG, VIEWER_DID)).toBe("contributor"); ··· 81 83 WIKI_SLUG, 82 84 VIEWER_DID, 83 85 "viewer", 84 - "at://did:plc:member-viewer/pub.coral.membership/m2", 86 + "at://did:plc:member-viewer/wiki.lichen.membership/m2", 85 87 "2026-01-02T00:00:00.000Z", 86 88 ); 87 89 expect(getMemberRole(WIKI_SLUG, VIEWER_DID)).toBe("viewer"); ··· 93 95 WIKI_SLUG, 94 96 CONTRIB_DID, 95 97 "contributor", 96 - "at://did:plc:member-contrib/pub.coral.membership/m3", 98 + "at://did:plc:member-contrib/wiki.lichen.membership/m3", 97 99 "2026-01-04T00:00:00.000Z", 98 100 ); 99 101 expect(getMemberRole(WIKI_SLUG, CONTRIB_DID)).toBe("contributor"); ··· 135 137 expect(admin?.role).toBe("admin"); 136 138 expect(admin?.wiki_slug).toBe(WIKI_SLUG); 137 139 expect(admin?.at_uri).toBe( 138 - "at://did:plc:member-admin/pub.coral.membership/m1", 140 + "at://did:plc:member-admin/wiki.lichen.membership/m1", 139 141 ); 140 142 expect(admin?.created_at).toBe("2026-01-01T00:00:00.000Z"); 141 143 }); ··· 167 169 expect(req).toBeDefined(); 168 170 expect(req?.wiki_slug).toBe(WIKI_SLUG); 169 171 expect(req?.at_uri).toBe( 170 - "at://did:plc:requester/pub.coral.memberRequest/r1", 172 + "at://did:plc:requester/wiki.lichen.memberRequest/r1", 171 173 ); 172 174 }); 173 175
+8 -7
tests/server/db/queries/note.test.ts
··· 18 18 const db = getDb(); 19 19 const TEST_DID = "did:plc:mock123"; 20 20 21 - const noteUri = () => `at://${TEST_DID}/pub.coral.note/${generateTid()}`; 22 - const revUri = () => `at://${TEST_DID}/pub.coral.noteRevision/${generateTid()}`; 21 + const noteUri = () => `at://${TEST_DID}/wiki.lichen.note/${generateTid()}`; 22 + const revUri = () => 23 + `at://${TEST_DID}/wiki.lichen.noteRevision/${generateTid()}`; 23 24 24 25 beforeAll(() => { 25 26 cleanupNotes("test", "write-test-*"); ··· 172 173 "content", 173 174 ); 174 175 expect(nUri).toMatch( 175 - /^at:\/\/did:plc:mock123\/pub\.coral\.note\/[a-z2-7]{13}$/, 176 + /^at:\/\/did:plc:mock123\/wiki\.lichen\.note\/[a-z2-7]{13}$/, 176 177 ); 177 178 }); 178 179 ··· 188 189 "content", 189 190 ); 190 191 expect(rUri).toMatch( 191 - /^at:\/\/did:plc:mock123\/pub\.coral\.noteRevision\/[a-z2-7]{13}$/, 192 + /^at:\/\/did:plc:mock123\/wiki\.lichen\.noteRevision\/[a-z2-7]{13}$/, 192 193 ); 193 194 }); 194 195 ··· 393 394 const rUri = revUri(); 394 395 saveNoteEdit(rUri, "test", "edit-test-tid", "updated", TEST_DID); 395 396 expect(rUri).toMatch( 396 - /^at:\/\/did:plc:mock123\/pub\.coral\.noteRevision\/[a-z2-7]{13}$/, 397 + /^at:\/\/did:plc:mock123\/wiki\.lichen\.noteRevision\/[a-z2-7]{13}$/, 397 398 ); 398 399 }); 399 400 }); ··· 468 469 469 470 test("returns null for nonexistent AT URI", () => { 470 471 expect( 471 - getNoteByAtUri("at://did:plc:fake/pub.coral.note/nonexistent"), 472 + getNoteByAtUri("at://did:plc:fake/wiki.lichen.note/nonexistent"), 472 473 ).toBeNull(); 473 474 }); 474 475 }); ··· 536 537 }); 537 538 538 539 test("no-op for nonexistent AT URI", () => { 539 - deleteNoteByAtUri("at://did:plc:fake/pub.coral.note/nope"); 540 + deleteNoteByAtUri("at://did:plc:fake/wiki.lichen.note/nope"); 540 541 }); 541 542 });
+3 -2
tests/server/db/queries/revision.test.ts
··· 13 13 const db = getDb(); 14 14 const TEST_DID = "did:plc:mock123"; 15 15 16 - const noteUri = () => `at://${TEST_DID}/pub.coral.note/${generateTid()}`; 17 - const revUri = () => `at://${TEST_DID}/pub.coral.noteRevision/${generateTid()}`; 16 + const noteUri = () => `at://${TEST_DID}/wiki.lichen.note/${generateTid()}`; 17 + const revUri = () => 18 + `at://${TEST_DID}/wiki.lichen.noteRevision/${generateTid()}`; 18 19 19 20 beforeAll(() => { 20 21 cleanupNotes("test", "backlink-test-*");
+4 -4
tests/server/db/queries/wiki-access.test.ts
··· 29 29 ALICE_DID, 30 30 "Public Wiki", 31 31 "public", 32 - `at://${ALICE_DID}/pub.coral.wiki/access-public`, 32 + `at://${ALICE_DID}/wiki.lichen.wiki/access-public`, 33 33 "2026-01-01T00:00:00.000Z", 34 34 ); 35 35 ··· 39 39 ALICE_DID, 40 40 "Private Alice Wiki", 41 41 "private", 42 - `at://${ALICE_DID}/pub.coral.wiki/access-private-alice`, 42 + `at://${ALICE_DID}/wiki.lichen.wiki/access-private-alice`, 43 43 "2026-01-01T00:00:00.000Z", 44 44 ); 45 45 ··· 49 49 ALICE_DID, 50 50 "Private Bob Member Wiki", 51 51 "private", 52 - `at://${ALICE_DID}/pub.coral.wiki/access-private-bob-member`, 52 + `at://${ALICE_DID}/wiki.lichen.wiki/access-private-bob-member`, 53 53 "2026-01-01T00:00:00.000Z", 54 54 ); 55 55 db.run( ··· 58 58 "access-private-bob-member", 59 59 BOB_DID, 60 60 "viewer", 61 - `at://${ALICE_DID}/pub.coral.membership/bob-member`, 61 + `at://${ALICE_DID}/wiki.lichen.membership/bob-member`, 62 62 "2026-01-01T00:00:00.000Z", 63 63 ], 64 64 );
+9 -9
tests/server/db/queries/wiki.test.ts
··· 24 24 expect(testWiki?.name).toBe("Test Wiki"); 25 25 expect(testWiki?.visibility).toBe("public"); 26 26 expect(testWiki?.did).toBe("did:plc:mock123"); 27 - expect(testWiki?.at_uri).toContain("pub.coral.wiki/test"); 27 + expect(testWiki?.at_uri).toContain("wiki.lichen.wiki/test"); 28 28 expect(testWiki?.created_at).toBeDefined(); 29 29 expect(testWiki?.updated_at).toBeDefined(); 30 30 }); ··· 85 85 "did:plc:mock123", 86 86 "Private Wiki", 87 87 "private", 88 - "at://did:plc:mock123/pub.coral.wiki/upsert-test-new", 88 + "at://did:plc:mock123/wiki.lichen.wiki/upsert-test-new", 89 89 ], 90 90 ); 91 91 const results = searchWikis("Private Wiki"); ··· 106 106 107 107 test("returns null for nonexistent AT URI", () => { 108 108 const found = getWikiByAtUri( 109 - "at://did:plc:fake/pub.coral.wiki/nonexistent", 109 + "at://did:plc:fake/wiki.lichen.wiki/nonexistent", 110 110 ); 111 111 expect(found).toBeNull(); 112 112 }); ··· 119 119 "did:plc:mock123", 120 120 "Upsert New", 121 121 "public", 122 - "at://did:plc:mock123/pub.coral.wiki/upsert-test-new", 122 + "at://did:plc:mock123/wiki.lichen.wiki/upsert-test-new", 123 123 "2026-01-01T00:00:00.000Z", 124 124 ); 125 125 ··· 135 135 "did:plc:mock123", 136 136 "Original Name", 137 137 "public", 138 - "at://did:plc:mock123/pub.coral.wiki/upsert-test-update", 138 + "at://did:plc:mock123/wiki.lichen.wiki/upsert-test-update", 139 139 "2026-01-01T00:00:00.000Z", 140 140 ); 141 141 upsertWiki( ··· 143 143 "did:plc:mock123", 144 144 "Updated Name", 145 145 "private", 146 - "at://did:plc:mock123/pub.coral.wiki/upsert-test-update", 146 + "at://did:plc:mock123/wiki.lichen.wiki/upsert-test-update", 147 147 "2026-01-01T00:00:00.000Z", 148 148 ); 149 149 ··· 155 155 156 156 describe("deleteWikiByAtUri", () => { 157 157 test("deletes wiki and cascades to notes", () => { 158 - const atUri = "at://did:plc:mock123/pub.coral.wiki/delete-test-wiki"; 158 + const atUri = "at://did:plc:mock123/wiki.lichen.wiki/delete-test-wiki"; 159 159 upsertWiki( 160 160 "delete-test-wiki", 161 161 "did:plc:mock123", ··· 172 172 "delete-test-wiki", 173 173 "Del Note", 174 174 "did:plc:mock123", 175 - "at://did:plc:mock123/pub.coral.note/del1", 175 + "at://did:plc:mock123/wiki.lichen.note/del1", 176 176 ], 177 177 ); 178 178 ··· 187 187 188 188 test("no-op for nonexistent AT URI", () => { 189 189 // Should not throw 190 - deleteWikiByAtUri("at://did:plc:fake/pub.coral.wiki/nope"); 190 + deleteWikiByAtUri("at://did:plc:fake/wiki.lichen.wiki/nope"); 191 191 }); 192 192 });