feat(server): put an installed package's models on globalThis - #2431
Merged
Conversation
#2427 and #2428 settled where package models do NOT belong: the migration generator, because a package owns its tables through the SQL it ships. This is the half that makes them usable. A package's actions and routes query its models by name, exactly as an application's own code does, so they have to be in the auto-import barrel. Both places that build the model set now include them, and the order is the precedence: userland, then packages, then the framework defaults. Dedupe on this side is first-wins, the opposite of the migration side's last-wins, so the same intent is spelled backwards in the two files. An application still overrides a package's model by name, and a package still overrides a default. The manifest reader moved to @stacksjs/config, which is the one package both the migration side and the barrel can reach. It was about to become a third copy of the same resolution rule, after the router's inline one, and copies of that rule eventually disagree about where a package is installed - at which point routes and models describe different trees. Models have no manifest key, so a package that ships them is taken to put them in `app/Models`, where every Stacks application does. Views keep their explicit key, which a package uses to ship more than one subtree. Reading the manifest is lazy and never fatal. This runs during boot, and a static import would pull the config graph in before the env layer has loaded; a boot that cannot read the manifest still has the application's own models, and refusing to build the barrel would take those away too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Completes the model half. #2427 and #2428 settled where package models do not belong, the migration generator, because a package owns its tables through the SQL it ships. This is what makes them usable: a package's actions and routes query its models by name, so they have to be in the auto-import barrel.
Precedence is spelled backwards in the two files
Both places that build the model set now include package dirs, ordered userland, then packages, then framework defaults.
The dedupe on this side is first-wins, the opposite of the migration side's last-wins, so the same intent is expressed in reverse order in the two files. An application still overrides a package's model by name, and a package still overrides a framework default. Worth knowing before editing either.
One manifest reader instead of three
The reader moved to
@stacksjs/config, the one package both the migration side and the barrel can reach.core/serverdoes not depend on@stacksjs/database, so this was about to become a third copy of the same rule, after the router's inline one. Copies of that rule eventually disagree about where a package is installed, and then routes and models describe different trees.core/database/src/package-models.tsnow delegates to it and keeps its local names, so nothing that imports it changes.Models have no manifest key
PackageStacksMetadeclaresviews,routes,migrationsand others, but nomodels. Rather than add one, a package that ships models is taken to put them inapp/Models, where every Stacks application does. Views keep their explicit key, which a package uses to ship more than one subtree.Failure is not fatal
The manifest read is lazy and wrapped. It runs during boot, and a static import would pull the config graph in before the env layer has finished loading. A boot that cannot read the manifest still has the application's own models, and refusing to build the barrel would take those away too. That matters more here than elsewhere:
auto-imports/index.tsusesexport *, so a barrel that fails to link takes every model offglobalThisat once.Known gap, not addressed here
Discovery runs after the barrel is built.
preloader.tscallsloadAutoImports()thendiscoverPackages(), so the first boot after installing a package builds the barrel from a manifest that does not list it, and the models appear on the second boot. Compounding it,autoImportsAreStale()is mtime-based and package managers preserve tarball mtimes, so an installed package's files are routinely older than the manifest and will not trip it. Both want a discovery-aware staleness signal, which is its own change.Verification
core/config197 pass,core/server168 pass,core/database880 pass / 1 fail (that failure is on clean main too)name-registries.test.tsgreen, which the mapping flagged as the one most likely to break@stacksjs/tlsxerrors./buddy lintclean apart from the pre-existing untrackedstorage/framework/libs/entries/🤖 Generated with Claude Code