Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #410 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 97 103 +6
Lines 13171 13590 +419
Branches 644 1125 +481
==========================================
+ Hits 13171 13590 +419
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
c14d00f to
9f97b92
Compare
9f97b92 to
648de2b
Compare
0be9f1d to
733b363
Compare
ide
left a comment
There was a problem hiding this comment.
I'm a little uncertain about the install mechanism but TypeScript does appear to support it.
One potential improvement is to pass in the classes we want the extensions to modify. So instead of
function install() {
SomeClass.knexLoader = ...
}
We'd do:
function install(SomeClass) {
SomeClass.knexLoader = ...
}
This is (1) slightly more testable (though test frameworks do allow module mocking) and (2) sets expectations around what is supposed to be modified or not by the installer.
733b363 to
b1423c1
Compare
648de2b to
e0893f6
Compare
|
I like this proposal. Will explore in a follow-up PR! |
Merge activity
|
# Why This is a long stack, but the goal is to make a place to put postgres-specific loading logic to avoid polluting the core dataloader-based library with logic specific to knex/postgres. And potentially even postgres-specific mutation logic but that's way down the road. The best place for this stuff is in the `entity-database-adapter-knex` library. Therefore, the strategy is (in PR order): 1. Move existing knex/postgres-specific loader methods to a new loader, `knexLoader`. I'm also open to calling this a "Postgres Loader" since the `entity-database-adapter-knex` package already uses postgres-specific queries (and more are added here since this stack eventually produces raw queries) within knex even though technically knex is database agnostic. 1. Move knex-specific logic out of EntityDataManager, move it to its own EntityKnexDataManager. 1. Move these two now-separated pieces into the `entity-database-adapter-knex` package. This requires creating a "plugin" system for database adapters, where when they are included their methods are added to the prototype so that seamless loading continues to work. 1. Add a codemod for these three pieces. 1. Add the first new feature to entity loading, load by tagged template string, which creates a better/safer way to express raw queries for loading entities. Future other things that could be added here (after thorough thought on authorization) are: - Pagination - Create, update, delete by tagged template string - Batch creation - Upsert (maybe) # For Reviewers The most critical things to assess are: - "install" method in #410 - sql tagged template API usability in #414 # How This is part 1. It refactors the loaders by moving `loadManyByFieldEqualityConjunctionAsync`, `loadFirstByFieldEqualityConjunctionAsync`, and `loadManyByRawWhereClauseAsync` to a separate loader. The new pattern for these is: ``` await PostgresTestEntity.knexLoader(vc1).loadManyByRawWhereClauseAsync(...) ``` # Test Plan This has full coverage in new tests.
e0893f6 to
2825aae
Compare
# Why An idea proposed by @ide in #410 (review). This makes it more testable and clearer about what objects installations are "allowed" to augment. # How Pass in the classes explicitly. # Test Plan `yarn tsc`
# Why Asked claude extended thinking to analyze the `installExtensions` monkey-patching strategy we went with in #410 and compare it to other typescript ORM libraries. It said essentially that it was ok, but most modern libraries choose not to do it that way, and instead recommended "free functions" (TIL about that term) to instantiate the loaders. (`knexLoader(BlahEntity, vc)`) Working with it even more, I came up with an even better method on top of free functions to keep the `BlahEntity.knexLoader(vc)` syntax. The strategy is to: 1. Convert to free functions (this PR). 2. Add `PostgresEntity`/`ReadonlyPostgresEntity` subclasses that application entities can extend that implement the static extension methods. 3. Revert the codemod change from this PR to keep the codemod to prefer the `BlahEntity.knexLoader(vc)` syntax since most applications should use the subclasses (though it's not strictly necessary if they prefer the free function at callsites). 4. Revert the codemod in the integration tests to express better syntax pattern. # How Claude did most of this, but broadly speaking the strategy is to have the free functions, and then instead of patching the prototypes/classes for singleton caching, use a WeakMap where the core package singleton is the key and the value is the new knex-specific object. # Test Plan Run all tests.
# Why In #410 we split out `EntityLoaderUtils` into two classes: - EntityInvalidationUtils - functions for invalidating entity caches from application code when underlying data is mutated outside of the entity framework - EntityConstructorUtils - functions for constructing and authorizing entities. Originally, these were both public due to them being the same class. Now, with them separated, we can better restrict access to entity construction which should only be done within the framework itself. # How Make constructor utils private everywhere except EntityLoaderFactory, which is deep enough in the abstraction that it's not immediately usable by top-level entity APIs (static methods on Entity/ReadonlyEntity, loader classes). # Test Plan `yarn tsc`, CI

Why
Part 3 of #407.
How
This is the main PR of the stack. It creates a mechanism for "installing" the extension methods on required objects, and then installs them and tests them.
The goal is to make it better self-contained about what is core in the library and guaranteed to be efficient (dataloader'd and cached) and what is not (raw SQL and SQLFragment loads in a future PR).
This is becoming more critical in a time of agentic coding where having a discrete set of loaders that can be linted separately and instruct agents to run
EXPLAIN ANALYZEin raw cases.Test Plan
Full test coverage.