Repository navigation
refactor(objectql): move the datasource-routing bodies out of engine.ts into engine-datasource-routing.ts (#25522 cut 2) - #25636
Conversation
…ts into engine-datasource-routing.ts (#25522 cut 2) Move-only. The bodies of ObjectQL#getDriver, #resolveDatasourceFromMapping and #matchPattern move unedited into engine-datasource-routing.ts and reach the class through DatasourceRoutingHost (getters for the fields, late-bound closures for resolveDatasourceBinding and matchPattern). Each method keeps its name, signature, docblock and position in the class. resolveDatasourceBinding stays: packages/spec/liveness ledgers and an ADR cite it by engine.ts#resolveDatasourceBinding. Claude-Session: https://claude.ai/code/session_017jiggF9SoTgeCHgPWuGdU9 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 19 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 7cc682b51b86125a29b06b9b51bba370a82ca5bc && git checkout 7cc682b51b86125a29b06b9b51bba370a82ca5bc
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e8de8a7ece08da5875cb50186eed3b6509a367c8 c70b6b5df402ddc1f0915ea65f0e049ec630e5eb && git checkout -B drift-repro e8de8a7ece08da5875cb50186eed3b6509a367c8 && git merge --no-ff c70b6b5df402ddc1f0915ea65f0e049ec630e5eb
node scripts/docs-audit/affected-docs.mjs --json e8de8a7ece08da5875cb50186eed3b6509a367c8
|
席位核对ACCEPT ·
Landing: Generated by Claude Code |
Part of #25522
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6348891160
Cut 2 of the
packages/objectql/src/engine.tssplit (#24586 ruling A, recorded in6270097840). The claim is6383544450. ThreeObjectQLmethod bodies move unedited into a new module,packages/objectql/src/engine-datasource-routing.ts:getDriver,resolveDatasourceFromMappingandmatchPattern, all private. #25522 stays open for the later cuts.Branch
claude/issue-25522-objectql-engine-cut-2, cut fromorigin/mainatcc10e74fe. Since thenmainhas moved two commits (#25621, #25620), and neither touchespackages/objectql/**. The branch has not been merged forward; CI builds the merge ref.Serial with the file's in-flight PRs. At start, none of the 14 open PRs touched
packages/objectql/**. I fetched each head and diffed it against its merge-base. Just before this PR was opened I measured again: none of the 16 open PRs touches it (file lists of #25633, #25632 and the moved #25626 read; the other 13 unchanged).Which part of Datasource routing moves, and why not all of it
The dispatch proposed the whole Datasource routing row less
setDatasourceMappingand the two public statics. I measured first, and it moves less than that:resolveDatasourceBindingstays. Twopackages/specliveness ledgers cite it by symbol as their evidence:packages/spec/liveness/object.json(object.datasource, limb 1) andpackages/spec/liveness/manifest.json(manifest.defaultDatasource, limb 4), both aspackages/objectql/src/engine.ts#resolveDatasourceBinding. So doesdocs/adr/0139-…md. Card ask 3 wants every path reader migrated in the same PR. This dispatch forbidspackages/spec/**anddocs/adr/**. Moving the body would leave both ledger gates green on the stub's name while the code they quote sat in another file, so it stays where they point.resolveEffectiveDatasource,resolveMappedDatasourceandisSystemLedgerObjectstay. Their bodies are 2, 1 and 4 lines, and each stub's host literal would be longer than the body it replaces. This follows the same reasoning cut 1 used forsetI18nService.vi.spyOn(engine as any, 'getDriver')inpackages/objectql/src/engine-judge-filter.test.ts, on a real engine. Every caller left inengine.tsstill callsthis.getDriver(…), and no moved body callsgetDriver, so the spy sees the same calls.resolveEffectiveDatasource, inplugin-audit/src/comment-thread-leaf.test.tsandservice-analytics/src/__tests__/effective-datasource-probe.test.ts. That method did not move.getDriverorresolveEffectiveDatasource(plugin-security, plugin-auth, service-analytics, service-storage, metadata-protocol). They replace the whole engine, so they never reachObjectQL.(engine as any).getDriver(name). They reach the stub, which keeps its name.resolveDatasourceBindingandmatchPatternthrough late-bound closures, so a double set on the instance is still the one they call.The shape (#23050 shape B, as cut 1 and #25222, #25223, #25224 and #25467 apply it)
fn.call(host, …), marked[#25522 cut 2].matchPatternreads no member, so it is called directly.DatasourceRoutingHostis new. It types each member as the class member itself (ObjectQL['drivers'],ObjectQL['resolveDatasourceBinding'], …). Each moved function types itsthisas thePickof the host over exactly the members its body reads, and each stub annotates its literal as theThisParameterTypeof the moved function. There is no cast, and no member becomes public or protected.drivers,logger,unavailableDatasourcesanddatasourceMapping. Each read happens when the body makes it, so a rule array replaced bysetDatasourceMapping, or a driver registered later, is seen as before.ObjectQLas a type only, so there is no module cycle at run time. It importsIDataDriveras a type from@objectstack/coreandDatasourceUnavailableErrorfrom./driver-connect-errors.js, the specifiersengine.tsuses. No moved body reads a static.engine.tsgains one import and keeps every import it had.DatasourceUnavailableErrorstays imported there because a{@link DatasourceUnavailableError}in a docblock that stays still names it (tscand eslint are clean). Nothing re-exports the module.The published
.d.tsdoes not changeThis is the series bar, ruling
6348891160. Base was aturbo run build --filter='@objectstack/objectql...'atcc10e74fe(14/14 tasks). Head waspnpm --filter @objectstack/objectql buildatc70b6b5dagainst the same dependency tree. The headdist/index.mjscarriesreadUnavailableDatasourcestwice, so the head build is the one compared.All six declaration files are byte-identical, and the raw diff of each is 0 lines. The util chunk name,
util-ByiLm3B6, is the same at both ends.index.d.ts9ca22071717e3f761198115fa54a143df4151a52950d826a10677420dcad16ae9ca22071717e…c15106196eb2…index.d.mts69dd1cfe3875024f434b2d7defce771ebc8c7e48a05be7c946fa6ecdbd912b4069dd1cfe3875…a2d6d7b241e8…core.d.tsd27b72bde2eec9a47c7dd3c104976321533cf397452ca9f84491a455ec28e73bd27b72bde2ee…d27b72bde2ee…core.d.mts5a0cca505296b678b31c742c285e028fe434f5467bb8d27e052e470ce0e096525a0cca505296…5a0cca505296…util-ByiLm3B6.d.tsand.d.mts1742a9cb029654d1ca4e8e392e98001eec85963987f516b1b76f672ecd08e5a91742a9cb0296…bffd1b630dab…util-ByiLm3B6.d.ts: line 8412private matchPattern;made public (matchPattern;, one line changed) gives raw = ruled-normalisede1ebae28ec516d6480a51cf8551a7eeb62498e24d2409140e0199dcf4ecc34be, which differs from1742a9cb…. The single-quote normalisation givesf715c98bfa40…, which differs frombffd1b630dab….import()and CJSrequire), are identical at base and head..has 182 exports under ESM and 182 under CJS;./corehas 54 and 54. The sorted lists hash881616cc179189098182bd6b8a95701d9922f9677b786bc12e2d31c1adb0adaaand393e806906e876c2e020aa96617d54050a35ae63cd483b67d8e968b7b5390f49at both ends.No changeset:
skip-changeset, per ruling6348891160item 4.Proof of move-only
getChildrenleaves). For each moved function, the parameters (without the addedthis), the return type and the body have the same token sequence as the base method:getDriver206,resolveDatasourceFromMapping168,matchPattern54. That is 428 tokens with 0 differences. Each body line is the base line less two leading spaces (56, 34 and 7 lines). Negative control: changing one token inresolveDatasourceFromMappingmakes the check report DIFFER.engine.ts. All 234 base statements outside the class are byte-identical at head. The one new statement is the import of the new module.engine.tsgoes from 23,881 to 23,807 lines.engine-datasource-routing.tshas 185 lines, most of them the module header and the host docblock.scripts/ablation-replace.mjs. Each restored to the HEAD blobf6252c183b12withgit diff HEADempty, and no test file was kept:matchPattern's.replace(/\*/g, '.*')changed to'Z':datasource-mapping.test.tsgoes 2 failed / 8 passed.getDriver'sunavailableDatasources.get(…)read, which goes through the getter, replaced byundefined:datasource-unavailable.test.tsgoes 4 failed. The same mutation leavesdatasource-mapping.test.tsgreen, which is noted under Acceptance notes.Readers of the moved lines
scripts/adr-anchors/packages__objectql__src__engine.ts.json's one paragraph is about the transaction seam. Its routing clause ("SYSTEM_LEDGER_LIFECYCLE_CLASSES, the same tuple ADR-0057 §3.6 routing reads") describesresolveDatasourceBindingstep 3 andisSystemLedgerObject, and both stay. No moved body reads the tuple or cites an ADR; the ADR-0057 §3.6 mention ingetDriver's docblock stays on the stub. So the new module owes no anchor entry, and none is added.check:adr-anchors: 85 anchored files at both ends; 1,871 decision-letter citations on the anchored surface at both ends; 52,481 citations across 6,594 files at both ends.pnpm --filter @objectstack/spec check:livenessexits 0 at head. 982path#symbolpointers each name a symbol the cited file contains. Key-mention anchoring asks 753 pairs: 752 anchored and 1 exempt, the same as at base.engine.tsstill namesdefaultDatasourceoutside the moved bodies.docs/adr/0062-…mdmentionsengine.ts(getDriver/registerDriverrouting) in prose, and both methods are still inengine.ts.check-adr-symbol-anchors: 2,384 anchors resolve at head.check-platform-checklist --changed-from origin/main: 1 anchored file in the change set, 14 anchors judged, 0 lost.Seam table (fresh,
origin/maincc10e74fe, 2026-10-24)engine.tsis 23,881 lines. 8,270 of them (34.6%) come from the last 30 days (git blame --since=2026-09-24T11:00Z, history covering 2026-09-15 onward). 126 commits touched it in 30 days, and 35 in 7. The file has the module preamble (lines 1–5015),class ObjectQL(5016–23206, 317 members) and a trailer. Sections are contiguous member ranges, docblocks included, with the same names and first members as cut 1's table.The columns:
this.FIELD.delete/insert/update(sites.txStore…ConstructorgetStatus…registryregisterHook…triggerHookshasHooksFor…assertBulkPerRowHookBudgetregisterAction…setActionActive_heldFileResolver…registerInsertRecomputeListenerreadableIdsSecurityReported…resolveDefaultPermissionswriteEpoch…hasObjectMiddlewareexecuteWithMiddleware…buildUserbuildDriverOptions…resolveEnginePostureprobeInstallOrganizations…transactionCoversDriverForresolveNowDefault…defaultFieldEntriesapplyAutonumbers…seedAutonumberregistrationConversionWarned…registerDriversupplyFileColumnsMovedResolver…listUnavailableDatasourcesassertWriteAllowed…publishBulkDataEventsetI18nService…objectDisplayLabelassertReferencesResolve…masterDeclaredFieldsinspectDanglingReferences…inspectCascadeDeletesetCryptoProvider…setTenancyPostureProviderencryptSecretFields…resolveInternalFieldgetSchema…judgeFilterLIFECYCLE_DATASOURCE…setDatasourceMappinginit…resolvePrimaryDatasourceobjectHasMediaField…valueShapeStrictForisFileReferencesMigrationVerified…wasDatastoreCreatedFromEmptydestroy…destroyMAX_EXPAND_DEPTH…MAX_BULK_PER_ROW_HOOK_ROWSautonumberCounters…recomputeSummariesexpandRelatedRecords…expandRelatedRecordsresolveFileReferences…readParentDerivedFilesresolveWhereTokens…requireFindOnePredicatefind…staticReadonlyCreateStripvalidate…validatereadUpdatePriorRow…insertManyupdate…updateplanCascadeAtomicity…organizationOwnedRowCountsdelete…deletecount…aggregateexecute…enforceTransactionOriginregisterObject…createAfter this cut, the Datasource routing row at head is lines 12307–12572 (266 lines).
For the next dispatch (cut 3)
init.inithas an inferred return type, so it stays. What moves ischeckDriversHealth(34-line body) andresolvePrimaryDatasource(37-line body):.d.tskeeps its bytes;this.FIELD.delete/insert/update(site, no static read;engine.ts#…path reader, and no spy or instance double on either, measured repo-wide.resolvePrimaryDatasourcereadsisSystemLedgerObjectandresolveDatasourceBinding(both late-bound closures), plus_registryanddrivers(getters).unregisterDriverand 1 inclearDatasourceUnavailable. Its other six bodies are 1–15 lines and mostly shorter than a host literal.Acceptance notes
datasource-mapping.test.ts's case "when the connect verdict is known, the error says WHY rather than 'not registered'" asserts/analytics database unreachable|ERR_DATASOURCE_UNAVAILABLE|broken/. The fallback "mapped … is not registered" message also containsbroken, so the case stays green with theunavailableDatasourcesbranch removed (mutation above).datasource-unavailable.test.tsdoes pin that branch. This predates this PR and is noted, not filed.Verification
All at head
c70b6b5d.pnpm --filter @objectstack/objectql typecheck(tsc --noEmitandcheck:test-typecheck): exit 0, 39 files / 233 errors / 64 pinned signatures held. Eslint with--no-inline-configonpackages/objectqland on both files: exit 0.packages/objectqlsuite (vitest run --project local --maxWorkers=2, underos-verify-lock, VERDICT command-exit 0): 466 files, 8,700 tests, 8,700 passed, 0 failed, 0 skipped.TypeScript Type Checkcovers the dependants.dispatch-gates --commandsderived 56 families on the real diff. All 56 were run, each exit code recorded, and--ranreconciles: 56 accounted for, 54 run, 2 NOT MEASURED. 53 exit 0. Three are NOT MEASURED locally; CI runs them:check-engine-split-ratio --days 90exit 2, CANNOT COMPUTE: history starts 2026-09-15, inside the window.check-plugin-teardown-shape --self-testexit 3: the pinned fixture commit is not in this shallow clone.check:dual-build-cjs-loadsexit 3, PREREQUISITE NOT MET: nodist/for the other packages. The export lists above answer its question for this package.check-adr-anchors,check-adr-symbol-anchors,check-nul-bytes,check-platform-checklist --changed-from origin/main, andpnpm --filter @objectstack/spec check:liveness.Generated by Claude Code