Skip to content

Commit 4a51e77

Browse files
os-zhuangclaude
andauthored
refactor(fields,layout,plugin-detail,plugin-grid,collaboration,plugin-charts,plugin-form,providers,runner,sdui-parser)!: burn ledger batch 7 — 14 symbols stop wearing spec names (#3224)
objectui#3161, objectstack#4115 ledger batch 7 — the long tail, one or two entries per package. All ten packages leave the ledger (17 -> 3 collisions, 11 -> 1 packages; what remains is batch 8's @object-ui/types trio, untouched). Verdicts, per SYMBOL rather than per cluster: @object-ui/fields - isFileIdToken -> re-export. The local copy was character-for-character identical to the spec's function while its comment claimed to "mirror" it, so every behaviour test passed against it and only reference identity could tell the two apart (objectui#3003, re-proved on isAggregatedViewContainer in objectui#3169). The regex is a wire decision: widen it server-side while a copy here keeps the old bound and every new id reads as "not a reference". - FieldWidgetProps -> FieldWidgetComponentProps (spec's is the DECLARED widget-plugin props contract). The two disagree on the validation-message key — spec `error`, objectui `errorMessage` — filed as objectui#3222; the `[key: string]: any` that hid it is objectui#3221. @object-ui/layout - PageHeaderProps -> PageHeaderComponentProps, the name app-shell already chose for the same split in objectui#3169, reused rather than re-invented. - Page -> PageNodeRenderer. The spec's Page is the authored page DOCUMENT; this renders types' PageNodeSchema, the SDUI NODE that objectui#3074 had already renamed off PageSchema. Unregistered and `& any`: objectui#3223. @object-ui/plugin-detail - FeedFilterMode -> re-export (a hand copy of the spec enum, in a file that already imported FeedItemType from the same spec subpath). - ObjectFieldLike -> ObjectDefFieldLike. The spec's is the i18n duck type translateObject walks, and it ends in `[key: string]: any` — deriving would trade a precise three-key layout contract for a bag (objectstack#4075 from the spec's side). @object-ui/plugin-grid - ColumnSummaryConfig -> ColumnSummarySetting, derived as NonNullable<ListColumn['summary']>. The spec owns that name for the OBJECT form ALONE, so the local declaration wore it for a strictly WIDER type: code written here accepted 'sum', the same name imported from the spec rejects it. The eleven-member union beside it is the spec's ColumnSummary enum now instead of a hand copy under a comment promising lockstep, which turns the total Record<ColumnSummaryType, string> label map into the thing that reports a divergence. - isMultiValueField -> hasMultiValueShape, delegating to the spec function it used to shadow. The two had different PRECONDITIONS (the spec's requires a def with a `type`; this one is called with `objectFields?.[name]`), which is worse than a plain fork: an import swapped between the modules keeps compiling and changes behaviour only on the missing-field path. @object-ui/collaboration - RealtimeConfig -> RealtimeSubscriptionConfig (spec's is the app-level realtime declaration; this is one browser socket's dial settings). Zero shared declared keys — measured against the spec's own schema shape at runtime, because the spec declares that schema as a PASSTHROUGH object and a type-level key comparison against a bag answers nothing. @object-ui/plugin-charts - ChartConfig -> ChartContainerConfig. The one collision in this batch that had already produced ambiguity in read code: AdvancedChartImpl declared `config?: ChartConfig` meaning the shadcn per-series style map, lines from comments reading "Spec `ChartConfig.yAxis`" meaning the authored document. @object-ui/plugin-form - FormSection -> FormSectionContainer. Deliberately NOT batch 6's ListView exemption: ListViewProps.schema takes the spec-derived metadata as one prop, so that renderer restates nothing, while FormSectionContainerProps takes the same metadata FLATTENED into individual props — a rival vocabulary. `columns` stays `1|2|3|4` rather than binding the spec key, which accepts the string forms and normalises them in its own pipe; the pin fails if that changes. @object-ui/providers - Theme -> ThemePreference, derived from the spec's ThemeMode input plus the legacy 'system' spelling. Neither of the two shapes the triage note predicted: it was Theme['mode'] wearing Theme's name, i.e. a string union standing where the spec has a whole document. ThemeMode, the spec's own name for it, is a spec export too — the objectui#3169 rule again. @object-ui/runner - App -> RunnerApp (spec owns App for the authored application metadata AND the App.create() builder). A default export, so no consumer churn. @object-ui/sdui-parser - ValidationResult -> ManifestValidationResult, per the <what was validated>Validation<Error|Result> convention registered on objectstack#4115; core took SchemaNodeValidationResult in batch 4. Renames are by identifier boundary, per package, no global replace; not one string literal changed (the `form-section` CSS class, component keys, error names). Docs that named the renamed public types are updated with them (content/docs, skills/objectui). Verification: `--ledger` regenerated and set-diffed — removed is exactly these 14, added empty, @object-ui/types byte-identical. Guard green; turbo type-check 78/78 (full repo); full suite 834 files / 9746 tests passed, 0 failed; ESLint on changed files 0 errors. plugin-detail/plugin-grid/plugin-form gained a tsconfig.typetests.json chained off type-check (their test trees are still in TEST_DEBT, so nothing would have compiled the new pins — objectui#3181); collaboration gained a tsconfig.test.json with its first test file. Five mutation directions, all red: re-forking isFileIdToken byte-for-byte (guard by name+file, and the reference-identity assertion — while all 20+ behaviour assertions in file-value.test.ts stayed green); restoring the original hand-written Theme (guard); re-forking the derived ColumnSummaryType with one member dropped (the runtime spec-vocabulary test names the missing member, and the typetests pin fails); leaving a burned name in DEBT (rotting ratchet); unchaining a tsconfig.typetests.json (type-check coverage ratchet). A sixth records a known hole: renaming ThemePreference back to `Theme` while KEEPING the derivation passes the guard — derivation is derivation, batch 5's ConflictResolutionStrategy trap — and is caught only by the tripwire test, which is why every rename here has one. Claude-Session: https://claude.ai/code/session_01PRJtkgUAaVG11FsJQbvZWA Co-authored-by: Claude <noreply@anthropic.com>
1 parent c5ccbd5 commit 4a51e77

109 files changed

Lines changed: 1565 additions & 297 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
"@object-ui/collaboration": major
3+
"@object-ui/fields": major
4+
"@object-ui/layout": major
5+
"@object-ui/plugin-charts": major
6+
"@object-ui/plugin-detail": major
7+
"@object-ui/plugin-form": major
8+
"@object-ui/plugin-grid": major
9+
"@object-ui/providers": major
10+
"@object-ui/runner": major
11+
"@object-ui/sdui-parser": major
12+
---
13+
14+
Stop declaring 14 symbols across ten packages under names `@objectstack/spec`
15+
owns (objectui#3161, objectstack#4115 batch 7 — the long tail, one or two
16+
entries per package). All ten packages leave the ledger, which drops from 17
17+
collisions across 11 packages to 3 across 1.
18+
19+
**Renamed exports** — in every case the spec exports the same name for a
20+
*different* thing, so the old name was a mis-description rather than a dialect:
21+
22+
| package | was | now | what the spec's same-named export is |
23+
|:--|:--|:--|:--|
24+
| `@object-ui/fields` | `FieldWidgetProps` | `FieldWidgetComponentProps` | the DECLARED field-widget plugin props contract (a zod object; `field.type` is the `FieldType` enum, `readonly`/`required` carry defaults) |
25+
| `@object-ui/layout` | `PageHeaderProps` | `PageHeaderComponentProps` | the authored `page:header` node — a zod schema of `title`, `subtitle`, an icon NAME, `breadcrumb`, `actions: string[]` |
26+
| `@object-ui/layout` | `Page` | `PageNodeRenderer` | the authored page metadata DOCUMENT (`name`, `label`, `type`, `regions`) |
27+
| `@object-ui/plugin-detail` | `ObjectFieldLike` | `ObjectDefFieldLike` | the i18n duck type `translateObject` walks (`help`/`description`, plus `[key: string]: any`) |
28+
| `@object-ui/plugin-grid` | `ColumnSummaryConfig` | `ColumnSummarySetting` | the OBJECT form of `ListColumn.summary` **only** — the local one was the whole union, shorthand included |
29+
| `@object-ui/plugin-grid` | `isMultiValueField` | `hasMultiValueShape` | the spec's classifier, which requires a def with a `type`; the local one is called with `undefined` |
30+
| `@object-ui/collaboration` | `RealtimeConfig` | `RealtimeSubscriptionConfig` | the app's realtime DECLARATION (`enabled`, `transport`, `subscriptions[]`) |
31+
| `@object-ui/plugin-charts` | `ChartConfig` | `ChartContainerConfig` | the authored chart document (`type`, `xAxis`, `series`, `showLegend`, …) |
32+
| `@object-ui/plugin-form` | `FormSection` / `FormSectionProps` | `FormSectionContainer` / `FormSectionContainerProps` | the authored form-section metadata (`name`, `pane`, `visibleWhen`, `fields`) |
33+
| `@object-ui/providers` | `Theme` | `ThemePreference` | a whole theme DOCUMENT (`name`, `label`, `colors`, `typography`) |
34+
| `@object-ui/runner` | `App` (default export) | `RunnerApp` | the authored application metadata type **and** the `App.create()` builder |
35+
| `@object-ui/sdui-parser` | `ValidationResult` | `ManifestValidationResult` | plugin-manifest validation (`{ valid, errors?, warnings? }`), exported from both `kernel` and `contracts` |
36+
37+
`ManifestValidationResult` follows the `<what was validated>Validation<Error|Result>`
38+
convention registered on objectstack#4115 (`@object-ui/core` took
39+
`SchemaNodeValidationResult` in batch 4). `PageHeaderComponentProps` deliberately
40+
reuses the name `@object-ui/app-shell` already chose for its own header props in
41+
batch 3, so one concept does not acquire two dialect names one package apart.
42+
43+
**Now derived from the spec instead of hand-written:**
44+
45+
- `@object-ui/fields``isFileIdToken` is re-exported from
46+
`@objectstack/spec/data`. The local copy was character-for-character identical
47+
to the spec's function while its comment said it "mirrors" it, so every
48+
behaviour test passed and only reference identity could tell the two apart.
49+
The regex is a wire decision: widening it server-side while a copy here kept
50+
the old bound would make every new id read as "not a reference", and the
51+
widget would submit the legacy inline blob to a backend expecting a reference.
52+
- `@object-ui/plugin-detail``FeedFilterMode` is re-exported from
53+
`@objectstack/spec/data`, in a file that already imported the sibling
54+
`FeedItemType` from the spec.
55+
- `@object-ui/plugin-grid` — the eleven-member aggregation union is now the
56+
spec's `ColumnSummary` enum, so the total `Record<ColumnSummaryType, string>`
57+
label map turns a member the spec adds into a compile error instead of a
58+
blank footer cell. `ColumnSummarySetting` is `NonNullable<ListColumn['summary']>`,
59+
i.e. whatever forms the spec itself accepts. `hasMultiValueShape` delegates to
60+
the spec's `isMultiValueField` rather than re-deriving it from
61+
`MULTI_OPTION_TYPES` / `MULTI_CAPABLE_TYPES`.
62+
- `@object-ui/providers``ThemePreference` is the spec's `ThemeMode` union
63+
plus the one legacy `'system'` spelling this provider still honours for stored
64+
preferences, read off the schema's own `_zod` carrier so the package takes no
65+
zod dependency.
66+
67+
`@objectstack/spec` moves from `devDependencies` to `dependencies` in
68+
`@object-ui/fields` (it re-exports a runtime function) and `@object-ui/providers`
69+
(its public `.d.ts` now references the spec).

content/docs/guide/plugin-development.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,11 @@ Now any schema with `"type": "board"` will resolve to your component.
176176

177177
## Implementing a Custom Field Widget
178178

179-
Field widgets follow the `FieldWidgetProps` interface from `@object-ui/fields`.
179+
Field widgets follow the `FieldWidgetComponentProps` interface from `@object-ui/fields`.
180180

181181
```typescript
182-
// FieldWidgetProps<T> shape (from packages/fields/src/widgets/types.ts)
183-
type FieldWidgetProps<T = any> = {
182+
// FieldWidgetComponentProps<T> shape (from packages/fields/src/widgets/types.ts)
183+
type FieldWidgetComponentProps<T = any> = {
184184
value: T;
185185
onChange: (val: T) => void;
186186
field: FieldMetadata;
@@ -197,7 +197,7 @@ type FieldWidgetProps<T = any> = {
197197
// src/ColorPickerField.tsx
198198
import React from 'react';
199199
import { Input } from '@object-ui/components';
200-
import type { FieldWidgetProps } from '@object-ui/fields';
200+
import type { FieldWidgetComponentProps } from '@object-ui/fields';
201201

202202
export function ColorPickerField({
203203
value,
@@ -206,7 +206,7 @@ export function ColorPickerField({
206206
readonly,
207207
disabled,
208208
errorMessage,
209-
}: FieldWidgetProps<string>) {
209+
}: FieldWidgetComponentProps<string>) {
210210
if (readonly) {
211211
return (
212212
<div className="flex items-center gap-2">
@@ -435,4 +435,4 @@ import '@object-ui/plugin-board';
435435
- [Component Registry](./component-registry.md) — registry internals and advanced usage
436436
- [Plugins Overview](./plugins.md) — official plugin catalog
437437
- [Schema Rendering](./schema-rendering.md) — how schemas become UI
438-
- [Fields Guide](./fields.md) — built-in field widgets and `FieldWidgetProps`
438+
- [Fields Guide](./fields.md) — built-in field widgets and `FieldWidgetComponentProps`

content/docs/layout/page-header.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { PageHeader } from '@object-ui/layout';
2525
## Component Props
2626

2727
```plaintext
28-
interface PageHeaderProps {
28+
interface PageHeaderComponentProps {
2929
title: string; // Page title (required)
3030
description?: string; // Page description
3131
action?: React.ReactNode; // Action buttons or elements

examples/byo-backend-console/src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ function ObjectFormPage() {
179179

180180
/**
181181
* Modal ObjectForm with explicit `sections` — exercises the Card-wrapped
182-
* `FormSection` path (formType: 'modal'/'split'/'tabbed'/'wizard' all share
182+
* `FormSectionContainer` path (formType: 'modal'/'split'/'tabbed'/'wizard' all share
183183
* it), as opposed to `ObjectFormPage` above which renders the flat
184184
* divider-only path used by the default `formType: 'simple'` + `fieldGroups`.
185185
*/

packages/app-shell/src/utils/paramToField.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export function resolveParamWidgetType(paramType: string): string {
4444
const LOOKUP_WIDGET_TYPES = new Set(['lookup', 'master_detail']);
4545

4646
/**
47-
* Map an `ActionParamDef` to the field-metadata shape `FieldWidgetProps.field`
47+
* Map an `ActionParamDef` to the field-metadata shape `FieldWidgetComponentProps.field`
4848
* expects. Lossless for the widget-relevant config: options, `multiple`,
4949
* upload `accept`/`maxSize`, and the full lookup picker config that
5050
* `resolveActionParams()` copies from the underlying object field.

packages/collaboration/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"build": "tsc",
3131
"clean": "rm -rf dist",
3232
"test": "vitest run",
33-
"type-check": "tsc --noEmit",
33+
"type-check": "tsc --noEmit && tsc -p tsconfig.test.json",
3434
"lint": "eslint ."
3535
},
3636
"peerDependencies": {
@@ -40,6 +40,7 @@
4040
"@object-ui/types": "workspace:*"
4141
},
4242
"devDependencies": {
43+
"@objectstack/spec": "^17.0.0-rc.1",
4344
"@types/react": "19.2.17",
4445
"react": "19.2.8",
4546
"typescript": "^6.0.3",
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**
2+
* ObjectUI
3+
* Copyright (c) 2024-present ObjectStack Inc.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
/**
10+
* `@object-ui/collaboration` ↔ `@objectstack/spec` symbol-collision guard
11+
* (objectui#3161, objectstack#4115 ledger batch 7).
12+
*
13+
* `RealtimeConfig` → `RealtimeSubscriptionConfig`. Batch 6's first method rule
14+
* — read the collision per SYMBOL, never per cluster — applies here in its
15+
* "looks like a dialect, is actually a different concept" direction, the same
16+
* verdict `SecurityPolicy` got: the spec's `RealtimeConfig` is the app's
17+
* realtime DECLARATION (`enabled`, one `transport`, a list of `subscriptions`
18+
* naming record events), and this is one browser socket's dial settings. The
19+
* `Extract<…> = never` pin below is what makes "they share nothing" a checked
20+
* fact rather than a claim in a comment — and it is also the tripwire: if the
21+
* spec grows a `url` or a `channel` at top level, the two concepts have started
22+
* to converge and this symbol needs re-triage.
23+
*/
24+
25+
import { describe, it, expect } from 'vitest';
26+
import { RealtimeConfigSchema } from '@objectstack/spec/api';
27+
import type { RealtimeConfig as SpecRealtimeConfig } from '@objectstack/spec/api';
28+
29+
import type { RealtimeSubscriptionConfig } from '../useRealtimeSubscription';
30+
31+
type Assert<T extends true> = T;
32+
type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false;
33+
type Extends<A, B> = [A] extends [B] ? true : false;
34+
type IsAny<T> = 0 extends 1 & T ? true : false;
35+
type HasKey<T, K extends string> = K extends keyof T ? true : false;
36+
37+
/**
38+
* Every key of the local config, exhaustively — `Record<keyof …, true>` makes
39+
* tsc reject both a missing key and an invented one, so this list cannot drift
40+
* out of step with the interface the way a hand-written array would.
41+
*/
42+
const LOCAL_KEYS: Record<keyof RealtimeSubscriptionConfig, true> = {
43+
url: true,
44+
channel: true,
45+
autoReconnect: true,
46+
reconnectInterval: true,
47+
maxReconnectAttempts: true,
48+
authToken: true,
49+
};
50+
51+
const specDeclaredKeys = Object.keys(
52+
(RealtimeConfigSchema as unknown as { shape?: Record<string, unknown> }).shape ?? {},
53+
);
54+
55+
describe('the two configs describe different things', () => {
56+
it('reads the spec schema shape (the probe itself is not vacuous)', () => {
57+
expect(specDeclaredKeys.length).toBeGreaterThan(0);
58+
});
59+
60+
it('shares not one declared key with the spec declaration', () => {
61+
// Read off the spec's own schema rather than restated here — the disjoint
62+
// key sets ARE the argument for the rename, so they have to be measured.
63+
for (const key of Object.keys(LOCAL_KEYS)) {
64+
expect(
65+
specDeclaredKeys,
66+
`the spec's RealtimeConfig now declares '${key}' too — the two concepts are converging, ` +
67+
`re-triage this symbol instead of leaving the rename standing on a stale reason`,
68+
).not.toContain(key);
69+
}
70+
});
71+
});
72+
73+
describe('RealtimeSubscriptionConfig is not a dialect of the spec RealtimeConfig', () => {
74+
it('is pinned at compile time', () => {
75+
// Load-bearing import: if the spec retires `RealtimeConfig`, this file
76+
// stops compiling and the local name is free to come back.
77+
type _SpecIsReal = Assert<Equal<IsAny<SpecRealtimeConfig>, false>>;
78+
79+
// NOTE — why the key comparison above is a runtime one. The spec declares
80+
// `RealtimeConfigSchema` as a PASSTHROUGH object (`z.core.$loose`), so the
81+
// inferred type carries an index signature: `keyof SpecRealtimeConfig`
82+
// includes `string`, every `K extends keyof Spec` probe answers `true`, and
83+
// `Extract<keyof Local, keyof Spec>` collapses to all of `keyof Local`.
84+
// That is the objectstack#4075 mechanism on the SPEC's side — the same
85+
// reason `ObjectFieldLike` (plugin-detail, this batch) could not be derived
86+
// either. Deriving from a bag is not deriving.
87+
type _SpecIsALooseBag = Assert<Extends<string, keyof SpecRealtimeConfig>>;
88+
89+
// What survives that: assignability, which still honours the REQUIRED keys.
90+
type _NotAssignableEitherWay = Assert<Equal<Extends<RealtimeSubscriptionConfig, SpecRealtimeConfig>, false>>;
91+
type _NorTheOtherWay = Assert<Equal<Extends<SpecRealtimeConfig, RealtimeSubscriptionConfig>, false>>;
92+
93+
// And what the local one models, so the reason survives with the pin.
94+
type _LocalDialsASocket = Assert<HasKey<RealtimeSubscriptionConfig, 'reconnectInterval'>>;
95+
96+
expect(true).toBe(true);
97+
});
98+
});

packages/collaboration/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
export {
2424
useRealtimeSubscription,
25-
type RealtimeConfig,
25+
type RealtimeSubscriptionConfig,
2626
type ConnectionState,
2727
type RealtimeMessage,
2828
type RealtimeResult,

packages/collaboration/src/useRealtimeSubscription.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,22 @@
88

99
import { useState, useEffect, useCallback, useRef } from 'react';
1010

11-
/** WebSocket connection configuration */
12-
export interface RealtimeConfig {
11+
/**
12+
* WebSocket connection configuration for {@link useRealtimeSubscription}.
13+
*
14+
* Named `RealtimeSubscriptionConfig`, not `RealtimeConfig` (objectui#3161,
15+
* objectstack#4115 ledger batch 7). The spec's `RealtimeConfig`
16+
* (`@objectstack/spec/api`) is the app-level realtime DECLARATION — `enabled`,
17+
* a `transport` of `websocket | sse | polling`, and a `subscriptions[]` array
18+
* whose entries name the record events to deliver. This is one browser socket's
19+
* dial settings: which URL, which channel, how to back off when it drops. The
20+
* two share no declared key at all — measured against the spec's own schema
21+
* shape in `__tests__/spec-symbol-batch7.test.ts` rather than assumed, because
22+
* the spec declares that schema as a PASSTHROUGH object and a type-level key
23+
* comparison against a bag answers nothing. So there was no dialect to
24+
* reconcile here, only a name to hand back.
25+
*/
26+
export interface RealtimeSubscriptionConfig {
1327
/** WebSocket URL */
1428
url?: string;
1529
/** Room/channel to subscribe to */
@@ -62,7 +76,7 @@ export interface RealtimeResult<T = unknown> {
6276
* Provides automatic reconnection with exponential backoff,
6377
* message buffering, and typed message subscriptions.
6478
*/
65-
export function useRealtimeSubscription<T = unknown>(config: RealtimeConfig): RealtimeResult<T> {
79+
export function useRealtimeSubscription<T = unknown>(config: RealtimeSubscriptionConfig): RealtimeResult<T> {
6680
const {
6781
url,
6882
channel,
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
// Type-checks this package's TESTS, which `tsconfig.json` excludes.
3+
// See `packages/types/tsconfig.test.json` for why that exclusion was a hole:
4+
// the build correctly keeps tests out of `dist`, but nothing else compiled
5+
// them, so a test could assert a contract the compiler never checked.
6+
//
7+
// Added with the package's first test file (objectui#3161): its load-bearing
8+
// content is compile-time `Assert< Equal< …, … > >` pins, which vitest erases
9+
// and only `tsc` can check.
10+
"extends": "../../tsconfig.json",
11+
"compilerOptions": {
12+
"noEmit": true,
13+
// The package build emits `dist`; this project emits nothing, so it must
14+
// not inherit `composite` / `declaration` from the build config.
15+
"composite": false,
16+
"declaration": false,
17+
"jsx": "react-jsx",
18+
// Drop the root tsconfig's source-tree `paths` so `@object-ui/*` and
19+
// `@objectstack/spec` resolve through the workspace dependency's built
20+
// `.d.ts` instead of pulling sibling sources in as program inputs (TS6059).
21+
"paths": {}
22+
},
23+
"include": ["src/**/*.test.ts", "src/**/*.test.tsx"]
24+
}

0 commit comments

Comments
 (0)