Skip to content

Remove any from activypub#28557

Merged
9larsons merged 19 commits into
TryGhost:mainfrom
PedroMarianoAlmeida:remove-any-from-activypub
Jun 15, 2026
Merged

Remove any from activypub#28557
9larsons merged 19 commits into
TryGhost:mainfrom
PedroMarianoAlmeida:remove-any-from-activypub

Conversation

@PedroMarianoAlmeida

@PedroMarianoAlmeida PedroMarianoAlmeida commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

This PR is another small any cleanup, like the previous ones.

What does it do?

Removes the loose any index signatures from the shared ActivityPub API types and replaces them with the props that are already consumed in the ActivityPub app.

I checked the existing usage first, then mapped the actor/object fields that callers already rely on, including things like:

  • attachments
  • counts and state flags
  • metadata
  • authored/reposted/liked state
  • handles and following state

Follow-up review changes export ActivityPubContext, reuse the existing shared JSONObject type for JSON-shaped ActivityPub values, and remove a redundant | undefined from attributedTo.

It also adds focused tests around the mapping helpers so the existing behavior stays covered.

Why?

Using any here made the shared ActivityPub types much looser than the code actually needs. Since the consumed props are known, we can model them directly and keep the behavior the same while making the types clearer.

Impact

  • Users: No impact, internal type-safety cleanup only
  • Developers: Clearer ActivityPub actor/object types and no unsafe any index signatures in this file

Verification

  • Focused ActivityPub tests pass:
    ../../apps/activitypub/node_modules/.bin/vitest run test/unit/utils/posts.test.ts test/unit/utils/pending-activity.ts --config vite.config.mjs
  • pnpm --dir apps/admin-x-framework test:types
  • pnpm --filter @tryghost/admin-x-framework test:types
  • pnpm --dir apps/admin-x-framework exec vitest run test/unit/hooks/use-confirm-unload.test.ts
  • Pre-commit lint passed for the changed files
  • ActivityPub typecheck passes after building Shade locally

Checklist

  • I've read and followed the Contributor Guide
  • I've explained my change
  • I've written an automated test to prove my change works

PedroMarianoAlmeida and others added 5 commits June 11, 2026 23:46
Removed the explicit any suppression so the option state uses react-select's OptionProps type.

Note: committed with --no-verify because the existing pre-commit lint step fails while loading eslint-plugin-tailwindcss against Tailwind v4 in apps/admin-x-design-system, unrelated to this typing change.
Added missing semicolons and normalized spacing in the select component type definitions.

Note: committed with --no-verify because the existing pre-commit lint step fails while loading eslint-plugin-tailwindcss against Tailwind v4 in apps/admin-x-design-system, unrelated to this same-file cleanup.
no ref

Mapped the ActivityPub actor and object fields that callers already depend on so the shared API types no longer need unsafe any index signatures, with focused tests covering the existing mapping behavior.
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: aab368fc-8cfd-4899-9bb9-c677179b0145

📥 Commits

Reviewing files that changed from the base of the PR and between 6c3b460 and 34c7e53.

📒 Files selected for processing (2)
  • apps/activitypub/package.json
  • apps/activitypub/test/unit/utils/posts.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/activitypub/package.json
  • apps/activitypub/test/unit/utils/posts.test.ts

Walkthrough

This PR tightens ActivityPub type definitions by introducing three new exported type aliases (ActivityPubContext, ActivityPubAttachment, ObjectMetadata) and updating core types. ObjectProperties gains required engagement counters (replyCount, likeCount, repostCount) and an authored flag, alongside optional liked, reposted, and metadata fields. ActorProperties adds optional profile fields including handle, followedByMe, followsMe, follower/following counts, and bio. The implementation updates pending activity generation to initialize likeCount: 0, normalizes feed item attachments to the ActivityPubAttachment shape, and guards notification profile navigation and follow-button rendering on the presence of actor.handle. Unit tests expand coverage to assert the new engagement fields, metadata preservation, and handle mappings.

Possibly related PRs

  • TryGhost/Ghost#28477: Overlaps on actor handle mapping in mapPostToActivity and related test assertions for the handle field.
🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The PR title is vague and contains a typo ('activypub' instead of 'activitypub'). While it refers to removing 'any' types, this is too generic and does not clearly convey what the changeset accomplishes. Consider revising to: 'Tighten ActivityPub types by removing any index signatures' or similar to better reflect the scope of type safety improvements.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is detailed and clearly related to the changeset. It explains the motivation (removing loose any index signatures), what was changed (explicit properties mapped from actual usage), and verification steps performed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

no ref

Removed the unsupported second message argument from Vitest expect calls because admin-x-framework test:types failed with TS2554: Expected 1 arguments, but got 2.
@PedroMarianoAlmeida PedroMarianoAlmeida marked this pull request as ready for review June 12, 2026 21:55

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/admin-x-framework/src/api/activitypub.ts (2)

3-3: 📐 Maintainability & Code Quality | ⚡ Quick win

Consider exporting ActivityPubContext for public API clarity.

ActivityPubContext is used in the public ObjectProperties and ActorProperties types but is not exported. While TypeScript permits this, exporting it would allow external consumers to reference the type directly and improve type reusability.

📤 Suggested export
-type ActivityPubContext = string | (string | Record<string, unknown>)[];
+export type ActivityPubContext = string | (string | Record<string, unknown>)[];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/admin-x-framework/src/api/activitypub.ts` at line 3, The type
ActivityPubContext is currently declared but not exported even though
ObjectProperties and ActorProperties (public API types) reference it; export
ActivityPubContext so external consumers can import and reuse it—update the
declaration of ActivityPubContext to be exported (export type ActivityPubContext
= ...) and ensure any dependent types like ObjectProperties and ActorProperties
still compile against the exported symbol.

32-32: 📐 Maintainability & Code Quality | 💤 Low value

Redundant | undefined in optional property.

The attributedTo property is marked optional with ?, which already includes | undefined in the type union. The explicit | undefined at the end is redundant.

♻️ Optional cleanup
-    attributedTo?: ActorProperties | string | ActorProperties[] | Record<string, unknown> | Record<string, unknown>[] | undefined;
+    attributedTo?: ActorProperties | string | ActorProperties[] | Record<string, unknown> | Record<string, unknown>[];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/admin-x-framework/src/api/activitypub.ts` at line 32, The optional
property attributedTo in the ActivityPub type includes a redundant "| undefined"
because the "?" already allows undefined; remove the final " | undefined" from
the attributedTo declaration to clean up the type signature (look for the
attributedTo property in the ActivityPub/interface declaration in
apps/admin-x-framework/src/api/activitypub.ts).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/admin-x-framework/src/api/activitypub.ts`:
- Line 3: The type ActivityPubContext is currently declared but not exported
even though ObjectProperties and ActorProperties (public API types) reference
it; export ActivityPubContext so external consumers can import and reuse
it—update the declaration of ActivityPubContext to be exported (export type
ActivityPubContext = ...) and ensure any dependent types like ObjectProperties
and ActorProperties still compile against the exported symbol.
- Line 32: The optional property attributedTo in the ActivityPub type includes a
redundant "| undefined" because the "?" already allows undefined; remove the
final " | undefined" from the attributedTo declaration to clean up the type
signature (look for the attributedTo property in the ActivityPub/interface
declaration in apps/admin-x-framework/src/api/activitypub.ts).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5a659aad-c5c5-4be2-ae3c-1c063cd238ae

📥 Commits

Reviewing files that changed from the base of the PR and between c490404 and b76361b.

📒 Files selected for processing (3)
  • apps/activitypub/test/unit/utils/pending-activity.ts
  • apps/activitypub/test/unit/utils/posts.test.ts
  • apps/admin-x-framework/src/api/activitypub.ts

@PedroMarianoAlmeida

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@PedroMarianoAlmeida

Copy link
Copy Markdown
Contributor Author

Implemented the CodeRabbit nitpicks in follow-up commits:

  • 1c09895df9 exported ActivityPubContext and removed the redundant | undefined from attributedTo
  • a5744dad14 replaced the remaining fallback Record<string, unknown> usage with the package's existing JSONObject type

Validation passed with:

pnpm --filter @tryghost/admin-x-framework test:types

9larsons added 2 commits June 13, 2026 13:18
The vitest expect(actual, message) overload typechecks fine (see
@vitest/expect ExpectStatic), so there was no need to drop the
diagnostic messages; keeping them preserves the failure context.

@9larsons 9larsons left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this! I removed the changes to use-confirm-unload.test.ts - vitest does support the message so this felt like a small regression.

@nx-cloud

nx-cloud Bot commented Jun 13, 2026

Copy link
Copy Markdown

🤖 Nx Cloud AI Fix

Ensure the fix-ci command is configured to always run in your CI pipeline to get automatic fixes in future runs. For more information, please see https://nx.dev/ci/features/self-healing-ci


View your CI Pipeline Execution ↗ for commit 34c7e53

Command Status Duration Result
nx run @tryghost/admin-x-settings:test:acceptance ✅ Succeeded 10m 31s View ↗
nx build @tryghost/announcement-bar ✅ Succeeded <1s View ↗
nx build @tryghost/admin-toolbar ✅ Succeeded <1s View ↗
nx build @tryghost/portal ✅ Succeeded <1s View ↗
nx build @tryghost/sodo-search ✅ Succeeded <1s View ↗
nx build @tryghost/signup-form ✅ Succeeded <1s View ↗
nx build @tryghost/activitypub ✅ Succeeded 1s View ↗
nx build @tryghost/comments-ui ✅ Succeeded <1s View ↗
Additional runs (8) ✅ Succeeded ... View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-06-15 15:53:33 UTC

@9larsons 9larsons enabled auto-merge (squash) June 13, 2026 18:23
@9larsons

9larsons commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Hey, I found a few issues when investigating further and running tests: CI is red because the activitypub app no longer typechecks against the new types.

apps/activitypub imports the built types from @tryghost/admin-x-framework (types/api/activitypub.d.ts), not the source. On a normal local checkout that built file is stale — it still contains the old [x: string]: any. So tsc in the app passes against the loose types, which is almost certainly why it looked clean for you ("typecheck passes after building Shade locally"). CI rebuilds admin-x-framework first, so it checks the app against the new strict types and the gaps surface.

Removing a catch-all index signature is only safe if every property the consumers actually read is modeled. A few are still missing:
ActorProperties — missing fields (profile-preview-hover-card.tsx:84-87):

  • followsMe, followingCount, followerCount, bio

ObjectProperties — missing / mismatched:

  • createdAt is read in render-timestamp.tsx:16 but isn't on the type
  • replyCount / repostCount are now optional (number | undefined), but callers in use-activity-pub-queries.ts:824,1920,1954 use them where a non-optional number is required — needs either a non-optional model or a fallback at the call sites

image union not narrowed (feed-item.tsx ~16 hits, image-lightbox.tsx:38-41):

  • .url / .mediaType / .name / .type are accessed on string | { url: string; mediaType?; type? } without first narrowing the string case. These read like they're actually iterating attachment, so it may be that the access sites need the ActivityPubAttachment shape rather than the image shape — worth checking which field they really walk.

string | undefined not assignable to string (feed-item-stats.tsx:65,71,138,146, feed-item.tsx:359, new-note-modal.tsx:81, notifications.tsx:375,392):

  • Optional fields (e.g. url, handles) flowing into params/props typed as required string — guard or default at the call site, or relax the consumer's signature.

auto-merge was automatically disabled June 13, 2026 19:16

Head branch was pushed to by a user without write access

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/activitypub/src/views/notifications/notifications.tsx (1)

271-276: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing handle guard in single-follow row click path

Line 275 still calls handleProfileClick without verifying a handle. This bypasses the new guard behavior added elsewhere and can route to an invalid/fallback profile path when handle is absent.

Suggested fix
         case 'follow':
             if (group.actors.length > 1) {
                 toggleOpen(group.id || `${group.type}_${index}`);
             } else {
-                handleProfileClick(group.actors[0].handle, navigate);
+                if (group.actors[0].handle) {
+                    handleProfileClick(group.actors[0].handle, navigate);
+                }
             }
             break;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/activitypub/src/views/notifications/notifications.tsx` around lines 271
- 276, In the 'follow' branch fix the single-actor path to guard the handle
before calling handleProfileClick: check group.actors[0]?.handle and if truthy
call handleProfileClick(group.actors[0].handle, navigate); otherwise fall back
to toggleOpen(group.id || `${group.type}_${index}`) (or no-op if you prefer), so
the code mirrors the new guard behavior and never calls handleProfileClick with
an undefined handle.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/activitypub/src/views/notifications/notifications.tsx`:
- Around line 271-276: In the 'follow' branch fix the single-actor path to guard
the handle before calling handleProfileClick: check group.actors[0]?.handle and
if truthy call handleProfileClick(group.actors[0].handle, navigate); otherwise
fall back to toggleOpen(group.id || `${group.type}_${index}`) (or no-op if you
prefer), so the code mirrors the new guard behavior and never calls
handleProfileClick with an undefined handle.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c8ce085d-d9bd-4d08-9f56-94c7a57da524

📥 Commits

Reviewing files that changed from the base of the PR and between 7093f8e and 969e6f4.

📒 Files selected for processing (5)
  • apps/activitypub/package.json
  • apps/activitypub/src/components/feed/feed-item.tsx
  • apps/activitypub/src/utils/pending-activity.ts
  • apps/activitypub/src/views/notifications/notifications.tsx
  • apps/admin-x-framework/src/api/activitypub.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/activitypub/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/admin-x-framework/src/api/activitypub.ts

@PedroMarianoAlmeida

Copy link
Copy Markdown
Contributor Author

Thanks for digging into this, that makes sense. The stale built types explanation was the missing piece here.

I pushed 969e6f4 to address the gaps you called out:

  • added the remaining ActivityPub fields the app is already reading
  • made the count fields match the non-optional usage in the cache update paths
  • normalized the image/attachment handling so render code works with the attachment shape instead of reading attachment fields from a possible string
  • guarded the optional handle cases where the consumer expects a real string

I also reproduced the same failing path locally by rebuilding the admin-x-framework declarations first, then running the activitypub checks. These are green now on my machine:

CI=true PATH=/Users/pedroalmeida/.nvm/versions/node/v22.13.1/bin:$PATH pnpm nx run @tryghost/activitypub:build
CI=true PATH=/Users/pedroalmeida/.nvm/versions/node/v22.13.1/bin:$PATH pnpm nx run @tryghost/activitypub:test:unit

So I think this should be fixed now. Waiting for the GitHub pipeline to confirm everything is good.

no ref

Guarded notification profile navigation before calling handleProfileClick because notification actors can arrive without a handle at runtime even though the local type currently marks it as required. Also formatted the visible notification actor counts after touching the TSX file.
@PedroMarianoAlmeida

PedroMarianoAlmeida commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up verification for the checks that failed previously:

  • pnpm nx run @tryghost/admin:build passed
  • pnpm nx run-many -t test:unit -p @tryghost/activitypub,@tryghost/admin,@tryghost/admin-x-framework,@tryghost/admin-x-settings,@tryghost/posts,@tryghost/stats passed
  • pnpm nx run-many --target=build --projects=@tryghost/portal,@tryghost/comments-ui,@tryghost/sodo-search,@tryghost/signup-form,@tryghost/announcement-bar passed

And I also added an extra test with all postprops

no ref

Covered the ActivityPub post mapper fields added for object engagement state so zero and false API values are preserved by tests.
@9larsons 9larsons enabled auto-merge (squash) June 15, 2026 15:45
@9larsons 9larsons merged commit df50579 into TryGhost:main Jun 15, 2026
45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants