@bleedingdev/modern-js-federation-runtime 0.0.0 → 3.9.0-ultramodern.6

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.
Files changed (33) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/module-federation/consume-surface.js +199 -0
  3. package/dist/cjs/module-federation/distributed-ssr.js +135 -0
  4. package/dist/cjs/module-federation/index.js +316 -0
  5. package/dist/cjs/module-federation/last-known-good.js +157 -0
  6. package/dist/cjs/module-federation/manifest-recovery-runtime-plugin.js +130 -0
  7. package/dist/cjs/module-federation/surface-resolution-types.js +61 -0
  8. package/dist/esm/module-federation/consume-surface.mjs +158 -0
  9. package/dist/esm/module-federation/distributed-ssr.mjs +82 -0
  10. package/dist/esm/module-federation/index.mjs +115 -0
  11. package/dist/esm/module-federation/last-known-good.mjs +113 -0
  12. package/dist/esm/module-federation/manifest-recovery-runtime-plugin.mjs +90 -0
  13. package/dist/esm/module-federation/surface-resolution-types.mjs +8 -0
  14. package/dist/esm-node/module-federation/consume-surface.mjs +159 -0
  15. package/dist/esm-node/module-federation/distributed-ssr.mjs +83 -0
  16. package/dist/esm-node/module-federation/index.mjs +116 -0
  17. package/dist/esm-node/module-federation/last-known-good.mjs +114 -0
  18. package/dist/esm-node/module-federation/manifest-recovery-runtime-plugin.mjs +91 -0
  19. package/dist/esm-node/module-federation/surface-resolution-types.mjs +9 -0
  20. package/dist/types/module-federation/consume-surface.d.ts +104 -0
  21. package/dist/types/module-federation/distributed-ssr.d.ts +61 -0
  22. package/dist/types/module-federation/index.d.ts +71 -0
  23. package/dist/types/module-federation/last-known-good.d.ts +52 -0
  24. package/dist/types/module-federation/manifest-recovery-runtime-plugin.d.ts +25 -0
  25. package/dist/types/module-federation/surface-resolution-types.d.ts +23 -0
  26. package/package.json +86 -4
  27. package/src/module-federation/consume-surface.ts +369 -0
  28. package/src/module-federation/distributed-ssr.tsx +240 -0
  29. package/src/module-federation/index.ts +274 -0
  30. package/src/module-federation/last-known-good.ts +292 -0
  31. package/src/module-federation/manifest-recovery-runtime-plugin.ts +215 -0
  32. package/src/module-federation/surface-resolution-types.ts +60 -0
  33. package/README.md +0 -3
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Surface-resolution types for the module-federation runtime.
3
+ *
4
+ * Thin re-export of the canonical module in `@modern-js/surface-resolution`
5
+ * (`packages/toolkit/surface-resolution/src/surface-resolution/`,
6
+ * RESOLUTION-0001). Kept as a file so existing `./surface-resolution-types`
7
+ * imports stay stable. Only helpers the canonical module deliberately does not
8
+ * ship (the (ref, env) cache key) live here, implemented against canonical
9
+ * types.
10
+ */
11
+ import { type EnvironmentId, type ParsedSurfaceRef } from '@modern-js/surface-resolution';
12
+ export { type CompatibilityStatus, type CompatibilityVerdict, createDiscoveryError, type DiscoveryError, type DiscoveryErrorCode, type DiscoveryResult, type EnvironmentId, formatSurfaceRef, type ParsedSurfaceRef, parseSurfaceRef, type ResolvedDeliveryUnit, type ResolvedSurface, type ResolvedSurfaceKind, type ResolvedSurfaceLocation, type ResolvedSurfaceLocationPlatform, type SurfaceRefParseError, type SurfaceRefParseResult, type SurfaceResolutionProvider, validateSurfaceRef, } from '@modern-js/surface-resolution';
13
+ /** Stable cache/identity key for a (ref, env) pair. */
14
+ export declare function surfaceResolutionKey(ref: ParsedSurfaceRef, env: EnvironmentId): string;
15
+ /**
16
+ * Stable cache key for a (deliveryUnit, env) pair — ONE record per unit per
17
+ * env (ADR-0019 atomicity). All surfaces of a unit share this key so a refresh
18
+ * replaces the whole unit snapshot atomically for every surface, and two
19
+ * surfaces of the same unit can never be served from different cached
20
+ * snapshots. The ref's external-major participates in lookup validation (see
21
+ * the LKG provider), not in this key.
22
+ */
23
+ export declare function deliveryUnitResolutionKey(unitId: string, env: EnvironmentId): string;
package/package.json CHANGED
@@ -1,16 +1,98 @@
1
1
  {
2
2
  "name": "@bleedingdev/modern-js-federation-runtime",
3
- "version": "0.0.0",
4
- "description": "Package-name registration for the forthcoming UltraModern.js release. This setup version contains no runtime implementation.",
3
+ "version": "3.9.0-ultramodern.6",
4
+ "description": "Federated surface consumption, distributed SSR, and bounded manifest recovery.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "git+https://github.com/BleedingDev/ultramodern.js.git"
8
+ "url": "git+https://github.com/BleedingDev/ultramodern.js.git",
9
+ "directory": "packages/runtime/federation-runtime"
10
+ },
11
+ "engines": {
12
+ "node": ">=26.7.0"
13
+ },
14
+ "types": "./dist/types/module-federation/index.d.ts",
15
+ "main": "./dist/cjs/module-federation/index.js",
16
+ "module": "./dist/esm/module-federation/index.mjs",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/types/module-federation/index.d.ts",
20
+ "node": {
21
+ "import": "./dist/esm-node/module-federation/index.mjs",
22
+ "require": "./dist/cjs/module-federation/index.js"
23
+ },
24
+ "default": "./dist/esm/module-federation/index.mjs"
25
+ },
26
+ "./distributed-ssr": {
27
+ "types": "./dist/types/module-federation/distributed-ssr.d.ts",
28
+ "node": {
29
+ "import": "./dist/esm-node/module-federation/distributed-ssr.mjs",
30
+ "require": "./dist/cjs/module-federation/distributed-ssr.js"
31
+ },
32
+ "default": "./dist/esm/module-federation/distributed-ssr.mjs"
33
+ },
34
+ "./manifest-recovery-runtime-plugin": {
35
+ "types": "./dist/types/module-federation/manifest-recovery-runtime-plugin.d.ts",
36
+ "node": {
37
+ "import": "./dist/esm-node/module-federation/manifest-recovery-runtime-plugin.mjs",
38
+ "require": "./dist/cjs/module-federation/manifest-recovery-runtime-plugin.js"
39
+ },
40
+ "default": "./dist/esm/module-federation/manifest-recovery-runtime-plugin.mjs"
41
+ }
42
+ },
43
+ "typesVersions": {
44
+ "*": {
45
+ ".": [
46
+ "./dist/types/module-federation/index.d.ts",
47
+ "./src/module-federation/index.ts"
48
+ ],
49
+ "distributed-ssr": [
50
+ "./dist/types/module-federation/distributed-ssr.d.ts",
51
+ "./src/module-federation/distributed-ssr.tsx"
52
+ ],
53
+ "manifest-recovery-runtime-plugin": [
54
+ "./dist/types/module-federation/manifest-recovery-runtime-plugin.d.ts",
55
+ "./src/module-federation/manifest-recovery-runtime-plugin.ts"
56
+ ]
57
+ }
9
58
  },
10
59
  "files": [
11
- "README.md"
60
+ "dist",
61
+ "src"
12
62
  ],
63
+ "sideEffects": false,
64
+ "dependencies": {
65
+ "@modern-js/surface-resolution": "npm:@bleedingdev/modern-js-surface-resolution@3.9.0-ultramodern.6"
66
+ },
67
+ "peerDependencies": {
68
+ "@modern-js/runtime": "3.9.0-ultramodern.6",
69
+ "react": "^19.2.8"
70
+ },
71
+ "devDependencies": {
72
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.9.0-ultramodern.6",
73
+ "@modern-js/tsconfig": "npm:@bleedingdev/modern-js-tsconfig@3.9.0-ultramodern.6",
74
+ "@rsbuild/core": "2.2.3",
75
+ "@rslib/core": "1.0.0",
76
+ "@scripts/rstest-config": "2.66.0",
77
+ "@types/node": "^26.4.1",
78
+ "@types/react": "^19.2.18",
79
+ "@types/react-dom": "^19.2.7",
80
+ "@typescript/native-preview": "7.0.0-dev.20260707.2",
81
+ "esbuild": "^0.28.2",
82
+ "react": "^19.2.8",
83
+ "react-dom": "^19.2.8",
84
+ "typescript": "^7.0.2"
85
+ },
13
86
  "publishConfig": {
14
87
  "access": "public"
88
+ },
89
+ "scripts": {
90
+ "dev": "rslib build --watch",
91
+ "build": "rslib build",
92
+ "test": "rstest run"
93
+ },
94
+ "homepage": "https://github.com/BleedingDev/ultramodern.js#readme",
95
+ "bugs": {
96
+ "url": "https://github.com/BleedingDev/ultramodern.js/issues"
15
97
  }
16
98
  }
@@ -0,0 +1,369 @@
1
+ // @effect-diagnostics asyncFunction:off strictBooleanExpressions:off
2
+ /**
3
+ * G22 — mandatory degraded consumption API.
4
+ *
5
+ * CONTEXT.md (Degraded State): "consuming a surface obliges the consumer to
6
+ * define a degraded state". This module turns that obligation into a
7
+ * compile-level one: {@link SurfaceConsumerOptions.degraded} is a REQUIRED
8
+ * field, so a call site that forgets its fallback does not type-check.
9
+ *
10
+ * The consumer wraps the loadRemote-ish flow (resolve a SurfaceRef through a
11
+ * provider, then load the execution artifact from the resolved record) and
12
+ * guarantees isolation: any discovery/load failure is caught, classified,
13
+ * reported through the existing MF fallback telemetry, and handed to the
14
+ * required degraded handler.
15
+ *
16
+ * Criticality (owner-default 5: unclassified consumption is `critical`). For a
17
+ * `noncritical` consumption the degraded handler's result is returned and the
18
+ * failure is swallowed, so one failing consumption can never propagate to a
19
+ * sibling (CONTEXT.md: faults isolate at the vertical boundary). For a
20
+ * `critical` consumption the degraded handler still runs — telemetry and
21
+ * fallback-UI obligations hold — but the returned promise then REJECTS with the
22
+ * typed failure so callers and rollout machinery observe it. Critical
23
+ * consumption during a rollout window should be driven by expand/contract, not
24
+ * by silent degrade.
25
+ */
26
+ import {
27
+ classifyModuleFederationFallback,
28
+ createModuleFederationFallbackTelemetry,
29
+ emitModuleFederationFallbackTelemetry,
30
+ type ModuleFederationFallbackClassification,
31
+ type ModuleFederationFallbackTelemetryEmitOptions,
32
+ type ModuleFederationFallbackTelemetryPayload,
33
+ } from './index';
34
+ import {
35
+ createDiscoveryError,
36
+ type DiscoveryError,
37
+ type DiscoveryErrorCode,
38
+ type DiscoveryResult,
39
+ type EnvironmentId,
40
+ formatSurfaceRef,
41
+ type ParsedSurfaceRef,
42
+ parseSurfaceRef,
43
+ type ResolvedDeliveryUnit,
44
+ type SurfaceResolutionProvider,
45
+ validateSurfaceRef,
46
+ } from './surface-resolution-types';
47
+
48
+ /** Where in the consumption lifecycle a failure was raised. */
49
+ export type SurfaceConsumptionPhase = 'discovery' | 'load' | 'mount';
50
+
51
+ /**
52
+ * The typed failure surfaced to the degraded handler. Exactly one of
53
+ * `discoveryError` (resolver returned a typed {@link DiscoveryError}) or
54
+ * `error` (something threw) is populated for a given failure.
55
+ */
56
+ export type SurfaceConsumptionFailure = {
57
+ ref: ParsedSurfaceRef;
58
+ env: EnvironmentId;
59
+ phase: SurfaceConsumptionPhase;
60
+ classification: ModuleFederationFallbackClassification;
61
+ /** Present when discovery returned a typed error state. */
62
+ discoveryError?: DiscoveryError;
63
+ /** Present when a thrown error caused the failure. */
64
+ error?: unknown;
65
+ /** The resolved record, when discovery succeeded but load/mount failed. */
66
+ resolved?: ResolvedDeliveryUnit;
67
+ /** The fallback telemetry payload emitted for this failure. */
68
+ telemetry: ModuleFederationFallbackTelemetryPayload;
69
+ };
70
+
71
+ export type SurfaceLoadInput = {
72
+ resolved: ResolvedDeliveryUnit;
73
+ ref: ParsedSurfaceRef;
74
+ env: EnvironmentId;
75
+ };
76
+
77
+ export type SurfaceConsumerOptions<T> = {
78
+ /** The logical surface to consume (object or canonical string form). */
79
+ ref: ParsedSurfaceRef | string;
80
+ env: EnvironmentId;
81
+ /** The pluggable resolution seam (RESOLUTION-0001 §2.2). */
82
+ provider: SurfaceResolutionProvider;
83
+ /** Consumer app name, used for telemetry attribution. */
84
+ appName: string;
85
+ /**
86
+ * Loads the execution artifact from a resolved record — the loadRemote-ish
87
+ * flow. Receives the whole {@link ResolvedDeliveryUnit} so the adapter reads
88
+ * locations under one buildMarker (RESOLUTION-0001 §2.3).
89
+ */
90
+ load: (input: SurfaceLoadInput) => Promise<T> | T;
91
+ /**
92
+ * REQUIRED degraded handler (G22 compile-level obligation). There is no
93
+ * default and no way to opt out at the type level: omitting this field is a
94
+ * compile error. Return the value to serve while the surface is unavailable
95
+ * or incompatible.
96
+ */
97
+ degraded: (failure: SurfaceConsumptionFailure) => T | Promise<T>;
98
+ /**
99
+ * Consumption criticality (owner-default 5). Defaults to `'critical'` when
100
+ * omitted.
101
+ *
102
+ * - `'noncritical'`: the degraded handler's result is returned and the
103
+ * failure is swallowed — the promise never rejects.
104
+ * - `'critical'`: the degraded handler still runs (telemetry + fallback-UI
105
+ * obligations hold), but the returned promise then REJECTS with the typed
106
+ * failure so callers / rollout machinery observe it. Prefer expand/contract
107
+ * over silent degrade for critical surfaces during rollout windows.
108
+ */
109
+ classification?: 'critical' | 'noncritical';
110
+ /** Optional telemetry emission options (endpoint/auth/fetch overrides). */
111
+ telemetry?: ModuleFederationFallbackTelemetryEmitOptions;
112
+ };
113
+
114
+ /** Map a typed discovery-error code onto the MF fallback classification space. */
115
+ function classifyDiscoveryError(
116
+ code: DiscoveryErrorCode,
117
+ ): ModuleFederationFallbackClassification {
118
+ switch (code) {
119
+ case 'major-not-published':
120
+ case 'stale-record':
121
+ return 'version-skew';
122
+ case 'identity-mismatch':
123
+ return 'contract';
124
+ case 'unknown-unit':
125
+ case 'unknown-surface':
126
+ case 'provider-unavailable':
127
+ return 'remote-unavailable';
128
+ default: {
129
+ // Exhaustiveness guard: a new code must be classified explicitly.
130
+ const _never: never = code;
131
+ return 'remote-unavailable';
132
+ }
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Canonical string form of a ref, without the never-throws guarantee of
138
+ * consumeSurface depending on the caller having passed a grammatically valid
139
+ * object ref (canonical formatSurfaceRef throws on invalid refs).
140
+ */
141
+ function toRemoteString(ref: ParsedSurfaceRef): string {
142
+ return validateSurfaceRef(ref) === undefined
143
+ ? formatSurfaceRef(ref)
144
+ : `${ref.unitId}#${ref.surfaceId}`;
145
+ }
146
+
147
+ async function reportFailure<T>(
148
+ options: SurfaceConsumerOptions<T>,
149
+ ref: ParsedSurfaceRef,
150
+ base: {
151
+ phase: SurfaceConsumptionPhase;
152
+ classification: ModuleFederationFallbackClassification;
153
+ discoveryError?: DiscoveryError;
154
+ error?: unknown;
155
+ resolved?: ResolvedDeliveryUnit;
156
+ },
157
+ ): Promise<T> {
158
+ const remote = toRemoteString(ref);
159
+ const telemetry = createModuleFederationFallbackTelemetry({
160
+ appName: options.appName,
161
+ classification: base.classification,
162
+ error: base.error ?? base.discoveryError,
163
+ metadata: {
164
+ env: options.env,
165
+ surfaceRef: remote,
166
+ ...(base.discoveryError
167
+ ? { discoveryErrorCode: base.discoveryError.code }
168
+ : {}),
169
+ ...(base.resolved ? { buildMarker: base.resolved.buildMarker } : {}),
170
+ },
171
+ phase: base.phase,
172
+ remote,
173
+ status: 'degraded',
174
+ });
175
+
176
+ // Fire-and-forget style, but awaited so tests can assert emission. Emission
177
+ // failures must never mask the degraded path.
178
+ try {
179
+ await emitModuleFederationFallbackTelemetry(
180
+ {
181
+ appName: options.appName,
182
+ classification: base.classification,
183
+ error: base.error ?? base.discoveryError,
184
+ metadata: telemetry.metadata,
185
+ phase: base.phase,
186
+ remote,
187
+ status: 'degraded',
188
+ },
189
+ options.telemetry ?? {},
190
+ );
191
+ } catch {
192
+ // Telemetry sink failure is itself a degraded condition; swallow it.
193
+ }
194
+
195
+ const failure: SurfaceConsumptionFailure = {
196
+ ref,
197
+ env: options.env,
198
+ phase: base.phase,
199
+ classification: base.classification,
200
+ telemetry,
201
+ };
202
+ if (base.discoveryError) {
203
+ failure.discoveryError = base.discoveryError;
204
+ }
205
+ if (base.error !== undefined) {
206
+ failure.error = base.error;
207
+ }
208
+ if (base.resolved) {
209
+ failure.resolved = base.resolved;
210
+ }
211
+
212
+ const critical = (options.classification ?? 'critical') === 'critical';
213
+
214
+ // The ORIGINAL typed failure a critical consumption rejects with: the typed
215
+ // discovery error, else the thrown error, else a synthesized discovery error
216
+ // for the incompatible-verdict path (which carries neither).
217
+ const reason: unknown =
218
+ base.discoveryError ??
219
+ (base.error !== undefined
220
+ ? base.error
221
+ : createDiscoveryError(
222
+ 'identity-mismatch',
223
+ remote,
224
+ `resolved record for "${remote}" is incompatible`,
225
+ { env: options.env },
226
+ ));
227
+
228
+ let degradedValue: T;
229
+ try {
230
+ degradedValue = await options.degraded(failure);
231
+ } catch (handlerError) {
232
+ // Degraded-handler rejection containment: a throwing/rejecting degraded
233
+ // handler must not propagate unconditionally. Emit handler-failure
234
+ // telemetry, then for noncritical resolve undefined; for critical reject
235
+ // with the ORIGINAL typed error (never the handler's).
236
+ try {
237
+ await emitModuleFederationFallbackTelemetry(
238
+ {
239
+ appName: options.appName,
240
+ classification: base.classification,
241
+ error: handlerError,
242
+ metadata: { ...telemetry.metadata, degradedHandlerFailed: true },
243
+ phase: base.phase,
244
+ remote,
245
+ status: 'failed',
246
+ },
247
+ options.telemetry ?? {},
248
+ );
249
+ } catch {
250
+ // Telemetry sink failure must never mask the containment path.
251
+ }
252
+ if (critical) {
253
+ throw reason;
254
+ }
255
+ return undefined as T;
256
+ }
257
+
258
+ if (critical) {
259
+ throw reason;
260
+ }
261
+ return degradedValue;
262
+ }
263
+
264
+ /**
265
+ * Consume one surface with a mandatory degraded fallback. On any discovery or
266
+ * load failure the required degraded handler runs (telemetry + fallback UI).
267
+ *
268
+ * For a `noncritical` consumption (see {@link SurfaceConsumerOptions.classification})
269
+ * this resolves to the degraded value and never rejects, so a failing
270
+ * consumption is isolated from any sibling. For a `critical` consumption (the
271
+ * default) it rejects with the typed failure AFTER the degraded handler has
272
+ * run, so callers / rollout machinery observe the failure.
273
+ */
274
+ export async function consumeSurface<T>(
275
+ options: SurfaceConsumerOptions<T>,
276
+ ): Promise<T> {
277
+ let ref: ParsedSurfaceRef;
278
+ if (typeof options.ref === 'string') {
279
+ const parsed = parseSurfaceRef(options.ref);
280
+ if (!parsed.ok) {
281
+ // A malformed ref can never resolve; degrade without calling the
282
+ // provider (providers only accept grammatically valid refs).
283
+ const hashIndex = options.ref.indexOf('#');
284
+ const echoed: ParsedSurfaceRef =
285
+ hashIndex === -1
286
+ ? { unitId: options.ref, surfaceId: '' }
287
+ : {
288
+ unitId: options.ref.slice(0, hashIndex),
289
+ surfaceId: options.ref.slice(hashIndex + 1),
290
+ };
291
+ return reportFailure(options, echoed, {
292
+ phase: 'discovery',
293
+ classification: 'remote-unavailable',
294
+ discoveryError: createDiscoveryError(
295
+ 'unknown-surface',
296
+ options.ref,
297
+ `SurfaceRef "${options.ref}" is not a valid canonical reference (${parsed.error.code}).`,
298
+ { parseError: parsed.error, env: options.env },
299
+ ),
300
+ });
301
+ }
302
+ ref = parsed.ref;
303
+ } else {
304
+ ref = options.ref;
305
+ }
306
+
307
+ let resolution: DiscoveryResult;
308
+ try {
309
+ resolution = await options.provider.resolve(ref, options.env);
310
+ } catch (error) {
311
+ return reportFailure(options, ref, {
312
+ phase: 'discovery',
313
+ classification: classifyModuleFederationFallback(error),
314
+ error,
315
+ });
316
+ }
317
+
318
+ if (!resolution.ok) {
319
+ return reportFailure(options, ref, {
320
+ phase: 'discovery',
321
+ classification: classifyDiscoveryError(resolution.error.code),
322
+ discoveryError: resolution.error,
323
+ });
324
+ }
325
+
326
+ const resolved = resolution.unit;
327
+
328
+ // A resolver may hand back a record it already judged incompatible; treat it
329
+ // as a degraded consumption rather than loading against a bad contract.
330
+ if (resolved.compatibility.status === 'incompatible') {
331
+ return reportFailure(options, ref, {
332
+ phase: 'discovery',
333
+ classification: 'contract',
334
+ resolved,
335
+ });
336
+ }
337
+
338
+ try {
339
+ return await options.load({ resolved, ref, env: options.env });
340
+ } catch (error) {
341
+ return reportFailure(options, ref, {
342
+ phase: 'load',
343
+ classification: classifyModuleFederationFallback(error),
344
+ error,
345
+ resolved,
346
+ });
347
+ }
348
+ }
349
+
350
+ /**
351
+ * Build a reusable consumer bound to a provider/env/app, still requiring a
352
+ * degraded handler per call. Per-call options override the bound base.
353
+ */
354
+ export function createSurfaceConsumer(
355
+ base: Pick<
356
+ SurfaceConsumerOptions<unknown>,
357
+ 'provider' | 'env' | 'appName' | 'telemetry'
358
+ >,
359
+ ) {
360
+ return function consume<T>(
361
+ options: Omit<SurfaceConsumerOptions<T>, keyof typeof base> &
362
+ Partial<Pick<SurfaceConsumerOptions<T>, keyof typeof base>>,
363
+ ): Promise<T> {
364
+ return consumeSurface<T>({
365
+ ...base,
366
+ ...options,
367
+ } as SurfaceConsumerOptions<T>);
368
+ };
369
+ }