@primafuture/contrib-kit-react 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/LICENSE +15 -0
- package/README.md +294 -0
- package/dist/contribution-component.d.ts +82 -0
- package/dist/contribution-component.d.ts.map +1 -0
- package/dist/contribution-component.js +12 -0
- package/dist/contribution-component.js.map +1 -0
- package/dist/extension-outlet.d.ts +155 -0
- package/dist/extension-outlet.d.ts.map +1 -0
- package/dist/extension-outlet.js +229 -0
- package/dist/extension-outlet.js.map +1 -0
- package/dist/extension-registry-provider.d.ts +26 -0
- package/dist/extension-registry-provider.d.ts.map +1 -0
- package/dist/extension-registry-provider.js +82 -0
- package/dist/extension-registry-provider.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/adapter-configuration.d.ts +54 -0
- package/dist/internal/adapter-configuration.d.ts.map +1 -0
- package/dist/internal/adapter-configuration.js +89 -0
- package/dist/internal/adapter-configuration.js.map +1 -0
- package/dist/internal/adapter-context.d.ts +24 -0
- package/dist/internal/adapter-context.d.ts.map +1 -0
- package/dist/internal/adapter-context.js +23 -0
- package/dist/internal/adapter-context.js.map +1 -0
- package/dist/internal/adapter-registry-bridge.d.ts +58 -0
- package/dist/internal/adapter-registry-bridge.d.ts.map +1 -0
- package/dist/internal/adapter-registry-bridge.js +494 -0
- package/dist/internal/adapter-registry-bridge.js.map +1 -0
- package/dist/internal/adapter-registry-protocol.d.ts +53 -0
- package/dist/internal/adapter-registry-protocol.d.ts.map +1 -0
- package/dist/internal/adapter-registry-protocol.js +182 -0
- package/dist/internal/adapter-registry-protocol.js.map +1 -0
- package/dist/internal/adapter-scope.d.ts +87 -0
- package/dist/internal/adapter-scope.d.ts.map +1 -0
- package/dist/internal/adapter-scope.js +346 -0
- package/dist/internal/adapter-scope.js.map +1 -0
- package/dist/internal/implementation-classifier.d.ts +21 -0
- package/dist/internal/implementation-classifier.d.ts.map +1 -0
- package/dist/internal/implementation-classifier.js +39 -0
- package/dist/internal/implementation-classifier.js.map +1 -0
- package/dist/internal/managed-lazy-generation.d.ts +100 -0
- package/dist/internal/managed-lazy-generation.d.ts.map +1 -0
- package/dist/internal/managed-lazy-generation.js +419 -0
- package/dist/internal/managed-lazy-generation.js.map +1 -0
- package/dist/internal/outlet-commit-lease.d.ts +46 -0
- package/dist/internal/outlet-commit-lease.d.ts.map +1 -0
- package/dist/internal/outlet-commit-lease.js +137 -0
- package/dist/internal/outlet-commit-lease.js.map +1 -0
- package/dist/internal/outlet-error-pipeline.d.ts +122 -0
- package/dist/internal/outlet-error-pipeline.d.ts.map +1 -0
- package/dist/internal/outlet-error-pipeline.js +214 -0
- package/dist/internal/outlet-error-pipeline.js.map +1 -0
- package/dist/internal/outlet-input.d.ts +42 -0
- package/dist/internal/outlet-input.d.ts.map +1 -0
- package/dist/internal/outlet-input.js +86 -0
- package/dist/internal/outlet-input.js.map +1 -0
- package/dist/internal/outlet-membership-boundary.d.ts +93 -0
- package/dist/internal/outlet-membership-boundary.d.ts.map +1 -0
- package/dist/internal/outlet-membership-boundary.js +710 -0
- package/dist/internal/outlet-membership-boundary.js.map +1 -0
- package/dist/internal/outlet-membership.d.ts +31 -0
- package/dist/internal/outlet-membership.d.ts.map +1 -0
- package/dist/internal/outlet-membership.js +65 -0
- package/dist/internal/outlet-membership.js.map +1 -0
- package/dist/internal/outlet-projection.d.ts +102 -0
- package/dist/internal/outlet-projection.d.ts.map +1 -0
- package/dist/internal/outlet-projection.js +228 -0
- package/dist/internal/outlet-projection.js.map +1 -0
- package/dist/internal/outlet-reporter-boundary.d.ts +42 -0
- package/dist/internal/outlet-reporter-boundary.d.ts.map +1 -0
- package/dist/internal/outlet-reporter-boundary.js +57 -0
- package/dist/internal/outlet-reporter-boundary.js.map +1 -0
- package/dist/internal/public-input-capture.d.ts +13 -0
- package/dist/internal/public-input-capture.d.ts.map +1 -0
- package/dist/internal/public-input-capture.js +34 -0
- package/dist/internal/public-input-capture.js.map +1 -0
- package/dist/internal/resolved-contributions-query.d.ts +42 -0
- package/dist/internal/resolved-contributions-query.d.ts.map +1 -0
- package/dist/internal/resolved-contributions-query.js +203 -0
- package/dist/internal/resolved-contributions-query.js.map +1 -0
- package/dist/react-adapter-diagnostics.d.ts +199 -0
- package/dist/react-adapter-diagnostics.d.ts.map +1 -0
- package/dist/react-adapter-diagnostics.js +257 -0
- package/dist/react-adapter-diagnostics.js.map +1 -0
- package/dist/react-adapter-error.d.ts +35 -0
- package/dist/react-adapter-error.d.ts.map +1 -0
- package/dist/react-adapter-error.js +37 -0
- package/dist/react-adapter-error.js.map +1 -0
- package/dist/react-extension-point.d.ts +26 -0
- package/dist/react-extension-point.d.ts.map +1 -0
- package/dist/react-extension-point.js +100 -0
- package/dist/react-extension-point.js.map +1 -0
- package/dist/react-lazy-contribution.d.ts +40 -0
- package/dist/react-lazy-contribution.d.ts.map +1 -0
- package/dist/react-lazy-contribution.js +18 -0
- package/dist/react-lazy-contribution.js.map +1 -0
- package/dist/resolved-contributions.d.ts +90 -0
- package/dist/resolved-contributions.d.ts.map +1 -0
- package/dist/resolved-contributions.js +455 -0
- package/dist/resolved-contributions.js.map +1 -0
- package/package.json +47 -0
- package/src/contribution-component.ts +118 -0
- package/src/extension-outlet.ts +590 -0
- package/src/extension-registry-provider.ts +179 -0
- package/src/index.ts +72 -0
- package/src/internal/adapter-configuration.ts +181 -0
- package/src/internal/adapter-context.ts +43 -0
- package/src/internal/adapter-registry-bridge.ts +723 -0
- package/src/internal/adapter-registry-protocol.ts +365 -0
- package/src/internal/adapter-scope.ts +500 -0
- package/src/internal/implementation-classifier.ts +59 -0
- package/src/internal/managed-lazy-generation.ts +540 -0
- package/src/internal/outlet-commit-lease.ts +216 -0
- package/src/internal/outlet-error-pipeline.ts +447 -0
- package/src/internal/outlet-input.ts +264 -0
- package/src/internal/outlet-membership-boundary.ts +1048 -0
- package/src/internal/outlet-membership.ts +125 -0
- package/src/internal/outlet-projection.ts +511 -0
- package/src/internal/outlet-reporter-boundary.ts +105 -0
- package/src/internal/public-input-capture.ts +55 -0
- package/src/internal/resolved-contributions-query.ts +318 -0
- package/src/react-adapter-diagnostics.ts +457 -0
- package/src/react-adapter-error.ts +88 -0
- package/src/react-extension-point.ts +181 -0
- package/src/react-lazy-contribution.ts +66 -0
- package/src/resolved-contributions.ts +895 -0
|
@@ -0,0 +1,1048 @@
|
|
|
1
|
+
import type * as contribKitCore from "@primafuture/contrib-kit-core";
|
|
2
|
+
import * as react from "react";
|
|
3
|
+
|
|
4
|
+
import type * as adapterScope from "./adapter-scope.js";
|
|
5
|
+
import type * as adapterConfiguration from "./adapter-configuration.js";
|
|
6
|
+
import type * as implementationClassifier from "./implementation-classifier.js";
|
|
7
|
+
import * as managedLazyGeneration from "./managed-lazy-generation.js";
|
|
8
|
+
import type * as outletCommitLease from "./outlet-commit-lease.js";
|
|
9
|
+
import * as outletErrorPipeline from "./outlet-error-pipeline.js";
|
|
10
|
+
import * as outletInput from "./outlet-input.js";
|
|
11
|
+
import type * as outletMembership from "./outlet-membership.js";
|
|
12
|
+
import * as outletReporterBoundary from "./outlet-reporter-boundary.js";
|
|
13
|
+
import type * as outletProjection from "./outlet-projection.js";
|
|
14
|
+
import type * as contributionComponent from "../contribution-component.js";
|
|
15
|
+
import * as reactAdapterDiagnostics from "../react-adapter-diagnostics.js";
|
|
16
|
+
import type * as reactAdapterError from "../react-adapter-error.js";
|
|
17
|
+
import * as reactAdapterErrorRuntime from "../react-adapter-error.js";
|
|
18
|
+
import type * as reactExtensionPoint from "../react-extension-point.js";
|
|
19
|
+
|
|
20
|
+
/** Props jedné interní package-owned membership boundary. */
|
|
21
|
+
export interface OutletMembershipBoundaryProps<
|
|
22
|
+
Point extends reactExtensionPoint.AnyReactExtensionPoint,
|
|
23
|
+
> {
|
|
24
|
+
/** Read-once outlet input pro current render snapshot. */
|
|
25
|
+
readonly input: outletInput.CapturedOutletInput<Point>;
|
|
26
|
+
/** Exact projected membership včetně retained local tokenu. */
|
|
27
|
+
readonly membership: outletMembership.OutletMembershipEntry<Point>;
|
|
28
|
+
/** Commit-owned outlet lease; server a první hydration render ji ještě nemají. */
|
|
29
|
+
readonly outletLease: outletCommitLease.OutletCommitLease | undefined;
|
|
30
|
+
/** Exact raw registry reference jako první osa identity. */
|
|
31
|
+
readonly rawRegistry: object;
|
|
32
|
+
/** Stabilní adapter scope vlastnící boundary ID allocator. */
|
|
33
|
+
readonly scope: adapterScope.AdapterScopeRuntime;
|
|
34
|
+
/** Exact provider/outlet generation pro commit acquire. */
|
|
35
|
+
readonly scopeGeneration: adapterScope.AdapterScopeGeneration;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Opaque generation token remountující pouze affected membership subtree. */
|
|
39
|
+
interface OutletRetryGenerationToken {
|
|
40
|
+
/** Privátní runtime-prázdný marker. */
|
|
41
|
+
readonly kind?: never;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Render mode jedné membership retry generation. */
|
|
45
|
+
type OutletMembershipRenderMode =
|
|
46
|
+
| "rendering"
|
|
47
|
+
| "captured"
|
|
48
|
+
| "hidden"
|
|
49
|
+
| "fallback"
|
|
50
|
+
| "escalating";
|
|
51
|
+
|
|
52
|
+
/** Boundary-local managed-lazy presentation nezávislá na Core membership seedu. */
|
|
53
|
+
type OutletManagedLazyStatus = "inactive" | "loading" | "ready" | "failed";
|
|
54
|
+
|
|
55
|
+
/** Kompletní class state včetně identity a current error presentation. */
|
|
56
|
+
interface OutletMembershipBoundaryState {
|
|
57
|
+
/** Přesný cause zachycený React boundary lifecyclem. */
|
|
58
|
+
readonly capturedCause: unknown;
|
|
59
|
+
/** Exact cause připravený pro host escalation. */
|
|
60
|
+
readonly escalationCause: unknown;
|
|
61
|
+
/** Opaque attempt token vzniklý pouze při captured failure. */
|
|
62
|
+
readonly errorAttemptToken: object | undefined;
|
|
63
|
+
/** Zda state skutečně nese captured cause i při undefined hodnotě. */
|
|
64
|
+
readonly hasCapturedCause: boolean;
|
|
65
|
+
/** Eager componenta vrácená current lazy generation. */
|
|
66
|
+
readonly lazyComponent: unknown;
|
|
67
|
+
/** Loader zachycený jednou při vzniku exact membership identity. */
|
|
68
|
+
readonly lazyLoad: implementationClassifier.CapturedReactLazyLoader | undefined;
|
|
69
|
+
/** Current boundary-local managed-lazy presentation. */
|
|
70
|
+
readonly lazyStatus: OutletManagedLazyStatus;
|
|
71
|
+
/** Raw registry osa current membership identity. */
|
|
72
|
+
readonly rawRegistry: object;
|
|
73
|
+
/** Opaque Core registration osa current membership identity. */
|
|
74
|
+
readonly registrationId: string;
|
|
75
|
+
/** Retained local membership token current identity. */
|
|
76
|
+
readonly membershipToken: outletMembership.OutletMembershipToken;
|
|
77
|
+
/** Aktuální render/policy větev. */
|
|
78
|
+
readonly mode: OutletMembershipRenderMode;
|
|
79
|
+
/** Frozen primary event pro hidden/fallback/escalation evidenci. */
|
|
80
|
+
readonly primaryError: reactAdapterDiagnostics.ReactAdapterErrorEvent | undefined;
|
|
81
|
+
/** Generation-bound retry capability current primary failure. */
|
|
82
|
+
readonly retry: (() => void) | undefined;
|
|
83
|
+
/** Monotonická retry generation začínající nulou. */
|
|
84
|
+
readonly retryGeneration: number;
|
|
85
|
+
/** Opaque token exact retry generation. */
|
|
86
|
+
readonly retryToken: OutletRetryGenerationToken;
|
|
87
|
+
/** Monotonická subtree identita měněná při registry/membership resetu. */
|
|
88
|
+
readonly subtreeGeneration: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Mutable interní authority jednoho primary failure attemptu. */
|
|
92
|
+
interface OutletPrimaryFailureAttempt {
|
|
93
|
+
/** Sync invalidace před accepted retry nebo lifecycle cleanupem. */
|
|
94
|
+
authoritative: boolean;
|
|
95
|
+
/** Exact boundary lease bránící oživení capability v nové scope epoše. */
|
|
96
|
+
readonly boundaryLease: outletCommitLease.OutletCommitLease;
|
|
97
|
+
/** At-most-once guard celé committed primary pipeline. */
|
|
98
|
+
published: boolean;
|
|
99
|
+
/** Exact error attempt token z React/synthetic capture. */
|
|
100
|
+
readonly errorAttemptToken: object;
|
|
101
|
+
/** Exact membership token chránící identity switch. */
|
|
102
|
+
readonly membershipToken: outletMembership.OutletMembershipToken;
|
|
103
|
+
/** Public capability s vlastní at-most-once autoritou. */
|
|
104
|
+
retry: () => void;
|
|
105
|
+
/** Exact retry generation token chránící ordinary rerender. */
|
|
106
|
+
readonly retryToken: OutletRetryGenerationToken;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Commit-time safe channel jednoho managed-lazy attemptu. */
|
|
110
|
+
interface OutletManagedLazyDiagnosticChannel {
|
|
111
|
+
/** Opaque boundary identita přidělená před loader startem. */
|
|
112
|
+
readonly boundaryId: string;
|
|
113
|
+
/** Effective read-once sink konfigurace owning outlet generation. */
|
|
114
|
+
readonly configuration: adapterConfiguration.AdapterScopeConfiguration;
|
|
115
|
+
/** Bezpečná contribution metadata bez dat nebo implementation. */
|
|
116
|
+
readonly contribution: reactAdapterDiagnostics.ReadonlyContributionMetadata;
|
|
117
|
+
/** Exact outlet lease, jejíž retirement umlčí pozdní settlement. */
|
|
118
|
+
readonly outletLease: outletCommitLease.OutletCommitLease;
|
|
119
|
+
/** Volitelná bezpečná point metadata bez runtime capability. */
|
|
120
|
+
readonly point?: reactAdapterDiagnostics.ReadonlyPointMetadata;
|
|
121
|
+
/** Exact raw registry pouze pro live retirement gate, nikdy pro event payload. */
|
|
122
|
+
readonly rawRegistry: object;
|
|
123
|
+
/** Public retry generation původního loader attemptu. */
|
|
124
|
+
readonly retryGeneration: number;
|
|
125
|
+
/** Stabilní scope, který vlastní diagnostickou epochu. */
|
|
126
|
+
readonly scope: adapterScope.AdapterScopeRuntime;
|
|
127
|
+
/** Exact scope generation owning loader start. */
|
|
128
|
+
readonly scopeGeneration: adapterScope.AdapterScopeGeneration;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Props čistého contribution/loading rendereru uvnitř primary boundary. */
|
|
132
|
+
interface OutletMembershipRendererProps<
|
|
133
|
+
Point extends reactExtensionPoint.AnyReactExtensionPoint,
|
|
134
|
+
> {
|
|
135
|
+
/** Current read-once outlet callback set. */
|
|
136
|
+
readonly input: outletInput.CapturedOutletInput<Point>;
|
|
137
|
+
/** Current ready nebo loading projected seed. */
|
|
138
|
+
readonly seed:
|
|
139
|
+
| outletProjection.OutletReadyMembershipSeed<Point>
|
|
140
|
+
| outletProjection.OutletLoadingMembershipSeed<Point>;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Props host fallback rendereru izolovaného reporter boundary. */
|
|
144
|
+
interface OutletContributionFallbackRendererProps<
|
|
145
|
+
Point extends reactExtensionPoint.AnyReactExtensionPoint,
|
|
146
|
+
> {
|
|
147
|
+
/** Frozen public error event current primary failure. */
|
|
148
|
+
readonly error: reactAdapterDiagnostics.ReactAdapterErrorEvent;
|
|
149
|
+
/** Read-once captured fallback callback. */
|
|
150
|
+
readonly renderContributionError: outletInput.CapturedOutletCallback<unknown, react.ReactNode>;
|
|
151
|
+
/** Current membership render seed s logical placementem. */
|
|
152
|
+
readonly seed: outletProjection.OutletProjectedMembershipSeed<Point>;
|
|
153
|
+
/** Generation-bound retry capability. */
|
|
154
|
+
readonly retry: () => void;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Vytvoří nový render-pure opaque retry generation token. */
|
|
158
|
+
function createRetryGenerationToken(): OutletRetryGenerationToken {
|
|
159
|
+
return Object.freeze({});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Vytvoří počáteční nebo identity-reset state před renderem nové membership. */
|
|
163
|
+
function createMembershipBoundaryState<
|
|
164
|
+
Point extends reactExtensionPoint.AnyReactExtensionPoint,
|
|
165
|
+
>(
|
|
166
|
+
props: OutletMembershipBoundaryProps<Point>,
|
|
167
|
+
subtreeGeneration = 0,
|
|
168
|
+
): OutletMembershipBoundaryState {
|
|
169
|
+
const seed = props.membership.seed;
|
|
170
|
+
return {
|
|
171
|
+
capturedCause: undefined,
|
|
172
|
+
escalationCause: undefined,
|
|
173
|
+
errorAttemptToken: undefined,
|
|
174
|
+
hasCapturedCause: false,
|
|
175
|
+
lazyComponent: undefined,
|
|
176
|
+
lazyLoad: seed.kind === "loading" ? seed.load : undefined,
|
|
177
|
+
lazyStatus: seed.kind === "loading" ? "loading" : "inactive",
|
|
178
|
+
rawRegistry: props.rawRegistry,
|
|
179
|
+
registrationId: props.membership.seed.registrationId,
|
|
180
|
+
membershipToken: props.membership.token,
|
|
181
|
+
mode: "rendering",
|
|
182
|
+
primaryError: undefined,
|
|
183
|
+
retry: undefined,
|
|
184
|
+
retryGeneration: 0,
|
|
185
|
+
retryToken: createRetryGenerationToken(),
|
|
186
|
+
subtreeGeneration,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Renderuje ready nebo loading větev uvnitř package class boundary. */
|
|
191
|
+
function OutletMembershipRenderer<
|
|
192
|
+
Point extends reactExtensionPoint.AnyReactExtensionPoint,
|
|
193
|
+
>(
|
|
194
|
+
props: OutletMembershipRendererProps<Point>,
|
|
195
|
+
): react.ReactElement | null {
|
|
196
|
+
const seed = props.seed;
|
|
197
|
+
if (seed.kind === "loading") {
|
|
198
|
+
if (props.input.renderLoading === undefined) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
const rendered = outletInput.callCapturedOutletCallback(
|
|
202
|
+
props.input.renderLoading,
|
|
203
|
+
seed.renderState,
|
|
204
|
+
);
|
|
205
|
+
return react.createElement(react.Fragment, null, rendered);
|
|
206
|
+
}
|
|
207
|
+
if (props.input.renderContribution !== undefined) {
|
|
208
|
+
const rendered = outletInput.callCapturedOutletCallback(
|
|
209
|
+
props.input.renderContribution,
|
|
210
|
+
seed.renderState,
|
|
211
|
+
);
|
|
212
|
+
return react.createElement(react.Fragment, null, rendered);
|
|
213
|
+
}
|
|
214
|
+
return react.createElement(seed.renderState.component, seed.renderState.componentProps);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Volá contribution fallback uvnitř samostatné reporter class boundary. */
|
|
218
|
+
function OutletContributionFallbackRenderer<
|
|
219
|
+
Point extends reactExtensionPoint.AnyReactExtensionPoint,
|
|
220
|
+
>(
|
|
221
|
+
props: OutletContributionFallbackRendererProps<Point>,
|
|
222
|
+
): react.ReactElement {
|
|
223
|
+
const state = Object.freeze({
|
|
224
|
+
...props.seed.renderState,
|
|
225
|
+
error: props.error,
|
|
226
|
+
retry: props.retry,
|
|
227
|
+
});
|
|
228
|
+
const rendered = outletInput.callCapturedOutletCallback(
|
|
229
|
+
props.renderContributionError,
|
|
230
|
+
state,
|
|
231
|
+
);
|
|
232
|
+
return react.createElement(react.Fragment, null, rendered);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Vrátí exact synthetic cause, nebo původní classifier cause ReactAdapterErroru. */
|
|
236
|
+
function resolveProjectedFailureCause(error: reactAdapterError.ReactAdapterError): unknown {
|
|
237
|
+
return Object.hasOwn(error, "cause") ? error.cause : error;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Odvodí public phase z bezpečných details, jinak použije render. */
|
|
241
|
+
function resolveProjectedFailurePhase(
|
|
242
|
+
error: reactAdapterError.ReactAdapterError,
|
|
243
|
+
): reactAdapterError.ReactAdapterErrorPhase {
|
|
244
|
+
const phase = error.details.phase;
|
|
245
|
+
return phase === "configuration"
|
|
246
|
+
|| phase === "resolve"
|
|
247
|
+
|| phase === "load"
|
|
248
|
+
|| phase === "render"
|
|
249
|
+
|| phase === "customRender"
|
|
250
|
+
|| phase === "fallback"
|
|
251
|
+
|| phase === "policy"
|
|
252
|
+
|| phase === "projection"
|
|
253
|
+
|| phase === "callback"
|
|
254
|
+
|| phase === "lifecycle"
|
|
255
|
+
? phase
|
|
256
|
+
: "render";
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Projektuje pouze bezpečnou identitu current managed-lazy contribution. */
|
|
260
|
+
function createManagedLazyContributionMetadata<
|
|
261
|
+
Point extends reactExtensionPoint.AnyReactExtensionPoint,
|
|
262
|
+
>(
|
|
263
|
+
seed: outletProjection.OutletLoadingMembershipSeed<Point>,
|
|
264
|
+
): reactAdapterDiagnostics.ReadonlyContributionMetadata {
|
|
265
|
+
const contribution = seed.renderState.contribution;
|
|
266
|
+
return Object.freeze({
|
|
267
|
+
registrationId: seed.registrationId,
|
|
268
|
+
contributionId: contribution.contributionId,
|
|
269
|
+
...(contribution.contributorId === undefined
|
|
270
|
+
? {}
|
|
271
|
+
: { contributorId: contribution.contributorId }),
|
|
272
|
+
...(contribution.label === undefined ? {} : { label: contribution.label }),
|
|
273
|
+
...(contribution.localName === undefined
|
|
274
|
+
? {}
|
|
275
|
+
: { localName: contribution.localName }),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** Ověří active live registry bez propouštění hostile state accessoru. */
|
|
280
|
+
function isManagedLazyRegistryActive(rawRegistry: object): boolean {
|
|
281
|
+
try {
|
|
282
|
+
return Reflect.get(rawRegistry, "state", rawRegistry) === "active";
|
|
283
|
+
} catch {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Interní class error boundary jedné exact Core membership.
|
|
290
|
+
*/
|
|
291
|
+
export class OutletMembershipBoundary<
|
|
292
|
+
Point extends reactExtensionPoint.AnyReactExtensionPoint,
|
|
293
|
+
> extends react.Component<
|
|
294
|
+
OutletMembershipBoundaryProps<Point>,
|
|
295
|
+
OutletMembershipBoundaryState
|
|
296
|
+
> {
|
|
297
|
+
/** Current identity/generation/presentation state. */
|
|
298
|
+
public override state: OutletMembershipBoundaryState;
|
|
299
|
+
|
|
300
|
+
/** Current commit-owned boundary ID lease. */
|
|
301
|
+
#boundaryLease: outletCommitLease.OutletCommitLease | undefined;
|
|
302
|
+
/** Membership token, pro který current lease vznikla. */
|
|
303
|
+
#boundaryLeaseToken: outletMembership.OutletMembershipToken | undefined;
|
|
304
|
+
/** Current mutable primary attempt authority. */
|
|
305
|
+
#primaryAttempt: OutletPrimaryFailureAttempt | undefined;
|
|
306
|
+
/** Poslední retry generation již doručená onRetry callbacku. */
|
|
307
|
+
#reportedRetryGeneration = 0;
|
|
308
|
+
/** Current resource-free nebo started managed-lazy operation. */
|
|
309
|
+
#lazyOperation: managedLazyGeneration.ManagedLazyGenerationOperation | undefined;
|
|
310
|
+
/** Membership token owning current lazy operation. */
|
|
311
|
+
#lazyOperationMembershipToken: outletMembership.OutletMembershipToken | undefined;
|
|
312
|
+
/** Retry token owning current lazy operation. */
|
|
313
|
+
#lazyOperationRetryToken: OutletRetryGenerationToken | undefined;
|
|
314
|
+
/** Zabraňuje novému attemptu během loading-error invalidace a abortu. */
|
|
315
|
+
#lazyStartBarrier = 0;
|
|
316
|
+
/** Terminální guard skutečného unmountu i Strict grace mezery. */
|
|
317
|
+
#unmounted = false;
|
|
318
|
+
|
|
319
|
+
/** Vytvoří resource-free initial state a operation bez loader side effectu. */
|
|
320
|
+
public constructor(props: OutletMembershipBoundaryProps<Point>) {
|
|
321
|
+
super(props);
|
|
322
|
+
this.state = createMembershipBoundaryState(props);
|
|
323
|
+
this.#synchronizeLazyOperation();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** Capture descendant error bez sinku, callbacku, policy nebo Core callu. */
|
|
327
|
+
public static getDerivedStateFromError(cause: unknown): Partial<OutletMembershipBoundaryState> {
|
|
328
|
+
return {
|
|
329
|
+
capturedCause: cause,
|
|
330
|
+
errorAttemptToken: Object.freeze({}),
|
|
331
|
+
hasCapturedCause: true,
|
|
332
|
+
mode: "captured",
|
|
333
|
+
primaryError: undefined,
|
|
334
|
+
retry: undefined,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Resetuje celý boundary state před renderem jiné identity tuple. */
|
|
339
|
+
public static getDerivedStateFromProps(
|
|
340
|
+
props: OutletMembershipBoundaryProps<reactExtensionPoint.AnyReactExtensionPoint>,
|
|
341
|
+
state: OutletMembershipBoundaryState,
|
|
342
|
+
): OutletMembershipBoundaryState | null {
|
|
343
|
+
return props.rawRegistry === state.rawRegistry
|
|
344
|
+
&& props.membership.seed.registrationId === state.registrationId
|
|
345
|
+
&& props.membership.token === state.membershipToken
|
|
346
|
+
? null
|
|
347
|
+
: createMembershipBoundaryState(props, state.subtreeGeneration + 1);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** Commit acquire boundary identity a případné synthetic seed failure. */
|
|
351
|
+
public override componentDidMount(): void {
|
|
352
|
+
this.#unmounted = false;
|
|
353
|
+
this.#synchronizeBoundaryLease();
|
|
354
|
+
this.#reactivatePreservedPrimaryAttempt();
|
|
355
|
+
const operationChanged = this.#synchronizeLazyOperation();
|
|
356
|
+
this.#commitProjectedFailure();
|
|
357
|
+
if (operationChanged) {
|
|
358
|
+
this.forceUpdate();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** Synchronizuje identity switch, committed retry event a current seed failure. */
|
|
363
|
+
public override componentDidUpdate(
|
|
364
|
+
_previousProps: OutletMembershipBoundaryProps<Point>,
|
|
365
|
+
previousState: OutletMembershipBoundaryState,
|
|
366
|
+
): void {
|
|
367
|
+
if (previousState.membershipToken !== this.state.membershipToken
|
|
368
|
+
|| previousState.rawRegistry !== this.state.rawRegistry
|
|
369
|
+
|| previousState.registrationId !== this.state.registrationId) {
|
|
370
|
+
this.#invalidatePrimaryAttempt();
|
|
371
|
+
const dispatchAbort = this.#invalidateLazyOperation();
|
|
372
|
+
dispatchAbort();
|
|
373
|
+
this.#reportedRetryGeneration = 0;
|
|
374
|
+
}
|
|
375
|
+
this.#synchronizeBoundaryLease();
|
|
376
|
+
this.#reactivatePreservedPrimaryAttempt();
|
|
377
|
+
const operationChanged = this.#synchronizeLazyOperation();
|
|
378
|
+
if (previousState.retryGeneration !== this.state.retryGeneration) {
|
|
379
|
+
this.#reportCommittedRetry();
|
|
380
|
+
}
|
|
381
|
+
this.#commitProjectedFailure();
|
|
382
|
+
if (operationChanged) {
|
|
383
|
+
this.forceUpdate();
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** Commit point primary render chyby spustí pipeline právě jednou. */
|
|
388
|
+
public override componentDidCatch(cause: unknown, info: react.ErrorInfo): void {
|
|
389
|
+
const attemptToken = this.state.errorAttemptToken;
|
|
390
|
+
if (!this.state.hasCapturedCause || attemptToken === undefined) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
const phase = this.#readRenderFailurePhase();
|
|
394
|
+
const reactComponentStack = outletErrorPipeline.normalizeReactComponentStack(
|
|
395
|
+
info.componentStack,
|
|
396
|
+
);
|
|
397
|
+
if (this.state.lazyStatus !== "loading") {
|
|
398
|
+
this.#commitPrimaryFailure(
|
|
399
|
+
attemptToken,
|
|
400
|
+
"REACT_RENDER_FAILED",
|
|
401
|
+
phase,
|
|
402
|
+
cause,
|
|
403
|
+
reactComponentStack,
|
|
404
|
+
);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const dispatchAbort = this.#invalidateLazyOperation();
|
|
409
|
+
this.#lazyStartBarrier += 1;
|
|
410
|
+
try {
|
|
411
|
+
this.#commitPrimaryFailure(
|
|
412
|
+
attemptToken,
|
|
413
|
+
"REACT_RENDER_FAILED",
|
|
414
|
+
phase,
|
|
415
|
+
cause,
|
|
416
|
+
reactComponentStack,
|
|
417
|
+
);
|
|
418
|
+
} finally {
|
|
419
|
+
try {
|
|
420
|
+
dispatchAbort();
|
|
421
|
+
} finally {
|
|
422
|
+
this.#lazyStartBarrier -= 1;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/** Synchronous invalidace před release commit-owned boundary lease. */
|
|
428
|
+
public override componentWillUnmount(): void {
|
|
429
|
+
this.#unmounted = true;
|
|
430
|
+
this.#invalidatePrimaryAttempt();
|
|
431
|
+
this.#releaseBoundaryLease();
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/** Renderuje current membership, provisional null, fallback nebo exact escalation. */
|
|
435
|
+
public override render(): react.ReactNode {
|
|
436
|
+
if (this.state.mode === "escalating") {
|
|
437
|
+
throw this.state.escalationCause;
|
|
438
|
+
}
|
|
439
|
+
if (this.state.mode === "captured" || this.state.mode === "hidden") {
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
if (this.state.mode === "fallback") {
|
|
443
|
+
return this.#renderFallback();
|
|
444
|
+
}
|
|
445
|
+
const seed = this.props.membership.seed;
|
|
446
|
+
if (seed.kind === "failed") {
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
if (seed.kind === "loading" && this.state.lazyStatus === "failed") {
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
const rendered = react.createElement(OutletMembershipRenderer<Point>, {
|
|
453
|
+
key: `${this.state.subtreeGeneration}:${this.state.retryGeneration}`,
|
|
454
|
+
input: this.props.input,
|
|
455
|
+
seed: this.#createCurrentRenderSeed(seed),
|
|
456
|
+
});
|
|
457
|
+
const operation = this.#readCurrentLazyOperation();
|
|
458
|
+
return operation === undefined
|
|
459
|
+
? rendered
|
|
460
|
+
: react.createElement(
|
|
461
|
+
managedLazyGeneration.ManagedLazyCommitLease,
|
|
462
|
+
{ children: rendered, operation },
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Převede loaded lazy componentu na ready seed s nejnovějším render inputem.
|
|
468
|
+
*/
|
|
469
|
+
#createCurrentRenderSeed(
|
|
470
|
+
seed:
|
|
471
|
+
| outletProjection.OutletReadyMembershipSeed<Point>
|
|
472
|
+
| outletProjection.OutletLoadingMembershipSeed<Point>,
|
|
473
|
+
):
|
|
474
|
+
| outletProjection.OutletReadyMembershipSeed<Point>
|
|
475
|
+
| outletProjection.OutletLoadingMembershipSeed<Point> {
|
|
476
|
+
if (seed.kind !== "loading" || this.state.lazyStatus !== "ready") {
|
|
477
|
+
return seed;
|
|
478
|
+
}
|
|
479
|
+
const component = this.state.lazyComponent as contributionComponent.ReactContributionComponent<
|
|
480
|
+
contribKitCore.ExtensionPointContext<Point>,
|
|
481
|
+
contribKitCore.ExtensionPointData<Point>
|
|
482
|
+
>;
|
|
483
|
+
const componentProps = Object.freeze({
|
|
484
|
+
context: seed.renderState.context,
|
|
485
|
+
contribution: seed.renderState.contribution,
|
|
486
|
+
placement: seed.renderState.placement,
|
|
487
|
+
});
|
|
488
|
+
return Object.freeze({
|
|
489
|
+
kind: "ready",
|
|
490
|
+
registrationId: seed.registrationId,
|
|
491
|
+
renderState: Object.freeze({
|
|
492
|
+
...seed.renderState,
|
|
493
|
+
component,
|
|
494
|
+
componentProps,
|
|
495
|
+
}),
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/** Vrátí operation pouze pro exact renderovanou loading generation. */
|
|
500
|
+
#readCurrentLazyOperation(): managedLazyGeneration.ManagedLazyGenerationOperation | undefined {
|
|
501
|
+
return this.state.lazyStatus === "loading"
|
|
502
|
+
&& this.#lazyStartBarrier === 0
|
|
503
|
+
&& this.#lazyOperationMembershipToken === this.state.membershipToken
|
|
504
|
+
&& this.#lazyOperationRetryToken === this.state.retryToken
|
|
505
|
+
? this.#lazyOperation
|
|
506
|
+
: undefined;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Vytvoří nejvýše jednu resource-free operation pro exact retry generation.
|
|
511
|
+
*/
|
|
512
|
+
#synchronizeLazyOperation(): boolean {
|
|
513
|
+
const load = this.state.lazyLoad;
|
|
514
|
+
const seed = this.props.membership.seed;
|
|
515
|
+
const outletLease = this.props.outletLease;
|
|
516
|
+
if (outletLease === undefined
|
|
517
|
+
|| this.state.lazyStatus !== "loading" || load === undefined
|
|
518
|
+
|| seed.kind !== "loading" || this.#lazyStartBarrier > 0) {
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
if (this.#lazyOperation !== undefined
|
|
522
|
+
&& this.#lazyOperationMembershipToken === this.state.membershipToken
|
|
523
|
+
&& this.#lazyOperationRetryToken === this.state.retryToken) {
|
|
524
|
+
this.#lazyOperation.requestStartRecheck();
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const dispatchPreviousAbort = this.#invalidateLazyOperation();
|
|
529
|
+
dispatchPreviousAbort();
|
|
530
|
+
const membershipToken = this.state.membershipToken;
|
|
531
|
+
const retryToken = this.state.retryToken;
|
|
532
|
+
const retryGeneration = this.state.retryGeneration;
|
|
533
|
+
const scope = this.props.scope;
|
|
534
|
+
const scopeGeneration = this.props.scopeGeneration;
|
|
535
|
+
const rawRegistry = this.props.rawRegistry;
|
|
536
|
+
const configuration = this.props.input.configuration;
|
|
537
|
+
const contribution = createManagedLazyContributionMetadata(seed);
|
|
538
|
+
const point = reactAdapterDiagnostics.createPointMetadata(seed.renderState.point);
|
|
539
|
+
let diagnosticChannel: OutletManagedLazyDiagnosticChannel | undefined;
|
|
540
|
+
let lifecycleFailureReported = false;
|
|
541
|
+
let operation: managedLazyGeneration.ManagedLazyGenerationOperation;
|
|
542
|
+
/** Otestuje current operation včetně committed outlet a scope lease. */
|
|
543
|
+
function isCurrentOperation(boundary: OutletMembershipBoundary<Point>): boolean {
|
|
544
|
+
return boundary.#lazyOperation === operation
|
|
545
|
+
&& boundary.state.lazyStatus === "loading"
|
|
546
|
+
&& boundary.#lazyStartBarrier === 0
|
|
547
|
+
&& boundary.#isGenerationCurrent(membershipToken, retryToken);
|
|
548
|
+
}
|
|
549
|
+
operation = new managedLazyGeneration.ManagedLazyGenerationOperation(
|
|
550
|
+
retryGeneration,
|
|
551
|
+
load,
|
|
552
|
+
Object.freeze({
|
|
553
|
+
isCurrent: (): boolean => isCurrentOperation(this),
|
|
554
|
+
onResolved: (component: unknown): void => {
|
|
555
|
+
this.#publishLazyComponent(operation, membershipToken, retryToken, component);
|
|
556
|
+
},
|
|
557
|
+
onFailed: (failure: managedLazyGeneration.ManagedLazyGenerationFailure): void => {
|
|
558
|
+
this.#publishLazyFailure(operation, membershipToken, retryToken, failure);
|
|
559
|
+
},
|
|
560
|
+
onLate: (outcome: "resolved" | "rejected"): void => {
|
|
561
|
+
this.#reportLateLazyResult(operation, diagnosticChannel, outcome);
|
|
562
|
+
},
|
|
563
|
+
onLifecycleFailure: (cause: unknown): void => {
|
|
564
|
+
if (lifecycleFailureReported) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
lifecycleFailureReported = true;
|
|
568
|
+
this.#reportLazyLifecycleFailure(diagnosticChannel, cause);
|
|
569
|
+
},
|
|
570
|
+
registerInvalidation: (
|
|
571
|
+
invalidate: () => managedLazyGeneration.ManagedLazyAbortDispatch,
|
|
572
|
+
): contribKitCore.Unsubscribe => {
|
|
573
|
+
const boundaryLease = this.#boundaryLease;
|
|
574
|
+
if (boundaryLease !== undefined
|
|
575
|
+
&& boundaryLease.isCurrent()
|
|
576
|
+
&& outletLease.isCurrent()
|
|
577
|
+
&& scope.isGenerationActive(scopeGeneration)) {
|
|
578
|
+
diagnosticChannel = Object.freeze({
|
|
579
|
+
boundaryId: boundaryLease.id,
|
|
580
|
+
configuration,
|
|
581
|
+
contribution,
|
|
582
|
+
outletLease,
|
|
583
|
+
...(point === undefined ? {} : { point }),
|
|
584
|
+
rawRegistry,
|
|
585
|
+
retryGeneration,
|
|
586
|
+
scope,
|
|
587
|
+
scopeGeneration,
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
return scope.registerEpochAbortable(
|
|
591
|
+
scopeGeneration,
|
|
592
|
+
(): managedLazyGeneration.ManagedLazyAbortDispatch => {
|
|
593
|
+
if (this.#lazyOperation === operation) {
|
|
594
|
+
this.#lazyOperation = undefined;
|
|
595
|
+
this.#lazyOperationMembershipToken = undefined;
|
|
596
|
+
this.#lazyOperationRetryToken = undefined;
|
|
597
|
+
}
|
|
598
|
+
return invalidate();
|
|
599
|
+
},
|
|
600
|
+
);
|
|
601
|
+
},
|
|
602
|
+
}),
|
|
603
|
+
);
|
|
604
|
+
this.#lazyOperation = operation;
|
|
605
|
+
this.#lazyOperationMembershipToken = membershipToken;
|
|
606
|
+
this.#lazyOperationRetryToken = retryToken;
|
|
607
|
+
return true;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/** Doručí jeden safe late-result event pouze do stále vlastněného channelu. */
|
|
611
|
+
#reportLateLazyResult(
|
|
612
|
+
operation: managedLazyGeneration.ManagedLazyGenerationOperation,
|
|
613
|
+
channel: OutletManagedLazyDiagnosticChannel | undefined,
|
|
614
|
+
outcome: "resolved" | "rejected",
|
|
615
|
+
): void {
|
|
616
|
+
if (channel === undefined
|
|
617
|
+
|| !channel.scope.isGenerationActive(channel.scopeGeneration)
|
|
618
|
+
|| !channel.outletLease.isCurrent()
|
|
619
|
+
|| !isManagedLazyRegistryActive(channel.rawRegistry)
|
|
620
|
+
|| !channel.scope.claimDiagnosticAttempt(operation.loadAttemptToken)) {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
reactAdapterDiagnostics.reportReactAdapterDiagnostic(
|
|
624
|
+
reactAdapterDiagnostics.createLateLazyResultDiagnostic({
|
|
625
|
+
boundaryId: channel.boundaryId,
|
|
626
|
+
contribution: channel.contribution,
|
|
627
|
+
outcome,
|
|
628
|
+
outletId: channel.outletLease.id,
|
|
629
|
+
...(channel.point === undefined ? {} : { point: channel.point }),
|
|
630
|
+
retryGeneration: channel.retryGeneration,
|
|
631
|
+
}),
|
|
632
|
+
channel.configuration,
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/** Publikuje abort/unsubscribe failure bez UI, callback nebo policy autority. */
|
|
637
|
+
#reportLazyLifecycleFailure(
|
|
638
|
+
channel: OutletManagedLazyDiagnosticChannel | undefined,
|
|
639
|
+
cause: unknown,
|
|
640
|
+
): void {
|
|
641
|
+
if (channel === undefined
|
|
642
|
+
|| !channel.scope.isGenerationActive(channel.scopeGeneration)
|
|
643
|
+
|| !channel.outletLease.isCurrent()
|
|
644
|
+
|| !isManagedLazyRegistryActive(channel.rawRegistry)) {
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
const error = reactAdapterDiagnostics.createReactAdapterErrorEvent({
|
|
648
|
+
boundaryId: channel.boundaryId,
|
|
649
|
+
code: "REACT_CALLBACK_FAILED",
|
|
650
|
+
cause,
|
|
651
|
+
contribution: channel.contribution,
|
|
652
|
+
outletId: channel.outletLease.id,
|
|
653
|
+
phase: "lifecycle",
|
|
654
|
+
...(channel.point === undefined ? {} : { point: channel.point }),
|
|
655
|
+
retryGeneration: channel.retryGeneration,
|
|
656
|
+
});
|
|
657
|
+
reactAdapterDiagnostics.reportReactAdapterDiagnostic(
|
|
658
|
+
reactAdapterDiagnostics.createErrorDiagnostic(error),
|
|
659
|
+
channel.configuration,
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/** Publikuje validní componentu pouze do exact current loading generation. */
|
|
664
|
+
#publishLazyComponent(
|
|
665
|
+
operation: managedLazyGeneration.ManagedLazyGenerationOperation,
|
|
666
|
+
membershipToken: outletMembership.OutletMembershipToken,
|
|
667
|
+
retryToken: OutletRetryGenerationToken,
|
|
668
|
+
component: unknown,
|
|
669
|
+
): void {
|
|
670
|
+
if (this.#lazyOperation !== operation
|
|
671
|
+
|| this.state.lazyStatus !== "loading"
|
|
672
|
+
|| !this.#isGenerationCurrent(membershipToken, retryToken)) {
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
this.setState({ lazyComponent: component, lazyStatus: "ready" });
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/** Převede current lazy failure do committed contribution error pipeline. */
|
|
679
|
+
#publishLazyFailure(
|
|
680
|
+
operation: managedLazyGeneration.ManagedLazyGenerationOperation,
|
|
681
|
+
membershipToken: outletMembership.OutletMembershipToken,
|
|
682
|
+
retryToken: OutletRetryGenerationToken,
|
|
683
|
+
failure: managedLazyGeneration.ManagedLazyGenerationFailure,
|
|
684
|
+
): void {
|
|
685
|
+
if (this.#lazyOperation !== operation
|
|
686
|
+
|| this.state.lazyStatus !== "loading"
|
|
687
|
+
|| !this.#isGenerationCurrent(membershipToken, retryToken)) {
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
const cause = Object.hasOwn(failure, "cause")
|
|
691
|
+
? failure.cause
|
|
692
|
+
: new reactAdapterErrorRuntime.ReactAdapterError(
|
|
693
|
+
"REACT_POINT_IMPLEMENTATION_INVALID",
|
|
694
|
+
"The managed React contribution loader returned a non-component value.",
|
|
695
|
+
{ details: { field: "loader.result", phase: "load" } },
|
|
696
|
+
);
|
|
697
|
+
const errorAttemptToken = Object.freeze({});
|
|
698
|
+
this.setState(
|
|
699
|
+
{ lazyComponent: undefined, lazyStatus: "failed" },
|
|
700
|
+
/** Pipeline smí začít až po commitu failed presentation. */
|
|
701
|
+
(): void => {
|
|
702
|
+
if (this.#lazyOperation !== operation
|
|
703
|
+
|| this.state.lazyStatus !== "failed"
|
|
704
|
+
|| !this.#isGenerationCurrent(membershipToken, retryToken)) {
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
this.#commitPrimaryFailure(
|
|
708
|
+
errorAttemptToken,
|
|
709
|
+
failure.code,
|
|
710
|
+
"load",
|
|
711
|
+
cause,
|
|
712
|
+
undefined,
|
|
713
|
+
);
|
|
714
|
+
},
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/** Zneplatní current lazy operation a vrátí oddělený abort dispatch. */
|
|
719
|
+
#invalidateLazyOperation(): managedLazyGeneration.ManagedLazyAbortDispatch {
|
|
720
|
+
const operation = this.#lazyOperation;
|
|
721
|
+
this.#lazyOperation = undefined;
|
|
722
|
+
this.#lazyOperationMembershipToken = undefined;
|
|
723
|
+
this.#lazyOperationRetryToken = undefined;
|
|
724
|
+
return operation?.invalidate() ?? managedLazyGeneration.skipManagedLazyAbortDispatch;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/** Ověří a případně obnoví commit lease current membership tokenu. */
|
|
728
|
+
#synchronizeBoundaryLease(): void {
|
|
729
|
+
const membershipToken = this.state.membershipToken;
|
|
730
|
+
if (this.props.outletLease?.isCurrent() !== true) {
|
|
731
|
+
this.#releaseBoundaryLease();
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
if (this.#boundaryLeaseToken === membershipToken
|
|
735
|
+
&& this.#boundaryLease?.isCurrent() === true) {
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
this.#releaseBoundaryLease();
|
|
739
|
+
this.#boundaryLease = this.props.scope.acquireCommitLease(
|
|
740
|
+
this.props.scopeGeneration,
|
|
741
|
+
"boundary",
|
|
742
|
+
membershipToken,
|
|
743
|
+
);
|
|
744
|
+
this.#boundaryLeaseToken = membershipToken;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/** Idempotentně release boundary lease bez změny Core registry. */
|
|
748
|
+
#releaseBoundaryLease(): void {
|
|
749
|
+
this.#boundaryLease?.release();
|
|
750
|
+
this.#boundaryLease = undefined;
|
|
751
|
+
this.#boundaryLeaseToken = undefined;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/** Ověří current committed membership, retry generation, outlet i scope lease. */
|
|
755
|
+
#isGenerationCurrent(
|
|
756
|
+
membershipToken: outletMembership.OutletMembershipToken,
|
|
757
|
+
retryToken: OutletRetryGenerationToken,
|
|
758
|
+
): boolean {
|
|
759
|
+
return !this.#unmounted
|
|
760
|
+
&& this.state.membershipToken === membershipToken
|
|
761
|
+
&& this.state.retryToken === retryToken
|
|
762
|
+
&& this.props.rawRegistry === this.state.rawRegistry
|
|
763
|
+
&& this.props.membership.token === membershipToken
|
|
764
|
+
&& this.props.membership.seed.registrationId === this.state.registrationId
|
|
765
|
+
&& this.#boundaryLeaseToken === membershipToken
|
|
766
|
+
&& this.#boundaryLease?.isCurrent() === true
|
|
767
|
+
&& this.props.outletLease?.isCurrent() === true;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/** Ověří celý primary attempt včetně sync retry invalidace. */
|
|
771
|
+
#isPrimaryAttemptCurrent(attempt: OutletPrimaryFailureAttempt): boolean {
|
|
772
|
+
return attempt.authoritative
|
|
773
|
+
&& this.#primaryAttempt === attempt
|
|
774
|
+
&& this.#boundaryLease === attempt.boundaryLease
|
|
775
|
+
&& attempt.boundaryLease.isCurrent()
|
|
776
|
+
&& this.#isGenerationCurrent(attempt.membershipToken, attempt.retryToken);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/** Vytvoří retry capability svázanou s current boundary lease a UI generation. */
|
|
780
|
+
#createPrimaryAttempt(
|
|
781
|
+
errorAttemptToken: object,
|
|
782
|
+
boundaryLease: outletCommitLease.OutletCommitLease,
|
|
783
|
+
): OutletPrimaryFailureAttempt {
|
|
784
|
+
const attempt: OutletPrimaryFailureAttempt = {
|
|
785
|
+
authoritative: true,
|
|
786
|
+
boundaryLease,
|
|
787
|
+
published: false,
|
|
788
|
+
errorAttemptToken,
|
|
789
|
+
membershipToken: this.state.membershipToken,
|
|
790
|
+
retry: (): void => {},
|
|
791
|
+
retryToken: this.state.retryToken,
|
|
792
|
+
};
|
|
793
|
+
const boundary = this;
|
|
794
|
+
/** Přijme právě první current retry call dané primary capability. */
|
|
795
|
+
function retry(): void {
|
|
796
|
+
boundary.#acceptRetry(attempt);
|
|
797
|
+
}
|
|
798
|
+
attempt.retry = Object.freeze(retry);
|
|
799
|
+
return attempt;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/** Zneplatní primary capability dříve než začne další lifecycle práce. */
|
|
803
|
+
#invalidatePrimaryAttempt(): void {
|
|
804
|
+
if (this.#primaryAttempt !== undefined) {
|
|
805
|
+
this.#primaryAttempt.authoritative = false;
|
|
806
|
+
this.#primaryAttempt = undefined;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* Naváže zachovanou failed presentation na novou Activity epochu bez replaye pipeline.
|
|
812
|
+
*/
|
|
813
|
+
#reactivatePreservedPrimaryAttempt(): void {
|
|
814
|
+
if (this.state.primaryError === undefined
|
|
815
|
+
|| this.state.retry === undefined
|
|
816
|
+
|| (this.state.mode !== "fallback" && this.state.mode !== "hidden")) {
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
const currentAttempt = this.#primaryAttempt;
|
|
820
|
+
if (currentAttempt !== undefined && this.#isPrimaryAttemptCurrent(currentAttempt)) {
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
this.#invalidatePrimaryAttempt();
|
|
824
|
+
const boundaryLease = this.#boundaryLease;
|
|
825
|
+
if (boundaryLease === undefined
|
|
826
|
+
|| !this.#isGenerationCurrent(this.state.membershipToken, this.state.retryToken)) {
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
const attempt = this.#createPrimaryAttempt(Object.freeze({}), boundaryLease);
|
|
830
|
+
attempt.published = true;
|
|
831
|
+
this.#primaryAttempt = attempt;
|
|
832
|
+
this.setState({ retry: attempt.retry });
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/** Převede current failed projection na committed primary pipeline. */
|
|
836
|
+
#commitProjectedFailure(): void {
|
|
837
|
+
const seed = this.props.membership.seed;
|
|
838
|
+
if (seed.kind !== "failed" || this.state.mode !== "rendering") {
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
this.#commitPrimaryFailure(
|
|
842
|
+
Object.freeze({}),
|
|
843
|
+
seed.error.code,
|
|
844
|
+
resolveProjectedFailurePhase(seed.error),
|
|
845
|
+
resolveProjectedFailureCause(seed.error),
|
|
846
|
+
undefined,
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/** Spustí at-most-once pipeline jednoho exact membership/error attemptu. */
|
|
851
|
+
#commitPrimaryFailure(
|
|
852
|
+
errorAttemptToken: object,
|
|
853
|
+
code: reactAdapterError.ReactAdapterErrorCode,
|
|
854
|
+
phase: reactAdapterError.ReactAdapterErrorPhase,
|
|
855
|
+
cause: unknown,
|
|
856
|
+
reactComponentStack: string | undefined,
|
|
857
|
+
): void {
|
|
858
|
+
this.#synchronizeBoundaryLease();
|
|
859
|
+
const membershipToken = this.state.membershipToken;
|
|
860
|
+
const retryToken = this.state.retryToken;
|
|
861
|
+
if (!this.#isGenerationCurrent(membershipToken, retryToken)) {
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
if (this.#primaryAttempt?.errorAttemptToken === errorAttemptToken) {
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
this.#invalidatePrimaryAttempt();
|
|
868
|
+
const lease = this.#boundaryLease;
|
|
869
|
+
const outletLease = this.props.outletLease;
|
|
870
|
+
if (lease === undefined || outletLease === undefined) {
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
const attempt = this.#createPrimaryAttempt(errorAttemptToken, lease);
|
|
874
|
+
this.#primaryAttempt = attempt;
|
|
875
|
+
if (!this.props.scope.claimDiagnosticAttempt(errorAttemptToken)) {
|
|
876
|
+
attempt.authoritative = false;
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
attempt.published = true;
|
|
880
|
+
const seed = this.props.membership.seed;
|
|
881
|
+
const decision = outletErrorPipeline.runOutletPrimaryErrorPipeline({
|
|
882
|
+
boundaryId: lease.id,
|
|
883
|
+
code,
|
|
884
|
+
configuration: this.props.input.configuration,
|
|
885
|
+
context: seed.renderState.context,
|
|
886
|
+
contribution: seed.renderState.contribution,
|
|
887
|
+
cause,
|
|
888
|
+
hasFallback: this.props.input.renderContributionError !== undefined,
|
|
889
|
+
isolateErrors: this.props.input.isolateErrors,
|
|
890
|
+
isCurrent: (): boolean => this.#isPrimaryAttemptCurrent(attempt),
|
|
891
|
+
onError: this.props.input.onError as outletInput.CapturedOutletCallback<
|
|
892
|
+
outletErrorPipeline.OutletContributionErrorEmission,
|
|
893
|
+
unknown
|
|
894
|
+
> | undefined,
|
|
895
|
+
outletId: outletLease.id,
|
|
896
|
+
phase,
|
|
897
|
+
point: seed.renderState.point,
|
|
898
|
+
...(reactComponentStack === undefined ? {} : { reactComponentStack }),
|
|
899
|
+
registrationId: seed.registrationId,
|
|
900
|
+
retry: attempt.retry,
|
|
901
|
+
retryGeneration: this.state.retryGeneration,
|
|
902
|
+
});
|
|
903
|
+
if (decision.kind === "stale" || !this.#isPrimaryAttemptCurrent(attempt)) {
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
if (decision.kind === "hide") {
|
|
907
|
+
this.setState({ mode: "hidden", primaryError: decision.error, retry: attempt.retry });
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
if (decision.kind === "renderFallback") {
|
|
911
|
+
this.setState({
|
|
912
|
+
mode: "fallback",
|
|
913
|
+
primaryError: decision.error,
|
|
914
|
+
retry: attempt.retry,
|
|
915
|
+
});
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
this.setState({
|
|
919
|
+
escalationCause: decision.cause,
|
|
920
|
+
mode: "escalating",
|
|
921
|
+
primaryError: decision.error,
|
|
922
|
+
retry: attempt.retry,
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/** Sync accepted retry invaliduje starý attempt a naplánuje novou subtree generation. */
|
|
927
|
+
#acceptRetry(attempt: OutletPrimaryFailureAttempt): void {
|
|
928
|
+
if (!this.#isPrimaryAttemptCurrent(attempt)) {
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
attempt.authoritative = false;
|
|
932
|
+
this.#primaryAttempt = undefined;
|
|
933
|
+
const dispatchAbort = this.#invalidateLazyOperation();
|
|
934
|
+
dispatchAbort();
|
|
935
|
+
this.setState((state): Pick<
|
|
936
|
+
OutletMembershipBoundaryState,
|
|
937
|
+
| "capturedCause"
|
|
938
|
+
| "escalationCause"
|
|
939
|
+
| "errorAttemptToken"
|
|
940
|
+
| "hasCapturedCause"
|
|
941
|
+
| "lazyComponent"
|
|
942
|
+
| "lazyStatus"
|
|
943
|
+
| "mode"
|
|
944
|
+
| "primaryError"
|
|
945
|
+
| "retry"
|
|
946
|
+
| "retryGeneration"
|
|
947
|
+
| "retryToken"
|
|
948
|
+
> => ({
|
|
949
|
+
capturedCause: undefined,
|
|
950
|
+
escalationCause: undefined,
|
|
951
|
+
errorAttemptToken: undefined,
|
|
952
|
+
hasCapturedCause: false,
|
|
953
|
+
lazyComponent: undefined,
|
|
954
|
+
lazyStatus: state.lazyLoad === undefined ? "inactive" : "loading",
|
|
955
|
+
mode: "rendering",
|
|
956
|
+
primaryError: undefined,
|
|
957
|
+
retry: undefined,
|
|
958
|
+
retryGeneration: state.retryGeneration + 1,
|
|
959
|
+
retryToken: createRetryGenerationToken(),
|
|
960
|
+
}));
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/** Doručí onRetry až po commitu nové current generation. */
|
|
964
|
+
#reportCommittedRetry(): void {
|
|
965
|
+
if (this.state.retryGeneration <= this.#reportedRetryGeneration) {
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
this.#reportedRetryGeneration = this.state.retryGeneration;
|
|
969
|
+
const lease = this.#boundaryLease;
|
|
970
|
+
const outletLease = this.props.outletLease;
|
|
971
|
+
const membershipToken = this.state.membershipToken;
|
|
972
|
+
const retryToken = this.state.retryToken;
|
|
973
|
+
if (lease === undefined
|
|
974
|
+
|| outletLease === undefined
|
|
975
|
+
|| !this.#isGenerationCurrent(membershipToken, retryToken)) {
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
const seed = this.props.membership.seed;
|
|
979
|
+
outletErrorPipeline.dispatchOutletRetryCallback({
|
|
980
|
+
boundaryId: lease.id,
|
|
981
|
+
configuration: this.props.input.configuration,
|
|
982
|
+
context: seed.renderState.context,
|
|
983
|
+
contribution: seed.renderState.contribution,
|
|
984
|
+
isCurrent: (): boolean => this.#isGenerationCurrent(membershipToken, retryToken),
|
|
985
|
+
onRetry: this.props.input.onRetry as outletInput.CapturedOutletCallback<
|
|
986
|
+
outletErrorPipeline.OutletRetryEvent,
|
|
987
|
+
unknown
|
|
988
|
+
> | undefined,
|
|
989
|
+
outletId: outletLease.id,
|
|
990
|
+
point: seed.renderState.point,
|
|
991
|
+
registrationId: seed.registrationId,
|
|
992
|
+
retryGeneration: this.state.retryGeneration,
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/** Odliší default render od custom ready/loading rendereru. */
|
|
997
|
+
#readRenderFailurePhase(): "render" | "customRender" {
|
|
998
|
+
if (this.props.membership.seed.kind === "loading"
|
|
999
|
+
&& this.state.lazyStatus !== "ready") {
|
|
1000
|
+
return this.props.input.renderLoading === undefined ? "render" : "customRender";
|
|
1001
|
+
}
|
|
1002
|
+
return this.props.input.renderContribution === undefined ? "render" : "customRender";
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
/** Renderuje current fallback pod sink-only reporter boundary. */
|
|
1006
|
+
#renderFallback(): react.ReactElement | null {
|
|
1007
|
+
const renderContributionError = this.props.input.renderContributionError;
|
|
1008
|
+
const error = this.state.primaryError;
|
|
1009
|
+
const retry = this.state.retry;
|
|
1010
|
+
const lease = this.#boundaryLease;
|
|
1011
|
+
const outletLease = this.props.outletLease;
|
|
1012
|
+
if (renderContributionError === undefined
|
|
1013
|
+
|| error === undefined
|
|
1014
|
+
|| retry === undefined
|
|
1015
|
+
|| lease === undefined
|
|
1016
|
+
|| outletLease === undefined) {
|
|
1017
|
+
return null;
|
|
1018
|
+
}
|
|
1019
|
+
const seed = this.props.membership.seed;
|
|
1020
|
+
const membershipToken = this.state.membershipToken;
|
|
1021
|
+
const retryToken = this.state.retryToken;
|
|
1022
|
+
const reporterInput = {
|
|
1023
|
+
boundaryId: lease.id,
|
|
1024
|
+
configuration: this.props.input.configuration,
|
|
1025
|
+
context: seed.renderState.context,
|
|
1026
|
+
contribution: seed.renderState.contribution,
|
|
1027
|
+
isCurrent: (): boolean => this.#isGenerationCurrent(membershipToken, retryToken),
|
|
1028
|
+
outletId: outletLease.id,
|
|
1029
|
+
point: seed.renderState.point,
|
|
1030
|
+
registrationId: seed.registrationId,
|
|
1031
|
+
retryGeneration: this.state.retryGeneration,
|
|
1032
|
+
};
|
|
1033
|
+
const children = react.createElement(OutletContributionFallbackRenderer<Point>, {
|
|
1034
|
+
error,
|
|
1035
|
+
renderContributionError,
|
|
1036
|
+
retry,
|
|
1037
|
+
seed,
|
|
1038
|
+
});
|
|
1039
|
+
return react.createElement(
|
|
1040
|
+
outletReporterBoundary.OutletReporterBoundary<Point>,
|
|
1041
|
+
{
|
|
1042
|
+
children,
|
|
1043
|
+
errorInput: reporterInput,
|
|
1044
|
+
generationToken: retryToken,
|
|
1045
|
+
},
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
}
|