···11-import * as v from 'valibot';
22-31import { At } from '@atcute/client/lexicons';
4253import { didDocument, DidDocument } from '../types/did-doc';
···5048 throw new Error(`unsupported did method`);
5149 }
52505353- return v.parse(didDocument, rawDoc);
5151+ return didDocument.parse(rawDoc, { mode: 'passthrough' });
5452};
+1-3
src/api/queries/plc.ts
···11-import * as v from 'valibot';
22-31import { At } from '@atcute/client/lexicons';
4253import { plcLogEntries } from '../types/plc';
···1210 }
13111412 const json = await response.json();
1515- return v.parse(plcLogEntries, json);
1313+ return plcLogEntries.parse(json);
1614};
+44-66
src/api/types/did-doc.ts
···11-import * as v from 'valibot';
11+import * as v from '@badrap/valita';
2233import { didString, serviceUrlString, urlString } from './strings';
44+55+const PUBLIC_KEY_MULTIBASE_RE = /^z[a-km-zA-HJ-NP-Z1-9]+$/;
4657const verificationMethod = v.object({
68 id: v.string(),
79 type: v.string(),
810 controller: didString,
99- publicKeyMultibase: v.optional(
1010- v.pipe(
1111- v.string(),
1212- v.regex(/^z[a-km-zA-HJ-NP-Z1-9]+$|^u[a-zA-Z0-9-_]+$/, 'must be a valid multibase value'),
1313- ),
1414- ),
1111+ publicKeyMultibase: v
1212+ .string()
1313+ .assert((input) => PUBLIC_KEY_MULTIBASE_RE.test(input), `must be a valid base58btc multibase key`),
1514});
16151717-const service = v.pipe(
1818- v.object({
1616+const service = v
1717+ .object({
1918 id: v.string(),
2019 type: v.string(),
2121- serviceEndpoint: v.union([urlString, v.record(v.string(), urlString), v.array(urlString)]),
2222- }),
2323- v.forward(
2424- v.check((input) => {
2525- switch (input.type) {
2626- case 'AtprotoPersonalDataServer':
2727- case 'AtprotoLabeler':
2828- case 'BskyFeedGenerator':
2929- case 'BskyNotificationService':
3030- return v.is(serviceUrlString, input.serviceEndpoint);
2020+ serviceEndpoint: v.union(urlString, v.record(urlString), v.array(urlString)),
2121+ })
2222+ .chain((input) => {
2323+ switch (input.type) {
2424+ case 'AtprotoPersonalDataServer':
2525+ case 'AtprotoLabeler':
2626+ case 'BskyFeedGenerator':
2727+ case 'BskyNotificationService': {
2828+ const result = serviceUrlString.try(input.serviceEndpoint);
2929+ if (!result.ok) {
3030+ return v.err({
3131+ message: `must be a valid atproto service url`,
3232+ path: ['serviceEndpoint'],
3333+ });
3434+ }
3135 }
3636+ }
32373333- return true;
3434- }, 'must be a valid atproto service endpoint'),
3535- ['serviceEndpoint'],
3636- ),
3737-);
3838+ return v.ok(input);
3939+ });
38403941export const didDocument = v.object({
4042 '@context': v.array(urlString),
4143 id: didString,
4242- alsoKnownAs: v.optional(v.array(urlString), []),
4343- verificationMethod: v.optional(v.array(verificationMethod), []),
4444- service: v.optional(
4545- v.pipe(
4646- v.array(service),
4747- v.rawCheck(({ dataset, addIssue }) => {
4848- if (dataset.typed) {
4949- const set = new Set<string>();
5050- const services = dataset.value;
4444+ alsoKnownAs: v.array(urlString).optional(() => []),
4545+ verificationMethod: v.array(verificationMethod).optional(() => []),
4646+ service: v.array(service).chain((input) => {
4747+ for (let i = 0, len = input.length; i < len; i++) {
4848+ const service = input[i];
4949+ const id = service.id;
51505252- for (let idx = 0, len = services.length; idx < len; idx++) {
5353- const service = services[idx];
5454- const id = service.id;
5151+ for (let j = 0; j < i; j++) {
5252+ if (input[j].id === id) {
5353+ return v.err({
5454+ message: `duplicate service id`,
5555+ path: [i, 'id'],
5656+ });
5757+ }
5858+ }
5959+ }
55605656- if (!set.has(id)) {
5757- set.add(id);
5858- } else {
5959- addIssue({
6060- message: `duplicate service id`,
6161- path: [
6262- {
6363- type: 'array',
6464- origin: 'value',
6565- input: services,
6666- key: idx,
6767- value: service,
6868- },
6969- {
7070- type: 'object',
7171- origin: 'value',
7272- input: service,
7373- key: 'id',
7474- value: id,
7575- },
7676- ],
7777- });
7878- }
7979- }
8080- }
8181- }),
8282- ),
8383- [],
8484- ),
6161+ return v.ok(input);
6262+ }),
8563});
86648787-export type DidDocument = v.InferOutput<typeof didDocument>;
6565+export type DidDocument = v.Infer<typeof didDocument>;
88668967export const getPdsEndpoint = (doc: DidDocument): string | undefined => {
9068 return getServiceEndpoint(doc, '#atproto_pds', 'AtprotoPersonalDataServer');