Skip to content

make config typesafe#1254

Open
mantrakp04 wants to merge 12 commits intodevfrom
codex/typed-stack-config
Open

make config typesafe#1254
mantrakp04 wants to merge 12 commits intodevfrom
codex/typed-stack-config

Conversation

@mantrakp04
Copy link
Copy Markdown
Collaborator

@mantrakp04 mantrakp04 commented Mar 14, 2026

Summary

  • add a public defineStackConfig helper and StackConfig type for nested config authoring
  • emit helper-based nested config files from the CLI and local emulator
  • update type coverage and e2e expectations for the new stack.config format

Testing

  • pnpm --filter ./packages/stack-shared typecheck
  • pnpm --filter ./packages/stack-cli typecheck
  • pnpm --filter ./apps/backend typecheck
  • pnpm --filter ./apps/e2e typecheck

Summary by CodeRabbit

  • New Features

    • Type-safe configuration API with compile-time validation
    • New config rendering utility for producing typed config files
    • Public local-emulator settings and a public helper to detect emulator mode
    • Added --overwrite flag for config pull
  • Improvements

    • Stronger validation and clearer errors for invalid or conflicting config shapes
    • Config output now includes explicit TypeScript typing
  • Tests

    • Added and strengthened tests for config authoring, rendering, CLI behavior, and emulator flows

Copilot AI review requested due to automatic review settings March 14, 2026 00:25
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 14, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stack-auth-hosted-components Ready Ready Preview, Comment Mar 23, 2026 10:55pm
stack-backend Ready Ready Preview, Comment Mar 23, 2026 10:55pm
stack-dashboard Ready Ready Preview, Comment Mar 23, 2026 10:55pm
stack-demo Ready Ready Preview, Comment Mar 23, 2026 10:55pm
stack-docs Ready Ready Preview, Comment Mar 23, 2026 10:55pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 14, 2026

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: e129559d-7dae-46b0-86d5-556e0d079441

📥 Commits

Reviewing files that changed from the base of the PR and between b25a167 and 35d685a.

📒 Files selected for processing (2)
  • apps/e2e/tests/backend/endpoints/api/v1/internal/config-local-emulator.test.ts
  • apps/e2e/tests/backend/endpoints/api/v1/internal/local-emulator-project.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/e2e/tests/backend/endpoints/api/v1/internal/config-local-emulator.test.ts

📝 Walkthrough

Walkthrough

Adds strict typed config authoring and a renderer in packages/stack-shared; switches CLI and init to use the renderer; updates backend local-emulator to use renderer and export new constants/functions; strengthens tests and CLI to expect typed StackConfig-based generated config files and adds an overwrite guard.

Changes

Cohort / File(s) Summary
Config authoring & rendering
packages/stack-shared/src/config-authoring.ts, packages/stack-shared/src/config-authoring.test.ts, packages/stack-shared/src/config-rendering.ts
Introduce StackConfig type, defineStackConfig, strict-shape utility types, and renderConfigFileContent(config) which validates, normalizes, and emits typed TS config content; add unit tests.
Package exports
packages/stack-shared/package.json
Expose new ./config entry and include src/config-authoring.ts in published files.
CLI & init integration
packages/stack-cli/src/commands/config-file.ts, packages/stack-cli/src/commands/init.ts
Replace inline JSON/TS template generation with renderConfigFileContent; add --overwrite guard to config pull; unify config import logic for push.
Backend local emulator
apps/backend/src/lib/local-emulator.ts
Refactor writeConfigToFile to use shared renderer; add exported constants (LOCAL_EMULATOR_ADMIN_EMAIL, LOCAL_EMULATOR_ADMIN_PASSWORD, LOCAL_EMULATOR_ENV_CONFIG_BLOCKED_MESSAGE, LOCAL_EMULATOR_ONLY_ENDPOINT_MESSAGE) and isLocalEmulatorEnabled().
End-to-end & CLI tests
apps/e2e/tests/general/cli.test.ts, apps/e2e/tests/backend/endpoints/api/v1/internal/local-emulator-project.test.ts, apps/e2e/tests/backend/endpoints/api/v1/internal/config-local-emulator.test.ts
Tighten assertions to require import type { StackConfig } and export const config: StackConfig = ...; add helper to extract object literal from generated .ts; require --overwrite behavior and verify written file contents.
Template exports
packages/template/src/index.ts
Re-export StackConfig (type) and defineStackConfig from @stackframe/stack-shared/config.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Stack CLI #1227: touches CLI config-file generation and aligns with replacing inline TS generation with the shared renderer.

Poem

🐰 I nudge the config, tidy and bright,
Types stand proud in gentle light,
defineStackConfig hums so wise,
Renderer scrawls the typed sunrise,
Hop — no stray keys in sight! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'make config typesafe' clearly and concisely summarizes the main objective of the PR: adding type safety to configuration.
Description check ✅ Passed The PR description follows the repository's minimal template requirements and includes essential sections: a Summary explaining the key changes and a Testing section with specific verification commands.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/typed-stack-config

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.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 14, 2026

Greptile Summary

This PR introduces a type-safe config authoring API for Stack Auth by adding defineStackConfig / StackConfig exports and a shared renderConfigFileContent utility that normalises flat dot-notation config maps into nested objects before writing TypeScript-annotated config files. The changes thread through the CLI (config pull, config push, init), the local emulator's file writer, and the e2e test suite.

Key changes:

  • packages/stack-shared/src/config-authoring.ts — new StackConfig type alias and defineStackConfig identity helper with a StrictConfigShape utility type that rejects extra keys and top-level dot-notation keys at compile time.
  • packages/stack-shared/src/config-rendering.ts — shared renderer that normalises the config via normalize() and emits an import type { StackConfig } header plus : StackConfig annotation. Always emits TypeScript syntax regardless of output extension — a regression for .js output already called out in prior review threads and not yet resolved.
  • packages/stack-cli/src/commands/config-file.ts — adds --overwrite safety guard; consolidates file loading to always use jiti.
  • apps/backend/src/lib/local-emulator.ts — delegates to renderConfigFileContent; the plain Error that can be thrown by that function is still not wrapped in StatusError, meaning conflicting dot-notation keys in a stored config would surface as HTTP 500 (prior thread, unresolved).
  • packages/template/src/index.ts — re-exports StackConfig and defineStackConfig so they are available from @stackframe/js.
  • E2e tests updated with precise inline snapshots of the new file format.

Confidence Score: 3/5

  • Core .ts config flow works, but a documented .js regression and unhandled errors in the emulator/CLI path remain open from prior review threads.
  • The new type-safe API (defineStackConfig, StackConfig, renderConfigFileContent) is well-implemented and tested. However, three issues flagged in previous review threads are still unresolved in the current code: (1) renderConfigFileContent always emits TypeScript syntax even for .js output files — the CLI description still advertises .js support, so this is a user-facing regression; (2) the plain Error thrown by renderConfigFileContent is not caught in writeConfigToFile (local-emulator), which can return HTTP 500 on edge-case configs; (3) the same plain Error in config pull surfaces as an unformatted stack trace instead of a clean CliError. Resolving at least items (1) and (2) before merge would raise this to a 4.
  • packages/stack-shared/src/config-rendering.ts (TypeScript-only output, unhandled errors) and apps/backend/src/lib/local-emulator.ts (unhandled Error from renderer).

Important Files Changed

Filename Overview
packages/stack-shared/src/config-authoring.ts New file implementing the StackConfig type alias and defineStackConfig identity helper with a StrictConfigShape utility type that rejects extra keys and dot-notation keys at compile time. Type logic is sound.
packages/stack-shared/src/config-rendering.ts New shared utility that normalises config and emits a TypeScript-flavoured config file. Always emits import type { StackConfig } and : StackConfig annotation regardless of target file extension — a regression for .js output files noted in a prior review thread (still unresolved). Throws plain Error on conflicting keys, which can surface as HTTP 500 in the local-emulator API path (also flagged previously, still unresolved).
packages/stack-cli/src/commands/config-file.ts Adds --overwrite guard for config pull and consolidates .js/.ts loading by always using jiti. The description still advertises .js support but renderConfigFileContent always emits TypeScript syntax, making the generated file invalid JavaScript (prior thread, unresolved). Plain Error from renderConfigFileContent also remains uncaught (prior thread, unresolved).
apps/backend/src/lib/local-emulator.ts Delegates file rendering to renderConfigFileContent. A plain Error thrown by that function can escape the API request handler as HTTP 500 instead of a structured 400 (prior thread, unresolved). The code is otherwise clean.
packages/stack-shared/package.json Adds a ./config sub-path export with types pointing at dist/config-authoring.d.ts and runtime conditions pointing to compiled dist/ files. This is correct. The raw src/config-authoring.ts is also included in files, which is an unusual practice flagged in a prior thread.
packages/template/src/index.ts Re-exports StackConfig type and defineStackConfig function from @stackframe/stack-shared/config so they are accessible via @stackframe/js. Clean change.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["CLI: config pull"] --> B["project.getConfigOverride('branch')"]
    B --> C["renderConfigFileContent(config)"]
    C --> D{isValidConfig?}
    D -- No --> E["throw Error (plain, not CliError)"]
    D -- Yes --> F["normalize(config)"]
    F --> G{droppedKeys?}
    G -- Yes --> H["throw Error (plain, not CliError)"]
    G -- No --> I["emit TS-flavoured file\n(always, even for .js)"]
    I --> J["fs.writeFileSync(filePath, content)"]

    K["Local Emulator API\nPATCH /branch"] --> L["writeConfigToFile(filePath, config)"]
    L --> C
    H -- uncaught --> M["HTTP 500 (should be 400)"]
    E -- uncaught in emulator path --> M

    N["defineStackConfig(config)"] --> O["Type-check:\nStrictConfigShape<T, StackConfig>"]
    O -- extra/dot keys --> P["TypeScript error (compile time)"]
    O -- valid --> Q["return config unchanged (identity)"]
Loading

Fix All in Claude Code Fix All in Cursor Fix All in Codex

Reviews (4): Last reviewed commit: "Update local emulator tests to use inlin..." | Re-trigger Greptile

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

Introduces a typed config authoring entrypoint (defineStackConfig) in @stackframe/stack-shared and updates Stack CLI + local emulator to generate config files that import and wrap the exported config in defineStackConfig(...), with e2e/tests updated accordingly.

Changes:

  • Add defineStackConfig helper + type-level strictness to reject unknown keys (including dot-notation keys) in typed config files.
  • Update CLI (init, config pull) and backend local emulator to render normalized, typed config files (and add --overwrite for config pull).
  • Update e2e tests to assert the new typed-config file format.

Reviewed changes

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

Show a summary per file
File Description
packages/stack-shared/src/config-authoring.ts Adds defineStackConfig and strict shape typing for config authoring.
packages/stack-shared/src/config-authoring.test.ts Type-level tests ensuring invalid keys/values are rejected.
packages/stack-shared/package.json Exposes new ./config entrypoint and includes authoring source in package files.
packages/stack-cli/src/lib/stack-config-file.ts Centralizes config file rendering with normalization + typed wrapper.
packages/stack-cli/src/commands/init.ts Uses renderConfigFile when generating stack.config.ts.
packages/stack-cli/src/commands/config-file.ts Adds --overwrite, uses renderConfigFile, and standardizes loading via jiti.
apps/e2e/tests/general/cli.test.ts Updates CLI tests for new generated config file structure and overwrite behavior.
apps/e2e/tests/backend/endpoints/api/v1/internal/local-emulator-project.test.ts Updates assertions for typed config output.
apps/e2e/tests/backend/endpoints/api/v1/internal/config-local-emulator.test.ts Updates test to validate typed config output after branch override updates.
apps/backend/src/lib/local-emulator.ts Writes typed config files and normalizes config before serialization.

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

You can also share your feedback on Copilot code review. Take the survey.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/backend/src/lib/local-emulator.ts (1)

73-84: Consider consolidating with CLI's renderConfigFile.

This implementation is nearly identical to packages/stack-cli/src/lib/stack-config-file.ts. Both validate, normalize with the same options, and produce the same output format. The only differences are the error types (StatusError vs CliError) and messages.

Consider extracting the core rendering logic to @stackframe/stack-shared and having both CLI and backend wrap it with their respective error handling. This would prevent the implementations from drifting apart.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/backend/src/lib/local-emulator.ts` around lines 73 - 84, Extract the
shared rendering logic into `@stackframe/stack-shared` as a single function (e.g.,
formatConfigExport or renderConfigCore) that accepts the raw config, runs
isValidConfig + normalize with the same options (onDotIntoNonObject: "ignore",
onDotIntoNull: "empty-object") and returns the export string (import + export
const config = defineStackConfig(...)); then update
apps/backend/src/lib/local-emulator.ts's renderConfigFile to call that shared
function and translate any thrown error into a StatusError with the existing
message, and similarly update packages/stack-cli/src/lib/stack-config-file.ts to
call the shared function and wrap errors in a CliError so both callers use the
identical core logic while preserving their specific error types.
packages/stack-shared/src/config-authoring.test.ts (1)

34-43: Make the enum-negative test fail for only the enum reason.

Line 38 uses @ts-expect-error, but the object is partial; that can let the test pass for a different type error. Include the same valid sibling fields and change only customerType to keep this assertion precise.

♻️ Suggested tightening
 defineStackConfig({
   payments: {
     items: {
       todos: {
+        displayName: "Todo Slots",
         // `@ts-expect-error` Invalid enum values should fail type-checking.
         customerType: "workspace",
       },
     },
   },
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stack-shared/src/config-authoring.test.ts` around lines 34 - 43, The
test using defineStackConfig currently marks customerType with //
`@ts-expect-error` but the surrounding object is partial which may allow other
type errors to satisfy the directive; update the test so the todos object
includes the same valid sibling fields (the required fields present in a normal
todos config) and change only payments.items.todos.customerType to the invalid
enum value ("workspace") so the `@ts-expect-error` fails solely for the enum
mismatch; locate the defineStackConfig call and adjust the payments.items.todos
object to be otherwise valid except for customerType.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/backend/src/lib/local-emulator.ts`:
- Around line 73-84: Extract the shared rendering logic into
`@stackframe/stack-shared` as a single function (e.g., formatConfigExport or
renderConfigCore) that accepts the raw config, runs isValidConfig + normalize
with the same options (onDotIntoNonObject: "ignore", onDotIntoNull:
"empty-object") and returns the export string (import + export const config =
defineStackConfig(...)); then update apps/backend/src/lib/local-emulator.ts's
renderConfigFile to call that shared function and translate any thrown error
into a StatusError with the existing message, and similarly update
packages/stack-cli/src/lib/stack-config-file.ts to call the shared function and
wrap errors in a CliError so both callers use the identical core logic while
preserving their specific error types.

In `@packages/stack-shared/src/config-authoring.test.ts`:
- Around line 34-43: The test using defineStackConfig currently marks
customerType with // `@ts-expect-error` but the surrounding object is partial
which may allow other type errors to satisfy the directive; update the test so
the todos object includes the same valid sibling fields (the required fields
present in a normal todos config) and change only
payments.items.todos.customerType to the invalid enum value ("workspace") so the
`@ts-expect-error` fails solely for the enum mismatch; locate the
defineStackConfig call and adjust the payments.items.todos object to be
otherwise valid except for customerType.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d773932d-7108-4560-af7b-77ecbddf271a

📥 Commits

Reviewing files that changed from the base of the PR and between 46cacd4 and 140b05e.

📒 Files selected for processing (10)
  • apps/backend/src/lib/local-emulator.ts
  • apps/e2e/tests/backend/endpoints/api/v1/internal/config-local-emulator.test.ts
  • apps/e2e/tests/backend/endpoints/api/v1/internal/local-emulator-project.test.ts
  • apps/e2e/tests/general/cli.test.ts
  • packages/stack-cli/src/commands/config-file.ts
  • packages/stack-cli/src/commands/init.ts
  • packages/stack-cli/src/lib/stack-config-file.ts
  • packages/stack-shared/package.json
  • packages/stack-shared/src/config-authoring.test.ts
  • packages/stack-shared/src/config-authoring.ts

- Introduced `renderConfigFileContent` function to handle config rendering and validation, replacing the previous `renderConfigFile` function.
- Updated `writeConfigToFile` to use the new rendering function.
- Added a test case to ensure that array config exports are rejected.
- Adjusted package.json to point to the correct type definitions for config authoring.
- Removed deprecated code related to config normalization and validation.
- Updated the `registerConfigCommand` function to explicitly define the type of `configModule` when importing the configuration file.
- Simplified the `renderConfigFile` function by removing error handling, as it now directly returns the result of `renderConfigFileContent` without additional validation.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/stack-shared/src/config-rendering.ts (1)

17-37: Consider adding a test for conflicting keys.

The tests cover normalization and invalid input rejection, but don't cover the scenario where keys conflict (e.g., {"a.b": "string", "a.b.c": "nested"}). Adding such a test would clarify the expected behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stack-shared/src/config-rendering.ts` around lines 17 - 37, Add a
unit test for renderConfigFileContent that asserts behavior when the input has
conflicting dotted keys (e.g., "a.b" vs "a.b.c"); locate renderConfigFileContent
in the test file and add a test case that either expects a thrown error
(toDocument the rejection) or expects a deterministic resolution (specify which
value wins), using expect(() =>
renderConfigFileContent(conflictingInput)).toThrow... or
expect(...).toContain... depending on the intended behavior; ensure the new test
name clearly describes "conflicting keys" so it complements the existing
normalization and invalid-input tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/stack-shared/src/config-rendering.ts`:
- Around line 10-13: The normalize call currently uses onDotIntoNonObject:
"ignore", which silently drops conflicting keys; change this to
onDotIntoNonObject: "throw" in the normalize invocation (the call that produces
normalizedConfig in config-rendering.ts) so path conflicts raise an error, or
alternatively pass a droppedKeys array into normalize and if droppedKeys is
non-empty throw or log an error that includes droppedKeys; ensure the thrown
error or logged message references the droppedKeys and occurs before returning
normalizedConfig.

---

Nitpick comments:
In `@packages/stack-shared/src/config-rendering.ts`:
- Around line 17-37: Add a unit test for renderConfigFileContent that asserts
behavior when the input has conflicting dotted keys (e.g., "a.b" vs "a.b.c");
locate renderConfigFileContent in the test file and add a test case that either
expects a thrown error (toDocument the rejection) or expects a deterministic
resolution (specify which value wins), using expect(() =>
renderConfigFileContent(conflictingInput)).toThrow... or
expect(...).toContain... depending on the intended behavior; ensure the new test
name clearly describes "conflicting keys" so it complements the existing
normalization and invalid-input tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4af8efa5-e487-4d7c-b022-a6ca80c2c9f8

📥 Commits

Reviewing files that changed from the base of the PR and between 140b05e and 1ee6cd5.

📒 Files selected for processing (6)
  • apps/backend/src/lib/local-emulator.ts
  • apps/e2e/tests/general/cli.test.ts
  • packages/stack-cli/src/lib/stack-config-file.ts
  • packages/stack-shared/package.json
  • packages/stack-shared/src/config-authoring.test.ts
  • packages/stack-shared/src/config-rendering.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/e2e/tests/general/cli.test.ts
  • packages/stack-shared/src/config-authoring.test.ts

- Introduced `createRequire` to support module aliasing in `local-emulator.ts` and `config-file.ts`, allowing for more flexible configuration imports.
- Updated the `createJiti` function to include an alias for `@stackframe/stack-shared/config`, improving module resolution.
- Added a test case in `config-authoring.test.ts` to verify that `defineStackConfig` returns its input unchanged, ensuring expected behavior in config authoring.
- Replaced `defineStackConfig` with type imports for `StackConfig` in multiple files, enhancing type safety.
- Updated the `renderConfigFile` function to `renderConfigFileContent`, streamlining the rendering process.
- Adjusted tests to reflect changes in config import statements and ensure compatibility with the new type definitions.
- Removed deprecated `stack-config-file.ts` as part of the refactor.
@mantrakp04
Copy link
Copy Markdown
Collaborator Author

@greptile-ai review and re-score

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/e2e/tests/backend/endpoints/api/v1/internal/config-local-emulator.test.ts (1)

114-117: Brittle whitespace-dependent assertion.

The string match '"teams": {\n "allowClientTeamCreation": true\n }' is sensitive to exact formatting (newlines, indentation). If renderConfigFileContent changes its output style, this test will break even if the content is semantically correct.

Consider parsing the config object from the file and asserting on the parsed structure, similar to how cli.test.ts uses extractConfigObjectString + JSON.parse.

♻️ Suggested approach
     const fileContent = await fs.readFile(filePath, "utf-8");
     expect(fileContent).toContain('import type { StackConfig } from "@stackframe/stack-shared/config";');
-    expect(fileContent).toContain('"teams": {\n    "allowClientTeamCreation": true\n  }');
+    // Extract and parse the config object for a more robust assertion
+    const configMatch = fileContent.match(/export const config:\s*StackConfig\s*=\s*(.+);\s*$/s);
+    expect(configMatch).not.toBeNull();
+    const parsedConfig = JSON.parse(configMatch![1]);
+    expect(parsedConfig.teams).toEqual({ allowClientTeamCreation: true });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/e2e/tests/backend/endpoints/api/v1/internal/config-local-emulator.test.ts`
around lines 114 - 117, The assertion is brittle because it depends on exact
whitespace in fileContent; instead extract the config object string from
fileContent (reuse the existing extractConfigObjectString helper used in
cli.test.ts), JSON.parse that string into an object, and assert on the parsed
shape (e.g., parsedConfig.teams.allowClientTeamCreation === true). Keep the
existing import line assertion but replace the raw string match with parsing via
extractConfigObjectString(fileContent) -> JSON.parse(...) and then use the
parsed object in assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@apps/e2e/tests/backend/endpoints/api/v1/internal/config-local-emulator.test.ts`:
- Around line 114-117: The assertion is brittle because it depends on exact
whitespace in fileContent; instead extract the config object string from
fileContent (reuse the existing extractConfigObjectString helper used in
cli.test.ts), JSON.parse that string into an object, and assert on the parsed
shape (e.g., parsedConfig.teams.allowClientTeamCreation === true). Keep the
existing import line assertion but replace the raw string match with parsing via
extractConfigObjectString(fileContent) -> JSON.parse(...) and then use the
parsed object in assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 50d7b0d3-cd61-4da7-9a7e-d7000f74c952

📥 Commits

Reviewing files that changed from the base of the PR and between 1ee6cd5 and 29d417d.

📒 Files selected for processing (7)
  • apps/e2e/tests/backend/endpoints/api/v1/internal/config-local-emulator.test.ts
  • apps/e2e/tests/backend/endpoints/api/v1/internal/local-emulator-project.test.ts
  • apps/e2e/tests/general/cli.test.ts
  • packages/stack-cli/src/commands/config-file.ts
  • packages/stack-cli/src/commands/init.ts
  • packages/stack-shared/src/config-authoring.test.ts
  • packages/stack-shared/src/config-rendering.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/stack-cli/src/commands/config-file.ts
  • packages/stack-shared/src/config-authoring.test.ts
  • packages/stack-shared/src/config-rendering.ts

@mantrakp04 mantrakp04 changed the title Add typed stack config authoring make config typesafe Mar 20, 2026
@mantrakp04 mantrakp04 requested review from BilalG1 and N2D4 March 21, 2026 02:24
Copy link
Copy Markdown
Contributor

@N2D4 N2D4 left a comment

Choose a reason for hiding this comment

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

left some comments!

…st files and CLI commands

- Changed import statements in various test files and the CLI command to reference the new StackConfig path.
- Updated error messages in the config-file command to reflect the new import structure.
- Adjusted the config rendering logic to align with the updated import path for StackConfig.
@mantrakp04
Copy link
Copy Markdown
Collaborator Author

@greptile-ai review

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/e2e/tests/backend/endpoints/api/v1/internal/config-local-emulator.test.ts (1)

115-117: Prefer inline snapshots over brittle multiline toContain checks.

These assertions are sensitive to whitespace/formatting. A single inline snapshot is usually clearer and more stable for generated-file output checks.

♻️ Suggested refactor
-    const fileContent = await fs.readFile(filePath, "utf-8");
-    expect(fileContent).toContain('import type { StackConfig } from "@stackframe/js";');
-    expect(fileContent).toContain('"teams": {\n    "allowClientTeamCreation": true\n  }');
+    const fileContent = await fs.readFile(filePath, "utf-8");
+    expect(fileContent).toMatchInlineSnapshot(`
+"import type { StackConfig } from \\"@stackframe/js\\";
+
+export const config: StackConfig = {
+  \\"teams\\": {
+    \\"allowClientTeamCreation\\": true
+  }
+};
+"
+`);

Based on learnings: "When writing tests, prefer .toMatchInlineSnapshot over other selectors, if possible."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/e2e/tests/backend/endpoints/api/v1/internal/config-local-emulator.test.ts`
around lines 115 - 117, Replace the brittle multiline toContain checks on the
generated file string with a single inline snapshot assertion: capture the value
returned by await fs.readFile(filePath, "utf-8") (variable fileContent) and use
expect(fileContent).toMatchInlineSnapshot(`...`) containing the exact expected
file output; update the test in config-local-emulator.test.ts to remove the two
expect(...).toContain(...) lines and instead assert a single inline snapshot for
fileContent so formatting/whitespace changes are versioned in the snapshot.
apps/e2e/tests/backend/endpoints/api/v1/internal/local-emulator-project.test.ts (1)

88-89: Good assertion target; consider snapshotting this generated snippet.

This is a solid check for typed output, but toMatchInlineSnapshot will usually be easier to maintain than multiple toContain assertions.

♻️ Suggested refactor
-    expect(fileContent).toContain('import type { StackConfig } from "@stackframe/js";');
-    expect(fileContent).toContain("export const config: StackConfig =");
+    expect(fileContent).toMatchInlineSnapshot(`
+"import type { StackConfig } from \\"@stackframe/js\\";
+
+export const config: StackConfig = {
+  ...
+};
+"
+`);

Based on learnings: "When writing tests, prefer .toMatchInlineSnapshot over other selectors, if possible."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/e2e/tests/backend/endpoints/api/v1/internal/local-emulator-project.test.ts`
around lines 88 - 89, Replace the two .toContain assertions on fileContent with
a single .toMatchInlineSnapshot assertion: locate the test that calls
expect(fileContent).toContain('import type { StackConfig } ...') and
expect(fileContent).toContain("export const config: StackConfig ="), remove
those two expects and replace with
expect(fileContent).toMatchInlineSnapshot(`...`) containing the exact generated
snippet (the import line and the export const config block) so the generated
code is snapshot-tested as a single, maintainable inline snapshot.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@apps/e2e/tests/backend/endpoints/api/v1/internal/config-local-emulator.test.ts`:
- Around line 115-117: Replace the brittle multiline toContain checks on the
generated file string with a single inline snapshot assertion: capture the value
returned by await fs.readFile(filePath, "utf-8") (variable fileContent) and use
expect(fileContent).toMatchInlineSnapshot(`...`) containing the exact expected
file output; update the test in config-local-emulator.test.ts to remove the two
expect(...).toContain(...) lines and instead assert a single inline snapshot for
fileContent so formatting/whitespace changes are versioned in the snapshot.

In
`@apps/e2e/tests/backend/endpoints/api/v1/internal/local-emulator-project.test.ts`:
- Around line 88-89: Replace the two .toContain assertions on fileContent with a
single .toMatchInlineSnapshot assertion: locate the test that calls
expect(fileContent).toContain('import type { StackConfig } ...') and
expect(fileContent).toContain("export const config: StackConfig ="), remove
those two expects and replace with
expect(fileContent).toMatchInlineSnapshot(`...`) containing the exact generated
snippet (the import line and the export const config block) so the generated
code is snapshot-tested as a single, maintainable inline snapshot.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c25255b7-119f-4525-8ac8-ba336be27d83

📥 Commits

Reviewing files that changed from the base of the PR and between 29d417d and b25a167.

📒 Files selected for processing (6)
  • apps/e2e/tests/backend/endpoints/api/v1/internal/config-local-emulator.test.ts
  • apps/e2e/tests/backend/endpoints/api/v1/internal/local-emulator-project.test.ts
  • apps/e2e/tests/general/cli.test.ts
  • packages/stack-cli/src/commands/config-file.ts
  • packages/stack-shared/src/config-rendering.ts
  • packages/template/src/index.ts
✅ Files skipped from review due to trivial changes (2)
  • packages/template/src/index.ts
  • apps/e2e/tests/general/cli.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/stack-cli/src/commands/config-file.ts
  • packages/stack-shared/src/config-rendering.ts

…alidation

- Replaced direct string containment checks with inline snapshots for file content validation in `config-local-emulator.test.ts` and `local-emulator-project.test.ts`.
- Ensured that the tests accurately reflect the expected structure of the StackConfig imports and configurations.
@mantrakp04
Copy link
Copy Markdown
Collaborator Author

@greptile-ai review

@mantrakp04 mantrakp04 requested a review from N2D4 March 24, 2026 02:41
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.

3 participants