diff --git a/.env.example b/.env.example
index c47fc14..38e3818 100644
--- a/.env.example
+++ b/.env.example
@@ -1,2 +1,7 @@
TMDB_ACCESS_TOKEN=
OMDB_API_KEY=
+
+# @svelte-atproto/oauth
+ORIGIN=
+COOKIE_SECRET=
+CLIENT_ASSERTION_KEY=
diff --git a/contrail.lock.json b/contrail.lock.json
new file mode 100644
index 0000000..bdca287
--- /dev/null
+++ b/contrail.lock.json
@@ -0,0 +1,35 @@
+{
+ "format": "contrail.provider-lock",
+ "version": 1,
+ "endpoint": "http://127.0.0.1:8787",
+ "namespace": "watch.atmo",
+ "contractDigest": "sha256:0f5f5b7930ba4502e4ef35f81ac33abd71896958e45841c96f55748a631e4d19",
+ "lexiconDigest": "sha256:ec400c9703839dd015a9d006e626f3b7d03f52815db8b4d7b183de23bb387385",
+ "methods": [
+ "watch.atmo.comment.getRecord",
+ "watch.atmo.comment.listRecords",
+ "watch.atmo.getCursor",
+ "watch.atmo.getProfile",
+ "watch.atmo.like.getRecord",
+ "watch.atmo.like.listRecords",
+ "watch.atmo.list.getRecord",
+ "watch.atmo.list.listRecords",
+ "watch.atmo.listItem.getRecord",
+ "watch.atmo.listItem.listRecords",
+ "watch.atmo.profile.getRecord",
+ "watch.atmo.profile.listRecords",
+ "watch.atmo.review.getRecord",
+ "watch.atmo.review.listRecords"
+ ],
+ "collections": [
+ "social.popfeed.actor.profile",
+ "social.popfeed.feed.comment",
+ "social.popfeed.feed.like",
+ "social.popfeed.feed.list",
+ "social.popfeed.feed.listItem",
+ "social.popfeed.feed.review"
+ ],
+ "serviceAuth": null,
+ "lexiconRoot": "src/lib/contrail/lexicons",
+ "allowInsecureHttp": true
+}
diff --git a/lex.config.js b/lex.config.js
new file mode 100644
index 0000000..1f75dc4
--- /dev/null
+++ b/lex.config.js
@@ -0,0 +1,20 @@
+// Generated by `contrail connect`. Re-run the command to update; do not edit.
+export default {
+ contrail: {
+ endpoint: "http://127.0.0.1:8787",
+ serviceDid: null,
+ scope: null,
+ collections: [
+ "social.popfeed.actor.profile",
+ "social.popfeed.feed.comment",
+ "social.popfeed.feed.like",
+ "social.popfeed.feed.list",
+ "social.popfeed.feed.listItem",
+ "social.popfeed.feed.review",
+ ],
+ },
+ generate: {
+ files: ["src/lib/contrail/lexicons/**/*.json"],
+ outdir: "src/lib/contrail/types/",
+ },
+};
diff --git a/package.json b/package.json
index 36759d8..fc04f63 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,9 @@
"version": "0.0.1",
"type": "module",
"scripts": {
+ "atproto:setup": "atproto-oauth setup",
+ "atproto:keygen": "atproto-oauth keygen",
+ "atproto:secret": "atproto-oauth secret",
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
@@ -45,12 +48,15 @@
"vitest-browser-svelte": "^2.1.1"
},
"dependencies": {
+ "@atcute/atproto": "^4.0.0",
"@atcute/client": "^5.1.1",
"@atcute/lexicons": "^2.0.3",
"@atmo-dev/contrail": "^0.17.0",
"@lorenzopant/tmdb": "^1.25.3",
+ "@svelte-atproto/oauth": "^0.3.0",
"add": "^2.0.9",
"bits-ui": "^2.18.1",
- "pnpm": "^11.21.0"
+ "pnpm": "^11.21.0",
+ "valibot": "^1.0.0"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 362bcbf..0451311 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,6 +8,9 @@ importers:
.:
dependencies:
+ '@atcute/atproto':
+ specifier: ^4.0.0
+ version: 4.0.4(@atcute/lexicons@2.0.3)
'@atcute/client':
specifier: ^5.1.1
version: 5.1.1(@atcute/lexicons@2.0.3)(typescript@6.0.3)
@@ -20,6 +23,9 @@ importers:
'@lorenzopant/tmdb':
specifier: ^1.25.3
version: 1.25.3
+ '@svelte-atproto/oauth':
+ specifier: ^0.3.0
+ version: 0.3.0(@atcute/identity@2.0.2(@atcute/lexicons@2.0.3)(typescript@6.0.3))(@sveltejs/kit@2.70.2(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1(@types/node@24.13.3)(jiti@2.7.0)))(svelte@5.56.9(@typescript-eslint/types@8.67.0))(typescript@6.0.3)(vite@8.2.1(@types/node@24.13.3)(jiti@2.7.0)))(svelte@5.56.9(@typescript-eslint/types@8.67.0))(typescript@6.0.3)
add:
specifier: ^2.0.9
version: 2.0.9
@@ -29,6 +35,9 @@ importers:
pnpm:
specifier: ^11.21.0
version: 11.21.0
+ valibot:
+ specifier: ^1.0.0
+ version: 1.4.2(typescript@6.0.3)
devDependencies:
'@eslint/js':
specifier: ^10.0.1
@@ -116,6 +125,11 @@ packages:
peerDependencies:
'@atcute/lexicons': ^2.0.0
+ '@atcute/bluesky@4.0.18':
+ resolution: {integrity: sha512-cbwJWdu69arjoxdIFSQYcSVUGtkG1IDYdG/uK4Grk5epWv6mEe6XVkr9UNWB0PmYxvX1f0s1jLSFeSAagUxewA==}
+ peerDependencies:
+ '@atcute/lexicons': ^2.0.0
+
'@atcute/car@6.0.2':
resolution: {integrity: sha512-6AaLjO0zrFD8R/aK7jwrqHEmLzfVilu/5pv4LAcUjxIYfBrw+nThV2FVLPtA4Jt59tIhjp5tQDP+9sx1eqQp+A==}
peerDependencies:
@@ -188,6 +202,27 @@ packages:
'@atcute/multibase@1.2.5':
resolution: {integrity: sha512-cReTONgYpQo/VHD3ZmzPNoyBKJgSk1J4h//cvvdVVJBMar+SjlQ/sUXeTjQfuyfmDKv+TLKhmutLcvbMcQ9Rvw==}
+ '@atcute/oauth-browser-client@4.0.1':
+ resolution: {integrity: sha512-k5GGDFWUqTifbi7bMk4LhwG12XWNJop9atZ3ocBpzHJvXboTqp7E2zaD/arhJWxHXp5+0A1R3EKXdHsVbWjHyA==}
+ peerDependencies:
+ '@atcute/identity-resolver': ^2.0.0
+ '@atcute/lexicons': ^2.0.0
+
+ '@atcute/oauth-crypto@1.0.1':
+ resolution: {integrity: sha512-ghC8ceFx0r6pi5X3dNoET1K6PNg8afeAOZLD4PKLhrpVYdRtRrgY4uqAe3ojtSIjoS6GXmVm063CoJ2xtg/MoQ==}
+
+ '@atcute/oauth-keyset@0.1.3':
+ resolution: {integrity: sha512-VSNUb2TLUTyntgzfn58kwNL4kp4TD+M868tyyrFWlCaDVy0pDnVFfUXa1z1W+DBWLL9ICy2qTdPitRokAZKivw==}
+
+ '@atcute/oauth-node-client@2.0.1':
+ resolution: {integrity: sha512-MgqN9Rnt5nuUH5zriETtbQrwcx/tWaELKaFVQtqggMtRI4k4IP5+ZbnG40tiJjbZfSclapA2kwOnT14kO0nAxg==}
+ peerDependencies:
+ '@atcute/identity-resolver': ^2.0.0
+ '@atcute/lexicons': ^2.0.0
+
+ '@atcute/oauth-types@1.0.1':
+ resolution: {integrity: sha512-9uFP/nK0PgPDsaNbLArzrCHvTZO1k04cSEX5jZ75W2Jh7Jxbh/CSTenSrad3CZXuX6y6hDquEtl6H/IqLtTmHw==}
+
'@atcute/repo@1.0.2':
resolution: {integrity: sha512-fsAuGbagOW52nSFtf/TWBx25A6xpZvzxBk0+S14Bp5sGNq7JoQ0zyu40P7b6+L8b/buB+1ACHPntYsKqCI4Awg==}
peerDependencies:
@@ -450,6 +485,13 @@ packages:
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
+ '@svelte-atproto/oauth@0.3.0':
+ resolution: {integrity: sha512-Tijm4GGMg15NrA2EH715GoGE0yHxPBL4dk8h+Zmb3L7ea/TLKayjVso3OgNg5drvBjrgrthO0oI3BYSudTbT0A==}
+ hasBin: true
+ peerDependencies:
+ '@sveltejs/kit': ^2.0.0
+ svelte: ^5.0.0
+
'@sveltejs/acorn-typescript@1.0.13':
resolution: {integrity: sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ==}
peerDependencies:
@@ -1238,6 +1280,11 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
+ nanoid@5.1.16:
+ resolution: {integrity: sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==}
+ engines: {node: ^18 || >=20}
+ hasBin: true
+
nanoid@6.0.1:
resolution: {integrity: sha512-3wVS3i51pE2pi1k5FFL/95BGfVS0kSsvDVuGXHOtxox/TywUmtgq+3qiTOTbs9J7KfHaXPiN171k/A6dBnaXFw==}
engines: {node: ^22 || ^24 || >=26}
@@ -1726,6 +1773,11 @@ snapshots:
dependencies:
'@atcute/lexicons': 2.0.3
+ '@atcute/bluesky@4.0.18(@atcute/lexicons@2.0.3)':
+ dependencies:
+ '@atcute/atproto': 4.0.4(@atcute/lexicons@2.0.3)
+ '@atcute/lexicons': 2.0.3
+
'@atcute/car@6.0.2(@atcute/cbor@2.3.6(@atcute/cid@2.4.2))(@atcute/cid@2.4.2)':
dependencies:
'@atcute/cbor': 2.3.6(@atcute/cid@2.4.2)
@@ -1846,6 +1898,57 @@ snapshots:
dependencies:
'@atcute/uint8array': 1.1.5
+ '@atcute/oauth-browser-client@4.0.1(@atcute/identity-resolver@2.0.1(@atcute/identity@2.0.2(@atcute/lexicons@2.0.3)(typescript@6.0.3))(@atcute/lexicons@2.0.3)(typescript@6.0.3))(@atcute/lexicons@2.0.3)(typescript@6.0.3)':
+ dependencies:
+ '@atcute/client': 5.1.1(@atcute/lexicons@2.0.3)(typescript@6.0.3)
+ '@atcute/identity-resolver': 2.0.1(@atcute/identity@2.0.2(@atcute/lexicons@2.0.3)(typescript@6.0.3))(@atcute/lexicons@2.0.3)(typescript@6.0.3)
+ '@atcute/lexicons': 2.0.3
+ '@atcute/multibase': 1.2.5
+ '@atcute/oauth-crypto': 1.0.1(typescript@6.0.3)
+ '@atcute/oauth-types': 1.0.1(typescript@6.0.3)
+ nanoid: 5.1.16
+ transitivePeerDependencies:
+ - typescript
+
+ '@atcute/oauth-crypto@1.0.1(typescript@6.0.3)':
+ dependencies:
+ '@atcute/multibase': 1.2.5
+ '@atcute/uint8array': 1.1.5
+ nanoid: 5.1.16
+ valibot: 1.4.2(typescript@6.0.3)
+ transitivePeerDependencies:
+ - typescript
+
+ '@atcute/oauth-keyset@0.1.3(typescript@6.0.3)':
+ dependencies:
+ '@atcute/oauth-crypto': 1.0.1(typescript@6.0.3)
+ transitivePeerDependencies:
+ - typescript
+
+ '@atcute/oauth-node-client@2.0.1(@atcute/identity-resolver@2.0.1(@atcute/identity@2.0.2(@atcute/lexicons@2.0.3)(typescript@6.0.3))(@atcute/lexicons@2.0.3)(typescript@6.0.3))(@atcute/lexicons@2.0.3)(typescript@6.0.3)':
+ dependencies:
+ '@atcute/client': 5.1.1(@atcute/lexicons@2.0.3)(typescript@6.0.3)
+ '@atcute/identity': 2.0.2(@atcute/lexicons@2.0.3)(typescript@6.0.3)
+ '@atcute/identity-resolver': 2.0.1(@atcute/identity@2.0.2(@atcute/lexicons@2.0.3)(typescript@6.0.3))(@atcute/lexicons@2.0.3)(typescript@6.0.3)
+ '@atcute/lexicons': 2.0.3
+ '@atcute/oauth-crypto': 1.0.1(typescript@6.0.3)
+ '@atcute/oauth-keyset': 0.1.3(typescript@6.0.3)
+ '@atcute/oauth-types': 1.0.1(typescript@6.0.3)
+ '@atcute/util-fetch': 2.0.2(typescript@6.0.3)
+ nanoid: 5.1.16
+ valibot: 1.4.2(typescript@6.0.3)
+ transitivePeerDependencies:
+ - typescript
+
+ '@atcute/oauth-types@1.0.1(typescript@6.0.3)':
+ dependencies:
+ '@atcute/identity': 2.0.2(@atcute/lexicons@2.0.3)(typescript@6.0.3)
+ '@atcute/lexicons': 2.0.3
+ '@atcute/oauth-keyset': 0.1.3(typescript@6.0.3)
+ valibot: 1.4.2(typescript@6.0.3)
+ transitivePeerDependencies:
+ - typescript
+
'@atcute/repo@1.0.2(@atcute/cbor@2.3.6(@atcute/cid@2.4.2))(@atcute/cid@2.4.2)(@atcute/lexicons@2.0.3)':
dependencies:
'@atcute/car': 6.0.2(@atcute/cbor@2.3.6(@atcute/cid@2.4.2))(@atcute/cid@2.4.2)
@@ -2072,6 +2175,22 @@ snapshots:
'@standard-schema/spec@1.1.0': {}
+ '@svelte-atproto/oauth@0.3.0(@atcute/identity@2.0.2(@atcute/lexicons@2.0.3)(typescript@6.0.3))(@sveltejs/kit@2.70.2(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1(@types/node@24.13.3)(jiti@2.7.0)))(svelte@5.56.9(@typescript-eslint/types@8.67.0))(typescript@6.0.3)(vite@8.2.1(@types/node@24.13.3)(jiti@2.7.0)))(svelte@5.56.9(@typescript-eslint/types@8.67.0))(typescript@6.0.3)':
+ dependencies:
+ '@atcute/atproto': 4.0.4(@atcute/lexicons@2.0.3)
+ '@atcute/bluesky': 4.0.18(@atcute/lexicons@2.0.3)
+ '@atcute/client': 5.1.1(@atcute/lexicons@2.0.3)(typescript@6.0.3)
+ '@atcute/identity-resolver': 2.0.1(@atcute/identity@2.0.2(@atcute/lexicons@2.0.3)(typescript@6.0.3))(@atcute/lexicons@2.0.3)(typescript@6.0.3)
+ '@atcute/lexicons': 2.0.3
+ '@atcute/oauth-browser-client': 4.0.1(@atcute/identity-resolver@2.0.1(@atcute/identity@2.0.2(@atcute/lexicons@2.0.3)(typescript@6.0.3))(@atcute/lexicons@2.0.3)(typescript@6.0.3))(@atcute/lexicons@2.0.3)(typescript@6.0.3)
+ '@atcute/oauth-node-client': 2.0.1(@atcute/identity-resolver@2.0.1(@atcute/identity@2.0.2(@atcute/lexicons@2.0.3)(typescript@6.0.3))(@atcute/lexicons@2.0.3)(typescript@6.0.3))(@atcute/lexicons@2.0.3)(typescript@6.0.3)
+ '@atcute/tid': 1.1.4
+ '@sveltejs/kit': 2.70.2(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1(@types/node@24.13.3)(jiti@2.7.0)))(svelte@5.56.9(@typescript-eslint/types@8.67.0))(typescript@6.0.3)(vite@8.2.1(@types/node@24.13.3)(jiti@2.7.0))
+ svelte: 5.56.9(@typescript-eslint/types@8.67.0)
+ transitivePeerDependencies:
+ - '@atcute/identity'
+ - typescript
+
'@sveltejs/acorn-typescript@1.0.13(acorn@8.18.0)':
dependencies:
acorn: 8.18.0
@@ -2797,6 +2916,8 @@ snapshots:
nanoid@3.3.18: {}
+ nanoid@5.1.16: {}
+
nanoid@6.0.1: {}
natural-compare@1.4.0: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index efc037a..5ed0b5a 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -1,2 +1,2 @@
-onlyBuiltDependencies:
- - esbuild
+allowBuilds:
+ esbuild: true
diff --git a/src/app.d.ts b/src/app.d.ts
index da08e6d..3a78063 100644
--- a/src/app.d.ts
+++ b/src/app.d.ts
@@ -1,9 +1,16 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
-// for information about these interfaces
+import type { OAuthSession } from '@atcute/oauth-node-client';
+import type { Client } from '@atcute/client';
+import type { Did } from '@atcute/lexicons';
+
declare global {
namespace App {
// interface Error {}
- // interface Locals {}
+ interface Locals {
+ session: OAuthSession | null;
+ client: Client | null;
+ did: Did | null;
+ }
// interface PageData {}
// interface PageState {}
// interface Platform {}
diff --git a/src/app.html b/src/app.html
index 66d9a6b..bd6d335 100644
--- a/src/app.html
+++ b/src/app.html
@@ -6,7 +6,7 @@
%sveltekit.head%
-
+
%sveltekit.body%
diff --git a/src/hooks.server.ts b/src/hooks.server.ts
new file mode 100644
index 0000000..e9dba0a
--- /dev/null
+++ b/src/hooks.server.ts
@@ -0,0 +1,3 @@
+import { atproto } from '$lib/atproto';
+
+export const handle = atproto.handle;
diff --git a/src/lib/atproto/index.ts b/src/lib/atproto/index.ts
new file mode 100644
index 0000000..555eb72
--- /dev/null
+++ b/src/lib/atproto/index.ts
@@ -0,0 +1,15 @@
+// Side-effect: loads `com.atproto.*` lexicon types into the atcute Client.
+import '@atcute/atproto';
+import { createAtprotoAuth } from '@svelte-atproto/oauth/server';
+import { cloudflareKV } from '@svelte-atproto/oauth/server/stores/cloudflare';
+import { env } from '$env/dynamic/private';
+
+// To enable signup, add: signupPDS: 'https://your-pds.example/'
+export const atproto = createAtprotoAuth({
+ origin: env.ORIGIN,
+ cookieSecret: env.COOKIE_SECRET,
+ clientAssertionKey: env.CLIENT_ASSERTION_KEY,
+ scope: 'atproto repo:xyz.statusphere.status',
+ sessions: cloudflareKV('OAUTH_SESSIONS'),
+ states: cloudflareKV('OAUTH_STATES', { ttl: 600 })
+});
diff --git a/src/lib/atproto/oauth.remote.ts b/src/lib/atproto/oauth.remote.ts
new file mode 100644
index 0000000..c4e7f7c
--- /dev/null
+++ b/src/lib/atproto/oauth.remote.ts
@@ -0,0 +1,37 @@
+import { command, getRequestEvent } from '$app/server';
+import { error } from '@sveltejs/kit';
+import * as v from 'valibot';
+import { atproto } from '$lib/atproto';
+import { createTID } from '@svelte-atproto/oauth/helper';
+
+export const oauthLogin = command(
+ v.object({
+ handle: v.optional(v.pipe(v.string(), v.minLength(3))),
+ signup: v.optional(v.boolean()),
+ returnTo: v.optional(v.string())
+ }),
+ (input) => atproto.api.startLogin(input)
+);
+
+export const oauthLogout = command(() => atproto.api.logout());
+
+const COLLECTION = 'xyz.statusphere.status';
+
+export const setStatus = command(v.string(), async (status) => {
+ const { locals } = getRequestEvent();
+ if (!locals.client || !locals.did) error(401, 'Not signed in');
+
+ await locals.client.post('com.atproto.repo.putRecord', {
+ input: {
+ repo: locals.did,
+ collection: COLLECTION,
+ rkey: createTID(),
+ record: {
+ $type: COLLECTION,
+ status,
+ createdAt: new Date().toISOString()
+ }
+ }
+ });
+ return { ok: true };
+});
diff --git a/src/lib/contrail.config.ts b/src/lib/contrail.config.ts
new file mode 100644
index 0000000..c5371d2
--- /dev/null
+++ b/src/lib/contrail.config.ts
@@ -0,0 +1,22 @@
+import type { ContrailConfig } from '@atmo-dev/contrail';
+
+export const config: ContrailConfig = {
+ namespace: 'watch.atmo',
+ collections: {
+ review: {
+ collection: 'social.popfeed.feed.review'
+ },
+ like: {
+ collection: 'social.popfeed.feed.like'
+ },
+ list: {
+ collection: 'social.popfeed.feed.list'
+ },
+ listItem: {
+ collection: 'social.popfeed.feed.listItem'
+ },
+ comment: {
+ collection: 'social.popfeed.feed.comment'
+ }
+ }
+};
diff --git a/src/lib/contrail/index.ts b/src/lib/contrail/index.ts
new file mode 100644
index 0000000..a6beffa
--- /dev/null
+++ b/src/lib/contrail/index.ts
@@ -0,0 +1,34 @@
+// Generated by `contrail connect`. Re-run the command to update; do not edit.
+import { createPublicServiceClient } from "@atmo-dev/contrail/client";
+import type {} from "./types/index.js";
+
+export const contrail = createPublicServiceClient({
+ endpoint: "http://127.0.0.1:8787",
+ allowInsecureHttp: true,
+ contractDigest: "sha256:0f5f5b7930ba4502e4ef35f81ac33abd71896958e45841c96f55748a631e4d19",
+ serviceMethods: [
+ "watch.atmo.comment.getRecord",
+ "watch.atmo.comment.listRecords",
+ "watch.atmo.getCursor",
+ "watch.atmo.getProfile",
+ "watch.atmo.like.getRecord",
+ "watch.atmo.like.listRecords",
+ "watch.atmo.list.getRecord",
+ "watch.atmo.list.listRecords",
+ "watch.atmo.listItem.getRecord",
+ "watch.atmo.listItem.listRecords",
+ "watch.atmo.profile.getRecord",
+ "watch.atmo.profile.listRecords",
+ "watch.atmo.review.getRecord",
+ "watch.atmo.review.listRecords",
+ ],
+ collections: [
+ "social.popfeed.actor.profile",
+ "social.popfeed.feed.comment",
+ "social.popfeed.feed.like",
+ "social.popfeed.feed.list",
+ "social.popfeed.feed.listItem",
+ "social.popfeed.feed.review",
+ ],
+ notifyMethod: "watch.atmo.notifyOfUpdate",
+});
diff --git a/src/lib/contrail/lexicons/watch/atmo/comment/getRecord.json b/src/lib/contrail/lexicons/watch/atmo/comment/getRecord.json
new file mode 100644
index 0000000..24f2aeb
--- /dev/null
+++ b/src/lib/contrail/lexicons/watch/atmo/comment/getRecord.json
@@ -0,0 +1,109 @@
+{
+ "defs": {
+ "main": {
+ "description": "Get a social.popfeed.feed.comment record by AT URI",
+ "output": {
+ "encoding": "application/json",
+ "schema": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "profiles": {
+ "items": {
+ "ref": "#profileEntry",
+ "type": "ref"
+ },
+ "type": "array"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "time_us": {
+ "type": "integer"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "ref": "social.popfeed.feed.comment#main",
+ "type": "ref"
+ }
+ },
+ "required": [
+ "uri",
+ "value",
+ "did",
+ "collection",
+ "rkey",
+ "time_us"
+ ],
+ "type": "object"
+ }
+ },
+ "parameters": {
+ "properties": {
+ "profiles": {
+ "description": "Include indexed profile and identity information",
+ "type": "boolean"
+ },
+ "uri": {
+ "description": "AT URI of the record",
+ "format": "at-uri",
+ "type": "string"
+ }
+ },
+ "required": [
+ "uri"
+ ],
+ "type": "params"
+ },
+ "type": "query"
+ },
+ "profileEntry": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "handle": {
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "type": "unknown"
+ }
+ },
+ "required": [
+ "did"
+ ],
+ "type": "object"
+ }
+ },
+ "id": "watch.atmo.comment.getRecord",
+ "lexicon": 1
+}
diff --git a/src/lib/contrail/lexicons/watch/atmo/comment/listRecords.json b/src/lib/contrail/lexicons/watch/atmo/comment/listRecords.json
new file mode 100644
index 0000000..b8dda53
--- /dev/null
+++ b/src/lib/contrail/lexicons/watch/atmo/comment/listRecords.json
@@ -0,0 +1,134 @@
+{
+ "defs": {
+ "main": {
+ "description": "Query social.popfeed.feed.comment records",
+ "output": {
+ "encoding": "application/json",
+ "schema": {
+ "properties": {
+ "cursor": {
+ "type": "string"
+ },
+ "profiles": {
+ "items": {
+ "ref": "#profileEntry",
+ "type": "ref"
+ },
+ "type": "array"
+ },
+ "records": {
+ "items": {
+ "ref": "#record",
+ "type": "ref"
+ },
+ "type": "array"
+ }
+ },
+ "required": [
+ "records"
+ ],
+ "type": "object"
+ }
+ },
+ "parameters": {
+ "properties": {
+ "actor": {
+ "description": "Filter by an indexed DID or cached handle",
+ "format": "at-identifier",
+ "type": "string"
+ },
+ "cursor": {
+ "type": "string"
+ },
+ "limit": {
+ "default": 50,
+ "maximum": 200,
+ "minimum": 1,
+ "type": "integer"
+ },
+ "profiles": {
+ "description": "Include indexed profile and identity information",
+ "type": "boolean"
+ }
+ },
+ "type": "params"
+ },
+ "type": "query"
+ },
+ "profileEntry": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "handle": {
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "type": "unknown"
+ }
+ },
+ "required": [
+ "did"
+ ],
+ "type": "object"
+ },
+ "record": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "time_us": {
+ "type": "integer"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "ref": "social.popfeed.feed.comment#main",
+ "type": "ref"
+ }
+ },
+ "required": [
+ "uri",
+ "cid",
+ "value",
+ "did",
+ "collection",
+ "rkey",
+ "time_us"
+ ],
+ "type": "object"
+ }
+ },
+ "id": "watch.atmo.comment.listRecords",
+ "lexicon": 1
+}
diff --git a/src/lib/contrail/lexicons/watch/atmo/getCursor.json b/src/lib/contrail/lexicons/watch/atmo/getCursor.json
new file mode 100644
index 0000000..e34e1a8
--- /dev/null
+++ b/src/lib/contrail/lexicons/watch/atmo/getCursor.json
@@ -0,0 +1,48 @@
+{
+ "defs": {
+ "main": {
+ "description": "Get the committed primary ordered-source position",
+ "output": {
+ "encoding": "application/json",
+ "schema": {
+ "properties": {
+ "position": {
+ "ref": "#sourcePosition",
+ "type": "ref"
+ },
+ "updatedAt": {
+ "type": "integer"
+ },
+ "updatedAtDate": {
+ "format": "datetime",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "type": "query"
+ },
+ "sourcePosition": {
+ "properties": {
+ "cursor": {
+ "type": "string"
+ },
+ "epoch": {
+ "type": "string"
+ },
+ "source": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "source",
+ "epoch",
+ "cursor"
+ ],
+ "type": "object"
+ }
+ },
+ "id": "watch.atmo.getCursor",
+ "lexicon": 1
+}
diff --git a/src/lib/contrail/lexicons/watch/atmo/getProfile.json b/src/lib/contrail/lexicons/watch/atmo/getProfile.json
new file mode 100644
index 0000000..2ba86f0
--- /dev/null
+++ b/src/lib/contrail/lexicons/watch/atmo/getProfile.json
@@ -0,0 +1,72 @@
+{
+ "defs": {
+ "main": {
+ "output": {
+ "encoding": "application/json",
+ "schema": {
+ "properties": {
+ "profiles": {
+ "items": {
+ "ref": "#profileEntry",
+ "type": "ref"
+ },
+ "type": "array"
+ }
+ },
+ "required": [
+ "profiles"
+ ],
+ "type": "object"
+ }
+ },
+ "parameters": {
+ "properties": {
+ "actor": {
+ "format": "at-identifier",
+ "type": "string"
+ }
+ },
+ "required": [
+ "actor"
+ ],
+ "type": "params"
+ },
+ "type": "query"
+ },
+ "profileEntry": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "handle": {
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "type": "unknown"
+ }
+ },
+ "required": [
+ "did"
+ ],
+ "type": "object"
+ }
+ },
+ "id": "watch.atmo.getProfile",
+ "lexicon": 1
+}
diff --git a/src/lib/contrail/lexicons/watch/atmo/like/getRecord.json b/src/lib/contrail/lexicons/watch/atmo/like/getRecord.json
new file mode 100644
index 0000000..3c7c6ab
--- /dev/null
+++ b/src/lib/contrail/lexicons/watch/atmo/like/getRecord.json
@@ -0,0 +1,109 @@
+{
+ "defs": {
+ "main": {
+ "description": "Get a social.popfeed.feed.like record by AT URI",
+ "output": {
+ "encoding": "application/json",
+ "schema": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "profiles": {
+ "items": {
+ "ref": "#profileEntry",
+ "type": "ref"
+ },
+ "type": "array"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "time_us": {
+ "type": "integer"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "ref": "social.popfeed.feed.like#main",
+ "type": "ref"
+ }
+ },
+ "required": [
+ "uri",
+ "value",
+ "did",
+ "collection",
+ "rkey",
+ "time_us"
+ ],
+ "type": "object"
+ }
+ },
+ "parameters": {
+ "properties": {
+ "profiles": {
+ "description": "Include indexed profile and identity information",
+ "type": "boolean"
+ },
+ "uri": {
+ "description": "AT URI of the record",
+ "format": "at-uri",
+ "type": "string"
+ }
+ },
+ "required": [
+ "uri"
+ ],
+ "type": "params"
+ },
+ "type": "query"
+ },
+ "profileEntry": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "handle": {
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "type": "unknown"
+ }
+ },
+ "required": [
+ "did"
+ ],
+ "type": "object"
+ }
+ },
+ "id": "watch.atmo.like.getRecord",
+ "lexicon": 1
+}
diff --git a/src/lib/contrail/lexicons/watch/atmo/like/listRecords.json b/src/lib/contrail/lexicons/watch/atmo/like/listRecords.json
new file mode 100644
index 0000000..c3cb7ce
--- /dev/null
+++ b/src/lib/contrail/lexicons/watch/atmo/like/listRecords.json
@@ -0,0 +1,134 @@
+{
+ "defs": {
+ "main": {
+ "description": "Query social.popfeed.feed.like records",
+ "output": {
+ "encoding": "application/json",
+ "schema": {
+ "properties": {
+ "cursor": {
+ "type": "string"
+ },
+ "profiles": {
+ "items": {
+ "ref": "#profileEntry",
+ "type": "ref"
+ },
+ "type": "array"
+ },
+ "records": {
+ "items": {
+ "ref": "#record",
+ "type": "ref"
+ },
+ "type": "array"
+ }
+ },
+ "required": [
+ "records"
+ ],
+ "type": "object"
+ }
+ },
+ "parameters": {
+ "properties": {
+ "actor": {
+ "description": "Filter by an indexed DID or cached handle",
+ "format": "at-identifier",
+ "type": "string"
+ },
+ "cursor": {
+ "type": "string"
+ },
+ "limit": {
+ "default": 50,
+ "maximum": 200,
+ "minimum": 1,
+ "type": "integer"
+ },
+ "profiles": {
+ "description": "Include indexed profile and identity information",
+ "type": "boolean"
+ }
+ },
+ "type": "params"
+ },
+ "type": "query"
+ },
+ "profileEntry": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "handle": {
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "type": "unknown"
+ }
+ },
+ "required": [
+ "did"
+ ],
+ "type": "object"
+ },
+ "record": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "time_us": {
+ "type": "integer"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "ref": "social.popfeed.feed.like#main",
+ "type": "ref"
+ }
+ },
+ "required": [
+ "uri",
+ "cid",
+ "value",
+ "did",
+ "collection",
+ "rkey",
+ "time_us"
+ ],
+ "type": "object"
+ }
+ },
+ "id": "watch.atmo.like.listRecords",
+ "lexicon": 1
+}
diff --git a/src/lib/contrail/lexicons/watch/atmo/list/getRecord.json b/src/lib/contrail/lexicons/watch/atmo/list/getRecord.json
new file mode 100644
index 0000000..bbb60cd
--- /dev/null
+++ b/src/lib/contrail/lexicons/watch/atmo/list/getRecord.json
@@ -0,0 +1,109 @@
+{
+ "defs": {
+ "main": {
+ "description": "Get a social.popfeed.feed.list record by AT URI",
+ "output": {
+ "encoding": "application/json",
+ "schema": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "profiles": {
+ "items": {
+ "ref": "#profileEntry",
+ "type": "ref"
+ },
+ "type": "array"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "time_us": {
+ "type": "integer"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "ref": "social.popfeed.feed.list#main",
+ "type": "ref"
+ }
+ },
+ "required": [
+ "uri",
+ "value",
+ "did",
+ "collection",
+ "rkey",
+ "time_us"
+ ],
+ "type": "object"
+ }
+ },
+ "parameters": {
+ "properties": {
+ "profiles": {
+ "description": "Include indexed profile and identity information",
+ "type": "boolean"
+ },
+ "uri": {
+ "description": "AT URI of the record",
+ "format": "at-uri",
+ "type": "string"
+ }
+ },
+ "required": [
+ "uri"
+ ],
+ "type": "params"
+ },
+ "type": "query"
+ },
+ "profileEntry": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "handle": {
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "type": "unknown"
+ }
+ },
+ "required": [
+ "did"
+ ],
+ "type": "object"
+ }
+ },
+ "id": "watch.atmo.list.getRecord",
+ "lexicon": 1
+}
diff --git a/src/lib/contrail/lexicons/watch/atmo/list/listRecords.json b/src/lib/contrail/lexicons/watch/atmo/list/listRecords.json
new file mode 100644
index 0000000..07d8a9b
--- /dev/null
+++ b/src/lib/contrail/lexicons/watch/atmo/list/listRecords.json
@@ -0,0 +1,134 @@
+{
+ "defs": {
+ "main": {
+ "description": "Query social.popfeed.feed.list records",
+ "output": {
+ "encoding": "application/json",
+ "schema": {
+ "properties": {
+ "cursor": {
+ "type": "string"
+ },
+ "profiles": {
+ "items": {
+ "ref": "#profileEntry",
+ "type": "ref"
+ },
+ "type": "array"
+ },
+ "records": {
+ "items": {
+ "ref": "#record",
+ "type": "ref"
+ },
+ "type": "array"
+ }
+ },
+ "required": [
+ "records"
+ ],
+ "type": "object"
+ }
+ },
+ "parameters": {
+ "properties": {
+ "actor": {
+ "description": "Filter by an indexed DID or cached handle",
+ "format": "at-identifier",
+ "type": "string"
+ },
+ "cursor": {
+ "type": "string"
+ },
+ "limit": {
+ "default": 50,
+ "maximum": 200,
+ "minimum": 1,
+ "type": "integer"
+ },
+ "profiles": {
+ "description": "Include indexed profile and identity information",
+ "type": "boolean"
+ }
+ },
+ "type": "params"
+ },
+ "type": "query"
+ },
+ "profileEntry": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "handle": {
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "type": "unknown"
+ }
+ },
+ "required": [
+ "did"
+ ],
+ "type": "object"
+ },
+ "record": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "time_us": {
+ "type": "integer"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "ref": "social.popfeed.feed.list#main",
+ "type": "ref"
+ }
+ },
+ "required": [
+ "uri",
+ "cid",
+ "value",
+ "did",
+ "collection",
+ "rkey",
+ "time_us"
+ ],
+ "type": "object"
+ }
+ },
+ "id": "watch.atmo.list.listRecords",
+ "lexicon": 1
+}
diff --git a/src/lib/contrail/lexicons/watch/atmo/listItem/getRecord.json b/src/lib/contrail/lexicons/watch/atmo/listItem/getRecord.json
new file mode 100644
index 0000000..7087dbb
--- /dev/null
+++ b/src/lib/contrail/lexicons/watch/atmo/listItem/getRecord.json
@@ -0,0 +1,109 @@
+{
+ "defs": {
+ "main": {
+ "description": "Get a social.popfeed.feed.listItem record by AT URI",
+ "output": {
+ "encoding": "application/json",
+ "schema": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "profiles": {
+ "items": {
+ "ref": "#profileEntry",
+ "type": "ref"
+ },
+ "type": "array"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "time_us": {
+ "type": "integer"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "ref": "social.popfeed.feed.listItem#main",
+ "type": "ref"
+ }
+ },
+ "required": [
+ "uri",
+ "value",
+ "did",
+ "collection",
+ "rkey",
+ "time_us"
+ ],
+ "type": "object"
+ }
+ },
+ "parameters": {
+ "properties": {
+ "profiles": {
+ "description": "Include indexed profile and identity information",
+ "type": "boolean"
+ },
+ "uri": {
+ "description": "AT URI of the record",
+ "format": "at-uri",
+ "type": "string"
+ }
+ },
+ "required": [
+ "uri"
+ ],
+ "type": "params"
+ },
+ "type": "query"
+ },
+ "profileEntry": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "handle": {
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "type": "unknown"
+ }
+ },
+ "required": [
+ "did"
+ ],
+ "type": "object"
+ }
+ },
+ "id": "watch.atmo.listItem.getRecord",
+ "lexicon": 1
+}
diff --git a/src/lib/contrail/lexicons/watch/atmo/listItem/listRecords.json b/src/lib/contrail/lexicons/watch/atmo/listItem/listRecords.json
new file mode 100644
index 0000000..58acdaa
--- /dev/null
+++ b/src/lib/contrail/lexicons/watch/atmo/listItem/listRecords.json
@@ -0,0 +1,134 @@
+{
+ "defs": {
+ "main": {
+ "description": "Query social.popfeed.feed.listItem records",
+ "output": {
+ "encoding": "application/json",
+ "schema": {
+ "properties": {
+ "cursor": {
+ "type": "string"
+ },
+ "profiles": {
+ "items": {
+ "ref": "#profileEntry",
+ "type": "ref"
+ },
+ "type": "array"
+ },
+ "records": {
+ "items": {
+ "ref": "#record",
+ "type": "ref"
+ },
+ "type": "array"
+ }
+ },
+ "required": [
+ "records"
+ ],
+ "type": "object"
+ }
+ },
+ "parameters": {
+ "properties": {
+ "actor": {
+ "description": "Filter by an indexed DID or cached handle",
+ "format": "at-identifier",
+ "type": "string"
+ },
+ "cursor": {
+ "type": "string"
+ },
+ "limit": {
+ "default": 50,
+ "maximum": 200,
+ "minimum": 1,
+ "type": "integer"
+ },
+ "profiles": {
+ "description": "Include indexed profile and identity information",
+ "type": "boolean"
+ }
+ },
+ "type": "params"
+ },
+ "type": "query"
+ },
+ "profileEntry": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "handle": {
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "type": "unknown"
+ }
+ },
+ "required": [
+ "did"
+ ],
+ "type": "object"
+ },
+ "record": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "time_us": {
+ "type": "integer"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "ref": "social.popfeed.feed.listItem#main",
+ "type": "ref"
+ }
+ },
+ "required": [
+ "uri",
+ "cid",
+ "value",
+ "did",
+ "collection",
+ "rkey",
+ "time_us"
+ ],
+ "type": "object"
+ }
+ },
+ "id": "watch.atmo.listItem.listRecords",
+ "lexicon": 1
+}
diff --git a/src/lib/contrail/lexicons/watch/atmo/notifyOfUpdate.json b/src/lib/contrail/lexicons/watch/atmo/notifyOfUpdate.json
new file mode 100644
index 0000000..9acb986
--- /dev/null
+++ b/src/lib/contrail/lexicons/watch/atmo/notifyOfUpdate.json
@@ -0,0 +1,56 @@
+{
+ "defs": {
+ "main": {
+ "description": "Fetch changed records from their authoritative PDS for immediate indexing",
+ "input": {
+ "encoding": "application/json",
+ "schema": {
+ "properties": {
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "uris": {
+ "items": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "maxLength": 25,
+ "type": "array"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "output": {
+ "encoding": "application/json",
+ "schema": {
+ "properties": {
+ "deleted": {
+ "minimum": 0,
+ "type": "integer"
+ },
+ "errors": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "indexed": {
+ "minimum": 0,
+ "type": "integer"
+ }
+ },
+ "required": [
+ "indexed",
+ "deleted"
+ ],
+ "type": "object"
+ }
+ },
+ "type": "procedure"
+ }
+ },
+ "id": "watch.atmo.notifyOfUpdate",
+ "lexicon": 1
+}
diff --git a/src/lib/contrail/lexicons/watch/atmo/profile/getRecord.json b/src/lib/contrail/lexicons/watch/atmo/profile/getRecord.json
new file mode 100644
index 0000000..b776845
--- /dev/null
+++ b/src/lib/contrail/lexicons/watch/atmo/profile/getRecord.json
@@ -0,0 +1,109 @@
+{
+ "defs": {
+ "main": {
+ "description": "Get a social.popfeed.actor.profile record by AT URI",
+ "output": {
+ "encoding": "application/json",
+ "schema": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "profiles": {
+ "items": {
+ "ref": "#profileEntry",
+ "type": "ref"
+ },
+ "type": "array"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "time_us": {
+ "type": "integer"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "ref": "social.popfeed.actor.profile#main",
+ "type": "ref"
+ }
+ },
+ "required": [
+ "uri",
+ "value",
+ "did",
+ "collection",
+ "rkey",
+ "time_us"
+ ],
+ "type": "object"
+ }
+ },
+ "parameters": {
+ "properties": {
+ "profiles": {
+ "description": "Include indexed profile and identity information",
+ "type": "boolean"
+ },
+ "uri": {
+ "description": "AT URI of the record",
+ "format": "at-uri",
+ "type": "string"
+ }
+ },
+ "required": [
+ "uri"
+ ],
+ "type": "params"
+ },
+ "type": "query"
+ },
+ "profileEntry": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "handle": {
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "type": "unknown"
+ }
+ },
+ "required": [
+ "did"
+ ],
+ "type": "object"
+ }
+ },
+ "id": "watch.atmo.profile.getRecord",
+ "lexicon": 1
+}
diff --git a/src/lib/contrail/lexicons/watch/atmo/profile/listRecords.json b/src/lib/contrail/lexicons/watch/atmo/profile/listRecords.json
new file mode 100644
index 0000000..536264f
--- /dev/null
+++ b/src/lib/contrail/lexicons/watch/atmo/profile/listRecords.json
@@ -0,0 +1,134 @@
+{
+ "defs": {
+ "main": {
+ "description": "Query social.popfeed.actor.profile records",
+ "output": {
+ "encoding": "application/json",
+ "schema": {
+ "properties": {
+ "cursor": {
+ "type": "string"
+ },
+ "profiles": {
+ "items": {
+ "ref": "#profileEntry",
+ "type": "ref"
+ },
+ "type": "array"
+ },
+ "records": {
+ "items": {
+ "ref": "#record",
+ "type": "ref"
+ },
+ "type": "array"
+ }
+ },
+ "required": [
+ "records"
+ ],
+ "type": "object"
+ }
+ },
+ "parameters": {
+ "properties": {
+ "actor": {
+ "description": "Filter by an indexed DID or cached handle",
+ "format": "at-identifier",
+ "type": "string"
+ },
+ "cursor": {
+ "type": "string"
+ },
+ "limit": {
+ "default": 50,
+ "maximum": 200,
+ "minimum": 1,
+ "type": "integer"
+ },
+ "profiles": {
+ "description": "Include indexed profile and identity information",
+ "type": "boolean"
+ }
+ },
+ "type": "params"
+ },
+ "type": "query"
+ },
+ "profileEntry": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "handle": {
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "type": "unknown"
+ }
+ },
+ "required": [
+ "did"
+ ],
+ "type": "object"
+ },
+ "record": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "time_us": {
+ "type": "integer"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "ref": "social.popfeed.actor.profile#main",
+ "type": "ref"
+ }
+ },
+ "required": [
+ "uri",
+ "cid",
+ "value",
+ "did",
+ "collection",
+ "rkey",
+ "time_us"
+ ],
+ "type": "object"
+ }
+ },
+ "id": "watch.atmo.profile.listRecords",
+ "lexicon": 1
+}
diff --git a/src/lib/contrail/lexicons/watch/atmo/review/getRecord.json b/src/lib/contrail/lexicons/watch/atmo/review/getRecord.json
new file mode 100644
index 0000000..e78cdaa
--- /dev/null
+++ b/src/lib/contrail/lexicons/watch/atmo/review/getRecord.json
@@ -0,0 +1,109 @@
+{
+ "defs": {
+ "main": {
+ "description": "Get a social.popfeed.feed.review record by AT URI",
+ "output": {
+ "encoding": "application/json",
+ "schema": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "profiles": {
+ "items": {
+ "ref": "#profileEntry",
+ "type": "ref"
+ },
+ "type": "array"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "time_us": {
+ "type": "integer"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "ref": "social.popfeed.feed.review#main",
+ "type": "ref"
+ }
+ },
+ "required": [
+ "uri",
+ "value",
+ "did",
+ "collection",
+ "rkey",
+ "time_us"
+ ],
+ "type": "object"
+ }
+ },
+ "parameters": {
+ "properties": {
+ "profiles": {
+ "description": "Include indexed profile and identity information",
+ "type": "boolean"
+ },
+ "uri": {
+ "description": "AT URI of the record",
+ "format": "at-uri",
+ "type": "string"
+ }
+ },
+ "required": [
+ "uri"
+ ],
+ "type": "params"
+ },
+ "type": "query"
+ },
+ "profileEntry": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "handle": {
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "type": "unknown"
+ }
+ },
+ "required": [
+ "did"
+ ],
+ "type": "object"
+ }
+ },
+ "id": "watch.atmo.review.getRecord",
+ "lexicon": 1
+}
diff --git a/src/lib/contrail/lexicons/watch/atmo/review/listRecords.json b/src/lib/contrail/lexicons/watch/atmo/review/listRecords.json
new file mode 100644
index 0000000..cab01d0
--- /dev/null
+++ b/src/lib/contrail/lexicons/watch/atmo/review/listRecords.json
@@ -0,0 +1,134 @@
+{
+ "defs": {
+ "main": {
+ "description": "Query social.popfeed.feed.review records",
+ "output": {
+ "encoding": "application/json",
+ "schema": {
+ "properties": {
+ "cursor": {
+ "type": "string"
+ },
+ "profiles": {
+ "items": {
+ "ref": "#profileEntry",
+ "type": "ref"
+ },
+ "type": "array"
+ },
+ "records": {
+ "items": {
+ "ref": "#record",
+ "type": "ref"
+ },
+ "type": "array"
+ }
+ },
+ "required": [
+ "records"
+ ],
+ "type": "object"
+ }
+ },
+ "parameters": {
+ "properties": {
+ "actor": {
+ "description": "Filter by an indexed DID or cached handle",
+ "format": "at-identifier",
+ "type": "string"
+ },
+ "cursor": {
+ "type": "string"
+ },
+ "limit": {
+ "default": 50,
+ "maximum": 200,
+ "minimum": 1,
+ "type": "integer"
+ },
+ "profiles": {
+ "description": "Include indexed profile and identity information",
+ "type": "boolean"
+ }
+ },
+ "type": "params"
+ },
+ "type": "query"
+ },
+ "profileEntry": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "handle": {
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "type": "unknown"
+ }
+ },
+ "required": [
+ "did"
+ ],
+ "type": "object"
+ },
+ "record": {
+ "properties": {
+ "cid": {
+ "format": "cid",
+ "type": "string"
+ },
+ "collection": {
+ "format": "nsid",
+ "type": "string"
+ },
+ "did": {
+ "format": "did",
+ "type": "string"
+ },
+ "rkey": {
+ "type": "string"
+ },
+ "time_us": {
+ "type": "integer"
+ },
+ "uri": {
+ "format": "at-uri",
+ "type": "string"
+ },
+ "value": {
+ "ref": "social.popfeed.feed.review#main",
+ "type": "ref"
+ }
+ },
+ "required": [
+ "uri",
+ "cid",
+ "value",
+ "did",
+ "collection",
+ "rkey",
+ "time_us"
+ ],
+ "type": "object"
+ }
+ },
+ "id": "watch.atmo.review.listRecords",
+ "lexicon": 1
+}
diff --git a/src/lib/contrail/types/index.ts b/src/lib/contrail/types/index.ts
new file mode 100644
index 0000000..b5da30d
--- /dev/null
+++ b/src/lib/contrail/types/index.ts
@@ -0,0 +1,22 @@
+export * as SocialPopfeedActorProfile from "./types/social/popfeed/actor/profile.js";
+export * as SocialPopfeedFeedComment from "./types/social/popfeed/feed/comment.js";
+export * as SocialPopfeedFeedLike from "./types/social/popfeed/feed/like.js";
+export * as SocialPopfeedFeedList from "./types/social/popfeed/feed/list.js";
+export * as SocialPopfeedFeedListItem from "./types/social/popfeed/feed/listItem.js";
+export * as SocialPopfeedFeedReview from "./types/social/popfeed/feed/review.js";
+export * as SocialPopfeedRichtextFacet from "./types/social/popfeed/richtext/facet.js";
+export * as WatchAtmoCommentGetRecord from "./types/watch/atmo/comment/getRecord.js";
+export * as WatchAtmoCommentListRecords from "./types/watch/atmo/comment/listRecords.js";
+export * as WatchAtmoGetCursor from "./types/watch/atmo/getCursor.js";
+export * as WatchAtmoGetProfile from "./types/watch/atmo/getProfile.js";
+export * as WatchAtmoLikeGetRecord from "./types/watch/atmo/like/getRecord.js";
+export * as WatchAtmoLikeListRecords from "./types/watch/atmo/like/listRecords.js";
+export * as WatchAtmoListGetRecord from "./types/watch/atmo/list/getRecord.js";
+export * as WatchAtmoListListRecords from "./types/watch/atmo/list/listRecords.js";
+export * as WatchAtmoListItemGetRecord from "./types/watch/atmo/listItem/getRecord.js";
+export * as WatchAtmoListItemListRecords from "./types/watch/atmo/listItem/listRecords.js";
+export * as WatchAtmoNotifyOfUpdate from "./types/watch/atmo/notifyOfUpdate.js";
+export * as WatchAtmoProfileGetRecord from "./types/watch/atmo/profile/getRecord.js";
+export * as WatchAtmoProfileListRecords from "./types/watch/atmo/profile/listRecords.js";
+export * as WatchAtmoReviewGetRecord from "./types/watch/atmo/review/getRecord.js";
+export * as WatchAtmoReviewListRecords from "./types/watch/atmo/review/listRecords.js";
diff --git a/src/lib/contrail/types/types/social/popfeed/actor/profile.ts b/src/lib/contrail/types/types/social/popfeed/actor/profile.ts
new file mode 100644
index 0000000..db20a3c
--- /dev/null
+++ b/src/lib/contrail/types/types/social/popfeed/actor/profile.ts
@@ -0,0 +1,92 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+
+const _externalLinkSchema = /*#__PURE__*/ v.object(
+ {
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("social.popfeed.actor.profile#externalLink")),
+ /**
+ * Name of the external service (e.g., 'Twitter', 'GitHub').
+ * @maxLength 100
+ */
+ "service": /*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [/*#__PURE__*/ v.stringLength(0, 100)]
+ ),
+ /**
+ * URL to the user's profile on the external service.
+ */
+ "url": /*#__PURE__*/ v.genericUriString(),
+ }
+);
+const _mainSchema = /*#__PURE__*/ v.record(
+ /*#__PURE__*/ v.literal("self"),
+ /*#__PURE__*/ v.object(
+ {
+ "$type": /*#__PURE__*/ v.literal("social.popfeed.actor.profile"),
+ /**
+ * Larger horizontal image to display behind profile view. Have not fully cut over yet so may not be shown
+ * @accept image/png, image/jpeg
+ * @maxSize 1000000
+ */
+ "banner": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.blob(),
+ [
+ /*#__PURE__*/ v.blobSize(1000000),
+ /*#__PURE__*/ v.blobAccept(["image/png", "image/jpeg"])
+ ]
+ )),
+ /**
+ * Optional URL to a banner image. DEPRECATED - soon to be ignored
+ */
+ "bannerUrl": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()),
+ "createdAt": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()),
+ /**
+ * Free-form profile description text.
+ * @maxLength 2560
+ * @maxGraphemes 256
+ */
+ "description": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [
+ /*#__PURE__*/ v.stringLength(0, 2560),
+ /*#__PURE__*/ v.stringGraphemes(0, 256)
+ ]
+ )),
+ /**
+ * @maxLength 640
+ * @maxGraphemes 64
+ */
+ "displayName": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [
+ /*#__PURE__*/ v.stringLength(0, 640),
+ /*#__PURE__*/ v.stringGraphemes(0, 64)
+ ]
+ )),
+ /**
+ * List of external links to the user's profiles on other services.
+ */
+ get "externalLinks"() {
+ return /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(externalLinkSchema))
+ },
+ }
+ )
+);
+type externalLink$schematype = typeof _externalLinkSchema;
+type main$schematype = typeof _mainSchema;
+
+export interface externalLinkSchema extends externalLink$schematype {}
+
+export interface mainSchema extends main$schematype {}
+export const externalLinkSchema = _externalLinkSchema as externalLinkSchema;
+export const mainSchema = _mainSchema as mainSchema;
+
+export interface ExternalLink extends v.InferInput {}
+
+export interface Main extends v.InferInput {}
+declare module '@atcute/lexicons/ambient' {
+ interface Records {
+ "social.popfeed.actor.profile": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/social/popfeed/feed/comment.ts b/src/lib/contrail/types/types/social/popfeed/feed/comment.ts
new file mode 100644
index 0000000..be74e41
--- /dev/null
+++ b/src/lib/contrail/types/types/social/popfeed/feed/comment.ts
@@ -0,0 +1,59 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+import * as SocialPopfeedRichtextFacet from "../richtext/facet.js";
+
+const _mainSchema = /*#__PURE__*/ v.record(
+ /*#__PURE__*/ v.tidString(),
+ /*#__PURE__*/ v.object(
+ {
+ "$type": /*#__PURE__*/ v.literal("social.popfeed.feed.comment"),
+ /**
+ * Client-declared timestamp when this comment was created.
+ */
+ "createdAt": /*#__PURE__*/ v.datetimeString(),
+ /**
+ * Annotations of text (mentions, URLs, hashtags, etc).
+ */
+ get "facets"() {
+ return /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(SocialPopfeedRichtextFacet.mainSchema))
+ },
+ /**
+ * The URI of the root entity of the comment thread, if applicable.
+ */
+ "rootUri": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()),
+ /**
+ * The type of subject being commented on, e.g., 'review'.
+ */
+ "subjectType": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literalEnum([
+ "comment",
+ "list",
+ "post",
+ "review"
+ ])),
+ /**
+ * The URI of the review being commented on.
+ */
+ "subjectUri": /*#__PURE__*/ v.genericUriString(),
+ /**
+ * The primary comment content.
+ * @maxLength 1000
+ */
+ "text": /*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [/*#__PURE__*/ v.stringLength(0, 1000)]
+ ),
+ }
+ )
+);
+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 {
+ "social.popfeed.feed.comment": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/social/popfeed/feed/like.ts b/src/lib/contrail/types/types/social/popfeed/feed/like.ts
new file mode 100644
index 0000000..d26f0bd
--- /dev/null
+++ b/src/lib/contrail/types/types/social/popfeed/feed/like.ts
@@ -0,0 +1,40 @@
+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("social.popfeed.feed.like"),
+ /**
+ * Client-declared timestamp when this like was created.
+ */
+ "createdAt": /*#__PURE__*/ v.datetimeString(),
+ /**
+ * The type of subject being liked, e.g., 'review'.
+ */
+ "subjectType": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literalEnum([
+ "comment",
+ "list",
+ "post",
+ "review"
+ ])),
+ /**
+ * The type of subject being liked, e.g., 'review'.
+ */
+ "subjectUri": /*#__PURE__*/ v.genericUriString(),
+ }
+ )
+);
+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 {
+ "social.popfeed.feed.like": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/social/popfeed/feed/list.ts b/src/lib/contrail/types/types/social/popfeed/feed/list.ts
new file mode 100644
index 0000000..b0b061f
--- /dev/null
+++ b/src/lib/contrail/types/types/social/popfeed/feed/list.ts
@@ -0,0 +1,66 @@
+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("social.popfeed.feed.list"),
+ /**
+ * The timestamp when the list was created.
+ */
+ "createdAt": /*#__PURE__*/ v.datetimeString(),
+ /**
+ * Optional description of the list.
+ * @maxLength 500
+ */
+ "description": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [/*#__PURE__*/ v.stringLength(0, 500)]
+ )),
+ /**
+ * An array of item uris in the order they appear in the list. Stored separately from the items themselves to allow for efficient reordering without needing to update each item record.
+ */
+ "itemOrder": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(/*#__PURE__*/ v.genericUriString())),
+ /**
+ * The type of list, e.g., 'watchlist', 'favorites', 'to-read', etc.
+ * @maxLength 50
+ */
+ "listType": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [/*#__PURE__*/ v.stringLength(0, 50)]
+ )),
+ /**
+ * The name of the list.
+ * @maxLength 100
+ */
+ "name": /*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [/*#__PURE__*/ v.stringLength(0, 100)]
+ ),
+ /**
+ * Indicates whether the list is ordered or unordered.
+ */
+ "ordered": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()),
+ /**
+ * Optional array of tags for categorizing or describing the list.
+ */
+ "tags": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [/*#__PURE__*/ v.stringLength(0, 50)]
+ ))),
+ }
+ )
+);
+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 {
+ "social.popfeed.feed.list": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/social/popfeed/feed/listItem.ts b/src/lib/contrail/types/types/social/popfeed/feed/listItem.ts
new file mode 100644
index 0000000..dc5019e
--- /dev/null
+++ b/src/lib/contrail/types/types/social/popfeed/feed/listItem.ts
@@ -0,0 +1,280 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+
+const _bookProgressSchema = /*#__PURE__*/ v.object(
+ {
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("social.popfeed.feed.listItem#bookProgress")),
+ /**
+ * Current chapter number (optional alternative to pages).
+ */
+ "currentChapter": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
+ /**
+ * Current page number (if applicable).
+ */
+ "currentPage": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
+ /**
+ * Optional explicit progress percentage (0β100).
+ */
+ "percent": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
+ /**
+ * Overall progress state.
+ */
+ "status": /*#__PURE__*/ v.literalEnum(["completed", "in_progress", "paused"]),
+ /**
+ * Total chapters (if known).
+ */
+ "totalChapters": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
+ /**
+ * Total pages (if known).
+ */
+ "totalPages": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
+ /**
+ * Last time progress was updated.
+ */
+ "updatedAt": /*#__PURE__*/ v.datetimeString(),
+ }
+);
+const _identifiersSchema = /*#__PURE__*/ v.object(
+ {
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("social.popfeed.feed.listItem#identifiers")),
+ /**
+ * Amazon Standard ID
+ */
+ "asin": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ /**
+ * Episode number for TV shows or series
+ */
+ "episodeNumber": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
+ /**
+ * IGDB ID for games
+ */
+ "igdbId": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ /**
+ * IMDb ID
+ */
+ "imdbId": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ /**
+ * ISBN-10 for books
+ */
+ "isbn10": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ /**
+ * ISBN-13 for books
+ */
+ "isbn13": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ /**
+ * DEPRECATED - soon to be ignored
+ */
+ "mbId": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ /**
+ * MusicBrainz ID for specific releases (albums, EPs, tracks)
+ */
+ "mbReleaseId": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ /**
+ * Other external ID
+ */
+ "other": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ /**
+ * MusicBrainz ID for parent release (e.g., album for a track)
+ */
+ "parentMbReleaseId": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ /**
+ * Season number for TV shows or series
+ */
+ "seasonNumber": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
+ /**
+ * TMDb ID for movies/TV shows
+ */
+ "tmdbId": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ /**
+ * TMDb ID for TV series
+ */
+ "tmdbTvSeriesId": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ }
+);
+const _mainSchema = /*#__PURE__*/ v.record(
+ /*#__PURE__*/ v.tidString(),
+ /*#__PURE__*/ v.object(
+ {
+ "$type": /*#__PURE__*/ v.literal("social.popfeed.feed.listItem"),
+ /**
+ * Timestamp when the creative work was added to the list.
+ */
+ "addedAt": /*#__PURE__*/ v.datetimeString(),
+ /**
+ * @accept image/*
+ * @maxSize 4000000
+ */
+ "backdrop": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.blob(),
+ [
+ /*#__PURE__*/ v.blobSize(4000000),
+ /*#__PURE__*/ v.blobAccept(["image/*"])
+ ]
+ )),
+ /**
+ * Backdrop image URL for the creative work. Soon to be deprecated in favor of the 'backdrop' blob field
+ */
+ "backdropUrl": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()),
+ /**
+ * Reading progress for books or book series (also supports audiobooks via time fields).
+ */
+ get "bookProgress"() {
+ return /*#__PURE__*/ v.optional(bookProgressSchema)
+ },
+ /**
+ * Timestamp when the user finished the creative work. Most relevant for books, TV shows, and video games.
+ */
+ "completedAt": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()),
+ /**
+ * The type of creative work this item represents.
+ */
+ "creativeWorkType": /*#__PURE__*/ v.literalEnum([
+ "album",
+ "album",
+ "book",
+ "book_series",
+ "ep",
+ "episode",
+ "movie",
+ "track",
+ "tv_episode",
+ "tv_season",
+ "tv_show",
+ "video_game"
+ ]),
+ /**
+ * Optional description or commentary for the list item.
+ * @maxLength 5000
+ */
+ "description": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [/*#__PURE__*/ v.stringLength(0, 5000)]
+ )),
+ "genres": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [/*#__PURE__*/ v.stringLength(0, 50)]
+ ))),
+ /**
+ * External identifiers for the creative work.
+ */
+ get "identifiers"() {
+ return identifiersSchema
+ },
+ /**
+ * The type of list, e.g., 'watchlist', 'favorites', 'to-read', etc.
+ * @maxLength 50
+ */
+ "listType": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [/*#__PURE__*/ v.stringLength(0, 50)]
+ )),
+ /**
+ * URI of the list where the item is stored.
+ */
+ "listUri": /*#__PURE__*/ v.genericUriString(),
+ /**
+ * Main actor, director, author, or artist of the creative work.
+ * @maxLength 1000
+ */
+ "mainCredit": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [/*#__PURE__*/ v.stringLength(0, 1000)]
+ )),
+ /**
+ * The role of the main credit.
+ */
+ "mainCreditRole": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literalEnum([
+ "artist",
+ "author",
+ "creator",
+ "developer",
+ "director",
+ "lead_actor",
+ "network",
+ "performer",
+ "publisher",
+ "showrunner",
+ "studio"
+ ])),
+ /**
+ * DEPRECATED
+ * @deprecated
+ */
+ "position": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
+ /**
+ * @accept image/*
+ * @maxSize 2000000
+ */
+ "poster": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.blob(),
+ [
+ /*#__PURE__*/ v.blobSize(2000000),
+ /*#__PURE__*/ v.blobAccept(["image/*"])
+ ]
+ )),
+ /**
+ * Poster or cover image URL for the creative work. Soon to be deprecated in favor of the 'poster' blob field
+ */
+ "posterUrl": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()),
+ /**
+ * Release date of the creative work.
+ */
+ "releaseDate": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()),
+ /**
+ * Timestamp when the user started the creative work. Most relevant for books, TV shows, and video games.
+ */
+ "startedAt": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()),
+ "status": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string<"#abandoned" | "#backlog" | "#finished" | "#in_progress" | (string & {})>()),
+ /**
+ * Title of the creative work.
+ * @maxLength 1000
+ */
+ "title": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [/*#__PURE__*/ v.stringLength(0, 1000)]
+ )),
+ /**
+ * For TV shows, the episodes the user has watched.
+ */
+ get "watchedEpisodes"() {
+ return /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(watchedEpisodeSchema))
+ },
+ }
+ )
+);
+const _watchedEpisodeSchema = /*#__PURE__*/ v.object({
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("social.popfeed.feed.listItem#watchedEpisode")),
+ "episodeNumber": /*#__PURE__*/ v.integer(),
+ "seasonNumber": /*#__PURE__*/ v.integer(),
+ "tmdbId": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+});
+type bookProgress$schematype = typeof _bookProgressSchema;
+type identifiers$schematype = typeof _identifiersSchema;
+type main$schematype = typeof _mainSchema;
+type watchedEpisode$schematype = typeof _watchedEpisodeSchema;
+
+export interface bookProgressSchema extends bookProgress$schematype {}
+
+export interface identifiersSchema extends identifiers$schematype {}
+
+export interface mainSchema extends main$schematype {}
+
+export interface watchedEpisodeSchema extends watchedEpisode$schematype {}
+export const bookProgressSchema = _bookProgressSchema as bookProgressSchema;
+export const identifiersSchema = _identifiersSchema as identifiersSchema;
+export const mainSchema = _mainSchema as mainSchema;
+export const watchedEpisodeSchema = _watchedEpisodeSchema as watchedEpisodeSchema;
+
+export interface BookProgress extends v.InferInput {}
+
+export interface Identifiers extends v.InferInput {}
+
+export interface Main extends v.InferInput {}
+
+export interface WatchedEpisode extends v.InferInput {}
+declare module '@atcute/lexicons/ambient' {
+ interface Records {
+ "social.popfeed.feed.listItem": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/social/popfeed/feed/review.ts b/src/lib/contrail/types/types/social/popfeed/feed/review.ts
new file mode 100644
index 0000000..ca14b5a
--- /dev/null
+++ b/src/lib/contrail/types/types/social/popfeed/feed/review.ts
@@ -0,0 +1,213 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+import * as SocialPopfeedRichtextFacet from "../richtext/facet.js";
+
+const _crossPostsSchema = /*#__PURE__*/ v.object(
+ {
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("social.popfeed.feed.review#crossPosts")),
+ /**
+ * The URI of the original Bluesky post being cross-posted.
+ */
+ "bluesky": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()),
+ /**
+ * The URI of the original Leaflet post being cross-posted.
+ */
+ "leaflet": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()),
+ }
+);
+const _identifiersSchema = /*#__PURE__*/ v.object(
+ {
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("social.popfeed.feed.review#identifiers")),
+ "asin": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ /**
+ * Episode number for TV shows or series
+ */
+ "episodeNumber": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
+ "igdbId": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ "imdbId": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ "isbn10": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ "isbn13": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ /**
+ * DEPRECATED - soon to be ignored
+ */
+ "mbId": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ /**
+ * MusicBrainz ID for specific releases (albums, EPs, tracks)
+ */
+ "mbReleaseId": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ "other": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ /**
+ * MusicBrainz ID for parent release (e.g., album for a track)
+ */
+ "parentMbReleaseId": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ /**
+ * Season number for TV shows or series
+ */
+ "seasonNumber": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
+ "tmdbId": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ /**
+ * TMDb ID for TV series
+ */
+ "tmdbTvSeriesId": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.string()),
+ }
+);
+const _mainSchema = /*#__PURE__*/ v.record(
+ /*#__PURE__*/ v.tidString(),
+ /*#__PURE__*/ v.object(
+ {
+ "$type": /*#__PURE__*/ v.literal("social.popfeed.feed.review"),
+ /**
+ * Backdrop image for the review post. Have not fully cut over yet so may not be shown
+ * @accept image/*
+ * @maxSize 4000000
+ */
+ "backdrop": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.blob(),
+ [
+ /*#__PURE__*/ v.blobSize(4000000),
+ /*#__PURE__*/ v.blobAccept(["image/*"])
+ ]
+ )),
+ /**
+ * Backdrop image URL for the creative work. Soon to be deprecated in favor of the 'backdrop' blob field
+ */
+ "backdropUrl": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()),
+ /**
+ * Indicates if the review contains spoilers.
+ */
+ "containsSpoilers": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()),
+ "createdAt": /*#__PURE__*/ v.datetimeString(),
+ "creativeWorkType": /*#__PURE__*/ v.literalEnum([
+ "album",
+ "album",
+ "book",
+ "book_series",
+ "ep",
+ "episode",
+ "movie",
+ "track",
+ "tv_episode",
+ "tv_season",
+ "tv_show",
+ "video_game"
+ ]),
+ /**
+ * Information about cross-posting to other platforms.
+ */
+ get "crossPosts"() {
+ return /*#__PURE__*/ v.optional(crossPostsSchema)
+ },
+ /**
+ * Annotations of text (mentions, URLs, hashtags, etc)
+ */
+ get "facets"() {
+ return /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(SocialPopfeedRichtextFacet.mainSchema))
+ },
+ "genres": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [/*#__PURE__*/ v.stringLength(0, 50)]
+ ))),
+ get "identifiers"() {
+ return identifiersSchema
+ },
+ /**
+ * Indicates if the user has experienced this work before (re-watch, re-read, replay).
+ */
+ "isRevisit": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()),
+ /**
+ * Main actor, director, author, or artist of the creative work.
+ * @maxLength 1000
+ */
+ "mainCredit": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [/*#__PURE__*/ v.stringLength(0, 1000)]
+ )),
+ /**
+ * The role of the main credit.
+ */
+ "mainCreditRole": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literalEnum([
+ "artist",
+ "author",
+ "creator",
+ "developer",
+ "director",
+ "lead_actor",
+ "network",
+ "performer",
+ "publisher",
+ "showrunner",
+ "studio"
+ ])),
+ /**
+ * @accept image/*
+ * @maxSize 2000000
+ */
+ "poster": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.blob(),
+ [
+ /*#__PURE__*/ v.blobSize(2000000),
+ /*#__PURE__*/ v.blobAccept(["image/*"])
+ ]
+ )),
+ /**
+ * Poster or cover image URL for the creative work. Soon to be deprecated in favor of the 'poster' blob field
+ */
+ "posterUrl": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.genericUriString()),
+ /**
+ * @minimum 0
+ * @maximum 10
+ */
+ "rating": /*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.integer(),
+ [/*#__PURE__*/ v.integerRange(0, 10)]
+ ),
+ /**
+ * Release date of the creative work.
+ */
+ "releaseDate": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()),
+ "tags": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [/*#__PURE__*/ v.stringLength(0, 50)]
+ ))),
+ /**
+ * @maxLength 100000
+ */
+ "text": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [/*#__PURE__*/ v.stringLength(0, 100000)]
+ )),
+ /**
+ * Title of the creative work.
+ * @maxLength 1000
+ */
+ "title": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [/*#__PURE__*/ v.stringLength(0, 1000)]
+ )),
+ }
+ )
+);
+type crossPosts$schematype = typeof _crossPostsSchema;
+type identifiers$schematype = typeof _identifiersSchema;
+type main$schematype = typeof _mainSchema;
+
+export interface crossPostsSchema extends crossPosts$schematype {}
+
+export interface identifiersSchema extends identifiers$schematype {}
+
+export interface mainSchema extends main$schematype {}
+export const crossPostsSchema = _crossPostsSchema as crossPostsSchema;
+export const identifiersSchema = _identifiersSchema as identifiersSchema;
+export const mainSchema = _mainSchema as mainSchema;
+
+export interface CrossPosts extends v.InferInput {}
+
+export interface Identifiers extends v.InferInput {}
+
+export interface Main extends v.InferInput {}
+declare module '@atcute/lexicons/ambient' {
+ interface Records {
+ "social.popfeed.feed.review": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/social/popfeed/richtext/facet.ts b/src/lib/contrail/types/types/social/popfeed/richtext/facet.ts
new file mode 100644
index 0000000..07494b4
--- /dev/null
+++ b/src/lib/contrail/types/types/social/popfeed/richtext/facet.ts
@@ -0,0 +1,85 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+
+const _byteSliceSchema = /*#__PURE__*/ v.object(
+ {
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("social.popfeed.richtext.facet#byteSlice")),
+ /**
+ * @minimum 0
+ */
+ "byteEnd": /*#__PURE__*/ v.integer(),
+ /**
+ * @minimum 0
+ */
+ "byteStart": /*#__PURE__*/ v.integer(),
+ }
+);
+const _linkSchema = /*#__PURE__*/ v.object({
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("social.popfeed.richtext.facet#link")),
+ "uri": /*#__PURE__*/ v.genericUriString(),
+});
+const _mainSchema = /*#__PURE__*/ v.object(
+ {
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("social.popfeed.richtext.facet")),
+ get "features"() {
+ return /*#__PURE__*/ v.array(/*#__PURE__*/ v.variant([
+ linkSchema,
+ mentionSchema,
+ tagSchema
+ ]))
+ },
+ get "index"() {
+ return byteSliceSchema
+ },
+ }
+);
+const _mentionSchema = /*#__PURE__*/ v.object({
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("social.popfeed.richtext.facet#mention")),
+ "did": /*#__PURE__*/ v.didString(),
+});
+const _tagSchema = /*#__PURE__*/ v.object(
+ {
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("social.popfeed.richtext.facet#tag")),
+ /**
+ * @maxLength 640
+ * @maxGraphemes 64
+ */
+ "tag": /*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.string(),
+ [
+ /*#__PURE__*/ v.stringLength(0, 640),
+ /*#__PURE__*/ v.stringGraphemes(0, 64)
+ ]
+ ),
+ }
+);
+type byteSlice$schematype = typeof _byteSliceSchema;
+type link$schematype = typeof _linkSchema;
+type main$schematype = typeof _mainSchema;
+type mention$schematype = typeof _mentionSchema;
+type tag$schematype = typeof _tagSchema;
+
+export interface byteSliceSchema extends byteSlice$schematype {}
+
+export interface linkSchema extends link$schematype {}
+
+export interface mainSchema extends main$schematype {}
+
+export interface mentionSchema extends mention$schematype {}
+
+export interface tagSchema extends tag$schematype {}
+export const byteSliceSchema = _byteSliceSchema as byteSliceSchema;
+export const linkSchema = _linkSchema as linkSchema;
+export const mainSchema = _mainSchema as mainSchema;
+export const mentionSchema = _mentionSchema as mentionSchema;
+export const tagSchema = _tagSchema as tagSchema;
+
+export interface ByteSlice extends v.InferInput {}
+
+export interface Link extends v.InferInput {}
+
+export interface Main extends v.InferInput {}
+
+export interface Mention extends v.InferInput {}
+
+export interface Tag extends v.InferInput {}
diff --git a/src/lib/contrail/types/types/watch/atmo/comment/getRecord.ts b/src/lib/contrail/types/types/watch/atmo/comment/getRecord.ts
new file mode 100644
index 0000000..e623b3c
--- /dev/null
+++ b/src/lib/contrail/types/types/watch/atmo/comment/getRecord.ts
@@ -0,0 +1,70 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+import * as SocialPopfeedFeedComment from "../../../social/popfeed/feed/comment.js";
+
+const _mainSchema = /*#__PURE__*/ v.query(
+ "watch.atmo.comment.getRecord",
+ {
+ "params": /*#__PURE__*/ v.object(
+ {
+ /**
+ * Include indexed profile and identity information
+ */
+ "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 SocialPopfeedFeedComment.mainSchema
+ },
+ }
+ ),
+ }
+ }
+);
+const _profileEntrySchema = /*#__PURE__*/ v.object({
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("watch.atmo.comment.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 {
+ "watch.atmo.comment.getRecord": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/watch/atmo/comment/listRecords.ts b/src/lib/contrail/types/types/watch/atmo/comment/listRecords.ts
new file mode 100644
index 0000000..0fce879
--- /dev/null
+++ b/src/lib/contrail/types/types/watch/atmo/comment/listRecords.ts
@@ -0,0 +1,98 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+import * as SocialPopfeedFeedComment from "../../../social/popfeed/feed/comment.js";
+
+const _mainSchema = /*#__PURE__*/ v.query(
+ "watch.atmo.comment.listRecords",
+ {
+ "params": /*#__PURE__*/ v.object(
+ {
+ /**
+ * Filter by an indexed DID or cached handle
+ */
+ "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
+ ),
+ /**
+ * Include indexed profile and identity information
+ */
+ "profiles": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()),
+ }
+ ),
+ "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("watch.atmo.comment.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("watch.atmo.comment.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 SocialPopfeedFeedComment.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 {
+ "watch.atmo.comment.listRecords": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/watch/atmo/getCursor.ts b/src/lib/contrail/types/types/watch/atmo/getCursor.ts
new file mode 100644
index 0000000..03a1b6d
--- /dev/null
+++ b/src/lib/contrail/types/types/watch/atmo/getCursor.ts
@@ -0,0 +1,47 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+
+const _mainSchema = /*#__PURE__*/ v.query(
+ "watch.atmo.getCursor",
+ {
+ "params": null,
+ "output": {
+ "type": "lex",
+ "schema": /*#__PURE__*/ v.object(
+ {
+ get "position"() {
+ return /*#__PURE__*/ v.optional(sourcePositionSchema)
+ },
+ "updatedAt": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.integer()),
+ "updatedAtDate": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.datetimeString()),
+ }
+ ),
+ }
+ }
+);
+const _sourcePositionSchema = /*#__PURE__*/ v.object({
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("watch.atmo.getCursor#sourcePosition")),
+ "cursor": /*#__PURE__*/ v.string(),
+ "epoch": /*#__PURE__*/ v.string(),
+ "source": /*#__PURE__*/ v.string(),
+});
+type main$schematype = typeof _mainSchema;
+type sourcePosition$schematype = typeof _sourcePositionSchema;
+
+export interface mainSchema extends main$schematype {}
+
+export interface sourcePositionSchema extends sourcePosition$schematype {}
+export const mainSchema = _mainSchema as mainSchema;
+export const sourcePositionSchema = _sourcePositionSchema as sourcePositionSchema;
+
+export interface SourcePosition extends v.InferInput {}
+
+export interface $params {}
+
+export interface $output extends v.InferXRPCBodyInput {}
+declare module '@atcute/lexicons/ambient' {
+ interface XRPCQueries {
+ "watch.atmo.getCursor": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/watch/atmo/getProfile.ts b/src/lib/contrail/types/types/watch/atmo/getProfile.ts
new file mode 100644
index 0000000..7d93fb1
--- /dev/null
+++ b/src/lib/contrail/types/types/watch/atmo/getProfile.ts
@@ -0,0 +1,51 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+
+const _mainSchema = /*#__PURE__*/ v.query(
+ "watch.atmo.getProfile",
+ {
+ "params": /*#__PURE__*/ v.object({
+ "actor": /*#__PURE__*/ v.actorIdentifierString(),
+ }),
+ "output": {
+ "type": "lex",
+ "schema": /*#__PURE__*/ v.object(
+ {
+ get "profiles"() {
+ return /*#__PURE__*/ v.array(profileEntrySchema)
+ },
+ }
+ ),
+ }
+ }
+);
+const _profileEntrySchema = /*#__PURE__*/ v.object({
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("watch.atmo.getProfile#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 {
+ "watch.atmo.getProfile": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/watch/atmo/like/getRecord.ts b/src/lib/contrail/types/types/watch/atmo/like/getRecord.ts
new file mode 100644
index 0000000..a73796d
--- /dev/null
+++ b/src/lib/contrail/types/types/watch/atmo/like/getRecord.ts
@@ -0,0 +1,70 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+import * as SocialPopfeedFeedLike from "../../../social/popfeed/feed/like.js";
+
+const _mainSchema = /*#__PURE__*/ v.query(
+ "watch.atmo.like.getRecord",
+ {
+ "params": /*#__PURE__*/ v.object(
+ {
+ /**
+ * Include indexed profile and identity information
+ */
+ "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 SocialPopfeedFeedLike.mainSchema
+ },
+ }
+ ),
+ }
+ }
+);
+const _profileEntrySchema = /*#__PURE__*/ v.object({
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("watch.atmo.like.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 {
+ "watch.atmo.like.getRecord": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/watch/atmo/like/listRecords.ts b/src/lib/contrail/types/types/watch/atmo/like/listRecords.ts
new file mode 100644
index 0000000..b4fc5fc
--- /dev/null
+++ b/src/lib/contrail/types/types/watch/atmo/like/listRecords.ts
@@ -0,0 +1,98 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+import * as SocialPopfeedFeedLike from "../../../social/popfeed/feed/like.js";
+
+const _mainSchema = /*#__PURE__*/ v.query(
+ "watch.atmo.like.listRecords",
+ {
+ "params": /*#__PURE__*/ v.object(
+ {
+ /**
+ * Filter by an indexed DID or cached handle
+ */
+ "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
+ ),
+ /**
+ * Include indexed profile and identity information
+ */
+ "profiles": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()),
+ }
+ ),
+ "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("watch.atmo.like.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("watch.atmo.like.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 SocialPopfeedFeedLike.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 {
+ "watch.atmo.like.listRecords": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/watch/atmo/list/getRecord.ts b/src/lib/contrail/types/types/watch/atmo/list/getRecord.ts
new file mode 100644
index 0000000..efbe30b
--- /dev/null
+++ b/src/lib/contrail/types/types/watch/atmo/list/getRecord.ts
@@ -0,0 +1,70 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+import * as SocialPopfeedFeedList from "../../../social/popfeed/feed/list.js";
+
+const _mainSchema = /*#__PURE__*/ v.query(
+ "watch.atmo.list.getRecord",
+ {
+ "params": /*#__PURE__*/ v.object(
+ {
+ /**
+ * Include indexed profile and identity information
+ */
+ "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 SocialPopfeedFeedList.mainSchema
+ },
+ }
+ ),
+ }
+ }
+);
+const _profileEntrySchema = /*#__PURE__*/ v.object({
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("watch.atmo.list.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 {
+ "watch.atmo.list.getRecord": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/watch/atmo/list/listRecords.ts b/src/lib/contrail/types/types/watch/atmo/list/listRecords.ts
new file mode 100644
index 0000000..537a1eb
--- /dev/null
+++ b/src/lib/contrail/types/types/watch/atmo/list/listRecords.ts
@@ -0,0 +1,98 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+import * as SocialPopfeedFeedList from "../../../social/popfeed/feed/list.js";
+
+const _mainSchema = /*#__PURE__*/ v.query(
+ "watch.atmo.list.listRecords",
+ {
+ "params": /*#__PURE__*/ v.object(
+ {
+ /**
+ * Filter by an indexed DID or cached handle
+ */
+ "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
+ ),
+ /**
+ * Include indexed profile and identity information
+ */
+ "profiles": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()),
+ }
+ ),
+ "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("watch.atmo.list.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("watch.atmo.list.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 SocialPopfeedFeedList.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 {
+ "watch.atmo.list.listRecords": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/watch/atmo/listItem/getRecord.ts b/src/lib/contrail/types/types/watch/atmo/listItem/getRecord.ts
new file mode 100644
index 0000000..3ac28a9
--- /dev/null
+++ b/src/lib/contrail/types/types/watch/atmo/listItem/getRecord.ts
@@ -0,0 +1,70 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+import * as SocialPopfeedFeedListItem from "../../../social/popfeed/feed/listItem.js";
+
+const _mainSchema = /*#__PURE__*/ v.query(
+ "watch.atmo.listItem.getRecord",
+ {
+ "params": /*#__PURE__*/ v.object(
+ {
+ /**
+ * Include indexed profile and identity information
+ */
+ "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 SocialPopfeedFeedListItem.mainSchema
+ },
+ }
+ ),
+ }
+ }
+);
+const _profileEntrySchema = /*#__PURE__*/ v.object({
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("watch.atmo.listItem.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 {
+ "watch.atmo.listItem.getRecord": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/watch/atmo/listItem/listRecords.ts b/src/lib/contrail/types/types/watch/atmo/listItem/listRecords.ts
new file mode 100644
index 0000000..ff8d1d1
--- /dev/null
+++ b/src/lib/contrail/types/types/watch/atmo/listItem/listRecords.ts
@@ -0,0 +1,98 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+import * as SocialPopfeedFeedListItem from "../../../social/popfeed/feed/listItem.js";
+
+const _mainSchema = /*#__PURE__*/ v.query(
+ "watch.atmo.listItem.listRecords",
+ {
+ "params": /*#__PURE__*/ v.object(
+ {
+ /**
+ * Filter by an indexed DID or cached handle
+ */
+ "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
+ ),
+ /**
+ * Include indexed profile and identity information
+ */
+ "profiles": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()),
+ }
+ ),
+ "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("watch.atmo.listItem.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("watch.atmo.listItem.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 SocialPopfeedFeedListItem.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 {
+ "watch.atmo.listItem.listRecords": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/watch/atmo/notifyOfUpdate.ts b/src/lib/contrail/types/types/watch/atmo/notifyOfUpdate.ts
new file mode 100644
index 0000000..0cdbb50
--- /dev/null
+++ b/src/lib/contrail/types/types/watch/atmo/notifyOfUpdate.ts
@@ -0,0 +1,56 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+
+const _mainSchema = /*#__PURE__*/ v.procedure(
+ "watch.atmo.notifyOfUpdate",
+ {
+ "params": null,
+ "input": {
+ "type": "lex",
+ "schema": /*#__PURE__*/ v.object(
+ {
+ "uri": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.resourceUriString()),
+ /**
+ * @maxLength 25
+ */
+ "uris": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.constrain(
+ /*#__PURE__*/ v.array(/*#__PURE__*/ v.resourceUriString()),
+ [/*#__PURE__*/ v.arrayLength(0, 25)]
+ )),
+ }
+ ),
+ },
+ "output": {
+ "type": "lex",
+ "schema": /*#__PURE__*/ v.object(
+ {
+ /**
+ * @minimum 0
+ */
+ "deleted": /*#__PURE__*/ v.integer(),
+ "errors": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.array(/*#__PURE__*/ v.string())),
+ /**
+ * @minimum 0
+ */
+ "indexed": /*#__PURE__*/ v.integer(),
+ }
+ ),
+ }
+ }
+);
+type main$schematype = typeof _mainSchema;
+
+export interface mainSchema extends main$schematype {}
+export const mainSchema = _mainSchema as mainSchema;
+
+export interface $params {}
+
+export interface $input extends v.InferXRPCBodyInput {}
+
+export interface $output extends v.InferXRPCBodyInput {}
+declare module '@atcute/lexicons/ambient' {
+ interface XRPCProcedures {
+ "watch.atmo.notifyOfUpdate": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/watch/atmo/profile/getRecord.ts b/src/lib/contrail/types/types/watch/atmo/profile/getRecord.ts
new file mode 100644
index 0000000..c302be7
--- /dev/null
+++ b/src/lib/contrail/types/types/watch/atmo/profile/getRecord.ts
@@ -0,0 +1,70 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+import * as SocialPopfeedActorProfile from "../../../social/popfeed/actor/profile.js";
+
+const _mainSchema = /*#__PURE__*/ v.query(
+ "watch.atmo.profile.getRecord",
+ {
+ "params": /*#__PURE__*/ v.object(
+ {
+ /**
+ * Include indexed profile and identity information
+ */
+ "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 SocialPopfeedActorProfile.mainSchema
+ },
+ }
+ ),
+ }
+ }
+);
+const _profileEntrySchema = /*#__PURE__*/ v.object({
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("watch.atmo.profile.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 {
+ "watch.atmo.profile.getRecord": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/watch/atmo/profile/listRecords.ts b/src/lib/contrail/types/types/watch/atmo/profile/listRecords.ts
new file mode 100644
index 0000000..0e84039
--- /dev/null
+++ b/src/lib/contrail/types/types/watch/atmo/profile/listRecords.ts
@@ -0,0 +1,98 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+import * as SocialPopfeedActorProfile from "../../../social/popfeed/actor/profile.js";
+
+const _mainSchema = /*#__PURE__*/ v.query(
+ "watch.atmo.profile.listRecords",
+ {
+ "params": /*#__PURE__*/ v.object(
+ {
+ /**
+ * Filter by an indexed DID or cached handle
+ */
+ "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
+ ),
+ /**
+ * Include indexed profile and identity information
+ */
+ "profiles": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()),
+ }
+ ),
+ "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("watch.atmo.profile.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("watch.atmo.profile.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 SocialPopfeedActorProfile.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 {
+ "watch.atmo.profile.listRecords": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/watch/atmo/review/getRecord.ts b/src/lib/contrail/types/types/watch/atmo/review/getRecord.ts
new file mode 100644
index 0000000..0aa4d3b
--- /dev/null
+++ b/src/lib/contrail/types/types/watch/atmo/review/getRecord.ts
@@ -0,0 +1,70 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+import * as SocialPopfeedFeedReview from "../../../social/popfeed/feed/review.js";
+
+const _mainSchema = /*#__PURE__*/ v.query(
+ "watch.atmo.review.getRecord",
+ {
+ "params": /*#__PURE__*/ v.object(
+ {
+ /**
+ * Include indexed profile and identity information
+ */
+ "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 SocialPopfeedFeedReview.mainSchema
+ },
+ }
+ ),
+ }
+ }
+);
+const _profileEntrySchema = /*#__PURE__*/ v.object({
+ "$type": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.literal("watch.atmo.review.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 {
+ "watch.atmo.review.getRecord": mainSchema;
+ }
+}
diff --git a/src/lib/contrail/types/types/watch/atmo/review/listRecords.ts b/src/lib/contrail/types/types/watch/atmo/review/listRecords.ts
new file mode 100644
index 0000000..fe44e51
--- /dev/null
+++ b/src/lib/contrail/types/types/watch/atmo/review/listRecords.ts
@@ -0,0 +1,98 @@
+import type {} from '@atcute/lexicons';
+import * as v from '@atcute/lexicons/validations';
+import type {} from '@atcute/lexicons/ambient';
+import * as SocialPopfeedFeedReview from "../../../social/popfeed/feed/review.js";
+
+const _mainSchema = /*#__PURE__*/ v.query(
+ "watch.atmo.review.listRecords",
+ {
+ "params": /*#__PURE__*/ v.object(
+ {
+ /**
+ * Filter by an indexed DID or cached handle
+ */
+ "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
+ ),
+ /**
+ * Include indexed profile and identity information
+ */
+ "profiles": /*#__PURE__*/ v.optional(/*#__PURE__*/ v.boolean()),
+ }
+ ),
+ "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("watch.atmo.review.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("watch.atmo.review.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 SocialPopfeedFeedReview.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 {
+ "watch.atmo.review.listRecords": mainSchema;
+ }
+}
diff --git a/src/routes/(app)/_components/ItemPage.svelte b/src/routes/(app)/_components/ItemPage.svelte
index 32f982b..4de08ed 100644
--- a/src/routes/(app)/_components/ItemPage.svelte
+++ b/src/routes/(app)/_components/ItemPage.svelte
@@ -120,7 +120,7 @@
{#if selectedSection === 'similar' && data.recommendations.length > 0}
{:else if selectedSection === 'cast' && data.cast.length > 0}
-
+
{#each data.cast as castMember (castMember.id)}
-
+
{#each items as item (`${item.media_type}-${item.id}`)}
{/each}
diff --git a/src/routes/(app)/_lib/images.ts b/src/routes/(app)/_lib/images.ts
index 5ec3df7..3eb984b 100644
--- a/src/routes/(app)/_lib/images.ts
+++ b/src/routes/(app)/_lib/images.ts
@@ -17,6 +17,7 @@ export function logoUrl(path: string | null | undefined, size: LogoSize = 'w92')
}
export function posterUrl(path: string | null | undefined, size: PosterSize = 'w500') {
+ if (path?.startsWith('https://')) return path;
return path ? images.poster(path, size) : undefined;
}
diff --git a/src/routes/(app)/profile/[actor]/+layout.server.ts b/src/routes/(app)/profile/[actor]/+layout.server.ts
new file mode 100644
index 0000000..6a58b4a
--- /dev/null
+++ b/src/routes/(app)/profile/[actor]/+layout.server.ts
@@ -0,0 +1,62 @@
+import { contrail } from '$lib/contrail';
+import type { PageServerLoad } from './$types';
+
+export const load: PageServerLoad = async ({ params }) => {
+ const actor = params.actor;
+
+ const reviews = await contrail.get('watch.atmo.review.listRecords', {
+ params: {
+ actor,
+ profiles: true
+ }
+ });
+
+ const profile = await contrail.get('watch.atmo.getProfile', {
+ params: {
+ actor
+ }
+ });
+
+ if (!reviews.ok) return;
+
+ const data = reviews.data.records;
+ console.log(data);
+
+ // export type Item = {
+ // id: number;
+ // ref: TmdbRef;
+ // media_type: MediaKind;
+ // title: string;
+ // poster_path: string | null;
+ // backdrop_path: string | null;
+ // overview: string;
+ // order?: number;
+ // };
+
+ const items = data
+ .filter(
+ (review) =>
+ (review.value.creativeWorkType === 'tv_show' ||
+ review.value.creativeWorkType === 'movie') &&
+ review.value.identifiers.tmdbId &&
+ review.value.posterUrl &&
+ review.value.title
+ )
+ .map((review) => {
+ const type = review.value.creativeWorkType == 'tv_show' ? 'tv' : 'movie';
+ const id = (type == 'movie' ? `tmdb:m-` : `tmdb:t-`) + review.value.identifiers.tmdbId;
+
+ return {
+ id: review.value.identifiers.tmdbId,
+ ref: id,
+ media_type: type,
+ title: review.value.title ?? 'Untitled',
+ poster_path: review.value.posterUrl
+ };
+ });
+ console.log(reviews.data.profiles);
+
+ if (!profile.ok) return;
+
+ return { items, profile: profile.data };
+};
diff --git a/src/routes/(app)/profile/[actor]/+page.svelte b/src/routes/(app)/profile/[actor]/+page.svelte
new file mode 100644
index 0000000..2e00355
--- /dev/null
+++ b/src/routes/(app)/profile/[actor]/+page.svelte
@@ -0,0 +1,17 @@
+
+
+
+ {#if data?.items?.length > 0}
+
+ {:else}
+
+ {/if}
+
diff --git a/src/routes/demo/atproto/+page.server.ts b/src/routes/demo/atproto/+page.server.ts
new file mode 100644
index 0000000..bfd9d48
--- /dev/null
+++ b/src/routes/demo/atproto/+page.server.ts
@@ -0,0 +1,66 @@
+import { redirect } from '@sveltejs/kit';
+import type { PageServerLoad } from './$types';
+import { loadHandle, recentRecords, loadHandles, listRecords, parseUri } from '@svelte-atproto/oauth/helper';
+import { memory } from '@svelte-atproto/oauth/server/stores/memory';
+
+// In-memory cache for handle lookups β fine for dev. For prod, swap in
+// cloudflareKV or upstashRedis (any `Store` works).
+const profileCache = memory();
+const COLLECTION = 'xyz.statusphere.status';
+
+export const load: PageServerLoad = async ({ locals, url }) => {
+ if (!locals.did) {
+ const returnTo = encodeURIComponent(url.pathname + url.search);
+ redirect(302, `/demo/atproto/login?returnTo=${returnTo}`);
+ }
+
+ // Lightweight: just resolve the handle from the user's PDS.
+ // For richer Bluesky profile data (display name, avatar) swap to:
+ // import { loadBskyProfile } from '@svelte-atproto/oauth/bsky';
+ // const profile = await loadBskyProfile(locals.did, { cache: profileCache });
+ const handle = await loadHandle(locals.did, { cache: profileCache });
+
+ // Recent statuses globally, from the firehose via UFO. UFO is slightly
+ // behind the firehose, so we also pull the user's own records and merge
+ // them in front so just-published statuses show up immediately.
+ const [globalRecent, own] = await Promise.all([
+ recentRecords(COLLECTION),
+ locals.client
+ ? listRecords({ did: locals.did, collection: COLLECTION, client: locals.client, limit: 10 })
+ : Promise.resolve([])
+ ]);
+
+ const ownAsItems = own.map((r) => {
+ const parts = parseUri(r.uri);
+ const record = r.value as { $type: string; createdAt?: string; [k: string]: unknown };
+ const parsed = typeof record.createdAt === 'string' ? new Date(record.createdAt).getTime() : NaN;
+ const time_us = (Number.isFinite(parsed) ? parsed : Date.now()) * 1000;
+ return {
+ did: parts?.repo ?? locals.did,
+ collection: parts?.collection ?? COLLECTION,
+ rkey: parts?.rkey ?? '',
+ record,
+ time_us
+ };
+ });
+
+ // Own records first so they win the dedupe (UFO can be stale on a record
+ // the user just published). Then sort by time_us so the merged list is
+ // in true reverse-chronological order regardless of source.
+ const seen = new Set
();
+ const merged = [];
+ for (const item of [...ownAsItems, ...globalRecent]) {
+ const key = `${item.did}/${item.rkey}`;
+ if (seen.has(key)) continue;
+ seen.add(key);
+ merged.push(item);
+ }
+ const recent = merged.sort((a, b) => b.time_us - a.time_us);
+
+ // Resolve the author handles in parallel (cached).
+ // For richer profile data, swap `loadHandles` for:
+ // import { loadBskyProfiles } from '@svelte-atproto/oauth/bsky';
+ const authorDids = [...new Set(recent.map((r) => r.did))];
+ const authors = await loadHandles(authorDids, { cache: profileCache });
+ return { did: locals.did, handle, recent, authors };
+};
diff --git a/src/routes/demo/atproto/+page.svelte b/src/routes/demo/atproto/+page.svelte
new file mode 100644
index 0000000..7b21fd4
--- /dev/null
+++ b/src/routes/demo/atproto/+page.svelte
@@ -0,0 +1,52 @@
+
+
+
+
What's your status?
+
Hi {data.handle ?? data.did}.
+
+
+ {#each emojis as emoji}
+
+ {/each}
+
+
+ {#if data.recent?.length}
+
Recent statuses (firehose)
+
+ {#each data.recent as item}
+ -
+ {item.record.status}
+ @{data.authors[item.did] ?? item.did}
+ {item.record.createdAt}
+
+ {/each}
+
+ {/if}
+
+
+
diff --git a/src/routes/demo/atproto/login/+page.server.ts b/src/routes/demo/atproto/login/+page.server.ts
new file mode 100644
index 0000000..e578edb
--- /dev/null
+++ b/src/routes/demo/atproto/login/+page.server.ts
@@ -0,0 +1,19 @@
+import { redirect } from '@sveltejs/kit';
+import type { PageServerLoad } from './$types';
+
+const DEFAULT_RETURN_TO = '/demo/atproto';
+
+function safeReturnTo(value: string | null | undefined): string {
+ if (!value) return DEFAULT_RETURN_TO;
+ try {
+ const decoded = decodeURIComponent(value);
+ if (decoded.startsWith('/') && !decoded.startsWith('//')) return decoded;
+ } catch {}
+ return DEFAULT_RETURN_TO;
+}
+
+export const load: PageServerLoad = ({ locals, url }) => {
+ const returnTo = safeReturnTo(url.searchParams.get('returnTo'));
+ if (locals.did) redirect(302, returnTo);
+ return { returnTo };
+};
diff --git a/src/routes/demo/atproto/login/+page.svelte b/src/routes/demo/atproto/login/+page.svelte
new file mode 100644
index 0000000..318663a
--- /dev/null
+++ b/src/routes/demo/atproto/login/+page.svelte
@@ -0,0 +1,42 @@
+
+
+
+
Sign in with atproto
+
+
+
diff --git a/todo.md b/todo.md
new file mode 100644
index 0000000..0add4f0
--- /dev/null
+++ b/todo.md
@@ -0,0 +1,4 @@
+- login with atproto, show account button at bottom of side menu (if not logged in show login modal, otherwise open profile)
+- show profile: avatar, handle, reviews, lists
+- create reviews
+- show reviews in movie/show detail page
diff --git a/vite.config.ts b/vite.config.ts
index a2d09d6..78610e4 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -5,18 +5,24 @@ import adapter from '@sveltejs/adapter-auto';
import { sveltekit } from '@sveltejs/kit/vite';
export default defineConfig({
+ server: { host: '127.0.0.1' },
plugins: [
tailwindcss(),
sveltekit({
compilerOptions: {
// Force runes mode for the project, except for libraries. Can be removed in svelte 6.
- runes: ({ filename }) => filename.split(/[/\\]/).includes('node_modules') ? undefined : true
+ runes: ({ filename }) =>
+ filename.split(/[/\\]/).includes('node_modules') ? undefined : true
},
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
- adapter: adapter()
+ adapter: adapter(),
+
+ experimental: {
+ remoteFunctions: true
+ }
})
],
test: {