diff --git a/docs/Contributing.md b/docs/Contributing.md index 39a0650..6cd5b6c 100644 --- a/docs/Contributing.md +++ b/docs/Contributing.md @@ -1,28 +1,37 @@ # Contributing Guidelines -For creating new cards see [here](CustomCards.md) (and check out [existing card ideas](CardIdeas.md)) +For new cards see [CustomCards](CustomCards.md) and [CardIdeas](CardIdeas.md). -## Development +## Setup -``` +```sh git clone https://github.com/flo-bit/blento.git cd blento -cp .env.example .env pnpm install -pnpm run dev +pnpm env:setup-dev # creates .env, fills COOKIE_SECRET + CLIENT_ASSERTION_KEY +``` + +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. + +```sh +pnpm dev # site falls back to PDS when D1 is empty — no backfill needed +pnpm backfill # populates local D1 via contrail; needed only for /xrpc/* paths and the UpdatedBlentos card ``` -## AI assisted development +`pnpm backfill` is resumable, takes a few minutes the first time. -You can submit PRs written with AI assistance but please make sure: +## Before opening a PR -- there's no extra unnecessary changes/unnecessary verbose code (keep it simple) -- you test everything yourself - - in light/dark mode - - with and without colored cards - - in edit mode and not in edit mode - - on mobile and desktop (note that there's two different mobile "modes", one dependent on screen size and one enabled when pointer: coarse) +- `pnpm check` — must complete with 0 errors and 0 warnings (existing baseline excepted). +- `pnpm format` — runs eslint --fix + prettier --write across the project. ## Subpages -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) +In-progress changes go on a subpage so your live profile stays clean: `/your.handle/p//edit` (any `` other than `edit` or `api`). Login redirects to the main page — navigate to the subpage URL manually. + +## AI-assisted PRs + +Welcome — please: + +- Keep diffs minimal; no unrelated cleanup or verbose code +- Test light/dark, colored cards, edit/view, desktop and both mobile modes (screen-size and `pointer: coarse`) diff --git a/lex.config.js b/lex.config.js index 7c88fce..7290070 100644 --- a/lex.config.js +++ b/lex.config.js @@ -1,11 +1,11 @@ import { defineLexiconConfig } from '@atcute/lex-cli'; export default defineLexiconConfig({ - files: ['lexicons/**/*.json', 'lexicons-pulled/**/*.json', 'lexicons-generated/**/*.json'], + files: ['lexicons/custom/**/*.json', 'lexicons/pulled/**/*.json', 'lexicons/generated/**/*.json'], outdir: 'src/lexicon-types/', imports: ['@atcute/atproto'], pull: { - outdir: 'lexicons-pulled/', + outdir: 'lexicons/pulled/', sources: [ { type: 'atproto', @@ -15,6 +15,7 @@ export default defineLexiconConfig({ 'app.blento.page', 'app.blento.section', 'app.bsky.actor.profile', + 'app.nearhorizon.actor.pronouns', 'site.standard.publication' ] } diff --git a/lexicons-generated/app/blento/card/listRecords.json b/lexicons-generated/app/blento/card/listRecords.json deleted file mode 100644 index 2e657ea..0000000 --- a/lexicons-generated/app/blento/card/listRecords.json +++ /dev/null @@ -1,236 +0,0 @@ -{ - "lexicon": 1, - "id": "app.blento.card.listRecords", - "defs": { - "main": { - "type": "query", - "description": "Query app.blento.card records with filters", - "parameters": { - "type": "params", - "properties": { - "limit": { - "type": "integer", - "minimum": 1, - "maximum": 200, - "default": 50 - }, - "cursor": { - "type": "string" - }, - "actor": { - "type": "string", - "format": "at-identifier", - "description": "Filter by DID or handle (triggers on-demand backfill)" - }, - "profiles": { - "type": "boolean", - "description": "Include profile + identity info keyed by DID" - }, - "wMin": { - "type": "string", - "description": "Minimum value for w" - }, - "wMax": { - "type": "string", - "description": "Maximum value for w" - }, - "hMin": { - "type": "string", - "description": "Minimum value for h" - }, - "hMax": { - "type": "string", - "description": "Maximum value for h" - }, - "xMin": { - "type": "string", - "description": "Minimum value for x" - }, - "xMax": { - "type": "string", - "description": "Maximum value for x" - }, - "yMin": { - "type": "string", - "description": "Minimum value for y" - }, - "yMax": { - "type": "string", - "description": "Maximum value for y" - }, - "mobileWMin": { - "type": "string", - "description": "Minimum value for mobileW" - }, - "mobileWMax": { - "type": "string", - "description": "Maximum value for mobileW" - }, - "mobileHMin": { - "type": "string", - "description": "Minimum value for mobileH" - }, - "mobileHMax": { - "type": "string", - "description": "Maximum value for mobileH" - }, - "mobileXMin": { - "type": "string", - "description": "Minimum value for mobileX" - }, - "mobileXMax": { - "type": "string", - "description": "Maximum value for mobileX" - }, - "mobileYMin": { - "type": "string", - "description": "Minimum value for mobileY" - }, - "mobileYMax": { - "type": "string", - "description": "Maximum value for mobileY" - }, - "cardType": { - "type": "string", - "description": "Filter by cardType" - }, - "color": { - "type": "string", - "description": "Filter by color" - }, - "page": { - "type": "string", - "description": "Filter by page" - }, - "updatedAtMin": { - "type": "string", - "description": "Minimum value for updatedAt" - }, - "updatedAtMax": { - "type": "string", - "description": "Maximum value for updatedAt" - }, - "versionMin": { - "type": "string", - "description": "Minimum value for version" - }, - "versionMax": { - "type": "string", - "description": "Maximum value for version" - }, - "sort": { - "type": "string", - "knownValues": [ - "w", - "h", - "x", - "y", - "mobileW", - "mobileH", - "mobileX", - "mobileY", - "cardType", - "color", - "page", - "updatedAt", - "version" - ], - "description": "Field to sort by (default: time_us)" - }, - "order": { - "type": "string", - "knownValues": ["asc", "desc"], - "description": "Sort direction (default: desc for dates/numbers/counts, asc for strings)" - } - } - }, - "output": { - "encoding": "application/json", - "schema": { - "type": "object", - "required": ["records"], - "properties": { - "records": { - "type": "array", - "items": { - "type": "ref", - "ref": "#record" - } - }, - "cursor": { - "type": "string" - }, - "profiles": { - "type": "array", - "items": { - "type": "ref", - "ref": "#profileEntry" - } - } - } - } - } - }, - "record": { - "type": "object", - "required": ["uri", "did", "collection", "rkey", "time_us"], - "properties": { - "uri": { - "type": "string", - "format": "at-uri" - }, - "did": { - "type": "string", - "format": "did" - }, - "collection": { - "type": "string", - "format": "nsid" - }, - "rkey": { - "type": "string" - }, - "cid": { - "type": "string" - }, - "record": { - "type": "ref", - "ref": "app.blento.card#main" - }, - "time_us": { - "type": "integer" - } - } - }, - "profileEntry": { - "type": "object", - "required": ["did"], - "properties": { - "did": { - "type": "string", - "format": "did" - }, - "handle": { - "type": "string" - }, - "uri": { - "type": "string", - "format": "at-uri" - }, - "collection": { - "type": "string", - "format": "nsid" - }, - "rkey": { - "type": "string" - }, - "cid": { - "type": "string" - }, - "record": { - "type": "unknown" - } - } - } - } -} diff --git a/lexicons/app/blento/card.json b/lexicons/custom/app/blento/card.json similarity index 100% rename from lexicons/app/blento/card.json rename to lexicons/custom/app/blento/card.json diff --git a/lexicons/app/blento/page.json b/lexicons/custom/app/blento/page.json similarity index 100% rename from lexicons/app/blento/page.json rename to lexicons/custom/app/blento/page.json diff --git a/lexicons/app/blento/section.json b/lexicons/custom/app/blento/section.json similarity index 100% rename from lexicons/app/blento/section.json rename to lexicons/custom/app/blento/section.json diff --git a/lexicons/generated/app/blento/authFull.json b/lexicons/generated/app/blento/authFull.json new file mode 100644 index 0000000..70cf8fc --- /dev/null +++ b/lexicons/generated/app/blento/authFull.json @@ -0,0 +1,35 @@ +{ + "lexicon": 1, + "id": "app.blento.authFull", + "defs": { + "main": { + "type": "permission-set", + "title": "app.blento", + "description": "Full access to the app.blento service", + "permissions": [ + { + "type": "permission", + "resource": "rpc", + "aud": "*", + "lxm": [ + "app.blento.card.getRecord", + "app.blento.card.listRecords", + "app.blento.getCursor", + "app.blento.getOverview", + "app.blento.getProfile", + "app.blento.notifyOfUpdate", + "app.blento.page.getRecord", + "app.blento.page.listRecords", + "app.blento.section.getRecord", + "app.blento.section.listRecords" + ] + }, + { + "type": "permission", + "resource": "repo", + "collection": ["app.blento.card", "app.blento.page", "app.blento.section"] + } + ] + } + } +} diff --git a/lexicons-generated/app/blento/card/getRecord.json b/lexicons/generated/app/blento/card/getRecord.json similarity index 89% rename from lexicons-generated/app/blento/card/getRecord.json rename to lexicons/generated/app/blento/card/getRecord.json index 2e5a76c..df2ff67 100644 --- a/lexicons-generated/app/blento/card/getRecord.json +++ b/lexicons/generated/app/blento/card/getRecord.json @@ -24,12 +24,20 @@ "encoding": "application/json", "schema": { "type": "object", - "required": ["uri", "did", "collection", "rkey", "time_us"], + "required": ["uri", "value", "did", "collection", "rkey", "time_us"], "properties": { "uri": { "type": "string", "format": "at-uri" }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "ref", + "ref": "app.blento.card#main" + }, "did": { "type": "string", "format": "did" @@ -41,13 +49,6 @@ "rkey": { "type": "string" }, - "cid": { - "type": "string" - }, - "record": { - "type": "ref", - "ref": "app.blento.card#main" - }, "time_us": { "type": "integer" }, @@ -77,18 +78,19 @@ "type": "string", "format": "at-uri" }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "unknown" + }, "collection": { "type": "string", "format": "nsid" }, "rkey": { "type": "string" - }, - "cid": { - "type": "string" - }, - "record": { - "type": "unknown" } } } diff --git a/lexicons-generated/app/blento/page/listRecords.json b/lexicons/generated/app/blento/card/listRecords.json similarity index 81% rename from lexicons-generated/app/blento/page/listRecords.json rename to lexicons/generated/app/blento/card/listRecords.json index 9127056..ae25dd6 100644 --- a/lexicons-generated/app/blento/page/listRecords.json +++ b/lexicons/generated/app/blento/card/listRecords.json @@ -1,10 +1,10 @@ { "lexicon": 1, - "id": "app.blento.page.listRecords", + "id": "app.blento.card.listRecords", "defs": { "main": { "type": "query", - "description": "Query app.blento.page records with filters", + "description": "Query app.blento.card records with filters", "parameters": { "type": "params", "properties": { @@ -26,17 +26,17 @@ "type": "boolean", "description": "Include profile + identity info keyed by DID" }, - "name": { + "page": { "type": "string", - "description": "Filter by name" + "description": "Filter by page" }, - "description": { + "cardType": { "type": "string", - "description": "Filter by description" + "description": "Filter by cardType" }, "sort": { "type": "string", - "knownValues": ["name", "description"], + "knownValues": ["page", "cardType"], "description": "Field to sort by (default: time_us)" }, "order": { @@ -75,12 +75,20 @@ }, "record": { "type": "object", - "required": ["uri", "did", "collection", "rkey", "time_us"], + "required": ["uri", "cid", "value", "did", "collection", "rkey", "time_us"], "properties": { "uri": { "type": "string", "format": "at-uri" }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "ref", + "ref": "app.blento.card#main" + }, "did": { "type": "string", "format": "did" @@ -92,13 +100,6 @@ "rkey": { "type": "string" }, - "cid": { - "type": "string" - }, - "record": { - "type": "ref", - "ref": "app.blento.page#main" - }, "time_us": { "type": "integer" } @@ -119,18 +120,19 @@ "type": "string", "format": "at-uri" }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "unknown" + }, "collection": { "type": "string", "format": "nsid" }, "rkey": { "type": "string" - }, - "cid": { - "type": "string" - }, - "record": { - "type": "unknown" } } } diff --git a/lexicons-generated/app/blento/getCursor.json b/lexicons/generated/app/blento/getCursor.json similarity index 100% rename from lexicons-generated/app/blento/getCursor.json rename to lexicons/generated/app/blento/getCursor.json diff --git a/lexicons-generated/app/blento/getOverview.json b/lexicons/generated/app/blento/getOverview.json similarity index 100% rename from lexicons-generated/app/blento/getOverview.json rename to lexicons/generated/app/blento/getOverview.json diff --git a/lexicons-generated/app/blento/getProfile.json b/lexicons/generated/app/blento/getProfile.json similarity index 94% rename from lexicons-generated/app/blento/getProfile.json rename to lexicons/generated/app/blento/getProfile.json index 22e735d..482aed6 100644 --- a/lexicons-generated/app/blento/getProfile.json +++ b/lexicons/generated/app/blento/getProfile.json @@ -48,18 +48,19 @@ "type": "string", "format": "at-uri" }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "unknown" + }, "collection": { "type": "string", "format": "nsid" }, "rkey": { "type": "string" - }, - "cid": { - "type": "string" - }, - "record": { - "type": "unknown" } } } diff --git a/lexicons-generated/app/blento/notifyOfUpdate.json b/lexicons/generated/app/blento/notifyOfUpdate.json similarity index 100% rename from lexicons-generated/app/blento/notifyOfUpdate.json rename to lexicons/generated/app/blento/notifyOfUpdate.json diff --git a/lexicons-generated/app/blento/page/getRecord.json b/lexicons/generated/app/blento/page/getRecord.json similarity index 89% rename from lexicons-generated/app/blento/page/getRecord.json rename to lexicons/generated/app/blento/page/getRecord.json index e6b996c..e57d73b 100644 --- a/lexicons-generated/app/blento/page/getRecord.json +++ b/lexicons/generated/app/blento/page/getRecord.json @@ -24,12 +24,20 @@ "encoding": "application/json", "schema": { "type": "object", - "required": ["uri", "did", "collection", "rkey", "time_us"], + "required": ["uri", "value", "did", "collection", "rkey", "time_us"], "properties": { "uri": { "type": "string", "format": "at-uri" }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "ref", + "ref": "app.blento.page#main" + }, "did": { "type": "string", "format": "did" @@ -41,13 +49,6 @@ "rkey": { "type": "string" }, - "cid": { - "type": "string" - }, - "record": { - "type": "ref", - "ref": "app.blento.page#main" - }, "time_us": { "type": "integer" }, @@ -77,18 +78,19 @@ "type": "string", "format": "at-uri" }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "unknown" + }, "collection": { "type": "string", "format": "nsid" }, "rkey": { "type": "string" - }, - "cid": { - "type": "string" - }, - "record": { - "type": "unknown" } } } diff --git a/lexicons/generated/app/blento/page/listRecords.json b/lexicons/generated/app/blento/page/listRecords.json new file mode 100644 index 0000000..c912c31 --- /dev/null +++ b/lexicons/generated/app/blento/page/listRecords.json @@ -0,0 +1,122 @@ +{ + "lexicon": 1, + "id": "app.blento.page.listRecords", + "defs": { + "main": { + "type": "query", + "description": "Query app.blento.page records with filters", + "parameters": { + "type": "params", + "properties": { + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 200, + "default": 50 + }, + "cursor": { + "type": "string" + }, + "actor": { + "type": "string", + "format": "at-identifier", + "description": "Filter by DID or handle (triggers on-demand backfill)" + }, + "profiles": { + "type": "boolean", + "description": "Include profile + identity info keyed by DID" + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["records"], + "properties": { + "records": { + "type": "array", + "items": { + "type": "ref", + "ref": "#record" + } + }, + "cursor": { + "type": "string" + }, + "profiles": { + "type": "array", + "items": { + "type": "ref", + "ref": "#profileEntry" + } + } + } + } + } + }, + "record": { + "type": "object", + "required": ["uri", "cid", "value", "did", "collection", "rkey", "time_us"], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "ref", + "ref": "app.blento.page#main" + }, + "did": { + "type": "string", + "format": "did" + }, + "collection": { + "type": "string", + "format": "nsid" + }, + "rkey": { + "type": "string" + }, + "time_us": { + "type": "integer" + } + } + }, + "profileEntry": { + "type": "object", + "required": ["did"], + "properties": { + "did": { + "type": "string", + "format": "did" + }, + "handle": { + "type": "string" + }, + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "unknown" + }, + "collection": { + "type": "string", + "format": "nsid" + }, + "rkey": { + "type": "string" + } + } + } + } +} diff --git a/lexicons/generated/app/blento/section/getRecord.json b/lexicons/generated/app/blento/section/getRecord.json new file mode 100644 index 0000000..2d5a172 --- /dev/null +++ b/lexicons/generated/app/blento/section/getRecord.json @@ -0,0 +1,98 @@ +{ + "lexicon": 1, + "id": "app.blento.section.getRecord", + "defs": { + "main": { + "type": "query", + "description": "Get a single app.blento.section record by AT URI", + "parameters": { + "type": "params", + "required": ["uri"], + "properties": { + "uri": { + "type": "string", + "format": "at-uri", + "description": "AT URI of the record" + }, + "profiles": { + "type": "boolean", + "description": "Include profile + identity info keyed by DID" + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["uri", "value", "did", "collection", "rkey", "time_us"], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "ref", + "ref": "app.blento.section#main" + }, + "did": { + "type": "string", + "format": "did" + }, + "collection": { + "type": "string", + "format": "nsid" + }, + "rkey": { + "type": "string" + }, + "time_us": { + "type": "integer" + }, + "profiles": { + "type": "array", + "items": { + "type": "ref", + "ref": "#profileEntry" + } + } + } + } + } + }, + "profileEntry": { + "type": "object", + "required": ["did"], + "properties": { + "did": { + "type": "string", + "format": "did" + }, + "handle": { + "type": "string" + }, + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "unknown" + }, + "collection": { + "type": "string", + "format": "nsid" + }, + "rkey": { + "type": "string" + } + } + } + } +} diff --git a/lexicons/generated/app/blento/section/listRecords.json b/lexicons/generated/app/blento/section/listRecords.json new file mode 100644 index 0000000..e384c7d --- /dev/null +++ b/lexicons/generated/app/blento/section/listRecords.json @@ -0,0 +1,140 @@ +{ + "lexicon": 1, + "id": "app.blento.section.listRecords", + "defs": { + "main": { + "type": "query", + "description": "Query app.blento.section records with filters", + "parameters": { + "type": "params", + "properties": { + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 200, + "default": 50 + }, + "cursor": { + "type": "string" + }, + "actor": { + "type": "string", + "format": "at-identifier", + "description": "Filter by DID or handle (triggers on-demand backfill)" + }, + "profiles": { + "type": "boolean", + "description": "Include profile + identity info keyed by DID" + }, + "page": { + "type": "string", + "description": "Filter by page" + }, + "sectionType": { + "type": "string", + "description": "Filter by sectionType" + }, + "sort": { + "type": "string", + "knownValues": ["page", "sectionType"], + "description": "Field to sort by (default: time_us)" + }, + "order": { + "type": "string", + "knownValues": ["asc", "desc"], + "description": "Sort direction (default: desc for dates/numbers/counts, asc for strings)" + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": ["records"], + "properties": { + "records": { + "type": "array", + "items": { + "type": "ref", + "ref": "#record" + } + }, + "cursor": { + "type": "string" + }, + "profiles": { + "type": "array", + "items": { + "type": "ref", + "ref": "#profileEntry" + } + } + } + } + } + }, + "record": { + "type": "object", + "required": ["uri", "cid", "value", "did", "collection", "rkey", "time_us"], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "ref", + "ref": "app.blento.section#main" + }, + "did": { + "type": "string", + "format": "did" + }, + "collection": { + "type": "string", + "format": "nsid" + }, + "rkey": { + "type": "string" + }, + "time_us": { + "type": "integer" + } + } + }, + "profileEntry": { + "type": "object", + "required": ["did"], + "properties": { + "did": { + "type": "string", + "format": "did" + }, + "handle": { + "type": "string" + }, + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "unknown" + }, + "collection": { + "type": "string", + "format": "nsid" + }, + "rkey": { + "type": "string" + } + } + } + } +} diff --git a/lexicons/generated/index.ts b/lexicons/generated/index.ts new file mode 100644 index 0000000..b951507 --- /dev/null +++ b/lexicons/generated/index.ts @@ -0,0 +1,20 @@ +// Auto-generated by @atmo-dev/contrail-lexicons. Do not edit. +// Pass `lexicons` to `createWorker(config, { lexicons })` to expose them +// at `/xrpc/.lexicons` for consumer apps to typegen against. + +import _0 from '../custom/app/blento/card.json'; +import _1 from '../custom/app/blento/page.json'; +import _2 from '../custom/app/blento/section.json'; +import _3 from './app/blento/authFull.json'; +import _4 from './app/blento/card/getRecord.json'; +import _5 from './app/blento/card/listRecords.json'; +import _6 from './app/blento/getCursor.json'; +import _7 from './app/blento/getOverview.json'; +import _8 from './app/blento/getProfile.json'; +import _9 from './app/blento/notifyOfUpdate.json'; +import _10 from './app/blento/page/getRecord.json'; +import _11 from './app/blento/page/listRecords.json'; +import _12 from './app/blento/section/getRecord.json'; +import _13 from './app/blento/section/listRecords.json'; + +export const lexicons: object[] = [_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13]; diff --git a/package.json b/package.json index 7384306..2768012 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "vite dev", - "build": "NODE_OPTIONS='--max-old-space-size=4096' vite build && tsx scripts/append-scheduled.ts", + "build": "NODE_OPTIONS='--max-old-space-size=4096' vite build && contrail append-scheduled", "preview": "pnpm run build && wrangler dev", "prepare": "svelte-kit sync || echo ''", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", @@ -19,13 +19,14 @@ "env:generate-secret": "npx tsx src/lib/atproto/scripts/generate-secret.ts", "env:setup-dev": "npx tsx src/lib/atproto/scripts/setup-dev.ts", "tunnel": "npx tsx src/lib/atproto/scripts/tunnel.ts", - "generate": "npx tsx scripts/generate.ts", - "sync": "npx tsx scripts/sync.ts", - "sync:remote": "npx tsx scripts/sync.ts --remote" + "generate": "contrail-lex generate", + "backfill": "contrail backfill", + "backfill:remote": "contrail backfill --remote" }, "devDependencies": { - "@atcute/lex-cli": "^2.6.1", + "@atcute/lex-cli": "^2.8.1", "@atcute/lexicon-doc": "^2.1.2", + "@atmo-dev/contrail-lexicons": "^0.4.5", "@eslint/compat": "^2.0.3", "@eslint/js": "^10.0.1", "@sveltejs/adapter-cloudflare": "^7.2.8", @@ -59,12 +60,12 @@ "@atcute/bluesky-richtext-segmenter": "^3.0.0", "@atcute/client": "^4.2.1", "@atcute/identity-resolver": "^1.2.2", - "@atcute/lexicons": "^1.2.9", + "@atcute/lexicons": "^1.3.0", "@atcute/oauth-browser-client": "^3.0.0", "@atcute/oauth-node-client": "^1.1.0", "@atcute/standard-site": "^1.0.1", "@atcute/tid": "^1.1.2", - "@atmo-dev/contrail": "^0.0.8", + "@atmo-dev/contrail": "^0.5.0", "@cloudflare/workers-types": "^4.20260313.1", "@ethercorps/sveltekit-og": "^4.2.1", "@floating-ui/dom": "^1.7.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 41aa821..98bd27d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,13 +25,13 @@ importers: version: 4.2.1 '@atcute/identity-resolver': specifier: ^1.2.2 - version: 1.2.2(@atcute/identity@1.1.3) + version: 1.2.2(@atcute/identity@1.1.4) '@atcute/lexicons': - specifier: ^1.2.9 - version: 1.2.9 + specifier: ^1.3.0 + version: 1.3.0 '@atcute/oauth-browser-client': specifier: ^3.0.0 - version: 3.0.0(@atcute/identity@1.1.3) + version: 3.0.0(@atcute/identity@1.1.4) '@atcute/oauth-node-client': specifier: ^1.1.0 version: 1.1.0 @@ -42,8 +42,8 @@ importers: specifier: ^1.1.2 version: 1.1.2 '@atmo-dev/contrail': - specifier: ^0.0.8 - version: 0.0.8(@atcute/identity@1.1.3)(react@19.2.4) + specifier: ^0.5.0 + version: 0.5.0(react@19.2.4)(wrangler@4.73.0(@cloudflare/workers-types@4.20260313.1)) '@cloudflare/workers-types': specifier: ^4.20260313.1 version: 4.20260313.1 @@ -208,11 +208,14 @@ importers: version: 4.73.0(@cloudflare/workers-types@4.20260313.1) devDependencies: '@atcute/lex-cli': - specifier: ^2.6.1 - version: 2.8.0 + specifier: ^2.8.1 + version: 2.8.1 '@atcute/lexicon-doc': specifier: ^2.1.2 version: 2.2.0 + '@atmo-dev/contrail-lexicons': + specifier: ^0.4.5 + version: 0.4.5(react@19.2.4)(wrangler@4.73.0(@cloudflare/workers-types@4.20260313.1)) '@eslint/compat': specifier: ^2.0.3 version: 2.0.3(eslint@10.0.3(jiti@2.6.1)) @@ -332,8 +335,8 @@ packages: '@atcute/jetstream@1.1.2': resolution: {integrity: sha512-u6p/h2xppp7LE6W/9xErAJ6frfN60s8adZuCKtfAaaBBiiYbb1CfpzN8Uc+2qtJZNorqGvuuDb5572Jmh7yHBQ==} - '@atcute/lex-cli@2.8.0': - resolution: {integrity: sha512-eNPO0hhGhrCXQ7vEgVhqAaSHSsT3me1Jcc99rHaPgne1xP7fBfprf+E02M6BUqwrBz95YpnyuLPmVKNEk1jLwA==} + '@atcute/lex-cli@2.8.1': + resolution: {integrity: sha512-ab+NpnwvW7gXMjZYYqK3zqhcAPOEYnAaNAXHp28gmL3M33zHgz4IjNQpDnRAjTitCpaJ3KoA3KXlnXNjsV6USg==} hasBin: true '@atcute/lexicon-doc@2.2.0': @@ -345,9 +348,6 @@ packages: '@atcute/identity': ^1.1.0 '@atcute/identity-resolver': ^1.1.3 - '@atcute/lexicons@1.2.9': - resolution: {integrity: sha512-/RRHm2Cw9o8Mcsrq0eo8fjS9okKYLGfuFwrQ0YoP/6sdSDsXshaTLJsvLlcUcaDaSJ1YFOuHIo3zr2Om2F/16g==} - '@atcute/lexicons@1.3.0': resolution: {integrity: sha512-Eq5y+9onnCXNVUlNiMf31beSXHKqptB7lUo/68YbhlmxdaR7ooywHmahya9goP5AsmlYEA1z+dRPXIDAa9O7cg==} @@ -393,22 +393,33 @@ packages: '@atcute/util-fetch@1.0.5': resolution: {integrity: sha512-qjHj01BGxjSjIFdPiAjSARnodJIIyKxnCMMEcXMESo9TAyND6XZQqrie5fia+LlYWVXdpsTds8uFQwc9jdKTig==} - '@atcute/util-text@1.1.1': - resolution: {integrity: sha512-JH0SxzUQJAmbOBTYyhxQbkkI6M33YpjlVLEcbP5GYt43xgFArzV0FJVmEpvIj0kjsmphHB45b6IitdvxPdec9w==} - '@atcute/util-text@1.2.0': resolution: {integrity: sha512-b8WSh+Z7K601eUFFmTFj8QPKDO8Ic0VDDj63sdKzpkm+ySQKsYT5nXekViGqFVKbyKj1V5FyvZvgXad6/aI4QQ==} + '@atcute/util-text@1.3.1': + resolution: {integrity: sha512-MRgJXkx67znuBXuoAYCJkBZyd3OApL7zZlNf5kXhuoCXcdiu1nblRDycYTADSkym4epBSQWxh26kmI9sewaq6A==} + '@atcute/varint@2.0.0': resolution: {integrity: sha512-CEY/oVK/nVpL4e5y3sdenLETDL6/Xu5xsE/0TupK+f0Yv8jcD60t2gD8SHROWSvUwYLdkjczLCSA7YrtnjCzWw==} - '@atmo-dev/contrail@0.0.8': - resolution: {integrity: sha512-sXtdd3Z8VNVoSinrX3ww978ctxtHBl0bX5tP51XOY0IWKJ4xl9zqkPOIIBMzbVE3IyU2Vq2B9Whi3VAhyd2Qdg==} + '@atcute/xrpc-server@0.1.12': + resolution: {integrity: sha512-70KIerQlljp5+s6t0u6YNN9klEboQUZa2hhoi/hmXIO1cIKEORettTMctnyjfcCJaSfAuj42dxPu51GTZBlm8w==} + + '@atmo-dev/contrail-lexicons@0.4.5': + resolution: {integrity: sha512-gd3k8tFbcokjsgTpC9gKitYgImI6M+iipFCUYy60nCHomDCYMxkgrvhjJlii61ol9LEbcVDSPdMKKcKJGwm3gA==} + hasBin: true + + '@atmo-dev/contrail@0.5.0': + resolution: {integrity: sha512-PeeA3Q6NDwaho2dgaW4QANPRvhjKBml17MMxjcq6DwQG7p0MTUqoa2RM1G7DpA8CAIPrRXTVcosb6ybqshlWtQ==} + hasBin: true peerDependencies: pg: ^8.0.0 + wrangler: ^4.0.0 peerDependenciesMeta: pg: optional: true + wrangler: + optional: true '@badrap/valita@0.4.6': resolution: {integrity: sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg==} @@ -986,12 +997,12 @@ packages: peerDependencies: svelte: ^4 || ^5 - '@optique/core@0.10.7': - resolution: {integrity: sha512-FwSX8ILFqzcCqZi6Xetsa4flJp/yyqFG4d4eFD98BtqdzxxuylzdrKvsXj/ow8mcoVjYkTuaIkqHSBxonqMcQg==} + '@optique/core@1.0.2': + resolution: {integrity: sha512-znsqMmjAdeOgSJzdJlpZpgAscojwQmeQYXzYnuEKllz5VCj6WyEkdzU4QuvJQtWQY3ve2taXwudEBRur0VHBOQ==} engines: {bun: '>=1.2.0', deno: '>=2.3.0', node: '>=20.0.0'} - '@optique/run@0.10.7': - resolution: {integrity: sha512-1CVdH8uyptj1nFGS2MLacSmZceRClez4LD/G/Gm38wrAVnJq6I+9Fvyh2bVHErsZLQzR0a12CYMUWIgDKY3X1w==} + '@optique/run@1.0.2': + resolution: {integrity: sha512-0Wc+zC8SLGV8zXQX+pk+o0c6wE/ddx/36CHZ0toTh5lApsjruUuGhqbxvljerAAG5un1xQbOLxzksBVC6UPgSg==} engines: {bun: '>=1.2.0', deno: '>=2.3.0', node: '>=20.0.0'} '@oxc-project/runtime@0.115.0': @@ -1741,6 +1752,10 @@ packages: resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} engines: {node: 18 || 20 || >=22} + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} + camelize@1.0.1: resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} @@ -2446,6 +2461,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@5.1.11: + resolution: {integrity: sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg==} + engines: {node: ^18 || >=20} + hasBin: true + nanoid@5.1.6: resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==} engines: {node: ^18 || >=20} @@ -3353,7 +3373,7 @@ snapshots: '@atcute/atproto@3.1.10': dependencies: - '@atcute/lexicons': 1.2.9 + '@atcute/lexicons': 1.3.0 '@atcute/bluesky-richtext-parser@2.1.1': {} @@ -3362,7 +3382,7 @@ snapshots: '@atcute/bluesky@3.3.0': dependencies: '@atcute/atproto': 3.1.10 - '@atcute/lexicons': 1.2.9 + '@atcute/lexicons': 1.3.0 '@atcute/car@5.1.1': dependencies: @@ -3374,7 +3394,7 @@ snapshots: '@atcute/cbor@2.3.2': dependencies: '@atcute/cid': 2.4.1 - '@atcute/multibase': 1.1.8 + '@atcute/multibase': 1.2.0 '@atcute/uint8array': 1.1.1 '@atcute/cid@2.4.1': @@ -3385,7 +3405,7 @@ snapshots: '@atcute/client@4.2.1': dependencies: '@atcute/identity': 1.1.3 - '@atcute/lexicons': 1.2.9 + '@atcute/lexicons': 1.3.0 '@atcute/crypto@2.4.1': dependencies: @@ -3396,20 +3416,20 @@ snapshots: '@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.3)': dependencies: '@atcute/identity': 1.1.3 - '@atcute/lexicons': 1.2.9 + '@atcute/lexicons': 1.3.0 '@atcute/util-fetch': 1.0.5 '@badrap/valita': 0.4.6 '@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.4)': dependencies: '@atcute/identity': 1.1.4 - '@atcute/lexicons': 1.2.9 + '@atcute/lexicons': 1.3.0 '@atcute/util-fetch': 1.0.5 '@badrap/valita': 0.4.6 '@atcute/identity@1.1.3': dependencies: - '@atcute/lexicons': 1.2.9 + '@atcute/lexicons': 1.3.0 '@badrap/valita': 0.4.6 '@atcute/identity@1.1.4': @@ -3419,7 +3439,7 @@ snapshots: '@atcute/jetstream@1.1.2(react@19.2.4)': dependencies: - '@atcute/lexicons': 1.2.9 + '@atcute/lexicons': 1.3.0 '@badrap/valita': 0.4.6 '@mary-ext/event-iterator': 1.0.0 '@mary-ext/simple-event-emitter': 1.0.1 @@ -3429,16 +3449,16 @@ snapshots: transitivePeerDependencies: - react - '@atcute/lex-cli@2.8.0': + '@atcute/lex-cli@2.8.1': dependencies: '@atcute/identity': 1.1.4 '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.4) '@atcute/lexicon-doc': 2.2.0 - '@atcute/lexicon-resolver': 0.1.6(@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.3))(@atcute/identity@1.1.4) + '@atcute/lexicon-resolver': 0.1.6(@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.4))(@atcute/identity@1.1.4) '@atcute/lexicons': 1.3.0 '@badrap/valita': 0.4.6 - '@optique/core': 0.10.7 - '@optique/run': 0.10.7 + '@optique/core': 1.0.2 + '@optique/run': 1.0.2 picocolors: 1.1.1 prettier: 3.8.3 @@ -3450,28 +3470,21 @@ snapshots: '@atcute/util-text': 1.2.0 '@badrap/valita': 0.4.6 - '@atcute/lexicon-resolver@0.1.6(@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.3))(@atcute/identity@1.1.4)': + '@atcute/lexicon-resolver@0.1.6(@atcute/identity-resolver@1.2.2(@atcute/identity@1.1.4))(@atcute/identity@1.1.4)': dependencies: '@atcute/crypto': 2.4.1 '@atcute/identity': 1.1.4 - '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.3) + '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.4) '@atcute/lexicon-doc': 2.2.0 '@atcute/lexicons': 1.3.0 '@atcute/repo': 0.1.4 '@atcute/util-fetch': 1.0.5 '@badrap/valita': 0.4.6 - '@atcute/lexicons@1.2.9': - dependencies: - '@atcute/uint8array': 1.1.1 - '@atcute/util-text': 1.1.1 - '@standard-schema/spec': 1.1.0 - esm-env: 1.2.2 - '@atcute/lexicons@1.3.0': dependencies: '@atcute/uint8array': 1.1.1 - '@atcute/util-text': 1.2.0 + '@atcute/util-text': 1.3.1 '@standard-schema/spec': 1.1.0 esm-env: 1.2.2 @@ -3489,11 +3502,11 @@ snapshots: dependencies: '@atcute/uint8array': 1.1.1 - '@atcute/oauth-browser-client@3.0.0(@atcute/identity@1.1.3)': + '@atcute/oauth-browser-client@3.0.0(@atcute/identity@1.1.4)': dependencies: '@atcute/client': 4.2.1 - '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.3) - '@atcute/lexicons': 1.2.9 + '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.4) + '@atcute/lexicons': 1.3.0 '@atcute/multibase': 1.1.8 '@atcute/oauth-crypto': 0.1.0 '@atcute/oauth-types': 0.1.1 @@ -3517,7 +3530,7 @@ snapshots: '@atcute/client': 4.2.1 '@atcute/identity': 1.1.3 '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.3) - '@atcute/lexicons': 1.2.9 + '@atcute/lexicons': 1.3.0 '@atcute/oauth-crypto': 0.1.0 '@atcute/oauth-keyset': 0.1.0 '@atcute/oauth-types': 0.1.1 @@ -3528,7 +3541,7 @@ snapshots: '@atcute/oauth-types@0.1.1': dependencies: '@atcute/identity': 1.1.3 - '@atcute/lexicons': 1.2.9 + '@atcute/lexicons': 1.3.0 '@atcute/oauth-keyset': 0.1.0 '@badrap/valita': 0.4.6 @@ -3545,7 +3558,7 @@ snapshots: '@atcute/standard-site@1.0.1': dependencies: '@atcute/atproto': 3.1.10 - '@atcute/lexicons': 1.2.9 + '@atcute/lexicons': 1.3.0 '@atcute/tid@1.1.2': dependencies: @@ -3559,26 +3572,54 @@ snapshots: dependencies: '@badrap/valita': 0.4.6 - '@atcute/util-text@1.1.1': + '@atcute/util-text@1.2.0': dependencies: unicode-segmenter: 0.14.5 - '@atcute/util-text@1.2.0': + '@atcute/util-text@1.3.1': dependencies: unicode-segmenter: 0.14.5 '@atcute/varint@2.0.0': {} - '@atmo-dev/contrail@0.0.8(@atcute/identity@1.1.3)(react@19.2.4)': + '@atcute/xrpc-server@0.1.12': + dependencies: + '@atcute/cbor': 2.3.2 + '@atcute/crypto': 2.4.1 + '@atcute/identity': 1.1.4 + '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.4) + '@atcute/lexicons': 1.3.0 + '@atcute/multibase': 1.2.0 + '@atcute/uint8array': 1.1.1 + '@badrap/valita': 0.4.6 + nanoid: 5.1.11 + + '@atmo-dev/contrail-lexicons@0.4.5(react@19.2.4)(wrangler@4.73.0(@cloudflare/workers-types@4.20260313.1))': + dependencies: + '@atcute/lex-cli': 2.8.1 + '@atmo-dev/contrail': 0.5.0(react@19.2.4)(wrangler@4.73.0(@cloudflare/workers-types@4.20260313.1)) + transitivePeerDependencies: + - pg + - react + - wrangler + + '@atmo-dev/contrail@0.5.0(react@19.2.4)(wrangler@4.73.0(@cloudflare/workers-types@4.20260313.1))': dependencies: '@atcute/atproto': 3.1.10 + '@atcute/cbor': 2.3.2 + '@atcute/cid': 2.4.1 '@atcute/client': 4.2.1 - '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.3) + '@atcute/identity': 1.1.4 + '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.4) '@atcute/jetstream': 1.1.2(react@19.2.4) - '@atcute/lexicons': 1.2.9 + '@atcute/lexicons': 1.3.0 + '@atcute/xrpc-server': 0.1.12 + cac: 7.0.0 hono: 4.12.14 + jiti: 2.6.1 + optionalDependencies: + wrangler: 4.73.0(@cloudflare/workers-types@4.20260313.1) transitivePeerDependencies: - - '@atcute/identity' - react '@badrap/valita@0.4.6': {} @@ -4143,11 +4184,11 @@ snapshots: number-flow: 0.6.0 svelte: 5.53.11 - '@optique/core@0.10.7': {} + '@optique/core@1.0.2': {} - '@optique/run@0.10.7': + '@optique/run@1.0.2': dependencies: - '@optique/core': 0.10.7 + '@optique/core': 1.0.2 '@oxc-project/runtime@0.115.0': {} @@ -4879,6 +4920,8 @@ snapshots: dependencies: balanced-match: 4.0.4 + cac@7.0.0: {} + camelize@1.0.1: {} camera-controls@3.1.2(three@0.183.2): @@ -5558,6 +5601,8 @@ snapshots: nanoid@3.3.11: {} + nanoid@5.1.11: {} + nanoid@5.1.6: {} natural-compare@1.4.0: {} diff --git a/scripts/append-scheduled.ts b/scripts/append-scheduled.ts deleted file mode 100644 index c9bd1cd..0000000 --- a/scripts/append-scheduled.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Post-build script: appends a `scheduled` handler to the SvelteKit worker output. - * - * SvelteKit's adapter-cloudflare doesn't support the `scheduled` export natively - * (see https://github.com/sveltejs/kit/issues/4841). This script patches the - * generated _worker.js to add one that self-calls the /api/cron endpoint. - */ -import { readFileSync, writeFileSync } from 'fs'; -import { join, dirname } from 'path'; -import { fileURLToPath } from 'url'; - -const root = join(dirname(fileURLToPath(import.meta.url)), '..'); -const workerPath = join(root, '.svelte-kit', 'cloudflare', '_worker.js'); - -let code = readFileSync(workerPath, 'utf-8'); - -code += ` -// --- Appended by scripts/append-scheduled.ts --- -worker_default.scheduled = async function (event, env, ctx) { - const req = new Request('http://localhost/api/cron', { - method: 'POST', - headers: { 'X-Cron-Secret': env.CRON_SECRET || '' } - }); - ctx.waitUntil(this.fetch(req, env, ctx)); -}; -`; - -writeFileSync(workerPath, code); -console.log('Appended scheduled handler to _worker.js'); diff --git a/scripts/generate.ts b/scripts/generate.ts deleted file mode 100644 index 4a893d5..0000000 --- a/scripts/generate.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { join, dirname } from 'path'; -import { fileURLToPath } from 'url'; -import { config } from '../src/lib/contrail/config'; -import { generateLexicons } from '@atmo-dev/contrail/generate'; - -const ROOT_DIR = join(dirname(fileURLToPath(import.meta.url)), '..'); - -generateLexicons({ - config, - rootDir: ROOT_DIR, - lexiconDir: join(ROOT_DIR, 'lexicons'), - outputDir: join(ROOT_DIR, 'lexicons-generated'), - writeRuntimeFiles: true -}); diff --git a/scripts/sync.ts b/scripts/sync.ts deleted file mode 100644 index c946f86..0000000 --- a/scripts/sync.ts +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Discover users from relays and backfill their records from PDS. - * - * Usage: - * pnpm sync # local D1 - * pnpm sync:remote # prod D1 - */ -import { Contrail } from '@atmo-dev/contrail'; -import { config } from '../src/lib/contrail/config'; -import { getPlatformProxy } from 'wrangler'; - -function elapsed(start: number): string { - const ms = Date.now() - start; - if (ms < 1000) return `${ms}ms`; - if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`; - const mins = Math.floor(ms / 60_000); - const secs = ((ms % 60_000) / 1000).toFixed(0); - return `${mins}m ${secs}s`; -} - -async function main() { - const remote = process.argv.includes('--remote'); - const syncStart = Date.now(); - - console.log(`=== Sync (${remote ? 'remote/prod' : 'local'} D1) ===\n`); - - const { env, dispose } = await getPlatformProxy<{ DB: D1Database }>({ - environment: remote ? 'production' : undefined - }); - - const contrail = new Contrail({ ...config, db: env.DB }); - - try { - await contrail.init(); - - console.log('--- Discovery ---'); - const discoveryStart = Date.now(); - const discovered = await contrail.discover(); - console.log(` Done: ${discovered.length} users in ${elapsed(discoveryStart)}\n`); - - console.log('--- Backfill ---'); - const backfillStart = Date.now(); - const total = await contrail.backfill({ - concurrency: 100, - onProgress: ({ records, usersComplete, usersTotal, usersFailed }) => { - const secs = (Date.now() - backfillStart) / 1000; - const rate = secs > 0 ? Math.round(records / secs) : 0; - const failStr = usersFailed > 0 ? ` | ${usersFailed} failed` : ''; - process.stdout.write( - `\r ${records} records | ${usersComplete}/${usersTotal} users | ${rate}/s | ${elapsed(backfillStart)}${failStr} ` - ); - } - }); - process.stdout.write('\n'); - console.log(` Done: ${total} records in ${elapsed(backfillStart)}\n`); - - console.log(`=== Finished in ${elapsed(syncStart)} ===`); - console.log(` Discovered: ${discovered.length} users`); - console.log(` Backfilled: ${total} records`); - } finally { - await dispose(); - } -} - -main().catch((err) => { - console.error(err); - process.exit(1); -}); diff --git a/src/lexicon-types/index.ts b/src/lexicon-types/index.ts index 9d550a9..fb11cde 100644 --- a/src/lexicon-types/index.ts +++ b/src/lexicon-types/index.ts @@ -8,3 +8,6 @@ export * as AppBlentoNotifyOfUpdate from './types/app/blento/notifyOfUpdate.js'; export * as AppBlentoPage from './types/app/blento/page.js'; export * as AppBlentoPageGetRecord from './types/app/blento/page/getRecord.js'; export * as AppBlentoPageListRecords from './types/app/blento/page/listRecords.js'; +export * as AppBlentoSection from './types/app/blento/section.js'; +export * as AppBlentoSectionGetRecord from './types/app/blento/section/getRecord.js'; +export * as AppBlentoSectionListRecords from './types/app/blento/section/listRecords.js'; diff --git a/src/lexicon-types/types/app/blento/card.ts b/src/lexicon-types/types/app/blento/card.ts index 585271f..4b4e61d 100644 --- a/src/lexicon-types/types/app/blento/card.ts +++ b/src/lexicon-types/types/app/blento/card.ts @@ -6,15 +6,17 @@ const _mainSchema = /*#__PURE__*/ v.record( /*#__PURE__*/ v.tidString(), /*#__PURE__*/ v.object({ $type: /*#__PURE__*/ v.literal('app.blento.card'), - cardData: /*#__PURE__*/ v.unknown(), + cardData: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), cardType: /*#__PURE__*/ v.string(), color: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), h: /*#__PURE__*/ v.integer(), - mobileH: /*#__PURE__*/ v.integer(), - mobileW: /*#__PURE__*/ v.integer(), - mobileX: /*#__PURE__*/ v.integer(), - mobileY: /*#__PURE__*/ v.integer(), + mobileH: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), + mobileW: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), + mobileX: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), + mobileY: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), page: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + rotation: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), + sectionId: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), updatedAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), version: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()), w: /*#__PURE__*/ v.integer(), diff --git a/src/lexicon-types/types/app/blento/card/getRecord.ts b/src/lexicon-types/types/app/blento/card/getRecord.ts index 3ad3987..78e2a34 100644 --- a/src/lexicon-types/types/app/blento/card/getRecord.ts +++ b/src/lexicon-types/types/app/blento/card/getRecord.ts @@ -17,18 +17,18 @@ const _mainSchema = /*#__PURE__*/ v.query('app.blento.card.getRecord', { output: { type: 'lex', schema: /*#__PURE__*/ v.object({ - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), collection: /*#__PURE__*/ v.nsidString(), did: /*#__PURE__*/ v.didString(), get profiles() { return /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(profileEntrySchema)); }, - get record() { - return /*#__PURE__*/ v.optional(AppBlentoCard.mainSchema); - }, rkey: /*#__PURE__*/ v.string(), time_us: /*#__PURE__*/ v.integer(), - uri: /*#__PURE__*/ v.resourceUriString() + uri: /*#__PURE__*/ v.resourceUriString(), + get value() { + return AppBlentoCard.mainSchema; + } }) } }); @@ -36,13 +36,13 @@ const _profileEntrySchema = /*#__PURE__*/ v.object({ $type: /*#__PURE__*/ v.optional( /*#__PURE__*/ v.literal('app.blento.card.getRecord#profileEntry') ), - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), did: /*#__PURE__*/ v.didString(), handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - record: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()) + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), + value: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()) }); type main$schematype = typeof _mainSchema; diff --git a/src/lexicon-types/types/app/blento/card/listRecords.ts b/src/lexicon-types/types/app/blento/card/listRecords.ts index db056ba..4946599 100644 --- a/src/lexicon-types/types/app/blento/card/listRecords.ts +++ b/src/lexicon-types/types/app/blento/card/listRecords.ts @@ -13,19 +13,7 @@ const _mainSchema = /*#__PURE__*/ v.query('app.blento.card.listRecords', { * Filter by cardType */ cardType: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Filter by color - */ - color: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Maximum value for h - */ - hMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Minimum value for h - */ - hMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), /** * @minimum 1 * @maximum 200 @@ -35,38 +23,6 @@ const _mainSchema = /*#__PURE__*/ v.query('app.blento.card.listRecords', { /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [/*#__PURE__*/ v.integerRange(1, 200)]), 50 ), - /** - * Maximum value for mobileH - */ - mobileHMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Minimum value for mobileH - */ - mobileHMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Maximum value for mobileW - */ - mobileWMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Minimum value for mobileW - */ - mobileWMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Maximum value for mobileX - */ - mobileXMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Minimum value for mobileX - */ - mobileXMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Maximum value for mobileY - */ - mobileYMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Minimum value for mobileY - */ - mobileYMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), /** * Sort direction (default: desc for dates/numbers/counts, asc for strings) */ @@ -82,64 +38,7 @@ const _mainSchema = /*#__PURE__*/ v.query('app.blento.card.listRecords', { /** * Field to sort by (default: time_us) */ - sort: /*#__PURE__*/ v.optional( - /*#__PURE__*/ v.string< - | 'cardType' - | 'color' - | 'h' - | 'mobileH' - | 'mobileW' - | 'mobileX' - | 'mobileY' - | 'page' - | 'updatedAt' - | 'version' - | 'w' - | 'x' - | 'y' - | (string & {}) - >() - ), - /** - * Maximum value for updatedAt - */ - updatedAtMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Minimum value for updatedAt - */ - updatedAtMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Maximum value for version - */ - versionMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Minimum value for version - */ - versionMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Maximum value for w - */ - wMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Minimum value for w - */ - wMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Maximum value for x - */ - xMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Minimum value for x - */ - xMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Maximum value for y - */ - yMax: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Minimum value for y - */ - yMin: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()) + sort: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string<'cardType' | 'page' | (string & {})>()) }), output: { type: 'lex', @@ -158,25 +57,25 @@ const _profileEntrySchema = /*#__PURE__*/ v.object({ $type: /*#__PURE__*/ v.optional( /*#__PURE__*/ v.literal('app.blento.card.listRecords#profileEntry') ), - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), did: /*#__PURE__*/ v.didString(), handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - record: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()) + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), + value: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()) }); const _recordSchema = /*#__PURE__*/ v.object({ $type: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal('app.blento.card.listRecords#record')), - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + cid: /*#__PURE__*/ v.cidString(), collection: /*#__PURE__*/ v.nsidString(), did: /*#__PURE__*/ v.didString(), - get record() { - return /*#__PURE__*/ v.optional(AppBlentoCard.mainSchema); - }, rkey: /*#__PURE__*/ v.string(), time_us: /*#__PURE__*/ v.integer(), - uri: /*#__PURE__*/ v.resourceUriString() + uri: /*#__PURE__*/ v.resourceUriString(), + get value() { + return AppBlentoCard.mainSchema; + } }); type main$schematype = typeof _mainSchema; diff --git a/src/lexicon-types/types/app/blento/getProfile.ts b/src/lexicon-types/types/app/blento/getProfile.ts index cded05a..8a06969 100644 --- a/src/lexicon-types/types/app/blento/getProfile.ts +++ b/src/lexicon-types/types/app/blento/getProfile.ts @@ -20,13 +20,13 @@ const _mainSchema = /*#__PURE__*/ v.query('app.blento.getProfile', { }); const _profileEntrySchema = /*#__PURE__*/ v.object({ $type: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal('app.blento.getProfile#profileEntry')), - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), did: /*#__PURE__*/ v.didString(), handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - record: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()) + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), + value: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()) }); type main$schematype = typeof _mainSchema; diff --git a/src/lexicon-types/types/app/blento/page.ts b/src/lexicon-types/types/app/blento/page.ts index 70f78e0..4e5db4c 100644 --- a/src/lexicon-types/types/app/blento/page.ts +++ b/src/lexicon-types/types/app/blento/page.ts @@ -10,7 +10,9 @@ const _mainSchema = /*#__PURE__*/ v.record( /** * @accept image/* */ - icon: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.blob()), + icon: /*#__PURE__*/ v.optional( + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.blob(), [/*#__PURE__*/ v.blobAccept(['image/*'])]) + ), name: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), get preferences() { return /*#__PURE__*/ v.optional(preferencesSchema); diff --git a/src/lexicon-types/types/app/blento/page/getRecord.ts b/src/lexicon-types/types/app/blento/page/getRecord.ts index 4f23ec9..bfab65e 100644 --- a/src/lexicon-types/types/app/blento/page/getRecord.ts +++ b/src/lexicon-types/types/app/blento/page/getRecord.ts @@ -17,18 +17,18 @@ const _mainSchema = /*#__PURE__*/ v.query('app.blento.page.getRecord', { output: { type: 'lex', schema: /*#__PURE__*/ v.object({ - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), collection: /*#__PURE__*/ v.nsidString(), did: /*#__PURE__*/ v.didString(), get profiles() { return /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(profileEntrySchema)); }, - get record() { - return /*#__PURE__*/ v.optional(AppBlentoPage.mainSchema); - }, rkey: /*#__PURE__*/ v.string(), time_us: /*#__PURE__*/ v.integer(), - uri: /*#__PURE__*/ v.resourceUriString() + uri: /*#__PURE__*/ v.resourceUriString(), + get value() { + return AppBlentoPage.mainSchema; + } }) } }); @@ -36,13 +36,13 @@ const _profileEntrySchema = /*#__PURE__*/ v.object({ $type: /*#__PURE__*/ v.optional( /*#__PURE__*/ v.literal('app.blento.page.getRecord#profileEntry') ), - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), did: /*#__PURE__*/ v.didString(), handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - record: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()) + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), + value: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()) }); type main$schematype = typeof _mainSchema; diff --git a/src/lexicon-types/types/app/blento/page/listRecords.ts b/src/lexicon-types/types/app/blento/page/listRecords.ts index 643a284..e0ff3b0 100644 --- a/src/lexicon-types/types/app/blento/page/listRecords.ts +++ b/src/lexicon-types/types/app/blento/page/listRecords.ts @@ -10,10 +10,6 @@ const _mainSchema = /*#__PURE__*/ v.query('app.blento.page.listRecords', { */ actor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.actorIdentifierString()), cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Filter by description - */ - description: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), /** * @minimum 1 * @maximum 200 @@ -23,22 +19,10 @@ const _mainSchema = /*#__PURE__*/ v.query('app.blento.page.listRecords', { /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [/*#__PURE__*/ v.integerRange(1, 200)]), 50 ), - /** - * Filter by name - */ - name: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - /** - * Sort direction (default: desc for dates/numbers/counts, asc for strings) - */ - order: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string<'asc' | 'desc' | (string & {})>()), /** * Include profile + identity info keyed by DID */ - profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), - /** - * Field to sort by (default: time_us) - */ - sort: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string<'description' | 'name' | (string & {})>()) + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()) }), output: { type: 'lex', @@ -57,25 +41,25 @@ const _profileEntrySchema = /*#__PURE__*/ v.object({ $type: /*#__PURE__*/ v.optional( /*#__PURE__*/ v.literal('app.blento.page.listRecords#profileEntry') ), - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), did: /*#__PURE__*/ v.didString(), handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - record: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), - uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()) + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), + value: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()) }); const _recordSchema = /*#__PURE__*/ v.object({ $type: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal('app.blento.page.listRecords#record')), - cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + cid: /*#__PURE__*/ v.cidString(), collection: /*#__PURE__*/ v.nsidString(), did: /*#__PURE__*/ v.didString(), - get record() { - return /*#__PURE__*/ v.optional(AppBlentoPage.mainSchema); - }, rkey: /*#__PURE__*/ v.string(), time_us: /*#__PURE__*/ v.integer(), - uri: /*#__PURE__*/ v.resourceUriString() + uri: /*#__PURE__*/ v.resourceUriString(), + get value() { + return AppBlentoPage.mainSchema; + } }); type main$schematype = typeof _mainSchema; diff --git a/src/lexicon-types/types/app/blento/section.ts b/src/lexicon-types/types/app/blento/section.ts new file mode 100644 index 0000000..17ee2e1 --- /dev/null +++ b/src/lexicon-types/types/app/blento/section.ts @@ -0,0 +1,31 @@ +import type {} from '@atcute/lexicons'; +import * as v from '@atcute/lexicons/validations'; +import type {} from '@atcute/lexicons/ambient'; + +const _mainSchema = /*#__PURE__*/ v.record( + /*#__PURE__*/ v.tidString(), + /*#__PURE__*/ v.object({ + $type: /*#__PURE__*/ v.literal('app.blento.section'), + index: /*#__PURE__*/ v.integer(), + name: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + page: /*#__PURE__*/ v.string(), + sectionData: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()), + sectionType: /*#__PURE__*/ v.string(), + updatedAt: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()), + version: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()) + }) +); + +type main$schematype = typeof _mainSchema; + +export interface mainSchema extends main$schematype {} + +export const mainSchema = _mainSchema as mainSchema; + +export interface Main extends v.InferInput {} + +declare module '@atcute/lexicons/ambient' { + interface Records { + 'app.blento.section': mainSchema; + } +} diff --git a/src/lexicon-types/types/app/blento/section/getRecord.ts b/src/lexicon-types/types/app/blento/section/getRecord.ts new file mode 100644 index 0000000..0574249 --- /dev/null +++ b/src/lexicon-types/types/app/blento/section/getRecord.ts @@ -0,0 +1,66 @@ +import type {} from '@atcute/lexicons'; +import * as v from '@atcute/lexicons/validations'; +import type {} from '@atcute/lexicons/ambient'; +import * as AppBlentoSection from '../section.js'; + +const _mainSchema = /*#__PURE__*/ v.query('app.blento.section.getRecord', { + params: /*#__PURE__*/ v.object({ + /** + * Include profile + identity info keyed by DID + */ + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), + /** + * AT URI of the record + */ + uri: /*#__PURE__*/ v.resourceUriString() + }), + output: { + type: 'lex', + schema: /*#__PURE__*/ v.object({ + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), + collection: /*#__PURE__*/ v.nsidString(), + did: /*#__PURE__*/ v.didString(), + get profiles() { + return /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(profileEntrySchema)); + }, + rkey: /*#__PURE__*/ v.string(), + time_us: /*#__PURE__*/ v.integer(), + uri: /*#__PURE__*/ v.resourceUriString(), + get value() { + return AppBlentoSection.mainSchema; + } + }) + } +}); +const _profileEntrySchema = /*#__PURE__*/ v.object({ + $type: /*#__PURE__*/ v.optional( + /*#__PURE__*/ v.literal('app.blento.section.getRecord#profileEntry') + ), + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), + did: /*#__PURE__*/ v.didString(), + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), + value: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()) +}); + +type main$schematype = typeof _mainSchema; +type profileEntry$schematype = typeof _profileEntrySchema; + +export interface mainSchema extends main$schematype {} +export interface profileEntrySchema extends profileEntry$schematype {} + +export const mainSchema = _mainSchema as mainSchema; +export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; + +export interface ProfileEntry extends v.InferInput {} + +export interface $params extends v.InferInput {} +export interface $output extends v.InferXRPCBodyInput {} + +declare module '@atcute/lexicons/ambient' { + interface XRPCQueries { + 'app.blento.section.getRecord': mainSchema; + } +} diff --git a/src/lexicon-types/types/app/blento/section/listRecords.ts b/src/lexicon-types/types/app/blento/section/listRecords.ts new file mode 100644 index 0000000..5d377c0 --- /dev/null +++ b/src/lexicon-types/types/app/blento/section/listRecords.ts @@ -0,0 +1,103 @@ +import type {} from '@atcute/lexicons'; +import * as v from '@atcute/lexicons/validations'; +import type {} from '@atcute/lexicons/ambient'; +import * as AppBlentoSection from '../section.js'; + +const _mainSchema = /*#__PURE__*/ v.query('app.blento.section.listRecords', { + params: /*#__PURE__*/ v.object({ + /** + * Filter by DID or handle (triggers on-demand backfill) + */ + actor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.actorIdentifierString()), + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + /** + * @minimum 1 + * @maximum 200 + * @default 50 + */ + limit: /*#__PURE__*/ v.optional( + /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.integer(), [/*#__PURE__*/ v.integerRange(1, 200)]), + 50 + ), + /** + * Sort direction (default: desc for dates/numbers/counts, asc for strings) + */ + order: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string<'asc' | 'desc' | (string & {})>()), + /** + * Filter by page + */ + page: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + /** + * Include profile + identity info keyed by DID + */ + profiles: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()), + /** + * Filter by sectionType + */ + sectionType: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + /** + * Field to sort by (default: time_us) + */ + sort: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string<'page' | 'sectionType' | (string & {})>()) + }), + output: { + type: 'lex', + schema: /*#__PURE__*/ v.object({ + cursor: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + get profiles() { + return /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(profileEntrySchema)); + }, + get records() { + return /*#__PURE__*/ v.array(recordSchema); + } + }) + } +}); +const _profileEntrySchema = /*#__PURE__*/ v.object({ + $type: /*#__PURE__*/ v.optional( + /*#__PURE__*/ v.literal('app.blento.section.listRecords#profileEntry') + ), + cid: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.cidString()), + collection: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.nsidString()), + did: /*#__PURE__*/ v.didString(), + handle: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + rkey: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()), + uri: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()), + value: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.unknown()) +}); +const _recordSchema = /*#__PURE__*/ v.object({ + $type: /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal('app.blento.section.listRecords#record')), + cid: /*#__PURE__*/ v.cidString(), + collection: /*#__PURE__*/ v.nsidString(), + did: /*#__PURE__*/ v.didString(), + rkey: /*#__PURE__*/ v.string(), + time_us: /*#__PURE__*/ v.integer(), + uri: /*#__PURE__*/ v.resourceUriString(), + get value() { + return AppBlentoSection.mainSchema; + } +}); + +type main$schematype = typeof _mainSchema; +type profileEntry$schematype = typeof _profileEntrySchema; +type record$schematype = typeof _recordSchema; + +export interface mainSchema extends main$schematype {} +export interface profileEntrySchema extends profileEntry$schematype {} +export interface recordSchema extends record$schematype {} + +export const mainSchema = _mainSchema as mainSchema; +export const profileEntrySchema = _profileEntrySchema as profileEntrySchema; +export const recordSchema = _recordSchema as recordSchema; + +export interface ProfileEntry extends v.InferInput {} +export interface Record extends v.InferInput {} + +export interface $params extends v.InferInput {} +export interface $output extends v.InferXRPCBodyInput {} + +declare module '@atcute/lexicons/ambient' { + interface XRPCQueries { + 'app.blento.section.listRecords': mainSchema; + } +} diff --git a/src/lib/cards/special/UpdatedBlentos/index.ts b/src/lib/cards/special/UpdatedBlentos/index.ts index 0ba6b83..b0a5e53 100644 --- a/src/lib/cards/special/UpdatedBlentos/index.ts +++ b/src/lib/cards/special/UpdatedBlentos/index.ts @@ -19,7 +19,7 @@ function extractProfiles( handle?: string; collection?: string; rkey?: string; - record?: unknown; + value?: unknown; }> ): Map { const map = new Map(); @@ -35,27 +35,27 @@ function extractProfiles( existing.handle = p.handle as `${string}.${string}`; } - const record = p.record as Record | undefined; + const value = p.value as Record | undefined; - if (p.collection === 'app.bsky.actor.profile' && record) { - existing.displayName ??= record.displayName as string | undefined; - if (!existing.avatar && record.avatar) { + if (p.collection === 'app.bsky.actor.profile' && value) { + existing.displayName ??= value.displayName as string | undefined; + if (!existing.avatar && value.avatar) { const cdnUrl = getCDNImageBlobUrl({ did: p.did, - blob: record.avatar as { $type: 'blob'; ref: { $link: string } } + blob: value.avatar as { $type: 'blob'; ref: { $link: string } } }); if (cdnUrl) existing.avatar = cdnUrl; } } - if (p.collection === 'site.standard.publication' && record) { + if (p.collection === 'site.standard.publication' && value) { existing.hasBlento = true; - existing.displayName = (record.name as string) ?? existing.displayName; - existing.url = record.url as string | undefined; - if (record.icon) { + existing.displayName = (value.name as string) ?? existing.displayName; + existing.url = value.url as string | undefined; + if (value.icon) { const cdnUrl = getCDNImageBlobUrl({ did: p.did, - blob: record.icon as { $type: 'blob'; ref: { $link: string } } + blob: value.icon as { $type: 'blob'; ref: { $link: string } } }); if (cdnUrl) existing.avatar = cdnUrl; } diff --git a/src/lib/contrail/config.ts b/src/lib/contrail.config.ts similarity index 76% rename from src/lib/contrail/config.ts rename to src/lib/contrail.config.ts index 60034a5..c7dc246 100644 --- a/src/lib/contrail/config.ts +++ b/src/lib/contrail.config.ts @@ -3,16 +3,18 @@ import type { ContrailConfig } from '@atmo-dev/contrail'; export const config: ContrailConfig = { namespace: 'app.blento', collections: { - 'app.blento.card': { + card: { + collection: 'app.blento.card', queryable: { page: {}, cardType: {} } }, - 'app.blento.page': { - queryable: {} + page: { + collection: 'app.blento.page' }, - 'app.blento.section': { + section: { + collection: 'app.blento.section', queryable: { page: {}, sectionType: {} diff --git a/src/lib/contrail/index.ts b/src/lib/contrail/index.ts index bb7ba5b..93b210a 100644 --- a/src/lib/contrail/index.ts +++ b/src/lib/contrail/index.ts @@ -1,8 +1,7 @@ import type { D1Database } from '@cloudflare/workers-types'; import { Contrail } from '@atmo-dev/contrail'; -import { createHandler } from '@atmo-dev/contrail/server'; -import { Client } from '@atcute/client'; -import { config } from './config'; +import { createHandler, createServerClient } from '@atmo-dev/contrail/server'; +import { config } from '../contrail.config'; export const contrail = new Contrail(config); @@ -19,14 +18,11 @@ const handle = createHandler(contrail); /** * Server-side: fully typed @atcute/client that routes through contrail in-process. - * No HTTP roundtrip — calls createHandler directly. + * No HTTP roundtrip — calls the handler directly with the per-request DB. */ export function getServerClient(db: D1Database) { - return new Client({ - handler: async (pathname, init) => { - await ensureInit(db); - const url = new URL(pathname, 'http://localhost'); - return handle(new Request(url, init), db) as Promise; - } + return createServerClient(async (req) => { + await ensureInit(db); + return handle(req, db); }); } diff --git a/src/lib/website/load.ts b/src/lib/website/load.ts index 7f318f0..91febde 100644 --- a/src/lib/website/load.ts +++ b/src/lib/website/load.ts @@ -40,7 +40,7 @@ type ContrailProfile = { handle?: string; collection?: string; rkey?: string; - record?: unknown; + value?: unknown; }; /** @@ -63,15 +63,15 @@ function extractProfileData( if (p.did !== did) continue; if (p.handle && p.handle !== 'handle.invalid') handle = p.handle; - const record = p.record as Record | undefined; - if (p.collection === 'app.bsky.actor.profile' && record) { - bskyRecord = record; + const value = p.value as Record | undefined; + if (p.collection === 'app.bsky.actor.profile' && value) { + bskyRecord = value; } - if (p.collection === 'site.standard.publication' && record) { - pubRecord = record; + if (p.collection === 'site.standard.publication' && value) { + pubRecord = value; } - if (p.collection === 'app.nearhorizon.actor.pronouns' && record) { - pronounsValue = record; + if (p.collection === 'app.nearhorizon.actor.pronouns' && value) { + pronounsValue = value; } } @@ -128,7 +128,7 @@ function loadCardFromContrail(did: Did, rkey: string, db: D1Database) { params: { uri } }); if (!res.ok) return null; - return { ...(res.data.record as object) } as Item; + return { ...(res.data.value as object) } as Item; }); } @@ -154,22 +154,22 @@ function loadFromContrail(actor: ActorIdentifier, db: D1Database, page: string) if (!cardRes.ok) return null; - const cards = cardRes.data.records.map((r) => ({ ...(r.record as object) }) as Item); + const cards = cardRes.data.records.map((r) => ({ ...(r.value as object) }) as Item); const pages = pageRes.ok ? pageRes.data.records - .filter((r) => r.record) + .filter((r) => r.value) .map((r) => ({ uri: r.uri, cid: r.cid ?? '', - value: r.record as Record + value: r.value as Record })) : []; const sections = sectionRes?.ok && sectionRes.data?.records ? (sectionRes.data.records as any[]).map( - (r: any) => ({ ...(r.record as object), id: parseUri(r.uri)?.rkey }) as SectionRecord + (r: any) => ({ ...(r.value as object), id: parseUri(r.uri)?.rkey }) as SectionRecord ) : []; diff --git a/wrangler.jsonc b/wrangler.jsonc index a882e82..773246a 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -59,8 +59,8 @@ "d1_databases": [ { "binding": "DB", - "database_name": "blento", - "database_id": "922639e7-6321-42c8-a4bd-cdf48428fdac", + "database_name": "blento-2", + "database_id": "5ccd30a3-c8d8-446a-b675-6a96a8f08263", "remote": true } ], -- 2.51.2 From 2e97867bb72e80fee4d3693bb98e1c6500648ffb Mon Sep 17 00:00:00 2001 From: Florian <45694132+flo-bit@users.noreply.github.com> Date: Mon, 4 May 2026 19:50:22 +0200 Subject: [PATCH 2/5] update contributing --- docs/Contributing.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/docs/Contributing.md b/docs/Contributing.md index 6cd5b6c..eca8834 100644 --- a/docs/Contributing.md +++ b/docs/Contributing.md @@ -9,16 +9,12 @@ git clone https://github.com/flo-bit/blento.git cd blento pnpm install pnpm env:setup-dev # creates .env, fills COOKIE_SECRET + CLIENT_ASSERTION_KEY +pnpm dev ``` -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. +Note: if cloudflare authorization website opens when running `pnpm dev` flip the `DB` binding's `"remote": true` to `false` in `wrangler.jsonc` and re-run. -```sh -pnpm dev # site falls back to PDS when D1 is empty — no backfill needed -pnpm backfill # populates local D1 via contrail; needed only for /xrpc/* paths and the UpdatedBlentos card -``` - -`pnpm backfill` is resumable, takes a few minutes the first time. +Individual `/handle` pages load directly from each user's PDS — no backfill needed. ## Before opening a PR -- 2.51.2 From c0bd7e985abaa97aad237a49ec3b97747bdaf7cd Mon Sep 17 00:00:00 2001 From: Florian <45694132+flo-bit@users.noreply.github.com> Date: Mon, 4 May 2026 19:50:47 +0200 Subject: [PATCH 3/5] switch remote off --- wrangler.jsonc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrangler.jsonc b/wrangler.jsonc index 773246a..d19604d 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -61,7 +61,7 @@ "binding": "DB", "database_name": "blento-2", "database_id": "5ccd30a3-c8d8-446a-b675-6a96a8f08263", - "remote": true + "remote": false } ], "analytics_engine_datasets": [ -- 2.51.2 From 31a1bda606ad197ebde1a694f90af0b88a8f09aa Mon Sep 17 00:00:00 2001 From: Florian <45694132+flo-bit@users.noreply.github.com> Date: Mon, 4 May 2026 20:18:14 +0200 Subject: [PATCH 4/5] update db --- wrangler.jsonc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wrangler.jsonc b/wrangler.jsonc index d19604d..9670f28 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -59,8 +59,8 @@ "d1_databases": [ { "binding": "DB", - "database_name": "blento-2", - "database_id": "5ccd30a3-c8d8-446a-b675-6a96a8f08263", + "database_name": "blento-v3", + "database_id": "83e7be08-8503-4b54-9f73-0bac105761b5", "remote": false } ], -- 2.51.2 From 18a3d63c19311ad7c8034bc05923be81f17792d8 Mon Sep 17 00:00:00 2001 From: Florian <45694132+flo-bit@users.noreply.github.com> Date: Mon, 4 May 2026 20:31:45 +0200 Subject: [PATCH 5/5] small fixes --- docs/Contributing.md | 5 +++-- src/lib/actor.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/Contributing.md b/docs/Contributing.md index eca8834..e2b852d 100644 --- a/docs/Contributing.md +++ b/docs/Contributing.md @@ -8,10 +8,11 @@ For new cards see [CustomCards](CustomCards.md) and [CardIdeas](CardIdeas.md). git clone https://github.com/flo-bit/blento.git cd blento pnpm install -pnpm env:setup-dev # creates .env, fills COOKIE_SECRET + CLIENT_ASSERTION_KEY pnpm dev ``` +No `.env` file required — dev uses the loopback OAuth public client, a fallback cookie secret, and `blento.app` as the default handle for `/`. + Note: if cloudflare authorization website opens when running `pnpm dev` flip the `DB` binding's `"remote": true` to `false` in `wrangler.jsonc` and re-run. Individual `/handle` pages load directly from each user's PDS — no backfill needed. @@ -27,7 +28,7 @@ In-progress changes go on a subpage so your live profile stays clean: `/your.han ## AI-assisted PRs -Welcome — please: +AI-assisted PRs are accepted, especially if you just create a new card, but please: - Keep diffs minimal; no unrelated cleanup or verbose code - Test light/dark, colored cards, edit/view, desktop and both mobile modes (screen-size and `pointer: coarse`) diff --git a/src/lib/actor.ts b/src/lib/actor.ts index 6b662ac..72f605a 100644 --- a/src/lib/actor.ts +++ b/src/lib/actor.ts @@ -30,7 +30,7 @@ export async function getActor({ console.error('failed to get custom domain kv', error); } } else { - actor = publicEnv.PUBLIC_HANDLE as ActorIdentifier; + actor = (publicEnv.PUBLIC_HANDLE || 'blento.app') as ActorIdentifier; } } else if (customDomain && paramActor && blockBoth) { actor = undefined;