your personal website on atproto - mirror blento.app
25
fork

Configure Feed

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

update

+903 -684
+23 -14
docs/Contributing.md
··· 1 1 # Contributing Guidelines 2 2 3 - For creating new cards see [here](CustomCards.md) (and check out [existing card ideas](CardIdeas.md)) 3 + For new cards see [CustomCards](CustomCards.md) and [CardIdeas](CardIdeas.md). 4 4 5 - ## Development 5 + ## Setup 6 6 7 - ``` 7 + ```sh 8 8 git clone https://github.com/flo-bit/blento.git 9 9 cd blento 10 - cp .env.example .env 11 10 pnpm install 12 - pnpm run dev 11 + pnpm env:setup-dev # creates .env, fills COOKIE_SECRET + CLIENT_ASSERTION_KEY 12 + ``` 13 + 14 + In `wrangler.jsonc`, flip the `DB` binding's `"remote": true` to `false` if not already set to false (don't commit that). Otherwise `pnpm dev` and `pnpm backfill` write to production and need cloudflare credentials. 15 + 16 + ```sh 17 + pnpm dev # site falls back to PDS when D1 is empty — no backfill needed 18 + pnpm backfill # populates local D1 via contrail; needed only for /xrpc/* paths and the UpdatedBlentos card 13 19 ``` 14 20 15 - ## AI assisted development 21 + `pnpm backfill` is resumable, takes a few minutes the first time. 16 22 17 - You can submit PRs written with AI assistance but please make sure: 23 + ## Before opening a PR 18 24 19 - - there's no extra unnecessary changes/unnecessary verbose code (keep it simple) 20 - - you test everything yourself 21 - - in light/dark mode 22 - - with and without colored cards 23 - - in edit mode and not in edit mode 24 - - on mobile and desktop (note that there's two different mobile "modes", one dependent on screen size and one enabled when pointer: coarse) 25 + - `pnpm check` — must complete with 0 errors and 0 warnings (existing baseline excepted). 26 + - `pnpm format` — runs eslint --fix + prettier --write across the project. 25 27 26 28 ## Subpages 27 29 28 - currently subpages exist but are not used yet, they are perfect for testing things though (as otherwise your profile on blento.app will show e.g. cards that dont exist on the deployed version yet), in the development verion go to `/your.handle/{pagename}/edit` to edit a subpage (where pagename can be any string that is not "edit" or "api") (note that currently when you login you always get redirected to your main page) 30 + In-progress changes go on a subpage so your live profile stays clean: `/your.handle/p/<page>/edit` (any `<page>` other than `edit` or `api`). Login redirects to the main page — navigate to the subpage URL manually. 31 + 32 + ## AI-assisted PRs 33 + 34 + Welcome — please: 35 + 36 + - Keep diffs minimal; no unrelated cleanup or verbose code 37 + - Test light/dark, colored cards, edit/view, desktop and both mobile modes (screen-size and `pointer: coarse`)
+3 -2
lex.config.js
··· 1 1 import { defineLexiconConfig } from '@atcute/lex-cli'; 2 2 3 3 export default defineLexiconConfig({ 4 - files: ['lexicons/**/*.json', 'lexicons-pulled/**/*.json', 'lexicons-generated/**/*.json'], 4 + files: ['lexicons/custom/**/*.json', 'lexicons/pulled/**/*.json', 'lexicons/generated/**/*.json'], 5 5 outdir: 'src/lexicon-types/', 6 6 imports: ['@atcute/atproto'], 7 7 pull: { 8 - outdir: 'lexicons-pulled/', 8 + outdir: 'lexicons/pulled/', 9 9 sources: [ 10 10 { 11 11 type: 'atproto', ··· 15 15 'app.blento.page', 16 16 'app.blento.section', 17 17 'app.bsky.actor.profile', 18 + 'app.nearhorizon.actor.pronouns', 18 19 'site.standard.publication' 19 20 ] 20 21 }
+16 -14
lexicons-generated/app/blento/card/getRecord.json lexicons/generated/app/blento/card/getRecord.json
··· 24 24 "encoding": "application/json", 25 25 "schema": { 26 26 "type": "object", 27 - "required": ["uri", "did", "collection", "rkey", "time_us"], 27 + "required": ["uri", "value", "did", "collection", "rkey", "time_us"], 28 28 "properties": { 29 29 "uri": { 30 30 "type": "string", 31 31 "format": "at-uri" 32 32 }, 33 + "cid": { 34 + "type": "string", 35 + "format": "cid" 36 + }, 37 + "value": { 38 + "type": "ref", 39 + "ref": "app.blento.card#main" 40 + }, 33 41 "did": { 34 42 "type": "string", 35 43 "format": "did" ··· 41 49 "rkey": { 42 50 "type": "string" 43 51 }, 44 - "cid": { 45 - "type": "string" 46 - }, 47 - "record": { 48 - "type": "ref", 49 - "ref": "app.blento.card#main" 50 - }, 51 52 "time_us": { 52 53 "type": "integer" 53 54 }, ··· 77 78 "type": "string", 78 79 "format": "at-uri" 79 80 }, 81 + "cid": { 82 + "type": "string", 83 + "format": "cid" 84 + }, 85 + "value": { 86 + "type": "unknown" 87 + }, 80 88 "collection": { 81 89 "type": "string", 82 90 "format": "nsid" 83 91 }, 84 92 "rkey": { 85 93 "type": "string" 86 - }, 87 - "cid": { 88 - "type": "string" 89 - }, 90 - "record": { 91 - "type": "unknown" 92 94 } 93 95 } 94 96 }
-236
lexicons-generated/app/blento/card/listRecords.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "app.blento.card.listRecords", 4 - "defs": { 5 - "main": { 6 - "type": "query", 7 - "description": "Query app.blento.card records with filters", 8 - "parameters": { 9 - "type": "params", 10 - "properties": { 11 - "limit": { 12 - "type": "integer", 13 - "minimum": 1, 14 - "maximum": 200, 15 - "default": 50 16 - }, 17 - "cursor": { 18 - "type": "string" 19 - }, 20 - "actor": { 21 - "type": "string", 22 - "format": "at-identifier", 23 - "description": "Filter by DID or handle (triggers on-demand backfill)" 24 - }, 25 - "profiles": { 26 - "type": "boolean", 27 - "description": "Include profile + identity info keyed by DID" 28 - }, 29 - "wMin": { 30 - "type": "string", 31 - "description": "Minimum value for w" 32 - }, 33 - "wMax": { 34 - "type": "string", 35 - "description": "Maximum value for w" 36 - }, 37 - "hMin": { 38 - "type": "string", 39 - "description": "Minimum value for h" 40 - }, 41 - "hMax": { 42 - "type": "string", 43 - "description": "Maximum value for h" 44 - }, 45 - "xMin": { 46 - "type": "string", 47 - "description": "Minimum value for x" 48 - }, 49 - "xMax": { 50 - "type": "string", 51 - "description": "Maximum value for x" 52 - }, 53 - "yMin": { 54 - "type": "string", 55 - "description": "Minimum value for y" 56 - }, 57 - "yMax": { 58 - "type": "string", 59 - "description": "Maximum value for y" 60 - }, 61 - "mobileWMin": { 62 - "type": "string", 63 - "description": "Minimum value for mobileW" 64 - }, 65 - "mobileWMax": { 66 - "type": "string", 67 - "description": "Maximum value for mobileW" 68 - }, 69 - "mobileHMin": { 70 - "type": "string", 71 - "description": "Minimum value for mobileH" 72 - }, 73 - "mobileHMax": { 74 - "type": "string", 75 - "description": "Maximum value for mobileH" 76 - }, 77 - "mobileXMin": { 78 - "type": "string", 79 - "description": "Minimum value for mobileX" 80 - }, 81 - "mobileXMax": { 82 - "type": "string", 83 - "description": "Maximum value for mobileX" 84 - }, 85 - "mobileYMin": { 86 - "type": "string", 87 - "description": "Minimum value for mobileY" 88 - }, 89 - "mobileYMax": { 90 - "type": "string", 91 - "description": "Maximum value for mobileY" 92 - }, 93 - "cardType": { 94 - "type": "string", 95 - "description": "Filter by cardType" 96 - }, 97 - "color": { 98 - "type": "string", 99 - "description": "Filter by color" 100 - }, 101 - "page": { 102 - "type": "string", 103 - "description": "Filter by page" 104 - }, 105 - "updatedAtMin": { 106 - "type": "string", 107 - "description": "Minimum value for updatedAt" 108 - }, 109 - "updatedAtMax": { 110 - "type": "string", 111 - "description": "Maximum value for updatedAt" 112 - }, 113 - "versionMin": { 114 - "type": "string", 115 - "description": "Minimum value for version" 116 - }, 117 - "versionMax": { 118 - "type": "string", 119 - "description": "Maximum value for version" 120 - }, 121 - "sort": { 122 - "type": "string", 123 - "knownValues": [ 124 - "w", 125 - "h", 126 - "x", 127 - "y", 128 - "mobileW", 129 - "mobileH", 130 - "mobileX", 131 - "mobileY", 132 - "cardType", 133 - "color", 134 - "page", 135 - "updatedAt", 136 - "version" 137 - ], 138 - "description": "Field to sort by (default: time_us)" 139 - }, 140 - "order": { 141 - "type": "string", 142 - "knownValues": ["asc", "desc"], 143 - "description": "Sort direction (default: desc for dates/numbers/counts, asc for strings)" 144 - } 145 - } 146 - }, 147 - "output": { 148 - "encoding": "application/json", 149 - "schema": { 150 - "type": "object", 151 - "required": ["records"], 152 - "properties": { 153 - "records": { 154 - "type": "array", 155 - "items": { 156 - "type": "ref", 157 - "ref": "#record" 158 - } 159 - }, 160 - "cursor": { 161 - "type": "string" 162 - }, 163 - "profiles": { 164 - "type": "array", 165 - "items": { 166 - "type": "ref", 167 - "ref": "#profileEntry" 168 - } 169 - } 170 - } 171 - } 172 - } 173 - }, 174 - "record": { 175 - "type": "object", 176 - "required": ["uri", "did", "collection", "rkey", "time_us"], 177 - "properties": { 178 - "uri": { 179 - "type": "string", 180 - "format": "at-uri" 181 - }, 182 - "did": { 183 - "type": "string", 184 - "format": "did" 185 - }, 186 - "collection": { 187 - "type": "string", 188 - "format": "nsid" 189 - }, 190 - "rkey": { 191 - "type": "string" 192 - }, 193 - "cid": { 194 - "type": "string" 195 - }, 196 - "record": { 197 - "type": "ref", 198 - "ref": "app.blento.card#main" 199 - }, 200 - "time_us": { 201 - "type": "integer" 202 - } 203 - } 204 - }, 205 - "profileEntry": { 206 - "type": "object", 207 - "required": ["did"], 208 - "properties": { 209 - "did": { 210 - "type": "string", 211 - "format": "did" 212 - }, 213 - "handle": { 214 - "type": "string" 215 - }, 216 - "uri": { 217 - "type": "string", 218 - "format": "at-uri" 219 - }, 220 - "collection": { 221 - "type": "string", 222 - "format": "nsid" 223 - }, 224 - "rkey": { 225 - "type": "string" 226 - }, 227 - "cid": { 228 - "type": "string" 229 - }, 230 - "record": { 231 - "type": "unknown" 232 - } 233 - } 234 - } 235 - } 236 - }
lexicons-generated/app/blento/getCursor.json lexicons/generated/app/blento/getCursor.json
lexicons-generated/app/blento/getOverview.json lexicons/generated/app/blento/getOverview.json
+7 -6
lexicons-generated/app/blento/getProfile.json lexicons/generated/app/blento/getProfile.json
··· 48 48 "type": "string", 49 49 "format": "at-uri" 50 50 }, 51 + "cid": { 52 + "type": "string", 53 + "format": "cid" 54 + }, 55 + "value": { 56 + "type": "unknown" 57 + }, 51 58 "collection": { 52 59 "type": "string", 53 60 "format": "nsid" 54 61 }, 55 62 "rkey": { 56 63 "type": "string" 57 - }, 58 - "cid": { 59 - "type": "string" 60 - }, 61 - "record": { 62 - "type": "unknown" 63 64 } 64 65 } 65 66 }
lexicons-generated/app/blento/notifyOfUpdate.json lexicons/generated/app/blento/notifyOfUpdate.json
+16 -14
lexicons-generated/app/blento/page/getRecord.json lexicons/generated/app/blento/page/getRecord.json
··· 24 24 "encoding": "application/json", 25 25 "schema": { 26 26 "type": "object", 27 - "required": ["uri", "did", "collection", "rkey", "time_us"], 27 + "required": ["uri", "value", "did", "collection", "rkey", "time_us"], 28 28 "properties": { 29 29 "uri": { 30 30 "type": "string", 31 31 "format": "at-uri" 32 32 }, 33 + "cid": { 34 + "type": "string", 35 + "format": "cid" 36 + }, 37 + "value": { 38 + "type": "ref", 39 + "ref": "app.blento.page#main" 40 + }, 33 41 "did": { 34 42 "type": "string", 35 43 "format": "did" ··· 41 49 "rkey": { 42 50 "type": "string" 43 51 }, 44 - "cid": { 45 - "type": "string" 46 - }, 47 - "record": { 48 - "type": "ref", 49 - "ref": "app.blento.page#main" 50 - }, 51 52 "time_us": { 52 53 "type": "integer" 53 54 }, ··· 77 78 "type": "string", 78 79 "format": "at-uri" 79 80 }, 81 + "cid": { 82 + "type": "string", 83 + "format": "cid" 84 + }, 85 + "value": { 86 + "type": "unknown" 87 + }, 80 88 "collection": { 81 89 "type": "string", 82 90 "format": "nsid" 83 91 }, 84 92 "rkey": { 85 93 "type": "string" 86 - }, 87 - "cid": { 88 - "type": "string" 89 - }, 90 - "record": { 91 - "type": "unknown" 92 94 } 93 95 } 94 96 }
+23 -21
lexicons-generated/app/blento/page/listRecords.json lexicons/generated/app/blento/section/listRecords.json
··· 1 1 { 2 2 "lexicon": 1, 3 - "id": "app.blento.page.listRecords", 3 + "id": "app.blento.section.listRecords", 4 4 "defs": { 5 5 "main": { 6 6 "type": "query", 7 - "description": "Query app.blento.page records with filters", 7 + "description": "Query app.blento.section records with filters", 8 8 "parameters": { 9 9 "type": "params", 10 10 "properties": { ··· 26 26 "type": "boolean", 27 27 "description": "Include profile + identity info keyed by DID" 28 28 }, 29 - "name": { 29 + "page": { 30 30 "type": "string", 31 - "description": "Filter by name" 31 + "description": "Filter by page" 32 32 }, 33 - "description": { 33 + "sectionType": { 34 34 "type": "string", 35 - "description": "Filter by description" 35 + "description": "Filter by sectionType" 36 36 }, 37 37 "sort": { 38 38 "type": "string", 39 - "knownValues": ["name", "description"], 39 + "knownValues": ["page", "sectionType"], 40 40 "description": "Field to sort by (default: time_us)" 41 41 }, 42 42 "order": { ··· 75 75 }, 76 76 "record": { 77 77 "type": "object", 78 - "required": ["uri", "did", "collection", "rkey", "time_us"], 78 + "required": ["uri", "cid", "value", "did", "collection", "rkey", "time_us"], 79 79 "properties": { 80 80 "uri": { 81 81 "type": "string", 82 82 "format": "at-uri" 83 83 }, 84 + "cid": { 85 + "type": "string", 86 + "format": "cid" 87 + }, 88 + "value": { 89 + "type": "ref", 90 + "ref": "app.blento.section#main" 91 + }, 84 92 "did": { 85 93 "type": "string", 86 94 "format": "did" ··· 91 99 }, 92 100 "rkey": { 93 101 "type": "string" 94 - }, 95 - "cid": { 96 - "type": "string" 97 - }, 98 - "record": { 99 - "type": "ref", 100 - "ref": "app.blento.page#main" 101 102 }, 102 103 "time_us": { 103 104 "type": "integer" ··· 119 120 "type": "string", 120 121 "format": "at-uri" 121 122 }, 123 + "cid": { 124 + "type": "string", 125 + "format": "cid" 126 + }, 127 + "value": { 128 + "type": "unknown" 129 + }, 122 130 "collection": { 123 131 "type": "string", 124 132 "format": "nsid" 125 133 }, 126 134 "rkey": { 127 135 "type": "string" 128 - }, 129 - "cid": { 130 - "type": "string" 131 - }, 132 - "record": { 133 - "type": "unknown" 134 136 } 135 137 } 136 138 }
lexicons/app/blento/card.json lexicons/custom/app/blento/card.json
lexicons/app/blento/page.json lexicons/custom/app/blento/page.json
lexicons/app/blento/section.json lexicons/custom/app/blento/section.json
+35
lexicons/generated/app/blento/authFull.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.blento.authFull", 4 + "defs": { 5 + "main": { 6 + "type": "permission-set", 7 + "title": "app.blento", 8 + "description": "Full access to the app.blento service", 9 + "permissions": [ 10 + { 11 + "type": "permission", 12 + "resource": "rpc", 13 + "aud": "*", 14 + "lxm": [ 15 + "app.blento.card.getRecord", 16 + "app.blento.card.listRecords", 17 + "app.blento.getCursor", 18 + "app.blento.getOverview", 19 + "app.blento.getProfile", 20 + "app.blento.notifyOfUpdate", 21 + "app.blento.page.getRecord", 22 + "app.blento.page.listRecords", 23 + "app.blento.section.getRecord", 24 + "app.blento.section.listRecords" 25 + ] 26 + }, 27 + { 28 + "type": "permission", 29 + "resource": "repo", 30 + "collection": ["app.blento.card", "app.blento.page", "app.blento.section"] 31 + } 32 + ] 33 + } 34 + } 35 + }
+140
lexicons/generated/app/blento/card/listRecords.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.blento.card.listRecords", 4 + "defs": { 5 + "main": { 6 + "type": "query", 7 + "description": "Query app.blento.card records with filters", 8 + "parameters": { 9 + "type": "params", 10 + "properties": { 11 + "limit": { 12 + "type": "integer", 13 + "minimum": 1, 14 + "maximum": 200, 15 + "default": 50 16 + }, 17 + "cursor": { 18 + "type": "string" 19 + }, 20 + "actor": { 21 + "type": "string", 22 + "format": "at-identifier", 23 + "description": "Filter by DID or handle (triggers on-demand backfill)" 24 + }, 25 + "profiles": { 26 + "type": "boolean", 27 + "description": "Include profile + identity info keyed by DID" 28 + }, 29 + "page": { 30 + "type": "string", 31 + "description": "Filter by page" 32 + }, 33 + "cardType": { 34 + "type": "string", 35 + "description": "Filter by cardType" 36 + }, 37 + "sort": { 38 + "type": "string", 39 + "knownValues": ["page", "cardType"], 40 + "description": "Field to sort by (default: time_us)" 41 + }, 42 + "order": { 43 + "type": "string", 44 + "knownValues": ["asc", "desc"], 45 + "description": "Sort direction (default: desc for dates/numbers/counts, asc for strings)" 46 + } 47 + } 48 + }, 49 + "output": { 50 + "encoding": "application/json", 51 + "schema": { 52 + "type": "object", 53 + "required": ["records"], 54 + "properties": { 55 + "records": { 56 + "type": "array", 57 + "items": { 58 + "type": "ref", 59 + "ref": "#record" 60 + } 61 + }, 62 + "cursor": { 63 + "type": "string" 64 + }, 65 + "profiles": { 66 + "type": "array", 67 + "items": { 68 + "type": "ref", 69 + "ref": "#profileEntry" 70 + } 71 + } 72 + } 73 + } 74 + } 75 + }, 76 + "record": { 77 + "type": "object", 78 + "required": ["uri", "cid", "value", "did", "collection", "rkey", "time_us"], 79 + "properties": { 80 + "uri": { 81 + "type": "string", 82 + "format": "at-uri" 83 + }, 84 + "cid": { 85 + "type": "string", 86 + "format": "cid" 87 + }, 88 + "value": { 89 + "type": "ref", 90 + "ref": "app.blento.card#main" 91 + }, 92 + "did": { 93 + "type": "string", 94 + "format": "did" 95 + }, 96 + "collection": { 97 + "type": "string", 98 + "format": "nsid" 99 + }, 100 + "rkey": { 101 + "type": "string" 102 + }, 103 + "time_us": { 104 + "type": "integer" 105 + } 106 + } 107 + }, 108 + "profileEntry": { 109 + "type": "object", 110 + "required": ["did"], 111 + "properties": { 112 + "did": { 113 + "type": "string", 114 + "format": "did" 115 + }, 116 + "handle": { 117 + "type": "string" 118 + }, 119 + "uri": { 120 + "type": "string", 121 + "format": "at-uri" 122 + }, 123 + "cid": { 124 + "type": "string", 125 + "format": "cid" 126 + }, 127 + "value": { 128 + "type": "unknown" 129 + }, 130 + "collection": { 131 + "type": "string", 132 + "format": "nsid" 133 + }, 134 + "rkey": { 135 + "type": "string" 136 + } 137 + } 138 + } 139 + } 140 + }
+122
lexicons/generated/app/blento/page/listRecords.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.blento.page.listRecords", 4 + "defs": { 5 + "main": { 6 + "type": "query", 7 + "description": "Query app.blento.page records with filters", 8 + "parameters": { 9 + "type": "params", 10 + "properties": { 11 + "limit": { 12 + "type": "integer", 13 + "minimum": 1, 14 + "maximum": 200, 15 + "default": 50 16 + }, 17 + "cursor": { 18 + "type": "string" 19 + }, 20 + "actor": { 21 + "type": "string", 22 + "format": "at-identifier", 23 + "description": "Filter by DID or handle (triggers on-demand backfill)" 24 + }, 25 + "profiles": { 26 + "type": "boolean", 27 + "description": "Include profile + identity info keyed by DID" 28 + } 29 + } 30 + }, 31 + "output": { 32 + "encoding": "application/json", 33 + "schema": { 34 + "type": "object", 35 + "required": ["records"], 36 + "properties": { 37 + "records": { 38 + "type": "array", 39 + "items": { 40 + "type": "ref", 41 + "ref": "#record" 42 + } 43 + }, 44 + "cursor": { 45 + "type": "string" 46 + }, 47 + "profiles": { 48 + "type": "array", 49 + "items": { 50 + "type": "ref", 51 + "ref": "#profileEntry" 52 + } 53 + } 54 + } 55 + } 56 + } 57 + }, 58 + "record": { 59 + "type": "object", 60 + "required": ["uri", "cid", "value", "did", "collection", "rkey", "time_us"], 61 + "properties": { 62 + "uri": { 63 + "type": "string", 64 + "format": "at-uri" 65 + }, 66 + "cid": { 67 + "type": "string", 68 + "format": "cid" 69 + }, 70 + "value": { 71 + "type": "ref", 72 + "ref": "app.blento.page#main" 73 + }, 74 + "did": { 75 + "type": "string", 76 + "format": "did" 77 + }, 78 + "collection": { 79 + "type": "string", 80 + "format": "nsid" 81 + }, 82 + "rkey": { 83 + "type": "string" 84 + }, 85 + "time_us": { 86 + "type": "integer" 87 + } 88 + } 89 + }, 90 + "profileEntry": { 91 + "type": "object", 92 + "required": ["did"], 93 + "properties": { 94 + "did": { 95 + "type": "string", 96 + "format": "did" 97 + }, 98 + "handle": { 99 + "type": "string" 100 + }, 101 + "uri": { 102 + "type": "string", 103 + "format": "at-uri" 104 + }, 105 + "cid": { 106 + "type": "string", 107 + "format": "cid" 108 + }, 109 + "value": { 110 + "type": "unknown" 111 + }, 112 + "collection": { 113 + "type": "string", 114 + "format": "nsid" 115 + }, 116 + "rkey": { 117 + "type": "string" 118 + } 119 + } 120 + } 121 + } 122 + }
+98
lexicons/generated/app/blento/section/getRecord.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.blento.section.getRecord", 4 + "defs": { 5 + "main": { 6 + "type": "query", 7 + "description": "Get a single app.blento.section record by AT URI", 8 + "parameters": { 9 + "type": "params", 10 + "required": ["uri"], 11 + "properties": { 12 + "uri": { 13 + "type": "string", 14 + "format": "at-uri", 15 + "description": "AT URI of the record" 16 + }, 17 + "profiles": { 18 + "type": "boolean", 19 + "description": "Include profile + identity info keyed by DID" 20 + } 21 + } 22 + }, 23 + "output": { 24 + "encoding": "application/json", 25 + "schema": { 26 + "type": "object", 27 + "required": ["uri", "value", "did", "collection", "rkey", "time_us"], 28 + "properties": { 29 + "uri": { 30 + "type": "string", 31 + "format": "at-uri" 32 + }, 33 + "cid": { 34 + "type": "string", 35 + "format": "cid" 36 + }, 37 + "value": { 38 + "type": "ref", 39 + "ref": "app.blento.section#main" 40 + }, 41 + "did": { 42 + "type": "string", 43 + "format": "did" 44 + }, 45 + "collection": { 46 + "type": "string", 47 + "format": "nsid" 48 + }, 49 + "rkey": { 50 + "type": "string" 51 + }, 52 + "time_us": { 53 + "type": "integer" 54 + }, 55 + "profiles": { 56 + "type": "array", 57 + "items": { 58 + "type": "ref", 59 + "ref": "#profileEntry" 60 + } 61 + } 62 + } 63 + } 64 + } 65 + }, 66 + "profileEntry": { 67 + "type": "object", 68 + "required": ["did"], 69 + "properties": { 70 + "did": { 71 + "type": "string", 72 + "format": "did" 73 + }, 74 + "handle": { 75 + "type": "string" 76 + }, 77 + "uri": { 78 + "type": "string", 79 + "format": "at-uri" 80 + }, 81 + "cid": { 82 + "type": "string", 83 + "format": "cid" 84 + }, 85 + "value": { 86 + "type": "unknown" 87 + }, 88 + "collection": { 89 + "type": "string", 90 + "format": "nsid" 91 + }, 92 + "rkey": { 93 + "type": "string" 94 + } 95 + } 96 + } 97 + } 98 + }
+20
lexicons/generated/index.ts
··· 1 + // Auto-generated by @atmo-dev/contrail-lexicons. Do not edit. 2 + // Pass `lexicons` to `createWorker(config, { lexicons })` to expose them 3 + // at `/xrpc/<namespace>.lexicons` for consumer apps to typegen against. 4 + 5 + import _0 from '../custom/app/blento/card.json'; 6 + import _1 from '../custom/app/blento/page.json'; 7 + import _2 from '../custom/app/blento/section.json'; 8 + import _3 from './app/blento/authFull.json'; 9 + import _4 from './app/blento/card/getRecord.json'; 10 + import _5 from './app/blento/card/listRecords.json'; 11 + import _6 from './app/blento/getCursor.json'; 12 + import _7 from './app/blento/getOverview.json'; 13 + import _8 from './app/blento/getProfile.json'; 14 + import _9 from './app/blento/notifyOfUpdate.json'; 15 + import _10 from './app/blento/page/getRecord.json'; 16 + import _11 from './app/blento/page/listRecords.json'; 17 + import _12 from './app/blento/section/getRecord.json'; 18 + import _13 from './app/blento/section/listRecords.json'; 19 + 20 + export const lexicons: object[] = [_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13];
+8 -7
package.json
··· 5 5 "type": "module", 6 6 "scripts": { 7 7 "dev": "vite dev", 8 - "build": "NODE_OPTIONS='--max-old-space-size=4096' vite build && tsx scripts/append-scheduled.ts", 8 + "build": "NODE_OPTIONS='--max-old-space-size=4096' vite build && contrail append-scheduled", 9 9 "preview": "pnpm run build && wrangler dev", 10 10 "prepare": "svelte-kit sync || echo ''", 11 11 "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", ··· 19 19 "env:generate-secret": "npx tsx src/lib/atproto/scripts/generate-secret.ts", 20 20 "env:setup-dev": "npx tsx src/lib/atproto/scripts/setup-dev.ts", 21 21 "tunnel": "npx tsx src/lib/atproto/scripts/tunnel.ts", 22 - "generate": "npx tsx scripts/generate.ts", 23 - "sync": "npx tsx scripts/sync.ts", 24 - "sync:remote": "npx tsx scripts/sync.ts --remote" 22 + "generate": "contrail-lex generate", 23 + "backfill": "contrail backfill", 24 + "backfill:remote": "contrail backfill --remote" 25 25 }, 26 26 "devDependencies": { 27 - "@atcute/lex-cli": "^2.6.1", 27 + "@atcute/lex-cli": "^2.8.1", 28 28 "@atcute/lexicon-doc": "^2.1.2", 29 + "@atmo-dev/contrail-lexicons": "^0.4.5", 29 30 "@eslint/compat": "^2.0.3", 30 31 "@eslint/js": "^10.0.1", 31 32 "@sveltejs/adapter-cloudflare": "^7.2.8", ··· 59 60 "@atcute/bluesky-richtext-segmenter": "^3.0.0", 60 61 "@atcute/client": "^4.2.1", 61 62 "@atcute/identity-resolver": "^1.2.2", 62 - "@atcute/lexicons": "^1.2.9", 63 + "@atcute/lexicons": "^1.3.0", 63 64 "@atcute/oauth-browser-client": "^3.0.0", 64 65 "@atcute/oauth-node-client": "^1.1.0", 65 66 "@atcute/standard-site": "^1.0.1", 66 67 "@atcute/tid": "^1.1.2", 67 - "@atmo-dev/contrail": "^0.0.8", 68 + "@atmo-dev/contrail": "^0.5.0", 68 69 "@cloudflare/workers-types": "^4.20260313.1", 69 70 "@ethercorps/sveltekit-og": "^4.2.1", 70 71 "@floating-ui/dom": "^1.7.6",
+104 -59
pnpm-lock.yaml
··· 25 25 version: 4.2.1 26 26 '@atcute/identity-resolver': 27 27 specifier: ^1.2.2 28 - version: 1.2.2(@atcute/identity@1.1.3) 28 + version: 1.2.2(@atcute/identity@1.1.4) 29 29 '@atcute/lexicons': 30 - specifier: ^1.2.9 31 - version: 1.2.9 30 + specifier: ^1.3.0 31 + version: 1.3.0 32 32 '@atcute/oauth-browser-client': 33 33 specifier: ^3.0.0 34 - version: 3.0.0(@atcute/identity@1.1.3) 34 + version: 3.0.0(@atcute/identity@1.1.4) 35 35 '@atcute/oauth-node-client': 36 36 specifier: ^1.1.0 37 37 version: 1.1.0 ··· 42 42 specifier: ^1.1.2 43 43 version: 1.1.2 44 44 '@atmo-dev/contrail': 45 - specifier: ^0.0.8 46 - version: 0.0.8(@atcute/identity@1.1.3)(react@19.2.4) 45 + specifier: ^0.5.0 46 + version: 0.5.0(react@19.2.4)(wrangler@4.73.0(@cloudflare/workers-types@4.20260313.1)) 47 47 '@cloudflare/workers-types': 48 48 specifier: ^4.20260313.1 49 49 version: 4.20260313.1 ··· 208 208 version: 4.73.0(@cloudflare/workers-types@4.20260313.1) 209 209 devDependencies: 210 210 '@atcute/lex-cli': 211 - specifier: ^2.6.1 212 - version: 2.8.0 211 + specifier: ^2.8.1 212 + version: 2.8.1 213 213 '@atcute/lexicon-doc': 214 214 specifier: ^2.1.2 215 215 version: 2.2.0 216 + '@atmo-dev/contrail-lexicons': 217 + specifier: ^0.4.5 218 + version: 0.4.5(react@19.2.4)(wrangler@4.73.0(@cloudflare/workers-types@4.20260313.1)) 216 219 '@eslint/compat': 217 220 specifier: ^2.0.3 218 221 version: 2.0.3(eslint@10.0.3(jiti@2.6.1)) ··· 332 335 '@atcute/jetstream@1.1.2': 333 336 resolution: {integrity: sha512-u6p/h2xppp7LE6W/9xErAJ6frfN60s8adZuCKtfAaaBBiiYbb1CfpzN8Uc+2qtJZNorqGvuuDb5572Jmh7yHBQ==} 334 337 335 - '@atcute/lex-cli@2.8.0': 336 - resolution: {integrity: sha512-eNPO0hhGhrCXQ7vEgVhqAaSHSsT3me1Jcc99rHaPgne1xP7fBfprf+E02M6BUqwrBz95YpnyuLPmVKNEk1jLwA==} 338 + '@atcute/lex-cli@2.8.1': 339 + resolution: {integrity: sha512-ab+NpnwvW7gXMjZYYqK3zqhcAPOEYnAaNAXHp28gmL3M33zHgz4IjNQpDnRAjTitCpaJ3KoA3KXlnXNjsV6USg==} 337 340 hasBin: true 338 341 339 342 '@atcute/lexicon-doc@2.2.0': ··· 344 347 peerDependencies: 345 348 '@atcute/identity': ^1.1.0 346 349 '@atcute/identity-resolver': ^1.1.3 347 - 348 - '@atcute/lexicons@1.2.9': 349 - resolution: {integrity: sha512-/RRHm2Cw9o8Mcsrq0eo8fjS9okKYLGfuFwrQ0YoP/6sdSDsXshaTLJsvLlcUcaDaSJ1YFOuHIo3zr2Om2F/16g==} 350 350 351 351 '@atcute/lexicons@1.3.0': 352 352 resolution: {integrity: sha512-Eq5y+9onnCXNVUlNiMf31beSXHKqptB7lUo/68YbhlmxdaR7ooywHmahya9goP5AsmlYEA1z+dRPXIDAa9O7cg==} ··· 393 393 '@atcute/util-fetch@1.0.5': 394 394 resolution: {integrity: sha512-qjHj01BGxjSjIFdPiAjSARnodJIIyKxnCMMEcXMESo9TAyND6XZQqrie5fia+LlYWVXdpsTds8uFQwc9jdKTig==} 395 395 396 - '@atcute/util-text@1.1.1': 397 - resolution: {integrity: sha512-JH0SxzUQJAmbOBTYyhxQbkkI6M33YpjlVLEcbP5GYt43xgFArzV0FJVmEpvIj0kjsmphHB45b6IitdvxPdec9w==} 398 - 399 396 '@atcute/util-text@1.2.0': 400 397 resolution: {integrity: sha512-b8WSh+Z7K601eUFFmTFj8QPKDO8Ic0VDDj63sdKzpkm+ySQKsYT5nXekViGqFVKbyKj1V5FyvZvgXad6/aI4QQ==} 401 398 399 + '@atcute/util-text@1.3.1': 400 + resolution: {integrity: sha512-MRgJXkx67znuBXuoAYCJkBZyd3OApL7zZlNf5kXhuoCXcdiu1nblRDycYTADSkym4epBSQWxh26kmI9sewaq6A==} 401 + 402 402 '@atcute/varint@2.0.0': 403 403 resolution: {integrity: sha512-CEY/oVK/nVpL4e5y3sdenLETDL6/Xu5xsE/0TupK+f0Yv8jcD60t2gD8SHROWSvUwYLdkjczLCSA7YrtnjCzWw==} 404 404 405 - '@atmo-dev/contrail@0.0.8': 406 - resolution: {integrity: sha512-sXtdd3Z8VNVoSinrX3ww978ctxtHBl0bX5tP51XOY0IWKJ4xl9zqkPOIIBMzbVE3IyU2Vq2B9Whi3VAhyd2Qdg==} 405 + '@atcute/xrpc-server@0.1.12': 406 + resolution: {integrity: sha512-70KIerQlljp5+s6t0u6YNN9klEboQUZa2hhoi/hmXIO1cIKEORettTMctnyjfcCJaSfAuj42dxPu51GTZBlm8w==} 407 + 408 + '@atmo-dev/contrail-lexicons@0.4.5': 409 + resolution: {integrity: sha512-gd3k8tFbcokjsgTpC9gKitYgImI6M+iipFCUYy60nCHomDCYMxkgrvhjJlii61ol9LEbcVDSPdMKKcKJGwm3gA==} 410 + hasBin: true 411 + 412 + '@atmo-dev/contrail@0.5.0': 413 + resolution: {integrity: sha512-PeeA3Q6NDwaho2dgaW4QANPRvhjKBml17MMxjcq6DwQG7p0MTUqoa2RM1G7DpA8CAIPrRXTVcosb6ybqshlWtQ==} 414 + hasBin: true 407 415 peerDependencies: 408 416 pg: ^8.0.0 417 + wrangler: ^4.0.0 409 418 peerDependenciesMeta: 410 419 pg: 420 + optional: true 421 + wrangler: 411 422 optional: true 412 423 413 424 '@badrap/valita@0.4.6': ··· 986 997 peerDependencies: 987 998 svelte: ^4 || ^5 988 999 989 - '@optique/core@0.10.7': 990 - resolution: {integrity: sha512-FwSX8ILFqzcCqZi6Xetsa4flJp/yyqFG4d4eFD98BtqdzxxuylzdrKvsXj/ow8mcoVjYkTuaIkqHSBxonqMcQg==} 1000 + '@optique/core@1.0.2': 1001 + resolution: {integrity: sha512-znsqMmjAdeOgSJzdJlpZpgAscojwQmeQYXzYnuEKllz5VCj6WyEkdzU4QuvJQtWQY3ve2taXwudEBRur0VHBOQ==} 991 1002 engines: {bun: '>=1.2.0', deno: '>=2.3.0', node: '>=20.0.0'} 992 1003 993 - '@optique/run@0.10.7': 994 - resolution: {integrity: sha512-1CVdH8uyptj1nFGS2MLacSmZceRClez4LD/G/Gm38wrAVnJq6I+9Fvyh2bVHErsZLQzR0a12CYMUWIgDKY3X1w==} 1004 + '@optique/run@1.0.2': 1005 + resolution: {integrity: sha512-0Wc+zC8SLGV8zXQX+pk+o0c6wE/ddx/36CHZ0toTh5lApsjruUuGhqbxvljerAAG5un1xQbOLxzksBVC6UPgSg==} 995 1006 engines: {bun: '>=1.2.0', deno: '>=2.3.0', node: '>=20.0.0'} 996 1007 997 1008 '@oxc-project/runtime@0.115.0': ··· 1741 1752 resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} 1742 1753 engines: {node: 18 || 20 || >=22} 1743 1754 1755 + cac@7.0.0: 1756 + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} 1757 + engines: {node: '>=20.19.0'} 1758 + 1744 1759 camelize@1.0.1: 1745 1760 resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} 1746 1761 ··· 2446 2461 engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 2447 2462 hasBin: true 2448 2463 2464 + nanoid@5.1.11: 2465 + resolution: {integrity: sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg==} 2466 + engines: {node: ^18 || >=20} 2467 + hasBin: true 2468 + 2449 2469 nanoid@5.1.6: 2450 2470 resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==} 2451 2471 engines: {node: ^18 || >=20} ··· 3353 3373 3354 3374 '@atcute/atproto@3.1.10': 3355 3375 dependencies: 3356 - '@atcute/lexicons': 1.2.9 3376 + '@atcute/lexicons': 1.3.0 3357 3377 3358 3378 '@atcute/bluesky-richtext-parser@2.1.1': {} 3359 3379 ··· 3362 3382 '@atcute/bluesky@3.3.0': 3363 3383 dependencies: 3364 3384 '@atcute/atproto': 3.1.10 3365 - '@atcute/lexicons': 1.2.9 3385 + '@atcute/lexicons': 1.3.0 3366 3386 3367 3387 '@atcute/car@5.1.1': 3368 3388 dependencies: ··· 3374 3394 '@atcute/cbor@2.3.2': 3375 3395 dependencies: 3376 3396 '@atcute/cid': 2.4.1 3377 - '@atcute/multibase': 1.1.8 3397 + '@atcute/multibase': 1.2.0 3378 3398 '@atcute/uint8array': 1.1.1 3379 3399 3380 3400 '@atcute/cid@2.4.1': ··· 3385 3405 '@atcute/client@4.2.1': 3386 3406 dependencies: 3387 3407 '@atcute/identity': 1.1.3 3388 - '@atcute/lexicons': 1.2.9 3408 + '@atcute/lexicons': 1.3.0 3389 3409 3390 3410 '@atcute/crypto@2.4.1': 3391 3411 dependencies: ··· 3396 3416 '@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.3)': 3397 3417 dependencies: 3398 3418 '@atcute/identity': 1.1.3 3399 - '@atcute/lexicons': 1.2.9 3419 + '@atcute/lexicons': 1.3.0 3400 3420 '@atcute/util-fetch': 1.0.5 3401 3421 '@badrap/valita': 0.4.6 3402 3422 3403 3423 '@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.4)': 3404 3424 dependencies: 3405 3425 '@atcute/identity': 1.1.4 3406 - '@atcute/lexicons': 1.2.9 3426 + '@atcute/lexicons': 1.3.0 3407 3427 '@atcute/util-fetch': 1.0.5 3408 3428 '@badrap/valita': 0.4.6 3409 3429 3410 3430 '@atcute/identity@1.1.3': 3411 3431 dependencies: 3412 - '@atcute/lexicons': 1.2.9 3432 + '@atcute/lexicons': 1.3.0 3413 3433 '@badrap/valita': 0.4.6 3414 3434 3415 3435 '@atcute/identity@1.1.4': ··· 3419 3439 3420 3440 '@atcute/jetstream@1.1.2(react@19.2.4)': 3421 3441 dependencies: 3422 - '@atcute/lexicons': 1.2.9 3442 + '@atcute/lexicons': 1.3.0 3423 3443 '@badrap/valita': 0.4.6 3424 3444 '@mary-ext/event-iterator': 1.0.0 3425 3445 '@mary-ext/simple-event-emitter': 1.0.1 ··· 3429 3449 transitivePeerDependencies: 3430 3450 - react 3431 3451 3432 - '@atcute/lex-cli@2.8.0': 3452 + '@atcute/lex-cli@2.8.1': 3433 3453 dependencies: 3434 3454 '@atcute/identity': 1.1.4 3435 3455 '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.4) 3436 3456 '@atcute/lexicon-doc': 2.2.0 3437 - '@atcute/lexicon-resolver': 0.1.6(@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.3))(@atcute/identity@1.1.4) 3457 + '@atcute/lexicon-resolver': 0.1.6(@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.4))(@atcute/identity@1.1.4) 3438 3458 '@atcute/lexicons': 1.3.0 3439 3459 '@badrap/valita': 0.4.6 3440 - '@optique/core': 0.10.7 3441 - '@optique/run': 0.10.7 3460 + '@optique/core': 1.0.2 3461 + '@optique/run': 1.0.2 3442 3462 picocolors: 1.1.1 3443 3463 prettier: 3.8.3 3444 3464 ··· 3450 3470 '@atcute/util-text': 1.2.0 3451 3471 '@badrap/valita': 0.4.6 3452 3472 3453 - '@atcute/lexicon-resolver@0.1.6(@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.3))(@atcute/identity@1.1.4)': 3473 + '@atcute/lexicon-resolver@0.1.6(@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.4))(@atcute/identity@1.1.4)': 3454 3474 dependencies: 3455 3475 '@atcute/crypto': 2.4.1 3456 3476 '@atcute/identity': 1.1.4 3457 - '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.3) 3477 + '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.4) 3458 3478 '@atcute/lexicon-doc': 2.2.0 3459 3479 '@atcute/lexicons': 1.3.0 3460 3480 '@atcute/repo': 0.1.4 3461 3481 '@atcute/util-fetch': 1.0.5 3462 3482 '@badrap/valita': 0.4.6 3463 3483 3464 - '@atcute/lexicons@1.2.9': 3465 - dependencies: 3466 - '@atcute/uint8array': 1.1.1 3467 - '@atcute/util-text': 1.1.1 3468 - '@standard-schema/spec': 1.1.0 3469 - esm-env: 1.2.2 3470 - 3471 3484 '@atcute/lexicons@1.3.0': 3472 3485 dependencies: 3473 3486 '@atcute/uint8array': 1.1.1 3474 - '@atcute/util-text': 1.2.0 3487 + '@atcute/util-text': 1.3.1 3475 3488 '@standard-schema/spec': 1.1.0 3476 3489 esm-env: 1.2.2 3477 3490 ··· 3489 3502 dependencies: 3490 3503 '@atcute/uint8array': 1.1.1 3491 3504 3492 - '@atcute/oauth-browser-client@3.0.0(@atcute/identity@1.1.3)': 3505 + '@atcute/oauth-browser-client@3.0.0(@atcute/identity@1.1.4)': 3493 3506 dependencies: 3494 3507 '@atcute/client': 4.2.1 3495 - '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.3) 3496 - '@atcute/lexicons': 1.2.9 3508 + '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.4) 3509 + '@atcute/lexicons': 1.3.0 3497 3510 '@atcute/multibase': 1.1.8 3498 3511 '@atcute/oauth-crypto': 0.1.0 3499 3512 '@atcute/oauth-types': 0.1.1 ··· 3517 3530 '@atcute/client': 4.2.1 3518 3531 '@atcute/identity': 1.1.3 3519 3532 '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.3) 3520 - '@atcute/lexicons': 1.2.9 3533 + '@atcute/lexicons': 1.3.0 3521 3534 '@atcute/oauth-crypto': 0.1.0 3522 3535 '@atcute/oauth-keyset': 0.1.0 3523 3536 '@atcute/oauth-types': 0.1.1 ··· 3528 3541 '@atcute/oauth-types@0.1.1': 3529 3542 dependencies: 3530 3543 '@atcute/identity': 1.1.3 3531 - '@atcute/lexicons': 1.2.9 3544 + '@atcute/lexicons': 1.3.0 3532 3545 '@atcute/oauth-keyset': 0.1.0 3533 3546 '@badrap/valita': 0.4.6 3534 3547 ··· 3545 3558 '@atcute/standard-site@1.0.1': 3546 3559 dependencies: 3547 3560 '@atcute/atproto': 3.1.10 3548 - '@atcute/lexicons': 1.2.9 3561 + '@atcute/lexicons': 1.3.0 3549 3562 3550 3563 '@atcute/tid@1.1.2': 3551 3564 dependencies: ··· 3559 3572 dependencies: 3560 3573 '@badrap/valita': 0.4.6 3561 3574 3562 - '@atcute/util-text@1.1.1': 3575 + '@atcute/util-text@1.2.0': 3563 3576 dependencies: 3564 3577 unicode-segmenter: 0.14.5 3565 3578 3566 - '@atcute/util-text@1.2.0': 3579 + '@atcute/util-text@1.3.1': 3567 3580 dependencies: 3568 3581 unicode-segmenter: 0.14.5 3569 3582 3570 3583 '@atcute/varint@2.0.0': {} 3571 3584 3572 - '@atmo-dev/contrail@0.0.8(@atcute/identity@1.1.3)(react@19.2.4)': 3585 + '@atcute/xrpc-server@0.1.12': 3586 + dependencies: 3587 + '@atcute/cbor': 2.3.2 3588 + '@atcute/crypto': 2.4.1 3589 + '@atcute/identity': 1.1.4 3590 + '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.4) 3591 + '@atcute/lexicons': 1.3.0 3592 + '@atcute/multibase': 1.2.0 3593 + '@atcute/uint8array': 1.1.1 3594 + '@badrap/valita': 0.4.6 3595 + nanoid: 5.1.11 3596 + 3597 + '@atmo-dev/contrail-lexicons@0.4.5(react@19.2.4)(wrangler@4.73.0(@cloudflare/workers-types@4.20260313.1))': 3598 + dependencies: 3599 + '@atcute/lex-cli': 2.8.1 3600 + '@atmo-dev/contrail': 0.5.0(react@19.2.4)(wrangler@4.73.0(@cloudflare/workers-types@4.20260313.1)) 3601 + transitivePeerDependencies: 3602 + - pg 3603 + - react 3604 + - wrangler 3605 + 3606 + '@atmo-dev/contrail@0.5.0(react@19.2.4)(wrangler@4.73.0(@cloudflare/workers-types@4.20260313.1))': 3573 3607 dependencies: 3574 3608 '@atcute/atproto': 3.1.10 3609 + '@atcute/cbor': 2.3.2 3610 + '@atcute/cid': 2.4.1 3575 3611 '@atcute/client': 4.2.1 3576 - '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.3) 3612 + '@atcute/identity': 1.1.4 3613 + '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.4) 3577 3614 '@atcute/jetstream': 1.1.2(react@19.2.4) 3578 - '@atcute/lexicons': 1.2.9 3615 + '@atcute/lexicons': 1.3.0 3616 + '@atcute/xrpc-server': 0.1.12 3617 + cac: 7.0.0 3579 3618 hono: 4.12.14 3619 + jiti: 2.6.1 3620 + optionalDependencies: 3621 + wrangler: 4.73.0(@cloudflare/workers-types@4.20260313.1) 3580 3622 transitivePeerDependencies: 3581 - - '@atcute/identity' 3582 3623 - react 3583 3624 3584 3625 '@badrap/valita@0.4.6': {} ··· 4143 4184 number-flow: 0.6.0 4144 4185 svelte: 5.53.11 4145 4186 4146 - '@optique/core@0.10.7': {} 4187 + '@optique/core@1.0.2': {} 4147 4188 4148 - '@optique/run@0.10.7': 4189 + '@optique/run@1.0.2': 4149 4190 dependencies: 4150 - '@optique/core': 0.10.7 4191 + '@optique/core': 1.0.2 4151 4192 4152 4193 '@oxc-project/runtime@0.115.0': {} 4153 4194 ··· 4879 4920 dependencies: 4880 4921 balanced-match: 4.0.4 4881 4922 4923 + cac@7.0.0: {} 4924 + 4882 4925 camelize@1.0.1: {} 4883 4926 4884 4927 camera-controls@3.1.2(three@0.183.2): ··· 5557 5600 murmurhash-js@1.0.0: {} 5558 5601 5559 5602 nanoid@3.3.11: {} 5603 + 5604 + nanoid@5.1.11: {} 5560 5605 5561 5606 nanoid@5.1.6: {} 5562 5607
-29
scripts/append-scheduled.ts
··· 1 - /** 2 - * Post-build script: appends a `scheduled` handler to the SvelteKit worker output. 3 - * 4 - * SvelteKit's adapter-cloudflare doesn't support the `scheduled` export natively 5 - * (see https://github.com/sveltejs/kit/issues/4841). This script patches the 6 - * generated _worker.js to add one that self-calls the /api/cron endpoint. 7 - */ 8 - import { readFileSync, writeFileSync } from 'fs'; 9 - import { join, dirname } from 'path'; 10 - import { fileURLToPath } from 'url'; 11 - 12 - const root = join(dirname(fileURLToPath(import.meta.url)), '..'); 13 - const workerPath = join(root, '.svelte-kit', 'cloudflare', '_worker.js'); 14 - 15 - let code = readFileSync(workerPath, 'utf-8'); 16 - 17 - code += ` 18 - // --- Appended by scripts/append-scheduled.ts --- 19 - worker_default.scheduled = async function (event, env, ctx) { 20 - const req = new Request('http://localhost/api/cron', { 21 - method: 'POST', 22 - headers: { 'X-Cron-Secret': env.CRON_SECRET || '' } 23 - }); 24 - ctx.waitUntil(this.fetch(req, env, ctx)); 25 - }; 26 - `; 27 - 28 - writeFileSync(workerPath, code); 29 - console.log('Appended scheduled handler to _worker.js');
-14
scripts/generate.ts
··· 1 - import { join, dirname } from 'path'; 2 - import { fileURLToPath } from 'url'; 3 - import { config } from '../src/lib/contrail/config'; 4 - import { generateLexicons } from '@atmo-dev/contrail/generate'; 5 - 6 - const ROOT_DIR = join(dirname(fileURLToPath(import.meta.url)), '..'); 7 - 8 - generateLexicons({ 9 - config, 10 - rootDir: ROOT_DIR, 11 - lexiconDir: join(ROOT_DIR, 'lexicons'), 12 - outputDir: join(ROOT_DIR, 'lexicons-generated'), 13 - writeRuntimeFiles: true 14 - });
-68
scripts/sync.ts
··· 1 - /** 2 - * Discover users from relays and backfill their records from PDS. 3 - * 4 - * Usage: 5 - * pnpm sync # local D1 6 - * pnpm sync:remote # prod D1 7 - */ 8 - import { Contrail } from '@atmo-dev/contrail'; 9 - import { config } from '../src/lib/contrail/config'; 10 - import { getPlatformProxy } from 'wrangler'; 11 - 12 - function elapsed(start: number): string { 13 - const ms = Date.now() - start; 14 - if (ms < 1000) return `${ms}ms`; 15 - if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`; 16 - const mins = Math.floor(ms / 60_000); 17 - const secs = ((ms % 60_000) / 1000).toFixed(0); 18 - return `${mins}m ${secs}s`; 19 - } 20 - 21 - async function main() { 22 - const remote = process.argv.includes('--remote'); 23 - const syncStart = Date.now(); 24 - 25 - console.log(`=== Sync (${remote ? 'remote/prod' : 'local'} D1) ===\n`); 26 - 27 - const { env, dispose } = await getPlatformProxy<{ DB: D1Database }>({ 28 - environment: remote ? 'production' : undefined 29 - }); 30 - 31 - const contrail = new Contrail({ ...config, db: env.DB }); 32 - 33 - try { 34 - await contrail.init(); 35 - 36 - console.log('--- Discovery ---'); 37 - const discoveryStart = Date.now(); 38 - const discovered = await contrail.discover(); 39 - console.log(` Done: ${discovered.length} users in ${elapsed(discoveryStart)}\n`); 40 - 41 - console.log('--- Backfill ---'); 42 - const backfillStart = Date.now(); 43 - const total = await contrail.backfill({ 44 - concurrency: 100, 45 - onProgress: ({ records, usersComplete, usersTotal, usersFailed }) => { 46 - const secs = (Date.now() - backfillStart) / 1000; 47 - const rate = secs > 0 ? Math.round(records / secs) : 0; 48 - const failStr = usersFailed > 0 ? ` | ${usersFailed} failed` : ''; 49 - process.stdout.write( 50 - `\r ${records} records | ${usersComplete}/${usersTotal} users | ${rate}/s | ${elapsed(backfillStart)}${failStr} ` 51 - ); 52 - } 53 - }); 54 - process.stdout.write('\n'); 55 - console.log(` Done: ${total} records in ${elapsed(backfillStart)}\n`); 56 - 57 - console.log(`=== Finished in ${elapsed(syncStart)} ===`); 58 - console.log(` Discovered: ${discovered.length} users`); 59 - console.log(` Backfilled: ${total} records`); 60 - } finally { 61 - await dispose(); 62 - } 63 - } 64 - 65 - main().catch((err) => { 66 - console.error(err); 67 - process.exit(1); 68 - });
+3
src/lexicon-types/index.ts
··· 8 8 export * as AppBlentoPage from './types/app/blento/page.js'; 9 9 export * as AppBlentoPageGetRecord from './types/app/blento/page/getRecord.js'; 10 10 export * as AppBlentoPageListRecords from './types/app/blento/page/listRecords.js'; 11 + export * as AppBlentoSection from './types/app/blento/section.js'; 12 + export * as AppBlentoSectionGetRecord from './types/app/blento/section/getRecord.js'; 13 + export * as AppBlentoSectionListRecords from './types/app/blento/section/listRecords.js';
+7 -5
src/lexicon-types/types/app/blento/card.ts
··· 6 6 /*#__PURE__*/ v.tidString(), 7 7 /*#__PURE__*/ v.object({ 8 8 $type: /*#__PURE__*/ v.literal('app.blento.card'), 9 - cardData: /*#__PURE__*/ v.unknown(), 9 + cardData: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), 10 10 cardType: /*#__PURE__*/ v.string(), 11 11 color: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 12 12 h: /*#__PURE__*/ v.integer(), 13 - mobileH: /*#__PURE__*/ v.integer(), 14 - mobileW: /*#__PURE__*/ v.integer(), 15 - mobileX: /*#__PURE__*/ v.integer(), 16 - mobileY: /*#__PURE__*/ v.integer(), 13 + mobileH: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 14 + mobileW: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 15 + mobileX: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 16 + mobileY: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 17 17 page: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 18 + rotation: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 19 + sectionId: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 18 20 updatedAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 19 21 version: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), 20 22 w: /*#__PURE__*/ v.integer(),
+8 -8
src/lexicon-types/types/app/blento/card/getRecord.ts
··· 17 17 output: { 18 18 type: 'lex', 19 19 schema: /*#__PURE__*/ v.object({ 20 - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 20 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 21 21 collection: /*#__PURE__*/ v.nsidString(), 22 22 did: /*#__PURE__*/ v.didString(), 23 23 get profiles() { 24 24 return /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(profileEntrySchema)); 25 25 }, 26 - get record() { 27 - return /*#__PURE__*/ v.optional(AppBlentoCard.mainSchema); 28 - }, 29 26 rkey: /*#__PURE__*/ v.string(), 30 27 time_us: /*#__PURE__*/ v.integer(), 31 - uri: /*#__PURE__*/ v.resourceUriString() 28 + uri: /*#__PURE__*/ v.resourceUriString(), 29 + get value() { 30 + return AppBlentoCard.mainSchema; 31 + } 32 32 }) 33 33 } 34 34 }); ··· 36 36 $type: /*#__PURE__*/ v.optional( 37 37 /*#__PURE__*/ v.literal('app.blento.card.getRecord#profileEntry') 38 38 ), 39 - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 39 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 40 40 collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 41 41 did: /*#__PURE__*/ v.didString(), 42 42 handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 43 - record: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), 44 43 rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 45 - uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()) 44 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 45 + value: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()) 46 46 }); 47 47 48 48 type main$schematype = typeof _mainSchema;
+9 -110
src/lexicon-types/types/app/blento/card/listRecords.ts
··· 13 13 * Filter by cardType 14 14 */ 15 15 cardType: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 16 - /** 17 - * Filter by color 18 - */ 19 - color: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 20 16 cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 21 17 /** 22 - * Maximum value for h 23 - */ 24 - hMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 25 - /** 26 - * Minimum value for h 27 - */ 28 - hMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 29 - /** 30 18 * @minimum 1 31 19 * @maximum 200 32 20 * @default 50 ··· 36 24 50 37 25 ), 38 26 /** 39 - * Maximum value for mobileH 40 - */ 41 - mobileHMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 42 - /** 43 - * Minimum value for mobileH 44 - */ 45 - mobileHMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 46 - /** 47 - * Maximum value for mobileW 48 - */ 49 - mobileWMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 50 - /** 51 - * Minimum value for mobileW 52 - */ 53 - mobileWMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 54 - /** 55 - * Maximum value for mobileX 56 - */ 57 - mobileXMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 58 - /** 59 - * Minimum value for mobileX 60 - */ 61 - mobileXMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 62 - /** 63 - * Maximum value for mobileY 64 - */ 65 - mobileYMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 66 - /** 67 - * Minimum value for mobileY 68 - */ 69 - mobileYMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 70 - /** 71 27 * Sort direction (default: desc for dates/numbers/counts, asc for strings) 72 28 */ 73 29 order: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string<'asc' | 'desc' | (string & {})>()), ··· 82 38 /** 83 39 * Field to sort by (default: time_us) 84 40 */ 85 - sort: /*#__PURE__*/ v.optional( 86 - /*#__PURE__*/ v.string< 87 - | 'cardType' 88 - | 'color' 89 - | 'h' 90 - | 'mobileH' 91 - | 'mobileW' 92 - | 'mobileX' 93 - | 'mobileY' 94 - | 'page' 95 - | 'updatedAt' 96 - | 'version' 97 - | 'w' 98 - | 'x' 99 - | 'y' 100 - | (string & {}) 101 - >() 102 - ), 103 - /** 104 - * Maximum value for updatedAt 105 - */ 106 - updatedAtMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 107 - /** 108 - * Minimum value for updatedAt 109 - */ 110 - updatedAtMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 111 - /** 112 - * Maximum value for version 113 - */ 114 - versionMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 115 - /** 116 - * Minimum value for version 117 - */ 118 - versionMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 119 - /** 120 - * Maximum value for w 121 - */ 122 - wMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 123 - /** 124 - * Minimum value for w 125 - */ 126 - wMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 127 - /** 128 - * Maximum value for x 129 - */ 130 - xMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 131 - /** 132 - * Minimum value for x 133 - */ 134 - xMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 135 - /** 136 - * Maximum value for y 137 - */ 138 - yMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 139 - /** 140 - * Minimum value for y 141 - */ 142 - yMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()) 41 + sort: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string<'cardType' | 'page' | (string & {})>()) 143 42 }), 144 43 output: { 145 44 type: 'lex', ··· 158 57 $type: /*#__PURE__*/ v.optional( 159 58 /*#__PURE__*/ v.literal('app.blento.card.listRecords#profileEntry') 160 59 ), 161 - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 60 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 162 61 collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 163 62 did: /*#__PURE__*/ v.didString(), 164 63 handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 165 - record: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), 166 64 rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 167 - uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()) 65 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 66 + value: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()) 168 67 }); 169 68 const _recordSchema = /*#__PURE__*/ v.object({ 170 69 $type: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal('app.blento.card.listRecords#record')), 171 - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 70 + cid: /*#__PURE__*/ v.cidString(), 172 71 collection: /*#__PURE__*/ v.nsidString(), 173 72 did: /*#__PURE__*/ v.didString(), 174 - get record() { 175 - return /*#__PURE__*/ v.optional(AppBlentoCard.mainSchema); 176 - }, 177 73 rkey: /*#__PURE__*/ v.string(), 178 74 time_us: /*#__PURE__*/ v.integer(), 179 - uri: /*#__PURE__*/ v.resourceUriString() 75 + uri: /*#__PURE__*/ v.resourceUriString(), 76 + get value() { 77 + return AppBlentoCard.mainSchema; 78 + } 180 79 }); 181 80 182 81 type main$schematype = typeof _mainSchema;
+3 -3
src/lexicon-types/types/app/blento/getProfile.ts
··· 20 20 }); 21 21 const _profileEntrySchema = /*#__PURE__*/ v.object({ 22 22 $type: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal('app.blento.getProfile#profileEntry')), 23 - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 23 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 24 24 collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 25 25 did: /*#__PURE__*/ v.didString(), 26 26 handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 27 - record: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), 28 27 rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 29 - uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()) 28 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 29 + value: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()) 30 30 }); 31 31 32 32 type main$schematype = typeof _mainSchema;
+3 -1
src/lexicon-types/types/app/blento/page.ts
··· 10 10 /** 11 11 * @accept image/* 12 12 */ 13 - icon: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.blob()), 13 + icon: /*#__PURE__*/ v.optional( 14 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [/*#__PURE__*/ v.blobAccept(['image/*'])]) 15 + ), 14 16 name: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 15 17 get preferences() { 16 18 return /*#__PURE__*/ v.optional(preferencesSchema);
+8 -8
src/lexicon-types/types/app/blento/page/getRecord.ts
··· 17 17 output: { 18 18 type: 'lex', 19 19 schema: /*#__PURE__*/ v.object({ 20 - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 20 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 21 21 collection: /*#__PURE__*/ v.nsidString(), 22 22 did: /*#__PURE__*/ v.didString(), 23 23 get profiles() { 24 24 return /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(profileEntrySchema)); 25 25 }, 26 - get record() { 27 - return /*#__PURE__*/ v.optional(AppBlentoPage.mainSchema); 28 - }, 29 26 rkey: /*#__PURE__*/ v.string(), 30 27 time_us: /*#__PURE__*/ v.integer(), 31 - uri: /*#__PURE__*/ v.resourceUriString() 28 + uri: /*#__PURE__*/ v.resourceUriString(), 29 + get value() { 30 + return AppBlentoPage.mainSchema; 31 + } 32 32 }) 33 33 } 34 34 }); ··· 36 36 $type: /*#__PURE__*/ v.optional( 37 37 /*#__PURE__*/ v.literal('app.blento.page.getRecord#profileEntry') 38 38 ), 39 - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 39 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 40 40 collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 41 41 did: /*#__PURE__*/ v.didString(), 42 42 handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 43 - record: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), 44 43 rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 45 - uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()) 44 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 45 + value: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()) 46 46 }); 47 47 48 48 type main$schematype = typeof _mainSchema;
+9 -25
src/lexicon-types/types/app/blento/page/listRecords.ts
··· 11 11 actor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.actorIdentifierString()), 12 12 cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 13 13 /** 14 - * Filter by description 15 - */ 16 - description: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 17 - /** 18 14 * @minimum 1 19 15 * @maximum 200 20 16 * @default 50 ··· 24 20 50 25 21 ), 26 22 /** 27 - * Filter by name 28 - */ 29 - name: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 30 - /** 31 - * Sort direction (default: desc for dates/numbers/counts, asc for strings) 32 - */ 33 - order: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string<'asc' | 'desc' | (string & {})>()), 34 - /** 35 23 * Include profile + identity info keyed by DID 36 24 */ 37 - profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 38 - /** 39 - * Field to sort by (default: time_us) 40 - */ 41 - sort: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string<'description' | 'name' | (string & {})>()) 25 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()) 42 26 }), 43 27 output: { 44 28 type: 'lex', ··· 57 41 $type: /*#__PURE__*/ v.optional( 58 42 /*#__PURE__*/ v.literal('app.blento.page.listRecords#profileEntry') 59 43 ), 60 - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 44 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 61 45 collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 62 46 did: /*#__PURE__*/ v.didString(), 63 47 handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 64 - record: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), 65 48 rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 66 - uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()) 49 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 50 + value: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()) 67 51 }); 68 52 const _recordSchema = /*#__PURE__*/ v.object({ 69 53 $type: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal('app.blento.page.listRecords#record')), 70 - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 54 + cid: /*#__PURE__*/ v.cidString(), 71 55 collection: /*#__PURE__*/ v.nsidString(), 72 56 did: /*#__PURE__*/ v.didString(), 73 - get record() { 74 - return /*#__PURE__*/ v.optional(AppBlentoPage.mainSchema); 75 - }, 76 57 rkey: /*#__PURE__*/ v.string(), 77 58 time_us: /*#__PURE__*/ v.integer(), 78 - uri: /*#__PURE__*/ v.resourceUriString() 59 + uri: /*#__PURE__*/ v.resourceUriString(), 60 + get value() { 61 + return AppBlentoPage.mainSchema; 62 + } 79 63 }); 80 64 81 65 type main$schematype = typeof _mainSchema;
+31
src/lexicon-types/types/app/blento/section.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.record( 6 + /*#__PURE__*/ v.tidString(), 7 + /*#__PURE__*/ v.object({ 8 + $type: /*#__PURE__*/ v.literal('app.blento.section'), 9 + index: /*#__PURE__*/ v.integer(), 10 + name: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 11 + page: /*#__PURE__*/ v.string(), 12 + sectionData: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), 13 + sectionType: /*#__PURE__*/ v.string(), 14 + updatedAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), 15 + version: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()) 16 + }) 17 + ); 18 + 19 + type main$schematype = typeof _mainSchema; 20 + 21 + export interface mainSchema extends main$schematype {} 22 + 23 + export const mainSchema = _mainSchema as mainSchema; 24 + 25 + export interface Main extends v.InferInput<typeof mainSchema> {} 26 + 27 + declare module '@atcute/lexicons/ambient' { 28 + interface Records { 29 + 'app.blento.section': mainSchema; 30 + } 31 + }
+66
src/lexicon-types/types/app/blento/section/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 AppBlentoSection from '../section.js'; 5 + 6 + const _mainSchema = /*#__PURE__*/ v.query('app.blento.section.getRecord', { 7 + params: /*#__PURE__*/ v.object({ 8 + /** 9 + * Include profile + identity info keyed by DID 10 + */ 11 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 12 + /** 13 + * AT URI of the record 14 + */ 15 + uri: /*#__PURE__*/ v.resourceUriString() 16 + }), 17 + output: { 18 + type: 'lex', 19 + schema: /*#__PURE__*/ v.object({ 20 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 21 + collection: /*#__PURE__*/ v.nsidString(), 22 + did: /*#__PURE__*/ v.didString(), 23 + get profiles() { 24 + return /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(profileEntrySchema)); 25 + }, 26 + rkey: /*#__PURE__*/ v.string(), 27 + time_us: /*#__PURE__*/ v.integer(), 28 + uri: /*#__PURE__*/ v.resourceUriString(), 29 + get value() { 30 + return AppBlentoSection.mainSchema; 31 + } 32 + }) 33 + } 34 + }); 35 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 36 + $type: /*#__PURE__*/ v.optional( 37 + /*#__PURE__*/ v.literal('app.blento.section.getRecord#profileEntry') 38 + ), 39 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 40 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 41 + did: /*#__PURE__*/ v.didString(), 42 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 43 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 44 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 45 + value: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()) 46 + }); 47 + 48 + type main$schematype = typeof _mainSchema; 49 + type profileEntry$schematype = typeof _profileEntrySchema; 50 + 51 + export interface mainSchema extends main$schematype {} 52 + export interface profileEntrySchema extends profileEntry$schematype {} 53 + 54 + export const mainSchema = _mainSchema as mainSchema; 55 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 56 + 57 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 58 + 59 + export interface $params extends v.InferInput<mainSchema['params']> {} 60 + export interface $output extends v.InferXRPCBodyInput<mainSchema['output']> {} 61 + 62 + declare module '@atcute/lexicons/ambient' { 63 + interface XRPCQueries { 64 + 'app.blento.section.getRecord': mainSchema; 65 + } 66 + }
+103
src/lexicon-types/types/app/blento/section/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 AppBlentoSection from '../section.js'; 5 + 6 + const _mainSchema = /*#__PURE__*/ v.query('app.blento.section.listRecords', { 7 + params: /*#__PURE__*/ v.object({ 8 + /** 9 + * Filter by DID or handle (triggers on-demand backfill) 10 + */ 11 + actor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.actorIdentifierString()), 12 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 13 + /** 14 + * @minimum 1 15 + * @maximum 200 16 + * @default 50 17 + */ 18 + limit: /*#__PURE__*/ v.optional( 19 + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [/*#__PURE__*/ v.integerRange(1, 200)]), 20 + 50 21 + ), 22 + /** 23 + * Sort direction (default: desc for dates/numbers/counts, asc for strings) 24 + */ 25 + order: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string<'asc' | 'desc' | (string & {})>()), 26 + /** 27 + * Filter by page 28 + */ 29 + page: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 30 + /** 31 + * Include profile + identity info keyed by DID 32 + */ 33 + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), 34 + /** 35 + * Filter by sectionType 36 + */ 37 + sectionType: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 38 + /** 39 + * Field to sort by (default: time_us) 40 + */ 41 + sort: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string<'page' | 'sectionType' | (string & {})>()) 42 + }), 43 + output: { 44 + type: 'lex', 45 + schema: /*#__PURE__*/ v.object({ 46 + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 47 + get profiles() { 48 + return /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(profileEntrySchema)); 49 + }, 50 + get records() { 51 + return /*#__PURE__*/ v.array(recordSchema); 52 + } 53 + }) 54 + } 55 + }); 56 + const _profileEntrySchema = /*#__PURE__*/ v.object({ 57 + $type: /*#__PURE__*/ v.optional( 58 + /*#__PURE__*/ v.literal('app.blento.section.listRecords#profileEntry') 59 + ), 60 + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), 61 + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), 62 + did: /*#__PURE__*/ v.didString(), 63 + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 64 + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), 65 + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), 66 + value: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()) 67 + }); 68 + const _recordSchema = /*#__PURE__*/ v.object({ 69 + $type: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal('app.blento.section.listRecords#record')), 70 + cid: /*#__PURE__*/ v.cidString(), 71 + collection: /*#__PURE__*/ v.nsidString(), 72 + did: /*#__PURE__*/ v.didString(), 73 + rkey: /*#__PURE__*/ v.string(), 74 + time_us: /*#__PURE__*/ v.integer(), 75 + uri: /*#__PURE__*/ v.resourceUriString(), 76 + get value() { 77 + return AppBlentoSection.mainSchema; 78 + } 79 + }); 80 + 81 + type main$schematype = typeof _mainSchema; 82 + type profileEntry$schematype = typeof _profileEntrySchema; 83 + type record$schematype = typeof _recordSchema; 84 + 85 + export interface mainSchema extends main$schematype {} 86 + export interface profileEntrySchema extends profileEntry$schematype {} 87 + export interface recordSchema extends record$schematype {} 88 + 89 + export const mainSchema = _mainSchema as mainSchema; 90 + export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; 91 + export const recordSchema = _recordSchema as recordSchema; 92 + 93 + export interface ProfileEntry extends v.InferInput<typeof profileEntrySchema> {} 94 + export interface Record extends v.InferInput<typeof recordSchema> {} 95 + 96 + export interface $params extends v.InferInput<mainSchema['params']> {} 97 + export interface $output extends v.InferXRPCBodyInput<mainSchema['output']> {} 98 + 99 + declare module '@atcute/lexicons/ambient' { 100 + interface XRPCQueries { 101 + 'app.blento.section.listRecords': mainSchema; 102 + } 103 + }
+11 -11
src/lib/cards/special/UpdatedBlentos/index.ts
··· 19 19 handle?: string; 20 20 collection?: string; 21 21 rkey?: string; 22 - record?: unknown; 22 + value?: unknown; 23 23 }> 24 24 ): Map<string, ProfileWithBlentoFlag> { 25 25 const map = new Map<string, ProfileWithBlentoFlag>(); ··· 35 35 existing.handle = p.handle as `${string}.${string}`; 36 36 } 37 37 38 - const record = p.record as Record<string, unknown> | undefined; 38 + const value = p.value as Record<string, unknown> | undefined; 39 39 40 - if (p.collection === 'app.bsky.actor.profile' && record) { 41 - existing.displayName ??= record.displayName as string | undefined; 42 - if (!existing.avatar && record.avatar) { 40 + if (p.collection === 'app.bsky.actor.profile' && value) { 41 + existing.displayName ??= value.displayName as string | undefined; 42 + if (!existing.avatar && value.avatar) { 43 43 const cdnUrl = getCDNImageBlobUrl({ 44 44 did: p.did, 45 - blob: record.avatar as { $type: 'blob'; ref: { $link: string } } 45 + blob: value.avatar as { $type: 'blob'; ref: { $link: string } } 46 46 }); 47 47 if (cdnUrl) existing.avatar = cdnUrl; 48 48 } 49 49 } 50 50 51 - if (p.collection === 'site.standard.publication' && record) { 51 + if (p.collection === 'site.standard.publication' && value) { 52 52 existing.hasBlento = true; 53 - existing.displayName = (record.name as string) ?? existing.displayName; 54 - existing.url = record.url as string | undefined; 55 - if (record.icon) { 53 + existing.displayName = (value.name as string) ?? existing.displayName; 54 + existing.url = value.url as string | undefined; 55 + if (value.icon) { 56 56 const cdnUrl = getCDNImageBlobUrl({ 57 57 did: p.did, 58 - blob: record.icon as { $type: 'blob'; ref: { $link: string } } 58 + blob: value.icon as { $type: 'blob'; ref: { $link: string } } 59 59 }); 60 60 if (cdnUrl) existing.avatar = cdnUrl; 61 61 }
+6 -4
src/lib/contrail/config.ts src/lib/contrail.config.ts
··· 3 3 export const config: ContrailConfig = { 4 4 namespace: 'app.blento', 5 5 collections: { 6 - 'app.blento.card': { 6 + card: { 7 + collection: 'app.blento.card', 7 8 queryable: { 8 9 page: {}, 9 10 cardType: {} 10 11 } 11 12 }, 12 - 'app.blento.page': { 13 - queryable: {} 13 + page: { 14 + collection: 'app.blento.page' 14 15 }, 15 - 'app.blento.section': { 16 + section: { 17 + collection: 'app.blento.section', 16 18 queryable: { 17 19 page: {}, 18 20 sectionType: {}
+6 -10
src/lib/contrail/index.ts
··· 1 1 import type { D1Database } from '@cloudflare/workers-types'; 2 2 import { Contrail } from '@atmo-dev/contrail'; 3 - import { createHandler } from '@atmo-dev/contrail/server'; 4 - import { Client } from '@atcute/client'; 5 - import { config } from './config'; 3 + import { createHandler, createServerClient } from '@atmo-dev/contrail/server'; 4 + import { config } from '../contrail.config'; 6 5 7 6 export const contrail = new Contrail(config); 8 7 ··· 19 18 20 19 /** 21 20 * Server-side: fully typed @atcute/client that routes through contrail in-process. 22 - * No HTTP roundtrip — calls createHandler directly. 21 + * No HTTP roundtrip — calls the handler directly with the per-request DB. 23 22 */ 24 23 export function getServerClient(db: D1Database) { 25 - return new Client({ 26 - handler: async (pathname, init) => { 27 - await ensureInit(db); 28 - const url = new URL(pathname, 'http://localhost'); 29 - return handle(new Request(url, init), db) as Promise<Response>; 30 - } 24 + return createServerClient(async (req) => { 25 + await ensureInit(db); 26 + return handle(req, db); 31 27 }); 32 28 }
+13 -13
src/lib/website/load.ts
··· 40 40 handle?: string; 41 41 collection?: string; 42 42 rkey?: string; 43 - record?: unknown; 43 + value?: unknown; 44 44 }; 45 45 46 46 /** ··· 63 63 if (p.did !== did) continue; 64 64 if (p.handle && p.handle !== 'handle.invalid') handle = p.handle; 65 65 66 - const record = p.record as Record<string, unknown> | undefined; 67 - if (p.collection === 'app.bsky.actor.profile' && record) { 68 - bskyRecord = record; 66 + const value = p.value as Record<string, unknown> | undefined; 67 + if (p.collection === 'app.bsky.actor.profile' && value) { 68 + bskyRecord = value; 69 69 } 70 - if (p.collection === 'site.standard.publication' && record) { 71 - pubRecord = record; 70 + if (p.collection === 'site.standard.publication' && value) { 71 + pubRecord = value; 72 72 } 73 - if (p.collection === 'app.nearhorizon.actor.pronouns' && record) { 74 - pronounsValue = record; 73 + if (p.collection === 'app.nearhorizon.actor.pronouns' && value) { 74 + pronounsValue = value; 75 75 } 76 76 } 77 77 ··· 128 128 params: { uri } 129 129 }); 130 130 if (!res.ok) return null; 131 - return { ...(res.data.record as object) } as Item; 131 + return { ...(res.data.value as object) } as Item; 132 132 }); 133 133 } 134 134 ··· 154 154 155 155 if (!cardRes.ok) return null; 156 156 157 - const cards = cardRes.data.records.map((r) => ({ ...(r.record as object) }) as Item); 157 + const cards = cardRes.data.records.map((r) => ({ ...(r.value as object) }) as Item); 158 158 159 159 const pages = pageRes.ok 160 160 ? pageRes.data.records 161 - .filter((r) => r.record) 161 + .filter((r) => r.value) 162 162 .map((r) => ({ 163 163 uri: r.uri, 164 164 cid: r.cid ?? '', 165 - value: r.record as Record<string, unknown> 165 + value: r.value as Record<string, unknown> 166 166 })) 167 167 : []; 168 168 169 169 const sections = 170 170 sectionRes?.ok && sectionRes.data?.records 171 171 ? (sectionRes.data.records as any[]).map( 172 - (r: any) => ({ ...(r.record as object), id: parseUri(r.uri)?.rkey }) as SectionRecord 172 + (r: any) => ({ ...(r.value as object), id: parseUri(r.uri)?.rkey }) as SectionRecord 173 173 ) 174 174 : []; 175 175
+2 -2
wrangler.jsonc
··· 59 59 "d1_databases": [ 60 60 { 61 61 "binding": "DB", 62 - "database_name": "blento", 63 - "database_id": "922639e7-6321-42c8-a4bd-cdf48428fdac", 62 + "database_name": "blento-2", 63 + "database_id": "5ccd30a3-c8d8-446a-b675-6a96a8f08263", 64 64 "remote": true 65 65 } 66 66 ],