@opetope/react 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +67 -50
- package/dist/act-delivery.d.ts +20 -0
- package/dist/command-hook.d.ts +8 -1
- package/dist/contribution-frame-DL0dyVP4.js +2 -0
- package/dist/contribution-frame-DL0dyVP4.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/scenario-mount.d.ts +2 -1
- package/dist/scenario-types.d.ts +4 -0
- package/dist/testing.js +3 -3
- package/dist/testing.js.map +1 -1
- package/package.json +5 -5
- package/dist/contribution-frame-D7_DN6Y2.js +0 -2
- package/dist/contribution-frame-D7_DN6Y2.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/command-hook-controller.ts","../src/command-hook.ts","../src/idle-subscription.ts","../src/resource-verb-hook.ts","../src/selection-hooks.ts","../src/model-selection-snapshot.ts","../src/model-hook.ts","../src/requires-models.ts","../src/resource-hook.ts","../src/slot.tsx"],"sourcesContent":["import { CommandError } from '@opetope/core';\nimport { compatibleAbortReason, isCancellation, reportDetachedError } from '@opetope/core/internal';\nimport type { CommandInputArgs, CallRunOptions } from '@opetope/core/internal';\n\nimport type { CommandOutcome } from './command';\nimport { ContributionError } from './errors';\n\n/**\n * D474: a callback's answer is not awaited and changes nothing — the run has already settled — so any value is\n * accepted, and a rejected promise among them is reported like a throw, detached from the outcome it watched.\n */\ntype CommandRunOptions<Output> = Readonly<{\n /** Called with a failure that is not a cancellation; its answer is not awaited, and a throw or rejection is reported. */\n onFailure?: (error: unknown) => unknown;\n /** Called with the body's value; its answer is not awaited and changes no outcome, and a throw or rejection is reported. */\n onSuccess?: (value: Output) => unknown;\n /** Cancels this run's wait: the run answers `cancelled`, and an already-aborted signal never reaches the command. */\n signal?: AbortSignal;\n}>;\n\n/*\n * D474: the options element of the spread form is `CommandRunOptions<Output> | undefined`, so a wrapper that forwards\n * `...args: [...CommandInputArgs<Input>, options?: …]` compiles under `exactOptionalPropertyTypes`, where a spread\n * optional element reads as possibly `undefined`.\n */\n/**\n * The options element of the spread form is `CommandRunOptions<Output> | undefined`.\n */\ntype CommandRun<Input, Output> = {\n (input: Input, options?: CommandRunOptions<Output>): Promise<CommandOutcome<Output>>;\n (\n ...args: [...CommandInputArgs<Input>, options?: CommandRunOptions<Output> | undefined]\n ): Promise<CommandOutcome<Output>>;\n};\n\ntype CommandInvoker = Readonly<{\n run: (input: never, options?: CallRunOptions<unknown>) => Promise<unknown>;\n}>;\n\ntype CommandNotification<Output> = (inFlight: boolean, outcome?: CommandOutcome<Output>) => void;\n\ntype CommandRequest<Input, Output> = {\n input: Input | undefined;\n options: CommandRunOptions<Output> | undefined;\n readonly promise: Promise<CommandOutcome<Output>>;\n resolve: ((outcome: CommandOutcome<Output>) => void) | undefined;\n};\n\nfunction failureOutcome<Output>(options: CommandRunOptions<Output> | undefined, error: unknown): CommandOutcome<never> {\n if (isCancellation(error)) return { reason: error, kind: 'cancelled' };\n\n try {\n const notification = options?.onFailure?.(error);\n\n if (notification !== undefined) void Promise.resolve(notification).catch(reportDetachedError);\n } catch (callbackError) {\n reportDetachedError(callbackError);\n }\n\n return { error, kind: 'failed' };\n}\n\nfunction runOptions<Output>(options: CommandRunOptions<Output> | undefined): CallRunOptions<Output> | undefined {\n const onSuccess = options?.onSuccess;\n const signal = options?.signal;\n\n if (onSuccess === undefined) return signal === undefined ? undefined : { signal };\n\n return signal === undefined ? { onSuccess } : { onSuccess, signal };\n}\n\nfunction requestOf<Input, Output>(\n input: Input,\n options: CommandRunOptions<Output> | undefined,\n): CommandRequest<Input, Output> {\n let resolve!: (outcome: CommandOutcome<Output>) => void;\n const promise = new Promise<CommandOutcome<Output>>(settle => {\n resolve = settle;\n });\n\n return { input, options: options === undefined ? undefined : { ...options }, promise, resolve };\n}\n\nfunction settle<Input, Output>(request: CommandRequest<Input, Output>, outcome: CommandOutcome<Output>): void {\n const resolve = request.resolve;\n request.input = undefined;\n request.options = undefined;\n request.resolve = undefined;\n resolve?.(outcome);\n}\n\n/**\n * D203: the consumer keeps a status, not a schedule. Every `run` reaches the command, and what the command was\n * declared with — `concurrency`, `memoize`, `dedupe` — decides what happens to it. What stays\n * local is what only this consumer knows: whether it is mounted, its own input signal, and the outcome it shows.\n */\nclass CommandHookController<Input, Output> {\n readonly run: CommandRun<Input, Output> = ((input: Input, options?: CommandRunOptions<Output>) => {\n if (this.notify === undefined) {\n return Promise.resolve({\n reason: new ContributionError('inactive', 'Command consumer is not mounted.'),\n kind: 'cancelled' as const,\n });\n }\n\n const aborted = this.cancelledInput(options);\n\n if (aborted !== undefined) return Promise.resolve(aborted);\n\n const request = requestOf(input, options);\n this.start(request);\n\n return request.promise;\n }) as CommandRun<Input, Output>;\n private inFlight = 0;\n private notify: CommandNotification<Output> | undefined;\n\n constructor(private readonly invoker: CommandInvoker) {}\n\n attach(notify: CommandNotification<Output>): () => void {\n this.notify = notify;\n\n return () => {\n if (this.notify !== notify) return;\n\n this.notify = undefined;\n };\n }\n\n synchronize(): void {\n this.notify?.(this.inFlight > 0);\n }\n\n private aborted(signal: AbortSignal): CommandOutcome<never> {\n return {\n reason: new CommandError('cancelled', 'Command input was cancelled.', compatibleAbortReason(signal)),\n kind: 'cancelled',\n };\n }\n\n private cancelledInput(options: CommandRunOptions<Output> | undefined): CommandOutcome<never> | undefined {\n return options?.signal?.aborted === true ? this.aborted(options.signal) : undefined;\n }\n\n private finish(request: CommandRequest<Input, Output>, outcome: CommandOutcome<Output>): void {\n if (request.resolve === undefined) return;\n\n this.inFlight -= 1;\n settle(request, outcome);\n this.notify?.(this.inFlight > 0, outcome);\n }\n\n private invoke(request: CommandRequest<Input, Output>): void {\n let invocation: Promise<Output>;\n\n try {\n invocation = this.invoker.run(request.input as never, runOptions(request.options) as never) as Promise<Output>;\n request.input = undefined;\n } catch (error) {\n this.finish(request, failureOutcome(request.options, error));\n\n return;\n }\n\n void invocation.then(\n value => this.finish(request, { kind: 'ok', value }),\n (error: unknown) => this.finish(request, failureOutcome(request.options, error)),\n );\n }\n\n private start(request: CommandRequest<Input, Output>): void {\n this.inFlight += 1;\n // Reserve the flight before publishing: a reentrant run must already see this consumer as busy.\n this.notify?.(true);\n this.invoke(request);\n }\n}\n\nexport { CommandHookController };\nexport type { CommandInvoker, CommandRun };\n","import { useInsertionEffect, useLayoutEffect, useMemo, useState } from 'react';\n\nimport type { Command } from '@opetope/core';\n\nimport type { CommandOutcome } from './command';\nimport { CommandHookController } from './command-hook-controller';\nimport type { CommandInvoker, CommandRun } from './command-hook-controller';\nimport { ContributionError } from './errors';\nimport { useFrame } from './mount-context';\n\n/** D292: a cancellation is not an answer of the product, so it never settles into the outcome this hook shows. */\ntype SettledOutcome<Output> = Exclude<CommandOutcome<Output>, { readonly kind: 'cancelled' }>;\n\ninterface CommandHookStatus<Output> {\n readonly inFlight: boolean;\n readonly outcome: SettledOutcome<Output> | undefined;\n}\n\nconst emptyStatus: CommandHookStatus<never> = { inFlight: false, outcome: undefined };\n\ntype CommandHook<Input, Output> = CommandHookStatus<Output> & {\n readonly run: CommandRun<Input, Output>;\n};\n\ntype CommandHookState<Input, Output> = CommandHookStatus<Output> & {\n readonly slot: CommandHookController<Input, Output>;\n};\n\n/** D292: a cancellation moves nothing, so it is the same non-event as a notification without an outcome at all. */\nfunction settledOutcome<Output>(outcome: CommandOutcome<Output> | undefined): SettledOutcome<Output> | undefined {\n return outcome === undefined || outcome.kind === 'cancelled' ? undefined : outcome;\n}\n\nfunction unchangedState<Input, Output>(\n current: CommandHookState<Input, Output>,\n slot: CommandHookController<Input, Output>,\n inFlight: boolean,\n settled: SettledOutcome<Output> | undefined,\n): boolean {\n return settled === undefined && current.slot === slot && current.inFlight === inFlight;\n}\n\nfunction nextState<Input, Output>(\n current: CommandHookState<Input, Output>,\n slot: CommandHookController<Input, Output>,\n inFlight: boolean,\n outcome?: CommandOutcome<Output>,\n): CommandHookState<Input, Output> {\n const settled = settledOutcome(outcome);\n\n if (unchangedState(current, slot, inFlight, settled)) return current;\n\n // A new run clears nothing: the previous answer stays readable while its retry is in flight.\n const previous = current.slot === slot ? current.outcome : undefined;\n\n return { inFlight, outcome: settled ?? previous, slot };\n}\n\nfunction useCommandSlot<Input, Output>(invoker: CommandInvoker): CommandHookController<Input, Output> {\n // Slots are inert until commit. An interrupted render cannot fence the committed source or admit its own work.\n const [slots] = useState(() => new WeakMap<object, CommandHookController<Input, Output>>());\n let slot = slots.get(invoker);\n\n if (slot === undefined) {\n slot = new CommandHookController(invoker);\n slots.set(invoker, slot);\n }\n\n return slot;\n}\n\n/**\n * A command consumer keeps its own status, attaches to its current slot and synchronizes a slot that was already\n * busy when this mount arrived.\n */\nfunction useCommandStatus<Input, Output>(\n currentSlot: CommandHookController<Input, Output>,\n): CommandHook<Input, Output> {\n const [hookState, setHookState] = useState<CommandHookState<Input, Output>>(() => ({\n inFlight: false,\n outcome: undefined,\n slot: currentSlot,\n }));\n useInsertionEffect(\n () =>\n currentSlot.attach((inFlight, outcome) =>\n setHookState(current => nextState(current, currentSlot, inFlight, outcome)),\n ),\n [currentSlot],\n );\n useLayoutEffect(() => currentSlot.synchronize(), [currentSlot]);\n const { inFlight, outcome } = hookState.slot === currentSlot ? hookState : emptyStatus;\n const run = currentSlot.run;\n\n return useMemo(() => ({ inFlight, outcome, run }), [inFlight, outcome, run]);\n}\n\nfunction useCommand<Input, Output>(command: Command<Input, Output>): CommandHook<Input, Output> {\n const frame = useFrame();\n const record = frame.commandRecords.get(command);\n\n if (record === undefined) {\n throw new ContributionError('missing', `Command ${command.id} is not bound in this contribution.`);\n }\n\n return useCommandStatus(useCommandSlot<Input, Output>(record.invoker));\n}\n\nexport { useCommand };\nexport type { CommandHook };\n","const idleRelease = (): void => undefined;\nconst subscribeIdle = (): (() => void) => idleRelease;\nconst idleRevision = (): number => 0;\n\nexport { idleRevision, subscribeIdle };\n","import { useInsertionEffect, useLayoutEffect, useMemo, useState } from 'react';\n\nimport type { ResourceOperationOutcome } from '@opetope/core';\n\n/** A Resource verb answers its own outcome; only its consumer owns this wait and status. */\ntype ResourceVerbHook = Readonly<{\n inFlight: boolean;\n outcome: ResourceOperationOutcome | undefined;\n run: () => Promise<ResourceOperationOutcome>;\n}>;\n\ntype VerbNotification = (inFlight: boolean, outcome?: ResourceOperationOutcome) => void;\n\ntype VerbWaiter = {\n active: boolean;\n next: VerbWaiter | undefined;\n previous: VerbWaiter | undefined;\n reject: ((error: unknown) => void) | undefined;\n resolve: ((outcome: ResourceOperationOutcome) => void) | undefined;\n};\n\nconst cancelled: ResourceOperationOutcome = Object.freeze({ kind: 'cancelled' });\n\n/**\n * Only Resource verbs use this controller. A detached consumer settles its own waits without cancelling the\n * materialization's work. The linked waiters add no retained collection to a Resource hook that never runs a verb.\n */\nclass ResourceVerbController {\n private inFlight = 0;\n private notify: VerbNotification | undefined;\n private pending: VerbWaiter | undefined;\n\n readonly run = (): Promise<ResourceOperationOutcome> => {\n if (this.notify === undefined) return Promise.resolve(cancelled);\n\n let resolve!: (outcome: ResourceOperationOutcome) => void;\n let reject!: (error: unknown) => void;\n const answer = new Promise<ResourceOperationOutcome>((settle, refuse) => {\n resolve = settle;\n reject = refuse;\n });\n const waiter: VerbWaiter = { active: true, next: this.pending, previous: undefined, reject, resolve };\n if (this.pending !== undefined) this.pending.previous = waiter;\n this.pending = waiter;\n this.inFlight += 1;\n this.notify(true);\n\n // A synchronous subscriber may detach while the busy status is published.\n if (!waiter.active) return answer;\n\n let operation: Promise<ResourceOperationOutcome>;\n\n try {\n operation = Promise.resolve(this.start());\n } catch (error) {\n this.fail(waiter, error);\n\n return answer;\n }\n\n void operation.then(\n outcome => this.finish(waiter, outcome),\n (error: unknown) => this.fail(waiter, error),\n );\n\n return answer;\n };\n\n constructor(private readonly start: () => Promise<ResourceOperationOutcome>) {}\n\n attach(notify: VerbNotification): () => void {\n this.notify = notify;\n\n return () => {\n if (this.notify !== notify) return;\n\n this.notify = undefined;\n let current = this.pending;\n this.pending = undefined;\n this.inFlight = 0;\n\n while (current !== undefined) {\n const next = current.next;\n current.active = false;\n current.next = undefined;\n current.previous = undefined;\n const resolve = current.resolve;\n current.resolve = undefined;\n current.reject = undefined;\n resolve?.(cancelled);\n current = next;\n }\n };\n }\n\n synchronize(): void {\n this.notify?.(this.inFlight > 0);\n }\n\n private unlink(waiter: VerbWaiter): boolean {\n if (!waiter.active) return false;\n\n waiter.active = false;\n if (waiter.previous === undefined) this.pending = waiter.next;\n else waiter.previous.next = waiter.next;\n if (waiter.next !== undefined) waiter.next.previous = waiter.previous;\n waiter.next = undefined;\n waiter.previous = undefined;\n this.inFlight -= 1;\n\n return true;\n }\n\n private finish(waiter: VerbWaiter, outcome: ResourceOperationOutcome): void {\n if (!this.unlink(waiter)) return;\n\n const resolve = waiter.resolve;\n waiter.resolve = undefined;\n waiter.reject = undefined;\n resolve?.(outcome);\n this.notify?.(this.inFlight > 0, outcome);\n }\n\n private fail(waiter: VerbWaiter, error: unknown): void {\n if (!this.unlink(waiter)) return;\n\n const reject = waiter.reject;\n waiter.resolve = undefined;\n waiter.reject = undefined;\n reject?.(error);\n this.notify?.(this.inFlight > 0);\n }\n}\n\ntype VerbHookState = ResourceVerbHook & { readonly slot: ResourceVerbController };\nconst emptyStatus = Object.freeze({ inFlight: false, outcome: undefined });\n\n/** D292: a cancelled wait does not replace the last product answer, even when the promise returns `cancelled`. */\nfunction visibleOutcome(outcome?: ResourceOperationOutcome): ResourceOperationOutcome | undefined {\n return outcome?.kind === 'cancelled' ? undefined : outcome;\n}\n\nfunction nextState(\n current: VerbHookState,\n slot: ResourceVerbController,\n inFlight: boolean,\n outcome?: ResourceOperationOutcome,\n): VerbHookState {\n const previous = current.slot === slot ? current.outcome : undefined;\n const settled = visibleOutcome(outcome);\n\n if (current.slot === slot && current.inFlight === inFlight && settled === undefined) return current;\n\n return { inFlight, outcome: settled ?? previous, run: slot.run, slot };\n}\n\nfunction useResourceVerbStatus(slot: ResourceVerbController): ResourceVerbHook {\n const [hookState, setHookState] = useState<VerbHookState>(() => ({\n inFlight: false,\n outcome: undefined,\n run: slot.run,\n slot,\n }));\n useInsertionEffect(\n () => slot.attach((inFlight, outcome) => setHookState(current => nextState(current, slot, inFlight, outcome))),\n [slot],\n );\n useLayoutEffect(() => slot.synchronize(), [slot]);\n const { inFlight, outcome } = hookState.slot === slot ? hookState : emptyStatus;\n const { run } = slot;\n\n return useMemo(() => ({ inFlight, outcome, run }), [inFlight, outcome, run]);\n}\n\nexport { ResourceVerbController, useResourceVerbStatus };\nexport type { ResourceVerbHook };\n","import { useInsertionEffect, useLayoutEffect, useRef, useSyncExternalStore } from 'react';\n\nimport type { Command } from '@opetope/core';\n\nimport type { CommandOutcome } from './command';\nimport type { CommandHook } from './command-hook';\nimport { CommandHookController } from './command-hook-controller';\nimport type { CommandInvoker } from './command-hook-controller';\nimport { ContributionError } from './errors';\nimport { idleRevision, subscribeIdle } from './idle-subscription';\nimport type { SelectedResource } from './model-selection-snapshot';\nimport { useFrame } from './mount-context';\nimport type { Frame } from './mount-frame';\nimport { resourceInvokers } from './resource-command';\nimport type { ResourceInvokers } from './resource-command';\nimport { ResourceVerbController } from './resource-verb-hook';\nimport type { ResourceVerbHook } from './resource-verb-hook';\n\ntype CommandSelection = Readonly<Record<string, Command<never, unknown>>>;\ntype ResourceSelection = ReadonlyMap<PropertyKey, SelectedResource>;\n/** D359: what one selected Resource answers — its three verbs, its state, and the pagination capability if it has one. */\ntype SelectedResourceHook = Readonly<{\n pagination: Readonly<{ loadNext: ResourceInvokers['loadNext']; state: unknown }> | undefined;\n refresh: ResourceVerbHook;\n reset: ResourceVerbHook;\n retry: ResourceVerbHook;\n state: unknown;\n}>;\ntype SelectionHooks = Readonly<Record<PropertyKey, CommandHook<never, unknown> | SelectedResourceHook>>;\n\nconst emptyHooks: SelectionHooks = Object.freeze({});\n\ntype CommandEntry = {\n detach: (() => void) | undefined;\n hook: CommandHook<never, unknown>;\n readonly invoker: CommandInvoker;\n readonly slot: CommandHookController<never, unknown>;\n};\n\ntype ResourceVerbEntry = {\n detach: (() => void) | undefined;\n hook: ResourceVerbHook;\n readonly invoker: ResourceInvokers['refresh'];\n readonly slot: ResourceVerbController;\n};\n\n/**\n * D359: one key of a selection, three consumers. The record is the value; `entries` still holds one entry per key.\n * D486: `loadNext` is no command consumer — it is the stable function of the Resource, with no status of its own.\n */\ntype ResourceEntry = {\n /** D359: the value this key hands the render, kept on the record so a still frame is the same object again. */\n readonly hook: SelectedResourceHook;\n readonly kind: 'resource';\n readonly loadNext: ResourceInvokers['loadNext'];\n readonly refresh: ResourceVerbEntry;\n readonly reset: ResourceVerbEntry;\n readonly retry: ResourceVerbEntry;\n};\n\ntype SelectionEntry = CommandEntry | ResourceEntry;\n\nfunction isResourceEntry(entry: SelectionEntry): entry is ResourceEntry {\n return 'kind' in entry;\n}\n\nfunction createEntry(invoker: CommandInvoker): CommandEntry {\n const slot = new CommandHookController<never, unknown>(invoker);\n\n return { detach: undefined, hook: { inFlight: false, outcome: undefined, run: slot.run }, invoker, slot };\n}\n\nfunction createVerbEntry(invoker: ResourceInvokers['refresh']): ResourceVerbEntry {\n const slot = new ResourceVerbController(invoker);\n\n return { detach: undefined, hook: { inFlight: false, outcome: undefined, run: slot.run }, invoker, slot };\n}\n\nfunction unchangedHook(\n previous: { readonly inFlight: boolean; readonly outcome: unknown },\n next: { readonly inFlight: boolean; readonly outcome: unknown },\n): boolean {\n return previous.inFlight === next.inFlight && Object.is(previous.outcome, next.outcome);\n}\n\nfunction updateEntry(entry: CommandEntry, inFlight: boolean, outcome?: CommandOutcome<unknown>): boolean {\n const previous = entry.hook;\n\n if (outcome === undefined && previous.inFlight === inFlight) return false;\n\n // D292: only a settled non-cancelled outcome replaces the one this key shows; a retry keeps the previous answer.\n const settled = outcome === undefined || outcome.kind === 'cancelled' ? undefined : outcome;\n const next = { inFlight, outcome: settled ?? previous.outcome, run: previous.run };\n\n if (unchangedHook(previous, next)) return false;\n\n entry.hook = next;\n\n return true;\n}\n\nfunction updateVerbEntry(entry: ResourceVerbEntry, inFlight: boolean, outcome?: ResourceVerbHook['outcome']): boolean {\n const previous = entry.hook;\n const settled = outcome?.kind === 'cancelled' ? undefined : outcome;\n\n if (settled === undefined && previous.inFlight === inFlight) return false;\n\n const next = { inFlight, outcome: settled ?? previous.outcome, run: previous.run };\n\n if (unchangedHook(previous, next)) return false;\n\n entry.hook = next;\n\n return true;\n}\n\nfunction selectEntry(previous: SelectionEntry | undefined, invoker: CommandInvoker): CommandEntry {\n return previous !== undefined && !isResourceEntry(previous) && previous.invoker === invoker\n ? previous\n : createEntry(invoker);\n}\n\nfunction resourceHook(entry: Omit<ResourceEntry, 'hook'>, selected: SelectedResource): SelectedResourceHook {\n return {\n pagination:\n selected.pagination === undefined ? undefined : { loadNext: entry.loadNext, state: selected.pagination },\n refresh: entry.refresh.hook,\n reset: entry.reset.hook,\n retry: entry.retry.hook,\n state: selected.state,\n };\n}\n\nfunction createResourceEntry(invokers: ResourceInvokers, selected: SelectedResource): ResourceEntry {\n const parts = {\n kind: 'resource' as const,\n loadNext: invokers.loadNext,\n refresh: createVerbEntry(invokers.refresh),\n reset: createVerbEntry(invokers.reset),\n retry: createVerbEntry(invokers.retry),\n };\n\n return { ...parts, hook: resourceHook(parts, selected) };\n}\n\n/** The three commands of a Resource, which every one has. */\nfunction unchangedResourceCommands(entry: ResourceEntry): boolean {\n const { hook } = entry;\n\n return hook.refresh === entry.refresh.hook && hook.reset === entry.reset.hook && hook.retry === entry.retry.hook;\n}\n\n/** What this selection read: the state of the Resource, and the state of its capability where it has one. */\nfunction unchangedResourceState(entry: ResourceEntry, selected: SelectedResource): boolean {\n return Object.is(entry.hook.state, selected.state) && Object.is(entry.hook.pagination?.state, selected.pagination);\n}\n\nfunction unchangedResourceHook(entry: ResourceEntry, selected: SelectedResource): boolean {\n return unchangedResourceCommands(entry) && unchangedResourceState(entry, selected);\n}\n\n/**\n * S6/D359: the same Resource that did not move answers the same object, so a child under `memo` is not re-rendered by\n * the mere act of reading it. Every part is a render input, so this is a memo and not a piece of state.\n */\nfunction selectResourceEntry(\n previous: SelectionEntry | undefined,\n invokers: ResourceInvokers,\n selected: SelectedResource,\n): ResourceEntry {\n if (previous === undefined || !isResourceEntry(previous) || previous.refresh.invoker !== invokers.refresh) {\n return createResourceEntry(invokers, selected);\n }\n\n // Preparing a suspended render must not replace the hook on the committed entry. The three command consumers are\n // still the same logical consumers; only the selected Resource wrapper is new until commit.\n return unchangedResourceHook(previous, selected) ? previous : { ...previous, hook: resourceHook(previous, selected) };\n}\n\nfunction detachEntry(entry: SelectionEntry): void {\n if (isResourceEntry(entry)) {\n for (const verb of [entry.refresh, entry.reset, entry.retry]) {\n verb.detach?.();\n verb.detach = undefined;\n }\n\n return;\n }\n\n entry.detach?.();\n entry.detach = undefined;\n}\n\nfunction sameResourceConsumers(previous: ResourceEntry, next: ResourceEntry): boolean {\n return previous.refresh === next.refresh && previous.reset === next.reset && previous.retry === next.retry;\n}\n\nfunction sameConsumers(previous: SelectionEntry, next: SelectionEntry | undefined): boolean {\n if (previous === next) return true;\n if (next === undefined || !isResourceEntry(previous) || !isResourceEntry(next)) return false;\n\n return sameResourceConsumers(previous, next);\n}\n\nfunction hookOf(entry: SelectionEntry): CommandHook<never, unknown> | SelectedResourceHook {\n return entry.hook;\n}\n\n/** Selection is inert during render; only a committed selection can admit work or discard an old queue. */\nclass CommandGroup {\n private entries = new Map<PropertyKey, SelectionEntry>();\n /** One set of invokers per Resource identity, so `run` keeps its reference across renders (D359). */\n private readonly invokers = new WeakMap<object, ResourceInvokers>();\n private readonly listeners = new Set<() => void>();\n private revision = 0;\n\n close(): void {\n for (const entry of this.entries.values()) detachEntry(entry);\n\n this.entries.clear();\n }\n\n commit(selected: Map<PropertyKey, SelectionEntry>): void {\n for (const [key, entry] of this.entries) {\n if (!sameConsumers(entry, selected.get(key))) detachEntry(entry);\n }\n\n this.entries = selected;\n\n for (const entry of selected.values()) this.attachEntry(entry);\n }\n\n /** S7: the attach runs on every commit, so the verbs are named rather than gathered into an array. */\n private attachEntry(entry: SelectionEntry): void {\n if (isResourceEntry(entry)) {\n this.attachVerb(entry.refresh);\n this.attachVerb(entry.retry);\n this.attachVerb(entry.reset);\n\n return;\n }\n\n this.attachCommand(entry);\n }\n\n private attachCommand(command: CommandEntry): void {\n command.detach ??= command.slot.attach((inFlight, outcome) => {\n if (!updateEntry(command, inFlight, outcome)) return;\n\n this.revision += 1;\n\n for (const listener of this.listeners) listener();\n });\n }\n\n private attachVerb(verb: ResourceVerbEntry): void {\n verb.detach ??= verb.slot.attach((inFlight, outcome) => {\n if (!updateVerbEntry(verb, inFlight, outcome)) return;\n\n this.revision += 1;\n\n for (const listener of this.listeners) listener();\n });\n }\n\n getSnapshot = (): number => this.revision;\n\n /**\n * D321: two outcomes no longer, but two kinds. A command that is not bound is still a `ContributionError`; a\n * resource never appears in `frame.commandRecords`, so it is prepared from its own identity instead.\n */\n prepare(commands: CommandSelection, resources: ResourceSelection, frame: Frame): Map<PropertyKey, SelectionEntry> {\n const selected = new Map<PropertyKey, SelectionEntry>();\n\n for (const key of Reflect.ownKeys(commands)) {\n if (!Object.prototype.propertyIsEnumerable.call(commands, key)) continue;\n\n const command = (commands as Readonly<Record<PropertyKey, Command<never, unknown>>>)[key];\n const record = command === undefined ? undefined : frame.commandRecords.get(command);\n\n if (record === undefined) {\n throw new ContributionError('missing', `Command at ${String(key)} is not bound in this contribution.`);\n }\n\n selected.set(key, selectEntry(this.entries.get(key), record.invoker));\n }\n\n for (const [key, resource] of resources) {\n const invokers = resourceInvokers(resource.resource, this.invokers);\n\n selected.set(key, selectResourceEntry(this.entries.get(key), invokers, resource));\n }\n\n return selected;\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n\n return () => {\n this.listeners.delete(listener);\n };\n };\n}\n\nfunction useCommandGroup(commands: CommandSelection, resources: ResourceSelection): CommandGroup | undefined {\n const groupRef = useRef<CommandGroup | undefined>(undefined);\n const populated =\n resources.size > 0 ||\n Reflect.ownKeys(commands).some(key => Object.prototype.propertyIsEnumerable.call(commands, key));\n\n return populated ? (groupRef.current ??= new CommandGroup()) : undefined;\n}\n\n/** The consumers of one selection: a command per key, and the two verbs of a resource per key (D321). */\nfunction useSelectionHooks(commands: CommandSelection, resources: ResourceSelection): SelectionHooks {\n const frame = useFrame();\n const group = useCommandGroup(commands, resources);\n const getSnapshot = group?.getSnapshot ?? idleRevision;\n useSyncExternalStore(group?.subscribe ?? subscribeIdle, getSnapshot, getSnapshot);\n const selected = group?.prepare(commands, resources, frame);\n useInsertionEffect(() => () => group?.close(), [group]);\n useInsertionEffect(() => {\n if (selected !== undefined) group?.commit(selected);\n });\n useLayoutEffect(() => {\n if (selected === undefined) return;\n\n // S7: the same branch as the attach — this runs after every commit, and the arrays were pure allocation.\n for (const entry of selected.values()) {\n if (isResourceEntry(entry)) {\n entry.refresh.slot.synchronize();\n entry.retry.slot.synchronize();\n entry.reset.slot.synchronize();\n } else entry.slot.synchronize();\n }\n });\n\n return selected === undefined\n ? emptyHooks\n : Object.fromEntries([...selected].map(([key, entry]) => [key, hookOf(entry)]));\n}\n\nexport { useSelectionHooks };\nexport type { SelectionHooks };\n","import type { Readable, ResourceView } from '@opetope/core';\nimport {\n enterSelectorEvaluation,\n isCallTarget,\n isReadable,\n isResourceView,\n leaveSelectorEvaluation,\n resourceViewDemands,\n resourceViewReadable,\n} from '@opetope/core/internal';\nimport type { ResourceDemand } from '@opetope/core/internal';\n\nimport { ContributionError } from './errors';\nimport type { AnyCommand } from './model-binding';\nimport { isResource } from './resource-command';\nimport type { AnyResource } from './resource-command';\n\ntype SelectionValue<Candidate> =\n Candidate extends ResourceView<infer Value> ? Value : Candidate extends Readable<infer Value> ? Value : never;\n\ninterface SelectionRead {\n <Candidate extends Readable<unknown> | ResourceView<unknown>>(source: Candidate): SelectionValue<Candidate>;\n <Candidate extends Readable<unknown> | ResourceView<unknown>, Selected>(\n source: Candidate,\n select: (value: SelectionValue<Candidate>) => Selected,\n ): Selected;\n}\n\n/** The tracked reader passed to a `useModel` selector. Reading a view records demand for commit. */\ntype SelectionContext = Readonly<{\n /** Read a source for this selection, tracking it so the selection updates when it changes. */\n read: SelectionRead;\n}>;\n\ntype SelectionRecord = Readonly<Record<PropertyKey, unknown>>;\n/**\n * D359: the Resource a field selected, and what this selection read from it — its state, and the state of its\n * pagination capability when it has one. Both are read passively: a selection takes no lease by reading.\n */\ntype SelectedResource = Readonly<{ pagination: unknown; resource: AnyResource; state: unknown }>;\ntype SelectionSnapshot = Readonly<{\n commands: Readonly<Record<PropertyKey, AnyCommand>>;\n /** The set the mount leases; two keys naming one resource are one lease. */\n leases: ReadonlySet<ResourceDemand>;\n resources: ReadonlyMap<PropertyKey, SelectedResource>;\n sources: ReadonlySet<Readable<unknown>>;\n values: SelectionRecord;\n}>;\n\nconst noResources: ReadonlyMap<PropertyKey, SelectedResource> = new Map();\n\n/**\n * D363: what this mount read last and the snapshot it answered with. It belongs to the mount and not to the reader:\n * React may discard the `useMemo` that builds the reader, and a memory kept there would be empty exactly when it is\n * needed. D364: it no longer remembers which sources ended — a source whose owner retired stops instead of refusing,\n * so it answers its last value on every read and there is nothing to remember or to step around.\n */\ntype SelectionMemory = {\n model: unknown;\n observed: Map<Readable<unknown>, unknown> | undefined;\n select: unknown;\n snapshot: SelectionSnapshot;\n};\n\nconst noLeases: ReadonlySet<ResourceDemand> = new Set();\n\nfunction readSource<Value>(source: Readable<Value>, sources: Map<Readable<unknown>, unknown>): Value {\n if (!isReadable(source)) {\n throw new ContributionError('binding-invalid', 'Model selection read requires a Readable or ResourceView.');\n }\n\n if (sources.has(source)) return sources.get(source) as Value;\n\n // D364: a refusal here is the source's own failure and belongs to the render, because no lifetime ends in one.\n const value: unknown = source.getSnapshot();\n sources.set(source, value);\n\n return value as Value;\n}\n\nfunction readPassively(source: Readable<unknown>, sources: Map<Readable<unknown>, unknown>): unknown {\n if (sources.has(source)) return sources.get(source);\n\n const value: unknown = source.getSnapshot();\n sources.set(source, value);\n\n return value;\n}\n\n/** D525: the built-in containers a selection result may not be, beside an array and a thenable. */\nconst CONTAINERS: readonly (abstract new (...args: never[]) => object)[] = [Date, Map, Set, RegExp, WeakMap, WeakSet];\n\n/** D525: what the types no longer refuse as a selection result, refused here as it was there. */\nfunction isContainer(value: object): boolean {\n if (Array.isArray(value) || typeof (value as { readonly then?: unknown }).then === 'function') return true;\n\n for (const container of CONTAINERS) if (value instanceof container) return true;\n\n return false;\n}\n\n/**\n * D525: a field of the result that is still a source would hand the component the source object — a truth test always\n * passes and a render never updates — so the selection refuses it by name. The types no longer check the result, and\n * this is where the refusal stands, in development and in production alike. A Resource is a field by D359 and answers\n * its hook; a Command answers its hook too. Only the field itself is asked, so a source one level down passes.\n */\nfunction refuseSource(key: PropertyKey, value: unknown): void {\n if (isReadable(value)) {\n throw new TypeError(`A selector returned a Readable in field \"${String(key)}\": read(source) inside the selector.`);\n }\n if (isResourceView(value)) {\n throw new TypeError(\n `A selector returned a ResourceView in field \"${String(key)}\": read(view) inside the selector, or select the Resource.`,\n );\n }\n}\n\n/** D359: the predicate that answers the question the type of the field asked, and the read that leases nothing. */\nfunction classifyResource(\n key: PropertyKey,\n value: unknown,\n resources: Map<PropertyKey, SelectedResource>,\n sources: Map<Readable<unknown>, unknown>,\n): void {\n if (!isResource(value)) {\n refuseSource(key, value);\n\n return;\n }\n\n const resource = value as AnyResource;\n const capability = (\n resource as { readonly pagination?: { readonly state: Readable<unknown> } | ResourceView<unknown> }\n ).pagination;\n\n resources.set(key, {\n pagination: capability === undefined ? undefined : readPassively(resourceViewReadable(capability), sources),\n resource,\n state: readPassively(resourceViewReadable(resource), sources),\n });\n}\n\nfunction selectedResources(resources: Map<PropertyKey, SelectedResource>): ReadonlyMap<PropertyKey, SelectedResource> {\n return resources.size === 0 ? noResources : resources;\n}\n\ntype CollectedSelection = Readonly<{\n leases: ReadonlySet<ResourceDemand>;\n resources: ReadonlyMap<PropertyKey, SelectedResource>;\n values: SelectionRecord;\n}>;\n\nfunction collectResourceDemands(\n resources: ReadonlyMap<PropertyKey, SelectedResource>,\n leases: Set<ResourceDemand>,\n): void {\n for (const { resource } of resources.values()) {\n if (isResourceView(resource)) {\n for (const demand of resourceViewDemands(resource)) leases.add(demand);\n } else {\n leases.add(resource);\n }\n }\n}\n\n/**\n * D359: recognition happens in the one pass that already walks the selection. `Resource` is a structural contract\n * (D199) and the type of a selected field promises `ResourceHook` for anything that satisfies it, so the predicate\n * this uses answers the same question the type asked. Reading the state here also puts it among `sources`, so its\n * publication invalidates the fast path of this reader exactly as a read source does.\n */\nfunction collectSelection(\n select: (context: SelectionContext) => object,\n sources: Map<Readable<unknown>, unknown>,\n): CollectedSelection {\n const leases = new Set<ResourceDemand>();\n let open = true;\n const read = <Value, Selected>(\n source: Readable<Value> | ResourceView<Value>,\n project?: (value: Value) => Selected,\n ) => {\n // D518: what a selection read belongs to the snapshot it built; a reader kept past it would add demand to a\n // committed snapshot nobody compares again.\n if (!open)\n throw new TypeError(\"A selector's read was called after the selector returned: read inside the selector.\");\n\n let readable: Readable<Value>;\n if (isResourceView(source)) {\n for (const demand of resourceViewDemands(source)) leases.add(demand);\n readable = source.observe();\n } else {\n readable = source;\n }\n const value = readSource(readable, sources);\n\n return project === undefined ? value : project(value);\n };\n enterSelectorEvaluation();\n try {\n const selected = select({ read });\n\n if (typeof selected !== 'object' || selected === null || isContainer(selected)) {\n throw new ContributionError(\n 'binding-invalid',\n 'Model selection requires a record of named fields, not an array, a promise or another container.',\n );\n }\n\n const entries: (readonly [PropertyKey, unknown])[] = [];\n const resources = new Map<PropertyKey, SelectedResource>();\n\n for (const key of Reflect.ownKeys(selected)) {\n if (!Object.prototype.propertyIsEnumerable.call(selected, key)) continue;\n\n const value = (selected as SelectionRecord)[key];\n\n entries.push([key, value]);\n classifyResource(key, value, resources, sources);\n }\n\n // `__proto__` is a key like any other here, and only a defined property keeps it one (D214).\n collectResourceDemands(resources, leases);\n return { leases, resources: selectedResources(resources), values: Object.fromEntries(entries) };\n } finally {\n open = false;\n leaveSelectorEvaluation();\n }\n}\n\n/**\n * D359: `values` keeps the Resource itself — a stable reference — so a selection that holds one is compared by\n * `Object.is` exactly as before. What moves is the state behind it, and that is compared here: without this the\n * selection would be «equal», the previous state would be kept, and a published Resource would reach no render.\n */\nfunction equalResources(\n previous: ReadonlyMap<PropertyKey, SelectedResource>,\n next: ReadonlyMap<PropertyKey, SelectedResource>,\n): boolean {\n if (previous.size !== next.size) return false;\n\n for (const [key, selected] of next) {\n const before = previous.get(key);\n\n if (\n before?.resource !== selected.resource ||\n !Object.is(before.state, selected.state) ||\n !Object.is(before.pagination, selected.pagination)\n ) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction equalMembers<Value>(previous: ReadonlySet<Value>, next: ReadonlySet<Value>): boolean {\n if (previous.size !== next.size) return false;\n for (const value of next) if (!previous.has(value)) return false;\n return true;\n}\n\nfunction equalSelection(\n previous: SelectionSnapshot,\n values: SelectionRecord,\n sources: ReadonlySet<Readable<unknown>>,\n resources: ReadonlyMap<PropertyKey, SelectedResource>,\n leases: ReadonlySet<ResourceDemand>,\n) {\n const keys = Reflect.ownKeys(values);\n const previousKeys = Reflect.ownKeys(previous.values);\n\n if (keys.length !== previousKeys.length) return false;\n if (!equalMembers(previous.sources, sources)) return false;\n if (!equalMembers(previous.leases, leases)) return false;\n\n if (!equalResources(previous.resources, resources)) return false;\n\n return keys.every((key, index) => previousKeys[index] === key && Object.is(previous.values[key], values[key]));\n}\n\ntype ModelSelector<Model> = (model: Model, context: SelectionContext) => object;\ntype SelectionReaderRef<Model> = { readonly current: ModelSelector<Model> | undefined };\n\nconst emptySelectionSnapshot: SelectionSnapshot = {\n commands: {},\n leases: noLeases,\n resources: noResources,\n sources: new Set(),\n values: {},\n};\n\nfunction selectionCommands(values: SelectionRecord): Readonly<Record<PropertyKey, AnyCommand>> {\n return Object.fromEntries(\n Reflect.ownKeys(values)\n .filter(key => isCallTarget(values[key]))\n .map(key => [key, values[key]]),\n ) as Readonly<Record<PropertyKey, AnyCommand>>;\n}\n\n/**\n * The fast path re-reads what it observed. D364: a source that stopped answers the same value it answered last, so\n * it compares equal and costs one read — there is no terminal source to record and step around any more.\n */\nfunction observedUnchanged(observed: Map<Readable<unknown>, unknown>): boolean {\n enterSelectorEvaluation();\n try {\n for (const [source, value] of observed) {\n if (!Object.is(source.getSnapshot(), value)) return false;\n }\n\n return true;\n } finally {\n leaveSelectorEvaluation();\n }\n}\n\n/** D363: the memory of one mount, empty until its first read and reset when the reader is built for another model. */\nfunction createSelectionMemory(): SelectionMemory {\n return { model: undefined, observed: undefined, select: undefined, snapshot: emptySelectionSnapshot };\n}\n\n/**\n * D214: one reader per model, not per render. The author's selector arrives through a ref, so an inline arrow does not\n * rebuild the reader; the fast path — nothing observed has moved — belongs to the selector that computed the snapshot,\n * so a new selector is always called again, its result compared field by field, and an equal selection keeps the\n * previous snapshot. A rejected or abandoned render still changes nothing: only a committed selection subscribes.\n *\n * D363: what the reader remembers lives in the mount's memory, so a reader React rebuilt — because it discarded the\n * memo — still knows what this selection read last.\n */\nfunction createSelectionSnapshot<Model>(\n model: Model,\n selectRef: SelectionReaderRef<Model>,\n memory: SelectionMemory,\n): () => SelectionSnapshot {\n if (memory.model !== model) {\n memory.model = model;\n memory.observed = undefined;\n memory.select = undefined;\n memory.snapshot = emptySelectionSnapshot;\n }\n\n return () => {\n const select = selectRef.current;\n\n if (select === undefined) return emptySelectionSnapshot;\n\n const observed = memory.observed;\n\n if (observed !== undefined && memory.select === select && observedUnchanged(observed)) {\n return memory.snapshot;\n }\n\n const nextObserved = new Map<Readable<unknown>, unknown>();\n const { leases, resources, values } = collectSelection(context => select(model, context), nextObserved);\n const sources = new Set(nextObserved.keys());\n\n if (observed === undefined || !equalSelection(memory.snapshot, values, sources, resources, leases)) {\n memory.snapshot = {\n commands: selectionCommands(values),\n leases,\n resources,\n sources,\n values,\n };\n }\n\n memory.observed = nextObserved;\n memory.select = select;\n\n return memory.snapshot;\n };\n}\n\nexport { createSelectionMemory, createSelectionSnapshot, emptySelectionSnapshot };\nexport type { ModelSelector, SelectedResource, SelectionContext, SelectionMemory, SelectionSnapshot };\n","import { useLayoutEffect, useMemo, useRef, useSyncExternalStore } from 'react';\n\nimport type { Command, ModelOf, PaginatedResource, Resource } from '@opetope/core';\n\nimport type { CommandHook } from './command-hook';\nimport { useSelectionHooks } from './selection-hooks';\nimport type { SelectionHooks } from './selection-hooks';\nimport { ContributionError } from './errors';\nimport { subscribeIdle } from './idle-subscription';\nimport type { AnyModel } from './model-binding';\nimport { createSelectionMemory, createSelectionSnapshot, emptySelectionSnapshot } from './model-selection-snapshot';\nimport type { ModelSelector, SelectionContext, SelectionMemory, SelectionSnapshot } from './model-selection-snapshot';\nimport { ModelSelectionStore } from './model-selection-store';\nimport { useFrame } from './mount-context';\nimport type { PaginatedResourceHook, ResourceHook, ResourceKey } from './resource-command';\nimport type { RefusedSelection, SelectorModel } from './selector-sources';\n\n/**\n * D359: a field whose value is a Resource answers the hook `useResource` answers, and the mount leases it exactly as\n * `useResource` does. `read(resource.state)` is unchanged — it hands out the state and leases nothing — so naming one\n * Resource both ways registers it twice: the subscription and the lease are independent.\n */\ntype SelectedValue<Value> =\n Value extends Command<infer Input, infer Output>\n ? CommandHook<Input, Output>\n : Value extends PaginatedResource<\n infer Data,\n infer Key extends ResourceKey,\n infer Cursor extends ResourceKey,\n 'consumers' | 'model'\n >\n ? PaginatedResourceHook<Data, Key, Cursor>\n : Value extends Resource<infer Data, infer Key extends ResourceKey, 'consumers' | 'model'>\n ? ResourceHook<Data, Key>\n : Value;\ntype ModelSelection<Selected> = { readonly [Key in keyof Selected]: SelectedValue<Selected[Key]> };\n\nconst readEmptySnapshot = () => emptySelectionSnapshot;\n\nfunction useSelectionStore(selected: boolean): ModelSelectionStore | undefined {\n const storeRef = useRef<ModelSelectionStore | undefined>(undefined);\n\n return selected ? (storeRef.current ??= new ModelSelectionStore()) : undefined;\n}\n\ntype SelectionResult = Readonly<Record<PropertyKey, unknown>>;\n\n/**\n * Two branches of one key: the consumer this selection prepared — a bound command, or the verbs, the state and the\n * capability of a selected Resource — and otherwise the value the selector wrote. S6/D359: the Resource branch is\n * assembled on the record and not here, so a key whose state did not move hands back the very same object.\n */\nfunction selectedValue(snapshot: SelectionSnapshot, hooks: SelectionHooks, key: PropertyKey): unknown {\n return Object.prototype.hasOwnProperty.call(hooks, key) ? hooks[key] : snapshot.values[key];\n}\n\nfunction mergeSelection(\n snapshot: SelectionSnapshot,\n hooks: SelectionHooks,\n committed: SelectionResult | undefined,\n): SelectionResult {\n const keys = Reflect.ownKeys(snapshot.values);\n\n if (committed !== undefined) {\n const oldKeys = Reflect.ownKeys(committed);\n\n if (keys.length === oldKeys.length) {\n let equal = true;\n for (let index = 0; index < keys.length; index += 1) {\n const key = keys[index]!;\n if (key !== oldKeys[index] || !Object.is(committed[key], selectedValue(snapshot, hooks, key))) {\n equal = false;\n break;\n }\n }\n if (equal) return committed;\n }\n }\n\n return Object.fromEntries(keys.map(key => [key, selectedValue(snapshot, hooks, key)]));\n}\n\n/** Read a granted model; a selection reads sources through `read` and binds Command or Resource fields. */\nfunction useModel<Declaration extends AnyModel>(declaration: Declaration): ModelOf<Declaration>;\nfunction useModel<Declaration extends AnyModel, const Selected extends object = RefusedSelection>(\n declaration: Declaration,\n select: (model: SelectorModel<ModelOf<Declaration>>, context: SelectionContext) => Selected,\n): ModelSelection<Selected>;\nfunction useModel<Declaration extends AnyModel>(\n declaration: Declaration,\n select?: (model: SelectorModel<ModelOf<Declaration>>, context: SelectionContext) => object,\n): unknown {\n const frame = useFrame();\n\n if (!frame.models.has(declaration)) {\n throw new ContributionError('missing', `Model ${declaration.id} is not granted to this contribution.`);\n }\n\n const model = frame.models.get(declaration) as SelectorModel<ModelOf<Declaration>>;\n const store = useSelectionStore(select !== undefined);\n // D214: the selector of this render, read by a reader that belongs to the model. Writing the latest callback into\n // a ref is what `useSyncExternalStoreWithSelector` does: the reader only reads it, and never writes to it.\n const selectRef = useRef<ModelSelector<SelectorModel<ModelOf<Declaration>>> | undefined>(undefined);\n selectRef.current = select;\n const selected = select !== undefined;\n // D363: the memory of this selection belongs to the mount, so a reader React rebuilt keeps what it read last.\n const memoryRef = useRef<SelectionMemory | undefined>(undefined);\n const memory = (memoryRef.current ??= createSelectionMemory());\n const getSnapshot = useMemo(\n () => (selected ? createSelectionSnapshot(model, selectRef, memory) : readEmptySnapshot),\n [memory, model, selected],\n );\n const snapshot = useSyncExternalStore(store?.subscribe ?? subscribeIdle, getSnapshot, getSnapshot);\n const hooks = useSelectionHooks(snapshot.commands, snapshot.resources);\n useLayoutEffect(() => () => store?.close(), [store]);\n // D359: a lease is taken in commit and never in a render, and the store diffs the set by identity.\n useLayoutEffect(() => store?.commit(snapshot.sources, snapshot.leases), [snapshot, store]);\n\n // An abandoned render may prepare another record, but only a committed render replaces the comparison baseline.\n const committedResult = useRef<SelectionResult | undefined>(undefined);\n const result = selected ? mergeSelection(snapshot, hooks, committedResult.current) : undefined;\n useLayoutEffect(() => {\n committedResult.current = result;\n }, [result]);\n\n if (select === undefined) return model;\n\n return result;\n}\n\nexport { useModel };\n","import type { FunctionComponent } from 'react';\n\nimport type { ModelIdentity } from '@opetope/core/internal';\n\nimport { ContributionError } from './errors';\n\ntype AnyModel = ModelIdentity;\n\n/*\n * The marker a component uses to state which UI models of its contribution it reads. `slot` checks by type that the\n * contribution grants every model of this list — it may grant more, because the mount serves its whole instance.\n *\n * The runtime authority is the mount frame, not this marker: `useModel` resolves against what the mount was given\n * and refuses anything else. The lint rule `opetope/require-declared-models` checks visible contribution sites and\n * each reader of a per-mount model within the same module. Imported implementations and dynamic declaration lists\n * remain unknown to this syntactic check (D85, D158, D250).\n */\n/**\n * The marker a component uses to state which UI models of its contribution it reads.\n */\ntype ComponentRequiringModels<Props, Models extends readonly AnyModel[]> = FunctionComponent<Props> & {\n readonly requires: Models;\n};\n\n/*\n * Declares the UI models a component reads and returns the same component marked with them, so `slot` can check by\n * type that the contribution grants each one. Marking a component that already declares its models throws.\n *\n * Write the component as a top-level function and wrap it after — `function CountView() { … }`, then\n * `export const Count = requiresModels([CountModel])(CountView)`: React Compiler in infer mode does not compile a\n * component written inside the call.\n */\n/**\n * Declares the UI models a component reads and returns the same component marked with them, so `slot` can check by type that the contribution grants each one.\n */\nfunction requiresModels<const Models extends readonly AnyModel[]>(\n models: Models,\n): <Props>(component: FunctionComponent<Props>) => ComponentRequiringModels<Props, Models> {\n const declared = Object.freeze([...models]) as unknown as Models;\n\n return <Props>(component: FunctionComponent<Props>): ComponentRequiringModels<Props, Models> => {\n const existing = (component as { readonly requires?: readonly AnyModel[] }).requires;\n\n if (existing !== undefined) {\n throw new ContributionError('duplicate', 'Component already declares the models it requires.');\n }\n\n Object.defineProperty(component, 'requires', { enumerable: false, value: declared });\n\n return component as ComponentRequiringModels<Props, Models>;\n };\n}\n\nexport { requiresModels };\n","import { useCallback, useLayoutEffect, useMemo, useState, useSyncExternalStore } from 'react';\n\nimport type { PaginatedResource, PaginationState, Readable, Resource } from '@opetope/core';\nimport type { ResourceKey } from '@opetope/core/internal';\nimport { resourceViewDemands, resourceViewReadable } from '@opetope/core/internal';\n\nimport { ModelSelectionStore } from './model-selection-store';\nimport { createResourceInvokers, paginationOf } from './resource-command';\nimport type { AnyResource, PaginatedResourceHook, ResourceHook, ResourcePaginationHook } from './resource-command';\nimport { ResourceVerbController, useResourceVerbStatus } from './resource-verb-hook';\n\ntype ResourceSlots = Readonly<{\n /** D486: the stable function of `pagination.loadNext`, kept with the verbs of the same Resource identity. */\n loadNext: (options?: { readonly retry?: boolean }) => Promise<unknown>;\n refresh: ResourceVerbController;\n reset: ResourceVerbController;\n retry: ResourceVerbController;\n}>;\n\n/**\n * D359: one set of controllers per Resource identity, exactly as `useCommandSlot` keeps one per invoker. The slots are\n * what the set of invokers is kept alive by, so one map answers for all three verbs and `loadNext`. Slots are inert\n * until commit, so an interrupted render neither admits work nor resets the status of the committed one.\n */\nfunction useResourceSlots(resource: AnyResource): ResourceSlots {\n const [cache] = useState(() => new WeakMap<object, ResourceSlots>());\n const existing = cache.get(resource);\n\n if (existing !== undefined) return existing;\n\n const invokers = createResourceInvokers(resource);\n const slots: ResourceSlots = {\n loadNext: invokers.loadNext,\n refresh: new ResourceVerbController(invokers.refresh),\n reset: new ResourceVerbController(invokers.reset),\n retry: new ResourceVerbController(invokers.retry),\n };\n cache.set(resource, slots);\n\n return slots;\n}\n\nconst unavailablePagination: PaginationState<ResourceKey> = Object.freeze({\n kind: 'unavailable',\n reason: 'resource-not-ready',\n});\nconst noPaginationSource: Readable<PaginationState<ResourceKey>> = Object.freeze({\n getSnapshot: () => unavailablePagination,\n subscribe: () => () => undefined,\n});\n\n/** Lease a Resource for this committed component and read its state and optional pagination. */\nfunction useResource<Data, Key extends ResourceKey, Cursor extends ResourceKey>(\n resource: PaginatedResource<Data, Key, Cursor, 'consumers' | 'model'>,\n): PaginatedResourceHook<Data, Key, Cursor>;\nfunction useResource<Data, Key extends ResourceKey>(\n resource: Resource<Data, Key, 'consumers' | 'model'>,\n): ResourceHook<Data, Key>;\nfunction useResource<Data, Key extends ResourceKey>(\n resource: Resource<Data, Key, 'consumers' | 'model'>,\n): ResourceHook<Data, Key> {\n const handle = resource as unknown as AnyResource;\n const capability = paginationOf(handle);\n const [store] = useState(() => new ModelSelectionStore());\n const stateSource = useMemo(() => resourceViewReadable(resource), [resource]);\n const subscribe = useCallback((listener: () => void) => store.subscribe(listener), [store]);\n /*\n * D364: `resource.state` is a total `Readable` for the life of the object this hook holds — a retired one answers\n * `idle('retired')` — so the read needs no memory of its own. What it can still refuse is a producer failure, a\n * broken `cursor` among them (D356), and that belongs to the render (G13).\n */\n const getState = useMemo(() => () => stateSource.getSnapshot(), [stateSource]);\n const state = useSyncExternalStore(subscribe, getState, getState);\n const paginationSource: Readable<PaginationState<ResourceKey>> =\n capability === undefined ? noPaginationSource : resourceViewReadable(capability);\n const subscribePagination = useCallback(\n (listener: () => void) => paginationSource.subscribe(listener),\n [paginationSource],\n );\n const getPaginationState = useMemo(() => () => paginationSource.getSnapshot(), [paginationSource]);\n const paginationState = useSyncExternalStore(subscribePagination, getPaginationState, getPaginationState);\n\n // Acquire in layout for a committed Resource. The store releases with its passive subscription, so a Suspense\n // hide that disconnects layout effects keeps the mounted component's lease.\n useLayoutEffect(() => {\n store.commit(new Set([stateSource]), new Set(resourceViewDemands(resource)));\n }, [resource, stateSource, store]);\n\n const slots = useResourceSlots(handle);\n const refresh = useResourceVerbStatus(slots.refresh);\n const retry = useResourceVerbStatus(slots.retry);\n const reset = useResourceVerbStatus(slots.reset);\n const { loadNext } = slots;\n const pagination = useMemo(\n () =>\n capability === undefined\n ? undefined\n : ({ loadNext, state: paginationState } as unknown as ResourcePaginationHook<ResourceKey>),\n [capability, loadNext, paginationState],\n );\n\n return useMemo(() => ({ pagination, refresh, reset, retry, state }), [pagination, refresh, reset, retry, state]);\n}\n\nexport { useResource };\n","import type { ComponentType, ReactNode } from 'react';\nimport { createElement, Fragment } from 'react';\n\nimport type { DeclarationId } from '@opetope/core';\nimport { declarationId } from '@opetope/core';\nimport type { ContributionTarget } from '@opetope/core/internal';\nimport { createContributionTarget } from '@opetope/core/internal';\nimport type { ContributionModel } from '@opetope/runtime/internal';\n\nimport { contributionAuthority, ContributionMount } from './contribution-frame';\nimport { useReadable } from './readable-hooks';\n\ntype SlotProperties = object | undefined;\ntype EmptySlotProperties = Readonly<Record<string, never>>;\ntype SlotComponentProperties<Props extends SlotProperties> = [Props] extends [undefined]\n ? EmptySlotProperties\n : Extract<Props, object>;\n\n/**\n * Contribution component and adapter published for a Slot target.\n */\ntype SlotContribution<Props extends SlotProperties = undefined> = {\n readonly Component: ComponentType<never>;\n readonly models?: readonly ContributionModel[];\n readonly props?: (slotProps: SlotComponentProperties<Props>) => object;\n};\n\ntype SlotTarget<Props extends SlotProperties = undefined> = ContributionTarget<SlotContribution<Props>>;\n\ntype SwitchSlotTarget<Route extends string, Props extends SlotProperties = undefined> = ((\n route: Route,\n) => SlotTarget<Props>) & {\n readonly id: DeclarationId;\n};\n\ntype SlotRenderProps<Props extends SlotProperties> = [Props] extends [undefined]\n ? {\n readonly props?: 'this slot has no props: omit the props field';\n readonly target: SlotTarget<Props>;\n }\n : {\n readonly props: SlotComponentProperties<Props>;\n readonly target: SlotTarget<Props>;\n };\n\nconst emptySlotProperties: EmptySlotProperties = Object.freeze({});\n\n/**\n * Declare a typed Slot target by its id.\n */\nfunction defineSlot<Props extends SlotProperties = undefined>(id: string): SlotTarget<Props> {\n return createContributionTarget<SlotContribution<Props>>(id, 'slot');\n}\n\nfunction defineSwitchSlot<Route extends string, Props extends SlotProperties = undefined>(\n id: string,\n): SwitchSlotTarget<Route, Props> {\n const declared = declarationId(id);\n const targets = new Map<Route, SlotTarget<Props>>();\n\n const switchSlot = (route: Route): SlotTarget<Props> => {\n const existing = targets.get(route);\n\n if (existing !== undefined) return existing;\n\n const target = defineSlot<Props>(`${declared}/${route}`);\n targets.set(route, target);\n\n return target;\n };\n\n Object.defineProperty(switchSlot, 'id', { enumerable: true, value: declared });\n\n return Object.freeze(switchSlot) as SwitchSlotTarget<Route, Props>;\n}\n\n/**\n * Render the published contributions of a Slot target within their owning mounts.\n */\nfunction Slot<Props extends SlotProperties>(\n options: SlotRenderProps<NoInfer<Props>> & { readonly target: SlotTarget<Props> },\n): ReactNode {\n const entries = useReadable(options.target.entries);\n const slotProps: object =\n 'props' in options ? ((options.props as object | undefined) ?? emptySlotProperties) : emptySlotProperties;\n\n if (entries.length === 0) return null;\n\n return entries.map(entry =>\n createElement(\n Fragment,\n { key: entry.id },\n createElement(ContributionMount, {\n authority: contributionAuthority(entry),\n contribution: entry.value,\n contributionId: entry.id,\n slotProps,\n targetId: options.target.id,\n }),\n ),\n );\n}\n\nexport { defineSlot, defineSwitchSlot, Slot };\nexport type { SlotComponentProperties, SlotContribution, SlotProperties, SlotTarget, SwitchSlotTarget };\n"],"names":["failureOutcome","options","error","isCancellation","notification","_a","reportDetachedError","callbackError","runOptions","onSuccess","signal","requestOf","input","resolve","promise","settle","request","outcome","CommandHookController","ContributionError","aborted","invoker","notify","CommandError","compatibleAbortReason","invocation","value","emptyStatus","settledOutcome","unchangedState","current","slot","inFlight","settled","nextState","previous","useCommandSlot","slots","useState","useCommandStatus","currentSlot","hookState","setHookState","useInsertionEffect","useLayoutEffect","run","useMemo","useCommand","command","record","useFrame","idleRelease","subscribeIdle","idleRevision","cancelled","ResourceVerbController","reject","answer","refuse","waiter","operation","start","next","visibleOutcome","useResourceVerbStatus","emptyHooks","isResourceEntry","entry","createEntry","createVerbEntry","unchangedHook","updateEntry","updateVerbEntry","selectEntry","resourceHook","selected","createResourceEntry","invokers","parts","unchangedResourceCommands","hook","unchangedResourceState","unchangedResourceHook","selectResourceEntry","detachEntry","verb","_b","sameResourceConsumers","sameConsumers","hookOf","CommandGroup","key","listener","commands","resources","frame","resource","resourceInvokers","useCommandGroup","groupRef","useRef","useSelectionHooks","group","getSnapshot","useSyncExternalStore","noResources","noLeases","readSource","source","sources","isReadable","readPassively","CONTAINERS","isContainer","container","refuseSource","isResourceView","classifyResource","isResource","capability","resourceViewReadable","selectedResources","collectResourceDemands","leases","demand","resourceViewDemands","collectSelection","select","open","read","project","readable","enterSelectorEvaluation","entries","leaveSelectorEvaluation","equalResources","before","equalMembers","equalSelection","values","keys","previousKeys","index","emptySelectionSnapshot","selectionCommands","isCallTarget","observedUnchanged","observed","createSelectionMemory","createSelectionSnapshot","model","selectRef","memory","nextObserved","context","readEmptySnapshot","useSelectionStore","storeRef","ModelSelectionStore","selectedValue","snapshot","hooks","mergeSelection","committed","oldKeys","equal","useModel","declaration","store","memoryRef","committedResult","result","requiresModels","models","declared","component","useResourceSlots","cache","existing","createResourceInvokers","unavailablePagination","noPaginationSource","useResource","handle","paginationOf","stateSource","subscribe","useCallback","getState","state","paginationSource","subscribePagination","getPaginationState","paginationState","refresh","retry","reset","loadNext","pagination","emptySlotProperties","defineSlot","id","createContributionTarget","defineSwitchSlot","declarationId","targets","switchSlot","route","target","Slot","useReadable","slotProps","createElement","Fragment","ContributionMount","contributionAuthority"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/command-hook-controller.ts","../src/command-hook.ts","../src/idle-subscription.ts","../src/resource-verb-hook.ts","../src/selection-hooks.ts","../src/model-selection-snapshot.ts","../src/model-hook.ts","../src/requires-models.ts","../src/resource-hook.ts","../src/slot.tsx"],"sourcesContent":["import { CommandError } from '@opetope/core';\nimport { compatibleAbortReason, isCancellation, reportDetachedError } from '@opetope/core/internal';\nimport type { CommandInputArgs, CallRunOptions } from '@opetope/core/internal';\n\nimport type { CommandOutcome } from './command';\nimport { ContributionError } from './errors';\n\n/**\n * D474: a callback's answer is not awaited and changes nothing — the run has already settled — so any value is\n * accepted, and a rejected promise among them is reported like a throw, detached from the outcome it watched.\n */\ntype CommandRunOptions<Output> = Readonly<{\n /** Called with a failure that is not a cancellation; its answer is not awaited, and a throw or rejection is reported. */\n onFailure?: (error: unknown) => unknown;\n /** Called with the body's value; its answer is not awaited and changes no outcome, and a throw or rejection is reported. */\n onSuccess?: (value: Output) => unknown;\n /** Cancels this run's wait: the run answers `cancelled`, and an already-aborted signal never reaches the command. */\n signal?: AbortSignal;\n}>;\n\n/*\n * D474: the options element of the spread form is `CommandRunOptions<Output> | undefined`, so a wrapper that forwards\n * `...args: [...CommandInputArgs<Input>, options?: …]` compiles under `exactOptionalPropertyTypes`, where a spread\n * optional element reads as possibly `undefined`.\n */\n/**\n * The options element of the spread form is `CommandRunOptions<Output> | undefined`.\n */\ntype CommandRun<Input, Output> = {\n (input: Input, options?: CommandRunOptions<Output>): Promise<CommandOutcome<Output>>;\n (\n ...args: [...CommandInputArgs<Input>, options?: CommandRunOptions<Output> | undefined]\n ): Promise<CommandOutcome<Output>>;\n};\n\ntype CommandInvoker = Readonly<{\n run: (input: never, options?: CallRunOptions<unknown>) => Promise<unknown>;\n}>;\n\ntype CommandNotification<Output> = (inFlight: boolean, outcome?: CommandOutcome<Output>) => void;\n\ntype CommandRequest<Input, Output> = {\n input: Input | undefined;\n options: CommandRunOptions<Output> | undefined;\n readonly promise: Promise<CommandOutcome<Output>>;\n resolve: ((outcome: CommandOutcome<Output>) => void) | undefined;\n};\n\nfunction failureOutcome<Output>(options: CommandRunOptions<Output> | undefined, error: unknown): CommandOutcome<never> {\n if (isCancellation(error)) return { reason: error, kind: 'cancelled' };\n\n try {\n const notification = options?.onFailure?.(error);\n\n if (notification !== undefined) void Promise.resolve(notification).catch(reportDetachedError);\n } catch (callbackError) {\n reportDetachedError(callbackError);\n }\n\n return { error, kind: 'failed' };\n}\n\nfunction runOptions<Output>(options: CommandRunOptions<Output> | undefined): CallRunOptions<Output> | undefined {\n const onSuccess = options?.onSuccess;\n const signal = options?.signal;\n\n if (onSuccess === undefined) return signal === undefined ? undefined : { signal };\n\n return signal === undefined ? { onSuccess } : { onSuccess, signal };\n}\n\nfunction requestOf<Input, Output>(\n input: Input,\n options: CommandRunOptions<Output> | undefined,\n): CommandRequest<Input, Output> {\n let resolve!: (outcome: CommandOutcome<Output>) => void;\n const promise = new Promise<CommandOutcome<Output>>(settle => {\n resolve = settle;\n });\n\n return { input, options: options === undefined ? undefined : { ...options }, promise, resolve };\n}\n\nfunction settle<Input, Output>(request: CommandRequest<Input, Output>, outcome: CommandOutcome<Output>): void {\n const resolve = request.resolve;\n request.input = undefined;\n request.options = undefined;\n request.resolve = undefined;\n resolve?.(outcome);\n}\n\n/**\n * D203: the consumer keeps a status, not a schedule. Every `run` reaches the command, and what the command was\n * declared with — `concurrency`, `memoize`, `dedupe` — decides what happens to it. What stays\n * local is what only this consumer knows: whether it is mounted, its own input signal, and the outcome it shows.\n */\nclass CommandHookController<Input, Output> {\n readonly run: CommandRun<Input, Output> = ((input: Input, options?: CommandRunOptions<Output>) => {\n if (this.notify === undefined) {\n return Promise.resolve({\n reason: new ContributionError('inactive', 'Command consumer is not mounted.'),\n kind: 'cancelled' as const,\n });\n }\n\n const aborted = this.cancelledInput(options);\n\n if (aborted !== undefined) return Promise.resolve(aborted);\n\n const request = requestOf(input, options);\n this.start(request);\n\n return request.promise;\n }) as CommandRun<Input, Output>;\n private inFlight = 0;\n private notify: CommandNotification<Output> | undefined;\n\n constructor(private readonly invoker: CommandInvoker) {}\n\n attach(notify: CommandNotification<Output>): () => void {\n this.notify = notify;\n\n return () => {\n if (this.notify !== notify) return;\n\n this.notify = undefined;\n };\n }\n\n synchronize(): void {\n this.notify?.(this.inFlight > 0);\n }\n\n private aborted(signal: AbortSignal): CommandOutcome<never> {\n return {\n reason: new CommandError('cancelled', 'Command input was cancelled.', compatibleAbortReason(signal)),\n kind: 'cancelled',\n };\n }\n\n private cancelledInput(options: CommandRunOptions<Output> | undefined): CommandOutcome<never> | undefined {\n return options?.signal?.aborted === true ? this.aborted(options.signal) : undefined;\n }\n\n private finish(request: CommandRequest<Input, Output>, outcome: CommandOutcome<Output>): void {\n if (request.resolve === undefined) return;\n\n this.inFlight -= 1;\n settle(request, outcome);\n this.notify?.(this.inFlight > 0, outcome);\n }\n\n private invoke(request: CommandRequest<Input, Output>): void {\n let invocation: Promise<Output>;\n\n try {\n invocation = this.invoker.run(request.input as never, runOptions(request.options) as never) as Promise<Output>;\n request.input = undefined;\n } catch (error) {\n this.finish(request, failureOutcome(request.options, error));\n\n return;\n }\n\n void invocation.then(\n value => this.finish(request, { kind: 'ok', value }),\n (error: unknown) => this.finish(request, failureOutcome(request.options, error)),\n );\n }\n\n private start(request: CommandRequest<Input, Output>): void {\n this.inFlight += 1;\n // Reserve the flight before publishing: a reentrant run must already see this consumer as busy.\n this.notify?.(true);\n this.invoke(request);\n }\n}\n\nexport { CommandHookController };\nexport type { CommandInvoker, CommandRun };\n","import { useInsertionEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';\nimport type { RefObject } from 'react';\n\nimport type { Command } from '@opetope/core';\n\nimport { enterCommit, inAct, leaveCommit } from './act-delivery';\nimport type { CommandOutcome } from './command';\nimport { CommandHookController } from './command-hook-controller';\nimport type { CommandInvoker, CommandRun } from './command-hook-controller';\nimport { ContributionError } from './errors';\nimport { useFrame } from './mount-context';\n\n/** D292: a cancellation is not an answer of the product, so it never settles into the outcome this hook shows. */\ntype SettledOutcome<Output> = Exclude<CommandOutcome<Output>, { readonly kind: 'cancelled' }>;\n\n/** D543: the dismissal of a snapshot without an outcome; one function, so its identity says «nothing to clear». */\nconst noOutcome = (): void => undefined;\n\ninterface CommandHookStatus<Output> {\n /**\n * Clears the outcome this snapshot shows, so `outcome` answers `undefined` until the next run settles. A later\n * outcome and the runs in flight stay; call it from an effect or an event handler, not during render.\n */\n readonly dismiss: () => void;\n readonly inFlight: boolean;\n readonly outcome: SettledOutcome<Output> | undefined;\n}\n\nconst emptyStatus: CommandHookStatus<never> = { dismiss: noOutcome, inFlight: false, outcome: undefined };\n\ntype CommandHook<Input, Output> = CommandHookStatus<Output> & {\n readonly run: CommandRun<Input, Output>;\n};\n\ntype CommandHookState<Input, Output> = CommandHookStatus<Output> & {\n readonly slot: CommandHookController<Input, Output>;\n};\n\n/** D292: a cancellation moves nothing, so it is the same non-event as a notification without an outcome at all. */\nfunction settledOutcome<Output>(outcome: CommandOutcome<Output> | undefined): SettledOutcome<Output> | undefined {\n return outcome === undefined || outcome.kind === 'cancelled' ? undefined : outcome;\n}\n\nfunction unchangedState<Input, Output>(\n current: CommandHookState<Input, Output>,\n slot: CommandHookController<Input, Output>,\n inFlight: boolean,\n settled: SettledOutcome<Output> | undefined,\n): boolean {\n return settled === undefined && current.slot === slot && current.inFlight === inFlight;\n}\n\n/** A settled outcome arrives with the dismissal that clears exactly it (D543). */\nfunction nextState<Input, Output>(\n current: CommandHookState<Input, Output>,\n slot: CommandHookController<Input, Output>,\n inFlight: boolean,\n settled: SettledOutcome<Output> | undefined,\n dismiss: () => void,\n): CommandHookState<Input, Output> {\n if (unchangedState(current, slot, inFlight, settled)) return current;\n if (settled !== undefined) return { dismiss, inFlight, outcome: settled, slot };\n // A new run clears nothing: the previous answer stays readable while its retry is in flight.\n if (current.slot === slot) return { dismiss: current.dismiss, inFlight, outcome: current.outcome, slot };\n\n return { dismiss: noOutcome, inFlight, outcome: undefined, slot };\n}\n\n/** D543: only the outcome a dismissal was read with leaves; one that replaced it meanwhile stays. */\nfunction dismissedState<Input, Output>(\n current: CommandHookState<Input, Output>,\n slot: CommandHookController<Input, Output>,\n outcome: SettledOutcome<Output>,\n): CommandHookState<Input, Output> {\n return current.slot === slot && current.outcome === outcome\n ? { dismiss: noOutcome, inFlight: current.inFlight, outcome: undefined, slot }\n : current;\n}\n\n/**\n * D543: the dismissal of one settled outcome. `shown` is the outcome this consumer was last told about, kept outside\n * React state, so a dismissal whose outcome was replaced returns before it asks React for anything.\n */\nfunction dismissalOf<Input, Output>(\n shown: RefObject<SettledOutcome<Output> | undefined>,\n slot: CommandHookController<Input, Output>,\n settled: SettledOutcome<Output>,\n setHookState: (update: (current: CommandHookState<Input, Output>) => CommandHookState<Input, Output>) => void,\n): () => void {\n return () => {\n if (shown.current !== settled) return;\n\n shown.current = undefined;\n setHookState(current => dismissedState(current, slot, settled));\n };\n}\n\nfunction useCommandSlot<Input, Output>(invoker: CommandInvoker): CommandHookController<Input, Output> {\n // Slots are inert until commit. An interrupted render cannot fence the committed source or admit its own work.\n const [slots] = useState(() => new WeakMap<object, CommandHookController<Input, Output>>());\n let slot = slots.get(invoker);\n\n if (slot === undefined) {\n slot = new CommandHookController(invoker);\n slots.set(invoker, slot);\n }\n\n return slot;\n}\n\n/**\n * A command consumer keeps its own status, attaches to its current slot and synchronizes a slot that was already\n * busy when this mount arrived.\n */\nfunction useCommandStatus<Input, Output>(\n currentSlot: CommandHookController<Input, Output>,\n): CommandHook<Input, Output> {\n const [hookState, setHookState] = useState<CommandHookState<Input, Output>>(() => ({\n dismiss: noOutcome,\n inFlight: false,\n outcome: undefined,\n slot: currentSlot,\n }));\n const shown = useRef<SettledOutcome<Output> | undefined>(undefined);\n useInsertionEffect(\n () =>\n currentSlot.attach(\n inAct((inFlight, outcome) => {\n const settled = settledOutcome(outcome);\n let dismiss = noOutcome;\n\n if (settled !== undefined) {\n shown.current = settled;\n // D541: a dismissal is a delivery too, so a timer that clears the outcome commits inside the scenario's\n // act. With no act installed it is the dismissal itself.\n dismiss = inAct(dismissalOf(shown, currentSlot, settled, setHookState));\n }\n\n setHookState(current => nextState(current, currentSlot, inFlight, settled, dismiss));\n }),\n ),\n [currentSlot],\n );\n useLayoutEffect(() => {\n const entered = enterCommit();\n try {\n currentSlot.synchronize();\n } finally {\n leaveCommit(entered);\n }\n }, [currentSlot]);\n const { dismiss, inFlight, outcome } = hookState.slot === currentSlot ? hookState : emptyStatus;\n const run = currentSlot.run;\n\n return useMemo(() => ({ dismiss, inFlight, outcome, run }), [dismiss, inFlight, outcome, run]);\n}\n\nfunction useCommand<Input, Output>(command: Command<Input, Output>): CommandHook<Input, Output> {\n const frame = useFrame();\n const record = frame.commandRecords.get(command);\n\n if (record === undefined) {\n throw new ContributionError('missing', `Command ${command.id} is not bound in this contribution.`);\n }\n\n return useCommandStatus(useCommandSlot<Input, Output>(record.invoker));\n}\n\nexport { noOutcome, useCommand };\nexport type { CommandHook };\n","const idleRelease = (): void => undefined;\nconst subscribeIdle = (): (() => void) => idleRelease;\nconst idleRevision = (): number => 0;\n\nexport { idleRevision, subscribeIdle };\n","import { useInsertionEffect, useLayoutEffect, useMemo, useState } from 'react';\n\nimport type { ResourceOperationOutcome } from '@opetope/core';\n\nimport { enterCommit, inAct, leaveCommit } from './act-delivery';\n\n/** A Resource verb answers its own outcome; only its consumer owns this wait and status. */\ntype ResourceVerbHook = Readonly<{\n inFlight: boolean;\n outcome: ResourceOperationOutcome | undefined;\n run: () => Promise<ResourceOperationOutcome>;\n}>;\n\ntype VerbNotification = (inFlight: boolean, outcome?: ResourceOperationOutcome) => void;\n\ntype VerbWaiter = {\n active: boolean;\n next: VerbWaiter | undefined;\n previous: VerbWaiter | undefined;\n reject: ((error: unknown) => void) | undefined;\n resolve: ((outcome: ResourceOperationOutcome) => void) | undefined;\n};\n\nconst cancelled: ResourceOperationOutcome = Object.freeze({ kind: 'cancelled' });\n\n/**\n * Only Resource verbs use this controller. A detached consumer settles its own waits without cancelling the\n * materialization's work. The linked waiters add no retained collection to a Resource hook that never runs a verb.\n */\nclass ResourceVerbController {\n private inFlight = 0;\n private notify: VerbNotification | undefined;\n private pending: VerbWaiter | undefined;\n\n readonly run = (): Promise<ResourceOperationOutcome> => {\n if (this.notify === undefined) return Promise.resolve(cancelled);\n\n let resolve!: (outcome: ResourceOperationOutcome) => void;\n let reject!: (error: unknown) => void;\n const answer = new Promise<ResourceOperationOutcome>((settle, refuse) => {\n resolve = settle;\n reject = refuse;\n });\n const waiter: VerbWaiter = { active: true, next: this.pending, previous: undefined, reject, resolve };\n if (this.pending !== undefined) this.pending.previous = waiter;\n this.pending = waiter;\n this.inFlight += 1;\n this.notify(true);\n\n // A synchronous subscriber may detach while the busy status is published.\n if (!waiter.active) return answer;\n\n let operation: Promise<ResourceOperationOutcome>;\n\n try {\n operation = Promise.resolve(this.start());\n } catch (error) {\n this.fail(waiter, error);\n\n return answer;\n }\n\n void operation.then(\n outcome => this.finish(waiter, outcome),\n (error: unknown) => this.fail(waiter, error),\n );\n\n return answer;\n };\n\n constructor(private readonly start: () => Promise<ResourceOperationOutcome>) {}\n\n attach(notify: VerbNotification): () => void {\n this.notify = notify;\n\n return () => {\n if (this.notify !== notify) return;\n\n this.notify = undefined;\n let current = this.pending;\n this.pending = undefined;\n this.inFlight = 0;\n\n while (current !== undefined) {\n const next = current.next;\n current.active = false;\n current.next = undefined;\n current.previous = undefined;\n const resolve = current.resolve;\n current.resolve = undefined;\n current.reject = undefined;\n resolve?.(cancelled);\n current = next;\n }\n };\n }\n\n synchronize(): void {\n this.notify?.(this.inFlight > 0);\n }\n\n private unlink(waiter: VerbWaiter): boolean {\n if (!waiter.active) return false;\n\n waiter.active = false;\n if (waiter.previous === undefined) this.pending = waiter.next;\n else waiter.previous.next = waiter.next;\n if (waiter.next !== undefined) waiter.next.previous = waiter.previous;\n waiter.next = undefined;\n waiter.previous = undefined;\n this.inFlight -= 1;\n\n return true;\n }\n\n private finish(waiter: VerbWaiter, outcome: ResourceOperationOutcome): void {\n if (!this.unlink(waiter)) return;\n\n const resolve = waiter.resolve;\n waiter.resolve = undefined;\n waiter.reject = undefined;\n resolve?.(outcome);\n this.notify?.(this.inFlight > 0, outcome);\n }\n\n private fail(waiter: VerbWaiter, error: unknown): void {\n if (!this.unlink(waiter)) return;\n\n const reject = waiter.reject;\n waiter.resolve = undefined;\n waiter.reject = undefined;\n reject?.(error);\n this.notify?.(this.inFlight > 0);\n }\n}\n\ntype VerbHookState = ResourceVerbHook & { readonly slot: ResourceVerbController };\nconst emptyStatus = Object.freeze({ inFlight: false, outcome: undefined });\n\n/** D292: a cancelled wait does not replace the last product answer, even when the promise returns `cancelled`. */\nfunction visibleOutcome(outcome?: ResourceOperationOutcome): ResourceOperationOutcome | undefined {\n return outcome?.kind === 'cancelled' ? undefined : outcome;\n}\n\nfunction nextState(\n current: VerbHookState,\n slot: ResourceVerbController,\n inFlight: boolean,\n outcome?: ResourceOperationOutcome,\n): VerbHookState {\n const previous = current.slot === slot ? current.outcome : undefined;\n const settled = visibleOutcome(outcome);\n\n if (current.slot === slot && current.inFlight === inFlight && settled === undefined) return current;\n\n return { inFlight, outcome: settled ?? previous, run: slot.run, slot };\n}\n\nfunction useResourceVerbStatus(slot: ResourceVerbController): ResourceVerbHook {\n const [hookState, setHookState] = useState<VerbHookState>(() => ({\n inFlight: false,\n outcome: undefined,\n run: slot.run,\n slot,\n }));\n useInsertionEffect(\n () =>\n slot.attach(inAct((inFlight, outcome) => setHookState(current => nextState(current, slot, inFlight, outcome)))),\n [slot],\n );\n useLayoutEffect(() => {\n const entered = enterCommit();\n try {\n slot.synchronize();\n } finally {\n leaveCommit(entered);\n }\n }, [slot]);\n const { inFlight, outcome } = hookState.slot === slot ? hookState : emptyStatus;\n const { run } = slot;\n\n return useMemo(() => ({ inFlight, outcome, run }), [inFlight, outcome, run]);\n}\n\nexport { ResourceVerbController, useResourceVerbStatus };\nexport type { ResourceVerbHook };\n","import { useInsertionEffect, useLayoutEffect, useRef, useSyncExternalStore } from 'react';\n\nimport type { Command } from '@opetope/core';\n\nimport { enterCommit, inAct, leaveCommit } from './act-delivery';\nimport type { CommandOutcome } from './command';\nimport type { CommandHook } from './command-hook';\nimport { noOutcome } from './command-hook';\nimport { CommandHookController } from './command-hook-controller';\nimport type { CommandInvoker } from './command-hook-controller';\nimport { ContributionError } from './errors';\nimport { idleRevision, subscribeIdle } from './idle-subscription';\nimport type { SelectedResource } from './model-selection-snapshot';\nimport { useFrame } from './mount-context';\nimport type { Frame } from './mount-frame';\nimport { resourceInvokers } from './resource-command';\nimport type { ResourceInvokers } from './resource-command';\nimport { ResourceVerbController } from './resource-verb-hook';\nimport type { ResourceVerbHook } from './resource-verb-hook';\n\ntype CommandSelection = Readonly<Record<string, Command<never, unknown>>>;\ntype ResourceSelection = ReadonlyMap<PropertyKey, SelectedResource>;\n/** D359: what one selected Resource answers — its three verbs, its state, and the pagination capability if it has one. */\ntype SelectedResourceHook = Readonly<{\n pagination: Readonly<{ loadNext: ResourceInvokers['loadNext']; state: unknown }> | undefined;\n refresh: ResourceVerbHook;\n reset: ResourceVerbHook;\n retry: ResourceVerbHook;\n state: unknown;\n}>;\ntype SelectionHooks = Readonly<Record<PropertyKey, CommandHook<never, unknown> | SelectedResourceHook>>;\n\nconst emptyHooks: SelectionHooks = Object.freeze({});\n\ntype CommandEntry = {\n detach: (() => void) | undefined;\n hook: CommandHook<never, unknown>;\n readonly invoker: CommandInvoker;\n readonly slot: CommandHookController<never, unknown>;\n};\n\ntype ResourceVerbEntry = {\n detach: (() => void) | undefined;\n hook: ResourceVerbHook;\n readonly invoker: ResourceInvokers['refresh'];\n readonly slot: ResourceVerbController;\n};\n\n/**\n * D359: one key of a selection, three consumers. The record is the value; `entries` still holds one entry per key.\n * D486: `loadNext` is no command consumer — it is the stable function of the Resource, with no status of its own.\n */\ntype ResourceEntry = {\n /** D359: the value this key hands the render, kept on the record so a still frame is the same object again. */\n readonly hook: SelectedResourceHook;\n readonly kind: 'resource';\n readonly loadNext: ResourceInvokers['loadNext'];\n readonly refresh: ResourceVerbEntry;\n readonly reset: ResourceVerbEntry;\n readonly retry: ResourceVerbEntry;\n};\n\ntype SelectionEntry = CommandEntry | ResourceEntry;\n\nfunction isResourceEntry(entry: SelectionEntry): entry is ResourceEntry {\n return 'kind' in entry;\n}\n\nfunction createEntry(invoker: CommandInvoker): CommandEntry {\n const slot = new CommandHookController<never, unknown>(invoker);\n\n return {\n detach: undefined,\n hook: { dismiss: noOutcome, inFlight: false, outcome: undefined, run: slot.run },\n invoker,\n slot,\n };\n}\n\nfunction createVerbEntry(invoker: ResourceInvokers['refresh']): ResourceVerbEntry {\n const slot = new ResourceVerbController(invoker);\n\n return { detach: undefined, hook: { inFlight: false, outcome: undefined, run: slot.run }, invoker, slot };\n}\n\nfunction unchangedHook(\n previous: { readonly inFlight: boolean; readonly outcome: unknown },\n next: { readonly inFlight: boolean; readonly outcome: unknown },\n): boolean {\n return previous.inFlight === next.inFlight && Object.is(previous.outcome, next.outcome);\n}\n\ntype SettledCommandOutcome = NonNullable<CommandHook<never, unknown>['outcome']>;\ntype DismissalOf = (entry: CommandEntry, outcome: SettledCommandOutcome) => () => void;\n\nfunction updateEntry(\n entry: CommandEntry,\n dismissalOf: DismissalOf,\n inFlight: boolean,\n outcome?: CommandOutcome<unknown>,\n): boolean {\n const previous = entry.hook;\n\n if (outcome === undefined && previous.inFlight === inFlight) return false;\n\n // D292: only a settled non-cancelled outcome replaces the one this key shows; a retry keeps the previous answer.\n const settled = outcome === undefined || outcome.kind === 'cancelled' ? undefined : outcome;\n\n if (settled === undefined) {\n if (previous.inFlight === inFlight) return false;\n\n entry.hook = { dismiss: previous.dismiss, inFlight, outcome: previous.outcome, run: previous.run };\n\n return true;\n }\n\n // D543: a settled outcome is a new object, and the dismissal read with it clears only it.\n entry.hook = { dismiss: dismissalOf(entry, settled), inFlight, outcome: settled, run: previous.run };\n\n return true;\n}\n\nfunction updateVerbEntry(entry: ResourceVerbEntry, inFlight: boolean, outcome?: ResourceVerbHook['outcome']): boolean {\n const previous = entry.hook;\n const settled = outcome?.kind === 'cancelled' ? undefined : outcome;\n\n if (settled === undefined && previous.inFlight === inFlight) return false;\n\n const next = { inFlight, outcome: settled ?? previous.outcome, run: previous.run };\n\n if (unchangedHook(previous, next)) return false;\n\n entry.hook = next;\n\n return true;\n}\n\nfunction selectEntry(previous: SelectionEntry | undefined, invoker: CommandInvoker): CommandEntry {\n return previous !== undefined && !isResourceEntry(previous) && previous.invoker === invoker\n ? previous\n : createEntry(invoker);\n}\n\nfunction resourceHook(entry: Omit<ResourceEntry, 'hook'>, selected: SelectedResource): SelectedResourceHook {\n return {\n pagination:\n selected.pagination === undefined ? undefined : { loadNext: entry.loadNext, state: selected.pagination },\n refresh: entry.refresh.hook,\n reset: entry.reset.hook,\n retry: entry.retry.hook,\n state: selected.state,\n };\n}\n\nfunction createResourceEntry(invokers: ResourceInvokers, selected: SelectedResource): ResourceEntry {\n const parts = {\n kind: 'resource' as const,\n loadNext: invokers.loadNext,\n refresh: createVerbEntry(invokers.refresh),\n reset: createVerbEntry(invokers.reset),\n retry: createVerbEntry(invokers.retry),\n };\n\n return { ...parts, hook: resourceHook(parts, selected) };\n}\n\n/** The three commands of a Resource, which every one has. */\nfunction unchangedResourceCommands(entry: ResourceEntry): boolean {\n const { hook } = entry;\n\n return hook.refresh === entry.refresh.hook && hook.reset === entry.reset.hook && hook.retry === entry.retry.hook;\n}\n\n/** What this selection read: the state of the Resource, and the state of its capability where it has one. */\nfunction unchangedResourceState(entry: ResourceEntry, selected: SelectedResource): boolean {\n return Object.is(entry.hook.state, selected.state) && Object.is(entry.hook.pagination?.state, selected.pagination);\n}\n\nfunction unchangedResourceHook(entry: ResourceEntry, selected: SelectedResource): boolean {\n return unchangedResourceCommands(entry) && unchangedResourceState(entry, selected);\n}\n\n/**\n * S6/D359: the same Resource that did not move answers the same object, so a child under `memo` is not re-rendered by\n * the mere act of reading it. Every part is a render input, so this is a memo and not a piece of state.\n */\nfunction selectResourceEntry(\n previous: SelectionEntry | undefined,\n invokers: ResourceInvokers,\n selected: SelectedResource,\n): ResourceEntry {\n if (previous === undefined || !isResourceEntry(previous) || previous.refresh.invoker !== invokers.refresh) {\n return createResourceEntry(invokers, selected);\n }\n\n // Preparing a suspended render must not replace the hook on the committed entry. The three command consumers are\n // still the same logical consumers; only the selected Resource wrapper is new until commit.\n return unchangedResourceHook(previous, selected) ? previous : { ...previous, hook: resourceHook(previous, selected) };\n}\n\nfunction detachEntry(entry: SelectionEntry): void {\n if (isResourceEntry(entry)) {\n for (const verb of [entry.refresh, entry.reset, entry.retry]) {\n verb.detach?.();\n verb.detach = undefined;\n }\n\n return;\n }\n\n entry.detach?.();\n entry.detach = undefined;\n}\n\nfunction sameResourceConsumers(previous: ResourceEntry, next: ResourceEntry): boolean {\n return previous.refresh === next.refresh && previous.reset === next.reset && previous.retry === next.retry;\n}\n\nfunction sameConsumers(previous: SelectionEntry, next: SelectionEntry | undefined): boolean {\n if (previous === next) return true;\n if (next === undefined || !isResourceEntry(previous) || !isResourceEntry(next)) return false;\n\n return sameResourceConsumers(previous, next);\n}\n\nfunction hookOf(entry: SelectionEntry): CommandHook<never, unknown> | SelectedResourceHook {\n return entry.hook;\n}\n\n/** Selection is inert during render; only a committed selection can admit work or discard an old queue. */\nclass CommandGroup {\n private entries = new Map<PropertyKey, SelectionEntry>();\n /** One set of invokers per Resource identity, so `run` keeps its reference across renders (D359). */\n private readonly invokers = new WeakMap<object, ResourceInvokers>();\n private readonly listeners = new Set<() => void>();\n private revision = 0;\n\n close(): void {\n for (const entry of this.entries.values()) detachEntry(entry);\n\n this.entries.clear();\n }\n\n commit(selected: Map<PropertyKey, SelectionEntry>): void {\n for (const [key, entry] of this.entries) {\n if (!sameConsumers(entry, selected.get(key))) detachEntry(entry);\n }\n\n this.entries = selected;\n\n for (const entry of selected.values()) this.attachEntry(entry);\n }\n\n /** S7: the attach runs on every commit, so the verbs are named rather than gathered into an array. */\n private attachEntry(entry: SelectionEntry): void {\n if (isResourceEntry(entry)) {\n this.attachVerb(entry.refresh);\n this.attachVerb(entry.retry);\n this.attachVerb(entry.reset);\n\n return;\n }\n\n this.attachCommand(entry);\n }\n\n private attachCommand(command: CommandEntry): void {\n command.detach ??= command.slot.attach((inFlight, outcome) => {\n if (!updateEntry(command, this.dismissalOf, inFlight, outcome)) return;\n\n this.publish();\n });\n }\n\n /** D543: a dismissal of a detached key or of an outcome already replaced changes nothing and notifies nobody. */\n private readonly dismissalOf: DismissalOf = (command, outcome) => () => {\n const hook = command.hook;\n\n if (command.detach === undefined || hook.outcome !== outcome) return;\n\n command.hook = { dismiss: noOutcome, inFlight: hook.inFlight, outcome: undefined, run: hook.run };\n this.publish();\n };\n\n private publish(): void {\n this.revision += 1;\n\n for (const listener of this.listeners) listener();\n }\n\n private attachVerb(verb: ResourceVerbEntry): void {\n verb.detach ??= verb.slot.attach((inFlight, outcome) => {\n if (!updateVerbEntry(verb, inFlight, outcome)) return;\n\n this.publish();\n });\n }\n\n getSnapshot = (): number => this.revision;\n\n /**\n * D321: two outcomes no longer, but two kinds. A command that is not bound is still a `ContributionError`; a\n * resource never appears in `frame.commandRecords`, so it is prepared from its own identity instead.\n */\n prepare(commands: CommandSelection, resources: ResourceSelection, frame: Frame): Map<PropertyKey, SelectionEntry> {\n const selected = new Map<PropertyKey, SelectionEntry>();\n\n for (const key of Reflect.ownKeys(commands)) {\n if (!Object.prototype.propertyIsEnumerable.call(commands, key)) continue;\n\n const command = (commands as Readonly<Record<PropertyKey, Command<never, unknown>>>)[key];\n const record = command === undefined ? undefined : frame.commandRecords.get(command);\n\n if (record === undefined) {\n throw new ContributionError('missing', `Command at ${String(key)} is not bound in this contribution.`);\n }\n\n selected.set(key, selectEntry(this.entries.get(key), record.invoker));\n }\n\n for (const [key, resource] of resources) {\n const invokers = resourceInvokers(resource.resource, this.invokers);\n\n selected.set(key, selectResourceEntry(this.entries.get(key), invokers, resource));\n }\n\n return selected;\n }\n\n subscribe = (listener: () => void): (() => void) => {\n const delivered = inAct(listener);\n this.listeners.add(delivered);\n\n return () => {\n this.listeners.delete(delivered);\n };\n };\n}\n\nfunction useCommandGroup(commands: CommandSelection, resources: ResourceSelection): CommandGroup | undefined {\n const groupRef = useRef<CommandGroup | undefined>(undefined);\n const populated =\n resources.size > 0 ||\n Reflect.ownKeys(commands).some(key => Object.prototype.propertyIsEnumerable.call(commands, key));\n\n return populated ? (groupRef.current ??= new CommandGroup()) : undefined;\n}\n\n/** The consumers of one selection: a command per key, and the two verbs of a resource per key (D321). */\nfunction useSelectionHooks(commands: CommandSelection, resources: ResourceSelection): SelectionHooks {\n const frame = useFrame();\n const group = useCommandGroup(commands, resources);\n const getSnapshot = group?.getSnapshot ?? idleRevision;\n useSyncExternalStore(group?.subscribe ?? subscribeIdle, getSnapshot, getSnapshot);\n const selected = group?.prepare(commands, resources, frame);\n useInsertionEffect(\n () => () => {\n const entered = enterCommit();\n try {\n group?.close();\n } finally {\n leaveCommit(entered);\n }\n },\n [group],\n );\n useInsertionEffect(() => {\n if (selected === undefined) return;\n\n const entered = enterCommit();\n try {\n group?.commit(selected);\n } finally {\n leaveCommit(entered);\n }\n });\n useLayoutEffect(() => {\n if (selected === undefined) return;\n\n // S7: the same branch as the attach — this runs after every commit, and the arrays were pure allocation.\n const entered = enterCommit();\n try {\n for (const entry of selected.values()) {\n if (isResourceEntry(entry)) {\n entry.refresh.slot.synchronize();\n entry.retry.slot.synchronize();\n entry.reset.slot.synchronize();\n } else entry.slot.synchronize();\n }\n } finally {\n leaveCommit(entered);\n }\n });\n\n return selected === undefined\n ? emptyHooks\n : Object.fromEntries([...selected].map(([key, entry]) => [key, hookOf(entry)]));\n}\n\nexport { useSelectionHooks };\nexport type { SelectionHooks };\n","import type { Readable, ResourceView } from '@opetope/core';\nimport {\n enterSelectorEvaluation,\n isCallTarget,\n isReadable,\n isResourceView,\n leaveSelectorEvaluation,\n resourceViewDemands,\n resourceViewReadable,\n} from '@opetope/core/internal';\nimport type { ResourceDemand } from '@opetope/core/internal';\n\nimport { ContributionError } from './errors';\nimport type { AnyCommand } from './model-binding';\nimport { isResource } from './resource-command';\nimport type { AnyResource } from './resource-command';\n\ntype SelectionValue<Candidate> =\n Candidate extends ResourceView<infer Value> ? Value : Candidate extends Readable<infer Value> ? Value : never;\n\ninterface SelectionRead {\n <Candidate extends Readable<unknown> | ResourceView<unknown>>(source: Candidate): SelectionValue<Candidate>;\n <Candidate extends Readable<unknown> | ResourceView<unknown>, Selected>(\n source: Candidate,\n select: (value: SelectionValue<Candidate>) => Selected,\n ): Selected;\n}\n\n/** The tracked reader passed to a `useModel` selector. Reading a view records demand for commit. */\ntype SelectionContext = Readonly<{\n /** Read a source for this selection, tracking it so the selection updates when it changes. */\n read: SelectionRead;\n}>;\n\ntype SelectionRecord = Readonly<Record<PropertyKey, unknown>>;\n/**\n * D359: the Resource a field selected, and what this selection read from it — its state, and the state of its\n * pagination capability when it has one. Both are read passively: a selection takes no lease by reading.\n */\ntype SelectedResource = Readonly<{ pagination: unknown; resource: AnyResource; state: unknown }>;\ntype SelectionSnapshot = Readonly<{\n commands: Readonly<Record<PropertyKey, AnyCommand>>;\n /** The set the mount leases; two keys naming one resource are one lease. */\n leases: ReadonlySet<ResourceDemand>;\n resources: ReadonlyMap<PropertyKey, SelectedResource>;\n sources: ReadonlySet<Readable<unknown>>;\n values: SelectionRecord;\n}>;\n\nconst noResources: ReadonlyMap<PropertyKey, SelectedResource> = new Map();\n\n/**\n * D363: what this mount read last and the snapshot it answered with. It belongs to the mount and not to the reader:\n * React may discard the `useMemo` that builds the reader, and a memory kept there would be empty exactly when it is\n * needed. D364: it no longer remembers which sources ended — a source whose owner retired stops instead of refusing,\n * so it answers its last value on every read and there is nothing to remember or to step around.\n */\ntype SelectionMemory = {\n model: unknown;\n observed: Map<Readable<unknown>, unknown> | undefined;\n select: unknown;\n snapshot: SelectionSnapshot;\n};\n\nconst noLeases: ReadonlySet<ResourceDemand> = new Set();\n\nfunction readSource<Value>(source: Readable<Value>, sources: Map<Readable<unknown>, unknown>): Value {\n if (!isReadable(source)) {\n throw new ContributionError('binding-invalid', 'Model selection read requires a Readable or ResourceView.');\n }\n\n if (sources.has(source)) return sources.get(source) as Value;\n\n // D364: a refusal here is the source's own failure and belongs to the render, because no lifetime ends in one.\n const value: unknown = source.getSnapshot();\n sources.set(source, value);\n\n return value as Value;\n}\n\nfunction readPassively(source: Readable<unknown>, sources: Map<Readable<unknown>, unknown>): unknown {\n if (sources.has(source)) return sources.get(source);\n\n const value: unknown = source.getSnapshot();\n sources.set(source, value);\n\n return value;\n}\n\n/** D525: the built-in containers a selection result may not be, beside an array and a thenable. */\nconst CONTAINERS: readonly (abstract new (...args: never[]) => object)[] = [Date, Map, Set, RegExp, WeakMap, WeakSet];\n\n/** D525: what the types no longer refuse as a selection result, refused here as it was there. */\nfunction isContainer(value: object): boolean {\n if (Array.isArray(value) || typeof (value as { readonly then?: unknown }).then === 'function') return true;\n\n for (const container of CONTAINERS) if (value instanceof container) return true;\n\n return false;\n}\n\n/**\n * D525: a field of the result that is still a source would hand the component the source object — a truth test always\n * passes and a render never updates — so the selection refuses it by name. The types no longer check the result, and\n * this is where the refusal stands, in development and in production alike. A Resource is a field by D359 and answers\n * its hook; a Command answers its hook too. Only the field itself is asked, so a source one level down passes.\n */\nfunction refuseSource(key: PropertyKey, value: unknown): void {\n if (isReadable(value)) {\n throw new TypeError(`A selector returned a Readable in field \"${String(key)}\": read(source) inside the selector.`);\n }\n if (isResourceView(value)) {\n throw new TypeError(\n `A selector returned a ResourceView in field \"${String(key)}\": read(view) inside the selector, or select the Resource.`,\n );\n }\n}\n\n/** D359: the predicate that answers the question the type of the field asked, and the read that leases nothing. */\nfunction classifyResource(\n key: PropertyKey,\n value: unknown,\n resources: Map<PropertyKey, SelectedResource>,\n sources: Map<Readable<unknown>, unknown>,\n): void {\n if (!isResource(value)) {\n refuseSource(key, value);\n\n return;\n }\n\n const resource = value as AnyResource;\n const capability = (\n resource as { readonly pagination?: { readonly state: Readable<unknown> } | ResourceView<unknown> }\n ).pagination;\n\n resources.set(key, {\n pagination: capability === undefined ? undefined : readPassively(resourceViewReadable(capability), sources),\n resource,\n state: readPassively(resourceViewReadable(resource), sources),\n });\n}\n\nfunction selectedResources(resources: Map<PropertyKey, SelectedResource>): ReadonlyMap<PropertyKey, SelectedResource> {\n return resources.size === 0 ? noResources : resources;\n}\n\ntype CollectedSelection = Readonly<{\n leases: ReadonlySet<ResourceDemand>;\n resources: ReadonlyMap<PropertyKey, SelectedResource>;\n values: SelectionRecord;\n}>;\n\nfunction collectResourceDemands(\n resources: ReadonlyMap<PropertyKey, SelectedResource>,\n leases: Set<ResourceDemand>,\n): void {\n for (const { resource } of resources.values()) {\n if (isResourceView(resource)) {\n for (const demand of resourceViewDemands(resource)) leases.add(demand);\n } else {\n leases.add(resource);\n }\n }\n}\n\n/**\n * D359: recognition happens in the one pass that already walks the selection. `Resource` is a structural contract\n * (D199) and the type of a selected field promises `ResourceHook` for anything that satisfies it, so the predicate\n * this uses answers the same question the type asked. Reading the state here also puts it among `sources`, so its\n * publication invalidates the fast path of this reader exactly as a read source does.\n */\nfunction collectSelection(\n select: (context: SelectionContext) => object,\n sources: Map<Readable<unknown>, unknown>,\n): CollectedSelection {\n const leases = new Set<ResourceDemand>();\n let open = true;\n const read = <Value, Selected>(\n source: Readable<Value> | ResourceView<Value>,\n project?: (value: Value) => Selected,\n ) => {\n // D518: what a selection read belongs to the snapshot it built; a reader kept past it would add demand to a\n // committed snapshot nobody compares again.\n if (!open)\n throw new TypeError(\"A selector's read was called after the selector returned: read inside the selector.\");\n\n let readable: Readable<Value>;\n if (isResourceView(source)) {\n for (const demand of resourceViewDemands(source)) leases.add(demand);\n readable = source.observe();\n } else {\n readable = source;\n }\n const value = readSource(readable, sources);\n\n return project === undefined ? value : project(value);\n };\n enterSelectorEvaluation();\n try {\n const selected = select({ read });\n\n if (typeof selected !== 'object' || selected === null || isContainer(selected)) {\n throw new ContributionError(\n 'binding-invalid',\n 'Model selection requires a record of named fields, not an array, a promise or another container.',\n );\n }\n\n const entries: (readonly [PropertyKey, unknown])[] = [];\n const resources = new Map<PropertyKey, SelectedResource>();\n\n for (const key of Reflect.ownKeys(selected)) {\n if (!Object.prototype.propertyIsEnumerable.call(selected, key)) continue;\n\n const value = (selected as SelectionRecord)[key];\n\n entries.push([key, value]);\n classifyResource(key, value, resources, sources);\n }\n\n // `__proto__` is a key like any other here, and only a defined property keeps it one (D214).\n collectResourceDemands(resources, leases);\n return { leases, resources: selectedResources(resources), values: Object.fromEntries(entries) };\n } finally {\n open = false;\n leaveSelectorEvaluation();\n }\n}\n\n/**\n * D359: `values` keeps the Resource itself — a stable reference — so a selection that holds one is compared by\n * `Object.is` exactly as before. What moves is the state behind it, and that is compared here: without this the\n * selection would be «equal», the previous state would be kept, and a published Resource would reach no render.\n */\nfunction equalResources(\n previous: ReadonlyMap<PropertyKey, SelectedResource>,\n next: ReadonlyMap<PropertyKey, SelectedResource>,\n): boolean {\n if (previous.size !== next.size) return false;\n\n for (const [key, selected] of next) {\n const before = previous.get(key);\n\n if (\n before?.resource !== selected.resource ||\n !Object.is(before.state, selected.state) ||\n !Object.is(before.pagination, selected.pagination)\n ) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction equalMembers<Value>(previous: ReadonlySet<Value>, next: ReadonlySet<Value>): boolean {\n if (previous.size !== next.size) return false;\n for (const value of next) if (!previous.has(value)) return false;\n return true;\n}\n\nfunction equalSelection(\n previous: SelectionSnapshot,\n values: SelectionRecord,\n sources: ReadonlySet<Readable<unknown>>,\n resources: ReadonlyMap<PropertyKey, SelectedResource>,\n leases: ReadonlySet<ResourceDemand>,\n) {\n const keys = Reflect.ownKeys(values);\n const previousKeys = Reflect.ownKeys(previous.values);\n\n if (keys.length !== previousKeys.length) return false;\n if (!equalMembers(previous.sources, sources)) return false;\n if (!equalMembers(previous.leases, leases)) return false;\n\n if (!equalResources(previous.resources, resources)) return false;\n\n return keys.every((key, index) => previousKeys[index] === key && Object.is(previous.values[key], values[key]));\n}\n\ntype ModelSelector<Model> = (model: Model, context: SelectionContext) => object;\ntype SelectionReaderRef<Model> = { readonly current: ModelSelector<Model> | undefined };\n\nconst emptySelectionSnapshot: SelectionSnapshot = {\n commands: {},\n leases: noLeases,\n resources: noResources,\n sources: new Set(),\n values: {},\n};\n\nfunction selectionCommands(values: SelectionRecord): Readonly<Record<PropertyKey, AnyCommand>> {\n return Object.fromEntries(\n Reflect.ownKeys(values)\n .filter(key => isCallTarget(values[key]))\n .map(key => [key, values[key]]),\n ) as Readonly<Record<PropertyKey, AnyCommand>>;\n}\n\n/**\n * The fast path re-reads what it observed. D364: a source that stopped answers the same value it answered last, so\n * it compares equal and costs one read — there is no terminal source to record and step around any more.\n */\nfunction observedUnchanged(observed: Map<Readable<unknown>, unknown>): boolean {\n enterSelectorEvaluation();\n try {\n for (const [source, value] of observed) {\n if (!Object.is(source.getSnapshot(), value)) return false;\n }\n\n return true;\n } finally {\n leaveSelectorEvaluation();\n }\n}\n\n/** D363: the memory of one mount, empty until its first read and reset when the reader is built for another model. */\nfunction createSelectionMemory(): SelectionMemory {\n return { model: undefined, observed: undefined, select: undefined, snapshot: emptySelectionSnapshot };\n}\n\n/**\n * D214: one reader per model, not per render. The author's selector arrives through a ref, so an inline arrow does not\n * rebuild the reader; the fast path — nothing observed has moved — belongs to the selector that computed the snapshot,\n * so a new selector is always called again, its result compared field by field, and an equal selection keeps the\n * previous snapshot. A rejected or abandoned render still changes nothing: only a committed selection subscribes.\n *\n * D363: what the reader remembers lives in the mount's memory, so a reader React rebuilt — because it discarded the\n * memo — still knows what this selection read last.\n */\nfunction createSelectionSnapshot<Model>(\n model: Model,\n selectRef: SelectionReaderRef<Model>,\n memory: SelectionMemory,\n): () => SelectionSnapshot {\n if (memory.model !== model) {\n memory.model = model;\n memory.observed = undefined;\n memory.select = undefined;\n memory.snapshot = emptySelectionSnapshot;\n }\n\n return () => {\n const select = selectRef.current;\n\n if (select === undefined) return emptySelectionSnapshot;\n\n const observed = memory.observed;\n\n if (observed !== undefined && memory.select === select && observedUnchanged(observed)) {\n return memory.snapshot;\n }\n\n const nextObserved = new Map<Readable<unknown>, unknown>();\n const { leases, resources, values } = collectSelection(context => select(model, context), nextObserved);\n const sources = new Set(nextObserved.keys());\n\n if (observed === undefined || !equalSelection(memory.snapshot, values, sources, resources, leases)) {\n memory.snapshot = {\n commands: selectionCommands(values),\n leases,\n resources,\n sources,\n values,\n };\n }\n\n memory.observed = nextObserved;\n memory.select = select;\n\n return memory.snapshot;\n };\n}\n\nexport { createSelectionMemory, createSelectionSnapshot, emptySelectionSnapshot };\nexport type { ModelSelector, SelectedResource, SelectionContext, SelectionMemory, SelectionSnapshot };\n","import { useLayoutEffect, useMemo, useRef, useSyncExternalStore } from 'react';\n\nimport type { Command, ModelOf, PaginatedResource, Resource } from '@opetope/core';\n\nimport { enterCommit, leaveCommit } from './act-delivery';\nimport type { CommandHook } from './command-hook';\nimport { useSelectionHooks } from './selection-hooks';\nimport type { SelectionHooks } from './selection-hooks';\nimport { ContributionError } from './errors';\nimport { subscribeIdle } from './idle-subscription';\nimport type { AnyModel } from './model-binding';\nimport { createSelectionMemory, createSelectionSnapshot, emptySelectionSnapshot } from './model-selection-snapshot';\nimport type { ModelSelector, SelectionContext, SelectionMemory, SelectionSnapshot } from './model-selection-snapshot';\nimport { ModelSelectionStore } from './model-selection-store';\nimport { useFrame } from './mount-context';\nimport type { PaginatedResourceHook, ResourceHook, ResourceKey } from './resource-command';\nimport type { RefusedSelection, SelectorModel } from './selector-sources';\n\n/**\n * D359: a field whose value is a Resource answers the hook `useResource` answers, and the mount leases it exactly as\n * `useResource` does. `read(resource.state)` is unchanged — it hands out the state and leases nothing — so naming one\n * Resource both ways registers it twice: the subscription and the lease are independent.\n */\ntype SelectedValue<Value> =\n Value extends Command<infer Input, infer Output>\n ? CommandHook<Input, Output>\n : Value extends PaginatedResource<\n infer Data,\n infer Key extends ResourceKey,\n infer Cursor extends ResourceKey,\n 'consumers' | 'model'\n >\n ? PaginatedResourceHook<Data, Key, Cursor>\n : Value extends Resource<infer Data, infer Key extends ResourceKey, 'consumers' | 'model'>\n ? ResourceHook<Data, Key>\n : Value;\ntype ModelSelection<Selected> = { readonly [Key in keyof Selected]: SelectedValue<Selected[Key]> };\n\nconst readEmptySnapshot = () => emptySelectionSnapshot;\n\nfunction useSelectionStore(selected: boolean): ModelSelectionStore | undefined {\n const storeRef = useRef<ModelSelectionStore | undefined>(undefined);\n\n return selected ? (storeRef.current ??= new ModelSelectionStore()) : undefined;\n}\n\ntype SelectionResult = Readonly<Record<PropertyKey, unknown>>;\n\n/**\n * Two branches of one key: the consumer this selection prepared — a bound command, or the verbs, the state and the\n * capability of a selected Resource — and otherwise the value the selector wrote. S6/D359: the Resource branch is\n * assembled on the record and not here, so a key whose state did not move hands back the very same object.\n */\nfunction selectedValue(snapshot: SelectionSnapshot, hooks: SelectionHooks, key: PropertyKey): unknown {\n return Object.prototype.hasOwnProperty.call(hooks, key) ? hooks[key] : snapshot.values[key];\n}\n\nfunction mergeSelection(\n snapshot: SelectionSnapshot,\n hooks: SelectionHooks,\n committed: SelectionResult | undefined,\n): SelectionResult {\n const keys = Reflect.ownKeys(snapshot.values);\n\n if (committed !== undefined) {\n const oldKeys = Reflect.ownKeys(committed);\n\n if (keys.length === oldKeys.length) {\n let equal = true;\n for (let index = 0; index < keys.length; index += 1) {\n const key = keys[index]!;\n if (key !== oldKeys[index] || !Object.is(committed[key], selectedValue(snapshot, hooks, key))) {\n equal = false;\n break;\n }\n }\n if (equal) return committed;\n }\n }\n\n return Object.fromEntries(keys.map(key => [key, selectedValue(snapshot, hooks, key)]));\n}\n\n/** Read a granted model; a selection reads sources through `read` and binds Command or Resource fields. */\nfunction useModel<Declaration extends AnyModel>(declaration: Declaration): ModelOf<Declaration>;\nfunction useModel<Declaration extends AnyModel, const Selected extends object = RefusedSelection>(\n declaration: Declaration,\n select: (model: SelectorModel<ModelOf<Declaration>>, context: SelectionContext) => Selected,\n): ModelSelection<Selected>;\nfunction useModel<Declaration extends AnyModel>(\n declaration: Declaration,\n select?: (model: SelectorModel<ModelOf<Declaration>>, context: SelectionContext) => object,\n): unknown {\n const frame = useFrame();\n\n if (!frame.models.has(declaration)) {\n throw new ContributionError('missing', `Model ${declaration.id} is not granted to this contribution.`);\n }\n\n const model = frame.models.get(declaration) as SelectorModel<ModelOf<Declaration>>;\n const store = useSelectionStore(select !== undefined);\n // D214: the selector of this render, read by a reader that belongs to the model. Writing the latest callback into\n // a ref is what `useSyncExternalStoreWithSelector` does: the reader only reads it, and never writes to it.\n const selectRef = useRef<ModelSelector<SelectorModel<ModelOf<Declaration>>> | undefined>(undefined);\n selectRef.current = select;\n const selected = select !== undefined;\n // D363: the memory of this selection belongs to the mount, so a reader React rebuilt keeps what it read last.\n const memoryRef = useRef<SelectionMemory | undefined>(undefined);\n const memory = (memoryRef.current ??= createSelectionMemory());\n const getSnapshot = useMemo(\n () => (selected ? createSelectionSnapshot(model, selectRef, memory) : readEmptySnapshot),\n [memory, model, selected],\n );\n const snapshot = useSyncExternalStore(store?.subscribe ?? subscribeIdle, getSnapshot, getSnapshot);\n const hooks = useSelectionHooks(snapshot.commands, snapshot.resources);\n useLayoutEffect(\n () => () => {\n const entered = enterCommit();\n try {\n store?.close();\n } finally {\n leaveCommit(entered);\n }\n },\n [store],\n );\n // D359: a lease is taken in commit and never in a render, and the store diffs the set by identity.\n useLayoutEffect(() => {\n const entered = enterCommit();\n try {\n store?.commit(snapshot.sources, snapshot.leases);\n } finally {\n leaveCommit(entered);\n }\n }, [snapshot, store]);\n\n // An abandoned render may prepare another record, but only a committed render replaces the comparison baseline.\n const committedResult = useRef<SelectionResult | undefined>(undefined);\n const result = selected ? mergeSelection(snapshot, hooks, committedResult.current) : undefined;\n useLayoutEffect(() => {\n committedResult.current = result;\n }, [result]);\n\n if (select === undefined) return model;\n\n return result;\n}\n\nexport { useModel };\n","import type { FunctionComponent } from 'react';\n\nimport type { ModelIdentity } from '@opetope/core/internal';\n\nimport { ContributionError } from './errors';\n\ntype AnyModel = ModelIdentity;\n\n/*\n * The marker a component uses to state which UI models of its contribution it reads. `slot` checks by type that the\n * contribution grants every model of this list — it may grant more, because the mount serves its whole instance.\n *\n * The runtime authority is the mount frame, not this marker: `useModel` resolves against what the mount was given\n * and refuses anything else. The lint rule `opetope/require-declared-models` checks visible contribution sites and\n * each reader of a per-mount model within the same module. Imported implementations and dynamic declaration lists\n * remain unknown to this syntactic check (D85, D158, D250).\n */\n/**\n * The marker a component uses to state which UI models of its contribution it reads.\n */\ntype ComponentRequiringModels<Props, Models extends readonly AnyModel[]> = FunctionComponent<Props> & {\n readonly requires: Models;\n};\n\n/*\n * Declares the UI models a component reads and returns the same component marked with them, so `slot` can check by\n * type that the contribution grants each one. Marking a component that already declares its models throws.\n *\n * Write the component as a top-level function and wrap it after — `function CountView() { … }`, then\n * `export const Count = requiresModels([CountModel])(CountView)`: React Compiler in infer mode does not compile a\n * component written inside the call.\n */\n/**\n * Declares the UI models a component reads and returns the same component marked with them, so `slot` can check by type that the contribution grants each one.\n */\nfunction requiresModels<const Models extends readonly AnyModel[]>(\n models: Models,\n): <Props>(component: FunctionComponent<Props>) => ComponentRequiringModels<Props, Models> {\n const declared = Object.freeze([...models]) as unknown as Models;\n\n return <Props>(component: FunctionComponent<Props>): ComponentRequiringModels<Props, Models> => {\n const existing = (component as { readonly requires?: readonly AnyModel[] }).requires;\n\n if (existing !== undefined) {\n throw new ContributionError('duplicate', 'Component already declares the models it requires.');\n }\n\n Object.defineProperty(component, 'requires', { enumerable: false, value: declared });\n\n return component as ComponentRequiringModels<Props, Models>;\n };\n}\n\nexport { requiresModels };\n","import { useCallback, useLayoutEffect, useMemo, useState, useSyncExternalStore } from 'react';\n\nimport type { PaginatedResource, PaginationState, Readable, Resource } from '@opetope/core';\nimport type { ResourceKey } from '@opetope/core/internal';\nimport { resourceViewDemands, resourceViewReadable } from '@opetope/core/internal';\n\nimport { enterCommit, inAct, leaveCommit } from './act-delivery';\nimport { ModelSelectionStore } from './model-selection-store';\nimport { createResourceInvokers, paginationOf } from './resource-command';\nimport type { AnyResource, PaginatedResourceHook, ResourceHook, ResourcePaginationHook } from './resource-command';\nimport { ResourceVerbController, useResourceVerbStatus } from './resource-verb-hook';\n\ntype ResourceSlots = Readonly<{\n /** D486: the stable function of `pagination.loadNext`, kept with the verbs of the same Resource identity. */\n loadNext: (options?: { readonly retry?: boolean }) => Promise<unknown>;\n refresh: ResourceVerbController;\n reset: ResourceVerbController;\n retry: ResourceVerbController;\n}>;\n\n/**\n * D359: one set of controllers per Resource identity, exactly as `useCommandSlot` keeps one per invoker. The slots are\n * what the set of invokers is kept alive by, so one map answers for all three verbs and `loadNext`. Slots are inert\n * until commit, so an interrupted render neither admits work nor resets the status of the committed one.\n */\nfunction useResourceSlots(resource: AnyResource): ResourceSlots {\n const [cache] = useState(() => new WeakMap<object, ResourceSlots>());\n const existing = cache.get(resource);\n\n if (existing !== undefined) return existing;\n\n const invokers = createResourceInvokers(resource);\n const slots: ResourceSlots = {\n loadNext: invokers.loadNext,\n refresh: new ResourceVerbController(invokers.refresh),\n reset: new ResourceVerbController(invokers.reset),\n retry: new ResourceVerbController(invokers.retry),\n };\n cache.set(resource, slots);\n\n return slots;\n}\n\nconst unavailablePagination: PaginationState<ResourceKey> = Object.freeze({\n kind: 'unavailable',\n reason: 'resource-not-ready',\n});\nconst noPaginationSource: Readable<PaginationState<ResourceKey>> = Object.freeze({\n getSnapshot: () => unavailablePagination,\n subscribe: () => () => undefined,\n});\n\n/** Lease a Resource for this committed component and read its state and optional pagination. */\nfunction useResource<Data, Key extends ResourceKey, Cursor extends ResourceKey>(\n resource: PaginatedResource<Data, Key, Cursor, 'consumers' | 'model'>,\n): PaginatedResourceHook<Data, Key, Cursor>;\nfunction useResource<Data, Key extends ResourceKey>(\n resource: Resource<Data, Key, 'consumers' | 'model'>,\n): ResourceHook<Data, Key>;\nfunction useResource<Data, Key extends ResourceKey>(\n resource: Resource<Data, Key, 'consumers' | 'model'>,\n): ResourceHook<Data, Key> {\n const handle = resource as unknown as AnyResource;\n const capability = paginationOf(handle);\n const [store] = useState(() => new ModelSelectionStore());\n const stateSource = useMemo(() => resourceViewReadable(resource), [resource]);\n const subscribe = useCallback((listener: () => void) => store.subscribe(listener), [store]);\n /*\n * D364: `resource.state` is a total `Readable` for the life of the object this hook holds — a retired one answers\n * `idle('retired')` — so the read needs no memory of its own. What it can still refuse is a producer failure, a\n * broken `cursor` among them (D356), and that belongs to the render (G13).\n */\n const getState = useMemo(() => () => stateSource.getSnapshot(), [stateSource]);\n const state = useSyncExternalStore(subscribe, getState, getState);\n const paginationSource: Readable<PaginationState<ResourceKey>> =\n capability === undefined ? noPaginationSource : resourceViewReadable(capability);\n const subscribePagination = useCallback(\n (listener: () => void) => paginationSource.subscribe(inAct(listener)),\n [paginationSource],\n );\n const getPaginationState = useMemo(() => () => paginationSource.getSnapshot(), [paginationSource]);\n const paginationState = useSyncExternalStore(subscribePagination, getPaginationState, getPaginationState);\n\n // Acquire in layout for a committed Resource. The store releases with its passive subscription, so a Suspense\n // hide that disconnects layout effects keeps the mounted component's lease.\n useLayoutEffect(() => {\n const entered = enterCommit();\n try {\n store.commit(new Set([stateSource]), new Set(resourceViewDemands(resource)));\n } finally {\n leaveCommit(entered);\n }\n }, [resource, stateSource, store]);\n\n const slots = useResourceSlots(handle);\n const refresh = useResourceVerbStatus(slots.refresh);\n const retry = useResourceVerbStatus(slots.retry);\n const reset = useResourceVerbStatus(slots.reset);\n const { loadNext } = slots;\n const pagination = useMemo(\n () =>\n capability === undefined\n ? undefined\n : ({ loadNext, state: paginationState } as unknown as ResourcePaginationHook<ResourceKey>),\n [capability, loadNext, paginationState],\n );\n\n return useMemo(() => ({ pagination, refresh, reset, retry, state }), [pagination, refresh, reset, retry, state]);\n}\n\nexport { useResource };\n","import type { ComponentType, ReactNode } from 'react';\nimport { createElement, Fragment } from 'react';\n\nimport type { DeclarationId } from '@opetope/core';\nimport { declarationId } from '@opetope/core';\nimport type { ContributionTarget } from '@opetope/core/internal';\nimport { createContributionTarget } from '@opetope/core/internal';\nimport type { ContributionModel } from '@opetope/runtime/internal';\n\nimport { contributionAuthority, ContributionMount } from './contribution-frame';\nimport { useReadable } from './readable-hooks';\n\ntype SlotProperties = object | undefined;\ntype EmptySlotProperties = Readonly<Record<string, never>>;\ntype SlotComponentProperties<Props extends SlotProperties> = [Props] extends [undefined]\n ? EmptySlotProperties\n : Extract<Props, object>;\n\n/**\n * Contribution component and adapter published for a Slot target.\n */\ntype SlotContribution<Props extends SlotProperties = undefined> = {\n readonly Component: ComponentType<never>;\n readonly models?: readonly ContributionModel[];\n readonly props?: (slotProps: SlotComponentProperties<Props>) => object;\n};\n\ntype SlotTarget<Props extends SlotProperties = undefined> = ContributionTarget<SlotContribution<Props>>;\n\ntype SwitchSlotTarget<Route extends string, Props extends SlotProperties = undefined> = ((\n route: Route,\n) => SlotTarget<Props>) & {\n readonly id: DeclarationId;\n};\n\ntype SlotRenderProps<Props extends SlotProperties> = [Props] extends [undefined]\n ? {\n readonly props?: 'this slot has no props: omit the props field';\n readonly target: SlotTarget<Props>;\n }\n : {\n readonly props: SlotComponentProperties<Props>;\n readonly target: SlotTarget<Props>;\n };\n\nconst emptySlotProperties: EmptySlotProperties = Object.freeze({});\n\n/**\n * Declare a typed Slot target by its id.\n */\nfunction defineSlot<Props extends SlotProperties = undefined>(id: string): SlotTarget<Props> {\n return createContributionTarget<SlotContribution<Props>>(id, 'slot');\n}\n\nfunction defineSwitchSlot<Route extends string, Props extends SlotProperties = undefined>(\n id: string,\n): SwitchSlotTarget<Route, Props> {\n const declared = declarationId(id);\n const targets = new Map<Route, SlotTarget<Props>>();\n\n const switchSlot = (route: Route): SlotTarget<Props> => {\n const existing = targets.get(route);\n\n if (existing !== undefined) return existing;\n\n const target = defineSlot<Props>(`${declared}/${route}`);\n targets.set(route, target);\n\n return target;\n };\n\n Object.defineProperty(switchSlot, 'id', { enumerable: true, value: declared });\n\n return Object.freeze(switchSlot) as SwitchSlotTarget<Route, Props>;\n}\n\n/**\n * Render the published contributions of a Slot target within their owning mounts.\n */\nfunction Slot<Props extends SlotProperties>(\n options: SlotRenderProps<NoInfer<Props>> & { readonly target: SlotTarget<Props> },\n): ReactNode {\n const entries = useReadable(options.target.entries);\n const slotProps: object =\n 'props' in options ? ((options.props as object | undefined) ?? emptySlotProperties) : emptySlotProperties;\n\n if (entries.length === 0) return null;\n\n return entries.map(entry =>\n createElement(\n Fragment,\n { key: entry.id },\n createElement(ContributionMount, {\n authority: contributionAuthority(entry),\n contribution: entry.value,\n contributionId: entry.id,\n slotProps,\n targetId: options.target.id,\n }),\n ),\n );\n}\n\nexport { defineSlot, defineSwitchSlot, Slot };\nexport type { SlotComponentProperties, SlotContribution, SlotProperties, SlotTarget, SwitchSlotTarget };\n"],"names":["failureOutcome","options","error","isCancellation","notification","_a","reportDetachedError","callbackError","runOptions","onSuccess","signal","requestOf","input","resolve","promise","settle","request","outcome","CommandHookController","ContributionError","aborted","invoker","notify","CommandError","compatibleAbortReason","invocation","value","noOutcome","emptyStatus","settledOutcome","unchangedState","current","slot","inFlight","settled","nextState","dismiss","dismissedState","dismissalOf","shown","setHookState","useCommandSlot","slots","useState","useCommandStatus","currentSlot","hookState","useRef","useInsertionEffect","inAct","useLayoutEffect","entered","enterCommit","leaveCommit","run","useMemo","useCommand","command","record","useFrame","idleRelease","subscribeIdle","idleRevision","cancelled","ResourceVerbController","reject","answer","refuse","waiter","operation","start","next","visibleOutcome","previous","useResourceVerbStatus","emptyHooks","isResourceEntry","entry","createEntry","createVerbEntry","unchangedHook","updateEntry","updateVerbEntry","selectEntry","resourceHook","selected","createResourceEntry","invokers","parts","unchangedResourceCommands","hook","unchangedResourceState","unchangedResourceHook","selectResourceEntry","detachEntry","verb","_b","sameResourceConsumers","sameConsumers","hookOf","CommandGroup","key","listener","commands","resources","frame","resource","resourceInvokers","delivered","useCommandGroup","groupRef","useSelectionHooks","group","getSnapshot","useSyncExternalStore","noResources","noLeases","readSource","source","sources","isReadable","readPassively","CONTAINERS","isContainer","container","refuseSource","isResourceView","classifyResource","isResource","capability","resourceViewReadable","selectedResources","collectResourceDemands","leases","demand","resourceViewDemands","collectSelection","select","open","read","project","readable","enterSelectorEvaluation","entries","leaveSelectorEvaluation","equalResources","before","equalMembers","equalSelection","values","keys","previousKeys","index","emptySelectionSnapshot","selectionCommands","isCallTarget","observedUnchanged","observed","createSelectionMemory","createSelectionSnapshot","model","selectRef","memory","nextObserved","context","readEmptySnapshot","useSelectionStore","storeRef","ModelSelectionStore","selectedValue","snapshot","hooks","mergeSelection","committed","oldKeys","equal","useModel","declaration","store","memoryRef","committedResult","result","requiresModels","models","declared","component","useResourceSlots","cache","existing","createResourceInvokers","unavailablePagination","noPaginationSource","useResource","handle","paginationOf","stateSource","subscribe","useCallback","getState","state","paginationSource","subscribePagination","getPaginationState","paginationState","refresh","retry","reset","loadNext","pagination","emptySlotProperties","defineSlot","id","createContributionTarget","defineSwitchSlot","declarationId","targets","switchSlot","route","target","Slot","useReadable","slotProps","createElement","Fragment","ContributionMount","contributionAuthority"],"mappings":"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"}
|
package/dist/scenario-mount.d.ts
CHANGED
|
@@ -2,13 +2,14 @@ import type { ScenarioHostMount } from './scenario-types.js';
|
|
|
2
2
|
/** A reservation exists before the host callback, so reentrant close also owns a mount returned later. */
|
|
3
3
|
declare class ScenarioMountLease<Mounted extends ScenarioHostMount> {
|
|
4
4
|
private readonly released;
|
|
5
|
+
private readonly act;
|
|
5
6
|
get closed(): boolean;
|
|
6
7
|
private available;
|
|
7
8
|
private completion;
|
|
8
9
|
private handle;
|
|
9
10
|
private reject;
|
|
10
11
|
private resolve;
|
|
11
|
-
constructor(released: (failed: boolean, error?: unknown) => void);
|
|
12
|
+
constructor(released: (failed: boolean, error?: unknown) => void, act: ((callback: () => unknown) => PromiseLike<unknown>) | undefined);
|
|
12
13
|
readonly close: () => Promise<void>;
|
|
13
14
|
provide(handle: Mounted | undefined): void;
|
|
14
15
|
private finish;
|
package/dist/scenario-types.d.ts
CHANGED
|
@@ -17,6 +17,10 @@ type ScenarioOptions<Features extends ScenarioFeatures, Mounted extends Scenario
|
|
|
17
17
|
/** Where the application's failures go: a function, or `'collect'` for a readonly `reported` list. */
|
|
18
18
|
readonly reporter: Choice;
|
|
19
19
|
} & {
|
|
20
|
+
/**
|
|
21
|
+
* The renderer's `act`: mount, props, unmount, close and every delivery to a mounted tree run inside it.
|
|
22
|
+
*/
|
|
23
|
+
readonly act?: (callback: () => unknown) => PromiseLike<unknown>;
|
|
20
24
|
/**
|
|
21
25
|
* Records of physical activity an observation keeps, an integer from 1 to 10000.
|
|
22
26
|
* @default 256
|
package/dist/testing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{hasOwn as
|
|
2
|
-
`)}class
|
|
3
|
-
${
|
|
1
|
+
import{hasOwn as O,isResourceView as x,createAggregateError as Q}from"@opetope/core/internal";import{openApplication as X}from"@opetope/runtime";import{createInspectionSession as Y,testingTimeoutMs as Z}from"@opetope/runtime/internal";import{a as N,u as _,c as q,M as ee,i as te}from"./contribution-frame-DL0dyVP4.js";import{createElement as ne}from"react";import{declarationId as re}from"@opetope/core";function oe(e){if(e.state.kind==="ready")return[];const t=e.state.kind==="waiting"?` (${e.state.reason})`:"";return[`Feature ${e.label}: ${e.state.kind}${t}.`]}function se(e){if(e.lane===null)return`Command ${e.declaration}: ${e.state}; lane parallel; blockers none (parallel).`;const t=e.lane.blockedBy==="unknown"?"unknown":e.lane.blockedBy.join(", ");return`Command ${e.declaration}: ${e.state}; lane ${e.lane.declaration}; blockers ${t}.`}function ie(e){if(e===void 0)return["Runtime activity: unknown."];const t=[];return(e.freshness==="stale"||e.truncated)&&t.push(`Activity: ${e.freshness}${e.truncated?", truncated":""}; completeness unknown.`),[...t,...e.features.map(n=>`Feature ${n.feature}: ${n.phase}; body ${n.body}; host demand unknown.`),...e.calls.map(se),...e.resources.map(n=>`Resource ${n.id}: ${n.state}; leases ${String(n.leases)}.`)]}function ce(e){const t=[...e.runtime.conditions.flatMap(n=>n.state.kind==="true"?[]:[`Condition ${n.label}: ${n.state.kind}.`]),...e.runtime.instances.flatMap(oe),...ie(e.activity)];return t.length===0?"No registered runtime blocker observed; external UI and host work are unknown.":t.join(`
|
|
2
|
+
`)}class W extends Error{label;timeoutMs;snapshot;history;code="timeout";constructor(t,n,r,s){super(`Scenario timed out after ${String(n)}ms: ${t}.
|
|
3
|
+
${ce(r)}`),this.label=t,this.timeoutMs=n,this.snapshot=r,this.history=s,this.name="ScenarioTimeoutError"}}function ae(e,t){return!t||e===void 0?!1:e.closed&&!e.truncated}function ue(e,t,n){if(e===void 0)return["Runtime activity is unavailable."];const r=[];return e.truncated&&r.push("The activity snapshot is truncated."),e.freshness==="stale"&&!t&&r.push("The activity snapshot is stale."),n||r.push("Successful application and mount cleanup has not been observed."),Object.freeze(r)}function le(e,t){return e===void 0||e.truncated||e.freshness==="stale"&&!t?{calls:"unknown",features:"unknown",resources:"unknown"}:{calls:e.calls.length,features:e.features.length,resources:e.resources.length}}function de(e,t){const n=e.activity,r=ae(n,t);return Object.freeze({...le(n,r),closed:t,reasons:ue(n,r,t),scope:"registered-runtime",status:r?"complete":"unknown"})}class fe{released;act;get closed(){return this.completion!==void 0}available=!1;completion;handle;reject;resolve;constructor(t,n){this.released=t,this.act=n}close=()=>this.completion!==void 0?this.completion:(this.completion=new Promise((t,n)=>{this.resolve=t,this.reject=n}),this.available&&this.releaseAvailable(),this.completion);provide(t){this.available=!0,this.handle=t,this.closed&&this.releaseAvailable()}finish(t,n){const r=this.resolve,s=this.reject;this.resolve=void 0,this.reject=void 0,this.released(t,n),t?s==null||s(n):r==null||r()}releaseAvailable(){const t=this.handle;this.handle=void 0,this.available=!1;try{const n=()=>t==null?void 0:t.unmount();Promise.resolve(this.act===void 0?n():N(this.act,n)).then(()=>this.finish(!1),r=>this.finish(!0,r))}catch(n){this.finish(!0,n)}}}const he=re("testing.contribution");function B(e,t={}){const n=t.models??[];let r=t.props??{};const s=new Set,g={getSnapshot:()=>r,subscribe:p=>(s.add(p),()=>s.delete(p))},w=t.contribution,m=t.reporter,u=m===void 0?void 0:{models:[],reporter:m};return Object.freeze({close:()=>{r={},s.clear()},Slot:()=>{const p=_(e.entries),l=_(g);return(w===void 0?p.map(i=>[i.id,i.value,q(i)??u]):[[he,w,u]]).map(([i,$,M])=>ne(ee,{authority:M,contribution:$,contributionId:i,fixtures:n,key:i,slotProps:l,targetId:e.id}))},updateProps:p=>{r=p;for(const l of[...s])l()}})}function pe(e,t={}){const n=B(e,t);return Object.freeze({Slot:n.Slot,updateProps:n.updateProps})}function T(e,t){if(!Number.isFinite(e)||e<=0||e>2147483647)throw new RangeError(`${t} must be a finite duration from 0 (exclusive) to 2147483647ms.`);return e}class me{source;listeners=new Set;pending=new Set;constructor(t){this.source=t}cancel(){for(const t of[...this.pending])t(new Error("Scenario is closing."))}deadline(t,n,r=this.source.timeoutMs){return T(r,"Scenario timeoutMs"),new Promise((s,g)=>{const w=setTimeout(()=>g(this.timeout(n,r)),r);Promise.resolve(t).then(m=>{clearTimeout(w),s(m)},m=>{clearTimeout(w),g(m)})})}notify=()=>{for(const t of[...this.listeners])t()};waitFor(t,n,r){const s=T(r.timeoutMs??this.source.timeoutMs,"Scenario timeoutMs"),g=T(r.pollIntervalMs??10,"Scenario pollIntervalMs"),w=t.kind==="source"?t.read:()=>t.read(this.source.getSnapshot()),m=(u,k)=>{if(u!==null&&(typeof u=="object"||typeof u=="function")&&typeof Reflect.get(u,"then")=="function")throw new TypeError(`Scenario.waitFor ${k} synchronously; put an awaited read inside eventually.`);return u};return new Promise((u,k)=>{let p=!1,l=()=>{};const y=(c,C=!1)=>{p||(p=!0,l(),C?k(c):u(c))},i=()=>{if(!p)try{const c=m(w(),"reads");(n===void 0?c!==!1&&c!==null&&c!==void 0:m(n(c),"decides"))&&y(c)}catch(c){y(c,!0)}},$=c=>y(c,!0),M=setTimeout(()=>y(this.timeout(r.label??(t.kind==="source"?"waitFor(source)":"waitFor"),s),!0),s),S=setInterval(i,g);l=()=>{clearTimeout(M),clearInterval(S),this.listeners.delete(i),this.pending.delete($)},this.listeners.add(i),this.pending.add($),i()})}timeout(t,n){return new W(t,n,this.source.getSnapshot(),this.source.history())}}function H(e,t){if(!Number.isSafeInteger(e)||e<1||e>1e4)throw new RangeError(`${t} must be an integer from 1 to 10000.`);return e}function we(e){if(e===null||typeof e!="object"||!("mount"in e)||typeof e.mount!="function")throw new TypeError("Scenario requires a host.mount(Component) adapter.")}function ye(e){return x(e)?!0:(typeof e!="object"||e===null)&&typeof e!="function"?!1:"getSnapshot"in e&&typeof e.getSnapshot=="function"}function be(e,t){return{...e.cleanupFailure===void 0?{}:{cleanupFailure:e.cleanupFailure},...O(e,"conditions")?{conditions:e.conditions}:{},...O(e,"grace")?{grace:e.grace}:{},...O(e,"imports")?{imports:e.imports}:{},reporter:t}}const ge=()=>{};function Se(e){if(O(e,"act")&&typeof e.act!="function")throw new TypeError("Scenario act must be the renderer's act function; leave it out to render without one.");return e.act}function ke(e){if(e!=="collect")return{reporter:e};const t=[];return{reported:t,reporter:n=>{t.push(n)}}}function $e(e,t){const n=t.timeoutMs;n!==void 0&&T(n,"Scenario timeoutMs");const r=Se(t),s=o=>r===void 0?o():N(r,o),g=()=>r===void 0?ge:te(r),w=H(t.historyCapacity??64,"Scenario historyCapacity"),m=H(t.activityCapacity??256,"Scenario activityCapacity");let u=t.host;we(u);const{reported:k,reporter:p}=ke(t.reporter);let l=X(e,be(t,p)),y;try{y=Y(l,{activityCapacity:m,ringCapacity:w})}catch(o){throw l.ready.catch(()=>{}),l.close().catch(()=>{}),o}let i="open",$=!1,M=!1;const S=[],c=new Set,C=[],P=()=>y.getSnapshot(),R=()=>Object.freeze([...S]),j=new me({getSnapshot:P,history:R,get timeoutMs(){return n??Z()}}),A=()=>{if(!M){M=!0;try{const o=P(),d=S[S.length-1];((d==null?void 0:d.stateRevision)!==o.stateRevision||d.activity!==o.activity)&&(S.push(o),S.length>w&&S.shift())}finally{M=!1}j.notify()}},V=y.subscribe(A);A();const I=j.deadline(l.ready,"application ready");I.catch(()=>{});let E,v;const D=()=>{if(E!==void 0)return E;let o,d;E=new Promise((f,a)=>{o=f,d=a}),i="closing",j.cancel();const b=[];try{b.push(l.close())}catch(f){b.push(Promise.reject(f))}return b.push(...[...c].map(f=>f.close())),Promise.allSettled(b).then(f=>{const a=[...new Set([...C,...f.flatMap(h=>h.status==="rejected"?[h.reason]:[])])];C.length=0,i="closed";try{A()}catch(h){a.push(h)}finally{V(),y.close(),c.clear(),l=void 0,u=void 0}$=a.length===0,a.length===0?o():d(Q(a,"Scenario cleanup failed."))}),E},L=()=>{const o=D();return v!==void 0||(v=j.deadline(o,"scenario close"),v.catch(()=>{i==="closing"&&(v=void 0)})),v},U=()=>v??s(L),G=(o,d={})=>{if(i!=="open")throw new Error("Scenario is closing.");const b=B(o,d),f=g(),a=new fe((F,K)=>{c.delete(a),b.close(),f(),F&&C.push(K)},r);c.add(a);let h;try{if(h=s(()=>u.mount(b.Slot)),h===null||typeof h!="object"||typeof h.unmount!="function")throw new TypeError("Scenario host.mount must return an unmount() handle.");a.provide(h)}catch(F){throw f(),a.provide(void 0),a.close().catch(()=>{}),F}return Object.freeze({host:h,unmount:a.close,updateProps:F=>{if(i!=="open"||a.closed)throw new Error("Scenario mount is closed.");s(()=>b.updateProps(F)),j.notify()}})};function J(o,d,b){if(i!=="open")return Promise.reject(new Error("Scenario is closing."));const f=typeof d=="function"?d:void 0,a=(f===void 0?d:b)??{},h=x(o)?{kind:"source",read:()=>o.observe().getSnapshot()}:ye(o)?{kind:"source",read:()=>o.getSnapshot()}:{kind:"graph",read:o};return j.waitFor(h,f,a)}const z={close:U,getSnapshot:P,history:R,mount:G,notify:()=>{i==="open"&&j.notify()},ownership:()=>de(P(),$),ready:I,waitFor:J};return k!==void 0&&Object.defineProperty(z,"reported",{enumerable:!0,get:()=>Object.freeze([...k])}),Object.freeze(z)}export{W as ScenarioTimeoutError,$e as createScenario,pe as renderSlot};
|
|
4
4
|
//# sourceMappingURL=testing.js.map
|