@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.
- package/README.md +12 -12
- package/fesm2022/@softarc/native-federation-orchestrator.mjs +490 -123
- package/fesm2022/@softarc/native-federation-orchestrator.mjs.map +4 -4
- package/fesm2022/module-federation.mjs.map +2 -2
- package/fesm2022/node.mjs +495 -123
- package/fesm2022/node.mjs.map +4 -4
- package/fesm2022/options.mjs +25 -2
- package/fesm2022/options.mjs.map +3 -3
- package/fesm2022/registry.mjs.map +2 -2
- package/package.json +3 -3
- package/quickstart.mjs +15 -13
- package/types/lib/core/1.domain/externals/basis.d.ts +2 -2
- package/types/lib/core/1.domain/externals/version.contract.d.ts +3 -0
- package/types/lib/core/1.domain/import-map/eager-preloads.d.ts +10 -0
- package/types/lib/core/1.domain/pooling/builds.d.ts +7 -1
- package/types/lib/core/1.domain/remote/remote-module.contract.d.ts +1 -0
- package/types/lib/core/1.domain/remote-entry/remote-entry.contract.d.ts +2 -2
- package/types/lib/core/2.app/config/import-map.contract.d.ts +4 -7
- package/types/lib/core/2.app/config/index.d.ts +2 -2
- package/types/lib/core/2.app/config/mode.contract.d.ts +1 -0
- package/types/lib/core/2.app/driver-ports/init/flow.contract.d.ts +2 -4
- package/types/lib/core/2.app/driver-ports/init/for-exposing-module-loader.port.d.ts +6 -2
- package/types/lib/core/2.app/driving-ports/for-browser-tasks.d.ts +1 -0
- package/types/lib/core/2.app/driving-ports/for-remote-info-storage.port.d.ts +1 -0
- package/types/lib/core/2.app/steps/collect-eager-preloads.d.ts +4 -0
- package/types/lib/core/2.app/steps/preload-hint.d.ts +4 -0
- package/types/lib/core/2.app/steps/store-remote-entry.d.ts +0 -14
- package/types/lib/core/4.config/import-map/index.d.ts +2 -0
- package/types/lib/core/4.config/import-map/no-preload.d.ts +2 -0
- package/types/lib/core/4.config/import-map/preload-in-dom.d.ts +2 -0
- package/types/lib/core/5.di/federation-result.factory.d.ts +3 -2
- package/types/lib/core/init-federation.contract.d.ts +2 -0
- package/types/lib/index.d.ts +1 -1
- package/types/lib/node/init-federation.node.d.ts +2 -7
- package/types/lib/node.index.d.ts +1 -1
- package/types/lib/registry/event-registry.contract.d.ts +1 -0
- package/types/lib/registry/event.contract.d.ts +6 -0
- package/types/lib/registry/for-managing-events.port.d.ts +1 -0
- package/types/lib/registry/registry.contract.d.ts +2 -0
- package/types/lib/registry/setup-registry.d.ts +1 -0
- package/types/scripts/announce-init-ready.d.ts +2 -0
- package/types/scripts/init-registry.d.ts +1 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/utils/compare-strings.ts", "../../src/lib/core/1.domain/externals/basis.ts", "../../src/lib/core/1.domain/externals/claims.ts", "../../src/lib/core/1.domain/externals/rows.ts", "../../src/lib/utils/union-find.ts", "../../src/lib/core/1.domain/externals/specifier.ts", "../../src/lib/core/1.domain/externals/serving.ts", "../../src/lib/utils/path.ts", "../../src/lib/module-federation/get-shared.ts"],
|
|
4
|
-
"sourcesContent": ["// Code-unit order: unlike `localeCompare`, the same in every browser and locale, so anything stored or elected\n// from a name comes out identical everywhere.\nfunction compareStrings(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\nexport { compareStrings };\n", "import type { DeepReadonly } from 'lib/utils/deep-readonly';\nimport { compareStrings } from 'lib/utils/compare-strings';\nimport type { SharedVersion, SharedVersionMeta } from './version.contract';\n\nconst coverage = (remote: SharedVersionMeta): number => Object.keys(remote.entries).length;\n\n// `remotes[0]` is the basis: the copy every consumer of the version resolves to. Host and cached\n// outrank coverage because repointing them invalidates a committed import map. An exact tie goes to the\n// name, so the basis does not depend on the order remotes register in.\nexport function isBetterBasis(candidate: SharedVersionMeta, basis: SharedVersionMeta): boolean {\n if (basis.cached !== candidate.cached) return candidate.cached;\n const byCoverage = coverage(candidate) - coverage(basis);\n return byCoverage === 0 ? compareStrings(candidate.name, basis.name) < 0 : byCoverage > 0;\n}\n\nexport function addRemoteToVersion(\n version: SharedVersion,\n remote: SharedVersionMeta,\n isHost = false\n): void {\n if (isHost) {\n version.host = true;\n version.remotes.unshift(remote);\n return;\n }\n\n const basis = version.remotes[0];\n if (basis && !version.host && isBetterBasis(remote, basis)) {\n version.remotes.unshift(remote);\n return;\n }\n\n version.remotes.push(remote);\n}\n\n// `remotes[0]` answers \"who serves this version\", never \"what does this version demand\":\n// `requiredVersion`, `strictVersion` and `cached` are per-build settings, so remotes sharing a tag\n// can disagree on all three. Negotiation must therefore aggregate over every remote, or basis\n// precedence would decide compatibility. Deduped because those settings repeat across a version's\n// remotes, which keeps the resolver's O(versions\u00B2) selection from also scaling with remote count.\nexport function versionDemands(version: SharedVersion): SharedVersionMeta[] {\n if (version.remotes.length < 2) return version.remotes;\n\n const distinct = new Map<string, SharedVersionMeta>();\n for (const remote of version.remotes) {\n const key = `${remote.requiredVersion}|${remote.strictVersion}|${remote.cached}`;\n if (!distinct.has(key)) distinct.set(key, remote);\n }\n return Array.from(distinct.values());\n}\n\n// Which copies of a version may publish a specifier, in basis precedence; callers keep the first claim. The\n// copies build one tag, so this is what the version ships, not whose files a consumer runs; see\n// docs/version-resolver.md#copies-of-one-version.\n//\n// Only copies that publish their own files count. Pooling places a copy in a subpool running a foreign\n// build via `servedBy`, and the import map then names that build's files for it, per consumer \u2014 so what\n// such a copy bundles answers for nobody but itself, and counting it would promise a specifier no\n// consumer of this version can resolve. Pooling keeps a subpool copy out of the basis slot for exactly\n// this reason, so a `share` version's basis always survives the skip. `accepts` lets a caller discount a\n// copy without dropping its specifiers for the next copy to claim.\nexport function forEachVersionEntry(\n version: DeepReadonly<SharedVersion>,\n accepts: ((remote: SharedVersionMeta) => boolean) | undefined,\n visit: (entrypoint: string, remote: SharedVersionMeta) => void\n): void {\n for (const remote of version.remotes) {\n if (remote.servedBy) continue;\n if (accepts && !accepts(remote)) continue;\n for (const entrypoint in remote.entries) visit(entrypoint, remote);\n }\n}\n\ntype Covered = Pick<ReadonlySet<string>, 'has'>;\n\n// Counted rather than listed: the resolver's O(versions\u00B2) selection loop asks this once per remote per\n// candidate.\nexport function countUncoveredEntrypoints(remote: SharedVersionMeta, covered: Covered): number {\n let uncovered = 0;\n for (const entrypoint in remote.entries) {\n if (!covered.has(entrypoint)) uncovered++;\n }\n return uncovered;\n}\n", "import type { DeepReadonly } from 'lib/utils/deep-readonly';\nimport type { RemoteName } from '../remote/remote-info.contract';\nimport { type ExternalName, GLOBAL_SCOPE, type SharedExternal } from './external.contract';\nimport type { SharedVersion, SharedVersionMeta, VersionName } from './version.contract';\nimport type { Specifier } from './specifier';\nimport { forEachVersionEntry } from './basis';\n\nexport type Claim = {\n external: ExternalName;\n tag: VersionName;\n copy: SharedVersionMeta;\n};\n\nexport type ClaimTable = {\n // What `imports` serves: an external with a serving build claims that build's copy alone, one without claims its\n // share rows in record order; the first claim per specifier wins. A skip copy claims nothing.\n claims: Map<Specifier, Claim>;\n // Share rows reached after their external's name was claimed at another tag. A row whose name another row\n // claimed claims nothing, duplicate or not.\n duplicates: Set<DeepReadonly<SharedVersion>>;\n // What a named scope serves: the same rule over every share row, duplicates included.\n shareClaims: Map<Specifier, Claim>;\n};\n\nexport function claimTable(\n externals: Iterable<readonly [ExternalName, DeepReadonly<SharedExternal>]>,\n publishes?: (copy: SharedVersionMeta) => boolean\n): ClaimTable {\n const table: ClaimTable = { claims: new Map(), duplicates: new Set(), shareClaims: new Map() };\n const ordered = [...externals];\n\n const claim = (\n into: Map<Specifier, Claim>,\n external: ExternalName,\n version: DeepReadonly<SharedVersion>,\n accepts = publishes\n ) =>\n forEachVersionEntry(version, accepts, (specifier, copy) => {\n if (!into.has(specifier)) into.set(specifier, { external, tag: version.tag, copy });\n });\n\n const seedServingBuild = (name: ExternalName, external: DeepReadonly<SharedExternal>) => {\n const isServing = (copy: SharedVersionMeta) =>\n copy.name === external.servingBuild && (!publishes || publishes(copy));\n for (const version of external.versions) {\n if (version.action !== 'share') continue;\n claim(table.shareClaims, name, version, isServing);\n claim(table.claims, name, version, isServing);\n }\n };\n\n const claimShareRow = (\n name: ExternalName,\n external: DeepReadonly<SharedExternal>,\n version: DeepReadonly<SharedVersion>\n ) => {\n const prior = table.claims.get(name);\n const duplicate = prior !== undefined && prior.tag !== version.tag;\n // A row the serving host leads is never a duplicate: it ships that tag beside the one claimed first.\n if (duplicate && !(version.host && version.remotes[0]!.name === external.servingBuild))\n table.duplicates.add(version);\n // The seed already claimed an external with a serving build.\n if (external.servingBuild) return;\n claim(table.shareClaims, name, version);\n if (!duplicate && (!prior || prior.external === name)) claim(table.claims, name, version);\n };\n\n for (const [name, external] of ordered)\n if (external.servingBuild) seedServingBuild(name, external);\n for (const [name, external] of ordered)\n for (const version of external.versions)\n if (version.action === 'share') claimShareRow(name, external, version);\n\n return table;\n}\n\n// What a committed map serves in one scope, from externals in record order: `imports` holds only the copies\n// marked cached, a named scope's override may name any copy. `without` is a loaded remote whose copies the\n// record already holds but the committed map does not.\nexport function committedClaims(\n externals: Iterable<readonly [ExternalName, DeepReadonly<SharedExternal>]>,\n { shareScope, without }: { shareScope: string; without?: RemoteName }\n): Map<Specifier, Claim> {\n const publishes = (copy: SharedVersionMeta) =>\n copy.name !== without && (shareScope !== GLOBAL_SCOPE || copy.cached);\n return claimTable(externals, publishes).claims;\n}\n\n// One version's claims as `claimTable` makes them: the serving copy alone when there is one, else every copy in\n// record order.\nexport function versionClaims(\n version: DeepReadonly<SharedVersion>,\n servingBuild: RemoteName | undefined\n): Map<Specifier, SharedVersionMeta> {\n const claims = new Map<Specifier, SharedVersionMeta>();\n const claim = (specifier: Specifier, copy: SharedVersionMeta) => {\n if (!claims.has(specifier)) claims.set(specifier, copy);\n };\n const accepts = servingBuild\n ? (copy: SharedVersionMeta) => copy.name === servingBuild\n : undefined;\n forEachVersionEntry(version, accepts, claim);\n return claims;\n}\n", "import type { RemoteName } from '../remote/remote-info.contract';\nimport type { AcceptsTag } from './compatibility';\nimport type {\n SharedVersion,\n SharedVersionAction,\n SharedVersionMeta,\n VersionName,\n} from './version.contract';\n\n// Newest tag first, as `write()` orders a record. Leaves a tag's rows in their order (`sort` is stable).\nexport const byTag =\n (compare: (a: VersionName, b: VersionName) => number) =>\n (a: SharedVersion, b: SharedVersion): number =>\n compare(b.tag, a.tag);\n\nexport const rowAt = (\n versions: readonly SharedVersion[],\n tag: VersionName,\n action: SharedVersionAction\n): SharedVersion | undefined => versions.find(v => v.tag === tag && v.action === action);\n\n// One row per (tag, action): the first row of each absorbs the copies of the later ones, in place. `lead` is\n// that first row wherever it stands, so its copies stay in front.\nexport function mergeRows(versions: SharedVersion[], lead?: SharedVersion): SharedVersion[] {\n const merged = new Map<string, SharedVersion>();\n if (lead) merged.set(`${lead.tag}|${lead.action}`, lead);\n return versions.filter(v => {\n const key = `${v.tag}|${v.action}`;\n const first = merged.get(key);\n if (first === v) return true;\n if (!first) {\n merged.set(key, v);\n return true;\n }\n first.remotes.push(...v.remotes);\n return false;\n });\n}\n\n// `redirectRow` and `scopeRemote` can leave a `scope` row beside a shareable one at the same tag.\nexport function findVersionForTag(\n versions: SharedVersion[],\n tag: string\n): SharedVersion | undefined {\n let scoped: SharedVersion | undefined;\n for (const version of versions) {\n if (version.tag !== tag) continue;\n if (version.action !== 'scope') return version;\n scoped ??= version;\n }\n return scoped;\n}\n\n// Points a row at `tag`: its copies skip to it, and only the strict ones that reject it keep their own build,\n// split off into a `scope` row beside it. Returns the rows that replace `version`, in order; mutates it in place.\nexport function redirectRow(\n version: SharedVersion,\n tag: VersionName,\n acceptsTag: AcceptsTag\n): SharedVersion[] {\n const objecting = new Set(\n version.remotes.filter(r => r.strictVersion && !acceptsTag(tag, version.tag, r.requiredVersion))\n );\n\n if (objecting.size === 0) {\n version.action = 'skip';\n return [version];\n }\n if (objecting.size === version.remotes.length) {\n version.action = 'scope';\n return [version];\n }\n\n version.remotes = version.remotes.filter(r => !objecting.has(r));\n version.action = 'skip';\n // Beside its source rather than appended: the record stays newest-first, which `write()`\n // guarantees and a later election reads as \"the latest\".\n return [version, { tag: version.tag, host: false, action: 'scope', remotes: [...objecting] }];\n}\n\n// Moves each of `remotes`' copies out of every `share` and `skip` row into the `scope` row of their tag; a new scope\n// row goes right after its source, so the record stays newest-first. In one pass, yet ordered as if each remote\n// moved in turn: copies by remote then by row, a host's in front. Mutates the source rows, returns the new list.\nexport function scopeRemotes(\n versions: SharedVersion[],\n remotes: readonly RemoteName[]\n): SharedVersion[] {\n if (remotes.length === 0) return versions;\n const turn = new Map(remotes.map((remote, i) => [remote, i]));\n const moves = new Map<VersionName, Move[]>();\n versions.forEach((source, at) => {\n if (source.action === 'scope') return;\n const byTurn = new Map<number, SharedVersionMeta[]>();\n for (const copy of source.remotes) {\n const i = turn.get(copy.name);\n if (i === undefined) continue;\n const copies = byTurn.get(i);\n if (copies) copies.push(copy);\n else byTurn.set(i, [copy]);\n }\n if (byTurn.size === 0) return;\n // The host leads its row and a later election finds the host's tag by the flag, so both move with it.\n const hostTurn = source.host ? turn.get(source.remotes[0]!.name) : undefined;\n source.remotes = source.remotes.filter(r => !turn.has(r.name));\n if (hostTurn !== undefined) source.host = false;\n const tagMoves = moves.get(source.tag) ?? [];\n for (const [i, copies] of byTurn)\n tagMoves.push({ turn: i, at, source, copies, host: i === hostTurn });\n moves.set(source.tag, tagMoves);\n });\n\n const rows = [...versions];\n for (const [tag, tagMoves] of moves) {\n tagMoves.sort((a, b) => a.turn - b.turn || a.at - b.at);\n let scoped = rowAt(rows, tag, 'scope');\n if (!scoped) {\n scoped = { tag, host: false, action: 'scope', remotes: [] };\n rows.splice(rows.indexOf(tagMoves[0]!.source) + 1, 0, scoped);\n }\n for (const { copies, host } of tagMoves)\n if (host) {\n scoped.host = true;\n scoped.remotes.unshift(...copies);\n } else scoped.remotes.push(...copies);\n }\n return rows.filter(v => v.remotes.length > 0);\n}\n\ntype Move = {\n turn: number;\n at: number;\n source: SharedVersion;\n copies: SharedVersionMeta[];\n host: boolean;\n};\n", "// Iterative path halving: no stack growth in the browser.\nexport function unionFind() {\n const parent = new Map<string, string>();\n const find = (node: string): string => {\n for (let up = parent.get(node); up !== undefined; up = parent.get(node)) {\n const grand = parent.get(up);\n if (grand === undefined) return up;\n parent.set(node, grand);\n node = grand;\n }\n return node;\n };\n const union = (a: string, b: string): void => {\n const rootA = find(a);\n const rootB = find(b);\n if (rootA !== rootB) parent.set(rootB, rootA);\n };\n return { find, union };\n}\n", "import type { DeepReadonly } from 'lib/utils/deep-readonly';\nimport { compareStrings } from 'lib/utils/compare-strings';\nimport { unionFind } from 'lib/utils/union-find';\nimport type { ExternalName, shareScope } from './external.contract';\nimport type { SharedVersionMeta, VersionName } from './version.contract';\n\n// An entrypoint as a consumer imports it; pooling keys by it, not by external name (see `pooling/builds.ts`).\nexport type Specifier = string;\n\n// An npm package name carries at most one `/` (after a leading `@scope`), so any deeper segment is a\n// subpath of the package above it: `@framework/core/testing` -> `@framework/core`, `rxjs/operators`\n// -> `rxjs`.\nexport function npmPackageOf(name: ExternalName): ExternalName {\n const depth = name.startsWith('@') ? 2 : 1;\n let cut = -1;\n for (let seen = 0; seen < depth; seen++) {\n cut = name.indexOf('/', cut + 1);\n if (cut === -1) return name;\n }\n return name.slice(0, cut);\n}\n\nexport type PackageOf = (specifier: Specifier) => ExternalName;\n\ntype StoredCopy = readonly [ExternalName, DeepReadonly<SharedVersionMeta>];\n\n// See docs/version-resolver.md \u00A7\"One build per package\".\nexport function packageOfIn(\n scope: DeepReadonly<shareScope>,\n loading: readonly StoredCopy[] = []\n): PackageOf {\n // A fast path, measured: without a declared library the records group by npm name anyway.\n if (!declaresLibrary(scope, loading)) return npmPackageOf;\n\n // Each component keeps its shortest name.\n const { find, union } = unionFind();\n const shortest = new Map<ExternalName, ExternalName>();\n for (const [name, copy] of copiesIn(scope, loading)) {\n const anchor = anchorOf(name, copy);\n for (const specifier in copy.entries) {\n const best = shorter(\n shortest.get(find(anchor)) ?? anchor,\n shortest.get(find(specifier)) ?? specifier\n );\n union(anchor, specifier);\n shortest.set(find(anchor), best);\n }\n }\n // An unlisted specifier joins whatever its npm name joined.\n return specifier => {\n const npm = npmPackageOf(specifier);\n return shortest.get(find(specifier)) ?? shortest.get(find(npm)) ?? npm;\n };\n}\n\nconst shorter = (a: ExternalName, b: ExternalName): ExternalName =>\n b.length < a.length || (b.length === a.length && compareStrings(b, a) < 0) ? b : a;\n\nfunction declaresLibrary(scope: DeepReadonly<shareScope>, loading: readonly StoredCopy[]): boolean {\n const isLibrary = (name: ExternalName, copies: readonly DeepReadonly<SharedVersionMeta>[]) =>\n npmPackageOf(name) !== name && copies.some(copy => copy.dense);\n return (\n loading.some(([name, copy]) => isLibrary(name, [copy])) ||\n Object.entries(scope).some(([name, external]) =>\n external.versions.some(version => isLibrary(name, version.remotes))\n )\n );\n}\n\n// A flat copy declares no package, so it is at least its npm package.\nconst anchorOf = (name: ExternalName, copy: DeepReadonly<SharedVersionMeta>): ExternalName =>\n copy.dense ? name : npmPackageOf(name);\n\nfunction* copiesIn(\n scope: DeepReadonly<shareScope>,\n loading: readonly StoredCopy[]\n): Generator<StoredCopy> {\n yield* loading;\n for (const [name, external] of Object.entries(scope))\n for (const version of external.versions) for (const copy of version.remotes) yield [name, copy];\n}\n\n// Tags per specifier that also pin the rest of their package: a package is one version, so its tag is the\n// root's, else the first entrypoint's \u2014 a package can ship entrypoints only (`material/table`, no root).\nexport class SpecifierTags extends Map<Specifier, VersionName> {\n private readonly packages = new Map<ExternalName, VersionName>();\n\n constructor(\n private readonly packageOf: PackageOf,\n entries: Iterable<readonly [Specifier, VersionName]> = []\n ) {\n super();\n for (const [specifier, tag] of entries) this.set(specifier, tag);\n }\n\n override set(specifier: Specifier, tag: VersionName): this {\n super.set(specifier, tag);\n const pkg = this.packageOf(specifier);\n if (pkg === specifier || !this.packages.has(pkg)) this.packages.set(pkg, tag);\n return this;\n }\n\n override clear(): void {\n super.clear();\n this.packages.clear();\n }\n\n pinnedTag(specifier: Specifier): VersionName | undefined {\n return this.get(specifier) ?? this.packages.get(this.packageOf(specifier));\n }\n}\n\nexport function groupByPackage<T>(\n items: Iterable<T>,\n nameOf: (item: T) => ExternalName,\n packageOf: PackageOf\n): Map<ExternalName, T[]> {\n const packages = new Map<ExternalName, T[]>();\n for (const item of items) {\n const pkg = packageOf(nameOf(item));\n const group = packages.get(pkg);\n if (group) group.push(item);\n else packages.set(pkg, [item]);\n }\n return packages;\n}\n", "import type { DeepReadonly } from 'lib/utils/deep-readonly';\nimport { compareStrings } from 'lib/utils/compare-strings';\nimport type { RemoteName } from '../remote/remote-info.contract';\nimport type { AcceptsTag } from './compatibility';\nimport type { ExternalName, SharedExternal, shareScope } from './external.contract';\nimport { mergeRows, redirectRow, rowAt, scopeRemotes } from './rows';\nimport { groupByPackage, packageOfIn, type Specifier } from './specifier';\nimport type { SharedVersion, SharedVersionMeta, VersionName } from './version.contract';\n\nexport type ServingBuild = { remote: RemoteName; tag: VersionName };\n\nexport type ServingCandidate = ServingBuild & {\n host: boolean;\n downloads: number;\n fallbacks: number;\n rootBasis: boolean;\n};\n\nexport type PackageExternals = readonly (readonly [ExternalName, DeepReadonly<SharedExternal>])[];\ntype Row = DeepReadonly<SharedVersion>;\ntype Placed = { row: Row; copy: DeepReadonly<SharedVersionMeta> };\n\nexport const byServingRank = (a: ServingCandidate, b: ServingCandidate): number =>\n Number(b.host) - Number(a.host) ||\n a.downloads - b.downloads ||\n a.fallbacks - b.fallbacks ||\n Number(b.rootBasis) - Number(a.rootBasis) ||\n compareStrings(a.remote, b.remote);\n\nexport const packagesOf = (scope: DeepReadonly<shareScope>): Map<ExternalName, ExternalName[]> =>\n groupByPackage(Object.keys(scope), name => name, packageOfIn(scope));\n\nexport const isPooled = (externals: PackageExternals): boolean =>\n externals.some(([, e]) => e.poolName !== undefined);\n\n// `externals` are package `pkg`'s, in record order. A share row of the root fixes the tag its secondaries follow.\nexport function servingBuildOf(\n pkg: ExternalName,\n externals: PackageExternals,\n accepts: AcceptsTag\n): ServingBuild | undefined {\n if (externals.length === 0) return undefined;\n // A dense build names the record of secondaries it does not declare by npm name, without shipping that root.\n const rootRows = shareRows(externals.find(([name]) => name === pkg)?.[1]).filter(row =>\n row.remotes.some(copy => pkg in copy.entries)\n );\n const rows = rootRows.length > 0 ? rootRows : externals.flatMap(([, e]) => shareRows(e));\n\n // The election already chose the global build; ranking again per package could split a pool.\n if (isPooled(externals)) return pooledBuild(externals, rows);\n\n const consumers = consumersOf(externals);\n const builds = buildsOf(rows, rootRows, consumers);\n // A host build outranks every other at any cost: only two or more of the builds left need pricing.\n const hosts = builds.filter(b => b.host);\n const field = hosts.length > 0 ? hosts : builds;\n if (field.length <= 1) return field[0] && { remote: field[0].remote, tag: field[0].tag };\n return cheapestBuild(field, costTable(consumers, accepts));\n}\n\nfunction cheapestBuild(\n field: UnpricedBuild[],\n costOf: (build: ServingBuild) => Cost\n): ServingBuild {\n let best: ServingCandidate | undefined;\n for (const b of field) {\n const { downloads, fallbacks } = costOf(b);\n const candidate: ServingCandidate = {\n remote: b.remote,\n tag: b.tag,\n host: b.host,\n rootBasis: b.rootBasis,\n downloads,\n fallbacks,\n };\n if (!best || byServingRank(candidate, best) < 0) best = candidate;\n }\n const { remote, tag } = best!;\n return { remote, tag };\n}\n\n// The serving build's copies publish even where that external's own election skipped them, so every specifier\n// the build ships stays in its surface. Mutates `external` in place.\nexport function shareServingCopies(external: SharedExternal, serving: ServingBuild): void {\n for (const row of external.versions) {\n const copy = row.remotes.find(c => c.name === serving.remote);\n if (row.action !== 'skip' || !copy || rowAt(external.versions, row.tag, 'share')) continue;\n row.action = 'share';\n // A host leading the row stops being its basis.\n if (row.remotes[0] !== copy) row.host = false;\n row.remotes = [copy, ...row.remotes.filter(c => c !== copy)];\n }\n}\n\nexport type ServingObjector = { copy: SharedVersionMeta; tag: VersionName };\n\n// Every share row at another tag follows the serving build the way a version follows its winner. Mutates\n// `external` in place; `objector` is the first strict copy that keeps its own build instead.\nexport function followServingBuild(\n external: SharedExternal,\n serving: ServingBuild,\n accepts: AcceptsTag\n): { external: SharedExternal; objector?: ServingObjector } {\n let objector: ServingObjector | undefined;\n const rebuilt = external.versions.flatMap(version => {\n if (\n version.action !== 'share' ||\n version.tag === serving.tag ||\n version.remotes[0]!.name === serving.remote\n )\n return [version];\n const rows = redirectRow(version, serving.tag, accepts);\n // A share row redirects into a `scope` row only of copies that reject the tag strictly.\n const scoped = rows.find(row => row.action === 'scope');\n if (scoped && !objector) objector = { copy: scoped.remotes[0]!, tag: scoped.tag };\n return rows;\n });\n external.versions = mergeRows(rebuilt);\n return { external, objector };\n}\n\n// A strict copy's range is its own tag, and a tag scope holds that tag only.\nconst sameTagOnly: AcceptsTag = (offered, ownTag) => offered === ownTag;\n\n// The strict scope shares every tag side by side, so each package at each tag is served on its own.\nexport const servedScopes = (scope: shareScope, strict: boolean): shareScope[] =>\n strict ? strictTagScopes(scope) : [scope];\n\n// See docs/version-resolver.md \u00A7\"Entrypoint coverage and tearing\". Rows are cloned, but each remote's copy in them is\n// the record's own, so the map's `cached` marks reach storage.\nfunction strictTagScopes(scope: shareScope): shareScope[] {\n const tagScopes: shareScope[] = [];\n for (const [pkg, names] of packagesOf(scope)) {\n const byTag = new Map<VersionName, shareScope>();\n for (const name of names)\n for (const row of scope[name]!.versions) {\n let tagScope = byTag.get(row.tag);\n if (!tagScope) byTag.set(row.tag, (tagScope = {}));\n const external = (tagScope[name] ??= { ...scope[name]!, versions: [] });\n external.versions.push({ ...row, remotes: [...row.remotes] });\n }\n for (const [tag, tagScope] of byTag) {\n // A record stored before determine skipped the strict scope can hold a skip row here, whose copy publishes\n // nothing.\n const lead = [tagScope[pkg], ...Object.values(tagScope)]\n .map(external => external && rowAt(external.versions, tag, 'share'))\n .find(row => row !== undefined);\n if (lead) {\n const serving = { remote: lead.remotes[0]!.name, tag };\n for (const external of Object.values(tagScope)) external.servingBuild = serving.remote;\n scopeFallbacks(Object.entries(tagScope), serving, sameTagOnly);\n }\n tagScopes.push(tagScope);\n }\n }\n return tagScopes;\n}\n\nfunction shareRows(external: DeepReadonly<SharedExternal> | undefined): Row[] {\n return external?.versions.filter(v => v.action === 'share') ?? [];\n}\n\n// Keyed by tag and remote rather than the first row per remote, so record order never decides.\nfunction buildsOf(\n rows: Row[],\n rootRows: Row[],\n consumers: Map<RemoteName, Placed[]>\n): UnpricedBuild[] {\n const candidates = new Map<string, UnpricedBuild>();\n for (const row of rows)\n for (const copy of row.remotes) {\n const key = `${row.tag}|${copy.name}`;\n if (copy.servedBy || candidates.has(key) || shipsTwoTags(consumers.get(copy.name)!)) continue;\n const basis = copy === row.remotes[0];\n candidates.set(key, {\n remote: copy.name,\n tag: row.tag,\n host: basis && row.host,\n rootBasis: basis && rootRows.includes(row),\n });\n }\n return [...candidates.values()];\n}\n\n// The pool's winner, where it ships the root (or, without one, any of the package); otherwise the widest copy:\n// every share-row copy lies within the one the election published, so the widest ships exactly it.\nfunction pooledBuild(externals: PackageExternals, rows: Row[]): ServingBuild | undefined {\n const winner = externals.find(([, e]) => e.poolWinner !== undefined)?.[1].poolWinner;\n const shipped = rows.flatMap(row =>\n row.remotes.filter(copy => !copy.servedBy).map(copy => ({ remote: copy.name, tag: row.tag }))\n );\n const own = shipped.find(c => c.remote === winner);\n if (own) return own;\n const width = ({ remote, tag }: ServingBuild) =>\n externals.reduce((n, [, e]) => {\n const copy = shareRows(e)\n .find(row => row.tag === tag)\n ?.remotes.find(c => c.name === remote);\n return n + Object.keys(copy?.entries ?? {}).length;\n }, 0);\n return shipped\n .map(c => ({ c, width: width(c) }))\n .sort((a, b) => b.width - a.width || compareStrings(a.c.remote, b.c.remote))[0]?.c;\n}\n\nexport function consumersOf(externals: PackageExternals): Map<RemoteName, Placed[]> {\n const consumers = new Map<RemoteName, Placed[]>();\n for (const [, external] of externals)\n for (const row of external.versions)\n for (const copy of row.remotes) {\n const placed = consumers.get(copy.name);\n if (placed) placed.push({ row, copy });\n else consumers.set(copy.name, [{ row, copy }]);\n }\n return consumers;\n}\n\ntype Cost = Pick<ServingCandidate, 'downloads' | 'fallbacks'>;\ntype UnpricedBuild = Omit<ServingCandidate, keyof Cost>;\n// The remotes that fall back and those that download their own build; a cached copy costs nothing.\ntype Toll = { fallbacks: Set<RemoteName>; downloads: Set<RemoteName> };\n\n// Builds at one tag that ship the same specifiers there cost the same but for their own remote, so each such\n// surface is tolled once.\nexport function costTable(\n consumers: Map<RemoteName, Placed[]>,\n accepts: AcceptsTag\n): (build: ServingBuild) => Cost {\n const scanned = new Map<VersionName, { surface: Surface; toll: Toll }[]>();\n const keyed = new Map<string, Toll>();\n const tollFor = (build: ServingBuild, surface: Surface): Toll => {\n let atTag = scanned.get(build.tag);\n if (!atTag) scanned.set(build.tag, (atTag = []));\n const known = atTag.find(t => sameSurface(t.surface, surface));\n if (known) return known.toll;\n if (atTag.length < SCANNED_SURFACES_PER_TAG) {\n const toll = tollOf(consumers, surface, build.tag, accepts);\n atTag.push({ surface, toll });\n return toll;\n }\n const key = [build.tag, ...[...surface].map(([s, tag]) => `${s}@${tag}`).sort()].join('\\n');\n let toll = keyed.get(key);\n if (!toll) keyed.set(key, (toll = tollOf(consumers, surface, build.tag, accepts)));\n return toll;\n };\n return build => {\n const surface = surfaceOf(consumers, build);\n const toll = tollFor(build, surface);\n const others = (remotes: Set<RemoteName>) => remotes.size - Number(remotes.has(build.remote));\n // The serving remote never falls back, and downloads its own build unless cached.\n return {\n downloads: others(toll.downloads) + Number(!allCached(consumers.get(build.remote) ?? [])),\n fallbacks: others(toll.fallbacks),\n };\n };\n}\n\nfunction tollOf(\n consumers: Map<RemoteName, Placed[]>,\n surface: Surface,\n tag: VersionName,\n accepts: AcceptsTag\n): Toll {\n const toll: Toll = { fallbacks: new Set(), downloads: new Set() };\n for (const [remote, placed] of consumers) {\n const fallback = fallsBack(placed, surface, tag, accepts);\n if (fallback) toll.fallbacks.add(remote);\n if (!allCached(placed) && (fallback || runsOffMap(placed, tag, accepts)))\n toll.downloads.add(remote);\n }\n return toll;\n}\n\nconst allCached = (placed: Placed[]): boolean => placed.every(({ copy }) => copy.cached);\n\n// The remotes that run part of the package off the map and part on it, ship a specifier outside the copies the\n// serving build publishes, or ship the package at two tags; without a serving build, every remote with a copy on\n// the map. Each serves the whole package from its own build instead, so none of its files imports another build's.\nexport function fallbacksOf(\n externals: PackageExternals,\n serving: ServingBuild | undefined,\n accepts: AcceptsTag\n): RemoteName[] {\n const consumers = consumersOf(externals);\n const surface: Surface = serving ? surfaceOf(consumers, serving) : new Map();\n return [...consumers]\n .filter(\n ([remote, placed]) =>\n remote !== serving?.remote && fallsBack(placed, surface, serving?.tag, accepts)\n )\n .map(([remote]) => remote);\n}\n\nexport function scopeFallbacks(\n externals: readonly (readonly [ExternalName, SharedExternal])[],\n serving: ServingBuild | undefined,\n accepts: AcceptsTag\n): void {\n const remotes = fallbacksOf(externals, serving, accepts);\n for (const [, external] of externals)\n external.versions = scopeRemotes(external.versions, remotes);\n}\n\n// The first copy, in record order, that `rejectsServingTag` while its remote's coverage alone (`splitsPackage`)\n// would keep it on the map: a remote its coverage already sends to its own build never refuses.\nexport function strictObjectorOf(\n externals: PackageExternals,\n serving: ServingBuild,\n accepts: AcceptsTag\n): (ServingObjector & { external: ExternalName }) | undefined {\n const consumers = consumersOf(externals);\n const surface = surfaceOf(consumers, serving);\n for (const [name, external] of externals)\n for (const row of external.versions)\n for (const copy of row.remotes)\n if (\n copy.name !== serving.remote &&\n rejectsServingTag({ row, copy }, surface, serving.tag, accepts) &&\n !splitsPackage(consumers.get(copy.name)!, surface, serving.tag, accepts)\n )\n return { external: name, copy, tag: row.tag };\n return undefined;\n}\n\n// Each specifier the serving build publishes, at the tag of the row it publishes it from.\ntype Surface = Map<Specifier, VersionName>;\n\n// Up to here a scan beats building the sorted key; a surface past it is looked up by that key after the scan.\nexport const SCANNED_SURFACES_PER_TAG = 8;\n\nfunction sameSurface(a: Surface, b: Surface): boolean {\n if (a.size !== b.size) return false;\n for (const [specifier, tag] of a) if (b.get(specifier) !== tag) return false;\n return true;\n}\n\nfunction surfaceOf(consumers: Map<RemoteName, Placed[]>, serving: ServingBuild): Surface {\n const surface: Surface = new Map();\n for (const p of consumers.get(serving.remote) ?? [])\n if ((p.row.tag === serving.tag && onMap(p)) || leadsShareRow(p))\n for (const s in p.copy.entries) if (!surface.has(s)) surface.set(s, p.row.tag);\n return surface;\n}\n\n// followServingBuild leaves these rows shared, so a host's copies at its other tags publish too.\nconst leadsShareRow = ({ row, copy }: Placed): boolean =>\n row.action === 'share' && row.remotes[0] === copy;\n\nconst onMap = ({ row }: Placed): boolean => row.action !== 'scope';\n\n// A remote with no copy on the map already runs the package wholly from its own build.\nfunction splitsPackage(\n placed: Placed[],\n surface: Surface,\n tag: VersionName | undefined,\n accepts: AcceptsTag\n): boolean {\n const uncovered = (p: Placed) =>\n Object.keys(p.copy.entries).some(s => {\n const servedAt = surface.get(s);\n // A strict copy takes a host's file at another tag only where its range accepts it; the serving tag is\n // `rejectsServingTag`'s to judge.\n return servedAt === undefined || (servedAt !== tag && rejectsTag(p, servedAt, accepts));\n });\n return (\n placed.some(onMap) && (shipsTwoTags(placed) || placed.some(p => !onMap(p) || uncovered(p)))\n );\n}\n\n// Whether a runtime load's copies split the package over the claims the committed map serves of it, as\n// `splitsPackage` judges a remote at init.\nexport function loadSplitsPackage(\n pkg: ExternalName,\n copies: readonly { tag: VersionName; copy: SharedVersionMeta }[],\n claims: readonly (readonly [Specifier, { tag: VersionName }])[],\n accepts: AcceptsTag\n): boolean {\n const root = claims.find(([specifier]) => specifier === pkg)?.[1];\n // Without a root claim the serving tag is unknown: every claim counts as at it, so only a gap or two tags\n // split.\n const servingTag = root?.tag ?? claims[0]![1].tag;\n const surface: Surface = new Map(\n claims.map(([specifier, { tag }]) => [specifier, root ? tag : servingTag])\n );\n // Each loaded copy as the share row it would join; a loader is never the host.\n const placed: Placed[] = copies.map(({ tag, copy }) => ({\n row: { tag, action: 'share', host: false, remotes: [copy] },\n copy,\n }));\n return splitsPackage(placed, surface, servingTag, accepts);\n}\n\nfunction fallsBack(\n placed: Placed[],\n surface: Surface,\n tag: VersionName | undefined,\n accepts: AcceptsTag\n): boolean {\n return (\n splitsPackage(placed, surface, tag, accepts) ||\n (tag !== undefined && placed.some(p => rejectsServingTag(p, surface, tag, accepts)))\n );\n}\n\n// A strict copy on the map whose range rejects the serving tag, where the serving build publishes one of its\n// files. A scoped copy already runs its own build, so it never objects.\nfunction rejectsServingTag(\n p: Placed,\n surface: Surface,\n tag: VersionName,\n accepts: AcceptsTag\n): boolean {\n return (\n onMap(p) &&\n rejectsTag(p, tag, accepts) &&\n Object.keys(p.copy.entries).some(s => surface.get(s) === tag)\n );\n}\n\nconst rejectsTag = ({ row, copy }: Placed, tag: VersionName, accepts: AcceptsTag): boolean =>\n copy.strictVersion && !accepts(tag, row.tag, copy.requiredVersion);\n\n// The host serves every tag it ships; any other remote shipping two tags has no one build to serve or follow.\nconst shipsTwoTags = (placed: Placed[]): boolean =>\n !placed.some(p => p.row.host && p.row.remotes[0] === p.copy) &&\n placed.some(p => p.row.tag !== placed[0]!.row.tag);\n\nfunction runsOffMap(placed: Placed[], tag: VersionName, accepts: AcceptsTag): boolean {\n return placed.some(p => !onMap(p) || rejectsTag(p, tag, accepts));\n}\n", "function join(pathA: string, pathB: string): string {\n pathA = pathA.endsWith('/') ? pathA.slice(0, -1) : pathA;\n pathB = pathB.startsWith('/') ? pathB.slice(1) : pathB;\n return `${pathA}/${pathB}`;\n}\n\nfunction getScope(path: string) {\n if (!path) return '';\n\n const parts = path.split('/');\n\n if (parts[parts.length - 1] === '' || parts[parts.length - 1]!.includes('.')) {\n parts.pop();\n }\n if (parts.length < 1) return '';\n\n return `${parts.join('/')}/`;\n}\n\nexport { join, getScope };\n", "import type { DrivingContract } from 'lib/core/2.app/driving-ports/driving.contract';\nimport type { ForSharedExternalsStorage } from 'lib/core/2.app/driving-ports/for-shared-externals-storage.port';\nimport type { SharedExternal, SharedVersion, SharedVersionMeta } from 'lib/core/1.domain';\nimport { claimTable } from 'lib/core/1.domain/externals/claims';\nimport { servedScopes } from 'lib/core/1.domain/externals/serving';\nimport type { Specifier } from 'lib/core/1.domain/externals/specifier';\nimport type { GetSharedOptions, ShareInfos, Shared } from './share-infos.contract';\nimport * as _path from 'lib/utils/path';\n\ntype ScopeType = ReturnType<ForSharedExternalsStorage['scopeType']>;\n\n/**\n * Adapts native federation's shared externals to the `ShareInfos` shape webpack\n * Module Federation expects (see `docs/module-federation.md`).\n */\nexport function createGetShared(\n ports: Pick<DrivingContract, 'sharedExternalsRepo' | 'remoteInfoRepo' | 'browser'>\n): (options?: GetSharedOptions) => ShareInfos {\n return (options = {}) => {\n const shared: ShareInfos = {};\n\n for (const scope of ports.sharedExternalsRepo.getScopes({ includeGlobal: true })) {\n const scopeType = ports.sharedExternalsRepo.scopeType(scope);\n const externals = ports.sharedExternalsRepo.getFromScope(scope);\n\n for (const { specifier, version, source } of sharedEntrypoints(externals, scopeType)) {\n const url = resolveUrl(source, source.entries[specifier]!);\n if (!url) continue;\n\n const shareObject: Shared = {\n version: version.tag,\n get: () => ports.browser.importModule(url).then(module => () => module),\n shareConfig: {\n singleton: scopeType === 'strict' ? false : (options.singleton ?? true),\n requiredVersion: resolveRequiredVersion(version, options, scopeType),\n ...(scopeType === 'strict' ? { strictVersion: true } : {}),\n },\n };\n if (scopeType !== 'global') shareObject.scope = scope;\n\n if (!shared[specifier]) shared[specifier] = [];\n shared[specifier]!.push(shareObject);\n }\n }\n\n return shared;\n };\n\n function resolveUrl(source: SharedVersionMeta, file: string): string | undefined {\n return ports.remoteInfoRepo\n .tryGet(source.name)\n .map(remote => _path.join(remote.scopeUrl, file))\n .get();\n }\n}\n\ntype SharedEntrypoint = { specifier: Specifier; version: SharedVersion; source: SharedVersionMeta };\n\nfunction sharedEntrypoints(\n externals: Record<string, SharedExternal>,\n scopeType: ScopeType\n): SharedEntrypoint[] {\n const view = scopeType === 'global' ? 'claims' : 'shareClaims';\n return servedScopes(externals, scopeType === 'strict').flatMap(served =>\n servedEntrypoints(served, view)\n );\n}\n\n// One singleton per specifier, from the build the import map serves it from.\nfunction servedEntrypoints(\n externals: Record<string, SharedExternal>,\n view: 'claims' | 'shareClaims'\n): SharedEntrypoint[] {\n return [...claimTable(Object.entries(externals))[view]].map(\n ([specifier, { external, tag, copy }]) => ({\n specifier,\n version: externals[external]!.versions.find(v => v.action === 'share' && v.tag === tag)!,\n source: copy,\n })\n );\n}\n\nfunction resolveRequiredVersion(\n version: SharedVersion,\n options: GetSharedOptions,\n scopeType: ScopeType\n): string {\n // Strict shares a specific version, not a range: pin to the exact tag so a\n // consumer reuses only that version (MF matches via satisfy(version, required)).\n if (scopeType === 'strict') return version.tag;\n if (typeof options.requiredVersionPrefix === 'string') {\n return `${options.requiredVersionPrefix}${version.tag}`;\n }\n // The copy MF hands out says nothing about what the version demands. Advertising its range would let MF\n // hand a sibling a copy that sibling's own range rejects, so a disagreement pins to the tag \u2014 the only value\n // no remote can reject, and the one the import map already commits to.\n const declared = new Set(version.remotes.map(r => r.requiredVersion));\n if (declared.size > 1) return version.tag;\n\n return version.remotes[0]?.requiredVersion || `^${version.tag}`;\n}\n"],
|
|
5
|
-
"mappings": ";AAEA,SAAS,eAAe,GAAW,GAAmB;AACpD,SAAO,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI;AAClC;;;
|
|
4
|
+
"sourcesContent": ["// Code-unit order: unlike `localeCompare`, the same in every browser and locale, so anything stored or elected\n// from a name comes out identical everywhere.\nfunction compareStrings(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\nexport { compareStrings };\n", "import type { DeepReadonly } from 'lib/utils/deep-readonly';\nimport { compareStrings } from 'lib/utils/compare-strings';\nimport type { SharedVersion, SharedVersionMeta } from './version.contract';\n\nconst coverage = (remote: SharedVersionMeta): number => Object.keys(remote.entries).length;\n\n// `remotes[0]` is the basis: the copy every consumer of the version resolves to. Host and cached\n// outrank coverage because repointing them invalidates a committed import map. An exact tie goes to the\n// name, so the basis does not depend on the order remotes register in. `preferDev` (#118) ranks a dev build\n// above cached: only page-load init passes it, where nothing is committed yet, so cached means an earlier page.\nexport function isBetterBasis(\n candidate: SharedVersionMeta,\n basis: SharedVersionMeta,\n preferDev = false\n): boolean {\n if (preferDev && Boolean(basis.dev) !== Boolean(candidate.dev)) return Boolean(candidate.dev);\n if (basis.cached !== candidate.cached) return candidate.cached;\n const byCoverage = coverage(candidate) - coverage(basis);\n return byCoverage === 0 ? compareStrings(candidate.name, basis.name) < 0 : byCoverage > 0;\n}\n\nexport function addRemoteToVersion(\n version: SharedVersion,\n remote: SharedVersionMeta,\n isHost = false,\n preferDev = false\n): void {\n if (isHost) {\n version.host = true;\n version.remotes.unshift(remote);\n return;\n }\n\n const basis = version.remotes[0];\n if (basis && !version.host && isBetterBasis(remote, basis, preferDev)) {\n version.remotes.unshift(remote);\n return;\n }\n\n version.remotes.push(remote);\n}\n\n// `remotes[0]` answers \"who serves this version\", never \"what does this version demand\":\n// `requiredVersion`, `strictVersion` and `cached` are per-build settings, so remotes sharing a tag\n// can disagree on all three. Negotiation must therefore aggregate over every remote, or basis\n// precedence would decide compatibility. Deduped because those settings repeat across a version's\n// remotes, which keeps the resolver's O(versions\u00B2) selection from also scaling with remote count.\nexport function versionDemands(version: SharedVersion): SharedVersionMeta[] {\n if (version.remotes.length < 2) return version.remotes;\n\n const distinct = new Map<string, SharedVersionMeta>();\n for (const remote of version.remotes) {\n const key = `${remote.requiredVersion}|${remote.strictVersion}|${remote.cached}`;\n if (!distinct.has(key)) distinct.set(key, remote);\n }\n return Array.from(distinct.values());\n}\n\n// Which copies of a version may publish a specifier, in basis precedence; callers keep the first claim. The\n// copies build one tag, so this is what the version ships, not whose files a consumer runs; see\n// docs/version-resolver.md#copies-of-one-version.\n//\n// Only copies that publish their own files count. Pooling places a copy in a subpool running a foreign\n// build via `servedBy`, and the import map then names that build's files for it, per consumer \u2014 so what\n// such a copy bundles answers for nobody but itself, and counting it would promise a specifier no\n// consumer of this version can resolve. Pooling keeps a subpool copy out of the basis slot for exactly\n// this reason, so a `share` version's basis always survives the skip. `accepts` lets a caller discount a\n// copy without dropping its specifiers for the next copy to claim.\nexport function forEachVersionEntry(\n version: DeepReadonly<SharedVersion>,\n accepts: ((remote: SharedVersionMeta) => boolean) | undefined,\n visit: (entrypoint: string, remote: SharedVersionMeta) => void\n): void {\n for (const remote of version.remotes) {\n if (remote.servedBy) continue;\n if (accepts && !accepts(remote)) continue;\n for (const entrypoint in remote.entries) visit(entrypoint, remote);\n }\n}\n\ntype Covered = Pick<ReadonlySet<string>, 'has'>;\n\n// Counted rather than listed: the resolver's O(versions\u00B2) selection loop asks this once per remote per\n// candidate.\nexport function countUncoveredEntrypoints(remote: SharedVersionMeta, covered: Covered): number {\n let uncovered = 0;\n for (const entrypoint in remote.entries) {\n if (!covered.has(entrypoint)) uncovered++;\n }\n return uncovered;\n}\n", "import type { DeepReadonly } from 'lib/utils/deep-readonly';\nimport type { RemoteName } from '../remote/remote-info.contract';\nimport { type ExternalName, GLOBAL_SCOPE, type SharedExternal } from './external.contract';\nimport type { SharedVersion, SharedVersionMeta, VersionName } from './version.contract';\nimport type { Specifier } from './specifier';\nimport { forEachVersionEntry } from './basis';\n\nexport type Claim = {\n external: ExternalName;\n tag: VersionName;\n copy: SharedVersionMeta;\n};\n\nexport type ClaimTable = {\n // What `imports` serves: an external with a serving build claims that build's copy alone, one without claims its\n // share rows in record order; the first claim per specifier wins. A skip copy claims nothing.\n claims: Map<Specifier, Claim>;\n // Share rows reached after their external's name was claimed at another tag. A row whose name another row\n // claimed claims nothing, duplicate or not.\n duplicates: Set<DeepReadonly<SharedVersion>>;\n // What a named scope serves: the same rule over every share row, duplicates included.\n shareClaims: Map<Specifier, Claim>;\n};\n\nexport function claimTable(\n externals: Iterable<readonly [ExternalName, DeepReadonly<SharedExternal>]>,\n publishes?: (copy: SharedVersionMeta) => boolean\n): ClaimTable {\n const table: ClaimTable = { claims: new Map(), duplicates: new Set(), shareClaims: new Map() };\n const ordered = [...externals];\n\n const claim = (\n into: Map<Specifier, Claim>,\n external: ExternalName,\n version: DeepReadonly<SharedVersion>,\n accepts = publishes\n ) =>\n forEachVersionEntry(version, accepts, (specifier, copy) => {\n if (!into.has(specifier)) into.set(specifier, { external, tag: version.tag, copy });\n });\n\n const seedServingBuild = (name: ExternalName, external: DeepReadonly<SharedExternal>) => {\n const isServing = (copy: SharedVersionMeta) =>\n copy.name === external.servingBuild && (!publishes || publishes(copy));\n for (const version of external.versions) {\n if (version.action !== 'share') continue;\n claim(table.shareClaims, name, version, isServing);\n claim(table.claims, name, version, isServing);\n }\n };\n\n const claimShareRow = (\n name: ExternalName,\n external: DeepReadonly<SharedExternal>,\n version: DeepReadonly<SharedVersion>\n ) => {\n const prior = table.claims.get(name);\n const duplicate = prior !== undefined && prior.tag !== version.tag;\n // A row the serving host leads is never a duplicate: it ships that tag beside the one claimed first.\n if (duplicate && !(version.host && version.remotes[0]!.name === external.servingBuild))\n table.duplicates.add(version);\n // The seed already claimed an external with a serving build.\n if (external.servingBuild) return;\n claim(table.shareClaims, name, version);\n if (!duplicate && (!prior || prior.external === name)) claim(table.claims, name, version);\n };\n\n for (const [name, external] of ordered)\n if (external.servingBuild) seedServingBuild(name, external);\n for (const [name, external] of ordered)\n for (const version of external.versions)\n if (version.action === 'share') claimShareRow(name, external, version);\n\n return table;\n}\n\n// What a committed map serves in one scope, from externals in record order: `imports` holds only the copies\n// marked cached, a named scope's override may name any copy. `without` is a loaded remote whose copies the\n// record already holds but the committed map does not.\nexport function committedClaims(\n externals: Iterable<readonly [ExternalName, DeepReadonly<SharedExternal>]>,\n { shareScope, without }: { shareScope: string; without?: RemoteName }\n): Map<Specifier, Claim> {\n const publishes = (copy: SharedVersionMeta) =>\n copy.name !== without && (shareScope !== GLOBAL_SCOPE || copy.cached);\n return claimTable(externals, publishes).claims;\n}\n\n// One version's claims as `claimTable` makes them: the serving copy alone when there is one, else every copy in\n// record order.\nexport function versionClaims(\n version: DeepReadonly<SharedVersion>,\n servingBuild: RemoteName | undefined\n): Map<Specifier, SharedVersionMeta> {\n const claims = new Map<Specifier, SharedVersionMeta>();\n const claim = (specifier: Specifier, copy: SharedVersionMeta) => {\n if (!claims.has(specifier)) claims.set(specifier, copy);\n };\n const accepts = servingBuild\n ? (copy: SharedVersionMeta) => copy.name === servingBuild\n : undefined;\n forEachVersionEntry(version, accepts, claim);\n return claims;\n}\n", "import type { RemoteName } from '../remote/remote-info.contract';\nimport type { AcceptsTag } from './compatibility';\nimport type {\n SharedVersion,\n SharedVersionAction,\n SharedVersionMeta,\n VersionName,\n} from './version.contract';\n\n// Newest tag first, as `write()` orders a record. Leaves a tag's rows in their order (`sort` is stable).\nexport const byTag =\n (compare: (a: VersionName, b: VersionName) => number) =>\n (a: SharedVersion, b: SharedVersion): number =>\n compare(b.tag, a.tag);\n\nexport const rowAt = (\n versions: readonly SharedVersion[],\n tag: VersionName,\n action: SharedVersionAction\n): SharedVersion | undefined => versions.find(v => v.tag === tag && v.action === action);\n\n// One row per (tag, action): the first row of each absorbs the copies of the later ones, in place. `lead` is\n// that first row wherever it stands, so its copies stay in front.\nexport function mergeRows(versions: SharedVersion[], lead?: SharedVersion): SharedVersion[] {\n const merged = new Map<string, SharedVersion>();\n if (lead) merged.set(`${lead.tag}|${lead.action}`, lead);\n return versions.filter(v => {\n const key = `${v.tag}|${v.action}`;\n const first = merged.get(key);\n if (first === v) return true;\n if (!first) {\n merged.set(key, v);\n return true;\n }\n first.remotes.push(...v.remotes);\n return false;\n });\n}\n\n// `redirectRow` and `scopeRemote` can leave a `scope` row beside a shareable one at the same tag.\nexport function findVersionForTag(\n versions: SharedVersion[],\n tag: string\n): SharedVersion | undefined {\n let scoped: SharedVersion | undefined;\n for (const version of versions) {\n if (version.tag !== tag) continue;\n if (version.action !== 'scope') return version;\n scoped ??= version;\n }\n return scoped;\n}\n\n// Points a row at `tag`: its copies skip to it, and only the strict ones that reject it keep their own build,\n// split off into a `scope` row beside it. Returns the rows that replace `version`, in order; mutates it in place.\nexport function redirectRow(\n version: SharedVersion,\n tag: VersionName,\n acceptsTag: AcceptsTag\n): SharedVersion[] {\n const objecting = new Set(\n version.remotes.filter(r => r.strictVersion && !acceptsTag(tag, version.tag, r.requiredVersion))\n );\n\n if (objecting.size === 0) {\n version.action = 'skip';\n return [version];\n }\n if (objecting.size === version.remotes.length) {\n version.action = 'scope';\n return [version];\n }\n\n version.remotes = version.remotes.filter(r => !objecting.has(r));\n version.action = 'skip';\n // Beside its source rather than appended: the record stays newest-first, which `write()`\n // guarantees and a later election reads as \"the latest\".\n return [version, { tag: version.tag, host: false, action: 'scope', remotes: [...objecting] }];\n}\n\n// Moves each of `remotes`' copies out of every `share` and `skip` row into the `scope` row of their tag; a new scope\n// row goes right after its source, so the record stays newest-first. In one pass, yet ordered as if each remote\n// moved in turn: copies by remote then by row, a host's in front. Mutates the source rows, returns the new list.\nexport function scopeRemotes(\n versions: SharedVersion[],\n remotes: readonly RemoteName[]\n): SharedVersion[] {\n if (remotes.length === 0) return versions;\n const turn = new Map(remotes.map((remote, i) => [remote, i]));\n const moves = new Map<VersionName, Move[]>();\n versions.forEach((source, at) => {\n if (source.action === 'scope') return;\n const byTurn = new Map<number, SharedVersionMeta[]>();\n for (const copy of source.remotes) {\n const i = turn.get(copy.name);\n if (i === undefined) continue;\n const copies = byTurn.get(i);\n if (copies) copies.push(copy);\n else byTurn.set(i, [copy]);\n }\n if (byTurn.size === 0) return;\n // The host leads its row and a later election finds the host's tag by the flag, so both move with it.\n const hostTurn = source.host ? turn.get(source.remotes[0]!.name) : undefined;\n source.remotes = source.remotes.filter(r => !turn.has(r.name));\n if (hostTurn !== undefined) source.host = false;\n const tagMoves = moves.get(source.tag) ?? [];\n for (const [i, copies] of byTurn)\n tagMoves.push({ turn: i, at, source, copies, host: i === hostTurn });\n moves.set(source.tag, tagMoves);\n });\n\n const rows = [...versions];\n for (const [tag, tagMoves] of moves) {\n tagMoves.sort((a, b) => a.turn - b.turn || a.at - b.at);\n let scoped = rowAt(rows, tag, 'scope');\n if (!scoped) {\n scoped = { tag, host: false, action: 'scope', remotes: [] };\n rows.splice(rows.indexOf(tagMoves[0]!.source) + 1, 0, scoped);\n }\n for (const { copies, host } of tagMoves)\n if (host) {\n scoped.host = true;\n scoped.remotes.unshift(...copies);\n } else scoped.remotes.push(...copies);\n }\n return rows.filter(v => v.remotes.length > 0);\n}\n\ntype Move = {\n turn: number;\n at: number;\n source: SharedVersion;\n copies: SharedVersionMeta[];\n host: boolean;\n};\n", "// Iterative path halving: no stack growth in the browser.\nexport function unionFind() {\n const parent = new Map<string, string>();\n const find = (node: string): string => {\n for (let up = parent.get(node); up !== undefined; up = parent.get(node)) {\n const grand = parent.get(up);\n if (grand === undefined) return up;\n parent.set(node, grand);\n node = grand;\n }\n return node;\n };\n const union = (a: string, b: string): void => {\n const rootA = find(a);\n const rootB = find(b);\n if (rootA !== rootB) parent.set(rootB, rootA);\n };\n return { find, union };\n}\n", "import type { DeepReadonly } from 'lib/utils/deep-readonly';\nimport { compareStrings } from 'lib/utils/compare-strings';\nimport { unionFind } from 'lib/utils/union-find';\nimport type { ExternalName, shareScope } from './external.contract';\nimport type { SharedVersionMeta, VersionName } from './version.contract';\n\n// An entrypoint as a consumer imports it; pooling keys by it, not by external name (see `pooling/builds.ts`).\nexport type Specifier = string;\n\n// An npm package name carries at most one `/` (after a leading `@scope`), so any deeper segment is a\n// subpath of the package above it: `@framework/core/testing` -> `@framework/core`, `rxjs/operators`\n// -> `rxjs`.\nexport function npmPackageOf(name: ExternalName): ExternalName {\n const depth = name.startsWith('@') ? 2 : 1;\n let cut = -1;\n for (let seen = 0; seen < depth; seen++) {\n cut = name.indexOf('/', cut + 1);\n if (cut === -1) return name;\n }\n return name.slice(0, cut);\n}\n\nexport type PackageOf = (specifier: Specifier) => ExternalName;\n\ntype StoredCopy = readonly [ExternalName, DeepReadonly<SharedVersionMeta>];\n\n// See docs/version-resolver.md \u00A7\"One build per package\".\nexport function packageOfIn(\n scope: DeepReadonly<shareScope>,\n loading: readonly StoredCopy[] = []\n): PackageOf {\n // A fast path, measured: without a declared library the records group by npm name anyway.\n if (!declaresLibrary(scope, loading)) return npmPackageOf;\n\n // Each component keeps its shortest name.\n const { find, union } = unionFind();\n const shortest = new Map<ExternalName, ExternalName>();\n for (const [name, copy] of copiesIn(scope, loading)) {\n const anchor = anchorOf(name, copy);\n for (const specifier in copy.entries) {\n const best = shorter(\n shortest.get(find(anchor)) ?? anchor,\n shortest.get(find(specifier)) ?? specifier\n );\n union(anchor, specifier);\n shortest.set(find(anchor), best);\n }\n }\n // An unlisted specifier joins whatever its npm name joined.\n return specifier => {\n const npm = npmPackageOf(specifier);\n return shortest.get(find(specifier)) ?? shortest.get(find(npm)) ?? npm;\n };\n}\n\nconst shorter = (a: ExternalName, b: ExternalName): ExternalName =>\n b.length < a.length || (b.length === a.length && compareStrings(b, a) < 0) ? b : a;\n\nfunction declaresLibrary(scope: DeepReadonly<shareScope>, loading: readonly StoredCopy[]): boolean {\n const isLibrary = (name: ExternalName, copies: readonly DeepReadonly<SharedVersionMeta>[]) =>\n npmPackageOf(name) !== name && copies.some(copy => copy.dense);\n return (\n loading.some(([name, copy]) => isLibrary(name, [copy])) ||\n Object.entries(scope).some(([name, external]) =>\n external.versions.some(version => isLibrary(name, version.remotes))\n )\n );\n}\n\n// A flat copy declares no package, so it is at least its npm package.\nconst anchorOf = (name: ExternalName, copy: DeepReadonly<SharedVersionMeta>): ExternalName =>\n copy.dense ? name : npmPackageOf(name);\n\nfunction* copiesIn(\n scope: DeepReadonly<shareScope>,\n loading: readonly StoredCopy[]\n): Generator<StoredCopy> {\n yield* loading;\n for (const [name, external] of Object.entries(scope))\n for (const version of external.versions) for (const copy of version.remotes) yield [name, copy];\n}\n\n// Tags per specifier that also pin the rest of their package: a package is one version, so its tag is the\n// root's, else the first entrypoint's \u2014 a package can ship entrypoints only (`material/table`, no root).\nexport class SpecifierTags extends Map<Specifier, VersionName> {\n private readonly packages = new Map<ExternalName, VersionName>();\n\n constructor(\n private readonly packageOf: PackageOf,\n entries: Iterable<readonly [Specifier, VersionName]> = []\n ) {\n super();\n for (const [specifier, tag] of entries) this.set(specifier, tag);\n }\n\n override set(specifier: Specifier, tag: VersionName): this {\n super.set(specifier, tag);\n const pkg = this.packageOf(specifier);\n if (pkg === specifier || !this.packages.has(pkg)) this.packages.set(pkg, tag);\n return this;\n }\n\n override clear(): void {\n super.clear();\n this.packages.clear();\n }\n\n pinnedTag(specifier: Specifier): VersionName | undefined {\n return this.get(specifier) ?? this.packages.get(this.packageOf(specifier));\n }\n}\n\nexport function groupByPackage<T>(\n items: Iterable<T>,\n nameOf: (item: T) => ExternalName,\n packageOf: PackageOf\n): Map<ExternalName, T[]> {\n const packages = new Map<ExternalName, T[]>();\n for (const item of items) {\n const pkg = packageOf(nameOf(item));\n const group = packages.get(pkg);\n if (group) group.push(item);\n else packages.set(pkg, [item]);\n }\n return packages;\n}\n", "import type { DeepReadonly } from 'lib/utils/deep-readonly';\nimport { compareStrings } from 'lib/utils/compare-strings';\nimport type { RemoteName } from '../remote/remote-info.contract';\nimport type { AcceptsTag } from './compatibility';\nimport type { ExternalName, SharedExternal, shareScope } from './external.contract';\nimport { mergeRows, redirectRow, rowAt, scopeRemotes } from './rows';\nimport { groupByPackage, packageOfIn, type Specifier } from './specifier';\nimport type { SharedVersion, SharedVersionMeta, VersionName } from './version.contract';\n\nexport type ServingBuild = { remote: RemoteName; tag: VersionName };\n\nexport type ServingCandidate = ServingBuild & {\n host: boolean;\n downloads: number;\n fallbacks: number;\n rootBasis: boolean;\n};\n\nexport type PackageExternals = readonly (readonly [ExternalName, DeepReadonly<SharedExternal>])[];\ntype Row = DeepReadonly<SharedVersion>;\ntype Placed = { row: Row; copy: DeepReadonly<SharedVersionMeta> };\n\nexport const byServingRank = (a: ServingCandidate, b: ServingCandidate): number =>\n Number(b.host) - Number(a.host) ||\n a.downloads - b.downloads ||\n a.fallbacks - b.fallbacks ||\n Number(b.rootBasis) - Number(a.rootBasis) ||\n compareStrings(a.remote, b.remote);\n\nexport const packagesOf = (scope: DeepReadonly<shareScope>): Map<ExternalName, ExternalName[]> =>\n groupByPackage(Object.keys(scope), name => name, packageOfIn(scope));\n\nexport const isPooled = (externals: PackageExternals): boolean =>\n externals.some(([, e]) => e.poolName !== undefined);\n\n// `externals` are package `pkg`'s, in record order. A share row of the root fixes the tag its secondaries follow.\nexport function servingBuildOf(\n pkg: ExternalName,\n externals: PackageExternals,\n accepts: AcceptsTag\n): ServingBuild | undefined {\n if (externals.length === 0) return undefined;\n // A dense build names the record of secondaries it does not declare by npm name, without shipping that root.\n const rootRows = shareRows(externals.find(([name]) => name === pkg)?.[1]).filter(row =>\n row.remotes.some(copy => pkg in copy.entries)\n );\n const rows = rootRows.length > 0 ? rootRows : externals.flatMap(([, e]) => shareRows(e));\n\n // The election already chose the global build; ranking again per package could split a pool.\n if (isPooled(externals)) return pooledBuild(externals, rows);\n\n const consumers = consumersOf(externals);\n const builds = buildsOf(rows, rootRows, consumers);\n // A host build outranks every other at any cost: only two or more of the builds left need pricing.\n const hosts = builds.filter(b => b.host);\n const field = hosts.length > 0 ? hosts : builds;\n if (field.length <= 1) return field[0] && { remote: field[0].remote, tag: field[0].tag };\n return cheapestBuild(field, costTable(consumers, accepts));\n}\n\nfunction cheapestBuild(\n field: UnpricedBuild[],\n costOf: (build: ServingBuild) => Cost\n): ServingBuild {\n let best: ServingCandidate | undefined;\n for (const b of field) {\n const { downloads, fallbacks } = costOf(b);\n const candidate: ServingCandidate = {\n remote: b.remote,\n tag: b.tag,\n host: b.host,\n rootBasis: b.rootBasis,\n downloads,\n fallbacks,\n };\n if (!best || byServingRank(candidate, best) < 0) best = candidate;\n }\n const { remote, tag } = best!;\n return { remote, tag };\n}\n\n// The serving build's copies publish even where that external's own election skipped them, so every specifier\n// the build ships stays in its surface. Mutates `external` in place.\nexport function shareServingCopies(external: SharedExternal, serving: ServingBuild): void {\n for (const row of external.versions) {\n if (row.action !== 'skip' || rowAt(external.versions, row.tag, 'share')) continue;\n const own = row.remotes.filter(c => c.name === serving.remote);\n if (own.length === 0) continue;\n row.action = 'share';\n // A host leading the row stops being its basis.\n if (row.remotes[0] !== own[0]) row.host = false;\n // Every record of a split build leads, or another copy publishes what the second one ships (#133).\n row.remotes = [...own, ...row.remotes.filter(c => c.name !== serving.remote)];\n }\n}\n\nexport type ServingObjector = { copy: SharedVersionMeta; tag: VersionName };\n\n// Every share row at another tag follows the serving build the way a version follows its winner. Mutates\n// `external` in place; `objector` is the first strict copy that keeps its own build instead.\nexport function followServingBuild(\n external: SharedExternal,\n serving: ServingBuild,\n accepts: AcceptsTag\n): { external: SharedExternal; objector?: ServingObjector } {\n let objector: ServingObjector | undefined;\n const rebuilt = external.versions.flatMap(version => {\n if (\n version.action !== 'share' ||\n version.tag === serving.tag ||\n version.remotes[0]!.name === serving.remote\n )\n return [version];\n const rows = redirectRow(version, serving.tag, accepts);\n // A share row redirects into a `scope` row only of copies that reject the tag strictly.\n const scoped = rows.find(row => row.action === 'scope');\n if (scoped && !objector) objector = { copy: scoped.remotes[0]!, tag: scoped.tag };\n return rows;\n });\n external.versions = mergeRows(rebuilt);\n return { external, objector };\n}\n\n// A strict copy's range is its own tag, and a tag scope holds that tag only.\nconst sameTagOnly: AcceptsTag = (offered, ownTag) => offered === ownTag;\n\n// The strict scope shares every tag side by side, so each package at each tag is served on its own.\nexport const servedScopes = (scope: shareScope, strict: boolean): shareScope[] =>\n strict ? strictTagScopes(scope) : [scope];\n\n// See docs/version-resolver.md \u00A7\"Entrypoint coverage and tearing\". Rows are cloned, but each remote's copy in them is\n// the record's own, so the map's `cached` marks reach storage.\nfunction strictTagScopes(scope: shareScope): shareScope[] {\n const tagScopes: shareScope[] = [];\n for (const [pkg, names] of packagesOf(scope)) {\n const byTag = new Map<VersionName, shareScope>();\n for (const name of names)\n for (const row of scope[name]!.versions) {\n let tagScope = byTag.get(row.tag);\n if (!tagScope) byTag.set(row.tag, (tagScope = {}));\n const external = (tagScope[name] ??= { ...scope[name]!, versions: [] });\n external.versions.push({ ...row, remotes: [...row.remotes] });\n }\n for (const [tag, tagScope] of byTag) {\n // A record stored before determine skipped the strict scope can hold a skip row here, whose copy publishes\n // nothing.\n const lead = [tagScope[pkg], ...Object.values(tagScope)]\n .map(external => external && rowAt(external.versions, tag, 'share'))\n .find(row => row !== undefined);\n if (lead) {\n const serving = { remote: lead.remotes[0]!.name, tag };\n for (const external of Object.values(tagScope)) external.servingBuild = serving.remote;\n scopeFallbacks(Object.entries(tagScope), serving, sameTagOnly);\n }\n tagScopes.push(tagScope);\n }\n }\n return tagScopes;\n}\n\nfunction shareRows(external: DeepReadonly<SharedExternal> | undefined): Row[] {\n return external?.versions.filter(v => v.action === 'share') ?? [];\n}\n\n// Keyed by tag and remote rather than the first row per remote, so record order never decides.\nfunction buildsOf(\n rows: Row[],\n rootRows: Row[],\n consumers: Map<RemoteName, Placed[]>\n): UnpricedBuild[] {\n const builds = new Map<string, UnpricedBuild>();\n for (const row of rows)\n for (const copy of row.remotes) {\n const key = `${row.tag}|${copy.name}`;\n if (copy.servedBy || builds.has(key) || shipsTwoTags(consumers.get(copy.name)!)) continue;\n const basis = copy === row.remotes[0];\n builds.set(key, {\n remote: copy.name,\n tag: row.tag,\n host: basis && row.host,\n rootBasis: basis && rootRows.includes(row),\n });\n }\n return [...builds.values()];\n}\n\n// The pool's winner, where it ships the root (or, without one, any of the package); otherwise the widest copy:\n// every share-row copy lies within the one the election published, so the widest ships exactly it.\nfunction pooledBuild(externals: PackageExternals, rows: Row[]): ServingBuild | undefined {\n const winner = externals.find(([, e]) => e.poolWinner !== undefined)?.[1].poolWinner;\n const shipped = rows.flatMap(row =>\n row.remotes.filter(copy => !copy.servedBy).map(copy => ({ remote: copy.name, tag: row.tag }))\n );\n const own = shipped.find(c => c.remote === winner);\n if (own) return own;\n const width = ({ remote, tag }: ServingBuild) =>\n externals\n .flatMap(([, e]) => shareRows(e).find(row => row.tag === tag)?.remotes ?? [])\n .filter(copy => copy.name === remote)\n .reduce((n, copy) => n + Object.keys(copy.entries).length, 0);\n return shipped\n .map(c => ({ c, width: width(c) }))\n .sort((a, b) => b.width - a.width || compareStrings(a.c.remote, b.c.remote))[0]?.c;\n}\n\nexport function consumersOf(externals: PackageExternals): Map<RemoteName, Placed[]> {\n const consumers = new Map<RemoteName, Placed[]>();\n for (const [, external] of externals)\n for (const row of external.versions)\n for (const copy of row.remotes) {\n const placed = consumers.get(copy.name);\n if (placed) placed.push({ row, copy });\n else consumers.set(copy.name, [{ row, copy }]);\n }\n return consumers;\n}\n\ntype Cost = Pick<ServingCandidate, 'downloads' | 'fallbacks'>;\ntype UnpricedBuild = Omit<ServingCandidate, keyof Cost>;\n// The remotes that fall back and those that download their own build; a cached copy costs nothing.\ntype Toll = { fallbacks: Set<RemoteName>; downloads: Set<RemoteName> };\n\n// Builds at one tag that ship the same specifiers there cost the same but for their own remote, so each such\n// surface is tolled once.\nexport function costTable(\n consumers: Map<RemoteName, Placed[]>,\n accepts: AcceptsTag\n): (build: ServingBuild) => Cost {\n const scanned = new Map<VersionName, { surface: Surface; toll: Toll }[]>();\n const keyed = new Map<string, Toll>();\n const tollFor = (build: ServingBuild, surface: Surface): Toll => {\n let atTag = scanned.get(build.tag);\n if (!atTag) scanned.set(build.tag, (atTag = []));\n const known = atTag.find(t => sameSurface(t.surface, surface));\n if (known) return known.toll;\n if (atTag.length < SCANNED_SURFACES_PER_TAG) {\n const toll = tollOf(consumers, surface, build.tag, accepts);\n atTag.push({ surface, toll });\n return toll;\n }\n const key = [build.tag, ...[...surface].map(([s, tag]) => `${s}@${tag}`).sort()].join('\\n');\n let toll = keyed.get(key);\n if (!toll) keyed.set(key, (toll = tollOf(consumers, surface, build.tag, accepts)));\n return toll;\n };\n return build => {\n const surface = surfaceOf(consumers, build);\n const toll = tollFor(build, surface);\n const others = (remotes: Set<RemoteName>) => remotes.size - Number(remotes.has(build.remote));\n // The serving remote never falls back, and downloads its own build unless cached.\n return {\n downloads: others(toll.downloads) + Number(!allCached(consumers.get(build.remote) ?? [])),\n fallbacks: others(toll.fallbacks),\n };\n };\n}\n\nfunction tollOf(\n consumers: Map<RemoteName, Placed[]>,\n surface: Surface,\n tag: VersionName,\n accepts: AcceptsTag\n): Toll {\n const toll: Toll = { fallbacks: new Set(), downloads: new Set() };\n for (const [remote, placed] of consumers) {\n const fallback = fallsBack(placed, surface, tag, accepts);\n if (fallback) toll.fallbacks.add(remote);\n if (!allCached(placed) && (fallback || runsOffMap(placed, tag, accepts)))\n toll.downloads.add(remote);\n }\n return toll;\n}\n\nconst allCached = (placed: Placed[]): boolean => placed.every(({ copy }) => copy.cached);\n\n// The remotes that run part of the package off the map and part on it, ship a specifier outside the copies the\n// serving build publishes, or ship the package at two tags; without a serving build, every remote with a copy on\n// the map. Each serves the whole package from its own build instead, so none of its files imports another build's.\nexport function fallbacksOf(\n externals: PackageExternals,\n serving: ServingBuild | undefined,\n accepts: AcceptsTag\n): RemoteName[] {\n const consumers = consumersOf(externals);\n const surface: Surface = serving ? surfaceOf(consumers, serving) : new Map();\n return [...consumers]\n .filter(\n ([remote, placed]) =>\n remote !== serving?.remote && fallsBack(placed, surface, serving?.tag, accepts)\n )\n .map(([remote]) => remote);\n}\n\nexport function scopeFallbacks(\n externals: readonly (readonly [ExternalName, SharedExternal])[],\n serving: ServingBuild | undefined,\n accepts: AcceptsTag\n): void {\n const remotes = fallbacksOf(externals, serving, accepts);\n for (const [, external] of externals)\n external.versions = scopeRemotes(external.versions, remotes);\n}\n\n// The first copy, in record order, that `rejectsServingTag` while its remote's coverage alone (`splitsPackage`)\n// would keep it on the map: a remote its coverage already sends to its own build never refuses.\nexport function strictObjectorOf(\n externals: PackageExternals,\n serving: ServingBuild,\n accepts: AcceptsTag\n): (ServingObjector & { external: ExternalName }) | undefined {\n const consumers = consumersOf(externals);\n const surface = surfaceOf(consumers, serving);\n for (const [name, external] of externals)\n for (const row of external.versions)\n for (const copy of row.remotes)\n if (\n copy.name !== serving.remote &&\n rejectsServingTag({ row, copy }, surface, serving.tag, accepts) &&\n !splitsPackage(consumers.get(copy.name)!, surface, serving.tag, accepts)\n )\n return { external: name, copy, tag: row.tag };\n return undefined;\n}\n\n// Each specifier the serving build publishes, at the tag of the row it publishes it from.\ntype Surface = Map<Specifier, VersionName>;\n\n// Up to here a scan beats building the sorted key; a surface past it is looked up by that key after the scan.\nexport const SCANNED_SURFACES_PER_TAG = 8;\n\nfunction sameSurface(a: Surface, b: Surface): boolean {\n if (a.size !== b.size) return false;\n for (const [specifier, tag] of a) if (b.get(specifier) !== tag) return false;\n return true;\n}\n\nfunction surfaceOf(consumers: Map<RemoteName, Placed[]>, serving: ServingBuild): Surface {\n const surface: Surface = new Map();\n for (const p of consumers.get(serving.remote) ?? [])\n if ((p.row.tag === serving.tag && onMap(p)) || leadsShareRow(p))\n for (const s in p.copy.entries) if (!surface.has(s)) surface.set(s, p.row.tag);\n return surface;\n}\n\n// followServingBuild leaves these rows shared, so a host's copies at its other tags publish too.\nconst leadsShareRow = ({ row, copy }: Placed): boolean =>\n row.action === 'share' && row.remotes[0] === copy;\n\nconst onMap = ({ row }: Placed): boolean => row.action !== 'scope';\n\n// A remote with no copy on the map already runs the package wholly from its own build.\nfunction splitsPackage(\n placed: Placed[],\n surface: Surface,\n tag: VersionName | undefined,\n accepts: AcceptsTag\n): boolean {\n const uncovered = (p: Placed) =>\n Object.keys(p.copy.entries).some(s => {\n const servedAt = surface.get(s);\n // A strict copy takes a host's file at another tag only where its range accepts it; the serving tag is\n // `rejectsServingTag`'s to judge.\n return servedAt === undefined || (servedAt !== tag && rejectsTag(p, servedAt, accepts));\n });\n return (\n placed.some(onMap) && (shipsTwoTags(placed) || placed.some(p => !onMap(p) || uncovered(p)))\n );\n}\n\n// Whether a runtime load's copies split the package over the claims the committed map serves of it, as\n// `splitsPackage` judges a remote at init.\nexport function loadSplitsPackage(\n pkg: ExternalName,\n copies: readonly { tag: VersionName; copy: SharedVersionMeta }[],\n claims: readonly (readonly [Specifier, { tag: VersionName }])[],\n accepts: AcceptsTag\n): boolean {\n const root = claims.find(([specifier]) => specifier === pkg)?.[1];\n // Without a root claim the serving tag is unknown: every claim counts as at it, so only a gap or two tags\n // split.\n const servingTag = root?.tag ?? claims[0]![1].tag;\n const surface: Surface = new Map(\n claims.map(([specifier, { tag }]) => [specifier, root ? tag : servingTag])\n );\n // Each loaded copy as the share row it would join; a loader is never the host.\n const placed: Placed[] = copies.map(({ tag, copy }) => ({\n row: { tag, action: 'share', host: false, remotes: [copy] },\n copy,\n }));\n return splitsPackage(placed, surface, servingTag, accepts);\n}\n\nfunction fallsBack(\n placed: Placed[],\n surface: Surface,\n tag: VersionName | undefined,\n accepts: AcceptsTag\n): boolean {\n return (\n splitsPackage(placed, surface, tag, accepts) ||\n (tag !== undefined && placed.some(p => rejectsServingTag(p, surface, tag, accepts)))\n );\n}\n\n// A strict copy on the map whose range rejects the serving tag, where the serving build publishes one of its\n// files. A scoped copy already runs its own build, so it never objects.\nfunction rejectsServingTag(\n p: Placed,\n surface: Surface,\n tag: VersionName,\n accepts: AcceptsTag\n): boolean {\n return (\n onMap(p) &&\n rejectsTag(p, tag, accepts) &&\n Object.keys(p.copy.entries).some(s => surface.get(s) === tag)\n );\n}\n\nconst rejectsTag = ({ row, copy }: Placed, tag: VersionName, accepts: AcceptsTag): boolean =>\n copy.strictVersion && !accepts(tag, row.tag, copy.requiredVersion);\n\n// The host serves every tag it ships; any other remote shipping two tags has no one build to serve or follow.\nconst shipsTwoTags = (placed: Placed[]): boolean =>\n !placed.some(p => p.row.host && p.row.remotes[0] === p.copy) &&\n placed.some(p => p.row.tag !== placed[0]!.row.tag);\n\nfunction runsOffMap(placed: Placed[], tag: VersionName, accepts: AcceptsTag): boolean {\n return placed.some(p => !onMap(p) || rejectsTag(p, tag, accepts));\n}\n", "function join(pathA: string, pathB: string): string {\n pathA = pathA.endsWith('/') ? pathA.slice(0, -1) : pathA;\n pathB = pathB.startsWith('/') ? pathB.slice(1) : pathB;\n return `${pathA}/${pathB}`;\n}\n\nfunction getScope(path: string) {\n if (!path) return '';\n\n const parts = path.split('/');\n\n if (parts[parts.length - 1] === '' || parts[parts.length - 1]!.includes('.')) {\n parts.pop();\n }\n if (parts.length < 1) return '';\n\n return `${parts.join('/')}/`;\n}\n\nexport { join, getScope };\n", "import type { DrivingContract } from 'lib/core/2.app/driving-ports/driving.contract';\nimport type { ForSharedExternalsStorage } from 'lib/core/2.app/driving-ports/for-shared-externals-storage.port';\nimport type { SharedExternal, SharedVersion, SharedVersionMeta } from 'lib/core/1.domain';\nimport { claimTable } from 'lib/core/1.domain/externals/claims';\nimport { servedScopes } from 'lib/core/1.domain/externals/serving';\nimport type { Specifier } from 'lib/core/1.domain/externals/specifier';\nimport type { GetSharedOptions, ShareInfos, Shared } from './share-infos.contract';\nimport * as _path from 'lib/utils/path';\n\ntype ScopeType = ReturnType<ForSharedExternalsStorage['scopeType']>;\n\n/**\n * Adapts native federation's shared externals to the `ShareInfos` shape webpack\n * Module Federation expects (see `docs/module-federation.md`).\n */\nexport function createGetShared(\n ports: Pick<DrivingContract, 'sharedExternalsRepo' | 'remoteInfoRepo' | 'browser'>\n): (options?: GetSharedOptions) => ShareInfos {\n return (options = {}) => {\n const shared: ShareInfos = {};\n\n for (const scope of ports.sharedExternalsRepo.getScopes({ includeGlobal: true })) {\n const scopeType = ports.sharedExternalsRepo.scopeType(scope);\n const externals = ports.sharedExternalsRepo.getFromScope(scope);\n\n for (const { specifier, version, source } of sharedEntrypoints(externals, scopeType)) {\n const url = resolveUrl(source, source.entries[specifier]!);\n if (!url) continue;\n\n const shareObject: Shared = {\n version: version.tag,\n get: () => ports.browser.importModule(url).then(module => () => module),\n shareConfig: {\n singleton: scopeType === 'strict' ? false : (options.singleton ?? true),\n requiredVersion: resolveRequiredVersion(version, options, scopeType),\n ...(scopeType === 'strict' ? { strictVersion: true } : {}),\n },\n };\n if (scopeType !== 'global') shareObject.scope = scope;\n\n if (!shared[specifier]) shared[specifier] = [];\n shared[specifier]!.push(shareObject);\n }\n }\n\n return shared;\n };\n\n function resolveUrl(source: SharedVersionMeta, file: string): string | undefined {\n return ports.remoteInfoRepo\n .tryGet(source.name)\n .map(remote => _path.join(remote.scopeUrl, file))\n .get();\n }\n}\n\ntype SharedEntrypoint = { specifier: Specifier; version: SharedVersion; source: SharedVersionMeta };\n\nfunction sharedEntrypoints(\n externals: Record<string, SharedExternal>,\n scopeType: ScopeType\n): SharedEntrypoint[] {\n const view = scopeType === 'global' ? 'claims' : 'shareClaims';\n return servedScopes(externals, scopeType === 'strict').flatMap(served =>\n servedEntrypoints(served, view)\n );\n}\n\n// One singleton per specifier, from the build the import map serves it from.\nfunction servedEntrypoints(\n externals: Record<string, SharedExternal>,\n view: 'claims' | 'shareClaims'\n): SharedEntrypoint[] {\n return [...claimTable(Object.entries(externals))[view]].map(\n ([specifier, { external, tag, copy }]) => ({\n specifier,\n version: externals[external]!.versions.find(v => v.action === 'share' && v.tag === tag)!,\n source: copy,\n })\n );\n}\n\nfunction resolveRequiredVersion(\n version: SharedVersion,\n options: GetSharedOptions,\n scopeType: ScopeType\n): string {\n // Strict shares a specific version, not a range: pin to the exact tag so a\n // consumer reuses only that version (MF matches via satisfy(version, required)).\n if (scopeType === 'strict') return version.tag;\n if (typeof options.requiredVersionPrefix === 'string') {\n return `${options.requiredVersionPrefix}${version.tag}`;\n }\n // The copy MF hands out says nothing about what the version demands. Advertising its range would let MF\n // hand a sibling a copy that sibling's own range rejects, so a disagreement pins to the tag \u2014 the only value\n // no remote can reject, and the one the import map already commits to.\n const declared = new Set(version.remotes.map(r => r.requiredVersion));\n if (declared.size > 1) return version.tag;\n\n return version.remotes[0]?.requiredVersion || `^${version.tag}`;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|