Skip to content

fix: Refactor project builder lib testing and project-builder-web creation invalidation#785

Merged
kingston merged 6 commits into
mainfrom
kingston/eng-981-fix-redirect-after-creating-new-enumpluginapp
Feb 26, 2026
Merged

fix: Refactor project builder lib testing and project-builder-web creation invalidation#785
kingston merged 6 commits into
mainfrom
kingston/eng-981-fix-redirect-after-creating-new-enumpluginapp

Conversation

@kingston

@kingston kingston commented Feb 26, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Exposed testing utilities as a public API: project-builder-lib/testing
    • Automatic, conflict-free dev port assignment for new web and backend apps
  • Improvements

    • Simplified navigation flows across admin dialogs and sections
    • Cleaner default-value handling in schemas for more predictable parsing
    • Enhanced test helpers and model parsing for more robust unit tests
  • Chores

    • Added example environment file for the todo-with-auth0 backend

@changeset-bot

changeset-bot Bot commented Feb 26, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a1f9ef4

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

This PR includes changesets to release 21 packages
Name Type
@baseplate-dev/project-builder-lib Patch
@baseplate-dev/project-builder-server Patch
@baseplate-dev/project-builder-web Patch
@baseplate-dev/plugin-auth Patch
@baseplate-dev/plugin-storage Patch
@baseplate-dev/project-builder-cli Patch
@baseplate-dev/create-project Patch
@baseplate-dev/project-builder-common Patch
@baseplate-dev/project-builder-dev Patch
@baseplate-dev/project-builder-test Patch
@baseplate-dev/plugin-email Patch
@baseplate-dev/plugin-queue Patch
@baseplate-dev/plugin-rate-limit Patch
@baseplate-dev/code-morph Patch
@baseplate-dev/core-generators Patch
@baseplate-dev/fastify-generators Patch
@baseplate-dev/react-generators Patch
@baseplate-dev/sync Patch
@baseplate-dev/tools Patch
@baseplate-dev/ui-components Patch
@baseplate-dev/utils 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

@coderabbitai

coderabbitai Bot commented Feb 26, 2026

Copy link
Copy Markdown

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between 61c2230 and a1f9ef4.

⛔ Files ignored due to path filters (2)
  • tests/simple/packages/backend/.env is excluded by !tests/**
  • tests/simple/packages/backend/baseplate/generated/.env is excluded by !**/generated/**, !tests/**, !**/generated/**
📒 Files selected for processing (2)
  • examples/todo-with-auth0/apps/backend/docker/.env
  • packages/project-builder-server/src/compiler/backend/graphql.ts
 _____________________________________________________________________________________________________________________________________________________
< Progress in a fixed context is almost always a form of optimization. Creative acts generally don't stay in the context that they are in. - Alan Kay >
 -----------------------------------------------------------------------------------------------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ

✏️ Tip: You can disable in-progress messages and the fortune message in your review settings.

📝 Walkthrough

Walkthrough

Adds a public testing export for project-builder-lib, refactors withDefault to use ZodPrefault (removing .optional()), reorganizes and exposes test helpers, updates fixtures to use ProjectDefinitionInput, and removes many defensive optional chaining assumptions across server, web, and plugin code paths.

Changes

Cohort / File(s) Summary
Changesets & Package Export
\.changeset/project-builder-lib-testing-export.md, \.changeset/withdefault-removes-optional-wrapper.md, packages/project-builder-lib/package.json, packages/project-builder-lib/tsconfig.build.json
Add patch changesets, add "./testing": "./dist/testing/index.js" export, and adjust build excludes to keep test-helper files inside testing/.
Schema defaulting / withDefault
packages/project-builder-lib/src/schema/creator/extend-parser-context-with-defaults.ts, packages/project-builder-lib/src/schema/models/models.ts
Change WithDefaultResult to use ZodPrefault<T> and remove .optional() after .prefault(); apply withDefault([]) to relations/uniqueConstraints and stop optional-wrapping nested schemas.
Testing helpers & testing export
packages/project-builder-lib/src/testing/index.ts, packages/project-builder-lib/src/testing/parser-context.test-helper.ts, packages/project-builder-lib/src/testing/definition-helpers.test-helper.ts, packages/project-builder-lib/src/testing/expression-stub-parser.test-helper.ts
Introduce central src/testing barrel, add createEmptyParserContext, refactor createTestModel to parse ModelConfigInput, and update internal import aliases to #src/....
Fixtures & package CLI e2e
packages/project-builder-cli/e2e/fixtures/server-fixture.test-helper.ts
Switch fixture APIs to use ProjectDefinitionInput, add writeProjectDefinition/readProjectDefinition, adjust temp dir wiring and cleanup.
Model utils & field helpers
packages/project-builder-lib/src/definition/model/model-field-utils.ts, packages/project-builder-lib/src/definition/model/model-utils.ts, packages/project-builder-lib/src/definition/model/model-field-utils.unit.test.ts
Remove optional chaining / fallbacks for relations and uniqueConstraints, altering code to assume these arrays exist; update unit test import paths to new test helper location.
Parser refs validation
packages/project-builder-lib/src/references/extend-parser-context-with-refs.ts, packages/project-builder-lib/src/references/extract-definition-refs.unit.test.ts
Tighten withRef schema to z.string().min(1) (reject empty strings); update test import paths.
Server compiler & core modules
packages/project-builder-server/src/compiler/backend/authorizers.ts, .../graphql.ts, .../models.ts, packages/project-builder-server/src/compiler/web/**, packages/project-builder-server/src/core-modules/**
Remove optional chaining and nullish fallbacks across authorizers, GraphQL generation, model building, and admin/web compilation paths—code now assumes adminApp, graphql, relations, and uniqueConstraints are present.
Web app & routes adjustments
packages/project-builder-web/src/app/project-definition-provider/project-definition-provider.tsx, packages/project-builder-web/src/routes/**, packages/project-builder-web/src/routes/packages/-components/new-dialog.tsx, packages/project-builder-web/src/routes/data/**
Remove some router.invalidate() flows (use direct navigate), await invalidate after saves in provider, compute dev ports for new apps, and remove optional chaining around adminApp/sections/actions in multiple route components.
Plugins: auth & storage
plugins/plugin-auth/src/local-auth/core/node.ts, plugins/plugin-storage/src/storage/**
Remove defensive defaults and optional chaining for role arrays and model relations; code now assumes those arrays exist and will error if missing.
Misc tests & imports
packages/project-builder-lib/src/tools/model-merger/model-merger.unit.test.ts, other unit tests
Update many test imports to consume helpers from #src/testing/... and adapt expectations to non-optional graphql fields where applicable.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: refactoring the testing infrastructure in project-builder-lib and fixing navigation/invalidation issues in project-builder-web.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kingston/eng-981-fix-redirect-after-creating-new-enumpluginapp

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (9)
plugins/plugin-storage/src/storage/transformers/node.ts (1)

18-20: Guard relations explicitly before .find for clearer compiler errors.

On Line 18, model.model.relations being undefined now throws a generic runtime error before the contextual error at Line 22 can run. Add an explicit invariant check so failures stay actionable.

Proposed adjustment
-      const foreignRelation = model.model.relations.find(
+      const relations = model.model.relations;
+      if (!relations) {
+        throw new Error(`Model ${model.name} is missing relations`);
+      }
+
+      const foreignRelation = relations.find(
         (relation) => relation.id === fileRelationRef,
       );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/plugin-storage/src/storage/transformers/node.ts` around lines 18 -
20, Guard access to model.model.relations before calling .find so undefined
relations produce an explicit invariant error instead of a generic runtime
error; add an explicit check for model.model.relations (e.g., if
(!model.model.relations) throw new Error(...) or use an invariant helper)
immediately before the line that computes foreignRelation which uses
fileRelationRef and ensure the error message names the model and fileRelationRef
for context so downstream code that expects foreignRelation still runs with a
clear, actionable error.
plugins/plugin-storage/src/storage/core/node.ts (2)

52-57: Preserve transformer-specific error context when relations are absent.

Line 52 can fail with a generic property-access error if m.model.relations is missing, bypassing the clearer message at Line 56. Add an explicit guard before .find.

Proposed adjustment
-                const relation = m.model.relations.find(
+                const relations = m.model.relations;
+                if (!relations) {
+                  throw new Error(`Model ${m.id} is missing relations`);
+                }
+
+                const relation = relations.find(
                   (r) => r.id === t.fileRelationRef,
                 );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/plugin-storage/src/storage/core/node.ts` around lines 52 - 57, The
code currently calls m.model.relations.find(...) which will throw a generic
property-access error if m.model.relations is missing; add an explicit guard
before the .find (e.g. check Array.isArray(m.model.relations) or
m.model.relations != null) and if the relations collection is absent throw a
clear Error that includes the transformer id (t.id) and any model identifier
(m.model.id or similar), otherwise proceed to call .find for t.fileRelationRef
and preserve the existing error when the relation itself is not found.

101-104: Use an explicit invariant check for adminApp before reading .enabled.

Line 103 currently relies on property access that can throw an opaque runtime error if adminApp is ever missing. An explicit check keeps the fail-fast intent but improves debuggability.

Proposed adjustment
+          if (!appDefinition.adminApp) {
+            throw new Error('appDefinition.adminApp is required');
+          }
+
           if (
             !appDefinition.includeUploadComponents &&
             !appDefinition.adminApp.enabled
           ) {
             return;
           }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/plugin-storage/src/storage/core/node.ts` around lines 101 - 104, The
condition reads appDefinition.adminApp.enabled directly which can throw if
adminApp is undefined; change the check to first assert or test that
appDefinition.adminApp exists before accessing .enabled (e.g., replace the
current combined condition in the if around
appDefinition.includeUploadComponents with an explicit presence check for
appDefinition.adminApp, then check adminApp.enabled), referencing the existing
appDefinition.includeUploadComponents and appDefinition.adminApp.enabled
expressions so the guard is applied where that condition is evaluated.
packages/project-builder-cli/e2e/fixtures/server-fixture.test-helper.ts (1)

174-178: Use a unique service ID per addProject() call.

tempDir is unique, but id is constant (Line 237). Repeated calls in one test can collide or overwrite depending on manager behavior.

♻️ Suggested change
-          const tempDir = path.join(
+          const nextProjectIdx = projectIdx++;
+          const tempDir = path.join(
             temporaryDirectory,
-            `project-${projectIdx}`,
+            `project-${nextProjectIdx}`,
           );
-          projectIdx++;
+          const projectId = `test-project-${parallelIndex}-${nextProjectIdx}`;
...
           const service = server.builderServiceManager.addService({
-            id: 'test-project',
+            id: projectId,
             directory: tempDir,
-            name: 'test-project',
+            name: projectId,
             isInternalExample: false,
           });

Also applies to: 236-247

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

In `@packages/project-builder-cli/e2e/fixtures/server-fixture.test-helper.ts`
around lines 174 - 178, In addProject(), the tempDir uses projectIdx but the
returned project id is a constant and can collide across calls; change the id
generation in the addProject() return so it is unique per call (for example
incorporate projectIdx, tempDir, a timestamp or a UUID) and ensure projectIdx is
incremented before creating the id so each call to addProject() produces a
distinct id; update references to the id variable accordingly (symbols:
addProject, tempDir, projectIdx, id).
packages/project-builder-web/src/routes/admin-sections.$appKey/-components/columns/foreign-column-form.tsx (1)

59-63: Minor inconsistency in optional chaining style.

Line 62 uses model?.model.relations.length — optional chaining stops at model?.model, but then accesses .relations.length directly. If model.model exists but relations were undefined, this would throw.

Given ZodPrefault guarantees relations is always defined, this is safe, but the mixed style could be confusing. Consider either:

  • model?.model.relations.length ?? 0 (current, relies on schema guarantee)
  • model?.model?.relations?.length ?? 0 (fully defensive, but redundant now)

No action required if the schema guarantees are solid.

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

In
`@packages/project-builder-web/src/routes/admin-sections`.$appKey/-components/columns/foreign-column-form.tsx
around lines 59 - 63, The isAvailableForModel arrow property currently uses
mixed optional chaining (model?.model.relations.length ?? 0) which can be
confusing and brittle; update the expression in isAvailableForModel so it
consistently uses defensive optional chaining (e.g.,
model?.model?.relations?.length ?? 0) to avoid any potential undefined access
and make intent clear when locating the check in the isAvailableForModel
implementation.
packages/project-builder-web/src/routes/packages/-components/new-dialog.tsx (1)

52-56: Narrow appType to explicit values and make branching exhaustive.

Using string here allows accidental unsupported values to fall into the web-port path silently. Tightening the type keeps this helper safer for future call sites.

🔒 Suggested typing hardening
+type NewAppType = 'backend' | 'web';
+
 function getNextDevPort(
   config: ProjectDefinition,
-  appType: string,
+  appType: NewAppType,
   appName: string,
 ): number {
   const { portOffset } = config.settings.general;
   const existingPorts = new Set(config.apps.map((app) => app.devPort));

   let port: number;
   if (appType === 'backend') {
     port = portOffset + 1;
-  } else {
+  } else if (appType === 'web') {
     // For web apps, compute alphabetical index including the new app
     const webAppNames = config.apps
       .filter((app) => app.type === 'web')
       .map((app) => app.name);
     webAppNames.push(appName);
     webAppNames.sort((a, b) => compareStrings(a, b));
     const webAppIndex = webAppNames.indexOf(appName);
     port = portOffset + 30 + webAppIndex;
+  } else {
+    throw new Error(`Unsupported app type: ${appType}`);
   }

Also applies to: 61-64

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

In `@packages/project-builder-web/src/routes/packages/-components/new-dialog.tsx`
around lines 52 - 56, Change getNextDevPort to accept a narrowed appType union
(e.g., "web" | "api" | "worker" or whatever concrete app kinds your app
supports) instead of string and update its internal branching to handle each
union member explicitly and exhaustively (use a final default that throws for
unknown types or a TypeScript never check) so unsupported values no longer fall
through to the web-port path; update any call sites that pass other strings to
use the new union values.
packages/project-builder-web/src/routes/admin-sections.$appKey/edit.$sectionKey.tsx (1)

48-50: Compute sectionId once instead of per-item callback calls.

adminSectionEntityType.idFromKey(sectionKey) is recalculated for each element in both searches. Precompute it once to reduce repetition and improve clarity.

♻️ Suggested refactor
   beforeLoad: ({ params: { sectionKey }, context: { adminApp } }) => {
     if (!adminApp) return {};
+    const sectionId = adminSectionEntityType.idFromKey(sectionKey);

     const section = adminApp.sections.find(
-      (s) => adminSectionEntityType.idFromKey(sectionKey) === s.id,
+      (s) => sectionId === s.id,
     );
   const onSubmit = handleSubmit((data) => {
     const { id: _, ...sectionData } = data;
+    const sectionId = adminSectionEntityType.idFromKey(sectionKey);
     return saveDefinitionWithFeedback((draftConfig) => {
       const webApp = draftConfig.apps.find((a) => a.id === app.id);
       if (webApp?.type !== 'web') return;

       const existingIndex = webApp.adminApp.sections.findIndex(
-        (s) => s.id === adminSectionEntityType.idFromKey(sectionKey),
+        (s) => s.id === sectionId,
       );

Also applies to: 92-94

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

In
`@packages/project-builder-web/src/routes/admin-sections`.$appKey/edit.$sectionKey.tsx
around lines 48 - 50, Precompute the result of
adminSectionEntityType.idFromKey(sectionKey) into a local constant (e.g.,
sectionId) and use that constant inside the array callbacks instead of calling
adminSectionEntityType.idFromKey(sectionKey) repeatedly; update the usages in
the adminApp.sections.find call that assigns section and the other occurrence
around lines 92-94 so both use sectionId for clarity and efficiency.
plugins/plugin-storage/src/storage/admin-crud/node.ts (1)

22-24: Fail fast on missing transformer before relation lookup.

This avoids a misleading “relation not found” error when the real issue is an absent file transformer.

Proposed refactor
       const transformer = model.service.transformers.find(
         (t): t is FileTransformerDefinition =>
           t.id === definition.modelRelationRef && t.type === 'file',
       );
+      if (!transformer) {
+        throw new Error(
+          `Could not find file transformer ${definition.modelRelationRef} in model ${model.name}`,
+        );
+      }
       const relation = model.model.relations.find(
-        (r) => r.id === transformer?.fileRelationRef,
+        (r) => r.id === transformer.fileRelationRef,
       );
@@
-      const category = transformer?.category;
+      const category = transformer.category;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/plugin-storage/src/storage/admin-crud/node.ts` around lines 22 - 24,
Add an explicit guard that throws/returns an immediate error when transformer is
missing before attempting relation lookup: check the transformer variable
(referenced as transformer) at the start of the block containing the relation
lookup (the code using model.model.relations.find and
transformer?.fileRelationRef) and fail fast with a clear error message
indicating the missing file transformer so the subsequent search for relation
(variable relation) does not produce a misleading “relation not found” error.
packages/project-builder-lib/src/testing/definition-helpers.test-helper.ts (1)

26-26: Move schema creation inside createTestModel to ensure test isolation.

The module-level modelSchema constant reuses a single parser context across all createTestModel() calls. While no test failures are currently visible, the codebase explicitly documents this pattern as a concern (see createPluginWithConfigSchema comment about avoiding metadata accumulation on shared schemas via the global ref registry). Creating a fresh context per call ensures clean test isolation.

💡 Suggested change
-const modelSchema = createModelSchema(createEmptyParserContext());
-
 export function createTestModel(
   model: Partial<ModelConfigInput> = {},
 ): ModelConfig {
@@
-  return modelSchema.parse(input);
+  return createModelSchema(createEmptyParserContext()).parse(input);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/project-builder-lib/src/testing/definition-helpers.test-helper.ts`
at line 26, The module-level modelSchema created via
createModelSchema(createEmptyParserContext()) should be moved into
createTestModel so each call gets a fresh parser context; remove the top-level
const modelSchema and instead call createModelSchema(createEmptyParserContext())
inside createTestModel (the function that builds test models) to avoid
shared/global metadata accumulation from the parser's ref registry and ensure
test isolation.
🤖 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/project-builder-cli/e2e/fixtures/server-fixture.test-helper.ts`:
- Around line 223-233: readProjectDefinition() assumes
baseplate/project-definition.json always exists and throws ENOENT when
addProject() runs with no input (no file written). Fix by guarding the read:
before calling fs.readFile in readProjectDefinition(), check for file existence
(e.g., fs.pathExists or fs.stat) and either return a sensible default/undefined
ProjectDefinition or throw a clear error; update any callers that expect a
nullable value. Refer to readProjectDefinition, writeProjectDefinition, tempDir
and the path 'baseplate/project-definition.json' when making the change.

In `@packages/project-builder-lib/tsconfig.build.json`:
- Line 13: Replace the unsupported extglob pattern
"src/!(testing)/**/*.test-helper.ts" in tsconfig.build.json: remove that include
entry (it is ignored) and instead either add an explicit exclude like
"src/testing/**" to exclude the testing directory from the build or, if you
meant to include test-helper files from specific dirs, list those directories
explicitly (e.g. add explicit include/exclude patterns for the intended paths).
Ensure the exact problematic pattern "src/!(testing)/**/*.test-helper.ts" is
removed or replaced with the explicit patterns.

In
`@packages/project-builder-web/src/app/project-definition-provider/project-definition-provider.tsx`:
- Around line 172-173: The router.invalidate() call inside the .then() success
handler should be isolated so its rejection doesn't override a successful save:
after the save success path (the .then() that currently awaits
router.invalidate()), wrap the router.invalidate() call in its own try-catch
block (referencing router.invalidate()) and ensure the original success response
is returned regardless of invalidate errors; log or handle the invalidate error
inside the catch without altering the returned { success: true } from the save
flow and do not let it propagate to the existing .catch() that reports save
failure.

---

Nitpick comments:
In `@packages/project-builder-cli/e2e/fixtures/server-fixture.test-helper.ts`:
- Around line 174-178: In addProject(), the tempDir uses projectIdx but the
returned project id is a constant and can collide across calls; change the id
generation in the addProject() return so it is unique per call (for example
incorporate projectIdx, tempDir, a timestamp or a UUID) and ensure projectIdx is
incremented before creating the id so each call to addProject() produces a
distinct id; update references to the id variable accordingly (symbols:
addProject, tempDir, projectIdx, id).

In `@packages/project-builder-lib/src/testing/definition-helpers.test-helper.ts`:
- Line 26: The module-level modelSchema created via
createModelSchema(createEmptyParserContext()) should be moved into
createTestModel so each call gets a fresh parser context; remove the top-level
const modelSchema and instead call createModelSchema(createEmptyParserContext())
inside createTestModel (the function that builds test models) to avoid
shared/global metadata accumulation from the parser's ref registry and ensure
test isolation.

In
`@packages/project-builder-web/src/routes/admin-sections`.$appKey/-components/columns/foreign-column-form.tsx:
- Around line 59-63: The isAvailableForModel arrow property currently uses mixed
optional chaining (model?.model.relations.length ?? 0) which can be confusing
and brittle; update the expression in isAvailableForModel so it consistently
uses defensive optional chaining (e.g., model?.model?.relations?.length ?? 0) to
avoid any potential undefined access and make intent clear when locating the
check in the isAvailableForModel implementation.

In
`@packages/project-builder-web/src/routes/admin-sections`.$appKey/edit.$sectionKey.tsx:
- Around line 48-50: Precompute the result of
adminSectionEntityType.idFromKey(sectionKey) into a local constant (e.g.,
sectionId) and use that constant inside the array callbacks instead of calling
adminSectionEntityType.idFromKey(sectionKey) repeatedly; update the usages in
the adminApp.sections.find call that assigns section and the other occurrence
around lines 92-94 so both use sectionId for clarity and efficiency.

In `@packages/project-builder-web/src/routes/packages/-components/new-dialog.tsx`:
- Around line 52-56: Change getNextDevPort to accept a narrowed appType union
(e.g., "web" | "api" | "worker" or whatever concrete app kinds your app
supports) instead of string and update its internal branching to handle each
union member explicitly and exhaustively (use a final default that throws for
unknown types or a TypeScript never check) so unsupported values no longer fall
through to the web-port path; update any call sites that pass other strings to
use the new union values.

In `@plugins/plugin-storage/src/storage/admin-crud/node.ts`:
- Around line 22-24: Add an explicit guard that throws/returns an immediate
error when transformer is missing before attempting relation lookup: check the
transformer variable (referenced as transformer) at the start of the block
containing the relation lookup (the code using model.model.relations.find and
transformer?.fileRelationRef) and fail fast with a clear error message
indicating the missing file transformer so the subsequent search for relation
(variable relation) does not produce a misleading “relation not found” error.

In `@plugins/plugin-storage/src/storage/core/node.ts`:
- Around line 52-57: The code currently calls m.model.relations.find(...) which
will throw a generic property-access error if m.model.relations is missing; add
an explicit guard before the .find (e.g. check Array.isArray(m.model.relations)
or m.model.relations != null) and if the relations collection is absent throw a
clear Error that includes the transformer id (t.id) and any model identifier
(m.model.id or similar), otherwise proceed to call .find for t.fileRelationRef
and preserve the existing error when the relation itself is not found.
- Around line 101-104: The condition reads appDefinition.adminApp.enabled
directly which can throw if adminApp is undefined; change the check to first
assert or test that appDefinition.adminApp exists before accessing .enabled
(e.g., replace the current combined condition in the if around
appDefinition.includeUploadComponents with an explicit presence check for
appDefinition.adminApp, then check adminApp.enabled), referencing the existing
appDefinition.includeUploadComponents and appDefinition.adminApp.enabled
expressions so the guard is applied where that condition is evaluated.

In `@plugins/plugin-storage/src/storage/transformers/node.ts`:
- Around line 18-20: Guard access to model.model.relations before calling .find
so undefined relations produce an explicit invariant error instead of a generic
runtime error; add an explicit check for model.model.relations (e.g., if
(!model.model.relations) throw new Error(...) or use an invariant helper)
immediately before the line that computes foreignRelation which uses
fileRelationRef and ensure the error message names the model and fileRelationRef
for context so downstream code that expects foreignRelation still runs with a
clear, actionable error.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a136dc9 and f122d77.

📒 Files selected for processing (43)
  • .changeset/project-builder-lib-testing-export.md
  • .changeset/withdefault-removes-optional-wrapper.md
  • packages/project-builder-cli/e2e/fixtures/server-fixture.test-helper.ts
  • packages/project-builder-lib/package.json
  • packages/project-builder-lib/src/definition/model/model-field-utils.ts
  • packages/project-builder-lib/src/definition/model/model-field-utils.unit.test.ts
  • packages/project-builder-lib/src/definition/model/model-utils.ts
  • packages/project-builder-lib/src/references/extend-parser-context-with-refs.ts
  • packages/project-builder-lib/src/references/extract-definition-refs.unit.test.ts
  • packages/project-builder-lib/src/schema/creator/extend-parser-context-with-defaults.ts
  • packages/project-builder-lib/src/schema/models/models.ts
  • packages/project-builder-lib/src/testing/definition-helpers.test-helper.ts
  • packages/project-builder-lib/src/testing/expression-stub-parser.test-helper.ts
  • packages/project-builder-lib/src/testing/index.ts
  • packages/project-builder-lib/src/testing/parser-context.test-helper.ts
  • packages/project-builder-lib/src/tools/model-merger/model-merger.unit.test.ts
  • packages/project-builder-lib/tsconfig.build.json
  • packages/project-builder-server/src/compiler/backend/authorizers.ts
  • packages/project-builder-server/src/compiler/backend/graphql.ts
  • packages/project-builder-server/src/compiler/backend/models.ts
  • packages/project-builder-server/src/compiler/web/admin/crud/index.ts
  • packages/project-builder-server/src/compiler/web/admin/index.ts
  • packages/project-builder-server/src/compiler/web/admin/sections.ts
  • packages/project-builder-server/src/compiler/web/web-compiler.ts
  • packages/project-builder-server/src/core-modules/admin-crud-column-compiler.ts
  • packages/project-builder-server/src/core-modules/admin-crud-input-compiler.ts
  • packages/project-builder-web/src/app/app.tsx
  • packages/project-builder-web/src/app/project-definition-provider/project-definition-provider.tsx
  • packages/project-builder-web/src/routes/admin-sections.$appKey/-components/columns/foreign-column-form.tsx
  • packages/project-builder-web/src/routes/admin-sections.$appKey/-components/crud-table-actions-form.tsx
  • packages/project-builder-web/src/routes/admin-sections.$appKey/-components/inputs/embedded-local-input-config.tsx
  • packages/project-builder-web/src/routes/admin-sections.$appKey/-components/inputs/foreign-input-config.tsx
  • packages/project-builder-web/src/routes/admin-sections.$appKey/-components/new-admin-section-dialog.tsx
  • packages/project-builder-web/src/routes/admin-sections.$appKey/edit.$sectionKey.tsx
  • packages/project-builder-web/src/routes/admin-sections.$appKey/index.tsx
  • packages/project-builder-web/src/routes/admin-sections.$appKey/route.tsx
  • packages/project-builder-web/src/routes/data/enums/-hooks/use-enum-form.tsx
  • packages/project-builder-web/src/routes/data/models/-components/new-model-dialog.tsx
  • packages/project-builder-web/src/routes/packages/-components/new-dialog.tsx
  • plugins/plugin-auth/src/local-auth/core/node.ts
  • plugins/plugin-storage/src/storage/admin-crud/node.ts
  • plugins/plugin-storage/src/storage/core/node.ts
  • plugins/plugin-storage/src/storage/transformers/node.ts

"exclude": [
"src/**/*.test.ts",
"src/**/*.test-helper.ts",
"src/!(testing)/**/*.test-helper.ts",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

TypeScript tsconfig.json include exclude glob patterns extglob support !(pattern)

💡 Result:

TypeScript’s tsconfig.json include/exclude patterns are not full Bash-style globs and do not support extglob like !(pattern).

Supported wildcards are only: *, ?, and **/ (recursive directories) in both include and exclude. Anything like !(...), +(...), @(…), {a,b}, etc. is not part of the supported syntax. [1]

Also note: exclude only filters what include finds—a file can still be part of the program if it’s pulled in via import, /// <reference>, types, or files. [1] [2]

Workaround for “everything except X”: use a broad include, then list the unwanted paths in exclude explicitly, e.g.

{
  "include": ["src/**/*"],
  "exclude": ["src/**/__generated__/**", "src/**/*.spec.ts"]
}

Sources: TypeScript TSConfig reference (include/exclude) [1], TypeScript Wiki FAQ on exclude behavior [2].


🏁 Script executed:

#!/bin/bash
# First, examine the tsconfig.build.json to understand the context
echo "=== tsconfig.build.json context ==="
cat packages/project-builder-lib/tsconfig.build.json

echo ""
echo "=== Searching for *.test-helper.ts files ==="
fd --type f '\.test-helper\.ts$' packages/project-builder-lib/

echo ""
echo "=== Specifically checking src/testing vs other locations ==="
fd --type f '\.test-helper\.ts$' packages/project-builder-lib/ | while read file; do
  if [[ $file == *"src/testing"* ]]; then
    echo "IN testing: $file"
  else
    echo "OUTSIDE testing: $file"
  fi
done

Repository: halfdomelabs/baseplate

Length of output: 1243


Line 13 uses an unsupported extglob pattern that has no effect.

TypeScript's tsconfig.json include/exclude matchers support only *, ?, and **/ wildcards. The pattern !(testing) is not recognized and will be ignored. While test-helper files currently only exist in src/testing/, this exclude rule is non-functional and should be replaced with explicit paths or removed if it's redundant. Consider either listing specific directories to exclude or using a different strategy for separating test helpers from compiled output.

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

In `@packages/project-builder-lib/tsconfig.build.json` at line 13, Replace the
unsupported extglob pattern "src/!(testing)/**/*.test-helper.ts" in
tsconfig.build.json: remove that include entry (it is ignored) and instead
either add an explicit exclude like "src/testing/**" to exclude the testing
directory from the build or, if you meant to include test-helper files from
specific dirs, list those directories explicitly (e.g. add explicit
include/exclude patterns for the intended paths). Ensure the exact problematic
pattern "src/!(testing)/**/*.test-helper.ts" is removed or replaced with the
explicit patterns.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/project-builder-cli/e2e/fixtures/server-fixture.test-helper.ts (1)

114-116: ⚠️ Potential issue | 🟡 Minor

Type mismatch: interface declares ProjectDefinition but implementation uses ProjectDefinitionInput.

The writeProjectDefinition parameter in the interface is typed as ProjectDefinition, but the actual implementation at line 215 uses ProjectDefinitionInput. These are semantically different types (schema output vs input).

🔧 Proposed fix
   writeProjectDefinition: (
-    projectDefinition: ProjectDefinition,
+    projectDefinition: ProjectDefinitionInput,
   ) => Promise<void>;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/project-builder-cli/e2e/fixtures/server-fixture.test-helper.ts`
around lines 114 - 116, The interface declaration for writeProjectDefinition
currently expects ProjectDefinition but the implementation uses
ProjectDefinitionInput; update one side so both use the same type: either change
the interface method signature writeProjectDefinition(projectDefinition:
ProjectDefinition) => Promise<void> to accept ProjectDefinitionInput, or convert
the implementation to construct/validate a ProjectDefinition before calling
writeProjectDefinition; reference the symbols writeProjectDefinition,
ProjectDefinition, and ProjectDefinitionInput and make the typesconsistent
across the interface and its implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/project-builder-cli/e2e/fixtures/server-fixture.test-helper.ts`:
- Around line 114-116: The interface declaration for writeProjectDefinition
currently expects ProjectDefinition but the implementation uses
ProjectDefinitionInput; update one side so both use the same type: either change
the interface method signature writeProjectDefinition(projectDefinition:
ProjectDefinition) => Promise<void> to accept ProjectDefinitionInput, or convert
the implementation to construct/validate a ProjectDefinition before calling
writeProjectDefinition; reference the symbols writeProjectDefinition,
ProjectDefinition, and ProjectDefinitionInput and make the typesconsistent
across the interface and its implementation.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f122d77 and 61c2230.

📒 Files selected for processing (2)
  • packages/project-builder-cli/e2e/fixtures/server-fixture.test-helper.ts
  • packages/project-builder-web/src/app/project-definition-provider/project-definition-provider.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/project-builder-web/src/app/project-definition-provider/project-definition-provider.tsx

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