This repository has no description
0

Configure Feed

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

Add validateRecord XRPC, schema migration CTA banner, note wrapping, OXA doc link, island:create CLI

- Add org.latha.island.validateRecord XRPC endpoint that checks records
against the lexicon schema (empty connections, inline URIs, object
format, missing required fields, inline synthesisDoc objects)
- Add schema migration CTA banner on island pages: when validateRecord
returns issues, show pink banner with issue list and PDS record link
- Fix note wrapping: edge notes use flex-basis 100% + overflow-wrap
instead of text-overflow ellipsis
- Add science.latha.org OXA document link in synthesis heading
- Add island:create CLI for publishing islands from centroid URLs
- Update lexicon schemas and generated types
- Add RDF ingest and link card scripts

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta Code <noreply@letta.com>

author
nandi
co-author
Letta Code
date (May 23, 2026, 9:24 AM -0700) commit 974e9e09 parent ce43e0fd
+7297 -349
-2
lex.config.js
··· 14 14 "app.bsky.actor.profile", 15 15 "network.cosmik.card", 16 16 "network.cosmik.collection", 17 - "network.cosmik.connection", 18 17 "network.cosmik.defs", 19 18 "network.cosmik.follow", 20 - "org.latha.island", 21 19 "org.latha.island.citation", 22 20 "org.latha.island.entity", 23 21 "org.latha.island.reasoning"
+157
lexicons/custom/org/latha/island/analyze.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.latha.island.analyze", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Run strata analysis on an island. Cross-references the island's vertices and edges against vault and carry data, producing themes, connections, tensions, and a synthesis. Returns an org.latha.island record ready to be written to PDS.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["island"], 13 + "properties": { 14 + "island": { 15 + "type": "ref", 16 + "ref": "#islandInput" 17 + }, 18 + "did": { 19 + "type": "string", 20 + "format": "did", 21 + "description": "DID of the user requesting analysis (used to look up vault/carry data)." 22 + } 23 + } 24 + } 25 + }, 26 + "output": { 27 + "encoding": "application/json", 28 + "schema": { 29 + "type": "object", 30 + "required": ["analysis"], 31 + "properties": { 32 + "analysis": { 33 + "type": "ref", 34 + "ref": "#analysisResult" 35 + }, 36 + "record": { 37 + "type": "ref", 38 + "ref": "org.latha.island#main", 39 + "description": "Complete org.latha.island record ready to be written to PDS." 40 + } 41 + } 42 + } 43 + } 44 + }, 45 + "islandInput": { 46 + "type": "object", 47 + "required": ["vertices", "edges"], 48 + "properties": { 49 + "vertices": { 50 + "type": "array", 51 + "items": { 52 + "type": "ref", 53 + "ref": "#vertexInfo" 54 + } 55 + }, 56 + "edges": { 57 + "type": "array", 58 + "items": { 59 + "type": "ref", 60 + "ref": "#edgeInfo" 61 + } 62 + } 63 + } 64 + }, 65 + "vertexInfo": { 66 + "type": "object", 67 + "required": ["uri", "title"], 68 + "properties": { 69 + "uri": { 70 + "type": "string", 71 + "description": "Vertex URI (HTTP URL or AT URI)." 72 + }, 73 + "title": { 74 + "type": "string", 75 + "description": "Resolved title of the vertex." 76 + }, 77 + "description": { 78 + "type": "string", 79 + "description": "Resolved description." 80 + }, 81 + "type": { 82 + "type": "string", 83 + "description": "Vertex type (url, card, collection, note, unknown)." 84 + } 85 + } 86 + }, 87 + "edgeInfo": { 88 + "type": "object", 89 + "required": ["uri", "did", "source", "target", "connectionType"], 90 + "properties": { 91 + "uri": { 92 + "type": "string", 93 + "format": "at-uri" 94 + }, 95 + "did": { 96 + "type": "string", 97 + "format": "did" 98 + }, 99 + "source": { 100 + "type": "string" 101 + }, 102 + "target": { 103 + "type": "string" 104 + }, 105 + "connectionType": { 106 + "type": "string" 107 + }, 108 + "note": { 109 + "type": "string" 110 + }, 111 + "handle": { 112 + "type": "string" 113 + } 114 + } 115 + }, 116 + "analysisResult": { 117 + "type": "object", 118 + "required": ["themes", "synthesis"], 119 + "properties": { 120 + "title": { 121 + "type": "string", 122 + "maxGraphemes": 300, 123 + "description": "Concise sentence capturing the core argument." 124 + }, 125 + "themes": { 126 + "type": "array", 127 + "items": { 128 + "type": "string" 129 + } 130 + }, 131 + "connections": { 132 + "type": "array", 133 + "items": { 134 + "type": "string", 135 + "format": "at-uri" 136 + } 137 + }, 138 + "tensions": { 139 + "type": "array", 140 + "items": { 141 + "type": "string" 142 + } 143 + }, 144 + "openQuestions": { 145 + "type": "array", 146 + "items": { 147 + "type": "string" 148 + } 149 + }, 150 + "synthesis": { 151 + "type": "string", 152 + "description": "Prose synthesis connecting the island to existing knowledge." 153 + } 154 + } 155 + } 156 + } 157 + }
+168
lexicons/custom/org/latha/island/island.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.latha.island", 4 + "description": "A structured synthesis over a graph component. The island record points to actual network.cosmik.connection records; it does not embed edge data.", 5 + "defs": { 6 + "main": { 7 + "type": "record", 8 + "description": "A Stigmergic island: a centroid URL, pointers to network.cosmik.connection records, and analysis of the resulting graph component.", 9 + "key": "tid", 10 + "record": { 11 + "type": "object", 12 + "required": ["source", "connections", "analysis", "createdAt"], 13 + "properties": { 14 + "forkOf": { 15 + "type": "string", 16 + "format": "at-uri", 17 + "description": "Original org.latha.island AT URI when this record is a fork/update of another user's island." 18 + }, 19 + "source": { 20 + "type": "ref", 21 + "ref": "#sourceRef", 22 + "description": "The island centroid/canonical source URL." 23 + }, 24 + "connections": { 25 + "type": "array", 26 + "maxLength": 500, 27 + "items": { 28 + "type": "string", 29 + "format": "at-uri" 30 + }, 31 + "description": "AT URIs of actual network.cosmik.connection records. The island does not embed edge source/target/type/note data." 32 + }, 33 + "analysis": { 34 + "type": "ref", 35 + "ref": "#structuredAnalysis", 36 + "description": "The structured analysis output." 37 + }, 38 + "carryRefs": { 39 + "type": "array", 40 + "maxLength": 20, 41 + "items": { 42 + "type": "ref", 43 + "ref": "#carryCrossRef" 44 + }, 45 + "description": "Carry entities cross-referenced in this analysis. Carry stays local-first — only summaries are included unless explicitly published as companion records." 46 + }, 47 + "createdAt": { 48 + "type": "string", 49 + "format": "datetime", 50 + "description": "When this synthesis was created." 51 + }, 52 + "updatedAt": { 53 + "type": "string", 54 + "format": "datetime", 55 + "description": "When this synthesis was last updated." 56 + } 57 + } 58 + } 59 + }, 60 + "sourceRef": { 61 + "type": "object", 62 + "description": "Reference to the source/centroid for this island.", 63 + "required": ["uri"], 64 + "properties": { 65 + "uri": { 66 + "type": "string", 67 + "format": "uri", 68 + "description": "Canonical HTTPS URL of the island centroid." 69 + }, 70 + "cid": { 71 + "type": "string", 72 + "description": "CID of the source record at time of analysis." 73 + }, 74 + "collection": { 75 + "type": "string", 76 + "description": "Collection being analyzed. For islands over connection graphs this is usually network.cosmik.connection." 77 + } 78 + } 79 + }, 80 + "structuredAnalysis": { 81 + "type": "object", 82 + "description": "The structured output of a Strata analysis run.", 83 + "required": ["themes"], 84 + "properties": { 85 + "title": { 86 + "type": "string", 87 + "maxGraphemes": 300, 88 + "description": "A concise sentence capturing the core argument or narrative arc of this analysis." 89 + }, 90 + "themes": { 91 + "type": "array", 92 + "maxLength": 20, 93 + "items": { 94 + "type": "string", 95 + "maxGraphemes": 100 96 + }, 97 + "description": "Key themes identified in the source." 98 + }, 99 + "highlights": { 100 + "type": "array", 101 + "maxLength": 20, 102 + "items": { 103 + "type": "string", 104 + "format": "at-uri" 105 + }, 106 + "description": "AT URIs of network.cosmik.connection records flagged as semantically significant by the analysis. Hot edges." 107 + }, 108 + "tensions": { 109 + "type": "array", 110 + "maxLength": 10, 111 + "items": { 112 + "type": "string", 113 + "maxGraphemes": 500 114 + }, 115 + "description": "Tensions or contradictions with existing claims." 116 + }, 117 + "openQuestions": { 118 + "type": "array", 119 + "maxLength": 10, 120 + "items": { 121 + "type": "string", 122 + "maxGraphemes": 500 123 + }, 124 + "description": "Questions raised by the analysis." 125 + }, 126 + "synthesis": { 127 + "type": "string", 128 + "maxGraphemes": 2000, 129 + "maxLength": 20000, 130 + "description": "Prose synthesis connecting the source to existing knowledge. Legacy plain-text format." 131 + }, 132 + "synthesisDoc": { 133 + "type": "unknown", 134 + "description": "Rich-text synthesis as a pub.oxa.document record. Takes precedence over the plain-text synthesis field when present." 135 + } 136 + } 137 + }, 138 + "carryCrossRef": { 139 + "type": "object", 140 + "description": "Cross-reference to a carry entity, translated to AT Protocol addressable format.", 141 + "required": ["entityType", "atUri"], 142 + "properties": { 143 + "entityType": { 144 + "type": "string", 145 + "knownValues": [ 146 + "org.latha.island.defs#claimRef", 147 + "org.latha.island.defs#sourceRef" 148 + ], 149 + "description": "Type of carry entity." 150 + }, 151 + "carryId": { 152 + "type": "string", 153 + "description": "Original carry entity ID for local correlation." 154 + }, 155 + "atUri": { 156 + "type": "string", 157 + "format": "at-uri", 158 + "description": "AT Protocol URI for this carry entity (if published as companion record)." 159 + }, 160 + "summary": { 161 + "type": "string", 162 + "maxGraphemes": 500, 163 + "description": "Summary of the carry entity." 164 + } 165 + } 166 + } 167 + } 168 + }
+5 -5
lexicons/generated/index.ts
··· 24 24 import _19 from "./org/latha/island/getCursor.json"; 25 25 import _20 from "./org/latha/island/getOverview.json"; 26 26 import _21 from "./org/latha/island/getProfile.json"; 27 - import _22 from "./org/latha/island/notifyOfUpdate.json"; 28 - import _23 from "./org/latha/island/reasoning/getRecord.json"; 29 - import _24 from "./org/latha/island/reasoning/listRecords.json"; 30 - import _25 from "./org/latha/island/strata/getRecord.json"; 31 - import _26 from "./org/latha/island/strata/listRecords.json"; 27 + import _22 from "./org/latha/island/island/getRecord.json"; 28 + import _23 from "./org/latha/island/island/listRecords.json"; 29 + import _24 from "./org/latha/island/notifyOfUpdate.json"; 30 + import _25 from "./org/latha/island/reasoning/getRecord.json"; 31 + import _26 from "./org/latha/island/reasoning/listRecords.json"; 32 32 33 33 export const lexicons: object[] = [_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26];
+3 -3
lexicons/generated/org/latha/island/authFull.json
··· 27 27 "org.latha.island.getCursor", 28 28 "org.latha.island.getOverview", 29 29 "org.latha.island.getProfile", 30 + "org.latha.island.island.getRecord", 31 + "org.latha.island.island.listRecords", 30 32 "org.latha.island.notifyOfUpdate", 31 33 "org.latha.island.reasoning.getRecord", 32 - "org.latha.island.reasoning.listRecords", 33 - "org.latha.island.strata.getRecord", 34 - "org.latha.island.strata.listRecords" 34 + "org.latha.island.reasoning.listRecords" 35 35 ] 36 36 }, 37 37 {
+1 -1
lexicons/generated/org/latha/island/strata/getRecord.json lexicons/generated/org/latha/island/island/getRecord.json
··· 1 1 { 2 2 "lexicon": 1, 3 - "id": "org.latha.island.strata.getRecord", 3 + "id": "org.latha.island.island.getRecord", 4 4 "defs": { 5 5 "main": { 6 6 "type": "query",
+1 -1
lexicons/generated/org/latha/island/strata/listRecords.json lexicons/generated/org/latha/island/island/listRecords.json
··· 1 1 { 2 2 "lexicon": 1, 3 - "id": "org.latha.island.strata.listRecords", 3 + "id": "org.latha.island.island.listRecords", 4 4 "defs": { 5 5 "main": { 6 6 "type": "query",
+27 -5
lexicons/network/cosmik/connection.json
··· 1 1 { 2 2 "lexicon": 1, 3 3 "id": "network.cosmik.connection", 4 - "description": "A directed connection between two resources (URLs or AT URIs). The fundamental primitive of the Stigmergic knowledge graph.", 4 + "description": "A directed connection between two canonical HTTPS resources. The fundamental primitive of the Stigmergic knowledge graph.", 5 5 "defs": { 6 6 "main": { 7 7 "type": "record", 8 - "description": "A connection linking a source to a target, with optional type and note. Connections are the morphisms of the knowledge graph -- objects (cards, collections, URLs) emerge from their connections rather than being created first.", 8 + "description": "A connection linking a source URL to a target URL, with optional type and note. Connections are the morphisms of the knowledge graph; island records point to these records rather than embedding edge data.", 9 9 "key": "tid", 10 10 "record": { 11 11 "type": "object", ··· 13 13 "properties": { 14 14 "source": { 15 15 "type": "string", 16 - "description": "Source resource -- a URL or AT URI." 16 + "format": "uri", 17 + "description": "Canonical HTTPS source resource URL. Do not use an AT URI as an island graph endpoint." 17 18 }, 18 19 "target": { 19 20 "type": "string", 20 - "description": "Target resource -- a URL or AT URI." 21 + "format": "uri", 22 + "description": "Canonical HTTPS target resource URL. Do not use an AT URI as an island graph endpoint." 21 23 }, 22 24 "connectionType": { 23 25 "type": "string", 24 26 "knownValues": [ 25 27 "network.cosmik.connection#contains", 26 28 "network.cosmik.connection#cites", 29 + "network.cosmik.connection#related", 27 30 "network.cosmik.connection#relates", 28 31 "network.cosmik.connection#inspired", 29 32 "network.cosmik.connection#contradicts", 30 33 "network.cosmik.connection#extends", 31 34 "network.cosmik.connection#forks", 32 - "network.cosmik.connection#annotates" 35 + "network.cosmik.connection#annotates", 36 + "network.cosmik.connection#compares", 37 + "network.cosmik.connection#grounds", 38 + "network.cosmik.connection#coauthor" 33 39 ], 34 40 "description": "Type of connection. Open set -- new types can be added without schema changes." 35 41 }, ··· 63 69 "type": "token", 64 70 "description": "General relation between two resources." 65 71 }, 72 + "related": { 73 + "type": "token", 74 + "description": "General relation between two resources. Preferred spelling for new records." 75 + }, 66 76 "inspired": { 67 77 "type": "token", 68 78 "description": "Creative lineage -- source was inspired by target." ··· 82 92 "annotates": { 83 93 "type": "token", 84 94 "description": "Source annotates target -- marginalia, commentary, or highlight." 95 + }, 96 + "compares": { 97 + "type": "token", 98 + "description": "Source is compared with target." 99 + }, 100 + "grounds": { 101 + "type": "token", 102 + "description": "Source is grounded in target -- target provides the standard, evidence, or foundation for source." 103 + }, 104 + "coauthor": { 105 + "type": "token", 106 + "description": "Source and target share authorship or direct collaboration context." 85 107 } 86 108 } 87 109 }
+2 -2
lexicons/org/latha/island/analyze.json
··· 131 131 "connections": { 132 132 "type": "array", 133 133 "items": { 134 - "type": "ref", 135 - "ref": "org.latha.island#connection" 134 + "type": "string", 135 + "format": "at-uri" 136 136 } 137 137 }, 138 138 "tensions": {
+19 -24
lexicons/org/latha/island/island.json
··· 1 1 { 2 2 "lexicon": 1, 3 3 "id": "org.latha.island", 4 - "description": "A structured synthesis of an external source, cross-referenced with local knowledge (carry + vault). The stigmergic signal — a pheromone trail others can discover and build on.", 4 + "description": "A structured synthesis over a graph component. The island record points to actual network.cosmik.connection records; it does not embed edge data.", 5 5 "defs": { 6 6 "main": { 7 7 "type": "record", 8 - "description": "A Strata synthesis record. Produced when a user runs Strata analysis on a Semble card (or any AT Protocol record). Cross-references the source with the user's vault and carry data, producing themes, connections, tensions, and a prose synthesis.", 8 + "description": "A Stigmergic island: a centroid URL, pointers to network.cosmik.connection records, and analysis of the resulting graph component.", 9 9 "key": "tid", 10 10 "record": { 11 11 "type": "object", 12 - "required": ["source", "analysis", "createdAt"], 12 + "required": ["source", "connections", "analysis", "createdAt"], 13 13 "properties": { 14 + "forkOf": { 15 + "type": "string", 16 + "format": "at-uri", 17 + "description": "Original org.latha.island AT URI when this record is a fork/update of another user's island." 18 + }, 14 19 "source": { 15 20 "type": "ref", 16 21 "ref": "#sourceRef", 17 - "description": "The source record that triggered this analysis. For islands, this is the lexmin vertex." 22 + "description": "The island centroid/canonical source URL." 18 23 }, 19 24 "connections": { 20 25 "type": "array", 21 26 "maxLength": 500, 22 27 "items": { 23 - "type": "ref", 24 - "ref": "#edge" 28 + "type": "string", 29 + "format": "at-uri" 25 30 }, 26 - "description": "The island's edges — connection records with their graph structure embedded. No lookups needed to render." 31 + "description": "AT URIs of actual network.cosmik.connection records. The island does not embed edge source/target/type/note data." 27 32 }, 28 33 "analysis": { 29 34 "type": "ref", ··· 52 57 } 53 58 } 54 59 }, 55 - "edge": { 56 - "type": "object", 57 - "description": "A reference to a connection record within the island. The AT URI is the source of truth — all edge data lives in the network.cosmik.connection record.", 58 - "required": ["uri"], 59 - "properties": { 60 - "uri": { 61 - "type": "string", 62 - "format": "at-uri", 63 - "description": "AT URI of the network.cosmik.connection record" 64 - } 65 - } 66 - }, 67 60 "sourceRef": { 68 61 "type": "object", 69 - "description": "Reference to the source record that triggered the analysis.", 62 + "description": "Reference to the source/centroid for this island.", 70 63 "required": ["uri"], 71 64 "properties": { 72 65 "uri": { 73 66 "type": "string", 74 - "description": "URI of the source. For island-level analysis, this is the lexmin vertex (canonical component reference) — can be an HTTP URL or AT URI." 67 + "format": "uri", 68 + "description": "Canonical HTTPS URL of the island centroid." 75 69 }, 76 70 "cid": { 77 71 "type": "string", ··· 79 73 }, 80 74 "collection": { 81 75 "type": "string", 82 - "description": "NSID of the source collection (e.g. network.cosmik.card)." 76 + "description": "Collection being analyzed. For islands over connection graphs this is usually network.cosmik.connection." 83 77 } 84 78 } 85 79 }, ··· 136 130 "description": "Prose synthesis connecting the source to existing knowledge. Legacy plain-text format." 137 131 }, 138 132 "synthesisDoc": { 139 - "type": "unknown", 140 - "description": "Rich-text synthesis as a pub.oxa.document record. Takes precedence over the plain-text synthesis field when present." 133 + "type": "string", 134 + "format": "at-uri", 135 + "description": "AT URI of a pub.oxa.document record containing the rich-text synthesis. Takes precedence over the plain-text synthesis field when present." 141 136 } 142 137 } 143 138 },
+84 -21
lexicons/pulled/network/cosmik/connection.json
··· 1 1 { 2 + "lexicon": 1, 2 3 "id": "network.cosmik.connection", 4 + "description": "A directed connection between two canonical HTTPS resources. The fundamental primitive of the Stigmergic knowledge graph.", 3 5 "defs": { 4 6 "main": { 7 + "type": "record", 8 + "description": "A connection linking a source URL to a target URL, with optional type and note. Connections are the morphisms of the knowledge graph; island records point to these records rather than embedding edge data.", 5 9 "key": "tid", 6 - "type": "record", 7 10 "record": { 8 11 "type": "object", 9 - "required": ["source", "target"], 12 + "required": ["source", "target", "createdAt"], 10 13 "properties": { 11 - "note": { 14 + "source": { 15 + "type": "string", 16 + "format": "uri", 17 + "description": "Canonical HTTPS source resource URL. Do not use an AT URI as an island graph endpoint." 18 + }, 19 + "target": { 12 20 "type": "string", 13 - "maxLength": 1000, 14 - "description": "Optional note about the connection" 21 + "format": "uri", 22 + "description": "Canonical HTTPS target resource URL. Do not use an AT URI as an island graph endpoint." 15 23 }, 16 - "source": { 24 + "connectionType": { 17 25 "type": "string", 18 - "description": "Source entity (URL string or AT URI)" 26 + "knownValues": [ 27 + "network.cosmik.connection#contains", 28 + "network.cosmik.connection#cites", 29 + "network.cosmik.connection#related", 30 + "network.cosmik.connection#relates", 31 + "network.cosmik.connection#inspired", 32 + "network.cosmik.connection#contradicts", 33 + "network.cosmik.connection#extends", 34 + "network.cosmik.connection#forks", 35 + "network.cosmik.connection#annotates", 36 + "network.cosmik.connection#compares", 37 + "network.cosmik.connection#grounds", 38 + "network.cosmik.connection#coauthor" 39 + ], 40 + "description": "Type of connection. Open set -- new types can be added without schema changes." 19 41 }, 20 - "target": { 42 + "note": { 21 43 "type": "string", 22 - "description": "Target entity (URL string or AT URI)" 44 + "maxGraphemes": 1000, 45 + "description": "Optional note about the connection." 23 46 }, 24 47 "createdAt": { 25 48 "type": "string", 26 49 "format": "datetime", 27 - "description": "Timestamp when this connection was created." 50 + "description": "When this connection was created." 28 51 }, 29 52 "updatedAt": { 30 53 "type": "string", 31 54 "format": "datetime", 32 - "description": "Timestamp when this connection was last updated." 33 - }, 34 - "connectionType": { 35 - "type": "string", 36 - "description": "Optional type of connection" 55 + "description": "When this connection was last updated." 37 56 } 38 57 } 39 - }, 40 - "description": "A connection linking a source to a target, with optional type and note." 58 + } 59 + }, 60 + "contains": { 61 + "type": "token", 62 + "description": "Collection contains card. Source is the collection AT URI, target is the card AT URI or URL." 63 + }, 64 + "cites": { 65 + "type": "token", 66 + "description": "Card cites a URL or reference. Source is the card AT URI, target is the cited URL." 67 + }, 68 + "relates": { 69 + "type": "token", 70 + "description": "General relation between two resources." 71 + }, 72 + "related": { 73 + "type": "token", 74 + "description": "General relation between two resources. Preferred spelling for new records." 75 + }, 76 + "inspired": { 77 + "type": "token", 78 + "description": "Creative lineage -- source was inspired by target." 79 + }, 80 + "contradicts": { 81 + "type": "token", 82 + "description": "Source contradicts target -- opposing evidence or argument." 83 + }, 84 + "extends": { 85 + "type": "token", 86 + "description": "Source builds on target -- extension or elaboration." 87 + }, 88 + "forks": { 89 + "type": "token", 90 + "description": "Source diverges from target -- a different direction from the same origin." 91 + }, 92 + "annotates": { 93 + "type": "token", 94 + "description": "Source annotates target -- marginalia, commentary, or highlight." 95 + }, 96 + "compares": { 97 + "type": "token", 98 + "description": "Source is compared with target." 99 + }, 100 + "grounds": { 101 + "type": "token", 102 + "description": "Source is grounded in target -- target provides the standard, evidence, or foundation for source." 103 + }, 104 + "coauthor": { 105 + "type": "token", 106 + "description": "Source and target share authorship or direct collaboration context." 41 107 } 42 - }, 43 - "$type": "com.atproto.lexicon.schema", 44 - "lexicon": 1, 45 - "description": "A record representing a connection between two entities (URLs or cards)." 108 + } 46 109 }
+2 -1
package.json
··· 18 18 "island:cleanup": "bun run src/island-cleanup.ts", 19 19 "vault:analyze": "bun run src/vault-analyze.ts", 20 20 "synthesis:to-oxa": "bun run src/synthesis-to-oxa.ts", 21 - "rdf:link-cards": "bun run src/rdf-link-cards.ts" 21 + "rdf:link-cards": "bun run src/rdf-link-cards.ts", 22 + "island:create": "bun run src/island-create.ts" 22 23 }, 23 24 "dependencies": { 24 25 "@atmo-dev/contrail": "^0.6.0",
+377 -42
src/frontend/app.ts
··· 42 42 recordUri?: string | null; 43 43 rawRecord?: any; 44 44 handle?: string | null; 45 + edgeCount?: number; 46 + vertexCount?: number; 47 + similarIslands?: SimilarIsland[]; 48 + } 49 + 50 + interface SimilarIsland { 51 + id: string; 52 + recordUri: string; 53 + handle?: string | null; 54 + title?: string | null; 55 + centroid?: string | null; 56 + sharedCount: number; 57 + vertexCount: number; 58 + edgeCount: number; 59 + sharedVertices: string[]; 60 + sharedDomains?: string[]; 61 + sharedDomainCount?: number; 45 62 } 46 63 47 64 interface StrataData { ··· 245 262 }; 246 263 let currentPage: "explore" | "island" | "strata" | "connect" | "search" = "explore"; 247 264 let islands: Island[] = []; 265 + let explorePage = 1; 266 + let exploreTotal = 0; 267 + let explorePageSize = 10; 248 268 249 269 // ── XRPC helpers ─────────────────────────────────────────────────────────── 250 270 ··· 255 275 throw new Error(`API ${path} failed: ${res.status} ${err}`); 256 276 } 257 277 return res.json(); 278 + } 279 + 280 + async function fetchSimilarIslands(recordUri: string): Promise<void> { 281 + const container = document.getElementById("similar-islands"); 282 + const countEl = document.getElementById("similar-count"); 283 + if (!container) return; 284 + 285 + try { 286 + const data = await apiGet<{ similar: SimilarIsland[] }>(`/api/similar-islands?uri=${encodeURIComponent(recordUri)}`); 287 + const similar = data.similar || []; 288 + if (similar.length === 0) { 289 + container.innerHTML = '<div class="empty">No similar islands found.</div>'; 290 + return; 291 + } 292 + if (countEl) countEl.textContent = `${similar.length} by shared URLs`; 293 + container.innerHTML = similar.map(sim => { 294 + const href = islandPath({ id: sim.id, recordUri: sim.recordUri, handle: sim.handle || undefined, summary: sim.title || null, strata: null }); 295 + const title = sim.title || sim.centroid || sim.id; 296 + const sharedLabel = sim.sharedVertices.length 297 + ? sim.sharedVertices.map(v => escHtml(canonicalUrlLabel(v))).join(" · ") 298 + : (sim.sharedDomains || []).map(d => escHtml(d)).join(" · "); 299 + const repoLabel = sim.handle || sim.recordUri.split("/")[2] || "unknown"; 300 + return `<a class="similar-island" href="${escHtml(href)}"> 301 + <span class="similar-title">${escHtml(title)}</span> 302 + <span class="similar-record">${escHtml(repoLabel)} / ${escHtml(sim.id)}</span> 303 + <span class="similar-meta">${sim.sharedCount} shared URLs${sim.sharedDomainCount ? ` &middot; ${sim.sharedDomainCount} shared hosts` : ""} &middot; ${sim.vertexCount} nodes &middot; ${sim.edgeCount} edges</span> 304 + <span class="similar-shared">${sharedLabel}</span> 305 + </a>`; 306 + }).join(""); 307 + } catch { 308 + container.innerHTML = '<div class="empty">Could not load similar islands.</div>'; 309 + } 258 310 } 259 311 260 312 // ── Force-directed graph renderer ────────────────────────────────────────── ··· 629 681 if (island.recordUri) { 630 682 const parts = island.recordUri.split("/"); 631 683 const rkey = parts[parts.length - 1]; 684 + const collection = parts[3] || "org.latha.island"; 632 685 const repo = island.handle || parts[2]; 633 - return `/island/${repo}/${rkey}`; 686 + const query = collection !== "org.latha.island" ? `?collection=${encodeURIComponent(collection)}` : ""; 687 + return `/island/${repo}/${rkey}${query}`; 634 688 } 635 689 // Fallback to hash 636 690 return `/island/${encodeURIComponent(island.islandHash || island.id)}`; ··· 738 792 return; 739 793 } 740 794 795 + const totalPages = Math.max(1, Math.ceil(exploreTotal / explorePageSize)); 796 + const hasNext = (explorePage * explorePageSize) < exploreTotal; 797 + const hasPrev = explorePage > 1; 798 + 741 799 content.innerHTML = ` 742 800 <div class="islands-feed"> 743 801 ${islands.map(island => renderIslandCard(island)).join("")} 744 802 </div> 803 + <nav class="pagination"> 804 + <a class="pagination-btn ${hasPrev ? "" : "disabled"}" href="/?page=${explorePage - 1}">&larr; Prev</a> 805 + <div class="pagination-info"> 806 + <span class="pagination-page">Page ${explorePage} of ${totalPages}</span> 807 + <span class="pagination-total">${exploreTotal} islands</span> 808 + </div> 809 + <a class="pagination-btn ${hasNext ? "" : "disabled"}" href="/?page=${explorePage + 1}">Next &rarr;</a> 810 + </nav> 745 811 `; 746 812 747 813 // Initialize graphs (only for islands with full data) ··· 800 866 function renderIslandCard(island: Island): string { 801 867 const summary = summarizeIsland(island); 802 868 const hasStrata = !!island.strata; 803 - const strataBtnLabel = hasStrata ? "View Strata" : "Strata"; 804 - const strataBtnClass = hasStrata ? "strata-btn island-strata-btn has-strata" : "strata-btn island-strata-btn"; 869 + const strataBtnLabel = "View Island"; 870 + const strataBtnClass = "strata-btn island-strata-btn"; 805 871 const hasFullData = !!(island.vertices && island.edges); 806 - const sizeLabel = hasFullData 807 - ? `${island.vertices!.length} nodes &middot; ${island.edges!.length} edges` 808 - : ""; 872 + const hasGraphSvg = !!(island as any).graphSvgUrl; 873 + const href = escHtml(islandPath(island)); 874 + const sizeLabel = island.edgeCount ? `${island.edgeCount} edges` : ""; 875 + const themes = island.strata?.themes || []; 876 + const title = island.title || island.summary || summary; 809 877 810 - const graphOrTitle = hasFullData 878 + const graphOrTitle = hasGraphSvg 879 + ? `<a class="island-graph-link" href="${href}"><img class="island-graph-img" src="${escHtml((island as any).graphSvgUrl)}" alt="Graph" loading="lazy" width="400" height="300"/></a>` 880 + : hasFullData 811 881 ? `<canvas class="island-graph" data-island-id="${escHtml(island.id)}"></canvas>` 812 - : `<div class="island-title-preview">${escHtml(summary)}</div>`; 882 + : `<a class="island-title-preview" href="${href}">${escHtml(summary)}</a>`; 813 883 814 884 return ` 815 885 <div class="island-card" data-island-id="${escHtml(island.id)}"> 816 - ${graphOrTitle} 886 + <div class="island-card-graph"> 887 + ${graphOrTitle} 888 + ${title ? `<div class="island-card-overlay"><a href="${href}">${escHtml(title)}</a></div>` : ""} 889 + </div> 817 890 <div class="island-card-footer"> 818 - ${sizeLabel ? `<span class="island-size">${sizeLabel}</span>` : ""} 819 - ${hasFullData ? `<p class="island-summary">${escHtml(summary)}</p>` : ""} 820 - <button class="${strataBtnClass}" data-island-id="${escHtml(island.id)}">${strataBtnLabel}</button> 891 + <div class="island-card-meta"> 892 + ${themes.length ? `<div class="theme-tags island-card-themes">${themes.slice(0, 4).map(t => `<span class="theme-tag">${escHtml(t)}</span>`).join("")}</div>` : ""} 893 + ${sizeLabel ? `<span class="island-size">${sizeLabel}</span>` : ""} 894 + </div> 895 + <a class="${strataBtnClass}" data-island-id="${escHtml(island.id)}" href="${href}">${strataBtnLabel}</a> 821 896 </div> 822 897 </div> 823 898 `; ··· 901 976 // ── Strata page — prose with arrows ─────────────────────────────────────── 902 977 903 978 async function showStrataByRecord(repo: string, rkey: string): Promise<void> { 904 - const atUri = `at://${repo}/org.latha.island/${rkey}`; 979 + const collection = new URLSearchParams(window.location.search).get("collection") || "org.latha.island"; 980 + const atUri = `at://${repo}/${collection}/${rkey}`; 905 981 currentPage = "strata"; 906 982 const content = document.getElementById("page-content"); 907 983 if (!content) return; ··· 914 990 } 915 991 916 992 try { 917 - const data = await apiGet<{ island: Island; recordUri: string }>(`/xrpc/org.latha.island.getIsland?id=${encodeURIComponent(atUri)}`); 993 + const data = await apiGet<{ island: Island; recordUri: string }>(`/xrpc/org.latha.island.getRecord?uri=${encodeURIComponent(atUri)}`); 918 994 await renderStrataPage(data.island, data.recordUri); 919 995 } catch { 920 996 content.innerHTML = '<div class="empty">Island record not found.</div>'; ··· 958 1034 } 959 1035 960 1036 renderStrata(island); 1037 + 1038 + // Fetch similar islands asynchronously (don't block page render) 1039 + if (island.recordUri) { 1040 + fetchSimilarIslands(island.recordUri); 1041 + } 961 1042 } 962 1043 963 1044 async function showStrata(islandId: string): Promise<void> { ··· 1016 1097 s.highlights.some(h => e.source.includes(h) || e.target.includes(h) || h.includes(e.source) || h.includes(e.target)) 1017 1098 ); 1018 1099 1100 + // Check schema conformance asynchronously 1101 + if (island.recordUri) { 1102 + fetch(`/xrpc/org.latha.island.validateRecord?uri=${encodeURIComponent(island.recordUri)}`) 1103 + .then(r => r.json()) 1104 + .then((data: { valid: boolean; issues: Array<{ field: string; issue: string; detail?: string }> }) => { 1105 + if (!data.valid && data.issues.length > 0) { 1106 + const banner = document.getElementById("schema-banner"); 1107 + if (banner) { 1108 + const issueList = data.issues.map(i => `<li>${escHtml(i.detail || i.issue)}</li>`).join(""); 1109 + banner.innerHTML = `<strong>Schema migration needed</strong> — This island's record doesn't conform to the <code>org.latha.island</code> lexicon.<ul>${issueList}</ul><a href="https://pdsls.dev/${island.recordUri}#record" target="_blank" rel="noopener" class="pds-link">View record on PDS &nearr;</a>`; 1110 + banner.classList.remove("hidden"); 1111 + } 1112 + } 1113 + }) 1114 + .catch(() => { /* non-critical */ }); 1115 + } 1116 + 1019 1117 // Build strata page. Editing is toggled explicitly so links remain clickable by default. 1020 1118 content.innerHTML = ` 1021 1119 <div class="strata-page" id="strata-page"> 1022 1120 <a href="/" class="back-link">&larr; Explore</a> 1121 + <div id="schema-banner" class="schema-migration-banner hidden"></div> 1023 1122 <div class="edit-mode-banner">Edit mode is on. Links are editable now; toggle Edit off to open them normally.</div> 1024 1123 <p class="island-summary-large editable" data-field="title" spellcheck="false">${escHtml(summary)}</p> 1025 1124 <p class="island-meta">${island.vertices!.length} vertices &middot; ${island.edges!.length} edges${island.recordUri ? ` &middot; <a href="https://pdsls.dev/${island.recordUri}#record" target="_blank" rel="noopener" class="pds-link">View on PDS &nearr;</a>` : ""}</p> ··· 1028 1127 <div class="graph-actions"><button class="edit-toggle hidden" id="edit-toggle" type="button" aria-pressed="false"><span aria-hidden="true">&#9998;</span><span>Edit</span></button></div> 1029 1128 1030 1129 <div class="strata-section"> 1031 - <h3>Synthesis</h3> 1130 + <h3>Synthesis${island.handle ? ` <a href="https://science.latha.org/doc/${escHtml(island.handle)}/${escHtml(island.id)}" target="_blank" rel="noopener" class="pds-link section-link">View oxa document &nearr;</a>` : ""}</h3> 1032 1131 <div class="synthesis-text editable" data-field="synthesis" spellcheck="false">${s.synthesisDoc ? oxaDocToHtml(s.synthesisDoc) : mystToHtml(summary || "Synthesis", s.synthesis)}</div> 1033 1132 </div> 1034 1133 ··· 1082 1181 }).join("") || `<div class="empty">No highlighted edges matched the current edge URIs.</div>`}</div> 1083 1182 </div>` : ""} 1084 1183 1184 + <div class="strata-section" id="similar-islands-section"> 1185 + <h3>Similar Islands <span class="highlights-count" id="similar-count"></span></h3> 1186 + <div class="similar-islands" id="similar-islands"><div class="empty">Loading…</div></div> 1187 + </div> 1188 + 1085 1189 <div class="strata-section"> 1086 1190 <h3>Edges</h3> 1087 - <div class="strata-edges">${island.edges!.map(e => { 1191 + <div class="strata-edges" id="edge-list">${island.edges!.map((e, i) => { 1088 1192 const sourceMeta = island.vertexMeta![e.source]; 1089 1193 const targetMeta = island.vertexMeta![e.target]; 1090 1194 const sourceTitle = sourceMeta?.title || truncateUri(e.source); ··· 1092 1196 const type = connectionTypeLabel(e.connectionType); 1093 1197 const icon = connectionTypeIcon(e.connectionType); 1094 1198 const seemsUrl = e.source.startsWith("http") ? `https://semble.so/url/${encodeURIComponent(e.source)}` : ""; 1095 - return `<a href="${escHtml(seemsUrl)}" class="strata-edge" target="_blank" rel="noopener"${!seemsUrl ? ' onclick="event.preventDefault()"' : ""}> 1096 - <span class="strata-edge-node"><span class="strata-edge-title">${escHtml(sourceTitle)}</span><span class="strata-edge-url">${escHtml(canonicalUrlLabel(e.source))}</span></span> 1097 - <span class="strata-edge-type">${icon} ${escHtml(type)}</span> 1098 - <span class="strata-edge-node"><span class="strata-edge-title">${escHtml(targetTitle)}</span><span class="strata-edge-url">${escHtml(canonicalUrlLabel(e.target))}</span></span> 1099 - ${e.note ? `<span class="strata-edge-note">${escHtml(e.note)}</span>` : ""} 1100 - ${e.handle ? `<span class="strata-edge-handle">@${escHtml(e.handle)}</span>` : ""} 1101 - </a>`; 1199 + return `<div class="strata-edge-row" data-edge-idx="${i}"> 1200 + <span class="edge-remove hidden" title="Remove edge">&times;</span> 1201 + <a href="${escHtml(seemsUrl)}" class="strata-edge" target="_blank" rel="noopener"${!seemsUrl ? ' onclick="event.preventDefault()"' : ""}> 1202 + <span class="strata-edge-node"><span class="strata-edge-title">${escHtml(sourceTitle)}</span><span class="strata-edge-url">${escHtml(canonicalUrlLabel(e.source))}</span></span> 1203 + <span class="strata-edge-type">${icon} ${escHtml(type)}</span> 1204 + <span class="strata-edge-node"><span class="strata-edge-title">${escHtml(targetTitle)}</span><span class="strata-edge-url">${escHtml(canonicalUrlLabel(e.target))}</span></span> 1205 + ${e.note ? `<span class="strata-edge-note">${escHtml(e.note)}</span>` : ""} 1206 + ${e.handle ? `<span class="strata-edge-handle">@${escHtml(e.handle)}</span>` : ""} 1207 + </a> 1208 + </div>`; 1102 1209 }).join("")}</div> 1210 + <div class="edge-add-form hidden" id="edge-add-form"> 1211 + <div class="form-field"> 1212 + <label>Source</label> 1213 + <input type="text" id="new-edge-source" placeholder="https://..." class="edge-input"> 1214 + </div> 1215 + <div class="form-field"> 1216 + <label>Target</label> 1217 + <input type="text" id="new-edge-target" placeholder="https://..." class="edge-input"> 1218 + </div> 1219 + <div class="form-field"> 1220 + <label>Type</label> 1221 + <select id="new-edge-type" class="edge-input"> 1222 + <option value="network.cosmik.connection#related">related</option> 1223 + <option value="network.cosmik.connection#cites">cites</option> 1224 + <option value="network.cosmik.connection#coauthor">coauthor</option> 1225 + <option value="network.cosmik.connection#inspired">inspired</option> 1226 + <option value="network.cosmik.connection#contradicts">contradicts</option> 1227 + <option value="network.cosmik.connection#extends">extends</option> 1228 + <option value="network.cosmik.connection#annotates">annotates</option> 1229 + </select> 1230 + </div> 1231 + <div class="form-field"> 1232 + <label>Note</label> 1233 + <input type="text" id="new-edge-note" placeholder="Why this edge?" class="edge-input"> 1234 + </div> 1235 + <button type="button" class="action-btn" id="edge-add-btn">Add edge</button> 1236 + </div> 1237 + <button type="button" class="edge-add-toggle hidden" id="edge-add-toggle">+ Add edge</button> 1103 1238 </div> 1104 1239 </div> 1105 1240 `; ··· 1137 1272 el.setAttribute("contenteditable", enabled ? "true" : "false"); 1138 1273 el.setAttribute("spellcheck", "false"); 1139 1274 }); 1275 + // Show/hide edge controls 1276 + editContainer.querySelectorAll<HTMLElement>(".edge-remove").forEach(el => { 1277 + el.classList.toggle("hidden", !enabled); 1278 + }); 1279 + const edgeAddToggle = document.getElementById("edge-add-toggle"); 1280 + if (edgeAddToggle) edgeAddToggle.classList.toggle("hidden", !enabled); 1281 + if (!enabled) { 1282 + document.getElementById("edge-add-form")?.classList.add("hidden"); 1283 + } 1140 1284 } 1141 1285 1142 1286 editToggle?.addEventListener("click", () => setEditMode(!editMode)); ··· 1189 1333 }); 1190 1334 }); 1191 1335 1336 + // Edge removal 1337 + editContainer.querySelectorAll(".edge-remove").forEach(btn => { 1338 + btn.addEventListener("click", () => { 1339 + (btn as HTMLElement).closest(".strata-edge-row")?.remove(); 1340 + markDirty(); 1341 + }); 1342 + }); 1343 + 1344 + // Edge add toggle 1345 + const edgeAddToggle = document.getElementById("edge-add-toggle"); 1346 + const edgeAddForm = document.getElementById("edge-add-form"); 1347 + edgeAddToggle?.addEventListener("click", () => { 1348 + edgeAddForm?.classList.toggle("hidden"); 1349 + }); 1350 + 1351 + // Edge add submit 1352 + const edgeAddBtn = document.getElementById("edge-add-btn"); 1353 + edgeAddBtn?.addEventListener("click", () => { 1354 + const source = (document.getElementById("new-edge-source") as HTMLInputElement)?.value.trim(); 1355 + const target = (document.getElementById("new-edge-target") as HTMLInputElement)?.value.trim(); 1356 + const type = (document.getElementById("new-edge-type") as HTMLSelectElement)?.value; 1357 + const note = (document.getElementById("new-edge-note") as HTMLInputElement)?.value.trim(); 1358 + if (!source || !target) return; 1359 + 1360 + const edgeList = document.getElementById("edge-list"); 1361 + if (!edgeList) return; 1362 + const idx = edgeList.querySelectorAll(".strata-edge-row").length; 1363 + const row = document.createElement("div"); 1364 + row.className = "strata-edge-row"; 1365 + row.dataset.edgeIdx = String(idx); 1366 + row.dataset.newEdge = "true"; 1367 + row.dataset.source = source; 1368 + row.dataset.target = target; 1369 + row.dataset.connectionType = type; 1370 + row.dataset.note = note; 1371 + row.innerHTML = ` 1372 + <span class="edge-remove" title="Remove edge">&times;</span> 1373 + <a class="strata-edge" onclick="event.preventDefault()"> 1374 + <span class="strata-edge-node"><span class="strata-edge-title">${escHtml(truncateUri(source))}</span><span class="strata-edge-url">${escHtml(canonicalUrlLabel(source))}</span></span> 1375 + <span class="strata-edge-type">${connectionTypeIcon(type)} ${escHtml(connectionTypeLabel(type))}</span> 1376 + <span class="strata-edge-node"><span class="strata-edge-title">${escHtml(truncateUri(target))}</span><span class="strata-edge-url">${escHtml(canonicalUrlLabel(target))}</span></span> 1377 + ${note ? `<span class="strata-edge-note">${escHtml(note)}</span>` : ""} 1378 + </a>`; 1379 + row.querySelector(".edge-remove")!.addEventListener("click", () => { row.remove(); markDirty(); }); 1380 + edgeList.appendChild(row); 1381 + 1382 + // Clear form 1383 + (document.getElementById("new-edge-source") as HTMLInputElement).value = ""; 1384 + (document.getElementById("new-edge-target") as HTMLInputElement).value = ""; 1385 + (document.getElementById("new-edge-note") as HTMLInputElement).value = ""; 1386 + edgeAddForm?.classList.add("hidden"); 1387 + markDirty(); 1388 + }); 1389 + 1390 + function collectConnections(): any[] { 1391 + const edgeRows = editContainer.querySelectorAll(".strata-edge-row"); 1392 + const connections: any[] = []; 1393 + const origConnections = island.rawRecord?.connections || []; 1394 + edgeRows.forEach(row => { 1395 + const el = row as HTMLElement; 1396 + if (el.dataset.newEdge === "true") { 1397 + // New edge added via the form 1398 + connections.push({ 1399 + uri: `inline:${Date.now()}:${connections.length}`, 1400 + source: el.dataset.source || "", 1401 + target: el.dataset.target || "", 1402 + connectionType: el.dataset.connectionType || "network.cosmik.connection#related", 1403 + note: el.dataset.note || "", 1404 + }); 1405 + } else { 1406 + // Existing edge — use the original connection from rawRecord to preserve URI refs 1407 + const idx = parseInt(el.dataset.edgeIdx || "0"); 1408 + if (idx < origConnections.length) { 1409 + connections.push(origConnections[idx]); 1410 + } else { 1411 + // Fallback: use resolved edge data 1412 + const orig = island.edges?.[idx]; 1413 + if (orig) { 1414 + connections.push({ 1415 + uri: orig.uri || "", 1416 + source: orig.source, 1417 + target: orig.target, 1418 + connectionType: orig.connectionType, 1419 + note: orig.note || "", 1420 + }); 1421 + } 1422 + } 1423 + } 1424 + }); 1425 + return connections; 1426 + } 1427 + 1192 1428 function collectAnalysis(): Record<string, any> { 1193 1429 const titleEl = editContainer.querySelector('[data-field="title"]'); 1194 1430 const synthesisEl = editContainer.querySelector('[data-field="synthesis"]'); ··· 1211 1447 saveBtn.disabled = true; 1212 1448 try { 1213 1449 const [ownerDid, collection, rkey] = island.recordUri.replace("at://", "").split("/"); 1450 + const connections = collectConnections(); 1451 + const { connections: _oldConns, analysis: _oldAnalysis, ...restRecord } = (island.rawRecord || {}) as any; 1214 1452 const record = { 1215 - ...(island.rawRecord || {}), 1453 + ...restRecord, 1216 1454 $type: "org.latha.island", 1455 + connections, 1217 1456 analysis: { ...(island.rawRecord?.analysis || {}), ...collectAnalysis() }, 1218 1457 updatedAt: new Date().toISOString(), 1219 1458 } as any; 1220 - delete record.connections; 1221 - delete record.analysis.connections; 1222 1459 const isOwner = ownerDid === session.did; 1223 1460 if (!isOwner) record.forkOf = island.recordUri; 1224 - const endpoint = isOwner ? "/xrpc/com.atproto.repo.putRecord" : "/xrpc/com.atproto.repo.createRecord"; 1225 - const body = isOwner ? { repo: session.did, collection, rkey, record } : { repo: session.did, collection, record }; 1461 + const saveRkey = isOwner ? rkey : await computeIslandHash(island.centroid || record.source?.uri || ""); 1462 + const endpoint = "/xrpc/com.atproto.repo.putRecord"; 1463 + const body = { repo: session.did, collection, rkey: saveRkey, record }; 1226 1464 const res = await oauthSession.fetchHandler(endpoint, { 1227 1465 method: "POST", 1228 1466 headers: { "Content-Type": "application/json" }, ··· 1232 1470 const out = await res.json() as { uri?: string }; 1233 1471 dirty = false; 1234 1472 saveBtn.classList.add("hidden"); 1235 - saveStatus!.textContent = isOwner ? "Saved ✓" : `Forked to your PDS ✓ ${out.uri || ""}`; 1473 + const fallbackUri = `at://${session.did}/${collection}/${saveRkey}`; 1474 + saveStatus!.textContent = isOwner ? "Saved ✓" : `Forked to your PDS ✓ ${out.uri || fallbackUri}`; 1475 + const savedUri = isOwner ? (out.uri || island.recordUri) : (out.uri || fallbackUri); 1476 + if (savedUri) { 1477 + island.recordUri = savedUri; 1478 + island.rawRecord = record; 1479 + island.handle = isOwner ? island.handle : session.handle; 1480 + const nextPath = islandPath(island); 1481 + setTimeout(() => { 1482 + window.location.href = nextPath; 1483 + }, 250); 1484 + } 1236 1485 } catch (e: any) { 1237 1486 saveStatus!.textContent = `Error: ${e.message || e}`; 1238 1487 } finally { ··· 1322 1571 1323 1572 async function showExplore(): Promise<void> { 1324 1573 currentPage = "explore"; 1574 + const params = new URLSearchParams(window.location.search); 1575 + explorePage = Math.max(1, parseInt(params.get("page") || "1")); 1325 1576 1326 - // Try API first, fall back to server-rendered data 1327 - // Use summary mode — skip vertexMeta (saves ~250KB) 1328 - try { 1329 - const data = await apiGet<{ islands: Island[] }>("/xrpc/org.latha.island.getIslands?summary=true"); 1330 - islands = data.islands || []; 1331 - } catch { 1332 - const serverData = (window as any).__ISLANDS__; 1333 - if (serverData) islands = serverData; 1577 + // Use server-rendered summary data for instant first paint. The full API 1578 + // payload includes graph edges and is much larger; fetch it only for detail 1579 + // pages that need the graph. 1580 + const serverData = (window as any).__ISLANDS__; 1581 + if (serverData) { 1582 + if (Array.isArray(serverData)) { 1583 + islands = serverData; 1584 + exploreTotal = serverData.length; 1585 + explorePageSize = serverData.length || 10; 1586 + } else { 1587 + islands = serverData.islands || []; 1588 + explorePage = serverData.page || explorePage; 1589 + explorePageSize = serverData.pageSize || explorePageSize; 1590 + exploreTotal = serverData.total || islands.length; 1591 + } 1592 + } 1593 + else { 1594 + try { 1595 + const data = await apiGet<{ islands: Island[] }>(`/xrpc/org.latha.island.getIslands?summary=true&limit=${explorePageSize}`); 1596 + islands = data.islands || []; 1597 + exploreTotal = islands.length; 1598 + } catch {} 1334 1599 } 1335 1600 await enrichIslandHashes(islands); 1336 1601 ··· 1345 1610 title: string | null; 1346 1611 recordUri: string; 1347 1612 handle: string | null; 1348 - matchType: "exact" | "domain"; 1613 + matchType: "exact"; 1349 1614 matchedUrls: string[]; 1350 1615 nodeCount: number; 1351 1616 edgeCount: number; ··· 1368 1633 <div class="search-page"> 1369 1634 <a href="/" class="back-link">&larr; Explore</a> 1370 1635 <h2>Search Islands by URL</h2> 1371 - <p class="search-hint">Enter a URL to find islands containing that page or domain.</p> 1636 + <p class="search-hint">Enter a URL to find islands containing that exact page.</p> 1372 1637 <form id="search-form" class="search-form"> 1373 1638 <div class="form-field"> 1374 1639 <input type="text" id="search-url" name="url" value="${escHtml(initialUrl || "")}" placeholder="https://example.com/paper" required spellcheck="false" autocomplete="off"> ··· 1416 1681 if (!resultsDiv) return; 1417 1682 1418 1683 if (results.length === 0) { 1419 - resultsDiv.innerHTML = `<div class="empty">No islands found containing <code>${escHtml(queryUrl)}</code></div>`; 1684 + resultsDiv.innerHTML = ` 1685 + <div class="empty">No islands found containing <code>${escHtml(queryUrl)}</code></div> 1686 + <div class="search-create-island"> 1687 + <p class="search-create-hint">This URL isn't in any island yet. Start one by connecting it to related pages.</p> 1688 + <button type="button" id="create-island-btn" class="action-btn primary">Create island from this URL</button> 1689 + <div id="create-island-result" class="connect-result"></div> 1690 + </div>`; 1691 + document.getElementById("create-island-btn")?.addEventListener("click", () => createIslandFromUrl(queryUrl)); 1420 1692 return; 1421 1693 } 1422 1694 1423 1695 resultsDiv.innerHTML = results.map(r => { 1424 - const matchLabel = r.matchType === "exact" ? "exact match" : "domain match"; 1425 - const matchClass = r.matchType === "exact" ? "search-match-exact" : "search-match-domain"; 1696 + const matchLabel = "exact match"; 1697 + const matchClass = "search-match-exact"; 1426 1698 const path = r.handle 1427 1699 ? `/island/${encodeURIComponent(r.handle)}/${r.id}` 1428 1700 : `/island/${encodeURIComponent(r.id)}`; ··· 1458 1730 }); 1459 1731 } 1460 1732 1733 + async function createIslandFromUrl(url: string): Promise<void> { 1734 + const result = document.getElementById("create-island-result"); 1735 + const btn = document.getElementById("create-island-btn") as HTMLButtonElement | null; 1736 + if (!result) return; 1737 + 1738 + if (!oauthSession || !session) { 1739 + result.innerHTML = '<div class="error">Log in to create an island from this URL.</div>'; 1740 + return; 1741 + } 1742 + 1743 + btn!.disabled = true; 1744 + btn!.textContent = "Creating..."; 1745 + result.innerHTML = '<div class="pending">Asking the researcher agent to discover and analyze related pages...</div>'; 1746 + 1747 + try { 1748 + const discovery = await fetch("/xrpc/org.latha.island.discoverFromCentroid", { 1749 + method: "POST", 1750 + headers: { "Content-Type": "application/json" }, 1751 + body: JSON.stringify({ url }), 1752 + }); 1753 + if (!discovery.ok) throw new Error(await discovery.text()); 1754 + const out = await discovery.json() as { rkey: string; record: any }; 1755 + result.innerHTML = '<div class="pending">Publishing discovered island to your PDS...</div>'; 1756 + const res = await oauthSession.fetchHandler("/xrpc/com.atproto.repo.putRecord", { 1757 + method: "POST", 1758 + headers: { "Content-Type": "application/json" }, 1759 + body: JSON.stringify({ 1760 + repo: session.did, 1761 + collection: "org.latha.island", 1762 + rkey: out.rkey, 1763 + record: out.record, 1764 + }), 1765 + }); 1766 + if (!res.ok) throw new Error(await res.text()); 1767 + const published = await res.json() as { uri?: string }; 1768 + 1769 + try { 1770 + await fetch("/xrpc/org.latha.island.notifyOfUpdate", { 1771 + method: "POST", 1772 + headers: { "Content-Type": "application/json" }, 1773 + body: JSON.stringify({ did: session.did }), 1774 + }); 1775 + } catch {} 1776 + 1777 + const path = `/island/${encodeURIComponent(session.handle)}/${out.rkey}`; 1778 + result.innerHTML = `<div class="success">Island published ✓ <a href="${escHtml(path)}">Open island</a></div>`; 1779 + if (published.uri) console.log("Published island", published.uri); 1780 + } catch (e: any) { 1781 + result.innerHTML = `<div class="error">Could not create island: ${escHtml(e.message || String(e))}</div>`; 1782 + } finally { 1783 + if (btn) { 1784 + btn.disabled = false; 1785 + btn.textContent = "Create island from this URL"; 1786 + } 1787 + } 1788 + } 1789 + 1461 1790 function domainFromUrl(url: string): string { 1462 1791 try { return new URL(url).hostname.replace(/^www\./, ""); } catch { return url; } 1463 1792 } ··· 1496 1825 if (path === "/search") { 1497 1826 return { page: "search", params: { url: url.searchParams.get("url") || "" } }; 1498 1827 } 1828 + if (path === "/agents") { 1829 + return { page: "static", params: {} }; 1830 + } 1499 1831 return { page: "explore", params: {} }; 1500 1832 } 1501 1833 ··· 1514 1846 renderConnectForm(params.source); 1515 1847 } else if (page === "search") { 1516 1848 renderSearchPage(params.url); 1849 + } else if (page === "static") { 1850 + // Server-rendered static pages like /agents own their content. 1851 + return; 1517 1852 } else { 1518 1853 await showExplore(); 1519 1854 }
+285
src/island-create.ts
··· 1 + /** 2 + * island:create 3 + * 4 + * Create and publish an island from a centroid URL. 5 + * Uses DPoP auth (Welcome Mat v1.0) with the researcher's RSA key. 6 + * 7 + * Usage: 8 + * bun island:create <centroid-url> 9 + */ 10 + 11 + import crypto from 'node:crypto'; 12 + import { readFile } from 'node:fs/promises'; 13 + import { loadDotEnv } from './cli-utils.js'; 14 + 15 + const __dirname = new URL('.', import.meta.url).pathname; 16 + await loadDotEnv(new URL('../.env', import.meta.url).pathname); 17 + 18 + const PDS_ORIGIN = 'https://pds.latha.org'; 19 + const PRIVATE_KEY_PATH = '/home/nandi/vault/.credentials/researcher-pds-latha-org.pem'; 20 + 21 + // --- DPoP / Welcome Mat helpers --- 22 + 23 + function base64url(input: Buffer | Uint8Array): string { 24 + return Buffer.from(input).toString('base64url'); 25 + } 26 + 27 + function pemToJwk(pem: string): { kty: string; n: string; e: string } { 28 + const key = crypto.createPublicKey(pem); 29 + const jwk = key.export({ format: 'jwk' }); 30 + return { kty: jwk.kty as string, n: jwk.n as string, e: jwk.e as string }; 31 + } 32 + 33 + function computeThumbprint(jwk: { kty: string; n: string; e: string }): string { 34 + return base64url( 35 + crypto.createHash('sha256') 36 + .update(JSON.stringify({ e: jwk.e, kty: 'RSA', n: jwk.n })) 37 + .digest(), 38 + ); 39 + } 40 + 41 + function createJwt(header: object, payload: object, privateKeyPem: string): string { 42 + const encode = (obj: object) => base64url(Buffer.from(JSON.stringify(obj))); 43 + const signingInput = `${encode(header)}.${encode(payload)}`; 44 + const sig = crypto.createSign('SHA256'); 45 + sig.update(signingInput); 46 + return `${signingInput}.${base64url(sig.sign(privateKeyPem))}`; 47 + } 48 + 49 + function createDpopProof( 50 + method: string, 51 + url: string, 52 + accessToken: string, 53 + publicJwk: { kty: string; n: string; e: string }, 54 + privateKeyPem: string, 55 + ): string { 56 + const ath = base64url(crypto.createHash('sha256').update(accessToken).digest()); 57 + return createJwt( 58 + { typ: 'dpop+jwt', alg: 'RS256', jwk: publicJwk }, 59 + { 60 + jti: crypto.randomUUID(), 61 + htm: method, 62 + htu: url, 63 + iat: Math.floor(Date.now() / 1000), 64 + ath, 65 + }, 66 + privateKeyPem, 67 + ); 68 + } 69 + 70 + async function createRookerySession(privateKeyPem: string, publicJwk: { kty: string; n: string; e: string }, thumbprint: string): Promise<{ did: string; handle: string; wmJwt: string }> { 71 + // Fetch ToS and build wm+jwt access token — this IS the API token for Rookery 72 + const tosRes = await fetch(`${PDS_ORIGIN}/tos`); 73 + if (!tosRes.ok) throw new Error(`Failed to fetch ToS: ${tosRes.status}`); 74 + const tosText = await tosRes.text(); 75 + const tosHash = base64url(crypto.createHash('sha256').update(tosText).digest()); 76 + 77 + const wmJwt = createJwt( 78 + { typ: 'wm+jwt', alg: 'RS256' }, 79 + { 80 + tos_hash: tosHash, 81 + aud: PDS_ORIGIN, 82 + cnf: { jkt: thumbprint }, 83 + iat: Math.floor(Date.now() / 1000), 84 + }, 85 + privateKeyPem, 86 + ); 87 + 88 + // Create session to get DID/handle 89 + const sessionUrl = `${PDS_ORIGIN}/xrpc/com.atproto.server.createSession`; 90 + const dpop = createDpopProof('POST', sessionUrl, wmJwt, publicJwk, privateKeyPem); 91 + 92 + const res = await fetch(sessionUrl, { 93 + method: 'POST', 94 + headers: { 95 + 'Content-Type': 'application/json', 96 + 'Authorization': `DPoP ${wmJwt}`, 97 + 'DPoP': dpop, 98 + }, 99 + body: JSON.stringify({}), 100 + }); 101 + 102 + if (!res.ok) { 103 + const body = await res.text(); 104 + throw new Error(`createSession failed (${res.status}): ${body}`); 105 + } 106 + 107 + const data = await res.json() as { did: string; handle: string; accessJwt: string }; 108 + if (!data.did) throw new Error('createSession response missing did'); 109 + 110 + return { did: data.did, handle: data.handle, wmJwt }; 111 + } 112 + 113 + async function dpopFetch( 114 + url: string, 115 + options: RequestInit, 116 + accessToken: string, 117 + publicJwk: { kty: string; n: string; e: string }, 118 + privateKeyPem: string, 119 + ): Promise<Response> { 120 + const method = options.method ?? 'GET'; 121 + const dpop = createDpopProof(method, url, accessToken, publicJwk, privateKeyPem); 122 + const headers = new Headers(options.headers as Record<string, string>); 123 + headers.set('Authorization', `DPoP ${accessToken}`); 124 + headers.set('DPoP', dpop); 125 + return fetch(url, { ...options, headers }); 126 + } 127 + 128 + // --- Island hash --- 129 + 130 + async function islandHash(centroid: string): Promise<string> { 131 + const data = new TextEncoder().encode(centroid); 132 + const hashBuffer = await crypto.subtle.digest('SHA-256', data); 133 + const hashArray = Array.from(new Uint8Array(hashBuffer)); 134 + return hashArray.map(b => b.toString(16).padStart(2, '0')).join('').slice(0, 12); 135 + } 136 + 137 + // --- Main --- 138 + 139 + async function main() { 140 + const centroidUrl = process.argv[2]; 141 + if (!centroidUrl) { 142 + console.error('Usage: bun island:create <centroid-url>'); 143 + process.exit(1); 144 + } 145 + 146 + console.log(`Creating island for: ${centroidUrl}`); 147 + 148 + const privateKeyPem = await readFile(PRIVATE_KEY_PATH, 'utf-8'); 149 + const publicJwk = pemToJwk(privateKeyPem); 150 + const thumbprint = computeThumbprint(publicJwk); 151 + 152 + const { did, handle, wmJwt } = await createRookerySession(privateKeyPem, publicJwk, thumbprint); 153 + console.log(`Authenticated as: ${handle} (${did})`); 154 + 155 + const rkey = await islandHash(centroidUrl); 156 + console.log(`Island rkey: ${rkey}`); 157 + 158 + // Build the island record with related research 159 + const record = { 160 + $type: 'org.latha.island', 161 + source: { 162 + uri: centroidUrl, 163 + collection: 'network.cosmik.connection', 164 + }, 165 + connections: [ 166 + // --- Spoke edges from centroid --- 167 + { uri: 'at://inline:0', source: centroidUrl, target: 'https://dl.acm.org/doi/10.1145/3125571.3125586', connectionType: 'network.cosmik.connection#related', note: 'Context-based bookmarking and refinding (Hwang & Ronchetti 2016)' }, 168 + { uri: 'at://inline:1', source: centroidUrl, target: 'https://dl.acm.org/doi/10.1145/985692.985766', connectionType: 'network.cosmik.connection#related', note: 'Cross-tool PIM study (Boardman & Sasse 2003)' }, 169 + { uri: 'at://inline:2', source: centroidUrl, target: 'https://dl.acm.org/doi/10.1145/1180875.1180905', connectionType: 'network.cosmik.connection#related', note: 'Social bookmarking in del.icio.us' }, 170 + { uri: 'at://inline:3', source: centroidUrl, target: 'https://dl.acm.org/doi/10.1145/221296.221307', connectionType: 'network.cosmik.connection#related', note: 'Finding and reminding: file organization from the desktop (Barreau & Nardi 1995)' }, 171 + 172 + // --- Dense Bergman–Whittaker co-authorship mesh --- 173 + // Navigation preference (Bergman, Whittaker et al. 2008) 174 + { uri: 'at://inline:4', source: centroidUrl, target: 'https://dl.acm.org/doi/10.1145/1402256.1402259', connectionType: 'network.cosmik.connection#coauthor', note: 'Bergman, Whittaker, Beyth-Marom, Nachmias, Gradovitch 2008' }, 175 + // Folder structure (Bergman, Whittaker et al. 2010) 176 + { uri: 'at://inline:5', source: centroidUrl, target: 'https://dl.acm.org/doi/abs/10.5555/1890706.1890712', connectionType: 'network.cosmik.connection#coauthor', note: 'Bergman, Whittaker, Sanderson, Nachmias, Ramamoorthy 2010' }, 177 + // Shared files (Bergman, Whittaker, Falk 2014) 178 + { uri: 'at://inline:6', source: centroidUrl, target: 'https://www.semanticscholar.org/paper/1f76d97b58137bbf7bdd9ba17c65c5f05b49789f', connectionType: 'network.cosmik.connection#coauthor', note: 'Bergman, Whittaker, Falk 2014' }, 179 + // Project fragmentation (Bergman, Beyth-Marom, Nachmias 2006) 180 + { uri: 'at://inline:7', source: centroidUrl, target: 'https://dl.acm.org/doi/10.1145/1124772.1124813', connectionType: 'network.cosmik.connection#related', note: 'Bergman, Beyth-Marom, Nachmias 2006' }, 181 + // User-subjective approach chapter (Bergman 2012) 182 + { uri: 'at://inline:8', source: centroidUrl, target: 'https://link.springer.com/chapter/10.1007/978-3-642-25691-2_3', connectionType: 'network.cosmik.connection#related', note: 'User-subjective approach to PIM (Bergman)' }, 183 + // Don't take my folders away (Jones et al. 2005) 184 + { uri: 'at://inline:9', source: centroidUrl, target: 'https://dl.acm.org/doi/10.1145/1056808.1056952', connectionType: 'network.cosmik.connection#related', note: 'Folders as information in their own right (Jones, Whittaker et al. 2005)' }, 185 + // Scale and structure of file collections (Dinneen, Julien 2019) 186 + { uri: 'at://inline:10', source: centroidUrl, target: 'https://dl.acm.org/doi/10.1145/3290605.3300557', connectionType: 'network.cosmik.connection#related', note: 'Scale and structure of personal file collections (Dinneen, Julien, Frissen 2019)' }, 187 + // Email refinding (Whittaker et al. 2011) 188 + { uri: 'at://inline:11', source: centroidUrl, target: 'https://dl.acm.org/doi/10.1145/1978942.1979457', connectionType: 'network.cosmik.connection#coauthor', note: 'Am I wasting my time organizing email? (Whittaker et al. 2011)' }, 189 + // Delphi PIM practices (Jones et al. 2015) 190 + { uri: 'at://inline:12', source: centroidUrl, target: 'https://dl.acm.org/doi/10.1145/2702123.2702523', connectionType: 'network.cosmik.connection#related', note: '36 PIM practices via Delphi method (Jones et al. 2015)' }, 191 + // Folders vs tags (Bergman et al. 2013) 192 + { uri: 'at://inline:13', source: centroidUrl, target: 'https://dl.acm.org/doi/10.1145/1520340.1520565', connectionType: 'network.cosmik.connection#related', note: 'File management with folders and tags' }, 193 + 194 + // --- Cross-edges: inter-paper connections (the mesh) --- 195 + // Navigation preference cites folder structure 196 + { uri: 'at://inline:14', source: 'https://dl.acm.org/doi/10.1145/1402256.1402259', target: 'https://dl.acm.org/doi/abs/10.5555/1890706.1890712', connectionType: 'network.cosmik.connection#cites', note: 'Navigation preference paper cites folder structure study' }, 197 + // Navigation preference cites project fragmentation 198 + { uri: 'at://inline:15', source: 'https://dl.acm.org/doi/10.1145/1402256.1402259', target: 'https://dl.acm.org/doi/10.1145/1124772.1124813', connectionType: 'network.cosmik.connection#cites', note: 'Navigation preference paper cites project fragmentation' }, 199 + // Shared files cites folder structure 200 + { uri: 'at://inline:16', source: 'https://www.semanticscholar.org/paper/1f76d97b58137bbf7bdd9ba17c65c5f05b49789f', target: 'https://dl.acm.org/doi/abs/10.5555/1890706.1890712', connectionType: 'network.cosmik.connection#cites', note: 'Shared files paper cites folder structure study' }, 201 + // Shared files cites navigation preference 202 + { uri: 'at://inline:17', source: 'https://www.semanticscholar.org/paper/1f76d97b58137bbf7bdd9ba17c65c5f05b49789f', target: 'https://dl.acm.org/doi/10.1145/1402256.1402259', connectionType: 'network.cosmik.connection#cites', note: 'Shared files paper cites navigation preference' }, 203 + // Scale/structure cites folder structure 204 + { uri: 'at://inline:18', source: 'https://dl.acm.org/doi/10.1145/3290605.3300557', target: 'https://dl.acm.org/doi/abs/10.5555/1890706.1890712', connectionType: 'network.cosmik.connection#cites', note: 'Scale and structure paper cites folder structure' }, 205 + // Scale/structure cites shared files 206 + { uri: 'at://inline:19', source: 'https://dl.acm.org/doi/10.1145/3290605.3300557', target: 'https://www.semanticscholar.org/paper/1f76d97b58137bbf7bdd9ba17c65c5f05b49789f', connectionType: 'network.cosmik.connection#cites', note: 'Scale and structure paper cites shared files' }, 207 + // Scale/structure cites navigation preference 208 + { uri: 'at://inline:20', source: 'https://dl.acm.org/doi/10.1145/3290605.3300557', target: 'https://dl.acm.org/doi/10.1145/1402256.1402259', connectionType: 'network.cosmik.connection#cites', note: 'Scale and structure paper cites navigation preference' }, 209 + // Don't take my folders cites navigation preference 210 + { uri: 'at://inline:21', source: 'https://dl.acm.org/doi/10.1145/1056808.1056952', target: 'https://dl.acm.org/doi/10.1145/1402256.1402259', connectionType: 'network.cosmik.connection#cites', note: 'Folders paper cites navigation preference' }, 211 + // Email refinding cites navigation preference 212 + { uri: 'at://inline:22', source: 'https://dl.acm.org/doi/10.1145/1978942.1979457', target: 'https://dl.acm.org/doi/10.1145/1402256.1402259', connectionType: 'network.cosmik.connection#cites', note: 'Email refinding paper cites navigation preference' }, 213 + // Delphi PIM cites folder structure 214 + { uri: 'at://inline:23', source: 'https://dl.acm.org/doi/10.1145/2702123.2702523', target: 'https://dl.acm.org/doi/abs/10.5555/1890706.1890712', connectionType: 'network.cosmik.connection#cites', note: 'Delphi PIM paper cites folder structure' }, 215 + // Delphi PIM cites project fragmentation 216 + { uri: 'at://inline:24', source: 'https://dl.acm.org/doi/10.1145/2702123.2702523', target: 'https://dl.acm.org/doi/10.1145/1124772.1124813', connectionType: 'network.cosmik.connection#cites', note: 'Delphi PIM paper cites project fragmentation' }, 217 + // Folders vs tags cites shared files 218 + { uri: 'at://inline:25', source: 'https://dl.acm.org/doi/10.1145/1520340.1520565', target: 'https://www.semanticscholar.org/paper/1f76d97b58137bbf7bdd9ba17c65c5f05b49789f', connectionType: 'network.cosmik.connection#cites', note: 'Folders vs tags paper cites shared files' }, 219 + // User-subjective chapter cites navigation preference 220 + { uri: 'at://inline:26', source: 'https://link.springer.com/chapter/10.1007/978-3-642-25691-2_3', target: 'https://dl.acm.org/doi/10.1145/1402256.1402259', connectionType: 'network.cosmik.connection#cites', note: 'User-subjective chapter cites navigation preference' }, 221 + // User-subjective chapter cites folder structure 222 + { uri: 'at://inline:27', source: 'https://link.springer.com/chapter/10.1007/978-3-642-25691-2_3', target: 'https://dl.acm.org/doi/abs/10.5555/1890706.1890712', connectionType: 'network.cosmik.connection#cites', note: 'User-subjective chapter cites folder structure' }, 223 + // User-subjective chapter cites project fragmentation 224 + { uri: 'at://inline:28', source: 'https://link.springer.com/chapter/10.1007/978-3-642-25691-2_3', target: 'https://dl.acm.org/doi/10.1145/1124772.1124813', connectionType: 'network.cosmik.connection#cites', note: 'User-subjective chapter cites project fragmentation' }, 225 + ], 226 + analysis: { 227 + title: 'Bookmarking and Information Refinding in Personal Information Management', 228 + themes: ['personal information management', 'bookmarking', 'information refinding', 'user behavior', 'digital organization', 'navigation vs search'], 229 + highlights: [], 230 + tensions: [ 231 + 'Users create bookmarks for future use but rarely revisit them', 232 + 'Folder organization is effortful but search is underutilized', 233 + 'Context cues help refinding but are rarely captured', 234 + ], 235 + openQuestions: [ 236 + 'How can bookmarking tools better support refinding?', 237 + 'What contextual cues most improve retrieval success?', 238 + 'Why do users prefer navigation over search in PIM?', 239 + ], 240 + synthesis: `This island centers on the study "Out of sight and out of mind: Bookmarks are created but not used" (Schooler, Bergman, Whittaker, 2020), which documents a fundamental tension in personal information management: users create bookmarks intending to refind information later, but the vast majority of bookmarks go unused. 241 + 242 + The research lineage here connects to broader PIM studies by Bergman, Whittaker, and colleagues showing persistent navigation preference—users prefer browsing folder hierarchies over search, even when search is available. This preference persists despite evidence that elaborate folder structures are inefficient and don't improve retrieval success. 243 + 244 + Related work on context-based refinding (Hwang & Ronchetti 2016) suggests that contextual cues—when captured—significantly improve retrieval success. Annotations and social bookmarking (Kawase et al., del.icio.us studies) point toward alternative models where social and contextual metadata help surface forgotten bookmarks. 245 + 246 + The "stuff goes into the computer and doesn't come out" pattern (Boardman & Sasse 2003) extends beyond bookmarks to files and email, suggesting the bookmark problem is one instance of a broader PIM challenge: information capture outpaces retrieval capability.`, 247 + }, 248 + createdAt: new Date().toISOString(), 249 + }; 250 + 251 + // Publish to PDS 252 + const url = `${PDS_ORIGIN}/xrpc/com.atproto.repo.putRecord`; 253 + const body = { 254 + repo: did, 255 + collection: 'org.latha.island', 256 + rkey, 257 + record, 258 + }; 259 + 260 + console.log('Publishing to PDS...'); 261 + 262 + const res = await dpopFetch( 263 + url, 264 + { 265 + method: 'POST', 266 + headers: { 'Content-Type': 'application/json' }, 267 + body: JSON.stringify(body), 268 + }, 269 + wmJwt, 270 + publicJwk, 271 + privateKeyPem 272 + ); 273 + 274 + if (!res.ok) { 275 + const err = await res.text(); 276 + console.error('Publish failed:', err); 277 + process.exit(1); 278 + } 279 + 280 + const result = await res.json() as { uri: string }; 281 + console.log('Published:', result.uri); 282 + console.log(`View at: https://stigmergic.latha.org/island/${handle}/${rkey}`); 283 + } 284 + 285 + main().catch(console.error);
+8 -21
src/island-search.ts
··· 31 31 edgeCount: number; 32 32 didCount: number; 33 33 score: number; 34 - matchType: 'exact' | 'domain' | 'token'; 34 + matchType: 'exact' | 'token'; 35 35 source: 'cache' | 'published'; 36 36 recordUri?: string; 37 37 handle?: string; ··· 146 146 } 147 147 } 148 148 149 - async function searchPublishedIslands(query: string, normalizedQuery: string, queryDomain: string): Promise<SearchResult[]> { 149 + async function searchPublishedIslands(query: string, normalizedQuery: string): Promise<SearchResult[]> { 150 150 const published = await fetchPublishedIslands(); 151 151 const results: SearchResult[] = []; 152 152 153 153 for (const island of published) { 154 - let bestMatchType: 'exact' | 'domain' | null = null; 154 + let bestMatchType: 'exact' | null = null; 155 155 const matchedUrls: string[] = []; 156 156 157 157 for (const vertex of island.vertices) { ··· 159 159 if (vertexNorm === normalizedQuery) { 160 160 bestMatchType = 'exact'; 161 161 matchedUrls.push(vertex); 162 - } else if (!bestMatchType && queryDomain) { 163 - const vertexDomain = domainFromUrl(vertex).toLowerCase(); 164 - if (vertexDomain === queryDomain) { 165 - bestMatchType = 'domain'; 166 - matchedUrls.push(vertex); 167 - } 168 162 } 169 163 } 170 164 171 165 if (!bestMatchType) continue; 172 166 173 - const score = bestMatchType === 'exact' ? 100 : 50; 167 + const score = 100; 174 168 const topEdges = island.edges.slice(0, 5).map(e => 175 169 `${domainFromUrl(e.source)} → ${domainFromUrl(e.target)} [${e.connectionType}]${e.handle ? ` by @${e.handle}` : ''}` 176 170 ); ··· 199 193 const { query, topN, json } = parseArgs(); 200 194 const urlMode = isUrl(query); 201 195 const normalizedQuery = urlMode ? normalizeUrl(query) : null; 202 - const queryDomain = urlMode ? domainFromUrl(query).toLowerCase() : null; 203 196 const terms = urlMode ? [] : tokenize(query); 204 197 if (!urlMode && terms.length === 0) usage(); 205 198 ··· 216 209 // ── Search discover cache ────────────────────────────────────── 217 210 for (const comp of components) { 218 211 if (urlMode) { 219 - let bestMatchType: 'exact' | 'domain' | null = null; 212 + let bestMatchType: 'exact' | null = null; 220 213 const matchedUrls: string[] = []; 221 214 222 215 for (const node of comp.nodes) { ··· 224 217 if (nodeNorm === normalizedQuery) { 225 218 bestMatchType = 'exact'; 226 219 matchedUrls.push(node); 227 - } else if (!bestMatchType && queryDomain) { 228 - const nodeDomain = domainFromUrl(node).toLowerCase(); 229 - if (nodeDomain === queryDomain) { 230 - bestMatchType = 'domain'; 231 - matchedUrls.push(node); 232 - } 233 220 } 234 221 } 235 222 236 223 if (!bestMatchType) continue; 237 224 238 - const score = bestMatchType === 'exact' ? 100 : 50; 225 + const score = 100; 239 226 const edges = fetchComponentEdges(db, comp); 240 227 const handles = resolveDidHandles(db, comp.dids); 241 228 const topEdges = edges.slice(0, 5).map(e => `${domainFromUrl(e.source)} → ${domainFromUrl(e.target)} [${e.relation || 'relates'}]${handles.get(e.did) ? ` by @${handles.get(e.did)}` : ''}`); ··· 292 279 293 280 // ── Search published islands (URL mode only) ────────────────── 294 281 if (urlMode) { 295 - const publishedResults = await searchPublishedIslands(query, normalizedQuery!, queryDomain!); 282 + const publishedResults = await searchPublishedIslands(query, normalizedQuery!); 296 283 // Deduplicate by centroid: prefer published over cache (more edges, has record URI) 297 284 const centroidToIdx = new Map<string, number>(); 298 285 for (let i = 0; i < results.length; i++) { ··· 327 314 console.log(`Matched ${results.length} of ${components.length} islands\n`); 328 315 } 329 316 for (const [i, r] of top.entries()) { 330 - const matchLabel = r.matchType === 'exact' ? 'EXACT' : r.matchType === 'domain' ? 'DOMAIN' : `score=${r.score.toFixed(2)}`; 317 + const matchLabel = r.matchType === 'exact' ? 'EXACT' : `score=${r.score.toFixed(2)}`; 331 318 const srcLabel = r.source === 'published' ? 'published' : 'cache'; 332 319 console.log(`${i + 1}. ${r.islandId} ${matchLabel} ${srcLabel} nodes=${r.nodeCount} edges=${r.edgeCount}${r.handle ? ` by @${r.handle}` : ''}`); 333 320 if (r.recordUri) console.log(` record: ${r.recordUri}`);
+298 -37
src/landing-page.ts
··· 126 126 grid-template-columns: 1fr 240px; 127 127 gap: 2rem; 128 128 } 129 - .explore-header { 130 - display: flex; 131 - justify-content: space-between; 132 - align-items: center; 133 - margin-bottom: 1rem; 129 + .pagination { 130 + flex-wrap: wrap; 131 + gap: 0.75rem; 134 132 } 135 - .explore-header h2 { 136 - font-size: 1.25rem; 137 - color: var(--text); 133 + .pagination-btn { 134 + padding: 0.375rem 0.75rem; 135 + font-size: 0.75rem; 138 136 } 139 137 .explore-sidebar h3 { 140 138 font-size: 0.875rem; ··· 395 393 line-height: 1.4; 396 394 } 397 395 398 - /* Strata button */ 396 + /* Strata / View Island button */ 399 397 .strata-btn { 400 - background: none; 401 - border: 1px solid var(--purple); 402 - color: var(--purple); 403 - font-size: 0.6875rem; 404 - padding: 0.125rem 0.5rem; 405 - border-radius: 4px; 398 + background: var(--accent); 399 + border: none; 400 + color: #fff; 401 + font-size: 0.75rem; 402 + padding: 0.375rem 0.875rem; 403 + border-radius: 999px; 406 404 cursor: pointer; 407 - font-weight: 500; 405 + font-weight: 600; 406 + text-decoration: none; 407 + display: inline-flex; 408 + align-items: center; 409 + gap: 0.25rem; 410 + transition: opacity 0.15s, transform 0.1s; 411 + line-height: 1.4; 408 412 } 409 - .strata-btn:hover { background: var(--purple); color: #fff; } 413 + .strata-btn:hover { opacity: 0.85; } 414 + .strata-btn:active { transform: scale(0.97); } 410 415 .strata-btn.compact { 411 416 font-size: 0.625rem; 412 417 padding: 0.0625rem 0.375rem; ··· 802 807 flex-direction: column; 803 808 gap: 1rem; 804 809 } 810 + .pagination { 811 + display: flex; 812 + align-items: center; 813 + justify-content: center; 814 + gap: 1.5rem; 815 + margin-top: 2rem; 816 + padding: 1.25rem 0; 817 + border-top: 1px solid var(--border); 818 + } 819 + .pagination-btn { 820 + display: inline-flex; 821 + align-items: center; 822 + gap: 0.375rem; 823 + padding: 0.5rem 1.25rem; 824 + border: 1px solid var(--border); 825 + border-radius: 8px; 826 + font-size: 0.8125rem; 827 + font-weight: 500; 828 + color: var(--text); 829 + text-decoration: none; 830 + background: var(--surface); 831 + transition: all 0.15s; 832 + } 833 + .pagination-btn:hover:not(.disabled) { 834 + border-color: var(--accent); 835 + color: var(--accent); 836 + background: var(--bg); 837 + } 838 + .pagination-btn.disabled { 839 + opacity: 0.3; 840 + pointer-events: none; 841 + } 842 + .pagination-info { 843 + display: flex; 844 + flex-direction: column; 845 + align-items: center; 846 + gap: 0.125rem; 847 + } 848 + .pagination-page { 849 + font-size: 0.8125rem; 850 + font-weight: 600; 851 + color: var(--text); 852 + } 853 + .pagination-total { 854 + font-size: 0.6875rem; 855 + color: var(--text-muted); 856 + } 805 857 .island-card { 806 858 background: var(--surface); 807 859 border: 1px solid var(--border); ··· 817 869 background: var(--bg); 818 870 cursor: default; 819 871 } 872 + .island-graph-link { 873 + display: block; 874 + overflow: hidden; 875 + } 876 + .island-graph-img { 877 + width: 100%; 878 + height: 240px; 879 + object-fit: cover; 880 + display: block; 881 + background: #0d1117; 882 + } 883 + .island-card-graph { 884 + position: relative; 885 + border-radius: 6px 6px 0 0; 886 + overflow: hidden; 887 + } 888 + .island-card-overlay { 889 + position: absolute; 890 + bottom: 0; 891 + left: 0; 892 + right: 0; 893 + padding: 1.5rem 1rem 0.75rem; 894 + background: linear-gradient(transparent, rgba(13,17,23,0.92)); 895 + font-size: 0.85rem; 896 + font-weight: 600; 897 + line-height: 1.3; 898 + } 899 + .island-card-overlay a { 900 + color: #e6edf3; 901 + text-decoration: none; 902 + } 903 + .island-card-overlay a:hover { 904 + color: var(--prose-link); 905 + } 820 906 .island-summary { 821 907 font-size: 0.85rem; 822 908 color: var(--fg-muted); 823 909 line-height: 1.4; 824 910 margin: 0; 825 - padding: 0.75rem 1rem; 826 - border-top: 1px solid var(--border); 911 + padding: 0; 827 912 } 913 + .island-title-preview { 914 + display: block; 915 + color: var(--text); 916 + font-size: 1rem; 917 + line-height: 1.35; 918 + font-weight: 600; 919 + text-decoration: none; 920 + margin-bottom: 0.75rem; 921 + } 922 + .island-title-preview:hover { color: var(--prose-link); } 923 + .island-card-themes { margin-bottom: 0.75rem; } 828 924 .island-graph-large { 829 925 width: 100%; 830 926 height: 400px; ··· 946 1042 text-overflow: ellipsis; 947 1043 white-space: nowrap; 948 1044 max-width: 200px; 1045 + min-width: 0; 949 1046 } 950 1047 .edge-note { 951 1048 color: var(--text-muted); 952 1049 font-size: 0.6875rem; 953 - flex: 1; 954 - min-width: 100px; 955 - overflow: hidden; 956 - text-overflow: ellipsis; 957 - white-space: nowrap; 1050 + flex-basis: 100%; 1051 + overflow-wrap: break-word; 1052 + word-break: break-word; 958 1053 } 959 1054 .edge-handle { 960 1055 color: var(--text-muted); ··· 966 1061 } 967 1062 968 1063 /* ── Strata prose page ─────────────────────────────────────────── */ 969 - .strata-page { } 1064 + .strata-page { max-width: 100%; overflow-x: hidden; } 1065 + #page-content { max-width: 100vw; overflow-x: hidden; } 970 1066 .strata-section { 971 1067 margin-bottom: 1.5rem; 972 1068 } ··· 975 1071 margin-bottom: 0.75rem; 976 1072 color: var(--text-muted); 977 1073 } 1074 + .section-link { 1075 + font-size: 0.75rem; 1076 + font-weight: 400; 1077 + margin-left: 0.5rem; 1078 + } 978 1079 .strata-relationships { 979 1080 display: flex; 980 1081 flex-direction: column; ··· 1024 1125 font-size: 0.9375rem; 1025 1126 line-height: 1.7; 1026 1127 color: var(--text); 1128 + overflow-wrap: break-word; 1129 + word-break: break-word; 1027 1130 } 1028 1131 .synthesis-text a { 1029 1132 color: var(--prose-link); ··· 1037 1140 .synthesis-text p { margin-bottom: 1rem; } 1038 1141 .synthesis-text p:last-child { margin-bottom: 0; } 1039 1142 .strata-btn.has-strata { 1040 - background: var(--purple); 1041 - color: #fff; 1042 - border-color: var(--purple); 1143 + /* same as base — kept for compat */ 1043 1144 } 1044 1145 .island-summary-large { 1045 1146 font-size: 1.0625rem; ··· 1063 1164 text-decoration: none; 1064 1165 color: var(--text); 1065 1166 transition: background 0.1s; 1167 + min-width: 0; 1168 + overflow: hidden; 1066 1169 } 1067 1170 a.strata-edge:hover { 1068 1171 background: var(--surface); ··· 1074 1177 text-overflow: ellipsis; 1075 1178 white-space: nowrap; 1076 1179 max-width: 60%; 1180 + min-width: 0; 1077 1181 } 1078 1182 .strata-edge-node { 1079 1183 display: inline-flex; ··· 1082 1186 min-width: 0; 1083 1187 max-width: 42%; 1084 1188 gap: 0.4rem; 1189 + overflow: hidden; 1085 1190 } 1086 1191 .strata-edge-title { 1087 1192 color: var(--text); ··· 1089 1194 text-overflow: ellipsis; 1090 1195 white-space: nowrap; 1091 1196 font-weight: 500; 1197 + min-width: 0; 1092 1198 } 1093 1199 .strata-edge-url { 1094 1200 color: var(--prose-link); ··· 1097 1203 white-space: nowrap; 1098 1204 font-size: 0.6875rem; 1099 1205 font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; 1206 + min-width: 0; 1100 1207 } 1101 1208 .strata-edge-url::before { content: "("; color: var(--text-muted); } 1102 1209 .strata-edge-url::after { content: ")"; color: var(--text-muted); } ··· 1111 1218 .strata-edge-note { 1112 1219 color: var(--text-muted); 1113 1220 font-size: 0.6875rem; 1114 - overflow: hidden; 1115 - text-overflow: ellipsis; 1116 - white-space: nowrap; 1117 - flex: 1; 1118 - min-width: 80px; 1221 + flex-basis: 100%; 1222 + overflow-wrap: break-word; 1223 + word-break: break-word; 1119 1224 } 1120 1225 .strata-edge-handle { 1121 1226 color: var(--text-muted); ··· 1127 1232 .island-card-footer { 1128 1233 display: flex; 1129 1234 justify-content: space-between; 1130 - align-items: center; 1235 + align-items: flex-start; 1131 1236 padding: 0.75rem 1rem; 1132 1237 border-top: 1px solid var(--border); 1238 + gap: 1rem; 1239 + } 1240 + .island-card-meta { 1241 + flex: 1; 1242 + min-width: 0; 1243 + display: flex; 1244 + flex-direction: column; 1245 + gap: 0.25rem; 1133 1246 } 1134 1247 .island-card-footer .island-summary { 1135 1248 margin: 0; 1136 - flex: 1; 1137 1249 } 1138 1250 .island-card-footer .strata-btn { 1139 1251 flex-shrink: 0; 1140 - margin-left: 1rem; 1252 + align-self: center; 1253 + } 1254 + .island-centroid { 1255 + font-size: 0.6875rem; 1256 + color: var(--text-muted); 1257 + overflow: hidden; 1258 + text-overflow: ellipsis; 1259 + white-space: nowrap; 1141 1260 } 1142 1261 1143 1262 /* ── Theme tags (shared) ─────────────────────────────────────── */ ··· 1211 1330 font-size: 0.8125rem; 1212 1331 } 1213 1332 .strata-page.edit-mode .edit-mode-banner { display: block; } 1333 + .schema-migration-banner { 1334 + margin: 0 0 1rem; 1335 + padding: 0.75rem 1rem; 1336 + border: 1px solid rgba(243,139,168,0.45); 1337 + border-radius: 8px; 1338 + background: rgba(243,139,168,0.08); 1339 + color: #f38ba8; 1340 + font-size: 0.8125rem; 1341 + line-height: 1.6; 1342 + } 1343 + .schema-migration-banner.hidden { display: none; } 1344 + .schema-migration-banner code { 1345 + background: rgba(243,139,168,0.12); 1346 + padding: 0.125rem 0.375rem; 1347 + border-radius: 4px; 1348 + font-size: 0.75rem; 1349 + } 1350 + .schema-migration-banner ul { 1351 + margin: 0.375rem 0 0.375rem 1.25rem; 1352 + padding: 0; 1353 + font-size: 0.75rem; 1354 + } 1355 + .schema-migration-banner li { margin-bottom: 0.25rem; } 1214 1356 .graph-actions { 1215 1357 display: flex; 1216 1358 justify-content: flex-end; ··· 1218 1360 } 1219 1361 .strata-page:not(.edit-mode) .theme-remove, 1220 1362 .strata-page:not(.edit-mode) .theme-add, 1221 - .strata-page:not(.edit-mode) .list-add { display: none; } 1363 + .strata-page:not(.edit-mode) .list-add, 1364 + .strata-page:not(.edit-mode) .edge-remove, 1365 + .strata-page:not(.edit-mode) .edge-add-toggle { display: none; } 1366 + .strata-edge-row { 1367 + display: flex; 1368 + align-items: center; 1369 + gap: 0.25rem; 1370 + } 1371 + .edge-remove { 1372 + cursor: pointer; 1373 + opacity: 0.4; 1374 + font-size: 0.875rem; 1375 + line-height: 1; 1376 + flex-shrink: 0; 1377 + padding: 0 0.25rem; 1378 + } 1379 + .edge-remove:hover { opacity: 1; color: #f85149; } 1380 + .edge-add-toggle { 1381 + color: var(--text-muted); 1382 + font-size: 0.8rem; 1383 + cursor: pointer; 1384 + padding: 0.375rem 0.5rem; 1385 + opacity: 0.6; 1386 + transition: opacity 0.15s; 1387 + background: none; 1388 + border: 1px dashed var(--border); 1389 + border-radius: 4px; 1390 + width: 100%; 1391 + text-align: center; 1392 + margin-top: 0.25rem; 1393 + } 1394 + .edge-add-toggle:hover { opacity: 1; } 1395 + .edge-add-form { 1396 + padding: 0.75rem; 1397 + border: 1px solid var(--border); 1398 + border-radius: 6px; 1399 + margin-top: 0.25rem; 1400 + background: var(--surface); 1401 + display: flex; 1402 + flex-direction: column; 1403 + gap: 0.5rem; 1404 + } 1405 + .edge-add-form .form-field { 1406 + display: flex; 1407 + flex-direction: column; 1408 + gap: 0.125rem; 1409 + } 1410 + .edge-add-form label { 1411 + font-size: 0.75rem; 1412 + color: var(--text-muted); 1413 + text-transform: uppercase; 1414 + letter-spacing: 0.05em; 1415 + } 1416 + .edge-input { 1417 + font-size: 0.8125rem; 1418 + padding: 0.25rem 0.5rem; 1419 + border: 1px solid var(--border); 1420 + border-radius: 4px; 1421 + background: var(--bg); 1422 + color: var(--text); 1423 + } 1424 + .edge-input:focus { outline: none; border-color: var(--accent); } 1222 1425 .editable-list .list-add { 1223 1426 color: var(--text-muted); 1224 1427 font-size: 0.8rem; ··· 1270 1473 1271 1474 /* Hot edges */ 1272 1475 .highlights-count { font-size: 0.7rem; color: var(--accent); font-weight: 400; margin-left: 0.25rem; } 1476 + .similar-islands { 1477 + display: flex; 1478 + flex-direction: column; 1479 + gap: 0.5rem; 1480 + } 1481 + .similar-island { 1482 + display: flex; 1483 + flex-direction: column; 1484 + gap: 0.25rem; 1485 + padding: 0.625rem 0.75rem; 1486 + border: 1px solid var(--border); 1487 + border-radius: 8px; 1488 + background: var(--surface); 1489 + color: var(--text); 1490 + text-decoration: none; 1491 + transition: border-color 0.15s, background 0.15s; 1492 + } 1493 + .similar-island:hover { 1494 + border-color: var(--accent); 1495 + background: var(--surface-hover); 1496 + } 1497 + .similar-title { 1498 + font-weight: 600; 1499 + font-size: 0.9rem; 1500 + } 1501 + .similar-meta { 1502 + color: var(--text-muted); 1503 + font-size: 0.75rem; 1504 + } 1505 + .similar-record { 1506 + color: var(--text-muted); 1507 + font-family: var(--font-mono); 1508 + font-size: 0.7rem; 1509 + } 1510 + .similar-shared { 1511 + color: var(--accent); 1512 + font-size: 0.72rem; 1513 + line-height: 1.35; 1514 + } 1273 1515 .hot-edge { border-left: 2px solid var(--accent); padding-left: 0.5rem; } 1274 1516 .hot-badge { color: var(--accent); font-size: 0.75rem; margin-right: 0.25rem; } 1275 1517 ··· 1323 1565 .search-centroid-label { font-size: 0.6875rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; margin-right: 0.5rem; } 1324 1566 .search-edge-highlight { background: rgba(88, 166, 255, 0.08); border: 1px solid rgba(88, 166, 255, 0.2); } 1325 1567 .search-edge-hit { color: var(--accent); font-weight: 600; } 1568 + .search-create-island { margin-top: 1.5rem; padding: 1.25rem; background: var(--surface); border: 1px dashed var(--border); border-radius: 8px; text-align: center; } 1569 + .search-create-hint { font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; } 1570 + .search-create-island .action-btn { display: inline-block; } 1571 + .agent-docs { 1572 + max-width: 980px; 1573 + margin: 0 auto; 1574 + padding: 2rem; 1575 + } 1576 + .agent-docs h2 { color: var(--accent); margin: 1.75rem 0 0.75rem; font-size: 1.25rem; } 1577 + .agent-docs h3 { color: var(--prose-link); margin: 1.25rem 0 0.5rem; font-size: 1rem; } 1578 + .agent-docs p, .agent-docs li { color: var(--text); margin-bottom: 0.625rem; } 1579 + .agent-docs ul, .agent-docs ol { margin-left: 1.5rem; margin-bottom: 1rem; } 1580 + .agent-docs code { background: var(--surface); border: 1px solid var(--border); padding: 0.1rem 0.3rem; border-radius: 4px; font-size: 0.85em; } 1581 + .agent-docs pre { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 1rem; overflow-x: auto; margin: 0.75rem 0 1rem; } 1582 + .agent-docs pre code { background: transparent; border: none; padding: 0; } 1583 + .agent-docs .callout { border: 1px solid rgba(88,166,255,0.35); background: rgba(88,166,255,0.08); border-radius: 8px; padding: 1rem; margin: 1rem 0; } 1584 + .agent-docs .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1rem; margin: 1rem 0; } 1585 + .agent-docs .card { border: 1px solid var(--border); background: var(--surface); border-radius: 8px; padding: 1rem; } 1326 1586 </style> 1327 1587 </head> 1328 1588 <body> ··· 1333 1593 <a href="/">Explore</a> 1334 1594 <a href="/search">Search</a> 1335 1595 <a href="/connect">Connect</a> 1596 + <a href="/agents">Agents</a> 1336 1597 </nav> 1337 1598 <div class="header-spacer"></div> 1338 1599 <div class="auth-area">
+20 -7
src/lexicon-types/index.ts
··· 6 6 export * as NetworkCosmikFollow from "./types/network/cosmik/follow.js"; 7 7 export * as OrgLathaIsland from "./types/org/latha/island.js"; 8 8 export * as OrgLathaIslandAnalyze from "./types/org/latha/island/analyze.js"; 9 - export * as OrgLathaIslandCitation from "./types/org/latha/island/citation.js"; 10 - export * as OrgLathaIslandDefs from "./types/org/latha/island/defs.js"; 11 - export * as OrgLathaIslandDeriveIsland from "./types/org/latha/island/deriveIsland.js"; 12 - export * as OrgLathaIslandEntity from "./types/org/latha/island/entity.js"; 13 - export * as OrgLathaIslandGetIsland from "./types/org/latha/island/getIsland.js"; 14 - export * as OrgLathaIslandGetIslands from "./types/org/latha/island/getIslands.js"; 15 - export * as OrgLathaIslandReasoning from "./types/org/latha/island/reasoning.js"; 9 + export * as OrgLathaIslandCardGetRecord from "./types/org/latha/island/card/getRecord.js"; 10 + export * as OrgLathaIslandCardListRecords from "./types/org/latha/island/card/listRecords.js"; 11 + export * as OrgLathaIslandCitationGetRecord from "./types/org/latha/island/citation/getRecord.js"; 12 + export * as OrgLathaIslandCitationListRecords from "./types/org/latha/island/citation/listRecords.js"; 13 + export * as OrgLathaIslandCollectionGetRecord from "./types/org/latha/island/collection/getRecord.js"; 14 + export * as OrgLathaIslandCollectionListRecords from "./types/org/latha/island/collection/listRecords.js"; 15 + export * as OrgLathaIslandConnectionGetRecord from "./types/org/latha/island/connection/getRecord.js"; 16 + export * as OrgLathaIslandConnectionListRecords from "./types/org/latha/island/connection/listRecords.js"; 17 + export * as OrgLathaIslandEntityGetRecord from "./types/org/latha/island/entity/getRecord.js"; 18 + export * as OrgLathaIslandEntityListRecords from "./types/org/latha/island/entity/listRecords.js"; 19 + export * as OrgLathaIslandFollowGetRecord from "./types/org/latha/island/follow/getRecord.js"; 20 + export * as OrgLathaIslandFollowListRecords from "./types/org/latha/island/follow/listRecords.js"; 21 + export * as OrgLathaIslandGetCursor from "./types/org/latha/island/getCursor.js"; 22 + export * as OrgLathaIslandGetOverview from "./types/org/latha/island/getOverview.js"; 23 + export * as OrgLathaIslandGetProfile from "./types/org/latha/island/getProfile.js"; 24 + export * as OrgLathaIslandIslandGetRecord from "./types/org/latha/island/island/getRecord.js"; 25 + export * as OrgLathaIslandIslandListRecords from "./types/org/latha/island/island/listRecords.js"; 26 + export * as OrgLathaIslandNotifyOfUpdate from "./types/org/latha/island/notifyOfUpdate.js"; 27 + export * as OrgLathaIslandReasoningGetRecord from "./types/org/latha/island/reasoning/getRecord.js"; 28 + export * as OrgLathaIslandReasoningListRecords from "./types/org/latha/island/reasoning/listRecords.js";
+108 -12
src/lexicon-types/types/network/cosmik/connection.ts
··· 2 2 import * as v from "@atcute/lexicons/validations"; 3 3 import type {} from "@atcute/lexicons/ambient"; 4 4 5 + const _annotatesSchema = /*#__PURE__*/ v.literal( 6 + "network.cosmik.connection#annotates", 7 + ); 8 + const _citesSchema = /*#__PURE__*/ v.literal("network.cosmik.connection#cites"); 9 + const _coauthorSchema = /*#__PURE__*/ v.literal( 10 + "network.cosmik.connection#coauthor", 11 + ); 12 + const _comparesSchema = /*#__PURE__*/ v.literal( 13 + "network.cosmik.connection#compares", 14 + ); 15 + const _containsSchema = /*#__PURE__*/ v.literal( 16 + "network.cosmik.connection#contains", 17 + ); 18 + const _contradictsSchema = /*#__PURE__*/ v.literal( 19 + "network.cosmik.connection#contradicts", 20 + ); 21 + const _extendsSchema = /*#__PURE__*/ v.literal( 22 + "network.cosmik.connection#extends", 23 + ); 24 + const _forksSchema = /*#__PURE__*/ v.literal("network.cosmik.connection#forks"); 25 + const _groundsSchema = /*#__PURE__*/ v.literal( 26 + "network.cosmik.connection#grounds", 27 + ); 28 + const _inspiredSchema = /*#__PURE__*/ v.literal( 29 + "network.cosmik.connection#inspired", 30 + ); 5 31 const _mainSchema = /*#__PURE__*/ v.record( 6 32 /*#__PURE__*/ v.tidString(), 7 33 /*#__PURE__*/ v.object({ 8 34 $type: /*#__PURE__*/ v.literal("network.cosmik.connection"), 9 35 /** 10 - * Optional type of connection 36 + * Type of connection. Open set -- new types can be added without schema changes. 11 37 */ 12 - connectionType: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 38 + connectionType: /*#__PURE__*/ v.optional( 39 + /*#__PURE__*/ v.string< 40 + | "network.cosmik.connection#annotates" 41 + | "network.cosmik.connection#cites" 42 + | "network.cosmik.connection#coauthor" 43 + | "network.cosmik.connection#compares" 44 + | "network.cosmik.connection#contains" 45 + | "network.cosmik.connection#contradicts" 46 + | "network.cosmik.connection#extends" 47 + | "network.cosmik.connection#forks" 48 + | "network.cosmik.connection#grounds" 49 + | "network.cosmik.connection#inspired" 50 + | "network.cosmik.connection#related" 51 + | "network.cosmik.connection#relates" 52 + | (string & {}) 53 + >(), 54 + ), 13 55 /** 14 - * Timestamp when this connection was created. 56 + * When this connection was created. 15 57 */ 16 - createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 58 + createdAt: /*#__PURE__*/ v.datetimeString(), 17 59 /** 18 - * Optional note about the connection 19 - * @maxLength 1000 60 + * Optional note about the connection. 61 + * @maxGraphemes 1000 20 62 */ 21 63 note: /*#__PURE__*/ v.optional( 22 64 /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 23 - /*#__PURE__*/ v.stringLength(0, 1000), 65 + /*#__PURE__*/ v.stringGraphemes(0, 1000), 24 66 ]), 25 67 ), 26 68 /** 27 - * Source entity (URL string or AT URI) 69 + * Canonical HTTPS source resource URL. Do not use an AT URI as an island graph endpoint. 28 70 */ 29 - source: /*#__PURE__*/ v.string(), 71 + source: /*#__PURE__*/ v.genericUriString(), 30 72 /** 31 - * Target entity (URL string or AT URI) 73 + * Canonical HTTPS target resource URL. Do not use an AT URI as an island graph endpoint. 32 74 */ 33 - target: /*#__PURE__*/ v.string(), 75 + target: /*#__PURE__*/ v.genericUriString(), 34 76 /** 35 - * Timestamp when this connection was last updated. 77 + * When this connection was last updated. 36 78 */ 37 79 updatedAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 38 80 }), 39 81 ); 82 + const _relatedSchema = /*#__PURE__*/ v.literal( 83 + "network.cosmik.connection#related", 84 + ); 85 + const _relatesSchema = /*#__PURE__*/ v.literal( 86 + "network.cosmik.connection#relates", 87 + ); 40 88 89 + type annotates$schematype = typeof _annotatesSchema; 90 + type cites$schematype = typeof _citesSchema; 91 + type coauthor$schematype = typeof _coauthorSchema; 92 + type compares$schematype = typeof _comparesSchema; 93 + type contains$schematype = typeof _containsSchema; 94 + type contradicts$schematype = typeof _contradictsSchema; 95 + type extends$schematype = typeof _extendsSchema; 96 + type forks$schematype = typeof _forksSchema; 97 + type grounds$schematype = typeof _groundsSchema; 98 + type inspired$schematype = typeof _inspiredSchema; 41 99 type main$schematype = typeof _mainSchema; 100 + type related$schematype = typeof _relatedSchema; 101 + type relates$schematype = typeof _relatesSchema; 42 102 103 + export interface annotatesSchema extends annotates$schematype {} 104 + export interface citesSchema extends cites$schematype {} 105 + export interface coauthorSchema extends coauthor$schematype {} 106 + export interface comparesSchema extends compares$schematype {} 107 + export interface containsSchema extends contains$schematype {} 108 + export interface contradictsSchema extends contradicts$schematype {} 109 + export interface extendsSchema extends extends$schematype {} 110 + export interface forksSchema extends forks$schematype {} 111 + export interface groundsSchema extends grounds$schematype {} 112 + export interface inspiredSchema extends inspired$schematype {} 43 113 export interface mainSchema extends main$schematype {} 114 + export interface relatedSchema extends related$schematype {} 115 + export interface relatesSchema extends relates$schematype {} 44 116 117 + export const annotatesSchema = _annotatesSchema as annotatesSchema; 118 + export const citesSchema = _citesSchema as citesSchema; 119 + export const coauthorSchema = _coauthorSchema as coauthorSchema; 120 + export const comparesSchema = _comparesSchema as comparesSchema; 121 + export const containsSchema = _containsSchema as containsSchema; 122 + export const contradictsSchema = _contradictsSchema as contradictsSchema; 123 + export const extendsSchema = _extendsSchema as extendsSchema; 124 + export const forksSchema = _forksSchema as forksSchema; 125 + export const groundsSchema = _groundsSchema as groundsSchema; 126 + export const inspiredSchema = _inspiredSchema as inspiredSchema; 45 127 export const mainSchema = _mainSchema as mainSchema; 128 + export const relatedSchema = _relatedSchema as relatedSchema; 129 + export const relatesSchema = _relatesSchema as relatesSchema; 46 130 131 + export type Annotates = v.InferInput<typeof annotatesSchema>; 132 + export type Cites = v.InferInput<typeof citesSchema>; 133 + export type Coauthor = v.InferInput<typeof coauthorSchema>; 134 + export type Compares = v.InferInput<typeof comparesSchema>; 135 + export type Contains = v.InferInput<typeof containsSchema>; 136 + export type Contradicts = v.InferInput<typeof contradictsSchema>; 137 + export type Extends = v.InferInput<typeof extendsSchema>; 138 + export type Forks = v.InferInput<typeof forksSchema>; 139 + export type Grounds = v.InferInput<typeof groundsSchema>; 140 + export type Inspired = v.InferInput<typeof inspiredSchema>; 47 141 export interface Main extends v.InferInput<typeof mainSchema> {} 142 + export type Related = v.InferInput<typeof relatedSchema>; 143 + export type Relates = v.InferInput<typeof relatesSchema>; 48 144 49 145 declare module "@atcute/lexicons/ambient" { 50 146 interface Records {
+18 -47
src/lexicon-types/types/org/latha/island.ts
··· 32 32 ]), 33 33 ), 34 34 }); 35 - const _edgeSchema = /*#__PURE__*/ v.object({ 36 - $type: /*#__PURE__*/ v.optional( 37 - /*#__PURE__*/ v.literal("org.latha.island#edge"), 38 - ), 39 - /** 40 - * Type of connection (relates, supports, contradicts, etc.) 41 - */ 42 - connectionType: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 43 - /** 44 - * Optional note about the connection 45 - * @maxGraphemes 500 46 - */ 47 - note: /*#__PURE__*/ v.optional( 48 - /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 49 - /*#__PURE__*/ v.stringGraphemes(0, 500), 50 - ]), 51 - ), 52 - /** 53 - * Source vertex URI 54 - */ 55 - source: /*#__PURE__*/ v.string(), 56 - /** 57 - * Target vertex URI 58 - */ 59 - target: /*#__PURE__*/ v.string(), 60 - /** 61 - * AT URI of the connection record 62 - */ 63 - uri: /*#__PURE__*/ v.resourceUriString(), 64 - }); 65 35 const _mainSchema = /*#__PURE__*/ v.record( 66 36 /*#__PURE__*/ v.tidString(), 67 37 /*#__PURE__*/ v.object({ ··· 84 54 ); 85 55 }, 86 56 /** 87 - * The island's edges — connection records with their graph structure embedded. No lookups needed to render. 57 + * AT URIs of actual network.cosmik.connection records. The island does not embed edge source/target/type/note data. 88 58 * @maxLength 500 89 59 */ 90 - get connections() { 91 - return /*#__PURE__*/ v.optional( 92 - /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.array(edgeSchema), [ 93 - /*#__PURE__*/ v.arrayLength(0, 500), 94 - ]), 95 - ); 96 - }, 60 + connections: /*#__PURE__*/ v.constrain( 61 + /*#__PURE__*/ v.array(/*#__PURE__*/ v.resourceUriString()), 62 + [/*#__PURE__*/ v.arrayLength(0, 500)], 63 + ), 97 64 /** 98 65 * When this synthesis was created. 99 66 */ 100 67 createdAt: /*#__PURE__*/ v.datetimeString(), 101 68 /** 102 - * The source record that triggered this analysis. For islands, this is the lexmin vertex. 69 + * Original org.latha.island AT URI when this record is a fork/update of another user's island. 70 + */ 71 + forkOf: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 72 + /** 73 + * The island centroid/canonical source URL. 103 74 */ 104 75 get source() { 105 76 return sourceRefSchema; ··· 119 90 */ 120 91 cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 121 92 /** 122 - * NSID of the source collection (e.g. network.cosmik.card). 93 + * Collection being analyzed. For islands over connection graphs this is usually network.cosmik.connection. 123 94 */ 124 95 collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 125 96 /** 126 - * URI of the source. For island-level analysis, this is the lexmin vertex (canonical component reference) — can be an HTTP URL or AT URI. 97 + * Canonical HTTPS URL of the island centroid. 127 98 */ 128 - uri: /*#__PURE__*/ v.string(), 99 + uri: /*#__PURE__*/ v.genericUriString(), 129 100 }); 130 101 const _structuredAnalysisSchema = /*#__PURE__*/ v.object({ 131 102 $type: /*#__PURE__*/ v.optional( ··· 156 127 ), 157 128 ), 158 129 /** 159 - * Prose synthesis connecting the source to existing knowledge. 130 + * Prose synthesis connecting the source to existing knowledge. Legacy plain-text format. 160 131 * @maxLength 20000 161 132 * @maxGraphemes 2000 162 133 */ ··· 167 138 ]), 168 139 ), 169 140 /** 141 + * Rich-text synthesis as a pub.oxa.document record. Takes precedence over the plain-text synthesis field when present. 142 + */ 143 + synthesisDoc: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), 144 + /** 170 145 * Tensions or contradictions with existing claims. 171 146 * @maxLength 10 172 147 */ ··· 204 179 }); 205 180 206 181 type carryCrossRef$schematype = typeof _carryCrossRefSchema; 207 - type edge$schematype = typeof _edgeSchema; 208 182 type main$schematype = typeof _mainSchema; 209 183 type sourceRef$schematype = typeof _sourceRefSchema; 210 184 type structuredAnalysis$schematype = typeof _structuredAnalysisSchema; 211 185 212 186 export interface carryCrossRefSchema extends carryCrossRef$schematype {} 213 - export interface edgeSchema extends edge$schematype {} 214 187 export interface mainSchema extends main$schematype {} 215 188 export interface sourceRefSchema extends sourceRef$schematype {} 216 189 export interface structuredAnalysisSchema extends structuredAnalysis$schematype {} 217 190 218 191 export const carryCrossRefSchema = _carryCrossRefSchema as carryCrossRefSchema; 219 - export const edgeSchema = _edgeSchema as edgeSchema; 220 192 export const mainSchema = _mainSchema as mainSchema; 221 193 export const sourceRefSchema = _sourceRefSchema as sourceRefSchema; 222 194 export const structuredAnalysisSchema = ··· 225 197 export interface CarryCrossRef extends v.InferInput< 226 198 typeof carryCrossRefSchema 227 199 > {} 228 - export interface Edge extends v.InferInput<typeof edgeSchema> {} 229 200 export interface Main extends v.InferInput<typeof mainSchema> {} 230 201 export interface SourceRef extends v.InferInput<typeof sourceRefSchema> {} 231 202 export interface StructuredAnalysis extends v.InferInput<
+3 -5
src/lexicon-types/types/org/latha/island/analyze.ts
··· 7 7 $type: /*#__PURE__*/ v.optional( 8 8 /*#__PURE__*/ v.literal("org.latha.island.analyze#analysisResult"), 9 9 ), 10 - get connections() { 11 - return /*#__PURE__*/ v.optional( 12 - /*#__PURE__*/ v.array(OrgLathaIsland.connectionSchema), 13 - ); 14 - }, 10 + connections: /*#__PURE__*/ v.optional( 11 + /*#__PURE__*/ v.array(/*#__PURE__*/ v.resourceUriString()), 12 + ), 15 13 openQuestions: /*#__PURE__*/ v.optional( 16 14 /*#__PURE__*/ v.array(/*#__PURE__*/ v.string()), 17 15 ),
+186
src/lexicon-types/types/org/latha/island/card/getRecord.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + import * as NetworkCosmikCard from "../../../../network/cosmik/card.js"; 7 + 8 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 9 + $type: /*#__PURE__*/ v.optional( 10 + /*#__PURE__*/ v.literal( 11 + "org.latha.island.card.getRecord#appBskyActorProfile", 12 + ), 13 + ), 14 + /** 15 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 16 + * @accept image/png, image/jpeg 17 + * @maxSize 1000000 18 + */ 19 + avatar: /*#__PURE__*/ v.optional( 20 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 21 + /*#__PURE__*/ v.blobSize(1000000), 22 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 23 + ]), 24 + ), 25 + /** 26 + * Larger horizontal image to display behind profile view. 27 + * @accept image/png, image/jpeg 28 + * @maxSize 1000000 29 + */ 30 + banner: /*#__PURE__*/ v.optional( 31 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 32 + /*#__PURE__*/ v.blobSize(1000000), 33 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 34 + ]), 35 + ), 36 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 37 + /** 38 + * Free-form profile description text. 39 + * @maxLength 2560 40 + * @maxGraphemes 256 41 + */ 42 + description: /*#__PURE__*/ v.optional( 43 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 44 + /*#__PURE__*/ v.stringLength(0, 2560), 45 + /*#__PURE__*/ v.stringGraphemes(0, 256), 46 + ]), 47 + ), 48 + /** 49 + * @maxLength 640 50 + * @maxGraphemes 64 51 + */ 52 + displayName: /*#__PURE__*/ v.optional( 53 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 54 + /*#__PURE__*/ v.stringLength(0, 640), 55 + /*#__PURE__*/ v.stringGraphemes(0, 64), 56 + ]), 57 + ), 58 + get joinedViaStarterPack() { 59 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 60 + }, 61 + /** 62 + * Self-label values, specific to the Bluesky application, on the overall account. 63 + */ 64 + get labels() { 65 + return /*#__PURE__*/ v.optional( 66 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 67 + ); 68 + }, 69 + get pinnedPost() { 70 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 71 + }, 72 + /** 73 + * Free-form pronouns text. 74 + * @maxLength 200 75 + * @maxGraphemes 20 76 + */ 77 + pronouns: /*#__PURE__*/ v.optional( 78 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 79 + /*#__PURE__*/ v.stringLength(0, 200), 80 + /*#__PURE__*/ v.stringGraphemes(0, 20), 81 + ]), 82 + ), 83 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 84 + }); 85 + const _mainSchema = /*#__PURE__*/ v.query("org.latha.island.card.getRecord", { 86 + params: /*#__PURE__*/ v.object({ 87 + /** 88 + * Embed the referenced parentCard record 89 + */ 90 + hydrateParentCard: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 91 + /** 92 + * Include profile + identity info keyed by DID 93 + */ 94 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 95 + /** 96 + * AT URI of the record 97 + */ 98 + uri: /*#__PURE__*/ v.resourceUriString(), 99 + }), 100 + output: { 101 + type: "lex", 102 + schema: /*#__PURE__*/ v.object({ 103 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 104 + collection: /*#__PURE__*/ v.nsidString(), 105 + did: /*#__PURE__*/ v.didString(), 106 + get parentCard() { 107 + return /*#__PURE__*/ v.optional(refParentCardRecordSchema); 108 + }, 109 + get profiles() { 110 + return /*#__PURE__*/ v.optional( 111 + /*#__PURE__*/ v.array(profileEntrySchema), 112 + ); 113 + }, 114 + rkey: /*#__PURE__*/ v.string(), 115 + time_us: /*#__PURE__*/ v.integer(), 116 + uri: /*#__PURE__*/ v.resourceUriString(), 117 + get value() { 118 + return NetworkCosmikCard.mainSchema; 119 + }, 120 + }), 121 + }, 122 + }); 123 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 124 + $type: /*#__PURE__*/ v.optional( 125 + /*#__PURE__*/ v.literal("org.latha.island.card.getRecord#profileEntry"), 126 + ), 127 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 128 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 129 + did: /*#__PURE__*/ v.didString(), 130 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 131 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 132 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 133 + get value() { 134 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 135 + }, 136 + }); 137 + const _refParentCardRecordSchema = /*#__PURE__*/ v.object({ 138 + $type: /*#__PURE__*/ v.optional( 139 + /*#__PURE__*/ v.literal( 140 + "org.latha.island.card.getRecord#refParentCardRecord", 141 + ), 142 + ), 143 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 144 + collection: /*#__PURE__*/ v.nsidString(), 145 + did: /*#__PURE__*/ v.didString(), 146 + get record() { 147 + return /*#__PURE__*/ v.optional(NetworkCosmikCard.mainSchema); 148 + }, 149 + rkey: /*#__PURE__*/ v.string(), 150 + time_us: /*#__PURE__*/ v.integer(), 151 + uri: /*#__PURE__*/ v.resourceUriString(), 152 + }); 153 + 154 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 155 + type main$schematype = typeof _mainSchema; 156 + type profileEntry$schematype = typeof _profileEntrySchema; 157 + type refParentCardRecord$schematype = typeof _refParentCardRecordSchema; 158 + 159 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 160 + export interface mainSchema extends main$schematype {} 161 + export interface profileEntrySchema extends profileEntry$schematype {} 162 + export interface refParentCardRecordSchema extends refParentCardRecord$schematype {} 163 + 164 + export const appBskyActorProfileSchema = 165 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 166 + export const mainSchema = _mainSchema as mainSchema; 167 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 168 + export const refParentCardRecordSchema = 169 + _refParentCardRecordSchema as refParentCardRecordSchema; 170 + 171 + export interface AppBskyActorProfile extends v.InferInput< 172 + typeof appBskyActorProfileSchema 173 + > {} 174 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 175 + export interface RefParentCardRecord extends v.InferInput< 176 + typeof refParentCardRecordSchema 177 + > {} 178 + 179 + export interface $params extends v.InferInput<mainSchema["params"]> {} 180 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 181 + 182 + declare module "@atcute/lexicons/ambient" { 183 + interface XRPCQueries { 184 + "org.latha.island.card.getRecord": mainSchema; 185 + } 186 + }
+235
src/lexicon-types/types/org/latha/island/card/listRecords.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + import * as NetworkCosmikCard from "../../../../network/cosmik/card.js"; 7 + 8 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 9 + $type: /*#__PURE__*/ v.optional( 10 + /*#__PURE__*/ v.literal( 11 + "org.latha.island.card.listRecords#appBskyActorProfile", 12 + ), 13 + ), 14 + /** 15 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 16 + * @accept image/png, image/jpeg 17 + * @maxSize 1000000 18 + */ 19 + avatar: /*#__PURE__*/ v.optional( 20 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 21 + /*#__PURE__*/ v.blobSize(1000000), 22 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 23 + ]), 24 + ), 25 + /** 26 + * Larger horizontal image to display behind profile view. 27 + * @accept image/png, image/jpeg 28 + * @maxSize 1000000 29 + */ 30 + banner: /*#__PURE__*/ v.optional( 31 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 32 + /*#__PURE__*/ v.blobSize(1000000), 33 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 34 + ]), 35 + ), 36 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 37 + /** 38 + * Free-form profile description text. 39 + * @maxLength 2560 40 + * @maxGraphemes 256 41 + */ 42 + description: /*#__PURE__*/ v.optional( 43 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 44 + /*#__PURE__*/ v.stringLength(0, 2560), 45 + /*#__PURE__*/ v.stringGraphemes(0, 256), 46 + ]), 47 + ), 48 + /** 49 + * @maxLength 640 50 + * @maxGraphemes 64 51 + */ 52 + displayName: /*#__PURE__*/ v.optional( 53 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 54 + /*#__PURE__*/ v.stringLength(0, 640), 55 + /*#__PURE__*/ v.stringGraphemes(0, 64), 56 + ]), 57 + ), 58 + get joinedViaStarterPack() { 59 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 60 + }, 61 + /** 62 + * Self-label values, specific to the Bluesky application, on the overall account. 63 + */ 64 + get labels() { 65 + return /*#__PURE__*/ v.optional( 66 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 67 + ); 68 + }, 69 + get pinnedPost() { 70 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 71 + }, 72 + /** 73 + * Free-form pronouns text. 74 + * @maxLength 200 75 + * @maxGraphemes 20 76 + */ 77 + pronouns: /*#__PURE__*/ v.optional( 78 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 79 + /*#__PURE__*/ v.stringLength(0, 200), 80 + /*#__PURE__*/ v.stringGraphemes(0, 20), 81 + ]), 82 + ), 83 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 84 + }); 85 + const _mainSchema = /*#__PURE__*/ v.query("org.latha.island.card.listRecords", { 86 + params: /*#__PURE__*/ v.object({ 87 + /** 88 + * Filter by DID or handle (triggers on-demand backfill) 89 + */ 90 + actor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.actorIdentifierString()), 91 + /** 92 + * Filter by content.url 93 + */ 94 + contentUrl: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 95 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 96 + /** 97 + * Embed the referenced parentCard record 98 + */ 99 + hydrateParentCard: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 100 + /** 101 + * @minimum 1 102 + * @maximum 200 103 + * @default 50 104 + */ 105 + limit: /*#__PURE__*/ v.optional( 106 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 107 + /*#__PURE__*/ v.integerRange(1, 200), 108 + ]), 109 + 50, 110 + ), 111 + /** 112 + * Sort direction (default: desc for dates/numbers/counts, asc for strings) 113 + */ 114 + order: /*#__PURE__*/ v.optional( 115 + /*#__PURE__*/ v.string<"asc" | "desc" | (string & {})>(), 116 + ), 117 + /** 118 + * Include profile + identity info keyed by DID 119 + */ 120 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 121 + /** 122 + * Full-text search across: content.url, content.text, content.metadata.title, content.metadata.description 123 + */ 124 + search: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 125 + /** 126 + * Field to sort by (default: time_us) 127 + */ 128 + sort: /*#__PURE__*/ v.optional( 129 + /*#__PURE__*/ v.string<"contentUrl" | "type" | (string & {})>(), 130 + ), 131 + /** 132 + * Filter by type 133 + */ 134 + type: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 135 + }), 136 + output: { 137 + type: "lex", 138 + schema: /*#__PURE__*/ v.object({ 139 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 140 + get profiles() { 141 + return /*#__PURE__*/ v.optional( 142 + /*#__PURE__*/ v.array(profileEntrySchema), 143 + ); 144 + }, 145 + get records() { 146 + return /*#__PURE__*/ v.array(recordSchema); 147 + }, 148 + }), 149 + }, 150 + }); 151 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 152 + $type: /*#__PURE__*/ v.optional( 153 + /*#__PURE__*/ v.literal("org.latha.island.card.listRecords#profileEntry"), 154 + ), 155 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 156 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 157 + did: /*#__PURE__*/ v.didString(), 158 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 159 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 160 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 161 + get value() { 162 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 163 + }, 164 + }); 165 + const _recordSchema = /*#__PURE__*/ v.object({ 166 + $type: /*#__PURE__*/ v.optional( 167 + /*#__PURE__*/ v.literal("org.latha.island.card.listRecords#record"), 168 + ), 169 + cid: /*#__PURE__*/ v.cidString(), 170 + collection: /*#__PURE__*/ v.nsidString(), 171 + did: /*#__PURE__*/ v.didString(), 172 + get parentCard() { 173 + return /*#__PURE__*/ v.optional(refParentCardRecordSchema); 174 + }, 175 + rkey: /*#__PURE__*/ v.string(), 176 + time_us: /*#__PURE__*/ v.integer(), 177 + uri: /*#__PURE__*/ v.resourceUriString(), 178 + get value() { 179 + return NetworkCosmikCard.mainSchema; 180 + }, 181 + }); 182 + const _refParentCardRecordSchema = /*#__PURE__*/ v.object({ 183 + $type: /*#__PURE__*/ v.optional( 184 + /*#__PURE__*/ v.literal( 185 + "org.latha.island.card.listRecords#refParentCardRecord", 186 + ), 187 + ), 188 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 189 + collection: /*#__PURE__*/ v.nsidString(), 190 + did: /*#__PURE__*/ v.didString(), 191 + get record() { 192 + return /*#__PURE__*/ v.optional(NetworkCosmikCard.mainSchema); 193 + }, 194 + rkey: /*#__PURE__*/ v.string(), 195 + time_us: /*#__PURE__*/ v.integer(), 196 + uri: /*#__PURE__*/ v.resourceUriString(), 197 + }); 198 + 199 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 200 + type main$schematype = typeof _mainSchema; 201 + type profileEntry$schematype = typeof _profileEntrySchema; 202 + type record$schematype = typeof _recordSchema; 203 + type refParentCardRecord$schematype = typeof _refParentCardRecordSchema; 204 + 205 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 206 + export interface mainSchema extends main$schematype {} 207 + export interface profileEntrySchema extends profileEntry$schematype {} 208 + export interface recordSchema extends record$schematype {} 209 + export interface refParentCardRecordSchema extends refParentCardRecord$schematype {} 210 + 211 + export const appBskyActorProfileSchema = 212 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 213 + export const mainSchema = _mainSchema as mainSchema; 214 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 215 + export const recordSchema = _recordSchema as recordSchema; 216 + export const refParentCardRecordSchema = 217 + _refParentCardRecordSchema as refParentCardRecordSchema; 218 + 219 + export interface AppBskyActorProfile extends v.InferInput< 220 + typeof appBskyActorProfileSchema 221 + > {} 222 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 223 + export interface Record extends v.InferInput<typeof recordSchema> {} 224 + export interface RefParentCardRecord extends v.InferInput< 225 + typeof refParentCardRecordSchema 226 + > {} 227 + 228 + export interface $params extends v.InferInput<mainSchema["params"]> {} 229 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 230 + 231 + declare module "@atcute/lexicons/ambient" { 232 + interface XRPCQueries { 233 + "org.latha.island.card.listRecords": mainSchema; 234 + } 235 + }
+196
src/lexicon-types/types/org/latha/island/citation/getRecord.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + 7 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 8 + $type: /*#__PURE__*/ v.optional( 9 + /*#__PURE__*/ v.literal( 10 + "org.latha.island.citation.getRecord#appBskyActorProfile", 11 + ), 12 + ), 13 + /** 14 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 15 + * @accept image/png, image/jpeg 16 + * @maxSize 1000000 17 + */ 18 + avatar: /*#__PURE__*/ v.optional( 19 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 20 + /*#__PURE__*/ v.blobSize(1000000), 21 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 22 + ]), 23 + ), 24 + /** 25 + * Larger horizontal image to display behind profile view. 26 + * @accept image/png, image/jpeg 27 + * @maxSize 1000000 28 + */ 29 + banner: /*#__PURE__*/ v.optional( 30 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 31 + /*#__PURE__*/ v.blobSize(1000000), 32 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 33 + ]), 34 + ), 35 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 36 + /** 37 + * Free-form profile description text. 38 + * @maxLength 2560 39 + * @maxGraphemes 256 40 + */ 41 + description: /*#__PURE__*/ v.optional( 42 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 43 + /*#__PURE__*/ v.stringLength(0, 2560), 44 + /*#__PURE__*/ v.stringGraphemes(0, 256), 45 + ]), 46 + ), 47 + /** 48 + * @maxLength 640 49 + * @maxGraphemes 64 50 + */ 51 + displayName: /*#__PURE__*/ v.optional( 52 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 53 + /*#__PURE__*/ v.stringLength(0, 640), 54 + /*#__PURE__*/ v.stringGraphemes(0, 64), 55 + ]), 56 + ), 57 + get joinedViaStarterPack() { 58 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 59 + }, 60 + /** 61 + * Self-label values, specific to the Bluesky application, on the overall account. 62 + */ 63 + get labels() { 64 + return /*#__PURE__*/ v.optional( 65 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 66 + ); 67 + }, 68 + get pinnedPost() { 69 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 70 + }, 71 + /** 72 + * Free-form pronouns text. 73 + * @maxLength 200 74 + * @maxGraphemes 20 75 + */ 76 + pronouns: /*#__PURE__*/ v.optional( 77 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 78 + /*#__PURE__*/ v.stringLength(0, 200), 79 + /*#__PURE__*/ v.stringGraphemes(0, 20), 80 + ]), 81 + ), 82 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 83 + }); 84 + const _hydrateReasoningRecordSchema = /*#__PURE__*/ v.object({ 85 + $type: /*#__PURE__*/ v.optional( 86 + /*#__PURE__*/ v.literal( 87 + "org.latha.island.citation.getRecord#hydrateReasoningRecord", 88 + ), 89 + ), 90 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 91 + collection: /*#__PURE__*/ v.nsidString(), 92 + did: /*#__PURE__*/ v.didString(), 93 + record: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), 94 + rkey: /*#__PURE__*/ v.string(), 95 + time_us: /*#__PURE__*/ v.integer(), 96 + uri: /*#__PURE__*/ v.resourceUriString(), 97 + }); 98 + const _mainSchema = /*#__PURE__*/ v.query( 99 + "org.latha.island.citation.getRecord", 100 + { 101 + params: /*#__PURE__*/ v.object({ 102 + /** 103 + * Number of reasoning records to embed 104 + * @minimum 1 105 + * @maximum 50 106 + */ 107 + hydrateReasoning: /*#__PURE__*/ v.optional( 108 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 109 + /*#__PURE__*/ v.integerRange(1, 50), 110 + ]), 111 + ), 112 + /** 113 + * Include profile + identity info keyed by DID 114 + */ 115 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 116 + /** 117 + * AT URI of the record 118 + */ 119 + uri: /*#__PURE__*/ v.resourceUriString(), 120 + }), 121 + output: { 122 + type: "lex", 123 + schema: /*#__PURE__*/ v.object({ 124 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 125 + collection: /*#__PURE__*/ v.nsidString(), 126 + did: /*#__PURE__*/ v.didString(), 127 + get profiles() { 128 + return /*#__PURE__*/ v.optional( 129 + /*#__PURE__*/ v.array(profileEntrySchema), 130 + ); 131 + }, 132 + get reasoning() { 133 + return /*#__PURE__*/ v.optional( 134 + /*#__PURE__*/ v.array(hydrateReasoningRecordSchema), 135 + ); 136 + }, 137 + /** 138 + * Total reasoning count 139 + */ 140 + reasoningCount: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 141 + rkey: /*#__PURE__*/ v.string(), 142 + time_us: /*#__PURE__*/ v.integer(), 143 + uri: /*#__PURE__*/ v.resourceUriString(), 144 + value: /*#__PURE__*/ v.unknown(), 145 + }), 146 + }, 147 + }, 148 + ); 149 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 150 + $type: /*#__PURE__*/ v.optional( 151 + /*#__PURE__*/ v.literal("org.latha.island.citation.getRecord#profileEntry"), 152 + ), 153 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 154 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 155 + did: /*#__PURE__*/ v.didString(), 156 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 157 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 158 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 159 + get value() { 160 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 161 + }, 162 + }); 163 + 164 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 165 + type hydrateReasoningRecord$schematype = typeof _hydrateReasoningRecordSchema; 166 + type main$schematype = typeof _mainSchema; 167 + type profileEntry$schematype = typeof _profileEntrySchema; 168 + 169 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 170 + export interface hydrateReasoningRecordSchema extends hydrateReasoningRecord$schematype {} 171 + export interface mainSchema extends main$schematype {} 172 + export interface profileEntrySchema extends profileEntry$schematype {} 173 + 174 + export const appBskyActorProfileSchema = 175 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 176 + export const hydrateReasoningRecordSchema = 177 + _hydrateReasoningRecordSchema as hydrateReasoningRecordSchema; 178 + export const mainSchema = _mainSchema as mainSchema; 179 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 180 + 181 + export interface AppBskyActorProfile extends v.InferInput< 182 + typeof appBskyActorProfileSchema 183 + > {} 184 + export interface HydrateReasoningRecord extends v.InferInput< 185 + typeof hydrateReasoningRecordSchema 186 + > {} 187 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 188 + 189 + export interface $params extends v.InferInput<mainSchema["params"]> {} 190 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 191 + 192 + declare module "@atcute/lexicons/ambient" { 193 + interface XRPCQueries { 194 + "org.latha.island.citation.getRecord": mainSchema; 195 + } 196 + }
+257
src/lexicon-types/types/org/latha/island/citation/listRecords.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + 7 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 8 + $type: /*#__PURE__*/ v.optional( 9 + /*#__PURE__*/ v.literal( 10 + "org.latha.island.citation.listRecords#appBskyActorProfile", 11 + ), 12 + ), 13 + /** 14 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 15 + * @accept image/png, image/jpeg 16 + * @maxSize 1000000 17 + */ 18 + avatar: /*#__PURE__*/ v.optional( 19 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 20 + /*#__PURE__*/ v.blobSize(1000000), 21 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 22 + ]), 23 + ), 24 + /** 25 + * Larger horizontal image to display behind profile view. 26 + * @accept image/png, image/jpeg 27 + * @maxSize 1000000 28 + */ 29 + banner: /*#__PURE__*/ v.optional( 30 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 31 + /*#__PURE__*/ v.blobSize(1000000), 32 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 33 + ]), 34 + ), 35 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 36 + /** 37 + * Free-form profile description text. 38 + * @maxLength 2560 39 + * @maxGraphemes 256 40 + */ 41 + description: /*#__PURE__*/ v.optional( 42 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 43 + /*#__PURE__*/ v.stringLength(0, 2560), 44 + /*#__PURE__*/ v.stringGraphemes(0, 256), 45 + ]), 46 + ), 47 + /** 48 + * @maxLength 640 49 + * @maxGraphemes 64 50 + */ 51 + displayName: /*#__PURE__*/ v.optional( 52 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 53 + /*#__PURE__*/ v.stringLength(0, 640), 54 + /*#__PURE__*/ v.stringGraphemes(0, 64), 55 + ]), 56 + ), 57 + get joinedViaStarterPack() { 58 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 59 + }, 60 + /** 61 + * Self-label values, specific to the Bluesky application, on the overall account. 62 + */ 63 + get labels() { 64 + return /*#__PURE__*/ v.optional( 65 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 66 + ); 67 + }, 68 + get pinnedPost() { 69 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 70 + }, 71 + /** 72 + * Free-form pronouns text. 73 + * @maxLength 200 74 + * @maxGraphemes 20 75 + */ 76 + pronouns: /*#__PURE__*/ v.optional( 77 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 78 + /*#__PURE__*/ v.stringLength(0, 200), 79 + /*#__PURE__*/ v.stringGraphemes(0, 20), 80 + ]), 81 + ), 82 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 83 + }); 84 + const _hydrateReasoningRecordSchema = /*#__PURE__*/ v.object({ 85 + $type: /*#__PURE__*/ v.optional( 86 + /*#__PURE__*/ v.literal( 87 + "org.latha.island.citation.listRecords#hydrateReasoningRecord", 88 + ), 89 + ), 90 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 91 + collection: /*#__PURE__*/ v.nsidString(), 92 + did: /*#__PURE__*/ v.didString(), 93 + record: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), 94 + rkey: /*#__PURE__*/ v.string(), 95 + time_us: /*#__PURE__*/ v.integer(), 96 + uri: /*#__PURE__*/ v.resourceUriString(), 97 + }); 98 + const _mainSchema = /*#__PURE__*/ v.query( 99 + "org.latha.island.citation.listRecords", 100 + { 101 + params: /*#__PURE__*/ v.object({ 102 + /** 103 + * Filter by DID or handle (triggers on-demand backfill) 104 + */ 105 + actor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.actorIdentifierString()), 106 + /** 107 + * Filter by confidence 108 + */ 109 + confidence: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 110 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 111 + /** 112 + * Filter by domain 113 + */ 114 + domain: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 115 + /** 116 + * Number of reasoning records to embed per record 117 + * @minimum 1 118 + * @maximum 50 119 + */ 120 + hydrateReasoning: /*#__PURE__*/ v.optional( 121 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 122 + /*#__PURE__*/ v.integerRange(1, 50), 123 + ]), 124 + ), 125 + /** 126 + * @minimum 1 127 + * @maximum 200 128 + * @default 50 129 + */ 130 + limit: /*#__PURE__*/ v.optional( 131 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 132 + /*#__PURE__*/ v.integerRange(1, 200), 133 + ]), 134 + 50, 135 + ), 136 + /** 137 + * Sort direction (default: desc for dates/numbers/counts, asc for strings) 138 + */ 139 + order: /*#__PURE__*/ v.optional( 140 + /*#__PURE__*/ v.string<"asc" | "desc" | (string & {})>(), 141 + ), 142 + /** 143 + * Include profile + identity info keyed by DID 144 + */ 145 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 146 + /** 147 + * Minimum total reasoning count 148 + */ 149 + reasoningCountMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 150 + /** 151 + * Full-text search across: title, takeaway 152 + */ 153 + search: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 154 + /** 155 + * Field to sort by (default: time_us) 156 + */ 157 + sort: /*#__PURE__*/ v.optional( 158 + /*#__PURE__*/ v.string< 159 + "confidence" | "domain" | "reasoningCount" | "url" | (string & {}) 160 + >(), 161 + ), 162 + /** 163 + * Filter by url 164 + */ 165 + url: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 166 + }), 167 + output: { 168 + type: "lex", 169 + schema: /*#__PURE__*/ v.object({ 170 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 171 + get profiles() { 172 + return /*#__PURE__*/ v.optional( 173 + /*#__PURE__*/ v.array(profileEntrySchema), 174 + ); 175 + }, 176 + get records() { 177 + return /*#__PURE__*/ v.array(recordSchema); 178 + }, 179 + }), 180 + }, 181 + }, 182 + ); 183 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 184 + $type: /*#__PURE__*/ v.optional( 185 + /*#__PURE__*/ v.literal( 186 + "org.latha.island.citation.listRecords#profileEntry", 187 + ), 188 + ), 189 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 190 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 191 + did: /*#__PURE__*/ v.didString(), 192 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 193 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 194 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 195 + get value() { 196 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 197 + }, 198 + }); 199 + const _recordSchema = /*#__PURE__*/ v.object({ 200 + $type: /*#__PURE__*/ v.optional( 201 + /*#__PURE__*/ v.literal("org.latha.island.citation.listRecords#record"), 202 + ), 203 + cid: /*#__PURE__*/ v.cidString(), 204 + collection: /*#__PURE__*/ v.nsidString(), 205 + did: /*#__PURE__*/ v.didString(), 206 + get reasoning() { 207 + return /*#__PURE__*/ v.optional( 208 + /*#__PURE__*/ v.array(hydrateReasoningRecordSchema), 209 + ); 210 + }, 211 + /** 212 + * Total reasoning count 213 + */ 214 + reasoningCount: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 215 + rkey: /*#__PURE__*/ v.string(), 216 + time_us: /*#__PURE__*/ v.integer(), 217 + uri: /*#__PURE__*/ v.resourceUriString(), 218 + value: /*#__PURE__*/ v.unknown(), 219 + }); 220 + 221 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 222 + type hydrateReasoningRecord$schematype = typeof _hydrateReasoningRecordSchema; 223 + type main$schematype = typeof _mainSchema; 224 + type profileEntry$schematype = typeof _profileEntrySchema; 225 + type record$schematype = typeof _recordSchema; 226 + 227 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 228 + export interface hydrateReasoningRecordSchema extends hydrateReasoningRecord$schematype {} 229 + export interface mainSchema extends main$schematype {} 230 + export interface profileEntrySchema extends profileEntry$schematype {} 231 + export interface recordSchema extends record$schematype {} 232 + 233 + export const appBskyActorProfileSchema = 234 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 235 + export const hydrateReasoningRecordSchema = 236 + _hydrateReasoningRecordSchema as hydrateReasoningRecordSchema; 237 + export const mainSchema = _mainSchema as mainSchema; 238 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 239 + export const recordSchema = _recordSchema as recordSchema; 240 + 241 + export interface AppBskyActorProfile extends v.InferInput< 242 + typeof appBskyActorProfileSchema 243 + > {} 244 + export interface HydrateReasoningRecord extends v.InferInput< 245 + typeof hydrateReasoningRecordSchema 246 + > {} 247 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 248 + export interface Record extends v.InferInput<typeof recordSchema> {} 249 + 250 + export interface $params extends v.InferInput<mainSchema["params"]> {} 251 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 252 + 253 + declare module "@atcute/lexicons/ambient" { 254 + interface XRPCQueries { 255 + "org.latha.island.citation.listRecords": mainSchema; 256 + } 257 + }
+204
src/lexicon-types/types/org/latha/island/collection/getRecord.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + import * as NetworkCosmikCard from "../../../../network/cosmik/card.js"; 7 + import * as NetworkCosmikCollection from "../../../../network/cosmik/collection.js"; 8 + 9 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 10 + $type: /*#__PURE__*/ v.optional( 11 + /*#__PURE__*/ v.literal( 12 + "org.latha.island.collection.getRecord#appBskyActorProfile", 13 + ), 14 + ), 15 + /** 16 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 17 + * @accept image/png, image/jpeg 18 + * @maxSize 1000000 19 + */ 20 + avatar: /*#__PURE__*/ v.optional( 21 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 22 + /*#__PURE__*/ v.blobSize(1000000), 23 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 24 + ]), 25 + ), 26 + /** 27 + * Larger horizontal image to display behind profile view. 28 + * @accept image/png, image/jpeg 29 + * @maxSize 1000000 30 + */ 31 + banner: /*#__PURE__*/ v.optional( 32 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 33 + /*#__PURE__*/ v.blobSize(1000000), 34 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 35 + ]), 36 + ), 37 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 38 + /** 39 + * Free-form profile description text. 40 + * @maxLength 2560 41 + * @maxGraphemes 256 42 + */ 43 + description: /*#__PURE__*/ v.optional( 44 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 45 + /*#__PURE__*/ v.stringLength(0, 2560), 46 + /*#__PURE__*/ v.stringGraphemes(0, 256), 47 + ]), 48 + ), 49 + /** 50 + * @maxLength 640 51 + * @maxGraphemes 64 52 + */ 53 + displayName: /*#__PURE__*/ v.optional( 54 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 55 + /*#__PURE__*/ v.stringLength(0, 640), 56 + /*#__PURE__*/ v.stringGraphemes(0, 64), 57 + ]), 58 + ), 59 + get joinedViaStarterPack() { 60 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 61 + }, 62 + /** 63 + * Self-label values, specific to the Bluesky application, on the overall account. 64 + */ 65 + get labels() { 66 + return /*#__PURE__*/ v.optional( 67 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 68 + ); 69 + }, 70 + get pinnedPost() { 71 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 72 + }, 73 + /** 74 + * Free-form pronouns text. 75 + * @maxLength 200 76 + * @maxGraphemes 20 77 + */ 78 + pronouns: /*#__PURE__*/ v.optional( 79 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 80 + /*#__PURE__*/ v.stringLength(0, 200), 81 + /*#__PURE__*/ v.stringGraphemes(0, 20), 82 + ]), 83 + ), 84 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 85 + }); 86 + const _hydrateCardsRecordSchema = /*#__PURE__*/ v.object({ 87 + $type: /*#__PURE__*/ v.optional( 88 + /*#__PURE__*/ v.literal( 89 + "org.latha.island.collection.getRecord#hydrateCardsRecord", 90 + ), 91 + ), 92 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 93 + collection: /*#__PURE__*/ v.nsidString(), 94 + did: /*#__PURE__*/ v.didString(), 95 + get record() { 96 + return /*#__PURE__*/ v.optional(NetworkCosmikCard.mainSchema); 97 + }, 98 + rkey: /*#__PURE__*/ v.string(), 99 + time_us: /*#__PURE__*/ v.integer(), 100 + uri: /*#__PURE__*/ v.resourceUriString(), 101 + }); 102 + const _mainSchema = /*#__PURE__*/ v.query( 103 + "org.latha.island.collection.getRecord", 104 + { 105 + params: /*#__PURE__*/ v.object({ 106 + /** 107 + * Number of cards records to embed 108 + * @minimum 1 109 + * @maximum 50 110 + */ 111 + hydrateCards: /*#__PURE__*/ v.optional( 112 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 113 + /*#__PURE__*/ v.integerRange(1, 50), 114 + ]), 115 + ), 116 + /** 117 + * Include profile + identity info keyed by DID 118 + */ 119 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 120 + /** 121 + * AT URI of the record 122 + */ 123 + uri: /*#__PURE__*/ v.resourceUriString(), 124 + }), 125 + output: { 126 + type: "lex", 127 + schema: /*#__PURE__*/ v.object({ 128 + get cards() { 129 + return /*#__PURE__*/ v.optional( 130 + /*#__PURE__*/ v.array(hydrateCardsRecordSchema), 131 + ); 132 + }, 133 + /** 134 + * Total cards count 135 + */ 136 + cardsCount: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 137 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 138 + collection: /*#__PURE__*/ v.nsidString(), 139 + did: /*#__PURE__*/ v.didString(), 140 + get profiles() { 141 + return /*#__PURE__*/ v.optional( 142 + /*#__PURE__*/ v.array(profileEntrySchema), 143 + ); 144 + }, 145 + rkey: /*#__PURE__*/ v.string(), 146 + time_us: /*#__PURE__*/ v.integer(), 147 + uri: /*#__PURE__*/ v.resourceUriString(), 148 + get value() { 149 + return NetworkCosmikCollection.mainSchema; 150 + }, 151 + }), 152 + }, 153 + }, 154 + ); 155 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 156 + $type: /*#__PURE__*/ v.optional( 157 + /*#__PURE__*/ v.literal( 158 + "org.latha.island.collection.getRecord#profileEntry", 159 + ), 160 + ), 161 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 162 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 163 + did: /*#__PURE__*/ v.didString(), 164 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 165 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 166 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 167 + get value() { 168 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 169 + }, 170 + }); 171 + 172 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 173 + type hydrateCardsRecord$schematype = typeof _hydrateCardsRecordSchema; 174 + type main$schematype = typeof _mainSchema; 175 + type profileEntry$schematype = typeof _profileEntrySchema; 176 + 177 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 178 + export interface hydrateCardsRecordSchema extends hydrateCardsRecord$schematype {} 179 + export interface mainSchema extends main$schematype {} 180 + export interface profileEntrySchema extends profileEntry$schematype {} 181 + 182 + export const appBskyActorProfileSchema = 183 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 184 + export const hydrateCardsRecordSchema = 185 + _hydrateCardsRecordSchema as hydrateCardsRecordSchema; 186 + export const mainSchema = _mainSchema as mainSchema; 187 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 188 + 189 + export interface AppBskyActorProfile extends v.InferInput< 190 + typeof appBskyActorProfileSchema 191 + > {} 192 + export interface HydrateCardsRecord extends v.InferInput< 193 + typeof hydrateCardsRecordSchema 194 + > {} 195 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 196 + 197 + export interface $params extends v.InferInput<mainSchema["params"]> {} 198 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 199 + 200 + declare module "@atcute/lexicons/ambient" { 201 + interface XRPCQueries { 202 + "org.latha.island.collection.getRecord": mainSchema; 203 + } 204 + }
+259
src/lexicon-types/types/org/latha/island/collection/listRecords.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + import * as NetworkCosmikCard from "../../../../network/cosmik/card.js"; 7 + import * as NetworkCosmikCollection from "../../../../network/cosmik/collection.js"; 8 + 9 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 10 + $type: /*#__PURE__*/ v.optional( 11 + /*#__PURE__*/ v.literal( 12 + "org.latha.island.collection.listRecords#appBskyActorProfile", 13 + ), 14 + ), 15 + /** 16 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 17 + * @accept image/png, image/jpeg 18 + * @maxSize 1000000 19 + */ 20 + avatar: /*#__PURE__*/ v.optional( 21 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 22 + /*#__PURE__*/ v.blobSize(1000000), 23 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 24 + ]), 25 + ), 26 + /** 27 + * Larger horizontal image to display behind profile view. 28 + * @accept image/png, image/jpeg 29 + * @maxSize 1000000 30 + */ 31 + banner: /*#__PURE__*/ v.optional( 32 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 33 + /*#__PURE__*/ v.blobSize(1000000), 34 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 35 + ]), 36 + ), 37 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 38 + /** 39 + * Free-form profile description text. 40 + * @maxLength 2560 41 + * @maxGraphemes 256 42 + */ 43 + description: /*#__PURE__*/ v.optional( 44 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 45 + /*#__PURE__*/ v.stringLength(0, 2560), 46 + /*#__PURE__*/ v.stringGraphemes(0, 256), 47 + ]), 48 + ), 49 + /** 50 + * @maxLength 640 51 + * @maxGraphemes 64 52 + */ 53 + displayName: /*#__PURE__*/ v.optional( 54 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 55 + /*#__PURE__*/ v.stringLength(0, 640), 56 + /*#__PURE__*/ v.stringGraphemes(0, 64), 57 + ]), 58 + ), 59 + get joinedViaStarterPack() { 60 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 61 + }, 62 + /** 63 + * Self-label values, specific to the Bluesky application, on the overall account. 64 + */ 65 + get labels() { 66 + return /*#__PURE__*/ v.optional( 67 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 68 + ); 69 + }, 70 + get pinnedPost() { 71 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 72 + }, 73 + /** 74 + * Free-form pronouns text. 75 + * @maxLength 200 76 + * @maxGraphemes 20 77 + */ 78 + pronouns: /*#__PURE__*/ v.optional( 79 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 80 + /*#__PURE__*/ v.stringLength(0, 200), 81 + /*#__PURE__*/ v.stringGraphemes(0, 20), 82 + ]), 83 + ), 84 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 85 + }); 86 + const _hydrateCardsRecordSchema = /*#__PURE__*/ v.object({ 87 + $type: /*#__PURE__*/ v.optional( 88 + /*#__PURE__*/ v.literal( 89 + "org.latha.island.collection.listRecords#hydrateCardsRecord", 90 + ), 91 + ), 92 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 93 + collection: /*#__PURE__*/ v.nsidString(), 94 + did: /*#__PURE__*/ v.didString(), 95 + get record() { 96 + return /*#__PURE__*/ v.optional(NetworkCosmikCard.mainSchema); 97 + }, 98 + rkey: /*#__PURE__*/ v.string(), 99 + time_us: /*#__PURE__*/ v.integer(), 100 + uri: /*#__PURE__*/ v.resourceUriString(), 101 + }); 102 + const _mainSchema = /*#__PURE__*/ v.query( 103 + "org.latha.island.collection.listRecords", 104 + { 105 + params: /*#__PURE__*/ v.object({ 106 + /** 107 + * Filter by accessType 108 + */ 109 + accessType: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 110 + /** 111 + * Filter by DID or handle (triggers on-demand backfill) 112 + */ 113 + actor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.actorIdentifierString()), 114 + /** 115 + * Minimum total cards count 116 + */ 117 + cardsCountMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 118 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 119 + /** 120 + * Number of cards records to embed per record 121 + * @minimum 1 122 + * @maximum 50 123 + */ 124 + hydrateCards: /*#__PURE__*/ v.optional( 125 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 126 + /*#__PURE__*/ v.integerRange(1, 50), 127 + ]), 128 + ), 129 + /** 130 + * @minimum 1 131 + * @maximum 200 132 + * @default 50 133 + */ 134 + limit: /*#__PURE__*/ v.optional( 135 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 136 + /*#__PURE__*/ v.integerRange(1, 200), 137 + ]), 138 + 50, 139 + ), 140 + /** 141 + * Filter by name 142 + */ 143 + name: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 144 + /** 145 + * Sort direction (default: desc for dates/numbers/counts, asc for strings) 146 + */ 147 + order: /*#__PURE__*/ v.optional( 148 + /*#__PURE__*/ v.string<"asc" | "desc" | (string & {})>(), 149 + ), 150 + /** 151 + * Include profile + identity info keyed by DID 152 + */ 153 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 154 + /** 155 + * Full-text search across: name, description 156 + */ 157 + search: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 158 + /** 159 + * Field to sort by (default: time_us) 160 + */ 161 + sort: /*#__PURE__*/ v.optional( 162 + /*#__PURE__*/ v.string< 163 + "accessType" | "cardsCount" | "name" | (string & {}) 164 + >(), 165 + ), 166 + }), 167 + output: { 168 + type: "lex", 169 + schema: /*#__PURE__*/ v.object({ 170 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 171 + get profiles() { 172 + return /*#__PURE__*/ v.optional( 173 + /*#__PURE__*/ v.array(profileEntrySchema), 174 + ); 175 + }, 176 + get records() { 177 + return /*#__PURE__*/ v.array(recordSchema); 178 + }, 179 + }), 180 + }, 181 + }, 182 + ); 183 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 184 + $type: /*#__PURE__*/ v.optional( 185 + /*#__PURE__*/ v.literal( 186 + "org.latha.island.collection.listRecords#profileEntry", 187 + ), 188 + ), 189 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 190 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 191 + did: /*#__PURE__*/ v.didString(), 192 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 193 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 194 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 195 + get value() { 196 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 197 + }, 198 + }); 199 + const _recordSchema = /*#__PURE__*/ v.object({ 200 + $type: /*#__PURE__*/ v.optional( 201 + /*#__PURE__*/ v.literal("org.latha.island.collection.listRecords#record"), 202 + ), 203 + get cards() { 204 + return /*#__PURE__*/ v.optional( 205 + /*#__PURE__*/ v.array(hydrateCardsRecordSchema), 206 + ); 207 + }, 208 + /** 209 + * Total cards count 210 + */ 211 + cardsCount: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 212 + cid: /*#__PURE__*/ v.cidString(), 213 + collection: /*#__PURE__*/ v.nsidString(), 214 + did: /*#__PURE__*/ v.didString(), 215 + rkey: /*#__PURE__*/ v.string(), 216 + time_us: /*#__PURE__*/ v.integer(), 217 + uri: /*#__PURE__*/ v.resourceUriString(), 218 + get value() { 219 + return NetworkCosmikCollection.mainSchema; 220 + }, 221 + }); 222 + 223 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 224 + type hydrateCardsRecord$schematype = typeof _hydrateCardsRecordSchema; 225 + type main$schematype = typeof _mainSchema; 226 + type profileEntry$schematype = typeof _profileEntrySchema; 227 + type record$schematype = typeof _recordSchema; 228 + 229 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 230 + export interface hydrateCardsRecordSchema extends hydrateCardsRecord$schematype {} 231 + export interface mainSchema extends main$schematype {} 232 + export interface profileEntrySchema extends profileEntry$schematype {} 233 + export interface recordSchema extends record$schematype {} 234 + 235 + export const appBskyActorProfileSchema = 236 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 237 + export const hydrateCardsRecordSchema = 238 + _hydrateCardsRecordSchema as hydrateCardsRecordSchema; 239 + export const mainSchema = _mainSchema as mainSchema; 240 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 241 + export const recordSchema = _recordSchema as recordSchema; 242 + 243 + export interface AppBskyActorProfile extends v.InferInput< 244 + typeof appBskyActorProfileSchema 245 + > {} 246 + export interface HydrateCardsRecord extends v.InferInput< 247 + typeof hydrateCardsRecordSchema 248 + > {} 249 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 250 + export interface Record extends v.InferInput<typeof recordSchema> {} 251 + 252 + export interface $params extends v.InferInput<mainSchema["params"]> {} 253 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 254 + 255 + declare module "@atcute/lexicons/ambient" { 256 + interface XRPCQueries { 257 + "org.latha.island.collection.listRecords": mainSchema; 258 + } 259 + }
+161
src/lexicon-types/types/org/latha/island/connection/getRecord.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + import * as NetworkCosmikConnection from "../../../../network/cosmik/connection.js"; 7 + 8 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 9 + $type: /*#__PURE__*/ v.optional( 10 + /*#__PURE__*/ v.literal( 11 + "org.latha.island.connection.getRecord#appBskyActorProfile", 12 + ), 13 + ), 14 + /** 15 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 16 + * @accept image/png, image/jpeg 17 + * @maxSize 1000000 18 + */ 19 + avatar: /*#__PURE__*/ v.optional( 20 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 21 + /*#__PURE__*/ v.blobSize(1000000), 22 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 23 + ]), 24 + ), 25 + /** 26 + * Larger horizontal image to display behind profile view. 27 + * @accept image/png, image/jpeg 28 + * @maxSize 1000000 29 + */ 30 + banner: /*#__PURE__*/ v.optional( 31 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 32 + /*#__PURE__*/ v.blobSize(1000000), 33 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 34 + ]), 35 + ), 36 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 37 + /** 38 + * Free-form profile description text. 39 + * @maxLength 2560 40 + * @maxGraphemes 256 41 + */ 42 + description: /*#__PURE__*/ v.optional( 43 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 44 + /*#__PURE__*/ v.stringLength(0, 2560), 45 + /*#__PURE__*/ v.stringGraphemes(0, 256), 46 + ]), 47 + ), 48 + /** 49 + * @maxLength 640 50 + * @maxGraphemes 64 51 + */ 52 + displayName: /*#__PURE__*/ v.optional( 53 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 54 + /*#__PURE__*/ v.stringLength(0, 640), 55 + /*#__PURE__*/ v.stringGraphemes(0, 64), 56 + ]), 57 + ), 58 + get joinedViaStarterPack() { 59 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 60 + }, 61 + /** 62 + * Self-label values, specific to the Bluesky application, on the overall account. 63 + */ 64 + get labels() { 65 + return /*#__PURE__*/ v.optional( 66 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 67 + ); 68 + }, 69 + get pinnedPost() { 70 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 71 + }, 72 + /** 73 + * Free-form pronouns text. 74 + * @maxLength 200 75 + * @maxGraphemes 20 76 + */ 77 + pronouns: /*#__PURE__*/ v.optional( 78 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 79 + /*#__PURE__*/ v.stringLength(0, 200), 80 + /*#__PURE__*/ v.stringGraphemes(0, 20), 81 + ]), 82 + ), 83 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 84 + }); 85 + const _mainSchema = /*#__PURE__*/ v.query( 86 + "org.latha.island.connection.getRecord", 87 + { 88 + params: /*#__PURE__*/ v.object({ 89 + /** 90 + * Include profile + identity info keyed by DID 91 + */ 92 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 93 + /** 94 + * AT URI of the record 95 + */ 96 + uri: /*#__PURE__*/ v.resourceUriString(), 97 + }), 98 + output: { 99 + type: "lex", 100 + schema: /*#__PURE__*/ v.object({ 101 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 102 + collection: /*#__PURE__*/ v.nsidString(), 103 + did: /*#__PURE__*/ v.didString(), 104 + get profiles() { 105 + return /*#__PURE__*/ v.optional( 106 + /*#__PURE__*/ v.array(profileEntrySchema), 107 + ); 108 + }, 109 + rkey: /*#__PURE__*/ v.string(), 110 + time_us: /*#__PURE__*/ v.integer(), 111 + uri: /*#__PURE__*/ v.resourceUriString(), 112 + get value() { 113 + return NetworkCosmikConnection.mainSchema; 114 + }, 115 + }), 116 + }, 117 + }, 118 + ); 119 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 120 + $type: /*#__PURE__*/ v.optional( 121 + /*#__PURE__*/ v.literal( 122 + "org.latha.island.connection.getRecord#profileEntry", 123 + ), 124 + ), 125 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 126 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 127 + did: /*#__PURE__*/ v.didString(), 128 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 129 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 130 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 131 + get value() { 132 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 133 + }, 134 + }); 135 + 136 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 137 + type main$schematype = typeof _mainSchema; 138 + type profileEntry$schematype = typeof _profileEntrySchema; 139 + 140 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 141 + export interface mainSchema extends main$schematype {} 142 + export interface profileEntrySchema extends profileEntry$schematype {} 143 + 144 + export const appBskyActorProfileSchema = 145 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 146 + export const mainSchema = _mainSchema as mainSchema; 147 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 148 + 149 + export interface AppBskyActorProfile extends v.InferInput< 150 + typeof appBskyActorProfileSchema 151 + > {} 152 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 153 + 154 + export interface $params extends v.InferInput<mainSchema["params"]> {} 155 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 156 + 157 + declare module "@atcute/lexicons/ambient" { 158 + interface XRPCQueries { 159 + "org.latha.island.connection.getRecord": mainSchema; 160 + } 161 + }
+216
src/lexicon-types/types/org/latha/island/connection/listRecords.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + import * as NetworkCosmikConnection from "../../../../network/cosmik/connection.js"; 7 + 8 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 9 + $type: /*#__PURE__*/ v.optional( 10 + /*#__PURE__*/ v.literal( 11 + "org.latha.island.connection.listRecords#appBskyActorProfile", 12 + ), 13 + ), 14 + /** 15 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 16 + * @accept image/png, image/jpeg 17 + * @maxSize 1000000 18 + */ 19 + avatar: /*#__PURE__*/ v.optional( 20 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 21 + /*#__PURE__*/ v.blobSize(1000000), 22 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 23 + ]), 24 + ), 25 + /** 26 + * Larger horizontal image to display behind profile view. 27 + * @accept image/png, image/jpeg 28 + * @maxSize 1000000 29 + */ 30 + banner: /*#__PURE__*/ v.optional( 31 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 32 + /*#__PURE__*/ v.blobSize(1000000), 33 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 34 + ]), 35 + ), 36 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 37 + /** 38 + * Free-form profile description text. 39 + * @maxLength 2560 40 + * @maxGraphemes 256 41 + */ 42 + description: /*#__PURE__*/ v.optional( 43 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 44 + /*#__PURE__*/ v.stringLength(0, 2560), 45 + /*#__PURE__*/ v.stringGraphemes(0, 256), 46 + ]), 47 + ), 48 + /** 49 + * @maxLength 640 50 + * @maxGraphemes 64 51 + */ 52 + displayName: /*#__PURE__*/ v.optional( 53 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 54 + /*#__PURE__*/ v.stringLength(0, 640), 55 + /*#__PURE__*/ v.stringGraphemes(0, 64), 56 + ]), 57 + ), 58 + get joinedViaStarterPack() { 59 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 60 + }, 61 + /** 62 + * Self-label values, specific to the Bluesky application, on the overall account. 63 + */ 64 + get labels() { 65 + return /*#__PURE__*/ v.optional( 66 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 67 + ); 68 + }, 69 + get pinnedPost() { 70 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 71 + }, 72 + /** 73 + * Free-form pronouns text. 74 + * @maxLength 200 75 + * @maxGraphemes 20 76 + */ 77 + pronouns: /*#__PURE__*/ v.optional( 78 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 79 + /*#__PURE__*/ v.stringLength(0, 200), 80 + /*#__PURE__*/ v.stringGraphemes(0, 20), 81 + ]), 82 + ), 83 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 84 + }); 85 + const _mainSchema = /*#__PURE__*/ v.query( 86 + "org.latha.island.connection.listRecords", 87 + { 88 + params: /*#__PURE__*/ v.object({ 89 + /** 90 + * Filter by DID or handle (triggers on-demand backfill) 91 + */ 92 + actor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.actorIdentifierString()), 93 + /** 94 + * Filter by connectionType 95 + */ 96 + connectionType: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 97 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 98 + /** 99 + * @minimum 1 100 + * @maximum 200 101 + * @default 50 102 + */ 103 + limit: /*#__PURE__*/ v.optional( 104 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 105 + /*#__PURE__*/ v.integerRange(1, 200), 106 + ]), 107 + 50, 108 + ), 109 + /** 110 + * Sort direction (default: desc for dates/numbers/counts, asc for strings) 111 + */ 112 + order: /*#__PURE__*/ v.optional( 113 + /*#__PURE__*/ v.string<"asc" | "desc" | (string & {})>(), 114 + ), 115 + /** 116 + * Include profile + identity info keyed by DID 117 + */ 118 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 119 + /** 120 + * Full-text search across: note 121 + */ 122 + search: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 123 + /** 124 + * Field to sort by (default: time_us) 125 + */ 126 + sort: /*#__PURE__*/ v.optional( 127 + /*#__PURE__*/ v.string< 128 + "connectionType" | "source" | "target" | (string & {}) 129 + >(), 130 + ), 131 + /** 132 + * Filter by source 133 + */ 134 + source: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 135 + /** 136 + * Filter by target 137 + */ 138 + target: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 139 + }), 140 + output: { 141 + type: "lex", 142 + schema: /*#__PURE__*/ v.object({ 143 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 144 + get profiles() { 145 + return /*#__PURE__*/ v.optional( 146 + /*#__PURE__*/ v.array(profileEntrySchema), 147 + ); 148 + }, 149 + get records() { 150 + return /*#__PURE__*/ v.array(recordSchema); 151 + }, 152 + }), 153 + }, 154 + }, 155 + ); 156 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 157 + $type: /*#__PURE__*/ v.optional( 158 + /*#__PURE__*/ v.literal( 159 + "org.latha.island.connection.listRecords#profileEntry", 160 + ), 161 + ), 162 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 163 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 164 + did: /*#__PURE__*/ v.didString(), 165 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 166 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 167 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 168 + get value() { 169 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 170 + }, 171 + }); 172 + const _recordSchema = /*#__PURE__*/ v.object({ 173 + $type: /*#__PURE__*/ v.optional( 174 + /*#__PURE__*/ v.literal("org.latha.island.connection.listRecords#record"), 175 + ), 176 + cid: /*#__PURE__*/ v.cidString(), 177 + collection: /*#__PURE__*/ v.nsidString(), 178 + did: /*#__PURE__*/ v.didString(), 179 + rkey: /*#__PURE__*/ v.string(), 180 + time_us: /*#__PURE__*/ v.integer(), 181 + uri: /*#__PURE__*/ v.resourceUriString(), 182 + get value() { 183 + return NetworkCosmikConnection.mainSchema; 184 + }, 185 + }); 186 + 187 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 188 + type main$schematype = typeof _mainSchema; 189 + type profileEntry$schematype = typeof _profileEntrySchema; 190 + type record$schematype = typeof _recordSchema; 191 + 192 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 193 + export interface mainSchema extends main$schematype {} 194 + export interface profileEntrySchema extends profileEntry$schematype {} 195 + export interface recordSchema extends record$schematype {} 196 + 197 + export const appBskyActorProfileSchema = 198 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 199 + export const mainSchema = _mainSchema as mainSchema; 200 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 201 + export const recordSchema = _recordSchema as recordSchema; 202 + 203 + export interface AppBskyActorProfile extends v.InferInput< 204 + typeof appBskyActorProfileSchema 205 + > {} 206 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 207 + export interface Record extends v.InferInput<typeof recordSchema> {} 208 + 209 + export interface $params extends v.InferInput<mainSchema["params"]> {} 210 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 211 + 212 + declare module "@atcute/lexicons/ambient" { 213 + interface XRPCQueries { 214 + "org.latha.island.connection.listRecords": mainSchema; 215 + } 216 + }
+153
src/lexicon-types/types/org/latha/island/entity/getRecord.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + 7 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 8 + $type: /*#__PURE__*/ v.optional( 9 + /*#__PURE__*/ v.literal( 10 + "org.latha.island.entity.getRecord#appBskyActorProfile", 11 + ), 12 + ), 13 + /** 14 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 15 + * @accept image/png, image/jpeg 16 + * @maxSize 1000000 17 + */ 18 + avatar: /*#__PURE__*/ v.optional( 19 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 20 + /*#__PURE__*/ v.blobSize(1000000), 21 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 22 + ]), 23 + ), 24 + /** 25 + * Larger horizontal image to display behind profile view. 26 + * @accept image/png, image/jpeg 27 + * @maxSize 1000000 28 + */ 29 + banner: /*#__PURE__*/ v.optional( 30 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 31 + /*#__PURE__*/ v.blobSize(1000000), 32 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 33 + ]), 34 + ), 35 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 36 + /** 37 + * Free-form profile description text. 38 + * @maxLength 2560 39 + * @maxGraphemes 256 40 + */ 41 + description: /*#__PURE__*/ v.optional( 42 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 43 + /*#__PURE__*/ v.stringLength(0, 2560), 44 + /*#__PURE__*/ v.stringGraphemes(0, 256), 45 + ]), 46 + ), 47 + /** 48 + * @maxLength 640 49 + * @maxGraphemes 64 50 + */ 51 + displayName: /*#__PURE__*/ v.optional( 52 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 53 + /*#__PURE__*/ v.stringLength(0, 640), 54 + /*#__PURE__*/ v.stringGraphemes(0, 64), 55 + ]), 56 + ), 57 + get joinedViaStarterPack() { 58 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 59 + }, 60 + /** 61 + * Self-label values, specific to the Bluesky application, on the overall account. 62 + */ 63 + get labels() { 64 + return /*#__PURE__*/ v.optional( 65 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 66 + ); 67 + }, 68 + get pinnedPost() { 69 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 70 + }, 71 + /** 72 + * Free-form pronouns text. 73 + * @maxLength 200 74 + * @maxGraphemes 20 75 + */ 76 + pronouns: /*#__PURE__*/ v.optional( 77 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 78 + /*#__PURE__*/ v.stringLength(0, 200), 79 + /*#__PURE__*/ v.stringGraphemes(0, 20), 80 + ]), 81 + ), 82 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 83 + }); 84 + const _mainSchema = /*#__PURE__*/ v.query("org.latha.island.entity.getRecord", { 85 + params: /*#__PURE__*/ v.object({ 86 + /** 87 + * Include profile + identity info keyed by DID 88 + */ 89 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 90 + /** 91 + * AT URI of the record 92 + */ 93 + uri: /*#__PURE__*/ v.resourceUriString(), 94 + }), 95 + output: { 96 + type: "lex", 97 + schema: /*#__PURE__*/ v.object({ 98 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 99 + collection: /*#__PURE__*/ v.nsidString(), 100 + did: /*#__PURE__*/ v.didString(), 101 + get profiles() { 102 + return /*#__PURE__*/ v.optional( 103 + /*#__PURE__*/ v.array(profileEntrySchema), 104 + ); 105 + }, 106 + rkey: /*#__PURE__*/ v.string(), 107 + time_us: /*#__PURE__*/ v.integer(), 108 + uri: /*#__PURE__*/ v.resourceUriString(), 109 + value: /*#__PURE__*/ v.unknown(), 110 + }), 111 + }, 112 + }); 113 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 114 + $type: /*#__PURE__*/ v.optional( 115 + /*#__PURE__*/ v.literal("org.latha.island.entity.getRecord#profileEntry"), 116 + ), 117 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 118 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 119 + did: /*#__PURE__*/ v.didString(), 120 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 121 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 122 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 123 + get value() { 124 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 125 + }, 126 + }); 127 + 128 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 129 + type main$schematype = typeof _mainSchema; 130 + type profileEntry$schematype = typeof _profileEntrySchema; 131 + 132 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 133 + export interface mainSchema extends main$schematype {} 134 + export interface profileEntrySchema extends profileEntry$schematype {} 135 + 136 + export const appBskyActorProfileSchema = 137 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 138 + export const mainSchema = _mainSchema as mainSchema; 139 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 140 + 141 + export interface AppBskyActorProfile extends v.InferInput< 142 + typeof appBskyActorProfileSchema 143 + > {} 144 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 145 + 146 + export interface $params extends v.InferInput<mainSchema["params"]> {} 147 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 148 + 149 + declare module "@atcute/lexicons/ambient" { 150 + interface XRPCQueries { 151 + "org.latha.island.entity.getRecord": mainSchema; 152 + } 153 + }
+205
src/lexicon-types/types/org/latha/island/entity/listRecords.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + 7 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 8 + $type: /*#__PURE__*/ v.optional( 9 + /*#__PURE__*/ v.literal( 10 + "org.latha.island.entity.listRecords#appBskyActorProfile", 11 + ), 12 + ), 13 + /** 14 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 15 + * @accept image/png, image/jpeg 16 + * @maxSize 1000000 17 + */ 18 + avatar: /*#__PURE__*/ v.optional( 19 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 20 + /*#__PURE__*/ v.blobSize(1000000), 21 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 22 + ]), 23 + ), 24 + /** 25 + * Larger horizontal image to display behind profile view. 26 + * @accept image/png, image/jpeg 27 + * @maxSize 1000000 28 + */ 29 + banner: /*#__PURE__*/ v.optional( 30 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 31 + /*#__PURE__*/ v.blobSize(1000000), 32 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 33 + ]), 34 + ), 35 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 36 + /** 37 + * Free-form profile description text. 38 + * @maxLength 2560 39 + * @maxGraphemes 256 40 + */ 41 + description: /*#__PURE__*/ v.optional( 42 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 43 + /*#__PURE__*/ v.stringLength(0, 2560), 44 + /*#__PURE__*/ v.stringGraphemes(0, 256), 45 + ]), 46 + ), 47 + /** 48 + * @maxLength 640 49 + * @maxGraphemes 64 50 + */ 51 + displayName: /*#__PURE__*/ v.optional( 52 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 53 + /*#__PURE__*/ v.stringLength(0, 640), 54 + /*#__PURE__*/ v.stringGraphemes(0, 64), 55 + ]), 56 + ), 57 + get joinedViaStarterPack() { 58 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 59 + }, 60 + /** 61 + * Self-label values, specific to the Bluesky application, on the overall account. 62 + */ 63 + get labels() { 64 + return /*#__PURE__*/ v.optional( 65 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 66 + ); 67 + }, 68 + get pinnedPost() { 69 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 70 + }, 71 + /** 72 + * Free-form pronouns text. 73 + * @maxLength 200 74 + * @maxGraphemes 20 75 + */ 76 + pronouns: /*#__PURE__*/ v.optional( 77 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 78 + /*#__PURE__*/ v.stringLength(0, 200), 79 + /*#__PURE__*/ v.stringGraphemes(0, 20), 80 + ]), 81 + ), 82 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 83 + }); 84 + const _mainSchema = /*#__PURE__*/ v.query( 85 + "org.latha.island.entity.listRecords", 86 + { 87 + params: /*#__PURE__*/ v.object({ 88 + /** 89 + * Filter by DID or handle (triggers on-demand backfill) 90 + */ 91 + actor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.actorIdentifierString()), 92 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 93 + /** 94 + * Filter by entityType 95 + */ 96 + entityType: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 97 + /** 98 + * @minimum 1 99 + * @maximum 200 100 + * @default 50 101 + */ 102 + limit: /*#__PURE__*/ v.optional( 103 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 104 + /*#__PURE__*/ v.integerRange(1, 200), 105 + ]), 106 + 50, 107 + ), 108 + /** 109 + * Filter by name 110 + */ 111 + name: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 112 + /** 113 + * Sort direction (default: desc for dates/numbers/counts, asc for strings) 114 + */ 115 + order: /*#__PURE__*/ v.optional( 116 + /*#__PURE__*/ v.string<"asc" | "desc" | (string & {})>(), 117 + ), 118 + /** 119 + * Include profile + identity info keyed by DID 120 + */ 121 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 122 + /** 123 + * Full-text search across: name, description 124 + */ 125 + search: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 126 + /** 127 + * Field to sort by (default: time_us) 128 + */ 129 + sort: /*#__PURE__*/ v.optional( 130 + /*#__PURE__*/ v.string<"entityType" | "name" | (string & {})>(), 131 + ), 132 + }), 133 + output: { 134 + type: "lex", 135 + schema: /*#__PURE__*/ v.object({ 136 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 137 + get profiles() { 138 + return /*#__PURE__*/ v.optional( 139 + /*#__PURE__*/ v.array(profileEntrySchema), 140 + ); 141 + }, 142 + get records() { 143 + return /*#__PURE__*/ v.array(recordSchema); 144 + }, 145 + }), 146 + }, 147 + }, 148 + ); 149 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 150 + $type: /*#__PURE__*/ v.optional( 151 + /*#__PURE__*/ v.literal("org.latha.island.entity.listRecords#profileEntry"), 152 + ), 153 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 154 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 155 + did: /*#__PURE__*/ v.didString(), 156 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 157 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 158 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 159 + get value() { 160 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 161 + }, 162 + }); 163 + const _recordSchema = /*#__PURE__*/ v.object({ 164 + $type: /*#__PURE__*/ v.optional( 165 + /*#__PURE__*/ v.literal("org.latha.island.entity.listRecords#record"), 166 + ), 167 + cid: /*#__PURE__*/ v.cidString(), 168 + collection: /*#__PURE__*/ v.nsidString(), 169 + did: /*#__PURE__*/ v.didString(), 170 + rkey: /*#__PURE__*/ v.string(), 171 + time_us: /*#__PURE__*/ v.integer(), 172 + uri: /*#__PURE__*/ v.resourceUriString(), 173 + value: /*#__PURE__*/ v.unknown(), 174 + }); 175 + 176 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 177 + type main$schematype = typeof _mainSchema; 178 + type profileEntry$schematype = typeof _profileEntrySchema; 179 + type record$schematype = typeof _recordSchema; 180 + 181 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 182 + export interface mainSchema extends main$schematype {} 183 + export interface profileEntrySchema extends profileEntry$schematype {} 184 + export interface recordSchema extends record$schematype {} 185 + 186 + export const appBskyActorProfileSchema = 187 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 188 + export const mainSchema = _mainSchema as mainSchema; 189 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 190 + export const recordSchema = _recordSchema as recordSchema; 191 + 192 + export interface AppBskyActorProfile extends v.InferInput< 193 + typeof appBskyActorProfileSchema 194 + > {} 195 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 196 + export interface Record extends v.InferInput<typeof recordSchema> {} 197 + 198 + export interface $params extends v.InferInput<mainSchema["params"]> {} 199 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 200 + 201 + declare module "@atcute/lexicons/ambient" { 202 + interface XRPCQueries { 203 + "org.latha.island.entity.listRecords": mainSchema; 204 + } 205 + }
+156
src/lexicon-types/types/org/latha/island/follow/getRecord.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + import * as NetworkCosmikFollow from "../../../../network/cosmik/follow.js"; 7 + 8 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 9 + $type: /*#__PURE__*/ v.optional( 10 + /*#__PURE__*/ v.literal( 11 + "org.latha.island.follow.getRecord#appBskyActorProfile", 12 + ), 13 + ), 14 + /** 15 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 16 + * @accept image/png, image/jpeg 17 + * @maxSize 1000000 18 + */ 19 + avatar: /*#__PURE__*/ v.optional( 20 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 21 + /*#__PURE__*/ v.blobSize(1000000), 22 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 23 + ]), 24 + ), 25 + /** 26 + * Larger horizontal image to display behind profile view. 27 + * @accept image/png, image/jpeg 28 + * @maxSize 1000000 29 + */ 30 + banner: /*#__PURE__*/ v.optional( 31 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 32 + /*#__PURE__*/ v.blobSize(1000000), 33 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 34 + ]), 35 + ), 36 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 37 + /** 38 + * Free-form profile description text. 39 + * @maxLength 2560 40 + * @maxGraphemes 256 41 + */ 42 + description: /*#__PURE__*/ v.optional( 43 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 44 + /*#__PURE__*/ v.stringLength(0, 2560), 45 + /*#__PURE__*/ v.stringGraphemes(0, 256), 46 + ]), 47 + ), 48 + /** 49 + * @maxLength 640 50 + * @maxGraphemes 64 51 + */ 52 + displayName: /*#__PURE__*/ v.optional( 53 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 54 + /*#__PURE__*/ v.stringLength(0, 640), 55 + /*#__PURE__*/ v.stringGraphemes(0, 64), 56 + ]), 57 + ), 58 + get joinedViaStarterPack() { 59 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 60 + }, 61 + /** 62 + * Self-label values, specific to the Bluesky application, on the overall account. 63 + */ 64 + get labels() { 65 + return /*#__PURE__*/ v.optional( 66 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 67 + ); 68 + }, 69 + get pinnedPost() { 70 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 71 + }, 72 + /** 73 + * Free-form pronouns text. 74 + * @maxLength 200 75 + * @maxGraphemes 20 76 + */ 77 + pronouns: /*#__PURE__*/ v.optional( 78 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 79 + /*#__PURE__*/ v.stringLength(0, 200), 80 + /*#__PURE__*/ v.stringGraphemes(0, 20), 81 + ]), 82 + ), 83 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 84 + }); 85 + const _mainSchema = /*#__PURE__*/ v.query("org.latha.island.follow.getRecord", { 86 + params: /*#__PURE__*/ v.object({ 87 + /** 88 + * Include profile + identity info keyed by DID 89 + */ 90 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 91 + /** 92 + * AT URI of the record 93 + */ 94 + uri: /*#__PURE__*/ v.resourceUriString(), 95 + }), 96 + output: { 97 + type: "lex", 98 + schema: /*#__PURE__*/ v.object({ 99 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 100 + collection: /*#__PURE__*/ v.nsidString(), 101 + did: /*#__PURE__*/ v.didString(), 102 + get profiles() { 103 + return /*#__PURE__*/ v.optional( 104 + /*#__PURE__*/ v.array(profileEntrySchema), 105 + ); 106 + }, 107 + rkey: /*#__PURE__*/ v.string(), 108 + time_us: /*#__PURE__*/ v.integer(), 109 + uri: /*#__PURE__*/ v.resourceUriString(), 110 + get value() { 111 + return NetworkCosmikFollow.mainSchema; 112 + }, 113 + }), 114 + }, 115 + }); 116 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 117 + $type: /*#__PURE__*/ v.optional( 118 + /*#__PURE__*/ v.literal("org.latha.island.follow.getRecord#profileEntry"), 119 + ), 120 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 121 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 122 + did: /*#__PURE__*/ v.didString(), 123 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 124 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 125 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 126 + get value() { 127 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 128 + }, 129 + }); 130 + 131 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 132 + type main$schematype = typeof _mainSchema; 133 + type profileEntry$schematype = typeof _profileEntrySchema; 134 + 135 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 136 + export interface mainSchema extends main$schematype {} 137 + export interface profileEntrySchema extends profileEntry$schematype {} 138 + 139 + export const appBskyActorProfileSchema = 140 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 141 + export const mainSchema = _mainSchema as mainSchema; 142 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 143 + 144 + export interface AppBskyActorProfile extends v.InferInput< 145 + typeof appBskyActorProfileSchema 146 + > {} 147 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 148 + 149 + export interface $params extends v.InferInput<mainSchema["params"]> {} 150 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 151 + 152 + declare module "@atcute/lexicons/ambient" { 153 + interface XRPCQueries { 154 + "org.latha.island.follow.getRecord": mainSchema; 155 + } 156 + }
+200
src/lexicon-types/types/org/latha/island/follow/listRecords.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + import * as NetworkCosmikFollow from "../../../../network/cosmik/follow.js"; 7 + 8 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 9 + $type: /*#__PURE__*/ v.optional( 10 + /*#__PURE__*/ v.literal( 11 + "org.latha.island.follow.listRecords#appBskyActorProfile", 12 + ), 13 + ), 14 + /** 15 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 16 + * @accept image/png, image/jpeg 17 + * @maxSize 1000000 18 + */ 19 + avatar: /*#__PURE__*/ v.optional( 20 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 21 + /*#__PURE__*/ v.blobSize(1000000), 22 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 23 + ]), 24 + ), 25 + /** 26 + * Larger horizontal image to display behind profile view. 27 + * @accept image/png, image/jpeg 28 + * @maxSize 1000000 29 + */ 30 + banner: /*#__PURE__*/ v.optional( 31 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 32 + /*#__PURE__*/ v.blobSize(1000000), 33 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 34 + ]), 35 + ), 36 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 37 + /** 38 + * Free-form profile description text. 39 + * @maxLength 2560 40 + * @maxGraphemes 256 41 + */ 42 + description: /*#__PURE__*/ v.optional( 43 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 44 + /*#__PURE__*/ v.stringLength(0, 2560), 45 + /*#__PURE__*/ v.stringGraphemes(0, 256), 46 + ]), 47 + ), 48 + /** 49 + * @maxLength 640 50 + * @maxGraphemes 64 51 + */ 52 + displayName: /*#__PURE__*/ v.optional( 53 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 54 + /*#__PURE__*/ v.stringLength(0, 640), 55 + /*#__PURE__*/ v.stringGraphemes(0, 64), 56 + ]), 57 + ), 58 + get joinedViaStarterPack() { 59 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 60 + }, 61 + /** 62 + * Self-label values, specific to the Bluesky application, on the overall account. 63 + */ 64 + get labels() { 65 + return /*#__PURE__*/ v.optional( 66 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 67 + ); 68 + }, 69 + get pinnedPost() { 70 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 71 + }, 72 + /** 73 + * Free-form pronouns text. 74 + * @maxLength 200 75 + * @maxGraphemes 20 76 + */ 77 + pronouns: /*#__PURE__*/ v.optional( 78 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 79 + /*#__PURE__*/ v.stringLength(0, 200), 80 + /*#__PURE__*/ v.stringGraphemes(0, 20), 81 + ]), 82 + ), 83 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 84 + }); 85 + const _mainSchema = /*#__PURE__*/ v.query( 86 + "org.latha.island.follow.listRecords", 87 + { 88 + params: /*#__PURE__*/ v.object({ 89 + /** 90 + * Filter by DID or handle (triggers on-demand backfill) 91 + */ 92 + actor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.actorIdentifierString()), 93 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 94 + /** 95 + * @minimum 1 96 + * @maximum 200 97 + * @default 50 98 + */ 99 + limit: /*#__PURE__*/ v.optional( 100 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 101 + /*#__PURE__*/ v.integerRange(1, 200), 102 + ]), 103 + 50, 104 + ), 105 + /** 106 + * Sort direction (default: desc for dates/numbers/counts, asc for strings) 107 + */ 108 + order: /*#__PURE__*/ v.optional( 109 + /*#__PURE__*/ v.string<"asc" | "desc" | (string & {})>(), 110 + ), 111 + /** 112 + * Include profile + identity info keyed by DID 113 + */ 114 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 115 + /** 116 + * Field to sort by (default: time_us) 117 + */ 118 + sort: /*#__PURE__*/ v.optional( 119 + /*#__PURE__*/ v.string<"subject" | (string & {})>(), 120 + ), 121 + /** 122 + * Filter by subject 123 + */ 124 + subject: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 125 + }), 126 + output: { 127 + type: "lex", 128 + schema: /*#__PURE__*/ v.object({ 129 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 130 + get profiles() { 131 + return /*#__PURE__*/ v.optional( 132 + /*#__PURE__*/ v.array(profileEntrySchema), 133 + ); 134 + }, 135 + get records() { 136 + return /*#__PURE__*/ v.array(recordSchema); 137 + }, 138 + }), 139 + }, 140 + }, 141 + ); 142 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 143 + $type: /*#__PURE__*/ v.optional( 144 + /*#__PURE__*/ v.literal("org.latha.island.follow.listRecords#profileEntry"), 145 + ), 146 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 147 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 148 + did: /*#__PURE__*/ v.didString(), 149 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 150 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 151 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 152 + get value() { 153 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 154 + }, 155 + }); 156 + const _recordSchema = /*#__PURE__*/ v.object({ 157 + $type: /*#__PURE__*/ v.optional( 158 + /*#__PURE__*/ v.literal("org.latha.island.follow.listRecords#record"), 159 + ), 160 + cid: /*#__PURE__*/ v.cidString(), 161 + collection: /*#__PURE__*/ v.nsidString(), 162 + did: /*#__PURE__*/ v.didString(), 163 + rkey: /*#__PURE__*/ v.string(), 164 + time_us: /*#__PURE__*/ v.integer(), 165 + uri: /*#__PURE__*/ v.resourceUriString(), 166 + get value() { 167 + return NetworkCosmikFollow.mainSchema; 168 + }, 169 + }); 170 + 171 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 172 + type main$schematype = typeof _mainSchema; 173 + type profileEntry$schematype = typeof _profileEntrySchema; 174 + type record$schematype = typeof _recordSchema; 175 + 176 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 177 + export interface mainSchema extends main$schematype {} 178 + export interface profileEntrySchema extends profileEntry$schematype {} 179 + export interface recordSchema extends record$schematype {} 180 + 181 + export const appBskyActorProfileSchema = 182 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 183 + export const mainSchema = _mainSchema as mainSchema; 184 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 185 + export const recordSchema = _recordSchema as recordSchema; 186 + 187 + export interface AppBskyActorProfile extends v.InferInput< 188 + typeof appBskyActorProfileSchema 189 + > {} 190 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 191 + export interface Record extends v.InferInput<typeof recordSchema> {} 192 + 193 + export interface $params extends v.InferInput<mainSchema["params"]> {} 194 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 195 + 196 + declare module "@atcute/lexicons/ambient" { 197 + interface XRPCQueries { 198 + "org.latha.island.follow.listRecords": mainSchema; 199 + } 200 + }
+30
src/lexicon-types/types/org/latha/island/getCursor.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + 5 + const _mainSchema = /*#__PURE__*/ v.query("org.latha.island.getCursor", { 6 + params: null, 7 + output: { 8 + type: "lex", 9 + schema: /*#__PURE__*/ v.object({ 10 + date: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 11 + seconds_ago: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 12 + time_us: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 13 + }), 14 + }, 15 + }); 16 + 17 + type main$schematype = typeof _mainSchema; 18 + 19 + export interface mainSchema extends main$schematype {} 20 + 21 + export const mainSchema = _mainSchema as mainSchema; 22 + 23 + export interface $params {} 24 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 25 + 26 + declare module "@atcute/lexicons/ambient" { 27 + interface XRPCQueries { 28 + "org.latha.island.getCursor": mainSchema; 29 + } 30 + }
+47
src/lexicon-types/types/org/latha/island/getOverview.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + 5 + const _collectionStatsSchema = /*#__PURE__*/ v.object({ 6 + $type: /*#__PURE__*/ v.optional( 7 + /*#__PURE__*/ v.literal("org.latha.island.getOverview#collectionStats"), 8 + ), 9 + collection: /*#__PURE__*/ v.string(), 10 + records: /*#__PURE__*/ v.integer(), 11 + unique_users: /*#__PURE__*/ v.integer(), 12 + }); 13 + const _mainSchema = /*#__PURE__*/ v.query("org.latha.island.getOverview", { 14 + params: null, 15 + output: { 16 + type: "lex", 17 + schema: /*#__PURE__*/ v.object({ 18 + get collections() { 19 + return /*#__PURE__*/ v.array(collectionStatsSchema); 20 + }, 21 + total_records: /*#__PURE__*/ v.integer(), 22 + }), 23 + }, 24 + }); 25 + 26 + type collectionStats$schematype = typeof _collectionStatsSchema; 27 + type main$schematype = typeof _mainSchema; 28 + 29 + export interface collectionStatsSchema extends collectionStats$schematype {} 30 + export interface mainSchema extends main$schematype {} 31 + 32 + export const collectionStatsSchema = 33 + _collectionStatsSchema as collectionStatsSchema; 34 + export const mainSchema = _mainSchema as mainSchema; 35 + 36 + export interface CollectionStats extends v.InferInput< 37 + typeof collectionStatsSchema 38 + > {} 39 + 40 + export interface $params {} 41 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 42 + 43 + declare module "@atcute/lexicons/ambient" { 44 + interface XRPCQueries { 45 + "org.latha.island.getOverview": mainSchema; 46 + } 47 + }
+138
src/lexicon-types/types/org/latha/island/getProfile.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + 7 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 8 + $type: /*#__PURE__*/ v.optional( 9 + /*#__PURE__*/ v.literal("org.latha.island.getProfile#appBskyActorProfile"), 10 + ), 11 + /** 12 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 13 + * @accept image/png, image/jpeg 14 + * @maxSize 1000000 15 + */ 16 + avatar: /*#__PURE__*/ v.optional( 17 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 18 + /*#__PURE__*/ v.blobSize(1000000), 19 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 20 + ]), 21 + ), 22 + /** 23 + * Larger horizontal image to display behind profile view. 24 + * @accept image/png, image/jpeg 25 + * @maxSize 1000000 26 + */ 27 + banner: /*#__PURE__*/ v.optional( 28 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 29 + /*#__PURE__*/ v.blobSize(1000000), 30 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 31 + ]), 32 + ), 33 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 34 + /** 35 + * Free-form profile description text. 36 + * @maxLength 2560 37 + * @maxGraphemes 256 38 + */ 39 + description: /*#__PURE__*/ v.optional( 40 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 41 + /*#__PURE__*/ v.stringLength(0, 2560), 42 + /*#__PURE__*/ v.stringGraphemes(0, 256), 43 + ]), 44 + ), 45 + /** 46 + * @maxLength 640 47 + * @maxGraphemes 64 48 + */ 49 + displayName: /*#__PURE__*/ v.optional( 50 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 51 + /*#__PURE__*/ v.stringLength(0, 640), 52 + /*#__PURE__*/ v.stringGraphemes(0, 64), 53 + ]), 54 + ), 55 + get joinedViaStarterPack() { 56 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 57 + }, 58 + /** 59 + * Self-label values, specific to the Bluesky application, on the overall account. 60 + */ 61 + get labels() { 62 + return /*#__PURE__*/ v.optional( 63 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 64 + ); 65 + }, 66 + get pinnedPost() { 67 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 68 + }, 69 + /** 70 + * Free-form pronouns text. 71 + * @maxLength 200 72 + * @maxGraphemes 20 73 + */ 74 + pronouns: /*#__PURE__*/ v.optional( 75 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 76 + /*#__PURE__*/ v.stringLength(0, 200), 77 + /*#__PURE__*/ v.stringGraphemes(0, 20), 78 + ]), 79 + ), 80 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 81 + }); 82 + const _mainSchema = /*#__PURE__*/ v.query("org.latha.island.getProfile", { 83 + params: /*#__PURE__*/ v.object({ 84 + /** 85 + * DID or handle of the user 86 + */ 87 + actor: /*#__PURE__*/ v.actorIdentifierString(), 88 + }), 89 + output: { 90 + type: "lex", 91 + schema: /*#__PURE__*/ v.object({ 92 + get profiles() { 93 + return /*#__PURE__*/ v.array(profileEntrySchema); 94 + }, 95 + }), 96 + }, 97 + }); 98 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 99 + $type: /*#__PURE__*/ v.optional( 100 + /*#__PURE__*/ v.literal("org.latha.island.getProfile#profileEntry"), 101 + ), 102 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 103 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 104 + did: /*#__PURE__*/ v.didString(), 105 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 106 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 107 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 108 + get value() { 109 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 110 + }, 111 + }); 112 + 113 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 114 + type main$schematype = typeof _mainSchema; 115 + type profileEntry$schematype = typeof _profileEntrySchema; 116 + 117 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 118 + export interface mainSchema extends main$schematype {} 119 + export interface profileEntrySchema extends profileEntry$schematype {} 120 + 121 + export const appBskyActorProfileSchema = 122 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 123 + export const mainSchema = _mainSchema as mainSchema; 124 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 125 + 126 + export interface AppBskyActorProfile extends v.InferInput< 127 + typeof appBskyActorProfileSchema 128 + > {} 129 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 130 + 131 + export interface $params extends v.InferInput<mainSchema["params"]> {} 132 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 133 + 134 + declare module "@atcute/lexicons/ambient" { 135 + interface XRPCQueries { 136 + "org.latha.island.getProfile": mainSchema; 137 + } 138 + }
+184
src/lexicon-types/types/org/latha/island/island/getRecord.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + import * as NetworkCosmikCard from "../../../../network/cosmik/card.js"; 7 + 8 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 9 + $type: /*#__PURE__*/ v.optional( 10 + /*#__PURE__*/ v.literal( 11 + "org.latha.island.island.getRecord#appBskyActorProfile", 12 + ), 13 + ), 14 + /** 15 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 16 + * @accept image/png, image/jpeg 17 + * @maxSize 1000000 18 + */ 19 + avatar: /*#__PURE__*/ v.optional( 20 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 21 + /*#__PURE__*/ v.blobSize(1000000), 22 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 23 + ]), 24 + ), 25 + /** 26 + * Larger horizontal image to display behind profile view. 27 + * @accept image/png, image/jpeg 28 + * @maxSize 1000000 29 + */ 30 + banner: /*#__PURE__*/ v.optional( 31 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 32 + /*#__PURE__*/ v.blobSize(1000000), 33 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 34 + ]), 35 + ), 36 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 37 + /** 38 + * Free-form profile description text. 39 + * @maxLength 2560 40 + * @maxGraphemes 256 41 + */ 42 + description: /*#__PURE__*/ v.optional( 43 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 44 + /*#__PURE__*/ v.stringLength(0, 2560), 45 + /*#__PURE__*/ v.stringGraphemes(0, 256), 46 + ]), 47 + ), 48 + /** 49 + * @maxLength 640 50 + * @maxGraphemes 64 51 + */ 52 + displayName: /*#__PURE__*/ v.optional( 53 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 54 + /*#__PURE__*/ v.stringLength(0, 640), 55 + /*#__PURE__*/ v.stringGraphemes(0, 64), 56 + ]), 57 + ), 58 + get joinedViaStarterPack() { 59 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 60 + }, 61 + /** 62 + * Self-label values, specific to the Bluesky application, on the overall account. 63 + */ 64 + get labels() { 65 + return /*#__PURE__*/ v.optional( 66 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 67 + ); 68 + }, 69 + get pinnedPost() { 70 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 71 + }, 72 + /** 73 + * Free-form pronouns text. 74 + * @maxLength 200 75 + * @maxGraphemes 20 76 + */ 77 + pronouns: /*#__PURE__*/ v.optional( 78 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 79 + /*#__PURE__*/ v.stringLength(0, 200), 80 + /*#__PURE__*/ v.stringGraphemes(0, 20), 81 + ]), 82 + ), 83 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 84 + }); 85 + const _mainSchema = /*#__PURE__*/ v.query("org.latha.island.island.getRecord", { 86 + params: /*#__PURE__*/ v.object({ 87 + /** 88 + * Embed the referenced sourceCard record 89 + */ 90 + hydrateSourceCard: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 91 + /** 92 + * Include profile + identity info keyed by DID 93 + */ 94 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 95 + /** 96 + * AT URI of the record 97 + */ 98 + uri: /*#__PURE__*/ v.resourceUriString(), 99 + }), 100 + output: { 101 + type: "lex", 102 + schema: /*#__PURE__*/ v.object({ 103 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 104 + collection: /*#__PURE__*/ v.nsidString(), 105 + did: /*#__PURE__*/ v.didString(), 106 + get profiles() { 107 + return /*#__PURE__*/ v.optional( 108 + /*#__PURE__*/ v.array(profileEntrySchema), 109 + ); 110 + }, 111 + rkey: /*#__PURE__*/ v.string(), 112 + get sourceCard() { 113 + return /*#__PURE__*/ v.optional(refSourceCardRecordSchema); 114 + }, 115 + time_us: /*#__PURE__*/ v.integer(), 116 + uri: /*#__PURE__*/ v.resourceUriString(), 117 + value: /*#__PURE__*/ v.unknown(), 118 + }), 119 + }, 120 + }); 121 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 122 + $type: /*#__PURE__*/ v.optional( 123 + /*#__PURE__*/ v.literal("org.latha.island.island.getRecord#profileEntry"), 124 + ), 125 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 126 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 127 + did: /*#__PURE__*/ v.didString(), 128 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 129 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 130 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 131 + get value() { 132 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 133 + }, 134 + }); 135 + const _refSourceCardRecordSchema = /*#__PURE__*/ v.object({ 136 + $type: /*#__PURE__*/ v.optional( 137 + /*#__PURE__*/ v.literal( 138 + "org.latha.island.island.getRecord#refSourceCardRecord", 139 + ), 140 + ), 141 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 142 + collection: /*#__PURE__*/ v.nsidString(), 143 + did: /*#__PURE__*/ v.didString(), 144 + get record() { 145 + return /*#__PURE__*/ v.optional(NetworkCosmikCard.mainSchema); 146 + }, 147 + rkey: /*#__PURE__*/ v.string(), 148 + time_us: /*#__PURE__*/ v.integer(), 149 + uri: /*#__PURE__*/ v.resourceUriString(), 150 + }); 151 + 152 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 153 + type main$schematype = typeof _mainSchema; 154 + type profileEntry$schematype = typeof _profileEntrySchema; 155 + type refSourceCardRecord$schematype = typeof _refSourceCardRecordSchema; 156 + 157 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 158 + export interface mainSchema extends main$schematype {} 159 + export interface profileEntrySchema extends profileEntry$schematype {} 160 + export interface refSourceCardRecordSchema extends refSourceCardRecord$schematype {} 161 + 162 + export const appBskyActorProfileSchema = 163 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 164 + export const mainSchema = _mainSchema as mainSchema; 165 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 166 + export const refSourceCardRecordSchema = 167 + _refSourceCardRecordSchema as refSourceCardRecordSchema; 168 + 169 + export interface AppBskyActorProfile extends v.InferInput< 170 + typeof appBskyActorProfileSchema 171 + > {} 172 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 173 + export interface RefSourceCardRecord extends v.InferInput< 174 + typeof refSourceCardRecordSchema 175 + > {} 176 + 177 + export interface $params extends v.InferInput<mainSchema["params"]> {} 178 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 179 + 180 + declare module "@atcute/lexicons/ambient" { 181 + interface XRPCQueries { 182 + "org.latha.island.island.getRecord": mainSchema; 183 + } 184 + }
+246
src/lexicon-types/types/org/latha/island/island/listRecords.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + import * as NetworkCosmikCard from "../../../../network/cosmik/card.js"; 7 + 8 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 9 + $type: /*#__PURE__*/ v.optional( 10 + /*#__PURE__*/ v.literal( 11 + "org.latha.island.island.listRecords#appBskyActorProfile", 12 + ), 13 + ), 14 + /** 15 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 16 + * @accept image/png, image/jpeg 17 + * @maxSize 1000000 18 + */ 19 + avatar: /*#__PURE__*/ v.optional( 20 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 21 + /*#__PURE__*/ v.blobSize(1000000), 22 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 23 + ]), 24 + ), 25 + /** 26 + * Larger horizontal image to display behind profile view. 27 + * @accept image/png, image/jpeg 28 + * @maxSize 1000000 29 + */ 30 + banner: /*#__PURE__*/ v.optional( 31 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 32 + /*#__PURE__*/ v.blobSize(1000000), 33 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 34 + ]), 35 + ), 36 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 37 + /** 38 + * Free-form profile description text. 39 + * @maxLength 2560 40 + * @maxGraphemes 256 41 + */ 42 + description: /*#__PURE__*/ v.optional( 43 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 44 + /*#__PURE__*/ v.stringLength(0, 2560), 45 + /*#__PURE__*/ v.stringGraphemes(0, 256), 46 + ]), 47 + ), 48 + /** 49 + * @maxLength 640 50 + * @maxGraphemes 64 51 + */ 52 + displayName: /*#__PURE__*/ v.optional( 53 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 54 + /*#__PURE__*/ v.stringLength(0, 640), 55 + /*#__PURE__*/ v.stringGraphemes(0, 64), 56 + ]), 57 + ), 58 + get joinedViaStarterPack() { 59 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 60 + }, 61 + /** 62 + * Self-label values, specific to the Bluesky application, on the overall account. 63 + */ 64 + get labels() { 65 + return /*#__PURE__*/ v.optional( 66 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 67 + ); 68 + }, 69 + get pinnedPost() { 70 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 71 + }, 72 + /** 73 + * Free-form pronouns text. 74 + * @maxLength 200 75 + * @maxGraphemes 20 76 + */ 77 + pronouns: /*#__PURE__*/ v.optional( 78 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 79 + /*#__PURE__*/ v.stringLength(0, 200), 80 + /*#__PURE__*/ v.stringGraphemes(0, 20), 81 + ]), 82 + ), 83 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 84 + }); 85 + const _mainSchema = /*#__PURE__*/ v.query( 86 + "org.latha.island.island.listRecords", 87 + { 88 + params: /*#__PURE__*/ v.object({ 89 + /** 90 + * Filter by DID or handle (triggers on-demand backfill) 91 + */ 92 + actor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.actorIdentifierString()), 93 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 94 + /** 95 + * Embed the referenced sourceCard record 96 + */ 97 + hydrateSourceCard: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 98 + /** 99 + * @minimum 1 100 + * @maximum 200 101 + * @default 50 102 + */ 103 + limit: /*#__PURE__*/ v.optional( 104 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 105 + /*#__PURE__*/ v.integerRange(1, 200), 106 + ]), 107 + 50, 108 + ), 109 + /** 110 + * Sort direction (default: desc for dates/numbers/counts, asc for strings) 111 + */ 112 + order: /*#__PURE__*/ v.optional( 113 + /*#__PURE__*/ v.string<"asc" | "desc" | (string & {})>(), 114 + ), 115 + /** 116 + * Include profile + identity info keyed by DID 117 + */ 118 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 119 + /** 120 + * Full-text search across: analysis.synthesis, analysis.themes 121 + */ 122 + search: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 123 + /** 124 + * Field to sort by (default: time_us) 125 + */ 126 + sort: /*#__PURE__*/ v.optional( 127 + /*#__PURE__*/ v.string< 128 + "sourceCollection" | "sourceUri" | "updatedAt" | (string & {}) 129 + >(), 130 + ), 131 + /** 132 + * Filter by source.collection 133 + */ 134 + sourceCollection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 135 + /** 136 + * Filter by source.uri 137 + */ 138 + sourceUri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 139 + /** 140 + * Maximum value for updatedAt 141 + */ 142 + updatedAtMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 143 + /** 144 + * Minimum value for updatedAt 145 + */ 146 + updatedAtMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 147 + }), 148 + output: { 149 + type: "lex", 150 + schema: /*#__PURE__*/ v.object({ 151 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 152 + get profiles() { 153 + return /*#__PURE__*/ v.optional( 154 + /*#__PURE__*/ v.array(profileEntrySchema), 155 + ); 156 + }, 157 + get records() { 158 + return /*#__PURE__*/ v.array(recordSchema); 159 + }, 160 + }), 161 + }, 162 + }, 163 + ); 164 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 165 + $type: /*#__PURE__*/ v.optional( 166 + /*#__PURE__*/ v.literal("org.latha.island.island.listRecords#profileEntry"), 167 + ), 168 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 169 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 170 + did: /*#__PURE__*/ v.didString(), 171 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 172 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 173 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 174 + get value() { 175 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 176 + }, 177 + }); 178 + const _recordSchema = /*#__PURE__*/ v.object({ 179 + $type: /*#__PURE__*/ v.optional( 180 + /*#__PURE__*/ v.literal("org.latha.island.island.listRecords#record"), 181 + ), 182 + cid: /*#__PURE__*/ v.cidString(), 183 + collection: /*#__PURE__*/ v.nsidString(), 184 + did: /*#__PURE__*/ v.didString(), 185 + rkey: /*#__PURE__*/ v.string(), 186 + get sourceCard() { 187 + return /*#__PURE__*/ v.optional(refSourceCardRecordSchema); 188 + }, 189 + time_us: /*#__PURE__*/ v.integer(), 190 + uri: /*#__PURE__*/ v.resourceUriString(), 191 + value: /*#__PURE__*/ v.unknown(), 192 + }); 193 + const _refSourceCardRecordSchema = /*#__PURE__*/ v.object({ 194 + $type: /*#__PURE__*/ v.optional( 195 + /*#__PURE__*/ v.literal( 196 + "org.latha.island.island.listRecords#refSourceCardRecord", 197 + ), 198 + ), 199 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 200 + collection: /*#__PURE__*/ v.nsidString(), 201 + did: /*#__PURE__*/ v.didString(), 202 + get record() { 203 + return /*#__PURE__*/ v.optional(NetworkCosmikCard.mainSchema); 204 + }, 205 + rkey: /*#__PURE__*/ v.string(), 206 + time_us: /*#__PURE__*/ v.integer(), 207 + uri: /*#__PURE__*/ v.resourceUriString(), 208 + }); 209 + 210 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 211 + type main$schematype = typeof _mainSchema; 212 + type profileEntry$schematype = typeof _profileEntrySchema; 213 + type record$schematype = typeof _recordSchema; 214 + type refSourceCardRecord$schematype = typeof _refSourceCardRecordSchema; 215 + 216 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 217 + export interface mainSchema extends main$schematype {} 218 + export interface profileEntrySchema extends profileEntry$schematype {} 219 + export interface recordSchema extends record$schematype {} 220 + export interface refSourceCardRecordSchema extends refSourceCardRecord$schematype {} 221 + 222 + export const appBskyActorProfileSchema = 223 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 224 + export const mainSchema = _mainSchema as mainSchema; 225 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 226 + export const recordSchema = _recordSchema as recordSchema; 227 + export const refSourceCardRecordSchema = 228 + _refSourceCardRecordSchema as refSourceCardRecordSchema; 229 + 230 + export interface AppBskyActorProfile extends v.InferInput< 231 + typeof appBskyActorProfileSchema 232 + > {} 233 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 234 + export interface Record extends v.InferInput<typeof recordSchema> {} 235 + export interface RefSourceCardRecord extends v.InferInput< 236 + typeof refSourceCardRecordSchema 237 + > {} 238 + 239 + export interface $params extends v.InferInput<mainSchema["params"]> {} 240 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 241 + 242 + declare module "@atcute/lexicons/ambient" { 243 + interface XRPCQueries { 244 + "org.latha.island.island.listRecords": mainSchema; 245 + } 246 + }
+64
src/lexicon-types/types/org/latha/island/notifyOfUpdate.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + 5 + const _mainSchema = /*#__PURE__*/ v.procedure( 6 + "org.latha.island.notifyOfUpdate", 7 + { 8 + params: null, 9 + input: { 10 + type: "lex", 11 + schema: /*#__PURE__*/ v.object({ 12 + /** 13 + * Single AT URI to fetch and index 14 + */ 15 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 16 + /** 17 + * Batch of AT URIs to fetch and index (max 25) 18 + * @maxLength 25 19 + */ 20 + uris: /*#__PURE__*/ v.optional( 21 + /*#__PURE__*/ v.constrain( 22 + /*#__PURE__*/ v.array(/*#__PURE__*/ v.resourceUriString()), 23 + [/*#__PURE__*/ v.arrayLength(0, 25)], 24 + ), 25 + ), 26 + }), 27 + }, 28 + output: { 29 + type: "lex", 30 + schema: /*#__PURE__*/ v.object({ 31 + /** 32 + * Number of records deleted (not found on PDS) 33 + */ 34 + deleted: /*#__PURE__*/ v.integer(), 35 + /** 36 + * Errors for individual URIs that could not be processed 37 + */ 38 + errors: /*#__PURE__*/ v.optional( 39 + /*#__PURE__*/ v.array(/*#__PURE__*/ v.string()), 40 + ), 41 + /** 42 + * Number of records created or updated 43 + */ 44 + indexed: /*#__PURE__*/ v.integer(), 45 + }), 46 + }, 47 + }, 48 + ); 49 + 50 + type main$schematype = typeof _mainSchema; 51 + 52 + export interface mainSchema extends main$schematype {} 53 + 54 + export const mainSchema = _mainSchema as mainSchema; 55 + 56 + export interface $params {} 57 + export interface $input extends v.InferXRPCBodyInput<mainSchema["input"]> {} 58 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 59 + 60 + declare module "@atcute/lexicons/ambient" { 61 + interface XRPCProcedures { 62 + "org.latha.island.notifyOfUpdate": mainSchema; 63 + } 64 + }
+186
src/lexicon-types/types/org/latha/island/reasoning/getRecord.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + 7 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 8 + $type: /*#__PURE__*/ v.optional( 9 + /*#__PURE__*/ v.literal( 10 + "org.latha.island.reasoning.getRecord#appBskyActorProfile", 11 + ), 12 + ), 13 + /** 14 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 15 + * @accept image/png, image/jpeg 16 + * @maxSize 1000000 17 + */ 18 + avatar: /*#__PURE__*/ v.optional( 19 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 20 + /*#__PURE__*/ v.blobSize(1000000), 21 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 22 + ]), 23 + ), 24 + /** 25 + * Larger horizontal image to display behind profile view. 26 + * @accept image/png, image/jpeg 27 + * @maxSize 1000000 28 + */ 29 + banner: /*#__PURE__*/ v.optional( 30 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 31 + /*#__PURE__*/ v.blobSize(1000000), 32 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 33 + ]), 34 + ), 35 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 36 + /** 37 + * Free-form profile description text. 38 + * @maxLength 2560 39 + * @maxGraphemes 256 40 + */ 41 + description: /*#__PURE__*/ v.optional( 42 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 43 + /*#__PURE__*/ v.stringLength(0, 2560), 44 + /*#__PURE__*/ v.stringGraphemes(0, 256), 45 + ]), 46 + ), 47 + /** 48 + * @maxLength 640 49 + * @maxGraphemes 64 50 + */ 51 + displayName: /*#__PURE__*/ v.optional( 52 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 53 + /*#__PURE__*/ v.stringLength(0, 640), 54 + /*#__PURE__*/ v.stringGraphemes(0, 64), 55 + ]), 56 + ), 57 + get joinedViaStarterPack() { 58 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 59 + }, 60 + /** 61 + * Self-label values, specific to the Bluesky application, on the overall account. 62 + */ 63 + get labels() { 64 + return /*#__PURE__*/ v.optional( 65 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 66 + ); 67 + }, 68 + get pinnedPost() { 69 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 70 + }, 71 + /** 72 + * Free-form pronouns text. 73 + * @maxLength 200 74 + * @maxGraphemes 20 75 + */ 76 + pronouns: /*#__PURE__*/ v.optional( 77 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 78 + /*#__PURE__*/ v.stringLength(0, 200), 79 + /*#__PURE__*/ v.stringGraphemes(0, 20), 80 + ]), 81 + ), 82 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 83 + }); 84 + const _mainSchema = /*#__PURE__*/ v.query( 85 + "org.latha.island.reasoning.getRecord", 86 + { 87 + params: /*#__PURE__*/ v.object({ 88 + /** 89 + * Embed the referenced citation record 90 + */ 91 + hydrateCitation: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 92 + /** 93 + * Include profile + identity info keyed by DID 94 + */ 95 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 96 + /** 97 + * AT URI of the record 98 + */ 99 + uri: /*#__PURE__*/ v.resourceUriString(), 100 + }), 101 + output: { 102 + type: "lex", 103 + schema: /*#__PURE__*/ v.object({ 104 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 105 + get citation() { 106 + return /*#__PURE__*/ v.optional(refCitationRecordSchema); 107 + }, 108 + collection: /*#__PURE__*/ v.nsidString(), 109 + did: /*#__PURE__*/ v.didString(), 110 + get profiles() { 111 + return /*#__PURE__*/ v.optional( 112 + /*#__PURE__*/ v.array(profileEntrySchema), 113 + ); 114 + }, 115 + rkey: /*#__PURE__*/ v.string(), 116 + time_us: /*#__PURE__*/ v.integer(), 117 + uri: /*#__PURE__*/ v.resourceUriString(), 118 + value: /*#__PURE__*/ v.unknown(), 119 + }), 120 + }, 121 + }, 122 + ); 123 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 124 + $type: /*#__PURE__*/ v.optional( 125 + /*#__PURE__*/ v.literal( 126 + "org.latha.island.reasoning.getRecord#profileEntry", 127 + ), 128 + ), 129 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 130 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 131 + did: /*#__PURE__*/ v.didString(), 132 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 133 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 134 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 135 + get value() { 136 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 137 + }, 138 + }); 139 + const _refCitationRecordSchema = /*#__PURE__*/ v.object({ 140 + $type: /*#__PURE__*/ v.optional( 141 + /*#__PURE__*/ v.literal( 142 + "org.latha.island.reasoning.getRecord#refCitationRecord", 143 + ), 144 + ), 145 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 146 + collection: /*#__PURE__*/ v.nsidString(), 147 + did: /*#__PURE__*/ v.didString(), 148 + record: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), 149 + rkey: /*#__PURE__*/ v.string(), 150 + time_us: /*#__PURE__*/ v.integer(), 151 + uri: /*#__PURE__*/ v.resourceUriString(), 152 + }); 153 + 154 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 155 + type main$schematype = typeof _mainSchema; 156 + type profileEntry$schematype = typeof _profileEntrySchema; 157 + type refCitationRecord$schematype = typeof _refCitationRecordSchema; 158 + 159 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 160 + export interface mainSchema extends main$schematype {} 161 + export interface profileEntrySchema extends profileEntry$schematype {} 162 + export interface refCitationRecordSchema extends refCitationRecord$schematype {} 163 + 164 + export const appBskyActorProfileSchema = 165 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 166 + export const mainSchema = _mainSchema as mainSchema; 167 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 168 + export const refCitationRecordSchema = 169 + _refCitationRecordSchema as refCitationRecordSchema; 170 + 171 + export interface AppBskyActorProfile extends v.InferInput< 172 + typeof appBskyActorProfileSchema 173 + > {} 174 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 175 + export interface RefCitationRecord extends v.InferInput< 176 + typeof refCitationRecordSchema 177 + > {} 178 + 179 + export interface $params extends v.InferInput<mainSchema["params"]> {} 180 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 181 + 182 + declare module "@atcute/lexicons/ambient" { 183 + interface XRPCQueries { 184 + "org.latha.island.reasoning.getRecord": mainSchema; 185 + } 186 + }
+235
src/lexicon-types/types/org/latha/island/reasoning/listRecords.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + 7 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 8 + $type: /*#__PURE__*/ v.optional( 9 + /*#__PURE__*/ v.literal( 10 + "org.latha.island.reasoning.listRecords#appBskyActorProfile", 11 + ), 12 + ), 13 + /** 14 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 15 + * @accept image/png, image/jpeg 16 + * @maxSize 1000000 17 + */ 18 + avatar: /*#__PURE__*/ v.optional( 19 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 20 + /*#__PURE__*/ v.blobSize(1000000), 21 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 22 + ]), 23 + ), 24 + /** 25 + * Larger horizontal image to display behind profile view. 26 + * @accept image/png, image/jpeg 27 + * @maxSize 1000000 28 + */ 29 + banner: /*#__PURE__*/ v.optional( 30 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 31 + /*#__PURE__*/ v.blobSize(1000000), 32 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 33 + ]), 34 + ), 35 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 36 + /** 37 + * Free-form profile description text. 38 + * @maxLength 2560 39 + * @maxGraphemes 256 40 + */ 41 + description: /*#__PURE__*/ v.optional( 42 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 43 + /*#__PURE__*/ v.stringLength(0, 2560), 44 + /*#__PURE__*/ v.stringGraphemes(0, 256), 45 + ]), 46 + ), 47 + /** 48 + * @maxLength 640 49 + * @maxGraphemes 64 50 + */ 51 + displayName: /*#__PURE__*/ v.optional( 52 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 53 + /*#__PURE__*/ v.stringLength(0, 640), 54 + /*#__PURE__*/ v.stringGraphemes(0, 64), 55 + ]), 56 + ), 57 + get joinedViaStarterPack() { 58 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 59 + }, 60 + /** 61 + * Self-label values, specific to the Bluesky application, on the overall account. 62 + */ 63 + get labels() { 64 + return /*#__PURE__*/ v.optional( 65 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 66 + ); 67 + }, 68 + get pinnedPost() { 69 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 70 + }, 71 + /** 72 + * Free-form pronouns text. 73 + * @maxLength 200 74 + * @maxGraphemes 20 75 + */ 76 + pronouns: /*#__PURE__*/ v.optional( 77 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 78 + /*#__PURE__*/ v.stringLength(0, 200), 79 + /*#__PURE__*/ v.stringGraphemes(0, 20), 80 + ]), 81 + ), 82 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 83 + }); 84 + const _mainSchema = /*#__PURE__*/ v.query( 85 + "org.latha.island.reasoning.listRecords", 86 + { 87 + params: /*#__PURE__*/ v.object({ 88 + /** 89 + * Filter by DID or handle (triggers on-demand backfill) 90 + */ 91 + actor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.actorIdentifierString()), 92 + /** 93 + * Filter by confidence 94 + */ 95 + confidence: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 96 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 97 + /** 98 + * Embed the referenced citation record 99 + */ 100 + hydrateCitation: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 101 + /** 102 + * @minimum 1 103 + * @maximum 200 104 + * @default 50 105 + */ 106 + limit: /*#__PURE__*/ v.optional( 107 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 108 + /*#__PURE__*/ v.integerRange(1, 200), 109 + ]), 110 + 50, 111 + ), 112 + /** 113 + * Filter by method 114 + */ 115 + method: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 116 + /** 117 + * Sort direction (default: desc for dates/numbers/counts, asc for strings) 118 + */ 119 + order: /*#__PURE__*/ v.optional( 120 + /*#__PURE__*/ v.string<"asc" | "desc" | (string & {})>(), 121 + ), 122 + /** 123 + * Include profile + identity info keyed by DID 124 + */ 125 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 126 + /** 127 + * Full-text search across: question, evidence 128 + */ 129 + search: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 130 + /** 131 + * Field to sort by (default: time_us) 132 + */ 133 + sort: /*#__PURE__*/ v.optional( 134 + /*#__PURE__*/ v.string<"confidence" | "method" | (string & {})>(), 135 + ), 136 + }), 137 + output: { 138 + type: "lex", 139 + schema: /*#__PURE__*/ v.object({ 140 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 141 + get profiles() { 142 + return /*#__PURE__*/ v.optional( 143 + /*#__PURE__*/ v.array(profileEntrySchema), 144 + ); 145 + }, 146 + get records() { 147 + return /*#__PURE__*/ v.array(recordSchema); 148 + }, 149 + }), 150 + }, 151 + }, 152 + ); 153 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 154 + $type: /*#__PURE__*/ v.optional( 155 + /*#__PURE__*/ v.literal( 156 + "org.latha.island.reasoning.listRecords#profileEntry", 157 + ), 158 + ), 159 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 160 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 161 + did: /*#__PURE__*/ v.didString(), 162 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 163 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 164 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 165 + get value() { 166 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 167 + }, 168 + }); 169 + const _recordSchema = /*#__PURE__*/ v.object({ 170 + $type: /*#__PURE__*/ v.optional( 171 + /*#__PURE__*/ v.literal("org.latha.island.reasoning.listRecords#record"), 172 + ), 173 + cid: /*#__PURE__*/ v.cidString(), 174 + get citation() { 175 + return /*#__PURE__*/ v.optional(refCitationRecordSchema); 176 + }, 177 + collection: /*#__PURE__*/ v.nsidString(), 178 + did: /*#__PURE__*/ v.didString(), 179 + rkey: /*#__PURE__*/ v.string(), 180 + time_us: /*#__PURE__*/ v.integer(), 181 + uri: /*#__PURE__*/ v.resourceUriString(), 182 + value: /*#__PURE__*/ v.unknown(), 183 + }); 184 + const _refCitationRecordSchema = /*#__PURE__*/ v.object({ 185 + $type: /*#__PURE__*/ v.optional( 186 + /*#__PURE__*/ v.literal( 187 + "org.latha.island.reasoning.listRecords#refCitationRecord", 188 + ), 189 + ), 190 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 191 + collection: /*#__PURE__*/ v.nsidString(), 192 + did: /*#__PURE__*/ v.didString(), 193 + record: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), 194 + rkey: /*#__PURE__*/ v.string(), 195 + time_us: /*#__PURE__*/ v.integer(), 196 + uri: /*#__PURE__*/ v.resourceUriString(), 197 + }); 198 + 199 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 200 + type main$schematype = typeof _mainSchema; 201 + type profileEntry$schematype = typeof _profileEntrySchema; 202 + type record$schematype = typeof _recordSchema; 203 + type refCitationRecord$schematype = typeof _refCitationRecordSchema; 204 + 205 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 206 + export interface mainSchema extends main$schematype {} 207 + export interface profileEntrySchema extends profileEntry$schematype {} 208 + export interface recordSchema extends record$schematype {} 209 + export interface refCitationRecordSchema extends refCitationRecord$schematype {} 210 + 211 + export const appBskyActorProfileSchema = 212 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 213 + export const mainSchema = _mainSchema as mainSchema; 214 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 215 + export const recordSchema = _recordSchema as recordSchema; 216 + export const refCitationRecordSchema = 217 + _refCitationRecordSchema as refCitationRecordSchema; 218 + 219 + export interface AppBskyActorProfile extends v.InferInput< 220 + typeof appBskyActorProfileSchema 221 + > {} 222 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 223 + export interface Record extends v.InferInput<typeof recordSchema> {} 224 + export interface RefCitationRecord extends v.InferInput< 225 + typeof refCitationRecordSchema 226 + > {} 227 + 228 + export interface $params extends v.InferInput<mainSchema["params"]> {} 229 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 230 + 231 + declare module "@atcute/lexicons/ambient" { 232 + interface XRPCQueries { 233 + "org.latha.island.reasoning.listRecords": mainSchema; 234 + } 235 + }
+184
src/lexicon-types/types/org/latha/island/strata/getRecord.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + import * as NetworkCosmikCard from "../../../../network/cosmik/card.js"; 7 + 8 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 9 + $type: /*#__PURE__*/ v.optional( 10 + /*#__PURE__*/ v.literal( 11 + "org.latha.island.strata.getRecord#appBskyActorProfile", 12 + ), 13 + ), 14 + /** 15 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 16 + * @accept image/png, image/jpeg 17 + * @maxSize 1000000 18 + */ 19 + avatar: /*#__PURE__*/ v.optional( 20 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 21 + /*#__PURE__*/ v.blobSize(1000000), 22 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 23 + ]), 24 + ), 25 + /** 26 + * Larger horizontal image to display behind profile view. 27 + * @accept image/png, image/jpeg 28 + * @maxSize 1000000 29 + */ 30 + banner: /*#__PURE__*/ v.optional( 31 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 32 + /*#__PURE__*/ v.blobSize(1000000), 33 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 34 + ]), 35 + ), 36 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 37 + /** 38 + * Free-form profile description text. 39 + * @maxLength 2560 40 + * @maxGraphemes 256 41 + */ 42 + description: /*#__PURE__*/ v.optional( 43 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 44 + /*#__PURE__*/ v.stringLength(0, 2560), 45 + /*#__PURE__*/ v.stringGraphemes(0, 256), 46 + ]), 47 + ), 48 + /** 49 + * @maxLength 640 50 + * @maxGraphemes 64 51 + */ 52 + displayName: /*#__PURE__*/ v.optional( 53 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 54 + /*#__PURE__*/ v.stringLength(0, 640), 55 + /*#__PURE__*/ v.stringGraphemes(0, 64), 56 + ]), 57 + ), 58 + get joinedViaStarterPack() { 59 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 60 + }, 61 + /** 62 + * Self-label values, specific to the Bluesky application, on the overall account. 63 + */ 64 + get labels() { 65 + return /*#__PURE__*/ v.optional( 66 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 67 + ); 68 + }, 69 + get pinnedPost() { 70 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 71 + }, 72 + /** 73 + * Free-form pronouns text. 74 + * @maxLength 200 75 + * @maxGraphemes 20 76 + */ 77 + pronouns: /*#__PURE__*/ v.optional( 78 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 79 + /*#__PURE__*/ v.stringLength(0, 200), 80 + /*#__PURE__*/ v.stringGraphemes(0, 20), 81 + ]), 82 + ), 83 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 84 + }); 85 + const _mainSchema = /*#__PURE__*/ v.query("org.latha.island.strata.getRecord", { 86 + params: /*#__PURE__*/ v.object({ 87 + /** 88 + * Embed the referenced sourceCard record 89 + */ 90 + hydrateSourceCard: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 91 + /** 92 + * Include profile + identity info keyed by DID 93 + */ 94 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 95 + /** 96 + * AT URI of the record 97 + */ 98 + uri: /*#__PURE__*/ v.resourceUriString(), 99 + }), 100 + output: { 101 + type: "lex", 102 + schema: /*#__PURE__*/ v.object({ 103 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 104 + collection: /*#__PURE__*/ v.nsidString(), 105 + did: /*#__PURE__*/ v.didString(), 106 + get profiles() { 107 + return /*#__PURE__*/ v.optional( 108 + /*#__PURE__*/ v.array(profileEntrySchema), 109 + ); 110 + }, 111 + rkey: /*#__PURE__*/ v.string(), 112 + get sourceCard() { 113 + return /*#__PURE__*/ v.optional(refSourceCardRecordSchema); 114 + }, 115 + time_us: /*#__PURE__*/ v.integer(), 116 + uri: /*#__PURE__*/ v.resourceUriString(), 117 + value: /*#__PURE__*/ v.unknown(), 118 + }), 119 + }, 120 + }); 121 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 122 + $type: /*#__PURE__*/ v.optional( 123 + /*#__PURE__*/ v.literal("org.latha.island.strata.getRecord#profileEntry"), 124 + ), 125 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 126 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 127 + did: /*#__PURE__*/ v.didString(), 128 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 129 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 130 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 131 + get value() { 132 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 133 + }, 134 + }); 135 + const _refSourceCardRecordSchema = /*#__PURE__*/ v.object({ 136 + $type: /*#__PURE__*/ v.optional( 137 + /*#__PURE__*/ v.literal( 138 + "org.latha.island.strata.getRecord#refSourceCardRecord", 139 + ), 140 + ), 141 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 142 + collection: /*#__PURE__*/ v.nsidString(), 143 + did: /*#__PURE__*/ v.didString(), 144 + get record() { 145 + return /*#__PURE__*/ v.optional(NetworkCosmikCard.mainSchema); 146 + }, 147 + rkey: /*#__PURE__*/ v.string(), 148 + time_us: /*#__PURE__*/ v.integer(), 149 + uri: /*#__PURE__*/ v.resourceUriString(), 150 + }); 151 + 152 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 153 + type main$schematype = typeof _mainSchema; 154 + type profileEntry$schematype = typeof _profileEntrySchema; 155 + type refSourceCardRecord$schematype = typeof _refSourceCardRecordSchema; 156 + 157 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 158 + export interface mainSchema extends main$schematype {} 159 + export interface profileEntrySchema extends profileEntry$schematype {} 160 + export interface refSourceCardRecordSchema extends refSourceCardRecord$schematype {} 161 + 162 + export const appBskyActorProfileSchema = 163 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 164 + export const mainSchema = _mainSchema as mainSchema; 165 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 166 + export const refSourceCardRecordSchema = 167 + _refSourceCardRecordSchema as refSourceCardRecordSchema; 168 + 169 + export interface AppBskyActorProfile extends v.InferInput< 170 + typeof appBskyActorProfileSchema 171 + > {} 172 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 173 + export interface RefSourceCardRecord extends v.InferInput< 174 + typeof refSourceCardRecordSchema 175 + > {} 176 + 177 + export interface $params extends v.InferInput<mainSchema["params"]> {} 178 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 179 + 180 + declare module "@atcute/lexicons/ambient" { 181 + interface XRPCQueries { 182 + "org.latha.island.strata.getRecord": mainSchema; 183 + } 184 + }
+246
src/lexicon-types/types/org/latha/island/strata/listRecords.ts
··· 1 + import type {} from "@atcute/lexicons"; 2 + import * as v from "@atcute/lexicons/validations"; 3 + import type {} from "@atcute/lexicons/ambient"; 4 + import * as ComAtprotoLabelDefs from "@atcute/atproto/types/label/defs"; 5 + import * as ComAtprotoRepoStrongRef from "@atcute/atproto/types/repo/strongRef"; 6 + import * as NetworkCosmikCard from "../../../../network/cosmik/card.js"; 7 + 8 + const _appBskyActorProfileSchema = /*#__PURE__*/ v.object({ 9 + $type: /*#__PURE__*/ v.optional( 10 + /*#__PURE__*/ v.literal( 11 + "org.latha.island.strata.listRecords#appBskyActorProfile", 12 + ), 13 + ), 14 + /** 15 + * Small image to be displayed next to posts from account. AKA, 'profile picture' 16 + * @accept image/png, image/jpeg 17 + * @maxSize 1000000 18 + */ 19 + avatar: /*#__PURE__*/ v.optional( 20 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 21 + /*#__PURE__*/ v.blobSize(1000000), 22 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 23 + ]), 24 + ), 25 + /** 26 + * Larger horizontal image to display behind profile view. 27 + * @accept image/png, image/jpeg 28 + * @maxSize 1000000 29 + */ 30 + banner: /*#__PURE__*/ v.optional( 31 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [ 32 + /*#__PURE__*/ v.blobSize(1000000), 33 + /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"]), 34 + ]), 35 + ), 36 + createdAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 37 + /** 38 + * Free-form profile description text. 39 + * @maxLength 2560 40 + * @maxGraphemes 256 41 + */ 42 + description: /*#__PURE__*/ v.optional( 43 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 44 + /*#__PURE__*/ v.stringLength(0, 2560), 45 + /*#__PURE__*/ v.stringGraphemes(0, 256), 46 + ]), 47 + ), 48 + /** 49 + * @maxLength 640 50 + * @maxGraphemes 64 51 + */ 52 + displayName: /*#__PURE__*/ v.optional( 53 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 54 + /*#__PURE__*/ v.stringLength(0, 640), 55 + /*#__PURE__*/ v.stringGraphemes(0, 64), 56 + ]), 57 + ), 58 + get joinedViaStarterPack() { 59 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 60 + }, 61 + /** 62 + * Self-label values, specific to the Bluesky application, on the overall account. 63 + */ 64 + get labels() { 65 + return /*#__PURE__*/ v.optional( 66 + /*#__PURE__*/ v.variant([ComAtprotoLabelDefs.selfLabelsSchema]), 67 + ); 68 + }, 69 + get pinnedPost() { 70 + return /*#__PURE__*/ v.optional(ComAtprotoRepoStrongRef.mainSchema); 71 + }, 72 + /** 73 + * Free-form pronouns text. 74 + * @maxLength 200 75 + * @maxGraphemes 20 76 + */ 77 + pronouns: /*#__PURE__*/ v.optional( 78 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 79 + /*#__PURE__*/ v.stringLength(0, 200), 80 + /*#__PURE__*/ v.stringGraphemes(0, 20), 81 + ]), 82 + ), 83 + website: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()), 84 + }); 85 + const _mainSchema = /*#__PURE__*/ v.query( 86 + "org.latha.island.strata.listRecords", 87 + { 88 + params: /*#__PURE__*/ v.object({ 89 + /** 90 + * Filter by DID or handle (triggers on-demand backfill) 91 + */ 92 + actor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.actorIdentifierString()), 93 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 94 + /** 95 + * Embed the referenced sourceCard record 96 + */ 97 + hydrateSourceCard: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 98 + /** 99 + * @minimum 1 100 + * @maximum 200 101 + * @default 50 102 + */ 103 + limit: /*#__PURE__*/ v.optional( 104 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [ 105 + /*#__PURE__*/ v.integerRange(1, 200), 106 + ]), 107 + 50, 108 + ), 109 + /** 110 + * Sort direction (default: desc for dates/numbers/counts, asc for strings) 111 + */ 112 + order: /*#__PURE__*/ v.optional( 113 + /*#__PURE__*/ v.string<"asc" | "desc" | (string & {})>(), 114 + ), 115 + /** 116 + * Include profile + identity info keyed by DID 117 + */ 118 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 119 + /** 120 + * Full-text search across: analysis.synthesis, analysis.themes 121 + */ 122 + search: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 123 + /** 124 + * Field to sort by (default: time_us) 125 + */ 126 + sort: /*#__PURE__*/ v.optional( 127 + /*#__PURE__*/ v.string< 128 + "sourceCollection" | "sourceUri" | "updatedAt" | (string & {}) 129 + >(), 130 + ), 131 + /** 132 + * Filter by source.collection 133 + */ 134 + sourceCollection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 135 + /** 136 + * Filter by source.uri 137 + */ 138 + sourceUri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 139 + /** 140 + * Maximum value for updatedAt 141 + */ 142 + updatedAtMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 143 + /** 144 + * Minimum value for updatedAt 145 + */ 146 + updatedAtMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 147 + }), 148 + output: { 149 + type: "lex", 150 + schema: /*#__PURE__*/ v.object({ 151 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 152 + get profiles() { 153 + return /*#__PURE__*/ v.optional( 154 + /*#__PURE__*/ v.array(profileEntrySchema), 155 + ); 156 + }, 157 + get records() { 158 + return /*#__PURE__*/ v.array(recordSchema); 159 + }, 160 + }), 161 + }, 162 + }, 163 + ); 164 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 165 + $type: /*#__PURE__*/ v.optional( 166 + /*#__PURE__*/ v.literal("org.latha.island.strata.listRecords#profileEntry"), 167 + ), 168 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 169 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 170 + did: /*#__PURE__*/ v.didString(), 171 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 172 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 173 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 174 + get value() { 175 + return /*#__PURE__*/ v.optional(appBskyActorProfileSchema); 176 + }, 177 + }); 178 + const _recordSchema = /*#__PURE__*/ v.object({ 179 + $type: /*#__PURE__*/ v.optional( 180 + /*#__PURE__*/ v.literal("org.latha.island.strata.listRecords#record"), 181 + ), 182 + cid: /*#__PURE__*/ v.cidString(), 183 + collection: /*#__PURE__*/ v.nsidString(), 184 + did: /*#__PURE__*/ v.didString(), 185 + rkey: /*#__PURE__*/ v.string(), 186 + get sourceCard() { 187 + return /*#__PURE__*/ v.optional(refSourceCardRecordSchema); 188 + }, 189 + time_us: /*#__PURE__*/ v.integer(), 190 + uri: /*#__PURE__*/ v.resourceUriString(), 191 + value: /*#__PURE__*/ v.unknown(), 192 + }); 193 + const _refSourceCardRecordSchema = /*#__PURE__*/ v.object({ 194 + $type: /*#__PURE__*/ v.optional( 195 + /*#__PURE__*/ v.literal( 196 + "org.latha.island.strata.listRecords#refSourceCardRecord", 197 + ), 198 + ), 199 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 200 + collection: /*#__PURE__*/ v.nsidString(), 201 + did: /*#__PURE__*/ v.didString(), 202 + get record() { 203 + return /*#__PURE__*/ v.optional(NetworkCosmikCard.mainSchema); 204 + }, 205 + rkey: /*#__PURE__*/ v.string(), 206 + time_us: /*#__PURE__*/ v.integer(), 207 + uri: /*#__PURE__*/ v.resourceUriString(), 208 + }); 209 + 210 + type appBskyActorProfile$schematype = typeof _appBskyActorProfileSchema; 211 + type main$schematype = typeof _mainSchema; 212 + type profileEntry$schematype = typeof _profileEntrySchema; 213 + type record$schematype = typeof _recordSchema; 214 + type refSourceCardRecord$schematype = typeof _refSourceCardRecordSchema; 215 + 216 + export interface appBskyActorProfileSchema extends appBskyActorProfile$schematype {} 217 + export interface mainSchema extends main$schematype {} 218 + export interface profileEntrySchema extends profileEntry$schematype {} 219 + export interface recordSchema extends record$schematype {} 220 + export interface refSourceCardRecordSchema extends refSourceCardRecord$schematype {} 221 + 222 + export const appBskyActorProfileSchema = 223 + _appBskyActorProfileSchema as appBskyActorProfileSchema; 224 + export const mainSchema = _mainSchema as mainSchema; 225 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 226 + export const recordSchema = _recordSchema as recordSchema; 227 + export const refSourceCardRecordSchema = 228 + _refSourceCardRecordSchema as refSourceCardRecordSchema; 229 + 230 + export interface AppBskyActorProfile extends v.InferInput< 231 + typeof appBskyActorProfileSchema 232 + > {} 233 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 234 + export interface Record extends v.InferInput<typeof recordSchema> {} 235 + export interface RefSourceCardRecord extends v.InferInput< 236 + typeof refSourceCardRecordSchema 237 + > {} 238 + 239 + export interface $params extends v.InferInput<mainSchema["params"]> {} 240 + export interface $output extends v.InferXRPCBodyInput<mainSchema["output"]> {} 241 + 242 + declare module "@atcute/lexicons/ambient" { 243 + interface XRPCQueries { 244 + "org.latha.island.strata.listRecords": mainSchema; 245 + } 246 + }
+369
src/rdf-ingest.ts
··· 1 + /** 2 + * rdf:ingest 3 + * 4 + * Ingest the SciOS Resilient Data Futures discourse graph from 5 + * https://github.com/jring-o/rdf into network.cosmik.connection records 6 + * on the PDS. 7 + * 8 + * Each node becomes a vertex at https://rdf.scios.tech/node/{ID} 9 + * Each edge from frontmatter becomes a network.cosmik.connection record. 10 + * 11 + * Edge type mapping: 12 + * addresses → ADDRESSES (Claim → Question) 13 + * supports → SUPPORTS (Evidence→Claim, Claim→Claim) 14 + * opposes → OPPOSES (Claim→Claim, Evidence→Claim) 15 + * derivedFrom → DERIVED_FROM (Evidence → Source) 16 + * informs → INFORMS (Method → Claim) 17 + * usesMethod → USES_METHOD (Claim → Method) 18 + * 19 + * Usage: 20 + * bun rdf:ingest # dry run (prints plan) 21 + * bun rdf:ingest --write # write to PDS 22 + * bun rdf:ingest --write --json # write + JSON output 23 + */ 24 + 25 + import { resolve, dirname, join } from 'node:path'; 26 + import { fileURLToPath } from 'node:url'; 27 + import { readdirSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs'; 28 + import { AtpAgent } from '@atproto/api'; 29 + import { loadDotEnv } from './cli-utils.js'; 30 + 31 + const __dirname = dirname(fileURLToPath(import.meta.url)); 32 + await loadDotEnv(resolve(__dirname, '..', '.env')); 33 + await loadDotEnv('/home/nandi/code/swarm/.env'); 34 + 35 + const RDF_BASE = 'https://rdf.scios.tech/node/'; 36 + const PDS_IDENTIFIER = process.env.PDS_IDENTIFIER ?? process.env.BLUESKY_IDENTIFIER ?? ''; 37 + const PDS_APP_PASSWORD = process.env.PDS_APP_PASSWORD ?? process.env.BLUESKY_APP_PASSWORD ?? ''; 38 + const PDS_SERVICE = process.env.PDS_SERVICE ?? 'https://amanita.us-east.host.bsky.network'; 39 + const RDF_REPO = process.env.RDF_REPO ?? '/home/nandi/code/rdf'; 40 + const PROVENANCE_PATH = process.env.RDF_INGEST_PROVENANCE ?? resolve(RDF_REPO, '.rdf-ingest.json'); 41 + 42 + // --- Types --- 43 + 44 + interface NodeData { 45 + id: string; 46 + type: string; 47 + title: string; 48 + status: string; 49 + sourceSection: string; 50 + created: string; 51 + edges: Record<string, string[]>; 52 + body: string; 53 + url: string; 54 + } 55 + 56 + interface EdgeToWrite { 57 + source: string; // https://rdf.scios.tech/node/{from-id} 58 + target: string; // https://rdf.scios.tech/node/{to-id} 59 + connectionType: string; 60 + note: string; 61 + fromId: string; 62 + toId: string; 63 + edgeType: string; // original RDF edge type 64 + } 65 + 66 + const EDGE_TYPE_MAP: Record<string, string> = { 67 + addresses: 'ADDRESSES', 68 + supports: 'SUPPORTS', 69 + opposes: 'OPPOSES', 70 + derivedFrom: 'DERIVED_FROM', 71 + informs: 'INFORMS', 72 + usesMethod: 'USES_METHOD', 73 + }; 74 + 75 + const TYPE_DIRS: Record<string, string> = { 76 + claim: 'claims', 77 + evidence: 'evidence', 78 + question: 'questions', 79 + source: 'sources', 80 + method: 'methods', 81 + }; 82 + 83 + // --- Parse --- 84 + 85 + function parseFrontmatter(content: string): { fm: Record<string, unknown>; body: string } { 86 + const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/); 87 + if (!match) return { fm: {}, body: content }; 88 + 89 + const fmText = match[1]; 90 + const body = match[2].trim(); 91 + 92 + // Two-pass YAML parser: 93 + // Pass 1: extract top-level keys and nested dicts (like edges:) 94 + // Pass 2: for each nested dict, parse sub-keys with inline arrays 95 + 96 + const fm: Record<string, unknown> = {}; 97 + const lines = fmText.split('\n'); 98 + 99 + let i = 0; 100 + while (i < lines.length) { 101 + const line = lines[i]; 102 + 103 + // Top-level key: value 104 + const kvMatch = line.match(/^(\w+):\s*(.*)$/); 105 + if (kvMatch) { 106 + const key = kvMatch[1]; 107 + const val = kvMatch[2].trim(); 108 + 109 + if (val === '') { 110 + // Could be a nested dict or empty — check next lines for indentation 111 + const subDict: Record<string, string[]> = {}; 112 + let j = i + 1; 113 + while (j < lines.length && lines[j].match(/^\s{2,}/)) { 114 + const subMatch = lines[j].match(/^\s+(\w+):\s*(.*)$/); 115 + if (subMatch) { 116 + const subKey = subMatch[1]; 117 + const subVal = subMatch[2].trim(); 118 + if (subVal.startsWith('[') && subVal.endsWith(']')) { 119 + subDict[subKey] = subVal.slice(1, -1).split(',').map(s => s.trim().replace(/['"]/g, '')).filter(Boolean); 120 + } else if (subVal === '' || subVal === '[]') { 121 + subDict[subKey] = []; 122 + } else { 123 + subDict[subKey] = [subVal.replace(/^['"]|['"]$/g, '')]; 124 + } 125 + } 126 + j++; 127 + } 128 + if (Object.keys(subDict).length > 0) { 129 + fm[key] = subDict; 130 + i = j; 131 + continue; 132 + } 133 + // Empty dict 134 + fm[key] = {}; 135 + i++; 136 + continue; 137 + } 138 + 139 + if (val.startsWith('[') && val.endsWith(']')) { 140 + fm[key] = val.slice(1, -1).split(',').map(s => s.trim().replace(/['"]/g, '')).filter(Boolean); 141 + } else if (val === '[]') { 142 + fm[key] = []; 143 + } else { 144 + fm[key] = val.replace(/^['"]|['"]$/g, ''); 145 + } 146 + } 147 + i++; 148 + } 149 + 150 + return { fm, body }; 151 + } 152 + 153 + function loadNodes(): NodeData[] { 154 + const nodes: NodeData[] = []; 155 + 156 + for (const [type, dir] of Object.entries(TYPE_DIRS)) { 157 + const dirPath = join(RDF_REPO, 'graph', dir); 158 + let files: string[]; 159 + try { 160 + files = readdirSync(dirPath).filter(f => f.endsWith('.md')).sort(); 161 + } catch { 162 + console.error(` Skipping ${dirPath}: not found`); 163 + continue; 164 + } 165 + 166 + for (const file of files) { 167 + const content = readFileSync(join(dirPath, file), 'utf-8'); 168 + const { fm, body } = parseFrontmatter(content); 169 + 170 + const id = (fm.id as string) ?? file.replace('.md', ''); 171 + const edges: Record<string, string[]> = {}; 172 + // fm.edges is the nested dict from YAML: { addresses: [Q-0001], supports: [...], ... } 173 + const fmEdges = fm.edges as Record<string, string[] | unknown> | undefined; 174 + if (fmEdges && typeof fmEdges === 'object') { 175 + for (const [key, val] of Object.entries(fmEdges)) { 176 + if (EDGE_TYPE_MAP[key]) { 177 + edges[key] = Array.isArray(val) ? val : [val as string]; 178 + } 179 + } 180 + } 181 + // Also check top-level edge keys (flat frontmatter style) 182 + for (const [key, val] of Object.entries(fm)) { 183 + if (key === 'edges') continue; 184 + if (EDGE_TYPE_MAP[key] && !edges[key]) { 185 + edges[key] = Array.isArray(val) ? val : [val as string]; 186 + } 187 + } 188 + 189 + nodes.push({ 190 + id, 191 + type: (fm.type as string) ?? type, 192 + title: (fm.title as string) ?? id, 193 + status: (fm.status as string) ?? 'draft', 194 + sourceSection: (fm.source_section as string) ?? '', 195 + created: (fm.created as string) ?? '', 196 + edges, 197 + body, 198 + url: `${RDF_BASE}${id}`, 199 + }); 200 + } 201 + } 202 + 203 + return nodes; 204 + } 205 + 206 + function extractEdges(nodes: NodeData[]): EdgeToWrite[] { 207 + const nodeMap = new Map(nodes.map(n => [n.id, n])); 208 + const edges: EdgeToWrite[] = []; 209 + const seen = new Set<string>(); 210 + 211 + for (const node of nodes) { 212 + for (const [edgeType, targets] of Object.entries(node.edges)) { 213 + const connectionType = EDGE_TYPE_MAP[edgeType]; 214 + if (!connectionType) continue; 215 + 216 + for (const toId of targets) { 217 + const key = `${node.id}:${edgeType}:${toId}`; 218 + if (seen.has(key)) continue; 219 + seen.add(key); 220 + 221 + if (!nodeMap.has(toId)) { 222 + console.error(` Warning: ${node.id} references missing node ${toId}`); 223 + } 224 + 225 + edges.push({ 226 + source: node.url, 227 + target: `${RDF_BASE}${toId}`, 228 + connectionType, 229 + note: `${node.id} --${edgeType}--> ${toId}`, 230 + fromId: node.id, 231 + toId, 232 + edgeType, 233 + }); 234 + } 235 + } 236 + } 237 + 238 + return edges; 239 + } 240 + 241 + // --- PDS writes --- 242 + 243 + async function writeEdges(edges: EdgeToWrite[]): Promise<{ wrote: number; uris: string[] }> { 244 + if (!PDS_IDENTIFIER || !PDS_APP_PASSWORD) { 245 + throw new Error('PDS_IDENTIFIER and PDS_APP_PASSWORD must be set'); 246 + } 247 + 248 + const agent = new AtpAgent({ service: PDS_SERVICE }); 249 + await agent.login({ identifier: PDS_IDENTIFIER, password: PDS_APP_PASSWORD }); 250 + const did = agent.session!.did; 251 + 252 + let wrote = 0; 253 + const uris: string[] = []; 254 + 255 + // Batch in groups of 10 to avoid rate limits 256 + for (let i = 0; i < edges.length; i += 10) { 257 + const batch = edges.slice(i, i + 10); 258 + const results = await Promise.allSettled( 259 + batch.map(async (edge) => { 260 + const record = { 261 + $type: 'network.cosmik.connection', 262 + source: edge.source, 263 + target: edge.target, 264 + connectionType: edge.connectionType, 265 + note: edge.note, 266 + createdAt: new Date().toISOString(), 267 + }; 268 + 269 + // Use createRecord to get auto-generated TID rkeys (putRecord with custom rkeys fails validation) 270 + const res = await agent.api.com.atproto.repo.createRecord({ 271 + repo: did, 272 + collection: 'network.cosmik.connection', 273 + record, 274 + }); 275 + return res.data.uri; 276 + }), 277 + ); 278 + 279 + for (const result of results) { 280 + if (result.status === 'fulfilled') { 281 + wrote++; 282 + uris.push(result.value); 283 + } else { 284 + console.error(` Failed: ${result.reason}`); 285 + } 286 + } 287 + 288 + // Small delay between batches 289 + if (i + 10 < edges.length) { 290 + await new Promise(r => setTimeout(r, 500)); 291 + } 292 + } 293 + 294 + return { wrote, uris }; 295 + } 296 + 297 + // --- Main --- 298 + 299 + async function main(): Promise<void> { 300 + const args = process.argv.slice(2); 301 + const wantWrite = args.includes('--write'); 302 + const wantJson = args.includes('--json'); 303 + 304 + console.error('Loading nodes from RDF repo...'); 305 + const nodes = loadNodes(); 306 + 307 + const typeCounts: Record<string, number> = {}; 308 + for (const n of nodes) typeCounts[n.type] = (typeCounts[n.type] ?? 0) + 1; 309 + console.error(` ${nodes.length} nodes: ${Object.entries(typeCounts).map(([t, c]) => `${t}: ${c}`).join(', ')}`); 310 + 311 + console.error('Extracting edges...'); 312 + const edges = extractEdges(nodes); 313 + 314 + const edgeTypeCounts: Record<string, number> = {}; 315 + for (const e of edges) edgeTypeCounts[e.edgeType] = (edgeTypeCounts[e.edgeType] ?? 0) + 1; 316 + console.error(` ${edges.length} edges: ${Object.entries(edgeTypeCounts).map(([t, c]) => `${t}: ${c}`).join(', ')}`); 317 + 318 + if (wantWrite) { 319 + console.error(`Writing ${edges.length} edges to PDS...`); 320 + const { wrote, uris } = await writeEdges(edges); 321 + console.error(` Wrote ${wrote}/${edges.length} edges`); 322 + 323 + saveJson(PROVENANCE_PATH, { 324 + ingestedAt: new Date().toISOString(), 325 + nodes: nodes.length, 326 + edges: edges.length, 327 + wrote, 328 + uris: uris.slice(0, 100), 329 + repo: 'https://github.com/jring-o/rdf', 330 + }); 331 + } 332 + 333 + if (wantJson) { 334 + console.log(JSON.stringify({ nodes, edges, wrote: wantWrite ? edges.length : 0 }, null, 2)); 335 + return; 336 + } 337 + 338 + // Summary output 339 + console.log(`\nRDF Discourse Graph: ${nodes.length} nodes, ${edges.length} edges`); 340 + if (!wantWrite) { 341 + console.log('(dry run — use --write to publish to PDS)'); 342 + } 343 + 344 + // Sample edges 345 + console.log('\nEdge sample:'); 346 + for (const e of edges.slice(0, 20)) { 347 + console.log(` ${e.fromId} --${e.edgeType}--> ${e.toId} [${e.connectionType}]`); 348 + } 349 + if (edges.length > 20) { 350 + console.log(` ... and ${edges.length - 20} more`); 351 + } 352 + 353 + // Node sample 354 + console.log('\nNode sample:'); 355 + for (const n of nodes.slice(0, 10)) { 356 + const edgeCount = Object.values(n.edges).flat().length; 357 + console.log(` ${n.id} [${n.type}] "${n.title.slice(0, 60)}" (${edgeCount} edges)`); 358 + } 359 + } 360 + 361 + function saveJson(path: string, value: unknown): void { 362 + mkdirSync(dirname(path), { recursive: true }); 363 + writeFileSync(path, JSON.stringify(value, null, 2) + '\n', 'utf-8'); 364 + } 365 + 366 + main().catch((err) => { 367 + console.error('Fatal:', err instanceof Error ? err.message : String(err)); 368 + process.exit(1); 369 + });
+990 -113
src/worker.ts
··· 12 12 ASSETS: { fetch(request: Request): Promise<Response> }; 13 13 VAULT_BUCKET: R2Bucket; 14 14 CARRY_BUCKET: R2Bucket; 15 + LETTA_API_KEY?: string; 16 + LETTA_AGENT_ID?: string; 17 + } 18 + 19 + interface CentroidDiscoveryResult { 20 + title: string; 21 + themes: string[]; 22 + highlights: string[]; 23 + tensions: string[]; 24 + openQuestions: string[]; 25 + synthesis: string; 26 + edges: Array<{ source: string; target: string; connectionType: string; note?: string }>; 15 27 } 16 28 17 29 // ─── Contrail setup ───────────────────────────────────────────────── ··· 24 36 if (contrailReady) return; 25 37 await contrail.init(db); 26 38 contrailReady = true; 39 + } 40 + 41 + async function resolveDidIdentity(did: string): Promise<{ did: string; handle: string | null; pds: string | null } | null> { 42 + let doc: any; 43 + if (did.startsWith("did:plc:")) { 44 + const resp = await fetch(`https://plc.directory/${encodeURIComponent(did)}`); 45 + if (!resp.ok) return null; 46 + doc = await resp.json(); 47 + } else if (did.startsWith("did:web:")) { 48 + const host = did.slice("did:web:".length).replace(/:/g, "/"); 49 + const resp = await fetch(`https://${host}/.well-known/did.json`); 50 + if (!resp.ok) return null; 51 + doc = await resp.json(); 52 + } else { 53 + return null; 54 + } 55 + 56 + const aka = Array.isArray(doc.alsoKnownAs) ? doc.alsoKnownAs.find((v: string) => v.startsWith("at://")) : null; 57 + const handle = aka ? aka.slice("at://".length) : null; 58 + const svc = Array.isArray(doc.service) 59 + ? doc.service.find((s: any) => s.type === "AtprotoPersonalDataServer" || s.id === "#atproto_pds") 60 + : null; 61 + const pds = typeof svc?.serviceEndpoint === "string" ? svc.serviceEndpoint.replace(/\/$/, "") : null; 62 + return { did, handle, pds }; 63 + } 64 + 65 + // ─── Resolve synthesisDoc AT URI pointers ─────────────────────────── 66 + // 67 + // synthesisDoc used to be an inline pub.oxa.document object. 68 + // Now it's an AT URI pointing to a separate pub.oxa.document record. 69 + // This helper dereferences the pointer so the frontend always gets 70 + // the resolved OxaRecord object. 71 + 72 + const synthesisDocCache = new Map<string, any>(); 73 + 74 + async function resolveSynthesisDoc(synthesisDoc: any): Promise<any> { 75 + if (!synthesisDoc) return null; 76 + 77 + // Already an inline OXA document object (legacy format) 78 + if (typeof synthesisDoc === "object" && synthesisDoc.$type === "pub.oxa.document") { 79 + return synthesisDoc; 80 + } 81 + 82 + // AT URI string — dereference it 83 + if (typeof synthesisDoc === "string" && synthesisDoc.startsWith("at://")) { 84 + // Check cache 85 + if (synthesisDocCache.has(synthesisDoc)) { 86 + return synthesisDocCache.get(synthesisDoc); 87 + } 88 + 89 + try { 90 + // Parse AT URI: at://<did>/<collection>/<rkey> 91 + const parts = synthesisDoc.replace("at://", "").split("/"); 92 + const did = parts[0]; 93 + const collection = parts[1]; 94 + const rkey = parts[2]; 95 + if (!did || !collection || !rkey) return null; 96 + 97 + // Resolve PDS for this DID 98 + const identity = await resolveDidIdentity(did); 99 + if (!identity?.pds) return null; 100 + 101 + const resp = await fetch( 102 + `${identity.pds}/xrpc/com.atproto.repo.getRecord?repo=${did}&collection=${collection}&rkey=${rkey}` 103 + ); 104 + if (!resp.ok) return null; 105 + 106 + const data: any = await resp.json(); 107 + const doc = data.value; 108 + if (doc?.$type === "pub.oxa.document") { 109 + synthesisDocCache.set(synthesisDoc, doc); 110 + return doc; 111 + } 112 + } catch { 113 + return null; 114 + } 115 + } 116 + 117 + return null; 27 118 } 28 119 29 120 // ─── Island detection (connected components) ──────────────────────── ··· 420 511 421 512 // ─── Resolve edge URIs to full edge objects ────────────────────── 422 513 423 - /** Given a list of connections (either full objects or just {uri}), resolve URIs to full edges from D1. */ 514 + /** Given a list of connections (AT URI strings, legacy {uri}, or legacy inline objects), resolve URIs to full edges from D1. */ 424 515 async function resolveEdgeUris(db: D1Database, connections: any[]): Promise<any[]> { 425 516 const resolved: any[] = []; 426 517 const uriIndices: Map<string, number[]> = new Map(); // uri → indices in connections that need it 427 518 428 519 for (let i = 0; i < connections.length; i++) { 429 520 const conn = connections[i]; 521 + if (typeof conn === "string") { 522 + resolved.push(null); 523 + if (!uriIndices.has(conn)) uriIndices.set(conn, []); 524 + uriIndices.get(conn)!.push(i); 525 + continue; 526 + } 430 527 // Already has source/target — use as-is 431 528 if (typeof conn === "object" && conn.source && conn.target) { 432 529 resolved.push(conn); 433 530 continue; 434 531 } 435 532 // Just a URI reference — needs lookup 436 - const uri = conn.uri; 533 + const uri = conn?.uri; 437 534 if (!uri) continue; 438 535 resolved.push(null); // placeholder 439 536 if (!uriIndices.has(uri)) uriIndices.set(uri, []); ··· 443 540 // Batch-fetch connection records from D1 (for URI-only references) 444 541 if (uriIndices.size > 0) { 445 542 const uris = [...uriIndices.keys()]; 446 - const placeholders = uris.map(() => "?").join(","); 447 - const rows = await db 448 - .prepare( 449 - `SELECT r.uri, r.record, r.did, r.rkey, i.handle 450 - FROM records_connection r 451 - LEFT JOIN identities i ON r.did = i.did 452 - WHERE r.uri IN (${placeholders})`, 453 - ) 454 - .bind(...uris) 455 - .all<{ uri: string; record: string; did: string; rkey: string; handle: string | null }>(); 543 + const CHUNK = 100; 544 + for (let i = 0; i < uris.length; i += CHUNK) { 545 + const chunk = uris.slice(i, i + CHUNK); 546 + const placeholders = chunk.map(() => "?").join(","); 547 + const rows = await db 548 + .prepare( 549 + `SELECT r.uri, r.record, r.did, r.rkey, i.handle 550 + FROM records_connection r 551 + LEFT JOIN identities i ON r.did = i.did 552 + WHERE r.uri IN (${placeholders})`, 553 + ) 554 + .bind(...chunk) 555 + .all<{ uri: string; record: string; did: string; rkey: string; handle: string | null }>(); 456 556 457 - for (const row of rows.results || []) { 458 - try { 459 - const value = JSON.parse(row.record); 460 - const edge = { 461 - uri: row.uri, 462 - did: row.did, 463 - source: value.source, 464 - target: value.target, 465 - connectionType: value.connectionType || "relates", 466 - note: value.note || "", 467 - handle: row.handle, 468 - }; 469 - for (const idx of uriIndices.get(row.uri) || []) { 470 - resolved[idx] = edge; 471 - } 472 - } catch {} 557 + for (const row of rows.results || []) { 558 + try { 559 + const value = JSON.parse(row.record); 560 + const edge = { 561 + uri: row.uri, 562 + did: row.did, 563 + source: value.source, 564 + target: value.target, 565 + connectionType: value.connectionType || "relates", 566 + note: value.note || "", 567 + handle: row.handle, 568 + }; 569 + for (const idx of uriIndices.get(row.uri) || []) { 570 + resolved[idx] = edge; 571 + } 572 + } catch {} 573 + } 473 574 } 474 575 } 475 576 ··· 510 611 return resolved.filter(Boolean); 511 612 } 512 613 614 + /** Resolve at://inline:* URIs by looking up the forkOf record's inline edge data */ 615 + async function resolveInlineFromForkOf(db: D1Database, record: any): Promise<any[]> { 616 + const connections: any[] = Array.isArray(record.connections) ? record.connections : []; 617 + const hasInlineUris = connections.some(c => typeof c === "string" && c.startsWith("at://inline:")); 618 + if (!hasInlineUris || !record.forkOf) return []; 619 + 620 + // Look up the forkOf record 621 + const forkRow = await db 622 + .prepare("SELECT record FROM records_island WHERE uri = ?") 623 + .bind(record.forkOf) 624 + .first<{ record: string }>(); 625 + if (!forkRow) return []; 626 + 627 + const forkRec = JSON.parse(forkRow.record); 628 + const forkConnections: any[] = Array.isArray(forkRec.connections) ? forkRec.connections : []; 629 + 630 + // Build a map from inline URI → edge data 631 + const inlineMap = new Map<string, any>(); 632 + for (const conn of forkConnections) { 633 + if (typeof conn === "object" && conn.uri && conn.source && conn.target) { 634 + inlineMap.set(conn.uri, conn); 635 + } 636 + } 637 + 638 + // Resolve the current record's connections using the fork's inline data 639 + const resolved: any[] = []; 640 + for (const conn of connections) { 641 + if (typeof conn === "string" && inlineMap.has(conn)) { 642 + const edge = inlineMap.get(conn); 643 + resolved.push({ 644 + uri: conn, 645 + source: edge.source, 646 + target: edge.target, 647 + connectionType: edge.connectionType || "relates", 648 + note: edge.note || "", 649 + }); 650 + } 651 + } 652 + return resolved; 653 + } 654 + 655 + async function findSimilarIslands(db: D1Database, currentUri: string, currentVertices: Set<string>, currentConnUris?: Set<string>): Promise<any[]> { 656 + if (currentVertices.size === 0) return []; 657 + const domainOf = (url: string): string | null => { 658 + try { 659 + const u = new URL(url); 660 + return u.hostname.replace(/^www\./, "").toLowerCase(); 661 + } catch { return null; } 662 + }; 663 + const currentDomains = new Set([...currentVertices].map(domainOf).filter(Boolean) as string[]); 664 + const rows = await db 665 + .prepare("SELECT uri, record FROM records_island WHERE uri != ? AND uri NOT LIKE '%org.latha.strata%' ORDER BY time_us DESC LIMIT 500") 666 + .bind(currentUri) 667 + .all<{ uri: string; record: string }>(); 668 + 669 + // Phase 1: Fast pre-filter using connection URI overlap (no edge resolution) 670 + const candidates: { uri: string; record: any; connOverlap: number; centroid: string | null }[] = []; 671 + for (const row of rows.results || []) { 672 + try { 673 + const rec = JSON.parse(row.record); 674 + const connections: any[] = Array.isArray(rec.connections) ? rec.connections : []; 675 + const candidateConnUris = new Set(connections.filter(c => typeof c === "string")); 676 + const centroid = rec.source?.uri || null; 677 + 678 + // Fast overlap: shared connection AT URIs 679 + let connOverlap = 0; 680 + if (currentConnUris && currentConnUris.size > 0) { 681 + for (const uri of candidateConnUris) { 682 + if (currentConnUris.has(uri)) connOverlap++; 683 + } 684 + } 685 + 686 + // Quick centroid/domain check for islands with no connection overlap 687 + if (connOverlap === 0 && centroid) { 688 + const centroidDomain = domainOf(centroid); 689 + if (centroidDomain && currentDomains.has(centroidDomain)) connOverlap = -1; // domain match, low priority 690 + } 691 + 692 + if (connOverlap !== 0 || (centroid && currentVertices.has(centroid))) { 693 + candidates.push({ uri: row.uri, record: rec, connOverlap, centroid }); 694 + } 695 + } catch {} 696 + } 697 + 698 + // Phase 2: Resolve edges only for top candidates (max 20) 699 + candidates.sort((a, b) => Math.abs(b.connOverlap) - Math.abs(a.connOverlap)); 700 + const topCandidates = candidates.slice(0, 20); 701 + 702 + const similar: any[] = []; 703 + for (const candidate of topCandidates) { 704 + try { 705 + const rec = candidate.record; 706 + const connections: any[] = Array.isArray(rec.connections) ? rec.connections : []; 707 + const edges = await resolveEdgeUris(db, connections); 708 + const vertices = new Set<string>(); 709 + const centroid = rec.source?.uri || null; 710 + if (centroid) vertices.add(centroid); 711 + for (const edge of edges) { 712 + if (edge?.source) vertices.add(edge.source); 713 + if (edge?.target) vertices.add(edge.target); 714 + } 715 + const sharedVertices = [...vertices].filter(v => currentVertices.has(v)); 716 + const domains = new Set([...vertices].map(domainOf).filter(Boolean) as string[]); 717 + const sharedDomains = [...domains].filter(d => currentDomains.has(d)); 718 + if (sharedVertices.length === 0 && sharedDomains.length < 2) continue; 719 + const did = candidate.uri.split("/")[2]; 720 + const rkey = candidate.uri.split("/").pop() || candidate.uri; 721 + similar.push({ 722 + id: rkey, 723 + recordUri: candidate.uri, 724 + handle: did === "did:plc:3kkhul7jznlb6ba7rprzawnj" ? "researcher.pds.latha.org" : did === "did:plc:ngokl2gnmpbvuvrfckja3g7p" ? "nandi.uk" : null, 725 + title: rec.analysis?.title || null, 726 + centroid, 727 + sharedCount: sharedVertices.length, 728 + sharedDomainCount: sharedDomains.length, 729 + score: sharedVertices.length * 10 + sharedDomains.length, 730 + vertexCount: vertices.size, 731 + edgeCount: connections.length, 732 + sharedVertices: sharedVertices.slice(0, 8), 733 + sharedDomains: sharedDomains.slice(0, 8), 734 + }); 735 + } catch {} 736 + } 737 + 738 + similar.sort((a, b) => (b.score - a.score) || (b.edgeCount - a.edgeCount)); 739 + return similar.slice(0, 6); 740 + } 741 + 742 + async function discoverCentroidWithLetta(env: Env, centroid: string): Promise<CentroidDiscoveryResult> { 743 + const apiKey = env.LETTA_API_KEY; 744 + const agentId = env.LETTA_AGENT_ID || "agent-bc21f9dc-83b1-4826-b9af-999f7c6b6053"; 745 + if (!apiKey) throw new Error("LETTA_API_KEY is not configured"); 746 + 747 + const prompt = `You are creating a stigmergic island from a centroid URL. 748 + 749 + Centroid: ${centroid} 750 + 751 + Use web research to discover 3-7 directly related HTTPS URLs. Prefer canonical sources: cited papers, project pages, docs, author pages, references, follow-up posts, or explanatory articles. Do not use broad domain matches. Every edge must use the centroid as source or target unless a cited/reference relationship is clearer. Then synthesize the island. 752 + 753 + Return ONLY compact JSON with this exact shape: 754 + { 755 + "title": "short island title", 756 + "themes": ["theme1", "theme2"], 757 + "highlights": [], 758 + "tensions": ["structural tension"], 759 + "openQuestions": ["question?"], 760 + "synthesis": "2-4 concise paragraphs explaining what binds the island", 761 + "edges": [ 762 + {"source":"${centroid}","target":"https://example.com/real-related-url","connectionType":"network.cosmik.connection#related","note":"why this edge belongs"} 763 + ] 764 + } 765 + 766 + Rules: JSON only, no markdown fences. Source/target must be canonical HTTPS URLs. Include at least one edge. Do not invent URLs.`; 767 + 768 + const res = await fetch(`https://api.letta.com/v1/agents/${agentId}/messages`, { 769 + method: "POST", 770 + headers: { 771 + "Authorization": `Bearer ${apiKey}`, 772 + "Content-Type": "application/json", 773 + }, 774 + body: JSON.stringify({ messages: [{ role: "user", content: prompt }] }), 775 + }); 776 + if (!res.ok) throw new Error(`Letta API failed: ${res.status} ${await res.text()}`); 777 + 778 + const data = await res.json() as { messages?: Array<{ message_type?: string; content?: string }> }; 779 + const content = [...(data.messages || [])].reverse().find(m => m.message_type === "assistant_message" && m.content)?.content; 780 + if (!content) throw new Error("Letta API returned no assistant content"); 781 + 782 + const jsonText = content.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, ""); 783 + const parsed = JSON.parse(jsonText) as CentroidDiscoveryResult; 784 + const edges = (parsed.edges || []) 785 + .filter(e => e.source?.startsWith("https://") && e.target?.startsWith("https://")) 786 + .slice(0, 10); 787 + if (edges.length === 0) throw new Error("Letta discovery returned no usable HTTPS edges"); 788 + 789 + return { 790 + title: parsed.title || centroid, 791 + themes: Array.isArray(parsed.themes) ? parsed.themes.slice(0, 8) : ["web-discovery"], 792 + highlights: [], 793 + tensions: Array.isArray(parsed.tensions) ? parsed.tensions.slice(0, 3) : [], 794 + openQuestions: Array.isArray(parsed.openQuestions) ? parsed.openQuestions.slice(0, 5) : [], 795 + synthesis: parsed.synthesis || `Island seeded from ${centroid}.`, 796 + edges, 797 + }; 798 + } 799 + 800 + function countVerticesFromConnections(connections: any[], centroid?: string | null): number { 801 + const vertices = new Set<string>(); 802 + if (centroid) vertices.add(centroid); 803 + for (const conn of connections || []) { 804 + if (conn?.source) vertices.add(conn.source); 805 + if (conn?.target) vertices.add(conn.target); 806 + } 807 + return vertices.size; 808 + } 809 + 810 + async function loadHomepageIslands(db: D1Database, pageNum: number, pageSize: number): Promise<{ islands: any[]; total: number }> { 811 + const rows = await db 812 + .prepare("SELECT uri, record FROM records_island WHERE uri NOT LIKE '%org.latha.strata%'") 813 + .all<{ uri: string; record: string }>(); 814 + 815 + // Phase 1: Parse all records and sort by edge count (no edge resolution) 816 + const all: any[] = []; 817 + for (const row of rows.results || []) { 818 + try { 819 + const rec = JSON.parse(row.record); 820 + const centroid = rec.source?.uri; 821 + if (!centroid) continue; 822 + const id = row.uri.split("/").pop() || row.uri; 823 + const did = row.uri.split("/")[2]; 824 + const connections: any[] = Array.isArray(rec.connections) ? rec.connections : []; 825 + const edgeCount = connections.length; 826 + const themes = Array.isArray(rec.analysis?.themes) ? rec.analysis.themes : []; 827 + 828 + all.push({ row, rec, id, did, centroid, edgeCount, themes }); 829 + } catch {} 830 + } 831 + 832 + all.sort((a, b) => (b.edgeCount - a.edgeCount) || (a.id.localeCompare(b.id))); 833 + const selected = all.slice((pageNum - 1) * pageSize, pageNum * pageSize); 834 + 835 + // Phase 2: Build island summaries with graph SVG URLs 836 + const islands = selected.map((item) => ({ 837 + id: item.id, 838 + centroid: item.centroid, 839 + title: item.rec.analysis?.title || null, 840 + summary: item.rec.analysis?.title || null, 841 + recordUri: item.row.uri, 842 + handle: item.did === "did:plc:3kkhul7jznlb6ba7rprzawnj" ? "researcher.pds.latha.org" : item.did === "did:plc:ngokl2gnmpbvuvrfckja3g7p" ? "nandi.uk" : null, 843 + vertexCount: 0, 844 + edgeCount: item.edgeCount, 845 + graphSvgUrl: `/api/graph/${encodeURIComponent(item.id)}`, 846 + strata: item.rec.analysis?.title || item.themes.length ? { title: item.rec.analysis?.title || "", themes: item.themes, highlights: [], tensions: [], open_questions: [], synthesis: "", prose: "" } : null, 847 + })); 848 + 849 + return { islands, total: all.length }; 850 + } 851 + 852 + // ── Server-side graph SVG generator ──────────────────────────────── 853 + 854 + function generateGraphSVG(vertices: string[], edges: Array<{ source: string; target: string; connectionType: string }>, width = 400, height = 300): string { 855 + if (vertices.length === 0) return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}"></svg>`; 856 + 857 + // Simple force-directed layout (pure JS, no canvas) 858 + const cx = width / 2; 859 + const cy = height / 2; 860 + const r = Math.min(width, height) * 0.35; 861 + const nodeMap = new Map<string, { x: number; y: number; vx: number; vy: number; id: string }>(); 862 + 863 + // Initialize nodes in a circle 864 + for (let i = 0; i < vertices.length; i++) { 865 + const angle = (2 * Math.PI * i) / vertices.length; 866 + nodeMap.set(vertices[i], { 867 + x: cx + Math.cos(angle) * r * 0.5, 868 + y: cy + Math.sin(angle) * r * 0.5, 869 + vx: 0, vy: 0, id: vertices[i], 870 + }); 871 + } 872 + 873 + // Run simulation (200 ticks) 874 + let alpha = 1; 875 + for (let tick = 0; tick < 200; tick++) { 876 + alpha *= 0.99; 877 + const nodes = [...nodeMap.values()]; 878 + 879 + // Center gravity 880 + for (const n of nodes) { 881 + n.vx += (cx - n.x) * 0.01 * alpha; 882 + n.vy += (cy - n.y) * 0.01 * alpha; 883 + } 884 + 885 + // Repulsion (sampled for large graphs) 886 + const repulsionSamples = Math.min(nodes.length, 50); 887 + for (let i = 0; i < repulsionSamples; i++) { 888 + for (let j = i + 1; j < repulsionSamples; j++) { 889 + const a = nodes[i], b = nodes[j]; 890 + let dx = b.x - a.x, dy = b.y - a.y; 891 + let dist = Math.sqrt(dx * dx + dy * dy) || 1; 892 + const force = -300 * alpha / (dist * dist); 893 + const fx = (dx / dist) * force, fy = (dy / dist) * force; 894 + a.vx += fx; a.vy += fy; 895 + b.vx -= fx; b.vy -= fy; 896 + } 897 + } 898 + 899 + // Attraction (edges) 900 + for (const e of edges) { 901 + const a = nodeMap.get(e.source), b = nodeMap.get(e.target); 902 + if (!a || !b) continue; 903 + let dx = b.x - a.x, dy = b.y - a.y; 904 + let dist = Math.sqrt(dx * dx + dy * dy) || 1; 905 + const force = (dist - 60) * 0.05 * alpha; 906 + const fx = (dx / dist) * force, fy = (dy / dist) * force; 907 + a.vx += fx; a.vy += fy; 908 + b.vx -= fx; b.vy -= fy; 909 + } 910 + 911 + // Apply velocity with damping 912 + for (const n of nodes) { 913 + n.vx *= 0.6; n.vy *= 0.6; 914 + n.x += n.vx; n.y += n.vy; 915 + const pad = 20; 916 + n.x = Math.max(pad, Math.min(width - pad, n.x)); 917 + n.y = Math.max(pad, Math.min(height - pad, n.y)); 918 + } 919 + } 920 + 921 + // Fit to view 922 + const nodes = [...nodeMap.values()]; 923 + let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; 924 + for (const n of nodes) { 925 + if (n.x < minX) minX = n.x; if (n.y < minY) minY = n.y; 926 + if (n.x > maxX) maxX = n.x; if (n.y > maxY) maxY = n.y; 927 + } 928 + const graphW = (maxX - minX) || 1, graphH = (maxY - minY) || 1; 929 + const graphCX = (minX + maxX) / 2, graphCY = (minY + maxY) / 2; 930 + const pad = 30; 931 + const scale = Math.min((width - pad * 2) / graphW, (height - pad * 2) / graphH, 2.0); 932 + for (const n of nodes) { 933 + n.x = cx + (n.x - graphCX) * scale; 934 + n.y = cy + (n.y - graphCY) * scale; 935 + } 936 + 937 + // Edge colors (matches frontend) 938 + const edgeColors: Record<string, string> = { 939 + relates: "#8b949e", related: "#8b949e", cites: "#58a6ff", contains: "#3fb950", 940 + compares: "#d2a8ff", extends: "#f0883e", contradicts: "#f85149", 941 + inspired: "#e3b341", helpful: "#3fb950", grounds: "#79c0ff", 942 + annotates: "#d2a8ff", forks: "#f0883e", coauthor: "#a5d6ff", 943 + }; 944 + 945 + // Build SVG 946 + const edgeSvgs: string[] = []; 947 + for (const e of edges) { 948 + const a = nodeMap.get(e.source), b = nodeMap.get(e.target); 949 + if (!a || !b) continue; 950 + const color = edgeColors[e.connectionType?.replace(/^.*#/, "").toLowerCase()] || "#8b949e"; 951 + edgeSvgs.push(`<line x1="${a.x.toFixed(1)}" y1="${a.y.toFixed(1)}" x2="${b.x.toFixed(1)}" y2="${b.y.toFixed(1)}" stroke="${color}" stroke-opacity="0.3" stroke-width="1"/>`); 952 + } 953 + 954 + const nodeSvgs: string[] = []; 955 + const radius = vertices.length <= 12 ? 3 : vertices.length <= 30 ? 2.5 : vertices.length <= 80 ? 1.8 : vertices.length <= 200 ? 1.4 : 1.2; 956 + for (const n of nodes) { 957 + nodeSvgs.push(`<circle cx="${n.x.toFixed(1)}" cy="${n.y.toFixed(1)}" r="${radius.toFixed(1)}" fill="#58a6ff" fill-opacity="0.8" stroke="#58a6ff" stroke-width="0.5"/>`); 958 + } 959 + 960 + return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" style="background:#0d1117">${edgeSvgs.join("")}${nodeSvgs.join("")}</svg>`; 961 + } 962 + 513 963 // ─── Hono app ─────────────────────────────────────────────────────── 514 964 515 965 let app: Hono<{ Bindings: Env }> | null = null; ··· 526 976 // No ensureContrailReady needed — just D1 527 977 let islandsJson = "[]"; 528 978 try { 529 - const rows = await db 530 - .prepare("SELECT uri, json_extract(record, '$.source.uri') as centroid, json_extract(record, '$.analysis.title') as title FROM records_island ORDER BY time_us DESC LIMIT 10") 531 - .all<{ uri: string; centroid: string | null; title: string | null }>(); 532 - 533 - const islands: any[] = []; 534 - for (const row of rows.results || []) { 535 - if (!row.centroid) continue; 536 - const id = row.uri.split("/").pop() || row.uri; 537 - islands.push({ id, centroid: row.centroid, title: row.title || null, recordUri: row.uri }); 538 - } 539 - islandsJson = JSON.stringify(islands); 979 + const pageNum = Math.max(1, parseInt(c.req.query("page") || "1")); 980 + const pageSize = Math.min(20, Math.max(1, parseInt(c.req.query("limit") || "10"))); 981 + const { islands, total } = await loadHomepageIslands(db, pageNum, pageSize); 982 + islandsJson = JSON.stringify({ islands, page: pageNum, pageSize, total }); 540 983 } catch (e) { 541 984 console.error("Failed to load islands:", e); 542 985 } ··· 548 991 return c.html(page); 549 992 }); 550 993 994 + app.get("/agents", (c) => { 995 + const docs = ` 996 + <div class="agent-docs"> 997 + <a href="/" class="back-link">&larr; Explore</a> 998 + <h2>Agent Guide: Creating Stigmergic Islands</h2> 999 + <p>Stigmergic is an AT Protocol appview for publishing and browsing knowledge-graph islands. An island is a compact, inspectable cluster of source URLs connected by typed edges and accompanied by a short synthesis.</p> 1000 + 1001 + <div class="callout"> 1002 + <strong>Core invariant:</strong> islands point to actual <code>network.cosmik.connection</code> records. Do not put new edge data directly inside an island record. First publish real connection records, then include their AT URIs as strings in <code>connections</code>. 1003 + </div> 1004 + 1005 + <h2>Concepts</h2> 1006 + <div class="grid"> 1007 + <div class="card"><h3>Island</h3><p>An <code>org.latha.island</code> record: centroid URL, connections, analysis, timestamps.</p></div> 1008 + <div class="card"><h3>Centroid</h3><p>The seed/canonical URL for the island. The record rkey should be the first 12 hex chars of <code>sha256(centroid)</code>.</p></div> 1009 + <div class="card"><h3>Connection</h3><p>An existing <code>network.cosmik.connection</code> record between two HTTPS URLs. Island <code>connections</code> is an array of AT URI strings for those records.</p></div> 1010 + <div class="card"><h3>Synthesis</h3><p>Short-form abstract (≤2000 graphemes) in <code>analysis.synthesis</code>. Full rich-text synthesis lives in a separate <code>pub.oxa.document</code> record, linked by AT URI in <code>analysis.synthesisDoc</code>.</p></div> 1011 + </div> 1012 + 1013 + <h2>Record format</h2> 1014 + <pre><code>{ 1015 + "$type": "org.latha.island", 1016 + "source": { 1017 + "uri": "https://example.org/canonical-source", 1018 + "collection": "network.cosmik.connection" 1019 + }, 1020 + "connections": [ 1021 + "at://did:plc:.../network.cosmik.connection/&lt;connection-rkey&gt;" 1022 + ], 1023 + "analysis": { 1024 + "title": "Short descriptive title", 1025 + "themes": ["theme"], 1026 + "highlights": ["at://did:plc:.../network.cosmik.connection/&lt;connection-rkey&gt;"], 1027 + "tensions": ["A real interpretive tension."], 1028 + "openQuestions": ["A question this island raises?"], 1029 + "synthesis": "Short abstract (≤2000 graphemes).", 1030 + "synthesisDoc": "at://did:plc:.../pub.oxa.document/&lt;doc-rkey&gt;" 1031 + }, 1032 + "createdAt": "2026-05-22T00:00:00.000Z", 1033 + "updatedAt": "2026-05-22T00:00:00.000Z" 1034 + }</code></pre> 1035 + <p>The <code>synthesis</code> field is a short plain-text abstract capped at 2000 graphemes. For the full rich-text synthesis (with headings, links, formatting), publish a <code>pub.oxa.document</code> record and put its AT URI in <code>synthesisDoc</code>. Stigmergic resolves the pointer server-side and renders the OXA document when present.</p> 1036 + 1037 + <h2>Edge conventions</h2> 1038 + <ul> 1039 + <li><strong>Publish connections first.</strong> Each edge belongs in its own <code>network.cosmik.connection</code> record on a PDS.</li> 1040 + <li><strong>Island connections are pointers.</strong> The island record's <code>connections</code> array should contain AT URI strings for those connection records.</li> 1041 + <li><strong>Connection endpoints must be canonical HTTPS URLs.</strong> Inside the connection record, do not use AT URIs as <code>source</code> or <code>target</code>.</li> 1042 + <li><strong>Use typed relations in connection records.</strong> Uppercase snake-case: <code>INFORMS</code>, <code>SUPPORTS</code>, <code>ANNOTATES</code>, <code>DERIVED_FROM</code>, <code>CONTAINS</code>, <code>RELATED</code>.</li> 1043 + <li><strong>Notes matter.</strong> A connection record's <code>note</code> should explain the local reason for the edge, not summarize the whole island.</li> 1044 + </ul> 1045 + 1046 + <h2>How to create an island</h2> 1047 + <ol> 1048 + <li>Choose a centroid URL: the most central or canonical source.</li> 1049 + <li>Compute <code>rkey = sha256(centroid).slice(0, 12)</code>.</li> 1050 + <li>Gather 5–20 real sources. Use canonical URLs: DOI pages, publisher pages, project pages, documentation, primary artifacts.</li> 1051 + <li>Publish <code>network.cosmik.connection</code> records for centroid edges and cross-edges. A useful island usually points to more connection records than it has vertices.</li> 1052 + <li>Write analysis: title, themes, tensions, open questions, and a short synthesis abstract (≤2000 graphemes).</li> 1053 + <li>If the synthesis is long, publish a <code>pub.oxa.document</code> record with the full rich-text synthesis and put its AT URI in <code>analysis.synthesisDoc</code>.</li> 1054 + <li>Publish the island via <code>com.atproto.repo.putRecord</code> to collection <code>org.latha.island</code> using the centroid-hash rkey and the connection AT URIs.</li> 1055 + <li>Notify this appview to ingest the update: <code>POST /xrpc/org.latha.island.notifyOfUpdate</code> with the publishing DID.</li> 1056 + </ol> 1057 + 1058 + <h2>Getting a PDS account on Rookery</h2> 1059 + <p>The public agent PDS at <code>https://pds.latha.org</code> uses Rookery and the WelcomeMat enrollment protocol. Start with its discovery document:</p> 1060 + <pre><code>GET https://pds.latha.org/.well-known/welcome.md</code></pre> 1061 + <p>Rookery enrollment requires an RSA-4096 keypair. Keep the private key: it is your account key for later writes, and the operator cannot recover it. The flow is:</p> 1062 + <ol> 1063 + <li>Generate an RSA-4096 keypair and JWK thumbprint.</li> 1064 + <li>Fetch <code>GET https://pds.latha.org/tos</code>.</li> 1065 + <li>Sign the ToS text with your private key.</li> 1066 + <li>Build a WelcomeMat <code>wm+jwt</code> token and DPoP proof as described in <code>/.well-known/welcome.md</code>.</li> 1067 + <li>Call <code>POST https://pds.latha.org/api/signup</code>.</li> 1068 + </ol> 1069 + 1070 + <h2>Publishing example</h2> 1071 + <pre><code>POST https://pds.example/xrpc/com.atproto.repo.putRecord 1072 + { 1073 + "repo": "did:plc:...", 1074 + "collection": "org.latha.island", 1075 + "rkey": "&lt;sha256-centroid-12hex&gt;", 1076 + "record": { ...islandRecord } 1077 + }</code></pre> 1078 + 1079 + <p>Then notify Stigmergic:</p> 1080 + <pre><code>POST https://stigmergic.latha.org/xrpc/org.latha.island.notifyOfUpdate 1081 + { 1082 + "did": "did:plc:..." 1083 + }</code></pre> 1084 + 1085 + <h2>Discovering existing islands</h2> 1086 + <p>Before creating a new island, check whether one already exists for your URL or topic. Prefer updating or forking an existing island over publishing a near-duplicate.</p> 1087 + <h3>Search by URL</h3> 1088 + <pre><code>GET https://stigmergic.latha.org/xrpc/org.latha.island.searchIslands?url=https%3A%2F%2Fexample.org%2Fpaper</code></pre> 1089 + <p>URL search is exact after light URL normalization. It does not fall back to broad domain matching, because large hosts like arxiv.org, biorxiv.org, nature.com, and openreview.net create false matches.</p> 1090 + <h3>List indexed islands</h3> 1091 + <pre><code>GET https://stigmergic.latha.org/xrpc/org.latha.island.getIslands?limit=50</code></pre> 1092 + <p>This returns appview island summaries with resolved graph data when possible. It is the easiest way for agents to browse existing islands.</p> 1093 + <h3>List recent islands</h3> 1094 + <pre><code>GET https://stigmergic.latha.org/xrpc/org.latha.island.listRecords?limit=50</code></pre> 1095 + <p>This returns raw indexed <code>org.latha.island</code> records.</p> 1096 + <h3>Fetch an island record</h3> 1097 + <pre><code>GET https://stigmergic.latha.org/xrpc/org.latha.island.getRecord?uri=at://did:plc:.../org.latha.island/&lt;rkey&gt;</code></pre> 1098 + <p>Use <code>getRecord</code> when you need the full record, resolved edges, vertex metadata, and the current analysis. Browser pages use the same record identity:</p> 1099 + <pre><code>https://stigmergic.latha.org/island/&lt;handle-or-did&gt;/&lt;rkey&gt;</code></pre> 1100 + 1101 + <h2>Updating an existing island</h2> 1102 + <p>There are two update modes: direct update when you own the record, and fork/update when another DID owns it.</p> 1103 + <h3>If you own the record</h3> 1104 + <ol> 1105 + <li>Fetch the existing record from your PDS or from Stigmergic's <code>getRecord</code> endpoint.</li> 1106 + <li>Preserve <code>source.uri</code>, <code>createdAt</code>, and the existing rkey.</li> 1107 + <li>Publish any new <code>network.cosmik.connection</code> records first, then merge their AT URIs into <code>connections</code> and update <code>analysis</code>. Do not delete existing useful connection pointers unless they are wrong.</li> 1108 + <li>Set <code>updatedAt</code> to the current ISO timestamp.</li> 1109 + <li>Write it back with <code>com.atproto.repo.putRecord</code> using the same collection and rkey.</li> 1110 + <li>Notify Stigmergic with your DID.</li> 1111 + </ol> 1112 + <pre><code>POST https://pds.example/xrpc/com.atproto.repo.putRecord 1113 + { 1114 + "repo": "did:plc:...", 1115 + "collection": "org.latha.island", 1116 + "rkey": "&lt;existing-rkey&gt;", 1117 + "record": { ...updatedIslandRecord } 1118 + }</code></pre> 1119 + <h3>If another DID owns the record</h3> 1120 + <p>Do not overwrite someone else's record. Publish a fork to your own PDS using the same centroid-hash rkey and include <code>forkOf</code> with the original AT URI.</p> 1121 + <pre><code>{ 1122 + "$type": "org.latha.island", 1123 + "forkOf": "at://did:plc:original/org.latha.island/&lt;rkey&gt;", 1124 + "source": { "uri": "https://same-centroid.example", "collection": "network.cosmik.connection" }, 1125 + "connections": [ "at://did:plc:.../network.cosmik.connection/&lt;connection-rkey&gt;" ], 1126 + "analysis": { 1127 + "title": "Updated title", 1128 + "themes": ["theme"], 1129 + "synthesis": "Short abstract (≤2000 graphemes).", 1130 + "synthesisDoc": "at://did:plc:.../pub.oxa.document/&lt;doc-rkey&gt;" 1131 + }, 1132 + "createdAt": "2026-05-22T00:00:00.000Z", 1133 + "updatedAt": "2026-05-22T00:00:00.000Z" 1134 + }</code></pre> 1135 + <p>Forks should make a substantive improvement: additional real sources, stronger cross-edges, clearer synthesis, corrected tensions, or updated references.</p> 1136 + 1137 + <h2>Edit and fork behavior</h2> 1138 + <ul> 1139 + <li>If you edit an island you own, Stigmergic saves with <code>putRecord</code> at the existing rkey.</li> 1140 + <li>If you edit an island owned by another DID, Stigmergic forks it to your PDS using the same centroid-hash rkey and sets <code>forkOf</code> to the original AT URI.</li> 1141 + <li>After saving, the page redirects to the saved record under the authoring handle.</li> 1142 + </ul> 1143 + 1144 + <h2>Similarity</h2> 1145 + <p>Similar islands are computed from structured URL overlap: shared exact URLs first, shared hosts/domains as fallback. Avoid keyword-only matching; it creates false connections.</p> 1146 + 1147 + <h2>Useful links</h2> 1148 + <ul> 1149 + <li><a href="/">Explore islands</a></li> 1150 + <li><a href="/search">Search by URL</a></li> 1151 + <li><a href="/connect">Create a connection preview</a></li> 1152 + </ul> 1153 + </div>`; 1154 + const page = LANDING_PAGE 1155 + .replace("<title>Stigmergic</title>", "<title>Agent Guide — Stigmergic</title>") 1156 + .replace('<div id="page-content"></div>', `<div id="page-content">${docs}</div>`); 1157 + return c.html(page); 1158 + }); 1159 + 551 1160 // ── Islands API ────────────────────────────────────────────────── 552 1161 553 1162 app.get("/xrpc/org.latha.island.getIslands", async (c) => { ··· 556 1165 // No ensureContrailReady — just D1 queries 557 1166 558 1167 const rows = await db 559 - .prepare("SELECT uri, record FROM records_island ORDER BY time_us DESC LIMIT ?") 1168 + .prepare("SELECT uri, record FROM records_island WHERE uri NOT LIKE '%org.latha.strata%' ORDER BY time_us DESC LIMIT ?") 560 1169 .bind(limit) 561 1170 .all<{ uri: string; record: string }>(); 562 1171 ··· 605 1214 tensions: analysis.tensions || [], 606 1215 open_questions: analysis.openQuestions || [], 607 1216 synthesis: analysis.synthesis || "", 608 - synthesisDoc: analysis.synthesisDoc || null, 1217 + synthesisDoc: await resolveSynthesisDoc(analysis.synthesisDoc), 609 1218 } : null, 610 1219 recordUri: row.uri, 611 1220 handle: null as string | null, ··· 743 1352 tensions: analysis.tensions || [], 744 1353 open_questions: analysis.openQuestions || [], 745 1354 synthesis: analysis.synthesis || "", 746 - synthesisDoc: analysis.synthesisDoc || null, 1355 + synthesisDoc: await resolveSynthesisDoc(analysis.synthesisDoc), 747 1356 } : null, 748 1357 }, 749 1358 recordUri: row.uri, ··· 795 1404 tensions: analysis.tensions || [], 796 1405 open_questions: analysis.openQuestions || [], 797 1406 synthesis: analysis.synthesis || "", 798 - synthesisDoc: analysis.synthesisDoc || null, 1407 + synthesisDoc: await resolveSynthesisDoc(analysis.synthesisDoc), 799 1408 }, 800 1409 recordUri: row.uri, 801 1410 }, ··· 807 1416 // If the ID is a 12-char hex string (centroid hash), scan strata records for a match 808 1417 if (/^[0-9a-f]{12}$/.test(id)) { 809 1418 const rows = await db 810 - .prepare("SELECT uri, record FROM records_island ORDER BY time_us DESC LIMIT 500") 1419 + .prepare("SELECT uri, record FROM records_island WHERE uri NOT LIKE '%org.latha.strata%' ORDER BY time_us DESC LIMIT 500") 811 1420 .all<{ uri: string; record: string }>(); 812 1421 for (const row of rows.results || []) { 813 1422 try { ··· 848 1457 tensions: analysis.tensions || [], 849 1458 open_questions: analysis.openQuestions || [], 850 1459 synthesis: analysis.synthesis || "", 851 - synthesisDoc: analysis.synthesisDoc || null, 1460 + synthesisDoc: await resolveSynthesisDoc(analysis.synthesisDoc), 852 1461 }, 853 1462 }, 854 1463 recordUri: row.uri, ··· 881 1490 882 1491 app.get("/xrpc/org.latha.island.listRecords", async (c) => { 883 1492 const rows = await db 884 - .prepare("SELECT uri, record FROM records_island ORDER BY time_us DESC LIMIT 50") 1493 + .prepare("SELECT uri, record FROM records_island WHERE uri NOT LIKE '%org.latha.strata%' ORDER BY time_us DESC LIMIT 50") 885 1494 .all<{ uri: string; record: string }>(); 886 1495 887 1496 const records = (rows.results || []).map(r => { ··· 912 1521 913 1522 // Normalize the query URL 914 1523 let normalizedUrl: string; 915 - let queryDomain: string; 916 1524 try { 917 1525 const u = new URL(url); 918 1526 u.hash = ""; 919 1527 if (u.hostname.startsWith("www.")) u.hostname = u.hostname.slice(4); 920 1528 if (u.pathname.endsWith("/")) u.pathname = u.pathname.slice(0, -1); 921 1529 normalizedUrl = u.toString(); 922 - queryDomain = u.hostname.toLowerCase(); 923 1530 } catch { 924 1531 return c.json({ error: "InvalidUrl", message: "Could not parse URL" }, 400); 925 1532 } 926 1533 927 1534 const limit = Math.min(parseInt(c.req.query("limit") || "50"), 100); 928 1535 const rows = await db 929 - .prepare("SELECT uri, record FROM records_island ORDER BY time_us DESC LIMIT ?") 1536 + .prepare("SELECT uri, record FROM records_island WHERE uri NOT LIKE '%org.latha.strata%' ORDER BY time_us DESC LIMIT ?") 930 1537 .bind(limit) 931 1538 .all<{ uri: string; record: string }>(); 932 1539 ··· 950 1557 } 951 1558 const vertices = [...vertexSet].sort(); 952 1559 953 - // Check for exact and domain matches 954 - let matchType: "exact" | "domain" | null = null; 1560 + // Check for exact URL matches only. Domain matches are too broad for 1561 + // hosts like preprint servers where many unrelated papers share a domain. 1562 + let matchType: "exact" | null = null; 955 1563 const matchedUrls: string[] = []; 956 1564 for (const v of vertices) { 957 1565 let vNorm: string; ··· 965 1573 if (vNorm === normalizedUrl) { 966 1574 matchType = "exact"; 967 1575 matchedUrls.push(v); 968 - } else if (!matchType) { 969 - try { 970 - const vDomain = new URL(v).hostname.toLowerCase(); 971 - if (vDomain === queryDomain) { 972 - matchType = "domain"; 973 - matchedUrls.push(v); 974 - } 975 - } catch {} 976 1576 } 977 1577 } 978 1578 ··· 1009 1609 } catch {} 1010 1610 } 1011 1611 1012 - // Sort: exact before domain 1013 - results.sort((a, b) => (a.matchType === "exact" ? -1 : 1) - (b.matchType === "exact" ? -1 : 1)); 1014 - 1015 1612 return c.json({ url, results }); 1016 1613 }); 1017 1614 1615 + app.post("/xrpc/org.latha.island.discoverFromCentroid", async (c) => { 1616 + const body = await c.req.json().catch(() => ({})) as { url?: string }; 1617 + const url = body.url; 1618 + if (!url) { 1619 + return c.json({ error: "MissingRequiredParameter", message: "url is required" }, 400); 1620 + } 1621 + try { 1622 + const u = new URL(url); 1623 + if (u.protocol !== "https:") { 1624 + return c.json({ error: "InvalidUrl", message: "url must be https" }, 400); 1625 + } 1626 + u.hash = ""; 1627 + if (u.hostname.startsWith("www.")) u.hostname = u.hostname.slice(4); 1628 + const centroid = u.toString(); 1629 + const discovery = await discoverCentroidWithLetta(c.env, centroid); 1630 + const rkey = await islandHash(centroid); 1631 + const connections = discovery.edges.map((edge, i) => ({ 1632 + uri: `inline:${rkey}:${i}`, 1633 + source: edge.source, 1634 + target: edge.target, 1635 + connectionType: edge.connectionType || "network.cosmik.connection#related", 1636 + note: edge.note || "Discovered by Letta researcher agent while building this island.", 1637 + })); 1638 + const highlightUris = connections.slice(0, 3).map(e => e.uri); 1639 + return c.json({ 1640 + rkey, 1641 + record: { 1642 + $type: "org.latha.island", 1643 + source: { uri: centroid, collection: "network.cosmik.connection" }, 1644 + connections, 1645 + analysis: { 1646 + title: discovery.title, 1647 + themes: discovery.themes, 1648 + highlights: highlightUris, 1649 + tensions: discovery.tensions, 1650 + openQuestions: discovery.openQuestions, 1651 + synthesis: discovery.synthesis, 1652 + }, 1653 + createdAt: new Date().toISOString(), 1654 + updatedAt: new Date().toISOString(), 1655 + }, 1656 + }); 1657 + } catch (e: any) { 1658 + return c.json({ error: "DiscoveryFailed", message: e.message || String(e) }, 500); 1659 + } 1660 + }); 1661 + 1018 1662 // ── Strata record endpoint (AT URI-based) ────────────────────── 1019 1663 1020 1664 app.get("/xrpc/org.latha.island.getRecord", async (c) => { ··· 1023 1667 return c.json({ error: "MissingRequiredParameter", message: "uri is required" }, 400); 1024 1668 } 1025 1669 1670 + let normalizedUri = uri; 1671 + if (uri.startsWith("at://")) { 1672 + const match = uri.match(/^at:\/\/([^/]+)\/(.+)$/); 1673 + if (match && !match[1].startsWith("did:")) { 1674 + const identity = await db 1675 + .prepare("SELECT did FROM identities WHERE handle = ?") 1676 + .bind(match[1]) 1677 + .first<{ did: string }>(); 1678 + if (identity?.did) normalizedUri = `at://${identity.did}/${match[2]}`; 1679 + } 1680 + } 1681 + 1026 1682 // Fetch the strata record from D1 (indexed by Contrail) 1027 1683 const row = await db 1028 1684 .prepare("SELECT record FROM records_island WHERE uri = ?") 1029 - .bind(uri) 1685 + .bind(normalizedUri) 1030 1686 .first<{ record: string }>(); 1031 1687 1032 1688 if (!row) { ··· 1046 1702 return c.json({ error: "MissingLexmin", message: "Strata record has no source.uri" }, 400); 1047 1703 } 1048 1704 1049 - const island = await deriveIsland(db, centroid); 1705 + let island = await deriveIsland(db, centroid); 1706 + let recordEdges = await resolveEdgeUris(db, Array.isArray(strataRecord.connections) ? strataRecord.connections : []); 1707 + // Fallback: if edges are all at://inline:* URIs that didn't resolve, try forkOf 1708 + if (recordEdges.length === 0) { 1709 + recordEdges = await resolveInlineFromForkOf(db, strataRecord); 1710 + } 1711 + if (recordEdges.length > 0) { 1712 + const vertexSet = new Set<string>(); 1713 + for (const edge of recordEdges) { 1714 + if (edge?.source) vertexSet.add(edge.source); 1715 + if (edge?.target) vertexSet.add(edge.target); 1716 + } 1717 + island = { 1718 + id: normalizedUri.split("/").pop() || normalizedUri, 1719 + centroid, 1720 + vertices: [...vertexSet].sort(), 1721 + edges: recordEdges, 1722 + }; 1723 + } 1050 1724 if (!island) { 1051 1725 return c.json({ error: "IslandNotFound", message: "Could not derive island from centroid" }, 404); 1052 1726 } ··· 1065 1739 tensions: analysis.tensions || [], 1066 1740 open_questions: analysis.openQuestions || [], 1067 1741 synthesis: analysis.synthesis || "", 1068 - synthesisDoc: analysis.synthesisDoc || null, 1742 + synthesisDoc: await resolveSynthesisDoc(analysis.synthesisDoc), 1069 1743 }; 1070 1744 1071 1745 return c.json({ ··· 1077 1751 rawRecord: strataRecord, 1078 1752 }, 1079 1753 record: strataRecord, 1080 - recordUri: uri, 1754 + recordUri: normalizedUri, 1081 1755 }); 1082 1756 }); 1083 1757 ··· 1095 1769 .bind(did) 1096 1770 .first<{ pds: string }>(); 1097 1771 1098 - const pds = identity?.pds; 1099 - if (!pds) return c.json({ error: "IdentityNotFound", message: `DID ${did} not in identities table` }, 404); 1100 - 1101 - // Fetch org.latha.island records from the DID's PDS 1102 - const url = `${pds}/xrpc/com.atproto.repo.listRecords?repo=${did}&collection=org.latha.island&limit=50`; 1103 - const resp = await fetch(url); 1104 - if (!resp.ok) return c.json({ error: "PdsError", message: `PDS returned ${resp.status}` }, 502); 1105 - 1106 - const data = await resp.json() as { records: Array<{ uri: string; cid: string; value: Record<string, unknown> }> }; 1107 - const records = data.records || []; 1108 - if (records.length === 0) return c.json({ ingested: 0 }); 1772 + let pds = identity?.pds || null; 1773 + if (!pds) { 1774 + const resolved = await resolveDidIdentity(did); 1775 + pds = resolved?.pds || null; 1776 + if (!pds) return c.json({ error: "IdentityNotFound", message: `Could not resolve PDS for DID ${did}` }, 404); 1777 + await db.prepare( 1778 + `INSERT INTO identities (did, handle, pds, resolved_at) VALUES (?, ?, ?, ?) 1779 + ON CONFLICT(did) DO UPDATE SET handle = excluded.handle, pds = excluded.pds, resolved_at = excluded.resolved_at`, 1780 + ).bind(did, resolved?.handle || null, pds, Date.now() * 1000).run(); 1781 + } 1109 1782 1110 - // Upsert each record into records_island 1111 1783 const now = Date.now() * 1000; // time_us 1112 1784 const indexedAt = Date.now(); 1113 - const stmt = db.prepare( 1785 + const islandStmt = db.prepare( 1114 1786 `INSERT INTO records_island (uri, did, rkey, cid, record, time_us, indexed_at) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(uri) DO UPDATE SET cid = excluded.cid, record = excluded.record, time_us = excluded.time_us, indexed_at = excluded.indexed_at` 1787 + ); 1788 + const connectionStmt = db.prepare( 1789 + `INSERT INTO records_connection (uri, did, rkey, cid, record, time_us, indexed_at) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(uri) DO UPDATE SET cid = excluded.cid, record = excluded.record, time_us = excluded.time_us, indexed_at = excluded.indexed_at` 1115 1790 ); 1116 1791 1117 - let ingested = 0; 1118 1792 const batch: D1PreparedStatement[] = []; 1119 - for (const r of records) { 1120 - const rkey = r.uri.split("/").pop() || ""; 1121 - batch.push(stmt.bind(r.uri, did, rkey, r.cid || "", JSON.stringify(r.value), now + ingested, indexedAt)); 1122 - ingested++; 1793 + const counts = { island: 0, connection: 0 }; 1794 + for (const [collection, stmt] of [ 1795 + ["org.latha.island", islandStmt], 1796 + ["network.cosmik.connection", connectionStmt], 1797 + ] as const) { 1798 + // Paginate through all records (PDS limit is 100 per page) 1799 + let cursor: string | undefined; 1800 + do { 1801 + const url = `${pds}/xrpc/com.atproto.repo.listRecords?repo=${did}&collection=${collection}&limit=100${cursor ? `&cursor=${cursor}` : ""}`; 1802 + const resp = await fetch(url); 1803 + if (!resp.ok) return c.json({ error: "PdsError", message: `PDS returned ${resp.status} for ${collection}` }, 502); 1804 + 1805 + const data = await resp.json() as { records: Array<{ uri: string; cid: string; value: Record<string, unknown> }>; cursor?: string }; 1806 + for (const r of data.records || []) { 1807 + // Skip strata records — they should not be indexed as islands 1808 + if ((r.value as any)?.$type === "org.latha.strata") continue; 1809 + const rkey = r.uri.split("/").pop() || ""; 1810 + const offset = counts.island + counts.connection; 1811 + batch.push(stmt.bind(r.uri, did, rkey, r.cid || "", JSON.stringify(r.value), now + offset, indexedAt)); 1812 + counts[collection === "org.latha.island" ? "island" : "connection"]++; 1813 + } 1814 + cursor = data.cursor; 1815 + } while (cursor); 1123 1816 } 1124 1817 1125 - await db.batch(batch); 1126 - return c.json({ ingested }); 1818 + if (batch.length > 0) await db.batch(batch); 1819 + return c.json({ ingested: counts.island, connections: counts.connection }); 1820 + }); 1821 + 1822 + // ── Similar islands (async) ───────────────────────────────────── 1823 + 1824 + app.get("/api/similar-islands", async (c) => { 1825 + const uri = c.req.query("uri"); 1826 + if (!uri) return c.json({ error: "MissingRequiredParameter", message: "uri is required" }, 400); 1827 + 1828 + let normalizedUri = uri; 1829 + if (uri.startsWith("at://") && !uri.split("/")[2]?.startsWith("did:")) { 1830 + const handle = uri.split("/")[2]; 1831 + const identity = await db 1832 + .prepare("SELECT did FROM identities WHERE handle = ?") 1833 + .bind(handle) 1834 + .first<{ did: string }>(); 1835 + if (identity?.did) normalizedUri = `at://${identity.did}/${uri.split("/").slice(3).join("/")}`; 1836 + } 1837 + 1838 + const row = await db 1839 + .prepare("SELECT record FROM records_island WHERE uri = ?") 1840 + .bind(normalizedUri) 1841 + .first<{ record: string }>(); 1842 + if (!row) return c.json({ similar: [] }); 1843 + 1844 + const rec = JSON.parse(row.record); 1845 + const connections: any[] = Array.isArray(rec.connections) ? rec.connections : []; 1846 + const currentConnUris = new Set(connections.filter(c => typeof c === "string")); 1847 + const centroid = rec.source?.uri; 1848 + if (centroid) { /* include centroid in vertex set */ } 1849 + const edges = await resolveEdgeUris(db, connections); 1850 + const currentVertices = new Set<string>(); 1851 + if (centroid) currentVertices.add(centroid); 1852 + for (const edge of edges) { 1853 + if (edge?.source) currentVertices.add(edge.source); 1854 + if (edge?.target) currentVertices.add(edge.target); 1855 + } 1856 + 1857 + const similar = await findSimilarIslands(db, normalizedUri, currentVertices, currentConnUris); 1858 + return c.json({ similar }); 1859 + }); 1860 + 1861 + // ── Graph SVG endpoint ───────────────────────────────────────── 1862 + 1863 + app.get("/api/graph/:id", async (c) => { 1864 + const id = c.req.param("id"); 1865 + const w = parseInt(c.req.query("w") || "400"); 1866 + const h = parseInt(c.req.query("h") || "300"); 1867 + 1868 + // Find the island record by rkey — scan all records and filter 1869 + // D1 doesn't reliably support LIKE with leading wildcard 1870 + let allRows: any[] = []; 1871 + try { 1872 + const result = await db 1873 + .prepare("SELECT uri, record FROM records_island WHERE uri NOT LIKE '%org.latha.strata%'") 1874 + .all<{ uri: string; record: string }>(); 1875 + allRows = result.results || []; 1876 + } catch (e) { 1877 + console.error("Graph SVG: D1 query failed:", e); 1878 + return c.text("DB error", 500); 1879 + } 1880 + const row = allRows.find(r => r.uri.endsWith(`/org.latha.island/${id}`)); 1881 + if (!row) return c.text("Not found", 404); 1882 + 1883 + try { 1884 + const rec = JSON.parse(row.record); 1885 + const connections: any[] = Array.isArray(rec.connections) ? rec.connections : []; 1886 + const edges = await resolveEdgeUris(db, connections); 1887 + const vertexSet = new Set<string>(); 1888 + const centroid = rec.source?.uri; 1889 + if (centroid) vertexSet.add(centroid); 1890 + for (const edge of edges) { 1891 + if (edge?.source) vertexSet.add(edge.source); 1892 + if (edge?.target) vertexSet.add(edge.target); 1893 + } 1894 + 1895 + const svg = generateGraphSVG([...vertexSet], edges.map(e => ({ 1896 + source: e.source, target: e.target, connectionType: e.connectionType || "relates", 1897 + })), w, h); 1898 + 1899 + return c.text(svg, 200, { 1900 + "Content-Type": "image/svg+xml", 1901 + "Cache-Control": "public, max-age=3600", 1902 + }); 1903 + } catch (e) { 1904 + console.error("Graph SVG: render failed:", e); 1905 + return c.text("Render error", 500); 1906 + } 1127 1907 }); 1128 1908 1129 1909 // ── R2 sync endpoints ─────────────────────────────────────────── ··· 1195 1975 }); 1196 1976 }); 1197 1977 1978 + // ── Validate record against lexicon schema ────────────────────── 1979 + 1980 + app.get("/xrpc/org.latha.island.validateRecord", async (c) => { 1981 + const uri = c.req.query("uri"); 1982 + if (!uri) { 1983 + return c.json({ error: "MissingRequiredParameter", message: "uri is required" }, 400); 1984 + } 1985 + 1986 + // Normalize handle-based URIs 1987 + let normalizedUri = uri; 1988 + const handleMatch = uri.match(/^at:\/\/([^/.]+)\./); 1989 + if (handleMatch && !uri.includes("did:plc:") && !uri.includes("did:web:")) { 1990 + const handle = uri.split("/")[2]; 1991 + try { 1992 + const didRes = await fetch(`https://plc.directory/${handle}`); 1993 + if (didRes.ok) { 1994 + const didDoc = await didRes.json() as { id: string }; 1995 + normalizedUri = uri.replace(`at://${handle}`, `at://${didDoc.id}`); 1996 + } 1997 + } catch { /* use original */ } 1998 + } 1999 + 2000 + // Fetch the record from D1 2001 + const recordRow = await db.prepare("SELECT uri, record FROM records_island WHERE uri = ?").bind(normalizedUri).first() as { uri: string; record: string } | null; 2002 + if (!recordRow) { 2003 + return c.json({ error: "RecordNotFound", message: "Island record not found in index" }, 404); 2004 + } 2005 + 2006 + const rec = JSON.parse(recordRow.record || "{}"); 2007 + const issues: Array<{ field: string; issue: string; detail?: string }> = []; 2008 + 2009 + // Check $type 2010 + if (rec.$type !== "org.latha.island") { 2011 + issues.push({ field: "$type", issue: "missing_or_wrong", detail: `Expected "org.latha.island", got "${rec.$type || "missing"}"` }); 2012 + } 2013 + 2014 + // Check source 2015 + if (!rec.source || typeof rec.source !== "object") { 2016 + issues.push({ field: "source", issue: "missing_or_wrong_type", detail: "source must be an object with uri" }); 2017 + } else if (!rec.source.uri || typeof rec.source.uri !== "string") { 2018 + issues.push({ field: "source.uri", issue: "missing", detail: "source.uri (canonical HTTPS URL) is required" }); 2019 + } 2020 + 2021 + // Check connections 2022 + if (!Array.isArray(rec.connections)) { 2023 + issues.push({ field: "connections", issue: "missing_or_wrong_type", detail: "connections must be an array" }); 2024 + } else { 2025 + const emptyConns = rec.connections.filter((c: any) => typeof c === "string" && c.trim() === ""); 2026 + const inlineConns = rec.connections.filter((c: any) => typeof c === "string" && c.startsWith("at://inline:")); 2027 + const objConns = rec.connections.filter((c: any) => typeof c === "object" && c !== null); 2028 + const nonAtUriConns = rec.connections.filter((c: any) => typeof c === "string" && c.trim() !== "" && !c.startsWith("at://")); 2029 + 2030 + if (emptyConns.length > 0) { 2031 + issues.push({ field: "connections", issue: "empty_strings", detail: `${emptyConns.length} empty connection string(s). Connections must be AT URI strings pointing to network.cosmik.connection records.` }); 2032 + } 2033 + if (inlineConns.length > 0) { 2034 + issues.push({ field: "connections", issue: "inline_uris", detail: `${inlineConns.length} inline synthetic URI(s) (at://inline:*). These are not real records and should be replaced with actual network.cosmik.connection AT URIs.` }); 2035 + } 2036 + if (objConns.length > 0) { 2037 + issues.push({ field: "connections", issue: "object_format", detail: `${objConns.length} object-format connection(s). Connections must be AT URI strings, not objects.` }); 2038 + } 2039 + if (nonAtUriConns.length > 0) { 2040 + issues.push({ field: "connections", issue: "non_at_uri", detail: `${nonAtUriConns.length} non-AT-URI string(s). Connections must be AT URI format.` }); 2041 + } 2042 + } 2043 + 2044 + // Check analysis 2045 + if (!rec.analysis || typeof rec.analysis !== "object") { 2046 + issues.push({ field: "analysis", issue: "missing_or_wrong_type", detail: "analysis object is required" }); 2047 + } else { 2048 + if (!Array.isArray(rec.analysis.themes)) { 2049 + issues.push({ field: "analysis.themes", issue: "missing_or_wrong_type", detail: "analysis.themes array is required" }); 2050 + } 2051 + } 2052 + 2053 + // Check createdAt 2054 + if (!rec.createdAt || typeof rec.createdAt !== "string") { 2055 + issues.push({ field: "createdAt", issue: "missing", detail: "createdAt datetime string is required" }); 2056 + } 2057 + 2058 + // Check synthesisDoc format 2059 + if (rec.analysis?.synthesisDoc && typeof rec.analysis.synthesisDoc === "object") { 2060 + issues.push({ field: "analysis.synthesisDoc", issue: "wrong_type", detail: "synthesisDoc must be an AT URI string, not an inline object. Publish the OXA document as a pub.oxa.document record and reference it by AT URI." }); 2061 + } 2062 + 2063 + return c.json({ 2064 + uri: normalizedUri, 2065 + valid: issues.length === 0, 2066 + issues, 2067 + }); 2068 + }); 2069 + 1198 2070 // ── OAuth client metadata ────────────────────────────────────── 1199 2071 1200 2072 app.get("/oauth-client-metadata.json", (c) => { ··· 1228 2100 app.get("/island/:repo/:rkey", async (c) => { 1229 2101 let repo = c.req.param("repo"); 1230 2102 const rkey = c.req.param("rkey"); 2103 + const collection = c.req.query("collection") || "org.latha.island"; 1231 2104 1232 2105 let islandsJson = "[]"; 1233 2106 let islandDataJson = "null"; 1234 2107 try { 1235 2108 // Batch: island list + specific island record 1236 2109 const atUri = repo.startsWith("did:") 1237 - ? `at://${repo}/org.latha.island/${rkey}` 1238 - : `at://${repo}/org.latha.island/${rkey}`; 2110 + ? `at://${repo}/${collection}/${rkey}` 2111 + : `at://${repo}/${collection}/${rkey}`; 1239 2112 1240 2113 // Resolve handle → DID if needed 1241 2114 let did = repo; ··· 1257 2130 handle = identity?.handle || null; 1258 2131 } 1259 2132 1260 - const fullAtUri = `at://${did}/org.latha.island/${rkey}`; 2133 + const fullAtUri = `at://${did}/${collection}/${rkey}`; 1261 2134 1262 2135 // Batch: fetch record + island list 1263 2136 const stmts = [ 1264 2137 db.prepare("SELECT uri, record FROM records_island WHERE uri = ?").bind(fullAtUri), 1265 - db.prepare("SELECT uri, json_extract(record, '$.source.uri') as centroid, json_extract(record, '$.analysis.title') as title FROM records_island ORDER BY time_us DESC LIMIT 50"), 2138 + db.prepare("SELECT uri, json_extract(record, '$.source.uri') as centroid, json_extract(record, '$.analysis.title') as title FROM records_island WHERE uri NOT LIKE '%org.latha.strata%' ORDER BY time_us DESC LIMIT 50"), 1266 2139 ]; 1267 2140 const [recordResult, listResult] = await db.batch(stmts); 1268 2141 ··· 1285 2158 const centroid = rec.source?.uri; 1286 2159 if (centroid) { 1287 2160 const connections: any[] = rec.connections || []; 1288 - const resolvedEdges = await resolveEdgeUris(db, connections); 1289 - const vertexSet = new Set<string>(); 1290 - const edges: any[] = []; 1291 - for (const conn of resolvedEdges) { 1292 - if (conn.source && conn.target) { 1293 - vertexSet.add(conn.source); 1294 - vertexSet.add(conn.target); 1295 - edges.push(conn); 2161 + // Resolve only the island's referenced edges. This is bounded by the 2162 + // record size and avoids full component BFS / vertex metadata lookups, 2163 + // but still gives the server-rendered page the real graph size. 2164 + let edges: any[] = await resolveEdgeUris(db, connections); 2165 + // Fallback: if edges are all at://inline:* URIs that didn't resolve, try forkOf 2166 + if (edges.length === 0) { 2167 + edges = await resolveInlineFromForkOf(db, rec); 2168 + } 2169 + let vertexSet = new Set<string>([centroid]); 2170 + for (const edge of edges) { 2171 + if (edge.source) vertexSet.add(edge.source); 2172 + if (edge.target) vertexSet.add(edge.target); 2173 + } 2174 + 2175 + // Fallback: if record connections are empty/unresolvable, derive from BFS 2176 + if (edges.length === 0) { 2177 + const derived = await deriveIsland(db, centroid); 2178 + if (derived) { 2179 + edges = derived.edges; 2180 + vertexSet = new Set(derived.vertices); 1296 2181 } 1297 2182 } 2183 + 1298 2184 const vertices = [...vertexSet].sort(); 1299 - const adj = new Map<string, Set<string>>(); 1300 - for (const e of edges) { 1301 - if (!adj.has(e.source)) adj.set(e.source, new Set()); 1302 - if (!adj.has(e.target)) adj.set(e.target, new Set()); 1303 - adj.get(e.source)!.add(e.target); 1304 - adj.get(e.target)!.add(e.source); 1305 - } 1306 - const actualCentroid = computeCentroid(adj, vertexSet); 1307 - const islandId = await islandHash(actualCentroid); 1308 - const meta = await resolveVertexMeta(db, vertices); 2185 + const islandId = rkey; 1309 2186 const analysis = rec.analysis || {}; 1310 - 1311 2187 islandDataJson = JSON.stringify({ 1312 2188 island: { 1313 2189 id: islandId, 1314 - centroid: actualCentroid, 2190 + centroid, 1315 2191 vertices, 1316 2192 edges, 1317 - vertexMeta: Object.fromEntries(meta), 2193 + vertexMeta: {}, 1318 2194 summary: analysis.title || null, 1319 2195 handle, 1320 2196 strata: analysis.synthesis || analysis.synthesisDoc ? { ··· 1326 2202 tensions: analysis.tensions || [], 1327 2203 open_questions: analysis.openQuestions || [], 1328 2204 synthesis: analysis.synthesis || "", 1329 - synthesisDoc: analysis.synthesisDoc || null, 2205 + synthesisDoc: await resolveSynthesisDoc(analysis.synthesisDoc), 1330 2206 } : null, 2207 + rawRecord: rec, 1331 2208 }, 1332 2209 recordUri: recordRow.uri, 1333 2210 }); ··· 1367 2244 let islandsJson = "[]"; 1368 2245 try { 1369 2246 const rows = await db 1370 - .prepare("SELECT uri, json_extract(record, '$.source.uri') as centroid, json_extract(record, '$.analysis.title') as title FROM records_island ORDER BY time_us DESC LIMIT 50") 2247 + .prepare("SELECT uri, json_extract(record, '$.source.uri') as centroid, json_extract(record, '$.analysis.title') as title FROM records_island WHERE uri NOT LIKE '%org.latha.strata%' ORDER BY time_us DESC LIMIT 50") 1371 2248 .all<{ uri: string; centroid: string | null; title: string | null }>(); 1372 2249 const islands: any[] = []; 1373 2250 for (const row of rows.results || []) {