@bleedingdev/modern-js-plugin-bff-extensions 3.9.0-ultramodern.17 → 3.9.0-ultramodern.19

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 (63) hide show
  1. package/README.md +12 -2
  2. package/dist/cjs/backend-federation/edge.js +1 -1
  3. package/dist/cjs/backend-federation/integrity-plugin.js +112 -0
  4. package/dist/cjs/backend-federation/node-shared.js +85 -0
  5. package/dist/cjs/backend-federation/node.js +8 -3
  6. package/dist/cjs/backend-federation/plugin.js +40 -21
  7. package/dist/cjs/backend-federation/runtime.js +16 -84
  8. package/dist/cjs/backend-federation-manifest/load.js +3 -0
  9. package/dist/cjs/backend-federation-manifest/node.js +5 -3
  10. package/dist/cjs/backend-federation-manifest/remote.js +1 -7
  11. package/dist/cjs/effect-source-loader/loader.js +5 -0
  12. package/dist/esm/backend-federation/edge.mjs +1 -1
  13. package/dist/esm/backend-federation/integrity-plugin.mjs +68 -0
  14. package/dist/esm/backend-federation/node-shared.mjs +41 -0
  15. package/dist/esm/backend-federation/node.mjs +6 -4
  16. package/dist/esm/backend-federation/plugin.mjs +37 -18
  17. package/dist/esm/backend-federation/runtime.mjs +16 -84
  18. package/dist/esm/backend-federation-manifest/load.mjs +3 -0
  19. package/dist/esm/backend-federation-manifest/node.mjs +5 -3
  20. package/dist/esm/backend-federation-manifest/remote.mjs +1 -7
  21. package/dist/esm/effect-source-loader/loader.mjs +5 -0
  22. package/dist/esm-node/backend-federation/edge.mjs +1 -1
  23. package/dist/esm-node/backend-federation/integrity-plugin.mjs +69 -0
  24. package/dist/esm-node/backend-federation/node-shared.mjs +42 -0
  25. package/dist/esm-node/backend-federation/node.mjs +6 -4
  26. package/dist/esm-node/backend-federation/plugin.mjs +37 -18
  27. package/dist/esm-node/backend-federation/runtime.mjs +16 -84
  28. package/dist/esm-node/backend-federation-manifest/load.mjs +3 -0
  29. package/dist/esm-node/backend-federation-manifest/node.mjs +5 -3
  30. package/dist/esm-node/backend-federation-manifest/remote.mjs +1 -7
  31. package/dist/esm-node/effect-source-loader/loader.mjs +5 -0
  32. package/dist/types/backend-federation/edge.d.ts +4 -2
  33. package/dist/types/backend-federation/integrity-plugin.d.ts +13 -0
  34. package/dist/types/backend-federation/node-shared.d.ts +20 -0
  35. package/dist/types/backend-federation/node.d.ts +3 -0
  36. package/dist/types/backend-federation/plugin.d.ts +4 -0
  37. package/dist/types/backend-federation/types.d.ts +2 -7
  38. package/dist/types/backend-federation-manifest/types.d.ts +2 -1
  39. package/package.json +11 -11
  40. package/src/backend-federation/edge.ts +7 -4
  41. package/src/backend-federation/integrity-plugin.ts +157 -0
  42. package/src/backend-federation/load.ts +0 -1
  43. package/src/backend-federation/node-shared.ts +76 -0
  44. package/src/backend-federation/node.ts +9 -4
  45. package/src/backend-federation/plugin.ts +73 -25
  46. package/src/backend-federation/runtime.ts +16 -90
  47. package/src/backend-federation/types.ts +2 -11
  48. package/src/backend-federation-manifest/load.ts +1 -0
  49. package/src/backend-federation-manifest/node.ts +5 -3
  50. package/src/backend-federation-manifest/remote.ts +0 -6
  51. package/src/backend-federation-manifest/types.ts +2 -0
  52. package/src/effect-source-loader/loader.ts +6 -0
  53. package/src/effect-source-loader/rspack-loader.ts +0 -1
  54. package/dist/cjs/backend-federation/entry.js +0 -94
  55. package/dist/cjs/backend-federation/node-evaluator.js +0 -48
  56. package/dist/esm/backend-federation/entry.mjs +0 -56
  57. package/dist/esm/backend-federation/node-evaluator.mjs +0 -7
  58. package/dist/esm-node/backend-federation/entry.mjs +0 -57
  59. package/dist/esm-node/backend-federation/node-evaluator.mjs +0 -8
  60. package/dist/types/backend-federation/entry.d.ts +0 -2
  61. package/dist/types/backend-federation/node-evaluator.d.ts +0 -2
  62. package/src/backend-federation/entry.ts +0 -125
  63. package/src/backend-federation/node-evaluator.ts +0 -13
@@ -1,12 +1,14 @@
1
+ import { evaluateNodeBackendFederationCommonJs } from "@modern-js/server-runtime-extensions/backend-federation-security/node";
1
2
  import { loadBackendFederatedEffectApi } from "./load.mjs";
2
- import { evaluateEffectBackendFederationCommonJs } from "./node-evaluator.mjs";
3
- function node_loadBackendFederatedEffectApi(options) {
3
+ import { effectBffHostShared, withEffectBffHostShared } from "./node-shared.mjs";
4
+ async function node_loadBackendFederatedEffectApi(options) {
4
5
  return loadBackendFederatedEffectApi({
5
6
  ...options,
7
+ shared: withEffectBffHostShared(options.shared),
6
8
  entryPolicy: {
7
9
  ...options.entryPolicy,
8
- evaluateCommonJs: options.entryPolicy?.evaluateCommonJs ?? evaluateEffectBackendFederationCommonJs
10
+ evaluateCommonJs: options.entryPolicy?.evaluateCommonJs ?? evaluateNodeBackendFederationCommonJs
9
11
  }
10
12
  });
11
13
  }
12
- export { node_loadBackendFederatedEffectApi as loadBackendFederatedEffectApi };
14
+ export { effectBffHostShared, node_loadBackendFederatedEffectApi as loadBackendFederatedEffectApi };
@@ -1,24 +1,43 @@
1
+ import { PROVIDED_ENTRY_SCHEME, registerRemoteContainer } from "./integrity-plugin.mjs";
2
+ function toContainer(entry) {
3
+ return {
4
+ get (id) {
5
+ return ()=>Promise.resolve(entry.get(id)).then((factory)=>factory());
6
+ },
7
+ init (...args) {
8
+ return entry.init?.(...args);
9
+ }
10
+ };
11
+ }
1
12
  function createBackendFederationLoadEntryPlugin(options) {
13
+ const provided = new WeakMap();
2
14
  return {
3
15
  name: 'modernjs-backend-federation-load-entry',
4
- loadEntry ({ remoteInfo }) {
5
- return Promise.resolve(options.resolveEntry({
6
- name: remoteInfo.name,
7
- entry: remoteInfo.entry,
8
- type: remoteInfo.type,
9
- entryGlobalName: remoteInfo.entryGlobalName,
10
- shareScope: remoteInfo.shareScope
11
- })).then((entry)=>{
12
- if (void 0 === entry) return;
13
- return {
14
- get (id) {
15
- return ()=>Promise.resolve(entry.get(id)).then((factory)=>factory());
16
- },
17
- init (...args) {
18
- return entry.init?.(...args);
19
- }
20
- };
21
- });
16
+ afterMatchRemote ({ origin, remoteInfo }) {
17
+ if (void 0 === remoteInfo || !PROVIDED_ENTRY_SCHEME.test(remoteInfo.entry)) return;
18
+ let entries = provided.get(origin);
19
+ if (entries?.has(remoteInfo.name) !== true && origin.moduleCache.get(remoteInfo.name)?.remoteEntryExports !== void 0) return;
20
+ if (void 0 === entries) {
21
+ entries = new Map();
22
+ provided.set(origin, entries);
23
+ }
24
+ const runtimeEntries = entries;
25
+ let pending = runtimeEntries.get(remoteInfo.name);
26
+ if (void 0 === pending) {
27
+ pending = Promise.resolve(options.resolveEntry({
28
+ name: remoteInfo.name,
29
+ entry: remoteInfo.entry,
30
+ type: remoteInfo.type
31
+ })).then((entry)=>{
32
+ if (void 0 !== entry) registerRemoteContainer(origin, remoteInfo, toContainer(entry));
33
+ });
34
+ const settled = pending;
35
+ runtimeEntries.set(remoteInfo.name, settled);
36
+ settled.catch(()=>{
37
+ if (runtimeEntries.get(remoteInfo.name) === settled) runtimeEntries.delete(remoteInfo.name);
38
+ });
39
+ }
40
+ return pending;
22
41
  }
23
42
  };
24
43
  }
@@ -1,90 +1,22 @@
1
1
  import { ModuleFederation } from "@module-federation/runtime";
2
- import { loadBackendFederationExpose } from "./entry.mjs";
3
- import { createBackendFederationLoadEntryPlugin } from "./plugin.mjs";
2
+ import { createBackendFederationIntegrityPlugin } from "./integrity-plugin.mjs";
4
3
  import { collectRemotes } from "./remotes.mjs";
5
- function _check_private_redeclaration(obj, privateCollection) {
6
- if (privateCollection.has(obj)) throw new TypeError("Cannot initialize the same private elements twice on an object");
7
- }
8
- function _class_apply_descriptor_get(receiver, descriptor) {
9
- if (descriptor.get) return descriptor.get.call(receiver);
10
- return descriptor.value;
11
- }
12
- function _class_apply_descriptor_set(receiver, descriptor, value) {
13
- if (descriptor.set) descriptor.set.call(receiver, value);
14
- else {
15
- if (!descriptor.writable) throw new TypeError("attempted to set read only private field");
16
- descriptor.value = value;
17
- }
18
- }
19
- function _class_extract_field_descriptor(receiver, privateMap, action) {
20
- if (!privateMap.has(receiver)) throw new TypeError("attempted to " + action + " private field on non-instance");
21
- return privateMap.get(receiver);
22
- }
23
- function _class_private_field_get(receiver, privateMap) {
24
- var descriptor = _class_extract_field_descriptor(receiver, privateMap, "get");
25
- return _class_apply_descriptor_get(receiver, descriptor);
26
- }
27
- function _class_private_field_init(obj, privateMap, value) {
28
- _check_private_redeclaration(obj, privateMap);
29
- privateMap.set(obj, value);
30
- }
31
- function _class_private_field_set(receiver, privateMap, value) {
32
- var descriptor = _class_extract_field_descriptor(receiver, privateMap, "set");
33
- _class_apply_descriptor_set(receiver, descriptor, value);
34
- return value;
35
- }
36
- let nextRuntimeId = 0;
37
- function parseRemoteRequest(request) {
38
- const [remoteName, ...exposeParts] = request.split('/');
39
- if (void 0 === remoteName || 0 === remoteName.length || 0 === exposeParts.length) return;
40
- return remoteName;
41
- }
42
- function createLocalEntryPlugin(options, remotes) {
43
- return createBackendFederationLoadEntryPlugin({
44
- resolveEntry (resolvedRemote) {
45
- const remote = remotes.find((candidate)=>candidate.name === resolvedRemote.name);
46
- if (void 0 === remote) return;
47
- return {
48
- get (expose) {
49
- return ()=>loadBackendFederationExpose(remote, expose, options);
50
- }
51
- };
52
- }
53
- });
54
- }
55
- function isolateRegisteredRemoteEntries(remotes) {
56
- const runtimeId = nextRuntimeId;
57
- nextRuntimeId += 1;
58
- return remotes.map((remote)=>({
59
- ...remote,
60
- entry: `${remote.entry}#modernjs-backend-runtime-${runtimeId}`
61
- }));
62
- }
63
- var _remoteNames = /*#__PURE__*/ new WeakMap();
64
- class BackendFederationRuntime extends ModuleFederation {
65
- loadRemote(request, loadOptions) {
66
- const remoteName = parseRemoteRequest(request);
67
- if (void 0 === remoteName) return Promise.reject(new Error(`[BFF][Effect] Invalid backend federation request ${request}.`));
68
- if (!_class_private_field_get(this, _remoteNames).has(remoteName)) return Promise.reject(new Error(`[BFF][Effect] Missing backend federation remote ${remoteName}.`));
69
- return super.loadRemote(request, loadOptions);
70
- }
71
- constructor(options, remotes){
72
- super({
73
- name: options.hostName,
74
- remotes: isolateRegisteredRemoteEntries(remotes),
75
- shared: {},
76
- plugins: [
77
- createLocalEntryPlugin(options, remotes)
78
- ]
79
- }), _class_private_field_init(this, _remoteNames, {
80
- writable: true,
81
- value: void 0
82
- });
83
- _class_private_field_set(this, _remoteNames, new Set(remotes.map((remote)=>remote.name)));
84
- }
85
- }
86
4
  function createBackendFederationRuntime(options) {
87
5
  const remotes = collectRemotes(options);
88
- return new BackendFederationRuntime(options, remotes);
6
+ return new ModuleFederation({
7
+ name: options.hostName,
8
+ remotes: remotes.map(({ name, entry, type })=>({
9
+ name,
10
+ entry,
11
+ ...type ? {
12
+ type
13
+ } : {}
14
+ })),
15
+ shared: options.shared ?? {},
16
+ plugins: [
17
+ ...options.plugins ?? [],
18
+ createBackendFederationIntegrityPlugin(options, remotes)
19
+ ]
20
+ });
89
21
  }
90
22
  export { createBackendFederationRuntime };
@@ -129,6 +129,9 @@ async function loadBackendFederatedEffectApiFromManifest(options) {
129
129
  ...options.plugins ? {
130
130
  plugins: options.plugins
131
131
  } : {},
132
+ ...options.shared ? {
133
+ shared: options.shared
134
+ } : {},
132
135
  entryPolicy: {
133
136
  ...options.entryPolicy,
134
137
  signal: scope.signal,
@@ -1,11 +1,13 @@
1
- import { evaluateEffectBackendFederationCommonJs } from "../backend-federation/node-evaluator.mjs";
1
+ import { evaluateNodeBackendFederationCommonJs } from "@modern-js/server-runtime-extensions/backend-federation-security/node";
2
+ import { withEffectBffHostShared } from "../backend-federation/node-shared.mjs";
2
3
  import { loadBackendFederatedEffectApiFromManifest } from "./load.mjs";
3
- function node_loadBackendFederatedEffectApiFromManifest(options) {
4
+ async function node_loadBackendFederatedEffectApiFromManifest(options) {
4
5
  return loadBackendFederatedEffectApiFromManifest({
5
6
  ...options,
7
+ shared: withEffectBffHostShared(options.shared),
6
8
  entryPolicy: {
7
9
  ...options.entryPolicy,
8
- evaluateCommonJs: options.entryPolicy?.evaluateCommonJs ?? evaluateEffectBackendFederationCommonJs
10
+ evaluateCommonJs: options.entryPolicy?.evaluateCommonJs ?? evaluateNodeBackendFederationCommonJs
9
11
  }
10
12
  });
11
13
  }
@@ -31,13 +31,7 @@ function resolveBackendFederationRemoteFromManifest(manifest, remoteOverride = {
31
31
  ...verification ? {
32
32
  verification
33
33
  } : {},
34
- ...remoteOverride.entryGlobalName ? {
35
- entryGlobalName: remoteOverride.entryGlobalName
36
- } : {},
37
- expose,
38
- ...remoteOverride.shareScope ? {
39
- shareScope: remoteOverride.shareScope
40
- } : {}
34
+ expose
41
35
  };
42
36
  }
43
37
  export { resolveBackendFederationRemoteFromManifest };
@@ -79,6 +79,7 @@ const nodeBuiltins = new Set([
79
79
  ...builtinModules.map((name)=>`node:${name}`)
80
80
  ]);
81
81
  const dependencyResolutionMarker = 'modern-js-effect-dependency-resolution';
82
+ const BFF_RUNTIME_PACKAGE = /^@modern-js\/(?:bff-effect|plugin-bff)(?:\/|$)/;
82
83
  async function isEsmOnlyFile(filename) {
83
84
  const extension = node_path.extname(filename).toLowerCase();
84
85
  if ('.mjs' === extension || '.mts' === extension) return true;
@@ -107,6 +108,10 @@ function externalizeInstalledDependencies(options) {
107
108
  buildApi.onResolve({
108
109
  filter: /.*/
109
110
  }, async (args)=>{
111
+ if (BFF_RUNTIME_PACKAGE.test(args.path)) return {
112
+ external: true,
113
+ path: args.path
114
+ };
110
115
  if (!isBareSpecifier(args.path) || nodeBuiltins.has(args.path) || args.pluginData?.[dependencyResolutionMarker] === true) return nodeBuiltins.has(args.path) ? {
111
116
  external: true,
112
117
  path: args.path
@@ -3,7 +3,7 @@ import { BACKEND_FEDERATION_CONTRACT_VERSION, BACKEND_FEDERATION_EFFECT_EXPOSE,
3
3
  import { createBackendFederationLoadEntryPlugin, createBackendFederationRuntime } from "./edge-runtime.mjs";
4
4
  import { normalizeExpose } from "./utils.mjs";
5
5
  function loadBackendFederatedEffectApi(options) {
6
- if (void 0 !== options.runtime || void 0 !== options.entryPolicy) return Promise.reject(new Error('[BFF][Effect] Edge backend federation does not execute custom runtimes or entry evaluators. Register a static or service-binding entry provider.'));
6
+ if (void 0 !== options.runtime || void 0 !== options.entryPolicy || void 0 !== options.shared) return Promise.reject(new Error('[BFF][Effect] Edge backend federation does not execute custom runtimes, entry evaluators or host shares. Register a static or service-binding entry provider.'));
7
7
  const remoteName = options.remote?.name ?? options.remoteName;
8
8
  if (void 0 === remoteName || 0 === remoteName.length) return Promise.reject(new Error('[BFF][Effect] Missing backend federation remote name.'));
9
9
  if (!options.expected?.unitId || !options.expected.buildMarker) return Promise.reject(new Error('[BFF][Effect] Backend federation requires expected.unitId and expected.buildMarker.'));
@@ -0,0 +1,69 @@
1
+ import "node:module";
2
+ import { evaluateBackendFederationCommonJsEntry, loadVerifiedBackendFederationEntry } from "@modern-js/server-runtime-extensions/backend-federation-security";
3
+ import { Module } from "@module-federation/runtime";
4
+ const PROVIDED_ENTRY_SCHEME = /^(?:binding|service|static):/u;
5
+ function decodeDataUrl(remote) {
6
+ const commaIndex = remote.entry.indexOf(',');
7
+ if (commaIndex < 0) throw new Error(`[BFF][Effect] Backend federation remote ${remote.name} has an invalid data URL entry.`);
8
+ return new TextEncoder().encode(decodeURIComponent(remote.entry.slice(commaIndex + 1)));
9
+ }
10
+ async function importContainer(remote) {
11
+ const namespace = await import(remote.entry);
12
+ return namespace.default ?? namespace;
13
+ }
14
+ function loadContainer(remote, options) {
15
+ const verified = Boolean(remote.verification || options.entryPolicy?.expected);
16
+ const scheme = /^[a-z][a-z0-9+.-]*:/iu.exec(remote.entry)?.[0].toLowerCase();
17
+ if ('https:' === scheme || 'http:' === scheme) {
18
+ if (!verified) throw new Error(`[BFF][Effect] Backend federation remote ${remote.name} requires verified entry bytes before network execution.`);
19
+ return loadVerifiedBackendFederationEntry({
20
+ ...options.entryPolicy,
21
+ remote,
22
+ ...remote.verification ? {
23
+ verification: remote.verification
24
+ } : {}
25
+ });
26
+ }
27
+ if (verified) throw new Error(`[BFF][Effect] Backend federation remote ${remote.name} declares entry verification, but only http(s) entries are fetched and verified.`);
28
+ if ('data:' === scheme && 'module' !== remote.type) return Promise.resolve(evaluateBackendFederationCommonJsEntry(remote, decodeDataUrl(remote), options.entryPolicy?.evaluateCommonJs));
29
+ if ('data:' === scheme || 'file:' === scheme) return importContainer(remote);
30
+ throw new Error(`[BFF][Effect] Backend federation remote ${remote.name} uses unsupported entry ${remote.entry}. Use a verified http(s) entry, a data: or file: entry, or a binding:, service: or static: entry served by createBackendFederationLoadEntryPlugin().`);
31
+ }
32
+ function registerRemoteContainer(origin, remoteInfo, container) {
33
+ const module = new Module({
34
+ host: origin,
35
+ remoteInfo
36
+ });
37
+ module.remoteEntryExports = container;
38
+ origin.moduleCache.set(remoteInfo.name, module);
39
+ }
40
+ function createBackendFederationIntegrityPlugin(options, remotes) {
41
+ const loading = new Map();
42
+ const register = async (origin, remoteInfo)=>{
43
+ const remote = remotes.find((candidate)=>candidate.name === remoteInfo.name);
44
+ if (void 0 === remote) throw new Error(`[BFF][Effect] Missing backend federation remote ${remoteInfo.name}.`);
45
+ if (PROVIDED_ENTRY_SCHEME.test(remote.entry)) {
46
+ if (origin.moduleCache.get(remote.name)?.remoteEntryExports === void 0) throw new Error(`[BFF][Effect] Backend federation remote ${remote.name} has no entry provider for ${remote.entry}. Pass createBackendFederationLoadEntryPlugin() in plugins.`);
47
+ return;
48
+ }
49
+ const container = await loadContainer(remote, options);
50
+ if ('function' != typeof container?.get) throw new Error(`[BFF][Effect] Backend federation remote ${remote.name} entry must expose get().`);
51
+ registerRemoteContainer(origin, remoteInfo, container);
52
+ };
53
+ return {
54
+ name: 'modernjs-backend-federation-integrity',
55
+ afterMatchRemote ({ origin, remoteInfo }) {
56
+ if (void 0 === remoteInfo) return;
57
+ let pending = loading.get(remoteInfo.name);
58
+ if (void 0 === pending) {
59
+ pending = register(origin, remoteInfo);
60
+ loading.set(remoteInfo.name, pending);
61
+ pending.catch(()=>{
62
+ if (loading.get(remoteInfo.name) === pending) loading.delete(remoteInfo.name);
63
+ });
64
+ }
65
+ return pending;
66
+ }
67
+ };
68
+ }
69
+ export { PROVIDED_ENTRY_SCHEME, createBackendFederationIntegrityPlugin, registerRemoteContainer };
@@ -0,0 +1,42 @@
1
+ import "node:module";
2
+ import { createRequire } from "node:module";
3
+ const fromAdapter = createRequire(import.meta.url);
4
+ const effectPackageJson = fromAdapter.resolve('@modern-js/bff-effect/package.json');
5
+ const fromEffectPackage = createRequire(effectPackageJson);
6
+ let hostShared;
7
+ function effectBffHostShared() {
8
+ if (void 0 !== hostShared) return hostShared;
9
+ const { EFFECT_BFF_CONTEXT_STORAGE_SHARE, EFFECT_BFF_HANDLER_FACTORY_REGISTRY_SHARE } = fromEffectPackage('@modern-js/bff-effect/effect-edge');
10
+ const { getEffectContextStorage } = fromEffectPackage('@modern-js/bff-effect/context');
11
+ const handlerFactoryRegistry = fromEffectPackage("#effect-entry-shape-registry");
12
+ const version = fromAdapter(effectPackageJson).version;
13
+ hostShared = {
14
+ [EFFECT_BFF_HANDLER_FACTORY_REGISTRY_SHARE]: {
15
+ version,
16
+ lib: ()=>handlerFactoryRegistry,
17
+ shareConfig: {
18
+ singleton: true,
19
+ requiredVersion: false
20
+ }
21
+ },
22
+ [EFFECT_BFF_CONTEXT_STORAGE_SHARE]: {
23
+ version,
24
+ lib: getEffectContextStorage,
25
+ shareConfig: {
26
+ singleton: true,
27
+ requiredVersion: false
28
+ }
29
+ }
30
+ };
31
+ return hostShared;
32
+ }
33
+ function withEffectBffHostShared(shared) {
34
+ const host = effectBffHostShared();
35
+ const reserved = Object.keys(host).find((name)=>shared && name in shared);
36
+ if (void 0 !== reserved) throw new Error(`[BFF][Effect] Share ${reserved} is reserved for the Node host. Remove it from options.shared.`);
37
+ return {
38
+ ...shared,
39
+ ...host
40
+ };
41
+ }
42
+ export { effectBffHostShared, withEffectBffHostShared };
@@ -1,13 +1,15 @@
1
1
  import "node:module";
2
+ import { evaluateNodeBackendFederationCommonJs } from "@modern-js/server-runtime-extensions/backend-federation-security/node";
2
3
  import { loadBackendFederatedEffectApi } from "./load.mjs";
3
- import { evaluateEffectBackendFederationCommonJs } from "./node-evaluator.mjs";
4
- function node_loadBackendFederatedEffectApi(options) {
4
+ import { effectBffHostShared, withEffectBffHostShared } from "./node-shared.mjs";
5
+ async function node_loadBackendFederatedEffectApi(options) {
5
6
  return loadBackendFederatedEffectApi({
6
7
  ...options,
8
+ shared: withEffectBffHostShared(options.shared),
7
9
  entryPolicy: {
8
10
  ...options.entryPolicy,
9
- evaluateCommonJs: options.entryPolicy?.evaluateCommonJs ?? evaluateEffectBackendFederationCommonJs
11
+ evaluateCommonJs: options.entryPolicy?.evaluateCommonJs ?? evaluateNodeBackendFederationCommonJs
10
12
  }
11
13
  });
12
14
  }
13
- export { node_loadBackendFederatedEffectApi as loadBackendFederatedEffectApi };
15
+ export { effectBffHostShared, node_loadBackendFederatedEffectApi as loadBackendFederatedEffectApi };
@@ -1,25 +1,44 @@
1
1
  import "node:module";
2
+ import { PROVIDED_ENTRY_SCHEME, registerRemoteContainer } from "./integrity-plugin.mjs";
3
+ function toContainer(entry) {
4
+ return {
5
+ get (id) {
6
+ return ()=>Promise.resolve(entry.get(id)).then((factory)=>factory());
7
+ },
8
+ init (...args) {
9
+ return entry.init?.(...args);
10
+ }
11
+ };
12
+ }
2
13
  function createBackendFederationLoadEntryPlugin(options) {
14
+ const provided = new WeakMap();
3
15
  return {
4
16
  name: 'modernjs-backend-federation-load-entry',
5
- loadEntry ({ remoteInfo }) {
6
- return Promise.resolve(options.resolveEntry({
7
- name: remoteInfo.name,
8
- entry: remoteInfo.entry,
9
- type: remoteInfo.type,
10
- entryGlobalName: remoteInfo.entryGlobalName,
11
- shareScope: remoteInfo.shareScope
12
- })).then((entry)=>{
13
- if (void 0 === entry) return;
14
- return {
15
- get (id) {
16
- return ()=>Promise.resolve(entry.get(id)).then((factory)=>factory());
17
- },
18
- init (...args) {
19
- return entry.init?.(...args);
20
- }
21
- };
22
- });
17
+ afterMatchRemote ({ origin, remoteInfo }) {
18
+ if (void 0 === remoteInfo || !PROVIDED_ENTRY_SCHEME.test(remoteInfo.entry)) return;
19
+ let entries = provided.get(origin);
20
+ if (entries?.has(remoteInfo.name) !== true && origin.moduleCache.get(remoteInfo.name)?.remoteEntryExports !== void 0) return;
21
+ if (void 0 === entries) {
22
+ entries = new Map();
23
+ provided.set(origin, entries);
24
+ }
25
+ const runtimeEntries = entries;
26
+ let pending = runtimeEntries.get(remoteInfo.name);
27
+ if (void 0 === pending) {
28
+ pending = Promise.resolve(options.resolveEntry({
29
+ name: remoteInfo.name,
30
+ entry: remoteInfo.entry,
31
+ type: remoteInfo.type
32
+ })).then((entry)=>{
33
+ if (void 0 !== entry) registerRemoteContainer(origin, remoteInfo, toContainer(entry));
34
+ });
35
+ const settled = pending;
36
+ runtimeEntries.set(remoteInfo.name, settled);
37
+ settled.catch(()=>{
38
+ if (runtimeEntries.get(remoteInfo.name) === settled) runtimeEntries.delete(remoteInfo.name);
39
+ });
40
+ }
41
+ return pending;
23
42
  }
24
43
  };
25
44
  }
@@ -1,91 +1,23 @@
1
1
  import "node:module";
2
2
  import { ModuleFederation } from "@module-federation/runtime";
3
- import { loadBackendFederationExpose } from "./entry.mjs";
4
- import { createBackendFederationLoadEntryPlugin } from "./plugin.mjs";
3
+ import { createBackendFederationIntegrityPlugin } from "./integrity-plugin.mjs";
5
4
  import { collectRemotes } from "./remotes.mjs";
6
- function _check_private_redeclaration(obj, privateCollection) {
7
- if (privateCollection.has(obj)) throw new TypeError("Cannot initialize the same private elements twice on an object");
8
- }
9
- function _class_apply_descriptor_get(receiver, descriptor) {
10
- if (descriptor.get) return descriptor.get.call(receiver);
11
- return descriptor.value;
12
- }
13
- function _class_apply_descriptor_set(receiver, descriptor, value) {
14
- if (descriptor.set) descriptor.set.call(receiver, value);
15
- else {
16
- if (!descriptor.writable) throw new TypeError("attempted to set read only private field");
17
- descriptor.value = value;
18
- }
19
- }
20
- function _class_extract_field_descriptor(receiver, privateMap, action) {
21
- if (!privateMap.has(receiver)) throw new TypeError("attempted to " + action + " private field on non-instance");
22
- return privateMap.get(receiver);
23
- }
24
- function _class_private_field_get(receiver, privateMap) {
25
- var descriptor = _class_extract_field_descriptor(receiver, privateMap, "get");
26
- return _class_apply_descriptor_get(receiver, descriptor);
27
- }
28
- function _class_private_field_init(obj, privateMap, value) {
29
- _check_private_redeclaration(obj, privateMap);
30
- privateMap.set(obj, value);
31
- }
32
- function _class_private_field_set(receiver, privateMap, value) {
33
- var descriptor = _class_extract_field_descriptor(receiver, privateMap, "set");
34
- _class_apply_descriptor_set(receiver, descriptor, value);
35
- return value;
36
- }
37
- let nextRuntimeId = 0;
38
- function parseRemoteRequest(request) {
39
- const [remoteName, ...exposeParts] = request.split('/');
40
- if (void 0 === remoteName || 0 === remoteName.length || 0 === exposeParts.length) return;
41
- return remoteName;
42
- }
43
- function createLocalEntryPlugin(options, remotes) {
44
- return createBackendFederationLoadEntryPlugin({
45
- resolveEntry (resolvedRemote) {
46
- const remote = remotes.find((candidate)=>candidate.name === resolvedRemote.name);
47
- if (void 0 === remote) return;
48
- return {
49
- get (expose) {
50
- return ()=>loadBackendFederationExpose(remote, expose, options);
51
- }
52
- };
53
- }
54
- });
55
- }
56
- function isolateRegisteredRemoteEntries(remotes) {
57
- const runtimeId = nextRuntimeId;
58
- nextRuntimeId += 1;
59
- return remotes.map((remote)=>({
60
- ...remote,
61
- entry: `${remote.entry}#modernjs-backend-runtime-${runtimeId}`
62
- }));
63
- }
64
- var _remoteNames = /*#__PURE__*/ new WeakMap();
65
- class BackendFederationRuntime extends ModuleFederation {
66
- loadRemote(request, loadOptions) {
67
- const remoteName = parseRemoteRequest(request);
68
- if (void 0 === remoteName) return Promise.reject(new Error(`[BFF][Effect] Invalid backend federation request ${request}.`));
69
- if (!_class_private_field_get(this, _remoteNames).has(remoteName)) return Promise.reject(new Error(`[BFF][Effect] Missing backend federation remote ${remoteName}.`));
70
- return super.loadRemote(request, loadOptions);
71
- }
72
- constructor(options, remotes){
73
- super({
74
- name: options.hostName,
75
- remotes: isolateRegisteredRemoteEntries(remotes),
76
- shared: {},
77
- plugins: [
78
- createLocalEntryPlugin(options, remotes)
79
- ]
80
- }), _class_private_field_init(this, _remoteNames, {
81
- writable: true,
82
- value: void 0
83
- });
84
- _class_private_field_set(this, _remoteNames, new Set(remotes.map((remote)=>remote.name)));
85
- }
86
- }
87
5
  function createBackendFederationRuntime(options) {
88
6
  const remotes = collectRemotes(options);
89
- return new BackendFederationRuntime(options, remotes);
7
+ return new ModuleFederation({
8
+ name: options.hostName,
9
+ remotes: remotes.map(({ name, entry, type })=>({
10
+ name,
11
+ entry,
12
+ ...type ? {
13
+ type
14
+ } : {}
15
+ })),
16
+ shared: options.shared ?? {},
17
+ plugins: [
18
+ ...options.plugins ?? [],
19
+ createBackendFederationIntegrityPlugin(options, remotes)
20
+ ]
21
+ });
90
22
  }
91
23
  export { createBackendFederationRuntime };
@@ -130,6 +130,9 @@ async function loadBackendFederatedEffectApiFromManifest(options) {
130
130
  ...options.plugins ? {
131
131
  plugins: options.plugins
132
132
  } : {},
133
+ ...options.shared ? {
134
+ shared: options.shared
135
+ } : {},
133
136
  entryPolicy: {
134
137
  ...options.entryPolicy,
135
138
  signal: scope.signal,
@@ -1,12 +1,14 @@
1
1
  import "node:module";
2
- import { evaluateEffectBackendFederationCommonJs } from "../backend-federation/node-evaluator.mjs";
2
+ import { evaluateNodeBackendFederationCommonJs } from "@modern-js/server-runtime-extensions/backend-federation-security/node";
3
+ import { withEffectBffHostShared } from "../backend-federation/node-shared.mjs";
3
4
  import { loadBackendFederatedEffectApiFromManifest } from "./load.mjs";
4
- function node_loadBackendFederatedEffectApiFromManifest(options) {
5
+ async function node_loadBackendFederatedEffectApiFromManifest(options) {
5
6
  return loadBackendFederatedEffectApiFromManifest({
6
7
  ...options,
8
+ shared: withEffectBffHostShared(options.shared),
7
9
  entryPolicy: {
8
10
  ...options.entryPolicy,
9
- evaluateCommonJs: options.entryPolicy?.evaluateCommonJs ?? evaluateEffectBackendFederationCommonJs
11
+ evaluateCommonJs: options.entryPolicy?.evaluateCommonJs ?? evaluateNodeBackendFederationCommonJs
10
12
  }
11
13
  });
12
14
  }
@@ -32,13 +32,7 @@ function resolveBackendFederationRemoteFromManifest(manifest, remoteOverride = {
32
32
  ...verification ? {
33
33
  verification
34
34
  } : {},
35
- ...remoteOverride.entryGlobalName ? {
36
- entryGlobalName: remoteOverride.entryGlobalName
37
- } : {},
38
- expose,
39
- ...remoteOverride.shareScope ? {
40
- shareScope: remoteOverride.shareScope
41
- } : {}
35
+ expose
42
36
  };
43
37
  }
44
38
  export { resolveBackendFederationRemoteFromManifest };
@@ -80,6 +80,7 @@ const nodeBuiltins = new Set([
80
80
  ...builtinModules.map((name)=>`node:${name}`)
81
81
  ]);
82
82
  const dependencyResolutionMarker = 'modern-js-effect-dependency-resolution';
83
+ const BFF_RUNTIME_PACKAGE = /^@modern-js\/(?:bff-effect|plugin-bff)(?:\/|$)/;
83
84
  async function isEsmOnlyFile(filename) {
84
85
  const extension = node_path.extname(filename).toLowerCase();
85
86
  if ('.mjs' === extension || '.mts' === extension) return true;
@@ -108,6 +109,10 @@ function externalizeInstalledDependencies(options) {
108
109
  buildApi.onResolve({
109
110
  filter: /.*/
110
111
  }, async (args)=>{
112
+ if (BFF_RUNTIME_PACKAGE.test(args.path)) return {
113
+ external: true,
114
+ path: args.path
115
+ };
111
116
  if (!isBareSpecifier(args.path) || nodeBuiltins.has(args.path) || args.pluginData?.[dependencyResolutionMarker] === true) return nodeBuiltins.has(args.path) ? {
112
117
  external: true,
113
118
  path: args.path