feat: add onPluginsReady callback to createApp, remove autoStart#280
feat: add onPluginsReady callback to createApp, remove autoStart#280MarioCadenas merged 6 commits intomainfrom
Conversation
d49f16d to
33e0b86
Compare
|
I think this is a step in the right direction and we should have this ASAP. That being said, as we talked on Slack (just surfacing this here), the API name could change. The use case we need to cover immediately is |
023d7e7 to
25b8c60
Compare
Replace the post-await extend/start ceremony with a declarative `customize` callback on createApp config. The callback runs after plugin setup but before the server starts, giving access to the full appkit handle for registering custom routes or async setup. - Add `customize` option to createApp config - Server start is now orchestrated by createApp (lookup by name) - Remove `autoStart` from public API, ServerConfig, and manifest - Remove `start()` from server plugin exports - Remove autoStart guards from extend() and getServer() - Remove ServerError.autoStartConflict() - Migrate dev-playground, template, and all tests Signed-off-by: MarioCadenas <[email protected]>
… detection Rename the lifecycle hook from `customize` to `onPluginsReady` to clearly communicate when it fires (after plugins are ready, before server starts). Add `appkit codemod customize-callback` CLI command that auto-migrates old autoStart/extend/start patterns to the new onPluginsReady callback. Supports both .then() chain (Pattern A) and await + imperative (Pattern B, with bail-out for complex cases). Add runtime detection that throws helpful errors when users pass autoStart to server() or call server.start() after upgrading, directing them to run the codemod. Signed-off-by: MarioCadenas <[email protected]>
The test fixture .ts files import @databricks/appkit which doesn't exist in the shared package, causing tsc to fail in CI. Exclude the fixtures directory from the shared tsconfig. Signed-off-by: MarioCadenas <[email protected]>
Remove the codemod CLI from this PR to keep the review focused on the core lifecycle change. The codemod will land as a follow-up with bug fixes from review. Runtime detection (constructor autoStart throw + exports().start() trap) stays since it's part of the migration story. Signed-off-by: MarioCadenas <[email protected]>
Log when the onPluginsReady hook starts and completes to aid debugging in development mode. Signed-off-by: MarioCadenas <[email protected]>
Update runtime detection error messages to point users to `npx appkit codemod on-plugins-ready` to match the hook name. Signed-off-by: MarioCadenas <[email protected]>
0e8847c to
c03f8d0
Compare
ditadi
left a comment
There was a problem hiding this comment.
I like this a lot.
I was thinking about something similar to AppKit's lifecycle.
I just don't know if the wording onPluginsReady is the best naming, but it is more personal. Maybe something beforeStart or something.
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
Rebase onto main pulled in the new `jobs` plugin (#280) and a duplicate `export * from "./plugins/stable-exports.generated";` from the conflict resolution in `packages/appkit/src/index.ts`. Two follow-on changes: - Re-run `tools/generate-plugin-entries.ts` so `jobs` is included in `stable-exports.generated.ts`. The codegen commit on this branch predates the jobs plugin landing. - Dedupe the duplicate `export *` line in `index.ts` that crept in during the merge resolution of the codegen commit. No behavior change beyond making `jobs` reachable through `@databricks/appkit` (matching what main already does via the hand-curated barrel that this PR replaces). Signed-off-by: MarioCadenas <[email protected]>
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
Rebase onto main pulled in the new `jobs` plugin (#280) and a duplicate `export * from "./plugins/stable-exports.generated";` from the conflict resolution in `packages/appkit/src/index.ts`. Two follow-on changes: - Re-run `tools/generate-plugin-entries.ts` so `jobs` is included in `stable-exports.generated.ts`. The codegen commit on this branch predates the jobs plugin landing. - Dedupe the duplicate `export *` line in `index.ts` that crept in during the merge resolution of the codegen commit. No behavior change beyond making `jobs` reachable through `@databricks/appkit` (matching what main already does via the hand-curated barrel that this PR replaces). Signed-off-by: MarioCadenas <[email protected]>
* feat: add plugin stability system with import-path enforcement Add a `stability` field to plugin manifests with full CLI tooling. Plugins declare their tier in manifest.json; the import path (`@databricks/appkit/<tier>` for non-stable, `@databricks/appkit` for stable) enforces the contract at the package boundary so consumers cannot accidentally take a dependency on an unstable API. This commit lands the initial three-tier implementation (experimental/preview/stable). A follow-up commit on this branch collapses to two tiers (beta/stable); the per-commit history shows the evolution. - Schema: stability enum on plugin-manifest and template-plugins schemas - Entrypoints: per-tier subpath exports in appkit and appkit-ui - CLI sync: propagate stability, strip requiredByTemplate for non-stable, orphan resource detection, template version bumped to 1.1 - CLI list: always-visible STABILITY column - CLI scaffold: stability prompt during plugin create - CLI promote: new command to promote plugins between tiers - Docs: stability tiers documentation page Signed-off-by: MarioCadenas <[email protected]> * fix(plugin): harden promote command against path traversal & sync failures Address review findings on the three-tier plugin stability system: Security: - Validate plugin name against npm-package charset (rejects "..", path separators, NUL, backslash; allows @scope/name) - Bound the node_modules manifest lookup with isWithinDirectory so a typo'd or malicious name cannot escape dist/plugins - Refuse to mutate manifests under node_modules unless --allow-installed is passed (reinstall would silently revert otherwise) - Replace console.error(err) in the action handler with a fixed message and a DEBUG-gated full-error dump to avoid leaking absolute paths / stack frames into telemetry Correctness: - Validate the manifest's stability field before any mutation; reject unknown values (e.g. "Stable") instead of silently treating them as stable and writing back - Make a failed post-promote 'plugin sync' exit non-zero and skip the success message - Guard sync.ts orphan detection against null / non-object plugin entries in a stale appkit.plugins.json Robustness: - Skip symlinked directories during the project walk (prevents both cycles and out-of-tree rewrites) - Expand the directory-skip list (.git, .turbo, .next, .nuxt, .cache, .svelte-kit, .vite, .parcel-cache, coverage, build, out) - Align findTsFiles depth cap with sync.ts MAX_SCAN_DEPTH - Add boundary check inside the recursive TS walk Tests: - Replace placeholder tests with real runPromote integration tests covering invalid names, path-traversal attempts, unknown tiers, invalid manifest stability, demotion, no-op, dry-run, import rewriting, symlink boundaries, and the node_modules guard Signed-off-by: MarioCadenas <[email protected]> * docs: regenerate plugin-manifest schema & typedoc for stability field Re-running 'pnpm docs:build' picks up three derived artifacts that the original feature commit missed: - docs/docs/api/appkit/Interface.PluginManifest.md (typedoc): adds the new optional 'stability' field on PluginManifest - docs/static/schemas/plugin-manifest.schema.json: adds the experimental/preview/stable enum (default "stable") - docs/static/schemas/template-plugins.schema.json: adds 'stability' and bumps schema version from const "1.0" to enum ["1.0","1.1"] Generated by 'pnpm docs:build'; no source changes. Signed-off-by: MarioCadenas <[email protected]> * refactor(plugin): collapse stability tiers to beta/stable Removes the "experimental" tier entirely and renames "preview" to "beta". The stability system now has two tiers instead of three: beta ──→ stable Beta plugins follow the previous Preview contract: APIs may change between minor releases, but the plugin is on a path to stable. Changes: - Schema: plugin-manifest and template-plugins enums updated to ["beta", "stable"]; manifest schema version stays at 1.1. - Subpath exports: drop @databricks/appkit/experimental and @databricks/appkit-ui/{js,react}/experimental; rename /preview to /beta. Source entry files renamed via git mv (experimental.ts files removed). - Promote command: TIER_ORDER, IMPORT_PATH_MAP, isStability(), help text, and tests updated. Legacy tier values are now rejected both as --to targets and as manifest stability values; tests assert this. - Create command: stability prompt offers Stable | Beta only. - list/sync/CreateAnswers types narrowed to "beta" | "stable". - Docs: stability.md rewritten for the two-tier system; auto-generated Interface.PluginManifest.md and docs/static/schemas/* match the new enum after pnpm docs:build. Signed-off-by: MarioCadenas <[email protected]> * fix(template): import beta plugins from /beta subpath The init template hard-coded a single `from '@databricks/appkit'` import for every plugin, which would resolve to `undefined` for beta plugins (they live behind the `/beta` subpath export). Splits the import emission by plugin stability: - Stable plugins stay on the existing line. - Each beta plugin gets its own `from '@databricks/appkit/beta'` import line. Backwards-compatible with the current Databricks CLI: until pluginVar exposes Stability, `$p.Stability` resolves to the empty string, every plugin compares unequal to "beta", and rendering matches today's output. Once databricks/cli#5090 lands the field, beta plugins start routing to the right import automatically. The plugins: [...] array body is unchanged — both stable and beta plugins are constructed identically (`plugin()`) once they're in scope, so the runtime call list doesn't depend on tier. Signed-off-by: MarioCadenas <[email protected]> * fix(template): consolidate beta plugin imports into a single line The previous template emitted a separate import line per beta plugin: import { betaA } from '@databricks/appkit/beta'; import { betaB } from '@databricks/appkit/beta'; Switch to a single combined import: import { betaA, betaB } from '@databricks/appkit/beta'; Implementation: a string-accumulator pre-pass over .plugins builds a comma-separated list of beta names using printf, then a single guarded import line is emitted if the list is non-empty. Pure text/template, no sprig functions required, so it works against the Databricks CLI's existing template engine. Behavior unchanged when no beta plugins are selected (no extra import line, identical to all-stable rendering today). Signed-off-by: MarioCadenas <[email protected]> * feat(appkit): codegen stable/beta export barrels from plugin manifests Closes a real gap in the stability system: the manifest's `stability` field, the synced appkit.plugins.json, and the runtime entry exports (src/index.ts, src/beta.ts) were three independent sources of truth that could drift apart. Promoting a plugin via manifest edit alone produced a broken state where the CLI generated `import { x } from "@databricks/appkit/beta"` for a plugin that was still actually exported from `@databricks/appkit`, leaving `x` undefined at runtime. Make the manifest the single source of truth. - New generator at tools/generate-plugin-entries.ts reads every packages/appkit/src/plugins/<name>/manifest.json, groups by `stability`, and writes two committed barrels: packages/appkit/src/plugins/stable-exports.generated.ts packages/appkit/src/plugins/beta-exports.generated.ts Hidden plugins (manifest.hidden = true) are skipped, preserving the current vector-search behaviour. - src/index.ts and src/beta.ts now delegate to the generated barrels via `export *`, so adding/moving a plugin only requires editing one file (its manifest). - The hand-curated kitchen-sink barrel at src/plugins/index.ts is removed; the generated barrels replace it and knip flagged it as unused. - Wired into the build: - packages/appkit/package.json#scripts.build:package now runs the generator before tsdown. - Root `generate:types` includes plugin-entries so existing CI "Check generated types are up to date" catches stale barrels. The CI step's git diff list is extended accordingly. - Added a focused `generate:plugin-entries` root script for convenience. - `appkit plugin promote` detects monorepo context (presence of tools/generate-plugin-entries.ts) and re-runs the generator after updating the manifest, before kicking off `plugin sync`. Outside the monorepo (third-party plugin projects) it's a no-op. - docs/docs/plugins/stability.md gains a "For First-Party Plugin Authors" section explaining the single-source-of-truth model and how to flip a plugin between tiers manually. Verified end-to-end: marking genie as `stability: "beta"` → `pnpm run generate:plugin-entries` → genie moves from stable-exports.generated.ts to beta-exports.generated.ts → `pnpm sync:template` → genie carries `stability: "beta"` and no `requiredByTemplate` in appkit.plugins.json. All three layers consistent without manual edits. Signed-off-by: MarioCadenas <[email protected]> * fix(plugin): close drift gaps surfaced by review Three review-blocker fixes that together restore the PR's headline "manifest is the single source of truth, all layers stay aligned" claim: 1. Post-promote sync writes to the right file in the monorepo. Previously `runPromote` shelled out to `npx appkit plugin sync --write` with no flags, which defaults `outputPath` to `<cwd>/appkit.plugins.json` and never touches `template/appkit.plugins.json` (the file shipped with the AppKit init template). In the monorepo path the manifest changed and the runtime barrels regenerated, but the synced template manifest stayed stale. Detect monorepo context the same way the generator step does (presence of `tools/generate-plugin-entries.ts`) and prefer `pnpm run sync:template` when available, which has the right flags wired (`--plugins-dir packages/appkit/src/plugins --output template/appkit.plugins.json --require-plugins server`). Outside the monorepo the original `npx appkit plugin sync --write` is still used. 2. CI freshness gate now covers `template/appkit.plugins.json`. The existing "Check generated types are up to date" step diffs the four schema/registry/barrel files but not the synced template manifest. A contributor editing a plugin's `stability` by hand and forgetting `pnpm sync:template` could land a drift between the manifest and what the Go init template branches on at scaffold time. Add a follow-up step that runs `pnpm run sync:template` and fails the build on a non-empty diff, mirroring the existing pattern. 3. Generator validates `manifest.name` and folder name against the schema regex AND a JS-identifier rule. `tools/generate-plugin-entries.ts` interpolates `name` directly into `export { ${name} } from "./${folder}";`. The schema accepts `^[a-z][a-z0-9-]*$` (kebab-case) and even shows `"my-custom-plugin"` as an example. Today this is masked because `vector-search` (the only kebab-named plugin) is `hidden: true`, but the instant any hyphenated plugin graduates the generator emits a TypeScript syntax error. The same gap is also a defense-in-depth code-injection vector (CWE-94) — a malicious manifest with `"name": "x }; await import('./evil'); //"` would generate parseable, side-effect-importing TS that runs at module load on every consumer. Add a `validateIdentifier` pass that runs both the schema regex (`^[a-z][a-z0-9-]*$`, mirroring `appkit plugin validate`) and a JS-identifier rule (`^[a-z][a-zA-Z0-9_]*$`, restricted to lowercase first letter to match the existing built-ins) on both `manifest.name` and the plugin folder name. Throws with a clear message pointing at the offending manifest and explaining how to fix it. Verified manually: - Adding a `bad-name` plugin folder with `"name": "bad-name"` makes the generator throw with the expected error message before tsdown runs. - Removing the bad plugin and re-running produces the canonical 6-stable / 0-beta barrels. - `pnpm build && pnpm -r typecheck && pnpm test && pnpm docs:build` all clean. 88 test files, 1680 tests passing. Signed-off-by: MarioCadenas <[email protected]> * chore: reconcile generated artifacts after rebase onto main Rebase onto main pulled in the new `jobs` plugin (#280) and a duplicate `export * from "./plugins/stable-exports.generated";` from the conflict resolution in `packages/appkit/src/index.ts`. Two follow-on changes: - Re-run `tools/generate-plugin-entries.ts` so `jobs` is included in `stable-exports.generated.ts`. The codegen commit on this branch predates the jobs plugin landing. - Dedupe the duplicate `export *` line in `index.ts` that crept in during the merge resolution of the codegen commit. No behavior change beyond making `jobs` reachable through `@databricks/appkit` (matching what main already does via the hand-curated barrel that this PR replaces). Signed-off-by: MarioCadenas <[email protected]> * fix(ci): build shared package before sync:template freshness check The new "Check synced template manifest is up to date" step in the lint job ran `pnpm run sync:template`, which goes through `packages/shared/bin/appkit.js` -> `packages/shared/dist/cli/index.js`. The lint job only does `pnpm install --frozen-lockfile` (no build), so dist doesn't exist and Node throws ERR_MODULE_NOT_FOUND. Initially tried bypassing the bin and running the CLI from source via `tsx packages/shared/src/cli/index.ts`, but that made knip discover the source file as a new entry point (since the workflow yaml now references it from outside the ignored `packages/shared` workspace) and flag `commander` and `dotenv/config` as unlisted root-level dependencies. Cleaner fix: add a step to build just the shared package (~1s) before the freshness check, then use the existing `pnpm run sync:template` script unchanged. Mirrors what users would do locally. Signed-off-by: MarioCadenas <[email protected]> * fix(ci): build shared dist directly via tsdown, not full build:package Previous attempt used `pnpm --filter shared build:package` to produce the dist needed by `pnpm sync:template`. That script also re-runs `tools/generate-schema-types.ts` which writes the raw (unformatted) version of `plugin-manifest.generated.ts` — and the next CI step ("Run Biome Check") then fails because the file no longer matches what biome would format it to. Invoke tsdown directly via `pnpm --filter shared exec tsdown` so we get the dist without regenerating any schema-derived sources. Reproduced locally: tsdown builds, sync:template succeeds, biome check stays clean. Signed-off-by: MarioCadenas <[email protected]> * feat(docs): auto-inject beta banner on plugin docs page from manifest Closes the docs side of the "manifest is the single source of truth" contract. Previously, flipping a plugin's `stability` to `"beta"` updated the runtime export barrel, the synced template manifest, and the API typedoc, but the plugin's hand-written docs page in `docs/docs/plugins/<name>.md` looked identical to a stable plugin's. A new `tools/generate-plugin-doc-banners.ts` reads each `packages/appkit/src/plugins/<name>/manifest.json` and, when stability is non-stable, injects a Docusaurus admonition immediately after the H1: :::warning Beta plugin This plugin is currently **beta**. APIs may change between minor releases. Import from `@databricks/appkit/beta`. See [Plugin Stability Tiers](./stability.md). ::: The block is delimited by marker comments and is fully idempotent: re-runs strip any existing banner first, then re-inject only when stability != "stable". Stable / absent stability removes the banner. Wiring: - New generator + a `generate:plugin-doc-banners` script in root `package.json`. Folded into `pnpm run generate:types` so the existing CI freshness gate covers it. - CI's "Check generated types are up to date" step now also diffs the eight built-in plugin doc pages (analytics, files, genie, jobs, lakebase, server, model-serving, vector-search). A drift between manifest stability and the docs page banner fails the PR. - `appkit plugin promote` (monorepo branch) now runs `pnpm run generate:types` instead of `generate-plugin-entries.ts` alone, so promote keeps both the runtime barrels AND the docs banner in sync after the manifest write. - Doc filename mapping handles the one mismatch (`serving` -> `model-serving.md`) via a small `DOC_FILE_OVERRIDES` table. - Stability docs (`docs/docs/plugins/stability.md`) updated to mention the new docs-side generator alongside the runtime barrels. Verified locally: - `pnpm run generate:types` against canonical state (all stable) writes the barrels and skips every doc page (no banner to add or remove). Working tree stays clean. - Temporarily marking serving as beta -> generator injects the banner into model-serving.md, with the manifest -> doc-file override picking the right file. Reverting -> banner stripped, file byte-identical to the committed version. - All quality gates pass: build, sync:template, check:fix, typecheck, 91 test files, docs:build. Signed-off-by: MarioCadenas <[email protected]> * fix(build): run plugin doc-banners generator as part of pnpm build The doc-banners generator was only wired into `pnpm run generate:types`, which means a maintainer flipping a plugin's `stability` field and running `pnpm build` (the most common build flow) got the runtime barrels and the synced template manifest updated but NOT the plugin's docs page banner. Discovered by trying it: marking the jobs plugin as beta and running `pnpm build` left `docs/docs/plugins/jobs.md` unchanged — no banner appeared until `pnpm run generate:types` was invoked manually. Append the generator to the root `pnpm build` script so the docs banner follows the manifest on the same flow that handles everything else. The generator is idempotent and fast (sub-100ms for ~7 plugins), so running it on every build has negligible cost. Verified end-to-end: - Mark jobs as beta -> `pnpm build` -> jobs.md gains the `:::warning Beta plugin` admonition immediately after the H1. - Revert manifest -> `pnpm build` -> banner stripped, jobs.md byte-identical to the committed version. Signed-off-by: MarioCadenas <[email protected]> * fix(plugin): scope promote import rewrite to the targeted plugin Address review feedback on PR #264. ## Comment: rewriteImportsInFile rewrites every beta import, not just the ## promoted one (#264 (comment)...) Pre-fix, the rewriter did: content.split("@databricks/appkit/beta").join("@databricks/appkit") which mangled multi-specifier imports — promoting only `betaA` would move `betaB` along with it onto the stable subpath, where `betaB` doesn't exist: // before import { betaA, betaB } from "@databricks/appkit/beta"; // promote betaA --to stable, pre-fix (BROKEN) import { betaA, betaB } from "@databricks/appkit"; ^^^^^ now resolves to undefined at runtime Now: take `pluginName` as an argument, match the import statement via regex, parse the specifier list, find the targeted plugin (by binding name — handles `name`, `name as alias`, `type name`, and the kebab-to-camelCase manifest convention like `vector-search` -> `vectorSearch`), and either: - Single-specifier import: rewrite the source. - Multi-specifier import: keep remaining specifiers on the original source AND emit a new import line for the promoted specifier from the new source. Imports that don't reference the targeted plugin are left untouched. Eight new unit tests cover: single-specifier rewrite, dry-run preserved, no-op when plugin not in file, multi-specifier split (the reviewer's case), preserves `import type`, matches aliased specifiers on the binding (not the alias), multi-line specifier lists, and multiple packages in the same file rewritten independently. The existing runPromote integration test was rewritten to lock in the contract that unrelated beta imports stay put. ## Comment: redundant `as "beta"` cast in sync.ts; would lie if a ## third tier is added later The cast was unnecessary — narrowing `manifest.stability !== "stable"` on top of a truthy check already produces the non-stable variant (currently `"beta"`). Removed the cast and added a comment explaining the narrowing so a future `"alpha"` tier flows through type-correctly without revisiting this code. Signed-off-by: MarioCadenas <[email protected]> * refactor(plugin): rename "stable" tier to "ga" Hard rename of the production-ready stability tier from "stable" to "ga" (general availability). Schema enum becomes ["beta", "ga"]; "stable" is no longer a valid manifest value. Why "ga": "stable" was overloaded — every git branch, plugin release, and SDK version is "stable" in some sense. "ga" matches the broader Databricks product vocabulary for the production-ready / semver-strict tier and reads unambiguously alongside "beta". Touched layers (every place "stable" had any meaning is renamed): - Schemas: `enum: ["beta", "stable"]` -> `["beta", "ga"]` in both `plugin-manifest.schema.json` and `template-plugins.schema.json`, with descriptions updated. Default value changes from "stable" to "ga" so absent stability still maps to the production-ready tier. - TypeScript types: `Stability = "beta" | "stable"` -> `"beta" | "ga"` across `manifest-types.ts`, `plugin-manifest.generated.ts`, `promote.ts`, `sync.ts`, `list.ts`, `create.ts`, and the two generators in `tools/`. - Promote command: TIER_ORDER, IMPORT_PATH_MAP, isStability, error messages, validation, --to flag help, and the post-promote generator/sync block. - Sync command: the strip-requiredByTemplate condition now compares against "ga" instead of "stable". - Create command: interactive prompt offers "GA" / "Beta" (label), with values "ga" / "beta". Existing default-omit logic preserved. - Generated barrel: renamed `stable-exports.generated.ts` -> `ga-exports.generated.ts` via `git mv`. `src/index.ts` and the `tools/generate-plugin-entries.ts` writer updated. CI freshness diff list and root `package.json` scripts updated too. - Doc banners generator: only beta plugins get a banner; the GA branch strips any existing banner. No banner is emitted for GA (matches the design — absence of beta = GA, no extra label needed). - Tests: bulk-renamed `to: "stable"` test args to `to: "ga"`, updated `isStability`/TIER_ORDER tests for the new tier name, and extended the legacy-rejection cases so manifests or --to flags carrying "stable" (now legacy), "experimental", or "preview" are all rejected up front. - Stability docs page: tier name updated everywhere; promotion path diagram now reads `beta ──→ ga`; current-plugins-by-tier section says "GA" instead of "Stable". Backwards compat: the schema rejects the legacy "stable" value, so a stale manifest must migrate to "ga" (or omit the field). Built-in plugins never wrote the field explicitly, so no first-party manifest needs editing. Third-party plugins that explicitly committed `"stability": "stable"` will fail validation until they update — this is the "hard rename" the user explicitly chose over a deprecation alias. CLI side (databricks/cli#5090) is intentionally NOT touched here. The CLI hardcodes a "stable" check in `Plugin.StabilityLabel()` to suppress the picker suffix for the default tier. After this rename, that check still suppresses "stable" (now legacy / never written by AppKit) but absent-stability plugins (the new GA default) already render with no suffix because the field is omitted from the JSON. So the CLI keeps working without changes; an explicit "ga" written into a manifest would render as "(ga)" in the picker, which is acceptable for now. A follow-up CLI PR can update the suppression check. Signed-off-by: MarioCadenas <[email protected]> * fix(plugin): propagate stability from node_modules sync; harden doc-banner paths Signed-off-by: MarioCadenas <[email protected]> * docs(plugins): document sync stability paths and doc-banner name checks Signed-off-by: MarioCadenas <[email protected]> --------- Signed-off-by: MarioCadenas <[email protected]> Co-authored-by: MarioCadenas <[email protected]>
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
`server({ autoStart: false }).then(appkit => appkit.server.extend(...).start())`
is gone — `createApp` now orchestrates server start itself, with the
post-setup hook surfaced as the `onPluginsReady` config callback.
Drop `autoStart: false`, hoist the `extend` block from the trailing
`.then` chain into `onPluginsReady`, and replace the dangling promise
with `.catch(console.error)` so unhandled rejections still surface.
Tracks #280 / #291 (autoStart removal + on-plugins-ready codemod).
Summary
onPluginsReadylifecycle hook tocreateAppthat runs after plugin setup but before the server starts, replacing theautoStart: false+.extend()+.start()ceremonycreateApp(looks up"server"plugin by name), makingautoStartand manualstart()unnecessaryautoStartfrom public API (ServerConfig,DEFAULT_CONFIG, manifest) andstart()from server exportsappkit codemod customize-callbackCLI command to auto-migrate existing appsBefore / After
Before:
After:
Migration
Auto-detects server entry files and rewrites both
.then()chain andawait+ imperative patterns. Dry-run by default (omit--writeto preview).Test plan
onPluginsReady(sync, async, without server plugin)