feat(client): inject slot declaration lifetimes

This commit is contained in:
imccyu
2026-08-05 23:38:29 +08:00
parent bb53e25ed0
commit 86965b053c
100 changed files with 1065 additions and 837 deletions
@@ -56,7 +56,16 @@ describe('directory-picker-native client half', () => {
for (const hole of HOLES) expect(after.slots.entries(hole)).toHaveLength(1)
})
it('rolls back wholesale and reports loudly when a rival provider wins after deferred activation', async () => {
it('fails loudly instead of deduplicating a duplicate package row', async () => {
const b = await bench()
b.declare()
await b.ctx.plugin({ inject: [...inject], apply }).await()
const duplicate = b.ctx.plugin({ inject: [...inject], apply })
await expect(duplicate.await()).rejects.toThrow(/already has a registration/)
for (const hole of HOLES) expect(b.slots.entries(hole)).toHaveLength(1)
})
it('rolls back wholesale and reports loudly when a rival injection wins declaration activation', async () => {
const b = await bench()
const rejections: unknown[] = []
const onUnhandled = (reason: unknown): void => { rejections.push(reason) }
@@ -64,15 +73,14 @@ describe('directory-picker-native client half', () => {
process.on('unhandledRejection', onUnhandled)
process.on('uncaughtException', onUnhandled)
try {
// This provider activates BEFORE any hole exists: both deferrals wait.
// (Duplicate rows of the SAME package converge silently — the deferral
// skips a hole its own component already occupies; the conflict needs
// a rival provider.)
// The rival subscribes first, so synchronous declaration notifications
// let it occupy the pair before this provider's waiting injection runs.
b.slots.inject(HOLES[0], () => b.slots.inject(HOLES[1], function* () {
yield b.slots.register({ name: HOLES[0] } as never, () => null)
yield b.slots.register({ name: HOLES[1] } as never, () => null)
}))
await b.ctx.plugin({ inject: [...inject], apply }).await()
b.declare()
// A rival occupies both holes ahead of the pending microtask flush.
b.slots.register({ name: HOLES[0] } as never, () => null)
b.slots.register({ name: HOLES[1] } as never, () => null)
await new Promise(resolve => setTimeout(resolve, 20))
// The rival keeps both holes; this provider rolled back wholesale and
// surfaced the conflict on the fail-loud channel — no partial mix.
@@ -97,11 +105,11 @@ describe('directory-picker-native client half', () => {
}
})
it('rolls back the first deferral when the second hole is already occupied', async () => {
it('rolls back the outer injection when the second hole is already occupied', async () => {
const b = await bench()
b.declare()
// Foreign occupant in the SECOND registered hole: the pair construction
// throws after the first deferral installed its subscription.
// throws after the outer injection installed its subscription.
b.slots.register({ name: HOLES[1] } as never, () => null)
const rejections: unknown[] = []
const onUnhandled = (reason: unknown): void => { rejections.push(reason) }