Skip to content

Support WarpDrive schema records as changeset content - #210

Open
Sadek-1801 wants to merge 4 commits into
adopted-ember-addons:mainfrom
Sadek-1801:schema-records
Open

Sadek-1801 wants to merge 4 commits into
adopted-ember-addons:mainfrom
Sadek-1801:schema-records

Conversation

@Sadek-1801

Copy link
Copy Markdown

Makes changesets work with content that throws on unknown keys, such as
WarpDrive schema records. Part of fixing
adopted-ember-addons/ember-changeset#710.

The problem

In development builds, a WarpDrive schema record throws
No field named X on <type> when code reads a property outside its schema.
A changeset reads several such properties off its content, so passing a
schema record to Changeset() fails at the first read.

What changes

Each commit fixes one read and adds a failing test first.

  1. isChangeset checks '__changeset__' in obj before reading it.
    Before, it threw for every schema record it was given.
  2. Symbol keys stay on the changeset. The changeset Proxy called
    key.toString() on every key. Ember's get reads the symbol
    PROXY_CONTENT, which became the string "Symbol(PROXY_CONTENT)" and was
    looked up on the content. Setting a symbol key also recorded a change.
    Both Proxy handlers now share one handler in src/utils/proxy-handler.ts.
  3. The changeset asks the content only for keys it has. A key missing
    from the changes was always read off the content. That broke get, set,
    save and validation. Every content read now goes through
    contentHasKey(content, key). It uses key in content. Content with its
    own unknownProperty, such as an Ember ObjectProxy, counts as having
    every key, since in cannot see the keys it forwards.
  4. get() skips the nested read when a changed key has no path left.
    It read the changed value at the path '', which a schema record throws on.

Behaviour change to review

Plain objects, class instances, Ember objects, Ember proxies and ember-data
models all behave as before.

Content that is a JavaScript Proxy with a get trap but no has trap does
not. Before, such content returned its values. Now it returns undefined.
A subclass can override contentHasKey for content like that.

This may count as a breaking change. I leave the version bump to you.

The changeset also gains two methods, contentHasKey and _getContent. A
content field with either name is now hidden behind the method, as a field
named save or validate already is.

Testing

  • pnpm test: 416 passed, 2 skipped (the same 2 as on main).
  • pnpm lint and tsc are clean, and each commit passes on its own.
  • ember-changeset master, unchanged, linked to this branch: 200 passed,
    1 todo, 0 failed.

The compat CI job will probably fail, but not because of this change.
bin/test-external-changeset.js runs cd ember-changeset inside the clone,
but the add-on is now at the repository root. The clone is also tested before
its dist is built. I ran ember-changeset's suite by hand instead, as above.

Reading an unknown property throws on some objects, such as WarpDrive schema
records, so isChangeset threw for every record it was given.
The changeset Proxy called key.toString() on every key. Ember's get reads the
symbol PROXY_CONTENT, which became the string "Symbol(PROXY_CONTENT)" and was
looked up on the content. Setting a symbol key also recorded a change.
A key missing from the changes and the changeset was always read off the
content. Content that throws on unknown keys, such as WarpDrive schema records,
broke get, set, save and validation. contentHasKey guards every content read.
Content with its own unknownProperty, such as an Ember ObjectProxy, counts as
having every key, since in cannot see the keys it forwards. Subclasses can
override contentHasKey for other content like that.
get() read the changed value at the key's remaining path even when there was
none, so it asked the value for a field named ''. A plain object answers
undefined; a strict record, such as a WarpDrive schema record, throws.
@Sadek-1801 Sadek-1801 changed the title Support content that throws on unknown keys, such as WarpDrive schema records Support WarpDrive schema records as changeset content Oct 7, 2026
@SergeAstapov SergeAstapov added the enhancement New feature or request label Oct 13, 2026
@@ -0,0 +1,12 @@
// Like a WarpDrive schema record: reading a string key outside its schema
// throws, while symbol reads and `in` answer without throwing.
export default function strictRecord(fields: Record<string, unknown>) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Sadek-1801 do you think you could create an actual use case where we use WarpDrive schema? that would help proof it works and long term help with maintainability so that we could rely on this as a true regression setup rather than mock we need assume matches how the WarpDrive creates schemas?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants