@opetope/react 0.1.1 → 0.4.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 +54 -0
- package/README.md +25 -5
- package/README.ru.md +25 -5
- package/dist/contribution-frame-B1PnpOaP.js +2 -0
- package/dist/contribution-frame-B1PnpOaP.js.map +1 -0
- package/dist/contribution-frame.d.ts +14 -6
- package/dist/contribution-isolation-Bzfbt4iM.js +2 -0
- package/dist/contribution-isolation-Bzfbt4iM.js.map +1 -0
- package/dist/contribution-isolation.d.ts +64 -0
- package/dist/errors.d.ts +20 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integration.d.ts +2 -0
- package/dist/integration.js +1 -1
- package/dist/integration.js.map +1 -1
- package/dist/scenario-slot.d.ts +5 -0
- package/dist/testing.js +2 -2
- package/dist/testing.js.map +1 -1
- package/package.json +5 -5
- package/dist/contribution-frame-1td5XTES.js +0 -2
- package/dist/contribution-frame-1td5XTES.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/commands-hook.ts","../src/model-selection-snapshot.ts","../src/model-selection-store.ts","../src/model-hook.ts","../src/requires-models.ts","../src/resource-hook.ts","../src/slot.tsx"],"sourcesContent":["import { CallError } from '@opetope/core';\nimport { compatibleAbortReason, isCancellation, reportDetachedError } from '@opetope/core/internal';\nimport type { CallRunOptions } from '@opetope/core/internal';\n\nimport type { CommandOutcome } from './command';\nimport { ContributionError } from './errors';\n\ntype CommandRunOptions<Output> = Readonly<{\n onFailure?: (error: unknown) => PromiseLike<void> | void;\n onSuccess?: (value: Output) => PromiseLike<void> | void;\n signal?: AbortSignal;\n}>;\n\ntype CommandRun<Input, Output> = [Input] extends [void]\n ? (input?: Input, options?: CommandRunOptions<Output>) => Promise<CommandOutcome<Output>>\n : (input: Input, options?: CommandRunOptions<Output>) => Promise<CommandOutcome<Output>>;\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, status: '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, status: '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 call, and the policy the call was\n * created with — `queue`, `latest`, `parallel`, `once`, `singleFlight` — 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 status: '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 CallError('cancelled', 'Command input was cancelled.', compatibleAbortReason(signal)),\n status: '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, { status: '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 { Call } 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\ninterface CommandHookStatus<Output> {\n readonly inFlight: boolean;\n readonly lastError: unknown | null;\n readonly result: Output | undefined;\n}\n\nconst emptyStatus: CommandHookStatus<never> = { inFlight: false, lastError: null, result: undefined };\n\ntype CommandHook<Input, Output> = CommandHookStatus<Output> & { readonly run: CommandRun<Input, Output> };\n\ntype CommandHookState<Input, Output> = CommandHookStatus<Output> & {\n readonly slot: CommandHookController<Input, Output>;\n};\n\nfunction unchangedState<Input, Output>(\n current: CommandHookState<Input, Output>,\n slot: CommandHookController<Input, Output>,\n inFlight: boolean,\n outcome: CommandOutcome<Output> | undefined,\n): boolean {\n return outcome === 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 if (unchangedState(current, slot, inFlight, outcome)) return current;\n\n const { lastError, result } = current.slot === slot ? current : { lastError: null, result: undefined };\n\n if (outcome?.status === 'ok') return { inFlight, lastError: null, result: outcome.value, slot };\n\n if (outcome?.status === 'failed') return { inFlight, lastError: outcome.error, result, slot };\n\n return { inFlight, lastError, result, 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\nfunction useCommand<Input, Output>(command: Call<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 const currentSlot = useCommandSlot<Input, Output>(record.invoker);\n const [hookState, setHookState] = useState<CommandHookState<Input, Output>>(() => ({\n inFlight: false,\n lastError: null,\n result: 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, lastError, result } = hookState.slot === currentSlot ? hookState : emptyStatus;\n const run = currentSlot.run;\n\n return useMemo(() => ({ inFlight, lastError, result, run }), [inFlight, lastError, result, run]);\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, useRef, useSyncExternalStore } from 'react';\n\nimport type { Call } 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 { useFrame } from './mount-context';\nimport type { Frame } from './mount-frame';\n\ntype CommandSelection = Readonly<Record<string, Call<never, unknown>>>;\ntype HookOf<Command> = Command extends Call<infer Input, infer Output> ? CommandHook<Input, Output> : never;\ntype CommandsHook<Commands> = {\n readonly [Key in keyof Commands]: HookOf<Commands[Key]>;\n};\nconst emptyHooks = 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\nfunction createEntry(invoker: CommandInvoker): CommandEntry {\n const slot = new CommandHookController<never, unknown>(invoker);\n\n return {\n detach: undefined,\n hook: { inFlight: false, lastError: null, result: undefined, run: slot.run },\n invoker,\n slot,\n };\n}\n\nfunction unchangedHook(previous: CommandHook<never, unknown>, next: CommandHook<never, unknown>): boolean {\n return (\n previous.inFlight === next.inFlight &&\n Object.is(previous.result, next.result) &&\n Object.is(previous.lastError, next.lastError)\n );\n}\n\nfunction outcomeStatus(previous: CommandHook<never, unknown>, outcome: CommandOutcome<unknown> | undefined) {\n if (outcome?.status === 'ok') return { lastError: null, result: outcome.value };\n\n if (outcome?.status === 'failed') return { lastError: outcome.error, result: previous.result };\n\n return previous;\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 const { lastError, result } = outcomeStatus(previous, outcome);\n const next = { inFlight, lastError, result, 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: CommandEntry | undefined, invoker: CommandInvoker): CommandEntry {\n return previous?.invoker === invoker ? previous : createEntry(invoker);\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, CommandEntry>();\n private readonly listeners = new Set<() => void>();\n private revision = 0;\n\n close(): void {\n for (const entry of this.entries.values()) {\n entry.detach?.();\n entry.detach = undefined;\n }\n\n this.entries.clear();\n }\n\n commit(selected: Map<PropertyKey, CommandEntry>): void {\n for (const [key, entry] of this.entries) {\n if (selected.get(key) === entry) continue;\n\n entry.detach?.();\n entry.detach = undefined;\n }\n\n this.entries = selected;\n\n for (const entry of selected.values()) {\n entry.detach ??= entry.slot.attach((inFlight, outcome) => {\n if (!updateEntry(entry, inFlight, outcome)) return;\n\n this.revision += 1;\n\n for (const listener of this.listeners) listener();\n });\n }\n }\n\n getSnapshot = (): number => this.revision;\n\n prepare(commands: CommandSelection, frame: Frame): Map<PropertyKey, CommandEntry> {\n const selected = new Map<PropertyKey, CommandEntry>();\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, Call<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 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): CommandGroup | undefined {\n const groupRef = useRef<CommandGroup | undefined>(undefined);\n const populated = Reflect.ownKeys(commands).some(key => Object.prototype.propertyIsEnumerable.call(commands, key));\n\n return populated ? (groupRef.current ??= new CommandGroup()) : undefined;\n}\n\nfunction useCommands<Commands extends { readonly [Key in keyof Commands]: Call<never, unknown> }>(\n commands: Commands,\n): CommandsHook<Commands> {\n const frame = useFrame();\n const group = useCommandGroup(commands);\n const getSnapshot = group?.getSnapshot ?? idleRevision;\n useSyncExternalStore(group?.subscribe ?? subscribeIdle, getSnapshot, getSnapshot);\n const selected = group?.prepare(commands, 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 for (const entry of selected.values()) entry.slot.synchronize();\n });\n\n return (\n selected === undefined ? emptyHooks : Object.fromEntries([...selected].map(([key, entry]) => [key, entry.hook]))\n ) as CommandsHook<Commands>;\n}\n\nexport { useCommands };\n","import type { Readable } from '@opetope/core';\nimport { isCallTarget, isReadable } from '@opetope/core/internal';\n\nimport { ContributionError } from './errors';\nimport type { AnyCommand } from './model-binding';\n\ninterface SelectionRead {\n <Value>(source: Readable<Value>): Value;\n <Value, Selected>(source: Readable<Value>, select: (value: Value) => Selected): Selected;\n}\n\ntype SelectionContext = Readonly<{ read: SelectionRead }>;\ntype NonSelectionRecord =\n | readonly unknown[]\n | ((...arguments_: never[]) => unknown)\n | (abstract new (...arguments_: never[]) => unknown)\n | Date\n | PromiseLike<unknown>\n | ReadonlyMap<unknown, unknown>\n | ReadonlySet<unknown>\n | RegExp\n | WeakMap<object, unknown>\n | WeakSet<object>;\n/** A named interface needs no open index signature; container and callable results are not field selections. */\ntype SelectionShape<Selected extends object> = Selected extends NonSelectionRecord ? never : Selected;\ntype SelectionRecord = Readonly<Record<PropertyKey, unknown>>;\ntype SelectionSnapshot = Readonly<{\n commands: Readonly<Record<PropertyKey, AnyCommand>>;\n sources: ReadonlySet<Readable<unknown>>;\n values: SelectionRecord;\n}>;\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.');\n }\n\n if (sources.has(source)) return sources.get(source) as Value;\n\n const value = source.getSnapshot();\n sources.set(source, value);\n\n return value;\n}\n\nfunction collectSelection(\n select: (context: SelectionContext) => object,\n sources: Map<Readable<unknown>, unknown>,\n): SelectionRecord {\n const read = <Value, Selected>(source: Readable<Value>, project?: (value: Value) => Selected) => {\n const value = readSource(source, sources);\n\n return project === undefined ? value : project(value);\n };\n const selected = select({ read });\n\n if (typeof selected !== 'object' || selected === null || Array.isArray(selected)) {\n throw new ContributionError('binding-invalid', 'Model selection requires a record.');\n }\n\n return Object.fromEntries(\n Reflect.ownKeys(selected)\n .filter(key => Object.prototype.propertyIsEnumerable.call(selected, key))\n .map(key => [key, (selected as SelectionRecord)[key]]),\n );\n}\n\nfunction equalSelection(previous: SelectionSnapshot, values: SelectionRecord, sources: ReadonlySet<Readable<unknown>>) {\n const keys = Reflect.ownKeys(values);\n const previousKeys = Reflect.ownKeys(previous.values);\n\n if (keys.length !== previousKeys.length || sources.size !== previous.sources.size) return false;\n\n for (const source of sources) if (!previous.sources.has(source)) 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 = { commands: {}, sources: new Set(), values: {} };\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 * 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 */\nfunction createSelectionSnapshot<Model>(model: Model, selectRef: SelectionReaderRef<Model>): () => SelectionSnapshot {\n let observed: Map<Readable<unknown>, unknown> | undefined;\n let observedSelect: ModelSelector<Model> | undefined;\n let snapshot: SelectionSnapshot = emptySelectionSnapshot;\n\n return () => {\n const select = selectRef.current;\n\n if (select === undefined) return emptySelectionSnapshot;\n\n if (\n observed !== undefined &&\n observedSelect === select &&\n [...observed].every(([source, value]) => Object.is(source.getSnapshot(), value))\n ) {\n return snapshot;\n }\n\n const nextObserved = new Map<Readable<unknown>, unknown>();\n const values = collectSelection(context => select(model, context), nextObserved);\n const sources = new Set(nextObserved.keys());\n\n if (observed === undefined || !equalSelection(snapshot, values, sources)) {\n snapshot = { commands: selectionCommands(values), sources, values };\n }\n\n observed = nextObserved;\n observedSelect = select;\n\n return snapshot;\n };\n}\n\nexport { createSelectionSnapshot, emptySelectionSnapshot };\nexport type { ModelSelector, SelectionContext, SelectionShape };\n","import type { Readable } from '@opetope/core';\nimport { createAggregateError } from '@opetope/core/internal';\n\ntype Subscription = { dispose: (() => void) | undefined };\ntype Listener = Readonly<{ notify: () => void }>;\n\nfunction releaseSubscriptions(subscriptions: Iterable<Subscription>, failures: unknown[] = []): void {\n for (const subscription of subscriptions) {\n const dispose = subscription.dispose;\n subscription.dispose = undefined;\n\n try {\n dispose?.();\n } catch (error) {\n failures.push(error);\n }\n }\n\n if (failures.length === 1) throw failures[0];\n\n if (failures.length > 1) throw createAggregateError(failures, 'Model selection sources failed to release.');\n}\n\n/** A subscription set, not a scheduler: source notifications use React's snapshot consistency check. */\nclass ModelSelectionStore {\n private enabled = false;\n private readonly listeners = new Set<Listener>();\n private sources: ReadonlySet<Readable<unknown>> = new Set();\n private readonly subscriptions = new Map<Readable<unknown>, Subscription>();\n\n close(): void {\n this.enabled = false;\n this.sources = new Set();\n this.releaseAll();\n }\n\n commit(sources: ReadonlySet<Readable<unknown>>): void {\n this.sources = sources;\n this.enabled = true;\n\n try {\n this.synchronize();\n } catch (error) {\n this.releaseAll([error]);\n }\n }\n\n subscribe = (notify: () => void): (() => void) => {\n const listener = { notify };\n this.listeners.add(listener);\n\n try {\n this.synchronize();\n } catch (error) {\n this.listeners.delete(listener);\n this.releaseAll([error]);\n }\n\n return () => {\n this.listeners.delete(listener);\n\n if (this.listeners.size === 0) this.releaseAll();\n };\n };\n\n private add(source: Readable<unknown>): void {\n const subscription: Subscription = { dispose: undefined };\n // Reserve before foreign code: a synchronous notification may replace or close this selection.\n this.subscriptions.set(source, subscription);\n const dispose = source.subscribe(this.notify);\n\n if (typeof dispose !== 'function') throw new TypeError('Model selection source must return a disposer.');\n\n if (this.subscriptions.get(source) === subscription) subscription.dispose = dispose;\n else dispose();\n }\n\n private notify = (): void => {\n for (const listener of [...this.listeners]) {\n if (this.listeners.has(listener)) listener.notify();\n }\n };\n\n private releaseAll(failures?: unknown[]): void {\n const subscriptions = [...this.subscriptions.values()];\n this.subscriptions.clear();\n releaseSubscriptions(subscriptions, failures);\n }\n\n private removeUnselected(): void {\n const removed: Subscription[] = [];\n\n for (const [source, subscription] of this.subscriptions) {\n if (this.sources.has(source)) continue;\n\n this.subscriptions.delete(source);\n removed.push(subscription);\n }\n\n releaseSubscriptions(removed);\n }\n\n private synchronize(): void {\n this.removeUnselected();\n\n for (const source of this.sources) {\n if (!this.enabled || this.listeners.size === 0) return;\n\n if (this.sources.has(source) && !this.subscriptions.has(source)) this.add(source);\n }\n }\n}\n\nexport { ModelSelectionStore };\n","import { useLayoutEffect, useMemo, useRef, useSyncExternalStore } from 'react';\n\nimport type { Call, ModelOf } from '@opetope/core';\n\nimport type { CommandHook } from './command-hook';\nimport { useCommands } from './commands-hook';\nimport { ContributionError } from './errors';\nimport { subscribeIdle } from './idle-subscription';\nimport type { AnyModel } from './model-binding';\nimport { createSelectionSnapshot, emptySelectionSnapshot } from './model-selection-snapshot';\nimport type { ModelSelector, SelectionContext, SelectionShape } from './model-selection-snapshot';\nimport { ModelSelectionStore } from './model-selection-store';\nimport { useFrame } from './mount-context';\n\ntype SelectedValue<Value> = Value extends Call<infer Input, infer Output> ? CommandHook<Input, Output> : 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\n/** D205: resolve only a granted model, explicitly read data, and reuse the ordinary per-key command consumers. */\nfunction useModel<Declaration extends AnyModel>(declaration: Declaration): ModelOf<Declaration>;\nfunction useModel<Declaration extends AnyModel, const Selected extends object>(\n declaration: Declaration,\n select: (model: ModelOf<Declaration>, context: SelectionContext) => Selected & SelectionShape<Selected>,\n): ModelSelection<Selected>;\nfunction useModel<Declaration extends AnyModel>(\n declaration: Declaration,\n select?: (model: 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 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<ModelOf<Declaration>> | undefined>(undefined);\n selectRef.current = select;\n const selected = select !== undefined;\n const getSnapshot = useMemo(\n () => (selected ? createSelectionSnapshot(model, selectRef) : readEmptySnapshot),\n [model, selected],\n );\n const snapshot = useSyncExternalStore(store?.subscribe ?? subscribeIdle, getSnapshot, getSnapshot);\n const commands = useCommands(snapshot.commands);\n useLayoutEffect(() => () => store?.close(), [store]);\n useLayoutEffect(() => store?.commit(snapshot.sources), [snapshot, store]);\n\n if (select === undefined) return model;\n\n return Object.fromEntries(\n Reflect.ownKeys(snapshot.values).map(key => [\n key,\n Object.prototype.hasOwnProperty.call(commands, key) ? commands[key] : snapshot.values[key],\n ]),\n );\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 */\ntype ComponentRequiringModels<Props, Models extends readonly AnyModel[]> = FunctionComponent<Props> & {\n readonly requires: Models;\n};\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, useEffect, useSyncExternalStore } from 'react';\n\nimport type { Resource, ResourceSnapshot } from '@opetope/runtime';\nimport type { ResourceRequestKey } from '@opetope/runtime/internal';\n\nfunction useResource<Data, Key extends ResourceRequestKey>(resource: Resource<Data, Key>): ResourceSnapshot<Data, Key> {\n const subscribe = useCallback((listener: () => void) => resource.subscribe(listener), [resource]);\n // D199: a resource is a structural contract, so it may be an object with methods. Reading through it keeps the\n // receiver the adapter expects; handing `resource.getSnapshot` to React would call it with none.\n const getSnapshot = useCallback(() => resource.getSnapshot(), [resource]);\n const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n useEffect(() => resource.retain(), [resource]);\n\n return snapshot;\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 * What a contribution publishes: the component, the optional props adapter and the optional UI models the mount\n * creates. Without an adapter the component takes the slot props; with one it takes exactly the adapter result (D85).\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?: never;\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\nfunction defineSlot<Props extends SlotProperties = undefined>(options: { readonly id: string }): SlotTarget<Props> {\n return createContributionTarget<SlotContribution<Props>>(options);\n}\n\nfunction defineSwitchSlot<Route extends string, Props extends SlotProperties = undefined>(options: {\n readonly id: string;\n}): SwitchSlotTarget<Route, Props> {\n const id = declarationId(options.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>({ id: `${id}/${route}` });\n targets.set(route, target);\n\n return target;\n };\n\n Object.defineProperty(switchSlot, 'id', { enumerable: true, value: id });\n\n return Object.freeze(switchSlot) as SwitchSlotTarget<Route, Props>;\n}\n\n/**\n * Every contribution renders inside its own mount: the models its generation owns plus the UI models it declared,\n * created on mount and closed on unmount (D70, D85).\n */\nfunction Slot<Props extends SlotProperties>(options: SlotRenderProps<Props>): ReactNode {\n const entries = useReadable(options.target.entries);\n const slotProps: object = 'props' in options ? (options.props ?? 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 slotProps,\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","CallError","compatibleAbortReason","invocation","value","emptyStatus","unchangedState","current","slot","inFlight","nextState","lastError","result","useCommandSlot","slots","useState","useCommand","command","record","useFrame","currentSlot","hookState","setHookState","useInsertionEffect","useLayoutEffect","run","useMemo","idleRelease","subscribeIdle","idleRevision","emptyHooks","createEntry","unchangedHook","previous","next","outcomeStatus","updateEntry","entry","selectEntry","CommandGroup","selected","key","listener","commands","frame","useCommandGroup","groupRef","useRef","useCommands","group","getSnapshot","useSyncExternalStore","readSource","source","sources","isReadable","collectSelection","select","project","equalSelection","values","keys","previousKeys","index","emptySelectionSnapshot","selectionCommands","isCallTarget","createSelectionSnapshot","model","selectRef","observed","observedSelect","snapshot","nextObserved","context","releaseSubscriptions","subscriptions","failures","subscription","dispose","createAggregateError","ModelSelectionStore","removed","readEmptySnapshot","useSelectionStore","storeRef","useModel","declaration","store","requiresModels","models","declared","component","useResource","resource","subscribe","useCallback","useEffect","emptySlotProperties","defineSlot","createContributionTarget","defineSwitchSlot","id","declarationId","targets","switchSlot","route","existing","target","Slot","entries","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/commands-hook.ts","../src/model-selection-snapshot.ts","../src/model-selection-store.ts","../src/model-hook.ts","../src/requires-models.ts","../src/resource-hook.ts","../src/slot.tsx"],"sourcesContent":["import { CallError } from '@opetope/core';\nimport { compatibleAbortReason, isCancellation, reportDetachedError } from '@opetope/core/internal';\nimport type { CallRunOptions } from '@opetope/core/internal';\n\nimport type { CommandOutcome } from './command';\nimport { ContributionError } from './errors';\n\ntype CommandRunOptions<Output> = Readonly<{\n onFailure?: (error: unknown) => PromiseLike<void> | void;\n onSuccess?: (value: Output) => PromiseLike<void> | void;\n signal?: AbortSignal;\n}>;\n\ntype CommandRun<Input, Output> = [Input] extends [void]\n ? (input?: Input, options?: CommandRunOptions<Output>) => Promise<CommandOutcome<Output>>\n : (input: Input, options?: CommandRunOptions<Output>) => Promise<CommandOutcome<Output>>;\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, status: '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, status: '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 call, and the policy the call was\n * created with — `queue`, `latest`, `parallel`, `once`, `singleFlight` — 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 status: '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 CallError('cancelled', 'Command input was cancelled.', compatibleAbortReason(signal)),\n status: '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, { status: '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 { Call } 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\ninterface CommandHookStatus<Output> {\n readonly inFlight: boolean;\n readonly lastError: unknown | null;\n readonly result: Output | undefined;\n}\n\nconst emptyStatus: CommandHookStatus<never> = { inFlight: false, lastError: null, result: undefined };\n\ntype CommandHook<Input, Output> = CommandHookStatus<Output> & { readonly run: CommandRun<Input, Output> };\n\ntype CommandHookState<Input, Output> = CommandHookStatus<Output> & {\n readonly slot: CommandHookController<Input, Output>;\n};\n\nfunction unchangedState<Input, Output>(\n current: CommandHookState<Input, Output>,\n slot: CommandHookController<Input, Output>,\n inFlight: boolean,\n outcome: CommandOutcome<Output> | undefined,\n): boolean {\n return outcome === 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 if (unchangedState(current, slot, inFlight, outcome)) return current;\n\n const { lastError, result } = current.slot === slot ? current : { lastError: null, result: undefined };\n\n if (outcome?.status === 'ok') return { inFlight, lastError: null, result: outcome.value, slot };\n\n if (outcome?.status === 'failed') return { inFlight, lastError: outcome.error, result, slot };\n\n return { inFlight, lastError, result, 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\nfunction useCommand<Input, Output>(command: Call<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 const currentSlot = useCommandSlot<Input, Output>(record.invoker);\n const [hookState, setHookState] = useState<CommandHookState<Input, Output>>(() => ({\n inFlight: false,\n lastError: null,\n result: 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, lastError, result } = hookState.slot === currentSlot ? hookState : emptyStatus;\n const run = currentSlot.run;\n\n return useMemo(() => ({ inFlight, lastError, result, run }), [inFlight, lastError, result, run]);\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, useRef, useSyncExternalStore } from 'react';\n\nimport type { Call } 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 { useFrame } from './mount-context';\nimport type { Frame } from './mount-frame';\n\ntype CommandSelection = Readonly<Record<string, Call<never, unknown>>>;\ntype HookOf<Command> = Command extends Call<infer Input, infer Output> ? CommandHook<Input, Output> : never;\ntype CommandsHook<Commands> = {\n readonly [Key in keyof Commands]: HookOf<Commands[Key]>;\n};\nconst emptyHooks = 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\nfunction createEntry(invoker: CommandInvoker): CommandEntry {\n const slot = new CommandHookController<never, unknown>(invoker);\n\n return {\n detach: undefined,\n hook: { inFlight: false, lastError: null, result: undefined, run: slot.run },\n invoker,\n slot,\n };\n}\n\nfunction unchangedHook(previous: CommandHook<never, unknown>, next: CommandHook<never, unknown>): boolean {\n return (\n previous.inFlight === next.inFlight &&\n Object.is(previous.result, next.result) &&\n Object.is(previous.lastError, next.lastError)\n );\n}\n\nfunction outcomeStatus(previous: CommandHook<never, unknown>, outcome: CommandOutcome<unknown> | undefined) {\n if (outcome?.status === 'ok') return { lastError: null, result: outcome.value };\n\n if (outcome?.status === 'failed') return { lastError: outcome.error, result: previous.result };\n\n return previous;\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 const { lastError, result } = outcomeStatus(previous, outcome);\n const next = { inFlight, lastError, result, 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: CommandEntry | undefined, invoker: CommandInvoker): CommandEntry {\n return previous?.invoker === invoker ? previous : createEntry(invoker);\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, CommandEntry>();\n private readonly listeners = new Set<() => void>();\n private revision = 0;\n\n close(): void {\n for (const entry of this.entries.values()) {\n entry.detach?.();\n entry.detach = undefined;\n }\n\n this.entries.clear();\n }\n\n commit(selected: Map<PropertyKey, CommandEntry>): void {\n for (const [key, entry] of this.entries) {\n if (selected.get(key) === entry) continue;\n\n entry.detach?.();\n entry.detach = undefined;\n }\n\n this.entries = selected;\n\n for (const entry of selected.values()) {\n entry.detach ??= entry.slot.attach((inFlight, outcome) => {\n if (!updateEntry(entry, inFlight, outcome)) return;\n\n this.revision += 1;\n\n for (const listener of this.listeners) listener();\n });\n }\n }\n\n getSnapshot = (): number => this.revision;\n\n prepare(commands: CommandSelection, frame: Frame): Map<PropertyKey, CommandEntry> {\n const selected = new Map<PropertyKey, CommandEntry>();\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, Call<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 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): CommandGroup | undefined {\n const groupRef = useRef<CommandGroup | undefined>(undefined);\n const populated = Reflect.ownKeys(commands).some(key => Object.prototype.propertyIsEnumerable.call(commands, key));\n\n return populated ? (groupRef.current ??= new CommandGroup()) : undefined;\n}\n\nfunction useCommands<Commands extends { readonly [Key in keyof Commands]: Call<never, unknown> }>(\n commands: Commands,\n): CommandsHook<Commands> {\n const frame = useFrame();\n const group = useCommandGroup(commands);\n const getSnapshot = group?.getSnapshot ?? idleRevision;\n useSyncExternalStore(group?.subscribe ?? subscribeIdle, getSnapshot, getSnapshot);\n const selected = group?.prepare(commands, 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 for (const entry of selected.values()) entry.slot.synchronize();\n });\n\n return (\n selected === undefined ? emptyHooks : Object.fromEntries([...selected].map(([key, entry]) => [key, entry.hook]))\n ) as CommandsHook<Commands>;\n}\n\nexport { useCommands };\n","import type { Readable } from '@opetope/core';\nimport { isCallTarget, isReadable } from '@opetope/core/internal';\n\nimport { ContributionError } from './errors';\nimport type { AnyCommand } from './model-binding';\n\ninterface SelectionRead {\n <Value>(source: Readable<Value>): Value;\n <Value, Selected>(source: Readable<Value>, select: (value: Value) => Selected): Selected;\n}\n\ntype SelectionContext = Readonly<{ read: SelectionRead }>;\ntype NonSelectionRecord =\n | readonly unknown[]\n | ((...arguments_: never[]) => unknown)\n | (abstract new (...arguments_: never[]) => unknown)\n | Date\n | PromiseLike<unknown>\n | ReadonlyMap<unknown, unknown>\n | ReadonlySet<unknown>\n | RegExp\n | WeakMap<object, unknown>\n | WeakSet<object>;\n/** A named interface needs no open index signature; container and callable results are not field selections. */\ntype SelectionShape<Selected extends object> = Selected extends NonSelectionRecord ? never : Selected;\ntype SelectionRecord = Readonly<Record<PropertyKey, unknown>>;\ntype SelectionSnapshot = Readonly<{\n commands: Readonly<Record<PropertyKey, AnyCommand>>;\n sources: ReadonlySet<Readable<unknown>>;\n values: SelectionRecord;\n}>;\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.');\n }\n\n if (sources.has(source)) return sources.get(source) as Value;\n\n const value = source.getSnapshot();\n sources.set(source, value);\n\n return value;\n}\n\nfunction collectSelection(\n select: (context: SelectionContext) => object,\n sources: Map<Readable<unknown>, unknown>,\n): SelectionRecord {\n const read = <Value, Selected>(source: Readable<Value>, project?: (value: Value) => Selected) => {\n const value = readSource(source, sources);\n\n return project === undefined ? value : project(value);\n };\n const selected = select({ read });\n\n if (typeof selected !== 'object' || selected === null || Array.isArray(selected)) {\n throw new ContributionError('binding-invalid', 'Model selection requires a record.');\n }\n\n return Object.fromEntries(\n Reflect.ownKeys(selected)\n .filter(key => Object.prototype.propertyIsEnumerable.call(selected, key))\n .map(key => [key, (selected as SelectionRecord)[key]]),\n );\n}\n\nfunction equalSelection(previous: SelectionSnapshot, values: SelectionRecord, sources: ReadonlySet<Readable<unknown>>) {\n const keys = Reflect.ownKeys(values);\n const previousKeys = Reflect.ownKeys(previous.values);\n\n if (keys.length !== previousKeys.length || sources.size !== previous.sources.size) return false;\n\n for (const source of sources) if (!previous.sources.has(source)) 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 = { commands: {}, sources: new Set(), values: {} };\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 * 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 */\nfunction createSelectionSnapshot<Model>(model: Model, selectRef: SelectionReaderRef<Model>): () => SelectionSnapshot {\n let observed: Map<Readable<unknown>, unknown> | undefined;\n let observedSelect: ModelSelector<Model> | undefined;\n let snapshot: SelectionSnapshot = emptySelectionSnapshot;\n\n return () => {\n const select = selectRef.current;\n\n if (select === undefined) return emptySelectionSnapshot;\n\n if (\n observed !== undefined &&\n observedSelect === select &&\n [...observed].every(([source, value]) => Object.is(source.getSnapshot(), value))\n ) {\n return snapshot;\n }\n\n const nextObserved = new Map<Readable<unknown>, unknown>();\n const values = collectSelection(context => select(model, context), nextObserved);\n const sources = new Set(nextObserved.keys());\n\n if (observed === undefined || !equalSelection(snapshot, values, sources)) {\n snapshot = { commands: selectionCommands(values), sources, values };\n }\n\n observed = nextObserved;\n observedSelect = select;\n\n return snapshot;\n };\n}\n\nexport { createSelectionSnapshot, emptySelectionSnapshot };\nexport type { ModelSelector, SelectionContext, SelectionShape };\n","import type { Readable } from '@opetope/core';\nimport { createAggregateError } from '@opetope/core/internal';\n\ntype Subscription = { dispose: (() => void) | undefined };\ntype Listener = Readonly<{ notify: () => void }>;\n\nfunction releaseSubscriptions(subscriptions: Iterable<Subscription>, failures: unknown[] = []): void {\n for (const subscription of subscriptions) {\n const dispose = subscription.dispose;\n subscription.dispose = undefined;\n\n try {\n dispose?.();\n } catch (error) {\n failures.push(error);\n }\n }\n\n if (failures.length === 1) throw failures[0];\n\n if (failures.length > 1) throw createAggregateError(failures, 'Model selection sources failed to release.');\n}\n\n/** A subscription set, not a scheduler: source notifications use React's snapshot consistency check. */\nclass ModelSelectionStore {\n private enabled = false;\n private readonly listeners = new Set<Listener>();\n private sources: ReadonlySet<Readable<unknown>> = new Set();\n private readonly subscriptions = new Map<Readable<unknown>, Subscription>();\n\n close(): void {\n this.enabled = false;\n this.sources = new Set();\n this.releaseAll();\n }\n\n commit(sources: ReadonlySet<Readable<unknown>>): void {\n this.sources = sources;\n this.enabled = true;\n\n try {\n this.synchronize();\n } catch (error) {\n this.releaseAll([error]);\n }\n }\n\n subscribe = (notify: () => void): (() => void) => {\n const listener = { notify };\n this.listeners.add(listener);\n\n try {\n this.synchronize();\n } catch (error) {\n this.listeners.delete(listener);\n this.releaseAll([error]);\n }\n\n return () => {\n this.listeners.delete(listener);\n\n if (this.listeners.size === 0) this.releaseAll();\n };\n };\n\n private add(source: Readable<unknown>): void {\n const subscription: Subscription = { dispose: undefined };\n // Reserve before foreign code: a synchronous notification may replace or close this selection.\n this.subscriptions.set(source, subscription);\n const dispose = source.subscribe(this.notify);\n\n if (typeof dispose !== 'function') throw new TypeError('Model selection source must return a disposer.');\n\n if (this.subscriptions.get(source) === subscription) subscription.dispose = dispose;\n else dispose();\n }\n\n private notify = (): void => {\n for (const listener of [...this.listeners]) {\n if (this.listeners.has(listener)) listener.notify();\n }\n };\n\n private releaseAll(failures?: unknown[]): void {\n const subscriptions = [...this.subscriptions.values()];\n this.subscriptions.clear();\n releaseSubscriptions(subscriptions, failures);\n }\n\n private removeUnselected(): void {\n const removed: Subscription[] = [];\n\n for (const [source, subscription] of this.subscriptions) {\n if (this.sources.has(source)) continue;\n\n this.subscriptions.delete(source);\n removed.push(subscription);\n }\n\n releaseSubscriptions(removed);\n }\n\n private synchronize(): void {\n this.removeUnselected();\n\n for (const source of this.sources) {\n if (!this.enabled || this.listeners.size === 0) return;\n\n if (this.sources.has(source) && !this.subscriptions.has(source)) this.add(source);\n }\n }\n}\n\nexport { ModelSelectionStore };\n","import { useLayoutEffect, useMemo, useRef, useSyncExternalStore } from 'react';\n\nimport type { Call, ModelOf } from '@opetope/core';\n\nimport type { CommandHook } from './command-hook';\nimport { useCommands } from './commands-hook';\nimport { ContributionError } from './errors';\nimport { subscribeIdle } from './idle-subscription';\nimport type { AnyModel } from './model-binding';\nimport { createSelectionSnapshot, emptySelectionSnapshot } from './model-selection-snapshot';\nimport type { ModelSelector, SelectionContext, SelectionShape } from './model-selection-snapshot';\nimport { ModelSelectionStore } from './model-selection-store';\nimport { useFrame } from './mount-context';\n\ntype SelectedValue<Value> = Value extends Call<infer Input, infer Output> ? CommandHook<Input, Output> : 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\n/** D205: resolve only a granted model, explicitly read data, and reuse the ordinary per-key command consumers. */\nfunction useModel<Declaration extends AnyModel>(declaration: Declaration): ModelOf<Declaration>;\nfunction useModel<Declaration extends AnyModel, const Selected extends object>(\n declaration: Declaration,\n select: (model: ModelOf<Declaration>, context: SelectionContext) => Selected & SelectionShape<Selected>,\n): ModelSelection<Selected>;\nfunction useModel<Declaration extends AnyModel>(\n declaration: Declaration,\n select?: (model: 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 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<ModelOf<Declaration>> | undefined>(undefined);\n selectRef.current = select;\n const selected = select !== undefined;\n const getSnapshot = useMemo(\n () => (selected ? createSelectionSnapshot(model, selectRef) : readEmptySnapshot),\n [model, selected],\n );\n const snapshot = useSyncExternalStore(store?.subscribe ?? subscribeIdle, getSnapshot, getSnapshot);\n const commands = useCommands(snapshot.commands);\n useLayoutEffect(() => () => store?.close(), [store]);\n useLayoutEffect(() => store?.commit(snapshot.sources), [snapshot, store]);\n\n if (select === undefined) return model;\n\n return Object.fromEntries(\n Reflect.ownKeys(snapshot.values).map(key => [\n key,\n Object.prototype.hasOwnProperty.call(commands, key) ? commands[key] : snapshot.values[key],\n ]),\n );\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 */\ntype ComponentRequiringModels<Props, Models extends readonly AnyModel[]> = FunctionComponent<Props> & {\n readonly requires: Models;\n};\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, useEffect, useSyncExternalStore } from 'react';\n\nimport type { Resource, ResourceSnapshot } from '@opetope/runtime';\nimport type { ResourceRequestKey } from '@opetope/runtime/internal';\n\nfunction useResource<Data, Key extends ResourceRequestKey>(resource: Resource<Data, Key>): ResourceSnapshot<Data, Key> {\n const subscribe = useCallback((listener: () => void) => resource.subscribe(listener), [resource]);\n // D199: a resource is a structural contract, so it may be an object with methods. Reading through it keeps the\n // receiver the adapter expects; handing `resource.getSnapshot` to React would call it with none.\n const getSnapshot = useCallback(() => resource.getSnapshot(), [resource]);\n const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n useEffect(() => resource.retain(), [resource]);\n\n return snapshot;\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 * What a contribution publishes: the component, the optional props adapter and the optional UI models the mount\n * creates. Without an adapter the component takes the slot props; with one it takes exactly the adapter result (D85).\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?: never;\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\nfunction defineSlot<Props extends SlotProperties = undefined>(options: { readonly id: string }): SlotTarget<Props> {\n return createContributionTarget<SlotContribution<Props>>(options);\n}\n\nfunction defineSwitchSlot<Route extends string, Props extends SlotProperties = undefined>(options: {\n readonly id: string;\n}): SwitchSlotTarget<Route, Props> {\n const id = declarationId(options.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>({ id: `${id}/${route}` });\n targets.set(route, target);\n\n return target;\n };\n\n Object.defineProperty(switchSlot, 'id', { enumerable: true, value: id });\n\n return Object.freeze(switchSlot) as SwitchSlotTarget<Route, Props>;\n}\n\n/**\n * Every contribution renders inside its own mount: the models its generation owns plus the UI models it declared,\n * created on mount and closed on unmount (D70, D85).\n */\nfunction Slot<Props extends SlotProperties>(options: SlotRenderProps<Props>): ReactNode {\n const entries = useReadable(options.target.entries);\n const slotProps: object = 'props' in options ? (options.props ?? 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","CallError","compatibleAbortReason","invocation","value","emptyStatus","unchangedState","current","slot","inFlight","nextState","lastError","result","useCommandSlot","slots","useState","useCommand","command","record","useFrame","currentSlot","hookState","setHookState","useInsertionEffect","useLayoutEffect","run","useMemo","idleRelease","subscribeIdle","idleRevision","emptyHooks","createEntry","unchangedHook","previous","next","outcomeStatus","updateEntry","entry","selectEntry","CommandGroup","selected","key","listener","commands","frame","useCommandGroup","groupRef","useRef","useCommands","group","getSnapshot","useSyncExternalStore","readSource","source","sources","isReadable","collectSelection","select","project","equalSelection","values","keys","previousKeys","index","emptySelectionSnapshot","selectionCommands","isCallTarget","createSelectionSnapshot","model","selectRef","observed","observedSelect","snapshot","nextObserved","context","releaseSubscriptions","subscriptions","failures","subscription","dispose","createAggregateError","ModelSelectionStore","removed","readEmptySnapshot","useSelectionStore","storeRef","useModel","declaration","store","requiresModels","models","declared","component","useResource","resource","subscribe","useCallback","useEffect","emptySlotProperties","defineSlot","createContributionTarget","defineSwitchSlot","id","declarationId","targets","switchSlot","route","existing","target","Slot","entries","useReadable","slotProps","createElement","Fragment","ContributionMount","contributionAuthority"],"mappings":"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"}
|
package/dist/integration.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export type { Command, CommandOutcome } from './command.js';
|
|
2
|
+
export { ContributionBoundary } from './contribution-isolation.js';
|
|
3
|
+
export type { ContributionBoundaryProps, ContributionErrorContent, ContributionFailure, } from './contribution-isolation.js';
|
|
2
4
|
export { FeatureBoundary, FeatureBoundaryError, useFeatureRetry } from './feature-boundary.js';
|
|
3
5
|
export type { FeatureBoundaryProps } from './feature-boundary.js';
|
|
4
6
|
export { useFeature } from './feature-demand.js';
|
package/dist/integration.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{jsx as
|
|
1
|
+
import{a as M}from"./contribution-isolation-Bzfbt4iM.js";import{jsx as C}from"react/jsx-runtime";import{useCallback as f,useSyncExternalStore as E,useMemo as h,useEffect as x,useState as S,createContext as B,useContext as g}from"react";const y=()=>{};function b(e){const r=f(t=>e.subscribe(t),[e]),u=f(()=>e.getState(),[e]),d=E(r,u,u),n=h(()=>({current:null,source:e}),[e]);x(()=>{const t=e.acquire();return t.settled.catch(y),()=>{t.release().catch(y)}},[e]);const o=f(()=>{if(n.current!==null)return n.current.settled;let t=()=>{};const s={lease:null,settled:new Promise(p=>{t=p})},c=()=>{n.current===s&&(n.current=null),t()};n.current=s;let i;try{i=n.source.acquire()}catch{return c(),s.settled}return s.lease=i,i.settled.catch(y).finally(c),i.release().catch(y),s.settled},[n]);return h(()=>Object.freeze({retry:o,state:d}),[o,d])}const F=B(void 0);class v extends Error{code;name="FeatureBoundaryError";constructor(r,u){super(u),this.code=r}}function w(){const e=g(F);if(e===void 0)throw new v("missing","Feature error hooks may only be used by the error subtree of a FeatureBoundary.");return e}function q(){return w().retry}function j({children:e,demand:r,error:u,fallback:d}){const{retry:n,state:o}=b(r),[t,s]=S(),c=f(()=>{const a={},m=()=>s(l=>(l==null?void 0:l.attempt)===a?void 0:l);s({attempt:a,demand:r});try{n().finally(m)}catch(l){throw m(),l}},[r,n]),i=h(()=>({retry:c}),[c]),p=(t==null?void 0:t.demand)===r;if(x(()=>{s(a=>a===void 0||a.demand===r&&o.instance===null?a:void 0)},[r,o.instance]),o.instance!==null)return typeof e=="function"?e(o.instance):e;if(o.error!==null&&!p){const a=typeof u=="function"?u({error:o.error,retry:c}):u;return C(F,{value:i,children:a})}return d}export{M as ContributionBoundary,j as FeatureBoundary,v as FeatureBoundaryError,b as useFeature,q as useFeatureRetry};
|
|
2
2
|
//# sourceMappingURL=integration.js.map
|
package/dist/integration.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.js","sources":["../src/feature-demand.ts","../src/feature-boundary.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useSyncExternalStore } from 'react';\n\ninterface FeatureDemandLease {\n readonly release: () => Promise<void>;\n readonly settled: Promise<void>;\n}\n\ninterface FeatureDemandState<Ready> {\n readonly error: unknown | null;\n readonly instance: Ready | null;\n}\n\ninterface FeatureDemandSource<Ready> {\n acquire(): FeatureDemandLease;\n getState(): FeatureDemandState<Ready>;\n subscribe(listener: () => void): () => void;\n}\n\ninterface FeatureDemandResult<Ready> {\n /** Resolves when the attempt it started has settled, in success and in failure alike (D198). */\n readonly retry: () => Promise<void>;\n readonly state: FeatureDemandState<Ready>;\n}\n\nconst ignoreFailure = (): void => undefined;\n\nfunction useFeature<Ready>(source: FeatureDemandSource<Ready>): FeatureDemandResult<Ready> {\n const subscribe = useCallback((listener: () => void) => source.subscribe(listener), [source]);\n const getSnapshot = useCallback(() => source.getState(), [source]);\n const state = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n const retrying = useMemo<{\n current: { lease: FeatureDemandLease | null; readonly settled: Promise<void> } | null;\n readonly source: FeatureDemandSource<Ready>;\n }>(() => ({ current: null, source }), [source]);\n\n useEffect(() => {\n const demand = source.acquire();\n void demand.settled.catch(ignoreFailure);\n\n return () => void demand.release().catch(ignoreFailure);\n }, [source]);\n\n const retry = useCallback((): Promise<void> => {\n // Single-flight per source: a second call joins the attempt already running instead of starting another.\n if (retrying.current !== null) return retrying.current.settled;\n\n // D198: `acquire` runs foreign code, and a host that answers it synchronously may call `retry` again. The\n // attempt is reserved with its own promise before that call, so the reentrant caller joins this attempt\n // instead of taking a second lease of its own.\n let settle = (): void => undefined;\n const attempt = {\n lease: null as FeatureDemandLease | null,\n settled: new Promise<void>(resolve => {\n settle = resolve;\n }),\n };\n const finish = (): void => {\n if (retrying.current === attempt) retrying.current = null;\n\n settle();\n };\n retrying.current = attempt;\n\n let lease: FeatureDemandLease;\n\n try {\n lease = retrying.source.acquire();\n } catch {\n // D198: `retry` answers with the promise of the attempt it started, and an acquisition that refused is an\n // attempt that is over. The refusal belongs to the state the source publishes, which is where the caller\n // reads it; throwing it back here would leave every caller of `retry` waiting for an attempt that ended.\n finish();\n\n return attempt.settled;\n }\n\n attempt.lease = lease;\n void lease.settled.catch(ignoreFailure).finally(finish);\n void lease.release().catch(ignoreFailure);\n\n return attempt.settled;\n }, [retrying]);\n\n return useMemo(() => Object.freeze({ retry, state }), [retry, state]);\n}\n\nexport { useFeature };\nexport type { FeatureDemandLease, FeatureDemandResult, FeatureDemandSource, FeatureDemandState };\n","import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';\nimport type { ReactNode } from 'react';\n\nimport { useFeature } from './feature-demand';\nimport type { FeatureDemandSource } from './feature-demand';\n\n/**\n * D177: a branch may be a node or a render callback. The callback receives what only the boundary knows — the ready\n * instance, or the failure with the retry that belongs to this demand — so a ready consumer needs no second\n * `useFeature` and no second lease. A function is never a `ReactNode`, so the two forms cannot be confused.\n */\ntype FeatureBoundaryChildren<Ready> = ((instance: Ready) => ReactNode) | ReactNode;\ntype FeatureBoundaryErrorContent =\n | ((context: { readonly error: unknown; readonly retry: () => void }) => ReactNode)\n | ReactNode;\n\n/** The boundary keeps the feature open while it is mounted; its UI enters the host through slots (D85). */\ntype FeatureBoundaryProps<Ready = unknown> = {\n readonly children: FeatureBoundaryChildren<Ready>;\n readonly demand: FeatureDemandSource<Ready>;\n readonly error: FeatureBoundaryErrorContent;\n readonly fallback: ReactNode;\n};\n\n/** Only `retry` is read: the error itself belongs to the `error` subtree a host already renders (D142). */\ntype FeatureBoundaryErrorContext = {\n readonly retry: () => void;\n};\n\n/** D198: the error is hidden for the life of the attempt that was started, not until the error object changes. */\ntype SuppressedError = {\n readonly attempt: object;\n readonly demand: object;\n};\n\nconst ErrorContext = createContext<FeatureBoundaryErrorContext | undefined>(undefined);\n\n/** One class per subject with the state in `code` (D69): the boundary is its own subject, not a feature failure. */\nclass FeatureBoundaryError extends Error {\n override readonly name = 'FeatureBoundaryError';\n\n constructor(\n readonly code: 'missing',\n message: string,\n ) {\n super(message);\n }\n}\n\nfunction useBoundaryErrorContext(): FeatureBoundaryErrorContext {\n const context = useContext(ErrorContext);\n\n if (context === undefined) {\n throw new FeatureBoundaryError(\n 'missing',\n 'Feature error hooks may only be used by the error subtree of a FeatureBoundary.',\n );\n }\n\n return context;\n}\n\nfunction useFeatureRetry(): () => void {\n return useBoundaryErrorContext().retry;\n}\n\nfunction FeatureBoundary<Ready>({ children, demand, error, fallback }: FeatureBoundaryProps<Ready>): ReactNode {\n const { retry: requestRetry, state } = useFeature(demand);\n const [suppressed, setSuppressed] = useState<SuppressedError>();\n const retry = useCallback(() => {\n const attempt = {};\n // The attempt is over: whatever it ended with, including the very same error object, is the answer to show.\n const reveal = (): void => setSuppressed(current => (current?.attempt === attempt ? undefined : current));\n setSuppressed({ attempt, demand });\n\n try {\n void requestRetry().finally(reveal);\n } catch (error) {\n // A retry that refuses synchronously has no promise to settle, and an error hidden by it would never be\n // shown again: the suppression is lifted here before the refusal goes back to whoever asked (D198).\n reveal();\n\n throw error;\n }\n }, [demand, requestRetry]);\n const errorContext = useMemo(() => ({ retry }), [retry]);\n const errorIsSuppressed = suppressed?.demand === demand;\n\n useEffect(() => {\n // A suppression outlives neither its source nor a ready feature: only the attempt of this demand may hide it.\n setSuppressed(current =>\n current === undefined || (current.demand === demand && state.instance === null) ? current : undefined,\n );\n }, [demand, state.instance]);\n\n // Only the branch that is shown runs its callback: an abandoned branch renders nothing and computes nothing.\n if (state.instance !== null) return typeof children === 'function' ? children(state.instance) : children;\n\n if (state.error !== null && !errorIsSuppressed) {\n const content = typeof error === 'function' ? error({ error: state.error, retry }) : error;\n\n return <ErrorContext value={errorContext}>{content}</ErrorContext>;\n }\n\n return fallback;\n}\n\nexport { FeatureBoundary, FeatureBoundaryError, useFeatureRetry };\nexport type { FeatureBoundaryProps };\n"],"names":["ignoreFailure","useFeature","source","subscribe","useCallback","listener","getSnapshot","state","useSyncExternalStore","retrying","useMemo","useEffect","demand","retry","settle","attempt","resolve","finish","lease","ErrorContext","createContext","FeatureBoundaryError","code","message","useBoundaryErrorContext","context","useContext","useFeatureRetry","FeatureBoundary","children","error","fallback","requestRetry","suppressed","setSuppressed","useState","reveal","current","errorContext","errorIsSuppressed","content","_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"integration.js","sources":["../src/feature-demand.ts","../src/feature-boundary.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useSyncExternalStore } from 'react';\n\ninterface FeatureDemandLease {\n readonly release: () => Promise<void>;\n readonly settled: Promise<void>;\n}\n\ninterface FeatureDemandState<Ready> {\n readonly error: unknown | null;\n readonly instance: Ready | null;\n}\n\ninterface FeatureDemandSource<Ready> {\n acquire(): FeatureDemandLease;\n getState(): FeatureDemandState<Ready>;\n subscribe(listener: () => void): () => void;\n}\n\ninterface FeatureDemandResult<Ready> {\n /** Resolves when the attempt it started has settled, in success and in failure alike (D198). */\n readonly retry: () => Promise<void>;\n readonly state: FeatureDemandState<Ready>;\n}\n\nconst ignoreFailure = (): void => undefined;\n\nfunction useFeature<Ready>(source: FeatureDemandSource<Ready>): FeatureDemandResult<Ready> {\n const subscribe = useCallback((listener: () => void) => source.subscribe(listener), [source]);\n const getSnapshot = useCallback(() => source.getState(), [source]);\n const state = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n const retrying = useMemo<{\n current: { lease: FeatureDemandLease | null; readonly settled: Promise<void> } | null;\n readonly source: FeatureDemandSource<Ready>;\n }>(() => ({ current: null, source }), [source]);\n\n useEffect(() => {\n const demand = source.acquire();\n void demand.settled.catch(ignoreFailure);\n\n return () => void demand.release().catch(ignoreFailure);\n }, [source]);\n\n const retry = useCallback((): Promise<void> => {\n // Single-flight per source: a second call joins the attempt already running instead of starting another.\n if (retrying.current !== null) return retrying.current.settled;\n\n // D198: `acquire` runs foreign code, and a host that answers it synchronously may call `retry` again. The\n // attempt is reserved with its own promise before that call, so the reentrant caller joins this attempt\n // instead of taking a second lease of its own.\n let settle = (): void => undefined;\n const attempt = {\n lease: null as FeatureDemandLease | null,\n settled: new Promise<void>(resolve => {\n settle = resolve;\n }),\n };\n const finish = (): void => {\n if (retrying.current === attempt) retrying.current = null;\n\n settle();\n };\n retrying.current = attempt;\n\n let lease: FeatureDemandLease;\n\n try {\n lease = retrying.source.acquire();\n } catch {\n // D198: `retry` answers with the promise of the attempt it started, and an acquisition that refused is an\n // attempt that is over. The refusal belongs to the state the source publishes, which is where the caller\n // reads it; throwing it back here would leave every caller of `retry` waiting for an attempt that ended.\n finish();\n\n return attempt.settled;\n }\n\n attempt.lease = lease;\n void lease.settled.catch(ignoreFailure).finally(finish);\n void lease.release().catch(ignoreFailure);\n\n return attempt.settled;\n }, [retrying]);\n\n return useMemo(() => Object.freeze({ retry, state }), [retry, state]);\n}\n\nexport { useFeature };\nexport type { FeatureDemandLease, FeatureDemandResult, FeatureDemandSource, FeatureDemandState };\n","import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';\nimport type { ReactNode } from 'react';\n\nimport { useFeature } from './feature-demand';\nimport type { FeatureDemandSource } from './feature-demand';\n\n/**\n * D177: a branch may be a node or a render callback. The callback receives what only the boundary knows — the ready\n * instance, or the failure with the retry that belongs to this demand — so a ready consumer needs no second\n * `useFeature` and no second lease. A function is never a `ReactNode`, so the two forms cannot be confused.\n */\ntype FeatureBoundaryChildren<Ready> = ((instance: Ready) => ReactNode) | ReactNode;\ntype FeatureBoundaryErrorContent =\n | ((context: { readonly error: unknown; readonly retry: () => void }) => ReactNode)\n | ReactNode;\n\n/** The boundary keeps the feature open while it is mounted; its UI enters the host through slots (D85). */\ntype FeatureBoundaryProps<Ready = unknown> = {\n readonly children: FeatureBoundaryChildren<Ready>;\n readonly demand: FeatureDemandSource<Ready>;\n readonly error: FeatureBoundaryErrorContent;\n readonly fallback: ReactNode;\n};\n\n/** Only `retry` is read: the error itself belongs to the `error` subtree a host already renders (D142). */\ntype FeatureBoundaryErrorContext = {\n readonly retry: () => void;\n};\n\n/** D198: the error is hidden for the life of the attempt that was started, not until the error object changes. */\ntype SuppressedError = {\n readonly attempt: object;\n readonly demand: object;\n};\n\nconst ErrorContext = createContext<FeatureBoundaryErrorContext | undefined>(undefined);\n\n/** One class per subject with the state in `code` (D69): the boundary is its own subject, not a feature failure. */\nclass FeatureBoundaryError extends Error {\n override readonly name = 'FeatureBoundaryError';\n\n constructor(\n readonly code: 'missing',\n message: string,\n ) {\n super(message);\n }\n}\n\nfunction useBoundaryErrorContext(): FeatureBoundaryErrorContext {\n const context = useContext(ErrorContext);\n\n if (context === undefined) {\n throw new FeatureBoundaryError(\n 'missing',\n 'Feature error hooks may only be used by the error subtree of a FeatureBoundary.',\n );\n }\n\n return context;\n}\n\nfunction useFeatureRetry(): () => void {\n return useBoundaryErrorContext().retry;\n}\n\nfunction FeatureBoundary<Ready>({ children, demand, error, fallback }: FeatureBoundaryProps<Ready>): ReactNode {\n const { retry: requestRetry, state } = useFeature(demand);\n const [suppressed, setSuppressed] = useState<SuppressedError>();\n const retry = useCallback(() => {\n const attempt = {};\n // The attempt is over: whatever it ended with, including the very same error object, is the answer to show.\n const reveal = (): void => setSuppressed(current => (current?.attempt === attempt ? undefined : current));\n setSuppressed({ attempt, demand });\n\n try {\n void requestRetry().finally(reveal);\n } catch (error) {\n // A retry that refuses synchronously has no promise to settle, and an error hidden by it would never be\n // shown again: the suppression is lifted here before the refusal goes back to whoever asked (D198).\n reveal();\n\n throw error;\n }\n }, [demand, requestRetry]);\n const errorContext = useMemo(() => ({ retry }), [retry]);\n const errorIsSuppressed = suppressed?.demand === demand;\n\n useEffect(() => {\n // A suppression outlives neither its source nor a ready feature: only the attempt of this demand may hide it.\n setSuppressed(current =>\n current === undefined || (current.demand === demand && state.instance === null) ? current : undefined,\n );\n }, [demand, state.instance]);\n\n // Only the branch that is shown runs its callback: an abandoned branch renders nothing and computes nothing.\n if (state.instance !== null) return typeof children === 'function' ? children(state.instance) : children;\n\n if (state.error !== null && !errorIsSuppressed) {\n const content = typeof error === 'function' ? error({ error: state.error, retry }) : error;\n\n return <ErrorContext value={errorContext}>{content}</ErrorContext>;\n }\n\n return fallback;\n}\n\nexport { FeatureBoundary, FeatureBoundaryError, useFeatureRetry };\nexport type { FeatureBoundaryProps };\n"],"names":["ignoreFailure","useFeature","source","subscribe","useCallback","listener","getSnapshot","state","useSyncExternalStore","retrying","useMemo","useEffect","demand","retry","settle","attempt","resolve","finish","lease","ErrorContext","createContext","FeatureBoundaryError","code","message","useBoundaryErrorContext","context","useContext","useFeatureRetry","FeatureBoundary","children","error","fallback","requestRetry","suppressed","setSuppressed","useState","reveal","current","errorContext","errorIsSuppressed","content","_jsx"],"mappings":"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"}
|
package/dist/scenario-slot.d.ts
CHANGED
|
@@ -10,6 +10,11 @@ type SlotTestFixture<Props extends SlotProperties> = {
|
|
|
10
10
|
readonly contribution?: SlotContribution<Props>;
|
|
11
11
|
readonly models?: readonly ModelFixture[];
|
|
12
12
|
readonly props?: SlotComponentProperties<Props>;
|
|
13
|
+
/**
|
|
14
|
+
* Where a contained mount failure is reported (D256). A component test has no feature to report to, so without it
|
|
15
|
+
* the failure is detached and the assertion has nothing to read.
|
|
16
|
+
*/
|
|
17
|
+
readonly reporter?: (error: unknown) => void;
|
|
13
18
|
};
|
|
14
19
|
interface SlotTestHarness<Props extends SlotProperties> {
|
|
15
20
|
readonly Slot: FC;
|
package/dist/testing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{defineCallTarget as x,createAggregateError as _}from"@opetope/core/internal";import{openApplication as N}from"@opetope/runtime";import{createInspectionSession as q}from"@opetope/runtime/internal";import{createElement as B}from"react";import{d as
|
|
1
|
+
import{defineCallTarget as x,createAggregateError as _}from"@opetope/core/internal";import{openApplication as N}from"@opetope/runtime";import{createInspectionSession as q}from"@opetope/runtime/internal";import{createElement as B}from"react";import{declarationId as H}from"@opetope/core";import{d as D,a as F,c as L,M as U}from"./contribution-frame-B1PnpOaP.js";function W(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 G(e){if(e.lane===null)return`Call ${e.declaration}: ${e.state}; lane parallel; blockers none (parallel).`;const t=e.lane.blockedBy==="unknown"?"unknown":e.lane.blockedBy.join(", ");return`Call ${e.declaration}: ${e.state}; lane ${e.lane.declaration}; blockers ${t}.`}function J(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(G),...e.resources.map(n=>`Resource ${n.id}: ${n.state}; retainers ${String(n.retainers)}.`)]}function K(e){const t=[...e.runtime.conditions.flatMap(n=>n.state.kind==="true"?[]:[`Condition ${n.label}: ${n.state.kind}.`]),...e.runtime.instances.flatMap(W),...J(e.activity)];return t.length===0?"No registered runtime blocker observed; external UI and host work are unknown.":t.join(`
|
|
2
2
|
`)}class E extends Error{label;timeoutMs;snapshot;history;code="timeout";constructor(t,n,r,o){super(`Scenario timed out after ${String(n)}ms: ${t}.
|
|
3
|
-
${
|
|
3
|
+
${K(r)}`),this.label=t,this.timeoutMs=n,this.snapshot=r,this.history=o,this.name="ScenarioTimeoutError"}}function Q(e,t){return!t||e===void 0?!1:e.closed&&!e.truncated}function V(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 X(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 Y(e,t){const n=e.activity,r=Q(n,t);return Object.freeze({...X(n,r),closed:t,reasons:V(n,r,t),scope:"registered-runtime",status:r?"complete":"unknown"})}class Z{released;get closed(){return this.completion!==void 0}available=!1;completion;handle;reject;resolve;constructor(t){this.released=t}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,o=this.reject;this.resolve=void 0,this.reject=void 0,this.released(t,n),t?o==null||o(n):r==null||r()}releaseAvailable(){const t=this.handle;this.handle=void 0,this.available=!1;try{Promise.resolve(t==null?void 0:t.unmount()).then(()=>this.finish(!1),n=>this.finish(!0,n))}catch(n){this.finish(!0,n)}}}const ee=H("testing.contribution");let T=0;function te(e){return T+=1,D(x({id:`testing.command.${String(T)}`,run:e}))}function I(e,t={}){const n=t.models??[];let r=t.props??{};const o=new Set,w={getSnapshot:()=>r,subscribe:i=>(o.add(i),()=>o.delete(i))},c=t.contribution,s=t.reporter,l=s===void 0?void 0:{models:[],reporter:s};return Object.freeze({close:()=>{r={},o.clear()},Slot:()=>{const i=F(e.entries),d=F(w);return(c===void 0?i.map(p=>[p.id,p.value,L(p)??l]):[[ee,c,l]]).map(([p,f,k])=>B(U,{authority:k,contribution:f,contributionId:p,fixtures:n,key:p,slotProps:d,targetId:e.id}))},updateProps:i=>{r=i;for(const d of[...o])d()}})}function ne(e,t={}){const n=I(e,t);return Object.freeze({Slot:n.Slot,updateProps:n.updateProps})}function C(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 re{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 C(r,"Scenario timeoutMs"),new Promise((o,w)=>{const c=setTimeout(()=>w(this.timeout(n,r)),r);Promise.resolve(t).then(s=>{clearTimeout(c),o(s)},s=>{clearTimeout(c),w(s)})})}notify=()=>{for(const t of[...this.listeners])t()};waitFor(t,n){const r=C(n.timeoutMs??this.source.timeoutMs,"Scenario timeoutMs"),o=C(n.pollIntervalMs??10,"Scenario pollIntervalMs");return new Promise((w,c)=>{let s=!1,l=()=>{};const g=(f,k=f!==void 0)=>{s||(s=!0,l(),k?c(f):w())},i=()=>{if(!s)try{t(this.source.getSnapshot())&&g()}catch(f){g(f,!0)}},d=f=>g(f),v=setTimeout(()=>g(this.timeout(n.label??"waitFor",r)),r),p=setInterval(i,o);l=()=>{clearTimeout(v),clearInterval(p),this.listeners.delete(i),this.pending.delete(d)},this.listeners.add(i),this.pending.add(d),i()})}timeout(t,n){return new E(t,n,this.source.getSnapshot(),this.source.history())}}function A(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 oe(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 se(e,t){const n=C(t.timeoutMs??1e3,"Scenario timeoutMs"),r=A(t.historyCapacity??64,"Scenario historyCapacity"),o=A(t.activityCapacity??256,"Scenario activityCapacity");let w=t.host;oe(w);let c=N(e,{...t.cleanupFailure===void 0?{}:{cleanupFailure:t.cleanupFailure},conditions:t.conditions,imports:t.imports}),s;try{s=q(c,{activityCapacity:o,ringCapacity:r})}catch(u){throw c.ready.catch(()=>{}),c.close().catch(()=>{}),u}let l="open",g=!1,i=!1;const d=[],v=new Set,p=[],f=()=>s.getSnapshot(),k=()=>Object.freeze([...d]),$=new re({getSnapshot:f,history:k,timeoutMs:n}),j=()=>{if(!i){i=!0;try{const u=f(),m=d[d.length-1];((m==null?void 0:m.stateRevision)!==u.stateRevision||m.activity!==u.activity)&&(d.push(u),d.length>r&&d.shift())}finally{i=!1}$.notify()}},R=s.subscribe(j);j();const P=$.deadline(c.ready,"application ready");P.catch(()=>{});let M,S;const z=()=>{if(M!==void 0)return M;let u,m;M=new Promise((a,h)=>{u=a,m=h}),l="closing",$.cancel();const y=[];try{y.push(c.close())}catch(a){y.push(Promise.reject(a))}return y.push(...[...v].map(a=>a.close())),Promise.allSettled(y).then(a=>{const h=[...new Set([...p,...a.flatMap(b=>b.status==="rejected"?[b.reason]:[])])];p.length=0,l="closed";try{j()}catch(b){h.push(b)}finally{R(),s.close(),v.clear(),c=void 0,w=void 0}g=h.length===0,h.length===0?u():m(_(h,"Scenario cleanup failed."))}),M};return Object.freeze({close:()=>{if(S!==void 0)return S;const u=z();return S!==void 0||(S=$.deadline(u,"scenario close"),S.catch(()=>{l==="closing"&&(S=void 0)})),S},getSnapshot:f,history:k,mount:(u,m={})=>{if(l!=="open")throw new Error("Scenario is closing.");const y=I(u,m),a=new Z((b,O)=>{v.delete(a),y.close(),b&&p.push(O)});v.add(a);let h;try{if(h=w.mount(y.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(b){throw a.provide(void 0),a.close().catch(()=>{}),b}return Object.freeze({host:h,unmount:a.close,updateProps:b=>{if(l!=="open"||a.closed)throw new Error("Scenario mount is closed.");y.updateProps(b),$.notify()}})},notify:()=>{l==="open"&&$.notify()},ownership:()=>Y(f(),g),ready:P,waitFor:(u,m={})=>l!=="open"?Promise.reject(new Error("Scenario is closing.")):$.waitFor(u,m)})}export{E as ScenarioTimeoutError,te as command,se as createScenario,ne as renderSlot};
|
|
4
4
|
//# sourceMappingURL=testing.js.map
|
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","sources":["../src/scenario-diagnostics.ts","../src/scenario-mount.ts","../src/scenario-slot.tsx","../src/scenario-wait.ts","../src/scenario.ts"],"sourcesContent":["import type { RuntimeActivitySnapshot, RuntimeGraphSnapshot } from '@opetope/runtime/internal';\n\nimport type { ScenarioOwnership } from './scenario-types';\n\nfunction instanceFacts(instance: RuntimeGraphSnapshot['runtime']['instances'][number]): readonly string[] {\n if (instance.state.kind === 'ready') return [];\n\n const waiting = instance.state.kind === 'waiting' ? ` (${instance.state.reason})` : '';\n\n return [`Feature ${instance.label}: ${instance.state.kind}${waiting}.`];\n}\n\nfunction callFact(call: RuntimeActivitySnapshot['calls'][number]): string {\n if (call.lane === null) return `Call ${call.declaration}: ${call.state}; lane parallel; blockers none (parallel).`;\n\n const blockers = call.lane.blockedBy === 'unknown' ? 'unknown' : call.lane.blockedBy.join(', ');\n\n return `Call ${call.declaration}: ${call.state}; lane ${call.lane.declaration}; blockers ${blockers}.`;\n}\n\nfunction activityFacts(activity: RuntimeActivitySnapshot | undefined): readonly string[] {\n if (activity === undefined) return ['Runtime activity: unknown.'];\n\n const facts: string[] = [];\n\n if (activity.freshness === 'stale' || activity.truncated) {\n facts.push(`Activity: ${activity.freshness}${activity.truncated ? ', truncated' : ''}; completeness unknown.`);\n }\n\n return [\n ...facts,\n ...activity.features.map(\n feature => `Feature ${feature.feature}: ${feature.phase}; body ${feature.body}; host demand unknown.`,\n ),\n ...activity.calls.map(callFact),\n ...activity.resources.map(\n resource => `Resource ${resource.id}: ${resource.state}; retainers ${String(resource.retainers)}.`,\n ),\n ];\n}\n\nfunction explainSnapshot(snapshot: RuntimeGraphSnapshot): string {\n const facts = [\n ...snapshot.runtime.conditions.flatMap(condition =>\n condition.state.kind === 'true' ? [] : [`Condition ${condition.label}: ${condition.state.kind}.`],\n ),\n ...snapshot.runtime.instances.flatMap(instanceFacts),\n ...activityFacts(snapshot.activity),\n ];\n\n return facts.length === 0\n ? 'No registered runtime blocker observed; external UI and host work are unknown.'\n : facts.join('\\n');\n}\n\n/** D206: the failure carries the producer's same data-only graph/activity facts, without product payloads. */\nclass ScenarioTimeoutError extends Error {\n readonly code = 'timeout';\n\n constructor(\n readonly label: string,\n readonly timeoutMs: number,\n readonly snapshot: RuntimeGraphSnapshot,\n readonly history: readonly RuntimeGraphSnapshot[],\n ) {\n super(`Scenario timed out after ${String(timeoutMs)}ms: ${label}.\\n${explainSnapshot(snapshot)}`);\n this.name = 'ScenarioTimeoutError';\n }\n}\n\nfunction ownershipComplete(activity: RuntimeActivitySnapshot | undefined, successfullyClosed: boolean): boolean {\n if (!successfullyClosed || activity === undefined) return false;\n\n return activity.closed && !activity.truncated;\n}\n\nfunction ownershipReasons(\n activity: RuntimeActivitySnapshot | undefined,\n complete: boolean,\n successfullyClosed: boolean,\n): readonly string[] {\n if (activity === undefined) return ['Runtime activity is unavailable.'];\n\n const reasons: string[] = [];\n\n if (activity.truncated) reasons.push('The activity snapshot is truncated.');\n\n if (activity.freshness === 'stale' && !complete) reasons.push('The activity snapshot is stale.');\n\n if (!successfullyClosed) reasons.push('Successful application and mount cleanup has not been observed.');\n\n return Object.freeze(reasons);\n}\n\nfunction ownershipCounts(\n activity: RuntimeActivitySnapshot | undefined,\n complete: boolean,\n): Pick<ScenarioOwnership, 'calls' | 'features' | 'resources'> {\n if (activity === undefined || activity.truncated || (activity.freshness === 'stale' && !complete)) {\n return { calls: 'unknown', features: 'unknown', resources: 'unknown' };\n }\n\n return { calls: activity.calls.length, features: activity.features.length, resources: activity.resources.length };\n}\n\nfunction scenarioOwnership(snapshot: RuntimeGraphSnapshot, successfullyClosed: boolean): ScenarioOwnership {\n const activity = snapshot.activity;\n const complete = ownershipComplete(activity, successfullyClosed);\n\n return Object.freeze({\n ...ownershipCounts(activity, complete),\n closed: successfullyClosed,\n reasons: ownershipReasons(activity, complete, successfullyClosed),\n scope: 'registered-runtime',\n status: complete ? 'complete' : 'unknown',\n });\n}\n\nexport { scenarioOwnership, ScenarioTimeoutError };\n","import type { ScenarioHostMount } from './scenario-types';\n\n/** A reservation exists before the host callback, so reentrant close also owns a mount returned later. */\nclass ScenarioMountLease<Mounted extends ScenarioHostMount> {\n get closed(): boolean {\n return this.completion !== undefined;\n }\n private available = false;\n private completion: Promise<void> | undefined;\n private handle: Mounted | undefined;\n private reject: ((error: unknown) => void) | undefined;\n\n private resolve: (() => void) | undefined;\n\n constructor(private readonly released: (failed: boolean, error?: unknown) => void) {}\n\n readonly close = (): Promise<void> => {\n if (this.completion !== undefined) return this.completion;\n\n this.completion = new Promise<void>((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n\n if (this.available) this.releaseAvailable();\n\n return this.completion;\n };\n\n provide(handle: Mounted | undefined): void {\n this.available = true;\n this.handle = handle;\n\n if (this.closed) this.releaseAvailable();\n }\n\n private finish(failed: boolean, error?: unknown): void {\n const resolve = this.resolve;\n const reject = this.reject;\n this.resolve = undefined;\n this.reject = undefined;\n this.released(failed, error);\n\n if (!failed) resolve?.();\n else reject?.(error);\n }\n\n private releaseAvailable(): void {\n const handle = this.handle;\n this.handle = undefined;\n this.available = false;\n try {\n Promise.resolve(handle?.unmount()).then(\n () => this.finish(false),\n (error: unknown) => this.finish(true, error),\n );\n } catch (error) {\n this.finish(true, error);\n }\n }\n}\n\nexport { ScenarioMountLease };\n","import { createElement } from 'react';\nimport type { FC, ReactNode } from 'react';\n\nimport type { Call } from '@opetope/core';\nimport { defineCallTarget } from '@opetope/core/internal';\n\nimport { bindCommand } from './command';\nimport { contributionAuthority, ContributionMount } from './contribution-frame';\nimport type { ContributionValue, ModelFixture } from './contribution-frame';\nimport { useReadable } from './readable-hooks';\nimport type { SlotComponentProperties, SlotContribution, SlotProperties, SlotTarget } from './slot';\n\n/**\n * Two forms, as `renderRoot` had (D19): without `contribution` the harness mounts what the target published, with it\n * the harness mounts one fixture contribution so a component test needs no generation.\n */\ntype SlotTestFixture<Props extends SlotProperties> = {\n readonly contribution?: SlotContribution<Props>;\n readonly models?: readonly ModelFixture[];\n readonly props?: SlotComponentProperties<Props>;\n};\n\ninterface SlotTestHarness<Props extends SlotProperties> {\n readonly Slot: FC;\n readonly updateProps: (props: SlotComponentProperties<Props>) => void;\n}\n\nlet fixtureSequence = 0;\n\n/** A command a fixture publishes: the test writes the body, the harness gives it the identity a model requires. */\nfunction command<Input, Output>(run: (input: Input) => Output | PromiseLike<Output>): Call<Input, Output> {\n fixtureSequence += 1;\n\n return bindCommand(defineCallTarget<Input, Output>({ id: `testing.command.${String(fixtureSequence)}`, run }));\n}\n\n/**\n * Mounts the contributions of one slot the way a host does, with optional model fixtures for a component test. D19 and D85: the same\n * ContributionMount is shared by component tests and the application scenario harness (D206).\n */\nfunction createSlotHarness<Props extends SlotProperties>(\n target: SlotTarget<Props>,\n fixture: SlotTestFixture<Props> = {},\n): SlotTestHarness<Props> & { readonly close: () => void } {\n const fixtures = fixture.models ?? [];\n let current: object = fixture.props ?? {};\n const listeners = new Set<() => void>();\n const props = {\n getSnapshot: (): object => current,\n subscribe: (listener: () => void): (() => void) => {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n };\n\n const supplied = fixture.contribution;\n const Mounted: FC = (): ReactNode => {\n const published = useReadable(target.entries);\n const slotProps = useReadable(props);\n const entries =\n supplied === undefined\n ? published.map(\n entry => [entry.id as string, entry.value as ContributionValue, contributionAuthority(entry)] as const,\n )\n : [['testing.contribution', supplied as unknown as ContributionValue, undefined] as const];\n\n return entries.map(([key, contribution, authority]) =>\n createElement(ContributionMount, { authority, contribution, fixtures, key, slotProps }),\n );\n };\n\n return Object.freeze({\n close: (): void => {\n current = {};\n listeners.clear();\n },\n Slot: Mounted,\n updateProps: (next: SlotComponentProperties<Props>): void => {\n current = next;\n\n for (const listener of [...listeners]) listener();\n },\n });\n}\n\nfunction renderSlot<Props extends SlotProperties>(\n target: SlotTarget<Props>,\n fixture: SlotTestFixture<Props> = {},\n): SlotTestHarness<Props> {\n const harness = createSlotHarness(target, fixture);\n\n return Object.freeze({ Slot: harness.Slot, updateProps: harness.updateProps });\n}\n\nexport { command, createSlotHarness, renderSlot };\nexport type { SlotTestFixture, SlotTestHarness };\n","import type { RuntimeGraphSnapshot } from '@opetope/runtime/internal';\n\nimport { ScenarioTimeoutError } from './scenario-diagnostics';\nimport type { ScenarioWaitOptions } from './scenario-types';\n\nfunction positiveDuration(value: number, label: string): number {\n if (!Number.isFinite(value) || value <= 0 || value > 2_147_483_647) {\n throw new RangeError(`${label} must be a finite duration from 0 (exclusive) to 2147483647ms.`);\n }\n\n return value;\n}\n\ninterface ScenarioWaitSource {\n readonly getSnapshot: () => RuntimeGraphSnapshot;\n readonly history: () => readonly RuntimeGraphSnapshot[];\n readonly timeoutMs: number;\n}\n\nclass ScenarioWaiters {\n private readonly listeners = new Set<() => void>();\n private readonly pending = new Set<(error: Error) => void>();\n\n constructor(private readonly source: ScenarioWaitSource) {}\n\n cancel(): void {\n for (const reject of [...this.pending]) reject(new Error('Scenario is closing.'));\n }\n\n deadline<Value>(result: PromiseLike<Value>, label: string, timeoutMs = this.source.timeoutMs): Promise<Value> {\n positiveDuration(timeoutMs, 'Scenario timeoutMs');\n\n return new Promise<Value>((resolve, reject) => {\n const timer = setTimeout(() => reject(this.timeout(label, timeoutMs)), timeoutMs);\n Promise.resolve(result).then(\n value => {\n clearTimeout(timer);\n resolve(value);\n },\n (error: unknown) => {\n clearTimeout(timer);\n reject(error);\n },\n );\n });\n }\n\n readonly notify = (): void => {\n for (const listener of [...this.listeners]) listener();\n };\n\n waitFor(predicate: (snapshot: RuntimeGraphSnapshot) => boolean, options: ScenarioWaitOptions): Promise<void> {\n const timeoutMs = positiveDuration(options.timeoutMs ?? this.source.timeoutMs, 'Scenario timeoutMs');\n const pollIntervalMs = positiveDuration(options.pollIntervalMs ?? 10, 'Scenario pollIntervalMs');\n\n return new Promise<void>((resolve, reject) => {\n let done = false;\n let dispose = (): void => undefined;\n const finish = (error?: unknown, failed = error !== undefined): void => {\n if (done) return;\n\n done = true;\n dispose();\n\n if (!failed) resolve();\n else reject(error);\n };\n const check = (): void => {\n if (done) return;\n\n try {\n if (predicate(this.source.getSnapshot())) finish();\n } catch (error) {\n finish(error, true);\n }\n };\n const cancel = (error: Error): void => finish(error);\n const deadline = setTimeout(() => finish(this.timeout(options.label ?? 'waitFor', timeoutMs)), timeoutMs);\n const poll = setInterval(check, pollIntervalMs);\n dispose = (): void => {\n clearTimeout(deadline);\n clearInterval(poll);\n this.listeners.delete(check);\n this.pending.delete(cancel);\n };\n this.listeners.add(check);\n this.pending.add(cancel);\n check();\n });\n }\n\n private timeout(label: string, timeoutMs: number): ScenarioTimeoutError {\n return new ScenarioTimeoutError(label, timeoutMs, this.source.getSnapshot(), this.source.history());\n }\n}\n\nexport { positiveDuration, ScenarioWaiters };\n","import { createAggregateError } from '@opetope/core/internal';\nimport { openApplication } from '@opetope/runtime';\nimport type { Application, ApplicationExecution } from '@opetope/runtime';\nimport { createInspectionSession } from '@opetope/runtime/internal';\nimport type { RuntimeGraphSnapshot, RuntimeInspectionSession } from '@opetope/runtime/internal';\n\nimport { scenarioOwnership } from './scenario-diagnostics';\nimport { ScenarioMountLease } from './scenario-mount';\nimport { createSlotHarness } from './scenario-slot';\nimport type {\n Scenario,\n ScenarioFeatures,\n ScenarioHost,\n ScenarioHostMount,\n ScenarioMount,\n ScenarioOptions,\n ScenarioWaitOptions,\n} from './scenario-types';\nimport { positiveDuration, ScenarioWaiters } from './scenario-wait';\nimport type { SlotComponentProperties, SlotProperties, SlotTarget } from './slot';\n\nfunction boundedCapacity(value: number, label: string): number {\n if (!Number.isSafeInteger(value) || value < 1 || value > 10_000) {\n throw new RangeError(`${label} must be an integer from 1 to 10000.`);\n }\n\n return value;\n}\n\nfunction validateHost(host: unknown): void {\n if (host === null || typeof host !== 'object' || !('mount' in host) || typeof host.mount !== 'function') {\n throw new TypeError('Scenario requires a host.mount(Component) adapter.');\n }\n}\n\n/** D206: one real application, its existing inspection session and renderer-owned Slot ingress. */\nfunction createScenario<const Features extends ScenarioFeatures, Mounted extends ScenarioHostMount>(\n application: Application<Features>,\n options: ScenarioOptions<Features, Mounted>,\n): Scenario<Mounted> {\n const timeoutMs = positiveDuration(options.timeoutMs ?? 1_000, 'Scenario timeoutMs');\n const historyCapacity = boundedCapacity(options.historyCapacity ?? 64, 'Scenario historyCapacity');\n const activityCapacity = boundedCapacity(options.activityCapacity ?? 256, 'Scenario activityCapacity');\n let host: ScenarioHost<Mounted> | undefined = options.host;\n\n validateHost(host);\n\n let execution: ApplicationExecution | undefined = openApplication(application, {\n ...(options.cleanupFailure === undefined ? {} : { cleanupFailure: options.cleanupFailure }),\n conditions: options.conditions,\n imports: options.imports,\n });\n let session: RuntimeInspectionSession;\n try {\n session = createInspectionSession(execution, { activityCapacity, ringCapacity: historyCapacity });\n } catch (error) {\n void execution.ready.catch(() => undefined);\n void execution.close().catch(() => undefined);\n throw error;\n }\n\n let phase: 'closed' | 'closing' | 'open' = 'open';\n let successfullyClosed = false;\n let capturing = false;\n const records: RuntimeGraphSnapshot[] = [];\n const mounts = new Set<ScenarioMountLease<Mounted>>();\n const mountFailures: unknown[] = [];\n const getSnapshot = (): RuntimeGraphSnapshot => session.getSnapshot();\n const history = (): readonly RuntimeGraphSnapshot[] => Object.freeze([...records]);\n const waiters = new ScenarioWaiters({ getSnapshot, history, timeoutMs });\n const capture = (): void => {\n if (capturing) return;\n\n capturing = true;\n try {\n const snapshot = getSnapshot();\n const previous = records[records.length - 1];\n\n if (previous?.stateRevision !== snapshot.stateRevision || previous.activity !== snapshot.activity) {\n records.push(snapshot);\n\n if (records.length > historyCapacity) records.shift();\n }\n } finally {\n capturing = false;\n }\n waiters.notify();\n };\n const release = session.subscribe(capture);\n capture();\n const ready = waiters.deadline(execution.ready, 'application ready');\n // A test may deliberately inspect loading without awaiting ready; rejection remains observable to that caller.\n void ready.catch(() => undefined);\n\n let physicalClose: Promise<void> | undefined;\n let closeAttempt: Promise<void> | undefined;\n const startClose = (): Promise<void> => {\n if (physicalClose !== undefined) return physicalClose;\n\n let resolve!: () => void;\n let reject!: (error: unknown) => void;\n physicalClose = new Promise<void>((accept, refuse) => {\n resolve = accept;\n reject = refuse;\n });\n phase = 'closing';\n waiters.cancel();\n const drains: Promise<void>[] = [];\n // Fence before any foreign unmount callback can reenter UI ingress; join all physical drains afterwards.\n try {\n drains.push(execution!.close());\n } catch (error) {\n drains.push(Promise.reject(error));\n }\n drains.push(...[...mounts].map(mount => mount.close()));\n void Promise.allSettled(drains).then(results => {\n const failures = [\n ...new Set([\n ...mountFailures,\n ...results.flatMap(result => (result.status === 'rejected' ? [result.reason as unknown] : [])),\n ]),\n ];\n mountFailures.length = 0;\n phase = 'closed';\n try {\n capture();\n } catch (error) {\n failures.push(error);\n } finally {\n release();\n session.close();\n mounts.clear();\n execution = undefined;\n host = undefined;\n }\n successfullyClosed = failures.length === 0;\n\n if (failures.length === 0) resolve();\n else reject(createAggregateError(failures, 'Scenario cleanup failed.'));\n });\n\n return physicalClose;\n };\n\n const close = (): Promise<void> => {\n if (closeAttempt !== undefined) return closeAttempt;\n\n const drain = startClose();\n\n // A nested close during unmount may have installed the same attempt already.\n if (closeAttempt !== undefined) return closeAttempt;\n\n closeAttempt = waiters.deadline(drain, 'scenario close');\n void closeAttempt.catch(() => {\n // A deadline cannot cancel physical work. A later close can await that same drain with a new deadline.\n if (phase === 'closing') closeAttempt = undefined;\n });\n\n return closeAttempt;\n };\n\n const mount: Scenario<Mounted>['mount'] = <Props extends SlotProperties>(\n target: SlotTarget<Props>,\n fixture: { readonly props?: SlotComponentProperties<Props> } = {},\n ): ScenarioMount<Props, Mounted> => {\n if (phase !== 'open') throw new Error('Scenario is closing.');\n\n const slot = createSlotHarness(target, fixture);\n const lease = new ScenarioMountLease<Mounted>((failed, error) => {\n mounts.delete(lease);\n slot.close();\n\n if (failed) mountFailures.push(error);\n });\n mounts.add(lease);\n let mounted: Mounted;\n try {\n mounted = host!.mount(slot.Slot);\n\n if (mounted === null || typeof mounted !== 'object' || typeof mounted.unmount !== 'function') {\n throw new TypeError('Scenario host.mount must return an unmount() handle.');\n }\n\n lease.provide(mounted);\n } catch (error) {\n lease.provide(undefined);\n void lease.close().catch(() => undefined);\n throw error;\n }\n\n return Object.freeze({\n host: mounted,\n unmount: lease.close,\n updateProps: (props: SlotComponentProperties<Props>): void => {\n if (phase !== 'open' || lease.closed) throw new Error('Scenario mount is closed.');\n\n slot.updateProps(props);\n waiters.notify();\n },\n });\n };\n\n return Object.freeze({\n close,\n getSnapshot,\n history,\n mount,\n notify: (): void => {\n if (phase === 'open') waiters.notify();\n },\n ownership: () => scenarioOwnership(getSnapshot(), successfullyClosed),\n ready,\n waitFor: (predicate: (snapshot: RuntimeGraphSnapshot) => boolean, waitOptions: ScenarioWaitOptions = {}) => {\n if (phase !== 'open') return Promise.reject(new Error('Scenario is closing.'));\n\n return waiters.waitFor(predicate, waitOptions);\n },\n });\n}\n\nexport { createScenario };\n"],"names":["instanceFacts","instance","waiting","callFact","call","blockers","activityFacts","activity","facts","feature","resource","explainSnapshot","snapshot","condition","ScenarioTimeoutError","label","timeoutMs","history","ownershipComplete","successfullyClosed","ownershipReasons","complete","reasons","ownershipCounts","scenarioOwnership","ScenarioMountLease","released","resolve","reject","handle","failed","error","fixtureSequence","command","run","bindCommand","defineCallTarget","createSlotHarness","target","fixture","fixtures","current","listeners","props","listener","supplied","published","useReadable","slotProps","entry","contributionAuthority","key","contribution","authority","createElement","ContributionMount","next","renderSlot","harness","positiveDuration","value","ScenarioWaiters","source","result","timer","predicate","options","pollIntervalMs","done","dispose","finish","check","cancel","deadline","poll","boundedCapacity","validateHost","host","createScenario","application","historyCapacity","activityCapacity","execution","openApplication","session","createInspectionSession","phase","capturing","records","mounts","mountFailures","getSnapshot","waiters","capture","previous","release","ready","physicalClose","closeAttempt","startClose","accept","refuse","drains","mount","results","failures","createAggregateError","drain","slot","lease","mounted","waitOptions"],"mappings":"2TAIA,SAASA,EAAcC,EAA8D,CACnF,GAAIA,EAAS,MAAM,OAAS,QAAS,MAAO,CAAA,EAE5C,MAAMC,EAAUD,EAAS,MAAM,OAAS,UAAY,KAAKA,EAAS,MAAM,MAAM,IAAM,GAEpF,MAAO,CAAC,WAAWA,EAAS,KAAK,KAAKA,EAAS,MAAM,IAAI,GAAGC,CAAO,GAAG,CACxE,CAEA,SAASC,EAASC,EAA8C,CAC9D,GAAIA,EAAK,OAAS,KAAM,MAAO,QAAQA,EAAK,WAAW,KAAKA,EAAK,KAAK,6CAEtE,MAAMC,EAAWD,EAAK,KAAK,YAAc,UAAY,UAAYA,EAAK,KAAK,UAAU,KAAK,IAAI,EAE9F,MAAO,QAAQA,EAAK,WAAW,KAAKA,EAAK,KAAK,UAAUA,EAAK,KAAK,WAAW,cAAcC,CAAQ,GACrG,CAEA,SAASC,EAAcC,EAA6C,CAClE,GAAIA,IAAa,OAAW,MAAO,CAAC,4BAA4B,EAEhE,MAAMC,EAAkB,CAAA,EAExB,OAAID,EAAS,YAAc,SAAWA,EAAS,YAC7CC,EAAM,KAAK,aAAaD,EAAS,SAAS,GAAGA,EAAS,UAAY,cAAgB,EAAE,yBAAyB,EAGxG,CACL,GAAGC,EACH,GAAGD,EAAS,SAAS,IACnBE,GAAW,WAAWA,EAAQ,OAAO,KAAKA,EAAQ,KAAK,UAAUA,EAAQ,IAAI,wBAAwB,EAEvG,GAAGF,EAAS,MAAM,IAAIJ,CAAQ,EAC9B,GAAGI,EAAS,UAAU,IACpBG,GAAY,YAAYA,EAAS,EAAE,KAAKA,EAAS,KAAK,eAAe,OAAOA,EAAS,SAAS,CAAC,GAAG,EAGxG,CAEA,SAASC,EAAgBC,EAA8B,CACrD,MAAMJ,EAAQ,CACZ,GAAGI,EAAS,QAAQ,WAAW,QAAQC,GACrCA,EAAU,MAAM,OAAS,OAAS,CAAA,EAAK,CAAC,aAAaA,EAAU,KAAK,KAAKA,EAAU,MAAM,IAAI,GAAG,CAAC,EAEnG,GAAGD,EAAS,QAAQ,UAAU,QAAQZ,CAAa,EACnD,GAAGM,EAAcM,EAAS,QAAQ,GAGpC,OAAOJ,EAAM,SAAW,EACpB,iFACAA,EAAM,KAAK;AAAA,CAAI,CACrB,CAGA,MAAMM,UAA6B,KAAK,CAI3B,MACA,UACA,SACA,QANF,KAAO,UAEhB,YACWC,EACAC,EACAJ,EACAK,EAAwC,CAEjD,MAAM,4BAA4B,OAAOD,CAAS,CAAC,OAAOD,CAAK;AAAA,EAAMJ,EAAgBC,CAAQ,CAAC,EAAE,EALvF,KAAA,MAAAG,EACA,KAAA,UAAAC,EACA,KAAA,SAAAJ,EACA,KAAA,QAAAK,EAGT,KAAK,KAAO,sBACd,CACD,CAED,SAASC,EAAkBX,EAA+CY,EAA2B,CACnG,MAAI,CAACA,GAAsBZ,IAAa,OAAkB,GAEnDA,EAAS,QAAU,CAACA,EAAS,SACtC,CAEA,SAASa,EACPb,EACAc,EACAF,EAA2B,CAE3B,GAAIZ,IAAa,OAAW,MAAO,CAAC,kCAAkC,EAEtE,MAAMe,EAAoB,CAAA,EAE1B,OAAIf,EAAS,WAAWe,EAAQ,KAAK,qCAAqC,EAEtEf,EAAS,YAAc,SAAW,CAACc,GAAUC,EAAQ,KAAK,iCAAiC,EAE1FH,GAAoBG,EAAQ,KAAK,iEAAiE,EAEhG,OAAO,OAAOA,CAAO,CAC9B,CAEA,SAASC,EACPhB,EACAc,EAAiB,CAEjB,OAAId,IAAa,QAAaA,EAAS,WAAcA,EAAS,YAAc,SAAW,CAACc,EAC/E,CAAE,MAAO,UAAW,SAAU,UAAW,UAAW,SAAS,EAG/D,CAAE,MAAOd,EAAS,MAAM,OAAQ,SAAUA,EAAS,SAAS,OAAQ,UAAWA,EAAS,UAAU,MAAM,CACjH,CAEA,SAASiB,EAAkBZ,EAAgCO,EAA2B,CACpF,MAAMZ,EAAWK,EAAS,SACpBS,EAAWH,EAAkBX,EAAUY,CAAkB,EAE/D,OAAO,OAAO,OAAO,CACnB,GAAGI,EAAgBhB,EAAUc,CAAQ,EACrC,OAAQF,EACR,QAASC,EAAiBb,EAAUc,EAAUF,CAAkB,EAChE,MAAO,qBACP,OAAQE,EAAW,WAAa,SACjC,CAAA,CACH,CCjHA,MAAMI,CAAkB,CAWO,SAV7B,IAAI,QAAM,CACR,OAAO,KAAK,aAAe,MAC7B,CACQ,UAAY,GACZ,WACA,OACA,OAEA,QAER,YAA6BC,EAAoD,CAApD,KAAA,SAAAA,CAAuD,CAE3E,MAAQ,IACX,KAAK,aAAe,OAAkB,KAAK,YAE/C,KAAK,WAAa,IAAI,QAAc,CAACC,EAASC,IAAU,CACtD,KAAK,QAAUD,EACf,KAAK,OAASC,CAChB,CAAC,EAEG,KAAK,WAAW,KAAK,iBAAgB,EAElC,KAAK,YAGd,QAAQC,EAA2B,CACjC,KAAK,UAAY,GACjB,KAAK,OAASA,EAEV,KAAK,QAAQ,KAAK,iBAAgB,CACxC,CAEQ,OAAOC,EAAiBC,EAAe,CAC7C,MAAMJ,EAAU,KAAK,QACfC,EAAS,KAAK,OACpB,KAAK,QAAU,OACf,KAAK,OAAS,OACd,KAAK,SAASE,EAAQC,CAAK,EAEtBD,EACAF,GAAA,MAAAA,EAASG,GADDJ,GAAA,MAAAA,GAEf,CAEQ,kBAAgB,CACtB,MAAME,EAAS,KAAK,OACpB,KAAK,OAAS,OACd,KAAK,UAAY,GACjB,GAAI,CACF,QAAQ,QAAQA,GAAA,YAAAA,EAAQ,SAAS,EAAE,KACjC,IAAM,KAAK,OAAO,EAAK,EACtBE,GAAmB,KAAK,OAAO,GAAMA,CAAK,CAAC,CAEhD,OAASA,EAAO,CACd,KAAK,OAAO,GAAMA,CAAK,CACzB,CACF,CACD,CCjCD,IAAIC,EAAkB,EAGtB,SAASC,EAAuBC,EAAmD,CACjF,OAAAF,GAAmB,EAEZG,EAAYC,EAAgC,CAAE,GAAI,mBAAmB,OAAOJ,CAAe,CAAC,GAAI,IAAAE,CAAG,CAAE,CAAC,CAC/G,CAMA,SAASG,EACPC,EACAC,EAAkC,GAAE,CAEpC,MAAMC,EAAWD,EAAQ,QAAU,CAAA,EACnC,IAAIE,EAAkBF,EAAQ,OAAS,CAAA,EACvC,MAAMG,EAAY,IAAI,IAChBC,EAAQ,CACZ,YAAa,IAAcF,EAC3B,UAAYG,IACVF,EAAU,IAAIE,CAAQ,EAEf,IAAMF,EAAU,OAAOE,CAAQ,IAIpCC,EAAWN,EAAQ,aAgBzB,OAAO,OAAO,OAAO,CACnB,MAAO,IAAW,CAChBE,EAAU,CAAA,EACVC,EAAU,MAAK,CACjB,EACA,KApBkB,IAAgB,CAClC,MAAMI,EAAYC,EAAYT,EAAO,OAAO,EACtCU,EAAYD,EAAYJ,CAAK,EAQnC,OANEE,IAAa,OACTC,EAAU,IACRG,GAAS,CAACA,EAAM,GAAcA,EAAM,MAA4BC,EAAsBD,CAAK,CAAC,CAAU,EAExG,CAAC,CAAC,uBAAwBJ,EAA0C,MAAS,CAAU,GAE9E,IAAI,CAAC,CAACM,EAAKC,EAAcC,CAAS,IAC/CC,EAAcC,EAAmB,CAAE,UAAAF,EAAW,aAAAD,EAAc,SAAAZ,EAAU,IAAAW,EAAK,UAAAH,CAAS,CAAE,CAAC,CAE3F,EAQE,YAAcQ,GAA8C,CAC1Df,EAAUe,EAEV,UAAWZ,IAAY,CAAC,GAAGF,CAAS,EAAGE,EAAQ,CACjD,CACD,CAAA,CACH,CAEA,SAASa,GACPnB,EACAC,EAAkC,GAAE,CAEpC,MAAMmB,EAAUrB,EAAkBC,EAAQC,CAAO,EAEjD,OAAO,OAAO,OAAO,CAAE,KAAMmB,EAAQ,KAAM,YAAaA,EAAQ,YAAa,CAC/E,CCxFA,SAASC,EAAiBC,EAAe7C,EAAa,CACpD,GAAI,CAAC,OAAO,SAAS6C,CAAK,GAAKA,GAAS,GAAKA,EAAQ,WACnD,MAAM,IAAI,WAAW,GAAG7C,CAAK,gEAAgE,EAG/F,OAAO6C,CACT,CAQA,MAAMC,EAAe,CAIU,OAHZ,UAAY,IAAI,IAChB,QAAU,IAAI,IAE/B,YAA6BC,EAA0B,CAA1B,KAAA,OAAAA,CAA6B,CAE1D,QAAM,CACJ,UAAWlC,IAAU,CAAC,GAAG,KAAK,OAAO,EAAGA,EAAO,IAAI,MAAM,sBAAsB,CAAC,CAClF,CAEA,SAAgBmC,EAA4BhD,EAAeC,EAAY,KAAK,OAAO,UAAS,CAC1F,OAAA2C,EAAiB3C,EAAW,oBAAoB,EAEzC,IAAI,QAAe,CAACW,EAASC,IAAU,CAC5C,MAAMoC,EAAQ,WAAW,IAAMpC,EAAO,KAAK,QAAQb,EAAOC,CAAS,CAAC,EAAGA,CAAS,EAChF,QAAQ,QAAQ+C,CAAM,EAAE,KACtBH,GAAQ,CACN,aAAaI,CAAK,EAClBrC,EAAQiC,CAAK,CACf,EACC7B,GAAkB,CACjB,aAAaiC,CAAK,EAClBpC,EAAOG,CAAK,CACd,CAAC,CAEL,CAAC,CACH,CAES,OAAS,IAAW,CAC3B,UAAWa,IAAY,CAAC,GAAG,KAAK,SAAS,EAAGA,EAAQ,CACtD,EAEA,QAAQqB,EAAwDC,EAA4B,CAC1F,MAAMlD,EAAY2C,EAAiBO,EAAQ,WAAa,KAAK,OAAO,UAAW,oBAAoB,EAC7FC,EAAiBR,EAAiBO,EAAQ,gBAAkB,GAAI,yBAAyB,EAE/F,OAAO,IAAI,QAAc,CAACvC,EAASC,IAAU,CAC3C,IAAIwC,EAAO,GACPC,EAAU,IAAA,GACd,MAAMC,EAAS,CAACvC,EAAiBD,EAASC,IAAU,SAAmB,CACjEqC,IAEJA,EAAO,GACPC,EAAO,EAEFvC,EACAF,EAAOG,CAAK,EADJJ,EAAO,EAEtB,EACM4C,EAAQ,IAAW,CACvB,GAAI,CAAAH,EAEJ,GAAI,CACEH,EAAU,KAAK,OAAO,YAAW,CAAE,GAAGK,EAAM,CAClD,OAASvC,EAAO,CACduC,EAAOvC,EAAO,EAAI,CACpB,CACF,EACMyC,EAAUzC,GAAuBuC,EAAOvC,CAAK,EAC7C0C,EAAW,WAAW,IAAMH,EAAO,KAAK,QAAQJ,EAAQ,OAAS,UAAWlD,CAAS,CAAC,EAAGA,CAAS,EAClG0D,EAAO,YAAYH,EAAOJ,CAAc,EAC9CE,EAAU,IAAW,CACnB,aAAaI,CAAQ,EACrB,cAAcC,CAAI,EAClB,KAAK,UAAU,OAAOH,CAAK,EAC3B,KAAK,QAAQ,OAAOC,CAAM,CAC5B,EACA,KAAK,UAAU,IAAID,CAAK,EACxB,KAAK,QAAQ,IAAIC,CAAM,EACvBD,EAAK,CACP,CAAC,CACH,CAEQ,QAAQxD,EAAeC,EAAiB,CAC9C,OAAO,IAAIF,EAAqBC,EAAOC,EAAW,KAAK,OAAO,cAAe,KAAK,OAAO,QAAO,CAAE,CACpG,CACD,CCzED,SAAS2D,EAAgBf,EAAe7C,EAAa,CACnD,GAAI,CAAC,OAAO,cAAc6C,CAAK,GAAKA,EAAQ,GAAKA,EAAQ,IACvD,MAAM,IAAI,WAAW,GAAG7C,CAAK,sCAAsC,EAGrE,OAAO6C,CACT,CAEA,SAASgB,GAAaC,EAAa,CACjC,GAAIA,IAAS,MAAQ,OAAOA,GAAS,UAAY,EAAE,UAAWA,IAAS,OAAOA,EAAK,OAAU,WAC3F,MAAM,IAAI,UAAU,oDAAoD,CAE5E,CAGA,SAASC,GACPC,EACAb,EAA2C,CAE3C,MAAMlD,EAAY2C,EAAiBO,EAAQ,WAAa,IAAO,oBAAoB,EAC7Ec,EAAkBL,EAAgBT,EAAQ,iBAAmB,GAAI,0BAA0B,EAC3Fe,EAAmBN,EAAgBT,EAAQ,kBAAoB,IAAK,2BAA2B,EACrG,IAAIW,EAA0CX,EAAQ,KAEtDU,GAAaC,CAAI,EAEjB,IAAIK,EAA8CC,EAAgBJ,EAAa,CAC7E,GAAIb,EAAQ,iBAAmB,OAAY,CAAA,EAAK,CAAE,eAAgBA,EAAQ,gBAC1E,WAAYA,EAAQ,WACpB,QAASA,EAAQ,OAClB,CAAA,EACGkB,EACJ,GAAI,CACFA,EAAUC,EAAwBH,EAAW,CAAE,iBAAAD,EAAkB,aAAcD,EAAiB,CAClG,OAASjD,EAAO,CACd,MAAKmD,EAAU,MAAM,MAAM,IAAA,EAAe,EACrCA,EAAU,MAAK,EAAG,MAAM,IAAA,EAAe,EACtCnD,CACR,CAEA,IAAIuD,EAAuC,OACvCnE,EAAqB,GACrBoE,EAAY,GAChB,MAAMC,EAAkC,CAAA,EAClCC,EAAS,IAAI,IACbC,EAA2B,CAAA,EAC3BC,EAAc,IAA4BP,EAAQ,YAAW,EAC7DnE,EAAU,IAAuC,OAAO,OAAO,CAAC,GAAGuE,CAAO,CAAC,EAC3EI,EAAU,IAAI/B,GAAgB,CAAE,YAAA8B,EAAa,QAAA1E,EAAS,UAAAD,EAAW,EACjE6E,EAAU,IAAW,CACzB,GAAI,CAAAN,EAEJ,CAAAA,EAAY,GACZ,GAAI,CACF,MAAM3E,EAAW+E,EAAW,EACtBG,EAAWN,EAAQA,EAAQ,OAAS,CAAC,IAEvCM,GAAA,YAAAA,EAAU,iBAAkBlF,EAAS,eAAiBkF,EAAS,WAAalF,EAAS,YACvF4E,EAAQ,KAAK5E,CAAQ,EAEjB4E,EAAQ,OAASR,GAAiBQ,EAAQ,MAAK,EAEvD,SACED,EAAY,EACd,CACAK,EAAQ,OAAM,EAChB,EACMG,EAAUX,EAAQ,UAAUS,CAAO,EACzCA,EAAO,EACP,MAAMG,EAAQJ,EAAQ,SAASV,EAAU,MAAO,mBAAmB,EAE9Dc,EAAM,MAAM,IAAA,EAAe,EAEhC,IAAIC,EACAC,EACJ,MAAMC,EAAa,IAAoB,CACrC,GAAIF,IAAkB,OAAW,OAAOA,EAExC,IAAItE,EACAC,EACJqE,EAAgB,IAAI,QAAc,CAACG,EAAQC,IAAU,CACnD1E,EAAUyE,EACVxE,EAASyE,CACX,CAAC,EACDf,EAAQ,UACRM,EAAQ,OAAM,EACd,MAAMU,EAA0B,CAAA,EAEhC,GAAI,CACFA,EAAO,KAAKpB,EAAW,OAAO,CAChC,OAASnD,EAAO,CACduE,EAAO,KAAK,QAAQ,OAAOvE,CAAK,CAAC,CACnC,CACA,OAAAuE,EAAO,KAAK,GAAG,CAAC,GAAGb,CAAM,EAAE,IAAIc,GAASA,EAAM,MAAK,CAAE,CAAC,EACjD,QAAQ,WAAWD,CAAM,EAAE,KAAKE,GAAU,CAC7C,MAAMC,EAAW,CACf,GAAG,IAAI,IAAI,CACT,GAAGf,EACH,GAAGc,EAAQ,QAAQzC,GAAWA,EAAO,SAAW,WAAa,CAACA,EAAO,MAAiB,EAAI,CAAA,CAAG,EAC9F,GAEH2B,EAAc,OAAS,EACvBJ,EAAQ,SACR,GAAI,CACFO,EAAO,CACT,OAAS9D,EAAO,CACd0E,EAAS,KAAK1E,CAAK,CACrB,SACEgE,EAAO,EACPX,EAAQ,MAAK,EACbK,EAAO,MAAK,EACZP,EAAY,OACZL,EAAO,MACT,CACA1D,EAAqBsF,EAAS,SAAW,EAErCA,EAAS,SAAW,EAAG9E,EAAO,EAC7BC,EAAO8E,EAAqBD,EAAU,0BAA0B,CAAC,CACxE,CAAC,EAEMR,CACT,EA4DA,OAAO,OAAO,OAAO,CACnB,MA3DY,IAAoB,CAChC,GAAIC,IAAiB,OAAW,OAAOA,EAEvC,MAAMS,EAAQR,EAAU,EAGxB,OAAID,IAAiB,SAErBA,EAAeN,EAAQ,SAASe,EAAO,gBAAgB,EAClDT,EAAa,MAAM,IAAK,CAEvBZ,IAAU,YAAWY,EAAe,OAC1C,CAAC,GAEMA,CACT,EA6CE,YAAAP,EACA,QAAA1E,EACA,MA7CwC,CACxCqB,EACAC,EAA+D,CAAA,IAC9B,CACjC,GAAI+C,IAAU,OAAQ,MAAM,IAAI,MAAM,sBAAsB,EAE5D,MAAMsB,EAAOvE,EAAkBC,EAAQC,CAAO,EACxCsE,EAAQ,IAAIpF,EAA4B,CAACK,EAAQC,IAAS,CAC9D0D,EAAO,OAAOoB,CAAK,EACnBD,EAAK,MAAK,EAEN9E,GAAQ4D,EAAc,KAAK3D,CAAK,CACtC,CAAC,EACD0D,EAAO,IAAIoB,CAAK,EAChB,IAAIC,EACJ,GAAI,CAGF,GAFAA,EAAUjC,EAAM,MAAM+B,EAAK,IAAI,EAE3BE,IAAY,MAAQ,OAAOA,GAAY,UAAY,OAAOA,EAAQ,SAAY,WAChF,MAAM,IAAI,UAAU,sDAAsD,EAG5ED,EAAM,QAAQC,CAAO,CACvB,OAAS/E,EAAO,CACd,MAAA8E,EAAM,QAAQ,MAAS,EAClBA,EAAM,MAAK,EAAG,MAAM,IAAA,EAAe,EAClC9E,CACR,CAEA,OAAO,OAAO,OAAO,CACnB,KAAM+E,EACN,QAASD,EAAM,MACf,YAAclE,GAA+C,CAC3D,GAAI2C,IAAU,QAAUuB,EAAM,OAAQ,MAAM,IAAI,MAAM,2BAA2B,EAEjFD,EAAK,YAAYjE,CAAK,EACtBiD,EAAQ,OAAM,CAChB,CACD,CAAA,CACH,EAOE,OAAQ,IAAW,CACbN,IAAU,QAAQM,EAAQ,OAAM,CACtC,EACA,UAAW,IAAMpE,EAAkBmE,EAAW,EAAIxE,CAAkB,EACpE,MAAA6E,EACA,QAAS,CAAC/B,EAAwD8C,EAAmC,KAC/FzB,IAAU,OAAe,QAAQ,OAAO,IAAI,MAAM,sBAAsB,CAAC,EAEtEM,EAAQ,QAAQ3B,EAAW8C,CAAW,CAEhD,CAAA,CACH"}
|
|
1
|
+
{"version":3,"file":"testing.js","sources":["../src/scenario-diagnostics.ts","../src/scenario-mount.ts","../src/scenario-slot.tsx","../src/scenario-wait.ts","../src/scenario.ts"],"sourcesContent":["import type { RuntimeActivitySnapshot, RuntimeGraphSnapshot } from '@opetope/runtime/internal';\n\nimport type { ScenarioOwnership } from './scenario-types';\n\nfunction instanceFacts(instance: RuntimeGraphSnapshot['runtime']['instances'][number]): readonly string[] {\n if (instance.state.kind === 'ready') return [];\n\n const waiting = instance.state.kind === 'waiting' ? ` (${instance.state.reason})` : '';\n\n return [`Feature ${instance.label}: ${instance.state.kind}${waiting}.`];\n}\n\nfunction callFact(call: RuntimeActivitySnapshot['calls'][number]): string {\n if (call.lane === null) return `Call ${call.declaration}: ${call.state}; lane parallel; blockers none (parallel).`;\n\n const blockers = call.lane.blockedBy === 'unknown' ? 'unknown' : call.lane.blockedBy.join(', ');\n\n return `Call ${call.declaration}: ${call.state}; lane ${call.lane.declaration}; blockers ${blockers}.`;\n}\n\nfunction activityFacts(activity: RuntimeActivitySnapshot | undefined): readonly string[] {\n if (activity === undefined) return ['Runtime activity: unknown.'];\n\n const facts: string[] = [];\n\n if (activity.freshness === 'stale' || activity.truncated) {\n facts.push(`Activity: ${activity.freshness}${activity.truncated ? ', truncated' : ''}; completeness unknown.`);\n }\n\n return [\n ...facts,\n ...activity.features.map(\n feature => `Feature ${feature.feature}: ${feature.phase}; body ${feature.body}; host demand unknown.`,\n ),\n ...activity.calls.map(callFact),\n ...activity.resources.map(\n resource => `Resource ${resource.id}: ${resource.state}; retainers ${String(resource.retainers)}.`,\n ),\n ];\n}\n\nfunction explainSnapshot(snapshot: RuntimeGraphSnapshot): string {\n const facts = [\n ...snapshot.runtime.conditions.flatMap(condition =>\n condition.state.kind === 'true' ? [] : [`Condition ${condition.label}: ${condition.state.kind}.`],\n ),\n ...snapshot.runtime.instances.flatMap(instanceFacts),\n ...activityFacts(snapshot.activity),\n ];\n\n return facts.length === 0\n ? 'No registered runtime blocker observed; external UI and host work are unknown.'\n : facts.join('\\n');\n}\n\n/** D206: the failure carries the producer's same data-only graph/activity facts, without product payloads. */\nclass ScenarioTimeoutError extends Error {\n readonly code = 'timeout';\n\n constructor(\n readonly label: string,\n readonly timeoutMs: number,\n readonly snapshot: RuntimeGraphSnapshot,\n readonly history: readonly RuntimeGraphSnapshot[],\n ) {\n super(`Scenario timed out after ${String(timeoutMs)}ms: ${label}.\\n${explainSnapshot(snapshot)}`);\n this.name = 'ScenarioTimeoutError';\n }\n}\n\nfunction ownershipComplete(activity: RuntimeActivitySnapshot | undefined, successfullyClosed: boolean): boolean {\n if (!successfullyClosed || activity === undefined) return false;\n\n return activity.closed && !activity.truncated;\n}\n\nfunction ownershipReasons(\n activity: RuntimeActivitySnapshot | undefined,\n complete: boolean,\n successfullyClosed: boolean,\n): readonly string[] {\n if (activity === undefined) return ['Runtime activity is unavailable.'];\n\n const reasons: string[] = [];\n\n if (activity.truncated) reasons.push('The activity snapshot is truncated.');\n\n if (activity.freshness === 'stale' && !complete) reasons.push('The activity snapshot is stale.');\n\n if (!successfullyClosed) reasons.push('Successful application and mount cleanup has not been observed.');\n\n return Object.freeze(reasons);\n}\n\nfunction ownershipCounts(\n activity: RuntimeActivitySnapshot | undefined,\n complete: boolean,\n): Pick<ScenarioOwnership, 'calls' | 'features' | 'resources'> {\n if (activity === undefined || activity.truncated || (activity.freshness === 'stale' && !complete)) {\n return { calls: 'unknown', features: 'unknown', resources: 'unknown' };\n }\n\n return { calls: activity.calls.length, features: activity.features.length, resources: activity.resources.length };\n}\n\nfunction scenarioOwnership(snapshot: RuntimeGraphSnapshot, successfullyClosed: boolean): ScenarioOwnership {\n const activity = snapshot.activity;\n const complete = ownershipComplete(activity, successfullyClosed);\n\n return Object.freeze({\n ...ownershipCounts(activity, complete),\n closed: successfullyClosed,\n reasons: ownershipReasons(activity, complete, successfullyClosed),\n scope: 'registered-runtime',\n status: complete ? 'complete' : 'unknown',\n });\n}\n\nexport { scenarioOwnership, ScenarioTimeoutError };\n","import type { ScenarioHostMount } from './scenario-types';\n\n/** A reservation exists before the host callback, so reentrant close also owns a mount returned later. */\nclass ScenarioMountLease<Mounted extends ScenarioHostMount> {\n get closed(): boolean {\n return this.completion !== undefined;\n }\n private available = false;\n private completion: Promise<void> | undefined;\n private handle: Mounted | undefined;\n private reject: ((error: unknown) => void) | undefined;\n\n private resolve: (() => void) | undefined;\n\n constructor(private readonly released: (failed: boolean, error?: unknown) => void) {}\n\n readonly close = (): Promise<void> => {\n if (this.completion !== undefined) return this.completion;\n\n this.completion = new Promise<void>((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n\n if (this.available) this.releaseAvailable();\n\n return this.completion;\n };\n\n provide(handle: Mounted | undefined): void {\n this.available = true;\n this.handle = handle;\n\n if (this.closed) this.releaseAvailable();\n }\n\n private finish(failed: boolean, error?: unknown): void {\n const resolve = this.resolve;\n const reject = this.reject;\n this.resolve = undefined;\n this.reject = undefined;\n this.released(failed, error);\n\n if (!failed) resolve?.();\n else reject?.(error);\n }\n\n private releaseAvailable(): void {\n const handle = this.handle;\n this.handle = undefined;\n this.available = false;\n try {\n Promise.resolve(handle?.unmount()).then(\n () => this.finish(false),\n (error: unknown) => this.finish(true, error),\n );\n } catch (error) {\n this.finish(true, error);\n }\n }\n}\n\nexport { ScenarioMountLease };\n","import { createElement } from 'react';\nimport type { FC, ReactNode } from 'react';\n\nimport { declarationId } from '@opetope/core';\nimport type { Call } from '@opetope/core';\nimport { defineCallTarget } from '@opetope/core/internal';\n\nimport { bindCommand } from './command';\nimport { contributionAuthority, ContributionMount } from './contribution-frame';\nimport type { ContributionValue, ModelFixture } from './contribution-frame';\nimport { useReadable } from './readable-hooks';\nimport type { SlotComponentProperties, SlotContribution, SlotProperties, SlotTarget } from './slot';\n\n/**\n * Two forms, as `renderRoot` had (D19): without `contribution` the harness mounts what the target published, with it\n * the harness mounts one fixture contribution so a component test needs no generation.\n */\ntype SlotTestFixture<Props extends SlotProperties> = {\n readonly contribution?: SlotContribution<Props>;\n readonly models?: readonly ModelFixture[];\n readonly props?: SlotComponentProperties<Props>;\n /**\n * Where a contained mount failure is reported (D256). A component test has no feature to report to, so without it\n * the failure is detached and the assertion has nothing to read.\n */\n readonly reporter?: (error: unknown) => void;\n};\n\ninterface SlotTestHarness<Props extends SlotProperties> {\n readonly Slot: FC;\n readonly updateProps: (props: SlotComponentProperties<Props>) => void;\n}\n\nconst testingContributionId = declarationId('testing.contribution');\n\nlet fixtureSequence = 0;\n\n/** A command a fixture publishes: the test writes the body, the harness gives it the identity a model requires. */\nfunction command<Input, Output>(run: (input: Input) => Output | PromiseLike<Output>): Call<Input, Output> {\n fixtureSequence += 1;\n\n return bindCommand(defineCallTarget<Input, Output>({ id: `testing.command.${String(fixtureSequence)}`, run }));\n}\n\n/**\n * Mounts the contributions of one slot the way a host does, with optional model fixtures for a component test. D19 and D85: the same\n * ContributionMount is shared by component tests and the application scenario harness (D206).\n */\nfunction createSlotHarness<Props extends SlotProperties>(\n target: SlotTarget<Props>,\n fixture: SlotTestFixture<Props> = {},\n): SlotTestHarness<Props> & { readonly close: () => void } {\n const fixtures = fixture.models ?? [];\n let current: object = fixture.props ?? {};\n const listeners = new Set<() => void>();\n const props = {\n getSnapshot: (): object => current,\n subscribe: (listener: () => void): (() => void) => {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n };\n\n const supplied = fixture.contribution;\n const reported = fixture.reporter;\n const fixtureAuthority = reported === undefined ? undefined : { models: [], reporter: reported };\n const Mounted: FC = (): ReactNode => {\n const published = useReadable(target.entries);\n const slotProps = useReadable(props);\n const entries =\n supplied === undefined\n ? published.map(\n entry =>\n [entry.id, entry.value as ContributionValue, contributionAuthority(entry) ?? fixtureAuthority] as const,\n )\n : [[testingContributionId, supplied as unknown as ContributionValue, fixtureAuthority] as const];\n\n return entries.map(([contributionId, contribution, authority]) =>\n createElement(ContributionMount, {\n authority,\n contribution,\n contributionId,\n fixtures,\n key: contributionId,\n slotProps,\n targetId: target.id,\n }),\n );\n };\n\n return Object.freeze({\n close: (): void => {\n current = {};\n listeners.clear();\n },\n Slot: Mounted,\n updateProps: (next: SlotComponentProperties<Props>): void => {\n current = next;\n\n for (const listener of [...listeners]) listener();\n },\n });\n}\n\nfunction renderSlot<Props extends SlotProperties>(\n target: SlotTarget<Props>,\n fixture: SlotTestFixture<Props> = {},\n): SlotTestHarness<Props> {\n const harness = createSlotHarness(target, fixture);\n\n return Object.freeze({ Slot: harness.Slot, updateProps: harness.updateProps });\n}\n\nexport { command, createSlotHarness, renderSlot };\nexport type { SlotTestFixture, SlotTestHarness };\n","import type { RuntimeGraphSnapshot } from '@opetope/runtime/internal';\n\nimport { ScenarioTimeoutError } from './scenario-diagnostics';\nimport type { ScenarioWaitOptions } from './scenario-types';\n\nfunction positiveDuration(value: number, label: string): number {\n if (!Number.isFinite(value) || value <= 0 || value > 2_147_483_647) {\n throw new RangeError(`${label} must be a finite duration from 0 (exclusive) to 2147483647ms.`);\n }\n\n return value;\n}\n\ninterface ScenarioWaitSource {\n readonly getSnapshot: () => RuntimeGraphSnapshot;\n readonly history: () => readonly RuntimeGraphSnapshot[];\n readonly timeoutMs: number;\n}\n\nclass ScenarioWaiters {\n private readonly listeners = new Set<() => void>();\n private readonly pending = new Set<(error: Error) => void>();\n\n constructor(private readonly source: ScenarioWaitSource) {}\n\n cancel(): void {\n for (const reject of [...this.pending]) reject(new Error('Scenario is closing.'));\n }\n\n deadline<Value>(result: PromiseLike<Value>, label: string, timeoutMs = this.source.timeoutMs): Promise<Value> {\n positiveDuration(timeoutMs, 'Scenario timeoutMs');\n\n return new Promise<Value>((resolve, reject) => {\n const timer = setTimeout(() => reject(this.timeout(label, timeoutMs)), timeoutMs);\n Promise.resolve(result).then(\n value => {\n clearTimeout(timer);\n resolve(value);\n },\n (error: unknown) => {\n clearTimeout(timer);\n reject(error);\n },\n );\n });\n }\n\n readonly notify = (): void => {\n for (const listener of [...this.listeners]) listener();\n };\n\n waitFor(predicate: (snapshot: RuntimeGraphSnapshot) => boolean, options: ScenarioWaitOptions): Promise<void> {\n const timeoutMs = positiveDuration(options.timeoutMs ?? this.source.timeoutMs, 'Scenario timeoutMs');\n const pollIntervalMs = positiveDuration(options.pollIntervalMs ?? 10, 'Scenario pollIntervalMs');\n\n return new Promise<void>((resolve, reject) => {\n let done = false;\n let dispose = (): void => undefined;\n const finish = (error?: unknown, failed = error !== undefined): void => {\n if (done) return;\n\n done = true;\n dispose();\n\n if (!failed) resolve();\n else reject(error);\n };\n const check = (): void => {\n if (done) return;\n\n try {\n if (predicate(this.source.getSnapshot())) finish();\n } catch (error) {\n finish(error, true);\n }\n };\n const cancel = (error: Error): void => finish(error);\n const deadline = setTimeout(() => finish(this.timeout(options.label ?? 'waitFor', timeoutMs)), timeoutMs);\n const poll = setInterval(check, pollIntervalMs);\n dispose = (): void => {\n clearTimeout(deadline);\n clearInterval(poll);\n this.listeners.delete(check);\n this.pending.delete(cancel);\n };\n this.listeners.add(check);\n this.pending.add(cancel);\n check();\n });\n }\n\n private timeout(label: string, timeoutMs: number): ScenarioTimeoutError {\n return new ScenarioTimeoutError(label, timeoutMs, this.source.getSnapshot(), this.source.history());\n }\n}\n\nexport { positiveDuration, ScenarioWaiters };\n","import { createAggregateError } from '@opetope/core/internal';\nimport { openApplication } from '@opetope/runtime';\nimport type { Application, ApplicationExecution } from '@opetope/runtime';\nimport { createInspectionSession } from '@opetope/runtime/internal';\nimport type { RuntimeGraphSnapshot, RuntimeInspectionSession } from '@opetope/runtime/internal';\n\nimport { scenarioOwnership } from './scenario-diagnostics';\nimport { ScenarioMountLease } from './scenario-mount';\nimport { createSlotHarness } from './scenario-slot';\nimport type {\n Scenario,\n ScenarioFeatures,\n ScenarioHost,\n ScenarioHostMount,\n ScenarioMount,\n ScenarioOptions,\n ScenarioWaitOptions,\n} from './scenario-types';\nimport { positiveDuration, ScenarioWaiters } from './scenario-wait';\nimport type { SlotComponentProperties, SlotProperties, SlotTarget } from './slot';\n\nfunction boundedCapacity(value: number, label: string): number {\n if (!Number.isSafeInteger(value) || value < 1 || value > 10_000) {\n throw new RangeError(`${label} must be an integer from 1 to 10000.`);\n }\n\n return value;\n}\n\nfunction validateHost(host: unknown): void {\n if (host === null || typeof host !== 'object' || !('mount' in host) || typeof host.mount !== 'function') {\n throw new TypeError('Scenario requires a host.mount(Component) adapter.');\n }\n}\n\n/** D206: one real application, its existing inspection session and renderer-owned Slot ingress. */\nfunction createScenario<const Features extends ScenarioFeatures, Mounted extends ScenarioHostMount>(\n application: Application<Features>,\n options: ScenarioOptions<Features, Mounted>,\n): Scenario<Mounted> {\n const timeoutMs = positiveDuration(options.timeoutMs ?? 1_000, 'Scenario timeoutMs');\n const historyCapacity = boundedCapacity(options.historyCapacity ?? 64, 'Scenario historyCapacity');\n const activityCapacity = boundedCapacity(options.activityCapacity ?? 256, 'Scenario activityCapacity');\n let host: ScenarioHost<Mounted> | undefined = options.host;\n\n validateHost(host);\n\n let execution: ApplicationExecution | undefined = openApplication(application, {\n ...(options.cleanupFailure === undefined ? {} : { cleanupFailure: options.cleanupFailure }),\n conditions: options.conditions,\n imports: options.imports,\n });\n let session: RuntimeInspectionSession;\n try {\n session = createInspectionSession(execution, { activityCapacity, ringCapacity: historyCapacity });\n } catch (error) {\n void execution.ready.catch(() => undefined);\n void execution.close().catch(() => undefined);\n throw error;\n }\n\n let phase: 'closed' | 'closing' | 'open' = 'open';\n let successfullyClosed = false;\n let capturing = false;\n const records: RuntimeGraphSnapshot[] = [];\n const mounts = new Set<ScenarioMountLease<Mounted>>();\n const mountFailures: unknown[] = [];\n const getSnapshot = (): RuntimeGraphSnapshot => session.getSnapshot();\n const history = (): readonly RuntimeGraphSnapshot[] => Object.freeze([...records]);\n const waiters = new ScenarioWaiters({ getSnapshot, history, timeoutMs });\n const capture = (): void => {\n if (capturing) return;\n\n capturing = true;\n try {\n const snapshot = getSnapshot();\n const previous = records[records.length - 1];\n\n if (previous?.stateRevision !== snapshot.stateRevision || previous.activity !== snapshot.activity) {\n records.push(snapshot);\n\n if (records.length > historyCapacity) records.shift();\n }\n } finally {\n capturing = false;\n }\n waiters.notify();\n };\n const release = session.subscribe(capture);\n capture();\n const ready = waiters.deadline(execution.ready, 'application ready');\n // A test may deliberately inspect loading without awaiting ready; rejection remains observable to that caller.\n void ready.catch(() => undefined);\n\n let physicalClose: Promise<void> | undefined;\n let closeAttempt: Promise<void> | undefined;\n const startClose = (): Promise<void> => {\n if (physicalClose !== undefined) return physicalClose;\n\n let resolve!: () => void;\n let reject!: (error: unknown) => void;\n physicalClose = new Promise<void>((accept, refuse) => {\n resolve = accept;\n reject = refuse;\n });\n phase = 'closing';\n waiters.cancel();\n const drains: Promise<void>[] = [];\n // Fence before any foreign unmount callback can reenter UI ingress; join all physical drains afterwards.\n try {\n drains.push(execution!.close());\n } catch (error) {\n drains.push(Promise.reject(error));\n }\n drains.push(...[...mounts].map(mount => mount.close()));\n void Promise.allSettled(drains).then(results => {\n const failures = [\n ...new Set([\n ...mountFailures,\n ...results.flatMap(result => (result.status === 'rejected' ? [result.reason as unknown] : [])),\n ]),\n ];\n mountFailures.length = 0;\n phase = 'closed';\n try {\n capture();\n } catch (error) {\n failures.push(error);\n } finally {\n release();\n session.close();\n mounts.clear();\n execution = undefined;\n host = undefined;\n }\n successfullyClosed = failures.length === 0;\n\n if (failures.length === 0) resolve();\n else reject(createAggregateError(failures, 'Scenario cleanup failed.'));\n });\n\n return physicalClose;\n };\n\n const close = (): Promise<void> => {\n if (closeAttempt !== undefined) return closeAttempt;\n\n const drain = startClose();\n\n // A nested close during unmount may have installed the same attempt already.\n if (closeAttempt !== undefined) return closeAttempt;\n\n closeAttempt = waiters.deadline(drain, 'scenario close');\n void closeAttempt.catch(() => {\n // A deadline cannot cancel physical work. A later close can await that same drain with a new deadline.\n if (phase === 'closing') closeAttempt = undefined;\n });\n\n return closeAttempt;\n };\n\n const mount: Scenario<Mounted>['mount'] = <Props extends SlotProperties>(\n target: SlotTarget<Props>,\n fixture: { readonly props?: SlotComponentProperties<Props> } = {},\n ): ScenarioMount<Props, Mounted> => {\n if (phase !== 'open') throw new Error('Scenario is closing.');\n\n const slot = createSlotHarness(target, fixture);\n const lease = new ScenarioMountLease<Mounted>((failed, error) => {\n mounts.delete(lease);\n slot.close();\n\n if (failed) mountFailures.push(error);\n });\n mounts.add(lease);\n let mounted: Mounted;\n try {\n mounted = host!.mount(slot.Slot);\n\n if (mounted === null || typeof mounted !== 'object' || typeof mounted.unmount !== 'function') {\n throw new TypeError('Scenario host.mount must return an unmount() handle.');\n }\n\n lease.provide(mounted);\n } catch (error) {\n lease.provide(undefined);\n void lease.close().catch(() => undefined);\n throw error;\n }\n\n return Object.freeze({\n host: mounted,\n unmount: lease.close,\n updateProps: (props: SlotComponentProperties<Props>): void => {\n if (phase !== 'open' || lease.closed) throw new Error('Scenario mount is closed.');\n\n slot.updateProps(props);\n waiters.notify();\n },\n });\n };\n\n return Object.freeze({\n close,\n getSnapshot,\n history,\n mount,\n notify: (): void => {\n if (phase === 'open') waiters.notify();\n },\n ownership: () => scenarioOwnership(getSnapshot(), successfullyClosed),\n ready,\n waitFor: (predicate: (snapshot: RuntimeGraphSnapshot) => boolean, waitOptions: ScenarioWaitOptions = {}) => {\n if (phase !== 'open') return Promise.reject(new Error('Scenario is closing.'));\n\n return waiters.waitFor(predicate, waitOptions);\n },\n });\n}\n\nexport { createScenario };\n"],"names":["instanceFacts","instance","waiting","callFact","call","blockers","activityFacts","activity","facts","feature","resource","explainSnapshot","snapshot","condition","ScenarioTimeoutError","label","timeoutMs","history","ownershipComplete","successfullyClosed","ownershipReasons","complete","reasons","ownershipCounts","scenarioOwnership","ScenarioMountLease","released","resolve","reject","handle","failed","error","testingContributionId","declarationId","fixtureSequence","command","run","bindCommand","defineCallTarget","createSlotHarness","target","fixture","fixtures","current","listeners","props","listener","supplied","reported","fixtureAuthority","published","useReadable","slotProps","entry","contributionAuthority","contributionId","contribution","authority","createElement","ContributionMount","next","renderSlot","harness","positiveDuration","value","ScenarioWaiters","source","result","timer","predicate","options","pollIntervalMs","done","dispose","finish","check","cancel","deadline","poll","boundedCapacity","validateHost","host","createScenario","application","historyCapacity","activityCapacity","execution","openApplication","session","createInspectionSession","phase","capturing","records","mounts","mountFailures","getSnapshot","waiters","capture","previous","release","ready","physicalClose","closeAttempt","startClose","accept","refuse","drains","mount","results","failures","createAggregateError","drain","slot","lease","mounted","waitOptions"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opetope/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=20.19.0"
|
|
6
6
|
},
|
|
@@ -50,16 +50,16 @@
|
|
|
50
50
|
}
|
|
51
51
|
],
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@opetope/core": "0.
|
|
54
|
-
"@opetope/runtime": "0.
|
|
53
|
+
"@opetope/core": "0.4.0",
|
|
54
|
+
"@opetope/runtime": "0.4.0",
|
|
55
55
|
"@testing-library/react": "16.3.3",
|
|
56
56
|
"@types/react": "19.2.18",
|
|
57
57
|
"react": "19.2.8",
|
|
58
58
|
"react-dom": "19.2.8"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
|
-
"@opetope/core": "0.
|
|
62
|
-
"@opetope/runtime": "0.
|
|
61
|
+
"@opetope/core": "0.4.0",
|
|
62
|
+
"@opetope/runtime": "0.4.0",
|
|
63
63
|
"react": ">=19.0.0 <20"
|
|
64
64
|
},
|
|
65
65
|
"sideEffects": false,
|