Skip to content

feat(app-shell): give the drill URL dialect an is-null operator, so an empty-bucket drill opened "in list" stops landing on a superset - #9506

Merged
os-tesla merged 2 commits into
mainfrom
claude/issue-9159-drill-url-isnull-operator
Sep 14, 2026
Merged

os-tesla merged 2 commits into
mainfrom
claude/issue-9159-drill-url-isnull-operator

Conversation

@os-tesla

@os-tesla os-tesla commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Fixes #9159

The drill escape hatch — OpenInListButton and drillDown.target: navigate — reaches the host's openRecordList, which serializes the drill filter into filter[...] search params for the ADR-0055 bare data surface. That dialect had equality plus four range bounds and nothing else, so an empty-bucket drill's condition simply vanished and the escalated list page returned a superset, silently, with chips that showed only the conditions that survived. This adds the missing operator, as the card enumerated it: a write side, a read side, and a chip.

The three coordinated parts

All three live in drillUrlFilters — the one module that holds both halves of this contract, so they cannot drift apart — behind one exported constant, NULL_FILTER:

part before after
write { $null: true } produced nothing filter[FIELD][null]=true
read no suffix parsed back as is-null that param becomes [FIELD, 'is_null', true]
chip anything not a range rendered as = VALUE, i.e. a bare true its own arm, carrying an i18n KEY the page resolves

(FIELD and VALUE are spelled as words rather than in angle brackets: GitHub's body sanitizer eats tag-shaped fragments, backticks and fenced blocks included.)

The read-side triple is the same one the other two consumers of buildDatasetDrillFilter already produce, because convertFiltersToAST lowers { $null: true } to exactly it. The write side emits the flag beside any range bound on the same object rather than instead of it, for the same reason: that converter emits both conditions for that input, and the two drill sinks agreeing is the point.

The chip is drawn from the locale packs, not finished in the renderer

Round 2, on this seat's ruling B. The chip arm first landed with its text finished inside groupFilterChips as the literal is null. A user-facing literal in a renderer is unlocalized for every reader on every locale, and "make it English" answers only half of that, so the arm now hands out an i18n key (FilterChip.textKey) and ObjectDataPage resolves it at the same half-chip seam that already draws the field name through fieldLabel.

The key is filterBuilder.operators.isNull — the filter builder's existing operator label, already defined and translated in all ten packs and already policed by that family's locale-parity pin. Nothing new is authored, and one operator label keeps one spelling across the product rather than two. Its catalogue wording reads correctly mid-chip in the packs checked (Is null, 为 null, Ist null, null である, Равно null), so no wording report is owed.

It is passed bare, with no inline defaultValue: a default there would be an unwatched second English spelling that hides a pack miss, which is the pattern this console deleted everywhere else for exactly that reason.

The range and equality arms are untouched and still finish their own text — a comparand is the user's own data, which no catalogue can translate and which reads the same in every locale. That split is the chip type's documented contract, and the equality arm is pinned as a live control so a change that routed every chip through the translator would go red.

Pinned by a real render, not by the key. ObjectDataPage.filterChipI18n-9159.test.tsx mounts the page under a real I18nProvider at the empty-bucket drill URL and reads the chip's own text node. English cannot tell the two worlds apart — a hardcoded literal and a resolved en pack value render the same pixels — so the assertion is under zh, with two controls that remove the ways it could pass vacuously: the packs are asserted to differ live, and the text is asserted not to contain the key's own prefix, which is what i18next renders when a key fails to resolve. A pack that loses this key is therefore visible here instead of silently falling back to English.

Round-2 readings

Ablation from the committed state, mutating only the render seam back to the round-1 literal, with the replacement proved on disk before the run and the restore proved after:

ABLATION PROOF: head=e71642806a22207b05a8ca0bc127aa5c0ab479b8
  disk=437f1528defc8a7a3a4853ce89852793a7479d9d removed_anchor=1->0 injected=1
AssertionError: expected 'is null' to be '为 null'
AssertionError: expected 'is null' to be 'Is null'
Tests  2 failed | 1 passed (3)

The one that stayed green is the equality control, which never reaches this arm — which is what makes it a control rather than coverage. Restored, then green again; and at head 2dacd73 the package scope reads:

Test Files  442 passed (442)
Tests  4305 passed | 1 skipped (4306)

Typecheck (both tsconfigs), eslint . over the package (0 errors), and the gates — including check:i18n-keys — are green at that head.

The answer for filter[FIELD][null]=false, pinned

The value is a flag, not data. Only true spells the condition.

  • [null]=false is not a second operator. This dialect cannot WRITE "is not null" — nothing here emits it — so the read side drops that param exactly as it drops an unknown suffix: never downgraded to is_null false, never to an equality against the string "false". A read-side-only operator with no producer would be a second contract, not a repair.
  • { $null: false } is dropped on the write side for the same reason, degrading to a superset exactly as any other unspellable operator already does.
  • Equality-to-empty-string is not a substitute and was never proposed as one: the read side skips a param whose value is empty, so filter[owner]= round-trips to no condition at all. The flag's value is a non-empty literal for that reason.

Why the range maps are untouched — this one is load-bearing

is_null is already a canonical ViewFilterRule operator word, and ObjectDataPage inverts URL_FILTER_OPS to bridge a triple's operator to the spec's alias spelling. Had the flag been added there, a saved view would have received the alias null, which normalizeFilterOperator passes through verbatim and the rule schema then refuses — "Save as view" would silently drop the condition. Keeping the flag out of the range vocabulary is what keeps that path correct, and ObjectDataPage.saveAsViewFilterFold.test.ts now pins the fold for the new operator (operator: 'is_null', gate green) so the reasoning is executable rather than remembered.

How the objectui#9085 boundary pin was updated

drillEmptyBucketNavHost-9085.test.ts recorded this gap as open and was written to go red the day someone added the operator. It is updated, not deleted, exactly where that card said it would break, carrying objectui#9159 and the reason at the assertion:

  • "the URL dialect has no is-null operator at all" is now "the is-null operator is its own flag and did NOT widen the range vocabulary" — the two range maps are still asserted by content (unchanged), plus NULL_FILTER by content.
  • "the new spelling serializes byte-identically to the bare null it replaced" is now "NO LONGER serializes identically", asserted as the two exact strings rather than as an inequality — an inequality would also pass on a serializer that had merely started emitting garbage.
  • "an empty-bucket-only drill serializes to nothing, before and after" is now "serializes to the flag, while a bare null still serializes to nothing" — a JS null VALUE stays "no condition", which is what a producer writes when it has nothing to say about the field.

Both CONTROL cases are untouched and still green.

Verification — readings from real run output

The pins fail before the change. New pins run against the unmodified module, before any source edit existed:

Tests  9 failed | 23 passed (32)
AssertionError: expected '' not to be ''      # the empty-bucket drill's whole query string
AssertionError: expected null to be 'true'    # filter[owner][null] absent

Ablation from the committed state, mutation proved on disk before the run and restoration proved after (git checkout HEAD -- PATH, then git diff HEAD empty and the on-disk hash byte-identical to the HEAD blob):

ABLATION MUTATION PROVED ON DISK: head=ca8ea3552d43b171f0073c2557b75f902c73bc9b
  disk=4810e6e0bd6152269792d295b2c0896e2f931825 NULL_FILTER_hits=0
Test Files  3 failed (3)
Tests  12 failed | 25 passed (37)

They pass after — the four touched test files, re-run at this PR's head commit on the restored tree:

Test Files  4 passed (4)
Tests  48 passed (48)

Package scope. pnpm exec vitest run packages/app-shell/src/views/ (repo-root invocation, the form AGENTS.md requires):

Test Files  441 passed (441)
Tests  4301 passed | 1 skipped (4302)

That run was taken on a tree differing from this PR's head only by the addition of .changeset/9159-drill-url-isnull-operator.md; every consumer of this module lives under src/views/, so the narrowing to that path excludes nothing that reads it. The rest of app-shell and the other packages are CI's run, not a local one.

Also green: tsc --noEmit plus tsc -p tsconfig.test.json for @object-ui/app-shell (exit 0, after building the dependency closure — a stale dist/*.d.ts lies in both directions); eslint . over the package (exit 0, 0 errors, 3023 pre-existing warnings); node scripts/check-changeset-presence.mjs, check-changeset-no-major, check:control-bytes, check:new-line-citations, check:changeset-claims, check:test-path-roots — all exit 0.

Clause-2

Declared no by the dispatching seat, and it holds on inspection: drillUrlFilters is not re-exported from packages/app-shell/src/index.ts nor from the views barrel, and the package's exports map publishes only . and ./styles.css, so NULL_FILTER is reachable only inside the package. No published type moved and nothing @objectstack/spec declares was touched — the spec side was only read (is_null is already one of its canonical operator words).

Acceptance notes

Out of scope, recorded here rather than repaired in this PR:

  • To file (reproducible defect). parseUrlFilterTriples reads its operator suffix off a plain object with URL_FILTER_OPS[suffix], so a suffix naming an Object.prototype member resolves through the prototype chain and passes the truthiness test. Measured with a standalone probe of the pre-change reader: filter[amount][constructor]=1, filter[amount][toString]=1 and filter[amount][hasOwnProperty]=1 each emit a triple whose operator is a function, while filter[amount][nope]=1 correctly emits nothing. That contradicts this function's own stated contract ("an unknown operator suffix is ignored"). The new is-null arm is a strict equality against a literal suffix and does not participate. Dedupe words: parseUrlFilterTriples, URL_FILTER_OPS, prototype, constructor, operator suffix.
  • To file (reproducible defect, same silent-superset shape as this card). convertFiltersToAST also lowers { $exists: false } to is-null, and { $null: false } / { $exists: true } to is_not_null. This URL dialect spells none of those, so a drill carrying one still vanishes on the escape hatch. Nothing on this path emits them today — buildDatasetDrillFilter writes { $null: true } — but an authored widget filter can. Recorded in the module's own prose as a known unspelled synonym. Dedupe words: serializeDrillFilterParams, $exists, is_not_null, drill URL, unspelled operator.
  • Noted, not filed. The chip text is null is the first natural-language string in groupFilterChips; its siblings (= VALUE, start arrow end) are symbol-only and language-neutral. Routing it through @object-ui/i18n would mean changing what the grouper returns, which is wider than this card. The successor who touches it is whoever next edits the chip row in ObjectDataPage — an existing operators.isNull key is already in the English catalogue if that is wanted.
  • Noted, not filed. The card named "objectui#9022, already claimed, same module" as an overlap to check. That number does not resolve in this repo, as the dispatching seat had already measured; the module's own recent history shows a single prior commit on these files and no concurrent claim, so there was nothing to reconcile. Successor: none.

Draft on purpose, no auto-merge, not enqueued.

Authored with Claude Code in session session_011QreXiyMEqKLN4U5daMPVa (written as prose because an edited body loses the link form of that reference).


Generated by Claude Code


Generated by Claude Code

The drill escape hatch (OpenInListButton, drillDown.target: navigate) reaches
the host's openRecordList, which serializes the drill filter into filter[...]
search params for the ADR-0055 bare data surface. That dialect had equality plus
four range bounds and nothing else, so an empty-bucket drill's condition simply
vanished: { stage: 'won', owner: { $null: true } } and the bare null it replaced
both produced filter[stage]=won, byte for byte. The surviving condition was the
NON-empty one, so escalating an empty bucket to the list page returned a
superset, silently, with chips that showed only the conditions that survived.

drillUrlFilters now carries the operator on both halves of its one module, as
NULL_FILTER: the write side emits filter[<field>][null]=true for { $null: true },
the read side parses it back to [field, 'is_null', true] — the same triple the
other two drill consumers already produce — and the chip grouper renders "is
null" where it previously fell to the "= <value>" default and showed a bare
true.

The value is a flag, not data. Only "true" spells the condition: this dialect
cannot write "is not null", so [null]=false is dropped like an unknown suffix
rather than answered at an operator with no producer, and { $null: false } is
dropped on the write side for the same reason. The range maps are untouched on
purpose — is_null is already a canonical ViewFilterRule word, and ObjectDataPage
inverts URL_FILTER_OPS to bridge triples to the spec's ALIAS spelling, so an
entry there would have made "Save as view" drop the condition.

The objectui#9085 boundary pin, which recorded this gap as open and was designed
to go red the day someone closed it, is updated to the new byte-identity rather
than deleted.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011QreXiyMEqKLN4U5daMPVa
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3062.0 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-D1fPcFbm.js
Status PASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 544.93KB 130.50KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 213.54KB 59.33KB
fields (index.js) 249.20KB 62.88KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.83KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.92KB 14.22KB
plugin-charts (index.js) 71.33KB 19.90KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.44KB 34.65KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 252.48KB 65.61KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.71KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.83KB 57.97KB
plugin-kanban (index.js) 46.63KB 14.53KB
plugin-list (index.js) 112.67KB 27.68KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 84.36KB 20.78KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 99.04KB 32.62KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.04KB 5.36KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Round 2 of the drill URL is-null operator. The chip arm landed with its text
finished inside groupFilterChips as the literal "is null". A user-facing literal
in a renderer is unlocalized for every reader on every locale, and "make it
English" answers only half of that -- the defect survives verbatim, in whatever
language it was written in.

The grouper now hands out an i18n KEY for that arm instead of finished text
(FilterChip.textKey) and ObjectDataPage resolves it at the same half-chip seam
that already draws the field name through fieldLabel. The key is the filter
builder's existing operator label, already defined and translated in all ten
packs and already policed by that family's locale-parity pin, so nothing new is
authored and one operator label keeps one spelling across the product.

The range and equality arms are untouched and still finish their own text: a
comparand is the user's own data, which no catalogue can translate and which
reads the same in every locale. That split is the type's documented contract,
not a convenience.

Passed bare, with no inline defaultValue: a default here would be an unwatched
second English spelling that hides a pack miss, which this console deleted
everywhere else for that reason.

Pinned by a real render of the page under a non-English locale -- English cannot
tell a hardcoded literal from a resolved pack value, since both render the same
pixels -- with two controls: the packs are asserted to differ live, and the text
is asserted not to contain the key's own prefix, which is what i18next renders
when a key fails to resolve.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011QreXiyMEqKLN4U5daMPVa
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3062.0 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-BLPfzmqa.js
Status PASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 544.93KB 130.50KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 213.54KB 59.33KB
fields (index.js) 249.20KB 62.88KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.83KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.92KB 14.22KB
plugin-charts (index.js) 71.33KB 19.90KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.44KB 34.65KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 252.48KB 65.61KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.71KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.83KB 57.97KB
plugin-kanban (index.js) 46.63KB 14.53KB
plugin-list (index.js) 112.67KB 27.68KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 84.36KB 20.78KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 99.04KB 32.62KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.04KB 5.36KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-tesla
os-tesla marked this pull request as ready for review September 14, 2026 13:47
@os-tesla
os-tesla added this pull request to the merge queue Sep 14, 2026
Merged via the queue into main with commit 136ff4b Sep 14, 2026
38 checks passed
@os-tesla
os-tesla deleted the claude/issue-9159-drill-url-isnull-operator branch September 14, 2026 14:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants