Skip to content

feat(query): type-referenced invalidation — .exact(TodoList()) over raw keys#156

Merged
zzal merged 1 commit into
mainfrom
feat/type-referenced-invalidation
Jul 6, 2026
Merged

feat(query): type-referenced invalidation — .exact(TodoList()) over raw keys#156
zzal merged 1 commit into
mainfrom
feat/type-referenced-invalidation

Conversation

@zzal

@zzal zzal commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Part II Wave 1 · #3 — invalidation restates keys the query type already owns

Invalidation was only .prefix(QueryKey) / .exact(QueryKey) / .tag(QueryTag) over raw keys. Inside one mutation the same query gets referenced type-safely in optimistic and stringly one line later:

func optimistic(_ title: String) -> [OptimisticEdit] {
    [.update(TodoList()) { $0 + [newTodo] }]      // typed
}
func invalidations(input: String, output: Todo) -> [Invalidation] {
    [.exact(["todos"])]                            // stringly — restates @Query(prefix: "todos")'s key
}

A renamed prefix silently breaks the invalidation; the compiler stays quiet.

Change

@MainActor static overloads of the case names taking the typed owner of the key, resolving eagerly to the existing cases:

static func exact<Q: Query>(_ query: Q) -> Invalidation   // .exact(query.queryKey)
static func prefix<Q: Query>(_ query: Q) -> Invalidation  // .prefix(query.queryKey)
  • No new vocabulary — the same words now accept the typed source of the key, so invalidations rhymes with optimistic: [.exact(TodoList())].
  • No ambiguity — an array literal can't be a Query; a test pins that raw-key spellings keep resolving to the cases.
  • Raw keys remain the escape hatch for keys no Query type owns.
  • @MainActor matches OptimisticEdit.update (the whole Query protocol is isolated).

Dogfood — all 4 stringly sites in the repo

  • TodoCRUD ×3: [.exact(["todos"])][.exact(TodoList())]
  • QueryDemo ×1 (parameterized): [.exact(["users", .int(id)])][.exact(UserByID(id: id))] — the macro (@Query(prefix:) + @Key) owns the key end-to-end
  • EmbeddedTemplates.swift regenerated (both examples are embedded templates)

Tests

New Invalidation/type-referenced suite (3): typed spellings produce values identical to the raw-key spellings — so the fully-tested dispatch machinery behind the cases is provably shared — plus exact≠prefix discrimination and the no-overload-capture pin.

Verification

  • Full host suite: 1379 tests, 277 suites green (incl. the TemplateEmbedder byte-pin on the regen)
  • TodoCRUD + QueryDemo wasm builds clean
  • Release CLI rebuilt with the new templates, then TodoCRUD Playwright e2e 5/5 against the real Bun backend (freshly scaffolded — optimistic-add-reconcile + rollback exercise the changed path)

🤖 Generated with Claude Code

…aw keys

Invalidation was only .prefix/.exact/.tag over raw QueryKeys, so inside one
mutation the same query was referenced type-safely in optimistic
(.update(TodoList())) and stringly one line later (.exact(["todos"])) —
restating a key that @query(prefix:) already owns. A renamed prefix silently
breaks the invalidation while the compiler stays quiet.

Add @mainactor static overloads of the case names taking the typed owner of
the key — exact<Q: Query>(_:) and prefix<Q: Query>(_:) — resolving eagerly to
the existing cases. No new vocabulary: the same words now accept the query
type, so invalidations rhymes with optimistic. Raw-key spellings remain for
keys no Query type owns.

Tests pin that the typed spellings produce values IDENTICAL to the raw-key
ones (the dispatch machinery behind the cases is provably shared) and that
array literals keep resolving to the cases (no overload capture).

Dogfood: all 4 stringly sites in the repo converted — TodoCRUD x3
(.exact(TodoList())) and QueryDemo's parameterized .exact(UserByID(id: id));
EmbeddedTemplates.swift regenerated (both are embedded templates).

Verified: host 1379/277, TodoCRUD + QueryDemo wasm builds clean, TodoCRUD
Playwright e2e 5/5 against the real Bun backend scaffolded from the new
templates (optimistic-add-reconcile + rollback exercise the changed path).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

📦 Bundle size

Artifact Baseline This PR Δ
App.wasm 5.06 MB 5.15 MB +1.91%
App.wasm (gzip) 1.80 MB 1.84 MB +2.20%
JS runtime 54.5 KB 54.5 KB +0.00%
JS runtime (gzip) 11.3 KB 11.3 KB +0.00%
Total (gzip) 1.81 MB 1.85 MB +2.19%

✅ Within budget (≤5% growth allowed).

Baseline: Swift 6.3, WASM SDK 6.3-RELEASE, measured 2026-06-18.

@zzal zzal merged commit d4a548a into main Jul 6, 2026
6 checks passed
@zzal zzal deleted the feat/type-referenced-invalidation branch July 6, 2026 23:27
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