Skip to content

Add public plugin settings helpers#148

Merged
ascorbic merged 7 commits intoemdash-cms:mainfrom
masonjames:codex/plugin-settings-helpers
Apr 11, 2026
Merged

Add public plugin settings helpers#148
ascorbic merged 7 commits intoemdash-cms:mainfrom
masonjames:codex/plugin-settings-helpers

Conversation

@masonjames
Copy link
Copy Markdown
Contributor

@masonjames masonjames commented Apr 3, 2026

What does this PR do?

Adds public getPluginSetting() and getPluginSettings() helpers to EmDash core so trusted/plugin SSR code can read persisted plugin settings without depending on the raw options-table key format.

It also fixes plugin-prefix lookups so plugin IDs containing % or _ are treated literally, and adds targeted tests for unset settings, round-tripping, and wildcard-like IDs.

Type of change

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

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm --silent lint:json | jq '.diagnostics | length' returns 0
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion: https://github.com/emdash-cms/emdash/discussions/...

AI-generated code disclosure

  • This PR includes AI-generated code

Screenshots / test output

  • pnpm --silent lint:quick
  • pnpm --filter emdash exec vitest run tests/unit/settings/settings.test.ts
  • pnpm typecheck
  • pnpm --silent lint:json | jq '.diagnostics | length'

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 3, 2026

🦋 Changeset detected

Latest commit: 3d9fabc

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

This PR includes changesets to release 9 packages
Name Type
emdash Patch
@emdash-cms/cloudflare Patch
@emdash-cms/plugin-ai-moderation Patch
@emdash-cms/plugin-atproto Patch
@emdash-cms/plugin-audit-log Patch
@emdash-cms/plugin-color Patch
@emdash-cms/plugin-embeds Patch
@emdash-cms/plugin-forms Patch
@emdash-cms/plugin-webhook-notifier 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

@masonjames masonjames changed the title [codex] Add public plugin settings helpers Add public plugin settings helpers Apr 3, 2026
@masonjames masonjames marked this pull request as ready for review April 3, 2026 02:00
Copilot AI review requested due to automatic review settings April 4, 2026 10:05
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 4, 2026

Open in StackBlitz

@emdash-cms/admin

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

@emdash-cms/auth

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

@emdash-cms/blocks

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

@emdash-cms/cloudflare

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

emdash

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

create-emdash

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

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

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

@emdash-cms/x402

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

@emdash-cms/plugin-ai-moderation

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

@emdash-cms/plugin-atproto

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

@emdash-cms/plugin-audit-log

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

@emdash-cms/plugin-color

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

@emdash-cms/plugin-embeds

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

@emdash-cms/plugin-forms

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

@emdash-cms/plugin-webhook-notifier

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

commit: 3d9fabc

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a public runtime API for reading persisted plugin-scoped settings from EmDash core, enabling SSR/template code and plugin block rendering to access plugin settings without directly querying the options-table layout.

Changes:

  • Added getPluginSetting() / getPluginSettings() (and *WithDb variants) to the core settings module.
  • Re-exported the new helpers from the emdash package entrypoint.

Reviewed changes

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

File Description
packages/core/src/settings/index.ts Introduces new plugin settings read helpers (single key + all settings by plugin).
packages/core/src/index.ts Re-exports the new helpers from the main public package entrypoint.

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


const settings: Record<string, unknown> = {};
for (const [key, value] of allOptions) {
settings[key.replace(prefix, "")] = value;
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

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

getPluginSettingsWithDb() builds a SQL LIKE prefix using pluginId and passes it to OptionsRepository.getByPrefix(), which does where name like "${prefix}%" without escaping. If pluginId contains %, _, or \, the query can match settings for other plugins and also break the key.replace(prefix, "") stripping (keys may not actually start with the literal prefix). Please escape LIKE metacharacters (e.g., using an ESCAPE '\\' clause like MediaRepository does) or validate/restrict pluginId/prefix before querying.

Suggested change
settings[key.replace(prefix, "")] = value;
if (!key.startsWith(prefix)) {
continue;
}
settings[key.slice(prefix.length)] = value;

Copilot uses AI. Check for mistakes.
Comment on lines +211 to +263
export async function getPluginSetting<T = unknown>(
pluginId: string,
key: string,
): Promise<T | undefined> {
const db = await getDb();
return getPluginSettingWithDb<T>(pluginId, key, db);
}

/**
* Get a single plugin setting by key (with explicit db).
*
* @internal Use `getPluginSetting()` in templates and plugin rendering code.
*/
export async function getPluginSettingWithDb<T = unknown>(
pluginId: string,
key: string,
db: Kysely<Database>,
): Promise<T | undefined> {
const options = new OptionsRepository(db);
const value = await options.get<T>(`plugin:${pluginId}:settings:${key}`);
return value ?? undefined;
}

/**
* Get all persisted plugin settings for a plugin.
*
* Defaults declared in `admin.settingsSchema` are not materialized
* automatically; callers should apply their own fallback defaults.
*/
export async function getPluginSettings(pluginId: string): Promise<Record<string, unknown>> {
const db = await getDb();
return getPluginSettingsWithDb(pluginId, db);
}

/**
* Get all persisted plugin settings for a plugin (with explicit db).
*
* @internal Use `getPluginSettings()` in templates and plugin rendering code.
*/
export async function getPluginSettingsWithDb(
pluginId: string,
db: Kysely<Database>,
): Promise<Record<string, unknown>> {
const prefix = `plugin:${pluginId}:settings:`;
const options = new OptionsRepository(db);
const allOptions = await options.getByPrefix(prefix);

const settings: Record<string, unknown> = {};
for (const [key, value] of allOptions) {
settings[key.replace(prefix, "")] = value;
}

return settings;
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

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

New public helpers getPluginSetting* / getPluginSettings* are added in settings/index.ts, but the existing unit test suite in packages/core/tests/unit/settings/settings.test.ts only covers site settings. Please add tests that verify (1) a single plugin setting can be read by key and returns undefined when unset, and (2) getPluginSettingsWithDb() returns a de-prefixed object for multiple stored keys (and ideally covers edge cases like falsy values).

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 5, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

Copy link
Copy Markdown
Collaborator

@ascorbic ascorbic left a comment

Choose a reason for hiding this comment

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

Thanks!

@ascorbic ascorbic enabled auto-merge (squash) April 11, 2026 09:10
@ascorbic ascorbic merged commit 1989e8b into emdash-cms:main Apr 11, 2026
25 checks passed
@emdashbot emdashbot bot mentioned this pull request Apr 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants