Conversation
Analyze the full query pipeline (DSL → RawSelectInput → Desugar → Canonicalize → Lower → IRSelectQuery) and propose five approaches for dynamic query building: raw IR helpers (A), fluent builder (B), dynamic DSL with string resolution (C), extending SelectQueryFactory (D), and composable path objects (E). Includes comparison matrix, code examples, and a phased implementation plan. Recommends B+C layered approach for CMS use case. https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
Expand the recommendation to B+C+E-style composability via a FieldSet primitive. Add concrete examples for all five CMS surfaces: table overview (columns as FieldSet), edit forms (shape-derived all()), drag-drop builder (merge component requirements), NL chat (incremental extend), and shape-level defaults. Include FieldSet API design, comparison table of when shapes suffice vs when FieldSet is needed, and updated phased implementation plan. https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
… example Three major additions to 003: 1. Scoped filters in FieldSet — filters that attach to specific traversals (e.g. "only active friends") vs top-level query filters. Merged FieldSets AND-combine scoped filters on the same traversal. 2. Query derivation — immutable QueryBuilder where every .where(), .include(), .limit() returns a new builder. Enables fork/extend patterns for table→detail page transitions and NL chat evolution. 3. Shape remapping — ShapeAdapter maps a query from one ontology to another (PersonShape → schema:Person) while preserving result keys so components render identically across different graph environments. Includes full end-to-end example combining all features across all five CMS surfaces, updated open questions, and revised implementation plan with shape remapping as Phase 4. https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
…ter format details
Four additions to 003:
1. FieldSet immutability — extend/omit/pick return new instances,
parallel to QueryBuilder immutability. Shared FieldSets across
components are safe from mutation.
2. Filter-on-selected-path semantics — selecting 'age' and filtering
'age > 30' share the same IR traversal/variable via existing
LoweringContext.getOrCreateTraversal() deduplication. Scoped
filters and top-level filters AND-combine on shared variables.
3. Variable reuse / shared bindings — document current implicit
variable sharing via alias dedup, and design direction for future
explicit .as()/$ref binding pattern. PropertyPath gets optional
bindingName slot. IRAliasScope already supports this structurally.
4. ShapeAdapter properties format — clarify string labels vs
PropertyShape references ({id: IRI}), mixed usage, and internal
resolution to IRI-to-IRI map. Add examples of both forms.
https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
…, and IR changes
Replace the placeholder binding design with comprehensive examples showing
how .as()/.joins() works across all API surfaces:
- Static DSL: .as('sharedHobby') names a path endpoint, .joins(hobby)
creates a structural join (vs .equals() which generates FILTER)
- QueryBuilder: callback and string-based binding patterns
- FieldSet composition: exported/consumed bindings that connect across
merged FieldSets (component A exports a binding, component B joins on it)
- Immutability: bindings are part of the cloned state on fork/extend
- NL chat: incremental binding construction
- Drag-drop: component-declared bindings auto-connect on merge
Also detail the IR changes needed: LoweringContext gets a namedBindings
map, IRTraversePattern gets optional bindingName/joinBinding fields.
Show that the change is small — if two patterns share a `to` alias,
existing downstream code already produces shared ?variables.
v1 types reserve optional fields (bindingName, joinBinding, $ref)
that cost nothing until implementation, but allow FieldSets created
now to carry .as() declarations that work when bindings land.
https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
…more options The previous examples were confusing — they conflated FieldSets with binding references and didn't show the generated SPARQL. This rewrite: - Adds a clear "problem" section explaining why shared variables matter - Shows side-by-side SPARQL: FILTER approach vs shared variable approach - Explains .as() and .joins() in plain terms (label a node / reuse that label) - Presents 4 API design options with pros/cons and recommendation - Every example now includes the generated SPARQL - Adds 7 open design questions specific to bindings (scope, validation, multiple exporters, cross-shape, OPTIONAL, serialization, naming) - Renames .join() to .constrain() for string-based API (less SQL confusion) https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
- New 008-shared-variable-bindings.md: full design with agreed decisions (symmetric .as(), .matches() sugar, no type checks, per-query scope) - 003: replaced ~500-line variable section with compact forward-compat summary + link to 008. Keeps only what v1 needs: reserved optional fields on PropertyPath, FieldSetEntry, WhereConditionValue, QueryBuilder https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
- New 009-shape-remapping.md: full ShapeAdapter design (3 options, CMS examples, pipeline placement, open questions) - 003: replaced shape remapping section with 6-line forward ref, replaced end-to-end step 7 with link, replaced Phase 4 with link - 008: added status note that nothing is implemented yet https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
- Question 9 resolved: serialize at QueryBuilder/FieldSet level (not IR) - Shape/property identifiers use prefixed IRIs (my:PersonShape) - Added QueryBuilder.toJSON()/fromJSON() to implementation plan - Note on question 4: plain-object where clause form used in JSON https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
- Replaced generic end-to-end example with 3 real CMS surface examples:
grid/table (add columns, filter, switch view mode), drag-drop page
builder (merge component requirements), NL chat (incremental refinement)
- Added nested selection syntax: { 'hobbies': ['label', 'description'] }
to avoid repeating long path prefixes
- Added method naming ideation section comparing .with()/.without(),
.expand()/.contract(), .addFields()/.removeFields(), etc.
- Note: .select() = replace fields (switch view mode, keep filters),
merge method name still open
- Updated FieldSetInput type to support nested object + nested FieldSet
https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
Key decisions captured:
- DSL is syntactic sugar over QueryBuilder + FieldSet (shared proxy)
- ProxiedPathBuilder is shared between DSL, FieldSet, QueryBuilder callbacks
- .path('string') is escape hatch on proxy for dynamic/runtime paths
- Method naming decided: setFields/addFields/removeFields (QueryBuilder),
set/add/remove/pick (FieldSet)
- Where clauses: proxy form (p => p.age.gt(18)) matches DSL,
string form ('age', '>', 18) is convenience shorthand
- Type validation: operators validated against sh:datatype
- .select() in callbacks = sub-selection (matches DSL), not field replacement
- .for(id) chainable pattern for DSL alignment
- Variable bindings: .as() on proxy, { path, as } for string form,
no separate .bind()/.constrain() needed
- Phase 3 added: DSL alignment (refactor DSL to use QueryBuilder internally)
Updated 008 to reflect shared proxy, new method names, resolved
.bind()/.constrain() question (not needed — .as() is enough)
https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
QueryBuilder implements PromiseLike so `await` triggers execution. No more mutable PatchedQueryPromise or nextTick scheduling. .select() and .query() both return QueryBuilder (same type). https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
…tes, decided APIs - Replace .include() → .select() in all conceptual examples - Replace .setFields() → .select() where it's initial field selection (creation) - Keep .setFields() only for replacing fields on existing builders - Mark .for(id)/.forAll() chainable pattern as decided (was "open for discussion") - Update delete API: delete(id) requires arg, deleteAll() for bulk - Update mutations: .for()/.forAll() required on update (type error without targeting) - Fix missing parentheses around callback params on line 87 - Clean up .include() placeholder note - Add .select() to QueryBuilder class design and method naming tables - Add .forAll() to QueryBuilder narrowing API - Update implementation plan phases 2 and 6 https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
…ment) Distills decided architecture from 003 ideation into actionable plan: - PropertyPath, FieldSet, QueryBuilder contracts - Files expected to change with accurate line references - Pitfalls, open questions, and scope boundaries https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
Added detailed file sizes from codebase exploration: - SelectQuery.ts: 72KB, 4 interrelated classes - Full pipeline file sizes (IRDesugar 12KB, IRLower 11KB, etc.) - Supporting files and existing test suite that must pass - QueryFactory.ts and IRDesugar.ts as additional modified files https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
- Remove .one(id) — .for(id) already implies singleResult - All targeting methods accept string | NodeReferenceValue for flexibility - Rename PropertyPath.steps → segments (clearer naming) - Add FieldSet.for(shape).select(fields) chained construction form - Remove backward compat pitfall (current version not in use) - Expand mutation builders open question with codebase context - Add note that scoped filter merging needs resolution before plan closes - Document QueryFactory.ts disposition: remove empty abstract class, keep types https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
- CreateBuilder, UpdateBuilder, DeleteBuilder replace existing factory classes - Same immutable + PromiseLike pattern as QueryBuilder - UpdateBuilder requires .for()/.forAll() targeting before execution - DeleteBuilder accepts ids at construction (string | NodeReferenceValue) - Reuse MutationQueryFactory input normalization, existing IR pipeline - Add contracts, new files, modified files for mutations - Remove mutation builders from open questions (decided: in scope) https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
- Add ProxiedPathBuilder contract (.path() escape hatch for dynamic strings)
- Add sh:datatype validation on comparison helpers (boolean=only equals, etc.)
- Add FieldSet.summary() for shape-metadata-derived summaries
- Add serialization format spec (QueryBuilder.toJSON/fromJSON, FieldSet.toJSON/fromJSON)
- Add shape resolution by prefixed IRI string
- Add Person.selectAll({ depth }) to scope
- Keep __id in data support alongside .withId() on CreateBuilder
- Add ProxiedPathBuilder.ts to new files list
- Move result typing to future work section
- Expand scope boundaries with all included items
https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
Plan: scoped filter merging defaults to AND with warnings on conflicts. OR support deferred to when it actually comes up. Skill: add rule to carry forward all decided features from ideation. No idea can be silently dropped — if unsure whether tentative or decided, ask the user rather than omitting. https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
Structures the plan into 5 phases: ProxiedPathBuilder extraction, QueryBuilder, FieldSet, mutation builders, and serialization. Removes FieldSet.summary() from scope (CMS-layer concern, not core). https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
Extract the proxy creation logic from SelectQueryFactory.getQueryShape() into a standalone createProxiedPathBuilder() function. Add PropertyPath value object and WhereCondition type as minimal foundations for Phase 2. All 477 existing tests pass with no behavioral changes. https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
Break the implementation phases into concrete tasks with: - Dependency graph showing Phase 3a/3b can run in parallel - Detailed task descriptions per phase - Test specifications with named test cases and assertions - Validation commands (tsc --noEmit, npm test) - Stubs for parallel execution noted - Phase 1 marked as complete https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
Documents the four test invariants that must hold across all implementation phases: existing test regression, new code coverage, Fuseki integration compatibility, and zero TypeScript errors. https://claude.ai/code/session_01G1WX9eoMGi6n9P8eMNW3uT
Immutable, PromiseLike QueryBuilder that produces identical IR to the existing DSL (verified via 12 IR equivalence tests). Delegates to SelectQueryFactory internally for guaranteed correctness. New files: - src/queries/QueryBuilder.ts — fluent API: from, select, selectAll, where, orderBy/sortBy, limit, offset, for, forAll, one, build, exec - src/tests/query-builder.test.ts — 28 tests (immutability, IR equivalence, walkPropertyPath, shape resolution, PromiseLike) Changes: - src/queries/PropertyPath.ts — add walkPropertyPath(shape, path) - src/index.ts — export QueryBuilder, PropertyPath, walkPropertyPath - jest.config.js — add query-builder.test.ts to testMatch - docs/plans/001-dynamic-queries.md — mark Phase 2 complete Tasks 2.3/2.4 (Shape.select() rewire, factory deprecation) deferred to Phase 4 — requires threading result types through QueryBuilder generics. 505 tests pass, tsc --noEmit clean, zero regressions. https://claude.ai/code/session_01JCtsSZg1vcWZ5jzhgH4TYy
Phase 3a — FieldSet:
- Immutable composable collection of PropertyPaths for shape field selection
- Static constructors: for() (string/callback/PropertyPath), all(), merge()
- Composition: select(), add(), remove(), set(), pick()
- Nesting support via object form: { friends: ['name', 'hobby'] }
- Integrated with QueryBuilder via .select(fieldSet) and .fields()
Phase 3b — Mutation builders:
- CreateBuilder: immutable fluent builder for create mutations with .set()/.withId()
- UpdateBuilder: immutable fluent builder with .for()/.set(), guards on missing target
- DeleteBuilder: immutable builder for delete mutations
- All implement PromiseLike for await-triggered execution
- All delegate to existing factories for identical IR generation
Tests: 39 new tests (544 total passing), IR equivalence verified against DSL.
https://claude.ai/code/session_01JCtsSZg1vcWZ5jzhgH4TYy
…ilder - FieldSet.toJSON() serializes shape IRI + dot-separated field paths - FieldSet.fromJSON() reconstructs via getShapeClass() + walkPropertyPath() - QueryBuilder.toJSON() serializes shape, fields, limit, offset, subject, orderDirection - QueryBuilder.fromJSON() reconstructs builder from JSON with FieldSet integration - Export new types: FieldSetJSON, FieldSetFieldJSON, QueryBuilderJSON - 14 new serialization tests (558 total passing) Dead code cleanup (4.4) deferred: PatchedQueryPromise, patchResultPromise, and nextTick removal blocked by Shape.select() DSL rewire type complexity. https://claude.ai/code/session_01JCtsSZg1vcWZ5jzhgH4TYy
Phases 1-4 all complete. 558 tests passing total. Dead code cleanup (PatchedQueryPromise, nextTick removal) deferred due to Shape.select() DSL rewire type complexity. https://claude.ai/code/session_01JCtsSZg1vcWZ5jzhgH4TYy
…code cleanup) Break deferred Phase 4.4 into 6 sub-phases (4.4a–4.4f) with code examples, files to edit, validation steps, and dependency graph. https://claude.ai/code/session_01JCtsSZg1vcWZ5jzhgH4TYy
…oval - Add type invariant: result types must stay identical, query.types.test.ts is source of truth, no test weakening allowed - Add query-builder.types.test.ts requirement mirroring DSL type tests - Mark Shape.query() for removal in 4.4e (breaking change, document in changelog) - Clarify fallback strategy: DSL types must never regress even if QueryBuilder types fall back to any https://claude.ai/code/session_01JCtsSZg1vcWZ5jzhgH4TYy
Type probe confirms QueryResponseToResultType resolves correctly through class generics and Awaited<PromiseLike>. Replaces monolithic 4.4a with 6 incremental steps, each independently verifiable. https://claude.ai/code/session_01JCtsSZg1vcWZ5jzhgH4TYy
Item 4: keep both FieldSet.select() and .set() as valid API surface. Item 9: TypeScript readonly annotation is sufficient for PropertyPath.segments. https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
…le TODOs - Remove commented where() method (SelectQuery.ts) - Remove commented TestNode/convertOriginal code blocks (SelectQuery.ts) - Remove abandoned TestNode approach (SelectQuery.ts) - Remove debug console.log(lim) from limit() method (SelectQuery.ts) - Remove commented console.error and old proxy return (SelectQuery.ts) - Remove old countable logic comments (SelectQuery.ts) - Remove stale "strange bug" TODO comment (SelectQuery.ts) - Remove commented validation in convertNodeReference (MutationQuery.ts) - Strip commented body from ensureShapeConstructor, keep passthrough stub (ShapeClass.ts) All 619 tests pass, no functional changes. https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
…d never errors
14.1: Type RawSelectInput.shape as structural type, removing 2 'as any' casts
in desugarSelectQuery
14.3: Add branded error types to never fallthroughs in QueryResponseToResultType,
GetQueryObjectResultType, and ToQueryPrimitive for better IDE diagnostics
14.2: Skipped — constraining QResult's Object generic to Record<string, unknown>
cascades through SubProperties in 4+ types and conflicts with
QueryResponseToResultType's union return type. Not worth the churn.
All 619 tests pass, tsc clean.
https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
…into QueryPrimitive Remove 4 empty subclasses — all logic was already in QueryPrimitive<T>. - Remove abstract from QueryPrimitive (now concrete) - Replace all new QueryString/Number/Boolean/Date with new QueryPrimitive<T> - Update instanceof QueryString → instanceof QueryPrimitive - Update SetSize to extend QueryPrimitive<number> directly - Update ToQueryPrimitive and all conditional types - Update comments All 619 tests pass, tsc clean. https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
Types are stable and well-tested — risk/reward unfavorable for a cleanup pass. Created docs/ideas/011-query-type-system-refactor.md with full analysis: decision trees, branch counts, proposed decomposition, and validation strategy. https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
The monkey-patched getQueryPaths on FieldSet was never called — getComponentQueryPaths() catches FieldSet via instanceof before the duck-type check, and fieldSetToSelectPath + entryToQueryPath already handle parent path nesting through entry.path.segments. - Remove two monkey-patch assignments in QueryShapeSet.select() and QueryShape.select() - Remove optional getQueryPaths property declaration from FieldSet - Keep duck-type checks (they target QueryBuilder, which has getQueryPaths as a real method) All 619 tests pass, tsc clean. https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
Expanded from a single phase into 18A-D after analyzing the 3 SelectPath consumers (QueryBuilder pipeline, getQueryPaths, preload system). Each sub-phase is independently shippable with its own validation strategy. 18A: Direct FieldSet → Desugar conversion (additive, parity tests) 18B: Switch QueryBuilder to new path 18C: Refactor preload to use FieldSet directly 18D: Delete old SelectPath types and bridge functions https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
…desugar Eliminates the entire SelectPath intermediate representation layer: - fieldSetToSelectPath() / entryToQueryPath() bridge functions (DELETED) - SelectPath, CustomQueryObject, SubQueryPaths, ComponentQueryPath types (DELETED) - BoundComponent.getComponentQueryPaths() and getPropertyPath() (DELETED) - QueryBuilder.getQueryPaths() (DELETED) - RawSelectInput.select replaced with RawSelectInput.entries The desugar pass now converts FieldSetEntry[] directly to DesugaredSelection[], removing the wasteful serialize-then-reparse roundtrip through SelectPath. Preload entries now store preloadSubSelect (FieldSet) instead of preloadQueryPath (opaque QueryStep[] from BoundComponent.getPropertyPath()). Pipeline: FieldSet entries → desugarSelectQuery → canonicalize → lower → IR Net: -165 lines, 619 tests pass, tsc clean. https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
Two related cleanups:
1. parentQueryPath?: any → parentSegments?: PropertyShape[]
- forSubSelect() now receives PropertyShape[] directly (via
collectPropertySegments) instead of QueryStep[] from getPropertyPath()
- convertTraceResult() no longer duck-types QueryStep objects to
extract PropertyShape — uses parentSegments directly
- collectPropertySegments() made public for use by SelectQuery.ts
2. SortByPath.paths: QueryPropertyPath[] → PropertyPath[]
- evaluateSortCallback() now builds PropertyPath directly via
collectPropertySegments instead of calling getPropertyPath()
- toSortBy() in IRDesugar reads path.segments directly instead
of duck-typing through toSelectionPath()
- Removed `as any` cast on sortBy in QueryBuilder
toSelectionPath() is now only used by the where clause path.
https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
Replace duck-typed unknown/any handling in toSelectionPath() and toWhereArg() with proper type imports and type guards for QueryStep, PropertyQueryStep, SizeStep, and ArgPath from SelectQuery. https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
Define ShapeConstructor<S> — a concrete (non-abstract) constructor type that includes static properties (shape, targetClass). Use it as: - The `this` parameter in all Shape static methods (select, selectAll, update, create, delete, mapPropertyShapes, getSetOf) - The shape field type in all Builders and their Init interfaces - The parameter type in mutation factory constructors - The parameter type in resolveShape, createProxiedPathBuilder, evaluateSortCallback, and FieldSet methods This eliminates the root cause of most `as any` casts: the mismatch between ShapeType (abstract new) and what runtime code needs (concrete new + static property access). Cast count: ~44 → ~31 in query files + Shape.ts. Remaining casts are inherent to proxy/dynamic patterns (callback generics, dynamic property access by string key). https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
ShapeType (with abstract new) is no longer imported anywhere. ShapeConstructor fully replaces it at all runtime boundaries. https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
Remove open questions from completed phases (13-19), condense Phase 18 sub-phases into summary, mark Phase 19 complete, update dependency graph, update Phase 11 status. Keep all context of what was done and why. https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
1. Change getShapeClass() return type from `typeof Shape` to `ShapeConstructor | undefined` — eliminates `as unknown as ShapeConstructor` casts at call sites (resolveShape.ts, FieldSet.ts) and removes `as any` casts for `new shapeClass()` (SelectQuery.ts). 2. Update FieldSet.resolveShapeInput() to look up ShapeConstructor from NodeShape via getShapeClass(), so NodeShape callers get the full ProxiedPathBuilder proxy (sub-selects, .size(), evaluations, etc.) instead of the limited string-only fallback. 3. Delete traceFieldsFromCallback — the old simple proxy that only captured top-level string keys. No longer needed now that NodeShape inputs resolve to ShapeConstructor and use the full proxy path. All 619 tests pass, tsc clean. https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
- FieldSetEntry: explain subSelect vs preloadSubSelect distinction - QueryPrimitive: document consolidation from former subclasses - SHACL.ts: explain Shape-as-unknown cast in prototype-walking - extractComponentFieldSet: document supported component interfaces - getPackageShape: clarify return type and use case https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
New documentation covering: - QueryBuilder: fluent API, shape class + IRI string modes, PromiseLike, targeting entities, sorting/limiting, FieldSet integration - FieldSet: composable field selections, string/callback/IRI creation, nested fields, composition (add/remove/pick/merge), inspection - Mutation Builders: CreateBuilder, UpdateBuilder, DeleteBuilder as programmatic equivalents of Shape.create/update/delete - JSON serialization: round-trip toJSON/fromJSON for queries and field sets - Use case examples: CMS, API gateway, component composition, progressive loading Also updates feature overview list and "Linked core offers" bullets. https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
Shape.select(id, callback) → Shape.select(callback).for(id) Shape.update(id, data) → Shape.update(data).for(id) Shape.selectAll(id) → Shape.selectAll().for(id) .for(id) now unwraps the array result type (returns single object, not array) to match the old single-subject overload behavior. - Updated all query-fixtures and test call sites to new API - Added 10 new tests for .for() and .forAll() chaining patterns - Updated README examples throughout - Cleaned up unused imports (QShape, QResult, ICoreIterable) from Shape.ts All 629 tests pass, tsc clean. https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
Documents all breaking changes and new features: - Shape.select/update id argument removed (use .for()) - ShapeType → ShapeConstructor rename - QueryPrimitive consolidation - SelectPath IR types removed - New: QueryBuilder, FieldSet composition, JSON serialization, PropertyPath export, mutation builders https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
Report at docs/reports/008-dynamic-queries.md covers architecture, public API, breaking changes, file map, and test coverage. https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
- Add .for(ids) fluent method matching UpdateBuilder's pattern - Make ids optional in .from() (backwards compatible) - Move empty-ids validation from constructor to .build() for consistency - Add immutability test for .for() - Update tests to exercise both .for() and .from() paths https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
Linked 2.0 - introducing dynamic query building. QueryBuilder, FieldSet, mutation builders.
- 011: Add result typing for dynamic queries (QueryBuilder.from<T>), QueryContext null handling note, remove Phase 16 naming - 006: Add callback-style mutation updates section with proxy design https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
- Delete docs/ideas/003-dynamic-ir-construction.md (superseded by QueryBuilder) - Delete docs/plans/001-dynamic-queries.md (completed, was left over) - Clean up report 008: remove raw IR helpers, FieldSet.summary, async shape loading, Phase 16 naming; point deferred items to idea docs https://claude.ai/code/session_01P1bPzwN55G6NHXVH1dDQpV
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.
Summary
Replaces the mutable
SelectQueryFactory+PatchedQueryPromise+nextTickquery system with an immutableQueryBuilder+FieldSetarchitecture. The DSL (Person.select(...),Person.create(...), etc.) is now syntactic sugar over composable, serializable builders. Mutation operations follow the same immutable builder pattern.Key Changes
.select(),.where(),.orderBy(),.limit(),.for(),.forAll()methodsCreateBuilder,UpdateBuilder,DeleteBuilderfollow the same immutable PromiseLike patternShapeTypefor clarity at runtime boundariesQueryString,QueryNumber,QueryBoolean,QueryDatemerged into single genericQueryPrimitive<T>classSelectPathintermediate representation;desugarSelectQuery()now acceptsFieldSetEntry[]directly viaRawSelectInput.entries.for(id)targets single subject (unwraps result type),.forAll(ids)generatesVALUESclause for multi-ID filteringShape.select()andShape.update()no longer accept ID as first argument; use.for(id)insteadBreaking Changes
Shape.select(id, fn)→Shape.select(fn).for(id)Shape.update(id, payload)→Shape.update(payload).for(id)ShapeTyperenamed toShapeConstructorSelectPath,QueryPath,CustomQueryObject,SubQueryPaths,ComponentQueryPathQueryString,QueryNumber,QueryBoolean,QueryDateclasses (useQueryPrimitive<T>)Implementation Details
FieldSet, which converts directly toRawSelectInput— the existing IR pipeline entry pointProxiedPathBuilderproxy implementation powers both DSL and dynamic query constructionbuildSelectQuery()→ IRDesugar → IRCanonicalize → IRLower → irToAlgebra pipeline reused unchanged