@softarc/native-federation-orchestrator 4.9.0 → 4.10.1

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 (42) hide show
  1. package/README.md +12 -12
  2. package/fesm2022/@softarc/native-federation-orchestrator.mjs +490 -123
  3. package/fesm2022/@softarc/native-federation-orchestrator.mjs.map +4 -4
  4. package/fesm2022/module-federation.mjs.map +2 -2
  5. package/fesm2022/node.mjs +495 -123
  6. package/fesm2022/node.mjs.map +4 -4
  7. package/fesm2022/options.mjs +25 -2
  8. package/fesm2022/options.mjs.map +3 -3
  9. package/fesm2022/registry.mjs.map +2 -2
  10. package/package.json +3 -3
  11. package/quickstart.mjs +15 -13
  12. package/types/lib/core/1.domain/externals/basis.d.ts +2 -2
  13. package/types/lib/core/1.domain/externals/version.contract.d.ts +3 -0
  14. package/types/lib/core/1.domain/import-map/eager-preloads.d.ts +10 -0
  15. package/types/lib/core/1.domain/pooling/builds.d.ts +7 -1
  16. package/types/lib/core/1.domain/remote/remote-module.contract.d.ts +1 -0
  17. package/types/lib/core/1.domain/remote-entry/remote-entry.contract.d.ts +2 -2
  18. package/types/lib/core/2.app/config/import-map.contract.d.ts +4 -7
  19. package/types/lib/core/2.app/config/index.d.ts +2 -2
  20. package/types/lib/core/2.app/config/mode.contract.d.ts +1 -0
  21. package/types/lib/core/2.app/driver-ports/init/flow.contract.d.ts +2 -4
  22. package/types/lib/core/2.app/driver-ports/init/for-exposing-module-loader.port.d.ts +6 -2
  23. package/types/lib/core/2.app/driving-ports/for-browser-tasks.d.ts +1 -0
  24. package/types/lib/core/2.app/driving-ports/for-remote-info-storage.port.d.ts +1 -0
  25. package/types/lib/core/2.app/steps/collect-eager-preloads.d.ts +4 -0
  26. package/types/lib/core/2.app/steps/preload-hint.d.ts +4 -0
  27. package/types/lib/core/2.app/steps/store-remote-entry.d.ts +0 -14
  28. package/types/lib/core/4.config/import-map/index.d.ts +2 -0
  29. package/types/lib/core/4.config/import-map/no-preload.d.ts +2 -0
  30. package/types/lib/core/4.config/import-map/preload-in-dom.d.ts +2 -0
  31. package/types/lib/core/5.di/federation-result.factory.d.ts +3 -2
  32. package/types/lib/core/init-federation.contract.d.ts +2 -0
  33. package/types/lib/index.d.ts +1 -1
  34. package/types/lib/node/init-federation.node.d.ts +2 -7
  35. package/types/lib/node.index.d.ts +1 -1
  36. package/types/lib/registry/event-registry.contract.d.ts +1 -0
  37. package/types/lib/registry/event.contract.d.ts +6 -0
  38. package/types/lib/registry/for-managing-events.port.d.ts +1 -0
  39. package/types/lib/registry/registry.contract.d.ts +2 -0
  40. package/types/lib/registry/setup-registry.d.ts +1 -0
  41. package/types/scripts/announce-init-ready.d.ts +2 -0
  42. package/types/scripts/init-registry.d.ts +1 -0
@@ -1,7 +1,7 @@
1
1
  import type { DeepReadonly } from '../../../utils/deep-readonly';
2
2
  import type { SharedVersion, SharedVersionMeta } from './version.contract';
3
- export declare function isBetterBasis(candidate: SharedVersionMeta, basis: SharedVersionMeta): boolean;
4
- export declare function addRemoteToVersion(version: SharedVersion, remote: SharedVersionMeta, isHost?: boolean): void;
3
+ export declare function isBetterBasis(candidate: SharedVersionMeta, basis: SharedVersionMeta, preferDev?: boolean): boolean;
4
+ export declare function addRemoteToVersion(version: SharedVersion, remote: SharedVersionMeta, isHost?: boolean, preferDev?: boolean): void;
5
5
  export declare function versionDemands(version: SharedVersion): SharedVersionMeta[];
6
6
  export declare function forEachVersionEntry(version: DeepReadonly<SharedVersion>, accepts: ((remote: SharedVersionMeta) => boolean) | undefined, visit: (entrypoint: string, remote: SharedVersionMeta) => void): void;
7
7
  type Covered = Pick<ReadonlySet<string>, 'has'>;
@@ -6,6 +6,7 @@ export type Version = {
6
6
  export type ScopedVersion = Version & {
7
7
  bundle?: string;
8
8
  entries: Record<string, string>;
9
+ preload?: true;
9
10
  };
10
11
  export type SharedVersion = Version & {
11
12
  host: boolean;
@@ -25,4 +26,6 @@ export type SharedVersionMeta = {
25
26
  entries: Record<string, string>;
26
27
  servedBy?: RemoteName;
27
28
  poolCause?: PoolCause;
29
+ preload?: true;
30
+ dev?: true;
28
31
  };
@@ -0,0 +1,10 @@
1
+ import type { ImportMap } from './import-map.contract';
2
+ export type PreloadCandidate = {
3
+ scopeUrl: string;
4
+ entryFiles: string[];
5
+ bundle?: {
6
+ name: string;
7
+ chunkFiles: string[];
8
+ };
9
+ };
10
+ export declare function eagerPreloadUrls(importMap: ImportMap, candidates: Iterable<PreloadCandidate>): string[];
@@ -1,4 +1,5 @@
1
- import type { ExternalName, RemoteName, VersionName } from '..';
1
+ import type { DeepReadonly } from '../../../utils/deep-readonly';
2
+ import type { ExternalName, RemoteName, SharedVersion, SharedVersionMeta, VersionName } from '..';
2
3
  import { type PackageOf, type Specifier, SpecifierTags } from '../externals/specifier';
3
4
  import type { PoolMember } from './membership';
4
5
  export type Copy = {
@@ -16,5 +17,10 @@ export type Build = {
16
17
  host: boolean;
17
18
  };
18
19
  export declare function copiesByRemote(members: readonly PoolMember[]): Map<RemoteName, Copy[]>;
20
+ export type RemoteRecord = DeepReadonly<{
21
+ version: SharedVersion;
22
+ meta: SharedVersionMeta;
23
+ }>;
24
+ export declare function recordsByRemote(member: PoolMember): Map<RemoteName, RemoteRecord[]>;
19
25
  export declare const specifierCount: (copies: readonly Copy[]) => number;
20
26
  export declare function buildOf(owner: RemoteName, copies: readonly Copy[], packageOf: PackageOf): Build;
@@ -1,4 +1,5 @@
1
1
  export type RemoteModule = {
2
2
  moduleName: string;
3
3
  file: string;
4
+ chunks?: string[];
4
5
  };
@@ -1,6 +1,6 @@
1
1
  import type { SharedVersionAction } from '../externals/version.contract';
2
2
  import type { RemoteEntryUrl } from './manifest.contract';
3
- import type { DenseSharedInfo, ExposesInfo, FederationInfo, SharedInfo } from '@softarc/native-federation/domain';
3
+ import type { ChunkMap, DenseSharedInfo, ExposesInfo, FederationInfo, SharedInfo } from '@softarc/native-federation/domain';
4
4
  type RawRemoteEntry = FederationInfo;
5
5
  type RemoteSharedInfo = DenseSharedInfo & {
6
6
  dense?: true;
@@ -15,4 +15,4 @@ type SharedInfoActions = Record<string, {
15
15
  action: SharedVersionAction;
16
16
  override?: Record<string, string>;
17
17
  }>;
18
- export { RawRemoteEntry, RemoteEntry, FederationInfo, ExposesInfo, SharedInfo, DenseSharedInfo, RemoteSharedInfo, SharedInfoActions, };
18
+ export { RawRemoteEntry, RemoteEntry, ChunkMap, FederationInfo, ExposesInfo, SharedInfo, DenseSharedInfo, RemoteSharedInfo, SharedInfoActions, };
@@ -3,19 +3,16 @@ export type ImportMapConfig = {
3
3
  loadModuleFn: (url: string) => Promise<unknown>;
4
4
  setImportMapFn: SetImportMap;
5
5
  reloadBrowserFn: () => void;
6
+ preloadModulesFn?: PreloadModules;
6
7
  };
8
+ export type PreloadModules = (urls: string[]) => void;
7
9
  export type SetImportMap = (importMap: ImportMap, opts?: {
8
10
  override?: boolean;
9
11
  }) => Promise<ImportMap>;
10
12
  export type ImportMapOptions = Partial<ImportMapConfig> & {
11
13
  /**
12
- * Name of the Trusted Types policy used to wrap import-map content and
13
- * dynamic-import URLs in the default `setImportMapFn` and `loadModuleFn`.
14
- * Pass `false` to disable the policy (e.g. when the host owns its own
15
- * Trusted Types pipeline). Defaults to `'nfo'`.
16
- *
17
- * Has no effect on browsers that do not support Trusted Types — the wrapper
18
- * falls back to a transparent pass-through in that case.
14
+ * Trusted Types policy wrapping the default `setImportMapFn` and `loadModuleFn`; `false` opts out.
15
+ * Defaults to `'nfo'`. No effect on browsers without Trusted Types.
19
16
  */
20
17
  trustedTypesPolicyName?: string | false;
21
18
  };
@@ -1,6 +1,6 @@
1
- export { ImportMapOptions, ImportMapConfig } from './import-map.contract';
1
+ export { ImportMapOptions, ImportMapConfig, PreloadModules } from './import-map.contract';
2
2
  export { LoggingOptions, LogLevel, LogType, LoggingConfig, LogHandler, Logger, } from './log.contract';
3
- export { StorageOptions, StorageEntry, StorageEntryKey, StorageConfig, StorageType, StorageKey, } from './storage.contract';
3
+ export { StorageOptions, StorageEntry, StorageEntryKey, StorageConfig, StorageEntryCreator, StorageEntryHandler, StorageType, StorageKey, } from './storage.contract';
4
4
  export { NFOrchestratorGlobal, NFOrchestratorStorageInfo } from './orchestrator-global.contract';
5
5
  export { ModeOptions, ModeConfig } from './mode.contract';
6
6
  export { HostOptions, HostConfig } from './host.contract';
@@ -5,6 +5,7 @@ export type ModeProfileConfig = {
5
5
  overrideCachedRemotesIfURLMatches: boolean;
6
6
  cacheTag?: string;
7
7
  mergeManifests?: boolean;
8
+ enableDebugging?: boolean;
8
9
  };
9
10
  export type ModeFeatureConfig = {
10
11
  convertFlatSharedInfo: boolean;
@@ -1,8 +1,6 @@
1
1
  import type { FederationManifest } from '../../../1.domain';
2
- import type { LoadRemoteModule } from '../../../init-federation.contract';
3
- export type InitResult = {
4
- loadRemoteModule: LoadRemoteModule;
5
- };
2
+ import type { RemoteModuleAccess } from './for-exposing-module-loader.port';
3
+ export type InitResult = RemoteModuleAccess;
6
4
  export type InitFlow = (remotesOrManifestUrl: string | FederationManifest) => Promise<InitResult>;
7
5
  export type RemoteRef = string | {
8
6
  name?: string;
@@ -1,2 +1,6 @@
1
- import type { LoadRemoteModule } from '../../../init-federation.contract';
2
- export type ForExposingModuleLoader = () => Promise<LoadRemoteModule>;
1
+ import type { LoadRemoteModule, PreloadRemoteModule } from '../../../init-federation.contract';
2
+ export type RemoteModuleAccess = {
3
+ loadRemoteModule: LoadRemoteModule;
4
+ preloadRemoteModule: PreloadRemoteModule;
5
+ };
6
+ export type ForExposingModuleLoader = () => Promise<RemoteModuleAccess>;
@@ -4,4 +4,5 @@ export type ForBrowserTasks = {
4
4
  override?: boolean;
5
5
  }) => Promise<ImportMap>;
6
6
  importModule: (url: string) => Promise<unknown>;
7
+ preloadModules?: (urls: string[]) => void;
7
8
  };
@@ -5,6 +5,7 @@ export type ForRemoteInfoStorage = {
5
5
  tryGet: (remoteName: RemoteName) => Optional<RemoteInfo>;
6
6
  remove: (remoteName: RemoteName) => ForRemoteInfoStorage;
7
7
  tryGetModule: (remoteName: RemoteName, exposedModule: string) => Optional<string>;
8
+ getModuleChunkUrls: (remoteName: RemoteName, exposedModule: string) => string[];
8
9
  addOrUpdate: (remoteName: string, remote: RemoteInfo) => ForRemoteInfoStorage;
9
10
  getAll: () => Remotes;
10
11
  commit: () => ForRemoteInfoStorage;
@@ -0,0 +1,4 @@
1
+ import type { DrivingContract } from '../driving-ports/driving.contract';
2
+ import type { ImportMap } from '../../1.domain';
3
+ export type CollectEagerPreloads = (importMap: ImportMap) => string[];
4
+ export declare function createCollectEagerPreloads(ports: Pick<DrivingContract, 'remoteInfoRepo' | 'scopedExternalsRepo' | 'sharedExternalsRepo' | 'sharedChunksRepo'>): CollectEagerPreloads;
@@ -0,0 +1,4 @@
1
+ import type { Logger } from '../config/log.contract';
2
+ import type { ForBrowserTasks } from '../driving-ports/for-browser-tasks';
3
+ export type PreloadHint = (collectUrls: () => string[]) => void;
4
+ export declare function createPreloadHint(log: Logger, browser: Pick<ForBrowserTasks, 'preloadModules'>, logStep: number): PreloadHint;
@@ -4,11 +4,6 @@ import type { LoggingConfig } from '../config/log.contract';
4
4
  import type { ModeConfig } from '../config/mode.contract';
5
5
  export type StoreRemoteEntry = (remoteEntry: RemoteEntry, onSharedExternal: SharedExternalHandler) => void;
6
6
  export type RemoveCachedRemoteEntries = (remoteNames: ReadonlySet<RemoteName>) => void;
7
- /**
8
- * Evict stored remotes: overridden ones before their replacements are merged in, and ones the manifest
9
- * doesn't list. Kept out of `storeRemoteEntry` because `removeFromAllScopes` costs a full traversal of the
10
- * shared-externals graph, so each caller evicts its whole set at once.
11
- */
12
7
  export declare function createRemoveCachedRemoteEntries(ports: Pick<DrivingContract, 'remoteInfoRepo' | 'scopedExternalsRepo' | 'sharedExternalsRepo' | 'sharedChunksRepo'>): RemoveCachedRemoteEntries;
13
8
  export type SharedExternalHandler = (remoteEntry: RemoteEntry, external: DenseSharedInfo, ctx: SharedExternalContext) => void;
14
9
  export type SharedExternalContext = {
@@ -19,13 +14,4 @@ export type SharedExternalContext = {
19
14
  assertSameVersionCompatibility: (matchingVersion: SharedVersion) => void;
20
15
  write: () => void;
21
16
  };
22
- /**
23
- * Not a pipeline step but the template both storage steps specialize: it owns the merge sequence
24
- * for a remoteEntry, and only the resolution of a singleton external differs between the init
25
- * pipeline (process-remote-entries, step 2) and the dynamic pipeline (update-cache, step 8),
26
- * injected per call. Evicting an overridden remote is the caller's job, see
27
- * `createRemoveCachedRemoteEntries` — every entry reaching here is merged additively. The
28
- * pipelines must not be merged, see docs/version-resolver.md "Dynamic Init". If a change ever
29
- * needs a flag or parameter here to serve only one caller, inline this back into the steps instead.
30
- */
31
17
  export declare function createStoreRemoteEntry(config: LoggingConfig & ModeConfig, ports: Pick<DrivingContract, 'remoteInfoRepo' | 'scopedExternalsRepo' | 'sharedExternalsRepo' | 'sharedChunksRepo' | 'versionCheck'>, logStep: number): StoreRemoteEntry;
@@ -1,3 +1,5 @@
1
1
  export * from './use-import-shim';
2
2
  export * from './use-default';
3
3
  export * from './replace-in-dom';
4
+ export * from './preload-in-dom';
5
+ export * from './no-preload';
@@ -0,0 +1,2 @@
1
+ import type { PreloadModules } from '../../2.app/config/import-map.contract';
2
+ export declare const noPreload: PreloadModules;
@@ -0,0 +1,2 @@
1
+ import type { PreloadModules } from '../../2.app/config/import-map.contract';
2
+ export declare const preloadInDOM: (rel: "modulepreload" | "modulepreload-shim") => PreloadModules;
@@ -1,12 +1,13 @@
1
1
  import type { ConfigContract } from '../2.app/config/config.contract';
2
2
  import type { DrivingContract } from '../2.app/driving-ports/driving.contract';
3
3
  import type { InitRemoteEntryFlow } from '../2.app/driver-ports/init/flow.contract';
4
- import type { LoadRemoteModule, NativeFederationResult } from '../init-federation.contract';
4
+ import type { LoadRemoteModule, NativeFederationResult, PreloadRemoteModule } from '../init-federation.contract';
5
5
  export { createStateDump } from '../2.app/steps/rollback-changes';
6
- export declare const createFederationResult: ({ config, adapters, loadRemoteModule, initRemoteEntryFlow, afterInitRemoteEntry, }: {
6
+ export declare const createFederationResult: ({ config, adapters, loadRemoteModule, preloadRemoteModule, initRemoteEntryFlow, afterInitRemoteEntry, }: {
7
7
  config: ConfigContract;
8
8
  adapters: DrivingContract;
9
9
  loadRemoteModule: LoadRemoteModule;
10
+ preloadRemoteModule?: PreloadRemoteModule;
10
11
  initRemoteEntryFlow: InitRemoteEntryFlow;
11
12
  afterInitRemoteEntry?: () => Promise<unknown>;
12
13
  }) => NativeFederationResult;
@@ -2,12 +2,14 @@ import type { ConfigContract } from './2.app/config/config.contract';
2
2
  import type { DrivingContract } from './2.app/driving-ports/driving.contract';
3
3
  import type { DynamicInitResult } from './2.app/driver-ports/init/flow.contract';
4
4
  export type LoadRemoteModule = <TModule = unknown>(remoteName: string, exposedModule: string) => Promise<TModule>;
5
+ export type PreloadRemoteModule = (remoteName: string, exposedModule: string) => void;
5
6
  export type LoadRemoteModuleOf<TModule> = (remoteName: string, exposedModule: string) => Promise<TModule>;
6
7
  export type NativeFederationResult = DynamicInitResult<{
7
8
  config: ConfigContract;
8
9
  adapters: DrivingContract;
9
10
  loadRemoteModule: LoadRemoteModule;
10
11
  load: LoadRemoteModule;
12
+ preloadRemoteModule: PreloadRemoteModule;
11
13
  as: <TModule = unknown>() => {
12
14
  loadRemoteModule: LoadRemoteModuleOf<TModule>;
13
15
  load: LoadRemoteModuleOf<TModule>;
@@ -1,6 +1,6 @@
1
1
  export { initFederation } from './core/init-federation';
2
2
  export { NFError } from './core/native-federation.error';
3
- export { LoadRemoteModule, LoadRemoteModuleOf, NativeFederationResult, } from './core/init-federation.contract';
3
+ export { LoadRemoteModule, LoadRemoteModuleOf, NativeFederationResult, PreloadRemoteModule, } from './core/init-federation.contract';
4
4
  export { FederationManifest } from './core/1.domain/remote-entry/manifest.contract';
5
5
  export { createConfigHandlers } from './core/5.di/config.factory';
6
6
  export { createDriving } from './core/5.di/driving.factory';
@@ -18,13 +18,8 @@ export type InitNodeFederationOptions = NFOptions & {
18
18
  * - **`{ include, exclude }`** — auto-derive, filtered by exact/prefix match,
19
19
  * e.g. `{ include: ['@angular/', 'rxjs', 'zone.js'] }`.
20
20
  *
21
- * This is an escape hatch that bypasses the version resolver, the import map,
22
- * and integrity checks entirely. Exports are value snapshots, not live
23
- * bindings — fine for packages whose exports are stable refs (Angular's
24
- * classes/functions), not for packages that reassign their exports after init.
25
- *
26
- * Omit in production: with no `hostInstances`, nothing is published and the
27
- * loader never routes to the bridge — resolution is import-map only.
21
+ * Bypasses the version resolver, the import map and integrity checks. Exports are value snapshots, not
22
+ * live bindings, so not for packages that reassign their exports after init. Omit in production.
28
23
  */
29
24
  hostInstances?: HostInstancesOption;
30
25
  };
@@ -1,5 +1,5 @@
1
1
  export { initNodeFederation } from './node/init-federation.node';
2
2
  export type { InitNodeFederationOptions, HostInstancesOption, HostInstancesAuto, } from './node/init-federation.node';
3
3
  export { NFError } from './core/native-federation.error';
4
- export type { LoadRemoteModule, LoadRemoteModuleOf, NativeFederationResult, } from './core/init-federation.contract';
4
+ export type { LoadRemoteModule, LoadRemoteModuleOf, NativeFederationResult, PreloadRemoteModule, } from './core/init-federation.contract';
5
5
  export type { FederationManifest } from './core/1.domain/remote-entry/manifest.contract';
@@ -1,4 +1,5 @@
1
1
  import type { NFEventConsumer, NFEventData, NFEventProvider, NFEventUnsubscribe } from './event.contract';
2
+ /** @deprecated Use @softarc/native-federation-communication. Removed in v5. */
2
3
  export type NFEventRegistry = {
3
4
  register<T>(type: string, resource: T | NFEventProvider<T>): Promise<void>;
4
5
  onReady<T>(type: string, callback: NFEventConsumer<T>): NFEventUnsubscribe;
@@ -1,11 +1,17 @@
1
+ /** @deprecated Use @softarc/native-federation-communication. Removed in v5. */
1
2
  export type NFEventProvider<T> = () => Promise<T> | T;
3
+ /** @deprecated Use @softarc/native-federation-communication. Removed in v5. */
2
4
  export type NFEventConsumer<T> = (resource: T) => void;
5
+ /** @deprecated Use @softarc/native-federation-communication. Removed in v5. */
3
6
  export type NFEventErrorHandler = (name: string, error: Error) => void;
7
+ /** @deprecated Use @softarc/native-federation-communication. Removed in v5. */
4
8
  export type NFEventData<T = any> = {
5
9
  data: T;
6
10
  timestamp: number;
7
11
  };
12
+ /** @deprecated Use @softarc/native-federation-communication. Removed in v5. */
8
13
  export type NFEventUnsubscribe = () => void;
14
+ /** @deprecated Use @softarc/native-federation-communication. Removed in v5. */
9
15
  export type NFEventStream<T = any> = {
10
16
  events: NFEventData<T>[];
11
17
  latest: NFEventData<T> | null;
@@ -1,2 +1,3 @@
1
1
  import type { NFEventRegistry } from './event-registry.contract';
2
+ /** @deprecated Use @softarc/native-federation-communication. Removed in v5. */
2
3
  export type ForManagingEvents = () => NFEventRegistry;
@@ -1,6 +1,8 @@
1
+ /** @deprecated Use @softarc/native-federation-communication. Removed in v5. */
1
2
  export type NFEventRegistryConfig = {
2
3
  maxStreams?: number;
3
4
  maxEvents: number;
4
5
  removePercentage?: number;
5
6
  };
7
+ /** @deprecated Use @softarc/native-federation-communication. Removed in v5. */
6
8
  export type NFEventRegistryOptions = Partial<NFEventRegistryConfig>;
@@ -1,3 +1,4 @@
1
1
  import type { ForManagingEvents } from './for-managing-events.port';
2
2
  import type { NFEventRegistryOptions } from './registry.contract';
3
+ /** @deprecated Use @softarc/native-federation-communication. Removed in v5. */
3
4
  export declare function createRegistry(opts: NFEventRegistryOptions): ForManagingEvents;
@@ -0,0 +1,2 @@
1
+ import type { NativeFederationResult } from '../lib/core/init-federation.contract';
2
+ export declare function announceInitReady(loaders: NativeFederationResult): void;
@@ -1,6 +1,7 @@
1
1
  import type { NFEventRegistry } from '../lib/registry/event-registry.contract';
2
2
  declare global {
3
3
  interface Window {
4
+ /** @deprecated Use @softarc/native-federation-communication. Removed in v5. */
4
5
  __NF_REGISTRY__: NFEventRegistry;
5
6
  }
6
7
  }