fix(seed): resolve relationship refs by natural key, not {externalId} objects (showcase + crm + compile-time guard)#1558
Merged
Conversation
…nalId} objects
The showcase seed datasets wrote lookup/master-detail fields as object
literals like `project: { externalId: 'Website Relaunch' }`. The seed
loader's reference resolution only processes plain-string natural keys
(it skips non-string values), so these objects passed through unresolved
and were handed straight to the driver.
On `:memory:` this was masked because every boot started empty and seeds
ran as INSERTs. With the persistent SQLite DB now used by `objectstack
dev`, restarts re-run seeds as UPDATEs, and better-sqlite3 throws
"SQLite3 can only bind numbers, strings, bigints, buffers, and null"
when it receives the raw `{ externalId: ... }` object — producing repeated
"Update operation failed" log noise and leaving those demo rows un-updated.
Switch every relationship/lookup reference (project.account, task.project,
category.parent, project_membership.team/project) to the plain natural-key
string the loader expects (e.g. `project: 'Website Relaunch'`). The loader
then resolves each to the referenced record's id via its externalId
mechanism on both the insert and update paths.
Verified by booting the showcase dev server twice against a persistent
dev.db: no "can only bind" / "Update operation failed" errors, and the
FK columns store resolved record ids matching across tables.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…pile time; fix app-crm
Root cause (same class as the showcase fix): seed datasets wrote lookup /
master-detail references as `{ externalId: 'X' }` object literals, but the
loader resolves references from the plain natural-key string. The object was
silently skipped, passed through unresolved, and crashed the SQL driver on the
update path ("can only bind ...") — masked on an empty :memory: DB.
This wasn't an isolated showcase typo: app-crm used the same object form
everywhere, and nothing stopped it — the record value type was `unknown`, so
both forms compiled, and the failure only surfaced on a persistent DB. Hardening
so it can't recur:
- defineDataset: constrain lookup/master_detail field values to `string | null`
at compile time (derived from each field's literal `type`). The object form is
now a type error; all other fields stay `unknown`.
- SeedLoaderService: when a reference value is an object, fail loudly with an
actionable message and drop the value instead of handing it to the driver —
consistent across all drivers, no longer silently masked. Covered by a new
unit test.
- app-crm seed data: switch every reference (contact/opportunity/lead/activity
→ account/contact/opportunity) to the natural-key string form.
Verified: ran app-crm dev twice against a persistent SQLite DB — 0 "can only
bind", 0 invalid-reference, 0 insert/update failures; all 12 opportunities seed
and every FK resolves to a real record id (incl. email-keyed activity.contact).
spec (6628), runtime (342), showcase (20) test suites pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Seed datasets wrote
lookup/master_detailreferences as object literals, e.g.project: { externalId: 'Website Relaunch' }. The seed-loader resolves references from the plain natural-key string — it skips non-string values (packages/runtime/src/seed-loader.ts:273), so the wrapper object was never resolved and got handed straight to the SQL driver.On a
:memory:DB this was masked (always-empty at boot ⇒ INSERTs). Now thatobjectstack devuses a persistent SQLite DB, restarting re-runs seeds as UPDATEs and better-sqlite3 throws:— repeated
Update operation failed { object: showcase_task ... project = {externalId:...} }noise, and those demo rows never update.This was not an isolated showcase typo:
examples/app-crmused the same object form throughout, and nothing stopped it — the seed record value type wasunknown, so both forms compiled, and the failure only ever surfaced on a persistent DB.Changes
1. Fix the example seed data (
examples/app-showcase,examples/app-crm) — switch every relationship reference from{ externalId: 'X' }to the plain natural-key string'X':project.account,task.project,category.parent,project_membership.team/projectcontact/opportunity/lead/activity→account/contact/opportunity2. Harden the framework so it can't recur (
@objectstack/spec,@objectstack/runtime):defineDatasetnow constrainslookup/master_detailfield values tostring | null(derived from each field's literaltype). The object form is now a type error; all other fields stayunknown.SeedLoaderServicenow fails loudly with an actionable message and drops the value (instead of silently skipping it and letting it reach the driver) when a reference is an object — consistent behavior across all drivers. Covered by a new unit test.Verification
task.project== the Website Relaunch id; crmcontact.account==opportunity.accountfor Acme). All 12 crm opportunities seed; even the email-keyedactivity.contactresolves.@objectstack/spec(6628) ✅,@objectstack/runtime(342, incl. new guard test) ✅,example-showcase(20) ✅. Typecheck of app-showcase clean.Note for reviewers
examples/app-crmhas pre-existing typecheck failures unrelated to this PR (src/security,src/themes,src/webhooksimport symbols likeSecuritythat aren't exported by the current@objectstack/spec— API drift). This PR does not touch those; the seed data file (src/data/index.ts) typechecks clean.https://claude.ai/code/session_014XvTsiH3dPPqKgU8uaRjXR