~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atlibs-cocore

0
0
main
62 lines · 2.9 KB Raw History
import { BrowserOAuthClient } from '@atproto/oauth-client-browser'
import { pageClientId, requestScope } from 'atlibs/oauth/index.js'
import { createPluginContext } from 'atlibs/plugin-context/index.js'
import { localModule } from 'atlibs/run-module/index.js'

import { NAMES, loadPolicy, runAction } from './cocore-page.js'

// Wiring only: one sign-in with the union of the modules' scopes, a form per action from module.json,
// each run held to its own module's session by runAction.
const base = new URL('./', import.meta.url)
const ctx = createPluginContext({ id: 'cocore' })
const modules = Object.fromEntries(NAMES.map((name) => [name, localModule(new URL(`modules/${name}.wasm`, base), new URL(`modules/${name}.json`, base))]))
const policy = await loadPolicy(base)
const clientId = await pageClientId(requestScope(policy, NAMES))
const client = await BrowserOAuthClient.load({ clientId, handleResolver: 'https://bsky.social' })
const session = (await client.init().catch((e) => (alert(e.message), undefined)))?.session

const $ = (selector) => document.querySelector(selector)
$('#login').hidden = Boolean(session)
$('#signed-in').hidden = !session
if (session) $('#who').textContent = session.did

$('#login').onsubmit = async (e) => {
	e.preventDefault()
	await client.signIn(new FormData(e.target).get('handle')).catch((error) => ($('#login-error').textContent = error.message))
}
$('#logout').onclick = async () => {
	await client.revoke(session.did).catch(() => undefined)
	location.reload()
}

function build(tag, props = {}, children = []) {
	const node = Object.assign(document.createElement(tag), props)
	node.append(...children)
	return node
}

for (const name of NAMES) {
	const spec = await modules[name] // name, description, actions, calls...
	const section = build('details', {}, [build('summary', { textContent: `${spec.name}: ${spec.description}` })])
	for (const action of spec.actions) {
		const out = build('pre', { className: 'out' })
		const fields = (action.fields ?? []).map(({ key, label }) => build('label', { textContent: label }, [key === 'prompt' || key === 'bio' ? build('textarea', { name: key, rows: 3 }) : build('input', { name: key })]))
		const form = build('form', {}, [build('h4', { textContent: action.label }), build('small', { textContent: action.description ?? '' }), ...fields, build('button', { textContent: action.label }), out])
		form.onsubmit = async (e) => {
			e.preventDefault()
			const button = form.querySelector('button')
			button.setAttribute('aria-busy', 'true')
			out.textContent = ''
			try {
				const input = Object.fromEntries([...new FormData(form)].filter(([, v]) => String(v).trim() !== ''))
				out.textContent = JSON.stringify(await runAction({ ctx, policy, modules, session }, name, action.entry, input), null, 2)
			} catch (error) {
				out.textContent = error.message
			} finally {
				button.removeAttribute('aria-busy')
			}
		}
		section.append(form)
	}
	$('#actions').append(section)
}