@player-ui/external-state-plugin 1.0.0--canary.865.36694 → 1.0.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/dist/ExternalStatePlugin.native.js +8033 -289
- package/dist/ExternalStatePlugin.native.js.map +1 -1
- package/dist/cjs/index.cjs +87 -26
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/index.legacy-esm.js +86 -26
- package/dist/index.mjs +86 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/ExternalStateError.ts +45 -0
- package/src/__tests__/index.test.ts +187 -27
- package/src/index.ts +101 -39
- package/types/ExternalStateError.d.ts +20 -0
- package/types/index.d.ts +14 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { expect, test, vitest, describe } from "vitest";
|
|
2
2
|
import type { Flow, InProgressState, NamedState } from "@player-ui/player";
|
|
3
3
|
import { Player } from "@player-ui/player";
|
|
4
|
-
import { ExternalStatePlugin } from "..";
|
|
4
|
+
import { ExternalStatePlugin, ExternalStateError } from "..";
|
|
5
5
|
import { waitFor } from "@testing-library/react";
|
|
6
6
|
|
|
7
7
|
const externalFlow = {
|
|
@@ -242,6 +242,12 @@ test("only transitions if player still on this external state", async () => {
|
|
|
242
242
|
});
|
|
243
243
|
},
|
|
244
244
|
},
|
|
245
|
+
{
|
|
246
|
+
ref: "test-2",
|
|
247
|
+
handlerFunction: () => {
|
|
248
|
+
return "Next";
|
|
249
|
+
},
|
|
250
|
+
},
|
|
245
251
|
]),
|
|
246
252
|
],
|
|
247
253
|
});
|
|
@@ -319,6 +325,151 @@ test("only transitions if player still on this external state", async () => {
|
|
|
319
325
|
).toBe("EXT_2");
|
|
320
326
|
});
|
|
321
327
|
|
|
328
|
+
const flowWithErrorTransitions = {
|
|
329
|
+
id: "test-flow",
|
|
330
|
+
data: {},
|
|
331
|
+
views: [
|
|
332
|
+
{
|
|
333
|
+
id: "error-view",
|
|
334
|
+
type: "test",
|
|
335
|
+
},
|
|
336
|
+
],
|
|
337
|
+
navigation: {
|
|
338
|
+
BEGIN: "FLOW_1",
|
|
339
|
+
FLOW_1: {
|
|
340
|
+
startState: "EXT_1",
|
|
341
|
+
errorTransitions: {
|
|
342
|
+
externalState: "ERROR_VIEW",
|
|
343
|
+
},
|
|
344
|
+
EXT_1: {
|
|
345
|
+
state_type: "EXTERNAL",
|
|
346
|
+
ref: "test-1",
|
|
347
|
+
transitions: { Next: "END_FWD" },
|
|
348
|
+
},
|
|
349
|
+
ERROR_VIEW: {
|
|
350
|
+
state_type: "VIEW",
|
|
351
|
+
ref: "error-view",
|
|
352
|
+
transitions: {},
|
|
353
|
+
},
|
|
354
|
+
END_FWD: {
|
|
355
|
+
state_type: "END",
|
|
356
|
+
outcome: "FWD",
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
test("missing handler error navigates via content errorTransitions", async () => {
|
|
363
|
+
const player = new Player({
|
|
364
|
+
plugins: [new ExternalStatePlugin([])],
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
player.start(flowWithErrorTransitions as Flow);
|
|
368
|
+
|
|
369
|
+
await waitFor(() => {
|
|
370
|
+
const state = player.getState() as InProgressState;
|
|
371
|
+
expect(state.controllers.flow.current?.currentState?.name).toBe(
|
|
372
|
+
"ERROR_VIEW",
|
|
373
|
+
);
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test("missing transition value error navigates via content errorTransitions", async () => {
|
|
378
|
+
const player = new Player({
|
|
379
|
+
plugins: [
|
|
380
|
+
new ExternalStatePlugin([
|
|
381
|
+
{ ref: "test-1", handlerFunction: () => undefined },
|
|
382
|
+
]),
|
|
383
|
+
],
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
player.start(flowWithErrorTransitions as Flow);
|
|
387
|
+
|
|
388
|
+
await waitFor(() => {
|
|
389
|
+
const state = player.getState() as InProgressState;
|
|
390
|
+
expect(state.controllers.flow.current?.currentState?.name).toBe(
|
|
391
|
+
"ERROR_VIEW",
|
|
392
|
+
);
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
test("missing handler error is observable via onError tap", async () => {
|
|
397
|
+
const onErrorSpy = vitest.fn().mockReturnValue(true);
|
|
398
|
+
|
|
399
|
+
const player = new Player({
|
|
400
|
+
plugins: [new ExternalStatePlugin([])],
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
player.hooks.errorController.tap("test", (ec) => {
|
|
404
|
+
ec.hooks.onError.tap("test", onErrorSpy);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
player.start(externalFlow as Flow);
|
|
408
|
+
|
|
409
|
+
await waitFor(() => expect(onErrorSpy).toHaveBeenCalledOnce());
|
|
410
|
+
|
|
411
|
+
const error = onErrorSpy.mock.calls[0]?.[0] as ExternalStateError;
|
|
412
|
+
expect(error).toBeInstanceOf(ExternalStateError);
|
|
413
|
+
expect(error.type).toBe("externalState");
|
|
414
|
+
expect(error.metadata).toStrictEqual({
|
|
415
|
+
ref: "test-1",
|
|
416
|
+
reason: "missing-handler",
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
test("missing transition value error is observable via onError tap", async () => {
|
|
421
|
+
const onErrorSpy = vitest.fn().mockReturnValue(true);
|
|
422
|
+
|
|
423
|
+
const player = new Player({
|
|
424
|
+
plugins: [
|
|
425
|
+
new ExternalStatePlugin([
|
|
426
|
+
{ ref: "test-1", handlerFunction: () => undefined },
|
|
427
|
+
]),
|
|
428
|
+
],
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
player.hooks.errorController.tap("test", (ec) => {
|
|
432
|
+
ec.hooks.onError.tap("test", onErrorSpy);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
player.start(externalFlow as Flow);
|
|
436
|
+
|
|
437
|
+
await waitFor(() => expect(onErrorSpy).toHaveBeenCalledOnce());
|
|
438
|
+
|
|
439
|
+
const error = onErrorSpy.mock.calls[0]?.[0] as ExternalStateError;
|
|
440
|
+
expect(error).toBeInstanceOf(ExternalStateError);
|
|
441
|
+
expect(error.metadata).toStrictEqual({
|
|
442
|
+
ref: "test-1",
|
|
443
|
+
reason: "missing-transition-value",
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
describe("ExternalStateError", () => {
|
|
448
|
+
test("missingHandler has correct shape", () => {
|
|
449
|
+
const error = ExternalStateError.missingHandler("my-ref");
|
|
450
|
+
expect(error.type).toBe("externalState");
|
|
451
|
+
expect(error.metadata).toStrictEqual({
|
|
452
|
+
ref: "my-ref",
|
|
453
|
+
reason: "missing-handler",
|
|
454
|
+
});
|
|
455
|
+
expect(error.message).toBe(
|
|
456
|
+
'No handler found for external state with ref: "my-ref". Ensure a handler is registered for this state.',
|
|
457
|
+
);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
test("missingTransitionValue has correct shape", () => {
|
|
461
|
+
const error = ExternalStateError.missingTransitionValue("my-ref");
|
|
462
|
+
expect(error.type).toBe("externalState");
|
|
463
|
+
expect(error.metadata).toStrictEqual({
|
|
464
|
+
ref: "my-ref",
|
|
465
|
+
reason: "missing-transition-value",
|
|
466
|
+
});
|
|
467
|
+
expect(error.message).toBe(
|
|
468
|
+
'Handler for external state with ref: "my-ref" did not return a transition value. Ensure the handler returns the name of a valid transition.',
|
|
469
|
+
);
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
|
|
322
473
|
describe("edge cases", () => {
|
|
323
474
|
test("async action nodes not transitioning from navigation states with *", async () => {
|
|
324
475
|
const player = new Player({
|
|
@@ -439,8 +590,29 @@ describe("edge cases", () => {
|
|
|
439
590
|
});
|
|
440
591
|
});
|
|
441
592
|
|
|
442
|
-
test("no handler registered for external state -
|
|
443
|
-
|
|
593
|
+
test("no handler registered for external state - calls captureError with ExternalStateError", async () => {
|
|
594
|
+
const player = new Player({
|
|
595
|
+
plugins: [new ExternalStatePlugin([])],
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
const captureSpy = vitest.fn().mockReturnValue(false);
|
|
599
|
+
player.hooks.errorController.tap("test", (ec) => {
|
|
600
|
+
vitest.spyOn(ec, "captureError").mockImplementation(captureSpy);
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
player.start(externalFlow as Flow);
|
|
604
|
+
|
|
605
|
+
await waitFor(() => expect(captureSpy).toHaveBeenCalledOnce());
|
|
606
|
+
|
|
607
|
+
const error = captureSpy.mock.calls[0]?.[0] as ExternalStateError;
|
|
608
|
+
expect(error).toBeInstanceOf(ExternalStateError);
|
|
609
|
+
expect(error.metadata).toStrictEqual({
|
|
610
|
+
ref: "test-1",
|
|
611
|
+
reason: "missing-handler",
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
test("no handler for this ref (but other refs registered) - calls captureError", async () => {
|
|
444
616
|
const player = new Player({
|
|
445
617
|
plugins: [
|
|
446
618
|
new ExternalStatePlugin([
|
|
@@ -450,33 +622,21 @@ describe("edge cases", () => {
|
|
|
450
622
|
],
|
|
451
623
|
});
|
|
452
624
|
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
expect(player.getState().status).toBe("in-progress"),
|
|
458
|
-
);
|
|
459
|
-
|
|
460
|
-
// Get the current state
|
|
461
|
-
const state = player.getState() as InProgressState;
|
|
462
|
-
const currentState = state.controllers.flow.current?.currentState;
|
|
463
|
-
|
|
464
|
-
// Should be stuck on the external state
|
|
465
|
-
expect(currentState?.name).toBe("EXT_1");
|
|
466
|
-
expect(currentState?.value.state_type).toBe("EXTERNAL");
|
|
625
|
+
const captureSpy = vitest.fn().mockReturnValue(false);
|
|
626
|
+
player.hooks.errorController.tap("test", (ec) => {
|
|
627
|
+
vitest.spyOn(ec, "captureError").mockImplementation(captureSpy);
|
|
628
|
+
});
|
|
467
629
|
|
|
468
|
-
|
|
469
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
630
|
+
player.start(externalFlow as Flow);
|
|
470
631
|
|
|
471
|
-
|
|
472
|
-
const laterState = player.getState() as InProgressState;
|
|
473
|
-
const laterCurrentState = laterState.controllers.flow.current?.currentState;
|
|
474
|
-
expect(laterCurrentState?.name).toBe("EXT_1");
|
|
475
|
-
expect(laterCurrentState?.value.state_type).toBe("EXTERNAL");
|
|
632
|
+
await waitFor(() => expect(captureSpy).toHaveBeenCalledOnce());
|
|
476
633
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
634
|
+
const error = captureSpy.mock.calls[0]?.[0] as ExternalStateError;
|
|
635
|
+
expect(error).toBeInstanceOf(ExternalStateError);
|
|
636
|
+
expect(error.metadata).toStrictEqual({
|
|
637
|
+
ref: "test-1",
|
|
638
|
+
reason: "missing-handler",
|
|
639
|
+
});
|
|
480
640
|
});
|
|
481
641
|
});
|
|
482
642
|
|
package/src/index.ts
CHANGED
|
@@ -5,9 +5,15 @@ import type {
|
|
|
5
5
|
PlayerFlowState,
|
|
6
6
|
NavigationFlowState,
|
|
7
7
|
NavigationFlowExternalState,
|
|
8
|
+
ErrorController,
|
|
9
|
+
FlowInstance,
|
|
8
10
|
} from "@player-ui/player";
|
|
9
11
|
import { Registry } from "@player-ui/partial-match-registry";
|
|
10
12
|
import { ExternalStatePluginSymbol } from "./symbols.js";
|
|
13
|
+
import { ExternalStateError } from "./ExternalStateError.js";
|
|
14
|
+
|
|
15
|
+
export { ExternalStateError } from "./ExternalStateError.js";
|
|
16
|
+
export type { ExternalStateErrorMetadata } from "./ExternalStateError.js";
|
|
11
17
|
|
|
12
18
|
export type ExternalStateHandlerMatch = Record<string, unknown>;
|
|
13
19
|
|
|
@@ -60,6 +66,11 @@ export class ExternalStatePlugin implements PlayerPlugin {
|
|
|
60
66
|
*/
|
|
61
67
|
private readonly handlers: ExternalStateHandler[];
|
|
62
68
|
|
|
69
|
+
/** The error controller to use for this plugin.
|
|
70
|
+
* Only the first instance of the plugin should tap the error controller hook.
|
|
71
|
+
*/
|
|
72
|
+
private errorController?: ErrorController;
|
|
73
|
+
|
|
63
74
|
/** Creates a new ExternalStatePlugin */
|
|
64
75
|
constructor(handlers: ExternalStateHandler[]) {
|
|
65
76
|
this.handlers = handlers;
|
|
@@ -74,52 +85,103 @@ export class ExternalStatePlugin implements PlayerPlugin {
|
|
|
74
85
|
return;
|
|
75
86
|
}
|
|
76
87
|
|
|
88
|
+
player.hooks.errorController.tap(this.name, (errorController) => {
|
|
89
|
+
this.errorController = errorController;
|
|
90
|
+
});
|
|
91
|
+
|
|
77
92
|
player.hooks.flowController.tap(this.name, (flowController) => {
|
|
78
93
|
flowController.hooks.flow.tap(this.name, (flow) => {
|
|
79
|
-
flow.hooks.afterTransition.tap(this.name,
|
|
80
|
-
|
|
81
|
-
const currentState = player.getState();
|
|
82
|
-
|
|
83
|
-
if (
|
|
84
|
-
toState &&
|
|
85
|
-
toState.value &&
|
|
86
|
-
isExternal(toState.value) &&
|
|
87
|
-
isInProgress(currentState)
|
|
88
|
-
) {
|
|
89
|
-
try {
|
|
90
|
-
const handler = this.registry?.get(toState.value);
|
|
91
|
-
const transitionValue = await handler?.(
|
|
92
|
-
toState.value,
|
|
93
|
-
currentState.controllers,
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
if (transitionValue !== undefined) {
|
|
97
|
-
const latestState = player.getState();
|
|
98
|
-
|
|
99
|
-
// Ensure the Player is still in the same state after waiting for transitionValue
|
|
100
|
-
if (
|
|
101
|
-
isInProgress(latestState) &&
|
|
102
|
-
latestState.controllers.flow.current?.currentState?.name ===
|
|
103
|
-
toState.name
|
|
104
|
-
) {
|
|
105
|
-
latestState.controllers.flow.transition(transitionValue);
|
|
106
|
-
} else {
|
|
107
|
-
player.logger.warn(
|
|
108
|
-
`External state resolved with [${transitionValue}], but Player already navigated away from [${toState.name}]`,
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
} catch (error) {
|
|
113
|
-
if (error instanceof Error) {
|
|
114
|
-
currentState.fail(error);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
94
|
+
flow.hooks.afterTransition.tap(this.name, (flowInstance) => {
|
|
95
|
+
this.handleAfterTransition(player, flowInstance);
|
|
118
96
|
});
|
|
119
97
|
});
|
|
120
98
|
});
|
|
121
99
|
}
|
|
122
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Resolve an EXTERNAL state transition.
|
|
103
|
+
*/
|
|
104
|
+
private async handleAfterTransition(
|
|
105
|
+
player: Player,
|
|
106
|
+
flowInstance: FlowInstance,
|
|
107
|
+
): Promise<void> {
|
|
108
|
+
const toState = flowInstance.currentState;
|
|
109
|
+
const currentState = player.getState();
|
|
110
|
+
|
|
111
|
+
if (
|
|
112
|
+
!toState ||
|
|
113
|
+
!toState.value ||
|
|
114
|
+
!isExternal(toState.value) ||
|
|
115
|
+
!isInProgress(currentState)
|
|
116
|
+
) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
const handler = this.registry?.get(toState.value);
|
|
122
|
+
|
|
123
|
+
if (!handler) {
|
|
124
|
+
this.reportError(
|
|
125
|
+
player,
|
|
126
|
+
ExternalStateError.missingHandler(toState.value.ref),
|
|
127
|
+
);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const transitionValue = await handler(
|
|
132
|
+
toState.value,
|
|
133
|
+
currentState.controllers,
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
if (!transitionValue) {
|
|
137
|
+
this.reportError(
|
|
138
|
+
player,
|
|
139
|
+
ExternalStateError.missingTransitionValue(toState.value.ref),
|
|
140
|
+
);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const latestState = player.getState();
|
|
145
|
+
|
|
146
|
+
// Ensure the Player is still in the same state after waiting for transitionValue
|
|
147
|
+
if (
|
|
148
|
+
isInProgress(latestState) &&
|
|
149
|
+
latestState.controllers.flow.current?.currentState?.name ===
|
|
150
|
+
toState.name
|
|
151
|
+
) {
|
|
152
|
+
latestState.controllers.flow.transition(transitionValue);
|
|
153
|
+
} else {
|
|
154
|
+
player.logger.warn(
|
|
155
|
+
`External state resolved with [${transitionValue}], but Player already navigated away from [${toState.name}]`,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
} catch (error) {
|
|
159
|
+
// Thrown errors are treated as purposefully unrecoverable: fail the flow rather than
|
|
160
|
+
// routing through captureError.
|
|
161
|
+
if (error instanceof Error) {
|
|
162
|
+
currentState.fail(error);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Report an ExternalStateError via the errorController.
|
|
169
|
+
*/
|
|
170
|
+
private reportError(player: Player, error: ExternalStateError): void {
|
|
171
|
+
// The compiler believes errorController could be nil, but in practice it should always
|
|
172
|
+
// be set by the time this method runs. The logger fallback exists only as defense
|
|
173
|
+
// against an unexpected lifecycle regression — if it ever fires, that's a bug to
|
|
174
|
+
// investigate, not normal operation.
|
|
175
|
+
if (!this.errorController) {
|
|
176
|
+
player.logger.error(
|
|
177
|
+
`${error.message} (errorController was unexpectedly undefined; it should always be set by the time this code runs)`,
|
|
178
|
+
);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
this.errorController.captureError(error);
|
|
183
|
+
}
|
|
184
|
+
|
|
123
185
|
/**
|
|
124
186
|
* Create or share the registry for this plugin instance.
|
|
125
187
|
*
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ErrorMetadata, PlayerErrorMetadata } from "@player-ui/player";
|
|
2
|
+
import { ErrorSeverity } from "@player-ui/player";
|
|
3
|
+
export type ExternalStateErrorReason = "missing-handler" | "missing-transition-value";
|
|
4
|
+
export interface ExternalStateErrorMetadata extends ErrorMetadata {
|
|
5
|
+
/** The `ref` of the EXTERNAL state that produced the error */
|
|
6
|
+
ref: string;
|
|
7
|
+
/** Which failure mode this error represents */
|
|
8
|
+
reason: ExternalStateErrorReason;
|
|
9
|
+
}
|
|
10
|
+
export declare class ExternalStateError extends Error implements PlayerErrorMetadata<ExternalStateErrorMetadata> {
|
|
11
|
+
readonly type: string;
|
|
12
|
+
readonly severity: ErrorSeverity;
|
|
13
|
+
readonly metadata: ExternalStateErrorMetadata;
|
|
14
|
+
private constructor();
|
|
15
|
+
/** No handler was registered for the EXTERNAL state's ref. */
|
|
16
|
+
static missingHandler(ref: string): ExternalStateError;
|
|
17
|
+
/** A handler ran but returned no transition value. */
|
|
18
|
+
static missingTransitionValue(ref: string): ExternalStateError;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=ExternalStateError.d.ts.map
|
package/types/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Player, PlayerPlugin, InProgressState, NavigationFlowExternalState } from "@player-ui/player";
|
|
2
|
+
export { ExternalStateError } from "./ExternalStateError.js";
|
|
3
|
+
export type { ExternalStateErrorMetadata } from "./ExternalStateError.js";
|
|
2
4
|
export type ExternalStateHandlerMatch = Record<string, unknown>;
|
|
3
5
|
export type ExternalStateHandlerFunction = (state: NavigationFlowExternalState, options: InProgressState["controllers"]) => string | undefined | Promise<string | undefined>;
|
|
4
6
|
export type ExternalStateHandler = {
|
|
@@ -30,9 +32,21 @@ export declare class ExternalStatePlugin implements PlayerPlugin {
|
|
|
30
32
|
* The handlers for this plugin instance.
|
|
31
33
|
*/
|
|
32
34
|
private readonly handlers;
|
|
35
|
+
/** The error controller to use for this plugin.
|
|
36
|
+
* Only the first instance of the plugin should tap the error controller hook.
|
|
37
|
+
*/
|
|
38
|
+
private errorController?;
|
|
33
39
|
/** Creates a new ExternalStatePlugin */
|
|
34
40
|
constructor(handlers: ExternalStateHandler[]);
|
|
35
41
|
apply(player: Player): void;
|
|
42
|
+
/**
|
|
43
|
+
* Resolve an EXTERNAL state transition.
|
|
44
|
+
*/
|
|
45
|
+
private handleAfterTransition;
|
|
46
|
+
/**
|
|
47
|
+
* Report an ExternalStateError via the errorController.
|
|
48
|
+
*/
|
|
49
|
+
private reportError;
|
|
36
50
|
/**
|
|
37
51
|
* Create or share the registry for this plugin instance.
|
|
38
52
|
*
|