@opetope/core 0.1.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 +5 -0
- package/LICENSE +21 -0
- package/README.md +250 -0
- package/README.ru.md +246 -0
- package/dist/abort-compat.d.ts +10 -0
- package/dist/abort-compat.js +2 -0
- package/dist/abort-compat.js.map +1 -0
- package/dist/adapter-method.d.ts +27 -0
- package/dist/attachment-lifecycle-cleanup.d.ts +3 -0
- package/dist/attachment-lifecycle-cleanup.js +2 -0
- package/dist/attachment-lifecycle-cleanup.js.map +1 -0
- package/dist/attachment-lifecycle-contract.d.ts +260 -0
- package/dist/attachment-lifecycle-contract.js +2 -0
- package/dist/attachment-lifecycle-contract.js.map +1 -0
- package/dist/attachment-lifecycle-helpers.d.ts +34 -0
- package/dist/attachment-lifecycle-helpers.js +2 -0
- package/dist/attachment-lifecycle-helpers.js.map +1 -0
- package/dist/attachment-lifecycle-machine.d.ts +3 -0
- package/dist/attachment-lifecycle-machine.js +2 -0
- package/dist/attachment-lifecycle-machine.js.map +1 -0
- package/dist/attachment-lifecycle-mailbox.d.ts +10 -0
- package/dist/attachment-lifecycle-mailbox.js +2 -0
- package/dist/attachment-lifecycle-mailbox.js.map +1 -0
- package/dist/attachment-lifecycle-open.d.ts +3 -0
- package/dist/attachment-lifecycle-open.js +2 -0
- package/dist/attachment-lifecycle-open.js.map +1 -0
- package/dist/attachment-lifecycle.d.ts +4 -0
- package/dist/attachment-lifecycle.js +2 -0
- package/dist/attachment-lifecycle.js.map +1 -0
- package/dist/attachment.d.ts +49 -0
- package/dist/attachment.js +2 -0
- package/dist/attachment.js.map +1 -0
- package/dist/call-authority.d.ts +26 -0
- package/dist/call-controller-support.d.ts +77 -0
- package/dist/call-controller-support.js +2 -0
- package/dist/call-controller-support.js.map +1 -0
- package/dist/call-controller.d.ts +49 -0
- package/dist/call-controller.js +2 -0
- package/dist/call-controller.js.map +1 -0
- package/dist/call-execution-inspection.d.ts +26 -0
- package/dist/call-execution-inspection.js +2 -0
- package/dist/call-execution-inspection.js.map +1 -0
- package/dist/call-executor-factory.d.ts +5 -0
- package/dist/call-executor-factory.js +2 -0
- package/dist/call-executor-factory.js.map +1 -0
- package/dist/call-executor-record.d.ts +66 -0
- package/dist/call-executor-record.js +2 -0
- package/dist/call-executor-record.js.map +1 -0
- package/dist/call-executor-support.d.ts +25 -0
- package/dist/call-executor-support.js +2 -0
- package/dist/call-executor-support.js.map +1 -0
- package/dist/call-executor-types.d.ts +56 -0
- package/dist/call-executor-waiters.d.ts +23 -0
- package/dist/call-executor-waiters.js +2 -0
- package/dist/call-executor-waiters.js.map +1 -0
- package/dist/call-executor.d.ts +6 -0
- package/dist/call-executor.js +2 -0
- package/dist/call-executor.js.map +1 -0
- package/dist/call-invocation.d.ts +61 -0
- package/dist/call-invocation.js +2 -0
- package/dist/call-invocation.js.map +1 -0
- package/dist/call-lane.d.ts +28 -0
- package/dist/call-lane.js +2 -0
- package/dist/call-lane.js.map +1 -0
- package/dist/call-target-result.d.ts +22 -0
- package/dist/call-target-result.js +2 -0
- package/dist/call-target-result.js.map +1 -0
- package/dist/call-target.d.ts +42 -0
- package/dist/call-target.js +2 -0
- package/dist/call-target.js.map +1 -0
- package/dist/call.d.ts +11 -0
- package/dist/call.js +2 -0
- package/dist/call.js.map +1 -0
- package/dist/contribution-hub.d.ts +100 -0
- package/dist/contribution-hub.js +2 -0
- package/dist/contribution-hub.js.map +1 -0
- package/dist/contribution-transaction.d.ts +20 -0
- package/dist/contribution-transaction.js +2 -0
- package/dist/contribution-transaction.js.map +1 -0
- package/dist/contribution.d.ts +64 -0
- package/dist/contribution.js +2 -0
- package/dist/contribution.js.map +1 -0
- package/dist/data-scheduler.d.ts +4 -0
- package/dist/data-scheduler.js +2 -0
- package/dist/data-scheduler.js.map +1 -0
- package/dist/errors.d.ts +56 -0
- package/dist/errors.js +2 -0
- package/dist/errors.js.map +1 -0
- package/dist/identity.d.ts +6 -0
- package/dist/identity.js +2 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +46 -0
- package/dist/internal.js +2 -0
- package/dist/internal.js.map +1 -0
- package/dist/lifetime.d.ts +19 -0
- package/dist/lifetime.js +2 -0
- package/dist/lifetime.js.map +1 -0
- package/dist/model-context.d.ts +155 -0
- package/dist/model.d.ts +21 -0
- package/dist/model.js +2 -0
- package/dist/model.js.map +1 -0
- package/dist/platform-compat.d.ts +9 -0
- package/dist/platform-compat.js +2 -0
- package/dist/platform-compat.js.map +1 -0
- package/dist/port.d.ts +26 -0
- package/dist/port.js +2 -0
- package/dist/port.js.map +1 -0
- package/dist/reactive-collection.d.ts +27 -0
- package/dist/reactive-collection.js +2 -0
- package/dist/reactive-collection.js.map +1 -0
- package/dist/reactive-node.d.ts +62 -0
- package/dist/reactive-node.js +2 -0
- package/dist/reactive-node.js.map +1 -0
- package/dist/reactive-scheduler-control.d.ts +34 -0
- package/dist/reactive-scheduler-control.js +2 -0
- package/dist/reactive-scheduler-control.js.map +1 -0
- package/dist/reactive-scheduler.d.ts +18 -0
- package/dist/reactive-scheduler.js +2 -0
- package/dist/reactive-scheduler.js.map +1 -0
- package/dist/reactive.d.ts +41 -0
- package/dist/reactive.js +2 -0
- package/dist/reactive.js.map +1 -0
- package/dist/readable.d.ts +13 -0
- package/dist/readable.js +2 -0
- package/dist/readable.js.map +1 -0
- package/dist/resource.d.ts +82 -0
- package/dist/type-helpers.d.ts +14 -0
- package/package.json +66 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { CallExecutionFacts } from './call-execution-inspection.js';
|
|
2
|
+
import type { CallInvocation } from './call-invocation.js';
|
|
3
|
+
import type { CallLane } from './call-lane.js';
|
|
4
|
+
import type { CallContext } from './call-target.js';
|
|
5
|
+
import type { Awaitable } from './type-helpers.js';
|
|
6
|
+
type CallKey = number | string;
|
|
7
|
+
type CallScheduling = {
|
|
8
|
+
readonly lane?: CallLane;
|
|
9
|
+
readonly policy?: 'latest' | 'queue';
|
|
10
|
+
} | {
|
|
11
|
+
readonly lane?: never;
|
|
12
|
+
readonly policy: 'parallel';
|
|
13
|
+
};
|
|
14
|
+
type CallExecutorScheduling = {
|
|
15
|
+
readonly lane: CallLane;
|
|
16
|
+
readonly policy: 'latest' | 'queue';
|
|
17
|
+
} | {
|
|
18
|
+
readonly policy: 'parallel';
|
|
19
|
+
};
|
|
20
|
+
type CallKeySelector<Input> = (input: Input) => CallKey;
|
|
21
|
+
/**
|
|
22
|
+
* One shape for one fact: the kernel takes the options an author writes. `once: true` caches the single result,
|
|
23
|
+
* `once: selector` caches per key, and `singleFlight` shares the flight of equal keys (D161).
|
|
24
|
+
*/
|
|
25
|
+
interface CallExecutorOptions<Input> {
|
|
26
|
+
readonly id: string;
|
|
27
|
+
readonly once?: CallKeySelector<Input> | true;
|
|
28
|
+
readonly scheduling?: CallScheduling;
|
|
29
|
+
readonly singleFlight?: CallKeySelector<Input>;
|
|
30
|
+
}
|
|
31
|
+
interface CallStartOptions {
|
|
32
|
+
readonly generation?: object;
|
|
33
|
+
readonly parent?: CallContext;
|
|
34
|
+
readonly signal?: AbortSignal;
|
|
35
|
+
}
|
|
36
|
+
interface CallExecutionContext extends CallContext {
|
|
37
|
+
readonly callId: string;
|
|
38
|
+
readonly parentCallId: string | undefined;
|
|
39
|
+
}
|
|
40
|
+
interface CallExecutor<Input, Output> {
|
|
41
|
+
readonly close: (reason?: unknown) => Promise<void>;
|
|
42
|
+
readonly inspect: (capacity: number) => CallExecutionFacts;
|
|
43
|
+
readonly retireGeneration: (generation: object, reason?: unknown) => Promise<void>;
|
|
44
|
+
readonly start: (input: Input, execute: (context: CallExecutionContext) => Awaitable<Output>, options?: CallStartOptions) => CallInvocation<Output>;
|
|
45
|
+
}
|
|
46
|
+
type CallExecutionMetadata = {
|
|
47
|
+
readonly callPath: readonly CallInvocation<unknown>[];
|
|
48
|
+
readonly laneStack: readonly CallLane[];
|
|
49
|
+
};
|
|
50
|
+
type CallExecutorSnapshotOptions<Input> = {
|
|
51
|
+
readonly id: string;
|
|
52
|
+
readonly once: CallKeySelector<Input> | true | undefined;
|
|
53
|
+
readonly scheduling: CallExecutorScheduling;
|
|
54
|
+
readonly singleFlight: CallKeySelector<Input> | undefined;
|
|
55
|
+
};
|
|
56
|
+
export type { Awaitable, CallExecutionContext, CallExecutionMetadata, CallExecutor, CallExecutorOptions, CallExecutorScheduling, CallExecutorSnapshotOptions, CallKey, CallKeySelector, CallScheduling, CallStartOptions, };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CallExecution, CallExecutionGroup, CallExecutorState, CallWaiter } from './call-executor-record.js';
|
|
2
|
+
import type { CallStartOptions } from './call-executor-types.js';
|
|
3
|
+
declare function removeFlight<Input, Output>(execution: CallExecution<Input, Output>): void;
|
|
4
|
+
declare function stopQueuedExecution<Input, Output>(execution: CallExecution<Input, Output>): void;
|
|
5
|
+
declare function settleCancelledWaiter<Input, Output>(execution: CallExecution<Input, Output>, waiter: CallWaiter<Output>, reason: unknown): void;
|
|
6
|
+
declare function attachWaiter<Input, Output>(execution: CallExecution<Input, Output>, waiter: CallWaiter<Output>, options: CallStartOptions | undefined): void;
|
|
7
|
+
declare function settleActiveWaiters<Input, Output>(execution: CallExecution<Input, Output>, outcome: {
|
|
8
|
+
readonly error: unknown;
|
|
9
|
+
readonly status: 'cancelled' | 'rejected';
|
|
10
|
+
} | {
|
|
11
|
+
readonly status: 'fulfilled';
|
|
12
|
+
readonly value: Output;
|
|
13
|
+
}): void;
|
|
14
|
+
/**
|
|
15
|
+
* D185: `latest` keeps one waiting place. A new input replaces the waiting invocation of the same call, which ends
|
|
16
|
+
* as a cancellation with its own callbacks and signal; a body that already runs is never interrupted. The
|
|
17
|
+
* replacement happens only while the waiting request is still the last on its lane: once another call has queued
|
|
18
|
+
* behind it, coalescing would reorder the lane, and the order of different calls is the stronger law.
|
|
19
|
+
*/
|
|
20
|
+
declare function findWaitingExecution<Input, Output>(executionGroup: CallExecutionGroup<Input, Output>): CallExecution<Input, Output> | undefined;
|
|
21
|
+
declare function displaceWaitingExecution<Input, Output>(state: CallExecutorState<Input, Output>, execution: CallExecution<Input, Output> | undefined): void;
|
|
22
|
+
declare function settleCachedExecution<Input, Output>(execution: CallExecution<Input, Output>, value: Output): void;
|
|
23
|
+
export { attachWaiter, displaceWaitingExecution, findWaitingExecution, removeFlight, settleActiveWaiters, settleCachedExecution, settleCancelledWaiter, stopQueuedExecution, };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{abortWithReason as p,compatibleAbortReason as f}from"./abort-compat.js";import{notifyCallObservation as i}from"./call-execution-inspection.js";import{forgetWaitingExecution as u}from"./call-executor-record.js";import{CallError as b}from"./errors.js";function o(e){for(const t of e.abortReleases)t();e.abortReleases.length=0}function n(e){const t=e.flightKey;t!==void 0&&e.executionGroup.flights.get(t)===e&&e.executionGroup.flights.delete(t)}function a(e){var t;e.started||e.stopped||(e.stopped=!0,u(e),(t=e.laneRequest)==null||t.cancel(),e.laneRequest=void 0,e.executionGroup.executions.delete(e),n(e),e.resolveDone(),i(e.executionGroup.observation))}function s(e,t,r){if(!t.active)return;t.active=!1,e.waiters.delete(t),o(t);const l=e.waiters.size===0;l&&(n(e),p(e.abort,r)),t.controller.cancelled(r),e.started?t.controller.draining():t.controller.stopped({reason:"cancelled-before-start",status:"not-started"}),l&&!e.started&&a(e),i(e.executionGroup.observation)}function c(e,t,r){if(r===void 0)return;if(r.aborted){s(e,t,f(r));return}const l=()=>s(e,t,f(r));r.addEventListener("abort",l,{once:!0}),t.abortReleases.push(()=>r.removeEventListener("abort",l))}function g(e,t,r){var d;if(r===void 0)return;const l=(d=r.parent)==null?void 0:d.signal;c(e,t,l),!(!t.active||r.signal===l)&&c(e,t,r.signal)}function h(e,t,r){e.waiters.add(t),t.cacheKey!==void 0&&e.cacheKeys.add(t.cacheKey),e.started&&(e.physicalCalls.add(t.controller),t.controller.running()),g(e,t,r)}function v(e,t){for(const r of[...e.waiters])r.active=!1,e.waiters.delete(r),o(r),t.status==="fulfilled"?r.controller.fulfilled(t.value):t.status==="cancelled"?r.controller.cancelled(t.error):r.controller.rejected(t.error)}function W(e){var r;const t=e.lastWaiting;return((r=t==null?void 0:t.laneRequest)==null?void 0:r.isLast())===!0?t:void 0}function E(e,t){if(t===void 0||t.started||t.stopped)return;const r=new b("cancelled",`Call ${e.options.id} replaced a waiting input.`);a(t);for(const l of[...t.waiters])s(t,l,r)}function R(e,t){n(e);for(const r of[...e.waiters])r.active=!1,e.waiters.delete(r),o(r),r.controller.fulfilled(t),r.controller.stopped({reason:"cached",status:"not-started"});a(e)}export{h as attachWaiter,E as displaceWaitingExecution,W as findWaitingExecution,n as removeFlight,v as settleActiveWaiters,R as settleCachedExecution,s as settleCancelledWaiter,a as stopQueuedExecution};
|
|
2
|
+
//# sourceMappingURL=call-executor-waiters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-executor-waiters.js","sources":["../src/call-executor-waiters.ts"],"sourcesContent":["import { abortWithReason, compatibleAbortReason } from './abort-compat';\nimport { notifyCallObservation } from './call-execution-inspection';\nimport { forgetWaitingExecution } from './call-executor-record';\nimport type { CallExecution, CallExecutionGroup, CallExecutorState, CallWaiter } from './call-executor-record';\nimport type { CallStartOptions } from './call-executor-types';\nimport { CallError } from './errors';\n\nfunction detachAbortListeners<Output>(waiter: CallWaiter<Output>): void {\n for (const release of waiter.abortReleases) release();\n\n waiter.abortReleases.length = 0;\n}\n\nfunction removeFlight<Input, Output>(execution: CallExecution<Input, Output>): void {\n const key = execution.flightKey;\n\n if (key !== undefined && execution.executionGroup.flights.get(key) === execution) {\n execution.executionGroup.flights.delete(key);\n }\n}\n\nfunction stopQueuedExecution<Input, Output>(execution: CallExecution<Input, Output>): void {\n if (execution.started || execution.stopped) return;\n\n execution.stopped = true;\n forgetWaitingExecution(execution);\n execution.laneRequest?.cancel();\n execution.laneRequest = undefined;\n execution.executionGroup.executions.delete(execution);\n removeFlight(execution);\n execution.resolveDone();\n notifyCallObservation(execution.executionGroup.observation);\n}\n\nfunction settleCancelledWaiter<Input, Output>(\n execution: CallExecution<Input, Output>,\n waiter: CallWaiter<Output>,\n reason: unknown,\n): void {\n if (!waiter.active) return;\n\n waiter.active = false;\n execution.waiters.delete(waiter);\n detachAbortListeners(waiter);\n const lastWaiter = execution.waiters.size === 0;\n\n if (lastWaiter) {\n removeFlight(execution);\n abortWithReason(execution.abort, reason);\n }\n\n waiter.controller.cancelled(reason);\n\n if (execution.started) waiter.controller.draining();\n else waiter.controller.stopped({ reason: 'cancelled-before-start', status: 'not-started' });\n\n if (lastWaiter && !execution.started) stopQueuedExecution(execution);\n\n notifyCallObservation(execution.executionGroup.observation);\n}\n\nfunction bindAbortSignal<Input, Output>(\n execution: CallExecution<Input, Output>,\n waiter: CallWaiter<Output>,\n signal: AbortSignal | undefined,\n): void {\n if (signal === undefined) return;\n\n if (signal.aborted) {\n settleCancelledWaiter(execution, waiter, compatibleAbortReason(signal));\n\n return;\n }\n\n const listener = (): void => settleCancelledWaiter(execution, waiter, compatibleAbortReason(signal));\n signal.addEventListener('abort', listener, { once: true });\n waiter.abortReleases.push(() => signal.removeEventListener('abort', listener));\n}\n\nfunction bindWaiterSignals<Input, Output>(\n execution: CallExecution<Input, Output>,\n waiter: CallWaiter<Output>,\n options: CallStartOptions | undefined,\n): void {\n if (options === undefined) return;\n\n const parentSignal = options.parent?.signal;\n bindAbortSignal(execution, waiter, parentSignal);\n\n if (!waiter.active || options.signal === parentSignal) return;\n\n bindAbortSignal(execution, waiter, options.signal);\n}\n\nfunction attachWaiter<Input, Output>(\n execution: CallExecution<Input, Output>,\n waiter: CallWaiter<Output>,\n options: CallStartOptions | undefined,\n): void {\n execution.waiters.add(waiter);\n\n if (waiter.cacheKey !== undefined) execution.cacheKeys.add(waiter.cacheKey);\n\n if (execution.started) {\n execution.physicalCalls.add(waiter.controller);\n waiter.controller.running();\n }\n\n bindWaiterSignals(execution, waiter, options);\n}\n\nfunction settleActiveWaiters<Input, Output>(\n execution: CallExecution<Input, Output>,\n outcome:\n | { readonly error: unknown; readonly status: 'cancelled' | 'rejected' }\n | { readonly status: 'fulfilled'; readonly value: Output },\n): void {\n for (const waiter of [...execution.waiters]) {\n waiter.active = false;\n execution.waiters.delete(waiter);\n detachAbortListeners(waiter);\n\n if (outcome.status === 'fulfilled') waiter.controller.fulfilled(outcome.value);\n else if (outcome.status === 'cancelled') waiter.controller.cancelled(outcome.error);\n else waiter.controller.rejected(outcome.error);\n }\n}\n\n/**\n * D185: `latest` keeps one waiting place. A new input replaces the waiting invocation of the same call, which ends\n * as a cancellation with its own callbacks and signal; a body that already runs is never interrupted. The\n * replacement happens only while the waiting request is still the last on its lane: once another call has queued\n * behind it, coalescing would reorder the lane, and the order of different calls is the stronger law.\n */\nfunction findWaitingExecution<Input, Output>(\n executionGroup: CallExecutionGroup<Input, Output>,\n): CallExecution<Input, Output> | undefined {\n const execution = executionGroup.lastWaiting;\n\n return execution?.laneRequest?.isLast() === true ? execution : undefined;\n}\n\nfunction displaceWaitingExecution<Input, Output>(\n state: CallExecutorState<Input, Output>,\n execution: CallExecution<Input, Output> | undefined,\n): void {\n if (execution === undefined || execution.started || execution.stopped) return;\n\n const reason = new CallError('cancelled', `Call ${state.options.id} replaced a waiting input.`);\n\n // Remove the old lane place before any of its cancellation subscribers can admit a newer input.\n stopQueuedExecution(execution);\n\n for (const waiter of [...execution.waiters]) settleCancelledWaiter(execution, waiter, reason);\n}\n\nfunction settleCachedExecution<Input, Output>(execution: CallExecution<Input, Output>, value: Output): void {\n removeFlight(execution);\n\n for (const waiter of [...execution.waiters]) {\n waiter.active = false;\n execution.waiters.delete(waiter);\n detachAbortListeners(waiter);\n waiter.controller.fulfilled(value);\n waiter.controller.stopped({ reason: 'cached', status: 'not-started' });\n }\n\n stopQueuedExecution(execution);\n}\n\nexport {\n attachWaiter,\n displaceWaitingExecution,\n findWaitingExecution,\n removeFlight,\n settleActiveWaiters,\n settleCachedExecution,\n settleCancelledWaiter,\n stopQueuedExecution,\n};\n"],"names":["detachAbortListeners","waiter","release","removeFlight","execution","key","stopQueuedExecution","forgetWaitingExecution","_a","notifyCallObservation","settleCancelledWaiter","reason","lastWaiter","abortWithReason","bindAbortSignal","signal","compatibleAbortReason","listener","bindWaiterSignals","options","parentSignal","attachWaiter","settleActiveWaiters","outcome","findWaitingExecution","executionGroup","displaceWaitingExecution","state","CallError","settleCachedExecution","value"],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CallExecutionGroup, CallExecutorState } from './call-executor-record.js';
|
|
2
|
+
import type { Awaitable, CallExecutionContext, CallStartOptions } from './call-executor-types.js';
|
|
3
|
+
import type { CallInvocation } from './call-invocation.js';
|
|
4
|
+
declare function startCall<Input, Output>(state: CallExecutorState<Input, Output>, input: Input, execute: (context: CallExecutionContext) => Awaitable<Output>, options?: CallStartOptions): CallInvocation<Output>;
|
|
5
|
+
declare function retireExecutionGroup<Input, Output>(state: CallExecutorState<Input, Output>, executionGroup: CallExecutionGroup<Input, Output>, reason: unknown): Promise<void>;
|
|
6
|
+
export { retireExecutionGroup, startCall };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{compatibleAbortReason as j,abortWithReason as p}from"./abort-compat.js";import{notifyCallObservation as f}from"./call-execution-inspection.js";import{parentMetadata as R,cancelledCall as q,rejectedCall as g,readExecutionGroup as I,computeFlightKey as L,computeCacheKey as M,createController as C,trackWaitingExecution as A,prepareExecutionStart as F}from"./call-executor-record.js";import{CallExecutorClosedError as w,CallLaneCycleError as O,hasCallInPath as z,CallSingleFlightCycleError as k,bindCallExecutionMetadata as $,releaseCallExecutionMetadata as D,callContextSettledReason as S}from"./call-executor-support.js";import{findWaitingExecution as Q,attachWaiter as v,displaceWaitingExecution as B,stopQueuedExecution as J,settleCancelledWaiter as y,settleCachedExecution as N,removeFlight as E,settleActiveWaiters as K}from"./call-executor-waiters.js";import{requestCallLane as V}from"./call-lane.js";import{isCancellation as x}from"./errors.js";import{lastOf as H}from"./platform-compat.js";let T=0;const d=new WeakMap;function U(e){var t;return[...((t=d.get(e))==null?void 0:t.callPath)??[],e.bodyCall]}function P(e){var n;return((n=d.get(e))==null?void 0:n.laneStack)??[]}function X(e,n){const t=d.get(e);d.set(e,{callPath:(t==null?void 0:t.callPath)??[],laneStack:n})}function Y(e){for(const n of e.physicalCalls){const t=n.call.getSnapshot().settlement.status==="cancelled";n.stopped({reason:t?"cancelled":"settled",status:"stopped"})}e.physicalCalls.clear()}function b(e){if(e.stopped)return;e.stopped=!0,Y(e),E(e),e.executionGroup.executions.delete(e),D(e.context);const n=e.releaseLane;e.releaseLane=void 0,n==null||n(),e.resolveDone(),f(e.executionGroup.observation)}function Z(e,n){if(!e.abort.signal.aborted)for(const t of e.cacheKeys)e.executionGroup.cache.set(t,n);E(e),p(e.abort,S),K(e,{status:"fulfilled",value:n}),b(e)}function W(e,n){E(e),p(e.abort,S),K(e,x(n)?{error:n,status:"cancelled"}:{error:n,status:"rejected"}),b(e)}function _(e,n){const t=F(e,n);if(t===void 0)return;$(e.context,{callPath:U(e),laneStack:P(e)});for(const l of t)l.active&&l.controller.running();if(f(e.executionGroup.observation),e.waiters.size===0||e.executionGroup.retired){b(e);return}let r;try{r=e.execute(e.context)}catch(l){W(e,l);return}Promise.resolve(r).then(l=>Z(e,l),l=>W(e,l))}function ee(e,n){if(n.once===void 0)return{found:!1};const t=e.cacheKeys.values().next().value;return t!==void 0&&e.executionGroup.cache.has(t)?{found:!0,value:e.executionGroup.cache.get(t)}:{found:!1}}function m(e,n,t){const r=ee(e,n);if(!r.found){_(e,t);return}N(e,r.value),t==null||t()}function ne(e,n){if(e.waiters.size===0){J(e);return}if(n.scheduling.policy==="parallel"){m(e,n);return}const t=n.scheduling.lane,r=P(e);if(H(r)===t){m(e,n);return}X(e,[...r,t]);const l=V(t,a=>m(e,n,a),e.identity);e.started||e.stopped||(e.laneRequest=l,n.scheduling.policy==="latest"&&A(e))}function te(e,n,t,r,l){let a;const i=new Promise(u=>{a=u}),o=new AbortController,s=Object.freeze({callId:n.call.callId,parentCallId:n.call.parentCallId,signal:o.signal}),c={abort:o,bodyCall:n.call,cacheKeys:new Set,context:s,done:i,execute:t,executionGroup:e,flightKey:r,identity:++T,laneRequest:void 0,nextWaiting:void 0,physicalCalls:new Set,previousWaiting:void 0,releaseLane:void 0,resolveDone:a,started:!1,stopped:!1,waiters:new Set};return d.set(c,l??{callPath:[],laneStack:[]}),c}function re(e,n){if(e.scheduling.policy==="parallel"||n===void 0)return;const t=e.scheduling.lane,r=n.laneStack.indexOf(t);return r>=0&&r!==n.laneStack.length-1?t:void 0}function G(e){return(e==null?void 0:e.aborted)===!0?{found:!0,reason:j(e)}:{found:!1}}function le(e){var t;if(e===void 0)return{found:!1};const n=G((t=e.parent)==null?void 0:t.signal);return n.found?n:G(e.signal)}function ae(e,n,t){if(e.closed)return{error:new w(`Call ${e.options.id} is closed.`),kind:"error"};const r=I(e,t);if(r===void 0)return{error:new w(`Call ${e.options.id} generation is retired.`),kind:"error"};try{return{cacheKey:M(e.options,n),executionGroup:r,flightKey:L(e.options,n),kind:"ready"}}catch(l){return{error:l,kind:"error"}}}function ie(e,n,t,r){if(t===void 0||!n.cache.has(t))return;const l=C(e,r,()=>{});return l.fulfilled(n.cache.get(t)),l.stopped({reason:"cached",status:"not-started"}),l.call}function ce(e,n,t,r,l){const a=re(e.options,r);if(a!==void 0)return g(e,r,new O(`Call lane cycle reaches ${String(a.id)}.`));const i=e.options.scheduling.policy==="latest"?Q(n.executionGroup):void 0;let o=()=>{};const s=C(e,r,h=>o(h)),c=te(n.executionGroup,s,t,n.flightKey,r),u={abortReleases:[],active:!0,cacheKey:n.cacheKey,controller:s};return o=h=>y(c,u,h),n.executionGroup.executions.add(c),n.flightKey!==void 0&&n.executionGroup.flights.set(n.flightKey,c),v(c,u,l),ne(c,e.options),B(e,i),f(n.executionGroup.observation),s.call}function oe(e,n,t){return n.started||e.options.scheduling.policy==="parallel"||t===void 0?!1:t.laneStack.includes(e.options.scheduling.lane)}function se(e,n,t){if(z(t,n.bodyCall))return new k("Call cannot join its own execution.");if(oe(e,n,t))return new k("Call cannot join an execution queued behind its lane lease.")}function de(e,n,t,r,l){const a=se(e,n,r);if(a!==void 0)return g(e,r,a);let i=()=>{};const o=C(e,r,c=>i(c)),s={abortReleases:[],active:!0,cacheKey:t,controller:o};return i=c=>y(n,s,c),v(n,s,l),f(n.executionGroup.observation),o.call}function ue(e,n,t,r){const l=R(r),a=le(r);if(a.found)return q(e,l,a.reason);const i=ae(e,n,r==null?void 0:r.generation);if(i.kind==="error")return g(e,l,i.error);const o=ie(e,i.executionGroup,i.cacheKey,l);if(o!==void 0)return o;const s=i.flightKey===void 0?void 0:i.executionGroup.flights.get(i.flightKey);return s===void 0?ce(e,i,t,l,r):de(e,s,i.cacheKey,l,r)}function fe(e,n,t){for(const r of[...e.waiters])y(e,r,n);p(e.abort,n);{const r=e.releaseLane;e.releaseLane=void 0,r==null||r()}}function he(e,n,t){if(n.retirement!==void 0)return n.retirement;n.retired=!0,n.cache.clear(),n.flights.clear();const r=[...n.executions];for(const a of r)fe(a,t);const l=Promise.all(r.map(a=>a.done)).then(()=>{e.executionGroups.delete(n)});return n.retirement=l,l}export{he as retireExecutionGroup,ue as startCall};
|
|
2
|
+
//# sourceMappingURL=call-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-executor.js","sources":["../src/call-executor.ts"],"sourcesContent":["import { abortWithReason, compatibleAbortReason } from './abort-compat';\nimport { notifyCallObservation } from './call-execution-inspection';\nimport {\n cancelledCall,\n computeCacheKey,\n computeFlightKey,\n createController,\n parentMetadata,\n prepareExecutionStart,\n readExecutionGroup,\n rejectedCall,\n trackWaitingExecution,\n} from './call-executor-record';\nimport type {\n CallCacheKey,\n CallExecution,\n CallExecutionGroup,\n CallExecutorState,\n CallWaiter,\n} from './call-executor-record';\nimport {\n bindCallExecutionMetadata,\n callContextSettledReason,\n CallExecutorClosedError,\n CallLaneCycleError,\n CallSingleFlightCycleError,\n hasCallInPath,\n releaseCallExecutionMetadata,\n} from './call-executor-support';\nimport type {\n Awaitable,\n CallExecutionContext,\n CallExecutionMetadata,\n CallExecutorSnapshotOptions,\n CallKey,\n CallStartOptions,\n} from './call-executor-types';\nimport {\n attachWaiter,\n displaceWaitingExecution,\n findWaitingExecution,\n removeFlight,\n settleActiveWaiters,\n settleCachedExecution,\n settleCancelledWaiter,\n stopQueuedExecution,\n} from './call-executor-waiters';\nimport type { CallInvocation, CallInvocationController } from './call-invocation';\nimport type { CallLane } from './call-lane';\nimport { requestCallLane } from './call-lane';\nimport { isCancellation } from './errors';\nimport { lastOf } from './platform-compat';\n\nlet nextExecutionIdentity = 0;\nconst executionMetadata = new WeakMap<object, CallExecutionMetadata>();\n\nfunction executionCallPath<Input, Output>(execution: CallExecution<Input, Output>): readonly CallInvocation<unknown>[] {\n const parent = executionMetadata.get(execution)?.callPath ?? [];\n\n return [...parent, execution.bodyCall];\n}\n\nfunction executionLaneStack<Input, Output>(execution: CallExecution<Input, Output>): readonly CallLane[] {\n return executionMetadata.get(execution)?.laneStack ?? [];\n}\n\nfunction setExecutionLaneStack<Input, Output>(\n execution: CallExecution<Input, Output>,\n laneStack: readonly CallLane[],\n): void {\n const metadata = executionMetadata.get(execution);\n executionMetadata.set(execution, { callPath: metadata?.callPath ?? [], laneStack });\n}\n\nfunction finishPhysicalCalls<Input, Output>(execution: CallExecution<Input, Output>): void {\n for (const controller of execution.physicalCalls) {\n const cancelled = controller.call.getSnapshot().settlement.status === 'cancelled';\n controller.stopped({ reason: cancelled ? 'cancelled' : 'settled', status: 'stopped' });\n }\n\n execution.physicalCalls.clear();\n}\n\nfunction finishExecution<Input, Output>(execution: CallExecution<Input, Output>): void {\n if (execution.stopped) return;\n\n execution.stopped = true;\n finishPhysicalCalls(execution);\n removeFlight(execution);\n execution.executionGroup.executions.delete(execution);\n releaseCallExecutionMetadata(execution.context);\n const releaseLane = execution.releaseLane;\n execution.releaseLane = undefined;\n releaseLane?.();\n execution.resolveDone();\n notifyCallObservation(execution.executionGroup.observation);\n}\n\nfunction completeExecution<Input, Output>(execution: CallExecution<Input, Output>, value: Output): void {\n if (!execution.abort.signal.aborted) {\n for (const key of execution.cacheKeys) execution.executionGroup.cache.set(key, value);\n }\n\n removeFlight(execution);\n abortWithReason(execution.abort, callContextSettledReason);\n settleActiveWaiters(execution, { status: 'fulfilled', value });\n finishExecution(execution);\n}\n\nfunction failExecution<Input, Output>(execution: CallExecution<Input, Output>, error: unknown): void {\n removeFlight(execution);\n abortWithReason(execution.abort, callContextSettledReason);\n settleActiveWaiters(\n execution,\n isCancellation(error) ? { error, status: 'cancelled' } : { error, status: 'rejected' },\n );\n finishExecution(execution);\n}\n\nfunction beginExecution<Input, Output>(execution: CallExecution<Input, Output>, releaseLane?: () => void): void {\n const starters = prepareExecutionStart(execution, releaseLane);\n\n if (starters === undefined) return;\n\n bindCallExecutionMetadata(execution.context, {\n callPath: executionCallPath(execution),\n laneStack: executionLaneStack(execution),\n });\n\n for (const waiter of starters) {\n if (waiter.active) waiter.controller.running();\n }\n\n notifyCallObservation(execution.executionGroup.observation);\n\n if (execution.waiters.size === 0 || execution.executionGroup.retired) {\n finishExecution(execution);\n\n return;\n }\n\n let result: Awaitable<Output>;\n\n try {\n result = execution.execute(execution.context);\n } catch (error) {\n failExecution(execution, error);\n\n return;\n }\n\n void Promise.resolve(result).then(\n value => completeExecution(execution, value),\n (error: unknown) => failExecution(execution, error),\n );\n}\n\nfunction readCachedValue<Input, Output>(\n execution: CallExecution<Input, Output>,\n options: CallExecutorSnapshotOptions<Input>,\n): { readonly found: false } | { readonly found: true; readonly value: Output } {\n if (options.once === undefined) return { found: false };\n\n const key = execution.cacheKeys.values().next().value;\n\n return key !== undefined && execution.executionGroup.cache.has(key)\n ? { found: true, value: execution.executionGroup.cache.get(key) as Output }\n : { found: false };\n}\n\nfunction acquireExecution<Input, Output>(\n execution: CallExecution<Input, Output>,\n options: CallExecutorSnapshotOptions<Input>,\n releaseLane?: () => void,\n): void {\n const cached = readCachedValue(execution, options);\n\n if (!cached.found) {\n beginExecution(execution, releaseLane);\n\n return;\n }\n\n settleCachedExecution(execution, cached.value);\n releaseLane?.();\n}\n\nfunction scheduleExecution<Input, Output>(\n execution: CallExecution<Input, Output>,\n options: CallExecutorSnapshotOptions<Input>,\n): void {\n if (execution.waiters.size === 0) {\n stopQueuedExecution(execution);\n\n return;\n }\n\n if (options.scheduling.policy === 'parallel') {\n acquireExecution(execution, options);\n\n return;\n }\n\n const lane = options.scheduling.lane;\n const stack = executionLaneStack(execution);\n\n if (lastOf(stack) === lane) {\n acquireExecution(execution, options);\n\n return;\n }\n\n setExecutionLaneStack(execution, [...stack, lane]);\n const request = requestCallLane(lane, release => acquireExecution(execution, options, release), execution.identity);\n\n // Acquisition may run synchronously and reenter scheduling. Only a place still waiting belongs in this index.\n if (execution.started || execution.stopped) return;\n\n execution.laneRequest = request;\n\n if (options.scheduling.policy === 'latest') trackWaitingExecution(execution);\n}\n\nfunction makeExecution<Input, Output>(\n executionGroup: CallExecutionGroup<Input, Output>,\n controller: CallInvocationController<Output>,\n execute: (context: CallExecutionContext) => Awaitable<Output>,\n flightKey: CallKey | undefined,\n metadata: CallExecutionMetadata | undefined,\n): CallExecution<Input, Output> {\n let resolveDone!: () => void;\n const done = new Promise<void>(resolve => {\n resolveDone = resolve;\n });\n const abort = new AbortController();\n const context: CallExecutionContext = Object.freeze({\n callId: controller.call.callId,\n parentCallId: controller.call.parentCallId,\n signal: abort.signal,\n });\n const execution: CallExecution<Input, Output> = {\n abort,\n bodyCall: controller.call,\n cacheKeys: new Set(),\n context,\n done,\n execute,\n executionGroup,\n flightKey,\n identity: ++nextExecutionIdentity,\n laneRequest: undefined,\n nextWaiting: undefined,\n physicalCalls: new Set(),\n previousWaiting: undefined,\n releaseLane: undefined,\n resolveDone,\n started: false,\n stopped: false,\n waiters: new Set(),\n };\n executionMetadata.set(execution, metadata ?? { callPath: [], laneStack: [] });\n\n return execution;\n}\n\nfunction laneCycle<Input>(options: CallExecutorSnapshotOptions<Input>, metadata: CallExecutionMetadata | undefined) {\n if (options.scheduling.policy === 'parallel' || metadata === undefined) return undefined;\n\n const lane = options.scheduling.lane;\n const index = metadata.laneStack.indexOf(lane);\n\n return index >= 0 && index !== metadata.laneStack.length - 1 ? lane : undefined;\n}\n\ntype CallStartPreparation<Input, Output> =\n | {\n readonly cacheKey: CallCacheKey | undefined;\n readonly executionGroup: CallExecutionGroup<Input, Output>;\n readonly flightKey: CallKey | undefined;\n readonly kind: 'ready';\n }\n | { readonly error: unknown; readonly kind: 'error' };\n\ntype AbortedStart = { readonly found: false } | { readonly found: true; readonly reason: unknown };\n\nfunction readAbortedStartSignal(signal: AbortSignal | undefined): AbortedStart {\n return signal?.aborted === true ? { found: true, reason: compatibleAbortReason(signal) } : { found: false };\n}\n\nfunction startCancellation(options: CallStartOptions | undefined): AbortedStart {\n if (options === undefined) return { found: false };\n\n const parent = readAbortedStartSignal(options.parent?.signal);\n\n return parent.found ? parent : readAbortedStartSignal(options.signal);\n}\n\nfunction prepareCallStart<Input, Output>(\n state: CallExecutorState<Input, Output>,\n input: Input,\n generation: object | undefined,\n): CallStartPreparation<Input, Output> {\n if (state.closed) return { error: new CallExecutorClosedError(`Call ${state.options.id} is closed.`), kind: 'error' };\n\n const executionGroup = readExecutionGroup(state, generation);\n\n if (executionGroup === undefined) {\n return {\n error: new CallExecutorClosedError(`Call ${state.options.id} generation is retired.`),\n kind: 'error',\n };\n }\n\n try {\n return {\n cacheKey: computeCacheKey(state.options, input),\n executionGroup,\n flightKey: computeFlightKey(state.options, input),\n kind: 'ready',\n };\n } catch (error) {\n return { error, kind: 'error' };\n }\n}\n\nfunction cachedCall<Input, Output>(\n state: CallExecutorState<Input, Output>,\n executionGroup: CallExecutionGroup<Input, Output>,\n cacheKey: CallCacheKey | undefined,\n metadata: CallExecutionMetadata | undefined,\n): CallInvocation<Output> | undefined {\n if (cacheKey === undefined || !executionGroup.cache.has(cacheKey)) return undefined;\n\n const controller = createController<Input, Output>(state, metadata, () => undefined);\n controller.fulfilled(executionGroup.cache.get(cacheKey) as Output);\n controller.stopped({ reason: 'cached', status: 'not-started' });\n\n return controller.call;\n}\n\nfunction startNewExecution<Input, Output>(\n state: CallExecutorState<Input, Output>,\n prepared: Extract<CallStartPreparation<Input, Output>, { readonly kind: 'ready' }>,\n execute: (context: CallExecutionContext) => Awaitable<Output>,\n metadata: CallExecutionMetadata | undefined,\n options: CallStartOptions | undefined,\n): CallInvocation<Output> {\n const cycleLane = laneCycle(state.options, metadata);\n\n if (cycleLane !== undefined) {\n return rejectedCall(state, metadata, new CallLaneCycleError(`Call lane cycle reaches ${String(cycleLane.id)}.`));\n }\n\n const displaced =\n state.options.scheduling.policy === 'latest' ? findWaitingExecution(prepared.executionGroup) : undefined;\n\n let cancelWaiter: (reason: unknown) => void = () => undefined;\n const controller = createController<Input, Output>(state, metadata, reason => cancelWaiter(reason));\n const execution = makeExecution(prepared.executionGroup, controller, execute, prepared.flightKey, metadata);\n const waiter: CallWaiter<Output> = {\n abortReleases: [],\n active: true,\n cacheKey: prepared.cacheKey,\n controller,\n };\n cancelWaiter = reason => settleCancelledWaiter(execution, waiter, reason);\n prepared.executionGroup.executions.add(execution);\n\n if (prepared.flightKey !== undefined) prepared.executionGroup.flights.set(prepared.flightKey, execution);\n\n attachWaiter(execution, waiter, options);\n scheduleExecution(execution, state.options);\n // D185: reserve this input's ordinal, waiter and lane place before cancellation can reenter start or close.\n displaceWaitingExecution(state, displaced);\n notifyCallObservation(prepared.executionGroup.observation);\n\n return controller.call;\n}\n\nfunction joinsQueuedBehindParentLane<Input, Output>(\n state: CallExecutorState<Input, Output>,\n execution: CallExecution<Input, Output>,\n metadata: CallExecutionMetadata | undefined,\n): boolean {\n if (execution.started || state.options.scheduling.policy === 'parallel' || metadata === undefined) return false;\n\n return metadata.laneStack.includes(state.options.scheduling.lane);\n}\n\nfunction singleFlightJoinError<Input, Output>(\n state: CallExecutorState<Input, Output>,\n execution: CallExecution<Input, Output>,\n metadata: CallExecutionMetadata | undefined,\n): CallSingleFlightCycleError | undefined {\n if (hasCallInPath(metadata, execution.bodyCall)) {\n return new CallSingleFlightCycleError('Call cannot join its own execution.');\n }\n\n if (joinsQueuedBehindParentLane(state, execution, metadata)) {\n return new CallSingleFlightCycleError('Call cannot join an execution queued behind its lane lease.');\n }\n\n return undefined;\n}\n\nfunction joinExecution<Input, Output>(\n state: CallExecutorState<Input, Output>,\n execution: CallExecution<Input, Output>,\n cacheKey: CallCacheKey | undefined,\n metadata: CallExecutionMetadata | undefined,\n options: CallStartOptions | undefined,\n): CallInvocation<Output> {\n const error = singleFlightJoinError(state, execution, metadata);\n\n if (error !== undefined) return rejectedCall(state, metadata, error);\n\n let cancelWaiter: (reason: unknown) => void = () => undefined;\n const controller = createController<Input, Output>(state, metadata, reason => cancelWaiter(reason));\n const waiter: CallWaiter<Output> = { abortReleases: [], active: true, cacheKey, controller };\n cancelWaiter = reason => settleCancelledWaiter(execution, waiter, reason);\n attachWaiter(execution, waiter, options);\n notifyCallObservation(execution.executionGroup.observation);\n\n return controller.call;\n}\n\nfunction startCall<Input, Output>(\n state: CallExecutorState<Input, Output>,\n input: Input,\n execute: (context: CallExecutionContext) => Awaitable<Output>,\n options?: CallStartOptions,\n): CallInvocation<Output> {\n const metadata = parentMetadata(options);\n const cancellation = startCancellation(options);\n\n if (cancellation.found) return cancelledCall(state, metadata, cancellation.reason);\n\n const prepared = prepareCallStart(state, input, options?.generation);\n\n if (prepared.kind === 'error') return rejectedCall(state, metadata, prepared.error);\n\n const cached = cachedCall(state, prepared.executionGroup, prepared.cacheKey, metadata);\n\n if (cached !== undefined) return cached;\n\n const existing =\n prepared.flightKey === undefined ? undefined : prepared.executionGroup.flights.get(prepared.flightKey);\n\n return existing === undefined\n ? startNewExecution(state, prepared, execute, metadata, options)\n : joinExecution(state, existing, prepared.cacheKey, metadata, options);\n}\n\nfunction retireExecution<Input, Output>(\n execution: CallExecution<Input, Output>,\n reason: unknown,\n releaseLane: boolean,\n): void {\n for (const waiter of [...execution.waiters]) settleCancelledWaiter(execution, waiter, reason);\n\n abortWithReason(execution.abort, reason);\n\n if (releaseLane) {\n const release = execution.releaseLane;\n execution.releaseLane = undefined;\n release?.();\n }\n}\n\nfunction retireExecutionGroup<Input, Output>(\n state: CallExecutorState<Input, Output>,\n executionGroup: CallExecutionGroup<Input, Output>,\n reason: unknown,\n): Promise<void> {\n if (executionGroup.retirement !== undefined) return executionGroup.retirement;\n\n executionGroup.retired = true;\n executionGroup.cache.clear();\n executionGroup.flights.clear();\n const executions = [...executionGroup.executions];\n\n for (const execution of executions) retireExecution(execution, reason, true);\n\n const retirement = Promise.all(executions.map(execution => execution.done)).then(() => {\n state.executionGroups.delete(executionGroup);\n });\n executionGroup.retirement = retirement;\n\n return retirement;\n}\n\nexport { retireExecutionGroup, startCall };\n"],"names":["nextExecutionIdentity","executionMetadata","executionCallPath","execution","_a","executionLaneStack","setExecutionLaneStack","laneStack","metadata","finishPhysicalCalls","controller","cancelled","finishExecution","removeFlight","releaseCallExecutionMetadata","releaseLane","notifyCallObservation","completeExecution","value","key","abortWithReason","callContextSettledReason","settleActiveWaiters","failExecution","error","isCancellation","beginExecution","starters","prepareExecutionStart","bindCallExecutionMetadata","waiter","result","readCachedValue","options","acquireExecution","cached","settleCachedExecution","scheduleExecution","stopQueuedExecution","lane","stack","lastOf","request","requestCallLane","release","trackWaitingExecution","makeExecution","executionGroup","execute","flightKey","resolveDone","done","resolve","abort","context","laneCycle","index","readAbortedStartSignal","signal","compatibleAbortReason","startCancellation","parent","prepareCallStart","state","input","generation","CallExecutorClosedError","readExecutionGroup","computeCacheKey","computeFlightKey","cachedCall","cacheKey","createController","startNewExecution","prepared","cycleLane","rejectedCall","CallLaneCycleError","displaced","findWaitingExecution","cancelWaiter","reason","settleCancelledWaiter","attachWaiter","displaceWaitingExecution","joinsQueuedBehindParentLane","singleFlightJoinError","hasCallInPath","CallSingleFlightCycleError","joinExecution","startCall","parentMetadata","cancellation","cancelledCall","existing","retireExecution","retireExecutionGroup","executions","retirement"],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
declare const callInvocationBrand: unique symbol;
|
|
2
|
+
type CallInvocationExecution = {
|
|
3
|
+
readonly reason: 'cached' | 'cancelled-before-start';
|
|
4
|
+
readonly status: 'not-started';
|
|
5
|
+
} | {
|
|
6
|
+
readonly reason: 'cancelled' | 'settled';
|
|
7
|
+
readonly status: 'stopped';
|
|
8
|
+
} | {
|
|
9
|
+
readonly reason?: never;
|
|
10
|
+
readonly status: 'not-started';
|
|
11
|
+
} | {
|
|
12
|
+
readonly status: 'draining';
|
|
13
|
+
} | {
|
|
14
|
+
readonly status: 'running';
|
|
15
|
+
};
|
|
16
|
+
type CallSettlement<Output> = {
|
|
17
|
+
readonly error: unknown;
|
|
18
|
+
readonly status: 'rejected';
|
|
19
|
+
} | {
|
|
20
|
+
readonly reason: unknown;
|
|
21
|
+
readonly status: 'cancelled';
|
|
22
|
+
} | {
|
|
23
|
+
readonly status: 'fulfilled';
|
|
24
|
+
readonly value: Output;
|
|
25
|
+
} | {
|
|
26
|
+
readonly status: 'pending';
|
|
27
|
+
};
|
|
28
|
+
interface CallInvocationSnapshot<Output> {
|
|
29
|
+
readonly execution: CallInvocationExecution;
|
|
30
|
+
readonly settlement: CallSettlement<Output>;
|
|
31
|
+
}
|
|
32
|
+
interface CallInvocation<Output> extends Promise<Output> {
|
|
33
|
+
readonly callId: string;
|
|
34
|
+
readonly [callInvocationBrand]: true;
|
|
35
|
+
readonly cancel: (reason?: unknown) => void;
|
|
36
|
+
readonly getSnapshot: () => CallInvocationSnapshot<Output>;
|
|
37
|
+
readonly parentCallId: string | undefined;
|
|
38
|
+
readonly subscribe: (listener: () => void) => () => void;
|
|
39
|
+
}
|
|
40
|
+
interface CallInvocationController<Output> {
|
|
41
|
+
readonly call: CallInvocation<Output>;
|
|
42
|
+
cancelled(reason: unknown): boolean;
|
|
43
|
+
draining(): void;
|
|
44
|
+
fulfilled(value: Output): boolean;
|
|
45
|
+
rejected(error: unknown): boolean;
|
|
46
|
+
running(): void;
|
|
47
|
+
stopped(execution: Extract<CallInvocationExecution, {
|
|
48
|
+
readonly reason: unknown;
|
|
49
|
+
}>): void;
|
|
50
|
+
}
|
|
51
|
+
declare function createCallInvocation<Output>(options: {
|
|
52
|
+
readonly callId: string;
|
|
53
|
+
readonly execution?: CallInvocationExecution;
|
|
54
|
+
readonly onCancel: (reason: unknown) => void;
|
|
55
|
+
readonly parentCallId?: string;
|
|
56
|
+
}): CallInvocationController<Output>;
|
|
57
|
+
declare function isCallInvocation(value: unknown): value is CallInvocation<unknown>;
|
|
58
|
+
export { isCallInvocation };
|
|
59
|
+
export type { CallInvocation, CallInvocationExecution, CallInvocationSnapshot, CallSettlement };
|
|
60
|
+
export { createCallInvocation };
|
|
61
|
+
export type { CallInvocationController };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{reportDetachedError as m}from"./attachment.js";import{cancelledCall as v}from"./call-controller-support.js";import{CallError as j}from"./errors.js";const x=Symbol("opetope.call-invocation"),C=Object.freeze({status:"not-started"}),f=()=>{},d=Object.freeze({status:"pending"}),I=new WeakMap;function p(e){e.snapshot=Object.freeze({execution:e.execution,settlement:e.settlement});for(const t of[...e.listeners])try{t()}catch(a){m(a)}}function o(e,t){e.execution.status==="stopped"||e.execution.status==="not-started"&&e.execution.reason!==void 0||(e.execution=Object.freeze(t),p(e),(t.status==="stopped"||t.status==="not-started"&&t.reason!==void 0)&&e.listeners.clear())}function g(e){return e instanceof j?e:v(e)}function b(e,t){e!==void 0&&e(t)}function h(e,t){e!==void 0&&e(t)}function O(e,t,a){switch(a.status){case"cancelled":b(t,g(a.reason));break;case"fulfilled":h(e,a.value);break;case"rejected":b(t,a.error);break}}function u(e,t){if(e.settlement.status!=="pending")return!1;e.settlement=Object.freeze(t);const a=e.resolve,c=e.reject;return e.cancel=f,e.resolve=void 0,e.reject=void 0,O(a,c,t),p(e),!0}function z(e){let t,a;const c=new Promise((l,r)=>{t=l,a=r}),i=e.execution??C,n={cancel:e.onCancel,execution:i,listeners:new Set,reject:a,resolve:t,settlement:d,snapshot:Object.freeze({execution:i,settlement:d})},s=c;return Object.defineProperties(s,{callId:{enumerable:!0,value:e.callId},[x]:{value:!0},cancel:{enumerable:!0,value:(l="Call invocation was cancelled by its caller.")=>{n.settlement.status==="pending"&&n.cancel(l)}},getSnapshot:{enumerable:!0,value:()=>n.snapshot},parentCallId:{enumerable:!0,value:e.parentCallId},subscribe:{enumerable:!0,value:l=>{if(typeof l!="function")throw new TypeError("CallInvocation listener must be a function.");if(n.execution.status==="stopped"||n.execution.status==="not-started"&&n.execution.reason!==void 0)return f;n.listeners.add(l);let r=!0;return()=>{r&&(r=!1,n.listeners.delete(l))}}}}),I.set(s,n),Object.freeze(s),Object.freeze({call:s,cancelled:l=>u(n,{reason:l,status:"cancelled"}),draining:()=>o(n,{status:"draining"}),fulfilled:l=>u(n,{status:"fulfilled",value:l}),rejected:l=>u(n,{error:l,status:"rejected"}),running:()=>o(n,{status:"running"}),stopped:l=>o(n,l)})}export{z as createCallInvocation};
|
|
2
|
+
//# sourceMappingURL=call-invocation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-invocation.js","sources":["../src/call-invocation.ts"],"sourcesContent":["import { reportDetachedError } from './attachment';\nimport { cancelledCall } from './call-controller-support';\nimport { CallError } from './errors';\n\nconst callInvocationBrand: unique symbol = Symbol('opetope.call-invocation');\n\ntype CallInvocationExecution =\n | { readonly reason: 'cached' | 'cancelled-before-start'; readonly status: 'not-started' }\n | { readonly reason: 'cancelled' | 'settled'; readonly status: 'stopped' }\n | { readonly reason?: never; readonly status: 'not-started' }\n | { readonly status: 'draining' }\n | { readonly status: 'running' };\n\ntype CallSettlement<Output> =\n | { readonly error: unknown; readonly status: 'rejected' }\n | { readonly reason: unknown; readonly status: 'cancelled' }\n | { readonly status: 'fulfilled'; readonly value: Output }\n | { readonly status: 'pending' };\n\ntype CallTerminalSettlement<Output> = Exclude<CallSettlement<Output>, { readonly status: 'pending' }>;\n\ninterface CallInvocationSnapshot<Output> {\n readonly execution: CallInvocationExecution;\n readonly settlement: CallSettlement<Output>;\n}\n\ninterface CallInvocation<Output> extends Promise<Output> {\n readonly callId: string;\n readonly [callInvocationBrand]: true;\n readonly cancel: (reason?: unknown) => void;\n readonly getSnapshot: () => CallInvocationSnapshot<Output>;\n readonly parentCallId: string | undefined;\n readonly subscribe: (listener: () => void) => () => void;\n}\n\ninterface CallInvocationController<Output> {\n readonly call: CallInvocation<Output>;\n cancelled(reason: unknown): boolean;\n draining(): void;\n fulfilled(value: Output): boolean;\n rejected(error: unknown): boolean;\n running(): void;\n stopped(execution: Extract<CallInvocationExecution, { readonly reason: unknown }>): void;\n}\n\ntype CallInvocationRecord<Output> = {\n cancel: (reason: unknown) => void;\n execution: CallInvocationExecution;\n readonly listeners: Set<() => void>;\n reject: ((reason: unknown) => void) | undefined;\n resolve: ((value: Output) => void) | undefined;\n settlement: CallSettlement<Output>;\n snapshot: CallInvocationSnapshot<Output>;\n};\n\nconst notStarted = Object.freeze({ status: 'not-started' as const });\nconst noop = (): void => undefined;\nconst pending = Object.freeze({ status: 'pending' as const });\nconst callInvocationRecords = new WeakMap<object, CallInvocationRecord<unknown>>();\n\nfunction publishCallInvocation<Output>(record: CallInvocationRecord<Output>): void {\n record.snapshot = Object.freeze({ execution: record.execution, settlement: record.settlement });\n\n for (const listener of [...record.listeners]) {\n try {\n listener();\n } catch (error) {\n reportDetachedError(error);\n }\n }\n}\n\nfunction updateExecution<Output>(record: CallInvocationRecord<Output>, execution: CallInvocationExecution): void {\n if (\n record.execution.status === 'stopped' ||\n (record.execution.status === 'not-started' && record.execution.reason !== undefined)\n ) {\n return;\n }\n\n record.execution = Object.freeze(execution);\n publishCallInvocation(record);\n\n if (execution.status === 'stopped' || (execution.status === 'not-started' && execution.reason !== undefined)) {\n record.listeners.clear();\n }\n}\n\n/** A `CallError` keeps its own code: the state lives in `code`, so `unavailable` never flattens to `cancelled`. */\nfunction cancellationError(reason: unknown): CallError {\n return reason instanceof CallError ? reason : cancelledCall(reason);\n}\n\nfunction rejectSettlement(reject: ((reason: unknown) => void) | undefined, reason: unknown): void {\n if (reject !== undefined) reject(reason);\n}\n\nfunction resolveSettlement<Output>(resolve: ((value: Output) => void) | undefined, value: Output): void {\n if (resolve !== undefined) resolve(value);\n}\n\nfunction deliverSettlement<Output>(\n resolve: ((value: Output) => void) | undefined,\n reject: ((reason: unknown) => void) | undefined,\n settlement: CallTerminalSettlement<Output>,\n): void {\n switch (settlement.status) {\n case 'cancelled':\n rejectSettlement(reject, cancellationError(settlement.reason));\n break;\n case 'fulfilled':\n resolveSettlement(resolve, settlement.value);\n break;\n case 'rejected':\n rejectSettlement(reject, settlement.error);\n break;\n }\n}\n\nfunction settleCallInvocation<Output>(\n record: CallInvocationRecord<Output>,\n settlement: CallTerminalSettlement<Output>,\n): boolean {\n if (record.settlement.status !== 'pending') return false;\n\n record.settlement = Object.freeze(settlement);\n const resolve = record.resolve;\n const reject = record.reject;\n record.cancel = noop;\n record.resolve = undefined;\n record.reject = undefined;\n deliverSettlement(resolve, reject, settlement);\n\n publishCallInvocation(record);\n\n return true;\n}\n\nfunction createCallInvocation<Output>(options: {\n readonly callId: string;\n readonly execution?: CallInvocationExecution;\n readonly onCancel: (reason: unknown) => void;\n readonly parentCallId?: string;\n}): CallInvocationController<Output> {\n let resolve!: (value: Output) => void;\n let reject!: (reason: unknown) => void;\n const promise = new Promise<Output>((promiseResolve, promiseReject) => {\n resolve = promiseResolve;\n reject = promiseReject;\n });\n const execution = options.execution ?? notStarted;\n const record: CallInvocationRecord<Output> = {\n cancel: options.onCancel,\n execution,\n listeners: new Set(),\n reject,\n resolve,\n settlement: pending,\n snapshot: Object.freeze({ execution, settlement: pending }),\n };\n const call = promise as CallInvocation<Output>;\n\n Object.defineProperties(call, {\n callId: { enumerable: true, value: options.callId },\n [callInvocationBrand]: { value: true },\n cancel: {\n enumerable: true,\n value: (reason: unknown = 'Call invocation was cancelled by its caller.') => {\n if (record.settlement.status === 'pending') record.cancel(reason);\n },\n },\n getSnapshot: { enumerable: true, value: () => record.snapshot },\n parentCallId: { enumerable: true, value: options.parentCallId },\n subscribe: {\n enumerable: true,\n value: (listener: () => void) => {\n if (typeof listener !== 'function') throw new TypeError('CallInvocation listener must be a function.');\n\n if (\n record.execution.status === 'stopped' ||\n (record.execution.status === 'not-started' && record.execution.reason !== undefined)\n ) {\n return noop;\n }\n\n record.listeners.add(listener);\n let subscribed = true;\n\n return (): void => {\n if (!subscribed) return;\n\n subscribed = false;\n record.listeners.delete(listener);\n };\n },\n },\n });\n callInvocationRecords.set(call, record as CallInvocationRecord<unknown>);\n Object.freeze(call);\n\n const controller: CallInvocationController<Output> = {\n call,\n cancelled: (reason: unknown) => settleCallInvocation(record, { reason, status: 'cancelled' }),\n draining: () => updateExecution(record, { status: 'draining' }),\n fulfilled: (value: Output) => settleCallInvocation(record, { status: 'fulfilled', value }),\n rejected: (error: unknown) => settleCallInvocation(record, { error, status: 'rejected' }),\n running: () => updateExecution(record, { status: 'running' }),\n stopped: (stoppedExecution: Extract<CallInvocationExecution, { readonly reason: unknown }>) =>\n updateExecution(record, stoppedExecution),\n };\n\n return Object.freeze(controller);\n}\n\nfunction isCallInvocation(value: unknown): value is CallInvocation<unknown> {\n return (typeof value === 'object' && value !== null) || typeof value === 'function'\n ? callInvocationRecords.has(value)\n : false;\n}\n\nexport { isCallInvocation };\nexport type { CallInvocation, CallInvocationExecution, CallInvocationSnapshot, CallSettlement };\nexport { createCallInvocation };\nexport type { CallInvocationController };\n"],"names":["callInvocationBrand","notStarted","noop","pending","callInvocationRecords","publishCallInvocation","record","listener","error","reportDetachedError","updateExecution","execution","cancellationError","reason","CallError","cancelledCall","rejectSettlement","reject","resolveSettlement","resolve","value","deliverSettlement","settlement","settleCallInvocation","createCallInvocation","options","promise","promiseResolve","promiseReject","call","subscribed","stoppedExecution"],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { DeclarationId } from './identity.js';
|
|
2
|
+
import type { NoExtraKeys } from './type-helpers.js';
|
|
3
|
+
declare const callLaneBrand: unique symbol;
|
|
4
|
+
interface CallLane {
|
|
5
|
+
readonly [callLaneBrand]: true;
|
|
6
|
+
readonly id: DeclarationId;
|
|
7
|
+
}
|
|
8
|
+
interface CallLaneRequest {
|
|
9
|
+
cancel(): boolean;
|
|
10
|
+
/** True while nothing else has queued behind this request: only then may a `latest` call replace it in place. */
|
|
11
|
+
isLast(): boolean;
|
|
12
|
+
}
|
|
13
|
+
declare function defineCallLane<const Options extends {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
}>(options: NoExtraKeys<Options, {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
}> & Options): CallLane;
|
|
18
|
+
declare function isCallLane(value: unknown): value is CallLane;
|
|
19
|
+
declare function requestCallLane(lane: CallLane, acquire: (release: () => void) => void, owner?: number): CallLaneRequest;
|
|
20
|
+
declare function inspectCallLane(lane: CallLane): Readonly<{
|
|
21
|
+
blockedBy: 'unknown' | number | null;
|
|
22
|
+
declaration: string;
|
|
23
|
+
id: number;
|
|
24
|
+
}>;
|
|
25
|
+
export { defineCallLane, inspectCallLane };
|
|
26
|
+
export type { CallLane };
|
|
27
|
+
export { isCallLane, requestCallLane };
|
|
28
|
+
export type { CallLaneRequest };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{declarationId as c}from"./identity.js";const s=Symbol("opetope.call-lane");let p=0;const h=Object.freeze({}),o=new WeakMap;function w(e){return typeof e=="object"&&e!==null}function L(e){const n=Object.getPrototypeOf(e);return n===Object.prototype||n===null}function g(e){if(!w(e)||!L(e))throw new TypeError("CallLane options must be a plain object record.");const n=Reflect.ownKeys(e);if(n.length!==1)throw new TypeError("CallLane options require the exact own id field.");if(n[0]!=="id")throw new TypeError("CallLane options require the exact own id field.")}function C(e){g(e);const n=Object.freeze({[s]:h,id:c(e.id)});return o.set(n,{declaration:e.id,flushing:!1,head:void 0,identity:++p,owner:void 0,running:!1,tail:void 0}),n}function b(e){return typeof e=="object"&&e!==null&&o.has(e)}function l(e){const n=o.get(e);if(n===void 0)throw new TypeError("CallLane is not authentic.");return n}function u(e){const n=e.state;return n===void 0?!1:(e.previous===void 0?n.head=e.next:e.previous.next=e.next,e.next===void 0?n.tail=e.previous:e.next.previous=e.previous,e.acquire=void 0,e.next=void 0,e.previous=void 0,e.state=void 0,!0)}function x(e,n,r,t){e.running=!0,e.owner=t;let i=!1;const a=()=>{i||(i=!0,e.running=!1,e.owner=void 0,r())};try{n(a)}catch(d){throw a(),d}}function f(e){if(!(e.flushing||e.running)){e.flushing=!0;try{for(;!e.running;){const n=e.head;if(n===void 0)return;const r=n.acquire;u(n),r!==void 0&&x(e,r,()=>f(e),n.owner)}}finally{e.flushing=!1}}}function y(e){return Object.freeze({cancel(){return u(e)},isLast(){var n;return((n=e.state)==null?void 0:n.tail)===e}})}function q(e,n,r){const t=l(e);if(typeof n!="function")throw new TypeError("CallLane acquisition must be a function.");const i={acquire:n,next:void 0,owner:r,previous:t.tail,state:t};return t.tail===void 0?t.head=i:t.tail.next=i,t.tail=i,f(t),y(i)}function j(e){const n=l(e);return{blockedBy:n.running?n.owner??"unknown":null,declaration:n.declaration,id:n.identity}}export{C as defineCallLane,j as inspectCallLane,b as isCallLane,q as requestCallLane};
|
|
2
|
+
//# sourceMappingURL=call-lane.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-lane.js","sources":["../src/call-lane.ts"],"sourcesContent":["import { declarationId } from './identity';\nimport type { DeclarationId } from './identity';\nimport type { NoExtraKeys } from './type-helpers';\n\nconst callLaneBrand: unique symbol = Symbol('opetope.call-lane');\n\ninterface CallLane {\n readonly [callLaneBrand]: true;\n readonly id: DeclarationId;\n}\n\ninterface CallLaneRequest {\n cancel(): boolean;\n /** True while nothing else has queued behind this request: only then may a `latest` call replace it in place. */\n isLast(): boolean;\n}\n\ntype PendingLaneEntry = {\n acquire: ((release: () => void) => void) | undefined;\n next: PendingLaneEntry | undefined;\n readonly owner: number | undefined;\n previous: PendingLaneEntry | undefined;\n state: CallLaneState | undefined;\n};\n\ntype CallLaneState = {\n readonly declaration: string;\n flushing: boolean;\n head: PendingLaneEntry | undefined;\n readonly identity: number;\n owner: number | undefined;\n running: boolean;\n tail: PendingLaneEntry | undefined;\n};\n\nlet nextLaneIdentity = 0;\nconst laneMarker = Object.freeze({});\nconst laneStates = new WeakMap<object, CallLaneState>();\n\nfunction isRecord(value: unknown): value is Readonly<Record<PropertyKey, unknown>> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction hasPlainPrototype(value: object): boolean {\n const prototype: unknown = Object.getPrototypeOf(value);\n\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction assertCallLaneOptions(value: unknown): asserts value is { readonly id: string } {\n if (!isRecord(value) || !hasPlainPrototype(value)) {\n throw new TypeError('CallLane options must be a plain object record.');\n }\n\n const keys = Reflect.ownKeys(value);\n\n if (keys.length !== 1) throw new TypeError('CallLane options require the exact own id field.');\n\n if (keys[0] !== 'id') throw new TypeError('CallLane options require the exact own id field.');\n}\n\nfunction defineCallLane<const Options extends { readonly id: string }>(\n options: NoExtraKeys<Options, { readonly id: string }> & Options,\n): CallLane {\n assertCallLaneOptions(options);\n const lane = Object.freeze({\n [callLaneBrand]: laneMarker as unknown as true,\n id: declarationId(options.id),\n });\n laneStates.set(lane, {\n declaration: options.id,\n flushing: false,\n head: undefined,\n identity: ++nextLaneIdentity,\n owner: undefined,\n running: false,\n tail: undefined,\n });\n\n return lane;\n}\n\nfunction isCallLane(value: unknown): value is CallLane {\n return typeof value === 'object' && value !== null && laneStates.has(value);\n}\n\nfunction requireCallLaneState(lane: CallLane): CallLaneState {\n const state = laneStates.get(lane);\n\n if (state === undefined) throw new TypeError('CallLane is not authentic.');\n\n return state;\n}\n\nfunction removePendingEntry(entry: PendingLaneEntry): boolean {\n const state = entry.state;\n\n if (state === undefined) return false;\n\n if (entry.previous === undefined) state.head = entry.next;\n else entry.previous.next = entry.next;\n\n if (entry.next === undefined) state.tail = entry.previous;\n else entry.next.previous = entry.previous;\n\n entry.acquire = undefined;\n entry.next = undefined;\n entry.previous = undefined;\n entry.state = undefined;\n\n return true;\n}\n\nfunction acquireCallLane(\n state: CallLaneState,\n acquire: (release: () => void) => void,\n resume: () => void,\n owner: number | undefined,\n): void {\n state.running = true;\n state.owner = owner;\n let released = false;\n const release = (): void => {\n if (released) return;\n\n released = true;\n state.running = false;\n state.owner = undefined;\n resume();\n };\n\n try {\n acquire(release);\n } catch (error) {\n release();\n throw error;\n }\n}\n\nfunction flushCallLane(state: CallLaneState): void {\n if (state.flushing || state.running) return;\n\n state.flushing = true;\n\n try {\n while (!state.running) {\n const entry = state.head;\n\n if (entry === undefined) return;\n\n const acquire = entry.acquire;\n removePendingEntry(entry);\n\n if (acquire !== undefined) acquireCallLane(state, acquire, () => flushCallLane(state), entry.owner);\n }\n } finally {\n state.flushing = false;\n }\n}\n\nfunction createCallLaneRequest(entry: PendingLaneEntry): CallLaneRequest {\n return Object.freeze({\n cancel(): boolean {\n return removePendingEntry(entry);\n },\n isLast(): boolean {\n return entry.state?.tail === entry;\n },\n });\n}\n\nfunction requestCallLane(lane: CallLane, acquire: (release: () => void) => void, owner?: number): CallLaneRequest {\n const state = requireCallLaneState(lane);\n\n if (typeof acquire !== 'function') throw new TypeError('CallLane acquisition must be a function.');\n\n const entry: PendingLaneEntry = { acquire, next: undefined, owner, previous: state.tail, state };\n\n if (state.tail === undefined) state.head = entry;\n else state.tail.next = entry;\n\n state.tail = entry;\n flushCallLane(state);\n\n return createCallLaneRequest(entry);\n}\n\nfunction inspectCallLane(\n lane: CallLane,\n): Readonly<{ blockedBy: 'unknown' | number | null; declaration: string; id: number }> {\n const state = requireCallLaneState(lane);\n\n return {\n blockedBy: state.running ? (state.owner ?? 'unknown') : null,\n declaration: state.declaration,\n id: state.identity,\n };\n}\n\nexport { defineCallLane, inspectCallLane };\nexport type { CallLane };\nexport { isCallLane, requestCallLane };\nexport type { CallLaneRequest };\n"],"names":["callLaneBrand","nextLaneIdentity","laneMarker","laneStates","isRecord","value","hasPlainPrototype","prototype","assertCallLaneOptions","keys","defineCallLane","options","lane","declarationId","isCallLane","requireCallLaneState","state","removePendingEntry","entry","acquireCallLane","acquire","resume","owner","released","release","error","flushCallLane","createCallLaneRequest","_a","requestCallLane","inspectCallLane"],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { CallAuthority } from './call-authority.js';
|
|
2
|
+
type CallTargetResultFinalizer = () => void;
|
|
3
|
+
declare function canHaveThen(value: unknown): value is ((...args: never[]) => unknown) | object;
|
|
4
|
+
declare function withCallTargetResultFinalizer<Output>(result: Output | PromiseLike<Output>, options: {
|
|
5
|
+
readonly assertCurrent?: () => void;
|
|
6
|
+
readonly finalize: CallTargetResultFinalizer;
|
|
7
|
+
readonly signal?: AbortSignal;
|
|
8
|
+
}): Output | PromiseLike<Output>;
|
|
9
|
+
declare function settleGuardedResult<Output>(result: Output | PromiseLike<Output>, options: {
|
|
10
|
+
readonly assertCurrent: () => void;
|
|
11
|
+
readonly finalize?: CallTargetResultFinalizer;
|
|
12
|
+
readonly signal?: AbortSignal | undefined;
|
|
13
|
+
/** Keep started work owned until its result settles, even after logical cancellation. */
|
|
14
|
+
readonly waitForPhysicalSettlement?: boolean;
|
|
15
|
+
}): Output | PromiseLike<Output>;
|
|
16
|
+
declare function mapGuardedResult<Source, Output>(result: PromiseLike<Source> | Source, options: {
|
|
17
|
+
readonly assertCurrent: () => void;
|
|
18
|
+
readonly map: (value: Source) => Output | PromiseLike<Output>;
|
|
19
|
+
readonly signal?: AbortSignal | undefined;
|
|
20
|
+
}): Output | PromiseLike<Output>;
|
|
21
|
+
declare function settleLifetimeResult<Output>(result: Output | PromiseLike<Output>, authority: CallAuthority, guard: () => void, finalize: () => void, signal: AbortSignal): Output | PromiseLike<Output>;
|
|
22
|
+
export { canHaveThen, mapGuardedResult, settleGuardedResult, settleLifetimeResult, withCallTargetResultFinalizer };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{throwIfSignalAborted as M}from"./abort-compat.js";import{createAggregateError as q}from"./platform-compat.js";const p=()=>{},g=new WeakMap,R=Promise.prototype.then,x={immediate:!0,then:R};function b(t){return typeof t=="object"&&t!==null||typeof t=="function"}function H(t,e){const r=Object.freeze({});return g.set(r,{...e,result:t}),r}function j(t,e,r){try{return R.call(t,e,r),!0}catch{return!1}}function E(t,e,r){if(t instanceof Promise){const n=r??t.then;if(typeof n!="function")throw e;return{immediate:!0,then:n}}if(!j(t,p,p))throw e;return x}function I(t,e){try{e()}catch(n){return E(t,n)}let r;try{r=t.then,e()}catch(n){try{e()}catch(c){return E(t,c,r)}throw n}return typeof r=="function"?{immediate:t instanceof Promise,then:r}:void 0}function k(t,e,r,n=!1){if(!b(t))return e(),{kind:"sync",value:t};if((r==null?void 0:r.has(t))===!0)throw e(),new TypeError("Thenable resolution cycle.");const c=I(t,e);if(c===void 0)return{kind:"sync",value:t};const l=r??new Set;l.add(t);const{immediate:u,then:a}=c;let o;const A=new Promise((L,O)=>{let d={assertCurrent:e,chain:l,then:a,value:t,waitForPhysicalSettlement:n},m=!1,h=!1;const v=()=>{d=void 0},y=i=>{h||(h=!0,v(),O(i))},S=i=>{h||(h=!0,v(),L({value:i}))},C=i=>{m||(m=!0,y(i))},T=i=>{if(m)return;m=!0;let s;try{const f=d;if(f===void 0)return;s=k(i,f.assertCurrent,f.chain,f.waitForPhysicalSettlement)}catch(f){y(f);return}if(s.kind==="sync"){S(s.value);return}const w=d;w!==void 0&&(w.nestedCancel=s.cancel,s.promise.then(({value:f})=>S(f),f=>y(f)))},G=(i,s)=>{try{i.assertCurrent(),C(s)}catch(w){if(!u&&j(i.value,T,C)&&i.waitForPhysicalSettlement)return;C(w)}},F=()=>{const i=d;if(!(h||i===void 0))try{u||i.assertCurrent(),i.then.call(i.value,T,C),u&&!i.waitForPhysicalSettlement&&i.assertCurrent()}catch(s){G(i,s)}};u?F():queueMicrotask(F),o=i=>{if(h)return;m=!0;const s=d==null?void 0:d.nestedCancel;s!==void 0&&s(i),y(i)}});return{cancel:o,kind:"async",promise:A}}function N(t,e){let r=!1,n;try{t==null||t.finalize()}catch(c){r=!0,n=c}try{e==null||e()}catch(c){throw r?q([n,c],"Call result finalizers failed.",{cause:n}):c}if(r)throw n}function V(t,e,r){return t===!0?void 0:(r==null?void 0:r.signal)??e}function z(t,e,r,n){if(t.kind==="sync")return r(),t.value;let c;const l=()=>{c!==void 0&&n!==void 0&&n.removeEventListener("abort",c),r()},u=t.promise.then(({value:a})=>{try{return e(),a}finally{l()}},a=>{try{throw e(),a}finally{l()}});return n!==void 0&&(c=()=>{try{e(),M(n)}catch(a){t.cancel(a)}},n.aborted?c():n.addEventListener("abort",c,{once:!0})),u}function P(t,e){var a;if(!b(t))try{return e.assertCurrent(),t}finally{(a=e.finalize)==null||a.call(e)}const r=g.get(t),n=r===void 0?t:r.result,c=()=>{var o;(o=r==null?void 0:r.assertCurrent)==null||o.call(r),e.assertCurrent()},l=()=>{N(r,e.finalize)};let u;try{u=k(n,c,void 0,e.waitForPhysicalSettlement)}catch(o){throw l(),o}return z(u,c,l,V(e.waitForPhysicalSettlement,e.signal,r))}function W(t,e){const r=k(t,e.assertCurrent);if(r.kind==="sync"){let l;try{e.assertCurrent(),l=e.map(r.value)}catch(u){throw e.assertCurrent(),u}return P(l,{assertCurrent:e.assertCurrent,signal:e.signal})}let n=r.cancel;const c=r.promise.then(({value:l})=>{let u;try{e.assertCurrent(),u=e.map(l)}catch(o){throw e.assertCurrent(),o}const a=k(u,e.assertCurrent);return a.kind==="async"&&(n=a.cancel),a.kind==="sync"?{value:a.value}:a.promise});return z({cancel:l=>n(l),kind:"async",promise:c},e.assertCurrent,p,e.signal)}function B(t,e,r,n,c){if(!b(t))try{return r(),e.assertCurrent(),t}finally{n()}return P(t,{assertCurrent:()=>{r(),e.assertCurrent()},finalize:n,signal:c})}export{b as canHaveThen,W as mapGuardedResult,P as settleGuardedResult,B as settleLifetimeResult,H as withCallTargetResultFinalizer};
|
|
2
|
+
//# sourceMappingURL=call-target-result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-target-result.js","sources":["../src/call-target-result.ts"],"sourcesContent":["import { throwIfSignalAborted } from './abort-compat';\nimport type { CallAuthority } from './call-authority';\nimport { createAggregateError } from './platform-compat';\n\nconst noop = (): void => undefined;\n\ntype CallTargetResultFinalizer = () => void;\n\ntype CallTargetResultRecord = {\n readonly assertCurrent?: () => void;\n readonly finalize: CallTargetResultFinalizer;\n readonly result: unknown;\n readonly signal?: AbortSignal;\n};\n\ntype SettledValue<Output> = {\n readonly value: Output;\n};\n\ntype LifetimeResultSettlement<Output> =\n | {\n readonly cancel: (reason: unknown) => void;\n readonly kind: 'async';\n readonly promise: Promise<SettledValue<Output>>;\n }\n | { readonly kind: 'sync'; readonly value: Output };\n\ntype ThenMethod = (\n this: ((...arguments_: never[]) => unknown) | object,\n resolve: (value: unknown) => void,\n reject: (error: unknown) => void,\n) => unknown;\n\ntype CallableThenRecord = {\n readonly immediate: boolean;\n readonly then: ThenMethod;\n};\n\ntype ActiveAssimilation = {\n readonly assertCurrent: () => void;\n readonly chain: Set<object>;\n nestedCancel?: (reason: unknown) => void;\n readonly then: ThenMethod;\n readonly value: ((...arguments_: never[]) => unknown) | object;\n readonly waitForPhysicalSettlement: boolean;\n};\n\nconst resultFinalizers = new WeakMap<object, CallTargetResultRecord>();\nconst nativePromiseThen = Promise.prototype.then as ThenMethod;\nconst retiredCallableThen: CallableThenRecord = { immediate: true, then: nativePromiseThen };\n\nfunction canHaveThen(value: unknown): value is ((...args: never[]) => unknown) | object {\n return (typeof value === 'object' && value !== null) || typeof value === 'function';\n}\n\nfunction withCallTargetResultFinalizer<Output>(\n result: Output | PromiseLike<Output>,\n options: {\n readonly assertCurrent?: () => void;\n readonly finalize: CallTargetResultFinalizer;\n readonly signal?: AbortSignal;\n },\n): Output | PromiseLike<Output> {\n const envelope = Object.freeze({});\n\n resultFinalizers.set(envelope, { ...options, result });\n\n return envelope as Output | PromiseLike<Output>;\n}\n\nfunction observeRetiredNativePromise(\n value: ActiveAssimilation['value'],\n resolve: (value: unknown) => void,\n reject: (reason: unknown) => void,\n): boolean {\n try {\n nativePromiseThen.call(value, resolve, reject);\n\n return true;\n } catch {\n return false;\n }\n}\n\nfunction readRetiredCallableThen(\n value: ActiveAssimilation['value'],\n reason: unknown,\n then?: unknown,\n): CallableThenRecord {\n if (value instanceof Promise) {\n const method = then ?? value.then;\n\n if (typeof method !== 'function') throw reason;\n\n return { immediate: true, then: method as ThenMethod };\n }\n\n // Preserve synchronous rejection for non-Promise values. The cancellation-only probe uses the intrinsic brand\n // check and also observes a rejected foreign Promise before the physical-drain subscription is installed.\n if (!observeRetiredNativePromise(value, noop, noop)) throw reason;\n\n return retiredCallableThen;\n}\n\nfunction readCurrentCallableThen(\n value: ((...args: never[]) => unknown) | object,\n assertCurrent: () => void,\n): CallableThenRecord | undefined {\n try {\n assertCurrent();\n } catch (reason) {\n // The intrinsic performs the native Promise brand check across realms. It cannot enter a custom thenable,\n // unlike rereading or invoking a foreign `then` after cancellation.\n return readRetiredCallableThen(value, reason);\n }\n\n let then: unknown;\n\n try {\n then = (value as PromiseLike<unknown>).then;\n assertCurrent();\n } catch (error) {\n try {\n assertCurrent();\n } catch (reason) {\n return readRetiredCallableThen(value, reason, then);\n }\n\n throw error;\n }\n\n // Native Promises are subscribed in the current turn so an already-rejected result is always\n // observed. Custom thenables remain behind a microtask fence and are rechecked before invocation.\n return typeof then === 'function' ? { immediate: value instanceof Promise, then: then as ThenMethod } : undefined;\n}\n\nfunction settleValue<Output>(\n value: unknown,\n assertCurrent: () => void,\n seen?: Set<object>,\n waitForPhysicalSettlement = false,\n): LifetimeResultSettlement<Output> {\n if (!canHaveThen(value)) {\n assertCurrent();\n\n return { kind: 'sync', value: value as Output };\n }\n\n if (seen?.has(value) === true) {\n assertCurrent();\n\n throw new TypeError('Thenable resolution cycle.');\n }\n\n const callable = readCurrentCallableThen(value, assertCurrent);\n\n if (callable === undefined) return { kind: 'sync', value: value as Output };\n\n const chain = seen ?? new Set<object>();\n chain.add(value);\n const { immediate, then } = callable;\n let cancel!: (reason: unknown) => void;\n const promise = new Promise<SettledValue<Output>>((resolve, reject) => {\n let active: ActiveAssimilation | undefined = { assertCurrent, chain, then, value, waitForPhysicalSettlement };\n let callbackAccepted = false;\n let completed = false;\n const release = (): void => {\n active = undefined;\n };\n const completeRejection = (error: unknown): void => {\n if (completed) return;\n\n completed = true;\n release();\n reject(error);\n };\n const completeResolution = (settled: Output): void => {\n if (completed) return;\n\n completed = true;\n release();\n resolve({ value: settled });\n };\n const rejectOnce = (error: unknown): void => {\n if (callbackAccepted) return;\n\n callbackAccepted = true;\n completeRejection(error);\n };\n const resolveOnce = (nested: unknown): void => {\n if (callbackAccepted) return;\n\n callbackAccepted = true;\n let settlement: LifetimeResultSettlement<Output>;\n\n try {\n const current = active;\n\n if (current === undefined) return;\n\n settlement = settleValue<Output>(\n nested,\n current.assertCurrent,\n current.chain,\n current.waitForPhysicalSettlement,\n );\n } catch (error) {\n completeRejection(error);\n\n return;\n }\n\n if (settlement.kind === 'sync') {\n completeResolution(settlement.value);\n\n return;\n }\n\n const current = active;\n\n if (current === undefined) return;\n\n current.nestedCancel = settlement.cancel;\n void settlement.promise.then(\n ({ value: settled }) => completeResolution(settled),\n (error: unknown) => completeRejection(error),\n );\n };\n const rejectAssimilation = (current: ActiveAssimilation, error: unknown): void => {\n try {\n current.assertCurrent();\n rejectOnce(error);\n } catch (currentError) {\n if (\n !immediate &&\n observeRetiredNativePromise(current.value, resolveOnce, rejectOnce) &&\n current.waitForPhysicalSettlement\n )\n return;\n\n rejectOnce(currentError);\n }\n };\n const assimilate = (): void => {\n const current = active;\n\n if (completed || current === undefined) return;\n\n try {\n if (!immediate) current.assertCurrent();\n\n current.then.call(current.value, resolveOnce, rejectOnce);\n\n if (immediate && !current.waitForPhysicalSettlement) current.assertCurrent();\n } catch (error) {\n rejectAssimilation(current, error);\n }\n };\n\n if (immediate) {\n assimilate();\n } else {\n queueMicrotask(assimilate);\n }\n\n cancel = (reason: unknown): void => {\n if (completed) return;\n\n callbackAccepted = true;\n\n const nestedCancel = active?.nestedCancel;\n\n if (nestedCancel !== undefined) nestedCancel(reason);\n\n completeRejection(reason);\n };\n });\n\n return { cancel, kind: 'async', promise };\n}\n\nfunction finalizeResult(\n record: CallTargetResultRecord | undefined,\n outer: CallTargetResultFinalizer | undefined,\n): void {\n let failed = false;\n let failure: unknown;\n\n try {\n record?.finalize();\n } catch (error) {\n failed = true;\n failure = error;\n }\n\n try {\n outer?.();\n } catch (error) {\n if (failed) throw createAggregateError([failure, error], 'Call result finalizers failed.', { cause: failure });\n\n throw error;\n }\n\n if (failed) throw failure;\n}\n\nfunction settlementSignal(\n waitForPhysicalSettlement: boolean | undefined,\n signal: AbortSignal | undefined,\n record: CallTargetResultRecord | undefined,\n): AbortSignal | undefined {\n return waitForPhysicalSettlement === true ? undefined : (record?.signal ?? signal);\n}\n\nfunction publishSettlement<Output>(\n settlement: LifetimeResultSettlement<Output>,\n assertCurrent: () => void,\n finalize: () => void,\n signal?: AbortSignal,\n): Output | PromiseLike<Output> {\n if (settlement.kind === 'sync') {\n finalize();\n\n return settlement.value;\n }\n\n let abortListener: (() => void) | undefined;\n const finish = (): void => {\n if (abortListener !== undefined && signal !== undefined) signal.removeEventListener('abort', abortListener);\n\n finalize();\n };\n const published = settlement.promise.then(\n ({ value: settled }) => {\n try {\n assertCurrent();\n\n return settled;\n } finally {\n finish();\n }\n },\n (error: unknown) => {\n try {\n assertCurrent();\n\n throw error;\n } finally {\n finish();\n }\n },\n );\n\n if (signal !== undefined) {\n abortListener = (): void => {\n try {\n assertCurrent();\n throwIfSignalAborted(signal);\n } catch (reason) {\n settlement.cancel(reason);\n }\n };\n\n if (signal.aborted) {\n abortListener();\n } else {\n signal.addEventListener('abort', abortListener, { once: true });\n }\n }\n\n return published;\n}\n\nfunction settleGuardedResult<Output>(\n result: Output | PromiseLike<Output>,\n options: {\n readonly assertCurrent: () => void;\n readonly finalize?: CallTargetResultFinalizer;\n readonly signal?: AbortSignal | undefined;\n /** Keep started work owned until its result settles, even after logical cancellation. */\n readonly waitForPhysicalSettlement?: boolean;\n },\n): Output | PromiseLike<Output> {\n if (!canHaveThen(result)) {\n try {\n options.assertCurrent();\n\n return result;\n } finally {\n options.finalize?.();\n }\n }\n\n const finalizerRecord = resultFinalizers.get(result);\n const value = finalizerRecord === undefined ? result : finalizerRecord.result;\n const assertCurrent = (): void => {\n finalizerRecord?.assertCurrent?.();\n options.assertCurrent();\n };\n const finalize = (): void => {\n finalizeResult(finalizerRecord, options.finalize);\n };\n let settlement: LifetimeResultSettlement<Output>;\n\n try {\n settlement = settleValue<Output>(value, assertCurrent, undefined, options.waitForPhysicalSettlement);\n } catch (error) {\n finalize();\n throw error;\n }\n\n return publishSettlement(\n settlement,\n assertCurrent,\n finalize,\n settlementSignal(options.waitForPhysicalSettlement, options.signal, finalizerRecord),\n );\n}\n\nfunction mapGuardedResult<Source, Output>(\n result: PromiseLike<Source> | Source,\n options: {\n readonly assertCurrent: () => void;\n readonly map: (value: Source) => Output | PromiseLike<Output>;\n readonly signal?: AbortSignal | undefined;\n },\n): Output | PromiseLike<Output> {\n const source = settleValue<Source>(result, options.assertCurrent);\n\n if (source.kind === 'sync') {\n let mapped: Output | PromiseLike<Output>;\n\n try {\n options.assertCurrent();\n mapped = options.map(source.value);\n } catch (error) {\n options.assertCurrent();\n\n throw error;\n }\n\n return settleGuardedResult(mapped, { assertCurrent: options.assertCurrent, signal: options.signal });\n }\n\n let cancelCurrent = source.cancel;\n const mapped = source.promise.then(({ value }) => {\n let result: Output | PromiseLike<Output>;\n\n try {\n options.assertCurrent();\n result = options.map(value);\n } catch (error) {\n options.assertCurrent();\n\n throw error;\n }\n\n const settlement = settleValue<Output>(result, options.assertCurrent);\n\n if (settlement.kind === 'async') cancelCurrent = settlement.cancel;\n\n return settlement.kind === 'sync' ? { value: settlement.value } : settlement.promise;\n });\n\n return publishSettlement(\n { cancel: reason => cancelCurrent(reason), kind: 'async', promise: mapped },\n options.assertCurrent,\n noop,\n options.signal,\n );\n}\n\nfunction settleLifetimeResult<Output>(\n result: Output | PromiseLike<Output>,\n authority: CallAuthority,\n guard: () => void,\n finalize: () => void,\n signal: AbortSignal,\n): Output | PromiseLike<Output> {\n if (!canHaveThen(result)) {\n try {\n guard();\n authority.assertCurrent();\n\n return result;\n } finally {\n finalize();\n }\n }\n\n return settleGuardedResult(result, {\n assertCurrent: () => {\n guard();\n authority.assertCurrent();\n },\n finalize,\n signal,\n });\n}\n\nexport { canHaveThen, mapGuardedResult, settleGuardedResult, settleLifetimeResult, withCallTargetResultFinalizer };\n"],"names":["noop","resultFinalizers","nativePromiseThen","retiredCallableThen","canHaveThen","value","withCallTargetResultFinalizer","result","options","envelope","observeRetiredNativePromise","resolve","reject","readRetiredCallableThen","reason","then","method","readCurrentCallableThen","assertCurrent","error","settleValue","seen","waitForPhysicalSettlement","callable","chain","immediate","cancel","promise","active","callbackAccepted","completed","release","completeRejection","completeResolution","settled","rejectOnce","resolveOnce","nested","settlement","current","rejectAssimilation","currentError","assimilate","nestedCancel","finalizeResult","record","outer","failed","failure","createAggregateError","settlementSignal","signal","publishSettlement","finalize","abortListener","finish","published","throwIfSignalAborted","settleGuardedResult","_a","finalizerRecord","mapGuardedResult","source","mapped","cancelCurrent","settleLifetimeResult","authority","guard"],"mappings":"qHAIA,MAAMA,EAAO,IAAA,GA2CPC,EAAmB,IAAI,QACvBC,EAAoB,QAAQ,UAAU,KACtCC,EAA0C,CAAE,UAAW,GAAM,KAAMD,CAAiB,EAE1F,SAASE,EAAYC,EAAc,CACjC,OAAQ,OAAOA,GAAU,UAAYA,IAAU,MAAS,OAAOA,GAAU,UAC3E,CAEA,SAASC,EACPC,EACAC,EAIC,CAED,MAAMC,EAAW,OAAO,OAAO,EAAE,EAEjC,OAAAR,EAAiB,IAAIQ,EAAU,CAAE,GAAGD,EAAS,OAAAD,CAAM,CAAE,EAE9CE,CACT,CAEA,SAASC,EACPL,EACAM,EACAC,EAAiC,CAEjC,GAAI,CACF,OAAAV,EAAkB,KAAKG,EAAOM,EAASC,CAAM,EAEtC,EACT,MAAQ,CACN,MAAO,EACT,CACF,CAEA,SAASC,EACPR,EACAS,EACAC,EAAc,CAEd,GAAIV,aAAiB,QAAS,CAC5B,MAAMW,EAASD,GAAQV,EAAM,KAE7B,GAAI,OAAOW,GAAW,WAAY,MAAMF,EAExC,MAAO,CAAE,UAAW,GAAM,KAAME,CAAoB,CACtD,CAIA,GAAI,CAACN,EAA4BL,EAAOL,EAAMA,CAAI,EAAG,MAAMc,EAE3D,OAAOX,CACT,CAEA,SAASc,EACPZ,EACAa,EAAyB,CAEzB,GAAI,CACFA,EAAa,CACf,OAASJ,EAAQ,CAGf,OAAOD,EAAwBR,EAAOS,CAAM,CAC9C,CAEA,IAAIC,EAEJ,GAAI,CACFA,EAAQV,EAA+B,KACvCa,EAAa,CACf,OAASC,EAAO,CACd,GAAI,CACFD,EAAa,CACf,OAASJ,EAAQ,CACf,OAAOD,EAAwBR,EAAOS,EAAQC,CAAI,CACpD,CAEA,MAAMI,CACR,CAIA,OAAO,OAAOJ,GAAS,WAAa,CAAE,UAAWV,aAAiB,QAAS,KAAMU,CAAkB,EAAK,MAC1G,CAEA,SAASK,EACPf,EACAa,EACAG,EACAC,EAA4B,GAAK,CAEjC,GAAI,CAAClB,EAAYC,CAAK,EACpB,OAAAa,EAAa,EAEN,CAAE,KAAM,OAAQ,MAAOb,CAAe,EAG/C,IAAIgB,GAAA,YAAAA,EAAM,IAAIhB,MAAW,GACvB,MAAAa,EAAa,EAEP,IAAI,UAAU,4BAA4B,EAGlD,MAAMK,EAAWN,EAAwBZ,EAAOa,CAAa,EAE7D,GAAIK,IAAa,OAAW,MAAO,CAAE,KAAM,OAAQ,MAAOlB,CAAe,EAEzE,MAAMmB,EAAQH,GAAQ,IAAI,IAC1BG,EAAM,IAAInB,CAAK,EACf,KAAM,CAAE,UAAAoB,EAAW,KAAAV,CAAI,EAAKQ,EAC5B,IAAIG,EACJ,MAAMC,EAAU,IAAI,QAA8B,CAAChB,EAASC,IAAU,CACpE,IAAIgB,EAAyC,CAAE,cAAAV,EAAe,MAAAM,EAAO,KAAAT,EAAM,MAAAV,EAAO,0BAAAiB,CAAyB,EACvGO,EAAmB,GACnBC,EAAY,GAChB,MAAMC,EAAU,IAAW,CACzBH,EAAS,MACX,EACMI,EAAqBb,GAAwB,CAC7CW,IAEJA,EAAY,GACZC,EAAO,EACPnB,EAAOO,CAAK,EACd,EACMc,EAAsBC,GAAyB,CAC/CJ,IAEJA,EAAY,GACZC,EAAO,EACPpB,EAAQ,CAAE,MAAOuB,EAAS,EAC5B,EACMC,EAAchB,GAAwB,CACtCU,IAEJA,EAAmB,GACnBG,EAAkBb,CAAK,EACzB,EACMiB,EAAeC,GAAyB,CAC5C,GAAIR,EAAkB,OAEtBA,EAAmB,GACnB,IAAIS,EAEJ,GAAI,CACF,MAAMC,EAAUX,EAEhB,GAAIW,IAAY,OAAW,OAE3BD,EAAalB,EACXiB,EACAE,EAAQ,cACRA,EAAQ,MACRA,EAAQ,yBAAyB,CAErC,OAASpB,EAAO,CACda,EAAkBb,CAAK,EAEvB,MACF,CAEA,GAAImB,EAAW,OAAS,OAAQ,CAC9BL,EAAmBK,EAAW,KAAK,EAEnC,MACF,CAEA,MAAMC,EAAUX,EAEZW,IAAY,SAEhBA,EAAQ,aAAeD,EAAW,OAC7BA,EAAW,QAAQ,KACtB,CAAC,CAAE,MAAOJ,CAAO,IAAOD,EAAmBC,CAAO,EACjDf,GAAmBa,EAAkBb,CAAK,CAAC,EAEhD,EACMqB,EAAqB,CAACD,EAA6BpB,IAAwB,CAC/E,GAAI,CACFoB,EAAQ,cAAa,EACrBJ,EAAWhB,CAAK,CAClB,OAASsB,EAAc,CACrB,GACE,CAAChB,GACDf,EAA4B6B,EAAQ,MAAOH,EAAaD,CAAU,GAClEI,EAAQ,0BAER,OAEFJ,EAAWM,CAAY,CACzB,CACF,EACMC,EAAa,IAAW,CAC5B,MAAMH,EAAUX,EAEhB,GAAI,EAAAE,GAAaS,IAAY,QAE7B,GAAI,CACGd,GAAWc,EAAQ,cAAa,EAErCA,EAAQ,KAAK,KAAKA,EAAQ,MAAOH,EAAaD,CAAU,EAEpDV,GAAa,CAACc,EAAQ,2BAA2BA,EAAQ,cAAa,CAC5E,OAASpB,EAAO,CACdqB,EAAmBD,EAASpB,CAAK,CACnC,CACF,EAEIM,EACFiB,EAAU,EAEV,eAAeA,CAAU,EAG3BhB,EAAUZ,GAAyB,CACjC,GAAIgB,EAAW,OAEfD,EAAmB,GAEnB,MAAMc,EAAef,GAAA,YAAAA,EAAQ,aAEzBe,IAAiB,QAAWA,EAAa7B,CAAM,EAEnDkB,EAAkBlB,CAAM,CAC1B,CACF,CAAC,EAED,MAAO,CAAE,OAAAY,EAAQ,KAAM,QAAS,QAAAC,CAAO,CACzC,CAEA,SAASiB,EACPC,EACAC,EAA4C,CAE5C,IAAIC,EAAS,GACTC,EAEJ,GAAI,CACFH,GAAA,MAAAA,EAAQ,UACV,OAAS1B,EAAO,CACd4B,EAAS,GACTC,EAAU7B,CACZ,CAEA,GAAI,CACF2B,GAAA,MAAAA,GACF,OAAS3B,EAAO,CACd,MAAI4B,EAAcE,EAAqB,CAACD,EAAS7B,CAAK,EAAG,iCAAkC,CAAE,MAAO6B,EAAS,EAEvG7B,CACR,CAEA,GAAI4B,EAAQ,MAAMC,CACpB,CAEA,SAASE,EACP5B,EACA6B,EACAN,EAA0C,CAE1C,OAAOvB,IAA8B,GAAO,QAAauB,GAAA,YAAAA,EAAQ,SAAUM,CAC7E,CAEA,SAASC,EACPd,EACApB,EACAmC,EACAF,EAAoB,CAEpB,GAAIb,EAAW,OAAS,OACtB,OAAAe,EAAQ,EAEDf,EAAW,MAGpB,IAAIgB,EACJ,MAAMC,EAAS,IAAW,CACpBD,IAAkB,QAAaH,IAAW,QAAWA,EAAO,oBAAoB,QAASG,CAAa,EAE1GD,EAAQ,CACV,EACMG,EAAYlB,EAAW,QAAQ,KACnC,CAAC,CAAE,MAAOJ,KAAa,CACrB,GAAI,CACF,OAAAhB,EAAa,EAENgB,CACT,SACEqB,EAAM,CACR,CACF,EACCpC,GAAkB,CACjB,GAAI,CACF,MAAAD,EAAa,EAEPC,CACR,SACEoC,EAAM,CACR,CACF,CAAC,EAGH,OAAIJ,IAAW,SACbG,EAAgB,IAAW,CACzB,GAAI,CACFpC,EAAa,EACbuC,EAAqBN,CAAM,CAC7B,OAASrC,EAAQ,CACfwB,EAAW,OAAOxB,CAAM,CAC1B,CACF,EAEIqC,EAAO,QACTG,EAAa,EAEbH,EAAO,iBAAiB,QAASG,EAAe,CAAE,KAAM,GAAM,GAI3DE,CACT,CAEA,SAASE,EACPnD,EACAC,EAMC,OAED,GAAI,CAACJ,EAAYG,CAAM,EACrB,GAAI,CACF,OAAAC,EAAQ,cAAa,EAEdD,CACT,UACEoD,EAAAnD,EAAQ,WAAR,MAAAmD,EAAA,KAAAnD,EACF,CAGF,MAAMoD,EAAkB3D,EAAiB,IAAIM,CAAM,EAC7CF,EAAQuD,IAAoB,OAAYrD,EAASqD,EAAgB,OACjE1C,EAAgB,IAAW,QAC/ByC,EAAAC,GAAA,YAAAA,EAAiB,gBAAjB,MAAAD,EAAA,KAAAC,GACApD,EAAQ,cAAa,CACvB,EACM6C,EAAW,IAAW,CAC1BT,EAAegB,EAAiBpD,EAAQ,QAAQ,CAClD,EACA,IAAI8B,EAEJ,GAAI,CACFA,EAAalB,EAAoBf,EAAOa,EAAe,OAAWV,EAAQ,yBAAyB,CACrG,OAASW,EAAO,CACd,MAAAkC,EAAQ,EACFlC,CACR,CAEA,OAAOiC,EACLd,EACApB,EACAmC,EACAH,EAAiB1C,EAAQ,0BAA2BA,EAAQ,OAAQoD,CAAe,CAAC,CAExF,CAEA,SAASC,EACPtD,EACAC,EAIC,CAED,MAAMsD,EAAS1C,EAAoBb,EAAQC,EAAQ,aAAa,EAEhE,GAAIsD,EAAO,OAAS,OAAQ,CAC1B,IAAIC,EAEJ,GAAI,CACFvD,EAAQ,cAAa,EACrBuD,EAASvD,EAAQ,IAAIsD,EAAO,KAAK,CACnC,OAAS3C,EAAO,CACd,MAAAX,EAAQ,cAAa,EAEfW,CACR,CAEA,OAAOuC,EAAoBK,EAAQ,CAAE,cAAevD,EAAQ,cAAe,OAAQA,EAAQ,OAAQ,CACrG,CAEA,IAAIwD,EAAgBF,EAAO,OAC3B,MAAMC,EAASD,EAAO,QAAQ,KAAK,CAAC,CAAE,MAAAzD,KAAW,CAC/C,IAAIE,EAEJ,GAAI,CACFC,EAAQ,cAAa,EACrBD,EAASC,EAAQ,IAAIH,CAAK,CAC5B,OAASc,EAAO,CACd,MAAAX,EAAQ,cAAa,EAEfW,CACR,CAEA,MAAMmB,EAAalB,EAAoBb,EAAQC,EAAQ,aAAa,EAEpE,OAAI8B,EAAW,OAAS,UAAS0B,EAAgB1B,EAAW,QAErDA,EAAW,OAAS,OAAS,CAAE,MAAOA,EAAW,OAAUA,EAAW,OAC/E,CAAC,EAED,OAAOc,EACL,CAAE,OAAQtC,GAAUkD,EAAclD,CAAM,EAAG,KAAM,QAAS,QAASiD,CAAM,EACzEvD,EAAQ,cACRR,EACAQ,EAAQ,MAAM,CAElB,CAEA,SAASyD,EACP1D,EACA2D,EACAC,EACAd,EACAF,EAAmB,CAEnB,GAAI,CAAC/C,EAAYG,CAAM,EACrB,GAAI,CACF,OAAA4D,EAAK,EACLD,EAAU,cAAa,EAEhB3D,CACT,SACE8C,EAAQ,CACV,CAGF,OAAOK,EAAoBnD,EAAQ,CACjC,cAAe,IAAK,CAClB4D,EAAK,EACLD,EAAU,cAAa,CACzB,EACA,SAAAb,EACA,OAAAF,CACD,CAAA,CACH"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { CallAuthoritySource } from './call-authority.js';
|
|
2
|
+
import { mapGuardedResult, settleGuardedResult, withCallTargetResultFinalizer } from './call-target-result.js';
|
|
3
|
+
import type { DeclarationId } from './identity.js';
|
|
4
|
+
declare const callTargetBrand: unique symbol;
|
|
5
|
+
declare const callTargetLifetimeBindingBrand: unique symbol;
|
|
6
|
+
interface CallContext {
|
|
7
|
+
readonly signal: AbortSignal;
|
|
8
|
+
}
|
|
9
|
+
interface Call<Input, Output> {
|
|
10
|
+
readonly [callTargetBrand]: (input: Input) => Output;
|
|
11
|
+
readonly id: DeclarationId;
|
|
12
|
+
}
|
|
13
|
+
interface CallTargetLifetimeBinding {
|
|
14
|
+
readonly authoritySource: CallAuthoritySource;
|
|
15
|
+
readonly [callTargetLifetimeBindingBrand]: true;
|
|
16
|
+
readonly lifetimeKey: object;
|
|
17
|
+
readonly origin: object;
|
|
18
|
+
}
|
|
19
|
+
declare function defineCallTarget<Input, Output>(options: {
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly run: (input: Input, context: CallContext) => Output | PromiseLike<Output>;
|
|
22
|
+
}): Call<Input, Output>;
|
|
23
|
+
declare function mintCallTargetWithLifetimeBinding<Input, Output>(options: {
|
|
24
|
+
readonly id: string;
|
|
25
|
+
readonly run: (input: Input, context: CallContext) => Output | PromiseLike<Output>;
|
|
26
|
+
}, binding: CallTargetLifetimeBinding): Call<Input, Output>;
|
|
27
|
+
declare function createCallTargetLifetimeBinding(authoritySource: CallAuthoritySource, origin: object): CallTargetLifetimeBinding;
|
|
28
|
+
declare function isCallTarget(value: unknown): value is Call<unknown, unknown>;
|
|
29
|
+
declare function revokeCallTarget<Input, Output>(target: Call<Input, Output>): boolean;
|
|
30
|
+
declare function invokeCallTargetWithGuard<Input, Output>(target: Call<Input, Output>, input: Input, context: CallContext, guard: () => void): Output | PromiseLike<Output>;
|
|
31
|
+
declare function invokeCallTarget<Input, Output>(target: Call<Input, Output>, input: Input, context: CallContext): Output | PromiseLike<Output>;
|
|
32
|
+
declare function assertCallContextCurrent(context: CallContext): void;
|
|
33
|
+
declare function inheritCallContextLifetime(parentContext: CallContext, context: CallContext): void;
|
|
34
|
+
declare function retireCallContext(context: CallContext): void;
|
|
35
|
+
declare function bindCallTargetLifetime<Input, Output>(target: Call<Input, Output>, binding: CallTargetLifetimeBinding): Call<Input, Output>;
|
|
36
|
+
declare function defineCallTargetWithInheritedLifetime<Input, Output>(source: Call<unknown, unknown>, options: {
|
|
37
|
+
readonly id: string;
|
|
38
|
+
readonly run: (input: Input, context: CallContext) => Output | PromiseLike<Output>;
|
|
39
|
+
}): Call<Input, Output>;
|
|
40
|
+
declare function getCallTargetLifetimeBinding<Input, Output>(target: Call<Input, Output>): CallTargetLifetimeBinding | undefined;
|
|
41
|
+
export { assertCallContextCurrent, bindCallTargetLifetime, createCallTargetLifetimeBinding, defineCallTarget, defineCallTargetWithInheritedLifetime, getCallTargetLifetimeBinding, inheritCallContextLifetime, invokeCallTarget, invokeCallTargetWithGuard, isCallTarget, mapGuardedResult, mintCallTargetWithLifetimeBinding, retireCallContext, revokeCallTarget, settleGuardedResult, withCallTargetResultFinalizer, };
|
|
42
|
+
export type { Call, CallContext, CallTargetLifetimeBinding };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{throwIfSignalAborted as h}from"./abort-compat.js";import{canHaveThen as L,settleGuardedResult as T,settleLifetimeResult as W}from"./call-target-result.js";import{mapGuardedResult as $,withCallTargetResultFinalizer as ee}from"./call-target-result.js";import{declarationId as E}from"./identity.js";const O=Symbol("opetope.call-target"),j=Symbol("opetope.call-target-lifetime-binding"),z=Object.freeze({}),m=new TypeError("Call context is no longer active."),S=()=>{},s=new WeakMap,u=new WeakMap,y=new WeakSet,d=new WeakSet,B=new WeakSet,C=new WeakMap;function g(e,n){const t=e.run,i=Object.freeze({[O]:z,id:E(e.id)}),r=n===void 0?{run:t}:{lifetimeBinding:n,run:t};return s.set(i,Object.freeze(r)),i}function v(e){return g(e)}function K(e,n){if(!y.has(n))throw new TypeError("Call lifetime binding is not authentic.");const t=g(e,n);return B.add(t),t}function x(e,n){const t=Object.freeze({authoritySource:e,[j]:!0,lifetimeKey:Object.freeze({}),origin:n});return y.add(t),t}function M(e){return typeof e=="object"&&e!==null&&s.has(e)}function R(e){const n=e;if(!B.has(n))throw new TypeError("Only a framework-owned Call can be revoked.");return s.delete(n)}function I(e,n){h(n);const t=e.authoritySource.getSnapshot();if(t.kind==="inactive")throw t.reason;return t.authority.assertCurrent(),t.authority}function A(e,n){h(n),e.assertCurrent()}function D(e,n){if(d.has(e))throw m;let t=u.get(e);for(;t!==void 0;){if(t.lifetimeKey===n)return t;t=t.parent}}function G(e,n,t){const i=D(n,e.lifetimeKey);if(i!==void 0)return A(i.authority,t),{authority:i.authority,context:n,release:S};const r=I(e,t),o=u.get(n),a=Object.freeze(o===void 0?{authority:r,lifetimeKey:e.lifetimeKey}:{authority:r,lifetimeKey:e.lifetimeKey,parent:o}),l=Object.freeze({signal:t});return u.set(l,a),{authority:r,context:l,release(){u.delete(l),d.add(l)}}}function b(e,n,t,i){const r=s.get(e);if(r===void 0)throw new TypeError("Call is not authentic.");const o=t.signal;i();const a=r,l=a.lifetimeBinding;if(l===void 0){let c;try{c=a.run(n,t)}catch(k){throw i(),k}return L(c)?T(c,{assertCurrent:i,signal:o}):(i(),c)}const f=G(l,t,o),w=f.authority;let p;try{p=a.run(n,f.context)}catch(c){try{throw i(),w.assertCurrent(),c}finally{f.release()}}return W(p,w,i,f.release,o)}function F(e,n,t){return b(e,n,t,()=>h(t.signal))}function H(e){if(d.has(e))throw m;h(e.signal)}function q(e,n){if(d.has(e)||d.has(n))throw m;const t=u.get(e);t!==void 0&&u.set(n,t)}function J(e){u.delete(e),d.add(e)}function N(e){const n=C.get(e);if(n!==void 0)return n;const t=new WeakMap;return C.set(e,t),t}function P(e,n){var f;if(!y.has(n))throw new TypeError("Call lifetime binding is not authentic.");const t=s.get(e);if(t===void 0)throw new TypeError("Call lifetime source is not authentic.");const i=t.lifetimeBinding;if(i!==void 0){if(i===n)return e;throw new TypeError("Call is already bound to another lifetime origin.")}const r=(f=C.get(e))==null?void 0:f.get(n);if(r!==void 0)return r;const o=t,a=g({id:e.id,run:o.run},n);return N(e).set(n,a),a}function Q(e,n){const t=s.get(e);if(t===void 0)throw new TypeError("Call adapter source is not authentic.");return g(n,t.lifetimeBinding)}function U(e){const n=s.get(e);if(n===void 0)throw new TypeError("Call is not authentic.");return n.lifetimeBinding}export{H as assertCallContextCurrent,P as bindCallTargetLifetime,x as createCallTargetLifetimeBinding,v as defineCallTarget,Q as defineCallTargetWithInheritedLifetime,U as getCallTargetLifetimeBinding,q as inheritCallContextLifetime,F as invokeCallTarget,b as invokeCallTargetWithGuard,M as isCallTarget,$ as mapGuardedResult,K as mintCallTargetWithLifetimeBinding,J as retireCallContext,R as revokeCallTarget,T as settleGuardedResult,ee as withCallTargetResultFinalizer};
|
|
2
|
+
//# sourceMappingURL=call-target.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-target.js","sources":["../src/call-target.ts"],"sourcesContent":["import { throwIfSignalAborted } from './abort-compat';\nimport type { CallAuthority, CallAuthoritySource } from './call-authority';\nimport {\n canHaveThen,\n mapGuardedResult,\n settleGuardedResult,\n settleLifetimeResult,\n withCallTargetResultFinalizer,\n} from './call-target-result';\nimport { declarationId } from './identity';\nimport type { DeclarationId } from './identity';\n\nconst callTargetBrand = Symbol('opetope.call-target');\nconst callTargetLifetimeBindingBrand = Symbol('opetope.call-target-lifetime-binding');\nconst callTargetMarker = Object.freeze({});\nconst retiredCallContextReason = new TypeError('Call context is no longer active.');\nconst noop = (): void => undefined;\n\ninterface CallContext {\n readonly signal: AbortSignal;\n}\n\ntype CallContextLifetime = {\n readonly authority: CallAuthority;\n readonly lifetimeKey: object;\n readonly parent?: CallContextLifetime;\n};\n\ninterface Call<Input, Output> {\n readonly [callTargetBrand]: (input: Input) => Output;\n readonly id: DeclarationId;\n}\n\ntype CallTargetDefinition<Input, Output> = {\n readonly lifetimeBinding?: CallTargetLifetimeBinding;\n readonly run: (input: Input, context: CallContext) => Output | PromiseLike<Output>;\n};\n\ninterface CallTargetLifetimeBinding {\n readonly authoritySource: CallAuthoritySource;\n readonly [callTargetLifetimeBindingBrand]: true;\n readonly lifetimeKey: object;\n readonly origin: object;\n}\n\nconst definitions = new WeakMap<Call<unknown, unknown>, CallTargetDefinition<unknown, unknown>>();\nconst contextLifetimes = new WeakMap<CallContext, CallContextLifetime>();\nconst lifetimeBindings = new WeakSet<CallTargetLifetimeBinding>();\nconst retiredCallContexts = new WeakSet<CallContext>();\nconst revocableCallTargets = new WeakSet<Call<unknown, unknown>>();\nconst boundTargetCache = new WeakMap<\n Call<unknown, unknown>,\n WeakMap<CallTargetLifetimeBinding, Call<unknown, unknown>>\n>();\n\nfunction createCallTarget<Input, Output>(\n options: {\n readonly id: string;\n readonly run: (input: Input, context: CallContext) => Output | PromiseLike<Output>;\n },\n lifetimeBinding?: CallTargetLifetimeBinding,\n): Call<Input, Output> {\n const run = options.run;\n const target = Object.freeze({\n [callTargetBrand]: callTargetMarker as unknown as (input: Input) => Output,\n id: declarationId(options.id),\n });\n const definition: CallTargetDefinition<Input, Output> =\n lifetimeBinding === undefined ? { run } : { lifetimeBinding, run };\n\n definitions.set(\n target as Call<unknown, unknown>,\n Object.freeze(definition) as CallTargetDefinition<unknown, unknown>,\n );\n\n return target;\n}\n\nfunction defineCallTarget<Input, Output>(options: {\n readonly id: string;\n readonly run: (input: Input, context: CallContext) => Output | PromiseLike<Output>;\n}): Call<Input, Output> {\n return createCallTarget(options);\n}\n\nfunction mintCallTargetWithLifetimeBinding<Input, Output>(\n options: {\n readonly id: string;\n readonly run: (input: Input, context: CallContext) => Output | PromiseLike<Output>;\n },\n binding: CallTargetLifetimeBinding,\n): Call<Input, Output> {\n if (!lifetimeBindings.has(binding)) throw new TypeError('Call lifetime binding is not authentic.');\n\n const target = createCallTarget(options, binding);\n\n revocableCallTargets.add(target as Call<unknown, unknown>);\n\n return target;\n}\n\nfunction createCallTargetLifetimeBinding(\n authoritySource: CallAuthoritySource,\n origin: object,\n): CallTargetLifetimeBinding {\n const binding = Object.freeze({\n authoritySource,\n [callTargetLifetimeBindingBrand]: true as const,\n lifetimeKey: Object.freeze({}),\n origin,\n });\n lifetimeBindings.add(binding);\n\n return binding;\n}\n\nfunction isCallTarget(value: unknown): value is Call<unknown, unknown> {\n return typeof value === 'object' && value !== null && definitions.has(value as Call<unknown, unknown>);\n}\n\nfunction revokeCallTarget<Input, Output>(target: Call<Input, Output>): boolean {\n const erased = target as Call<unknown, unknown>;\n\n if (!revocableCallTargets.has(erased)) {\n throw new TypeError('Only a framework-owned Call can be revoked.');\n }\n\n return definitions.delete(erased);\n}\n\nfunction captureLifetimeAuthority(binding: CallTargetLifetimeBinding, signal: AbortSignal): CallAuthority {\n throwIfSignalAborted(signal);\n const snapshot = binding.authoritySource.getSnapshot();\n\n if (snapshot.kind === 'inactive') throw snapshot.reason;\n\n snapshot.authority.assertCurrent();\n\n return snapshot.authority;\n}\n\nfunction assertAuthorityCurrent(authority: CallAuthority, signal: AbortSignal): void {\n throwIfSignalAborted(signal);\n authority.assertCurrent();\n}\n\nfunction findContextLifetime(context: CallContext, lifetimeKey: object): CallContextLifetime | undefined {\n if (retiredCallContexts.has(context)) throw retiredCallContextReason;\n\n let lifetime = contextLifetimes.get(context);\n\n while (lifetime !== undefined) {\n if (lifetime.lifetimeKey === lifetimeKey) return lifetime;\n\n lifetime = lifetime.parent;\n }\n\n return undefined;\n}\n\nfunction captureInvocationLifetime(\n binding: CallTargetLifetimeBinding,\n context: CallContext,\n signal: AbortSignal,\n): { readonly authority: CallAuthority; readonly context: CallContext; readonly release: () => void } {\n const inherited = findContextLifetime(context, binding.lifetimeKey);\n\n if (inherited !== undefined) {\n assertAuthorityCurrent(inherited.authority, signal);\n\n return { authority: inherited.authority, context, release: noop };\n }\n\n const authority = captureLifetimeAuthority(binding, signal);\n const parent = contextLifetimes.get(context);\n const lifetime: CallContextLifetime = Object.freeze(\n parent === undefined\n ? { authority, lifetimeKey: binding.lifetimeKey }\n : { authority, lifetimeKey: binding.lifetimeKey, parent },\n );\n const nestedContext: CallContext = Object.freeze({ signal });\n contextLifetimes.set(nestedContext, lifetime);\n\n return {\n authority,\n context: nestedContext,\n release(): void {\n contextLifetimes.delete(nestedContext);\n retiredCallContexts.add(nestedContext);\n },\n };\n}\n\nfunction invokeCallTargetWithGuard<Input, Output>(\n target: Call<Input, Output>,\n input: Input,\n context: CallContext,\n guard: () => void,\n): Output | PromiseLike<Output> {\n const definition = definitions.get(target as Call<unknown, unknown>);\n\n if (definition === undefined) throw new TypeError('Call is not authentic.');\n\n const signal = context.signal;\n guard();\n\n const typedDefinition = definition as CallTargetDefinition<Input, Output>;\n const binding = typedDefinition.lifetimeBinding;\n\n if (binding === undefined) {\n let result: Output | PromiseLike<Output>;\n\n try {\n result = typedDefinition.run(input, context);\n } catch (error) {\n guard();\n throw error;\n }\n\n if (!canHaveThen(result)) {\n guard();\n\n return result;\n }\n\n return settleGuardedResult(result, { assertCurrent: guard, signal });\n }\n\n const lifetime = captureInvocationLifetime(binding, context, signal);\n const authority = lifetime.authority;\n let result: Output | PromiseLike<Output>;\n\n try {\n result = typedDefinition.run(input, lifetime.context);\n } catch (error) {\n try {\n guard();\n authority.assertCurrent();\n\n throw error;\n } finally {\n lifetime.release();\n }\n }\n\n return settleLifetimeResult(result, authority, guard, lifetime.release, signal);\n}\n\nfunction invokeCallTarget<Input, Output>(\n target: Call<Input, Output>,\n input: Input,\n context: CallContext,\n): Output | PromiseLike<Output> {\n return invokeCallTargetWithGuard(target, input, context, () => throwIfSignalAborted(context.signal));\n}\n\nfunction assertCallContextCurrent(context: CallContext): void {\n if (retiredCallContexts.has(context)) throw retiredCallContextReason;\n\n throwIfSignalAborted(context.signal);\n}\n\nfunction inheritCallContextLifetime(parentContext: CallContext, context: CallContext): void {\n if (retiredCallContexts.has(parentContext)) throw retiredCallContextReason;\n\n if (retiredCallContexts.has(context)) throw retiredCallContextReason;\n\n const parentLifetime = contextLifetimes.get(parentContext);\n\n if (parentLifetime !== undefined) contextLifetimes.set(context, parentLifetime);\n}\n\nfunction retireCallContext(context: CallContext): void {\n contextLifetimes.delete(context);\n retiredCallContexts.add(context);\n}\n\nfunction getOrCreateTargetsByBinding(\n target: Call<unknown, unknown>,\n): WeakMap<CallTargetLifetimeBinding, Call<unknown, unknown>> {\n const existing = boundTargetCache.get(target);\n\n if (existing !== undefined) return existing;\n\n const created = new WeakMap<CallTargetLifetimeBinding, Call<unknown, unknown>>();\n boundTargetCache.set(target, created);\n\n return created;\n}\n\nfunction bindCallTargetLifetime<Input, Output>(\n target: Call<Input, Output>,\n binding: CallTargetLifetimeBinding,\n): Call<Input, Output> {\n if (!lifetimeBindings.has(binding)) throw new TypeError('Call lifetime binding is not authentic.');\n\n const definition = definitions.get(target as Call<unknown, unknown>);\n\n if (definition === undefined) throw new TypeError('Call lifetime source is not authentic.');\n\n const existing = definition.lifetimeBinding;\n\n if (existing !== undefined) {\n if (existing === binding) return target;\n\n throw new TypeError('Call is already bound to another lifetime origin.');\n }\n\n const cached = boundTargetCache.get(target as Call<unknown, unknown>)?.get(binding);\n\n if (cached !== undefined) return cached as Call<Input, Output>;\n\n const typedDefinition = definition as CallTargetDefinition<Input, Output>;\n const bound = createCallTarget({ id: target.id, run: typedDefinition.run }, binding);\n const targetsByBinding = getOrCreateTargetsByBinding(target as Call<unknown, unknown>);\n\n targetsByBinding.set(binding, bound as Call<unknown, unknown>);\n\n return bound;\n}\n\nfunction defineCallTargetWithInheritedLifetime<Input, Output>(\n source: Call<unknown, unknown>,\n options: {\n readonly id: string;\n readonly run: (input: Input, context: CallContext) => Output | PromiseLike<Output>;\n },\n): Call<Input, Output> {\n const sourceDefinition = definitions.get(source);\n\n if (sourceDefinition === undefined) throw new TypeError('Call adapter source is not authentic.');\n\n return createCallTarget(options, sourceDefinition.lifetimeBinding);\n}\n\nfunction getCallTargetLifetimeBinding<Input, Output>(\n target: Call<Input, Output>,\n): CallTargetLifetimeBinding | undefined {\n const definition = definitions.get(target as Call<unknown, unknown>);\n\n if (definition === undefined) throw new TypeError('Call is not authentic.');\n\n return definition.lifetimeBinding;\n}\n\nexport {\n assertCallContextCurrent,\n bindCallTargetLifetime,\n createCallTargetLifetimeBinding,\n defineCallTarget,\n defineCallTargetWithInheritedLifetime,\n getCallTargetLifetimeBinding,\n inheritCallContextLifetime,\n invokeCallTarget,\n invokeCallTargetWithGuard,\n isCallTarget,\n mapGuardedResult,\n mintCallTargetWithLifetimeBinding,\n retireCallContext,\n revokeCallTarget,\n settleGuardedResult,\n withCallTargetResultFinalizer,\n};\nexport type { Call, CallContext, CallTargetLifetimeBinding };\n"],"names":["callTargetBrand","callTargetLifetimeBindingBrand","callTargetMarker","retiredCallContextReason","noop","definitions","contextLifetimes","lifetimeBindings","retiredCallContexts","revocableCallTargets","boundTargetCache","createCallTarget","options","lifetimeBinding","run","target","declarationId","definition","defineCallTarget","mintCallTargetWithLifetimeBinding","binding","createCallTargetLifetimeBinding","authoritySource","origin","isCallTarget","value","revokeCallTarget","erased","captureLifetimeAuthority","signal","throwIfSignalAborted","snapshot","assertAuthorityCurrent","authority","findContextLifetime","context","lifetimeKey","lifetime","captureInvocationLifetime","inherited","parent","nestedContext","invokeCallTargetWithGuard","input","guard","typedDefinition","result","error","canHaveThen","settleGuardedResult","settleLifetimeResult","invokeCallTarget","assertCallContextCurrent","inheritCallContextLifetime","parentContext","parentLifetime","retireCallContext","getOrCreateTargetsByBinding","existing","created","bindCallTargetLifetime","cached","_a","bound","defineCallTargetWithInheritedLifetime","source","sourceDefinition","getCallTargetLifetimeBinding"],"mappings":"+SAYA,MAAMA,EAAkB,OAAO,qBAAqB,EAC9CC,EAAiC,OAAO,sCAAsC,EAC9EC,EAAmB,OAAO,OAAO,EAAE,EACnCC,EAA2B,IAAI,UAAU,mCAAmC,EAC5EC,EAAO,IAAA,GA6BPC,EAAc,IAAI,QAClBC,EAAmB,IAAI,QACvBC,EAAmB,IAAI,QACvBC,EAAsB,IAAI,QAC1BC,EAAuB,IAAI,QAC3BC,EAAmB,IAAI,QAK7B,SAASC,EACPC,EAIAC,EAA2C,CAE3C,MAAMC,EAAMF,EAAQ,IACdG,EAAS,OAAO,OAAO,CAC3B,CAACf,CAAe,EAAGE,EACnB,GAAIc,EAAcJ,EAAQ,EAAE,CAC7B,CAAA,EACKK,EACJJ,IAAoB,OAAY,CAAE,IAAAC,GAAQ,CAAE,gBAAAD,EAAiB,IAAAC,CAAG,EAElE,OAAAT,EAAY,IACVU,EACA,OAAO,OAAOE,CAAU,CAA2C,EAG9DF,CACT,CAEA,SAASG,EAAgCN,EAGxC,CACC,OAAOD,EAAiBC,CAAO,CACjC,CAEA,SAASO,EACPP,EAIAQ,EAAkC,CAElC,GAAI,CAACb,EAAiB,IAAIa,CAAO,EAAG,MAAM,IAAI,UAAU,yCAAyC,EAEjG,MAAML,EAASJ,EAAiBC,EAASQ,CAAO,EAEhD,OAAAX,EAAqB,IAAIM,CAAgC,EAElDA,CACT,CAEA,SAASM,EACPC,EACAC,EAAc,CAEd,MAAMH,EAAU,OAAO,OAAO,CAC5B,gBAAAE,EACA,CAACrB,CAA8B,EAAG,GAClC,YAAa,OAAO,OAAO,EAAE,EAC7B,OAAAsB,CACD,CAAA,EACD,OAAAhB,EAAiB,IAAIa,CAAO,EAErBA,CACT,CAEA,SAASI,EAAaC,EAAc,CAClC,OAAO,OAAOA,GAAU,UAAYA,IAAU,MAAQpB,EAAY,IAAIoB,CAA+B,CACvG,CAEA,SAASC,EAAgCX,EAA2B,CAClE,MAAMY,EAASZ,EAEf,GAAI,CAACN,EAAqB,IAAIkB,CAAM,EAClC,MAAM,IAAI,UAAU,6CAA6C,EAGnE,OAAOtB,EAAY,OAAOsB,CAAM,CAClC,CAEA,SAASC,EAAyBR,EAAoCS,EAAmB,CACvFC,EAAqBD,CAAM,EAC3B,MAAME,EAAWX,EAAQ,gBAAgB,YAAW,EAEpD,GAAIW,EAAS,OAAS,WAAY,MAAMA,EAAS,OAEjD,OAAAA,EAAS,UAAU,cAAa,EAEzBA,EAAS,SAClB,CAEA,SAASC,EAAuBC,EAA0BJ,EAAmB,CAC3EC,EAAqBD,CAAM,EAC3BI,EAAU,cAAa,CACzB,CAEA,SAASC,EAAoBC,EAAsBC,EAAmB,CACpE,GAAI5B,EAAoB,IAAI2B,CAAO,EAAG,MAAMhC,EAE5C,IAAIkC,EAAW/B,EAAiB,IAAI6B,CAAO,EAE3C,KAAOE,IAAa,QAAW,CAC7B,GAAIA,EAAS,cAAgBD,EAAa,OAAOC,EAEjDA,EAAWA,EAAS,MACtB,CAGF,CAEA,SAASC,EACPlB,EACAe,EACAN,EAAmB,CAEnB,MAAMU,EAAYL,EAAoBC,EAASf,EAAQ,WAAW,EAElE,GAAImB,IAAc,OAChB,OAAAP,EAAuBO,EAAU,UAAWV,CAAM,EAE3C,CAAE,UAAWU,EAAU,UAAW,QAAAJ,EAAS,QAAS/B,CAAI,EAGjE,MAAM6B,EAAYL,EAAyBR,EAASS,CAAM,EACpDW,EAASlC,EAAiB,IAAI6B,CAAO,EACrCE,EAAgC,OAAO,OAC3CG,IAAW,OACP,CAAE,UAAAP,EAAW,YAAab,EAAQ,WAAW,EAC7C,CAAE,UAAAa,EAAW,YAAab,EAAQ,YAAa,OAAAoB,CAAM,CAAE,EAEvDC,EAA6B,OAAO,OAAO,CAAE,OAAAZ,CAAM,CAAE,EAC3D,OAAAvB,EAAiB,IAAImC,EAAeJ,CAAQ,EAErC,CACL,UAAAJ,EACA,QAASQ,EACT,SAAO,CACLnC,EAAiB,OAAOmC,CAAa,EACrCjC,EAAoB,IAAIiC,CAAa,CACvC,EAEJ,CAEA,SAASC,EACP3B,EACA4B,EACAR,EACAS,EAAiB,CAEjB,MAAM3B,EAAaZ,EAAY,IAAIU,CAAgC,EAEnE,GAAIE,IAAe,OAAW,MAAM,IAAI,UAAU,wBAAwB,EAE1E,MAAMY,EAASM,EAAQ,OACvBS,EAAK,EAEL,MAAMC,EAAkB5B,EAClBG,EAAUyB,EAAgB,gBAEhC,GAAIzB,IAAY,OAAW,CACzB,IAAI0B,EAEJ,GAAI,CACFA,EAASD,EAAgB,IAAIF,EAAOR,CAAO,CAC7C,OAASY,EAAO,CACd,MAAAH,EAAK,EACCG,CACR,CAEA,OAAKC,EAAYF,CAAM,EAMhBG,EAAoBH,EAAQ,CAAE,cAAeF,EAAO,OAAAf,CAAM,CAAE,GALjEe,EAAK,EAEEE,EAIX,CAEA,MAAMT,EAAWC,EAA0BlB,EAASe,EAASN,CAAM,EAC7DI,EAAYI,EAAS,UAC3B,IAAIS,EAEJ,GAAI,CACFA,EAASD,EAAgB,IAAIF,EAAON,EAAS,OAAO,CACtD,OAASU,EAAO,CACd,GAAI,CACF,MAAAH,EAAK,EACLX,EAAU,cAAa,EAEjBc,CACR,SACEV,EAAS,QAAO,CAClB,CACF,CAEA,OAAOa,EAAqBJ,EAAQb,EAAWW,EAAOP,EAAS,QAASR,CAAM,CAChF,CAEA,SAASsB,EACPpC,EACA4B,EACAR,EAAoB,CAEpB,OAAOO,EAA0B3B,EAAQ4B,EAAOR,EAAS,IAAML,EAAqBK,EAAQ,MAAM,CAAC,CACrG,CAEA,SAASiB,EAAyBjB,EAAoB,CACpD,GAAI3B,EAAoB,IAAI2B,CAAO,EAAG,MAAMhC,EAE5C2B,EAAqBK,EAAQ,MAAM,CACrC,CAEA,SAASkB,EAA2BC,EAA4BnB,EAAoB,CAGlF,GAFI3B,EAAoB,IAAI8C,CAAa,GAErC9C,EAAoB,IAAI2B,CAAO,EAAG,MAAMhC,EAE5C,MAAMoD,EAAiBjD,EAAiB,IAAIgD,CAAa,EAErDC,IAAmB,QAAWjD,EAAiB,IAAI6B,EAASoB,CAAc,CAChF,CAEA,SAASC,EAAkBrB,EAAoB,CAC7C7B,EAAiB,OAAO6B,CAAO,EAC/B3B,EAAoB,IAAI2B,CAAO,CACjC,CAEA,SAASsB,EACP1C,EAA8B,CAE9B,MAAM2C,EAAWhD,EAAiB,IAAIK,CAAM,EAE5C,GAAI2C,IAAa,OAAW,OAAOA,EAEnC,MAAMC,EAAU,IAAI,QACpB,OAAAjD,EAAiB,IAAIK,EAAQ4C,CAAO,EAE7BA,CACT,CAEA,SAASC,EACP7C,EACAK,EAAkC,OAElC,GAAI,CAACb,EAAiB,IAAIa,CAAO,EAAG,MAAM,IAAI,UAAU,yCAAyC,EAEjG,MAAMH,EAAaZ,EAAY,IAAIU,CAAgC,EAEnE,GAAIE,IAAe,OAAW,MAAM,IAAI,UAAU,wCAAwC,EAE1F,MAAMyC,EAAWzC,EAAW,gBAE5B,GAAIyC,IAAa,OAAW,CAC1B,GAAIA,IAAatC,EAAS,OAAOL,EAEjC,MAAM,IAAI,UAAU,mDAAmD,CACzE,CAEA,MAAM8C,GAASC,EAAApD,EAAiB,IAAIK,CAAgC,IAArD,YAAA+C,EAAwD,IAAI1C,GAE3E,GAAIyC,IAAW,OAAW,OAAOA,EAEjC,MAAMhB,EAAkB5B,EAClB8C,EAAQpD,EAAiB,CAAE,GAAII,EAAO,GAAI,IAAK8B,EAAgB,GAAG,EAAIzB,CAAO,EAGnF,OAFyBqC,EAA4B1C,CAAgC,EAEpE,IAAIK,EAAS2C,CAA+B,EAEtDA,CACT,CAEA,SAASC,EACPC,EACArD,EAGC,CAED,MAAMsD,EAAmB7D,EAAY,IAAI4D,CAAM,EAE/C,GAAIC,IAAqB,OAAW,MAAM,IAAI,UAAU,uCAAuC,EAE/F,OAAOvD,EAAiBC,EAASsD,EAAiB,eAAe,CACnE,CAEA,SAASC,EACPpD,EAA2B,CAE3B,MAAME,EAAaZ,EAAY,IAAIU,CAAgC,EAEnE,GAAIE,IAAe,OAAW,MAAM,IAAI,UAAU,wBAAwB,EAE1E,OAAOA,EAAW,eACpB"}
|
package/dist/call.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CallController, CallControllerOptions, CallDiagnostics, CallRunOptions, NoExtraKeys } from './call-controller-support.js';
|
|
2
|
+
import type { Call } from './call-target.js';
|
|
3
|
+
declare function createCallController<Input, Output, const Options extends CallControllerOptions = CallControllerOptions>(target: Call<Input, Output>, options: CallControllerOptions & NoExtraKeys<Options, CallControllerOptions> & Options): CallController<Input, Output>;
|
|
4
|
+
declare function mapCallTarget<Input, SourceInput, SourceOutput, Output>(options: {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly input: (input: Input) => SourceInput;
|
|
7
|
+
readonly output: (output: SourceOutput) => Output | PromiseLike<Output>;
|
|
8
|
+
readonly target: Call<SourceInput, SourceOutput>;
|
|
9
|
+
}): Call<Input, Output>;
|
|
10
|
+
export { createCallController, mapCallTarget };
|
|
11
|
+
export type { CallController, CallControllerOptions, CallDiagnostics, CallRunOptions };
|
package/dist/call.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{throwIfSignalAborted as o}from"./abort-compat.js";import{CallControllerImpl as u}from"./call-controller.js";import{assertAuthenticCallTarget as m}from"./call-controller-support.js";import{defineCallTargetWithInheritedLifetime as s,invokeCallTarget as p}from"./call-target.js";import{mapGuardedResult as f}from"./call-target-result.js";function c(r,e){if(Reflect.ownKeys(e).some(t=>t!=="captureAuthority"&&t!=="id"&&t!=="scheduling"))throw new TypeError("Call lifetime authority is carried by its target; extra controller options are forbidden.");return Object.preventExtensions(new u(r,e))}function g(r){m(r.target);const e=r.input,t=r.output,i=r.target;return s(i,{id:r.id,run(l,a){const n=p(i,e(l),a);return Promise.resolve(f(n,{assertCurrent:()=>o(a.signal),map:t,signal:a.signal}))}})}export{c as createCallController,g as mapCallTarget};
|
|
2
|
+
//# sourceMappingURL=call.js.map
|
package/dist/call.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call.js","sources":["../src/call.ts"],"sourcesContent":["import { throwIfSignalAborted } from './abort-compat';\nimport { CallControllerImpl } from './call-controller';\nimport { assertAuthenticCallTarget } from './call-controller-support';\nimport type {\n CallController,\n CallControllerOptions,\n CallDiagnostics,\n CallRunOptions,\n NoExtraKeys,\n} from './call-controller-support';\nimport { defineCallTargetWithInheritedLifetime, invokeCallTarget, mapGuardedResult } from './call-target';\nimport type { Call } from './call-target';\n\nfunction createCallController<Input, Output, const Options extends CallControllerOptions = CallControllerOptions>(\n target: Call<Input, Output>,\n options: CallControllerOptions & NoExtraKeys<Options, CallControllerOptions> & Options,\n): CallController<Input, Output> {\n if (Reflect.ownKeys(options).some(key => key !== 'captureAuthority' && key !== 'id' && key !== 'scheduling')) {\n throw new TypeError('Call lifetime authority is carried by its target; extra controller options are forbidden.');\n }\n\n // The controller mutates its own state on the hot call path, so its fields are TS `private` and the instance is\n // sealed instead of frozen: nobody can bolt a property onto it, and no write goes through a WeakMap (D148).\n return Object.preventExtensions(new CallControllerImpl(target, options));\n}\n\nfunction mapCallTarget<Input, SourceInput, SourceOutput, Output>(options: {\n readonly id: string;\n readonly input: (input: Input) => SourceInput;\n readonly output: (output: SourceOutput) => Output | PromiseLike<Output>;\n readonly target: Call<SourceInput, SourceOutput>;\n}): Call<Input, Output> {\n assertAuthenticCallTarget(options.target);\n\n const mapInput = options.input;\n const mapOutput = options.output;\n const target = options.target;\n\n return defineCallTargetWithInheritedLifetime(target as Call<unknown, unknown>, {\n id: options.id,\n run(input, context) {\n const sourceOutput = invokeCallTarget(target, mapInput(input), context);\n\n return Promise.resolve(\n mapGuardedResult(sourceOutput, {\n assertCurrent: () => throwIfSignalAborted(context.signal),\n map: mapOutput,\n signal: context.signal,\n }),\n );\n },\n });\n}\n\nexport { createCallController, mapCallTarget };\nexport type { CallController, CallControllerOptions, CallDiagnostics, CallRunOptions };\n"],"names":["createCallController","target","options","key","CallControllerImpl","mapCallTarget","assertAuthenticCallTarget","mapInput","mapOutput","defineCallTargetWithInheritedLifetime","input","context","sourceOutput","invokeCallTarget","mapGuardedResult","throwIfSignalAborted"],"mappings":"sVAaA,SAASA,EACPC,EACAC,EAAsF,CAEtF,GAAI,QAAQ,QAAQA,CAAO,EAAE,KAAKC,GAAOA,IAAQ,oBAAsBA,IAAQ,MAAQA,IAAQ,YAAY,EACzG,MAAM,IAAI,UAAU,2FAA2F,EAKjH,OAAO,OAAO,kBAAkB,IAAIC,EAAmBH,EAAQC,CAAO,CAAC,CACzE,CAEA,SAASG,EAAwDH,EAKhE,CACCI,EAA0BJ,EAAQ,MAAM,EAExC,MAAMK,EAAWL,EAAQ,MACnBM,EAAYN,EAAQ,OACpBD,EAASC,EAAQ,OAEvB,OAAOO,EAAsCR,EAAkC,CAC7E,GAAIC,EAAQ,GACZ,IAAIQ,EAAOC,EAAO,CAChB,MAAMC,EAAeC,EAAiBZ,EAAQM,EAASG,CAAK,EAAGC,CAAO,EAEtE,OAAO,QAAQ,QACbG,EAAiBF,EAAc,CAC7B,cAAe,IAAMG,EAAqBJ,EAAQ,MAAM,EACxD,IAAKH,EACL,OAAQG,EAAQ,MACjB,CAAA,CAAC,CAEN,CACD,CAAA,CACH"}
|