Repository navigation
refactor(rest): move registerBatchEndpoints into batch-routes.ts (#23050 slice 2) - #23642
Conversation
Slice 2 of the rest-server.ts split, by slice 1's pattern: the body of RestServer#registerBatchEndpoints moves, re-indented and otherwise unedited, to packages/rest/src/batch-routes.ts. The class keeps the private method and its docblock as a BatchRouteHost literal plus .call. Claude-Session: https://claude.ai/code/session_01FLwgbTSmGMrqsXkBDsiwfG Co-Authored-By: Claude <noreply@anthropic.com>
…tes.ts Every reader whose reading included a line of registerBatchEndpoints' body now reads it in batch-routes.ts, with its sum unchanged: the resolveExecCtx and objectQLProvider consumer censuses, the ui-view registrar spelling checks, the dogfood blind-spot census, the route-envelope ledger and one platform-checklist source anchor. Claude-Session: https://claude.ai/code/session_01FLwgbTSmGMrqsXkBDsiwfG Co-Authored-By: Claude <noreply@anthropic.com>
The host interface spells the slot once as an indexed-access TYPE, which reads nothing; that one spelling is set aside and pinned at one, so the dynamic-read check holds over both files. Claude-Session: https://claude.ai/code/session_01FLwgbTSmGMrqsXkBDsiwfG Co-Authored-By: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 34 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 11 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 18 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 5c15a83dd61146a2a95210e1b6687b2a58bd1b78 && git checkout 5c15a83dd61146a2a95210e1b6687b2a58bd1b78
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 23e689dc7d1de961a168997685295ddf009d5c2c 43903e69992fc281f2515d03c1c66bd47b1a3315 && git checkout -B drift-repro 23e689dc7d1de961a168997685295ddf009d5c2c && git merge --no-ff 43903e69992fc281f2515d03c1c66bd47b1a3315
node scripts/docs-audit/affected-docs.mjs --json 23e689dc7d1de961a168997685295ddf009d5c2c
|
✅ ACCEPT: PR #23642 at
|
Contract reviewServed-tier: Inputs: card #23050 (body and every comment, the slice 1 rulings and this slice's claim, report and ACCEPT included), PR #23642 (body, file list, net diff against ① Derived judgmentsEvery accept-set and public-surface change the diff implies, each named right or wrong.
Wrong: nothing found. ② Semver level
③ Boundary flagsEvery dev flag and every
Implemented-by: VERDICT: PASS Generated by Claude Code |
|
Seat note: the contract-review record
The seat flips the PR ready and arms auto-merge through the relay. Generated by Claude Code |
Part of #23050
Clause-②: no
Slice 2 of the
rest-server.tssplit, by slice 1's pattern (landed note6136102437, steps 1-6): the body ofRestServer#registerBatchEndpoints(the cross-object transactionalPOST {basePath}/batchand the per-objectPOST /data/:object/batch,createMany,updateMany,deleteMany) moves, unedited, to the new modulepackages/rest/src/batch-routes.ts. It is move-only. Routes, the route ledgers and the publisheddist/*.d.tsare byte-identical, and every reader whose reading included a moved line now reads it where it is.rest-server.tsgoes from 12,997 to 12,521 lines. The card stays open for the later slices.The pattern, applied
Members the moved body reads, from an AST walk of the body (base
e611e2bdaline numbers): fieldsprotocol:1959,config:1960,routeManager:1961,objectQLProvider:2012. MethodsresolveProtocol:2339,enforceAuth:2407,enforceApiAccess:2594,enforceBatchSize:2662,loadObjectItems:2797,resolveExecCtx:2807. That is 10 members. The body has no barethisand no element access onthis.Host shape (slice 1's shape B): the class keeps
private registerBatchEndpoints(basePath)with its name, docblock and position. Its body is now oneBatchRouteHostliteral andregisterBatchEndpoints.call(host, basePath). Each interface member is typedRestServer['m']. Methods go as late-bound closures,(...a) => this.m(...a).Fields, checked one by one:
configcrud,batch)routeManagerregistercall):5670/:5674) wrapsregisterMetadataEndpointsInner, not this registrarprotocolobjectQLProviderwiredEngineOrLoud(Boolean(this.objectQLProvider), ...))readonlyThe getter is measured, not argued. A one-off script boots a source
RestServerwith no provider, callsPOST /api/v1/batchwith{ operations: [] }, then assignsobjectQLProvideron the instance AFTERregisterRoutes()and calls again:501 NOT_IMPLEMENTED, then200 {"results":[]}. The door reads the live field, as it did when it readthis.objectQLProvideron the server.scripts/ablation-replace.mjs, the getter replaced byobjectQLProvider: this.objectQLProvider,):501, then501again. The value shape would freeze the field at registration. Restored: blobbee3d9b7bf56equalsHEAD,git diff HEADempty.Module-level helpers:
apiAccessDenialFromEnablewas already exported.wiredEngineOrLoudgainsexport(one word, same line). The package entry re-exports neither.ServerScopedDataRequestis imported fromdata-action-routes.ts, where slice 1 moved it; no type alias is exported fromrest-server.ts.Proof of move-only
.d.ts:packages/rest/dist/index.d.tsandindex.d.ctsare byte-identical to basee611e2bda, sha2567489de84bc6feccb7cb1f28bcbe4a97775324959c61c9bfc35e8a8c45a6f3bc9for all four files (cmpsilent), rebuilt at the final head43903e699(the mergedorigin/maintouched no file inpackages/rest). The ESM and CJS bundles both load, with 39 exports each and one sorted export-name list (sha256 prefix84008c9643d55420);packages/rest/src/index.tsis untouched. The bundle JS bytes differ, because code moved within it.e611e2bda, head43903e699): a sourceRestServeron a recording mock server, with a protocol that implements every member (so all five batch routes mount), dumped under three configs.getRoutes()(functions recorded as arity), the ordered server registration calls, and a whitespace-free source hash of every handler are byte-identical base vs head, 9 dumps of 9:ee3a764e19bd...ecde0d, calls354b948093a5...bc1a6e, handler sourcesd8a6201db1e0...e4c767(75 routes, 5 batch)eb7a21beb5de...56d66a/6b60e9f79a6b...c4c0d6/38c46865076...6c6c74(150 routes, 10 batch)d6d0e5068384...9263e6/805edda24595...befc38/c077f433ff44...515295(75 routes, 5 batch)packages/rest/src/rest-route-ledger.ts,packages/runtime/src/route-ledger.tsandpackages/rest/src/index.tsare untouched.rest-server.tslines 12355-12848 (git show e611e2bda:packages/rest/src/rest-server.ts | sed -n 12355,12848p), with one 4-space indent level removed (sed 's/^ //'), againstbatch-routes.tslines 90-583:diffexits 0 and prints nothing (494 lines).git diff --color-moved=plain --color-moved-ws=allow-indentation-change f7cd580bf HEADover the two files): 501 deleted and 508 added lines are detected as moved. The non-moved hunks:rest-server.ts(+26 / -503): the stub body (18 lines); the import of the new module plus its one-line comment; two pointer comments where imports the move left unused were (omitInternalFieldsFromWriteResponse; the four bulk request typesBatchDataRequest/CreateManyDataRequest/UpdateManyDataRequest/DeleteManyDataRequest); the unusedDroppedFieldsEventimport removed;exportonwiredEngineOrLoud.batch-routes.ts: the module docblock, the imports, theBatchRouteHostinterface and its docblock, and the function docblock and signature.Readers re-pointed (sums unchanged, no gate loosened)
scripts/check-route-envelope.mjs(a gate's target paths; see the note at the end):batch-routes.tsis discovered by the*-routes.tsconvention and declareddialectOnlywith ratchet [tracking] Envelope-position convergence line for packages/rest's flat dialect — the live ratchet owner #9559, asdata-action-routes.tsis.rest-server.tsstringError 26 to 18 and siblingCode 39 to 31; the new module carries 8 and 8, both measured by the gate. Sums 18 + 13 + 8 = 39 and 31 + 15 + 8 = 54, unchanged. The gate's output differs from basef7cd580bfonly in those lines and the counts (12 to 13 modules, 3 to 4 ratcheted).packages/rest/src/execctx-consumer-census.test.ts: reads three files; 62 sites = 48 + 9 + 5, unchanged. Mentions 81 to 85: the four new ones are this slice's seam (forwarder, two; interface member, two). There are now two forwarder frames, andsiteOfStacksteps over both. The control pins one forwarder per moved module (SITE_FILES.length - 1), so a module moved without joining the census is red.packages/rest/src/objectql-slot-consumer-census.test.ts: the batch door, the third consumer, now lives inbatch-routes.ts. The census reads both files: 3 invocation sites = 2 + 1, all 3 throughwiredEngineOrLoud, and the batch one is pinned tobatch-routes.ts. The no-alias check runs over both files. The host interface'sRestServer['objectQLProvider']is a type that reads nothing, so it is set aside before the check and pinned at exactly one. One new control: oneget objectQLProvider()inrest-server.tsper censused module, so a slot handed to an uncensused module is red.ui-view-route-identity.measurement.test.tsandui-view-route-tenancy.measurement.test.ts: the whole-file spelling checks count over the three registrar files.this.enforceAuth(48 to 49 andthis.resolveExecCtx(63 to 64 (the +1 each is the forwarder),this.enforceEnvironmentOwnership(stays 1.packages/qa/dogfood/test/authz-probe-blind-spot.census.ts: the rest-server row's population also readsbatch-routes.ts, so 70 / 19 / 51 is unchanged; the five moved routes are still RestServer's and still in the blind spot.this.routeManager.register(stays 63 (49 + 9 + 5), with a new control for the module's own 5.enforceAuth64 to 68, re-anchored: four seam occurrences, no call site added.docs/qa/platform-checklist/areas/api-backend.json: the batch-config item's sourcerest-server.ts#registerBatchEndpoints (the four per-object gates ...)now namesbatch-routes.ts#registerBatchEndpointsfor the gates, andrest-server.ts#enforceBatchSizefor the parentheticals that stay inrest-server.ts(enforceBatchSize,normalizeConfig,parseDeclaredSubConfig). Both anchors are declarations.check:platform-checklistoutput is identical base vs head.Red before the re-points: the rest suite showed
5 failed(execctx census x3, slot census x2). The dogfood census with its base text against the moved tree read population 65 against 70, andthis.routeManager.register(58 against 63.Readers examined and not re-pointed, because the moved lines contribute zero to them (measured on the moved body):
rest-server-query-number-census(0 coercions),rest-server-canonical-query-ast(0query:slots),meta-draft-read-door-census(0 draft switches),rest-server-docblock-position(0 docblocks moved), the dogfood realtime tripwires (0 mechanics, 0 route hits).auth-gate-allowlist-fault-window,execctx-authz-input-seam-reachability, runtimemeta-list-projection-parity: they slice other members.f7cd580bfvs head43903e699:check-durability-degradation-log-level,check-system-context-census,check-single-authz-resolver,check-rest-log-declared,check:liveness. Identical except one more scanned file:check-meta-type-normalized,check-nul-bytes,check-adr-anchors,check-undeclared-dep-imports(+3@objectstack/*specifiers: the new module's 4 imports less the onerest-server.tsdropped).packages__rest__src__rest-server.ts.json(ADR-0045,filterAppForUser) is unaffected.Acceptance notes
packages/specreaders, not edited (the claim keepspackages/specout):packages/spec/liveness/batch_endpoints.jsonhas fiveevidencestrings that namerest-server.ts#registerBatchEndpointsand quote a moved line (const maxBatch = batch.maxBatchSize ?? 200, and the fourif (... && this.protocol.X)mount conditions).crud_endpoints.jsondataPrefixnames it too, fordataPath.check:livenessreads them as before and its output is identical base vs head: the symbol anchor is the private method that still registers these routes, and every key-mention (maxBatchSize,enableBatchEndpoint,createMany,updateMany,deleteMany,dataPrefix) still occurs inrest-server.tsoutside the moved range. The quoted code now lives inbatch-routes.ts. Named in the report for thedomain:specseat.docs/adr/0034-...mdnamesrest-server.tsregisterBatchEndpointsin a status block. The private method is still the registrar, so the pointer still lands. It is a governed surface and is not edited here.docs/qa/platform-checklist/areas/integration-system.jsoncitesrest-server.ts#deleteManyfor the deleteMany door's parse. That anchor lands on thedeleteMany: booleanmember ofNormalizedRestServerConfig(:1244, outside the moved range), base and head.batch-routes.tsdeclares nodeleteMany, so a re-point there would not land. Not edited.rest-server.ts(plusdata-action-routes.tswhere slice 1 added it). New code written later inbatch-routes.tsis outside them. Slice 1 set the rule "re-point only readers the moved lines contribute to", and this slice follows it. One shared list of the files RestServer's registrars live in is the long-run shape; it is raised in the report, not done here.rest-server.tsbetween :323 and the moved block shift by -1, and lines after it by -477. No line citation in the repo points into the moved range (base :12351-:12849); the highest is:11108, in a release-owned CHANGELOG.ServerScopedDataRequeststays indata-action-routes.ts(slice 1's ruling A: the CRUD slice rehomes it).batch-routes.tsimports it type-only from there.Gates and tests
All at
43903e699(the slice plus one merge oforigin/mainf7cd580bf, which touched none of these files).pnpm --filter @objectstack/rest test: Test Files 285 passed (285), Tests 5845 passed / 326 skipped (6171), the same as basee611e2bda. VERDICT command-exit 0.pnpm --filter @objectstack/rest test:repo: Test Files 5 passed (5), Tests 213 passed / 2 skipped (215), the same as base. VERDICT command-exit 0.pnpm --filter @objectstack/rest typecheck: exit 0 (tsc --noEmitpluscheck:test-typecheck: 0 files / 0 errors).pnpm --filter @objectstack/dogfood exec vitest run test/authz-probe-blind-spot.test.ts test/authz-conformance.test.ts: 2 files, 108 passed. VERDICT command-exit 0.eslint --no-inline-config --format jsonoverpackages/rest,scripts/check-route-envelope.mjsand the dogfood census: 323 files, 0 errors, 0 warnings. Every touched lintable file is in the result set. Repo-widepnpm lintis CI's.dispatch-gates --commands --repo objectstack-ai/objectstack, no paths: 80 commands.--ran: 80 derived, 79 run, 1 NOT-MEASURED, 0 UNRUN. 78 exit 0.pnpm check:platform-checklistexits 1 with the same 8 findings as basef7cd580bf(none inapi-backend.json); the whole output is identical base vs head.pnpm check:dual-build-cjs-loads, reason: PREREQUISITE NOT MET (exit 3; workspace packages withoutdist/in this container). Stand-in:packages/rest/dist/index.cjsandindex.jsboth load, 39 exports each. CI runs the gate over a full build.pnpm check:route-envelopeandpnpm check:durability-log-levelare both in the derivation and both exit 0.Changeset: none.
@objectstack/restshipsfiles: ["dist", "README.md", "CHANGELOG.md"]. Itsdist/index.d.tsandindex.d.ctsare byte-identical, its export list is identical, and its route table is identical under three configs. The bundle JS differs only by where the code sits.skip-changesetis the seat's to apply on its own check.Contract review: this PR edits a gate script's target paths (
scripts/check-route-envelope.mjs, one newMODULESentry and two lowered counts onrest-server.ts), as slice 1 did.Per command (exit code):
node scripts/check-ci-filter-parity.mjsexit 0node scripts/check-closing-keyword-parity.mjsexit 0node scripts/check-closing-keyword-parity.mjs --self-testexit 0node scripts/check-comment-mask-adoption.mjsexit 0node scripts/check-comment-mask-adoption.mjs --self-testexit 0node scripts/check-comment-mask-corpus.mjsexit 0node scripts/check-declaration-mirrors.mjsexit 0node scripts/check-declaration-mirrors.mjs --self-testexit 0node scripts/check-dts-emitted.mjs --self-testexit 0node scripts/check-issue-citations.mjsexit 0node scripts/check-keyed-text-bounds.mjsexit 0node scripts/check-keyed-text-bounds.mjs --self-testexit 0node scripts/check-platform-object-tenancy-census.mjsexit 0node scripts/check-platform-object-tenancy-census.mjs --self-testexit 0node scripts/check-plugin-teardown-shape.mjsexit 0node scripts/check-plugin-teardown-shape.mjs --self-testexit 0node scripts/check-registry-log-declared.mjsexit 0node scripts/check-registry-log-declared.mjs --self-testexit 0node scripts/check-rest-log-spy-declared.mjsexit 0node scripts/check-rest-log-spy-declared.mjs --self-testexit 0node scripts/check-scripts-symbol-anchors.mjsexit 0node scripts/check-scripts-symbol-anchors.mjs --self-testexit 0node scripts/check-self-test-wired.mjsexit 0node scripts/check-self-test-wired.mjs --self-testexit 0node scripts/check-self-test-workflow-commands.mjsexit 0node scripts/check-self-test-workflow-commands.mjs --self-testexit 0node scripts/check-system-context-census.mjsexit 0node scripts/check-system-context-census.mjs --self-testexit 0node scripts/check-undeclared-dep-imports.mjsexit 0node scripts/check-undeclared-dep-imports.mjs --self-testexit 0node scripts/check-whole-set-label-write.mjsexit 0node scripts/check-whole-set-label-write.mjs --self-testexit 0node scripts/docs-audit/check-affected-docs.mjsexit 0node scripts/docs-audit/check-drift-comment.mjsexit 0node scripts/pm/bare-root-worklist.mjs --self-testexit 0pnpm --filter @objectstack/lint run check:doc-formula-expressionsexit 0pnpm --filter @objectstack/spec run check:duration-unit-keysexit 0pnpm --filter @objectstack/spec run check:empty-stateexit 0pnpm --filter @objectstack/spec run check:livenessexit 0pnpm --filter @objectstack/spec run check:strictness-ledgerexit 0pnpm --filter @objectstack/spec run check:variant-docsexit 0pnpm check:agent-test-spellingexit 0pnpm check:authz-resolverexit 0pnpm check:bash32-floorexit 0pnpm check:cli-command-idsexit 0pnpm check:cross-package-test-inputsexit 0pnpm check:dispatcher-error-vocabularyexit 0pnpm check:doc-authoringexit 0pnpm check:driver-memory-censusexit 0pnpm check:dts-closureexit 0pnpm check:dual-build-cjs-loadsexit 3pnpm check:durability-log-levelexit 0pnpm check:engine-double-contractexit 0pnpm check:entry-guardexit 0pnpm check:error-status-conformanceexit 0pnpm check:gitlink-declaredexit 0pnpm check:issue-citationsexit 0pnpm check:lean-entry-closureexit 0pnpm check:logger-receiver-detachexit 0pnpm check:nul-bytesexit 0pnpm check:objectql-double-limitexit 0pnpm check:org-identifierexit 0pnpm check:page-declaration-shapeexit 0pnpm check:parse-guardexit 0pnpm check:platform-checklistexit 1pnpm check:pm-dispatch-gatesexit 0pnpm check:pnpm-filter-targetsexit 0pnpm check:published-filesexit 0pnpm check:query-options-erasureexit 0pnpm check:ratchet-remedy-authorityexit 0pnpm check:refd-timer-probeexit 0pnpm check:route-envelopeexit 0pnpm check:slot-lookupexit 0pnpm check:sourcemap-no-sources-contentexit 0pnpm check:test-source-aliasexit 0pnpm check:tier-file-adoptionexit 0pnpm check:type-check-coverageexit 0pnpm check:type-check-debtexit 0pnpm check:watch-hint-literalexit 0pnpm check:where-matcherexit 0Generated by Claude Code