@getuserfeedback/protocol 0.6.0 → 0.7.2
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/dist/app-event.d.ts +659 -17
- package/dist/app-event.d.ts.map +1 -1
- package/dist/app-event.js +71 -8
- package/dist/errors.d.ts +73 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +43 -0
- package/dist/flow-assignments.d.ts +83 -4
- package/dist/flow-assignments.d.ts.map +1 -1
- package/dist/flow-assignments.js +1 -0
- package/dist/host/constants.d.ts +2 -2
- package/dist/host/constants.d.ts.map +1 -1
- package/dist/host/constants.js +3 -1
- package/dist/host/sdk-types.d.ts +73 -28
- package/dist/host/sdk-types.d.ts.map +1 -1
- package/dist/host/sdk.d.ts.map +1 -1
- package/dist/identity-type.d.ts +4 -2
- package/dist/identity-type.d.ts.map +1 -1
- package/dist/identity-type.js +4 -2
- package/dist/index.d.ts +4 -86
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -6
- package/dist/widget-commands.d.ts +102 -3
- package/dist/widget-commands.d.ts.map +1 -1
- package/dist/widget-commands.js +5 -0
- package/dist/widget-config.d.ts +5 -5
- package/package.json +22 -2
- package/src/app-event.ts +211 -0
- package/src/client-meta.ts +25 -0
- package/src/defaults.ts +4 -0
- package/src/errors.ts +58 -0
- package/src/flow-assignments.test.ts +63 -0
- package/src/flow-assignments.ts +125 -0
- package/src/host/command-dispatch.ts +59 -0
- package/src/host/command-envelope.ts +41 -0
- package/src/host/command-settlement.ts +121 -0
- package/src/host/constants.ts +103 -0
- package/src/host/host-event-contract.ts +277 -0
- package/src/host/index.ts +13 -0
- package/src/host/lazy-handle-client.ts +207 -0
- package/src/host/request-id.ts +3 -0
- package/src/host/sdk-types.ts +506 -0
- package/src/host/sdk.ts +43 -0
- package/src/host/unique-id.ts +17 -0
- package/src/identity-type.ts +102 -0
- package/src/index.ts +136 -0
- package/src/protocol-root.test.ts +476 -0
- package/src/public-grant-scope.ts +25 -0
- package/src/runtime-endpoints.ts +69 -0
- package/src/scopes.ts +79 -0
- package/src/trpc-envelope.ts +9 -0
- package/src/version-resolution.ts +18 -0
- package/src/widget-commands.ts +152 -0
- package/src/widget-config.ts +157 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types and type guards for flow state and lifecycle events.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/** Current state of the flow: whether it is open, opening, and its dimensions (if known). */
|
|
6
|
+
export interface FlowState {
|
|
7
|
+
/** True when the flow is visible. */
|
|
8
|
+
isOpen: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* True after an open request while the flow is not visible yet. Background
|
|
11
|
+
* prefetching or prerendering without open intent must not set this.
|
|
12
|
+
*/
|
|
13
|
+
isLoading: boolean;
|
|
14
|
+
/** Flow width in pixels when known. */
|
|
15
|
+
width?: number;
|
|
16
|
+
/** Flow height in pixels when known. */
|
|
17
|
+
height?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface FlowStateChangedDetail extends FlowState {
|
|
21
|
+
instanceId: string;
|
|
22
|
+
flowHandleId: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface InstanceFlowStateChangedDetail extends FlowState {
|
|
26
|
+
instanceId: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface OpenRequestedDetail {
|
|
30
|
+
instanceId: string;
|
|
31
|
+
source: "command" | "targeting";
|
|
32
|
+
flowId: string;
|
|
33
|
+
flowHandleId?: string;
|
|
34
|
+
hideCloseButton?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const HANDLE_INVALIDATED_REASON_CODES: readonly [
|
|
38
|
+
"RESET",
|
|
39
|
+
"CLOSED",
|
|
40
|
+
"STALE_HANDLE",
|
|
41
|
+
"OWNERSHIP_CONFLICT",
|
|
42
|
+
"OWNER_DISPOSED",
|
|
43
|
+
"UPSTREAM_INVALIDATED",
|
|
44
|
+
"INTERNAL",
|
|
45
|
+
] = [
|
|
46
|
+
"RESET",
|
|
47
|
+
"CLOSED",
|
|
48
|
+
"STALE_HANDLE",
|
|
49
|
+
"OWNERSHIP_CONFLICT",
|
|
50
|
+
"OWNER_DISPOSED",
|
|
51
|
+
"UPSTREAM_INVALIDATED",
|
|
52
|
+
"INTERNAL",
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
export type HandleInvalidatedReasonCode =
|
|
56
|
+
(typeof HANDLE_INVALIDATED_REASON_CODES)[number];
|
|
57
|
+
|
|
58
|
+
export const HANDLE_INVALIDATED_SOURCES: readonly ["loader", "core"] = [
|
|
59
|
+
"loader",
|
|
60
|
+
"core",
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
export type HandleInvalidatedSource =
|
|
64
|
+
(typeof HANDLE_INVALIDATED_SOURCES)[number];
|
|
65
|
+
|
|
66
|
+
/** Generic handle lifecycle event emitted when a previously issued handle is invalidated upstream. */
|
|
67
|
+
export interface HandleInvalidatedDetail {
|
|
68
|
+
instanceId: string;
|
|
69
|
+
handleKind: string;
|
|
70
|
+
handleId: string;
|
|
71
|
+
reasonCode: HandleInvalidatedReasonCode;
|
|
72
|
+
reasonMessage?: string;
|
|
73
|
+
relatedRequestId?: string;
|
|
74
|
+
source: HandleInvalidatedSource;
|
|
75
|
+
at: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const isNonEmptyString = (v: unknown): v is string =>
|
|
79
|
+
typeof v === "string" && v.trim().length > 0;
|
|
80
|
+
|
|
81
|
+
const isRecord = (v: unknown): v is Record<string, unknown> =>
|
|
82
|
+
typeof v === "object" && v !== null;
|
|
83
|
+
|
|
84
|
+
const HANDLE_INVALIDATED_REASON_CODE_SET: ReadonlySet<string> = new Set(
|
|
85
|
+
HANDLE_INVALIDATED_REASON_CODES,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const isHandleInvalidatedReasonCode = (
|
|
89
|
+
v: unknown,
|
|
90
|
+
): v is HandleInvalidatedReasonCode =>
|
|
91
|
+
typeof v === "string" && HANDLE_INVALIDATED_REASON_CODE_SET.has(v);
|
|
92
|
+
|
|
93
|
+
const HANDLE_INVALIDATED_SOURCE_SET: ReadonlySet<string> = new Set(
|
|
94
|
+
HANDLE_INVALIDATED_SOURCES,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const isHandleInvalidatedSource = (v: unknown): v is HandleInvalidatedSource =>
|
|
98
|
+
typeof v === "string" && HANDLE_INVALIDATED_SOURCE_SET.has(v);
|
|
99
|
+
|
|
100
|
+
export function isFlowStateChangedDetail(
|
|
101
|
+
detail: unknown,
|
|
102
|
+
): detail is FlowStateChangedDetail {
|
|
103
|
+
if (!isRecord(detail)) return false;
|
|
104
|
+
const { instanceId, isOpen, isLoading, width, height, flowHandleId } = detail;
|
|
105
|
+
return (
|
|
106
|
+
isNonEmptyString(instanceId) &&
|
|
107
|
+
typeof isOpen === "boolean" &&
|
|
108
|
+
typeof isLoading === "boolean" &&
|
|
109
|
+
(typeof width === "undefined" || typeof width === "number") &&
|
|
110
|
+
(typeof height === "undefined" || typeof height === "number") &&
|
|
111
|
+
isNonEmptyString(flowHandleId)
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function isInstanceFlowStateChangedDetail(
|
|
116
|
+
detail: unknown,
|
|
117
|
+
): detail is InstanceFlowStateChangedDetail {
|
|
118
|
+
if (!isRecord(detail)) return false;
|
|
119
|
+
const { instanceId, isOpen, isLoading, width, height } = detail;
|
|
120
|
+
return (
|
|
121
|
+
isNonEmptyString(instanceId) &&
|
|
122
|
+
typeof isOpen === "boolean" &&
|
|
123
|
+
typeof isLoading === "boolean" &&
|
|
124
|
+
(typeof width === "undefined" || typeof width === "number") &&
|
|
125
|
+
(typeof height === "undefined" || typeof height === "number")
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function isOpenRequestedDetail(
|
|
130
|
+
detail: unknown,
|
|
131
|
+
): detail is OpenRequestedDetail {
|
|
132
|
+
if (!isRecord(detail)) return false;
|
|
133
|
+
const { instanceId, source, flowId, flowHandleId, hideCloseButton } = detail;
|
|
134
|
+
return (
|
|
135
|
+
isNonEmptyString(instanceId) &&
|
|
136
|
+
(source === "command" || source === "targeting") &&
|
|
137
|
+
isNonEmptyString(flowId) &&
|
|
138
|
+
(typeof flowHandleId === "undefined" || isNonEmptyString(flowHandleId)) &&
|
|
139
|
+
(typeof hideCloseButton === "undefined" ||
|
|
140
|
+
typeof hideCloseButton === "boolean")
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function isHandleInvalidatedDetail(
|
|
145
|
+
detail: unknown,
|
|
146
|
+
): detail is HandleInvalidatedDetail {
|
|
147
|
+
if (!isRecord(detail)) return false;
|
|
148
|
+
const {
|
|
149
|
+
instanceId,
|
|
150
|
+
handleKind,
|
|
151
|
+
handleId,
|
|
152
|
+
reasonCode,
|
|
153
|
+
reasonMessage,
|
|
154
|
+
relatedRequestId,
|
|
155
|
+
source,
|
|
156
|
+
at,
|
|
157
|
+
} = detail;
|
|
158
|
+
return (
|
|
159
|
+
isNonEmptyString(instanceId) &&
|
|
160
|
+
isNonEmptyString(handleKind) &&
|
|
161
|
+
isNonEmptyString(handleId) &&
|
|
162
|
+
isHandleInvalidatedReasonCode(reasonCode) &&
|
|
163
|
+
(typeof reasonMessage === "undefined" ||
|
|
164
|
+
typeof reasonMessage === "string") &&
|
|
165
|
+
(typeof relatedRequestId === "undefined" ||
|
|
166
|
+
isNonEmptyString(relatedRequestId)) &&
|
|
167
|
+
isHandleInvalidatedSource(source) &&
|
|
168
|
+
typeof at === "number" &&
|
|
169
|
+
Number.isFinite(at)
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface CommandSettledSuccessDetail {
|
|
174
|
+
requestId: string;
|
|
175
|
+
instanceId: string | null;
|
|
176
|
+
kind: string;
|
|
177
|
+
ok: true;
|
|
178
|
+
result?: unknown;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export interface CommandSettledFailureDetail {
|
|
182
|
+
requestId: string;
|
|
183
|
+
instanceId: string | null;
|
|
184
|
+
kind: string;
|
|
185
|
+
ok: false;
|
|
186
|
+
error: { message: string; code?: string };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export type CommandSettledDetail =
|
|
190
|
+
| CommandSettledSuccessDetail
|
|
191
|
+
| CommandSettledFailureDetail;
|
|
192
|
+
|
|
193
|
+
export function isCommandSettledDetail(
|
|
194
|
+
detail: unknown,
|
|
195
|
+
): detail is CommandSettledDetail {
|
|
196
|
+
if (!isRecord(detail)) return false;
|
|
197
|
+
const { requestId, instanceId, kind, ok, error } = detail;
|
|
198
|
+
if (
|
|
199
|
+
!isNonEmptyString(requestId) ||
|
|
200
|
+
(typeof instanceId !== "string" && instanceId !== null) ||
|
|
201
|
+
typeof kind !== "string" ||
|
|
202
|
+
typeof ok !== "boolean"
|
|
203
|
+
) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
if (ok === true) {
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
if (ok === false && isRecord(error)) {
|
|
210
|
+
return typeof error.message === "string";
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Result of extracting a flow handle from a successful open/prerender/prefetch settlement. */
|
|
216
|
+
export type FlowHandleFromSettlement = {
|
|
217
|
+
flowHandleId: string;
|
|
218
|
+
flowRunId?: string;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Host-facing handle result: same shape as FlowHandleFromSettlement.
|
|
223
|
+
* Used when projecting instance:command:settled so the SDK receives flowHandleId (and flowRunId when present).
|
|
224
|
+
*/
|
|
225
|
+
export type HostHandleSettlementResult = FlowHandleFromSettlement;
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Return the host-facing handle result for a command settlement, or null.
|
|
229
|
+
* Use in the loader when projecting instance:command:settled; keeps the host contract in one place (no zod).
|
|
230
|
+
*/
|
|
231
|
+
export function getHostHandleResultFromSettlement(detail: {
|
|
232
|
+
ok: boolean;
|
|
233
|
+
kind: string;
|
|
234
|
+
result?: unknown;
|
|
235
|
+
}): HostHandleSettlementResult | null {
|
|
236
|
+
return getFlowHandleFromSettlementDetail(detail);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const HANDLE_RETURNING_KINDS: readonly ["open", "prerender", "prefetch"] = [
|
|
240
|
+
"open",
|
|
241
|
+
"prerender",
|
|
242
|
+
"prefetch",
|
|
243
|
+
];
|
|
244
|
+
type HandleReturningKind = (typeof HANDLE_RETURNING_KINDS)[number];
|
|
245
|
+
const HANDLE_RETURNING_KIND_SET: ReadonlySet<string> = new Set(
|
|
246
|
+
HANDLE_RETURNING_KINDS,
|
|
247
|
+
);
|
|
248
|
+
const isHandleReturningKind = (kind: string): kind is HandleReturningKind =>
|
|
249
|
+
HANDLE_RETURNING_KIND_SET.has(kind);
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Extract flow handle from a successful command settlement when the command
|
|
253
|
+
* is one that can return a handle (open, prerender, prefetch). Uses plain
|
|
254
|
+
* checks only (no Zod). Returns null if detail is not ok, kind is not one of
|
|
255
|
+
* those, or result does not contain a non-empty flowHandleId.
|
|
256
|
+
*/
|
|
257
|
+
export function getFlowHandleFromSettlementDetail(detail: {
|
|
258
|
+
ok: boolean;
|
|
259
|
+
kind: string;
|
|
260
|
+
result?: unknown;
|
|
261
|
+
}): FlowHandleFromSettlement | null {
|
|
262
|
+
if (!detail.ok || !isHandleReturningKind(detail.kind)) {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
const result = detail.result;
|
|
266
|
+
if (!isRecord(result)) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
const { flowHandleId, flowRunId } = result;
|
|
270
|
+
if (!isNonEmptyString(flowHandleId)) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
flowHandleId,
|
|
275
|
+
...(isNonEmptyString(flowRunId) && { flowRunId }),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-page / npm SDK protocol surface (Zod-free).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from "./command-dispatch.js";
|
|
6
|
+
export * from "./command-envelope.js";
|
|
7
|
+
export * from "./command-settlement.js";
|
|
8
|
+
export * from "./constants.js";
|
|
9
|
+
export * from "./host-event-contract.js";
|
|
10
|
+
export * from "./lazy-handle-client.js";
|
|
11
|
+
export { createCommandRequestId } from "./request-id.js";
|
|
12
|
+
export * from "./sdk-types.js";
|
|
13
|
+
export { createUniqueId } from "./unique-id.js";
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
type Subscriber<THandleId extends string> = (handleId: THandleId) => void;
|
|
2
|
+
type StageSubscriber<TStage extends string> = (stage: TStage) => void;
|
|
3
|
+
|
|
4
|
+
type LifecycleStage = "uninitialized" | "ready" | "invalidated";
|
|
5
|
+
type StageMap<TStage extends string> = {
|
|
6
|
+
uninitialized: TStage;
|
|
7
|
+
ready: TStage;
|
|
8
|
+
invalidated: TStage;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const DEFAULT_LIFECYCLE_STAGES: StageMap<LifecycleStage> = {
|
|
12
|
+
uninitialized: "uninitialized",
|
|
13
|
+
ready: "ready",
|
|
14
|
+
invalidated: "invalidated",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type LazyHandleClient<
|
|
18
|
+
THandleId extends string,
|
|
19
|
+
TSettlement,
|
|
20
|
+
TStage extends string = LifecycleStage,
|
|
21
|
+
> = {
|
|
22
|
+
runCommand<TPayload>(
|
|
23
|
+
buildPayload: (handleId: THandleId | undefined) => TPayload,
|
|
24
|
+
dispatchAndWait: (payload: TPayload) => Promise<TSettlement>,
|
|
25
|
+
): Promise<TSettlement>;
|
|
26
|
+
getHandleId(): THandleId | undefined;
|
|
27
|
+
subscribeHandle(subscriber: Subscriber<THandleId>): () => void;
|
|
28
|
+
getStage(): TStage;
|
|
29
|
+
setStage(stage: TStage): void;
|
|
30
|
+
subscribeStage(subscriber: StageSubscriber<TStage>): () => void;
|
|
31
|
+
reset(): void;
|
|
32
|
+
invalidate(): void;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type CreateLazyHandleClientOptions<
|
|
36
|
+
THandleId extends string,
|
|
37
|
+
TSettlement,
|
|
38
|
+
TStage extends string = LifecycleStage,
|
|
39
|
+
> = {
|
|
40
|
+
getHandleIdFromSettlement: (settlement: TSettlement) => THandleId | undefined;
|
|
41
|
+
stages?: StageMap<TStage>;
|
|
42
|
+
initialStage?: TStage;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type CreateLazyHandleClientDefaultOptions<
|
|
46
|
+
THandleId extends string,
|
|
47
|
+
TSettlement,
|
|
48
|
+
> = {
|
|
49
|
+
getHandleIdFromSettlement: (settlement: TSettlement) => THandleId | undefined;
|
|
50
|
+
initialStage?: LifecycleStage;
|
|
51
|
+
stages?: undefined;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
type CreateLazyHandleClientCustomOptions<
|
|
55
|
+
THandleId extends string,
|
|
56
|
+
TSettlement,
|
|
57
|
+
TStage extends string,
|
|
58
|
+
> = {
|
|
59
|
+
getHandleIdFromSettlement: (settlement: TSettlement) => THandleId | undefined;
|
|
60
|
+
stages: StageMap<TStage>;
|
|
61
|
+
initialStage?: TStage;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Serializes commands for a lazily initialized handle.
|
|
66
|
+
* Each command payload is built only when its turn runs, so it always sees
|
|
67
|
+
* the latest settled handle id from prior commands.
|
|
68
|
+
*/
|
|
69
|
+
const createLazyHandleClientWithStages = <
|
|
70
|
+
THandleId extends string,
|
|
71
|
+
TSettlement,
|
|
72
|
+
TStage extends string,
|
|
73
|
+
>(input: {
|
|
74
|
+
getHandleIdFromSettlement: (settlement: TSettlement) => THandleId | undefined;
|
|
75
|
+
stages: StageMap<TStage>;
|
|
76
|
+
initialStage?: TStage;
|
|
77
|
+
}): LazyHandleClient<THandleId, TSettlement, TStage> => {
|
|
78
|
+
let handleId: THandleId | undefined;
|
|
79
|
+
let tail = Promise.resolve();
|
|
80
|
+
let isIdle = true;
|
|
81
|
+
let epoch = 0;
|
|
82
|
+
const { stages } = input;
|
|
83
|
+
let stage = input.initialStage ?? stages.uninitialized;
|
|
84
|
+
const subscribers = new Set<Subscriber<THandleId>>();
|
|
85
|
+
const stageSubscribers = new Set<StageSubscriber<TStage>>();
|
|
86
|
+
|
|
87
|
+
const setStage = (nextStage: TStage): void => {
|
|
88
|
+
if (stage === nextStage) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (stage === stages.invalidated && nextStage !== stages.invalidated) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
stage = nextStage;
|
|
95
|
+
stageSubscribers.forEach((subscriber) => {
|
|
96
|
+
subscriber(nextStage);
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const publishHandle = (nextHandleId: THandleId): void => {
|
|
101
|
+
if (stage === stages.invalidated) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
handleId = nextHandleId;
|
|
105
|
+
setStage(stages.ready);
|
|
106
|
+
subscribers.forEach((subscriber) => {
|
|
107
|
+
subscriber(nextHandleId);
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const runCommand: LazyHandleClient<
|
|
112
|
+
THandleId,
|
|
113
|
+
TSettlement,
|
|
114
|
+
TStage
|
|
115
|
+
>["runCommand"] = (buildPayload, dispatchAndWait) => {
|
|
116
|
+
const execute = async (): Promise<TSettlement> => {
|
|
117
|
+
if (stage === stages.invalidated) {
|
|
118
|
+
throw new Error("Handle client is invalidated");
|
|
119
|
+
}
|
|
120
|
+
const commandEpoch = epoch;
|
|
121
|
+
const payload = buildPayload(handleId);
|
|
122
|
+
const settlement = await dispatchAndWait(payload);
|
|
123
|
+
const settledHandleId = input.getHandleIdFromSettlement(settlement);
|
|
124
|
+
if (settledHandleId && commandEpoch === epoch) {
|
|
125
|
+
publishHandle(settledHandleId);
|
|
126
|
+
}
|
|
127
|
+
return settlement;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const run = isIdle ? execute() : tail.then(execute, execute);
|
|
131
|
+
isIdle = false;
|
|
132
|
+
tail = run.then(
|
|
133
|
+
() => {
|
|
134
|
+
isIdle = true;
|
|
135
|
+
},
|
|
136
|
+
() => {
|
|
137
|
+
isIdle = true;
|
|
138
|
+
},
|
|
139
|
+
);
|
|
140
|
+
return run;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
runCommand,
|
|
145
|
+
getHandleId: () => handleId,
|
|
146
|
+
subscribeHandle: (subscriber) => {
|
|
147
|
+
subscribers.add(subscriber);
|
|
148
|
+
if (handleId) {
|
|
149
|
+
subscriber(handleId);
|
|
150
|
+
}
|
|
151
|
+
return () => {
|
|
152
|
+
subscribers.delete(subscriber);
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
getStage: () => stage,
|
|
156
|
+
setStage,
|
|
157
|
+
subscribeStage: (subscriber) => {
|
|
158
|
+
stageSubscribers.add(subscriber);
|
|
159
|
+
subscriber(stage);
|
|
160
|
+
return () => {
|
|
161
|
+
stageSubscribers.delete(subscriber);
|
|
162
|
+
};
|
|
163
|
+
},
|
|
164
|
+
reset: () => {
|
|
165
|
+
epoch += 1;
|
|
166
|
+
handleId = undefined;
|
|
167
|
+
stage = stages.uninitialized;
|
|
168
|
+
stageSubscribers.forEach((subscriber) => {
|
|
169
|
+
subscriber(stage);
|
|
170
|
+
});
|
|
171
|
+
},
|
|
172
|
+
invalidate: () => {
|
|
173
|
+
epoch += 1;
|
|
174
|
+
handleId = undefined;
|
|
175
|
+
setStage(stages.invalidated);
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export function createLazyHandleClient<THandleId extends string, TSettlement>(
|
|
181
|
+
options: CreateLazyHandleClientDefaultOptions<THandleId, TSettlement>,
|
|
182
|
+
): LazyHandleClient<THandleId, TSettlement, LifecycleStage>;
|
|
183
|
+
export function createLazyHandleClient<
|
|
184
|
+
THandleId extends string,
|
|
185
|
+
TSettlement,
|
|
186
|
+
TStage extends string,
|
|
187
|
+
>(
|
|
188
|
+
options: CreateLazyHandleClientCustomOptions<THandleId, TSettlement, TStage>,
|
|
189
|
+
): LazyHandleClient<THandleId, TSettlement, TStage>;
|
|
190
|
+
export function createLazyHandleClient<THandleId extends string, TSettlement>(
|
|
191
|
+
options:
|
|
192
|
+
| CreateLazyHandleClientDefaultOptions<THandleId, TSettlement>
|
|
193
|
+
| CreateLazyHandleClientCustomOptions<THandleId, TSettlement, string>,
|
|
194
|
+
): LazyHandleClient<THandleId, TSettlement, string> {
|
|
195
|
+
if ("stages" in options && options.stages) {
|
|
196
|
+
return createLazyHandleClientWithStages({
|
|
197
|
+
getHandleIdFromSettlement: options.getHandleIdFromSettlement,
|
|
198
|
+
stages: options.stages,
|
|
199
|
+
initialStage: options.initialStage,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
return createLazyHandleClientWithStages({
|
|
203
|
+
getHandleIdFromSettlement: options.getHandleIdFromSettlement,
|
|
204
|
+
stages: DEFAULT_LIFECYCLE_STAGES,
|
|
205
|
+
initialStage: options.initialStage,
|
|
206
|
+
});
|
|
207
|
+
}
|