Skip to content

feat: plugin client config#190

Open
MarioCadenas wants to merge 1 commit intomainfrom
client-exports-config
Open

feat: plugin client config#190
MarioCadenas wants to merge 1 commit intomainfrom
client-exports-config

Conversation

@MarioCadenas
Copy link
Collaborator

@MarioCadenas MarioCadenas commented Mar 17, 2026

Summary

Add a clientConfig() method to the Plugin base class so plugins can expose server-side boot-time configuration to the frontend without dedicated API endpoints, with automatic redaction of leaked environment variables.

Main changes

Plugin clientConfig() API (packages/appkit/src/plugin/plugin.ts, packages/shared/src/plugin.ts):
Adds clientConfig() to the Plugin base class and BasePlugin interface. Plugins override this method to return JSON-serializable data that gets injected into the HTML at server startup. The method is excluded from the asUser() proxy since it runs once at boot, not per-request. FilesPlugin adopts it immediately to expose its volumes list.

Env var redaction & config validation (packages/appkit/src/plugins/server/utils.ts):
Introduces sanitizeClientConfig() which validates the return value (rejects BigInts, functions, circular refs, __proto__ keys) and then scans all string values for matches against non-public process.env values. Matches are replaced with [redacted by appkit] and a warning is logged. Values from PUBLIC_APPKIT_-prefixed env vars are allowed through, with range-based overlap resolution to handle substring relationships between public and non-public values.

Server-side config delivery pipeline (packages/appkit/src/plugins/server/index.ts, base-server.ts, static-server.ts, vite-dev-server.ts):
Replaces window.__CONFIG__ = ... with an inert <script id="__appkit__" type="application/json"> tag containing XSS-escaped JSON, parsed by a companion <script>. The extendRoutes() method collects and sanitizes each plugin's clientConfig(), then threads the result through BaseServerStaticServer/ViteDevServergetConfigScript().

Client-side config reader (packages/appkit-ui/src/js/config.ts, packages/appkit-ui/src/react/hooks/use-plugin-config.ts):
Provides getClientConfig() which reads from the boot-time <script> payload, caches in a module-scoped variable (immune to window.__appkit__ mutations), and normalizes partial data. getPluginClientConfig() returns a specific plugin's config with a frozen empty-object fallback for referential stability. The usePluginClientConfig React hook wraps this with useMemo.

Supporting infrastructure

  • packages/appkit-ui/src/js/config.test.ts — 7 tests covering DOM parsing, malformed JSON, caching, partial data, empty content, and stable references.
  • packages/appkit-ui/src/react/hooks/__tests__/use-plugin-config.test.ts — 3 tests for the React hook (typed config, unknown plugin, referential stability).
  • packages/appkit/src/plugins/server/tests/utils.test.ts — 28 new tests for getRuntimeConfig, getConfigScript, and sanitizeClientConfig (redaction, overlap resolution, embedded values, keys, validation errors).
  • packages/appkit/src/plugins/server/tests/server.test.ts — 3 new tests for extendRoutes collecting clientConfig, handling null returns, and rejecting invalid values.
  • packages/shared/src/plugin.ts — New PluginClientConfigs type alias.
  • docs/docs/api/appkit/Class.Plugin.md — API reference for clientConfig().

Other changes

  • apps/dev-playground/package.json — Added NODE_ENV=development to dev:inspect script for consistency with dev.
  • apps/dev-playground/client/src/routes/files.route.tsx — Replaced fetch("/api/files/volumes") + useState with usePluginClientConfig("files").
  • packages/appkit/src/plugins/server/remote-tunnel/remote-tunnel-manager.test.ts — Updated assertion from window.__CONFIG__ to window.__appkit__.
  • packages/appkit/src/plugins/server/tests/static-server.test.ts — Updated mocks and assertions to match the new config script format.
  • docs/static/appkit-ui/styles.gen.css — Auto-generated CSS additions (grid utilities, inactive state).

How it looks like

if a plugin tries to expose a secret

image image

@MarioCadenas MarioCadenas force-pushed the client-exports-config branch 8 times, most recently from 68594ae to 29c347d Compare March 17, 2026 17:07
@MarioCadenas MarioCadenas changed the title chore: poc client config feat: plugin client config Mar 17, 2026
@MarioCadenas MarioCadenas marked this pull request as ready for review March 17, 2026 17:07
@MarioCadenas MarioCadenas force-pushed the client-exports-config branch from 29c347d to a797684 Compare March 17, 2026 17:19
chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup

chore: fixup
@MarioCadenas MarioCadenas force-pushed the client-exports-config branch from a797684 to 80ec1d2 Compare March 17, 2026 17:21
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.

1 participant