Merge remote-tracking branch 'origin/feat/directory-picker' into feat/workspace-directory-browser
This commit is contained in:
@@ -36,6 +36,12 @@ export interface DeferredRegistration {
|
||||
* @param name - target slot name.
|
||||
* @param component - the component whose ledger presence marks "registered".
|
||||
* @param register - performs the actual registration; returns its disposer.
|
||||
* @param onFailure - owns a registration failure that fires from a LATER
|
||||
* ledger flush (a declaration landing after two providers deferred, say):
|
||||
* the deferral first removes its own subscription, then hands the error
|
||||
* over instead of throwing through the flush — the callback's chance to
|
||||
* roll back sibling deferrals and surface the conflict on a loud channel.
|
||||
* Absent, a late failure rethrows out of the flush.
|
||||
* @returns the deferral handle (dispose in the owning effect's disposer).
|
||||
* @throws the immediate registration's failure, after removing the
|
||||
* just-installed subscription — a throwing construction leaves nothing live.
|
||||
@@ -45,6 +51,7 @@ export function deferRegistration(
|
||||
name: string,
|
||||
component: unknown,
|
||||
register: () => () => void,
|
||||
onFailure?: (error: unknown) => void,
|
||||
): DeferredRegistration {
|
||||
let dispose: (() => void) | undefined
|
||||
const tryRegister = (): void => {
|
||||
@@ -52,7 +59,15 @@ export function deferRegistration(
|
||||
if (registry.entries(name).some(e => e.component === component)) return
|
||||
dispose = register()
|
||||
}
|
||||
const unsubscribe = registry.subscribe(name, () => { tryRegister() })
|
||||
const unsubscribe = registry.subscribe(name, () => {
|
||||
try {
|
||||
tryRegister()
|
||||
} catch (error) {
|
||||
unsubscribe()
|
||||
if (onFailure === undefined) throw error
|
||||
onFailure(error)
|
||||
}
|
||||
})
|
||||
try {
|
||||
tryRegister()
|
||||
} catch (error) {
|
||||
|
||||
@@ -24,6 +24,27 @@ describe('deferRegistration', () => {
|
||||
expect(core.entries(HOLE)).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('hands a late registration failure to onFailure after unsubscribing itself', async () => {
|
||||
const core = new SlotCore()
|
||||
const component = (): null => null
|
||||
const foreign = (): null => null
|
||||
const failures: unknown[] = []
|
||||
// Nothing is declared yet: the deferral just subscribes and waits.
|
||||
const register = vi.fn(() => core.register({ name: HOLE } as never, component as never))
|
||||
deferRegistration(core, HOLE, component, register, (error) => { failures.push(error) })
|
||||
// The declaration lands with a foreign occupant racing in first: the
|
||||
// deferral's flush-time attempt fails, unsubscribes itself, and reports
|
||||
// through onFailure instead of throwing out of the flush.
|
||||
core.register({ name: 'root', children: { [HOLE]: { kind: 'single', scope: 'root' } } } as never, (() => null) as never)
|
||||
const disposeForeign = core.register({ name: HOLE } as never, foreign as never)
|
||||
await Promise.resolve()
|
||||
expect(failures.map(String).join('')).toContain('already has a registration')
|
||||
// Unsubscribed: freeing the hole must not resurrect the loser.
|
||||
disposeForeign()
|
||||
await Promise.resolve()
|
||||
expect(core.entries(HOLE)).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('drops its subscription when the immediate registration throws', async () => {
|
||||
const core = declared()
|
||||
const foreign = (): null => null
|
||||
|
||||
Reference in New Issue
Block a user