Skip to content

feat: port improvements from main repo#1

Open
AMar4enko wants to merge 2 commits intomainfrom
improvements
Open

feat: port improvements from main repo#1
AMar4enko wants to merge 2 commits intomainfrom
improvements

Conversation

@AMar4enko
Copy link
Copy Markdown

@AMar4enko AMar4enko commented Feb 20, 2026

Summary

field can be given drizzle column definition directly

const UUID = S.UUID.pipe(
  field(uuid())
)

const Field = UUID.pipe(
  field((col) => col.unique())
)

allows for more granular composition

NullOr now properly propagates in types and runtime. By default all columns are notNull. To make column nullable wrap schema with NullOr or OptionalFromNull.

Important - order of composition matters. Drizzle field annotations must be added on the lowest level.

// Right

const UUID = S.UUID.pipe(
  field(uuid())
)
const Ref = NullOr(UUID)

// Wrong

const UUID = NullOr(S.UUID).pipe(
  field(uuid())
)
  • rewire internals
  • make sure type definitions are sound
  • add oxlint

Context / Linked Task

  • Linear issue: ENG-123
  • Short context: why are we doing this?

Scope

  • packages/or-apps-you-touched

Task Plan

  • Step 1:
  • Step 2:
  • Step 3:

Keep this in sync with your technical plan in Notion/Linear.

Tests

  • Unit tests added/updated
  • Integration/e2e tests added/updated
  • Manual QA done (if applicable)

Test notes:

  • Command(s) run:
    • pnpm test
    • pnpm test:e2e

Screenshots (frontend-only)

Delete this section if this PR has no user-facing UI changes.

Before:

(attach image / gif)

After:

(attach image / gif)

Checklist

  • No unrelated refactors
  • No console.log or debugger left in code
  • I’ve followed our coding standards
  • I’ve updated docs (if needed)

Copilot AI review requested due to automatic review settings February 20, 2026 16:01
@github-actions github-actions bot requested a review from hiaaryan February 20, 2026 16:02
@github-actions
Copy link
Copy Markdown

Reviewer assigned: @hiaaryan

You are the primary reviewer for this PR. Please complete your review within 24 hours.
If you can't, re-assign to someone else.

@github-actions
Copy link
Copy Markdown

@AMar4enko This PR has 1 guardrail violation:

1. Tests Required

No test files modified. Please add tests or use the no-tests-needed label.


Fix the issues above and push again. Add no-guardrails label to skip all checks.

@github-actions
Copy link
Copy Markdown

Reviewer assigned: @francisco-m001

You are the primary reviewer for this PR. Please complete your review within 24 hours.
If you can't, re-assign to someone else.

Copy link
Copy Markdown

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

This PR ports upstream improvements to the Effect Schema → Drizzle integration, primarily enabling composable field(...) annotations (accepting either a Drizzle column builder or a transformation), and fixing NullOr propagation in both types and runtime compilation. It also rewires internal annotations/types and adds new tooling/config for type tests (tstyche) and linting (oxlint).

Changes:

  • Add composable Drizzle field(...) / drizzleType(...) support and rewire annotation IDs (ColumnId, TableId, etc.).
  • Fix NullOr propagation so nullability is preserved/overridden correctly through composition.
  • Add/expand test coverage (type tests + runtime snapshot tests) and introduce oxlint + tstyche config.

Reviewed changes

Copilot reviewed 23 out of 24 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tstyche.config.json Adds tstyche configuration for type-level tests.
tsdown.config.ts Removes previous tsdown build configuration.
src/types.ts Updates ColumnBuilderForField / TableForSchema typing to support new annotations and better nullability behavior.
src/model.ts Reworks schemaDrizzle internals; introduces drizzleType (aliased as field) composition logic.
src/compiler.ts Updates compiler to apply column annotations and preserve/override notNull correctly during transformations.
src/annotations.ts Replaces old annotation symbols with ColumnId/TableId/etc and new accessors.
src/pg/index.ts Adjusts pg adapter behavior to ensure default notNull semantics and correct nullable handling.
src/const.ts Updates package namespace constant for annotation symbols.
src/adapter.ts Updates schema typing imports and lint directives.
src/tests/types.tst.ts Expands type tests for field composition, NullOr typing, branded schemas, and Generated.
src/tests/annotations.tst.ts Updates type assertion to new ColumnId symbol-based annotation.
src/tests/makeBaseModel.tst.ts Updates table-name annotation usage to TableNameId.
src/tests/makeBaseModel.test.ts Same as above for runtime tests.
src/tests/support.ts Adds oxlint directive for consistent type imports.
src/tests/pg/fixtures.ts Updates fixtures to new field usage and exports additional helpers.
src/tests/pg/compiler.spec.ts Adds coverage for type-preserving NullOr with explicit column builders.
src/tests/pg/test.tst.ts Adds type test for jsonb + NullOr composition.
src/tests/pg/test.spec.ts Adds runtime tests for field composition and NullOr notNull override; adds snapshots.
src/tests/pg/full.spec.ts Enables previously skipped snapshot-style “real-world example” compilation test.
src/tests/pg/snapshots/test.spec.ts.snap Adds vitest snapshot output for runtime compilation tests.
src/tests/pg/snapshots/full.spec.ts.snap Adds snapshot output for the “full” compilation test.
package.json Switches build/test scripts and adds oxlint + updated tooling deps.
pnpm-lock.yaml Updates lockfile for new tooling dependencies.
.oxlintrc.jsonc Adds oxlint configuration and plugin setup.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

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

import { type Adapter as AdapterNs } from "./adapter";
import type { Model } from "@effect/sql";

export { Schema }
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

Schema is imported with import type, but then re-exported as a value (export { Schema }). With isolatedModules: true, this will fail to compile; re-export it as a type-only export (or change the import to a value import if you truly need a runtime export).

Suggested change
export { Schema }
export type { Schema }

Copilot uses AI. Check for mistakes.
> = S extends Annotations.AnyWithAnnotation<WithTableFactory, infer C>
? C
: Adapter.BuildTable<
> = S extends {[TableId]: infer T}
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

TableForSchema checks S extends { [TableId]: infer T }, but TableId is added as an optional property in Annotations.Table (readonly [TableId]?: ...). Because an optional property does not satisfy a required one, this conditional will never match and annotated tables will be ignored. Change the conditional to match an optional TableId property (e.g. S extends { readonly [TableId]?: infer T }).

Suggested change
> = S extends {[TableId]: infer T}
> = S extends { readonly [TableId]?: infer T }

Copilot uses AI. Check for mistakes.
Comment on lines +146 to +147

return stored;
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

There is unreachable code in the column transformation rule: after returning from the if/else that handles notNull, there is another return stored; that can never execute. This makes the control flow harder to follow; remove the dead return (and the now-unneeded blank line) to keep the rule clear.

Suggested change
return stored;

Copilot uses AI. Check for mistakes.
Comment on lines +76 to +78
export const getTableAnnotation: (
ast: SchemaAST.AST,
) => Option<DrizzleTable> = SchemaAST.getAnnotation<DrizzleTable>(TableId);
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

TableId is used to store the table factory in schemaDrizzle.table(...) (see src/model.ts), but getTableAnnotation is typed as returning a DrizzleTable. This is a type mismatch that will mislead callers and can hide runtime errors (they'll receive a function, not a table). Update getTableAnnotation (and its generic type argument) to reflect the actual annotation type stored under TableId (the table factory).

Copilot uses AI. Check for mistakes.
package.json Outdated
"prepublishOnly": "pnpm run build && pnpm run test && pnpm run test:types"
"test": "vitest run && tstyche",
"typecheck": "tsgo --noEmit",
"build": "tsc",
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

build runs tsc, but typescript is not listed in devDependencies (and tsc is not provided by @typescript/native-preview, which provides tsgo). This will make pnpm build fail in a clean install; add typescript as a devDependency or change the build script to use the installed compiler binary you intend (e.g. tsgo).

Suggested change
"build": "tsc",
"build": "tsgo",

Copilot uses AI. Check for mistakes.
.oxlintrc.jsonc Outdated
Comment on lines +198 to +212
"drizzle/enforce-delete-with-where": "error",
"react/jsx-pascal-case": "error",
},
"globals": {
"node": "readonly",
"es2021": "readonly"
},
"jsPlugins": [
"@sellhub/oxlint-plugin",
{
"name": "import-js",
"specifier": "eslint-plugin-import"
},
"eslint-plugin-drizzle"
]
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

The oxlint config enables JS plugins (@sellhub/oxlint-plugin, eslint-plugin-import, eslint-plugin-drizzle), but none of these packages are declared in this repo's dependencies/devDependencies. Unless they are provided by the workspace root, pnpm lint will fail at runtime when oxlint tries to load them; add the missing plugin packages or remove them from jsPlugins.

Suggested change
"drizzle/enforce-delete-with-where": "error",
"react/jsx-pascal-case": "error",
},
"globals": {
"node": "readonly",
"es2021": "readonly"
},
"jsPlugins": [
"@sellhub/oxlint-plugin",
{
"name": "import-js",
"specifier": "eslint-plugin-import"
},
"eslint-plugin-drizzle"
]
"react/jsx-pascal-case": "error",
},
"globals": {
"node": "readonly",
"es2021": "readonly"
}

Copilot uses AI. Check for mistakes.
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.

2 participants