chore(lint): apply eslint auto-fixes across the .tsx backlog

Mechanical --fix output over the newly linted .tsx files (indent,
arrow-parens, comma-dangle, member-delimiter-style, unnecessary type
assertions), plus the three generic-arrow test hooks converted to
function declarations up front: the comma-dangle fixer strips the
<T,> disambiguation comma and turns them into parse errors otherwise.
This commit is contained in:
imccyu
2026-07-27 21:49:40 +08:00
parent 36e8141145
commit 49c2e85ac7
58 changed files with 479 additions and 475 deletions
+13 -13
View File
@@ -232,13 +232,13 @@ function standardKit(
kit['renderSlot'] = boundRenderSlot(host, entry)
// renderSlotChain rides the same declaration source: only entries whose
// children include a chain-kind slot receive the chain dispatch seat.
if (Object.values(entry.children).some((spec) => spec.kind === 'chain')) {
if (Object.values(entry.children).some(spec => spec.kind === 'chain')) {
kit['renderSlotChain'] = boundRenderSlotChain(host, entry)
}
// SessionProvider standard seat: entries declaring a session-scope child
// render the session area, so the framework hands them the self-wired
// provider (module-level component = stable reference; no value import).
if (Object.values(entry.children).some((spec) => spec.scope === 'session')) {
if (Object.values(entry.children).some(spec => spec.scope === 'session')) {
kit['SessionProvider'] = SessionProvider
}
}
@@ -297,7 +297,7 @@ function SlotOutlet({ slotKey, ownerProps, opts }: {
const host = useHost()
// Version tick drives entries() re-read; the host batches per microtask.
useSyncExternalStore(
(fn) => host.subscribe(slotKey, fn),
fn => host.subscribe(slotKey, fn),
() => host.getVersion(slotKey),
)
const sessionInfo = useSessionMaybeProvideInfo()
@@ -321,12 +321,12 @@ function SlotOutlet({ slotKey, ownerProps, opts }: {
spec.scope === 'session'
? <StrictSessionEntry slotKey={slotKey} entry={entry} ownerProps={owner} key={key} />
: (
<SlotErrorBoundary slotKey={slotKey} key={key}>
{spec.scope === 'session-maybe'
? <SessionMaybeEntry entry={entry} ownerProps={owner} />
: <RootEntry entry={entry} ownerProps={owner} />}
</SlotErrorBoundary>
)
<SlotErrorBoundary slotKey={slotKey} key={key}>
{spec.scope === 'session-maybe'
? <SessionMaybeEntry entry={entry} ownerProps={owner} />
: <RootEntry entry={entry} ownerProps={owner} />}
</SlotErrorBoundary>
)
)
if (spec.kind === 'single') {
@@ -335,7 +335,7 @@ function SlotOutlet({ slotKey, ownerProps, opts }: {
return guarded(entry)
}
if (spec.kind === 'keyed') {
const entry = entries.find((e) => e.options?.key === opts?.entryKey)
const entry = entries.find(e => e.options?.key === opts?.entryKey)
if (!entry) return <>{opts?.fallback ?? null}</>
return guarded(entry)
}
@@ -388,13 +388,13 @@ function SlotOutlet({ slotKey, ownerProps, opts }: {
return elected ?? <>{opts?.fallback ?? null}</>
}
// list: registration order refined by explicit order, optional id filter.
const withListOptions = entries.map((entry) => ({
const withListOptions = entries.map(entry => ({
entry,
id: entry.options?.id,
order: entry.options?.order ?? 0,
}))
let list = [...withListOptions].sort((a, b) => a.order - b.order)
if (opts?.only !== undefined) list = list.filter((item) => item.id === opts.only)
if (opts?.only !== undefined) list = list.filter(item => item.id === opts.only)
if (list.length === 0) return <>{opts?.fallback ?? null}</>
return <>{list.map((item, i) => guarded(item.entry, item.id ?? i))}</>
}
@@ -403,7 +403,7 @@ function SlotOutlet({ slotKey, ownerProps, opts }: {
function RootOutlet({ ownerProps }: { ownerProps: object }) {
const host = useHost()
useSyncExternalStore(
(fn) => host.subscribe('root', fn),
fn => host.subscribe('root', fn),
() => host.getVersion('root'),
)
const entry = host.entriesOf('root')[0]
@@ -73,11 +73,11 @@ const absentSource: HostObservable<undefined> = {
/** Bind a source that disappears with the current session to an optional selector hook. */
export function maybeObservableHook<T>(source: HostObservable<T> | undefined): MaybeSnapshotSelectorHook<T> {
if (source !== undefined) return observableHook(source)
return useAbsentSnapshot as MaybeSnapshotSelectorHook<T>
return useAbsentSnapshot
}
function useAbsentSnapshot<S>(_selector: (snapshot: never) => S, _equal?: (a: S, b: S) => boolean): S | undefined {
return observableHook(absentSource)(() => undefined)
observableHook(absentSource)(() => undefined)
}
/**
@@ -87,7 +87,7 @@ function useAbsentSnapshot<S>(_selector: (snapshot: never) => S, _equal?: (a: S,
*/
export function SessionMaybeProvider({ children }: { children: ReactNode }) {
const host = useHost()
const id = observableHook(host.sessions.current)((s) => s)
const id = observableHook(host.sessions.current)(s => s)
return (
<BindingContext.Provider value={host.sessions.maybeProvideInfo(id)}>
{children}
@@ -112,7 +112,7 @@ export interface SessionProviderProps {
*/
export function SessionProvider({ empty, children }: SessionProviderProps) {
const host = useHost()
const id = observableHook(host.sessions.current)((s) => s)
const id = observableHook(host.sessions.current)(s => s)
const info = id === undefined ? undefined : host.sessions.provideInfo(id)
if (id === undefined || info === undefined) return <>{empty?.() ?? null}</>
return (
@@ -8,7 +8,7 @@ import type { HostObservable as ObservableSnapshot, SnapshotSelectorHook } from
// Keep equality local: this suite asserts the eq parameter contract without
// adding a reverse dependency from web-react to runtime.
const shallowEqual = (a: Record<string, unknown>, b: Record<string, unknown>): boolean =>
Object.keys(a).length === Object.keys(b).length && Object.keys(a).every((k) => Object.is(a[k], b[k]))
Object.keys(a).length === Object.keys(b).length && Object.keys(a).every(k => Object.is(a[k], b[k]))
interface Snap { a: number; b: number }
@@ -51,7 +51,7 @@ describe('bindSnapshotSelector', () => {
const { source, set } = makeSource({ a: 1, b: 10 })
const useSelector = bindSnapshotSelector(source)
const probe = { renders: 0, value: undefined as number | undefined }
render(<Harness useSelector={useSelector} sel={(s) => s.a} probe={probe} />)
render(<Harness useSelector={useSelector} sel={s => s.a} probe={probe} />)
expect(probe.value).toBe(1)
const before = probe.renders
act(() => { set({ a: 1, b: 11 }) }) // unrelated field: Object.is bail
@@ -65,7 +65,7 @@ describe('bindSnapshotSelector', () => {
const { source, set } = makeSource({ a: 1, b: 10 })
const useSelector = bindSnapshotSelector(source)
const probe = { renders: 0, value: undefined as { a: number } | undefined }
render(<Harness useSelector={useSelector} sel={(s) => ({ a: s.a })} eq={shallowEqual} probe={probe} />)
render(<Harness useSelector={useSelector} sel={s => ({ a: s.a })} eq={shallowEqual} probe={probe} />)
const before = probe.renders
act(() => { set({ a: 1, b: 99 }) }) // fresh object, shallow-equal slice
expect(probe.renders).toBe(before)
@@ -78,11 +78,11 @@ describe('bindSnapshotSelector', () => {
const { source, set, stats } = makeSource({ a: 1, b: 10 })
const useSelector = bindSnapshotSelector(source)
const probe = { renders: 0, value: undefined as number | undefined }
const { rerender } = render(<Harness useSelector={useSelector} sel={(s) => s.a} probe={probe} />)
const { rerender } = render(<Harness useSelector={useSelector} sel={s => s.a} probe={probe} />)
const after = stats.subscribeCalls
rerender(<Harness useSelector={useSelector} sel={(s) => s.a} probe={probe} />)
rerender(<Harness useSelector={useSelector} sel={s => s.a} probe={probe} />)
act(() => { set({ a: 2, b: 10 }) })
rerender(<Harness useSelector={useSelector} sel={(s) => s.a} probe={probe} />)
rerender(<Harness useSelector={useSelector} sel={s => s.a} probe={probe} />)
expect(stats.subscribeCalls).toBe(after)
})
@@ -92,7 +92,7 @@ describe('bindSnapshotSelector', () => {
const probe = { renders: 0, value: undefined as number | undefined }
const view = render(
<StrictMode>
<Harness useSelector={useSelector} sel={(s) => s.a} probe={probe} />
<Harness useSelector={useSelector} sel={s => s.a} probe={probe} />
</StrictMode>,
)
expect(probe.value).toBe(1)
@@ -112,7 +112,7 @@ describe('bindSnapshotSelector', () => {
}
const useSelector = bindSnapshotSelector(new MethodSource())
const probe = { renders: 0, value: undefined as number | undefined }
render(<Harness useSelector={useSelector} sel={(s) => s.a} probe={probe} />)
render(<Harness useSelector={useSelector} sel={s => s.a} probe={probe} />)
expect(probe.value).toBe(7)
})
@@ -28,10 +28,10 @@ type FrameSlots = PropsRenderSlots<'spec.single' | 'spec.list'>
function hostOver(core: SlotCore): SlotRendererHost {
return {
subscribe: (key, fn) => core.subscribe(key, fn),
getVersion: (key) => core.getVersion(key),
entriesOf: (key) => core.entries(key),
specOf: (key) => core.specDynamic(key),
isLive: (entry) => core.isLive(entry),
getVersion: key => core.getVersion(key),
entriesOf: key => core.entries(key),
specOf: key => core.specDynamic(key),
isLive: entry => core.isLive(entry),
storeOf: () => undefined,
sessions: {
list: { getSnapshot: () => ({}), subscribe: () => () => {} },
@@ -61,7 +61,7 @@ function mountFrame(core: SlotCore, body: (renderSlot: FrameSlots['renderSlot'])
describe('createSlotRenderer over the real SlotCore', () => {
it('renders registrations live through real microtask batching: register, dispose back to fallback', async () => {
const core = new SlotCore()
const { view } = mountFrame(core, (renderSlot) =>
const { view } = mountFrame(core, renderSlot =>
renderSlot('spec.single', {}, { fallback: <i>none</i> }))
expect(view.container.textContent).toBe('none')
let dispose = () => {}
@@ -78,7 +78,7 @@ describe('createSlotRenderer over the real SlotCore', () => {
const core = new SlotCore()
const notified = vi.fn()
core.subscribe('spec.list', notified)
const { view } = mountFrame(core, (renderSlot) => renderSlot('spec.list', {}))
const { view } = mountFrame(core, renderSlot => renderSlot('spec.list', {}))
await act(async () => {
core.register({ name: 'spec.list', id: 'two', order: 2 }, () => <span>2</span>)
core.register({ name: 'spec.list', id: 'one', order: 1 }, () => <span>1</span>)
@@ -96,10 +96,10 @@ function makeHost() {
subs.set(key, set)
return () => { set.delete(fn) }
},
getVersion: (key) => versions.get(key) ?? 0,
entriesOf: (key) => entries.get(key) ?? [],
specOf: (key) => specs.get(key),
isLive: (entry) => live.has(entry),
getVersion: key => versions.get(key) ?? 0,
entriesOf: key => entries.get(key) ?? [],
specOf: key => specs.get(key),
isLive: entry => live.has(entry),
storeOf: (entry, scopeKey) => {
if (entry.store === undefined) return undefined
let perScope = storeCache.get(entry)
@@ -121,8 +121,8 @@ function makeHost() {
sessions: {
list,
current,
provideInfo: (id) => infos.get(id),
maybeProvideInfo: (id) => (id === undefined ? undefined : infos.get(id))
provideInfo: id => infos.get(id),
maybeProvideInfo: id => (id === undefined ? undefined : infos.get(id))
?? { sessionId: undefined, hooks: {}, props: {} },
},
workspaces: { list: workspaces },
@@ -145,7 +145,7 @@ function makeHost() {
live.add(entry)
bump(key)
return () => {
entries.set(key, (entries.get(key) ?? []).filter((e) => e !== entry))
entries.set(key, (entries.get(key) ?? []).filter(e => e !== entry))
live.delete(entry)
bump(key)
}
@@ -187,7 +187,7 @@ const chainEntryOf = (partial: {
priority?: number
}): Omit<StoredEntry, 'options'> & { options?: StoredEntry['options'] } => ({
component: partial.component,
select: partial.select as StoredEntry['select'],
select: partial.select,
...(partial.priority !== undefined ? { options: { priority: partial.priority } } : {}),
})
@@ -230,7 +230,7 @@ describe('child outlets and the renderSlot binding', () => {
const h = makeHost()
h.declare('k.single', SINGLE_ROOT)
const { view } = mountRoot(h, { 'k.single': SINGLE_ROOT },
(renderSlot) => renderSlot('k.single', {}, { fallback: <i>none</i> }))
renderSlot => renderSlot('k.single', {}, { fallback: <i>none</i> }))
expect(view.container.textContent).toBe('none')
let dispose = () => {}
act(() => { dispose = h.add('k.single', { component: () => <b>SB</b> }) })
@@ -242,7 +242,7 @@ describe('child outlets and the renderSlot binding', () => {
it('renders an undeclared key as empty (declaring entry unloaded = natural blank, not a crash)', () => {
const h = makeHost()
const { view } = mountRoot(h, { 'k.single': SINGLE_ROOT },
(renderSlot) => <main>{renderSlot('k.single', {}, { fallback: <i>fb</i> })}</main>)
renderSlot => <main>{renderSlot('k.single', {}, { fallback: <i>fb</i> })}</main>)
// Declared by children (authorization) but absent from the ledger (specOf
// undefined): the outlet renders nothing, not even the fallback path's spec dispatch.
expect(view.container.querySelector('main')!.textContent).toBe('')
@@ -256,7 +256,7 @@ describe('child outlets and the renderSlot binding', () => {
h.add('k.list', { component: () => <span>a</span>, options: { id: 'a', order: 1 } })
h.add('k.keyed', { component: () => <span>goal</span>, options: { key: 'goal' } })
const children = { 'k.list': { kind: 'list', scope: 'root' } as DeclaredSpec, 'k.keyed': { kind: 'keyed', scope: 'root' } as DeclaredSpec }
const { view } = mountRoot(h, children, (renderSlot) => <>
const { view } = mountRoot(h, children, renderSlot => <>
<main>{renderSlot('k.list', {})}</main>
<aside>{renderSlot('k.list', {}, { only: 'b' })}</aside>
<nav>{renderSlot('k.keyed', {}, { entryKey: 'goal' })}</nav>
@@ -291,7 +291,7 @@ describe('child outlets and the renderSlot binding', () => {
h.add('k.list', { component: () => <span>alive</span>, options: { id: 'ok', order: 2 } })
const spy = vi.spyOn(console, 'error').mockImplementation(() => {})
const { view } = mountRoot(h, { 'k.list': { kind: 'list', scope: 'root' } },
(renderSlot) => renderSlot('k.list', {}))
renderSlot => renderSlot('k.list', {}))
spy.mockRestore()
expect(view.container.textContent).toBe('alive')
expect(view.container.querySelector('[data-slot-error]')).not.toBeNull()
@@ -309,10 +309,10 @@ describe('chain outlets and the renderSlotChain binding', () => {
}))
h.add('k.chain', chainEntryOf({
component: ({ matched }: { matched?: { label: string } }) => <b>{matched?.label}</b>,
select: (owner) => ({ label: `hit:${(owner as { tag: string }).tag}` }),
select: owner => ({ label: `hit:${(owner as { tag: string }).tag}` }),
}))
const { view } = mountChainRoot(h, { 'k.chain': CHAIN_ROOT },
(renderSlotChain) => renderSlotChain('k.chain', { tag: 'T' }))
renderSlotChain => renderSlotChain('k.chain', { tag: 'T' }))
// The declining entry never mounts: the routing decision is select-layer only.
expect(view.container.textContent).toBe('hit:T')
expect(declinerBody).not.toHaveBeenCalled()
@@ -327,10 +327,10 @@ describe('chain outlets and the renderSlotChain binding', () => {
}))
h.add('k.chain', chainEntryOf({
component: ({ matched }: { matched?: string }) => <b>{matched}</b>,
select: (owner) => (owner as { pick?: string }).pick ?? null,
select: owner => (owner as { pick?: string }).pick ?? null,
}))
const spy = vi.spyOn(console, 'error').mockImplementation(() => {})
const { view } = mountChainRoot(h, { 'k.chain': CHAIN_ROOT }, (renderSlotChain) => <>
const { view } = mountChainRoot(h, { 'k.chain': CHAIN_ROOT }, renderSlotChain => <>
<main>{renderSlotChain('k.chain', { pick: 'OK' })}</main>
<aside>{renderSlotChain('k.chain', {}, { fallback: <i>fb</i> })}</aside>
</>)
@@ -347,16 +347,16 @@ describe('chain outlets and the renderSlotChain binding', () => {
h.declare('k.chain', CHAIN_ROOT)
h.add('k.chain', chainEntryOf({
component: () => { throw new Error('entry A boom') },
select: (owner) => (owner as { pick?: string }).pick === 'A' ? {} : null,
select: owner => (owner as { pick?: string }).pick === 'A' ? {} : null,
}))
h.add('k.chain', chainEntryOf({
component: () => <b>B-ok</b>,
select: (owner) => (owner as { pick?: string }).pick === 'B' ? {} : null,
select: owner => (owner as { pick?: string }).pick === 'B' ? {} : null,
}))
let pick = 'A'
const spy = vi.spyOn(console, 'error').mockImplementation(() => {})
const { view } = mountChainRoot(h, { 'k.chain': CHAIN_ROOT },
(renderSlotChain) => renderSlotChain('k.chain', { pick }))
renderSlotChain => renderSlotChain('k.chain', { pick }))
spy.mockRestore()
expect(view.container.querySelector('[data-slot-error]')).not.toBeNull()
// Re-elect entry B: the entry-keyed boundary remounts fresh instead of
@@ -372,9 +372,9 @@ describe('chain outlets and the renderSlotChain binding', () => {
h.declare('k.chain', CHAIN_ROOT)
h.add('k.chain', chainEntryOf({
component: ({ matched }: { matched?: string }) => <b>{matched}</b>,
select: (owner) => (owner as { pick?: string }).pick ?? null,
select: owner => (owner as { pick?: string }).pick ?? null,
}))
const { view } = mountChainRoot(h, { 'k.chain': CHAIN_ROOT }, (renderSlotChain) => <>
const { view } = mountChainRoot(h, { 'k.chain': CHAIN_ROOT }, renderSlotChain => <>
<main>{renderSlotChain('k.chain', {}, { fallback: <i>bar</i> })}</main>
<aside>{renderSlotChain('k.chain', { pick: 'P' }, { fallback: <i>bar</i> })}</aside>
</>)
@@ -387,7 +387,7 @@ describe('chain outlets and the renderSlotChain binding', () => {
const h = makeHost()
h.declare('k.chain', CHAIN_ROOT)
const { view } = mountChainRoot(h, { 'k.chain': CHAIN_ROOT },
(renderSlotChain) => renderSlotChain('k.chain', {}, { fallback: <i>none</i> }))
renderSlotChain => renderSlotChain('k.chain', {}, { fallback: <i>none</i> }))
expect(view.container.textContent).toBe('none')
let dispose = () => {}
act(() => {
@@ -422,7 +422,7 @@ describe('chain outlets and the renderSlotChain binding', () => {
priority: 1,
}))
const { view } = mountChainRoot(h, { 'k.chain': CHAIN_ROOT },
(renderSlotChain) => renderSlotChain('k.chain', {}))
renderSlotChain => renderSlotChain('k.chain', {}))
expect(view.container.textContent).toBe('early')
})
@@ -490,13 +490,13 @@ describe('overlay chains (ChainRenderOpts.overlay)', () => {
h.declare('k.chain', CHAIN_ROOT)
h.add('k.chain', chainEntryOf({
component: () => <b>TAKEOVER</b>,
select: (owner) => (owner as { take?: boolean }).take ? {} : null,
select: owner => (owner as { take?: boolean }).take ? {} : null,
}))
const mounted = vi.fn()
const Probe = fallbackProbe(mounted)
let take = false
const { view } = mountChainRoot(h, { 'k.chain': CHAIN_ROOT },
(renderSlotChain) => renderSlotChain('k.chain', { take }, { fallback: <Probe />, overlay: true }))
renderSlotChain => renderSlotChain('k.chain', { take }, { fallback: <Probe />, overlay: true }))
const wrapper = () => view.container.querySelector<HTMLElement>('[data-chain-overlay-fallback="k.chain"]')!
const input = () => view.container.querySelector<HTMLInputElement>('input[aria-label="probe"]')!
@@ -525,13 +525,13 @@ describe('overlay chains (ChainRenderOpts.overlay)', () => {
h.declare('k.chain', CHAIN_ROOT)
h.add('k.chain', chainEntryOf({
component: () => <b>TAKEOVER</b>,
select: (owner) => (owner as { take?: boolean }).take ? {} : null,
select: owner => (owner as { take?: boolean }).take ? {} : null,
}))
const mounted = vi.fn()
const Probe = fallbackProbe(mounted)
let take = false
const { view } = mountChainRoot(h, { 'k.chain': CHAIN_ROOT },
(renderSlotChain) => renderSlotChain('k.chain', { take }, { fallback: <Probe /> }))
renderSlotChain => renderSlotChain('k.chain', { take }, { fallback: <Probe /> }))
fireEvent.change(view.container.querySelector('input[aria-label="probe"]')!, { target: { value: 'gone' } })
expect(view.container.querySelector('[data-chain-overlay-fallback]')).toBeNull()
@@ -561,7 +561,7 @@ describe('overlay chains (ChainRenderOpts.overlay)', () => {
priority: 2,
}))
const { view } = mountChainRoot(h, { 'k.chain': CHAIN_ROOT },
(renderSlotChain) => renderSlotChain('k.chain', {}, { fallback: <i>resident</i>, overlay: true }))
renderSlotChain => renderSlotChain('k.chain', {}, { fallback: <i>resident</i>, overlay: true }))
expect(view.container.textContent).toContain('ELECTED')
expect(spy.mock.calls.some(([msg]) => String(msg).includes('chain selector crashed'))).toBe(true)
spy.mockRestore()
@@ -578,9 +578,9 @@ describe('standard-kit synthesis', () => {
h.declare('k.single', SINGLE_ROOT)
h.add('k.single', {
component: ({ useSessions }: { useSessions: <S>(sel: (s: { ids: string[] }) => S) => S }) =>
<b>{useSessions((s) => s.ids.length)}</b>,
<b>{useSessions(s => s.ids.length)}</b>,
})
const { view } = mountRoot(h, { 'k.single': SINGLE_ROOT }, (renderSlot) => renderSlot('k.single', {}))
const { view } = mountRoot(h, { 'k.single': SINGLE_ROOT }, renderSlot => renderSlot('k.single', {}))
expect(view.container.textContent).toBe('0')
act(() => { h.list.set({ ids: ['a', 'b'] }) })
expect(view.container.textContent).toBe('2')
@@ -591,9 +591,9 @@ describe('standard-kit synthesis', () => {
h.declare('k.single', SINGLE_ROOT)
h.add('k.single', {
component: ({ useWorkspaces }: { useWorkspaces: <S>(sel: (s: { ids: string[] }) => S) => S }) =>
<b>{useWorkspaces((s) => s.ids.length)}</b>,
<b>{useWorkspaces(s => s.ids.length)}</b>,
})
const { view } = mountRoot(h, { 'k.single': SINGLE_ROOT }, (renderSlot) => renderSlot('k.single', {}))
const { view } = mountRoot(h, { 'k.single': SINGLE_ROOT }, renderSlot => renderSlot('k.single', {}))
expect(view.container.textContent).toBe('0')
act(() => { h.workspaces.set({ ids: ['w1'] }) })
expect(view.container.textContent).toBe('1')
@@ -606,11 +606,11 @@ describe('standard-kit synthesis', () => {
const seen: AnyProps[] = []
h.add('k.session', {
component: (props: { useSession?: <S>(sel: (s: { sid: string }) => S) => S; sessionId?: string }) => {
seen.push({ ...props, read: props.useSession!((s) => s.sid) })
seen.push({ ...props, read: props.useSession!(s => s.sid) })
return null
},
})
mountRoot(h, { 'k.session': SINGLE_SESSION }, (renderSlot) => (
mountRoot(h, { 'k.session': SINGLE_SESSION }, renderSlot => (
<SessionProvider empty={() => <i>empty</i>}>
{() => renderSlot('k.session', {})}
</SessionProvider>
@@ -669,14 +669,14 @@ describe('standard-kit synthesis', () => {
h.declare('k.session', SINGLE_SESSION)
h.add('k.session', { component: () => <b>x</b> })
const { view } = mountRoot(h, { 'k.session': SINGLE_SESSION },
(renderSlot) => renderSlot('k.session', {}))
renderSlot => renderSlot('k.session', {}))
expect(view.container.querySelector('b')).toBeNull()
})
it('delivers the store pair for store-declaring entries and writes through baked actions', () => {
const h = makeHost()
h.declare('k.single', SINGLE_ROOT)
const handle = miniStore(() => ({ n: 0 }), { inc: (s) => ({ n: s.n + 1 }) })
const handle = miniStore(() => ({ n: 0 }), { inc: s => ({ n: s.n + 1 }) })
let bump = () => {}
h.add('k.single', {
component: ({ useStore, actions }: {
@@ -684,11 +684,11 @@ describe('standard-kit synthesis', () => {
actions: { inc: () => void }
}) => {
bump = actions.inc
return <b>{useStore((s) => s.n)}</b>
return <b>{useStore(s => s.n)}</b>
},
store: handle,
})
const { view } = mountRoot(h, { 'k.single': SINGLE_ROOT }, (renderSlot) => renderSlot('k.single', {}))
const { view } = mountRoot(h, { 'k.single': SINGLE_ROOT }, renderSlot => renderSlot('k.single', {}))
expect(view.container.textContent).toBe('0')
act(() => { bump() })
expect(view.container.textContent).toBe('1')
@@ -707,11 +707,11 @@ describe('standard-kit synthesis', () => {
actions: { setDraft: (text: string) => void }
}) => {
setDraft = actions.setDraft
return <b>{useStore((s) => s.draft) || '(blank)'}</b>
return <b>{useStore(s => s.draft) || '(blank)'}</b>
},
store: handle,
})
const { view } = mountRoot(h, { 'k.session': SINGLE_SESSION }, (renderSlot) => (
const { view } = mountRoot(h, { 'k.session': SINGLE_SESSION }, renderSlot => (
<SessionProvider>{() => renderSlot('k.session', {})}</SessionProvider>
))
act(() => { h.current.set('s1') })
@@ -730,7 +730,7 @@ describe('inject: execution point, parameter derivation, cache granularity', ()
h.declare('k.single', SINGLE_ROOT)
const inject = vi.fn(() => ({ tag: 'FROM-INJECT' }))
h.add('k.single', { component: ({ tag }: { tag?: string }) => <b>{tag}</b>, inject })
const { view } = mountRoot(h, { 'k.single': SINGLE_ROOT }, (renderSlot) => renderSlot('k.single', {}))
const { view } = mountRoot(h, { 'k.single': SINGLE_ROOT }, renderSlot => renderSlot('k.single', {}))
expect(view.container.textContent).toBe('FROM-INJECT')
act(() => { h.add('k.single', { component: () => null }) }) // sibling bump re-renders the outlet
expect(inject).toHaveBeenCalledTimes(1)
@@ -745,9 +745,9 @@ describe('inject: execution point, parameter derivation, cache granularity', ()
const inject = vi.fn((sessionId: string) => ({ sid: sessionId }))
h.add('k.session', {
component: ({ sid }: { sid?: string }) => <b>{sid}</b>,
inject: inject as unknown as StoredEntry['inject'],
inject: inject,
})
const { view } = mountRoot(h, { 'k.session': SINGLE_SESSION }, (renderSlot) => (
const { view } = mountRoot(h, { 'k.session': SINGLE_SESSION }, renderSlot => (
<SessionProvider>{() => renderSlot('k.session', {})}</SessionProvider>
))
act(() => { h.current.set('s1') })
@@ -767,22 +767,22 @@ describe('inject: execution point, parameter derivation, cache granularity', ()
h.declare('k.single', SINGLE_ROOT)
h.declare('k.session', SINGLE_SESSION)
h.addSession('s1')
const handle = miniStore(() => ({ n: 0 }), { inc: (s) => ({ n: s.n + 1 }) })
const handle = miniStore(() => ({ n: 0 }), { inc: s => ({ n: s.n + 1 }) })
const rootInject = vi.fn((actions: { inc: () => void }) => ({ viaRoot: actions }))
const sessionInject = vi.fn((sessionId: string, actions: { inc: () => void }) => ({ sid: sessionId, viaSession: actions }))
const seenRoot: AnyProps[] = []
const seenSession: AnyProps[] = []
h.add('k.single', {
component: (props: object) => { seenRoot.push(props as AnyProps); return null },
inject: rootInject as unknown as StoredEntry['inject'],
inject: rootInject,
store: handle,
})
h.add('k.session', {
component: (props: object) => { seenSession.push(props as AnyProps); return null },
inject: sessionInject as unknown as StoredEntry['inject'],
inject: sessionInject,
store: handle,
})
mountRoot(h, { 'k.single': SINGLE_ROOT, 'k.session': SINGLE_SESSION }, (renderSlot) => <>
mountRoot(h, { 'k.single': SINGLE_ROOT, 'k.session': SINGLE_SESSION }, renderSlot => <>
{renderSlot('k.single', {})}
<SessionProvider>{() => renderSlot('k.session', {})}</SessionProvider>
</>)
@@ -807,7 +807,7 @@ describe('inject: execution point, parameter derivation, cache granularity', ()
h.add('k.list', { component: () => <span>alive</span>, options: { id: 'ok', order: 2 } })
const spy = vi.spyOn(console, 'error').mockImplementation(() => {})
const { view } = mountRoot(h, { 'k.list': { kind: 'list', scope: 'root' } },
(renderSlot) => <main>{renderSlot('k.list', {})}</main>)
renderSlot => <main>{renderSlot('k.list', {})}</main>)
spy.mockRestore()
// The failing entry blacks out alone; the sibling and the tree above survive.
expect(view.container.querySelector('main')).not.toBeNull()
@@ -824,7 +824,7 @@ describe('inject: execution point, parameter derivation, cache granularity', ()
inject: () => ({ fromInject: 'inject', shared: 'inject' }),
})
mountRoot(h, { 'k.single': SINGLE_ROOT },
(renderSlot) => renderSlot('k.single', { owner: 'owner', shared: 'owner' }))
renderSlot => renderSlot('k.single', { owner: 'owner', shared: 'owner' }))
const props = seen.at(-1)!
expect(typeof props['useSessions']).toBe('function') // kit always present
expect(typeof props['useWorkspaces']).toBe('function')
@@ -43,15 +43,15 @@ function makeHost(bodies: { root: (rp: (key: string, owner: object) => React.Rea
const host: SlotRendererHost = {
subscribe: () => () => {},
getVersion: () => 0,
entriesOf: (key) => key === 'root' ? [rootEntry] : sessionEntries,
specOf: (key) => key === 'k.session' ? { kind: 'single', scope: 'session' } : undefined,
entriesOf: key => key === 'root' ? [rootEntry] : sessionEntries,
specOf: key => key === 'k.session' ? { kind: 'single', scope: 'session' } : undefined,
isLive: () => true,
storeOf: () => undefined,
sessions: {
list: observable<unknown>({ ids: [] }),
current,
provideInfo: (id) => infos.get(id),
maybeProvideInfo: (id) => (id === undefined ? undefined : infos.get(id))
provideInfo: id => infos.get(id),
maybeProvideInfo: id => (id === undefined ? undefined : infos.get(id))
?? { sessionId: undefined, hooks: { session: undefined }, props: {} },
},
workspaces: { list: observable<unknown>({ items: [] }) },
@@ -78,7 +78,7 @@ describe('SessionProvider', () => {
const h = makeHost({
root: () => (
<SessionProvider empty={() => <span>empty</span>}>
{(id) => <div data-testid="body">{id}</div>}
{id => <div data-testid="body">{id}</div>}
</SessionProvider>
),
})
@@ -93,7 +93,7 @@ describe('SessionProvider', () => {
it('renders null empty state when the empty prop is omitted', () => {
const h = makeHost({
root: () => <SessionProvider>{(id) => <b>{id}</b>}</SessionProvider>,
root: () => <SessionProvider>{id => <b>{id}</b>}</SessionProvider>,
})
const view = render(<>{createSlotRenderer().renderRoot(h.host, {})}</>)
expect(view.container.textContent).toBe('')
@@ -110,7 +110,7 @@ describe('SessionProvider', () => {
return <div>{id}</div>
}
const h = makeHost({
root: () => <SessionProvider>{(id) => <Body id={id} />}</SessionProvider>,
root: () => <SessionProvider>{id => <Body id={id} />}</SessionProvider>,
})
h.addSession('s1')
h.addSession('s2')
@@ -127,7 +127,7 @@ describe('SessionProvider', () => {
it('delivers the resolved cell to session slots under it (observable behavior, not context internals)', () => {
const seen: Record<string, unknown>[] = []
const h = makeHost({
root: (renderSlot) => <SessionProvider>{() => renderSlot('k.session', {})}</SessionProvider>,
root: renderSlot => <SessionProvider>{() => renderSlot('k.session', {})}</SessionProvider>,
})
h.addSession('s1')
h.addSession('s2')
@@ -135,7 +135,7 @@ describe('SessionProvider', () => {
component: (props: { useSession?: <S>(sel: (s: { sid: string }) => S) => S; sessionId?: string }) => {
// The bound hook reads the cell's bare source — asserting through it
// proves the machinery wired THIS session's source, not another's.
seen.push({ sessionId: props.sessionId, read: props.useSession!((s) => s.sid) })
seen.push({ sessionId: props.sessionId, read: props.useSession!(s => s.sid) })
return null
},
options: {},
@@ -152,7 +152,7 @@ describe('SessionProvider', () => {
it('fails loud when mounted outside the renderer tree (no host channel)', () => {
const spy = vi.spyOn(console, 'error').mockImplementation(() => {})
expect(() => render(
<SessionProvider>{(id) => <b>{id}</b>}</SessionProvider>,
<SessionProvider>{id => <b>{id}</b>}</SessionProvider>,
)).toThrow(/outside the installed renderer tree/)
spy.mockRestore()
})
@@ -32,10 +32,10 @@ function makeHost() {
subs.set(key, set)
return () => { set.delete(fn) }
},
getVersion: (key) => versions.get(key) ?? 0,
entriesOf: (key) => entries.get(key) ?? [],
getVersion: key => versions.get(key) ?? 0,
entriesOf: key => entries.get(key) ?? [],
specOf: () => ({ kind: 'single', scope: 'root' }),
isLive: (entry) => live.has(entry),
isLive: entry => live.has(entry),
storeOf: () => undefined,
sessions: {
list: { getSnapshot: () => ({}), subscribe: () => () => {} },
@@ -54,7 +54,7 @@ function makeHost() {
live.add(entry)
bump(key)
return () => {
entries.set(key, (entries.get(key) ?? []).filter((e) => e !== entry))
entries.set(key, (entries.get(key) ?? []).filter(e => e !== entry))
live.delete(entry)
bump(key)
}