refactor(client): rename currentProvide to currentProvideInfo
This commit is contained in:
@@ -157,7 +157,7 @@ export class SessionsService {
|
|||||||
* host's `sessions.provide` feed), so a roster change under a stable
|
* host's `sessions.provide` feed), so a roster change under a stable
|
||||||
* current id republishes the bundle instead of stranding mounted entries.
|
* current id republishes the bundle instead of stranding mounted entries.
|
||||||
*/
|
*/
|
||||||
readonly currentProvide: HostObservable<SessionMaybeProvideInfo>
|
readonly currentProvideInfo: HostObservable<SessionMaybeProvideInfo>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persisted selection cell (the durable half of `list.current`). Private on
|
* Persisted selection cell (the durable half of `list.current`). Private on
|
||||||
@@ -174,10 +174,10 @@ export class SessionsService {
|
|||||||
private readonly providers: SessionProvideDescriptor[] = []
|
private readonly providers: SessionProvideDescriptor[] = []
|
||||||
/** Static no-session projection, rebuilt only when the provider roster changes. */
|
/** Static no-session projection, rebuilt only when the provider roster changes. */
|
||||||
private maybeInfo: SessionMaybeProvideInfo
|
private maybeInfo: SessionMaybeProvideInfo
|
||||||
/** Latest published {@link SessionsService.currentProvide} bundle (identity comparison dedupes republish). */
|
/** Latest published {@link SessionsService.currentProvideInfo} bundle (identity comparison dedupes republish). */
|
||||||
private currentProvideSnapshot: SessionMaybeProvideInfo
|
private currentProvideInfoSnapshot: SessionMaybeProvideInfo
|
||||||
/** currentProvide subscribers (plain cell: bundles hold live Session sources, so no store freeze may touch them). */
|
/** currentProvideInfo subscribers (plain cell: bundles hold live Session sources, so no store freeze may touch them). */
|
||||||
private readonly currentProvideListeners = new Set<() => void>()
|
private readonly currentProvideInfoListeners = new Set<() => void>()
|
||||||
/**
|
/**
|
||||||
* The staged session id — follows `list.current` exactly, holding its last
|
* The staged session id — follows `list.current` exactly, holding its last
|
||||||
* defined value across masked gaps (a transiently absent selection blanks
|
* defined value across masked gaps (a transiently absent selection blanks
|
||||||
@@ -221,12 +221,12 @@ export class SessionsService {
|
|||||||
resolve: binding => ({ hooks: { session: binding.session } }),
|
resolve: binding => ({ hooks: { session: binding.session } }),
|
||||||
})
|
})
|
||||||
this.maybeInfo = this.materializeMaybeProvideInfo()
|
this.maybeInfo = this.materializeMaybeProvideInfo()
|
||||||
this.currentProvideSnapshot = this.maybeInfo
|
this.currentProvideInfoSnapshot = this.maybeInfo
|
||||||
this.currentProvide = {
|
this.currentProvideInfo = {
|
||||||
getSnapshot: () => this.currentProvideSnapshot,
|
getSnapshot: () => this.currentProvideInfoSnapshot,
|
||||||
subscribe: (fn) => {
|
subscribe: (fn) => {
|
||||||
this.currentProvideListeners.add(fn)
|
this.currentProvideInfoListeners.add(fn)
|
||||||
return () => { this.currentProvideListeners.delete(fn) }
|
return () => { this.currentProvideInfoListeners.delete(fn) }
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
rootCtx.reflect.provide('sessions', this, undefined)
|
rootCtx.reflect.provide('sessions', this, undefined)
|
||||||
@@ -272,9 +272,9 @@ export class SessionsService {
|
|||||||
*/
|
*/
|
||||||
private projectCurrentProvide(): void {
|
private projectCurrentProvide(): void {
|
||||||
const next = this.maybeProvideInfo(this.list.getSnapshot().current)
|
const next = this.maybeProvideInfo(this.list.getSnapshot().current)
|
||||||
if (next === this.currentProvideSnapshot) return
|
if (next === this.currentProvideInfoSnapshot) return
|
||||||
this.currentProvideSnapshot = next
|
this.currentProvideInfoSnapshot = next
|
||||||
for (const fn of [...this.currentProvideListeners]) fn()
|
for (const fn of [...this.currentProvideInfoListeners]) fn()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Build the static no-session kit and reject duplicate declared names. */
|
/** Build the static no-session kit and reject duplicate declared names. */
|
||||||
@@ -443,7 +443,7 @@ export class SessionsService {
|
|||||||
/**
|
/**
|
||||||
* Resolve one session's render-layer standard-props bundle (ctx never
|
* Resolve one session's render-layer standard-props bundle (ctx never
|
||||||
* enters the render layer; the renderer subscribes to
|
* enters the render layer; the renderer subscribes to
|
||||||
* {@link SessionsService.currentProvide}). Pure resolution — render-safe:
|
* {@link SessionsService.currentProvideInfo}). Pure resolution — render-safe:
|
||||||
* no staging, no window side effects (StrictMode double-invokes and
|
* no staging, no window side effects (StrictMode double-invokes and
|
||||||
* concurrent discarded passes must stay free).
|
* concurrent discarded passes must stay free).
|
||||||
* @param id - session id.
|
* @param id - session id.
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ export class SlotsService extends Service {
|
|||||||
entry.store === undefined ? undefined : this.resolveStore(entry.store as unknown as EngineStoreHandle, scopeKey),
|
entry.store === undefined ? undefined : this.resolveStore(entry.store as unknown as EngineStoreHandle, scopeKey),
|
||||||
sessions: {
|
sessions: {
|
||||||
list: sessions.list,
|
list: sessions.list,
|
||||||
provideInfo: sessions.currentProvide,
|
provideInfo: sessions.currentProvideInfo,
|
||||||
},
|
},
|
||||||
workspaces: { list: workspaces.list },
|
workspaces: { list: workspaces.list },
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,55 +195,55 @@ describe('cell (render-layer session kit)', () => {
|
|||||||
expect(b.svc.provideInfo('ghost')).toBeUndefined()
|
expect(b.svc.provideInfo('ghost')).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('currentProvide follows selection: absent projection ↔ definite bundle, notified on each move', async () => {
|
it('currentProvideInfo follows selection: absent projection ↔ definite bundle, notified on each move', async () => {
|
||||||
const b = bench()
|
const b = bench()
|
||||||
await feedList(b, [{ id: 's1' }, { id: 's2' }])
|
await feedList(b, [{ id: 's1' }, { id: 's2' }])
|
||||||
const absent = b.svc.currentProvide.getSnapshot()
|
const absent = b.svc.currentProvideInfo.getSnapshot()
|
||||||
expect(absent.sessionId).toBeUndefined()
|
expect(absent.sessionId).toBeUndefined()
|
||||||
expect(Object.hasOwn(absent.hooks, 'session')).toBe(true)
|
expect(Object.hasOwn(absent.hooks, 'session')).toBe(true)
|
||||||
const notified = vi.fn()
|
const notified = vi.fn()
|
||||||
b.svc.currentProvide.subscribe(notified)
|
b.svc.currentProvideInfo.subscribe(notified)
|
||||||
b.svc.open(sid('s1'))
|
b.svc.open(sid('s1'))
|
||||||
expect(b.svc.currentProvide.getSnapshot()).toBe(b.svc.provideInfo('s1'))
|
expect(b.svc.currentProvideInfo.getSnapshot()).toBe(b.svc.provideInfo('s1'))
|
||||||
expect(notified).toHaveBeenCalledTimes(1)
|
expect(notified).toHaveBeenCalledTimes(1)
|
||||||
b.svc.open(sid('s2'))
|
b.svc.open(sid('s2'))
|
||||||
expect(b.svc.currentProvide.getSnapshot()).toBe(b.svc.provideInfo('s2'))
|
expect(b.svc.currentProvideInfo.getSnapshot()).toBe(b.svc.provideInfo('s2'))
|
||||||
expect(notified).toHaveBeenCalledTimes(2)
|
expect(notified).toHaveBeenCalledTimes(2)
|
||||||
b.svc.clear()
|
b.svc.clear()
|
||||||
await Promise.resolve() // clearSelection projects through the manager notifier
|
await Promise.resolve() // clearSelection projects through the manager notifier
|
||||||
expect(b.svc.currentProvide.getSnapshot().sessionId).toBeUndefined()
|
expect(b.svc.currentProvideInfo.getSnapshot().sessionId).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('a provider roster change under a stable current id republishes the bundle', async () => {
|
it('a provider roster change under a stable current id republishes the bundle', async () => {
|
||||||
const b = bench()
|
const b = bench()
|
||||||
await feedList(b, [{ id: 's1' }])
|
await feedList(b, [{ id: 's1' }])
|
||||||
b.svc.open(sid('s1'))
|
b.svc.open(sid('s1'))
|
||||||
const before = b.svc.currentProvide.getSnapshot()
|
const before = b.svc.currentProvideInfo.getSnapshot()
|
||||||
const notified = vi.fn()
|
const notified = vi.fn()
|
||||||
b.svc.currentProvide.subscribe(notified)
|
b.svc.currentProvideInfo.subscribe(notified)
|
||||||
const source = { getSnapshot: () => 'live', subscribe: () => () => {} }
|
const source = { getSnapshot: () => 'live', subscribe: () => () => {} }
|
||||||
const dispose = b.svc.provide({
|
const dispose = b.svc.provide({
|
||||||
hooks: ['extra'],
|
hooks: ['extra'],
|
||||||
props: ['marker'],
|
props: ['marker'],
|
||||||
resolve: () => ({ hooks: { extra: source }, props: { marker: 7 } }),
|
resolve: () => ({ hooks: { extra: source }, props: { marker: 7 } }),
|
||||||
})
|
})
|
||||||
const added = b.svc.currentProvide.getSnapshot()
|
const added = b.svc.currentProvideInfo.getSnapshot()
|
||||||
expect(added).not.toBe(before)
|
expect(added).not.toBe(before)
|
||||||
expect(added).toMatchObject({ sessionId: 's1', props: { marker: 7 } })
|
expect(added).toMatchObject({ sessionId: 's1', props: { marker: 7 } })
|
||||||
expect(added.hooks['extra']).toBe(source)
|
expect(added.hooks['extra']).toBe(source)
|
||||||
expect(notified).toHaveBeenCalledTimes(1)
|
expect(notified).toHaveBeenCalledTimes(1)
|
||||||
dispose()
|
dispose()
|
||||||
const removed = b.svc.currentProvide.getSnapshot()
|
const removed = b.svc.currentProvideInfo.getSnapshot()
|
||||||
expect(removed).not.toBe(added)
|
expect(removed).not.toBe(added)
|
||||||
expect(Object.hasOwn(removed.hooks, 'extra')).toBe(false)
|
expect(Object.hasOwn(removed.hooks, 'extra')).toBe(false)
|
||||||
expect(notified).toHaveBeenCalledTimes(2)
|
expect(notified).toHaveBeenCalledTimes(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('an unsubscribed currentProvide listener stops receiving notifications', async () => {
|
it('an unsubscribed currentProvideInfo listener stops receiving notifications', async () => {
|
||||||
const b = bench()
|
const b = bench()
|
||||||
await feedList(b, [{ id: 's1' }])
|
await feedList(b, [{ id: 's1' }])
|
||||||
const notified = vi.fn()
|
const notified = vi.fn()
|
||||||
const off = b.svc.currentProvide.subscribe(notified)
|
const off = b.svc.currentProvideInfo.subscribe(notified)
|
||||||
off()
|
off()
|
||||||
b.svc.open(sid('s1'))
|
b.svc.open(sid('s1'))
|
||||||
expect(notified).not.toHaveBeenCalled()
|
expect(notified).not.toHaveBeenCalled()
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ function fakeSessions() {
|
|||||||
const absentInfo = { sessionId: undefined, hooks: { session: undefined }, props: {} }
|
const absentInfo = { sessionId: undefined, hooks: { session: undefined }, props: {} }
|
||||||
return {
|
return {
|
||||||
list: { getSnapshot: () => state, subscribe: () => () => undefined },
|
list: { getSnapshot: () => state, subscribe: () => () => undefined },
|
||||||
currentProvide: { getSnapshot: () => absentInfo, subscribe: () => () => undefined },
|
currentProvideInfo: { getSnapshot: () => absentInfo, subscribe: () => () => undefined },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ async function bench() {
|
|||||||
binding: (id: SessionId) => ({ sessionId: id, session: sessionFake, ctx: mint(id) }),
|
binding: (id: SessionId) => ({ sessionId: id, session: sessionFake, ctx: mint(id) }),
|
||||||
scope: (id: SessionId) => mint(id),
|
scope: (id: SessionId) => mint(id),
|
||||||
provideInfo: () => undefined,
|
provideInfo: () => undefined,
|
||||||
currentProvide: { getSnapshot: () => absentInfo, subscribe: () => () => {} },
|
currentProvideInfo: { getSnapshot: () => absentInfo, subscribe: () => () => {} },
|
||||||
provide: (descriptor: TestProvider) => { providers.push(descriptor); return () => {} },
|
provide: (descriptor: TestProvider) => { providers.push(descriptor); return () => {} },
|
||||||
scopeOf,
|
scopeOf,
|
||||||
sessionOf: (actx: Context) => (scopeOf(actx) === undefined ? undefined : sessionFake),
|
sessionOf: (actx: Context) => (scopeOf(actx) === undefined ? undefined : sessionFake),
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ async function bench() {
|
|||||||
binding: vi.fn(),
|
binding: vi.fn(),
|
||||||
scope: () => undefined,
|
scope: () => undefined,
|
||||||
provideInfo: () => undefined,
|
provideInfo: () => undefined,
|
||||||
currentProvide: { getSnapshot: () => absentInfo, subscribe: () => () => {} },
|
currentProvideInfo: { getSnapshot: () => absentInfo, subscribe: () => () => {} },
|
||||||
provide: vi.fn(() => () => {}),
|
provide: vi.fn(() => () => {}),
|
||||||
create: vi.fn(),
|
create: vi.fn(),
|
||||||
open: vi.fn(),
|
open: vi.fn(),
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ async function bench(snapshot: ConversationSnapshot) {
|
|||||||
// Provide-channel contributions land in this bundle the way the runtime
|
// Provide-channel contributions land in this bundle the way the runtime
|
||||||
// materializes them; the renderer host serves it through provideInfo.
|
// materializes them; the renderer host serves it through provideInfo.
|
||||||
const provided: { hooks: Record<string, unknown>; props: Record<string, unknown> } = { hooks: {}, props: {} }
|
const provided: { hooks: Record<string, unknown>; props: Record<string, unknown> } = { hooks: {}, props: {} }
|
||||||
// Identity-stable currentProvide snapshot (uSES getSnapshot contract),
|
// Identity-stable currentProvideInfo snapshot (uSES getSnapshot contract),
|
||||||
// materialized on first render after the provide contributions landed.
|
// materialized on first render after the provide contributions landed.
|
||||||
let infoCell: { sessionId: SessionId; hooks: Record<string, unknown>; props: Record<string, unknown> } | undefined
|
let infoCell: { sessionId: SessionId; hooks: Record<string, unknown>; props: Record<string, unknown> } | undefined
|
||||||
const sessionsFake = {
|
const sessionsFake = {
|
||||||
@@ -106,7 +106,7 @@ async function bench(snapshot: ConversationSnapshot) {
|
|||||||
provideInfo: (id: string) => (id === SID
|
provideInfo: (id: string) => (id === SID
|
||||||
? { sessionId: SID, hooks: { session, ...provided.hooks }, props: provided.props }
|
? { sessionId: SID, hooks: { session, ...provided.hooks }, props: provided.props }
|
||||||
: undefined),
|
: undefined),
|
||||||
currentProvide: {
|
currentProvideInfo: {
|
||||||
getSnapshot: () => infoCell ??= { sessionId: SID, hooks: { session, ...provided.hooks }, props: provided.props },
|
getSnapshot: () => infoCell ??= { sessionId: SID, hooks: { session, ...provided.hooks }, props: provided.props },
|
||||||
subscribe: () => () => {},
|
subscribe: () => () => {},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ async function bench(nodes: ToolResultNode[]) {
|
|||||||
binding: bindingOf,
|
binding: bindingOf,
|
||||||
scope: () => actxFake,
|
scope: () => actxFake,
|
||||||
provideInfo,
|
provideInfo,
|
||||||
currentProvide: {
|
currentProvideInfo: {
|
||||||
getSnapshot: () => provideInfo(SID),
|
getSnapshot: () => provideInfo(SID),
|
||||||
subscribe: () => () => {},
|
subscribe: () => () => {},
|
||||||
},
|
},
|
||||||
@@ -255,7 +255,7 @@ describe('registrant load-order seam', () => {
|
|||||||
binding: () => undefined,
|
binding: () => undefined,
|
||||||
scope: () => undefined,
|
scope: () => undefined,
|
||||||
provideInfo: () => undefined,
|
provideInfo: () => undefined,
|
||||||
currentProvide: {
|
currentProvideInfo: {
|
||||||
getSnapshot: () => ABSENT_INFO,
|
getSnapshot: () => ABSENT_INFO,
|
||||||
subscribe: () => () => {},
|
subscribe: () => () => {},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ function bench(): Bench {
|
|||||||
ids: [], byId: {}, current: undefined, phase: 'ready',
|
ids: [], byId: {}, current: undefined, phase: 'ready',
|
||||||
}),
|
}),
|
||||||
provideInfo: () => undefined,
|
provideInfo: () => undefined,
|
||||||
currentProvide: {
|
currentProvideInfo: {
|
||||||
getSnapshot: () => ABSENT_INFO,
|
getSnapshot: () => ABSENT_INFO,
|
||||||
subscribe: () => () => {},
|
subscribe: () => () => {},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user