From 67d676699b3939db94bb6bbbb966bf887c085073 Mon Sep 17 00:00:00 2001 From: "Kim, Hyeonseo" Date: Thu, 1 Oct 2026 15:28:24 +0900 Subject: [PATCH 1/6] Move @faker-js/faker to catalog --- packages/web/package.json | 2 +- pnpm-lock.yaml | 9 ++++++--- pnpm-workspace.yaml | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/web/package.json b/packages/web/package.json index 8199551..dc88a13 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -46,6 +46,7 @@ "preview": "vite preview" }, "dependencies": { + "@faker-js/faker": "catalog:", "@formisch/solid": "1.0.0", "@kobalte/core": "^0.13.13", "@solidjs/meta": "^0.29.4", @@ -60,7 +61,6 @@ "vite": "^8.2.0" }, "devDependencies": { - "@faker-js/faker": "^10.5.0", "@types/relay-runtime": "^20.1.1", "eslint-plugin-solid": "^0.14.5", "relay-compiler": "^21.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 795d301..223665c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,6 +9,9 @@ catalogs: '@electric-sql/pglite': specifier: ^0.5.3 version: 0.5.3 + '@faker-js/faker': + specifier: ^10.5.0 + version: 10.5.0 '@fedify/fedify': specifier: 2.4.0-pr.1020.43+41cebe9c version: 2.4.0-pr.1020.43 @@ -275,6 +278,9 @@ importers: packages/web: dependencies: + '@faker-js/faker': + specifier: 'catalog:' + version: 10.5.0 '@formisch/solid': specifier: 1.0.0 version: 1.0.0(solid-js@1.9.14)(typescript@7.0.2)(valibot@1.4.2(typescript@7.0.2)) @@ -312,9 +318,6 @@ importers: specifier: ^8.2.0 version: 8.2.0(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) devDependencies: - '@faker-js/faker': - specifier: ^10.5.0 - version: 10.5.0 '@types/relay-runtime': specifier: ^20.1.1 version: 20.1.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 469756d..3ce0a1d 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -8,6 +8,7 @@ allowBuilds: catalog: "@electric-sql/pglite": ^0.5.3 + "@faker-js/faker": ^10.5.0 "@fedify/fedify": 2.4.0-pr.1020.43+41cebe9c "@fedify/postgres": 2.4.0-pr.1020.43+41cebe9c "@fedify/pglite": 2.4.0-pr.1020.43+41cebe9c From 8301c08ff9cd98490598de0ffc7689fbcfedfc00 Mon Sep 17 00:00:00 2001 From: "Kim, Hyeonseo" Date: Thu, 1 Oct 2026 15:32:48 +0900 Subject: [PATCH 2/6] Add @faker-js/faker to @drfed/grapql --- packages/graphql/package.json | 1 + pnpm-lock.yaml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/packages/graphql/package.json b/packages/graphql/package.json index 9b2e908..d1b6fcd 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -119,6 +119,7 @@ }, "dependencies": { "@drfed/models": "workspace:*", + "@faker-js/faker": "catalog:", "@fedify/uri-template": "^2.3.1", "@fedify/fedify": "catalog:", "@fedify/vocab": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 223665c..2eea2ce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -174,6 +174,9 @@ importers: '@drfed/models': specifier: workspace:* version: link:../models + '@faker-js/faker': + specifier: 'catalog:' + version: 10.5.0 '@fedify/fedify': specifier: 'catalog:' version: 2.4.0-pr.1020.43 From 8713365fb629e609ef1d1d54a978b82d36f9d7e9 Mon Sep 17 00:00:00 2001 From: "Kim, Hyeonseo" Date: Thu, 1 Oct 2026 15:33:06 +0900 Subject: [PATCH 3/6] Change username generate logic in generateActor --- packages/graphql/src/actor.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/graphql/src/actor.ts b/packages/graphql/src/actor.ts index 6849692..8184d50 100644 --- a/packages/graphql/src/actor.ts +++ b/packages/graphql/src/actor.ts @@ -22,6 +22,7 @@ import { type Database, promoteResource, schema } from "@drfed/models"; import { type CollectionRole, actorTypeEnum } from "@drfed/models/schema"; import { type Uuid, uuidV7 as uuid } from "@drfed/models/uuid"; +import { faker } from "@faker-js/faker"; import type { Context } from "@fedify/fedify"; import { drizzleConnectionHelpers } from "@pothos/plugin-drizzle"; import type { PgInsertValue } from "drizzle-orm/pg-core"; @@ -406,11 +407,15 @@ function generateActor( instanceId: Uuid, fedCtx: Context, ): PgInsertValue { + const username = faker.internet + .username() + .toLowerCase() + .replace(/[^a-z0-9_]/gu, "_"); + return { id, localId: id, - // FIXME: https://github.com/fedify-dev/drfed/issues/85 - username: id, + username, instanceId, type: "Person", inboxUrl: fedCtx.getInboxUri(id).href, From 619e646781ccce662015972a1f8f3dd63bc4de6b Mon Sep 17 00:00:00 2001 From: "Kim, Hyeonseo" Date: Thu, 1 Oct 2026 15:47:09 +0900 Subject: [PATCH 4/6] Add 10 attempt to Make a new geneartor --- packages/graphql/src/actor.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/graphql/src/actor.ts b/packages/graphql/src/actor.ts index 8184d50..aa4b7c7 100644 --- a/packages/graphql/src/actor.ts +++ b/packages/graphql/src/actor.ts @@ -359,14 +359,19 @@ builder.mutationFields((t) => ({ fedCtx.getActorUri(id).href, "actor", async (inner, resource) => { - const [createdActor] = await inner - .insert(schema.actors) - .values(generateActor(resource.id, targetInstanceId, fedCtx)) - .returning(); - if (createdActor == null) { - throw new Error("Actor insertion returned no row."); + for (let attempt = 0; attempt < 10; attempt += 1) { + const [createdActor] = await inner + .insert(schema.actors) + .values(generateActor(resource.id, targetInstanceId, fedCtx)) + .onConflictDoNothing({ + target: [schema.actors.username, schema.actors.instanceId], + }) + .returning(); + if (createdActor != null) { + return createdActor; + } } - return createdActor; + throw new Error("Actor insertion returned no row."); }, id, ); From 45feb77f1fda1c334ddc8b951ad02a8f654a5685 Mon Sep 17 00:00:00 2001 From: "Kim, Hyeonseo" Date: Thu, 1 Oct 2026 15:47:53 +0900 Subject: [PATCH 5/6] Change Profile picture username --- packages/graphql/src/actor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graphql/src/actor.ts b/packages/graphql/src/actor.ts index aa4b7c7..f11dfdc 100644 --- a/packages/graphql/src/actor.ts +++ b/packages/graphql/src/actor.ts @@ -424,7 +424,7 @@ function generateActor( instanceId, type: "Person", inboxUrl: fedCtx.getInboxUri(id).href, - profileUrl: new URL(`/@${id}`, fedCtx.origin).href, + profileUrl: new URL(`/@${username}`, fedCtx.origin).href, }; } From adb0404fcc9d739b85ea8c1c28d38825a4391e6d Mon Sep 17 00:00:00 2001 From: "Kim, Hyeonseo" Date: Thu, 1 Oct 2026 15:54:29 +0900 Subject: [PATCH 6/6] Make a test for username generation Assistedy-by Codex:gpt-6.1-sol Prompt Summary ``` Make a test for username generation ``` --- packages/graphql/src/actor.test.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/graphql/src/actor.test.ts b/packages/graphql/src/actor.test.ts index 65670cc..19a86c0 100644 --- a/packages/graphql/src/actor.test.ts +++ b/packages/graphql/src/actor.test.ts @@ -22,6 +22,7 @@ import assert from "node:assert/strict"; import { schema } from "@drfed/models"; import { type Uuid, uuidV7 as uuid } from "@drfed/models/uuid"; +import { faker } from "@faker-js/faker"; import { describe, it } from "@logtape/testing-node/autoload"; import { and, eq } from "drizzle-orm"; @@ -143,7 +144,14 @@ describe("Mutation.generateActors", () => { }); }); - it("creates local actors", async () => { + it("creates local actors with normalized usernames and retries collisions", async (t) => { + const username = t.mock.method( + faker.internet, + "username", + () => "Dr.FED42", + ); + username.mock.mockImplementationOnce(() => "Dr-Fed43", 2); + await withTestHarness(async ({ db, post }) => { const auth = await seedAuthenticatedLocalInstance(db); @@ -189,6 +197,18 @@ describe("Mutation.generateActors", () => { const actors = await db.select().from(schema.actors); assert.equal(actors.length, 2); + assert.equal(username.mock.callCount(), 3); + assert.deepEqual( + new Set(actors.map((actor) => actor.username)), + new Set(["dr_fed42", "dr_fed43"]), + ); + for (const actor of actors) { + assert.match(actor.username, /^[a-z0-9_]+$/u); + assert.equal( + actor.profileUrl, + `https://test-instance.drfed.org/@${actor.username}`, + ); + } assert.equal( actors.every( (actor) =>