From e67d01da59ccb61c2af68433d206969cfddcb326 Mon Sep 17 00:00:00 2001 From: Seongmin Lee Date: Fri, 2 Oct 2026 23:57:44 +0900 Subject: [PATCH] web: `sh.tangled.repo` -> `org.tangled.repo.manifest` Signed-off-by: Seongmin Lee --- web/src/lib/api/descriptors.ts | 12 +++-- web/src/lib/api/labels.ts | 20 +------- web/src/lib/api/notifications.test.ts | 37 ++++++++------ web/src/lib/api/notifications.ts | 22 ++++----- web/src/lib/api/pullCompose.ts | 32 +++++++----- web/src/lib/api/records.ts | 2 + web/src/lib/api/repo.test.ts | 49 +++++-------------- web/src/lib/api/repo.ts | 43 ++++++++++------ web/src/lib/api/repoCreate.ts | 2 +- web/src/lib/api/repoSettings.test.ts | 11 +++-- web/src/lib/api/repoSettings.ts | 14 +++--- web/src/lib/api/spindle.ts | 3 +- web/src/lib/components/profile/pages.test.ts | 46 ++++++++--------- web/src/lib/components/profile/pages.ts | 39 ++++++++------- .../components/repo/SubjectLabelPanel.svelte | 4 +- .../settings/RepoGeneralForm.svelte | 9 ++-- web/src/lib/server/repo.ts | 3 +- web/src/routes/[handle]/[repo]/+layout.ts | 45 ++++++----------- .../[repo]/commit-statuses/+server.ts | 7 ++- .../[repo]/pipelines/pipelines.test.ts | 10 ++-- .../[version]/[[range]]/PullViewPage.svelte | 6 ++- .../[repo]/settings/labels/new/+page.svelte | 2 +- .../[repo]/settings/pipelines/+page.svelte | 2 +- .../[repo]/settings/rename/+page.svelte | 5 +- web/src/routes/[handle]/handle.test.ts | 7 ++- 25 files changed, 209 insertions(+), 223 deletions(-) diff --git a/web/src/lib/api/descriptors.ts b/web/src/lib/api/descriptors.ts index 3520ed417..5244613d1 100644 --- a/web/src/lib/api/descriptors.ts +++ b/web/src/lib/api/descriptors.ts @@ -11,7 +11,7 @@ import { // author docs come back keyed by the author's did export const REPO_OWNER_DOCS: DocDescriptor = { - source: "sh.tangled.repo:.repo", + source: "org.tangled.repo.manifest:.repo", type: TYPE_MINIDOC }; export const ISSUE_AUTHOR_DOCS: DocDescriptor = { @@ -32,9 +32,15 @@ export const REACTION_AUTHOR_DOCS: DocDescriptor = { }; // without a viewer the view has no star state, so cards can't tell starred from unknown -export const REPO_VIEW: RepoViewDescriptor = { source: "sh.tangled.repo:.", type: TYPE_REPO_VIEW }; +export const REPO_VIEW: RepoViewDescriptor = { + source: "org.tangled.repo.manifest:.", + type: TYPE_REPO_VIEW +}; -export const REPO_COUNT: CountDescriptor = { source: "sh.tangled.repo:subject", type: TYPE_COUNT }; +export const REPO_COUNT: CountDescriptor = { + source: "org.tangled.repo.manifest:subject", + type: TYPE_COUNT +}; export const STRING_COUNT: CountDescriptor = { source: "sh.tangled.string:subject", type: TYPE_COUNT diff --git a/web/src/lib/api/labels.ts b/web/src/lib/api/labels.ts index 88cc69cc0..fa3ebb8f4 100644 --- a/web/src/lib/api/labels.ts +++ b/web/src/lib/api/labels.ts @@ -6,8 +6,6 @@ import { createBobbinClient, type BobbinContext, type XrpcRequestInit } from "$l import type * as ShTangledLabelDefinition from "$lib/api/lexicons/types/sh/tangled/label/definition"; import type * as ShTangledLabelOp from "$lib/api/lexicons/types/sh/tangled/label/op"; import { collect } from "$lib/api/pagination"; -import type { RecordView, RepoRecord } from "$lib/api/records"; -import { saveRepoRecord } from "$lib/api/repoSettings"; import { createRecord, deleteRecord, type WrittenRecord } from "$lib/api/write"; import { didFromUri, rkeyFromUri } from "$lib/api/uri"; @@ -64,9 +62,9 @@ export interface CustomLabelInput { export const createRepoLabel = async ( agent: OAuthUserAgent, bobbinUrl: string, - repo: RecordView, input: CustomLabelInput ): Promise => { + // TODO: create label under repository's authority const value: LabelDefinition = { $type: "sh.tangled.label.definition", name: input.name, @@ -84,15 +82,6 @@ export const createRepoLabel = async ( tidNow() ); await awaitIndexedOrSaved(createBobbinClient({ serviceUrl: bobbinUrl }), written); - try { - await saveRepoRecord(agent, bobbinUrl, repo, { - labels: [...(repo.value.labels ?? []), written.uri] - }); - } catch (cause) { - // roll back the orphaned definition; an unsubscribed label is invisible - await deleteLabelDefinition(agent, written.uri).catch(() => {}); - throw cause; - } return { uri: written.uri, value }; }; @@ -102,15 +91,8 @@ export const deleteLabelDefinition = (agent: OAuthUserAgent, uri: string): Promi export const deleteRepoLabel = async ( agent: OAuthUserAgent, bobbinUrl: string, - repo: RecordView, uri: string ): Promise => { - const labels: readonly string[] = repo.value.labels ?? []; - if (labels.includes(uri)) { - await saveRepoRecord(agent, bobbinUrl, repo, { - labels: labels.filter((candidate) => candidate !== uri) - }); - } await deleteLabelDefinition(agent, uri); await awaitDeletedRecord(createBobbinClient({ serviceUrl: bobbinUrl }), uri as ResourceUri); }; diff --git a/web/src/lib/api/notifications.test.ts b/web/src/lib/api/notifications.test.ts index ad31507d9..33b5ece0b 100644 --- a/web/src/lib/api/notifications.test.ts +++ b/web/src/lib/api/notifications.test.ts @@ -12,26 +12,33 @@ const collaborationOffer: NotificationItem = { repoDid: "did:plc:scallop" }; +// the knot is the pds hosting the repo did const repoRecord = (knot: string) => ({ - $type: "sh.tangled.repo", - name: "shoal", - knot, - repoDid: "did:plc:scallop", - createdAt: "2026-05-01T00:00:00.000Z" + $type: "org.tangled.repo.manifest", + declaration: { owner: "did:plc:akshay", slug: "shoal" }, + createdAt: "2026-05-01T00:00:00.000Z", + pds: `https://${knot}` }); -const ctxWithRepo = (value: Record) => +const ctxWithRepo = ({ pds, ...value }: ReturnType) => createBobbinClient({ serviceUrl: "https://bobbin.oyster.cafe", - fetch: vi.fn(async (input) => - String(input).includes("sh.tangled.repo.getReposByRepoDids") - ? Response.json({ - items: [ - { uri: "at://did:plc:akshay/sh.tangled.repo/3lk", cid: "bafy", value } - ] - }) - : new Response(null, { status: 404 }) - ) + fetch: vi.fn(async (input) => { + const url = String(input); + if (url.includes("sh.tangled.repo.getReposByRepoDids")) + return Response.json({ + items: [ + { + uri: "at://did:plc:scallop/org.tangled.repo.manifest/self", + cid: "bafy", + value + } + ] + }); + if (url.includes("identifier=did%3Aplc%3Ascallop")) + return Response.json({ did: "did:plc:scallop", handle: "handle.invalid", pds }); + return new Response(null, { status: 404 }); + }) }); const ctxWithoutBobbin = () => ({ xrpc: { call: vi.fn() } }) as unknown as BobbinContext; diff --git a/web/src/lib/api/notifications.ts b/web/src/lib/api/notifications.ts index d9ebd94c2..1abd229b6 100644 --- a/web/src/lib/api/notifications.ts +++ b/web/src/lib/api/notifications.ts @@ -4,7 +4,7 @@ import { resolveMiniDoc } from "$lib/api/identity"; import { getReposByRepoDids } from "$lib/api/records"; import { getIssues } from "$lib/api/issue"; import { getPulls } from "$lib/api/records"; -import { repoNameOf } from "$lib/api/repo"; +import { resolveRepoKnot } from "$lib/api/repo"; import { didFromUri, rkeyFromUri } from "$lib/api/uri"; import { isDid, type Did } from "@atcute/lexicons/syntax"; import { parseDidRkey, type NotificationOffer } from "$lib/components/notifications/types"; @@ -153,10 +153,11 @@ export const hydrateNotifications = async ( ? getPulls(ctx, pullUris).catch(() => ({ items: [] })) : Promise.resolve({ items: [] }) ]); - const ownerDids = [...new Set(repoList.items.map((r) => didFromUri(r.uri)))]; - const ownerDocs = await Promise.all( - ownerDids.map((did) => resolveMiniDoc(ctx, did).catch(() => null)) - ); + const ownerDids = [...new Set(repoList.items.map((r) => r.value.declaration.owner))]; + const [ownerDocs, knots] = await Promise.all([ + Promise.all(ownerDids.map((did) => resolveMiniDoc(ctx, did).catch(() => null))), + Promise.all(repoList.items.map((r) => resolveRepoKnot(ctx, didFromUri(r.uri)))) + ]); const ownerMap = new Map(); for (const doc of ownerDocs) { if (doc) ownerMap.set(doc.did, doc.handle); @@ -169,17 +170,16 @@ export const hydrateNotifications = async ( const repoMap = new Map(); const knotOfRepo = new Map(); - for (const record of repoList.items) { - const repoDid = record.value.repoDid; - if (!repoDid) continue; - const ownerDid = didFromUri(record.uri); + for (const [index, record] of repoList.items.entries()) { + const repoDid = didFromUri(record.uri); + const ownerDid = record.value.declaration.owner; const ownerHandle = ownerMap.get(ownerDid) ?? ownerDid; repoMap.set(repoDid, { ownerHandle, - name: repoNameOf(record) + name: record.value.declaration.slug, }); if (isDid(repoDid)) { - const knot = asKnot(record.value.knot); + const knot = asKnot(knots[index]); if (knot) knotOfRepo.set(repoDid, knot); } } diff --git a/web/src/lib/api/pullCompose.ts b/web/src/lib/api/pullCompose.ts index fc01f9c9f..d82e8900a 100644 --- a/web/src/lib/api/pullCompose.ts +++ b/web/src/lib/api/pullCompose.ts @@ -6,7 +6,13 @@ import { branches as listBranches, gitTarget, type GitServiceConfig } from "$lib import { listCommits, toMirrorCommitSummary } from "$lib/api/gitmirror"; import { getMergeBase } from "$lib/api/knotmirror"; import { listRepos } from "$lib/api/records"; -import { repoNameOf, branchSummaries, type BranchSummary, type CommitSummary } from "$lib/api/repo"; +import { + resolveRepoKnot, + branchSummaries, + type BranchSummary, + type CommitSummary +} from "$lib/api/repo"; +import { didFromUri } from "$lib/api/uri"; export type PullSource = "patch" | "branch" | "fork"; @@ -132,23 +138,23 @@ export const loadCompose = async (args: { .catch(() => [] as BranchSummary[]), viewer ? listRepos(ctx, viewer.did, { limit: 100 }) - .then((page) => + .then((page) => + Promise.all( page.items - .filter( - (item) => - (item.value.source === repoDid || - item.value.source === repo.uri) && - item.value.repoDid - ) - .map((item): ForkOption => ({ + .filter((item) => { + const source = item.value.source; + return !!source && "did" in source && source.did === repoDid; + }) + .map(async (item): Promise => ({ uri: item.uri, - repoDid: item.value.repoDid as string, + repoDid: didFromUri(item.uri), owner: viewer.handle, - name: repoNameOf(item), - knot: item.value.knot + name: item.value.declaration.slug, + knot: (await resolveRepoKnot(ctx, didFromUri(item.uri))) ?? "" })) ) - .catch(() => [] as ForkOption[]) + ) + .catch(() => [] as ForkOption[]) : Promise.resolve([] as ForkOption[]) ]); diff --git a/web/src/lib/api/records.ts b/web/src/lib/api/records.ts index 82dea9351..4fa54de50 100644 --- a/web/src/lib/api/records.ts +++ b/web/src/lib/api/records.ts @@ -6,6 +6,7 @@ import type * as ShTangledPullGetPullView from "$lib/api/lexicons/types/sh/tangl import type * as ShTangledFeedStar from "$lib/api/lexicons/types/sh/tangled/feed/star"; import type * as ShTangledGraphVouch from "$lib/api/lexicons/types/sh/tangled/graph/vouch"; import type * as OrgTangledRepoManifest from "$lib/api/lexicons/types/org/tangled/repo/manifest"; +import type * as ShTangledRepo from "$lib/api/lexicons/types/sh/tangled/repo"; import type * as ShTangledRepoPull from "$lib/api/lexicons/types/sh/tangled/repo/pull"; import type * as ShTangledRepoPullStatus from "$lib/api/lexicons/types/sh/tangled/repo/pull/status"; import type * as ShTangledString from "$lib/api/lexicons/types/sh/tangled/string"; @@ -20,6 +21,7 @@ export interface RecordList { } export type RepoRecord = OrgTangledRepoManifest.Main; +export type LegacyRepoRecord = ShTangledRepo.Main; export type ProfileRecord = ShTangledActorProfile.Main; export type PullRecord = ShTangledRepoPull.Main; export type PullStatusRecord = ShTangledRepoPullStatus.Main; diff --git a/web/src/lib/api/repo.test.ts b/web/src/lib/api/repo.test.ts index f0432e51d..72511208d 100644 --- a/web/src/lib/api/repo.test.ts +++ b/web/src/lib/api/repo.test.ts @@ -2,7 +2,6 @@ import { describe, expect, it, vi } from "vitest"; import { coAuthorsFrom, importState, - repoNameOf, resolveForkRepoLabels, resolveRepoByName, resolveRepoByRepoDid, @@ -14,7 +13,6 @@ import { type TreeEntrySummary } from "$lib/api/repo"; import { ClientResponseError, createBobbinClient, type BobbinContext } from "$lib/api/client"; -import type { RecordView, RepoRecord } from "$lib/api/records"; const jsonResponse = (body: unknown): Response => new Response(JSON.stringify(body), { @@ -25,23 +23,6 @@ const jsonResponse = (body: unknown): Response => const makeCtx = (fetchMock: typeof globalThis.fetch): BobbinContext => createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock }); -describe("repoNameOf", () => { - const view = (uri: string, name?: string): RecordView => ({ - uri: uri as RecordView["uri"], - value: { $type: "sh.tangled.repo", createdAt: "", knot: "knot.test", name } - }); - - it("prefers the record's cosmetic name over a tid rkey", () => { - expect(repoNameOf(view("at://did:plc:o/sh.tangled.repo/3lzg6enurmo22", "infra"))).toBe( - "infra" - ); - }); - - it("falls back to the rkey for unnamed records", () => { - expect(repoNameOf(view("at://did:plc:o/sh.tangled.repo/infra"))).toBe("infra"); - }); -}); - describe("treeEntryKind", () => { it("maps git file modes to entry kinds", () => { expect(treeEntryKind("0040000")).toBe("directory"); @@ -258,19 +239,17 @@ describe("resolveForkRepoLabels", () => { output: { items: [ { - uri: "at://did:plc:forkowner/sh.tangled.repo/core", + uri: "at://did:plc:forkrepo/org.tangled.repo.manifest/self", value: { - $type: "sh.tangled.repo", - knot: "knot.test", - name: "core", - repoDid: "did:plc:forkrepo" + $type: "org.tangled.repo.manifest", + declaration: { owner: "did:plc:forkowner", slug: "core" } } } ] }, data: { "did:plc:forkowner": { - "sh.tangled.repo:.repo": { + "org.tangled.repo.manifest:.repo": { "blue.microcosm.identity.resolveMiniDoc": { handle: "fork.example" } } } @@ -307,17 +286,15 @@ describe("resolveForkRepoLabels", () => { describe("resolveRepoByRepoDid", () => { const enrichReply = jsonResponse({ output: { - uri: "at://did:plc:owner/sh.tangled.repo/core", + uri: "at://did:plc:j5hmlfdrwkvtxm7cjmu7j2is/org.tangled.repo.manifest/self", value: { - $type: "sh.tangled.repo", - knot: "knot.test", - name: "core", - repoDid: "did:plc:j5hmlfdrwkvtxm7cjmu7j2is" + $type: "org.tangled.repo.manifest", + declaration: { owner: "did:plc:owner", slug: "core" } } }, data: { "did:plc:owner": { - "sh.tangled.repo:.repo": { + "org.tangled.repo.manifest:.repo": { "blue.microcosm.identity.resolveMiniDoc": { did: "did:plc:owner", handle: "tangled.org" @@ -346,17 +323,15 @@ describe("resolveRepoByRepoDid", () => { it("falls back to owner did when handle is missing or .invalid", async () => { const enrichReplyNoHandle = jsonResponse({ output: { - uri: "at://did:plc:owner/sh.tangled.repo/core", + uri: "at://did:plc:j5hmlfdrwkvtxm7cjmu7j2is/org.tangled.repo.manifest/self", value: { - $type: "sh.tangled.repo", - knot: "knot.test", - name: "core", - repoDid: "did:plc:j5hmlfdrwkvtxm7cjmu7j2is" + $type: "org.tangled.repo.manifest", + declaration: { owner: "did:plc:owner", slug: "core" } } }, data: { "did:plc:owner": { - "sh.tangled.repo:.repo": { + "org.tangled.repo.manifest:.repo": { "blue.microcosm.identity.resolveMiniDoc": { did: "did:plc:owner", handle: "handle.invalid" diff --git a/web/src/lib/api/repo.ts b/web/src/lib/api/repo.ts index f7d54ec18..0db887eba 100644 --- a/web/src/lib/api/repo.ts +++ b/web/src/lib/api/repo.ts @@ -2,7 +2,8 @@ import { isDid } from "@atcute/lexicons/syntax"; import { ClientResponseError, type BobbinContext, type XrpcRequestInit } from "$lib/api/client"; import type { LineStat } from "$lib/api/diff"; import { REPO_OWNER_DOCS } from "$lib/api/descriptors"; -import { authorOf, enrich, target } from "$lib/api/enrich"; +import { enrich, handleOf, target, type Sidecar } from "$lib/api/enrich"; +import { resolveMiniDoc } from "$lib/api/identity"; import { getRepoByName, type RecordList, type RecordView, type RepoRecord } from "$lib/api/records"; import { ok } from "@atcute/client"; import { @@ -11,7 +12,7 @@ import { type $params as DescribeRepoParams } from "$lib/api/lexicons/types/sh/tangled/repo/describeRepo"; import { httpStatusFor } from "$lib/api/load"; -import { didFromUri, rkeyFromUri } from "$lib/api/uri"; +import { didFromUri } from "$lib/api/uri"; import type * as Tree from "$lib/api/lexicons/types/sh/tangled/repo/tree"; import type * as ListBranches from "$lib/api/lexicons/types/org/tangled/temp/git/listBranches"; import type * as ListTags from "$lib/api/lexicons/types/org/tangled/temp/git/listTags"; @@ -35,9 +36,23 @@ export const parseRawCommit = (spec: string): { ref: string; format: "patch" | " return FULL_HASH_RE.test(ref) ? { ref, format } : null; }; -// newer repos get tid rkeys and keep their display name in the record -export const repoNameOf = (view: RecordView): string => - view.value.name ?? rkeyFromUri(view.uri); +export const repoOwnerOf = ( + data: Sidecar, + view: RecordView +): { did: string; handle: string } => { + const did = view.value.declaration.owner; + return { did, handle: handleOf(data, did, REPO_OWNER_DOCS) }; +}; + +// TODO(pre-1.0): resolve knot from #tangled_knot service. Don't do PDS fallback. +export const resolveRepoKnot = async ( + ctx: BobbinContext, + repoDid: string, + init?: XrpcRequestInit +): Promise => { + const doc = await resolveMiniDoc(ctx, repoDid, init).catch(() => null); + return (doc?.pds && URL.parse(doc.pds)?.host) || undefined; +}; // a repo bobbin has never indexed is a miss, not an error export const resolveRepoByName = async ( @@ -67,15 +82,15 @@ export const resolveForkRepoLabels = async ( { xrpc: "sh.tangled.repo.getReposByRepoDids", params: { dids: repoDids }, - enrich: [target(REPO_OWNER_DOCS, ["items[].uri"])] + enrich: [target(REPO_OWNER_DOCS, ["items[].value.declaration.owner"])] }, init ); const labels: Record = {}; for (const item of page.output.items ?? []) { - if (!item.value.repoDid) continue; - const owner = authorOf(page.data, item.uri, REPO_OWNER_DOCS); - labels[item.value.repoDid] = `${owner.handle}/${repoNameOf(item)}`; + const owner = repoOwnerOf(page.data, item); + const slug = item.value.declaration.slug.toLowerCase(); + labels[didFromUri(item.uri)] = `${owner.handle}/${slug}`; } return labels; }; @@ -97,20 +112,18 @@ export const resolveRepoByRepoDid = async ( { xrpc: "sh.tangled.repo.getRepoByRepoDid", params: { repoDid }, - enrich: [target(REPO_OWNER_DOCS, ["uri"])] + enrich: [target(REPO_OWNER_DOCS, ["value.declaration.owner"])] }, init ); const view = page.output; if (!view?.uri) return null; - const owner = authorOf(page.data, view.uri, REPO_OWNER_DOCS); + const owner = repoOwnerOf(page.data, view); const ownerIdentifier = - owner.handle && !owner.handle.endsWith(".invalid") - ? owner.handle - : didFromUri(view.uri); + owner.handle && !owner.handle.endsWith(".invalid") ? owner.handle : owner.did; return { owner: ownerIdentifier, - name: repoNameOf(view) + name: view.value.declaration.slug, }; } catch (cause) { if (cause instanceof ClientResponseError && httpStatusFor(cause) === 404) return null; diff --git a/web/src/lib/api/repoCreate.ts b/web/src/lib/api/repoCreate.ts index 35028d070..f92663ba4 100644 --- a/web/src/lib/api/repoCreate.ts +++ b/web/src/lib/api/repoCreate.ts @@ -8,7 +8,7 @@ import { createBobbinClient, type BobbinContext } from "$lib/api/client"; import { DEFAULT_LABELS } from "$lib/api/labels"; import { mainSchema as createRepoSchema } from "$lib/api/lexicons/types/sh/tangled/repo/create"; import { mainSchema as deleteRepoSchema } from "$lib/api/lexicons/types/sh/tangled/repo/delete"; -import type { RepoRecord } from "$lib/api/records"; +import type { LegacyRepoRecord as RepoRecord } from "$lib/api/records"; import { createRecord, deleteRecord, diff --git a/web/src/lib/api/repoSettings.test.ts b/web/src/lib/api/repoSettings.test.ts index e6d2d5e21..5bf9c0c75 100644 --- a/web/src/lib/api/repoSettings.test.ts +++ b/web/src/lib/api/repoSettings.test.ts @@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { ClientResponseError } from "@atcute/client"; import type { OAuthUserAgent } from "@atcute/oauth-browser-client"; import type { BobbinContext } from "$lib/api/client"; -import type { RecordView, RepoRecord } from "$lib/api/records"; +import type { RecordView, LegacyRepoRecord as RepoRecord } from "$lib/api/records"; const putRecord = vi.fn(async () => ({ uri: "at://did:plc:owner/sh.tangled.repo/core", @@ -17,7 +17,12 @@ const awaitIndexedRecord = vi.fn(async () => undefined); const awaitDeletedRecord = vi.fn(async () => true); const pingIndexNow = vi.fn(); -vi.mock("./write", () => ({ createRecord, putRecord, deleteRecord })); +const readRecord = vi.fn(async () => ({ + value: view.value, + written: { uri: view.uri, cid: view.cid } +})); + +vi.mock("./write", () => ({ createRecord, putRecord, deleteRecord, readRecord })); vi.mock("./awaitRecord", () => ({ awaitIndexedRecord, awaitDeletedRecord })); vi.mock("$lib/api/indexnow", () => ({ pingIndexNow })); @@ -73,7 +78,7 @@ describe("editedRepoRecord", () => { describe("saveRepoRecord", () => { it("swaps on the cid it read and waits for the appview", async () => { - await api.saveRepoRecord(agent, bobbin, view, { description: "a forge for git" }); + await api.saveRepoRecord(agent, bobbin, "core", { description: "a forge for git" }); expect(putRecord).toHaveBeenCalledWith( agent, diff --git a/web/src/lib/api/repoSettings.ts b/web/src/lib/api/repoSettings.ts index d52c97593..aac90f92c 100644 --- a/web/src/lib/api/repoSettings.ts +++ b/web/src/lib/api/repoSettings.ts @@ -12,9 +12,9 @@ import { } from "$lib/api/lexicons/types/sh/tangled/repo/delete"; import { mainSchema as removeCollaboratorSchema } from "$lib/api/lexicons/types/sh/tangled/repo/removeCollaborator"; import { mainSchema as setDefaultBranchSchema } from "$lib/api/lexicons/types/sh/tangled/repo/setDefaultBranch"; -import type { RecordView, RepoRecord } from "$lib/api/records"; +import type { LegacyRepoRecord as RepoRecord } from "$lib/api/records"; import { rkeyFromUri } from "$lib/api/uri"; -import { deleteRecord, putRecord, type WrittenRecord } from "$lib/api/write"; +import { deleteRecord, putRecord, readRecord, type WrittenRecord } from "$lib/api/write"; import { didOf, recordKeyOf } from "$lib/api/syntax"; const REPO_COLLECTION = "sh.tangled.repo" as Nsid; @@ -41,15 +41,17 @@ export const editedRepoRecord = (record: RepoRecord, edit: RepoRecordEdit): Repo export const saveRepoRecord = async ( agent: OAuthUserAgent, bobbinUrl: string, - view: RecordView, + rkey: string, edit: RepoRecordEdit ): Promise => { + const current = await readRecord(agent, REPO_COLLECTION, rkey); + if (!current) throw new Error("This repository has no record in your account to edit."); const written = await putRecord( agent, REPO_COLLECTION, - rkeyFromUri(view.uri), - editedRepoRecord(view.value, edit), - view.cid + rkey, + editedRepoRecord(current.value, edit), + current.written?.cid ); await awaitIndexedRecord(createBobbinClient({ serviceUrl: bobbinUrl }), written); return written; diff --git a/web/src/lib/api/spindle.ts b/web/src/lib/api/spindle.ts index 36e31d104..89152c824 100644 --- a/web/src/lib/api/spindle.ts +++ b/web/src/lib/api/spindle.ts @@ -1,6 +1,7 @@ import { createBobbinClient, type BobbinContext, type QueryValue } from "$lib/api/client"; import type { XrpcRequestInit } from "$lib/api/client"; import { jsonGet } from "$lib/api/_request"; +import { hostForServiceDid } from "$lib/auth/agent"; import type * as GetPipeline from "$lib/api/lexicons/types/sh/tangled/ci/getPipeline"; import type * as Pipeline from "$lib/api/lexicons/types/sh/tangled/ci/pipeline"; import type * as QueryPipelines from "$lib/api/lexicons/types/sh/tangled/ci/queryPipelines"; @@ -10,7 +11,7 @@ const asParams = (params: object): Record => params as unknown as Record; export const spindleUrl = (host: string): string => { - const raw = host.trim().replace(/\/+$/, ""); + const raw = (hostForServiceDid(host.trim()) ?? host.trim()).replace(/\/+$/, ""); if (raw.includes("://")) return raw; return raw.startsWith("localhost:") ? `http://${raw}` : `https://${raw}`; }; diff --git a/web/src/lib/components/profile/pages.test.ts b/web/src/lib/components/profile/pages.test.ts index cfcf227f9..248eaab88 100644 --- a/web/src/lib/components/profile/pages.test.ts +++ b/web/src/lib/components/profile/pages.test.ts @@ -61,21 +61,21 @@ describe("fetchStarredPage", () => { const body = requestBody(fetchMock, 1); expect(body.enrich).toEqual([ { - source: "sh.tangled.repo:.", + source: "org.tangled.repo.manifest:.", type: TYPE_REPO_VIEW, - targets: ["items[].value.repoDid"] + targets: ["items[].uri"] }, { - source: "sh.tangled.repo:.repo", + source: "org.tangled.repo.manifest:.repo", type: "blue.microcosm.identity.resolveMiniDoc", - targets: ["items[].uri"] + targets: ["items[].value.declaration.owner"] } ]); }); it.each([ - [undefined, "items[].value.repoDid"], - ["needle", "hits[].value.repoDid"] + [undefined, "items[].uri"], + ["needle", "hits[].uri"] ])("targets repo views for list/search results", async (q, expectedTarget) => { const fetchMock = vi.fn().mockResolvedValue(enriched()); const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock }); @@ -89,7 +89,11 @@ describe("fetchStarredPage", () => { const body = requestBody(fetchMock); expect(body.enrich).toEqual([ - { source: "sh.tangled.repo:.", type: TYPE_REPO_VIEW, targets: [expectedTarget] } + { + source: "org.tangled.repo.manifest:.", + type: TYPE_REPO_VIEW, + targets: [expectedTarget] + } ]); }); @@ -103,18 +107,17 @@ describe("fetchStarredPage", () => { output: { items: [ { - uri: "at://did:plc:alice/sh.tangled.repo/reef", + uri: "at://did:plc:reef/org.tangled.repo.manifest/self", value: { - repoDid: "did:plc:reef", - name: "reef", + declaration: { owner: "did:plc:alice", slug: "reef" }, createdAt: "2026-08-01T00:00:00Z" } } ] }, data: { - "did:plc:reef": { - "sh.tangled.repo:.": { + "at://did:plc:reef/org.tangled.repo.manifest/self": { + "org.tangled.repo.manifest:.": { [TYPE_REPO_VIEW]: { did: "did:plc:reef", slug: "reef", @@ -213,8 +216,8 @@ describe("fetchStarredPage", () => { bodies.every((body) => body.enrich.every((descriptor) => descriptor.type === TYPE_MINIDOC - ? descriptor.targets?.[0] === "items[].uri" - : descriptor.targets?.[0] === "items[].value.repoDid" + ? descriptor.targets?.[0] === "items[].value.declaration.owner" + : descriptor.targets?.[0] === "items[].uri" ) ) ).toBe(true); @@ -226,26 +229,23 @@ describe("fetchStarredPage", () => { output: { items: [ { - uri: "at://did:plc:alice/sh.tangled.repo/repo-one", + uri: "at://did:plc:repo-one/org.tangled.repo.manifest/self", value: { - repoDid: "did:plc:repo-one", - name: "repo-one", + declaration: { owner: "did:plc:alice", slug: "repo-one" }, createdAt: "2026-08-01T00:00:00Z" } }, { - uri: "at://did:plc:alice/sh.tangled.repo/repo-one-dup", + uri: "at://did:plc:repo-one/org.tangled.repo.manifest/self", value: { - repoDid: "did:plc:repo-one", - name: "repo-one-dup", + declaration: { owner: "did:plc:alice", slug: "repo-one-dup" }, createdAt: "2026-08-02T00:00:00Z" } }, { - uri: "at://did:plc:alice/sh.tangled.repo/repo-two", + uri: "at://did:plc:repo-two/org.tangled.repo.manifest/self", value: { - repoDid: "did:plc:repo-two", - name: "repo-two", + declaration: { owner: "did:plc:alice", slug: "repo-two" }, createdAt: "2026-08-03T00:00:00Z" } } diff --git a/web/src/lib/components/profile/pages.ts b/web/src/lib/components/profile/pages.ts index 5a95eb19e..3771b214b 100644 --- a/web/src/lib/components/profile/pages.ts +++ b/web/src/lib/components/profile/pages.ts @@ -34,6 +34,7 @@ import { type RepoRecord, type ProfileRecord } from "$lib/api/records"; +import { repoOwnerOf } from "$lib/api/repo"; import type { SearchPage } from "$lib/api/search"; import { didFromUri, rkeyFromUri } from "$lib/api/uri"; import type { VouchRecord } from "$lib/api/graph"; @@ -76,10 +77,11 @@ const FOLLOW_STATS = [FOLLOWER_COUNT, FOLLOWING_COUNT, FOLLOW_VIEWER]; const emptyItems = (): Enriched<{ items: T[] }> => ({ output: { items: [] }, data: {} }); const toRepoCard = (item: ListItem, ownerHandle: string): RepoCardData => { - const value = item.value as RepoRecord; + const view = item as RecordView; + const value = view.value; return { - name: value.name ?? rkeyFromUri(item.uri), - repoDid: value.repoDid ?? "", + name: value.declaration.slug, + repoDid: didFromUri(view.uri), ownerHandle, description: value.description, createdAt: value.createdAt @@ -89,7 +91,7 @@ const toRepoCard = (item: ListItem, ownerHandle: string): RepoCardData => { // no view means a legacy repo without a repoDid, or one bobbin couldn't build const resolveRepoCard = (item: ListItem, ownerHandle: string, data: Sidecar): RepoCardData => { const repo = toRepoCard(item, ownerHandle); - const view = repoViewOf(data, repo.repoDid || undefined, REPO_VIEW); + const view = repoViewOf(data, item.uri, REPO_VIEW); if (!view) return { ...repo, stars: 0, viewerStarRkey: null }; const card = viewToRepoCard(view); // a view without viewer state was built for nobody, so the star is unknown, not absent @@ -181,14 +183,14 @@ const resolveStars = async ( xrpc: "sh.tangled.repo.getReposByRepoDids", params: { dids: repoDids }, enrich: [ - target(REPO_VIEW, ["items[].value.repoDid"]), - target(REPO_OWNER_DOCS, ["items[].uri"]) + target(REPO_VIEW, ["items[].uri"]), + target(REPO_OWNER_DOCS, ["items[].value.declaration.owner"]) ], viewer: viewerDid }) : emptyItems(); const reposByDid = new Map( - enriched.output.items.map((item) => [(item.value as RepoRecord).repoDid, item]) + enriched.output.items.map((item) => [didFromUri(item.uri), item]) ); const resolved = items.map((item): StarData | null => { const value = item.value as ShTangledFeedStar.Main; @@ -196,7 +198,7 @@ const resolveStars = async ( if (subject && "did" in subject && subject.did) { const repo = reposByDid.get(subject.did); if (!repo) return null; - const owner = authorOf(enriched.data, repo.uri, REPO_OWNER_DOCS); + const owner = repoOwnerOf(enriched.data, repo as RecordView); return { kind: "repo", uri: item.uri, @@ -236,7 +238,7 @@ export const fetchReposPage = async ( const enriched = await enrich>(ctx, { xrpc: "sh.tangled.repo.listRepos", params: { subject: did, limit, offset }, - enrich: [target(REPO_VIEW, ["items[].value.repoDid"])], + enrich: [target(REPO_VIEW, ["items[].uri"])], viewer: viewerDid }); const cards = new Map(); @@ -251,8 +253,8 @@ export const fetchReposPage = async ( } const enriched = await enrich(ctx, { xrpc: "sh.tangled.search.query", - params: { q, nsid: "sh.tangled.repo", author: did, limit, offset }, - enrich: [target(REPO_VIEW, ["hits[].value.repoDid"])], + params: { q, nsid: "org.tangled.repo.manifest", author: did, limit, offset }, + enrich: [target(REPO_VIEW, ["hits[].uri"])], viewer: viewerDid }); return { @@ -405,7 +407,7 @@ export const fetchPinned = async ( { keys, did, handle, viewerDid }: PinnedOptions ): Promise => { const ownerHandleOf = (item: ListItem, data: Sidecar): string => { - const owner = authorOf(data, item.uri, REPO_OWNER_DOCS); + const owner = repoOwnerOf(data, item as RecordView); const resolved = owner.did === did ? handle : owner.handle; return resolved.endsWith(".invalid") ? owner.did : resolved; }; @@ -417,8 +419,8 @@ export const fetchPinned = async ( xrpc: "sh.tangled.repo.getReposByRepoDids", params: { dids }, enrich: [ - target(REPO_VIEW, ["items[].value.repoDid"]), - target(REPO_OWNER_DOCS, ["items[].uri"]) + target(REPO_VIEW, ["items[].uri"]), + target(REPO_OWNER_DOCS, ["items[].value.declaration.owner"]) ], viewer: viewerDid }) @@ -428,8 +430,8 @@ export const fetchPinned = async ( xrpc: "sh.tangled.repo.getRepos", params: { repos: uris }, enrich: [ - target(REPO_VIEW, ["items[].value.repoDid"]), - target(REPO_OWNER_DOCS, ["items[].uri"]) + target(REPO_VIEW, ["items[].uri"]), + target(REPO_OWNER_DOCS, ["items[].value.declaration.owner"]) ], viewer: viewerDid }) @@ -437,9 +439,8 @@ export const fetchPinned = async ( ]); const cards = new Map(); for (const item of byDid.output.items) { - const repoDid = (item.value as RepoRecord).repoDid; - if (repoDid) - cards.set(repoDid, resolveRepoCard(item, ownerHandleOf(item, byDid.data), byDid.data)); + const repoDid = didFromUri(item.uri); + cards.set(repoDid, resolveRepoCard(item, ownerHandleOf(item, byDid.data), byDid.data)); } for (const item of byUri.output.items) { cards.set(item.uri, resolveRepoCard(item, ownerHandleOf(item, byUri.data), byUri.data)); diff --git a/web/src/lib/components/repo/SubjectLabelPanel.svelte b/web/src/lib/components/repo/SubjectLabelPanel.svelte index a745e5d17..f3ffef950 100644 --- a/web/src/lib/components/repo/SubjectLabelPanel.svelte +++ b/web/src/lib/components/repo/SubjectLabelPanel.svelte @@ -83,8 +83,8 @@ const agent = auth.agent; if (!agent) return; const label = await getRepo(createBobbinClient({ serviceUrl: auth.bobbinUrl }), repoUri) - .then((repo) => - createRepoLabel(agent, auth.bobbinUrl, repo, { + .then(() => + createRepoLabel(agent, auth.bobbinUrl, { name, color, issues: true, diff --git a/web/src/lib/components/settings/RepoGeneralForm.svelte b/web/src/lib/components/settings/RepoGeneralForm.svelte index 3655702e7..00a1bd519 100644 --- a/web/src/lib/components/settings/RepoGeneralForm.svelte +++ b/web/src/lib/components/settings/RepoGeneralForm.svelte @@ -54,7 +54,7 @@ description: record.value.description ?? "", website: record.value.website ?? "", topics: record.value.topics ?? [], - labels: record.value.labels ?? [], + labels: [] as string[], defaultBranch }); @@ -109,11 +109,10 @@ if (!agent) return; try { if (recordChanged) { - await saveRepoRecord(agent, bobbinUrl, record, { + await saveRepoRecord(agent, bobbinUrl, repo.rkey, { description: form.description.trim() || undefined, website: form.website.trim() || undefined, - topics: topicList, - labels: [...form.labels] + topics: topicList }); } if (branchChanged) { @@ -135,7 +134,7 @@ const agent = auth.agent; if (!agent) return; try { - await deleteRepoLabel(agent, bobbinUrl, record, label.uri); + await deleteRepoLabel(agent, bobbinUrl, label.uri); form.labels.delete(label.uri); } finally { await invalidateAll(); diff --git a/web/src/lib/server/repo.ts b/web/src/lib/server/repo.ts index f7704d328..81a73fb92 100644 --- a/web/src/lib/server/repo.ts +++ b/web/src/lib/server/repo.ts @@ -6,6 +6,7 @@ import { resolveMiniDoc, type MiniDoc } from "$lib/api/identity"; import { toHttpError } from "$lib/api/load"; import type { RecordView, RepoRecord } from "$lib/api/records"; import { resolveRepoByName } from "$lib/api/repo"; +import { didFromUri } from "$lib/api/uri"; import { getConfig } from "$lib/server/config"; export interface ResolvedRepo { @@ -40,7 +41,7 @@ export const resolveRepoFromParams = async (event: { ); if (!view) error(404, `${doc.handle}/${name} does not exist`); - const git = gitTarget(config, { uri: view.uri, repoDid: view.value.repoDid }, event.fetch); + const git = gitTarget(config, { uri: view.uri, repoDid: didFromUri(view.uri) }, event.fetch); return { ctx, git, doc, view }; }; diff --git a/web/src/routes/[handle]/[repo]/+layout.ts b/web/src/routes/[handle]/[repo]/+layout.ts index 05192b042..234dfe8e3 100644 --- a/web/src/routes/[handle]/[repo]/+layout.ts +++ b/web/src/routes/[handle]/[repo]/+layout.ts @@ -1,18 +1,16 @@ import { browser } from "$app/environment"; -import { isDid, isHandle, isResourceUri } from "@atcute/lexicons/syntax"; +import { isDid, isHandle } from "@atcute/lexicons/syntax"; import { error, redirect } from "@sveltejs/kit"; import { NO_ACCESS, resolveRepoAccess } from "$lib/api/access"; import { createBobbinClient } from "$lib/api/client"; import { count } from "$lib/api/count"; -import { REPO_OWNER_DOCS } from "$lib/api/descriptors"; -import { authorOf, enrich, target } from "$lib/api/enrich"; import { gitTarget, resolveDefaultBranch } from "$lib/api/gitclient"; import { getStarRkey } from "$lib/api/graph"; import { resolveMiniDoc } from "$lib/api/identity"; import { orNull, parallel, settle, stream, toHttpError } from "$lib/api/load"; -import type { RecordView, RepoRecord } from "$lib/api/records"; -import { repoNameOf, resolveRepoByName, resolveRepoByRepoDid } from "$lib/api/repo"; -import { rkeyFromUri } from "$lib/api/uri"; +import type { RepoRecord } from "$lib/api/records"; +import { resolveRepoByName, resolveRepoByRepoDid, resolveRepoKnot } from "$lib/api/repo"; +import { didFromUri } from "$lib/api/uri"; import type { BobbinContext } from "$lib/api/client"; import type { RepoCounts, RepoInfo, RepoSource } from "$lib/components/repo/types"; import type { LayoutLoad } from "./$types"; @@ -21,24 +19,12 @@ const FALLBACK_BRANCH = "main"; const resolveSource = async ( ctx: BobbinContext, - uri: string | undefined + upstream: RepoRecord["source"] ): Promise => { - if (!uri || !isResourceUri(uri)) return null; - try { - const page = await enrich>(ctx, { - xrpc: "sh.tangled.repo.getRepo", - params: { repo: uri }, - enrich: [target(REPO_OWNER_DOCS, ["uri"])] - }); - const view = page.output; - return { - ownerHandle: authorOf(page.data, view.uri, REPO_OWNER_DOCS).handle, - name: repoNameOf(view) - }; - } catch { - // a fork whose source is gone still renders, just without the attribution - return null; - } + if (!upstream || !("did" in upstream)) return null; + // a fork whose source is gone still renders, just without the attribution + const source = await resolveRepoByRepoDid(ctx, upstream.did); + return source && { ownerHandle: source.owner, name: source.name }; }; // the layout reset also cuts us off from `[handle]/+layout.ts`, so identity gets @@ -80,25 +66,24 @@ export const load: LayoutLoad = async (event) => { if (!view) error(404, `${doc.handle}/${name} does not exist`); const record = view.value; - const repoDid = record.repoDid; + const repoDid = didFromUri(view.uri); + const knot = (await resolveRepoKnot(ctx, repoDid)) ?? ""; const git = gitTarget(parent.publicConfig, { uri: view.uri, repoDid }, event.fetch); const repo: RepoInfo = { uri: view.uri, - rkey: rkeyFromUri(view.uri), - name: repoNameOf(view), + rkey: record.declaration.slug, + name: record.declaration.slug, ownerDid: doc.did, ownerHandle: doc.handle, repoDid, - knot: record.knot, + knot, spindle: record.spindle, description: record.description, createdAt: record.createdAt, website: record.website, topics: record.topics, - importSource: - record.source && !isResourceUri(record.source) ? record.source : undefined, - labels: record.labels, + importSource: record.source && "uri" in record.source ? record.source.uri : undefined, defaultBranch: stream( resolveDefaultBranch(git) .then((branch) => branch ?? FALLBACK_BRANCH) diff --git a/web/src/routes/[handle]/[repo]/commit-statuses/+server.ts b/web/src/routes/[handle]/[repo]/commit-statuses/+server.ts index 8438a00fa..026145d73 100644 --- a/web/src/routes/[handle]/[repo]/commit-statuses/+server.ts +++ b/web/src/routes/[handle]/[repo]/commit-statuses/+server.ts @@ -1,19 +1,22 @@ import { json } from "@sveltejs/kit"; import { createSpindleClient, queryPipelines, toPipelineSummary } from "$lib/api/spindle"; +import { didOf } from "$lib/api/syntax"; import { resolveRepoFromParams } from "$lib/server/repo"; import type { CommitStatuses } from "$lib/api/commitStatuses"; import type { RequestHandler } from "./$types"; +import { didFromUri } from "$lib/api"; export const GET: RequestHandler = async (event) => { const { view } = await resolveRepoFromParams(event); - const { spindle, repoDid } = view.value; + const repoDid = didFromUri(view.uri); + const spindle = view.value.spindle; const shas = [...new Set(event.url.searchParams.getAll("sha"))].slice(0, 250); if (!spindle || !repoDid || shas.length === 0) return json({}); const ctx = createSpindleClient(spindle, event.fetch); const page = await queryPipelines(ctx, { - repo: repoDid, + repo: didOf(repoDid), commits: shas, limit: shas.length }).catch(() => null); diff --git a/web/src/routes/[handle]/[repo]/pipelines/pipelines.test.ts b/web/src/routes/[handle]/[repo]/pipelines/pipelines.test.ts index da7d1a95e..b03cca3ca 100644 --- a/web/src/routes/[handle]/[repo]/pipelines/pipelines.test.ts +++ b/web/src/routes/[handle]/[repo]/pipelines/pipelines.test.ts @@ -162,19 +162,17 @@ describe("pipelines load", () => { output: { items: [ { - uri: "at://did:plc:forkowner/sh.tangled.repo/core", + uri: "at://did:plc:forkrepo/org.tangled.repo.manifest/self", value: { - $type: "sh.tangled.repo", - knot: "knot.test", - name: "core", - repoDid: "did:plc:forkrepo" + $type: "org.tangled.repo.manifest", + declaration: { owner: "did:plc:forkowner", slug: "core" } } } ] }, data: { "did:plc:forkowner": { - "sh.tangled.repo:.repo": { + "org.tangled.repo.manifest:.repo": { "blue.microcosm.identity.resolveMiniDoc": { handle: "fork.example" } } } diff --git a/web/src/routes/[handle]/[repo]/pulls/[aturi]/[version]/[[range]]/PullViewPage.svelte b/web/src/routes/[handle]/[repo]/pulls/[aturi]/[version]/[[range]]/PullViewPage.svelte index fa7128f61..1321863dc 100644 --- a/web/src/routes/[handle]/[repo]/pulls/[aturi]/[version]/[[range]]/PullViewPage.svelte +++ b/web/src/routes/[handle]/[repo]/pulls/[aturi]/[version]/[[range]]/PullViewPage.svelte @@ -34,7 +34,7 @@ import { gitTarget } from "$lib/api/gitclient"; import { repoBranches } from "$lib/api/repoIndex"; import { getPatchStream } from "$lib/api/patchStream"; - import { getRepoByRepoDid } from "$lib/api/records"; + import { resolveRepoKnot } from "$lib/api/repo"; import { loadDiff, type RepoDiffDeps, type RepoDiffSpec } from "$lib/api/repoDiff"; import { fileAnchor } from "$lib/components/repo/fileAnchor.svelte"; import type { PullView as PullRouteView } from "$lib/api/pullRoute"; @@ -536,7 +536,9 @@ const sourceKnot = await (async () => { if (!isForkBased) return data.repo.knot; const ctx = createBobbinClient({ serviceUrl: data.publicConfig.bobbinUrl }); - return (await getRepoByRepoDid(ctx, data.sourceRepoDid)).value.knot; + const knot = await resolveRepoKnot(ctx, data.sourceRepoDid); + if (!knot) throw new Error("Could not find the source repository's knot."); + return knot; })(); await deleteBranch(agent, data.publicConfig.bobbinUrl, sourceKnot, { repo: data.sourceRepoDid, diff --git a/web/src/routes/[handle]/[repo]/settings/labels/new/+page.svelte b/web/src/routes/[handle]/[repo]/settings/labels/new/+page.svelte index 670676d02..36df197bf 100644 --- a/web/src/routes/[handle]/[repo]/settings/labels/new/+page.svelte +++ b/web/src/routes/[handle]/[repo]/settings/labels/new/+page.svelte @@ -44,7 +44,7 @@ const create = createAction(async () => { const agent = auth.agent; if (!agent || !valid) return; - await createRepoLabel(agent, data.publicConfig.bobbinUrl, data.record, { + await createRepoLabel(agent, data.publicConfig.bobbinUrl, { name: name.trim(), color, issues, diff --git a/web/src/routes/[handle]/[repo]/settings/pipelines/+page.svelte b/web/src/routes/[handle]/[repo]/settings/pipelines/+page.svelte index 7f235e6a6..45b19e84d 100644 --- a/web/src/routes/[handle]/[repo]/settings/pipelines/+page.svelte +++ b/web/src/routes/[handle]/[repo]/settings/pipelines/+page.svelte @@ -44,7 +44,7 @@ const save = createAction(async () => { const agent = auth.agent; if (!agent) return; - await saveRepoRecord(agent, data.publicConfig.bobbinUrl, data.record, { + await saveRepoRecord(agent, data.publicConfig.bobbinUrl, repo.rkey, { spindle: chosen || undefined }); await invalidateAll(); diff --git a/web/src/routes/[handle]/[repo]/settings/rename/+page.svelte b/web/src/routes/[handle]/[repo]/settings/rename/+page.svelte index 694b28238..7841e6e88 100644 --- a/web/src/routes/[handle]/[repo]/settings/rename/+page.svelte +++ b/web/src/routes/[handle]/[repo]/settings/rename/+page.svelte @@ -4,7 +4,6 @@ import { createAction } from "$lib/action.svelte"; import { validateRepoName } from "$lib/api/repoCreate"; import { saveRepoRecord } from "$lib/api/repoSettings"; - import { rkeyFromUri } from "$lib/api/uri"; import { getAuth } from "$lib/auth.svelte"; import Button from "$lib/components/ui/Button.svelte"; import CopyButton from "$lib/components/ui/CopyButton.svelte"; @@ -32,8 +31,8 @@ const agent = auth.agent; if (!agent) return; const next = validateRepoName(name.trim()); - await saveRepoRecord(agent, data.publicConfig.bobbinUrl, data.record, { - name: next === rkeyFromUri(data.record.uri) ? undefined : next + await saveRepoRecord(agent, data.publicConfig.bobbinUrl, repo.rkey, { + name: next === repo.rkey ? undefined : next }); await goto(resolve(`/${repo.ownerHandle}/${next}/settings` as "/"), { invalidateAll: true diff --git a/web/src/routes/[handle]/handle.test.ts b/web/src/routes/[handle]/handle.test.ts index da6e652b8..ae1a65c27 100644 --- a/web/src/routes/[handle]/handle.test.ts +++ b/web/src/routes/[handle]/handle.test.ts @@ -30,11 +30,10 @@ const makeMockFetch = () => if (bodyStr.includes("getRepoByRepoDid")) { return jsonResponse({ output: { - uri: `at://${OWNER_DID}/sh.tangled.repo/core`, + uri: `at://${REPO_DID}/org.tangled.repo.manifest/self`, value: { - $type: "sh.tangled.repo", - name: "core", - repoDid: REPO_DID + $type: "org.tangled.repo.manifest", + declaration: { owner: OWNER_DID, slug: "core" } } }, data: { -- 2.51.2