this repo has no description
32
fork

Configure Feed

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

Add tranquil-pds adapter

alice 245581f1 690a4c1d

+197 -11
+1
README.md
··· 55 55 56 56 - **`bring-your-own`** — the default. Works with any PDS that has accounts you can log into. 57 57 - **`perlsky`** — thin adapter for `perlsky`-specific defaults like cleanup prefixes. 58 + - **`tranquil-pds`** — thin adapter for `tranquil-pds`-specific defaults like cleanup prefixes and hosted-handle examples. 58 59 59 60 Other PDS projects (rsky, pegasus, etc.) can add their own adapters without changing the core browser flows. The adapter contract is documented in [docs/ADAPTERS.md](./docs/ADAPTERS.md). 60 61
+9
docs/ADAPTERS.md
··· 56 56 `perlsky` still owns the higher-level workflows around invites, reusable smoke 57 57 pairs, and local wrappers. Those helpers do not live in `atproto-smoke`. 58 58 59 + ### `tranquil-pds` 60 + 61 + Use the same core browser flows, but apply `tranquil-pds`-specific defaults 62 + such as cleanup prefixes, adapter tagging, and hosted-handle examples. 63 + 64 + `tranquil-pds` can self-register accounts over the standard AT Protocol 65 + create-account endpoint, but the bootstrap workflow still lives outside the 66 + generic smoke suite. 67 + 59 68 ## Minimal Adapter Contract 60 69 61 70 The current registry lives in `src/adapters/registry.mjs`. A built-in adapter
+14
examples/tranquil-pds-dual.json
··· 1 + { 2 + "pdsUrl": "https://tranquil.mosphere.at", 3 + "artifactsDir": "data/browser-smoke/tranquil-pds-dual", 4 + "targetHandle": "alice.tranquil.mosphere.at", 5 + "strictErrors": true, 6 + "primary": { 7 + "handle": "smoke-primary.tranquil.mosphere.at", 8 + "password": "replace-me" 9 + }, 10 + "secondary": { 11 + "handle": "smoke-secondary.tranquil.mosphere.at", 12 + "password": "replace-me-too" 13 + } 14 + }
+3 -1
package.json
··· 10 10 "print-example:dual": "node bin/atproto-smoke.mjs print-example --mode dual", 11 11 "validate:example:single": "node bin/atproto-smoke.mjs validate --mode single --config examples/bring-your-own-single.json", 12 12 "validate:example:dual": "node bin/atproto-smoke.mjs validate --mode dual --config examples/bring-your-own-dual.json", 13 - "validate:example:perlsky": "node bin/atproto-smoke.mjs validate --mode dual --adapter perlsky --config examples/perlsky-dual.json" 13 + "validate:example:perlsky": "node bin/atproto-smoke.mjs validate --mode dual --adapter perlsky --config examples/perlsky-dual.json", 14 + "validate:example:tranquil-pds": "node bin/atproto-smoke.mjs validate --mode dual --adapter tranquil-pds --config examples/tranquil-pds-dual.json" 14 15 }, 15 16 "engines": { 16 17 "node": ">=20" ··· 31 32 "./config": "./src/config.mjs", 32 33 "./adapters/bring-your-own": "./src/adapters/bring-your-own.mjs", 33 34 "./adapters/perlsky": "./src/adapters/perlsky.mjs", 35 + "./adapters/tranquil-pds": "./src/adapters/tranquil-pds.mjs", 34 36 "./adapters/registry": "./src/adapters/registry.mjs", 35 37 "./browser/run-single": "./src/browser/run-single.mjs", 36 38 "./browser/run-dual": "./src/browser/run-dual.mjs",
+2
src/adapters/registry.mjs
··· 1 1 import { BRING_YOUR_OWN_ADAPTER } from './bring-your-own.mjs'; 2 2 import { PERLSKY_ADAPTER } from './perlsky.mjs'; 3 + import { TRANQUIL_PDS_ADAPTER } from './tranquil-pds.mjs'; 3 4 4 5 /** 5 6 * Adapter definitions normalize raw user config into smoke-suite config. ··· 15 16 export const ADAPTERS = Object.freeze({ 16 17 [BRING_YOUR_OWN_ADAPTER.name]: BRING_YOUR_OWN_ADAPTER, 17 18 [PERLSKY_ADAPTER.name]: PERLSKY_ADAPTER, 19 + [TRANQUIL_PDS_ADAPTER.name]: TRANQUIL_PDS_ADAPTER, 18 20 }); 19 21 20 22 export const ADAPTER_NAMES = Object.freeze(Object.keys(ADAPTERS));
+124
src/adapters/tranquil-pds.mjs
··· 1 + import { 2 + createAccountConfig, 3 + createDualRunConfig, 4 + createSingleRunConfig, 5 + } from '../config.mjs'; 6 + 7 + export const TRANQUIL_PDS_PRIMARY_CLEANUP_PREFIXES = Object.freeze([ 8 + 'tranquil browser smoke ', 9 + ]); 10 + 11 + export const TRANQUIL_PDS_SECONDARY_CLEANUP_PREFIXES = Object.freeze([ 12 + 'tranquil browser secondary ', 13 + ]); 14 + 15 + const tranquilRoleDefaults = (role) => { 16 + if (role === 'secondary') { 17 + return { 18 + postText: 'tranquil browser secondary post', 19 + quoteText: 'tranquil browser secondary quote', 20 + replyText: 'tranquil browser secondary reply', 21 + profileNote: 'tranquil browser secondary profile edit', 22 + }; 23 + } 24 + 25 + return { 26 + postText: 'tranquil browser smoke post', 27 + quoteText: 'tranquil browser smoke quote', 28 + replyText: 'tranquil browser smoke reply', 29 + profileNote: 'tranquil browser smoke profile edit', 30 + }; 31 + }; 32 + 33 + const createTranquilExampleConfig = ({ mode }) => { 34 + const base = { 35 + pdsUrl: 'https://tranquil.mosphere.at', 36 + artifactsDir: `data/browser-smoke/tranquil-pds-${mode}`, 37 + targetHandle: 'alice.tranquil.mosphere.at', 38 + strictErrors: true, 39 + }; 40 + 41 + if (mode === 'single') { 42 + return { 43 + ...base, 44 + editProfile: true, 45 + account: { 46 + handle: 'smoke-primary.tranquil.mosphere.at', 47 + password: 'replace-me', 48 + }, 49 + }; 50 + } 51 + 52 + return { 53 + ...base, 54 + primary: { 55 + handle: 'smoke-primary.tranquil.mosphere.at', 56 + password: 'replace-me', 57 + }, 58 + secondary: { 59 + handle: 'smoke-secondary.tranquil.mosphere.at', 60 + password: 'replace-me-too', 61 + }, 62 + }; 63 + }; 64 + 65 + export const createTranquilPdsAccountConfig = ({ 66 + role = 'primary', 67 + ...account 68 + } = {}) => { 69 + const cleanupPostPrefixes = role === 'secondary' 70 + ? TRANQUIL_PDS_SECONDARY_CLEANUP_PREFIXES 71 + : TRANQUIL_PDS_PRIMARY_CLEANUP_PREFIXES; 72 + 73 + return createAccountConfig({ 74 + cleanupPostPrefixes, 75 + ...tranquilRoleDefaults(role), 76 + ...account, 77 + }); 78 + }; 79 + 80 + export const createTranquilPdsSingleConfig = ({ 81 + account, 82 + ...rest 83 + } = {}) => { 84 + return createSingleRunConfig({ 85 + ...rest, 86 + adapter: 'tranquil-pds', 87 + account: createTranquilPdsAccountConfig({ 88 + role: 'primary', 89 + ...account, 90 + }), 91 + }); 92 + }; 93 + 94 + export const createTranquilPdsDualConfig = ({ 95 + primary, 96 + secondary, 97 + ...rest 98 + } = {}) => { 99 + return createDualRunConfig({ 100 + ...rest, 101 + adapter: 'tranquil-pds', 102 + primary: createTranquilPdsAccountConfig({ 103 + role: 'primary', 104 + ...primary, 105 + }), 106 + secondary: createTranquilPdsAccountConfig({ 107 + role: 'secondary', 108 + ...secondary, 109 + }), 110 + }); 111 + }; 112 + 113 + export const TRANQUIL_PDS_ADAPTER = Object.freeze({ 114 + name: 'tranquil-pds', 115 + description: 'Use tranquil-pds-flavored defaults like cleanup prefixes and hosted example handles.', 116 + accountStrategy: 'self-register-or-existing-accounts', 117 + notes: [ 118 + 'The standalone suite still expects credentials in the config.', 119 + 'tranquil-pds can self-register accounts via com.atproto.server.createAccount, but that bootstrap stays outside the generic smoke runner.', 120 + ], 121 + createSingleConfig: createTranquilPdsSingleConfig, 122 + createDualConfig: createTranquilPdsDualConfig, 123 + createExampleConfig: createTranquilExampleConfig, 124 + });
+5 -2
src/browser/lib/dual-actions.mjs
··· 91 91 text: normalize(node.textContent || ''), 92 92 })); 93 93 const pick = (pattern) => entries.find((entry) => pattern.test(entry.href))?.text; 94 + const bodyText = normalize(document.body?.innerText || ''); 95 + const followersFallback = bodyText.match(/([0-9][0-9.,]*\s*[KMB]?)\s+followers?/i)?.[0]; 96 + const followsFallback = bodyText.match(/([0-9][0-9.,]*\s*[KMB]?)\s+(?:following|follows?)/i)?.[0]; 94 97 return { 95 - followersText: pick(/\/followers(?:[/?#]|$)/i), 96 - followsText: pick(/\/follows(?:[/?#]|$)/i), 98 + followersText: pick(/\/followers(?:[/?#]|$)/i) || followersFallback, 99 + followsText: pick(/\/follows(?:[/?#]|$)/i) || followsFallback, 97 100 }; 98 101 }); 99 102
+17 -2
src/browser/lib/dual-api.mjs
··· 47 47 if (body !== undefined) { 48 48 headers['content-type'] = 'application/json'; 49 49 } 50 - return fetchJson(url.toString(), { 50 + const run = (extraHeaders = {}) => fetchJson(url.toString(), { 51 51 method, 52 - headers, 52 + headers: { 53 + ...headers, 54 + ...extraHeaders, 55 + }, 53 56 timeoutMs, 54 57 body: body === undefined ? undefined : JSON.stringify(body), 55 58 }); 59 + const result = await run(); 60 + if ( 61 + !result.ok && 62 + result.status === 501 && 63 + nsid.startsWith('app.bsky.') && 64 + /atproto-proxy/i.test(result.text || '') 65 + ) { 66 + return run({ 67 + 'atproto-proxy': 'did:web:api.bsky.app#bsky_appview', 68 + }); 69 + } 70 + return result; 56 71 }; 57 72 58 73 const listOwnRecords = async (account, collection, limit = 100) => {
+14
src/browser/lib/lists.mjs
··· 144 144 await openAddPeopleToList(page); 145 145 await searchAddPeopleList(page, handle); 146 146 const add = page.getByRole('button', { name: /add user to list/i }).last(); 147 + if (!(await add.count())) { 148 + const shortHandle = handle.replace(/^@/, ''); 149 + const profileLink = page 150 + .locator('[role="dialog"]') 151 + .last() 152 + .getByRole('link', { name: new RegExp(`@?${shortHandle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`, 'i') }) 153 + .last(); 154 + if (await profileLink.count()) { 155 + throw new Error( 156 + `search result for @${shortHandle} rendered in "Add people to list" modal, but no add action was available`, 157 + ); 158 + } 159 + throw new Error(`no add action was available for @${shortHandle} in "Add people to list" modal`); 160 + } 147 161 await add.click({ noWaitAfter: true }); 148 162 await wait(page, 2000); 149 163 const remove = page.getByRole('button', { name: /remove user from list/i }).last();
+8 -6
src/cli.mjs
··· 1 1 import fs from 'node:fs/promises'; 2 - import { getAdapter, listAdapters } from './adapters/registry.mjs'; 2 + import { ADAPTER_NAMES, getAdapter, listAdapters } from './adapters/registry.mjs'; 3 + 4 + const adapterUsage = ADAPTER_NAMES.join('|'); 3 5 4 6 const usage = `Usage: 5 - atproto-smoke run-single [--adapter bring-your-own|perlsky] --config config.json 6 - atproto-smoke run-dual [--adapter bring-your-own|perlsky] --config config.json 7 - atproto-smoke validate --mode single|dual [--adapter bring-your-own|perlsky] --config config.json 8 - atproto-smoke write-example --mode single|dual [--adapter bring-your-own|perlsky] --output config.json 9 - atproto-smoke print-example --mode single|dual [--adapter bring-your-own|perlsky] 7 + atproto-smoke run-single [--adapter ${adapterUsage}] --config config.json 8 + atproto-smoke run-dual [--adapter ${adapterUsage}] --config config.json 9 + atproto-smoke validate --mode single|dual [--adapter ${adapterUsage}] --config config.json 10 + atproto-smoke write-example --mode single|dual [--adapter ${adapterUsage}] --output config.json 11 + atproto-smoke print-example --mode single|dual [--adapter ${adapterUsage}] 10 12 atproto-smoke list-adapters 11 13 12 14 Notes: