Skip to content

Add after-restore hook - #1658

Merged
ascorbic merged 3 commits into
emdash-cms:mainfrom
ttmx:feat/content-after-restore-hook
Jun 30, 2026
Merged

Add after-restore hook#1658
ascorbic merged 3 commits into
emdash-cms:mainfrom
ttmx:feat/content-after-restore-hook

Conversation

@ttmx

@ttmx ttmx commented Jun 29, 2026

Copy link
Copy Markdown
Contributor
  • refactor(core): return restored content item
    Refactored the restore content call in core as to avoid extra db lookups, now "restore" returns the restored item.

  • feat(core): add content restore hook

  • docs: add content restore hook docs

What does this PR do?

Adds a hook so plugins can be notified when an item is restored from the bin, so plugins can keep up with what posts are visible.

Closes #

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). Do not include messages.po changes except in translation PRs — a workflow extracts catalogs on merge to main.
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion: (Discussed in internal gchat)

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: GPT-5.5

Screenshots / test output

Copilot AI review requested due to automatic review settings June 29, 2026 14:12
@changeset-bot

changeset-bot Bot commented Jun 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9585274

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
Name Type
emdash Minor
@emdash-cms/cloudflare Minor
@emdash-cms/sandbox-workerd Patch
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/admin Minor
@emdash-cms/auth Minor
@emdash-cms/blocks Minor
@emdash-cms/gutenberg-to-portable-text Minor
@emdash-cms/x402 Minor
create-emdash Minor
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

PR template validation failed

Please fix the following issues by editing your PR description:

See CONTRIBUTING.md for the full contribution policy.

@github-actions github-actions Bot added review/needs-review No maintainer or bot review yet query-count changed PR diff modifies query-count snapshot files labels Jun 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Query-count snapshot changes

40 routes changed, total Δ +182 queries.

SQLite

Route Before After Δ
GET / (cold) 6 11 +5
GET / (warm) 6 11 +5
GET /category/development (cold) 10 15 +5
GET /category/development (warm) 9 14 +5
GET /contributors (cold) 6 11 +5
GET /contributors (warm) 6 11 +5
GET /contributors-naive (cold) 13 18 +5
GET /contributors-naive (warm) 13 18 +5
GET /pages/about (cold) 6 10 +4
GET /pages/about (warm) 6 10 +4
GET /posts (cold) 6 11 +5
GET /posts (warm) 6 11 +5
GET /posts/building-for-the-long-term (cold) 17 24 +7
GET /posts/building-for-the-long-term (warm) 17 24 +7
GET /rss.xml (cold) 2 5 +3
GET /rss.xml (warm) 2 5 +3
GET /search (cold) 10 12 +2
GET /search (warm) 10 12 +2
GET /tag/webdev (cold) 9 14 +5
GET /tag/webdev (warm) 9 14 +5

D1

Route Before After Δ
GET / (cold) 17 22 +5
GET / (warm) 6 11 +5
GET /category/development (cold) 21 26 +5
GET /category/development (warm) 9 14 +5
GET /contributors (cold) 17 22 +5
GET /contributors (warm) 6 11 +5
GET /contributors-naive (cold) 24 29 +5
GET /contributors-naive (warm) 13 18 +5
GET /pages/about (cold) 17 20 +3
GET /pages/about (warm) 6 10 +4
GET /posts (cold) 17 22 +5
GET /posts (warm) 6 11 +5
GET /posts/building-for-the-long-term (cold) 28 35 +7
GET /posts/building-for-the-long-term (warm) 17 24 +7
GET /rss.xml (cold) 12 15 +3
GET /rss.xml (warm) 2 5 +3
GET /search (cold) 21 22 +1
GET /search (warm) 10 12 +2
GET /tag/webdev (cold) 21 26 +5
GET /tag/webdev (warm) 9 14 +5

Comparing snapshot files between base and head. Updated automatically on each push.

@github-actions

Copy link
Copy Markdown
Contributor

Scope check

This PR touches 26 files. PRs with a broad scope are harder to review. Please confirm the scope hasn't drifted beyond the intended change.

If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs.

See CONTRIBUTING.md for contribution guidelines.

@pkg-pr-new

pkg-pr-new Bot commented Jun 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@1658

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@1658

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@1658

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@1658

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@1658

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@1658

emdash

npm i https://pkg.pr.new/emdash@1658

create-emdash

npm i https://pkg.pr.new/create-emdash@1658

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@1658

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@1658

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@1658

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@1658

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@1658

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@1658

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@1658

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@1658

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@1658

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@1658

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@1658

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@1658

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@1658

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@1658

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@1658

commit: 9585274

collection: string,
id: string,
): Promise<ApiResult<{ restored: true }>> {
): Promise<ApiResult<{ restored: true; item: ContentItem }>> {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

kept restored: true for backwards compat

const tableName = getTableName(type);

const result = await sql`
const result = await sql<Record<string, unknown>>`

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added returning the restored element instead of a true or false, to avoid extra queries and concurrency inconsistencies.

Copilot AI 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.

Pull request overview

This PR adds a new content:afterRestore plugin hook that fires when trashed content is restored, and refactors the core restore path to return the restored ContentItem (avoiding an extra DB lookup). It also updates plugin/reference documentation and adjusts tests to match the new restore return shape.

Changes:

  • Add content:afterRestore to the hook system (types, manifest validation, capability enforcement, pipeline execution, and runtime dispatch to trusted + sandboxed plugins).
  • Refactor content restore to return the restored item (ContentItem | null) and thread that through the handler/runtime.
  • Update docs/templates and tests to document/validate the new hook and restore behavior.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated no comments.

Show a summary per file
File Description
templates/starter/.agents/skills/creating-plugins/references/hooks.md Document the new content:afterRestore hook in the starter template reference.
templates/starter-cloudflare/.agents/skills/creating-plugins/references/hooks.md Document the new content:afterRestore hook in the Cloudflare starter template reference.
templates/portfolio/.agents/skills/creating-plugins/references/hooks.md Document the new content:afterRestore hook in the portfolio template reference.
templates/portfolio-cloudflare/.agents/skills/creating-plugins/references/hooks.md Document the new content:afterRestore hook in the Cloudflare portfolio template reference.
templates/marketing/.agents/skills/creating-plugins/references/hooks.md Document the new content:afterRestore hook in the marketing template reference.
templates/marketing-cloudflare/.agents/skills/creating-plugins/references/hooks.md Document the new content:afterRestore hook in the Cloudflare marketing template reference.
templates/blog/.agents/skills/creating-plugins/references/hooks.md Document the new content:afterRestore hook in the blog template reference.
templates/blog-cloudflare/.agents/skills/creating-plugins/references/hooks.md Document the new content:afterRestore hook in the Cloudflare blog template reference.
templates/blank/.agents/skills/creating-plugins/references/hooks.md Document the new content:afterRestore hook in the blank template reference.
skills/creating-plugins/references/hooks.md Update the canonical plugin-creation hook reference to include content:afterRestore.
packages/core/tests/unit/plugins/restore-hooks.test.ts Add a unit test asserting content:afterRestore fires with the restored item payload.
packages/core/tests/unit/plugins/hooks.test.ts Extend HookPipeline tests to include registration/capability enforcement for content:afterRestore.
packages/core/tests/integration/search/fts-corruption.test.ts Update integration assertion to match restore() returning the restored row (not boolean).
packages/core/tests/integration/i18n/i18n.test.ts Update i18n integration assertion to match restore() returning the restored row (not boolean).
packages/core/src/plugins/types.ts Add ContentAfterRestoreHandler and hook typing for content:afterRestore.
packages/core/src/plugins/manifest-schema.ts Allow content:afterRestore in plugin manifest hook-name validation.
packages/core/src/plugins/manager.ts Add runContentAfterRestore() dispatch on the plugin manager.
packages/core/src/plugins/index.ts Export the new ContentAfterRestoreHandler type from the plugin entrypoint.
packages/core/src/plugins/hooks.ts Register/enforce/run the content:afterRestore hook in the HookPipeline.
packages/core/src/plugin-types.ts Add content:afterRestore to the HookHandlers typing map.
packages/core/src/emdash-runtime.ts Run content:afterRestore after a successful restore (deferred via after()), for trusted + sandboxed plugins.
packages/core/src/database/repositories/content.ts Change restore() to UPDATE ... RETURNING * and return `ContentItem
packages/core/src/api/handlers/content.ts Return { restored: true, item } from handleContentRestore() to propagate the restored item.
docs/src/content/docs/reference/hooks.mdx Document content:afterRestore in the public hook reference.
docs/src/content/docs/plugins/creating-plugins/hooks.mdx Document content:afterRestore in the plugin creation docs and hook summary table.
.changeset/content-restore-hook.md Add a minor changeset announcing the new restore hook for plugins.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions

Copy link
Copy Markdown
Contributor

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond and removed review/needs-review No maintainer or bot review yet labels Jun 29, 2026
@ascorbic ascorbic added the bot:review Trigger an emdashbot code review on this PR label Jun 30, 2026

@emdashbot emdashbot 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.

The approach is sound: content:afterRestore fills a clear gap in the content lifecycle hook surface, and refactoring ContentRepository.restore to return the restored item lets the runtime hand it to plugins without an extra round-trip.

I checked the restore handler (packages/core/src/api/handlers/content.ts), the repository update (packages/core/src/database/repositories/content.ts), runtime hook dispatch (packages/core/src/emdash-runtime.ts), hook-pipeline registration and capability gating (packages/core/src/plugins/hooks.ts), the plugin manager/types/manifest-schema exports, the route consumer, and the new/updated tests and docs.

Headline conclusion: the code is clean. The response widening from { restored: true } to { restored: true; item } is backwards-compatible, the RETURNING * update is safe and avoids the extra lookup, and the fire-and-forget dispatch matches the existing afterSave/afterPublish pattern. Tests cover the trusted-plugin path and existing .restore() callers were updated for the new return type.

One non-blocking process note: AGENTS.md/CONTRIBUTING.md require a maintainer-approved Discussion for new features. The PR description cites internal gchat discussion but does not link to a public approved Discussion. Worth confirming that sign-off exists before merging, though the feature itself is a natural fit.

@ascorbic ascorbic left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good. Thanks

@ascorbic
ascorbic merged commit 1f4aa59 into emdash-cms:main Jun 30, 2026
55 of 57 checks passed
@emdashbot emdashbot Bot mentioned this pull request Jun 30, 2026
marcusbellamyshaw-cell pushed a commit to Emdash-Bug-Testing/emdash that referenced this pull request Jul 22, 2026
* refactor(core): return restored content item

* feat(core): add content restore hook

* docs: add content restore hook docs
marcusbellamyshaw-cell pushed a commit to Emdash-Bug-Testing/emdash that referenced this pull request Jul 22, 2026
* refactor(core): return restored content item

* feat(core): add content restore hook

* docs: add content restore hook docs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core area/docs area/templates bot:review Trigger an emdashbot code review on this PR overlap query-count changed PR diff modifies query-count snapshot files review/awaiting-author Reviewed; waiting on the author to respond size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants