Repository navigation
build(deps): bump @antelopejs/tooling-configs to 0.5.0 - #152
Merged
Merged
Conversation
tooling-configs 0.5.0 adds antelopejs/no-unsafe-object-assign, which flagged the Object.assign over the protected stages field. Each stage now goes through the StagedObject constructor, so a staged value with no stage is refused with a 400 instead of reaching the driver. The verify journeys drop their two type assertions.
This was referenced Nov 10, 2026
Upd4ting
added a commit
that referenced
this pull request
Nov 10, 2026
…#153) ## Why The DMS database console should stay fast on schemas with one instance per tenant. The Schemas page, the Overview, the health card, the data browser's table list and the row references all read `listSchemaSummaries()`. It infers each table's columns from a 5-row sample, cached for 30 s. When a table's default instance held no row, `sampleAnyInstance` probed the named instances one query at a time until one answered rows. A table empty in every instance paid one round trip per tenant, on every cache miss. ## Change `src/service/introspect.ts`: when the default instance holds no row, read the 5-row sample once through `CROSS_INSTANCE` instead of looping over `listSchemaInstances()`. On MongoDB a cross-instance read adds no `$match`, so it is one `aggregate` with `$limit: 5`. ## Workload Two isolated DMS runs (`dms-control up`), each with its own `mongod`: A on `origin/main`, B on this branch. The playground's `links` schema has five tables, empty in every instance. Its named instances were raised to 3, 50 and 500 by rows in `__antelope_instances`. Each sample is one cold `GET /api/database/schemas/` after the 30 s caches expired. 7 samples per side, interleaved, with the order alternating each round. mongod's profiler counted the queries on `links__*` in each request. ## Evidence Confirmed in an end-to-end benchmark. | Named instances | Before (median, range) | After (median, range) | Queries on `links` | | --- | --- | --- | --- | | 3 | 39.7 ms (37.0 to 56.5) | 44.5 ms (38.1 to 97.9) | 25 → 15 | | 50 | 70.9 ms (59.9 to 128.2) | 33.7 ms (29.6 to 45.3) | 260 → 15 | | 500 | 340.5 ms (335.3 to 533.4) | 34.9 ms (30.9 to 52.6) | 1,241 → 15 | Primary number: a cold Schemas request at 500 named instances goes from 340.5 ms to 34.9 ms, 90% faster. At 3 instances the two sides overlap, so normal-size schemas see no measurable difference. - **Limiter.** Node was idle. The cost is serial round trips: 1,226 fewer queries at about 0.25 ms each comes to about 306 ms, and the measured saving is 305 ms. - **Errors.** 0 errors on both sides. The inferred fields of all 19 playground schemas match between A and B, `shop` included, whose rows live only in its `eu` and `us` instances. ## Impact One cold request every 30 s for whoever opens a Schemas, Overview or data-browser page, per schema with named instances. On a 500-tenant schema that is about 0.3 s saved each time, and 1,226 fewer queries against the database. The impact is low, but it scales with the tenant count. ## Behaviour note The sample now spans tenants instead of coming from the first tenant, in creation order, that holds rows. Both are 5-row samples. When tenants store different types in one column, or a column only some tenants fill, the inferred type can differ. One case is a column typed `number` by tenant A's rows and `string` by tenant B's. The design note (`docs/design/redesign-v2-grill.md`) says only that a named instance is sampled when the default one is empty. Keeping the exact old rule would take an ordered cross-instance read, which sorts the whole table on every cold request. ## Test `src/test/schemas-page.test.ts` adds a tenanted schema whose only row is in a named instance. The test asserts its `amount` column is typed `number`. It fails when sampling reads only the default instance: ``` 1) types the columns of a table whose rows are all in a named instance AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal: ``` It passes on this branch, and `pnpm run checks` passes (`Checks: 8 passed, 0 failed.`). ```mermaid flowchart LR S[sampleAnyInstance] --> D{default instance has rows?} D -- yes --> R[use them] D -- no, before --> L[for each named instance: one query] --> R D -- no, after --> X[one CROSS_INSTANCE slice 0..5] --> R ``` ## Readability The function loses its loop and its call to `listSchemaInstances`. The diff is 5 lines added and 9 removed. No overlap with #152, which changes `src/service/stagedSerialization.ts`. ## Verify skill `verify-dms-database` on `ae8afb3`, with the features `plan` lists. The verdict is `PASS+NOTES`. | Feature | Passed | Failed | | --- | --- | --- | | smoke | 1 | 0 | | extending | 3 | 0 | | schemas | 22 | 0 | The schemas check that a table whose rows live in named instances (`shop.order_items`) lists its sampled columns still passes. Its wording described the old one-instance rule, so `a50a4b0` rewords it.
Upd4ting
added a commit
that referenced
this pull request
Nov 10, 2026
) ## Why The DMS database console should show a query's result without freezing the page. A query with no `slice` on a table of more than 1,000 rows, such as `schemas.metrics.instance().table("readings")`, returns the console's 1,000-row cap. When the result is browsable, each row renders its "Open in data browser" link as a Nuxt UI `UButton`. Each of those buttons resolves its own variant classes and mounts a `ULink`, a `ULinkBase` and a `UIcon`. Mounting 1,000 of them took most of the render. ## Change `frontend-vue/app/components/QueryResultPanel.vue`: the row link is now a `<ULink raw>` with the classes the `UButton` resolved to, wrapping one `<UIcon>`. It keeps the same role, accessible name, title and `href`. It still navigates in-app through `ULink`, and still shows only on row hover or keyboard focus. ## Workload The production dashboard build of an isolated DMS run (`dms-control up`). Chromium is headless at 1280×900 with 4× CPU throttling (`Emulation.setCPUThrottlingRate`). It runs `schemas.metrics.instance().table("readings").slice(0, 1000)` with Ctrl+Enter, after a 1-row query, so the table mounts fresh each time. Timing runs from the keypress to the paint after 1,000 rows exist, with long tasks read from a `PerformanceObserver`. Base and change ran as two separate DMS runs: 3 rounds of 5 runs per side, interleaved. ## Evidence Confirmed in an end-to-end browser benchmark. | 1,000-row result, 4× CPU | Before (median, range, n=15) | After (median, range, n=15) | | --- | --- | --- | | Result visible | 5,255 ms (4,546 to 6,740) | 2,252 ms (2,030 to 5,893) | | Long tasks | 5,136 ms | 2,232 ms | | Longest task | 3,657 ms | 1,195 ms | Primary number: a 1,000-row result is visible after 2.25 s instead of 5.26 s, 57% faster on a mid-range laptop profile. - **Limiter.** The same 1,000 rows with `.without("_id")`, which renders no row links, show in about 1.1 s. The rest of the remaining time is the per-row `ULink` and the table cells. - **Variance.** Every round's medians are apart: before 5,488, 5,822 and 4,725 ms, after 2,979, 2,234 and 2,175 ms. - **When this was measured.** The timed commit was `ce0901f`. After review, the change added two classes to the same element (`text-xs active:scale-[0.98]`), with no new component or node. ## Impact Anyone who runs a broad query in the console. The longest frozen stretch drops from 3.7 s to 1.2 s at 4× throttling, about 0.9 s to 0.3 s on an unthrottled machine. The impact is medium. It happens on every query that hits the 1,000-row cap. ## Proof the link is unchanged The `UButton` link (base `9260525`) and this branch (`be8f22b`) were compared in a real browser on `shop.products` and `shop.customers` results: ``` rest 24×24, radius 6px, icon 14×14, font 12px, opacity 0 row hover opacity 1 hover rgb(11,17,20) on rgb(238,241,243) pressed 23.52×23.52 (scale 0.98) focus outline 3px solid, opacity 1, reached with Tab href /modules/database/data?schema=shop&table=products&match=_id%3Asku_lamp Enter reaches the data browser with no full reload (window marker kept) ``` Rows with no `_id`, and a result that is not browsable, render no link in either build. - `pnpm run checks`: `Checks: 8 passed, 0 failed.` - `pnpm test:layer`: `19 passed (19)` files, `166 passed (166)` tests. - `pnpm test:frontend`: `Verified 2 modules`. ```mermaid flowchart LR R[result row] --> B{browsable and has _id?} B -- before --> U[UButton: ULink + ULinkBase + UIcon + own variant classes] B -- after --> L[ULink raw + UIcon, fixed classes] ``` ## Readability The template names the link's classes instead of the button props `color`, `variant` and `size`. They are the classes the DMS button theme resolved to for a ghost, neutral, xs, square button, so a theme change to that button no longer reaches this link. No overlap with #152. ## Verify skill `verify-dms-database` on `be8f22b`, with the features `plan` lists: | Feature | Passed | Failed | | --- | --- | --- | | smoke | 1 | 0 | | query | 15 | 0 | | guard | 6 | 1 | `open-in-data-browser-opens-the-target-table-or-the-row` passes. It covers a result row's own link opening the data browser filtered on that row. The verdict reads `FAIL` because of one guard journey, `a-replace-on-a-selection-changes-every-row-the-dry-run-announced`. It is the known product finding in `.agents/skills/verify-dms-database/features/guard.md`: `@antelopejs/mongodb` 1.6.2 runs a selection replace as `findOneAndReplace`. The journey fails on `main` as well. It counts against this PR only because `QueryResultPanel.vue` is under the guard feature's globs. This diff does not touch the replace path.
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.
Why
Put dms-database on the latest published
@antelopejs/tooling-configs(0.5.0) and make it pass cleanly. It also clears the two notes that tooling-configs#282 turns intosync --checkfailures in the next release:skipLibCheckand a release that skipstypecheck. ANT-1760, split from ANT-1694.What changes
@antelopejs/tooling-configsgoes from^0.3.3to^0.5.0, andpnpm run syncupdatedpackage.json(prepare),.agents/setupandAGENTS.md.antelope-lock/@antelopejs/dms@0.19.1is whatpnpm testrecorded.antelopejs/no-unsafe-object-assign. It flagged theObject.assignthat wrote decoded stages over the protectedstagesfield of a staged value insrc/service/stagedSerialization.ts. The decoder now passes each stage through theStagedObjectconstructor instead.{ "__cls": "ValueProxy", "stages": [] }as the predicate of a delete or update. It now refuses that body with a 400. The newFORGED_BODIESentry insrc/test/query-console.test.tsfails on main withMissing expected rejectionand passes here.stages, the decoded value has the same prototype, own keys and stages as before. A reviewer checked this with a probe over all 9 classes.extending.mjs,parts/query-wire.mjs) became a narrowedconstand a@typeannotation. Before,anti-slop/require-safety-comment-for-type-assertionflagged them, once more through the.claude/skillssymlink.skipLibCheckis removed fromfrontend-vue/tsconfig.jsonand from the verify playground'stsconfig.build.json. Both still type-check..release-it.jsonrunspnpm typecheckfirst inbefore:init.No suppression was added.
Proof
Before: main with only the bump to 0.5.0
antelopejs-sync --check:pnpm run checks(node module-type warnings removed):After: this branch
antelopejs-sync --check:pnpm run checks:Vue jobs, run separately:
The new forged body on main's decoder (
ajs module test . -f dist/test/query-console.test.js):and on this branch:
Verify layer
dms-control plan --base origin/mainmaps the diff tosmoke,editingandpackage, with nothing unmapped. All 22 journeys passed: smoke 1/1, editing 18/18, package 3/3.flowchart LR B[query body JSON] --> G{__cls known and every stage valid?} G -- no --> R[400 StagedDecodeError] G -- yes --> S[decode each stage] S --> C["reduce: Reflect.construct(StagedObject, [stage, previous], cls)"] C --> I{instance of StagedObject?} I -- "no (zero stages)" --> R I -- yes --> D[driver]