Skip to content

release: v1.2 Connector SDK & Plugin Seam → dev - #1142

Merged
alfredo1996 merged 9 commits into
devfrom
release/1.2
Jul 1, 2026
Merged

release: v1.2 Connector SDK & Plugin Seam → dev#1142
alfredo1996 merged 9 commits into
devfrom
release/1.2

Conversation

@alfredo1996

Copy link
Copy Markdown
Owner

Consolidates the v1.2 Connector SDK & Plugin Seam epic (#1093) from release/1.2 into dev.

dev has not advanced since the branch point, so this is a conflict-free merge.

Included (10 PRs)

Epic (#1116#1122):

Follow-ups:

Net effect

The connector layer is now a versioned SDK + runtime plugin registry: external connectors are first-class through the form, schema, query-language, validation, execution, and storage layers, with a shared query-safety conformance harness. Every constituent PR landed 100% green (CI + SonarCloud + CodeRabbit).

Note: DB migrations were squashed 12→1 (pre-launch, no users) — connection.type is now text, no connection_type enum.

🤖 Generated with Claude Code

alfredo1996 and others added 9 commits June 30, 2026 10:25
…ach-in (#1123)

- Delete unused generalized/errors.ts (ConnectionError/QueryError/SchemaError/
  QueryTimeoutError) + its test + index re-export; the live error system is
  ConnectorError, which nothing replaced these with.
- Fix extractNodeAndRelPropertiesFromRecords to read the public neo4j-driver
  Record API (keys/get) instead of the private _fields array (breaks silently
  on driver upgrades). New neo4j/__tests__/utils.test.ts covers node/rel/path.
- Remove empty BaseAdvancedOptions interface + export.

checkConnection is already unified (#900) and ConnectionTypes is load-bearing
via query-executor + audit (moved to #1121), so neither is touched here.

Closes #1116
Closes #996

Co-authored-by: alfredorubin96 <alfredo.rubin@neotechnology.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(connection): extract @neoboard/connector-sdk and dogfood it

New publishable (0.1.0, unpublished) workspace package @neoboard/connector-sdk
holding the stable connector contract, extracted out of @neoboard/connection:
plugin interface + registry factory, ConnectionModule/AuthenticationModule base
classes, ConnectorError + detection, core config/result types, NeodashRecord(+
parser), query-safety helpers (collectUpToLimit), schema data types, and the
connector-type constants.

- connection/ now depends on the SDK and re-exports it, so the @neoboard/connection
  public API (incl. the /connector-types subpath, via a thin shim) is unchanged.
- Neo4j + PostgreSQL connectors refactored onto the SDK — no parallel type copy,
  no behavior change.
- Build order: SDK builds before connection (its dist provides the types);
  generate-connector-imports codegen now emits the SDK import path.
- Tests: 6 contract tests split by ownership — 3 pure-SDK tests moved into the SDK
  package (Docker-free jest); the 3 that exercise concrete modules/registry stay in
  connection. jest maps the SDK to source; globalSetup imports SDK source directly.

Schema *types* only — the SchemaManager interface is deferred to #1119.
ConnectionTypes enum moved as-is (collapse deferred to #1121).

Part of #1093. Refs #1117

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: update lockfile for @neoboard/connector-sdk workspace

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ci(connector-sdk): build SDK before connection in CI + Docker (#1117)

The connector-sdk workspace extraction added @neoboard/connector-sdk as a
dependency connection resolves via its built dist, but only the root build
script was updated to compile it first. The CI typecheck / unit-tests / e2e
jobs and the Dockerfile hand-roll `npm -w connection run build` without a
preceding SDK build, so connection's tsc fails with TS2307 (cannot find
@neoboard/connector-sdk) on a clean checkout.

- ci.yml: build connector-sdk before connection in all three jobs.
- Dockerfile: COPY connector-sdk/package.json (npm ci needs the workspace
  manifest) and build the SDK before connection.
- eslint.config.js: ignore connector-sdk at the root like its sibling
  connection (both have their own setups); restores the pre-extraction
  state where this code was not root-linted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(connector-sdk): add README + LICENSE for publish hygiene (#1117)

package.json `files` referenced README.md and LICENSE that didn't exist —
`npm publish` would have shipped the package without them. Add a concise
README (contract overview + quick start) and copy the repo's ELv2 LICENSE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: alfredorubin96 <alfredo.rubin@neotechnology.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
… (#1132)

* test(component): failing spec for DynamicConnectionFields (#1118 WIP, TDD red)

Parking #1118 at a clean red checkpoint to pivot to the design-system audit (Epic A).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(connector): dynamic connection form from plugin.formFields (#1118)

Render the connection form from a connector's formFields instead of
hardcoded per-connector input arrays.

- component: new DynamicConnectionFields — controlled renderer for
  text/number/password/select/boolean fields, required markers,
  descriptions, per-field errors. Default `conn-` id prefix preserves
  existing E2E selectors. (turns the parked TDD-red spec green)
- connection: built-in field defs single-sourced in client-safe
  form-fields.ts (no driver imports) and exposed via the new
  @neoboard/connection/form-fields subpath; neo4j/pg plugins reference
  them.
- app: connections create dialog credential block is now generated from
  plugin.formFields via connectionFieldsFor().

Verified: component 11/11, connection unit 23/23, app tsc + lint clean,
connections E2E 15/15.

Closes #1118

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: alfredorubin96 <alfredo.rubin@neotechnology.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
) (#1136)

* feat(connection): schema-manager registry keyed by connector type (#1119)

Schema introspection dispatched on a hardcoded 'neo4j' | 'postgresql'
branch in app/schema-prefetch — a registry-supplied connector couldn't
provide its own schema manager.

- sdk: host the SchemaManager contract in @neoboard/connector-sdk and add
  an optional createSchemaManager() factory to ConnectorPlugin (mirrors
  createModule). Optional → connectors without introspection resolve to
  undefined.
- connection: built-in Neo4j + Postgres plugins declare createSchemaManager();
  schema/schema-manager re-exports the SDK contract (stable import path); new
  getSchemaManager(type) registry helper.
- app: schema-prefetch resolves via getSchemaManager(type) — the hardcoded
  if/else is gone; an unknown/introspection-less type yields null (schema is
  a non-critical cache).

Registry-refactor only; #569's introspection counts + visualization shape
are deferred (stays open).

Closes #1119

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(connector): cover fetchConnectionSchema registry dispatch (#1119)

SonarCloud new-code coverage gate failed (54.5% < 80%): the rewritten
fetchConnectionSchema branches were E2E-only. Route getSchemaManager
through connection-adapter (the app's mockable connection seam) and add
unit tests for both branches (manager resolved → fetch; none → null) plus
prefetchSchema's fire-and-forget + error-swallow paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(connector-sdk): open DatabaseSchema.type for registry plugins (#1119)

Addresses CodeRabbit (Major): now that getSchemaManager resolves arbitrary
connector types, a registry-supplied plugin's createSchemaManager() could
not describe its own type — DatabaseSchema.type was pinned to
'neo4j' | 'postgresql'. Widen to an open string so external connectors
express their type through the SchemaManager contract without editing core
SDK types. Built-ins still use "neo4j" / "postgresql".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: alfredorubin96 <alfredo.rubin@neotechnology.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…age (#1120) (#1137)

* feat(component): route query-editor language from plugin.queryLanguage (#1120)

The editor picked its CodeMirror language from the connector *type* and fell
back to SQL for anything unregistered — a registry-supplied connector got the
wrong language (or misleading SQL highlighting).

- component: resolveLanguageExt falls back to plain text (no extensions, no
  highlighting) for unregistered languages instead of SQL.
- connection: new client-safe query-languages.ts (CONNECTOR_QUERY_LANGUAGES,
  no driver imports) → @neoboard/connection/query-languages subpath; built-in
  plugins source their queryLanguage from it.
- app: widget-editor-modal derives editorLanguage from CONNECTOR_QUERY_LANGUAGES
  (the connector's queryLanguage) instead of its type; unknown/no connector →
  plain text. This also fixes the latent `=== "sql"` placeholder check that was
  always false when given the connector type.

Deferred to #1121: the separate hardcoded CONNECTOR_LANGUAGES display map.

Verified: resolver 16/16, connection 23/23, app tsc + lint clean,
code-completion E2E 6/6.

Closes #1120

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(connector): cover editor-language routing via extracted helper (#1120)

SonarCloud new-code coverage gate failed (58.3% < 80%): the editorLanguage
derivation lived inline in the untested widget-editor-modal. Extract it to a
pure editorLanguageForConnector() helper and unit-test all branches
(neo4j→cypher, postgresql→sql, unknown/none→plain text); the modal now calls
the helper.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: alfredorubin96 <alfredo.rubin@neotechnology.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…tor/storage (#1121) (#1138)

* feat(app): de-hardcode connector type unions through validation/executor/storage (#1121)

Several paths assumed only the built-in 'neo4j' | 'postgresql' union, so a
registry-supplied connector type couldn't flow through the app.

- validation: schemas.ts accepts any registry-registered type via a runtime
  refine (isRegisteredConnectorType, routed through connection-adapter for
  test mockability) instead of z.enum(CONNECTOR_TYPES).
- execution: query-executor DbType widened to string; toConnectionTypeEnum
  maps unknown types to a new ConnectionTypes.UNKNOWN sentinel rather than
  mislabeling them PostgreSQL. pipeline-types.connectionType + prefetchSchema
  widened to string.
- storage: connection.type column pgEnum -> text; the accepted set is
  enforced at the API layer, not the DB.

Migrations squashed 12 -> 1 (pre-launch, no users): the single initial now
creates connection.type as text from the start, so there's no connection_type
enum at all.

CLI built-in list left as-is on purpose: a registry/external connector already
lists via neoboard-connectors.json, and coupling the *published* @neoboard/cli
to the *unpublished* @neoboard/connector-sdk would break `npm i -g`. Adding a
built-in is itself a core change, so the "no core change per registry type"
acceptance still holds.

Verified: app tsc + lint clean, connections E2E 15/15 (squashed migration
applies + registry validation + text-column store).

Closes #1121

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(connector): stub registry validation in connection route tests (#1121)

Registry-driven connector-type validation made schemas.ts transitively import
the driver-heavy connection registry via connection-adapter. Route tests that
validate `type` (create / test-inline / list-databases-inline) don't mock that
seam, so `getConnector` was undefined under Vitest (production/E2E load it
fine). Stub isRegisteredConnectorType in those three tests (true for built-ins,
false otherwise — preserves the mysql→400 cases), and add a dedicated
registered-types unit test for coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: alfredorubin96 <alfredo.rubin@neotechnology.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…1139)

A reusable, framework-agnostic harness shipped from @neoboard/connector-sdk
that any connector runs to prove it honors Query Safety. buildConformanceCases
returns {name, run}[] whose run() throws on violation, so connectors wire the
cases into their own test runner without the SDK depending on one.

Checks:
- read-only: a write query is rejected under READ access mode
- MAX_ROWS+1: results capped at rowLimit, truncation flagged (COMPLETE_TRUNCATED)
- timeout: a slow query times out / fails within the configured timeout

The contract has no generic cancel API, so cancellation-cleanup ("no leaked
cursor after a timed-out query") is exposed as an optional connector-supplied
assertNoLeak() hook rather than a generic check (the built-ins' cursor cleanup
is already guarded by the #978 pg-cursor timeout tests).

Wires both built-ins (Neo4j + PostgreSQL) via Testcontainers integration tests.

Closes #1122

Co-authored-by: alfredorubin96 <alfredo.rubin@neotechnology.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…GUAGES (#1140)

The hardcoded CONNECTOR_LANGUAGES map ({neo4j:"Cypher", postgresql:"SQL"})
duplicated plugin.queryLanguage — already single-sourced client-safe as
CONNECTOR_QUERY_LANGUAGES (#1120). Its only two consumers (widget-library +
template-browser CodePreview) used it purely as a language key (CodePreview
lowercases it), so:

- both now read CONNECTOR_QUERY_LANGUAGES; unknown connector type → "" (plain
  text) instead of defaulting to Cypher
- CONNECTOR_LANGUAGES removed from the SDK definition and the whole re-export
  chain (connector-sdk → connection → app)

Deferred follow-up from the v1.2 connector epic (#1093). Functionally
identical for built-ins.

Co-authored-by: alfredorubin96 <alfredo.rubin@neotechnology.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…rtNoLeak hook (#1141)

Ponytail trim of the v1.2 connector epic before consolidating release/1.2 → dev:

- registered-types: remove registeredConnectorTypes() — zero callers (only
  isRegisteredConnectorType is used, by schemas.ts). Drop its getAllConnectors
  plumbing from connection-adapter + the test block.
- conformance harness: remove the assertNoLeak hook — no connector wired it, so
  it was an unused extension point. The suite covers the 3 generically-verifiable
  safety checks; cancellation-cleanup for the built-ins is guarded by the #978
  pg-cursor timeout tests.

Verified: sdk + connection build, app tsc + lint clean.

Co-authored-by: alfredorubin96 <alfredo.rubin@neotechnology.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@alfredo1996, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 47 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 78148a2c-ce83-453b-a136-a7913aae3906

📥 Commits

Reviewing files that changed from the base of the PR and between 66428e1 and 3f124a2.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (133)
  • .github/workflows/ci.yml
  • Dockerfile
  • app/drizzle/migrations/0000_cooing_greymalkin.sql
  • app/drizzle/migrations/0001_rapid_iron_monger.sql
  • app/drizzle/migrations/0002_redundant_night_nurse.sql
  • app/drizzle/migrations/0003_loving_centennial.sql
  • app/drizzle/migrations/0004_furry_scourge.sql
  • app/drizzle/migrations/0005_perfect_paibok.sql
  • app/drizzle/migrations/0006_busy_champions.sql
  • app/drizzle/migrations/0007_free_loners.sql
  • app/drizzle/migrations/0008_lumpy_jubilee.sql
  • app/drizzle/migrations/0009_dazzling_stranger.sql
  • app/drizzle/migrations/0010_tiny_amphibian.sql
  • app/drizzle/migrations/meta/0000_snapshot.json
  • app/drizzle/migrations/meta/0001_snapshot.json
  • app/drizzle/migrations/meta/0002_snapshot.json
  • app/drizzle/migrations/meta/0003_snapshot.json
  • app/drizzle/migrations/meta/0004_snapshot.json
  • app/drizzle/migrations/meta/0005_snapshot.json
  • app/drizzle/migrations/meta/0006_snapshot.json
  • app/drizzle/migrations/meta/0007_snapshot.json
  • app/drizzle/migrations/meta/0008_snapshot.json
  • app/drizzle/migrations/meta/0009_snapshot.json
  • app/drizzle/migrations/meta/0010_snapshot.json
  • app/drizzle/migrations/meta/_journal.json
  • app/next.config.ts
  • app/src/app/(dashboard)/connections/page.tsx
  • app/src/app/(dashboard)/widget-library/page.tsx
  • app/src/app/api/connections/__tests__/route.test.ts
  • app/src/app/api/connections/list-databases-inline/__tests__/route.test.ts
  • app/src/app/api/connections/test-inline/__tests__/route.test.ts
  • app/src/components/widget-editor-modal.tsx
  • app/src/components/widget-editor/__tests__/template-browser.test.tsx
  • app/src/components/widget-editor/template-browser.tsx
  • app/src/lib/__tests__/connector/editor-language.test.ts
  • app/src/lib/__tests__/connector/registered-types.test.ts
  • app/src/lib/__tests__/connector/schema-prefetch.test.ts
  • app/src/lib/__tests__/query/query-executor-core.test.ts
  • app/src/lib/__tests__/shared/schemas.test.ts
  • app/src/lib/connector/connection-adapter.ts
  • app/src/lib/connector/connection-form-fields.ts
  • app/src/lib/connector/connector-types.ts
  • app/src/lib/connector/editor-language.ts
  • app/src/lib/connector/registered-types.ts
  • app/src/lib/connector/schema-prefetch.ts
  • app/src/lib/db/schema.ts
  • app/src/lib/query/pipeline-types.ts
  • app/src/lib/query/query-executor.ts
  • app/src/lib/shared/schemas.ts
  • component/src/components/composed/__tests__/dynamic-connection-fields.test.tsx
  • component/src/components/composed/dynamic-connection-fields.tsx
  • component/src/components/composed/index.ts
  • component/src/lib/__tests__/language-resolvers.test.ts
  • component/src/lib/language-resolvers.ts
  • connection/__tests__/adapters/factory.ts
  • connection/__tests__/advanced-connection-options.test.ts
  • connection/__tests__/authentication/authentication.ts
  • connection/__tests__/conformance/neo4j-conformance.test.ts
  • connection/__tests__/conformance/pg-conformance.test.ts
  • connection/__tests__/connection/connection-resilience.ts
  • connection/__tests__/connection/list-databases.ts
  • connection/__tests__/connection/query-basic.ts
  • connection/__tests__/connection/query-status.ts
  • connection/__tests__/connection/query-write.ts
  • connection/__tests__/connector-registry.test.ts
  • connection/__tests__/get-schema-manager.test.ts
  • connection/__tests__/neo4j/callback/setField-query.ts
  • connection/__tests__/neo4j/callback/setSchema-query.ts
  • connection/__tests__/neo4j/neo4j-rollback.ts
  • connection/__tests__/neo4j/parser/parser-config.ts
  • connection/__tests__/neo4j/parser/parser-record-objects.ts
  • connection/__tests__/neo4j/parser/parser-record-primitive.ts
  • connection/__tests__/neo4j/parser/parser-record-temporal.ts
  • connection/__tests__/neodash_record/neodash-record.ts
  • connection/__tests__/postgresql/postgres-authentication.ts
  • connection/__tests__/postgresql/postgres-check-connection.ts
  • connection/__tests__/postgresql/postgres-param-ordering.ts
  • connection/__tests__/postgresql/postgres-parser.ts
  • connection/__tests__/postgresql/postgres-query.ts
  • connection/__tests__/schema/neo4j-schema.test.ts
  • connection/__tests__/schema/pg-schema.test.ts
  • connection/__tests__/uri-validation.test.ts
  • connection/__tests__/utils/setup.ts
  • connection/jest.config.js
  • connection/package.json
  • connection/src/connector-registry.ts
  • connection/src/connector-types.ts
  • connection/src/external-connectors.generated.ts
  • connection/src/form-fields.ts
  • connection/src/generalized/__tests__/errors.test.ts
  • connection/src/generalized/errors.ts
  • connection/src/index.ts
  • connection/src/neo4j/Neo4jAuthenticationModule.ts
  • connection/src/neo4j/Neo4jConnectionModule.ts
  • connection/src/neo4j/Neo4jRecordParser.ts
  • connection/src/neo4j/__tests__/utils.test.ts
  • connection/src/neo4j/plugin.ts
  • connection/src/neo4j/utils.ts
  • connection/src/postgresql/PostgresAuthenticationModule.ts
  • connection/src/postgresql/PostgresConnectionModule.ts
  • connection/src/postgresql/PostgresRecordParser.ts
  • connection/src/postgresql/plugin.ts
  • connection/src/postgresql/utils.ts
  • connection/src/query-languages.ts
  • connection/src/schema/neo4j-schema.ts
  • connection/src/schema/pg-schema.ts
  • connection/src/schema/schema-manager.ts
  • connector-sdk/LICENSE
  • connector-sdk/README.md
  • connector-sdk/__tests__/connector-error.test.ts
  • connector-sdk/__tests__/determine-query-status.test.ts
  • connector-sdk/__tests__/stream-rows.test.ts
  • connector-sdk/jest.config.js
  • connector-sdk/package.json
  • connector-sdk/src/ConnectionModuleConfig.ts
  • connector-sdk/src/conformance/query-safety.ts
  • connector-sdk/src/connector-types.ts
  • connector-sdk/src/generalized/AuthenticationModule.ts
  • connector-sdk/src/generalized/ConnectionModule.ts
  • connector-sdk/src/generalized/ConnectorError.ts
  • connector-sdk/src/generalized/NeodashRecord.ts
  • connector-sdk/src/generalized/NeodashRecordParser.ts
  • connector-sdk/src/generalized/connector-plugin.ts
  • connector-sdk/src/generalized/interfaces.ts
  • connector-sdk/src/generalized/stream-rows.ts
  • connector-sdk/src/generalized/utils.ts
  • connector-sdk/src/index.ts
  • connector-sdk/src/schema/types.ts
  • connector-sdk/tsconfig.build.json
  • connector-sdk/tsconfig.json
  • eslint.config.js
  • package.json
  • scripts/generate-connector-imports.mjs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch release/1.2

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.

@sonarqubecloud

sonarqubecloud Bot commented Jul 1, 2026

Copy link
Copy Markdown

@alfredo1996
alfredo1996 merged commit 2031506 into dev Jul 1, 2026
28 checks passed
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