chadtmiller.com/knotless
Git hosted in your atproto account, on any stock PDS, fast enough for agents.
git clone knotless://chadtmiller.com/knotlessneeds the knotless git helper. set it up
// The screenshots without a PDS: what knotless-serve answered for each page,
// recorded once in fixtures.json, and played back by a stand-in for it in
// front of the pages. The pages are drawn as they are by the real app, from
// web/build; only their data is fixed.
//
// node harness/shots/fixtures.mjs record them again, from the world
// `just shots-up` keeps up, or a new
// one: `just shots-record`
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
import { createServer, request } from 'node:http'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { base, create, pages, port, root, saved, serve, until } from './world.mjs'
export const fixtures = join(root, 'harness/shots/fixtures.json')
/// What knotless-serve answers itself, rather than handing to the pages.
const answered = (path) => /^\/(_data|xrpc)\//.test(path) || /^\/[^/]+\/[^/]+\/raw\//.test(path)
/// The headers of an answer worth keeping.
const KEPT = ['content-type', 'cache-control', 'location', 'content-security-policy']
/// knotless-serve's place on `port`, in front of the pages: answering from
/// `recorded`, or, given `from`, asking knotless-serve there and recording
/// what it says. Returns the recording, and how to stop.
export async function front({ recorded = {}, from } = {}) {
const drawn = pages(port + 1, base)
const answer = async (path) => {
if (!from) return recorded[path]
const res = await fetch(from + path, { redirect: 'manual' })
const body = Buffer.from(await res.arrayBuffer())
const text = body.toString('utf8')
const binary = !Buffer.from(text, 'utf8').equals(body)
const headers = Object.fromEntries(KEPT.filter((h) => res.headers.has(h)).map((h) => [h, res.headers.get(h)]))
return (recorded[path] = { status: res.status, headers, ...(binary ? { base64: body.toString('base64') } : { body: text }) })
}
const server = createServer(async (req, res) => {
if (answered(req.url)) {
const a = await answer(req.url).catch(() => null)
if (!a) {
res.writeHead(404, { 'content-type': 'application/json' })
res.end(JSON.stringify({ message: `nothing recorded for ${req.url}` }))
return
}
res.writeHead(a.status, a.headers)
res.end(a.base64 === undefined ? a.body : Buffer.from(a.base64, 'base64'))
return
}
const headers = { ...req.headers, 'x-forwarded-host': req.headers.host, 'x-forwarded-proto': 'http' }
const on = request({ host: '127.0.0.1', port: port + 1, path: req.url, method: req.method, headers }, (up) => {
res.writeHead(up.statusCode, up.headers)
up.pipe(res)
})
on.on('error', (e) => {
res.writeHead(502)
res.end(`The pages are not being drawn: ${e.message}`)
})
req.pipe(on)
})
await new Promise((r) => server.listen(port, '127.0.0.1', r))
const kill = () => {
server.close()
drawn.kill()
}
try {
await until('the pages', async () => (await fetch(`${base}/start`)).ok)
} catch (e) {
kill()
throw e
}
return { recorded, kill }
}
/// The recording, if there is one.
export const recording = () => (existsSync(fixtures) ? JSON.parse(readFileSync(fixtures, 'utf8')) : null)
/// Records every page's answers from `world`, and writes them with the pages.
async function record(world) {
const at = port + 2
const server = await serve(world, { at, alone: true })
const stand = await front({ from: `http://127.0.0.1:${at}` })
try {
for (const path of Object.values(world.views)) {
const html = await (await fetch(base + path)).text()
// What the browser asks for itself: images, from the raw files.
for (const [, src] of html.matchAll(/src="(\/[^"]+\/raw\/[^"]+)"/g)) await fetch(base + src)
}
} finally {
stand.kill()
server.kill()
}
const sorted = Object.fromEntries(Object.entries(stand.recorded).sort(([a], [b]) => (a < b ? -1 : 1)))
writeFileSync(fixtures, JSON.stringify({ views: world.views, answers: sorted }, null, 1) + '\n')
return Object.keys(sorted).length
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const kept = existsSync(saved) ? JSON.parse(readFileSync(saved, 'utf8')) : null
const up = kept && (await fetch(`${kept.net.pds}/xrpc/com.atproto.server.describeServer`).then((r) => r.ok, () => false))
const world = up ? kept : await create()
// A new world's server is stopped first: the recording starts its own.
if (!up) world.server.kill()
const n = await record(world)
if (!up) world.stop()
console.log(`recorded ${n} answers for ${Object.keys(world.views).length} pages in ${fixtures}`)
process.exit(0)
}