@module-federation/runtime 0.0.0-next-20241101093646 → 0.0.0-next-20241104024700

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.
@@ -88,9 +88,6 @@ class FederationHost {
88
88
  get loaderHook() {
89
89
  return this._getInstance().loaderHook;
90
90
  }
91
- get bridgeHook() {
92
- return this._getInstance().bridgeHook;
93
- }
94
91
  initOptions(...args) {
95
92
  return this._getInstance().initOptions(...args);
96
93
  }
@@ -86,9 +86,6 @@ class FederationHost {
86
86
  get loaderHook() {
87
87
  return this._getInstance().loaderHook;
88
88
  }
89
- get bridgeHook() {
90
- return this._getInstance().bridgeHook;
91
- }
92
89
  initOptions(...args) {
93
90
  return this._getInstance().initOptions(...args);
94
91
  }
@@ -1,17 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var share = require('./share.cjs.js');
4
- require('@module-federation/sdk');
5
4
  require('./polyfills.cjs.js');
6
-
7
- var pluginHelper = /*#__PURE__*/Object.freeze({
8
- __proto__: null,
9
- AsyncHook: share.AsyncHook,
10
- AsyncWaterfallHook: share.AsyncWaterfallHook,
11
- PluginSystem: share.PluginSystem,
12
- SyncHook: share.SyncHook,
13
- SyncWaterfallHook: share.SyncWaterfallHook
14
- });
5
+ require('@module-federation/sdk');
15
6
 
16
7
  const ShareUtils = {
17
8
  getRegisteredShare: share.getRegisteredShare,
@@ -35,9 +26,7 @@ const GlobalUtils = {
35
26
  registerGlobalPlugins: share.registerGlobalPlugins,
36
27
  getGlobalHostPlugins: share.getGlobalHostPlugins,
37
28
  getPreloaded: share.getPreloaded,
38
- setPreloaded: share.setPreloaded,
39
- registerPlugins: share.registerPlugins,
40
- pluginHelper
29
+ setPreloaded: share.setPreloaded
41
30
  };
42
31
  var helpers = {
43
32
  global: GlobalUtils,
@@ -1,15 +1,6 @@
1
- import { A as AsyncHook, a as AsyncWaterfallHook, P as PluginSystem, S as SyncHook, b as SyncWaterfallHook, g as getRegisteredShare, c as getGlobalShareScope, G as Global, n as nativeGlobal, r as resetFederationGlobalInfo, d as getGlobalFederationInstance, s as setGlobalFederationInstance, e as getGlobalFederationConstructor, f as setGlobalFederationConstructor, h as getInfoWithoutType, i as getGlobalSnapshot, j as getTargetSnapshotInfoByModuleInfo, k as getGlobalSnapshotInfoByModuleInfo, l as setGlobalSnapshotInfoByModuleInfo, m as addGlobalSnapshot, o as getRemoteEntryExports, p as registerGlobalPlugins, q as getGlobalHostPlugins, t as getPreloaded, u as setPreloaded, v as registerPlugins } from './share.esm.js';
2
- import '@module-federation/sdk';
1
+ import { g as getRegisteredShare, a as getGlobalShareScope, G as Global, n as nativeGlobal, r as resetFederationGlobalInfo, b as getGlobalFederationInstance, s as setGlobalFederationInstance, c as getGlobalFederationConstructor, d as setGlobalFederationConstructor, e as getInfoWithoutType, f as getGlobalSnapshot, h as getTargetSnapshotInfoByModuleInfo, i as getGlobalSnapshotInfoByModuleInfo, j as setGlobalSnapshotInfoByModuleInfo, k as addGlobalSnapshot, l as getRemoteEntryExports, m as registerGlobalPlugins, o as getGlobalHostPlugins, p as getPreloaded, q as setPreloaded } from './share.esm.js';
3
2
  import './polyfills.esm.js';
4
-
5
- var pluginHelper = /*#__PURE__*/Object.freeze({
6
- __proto__: null,
7
- AsyncHook: AsyncHook,
8
- AsyncWaterfallHook: AsyncWaterfallHook,
9
- PluginSystem: PluginSystem,
10
- SyncHook: SyncHook,
11
- SyncWaterfallHook: SyncWaterfallHook
12
- });
3
+ import '@module-federation/sdk';
13
4
 
14
5
  const ShareUtils = {
15
6
  getRegisteredShare,
@@ -33,9 +24,7 @@ const GlobalUtils = {
33
24
  registerGlobalPlugins,
34
25
  getGlobalHostPlugins,
35
26
  getPreloaded,
36
- setPreloaded,
37
- registerPlugins,
38
- pluginHelper
27
+ setPreloaded
39
28
  };
40
29
  var helpers = {
41
30
  global: GlobalUtils,
package/dist/index.cjs.js CHANGED
@@ -68,6 +68,26 @@ function matchRemote(remotes, nameOrAlias) {
68
68
  return;
69
69
  }
70
70
 
71
+ function registerPlugins$1(plugins, hookInstances) {
72
+ const globalPlugins = share.getGlobalHostPlugins();
73
+ // Incorporate global plugins
74
+ if (globalPlugins.length > 0) {
75
+ globalPlugins.forEach((plugin)=>{
76
+ if (plugins == null ? void 0 : plugins.find((item)=>item.name !== plugin.name)) {
77
+ plugins.push(plugin);
78
+ }
79
+ });
80
+ }
81
+ if (plugins && plugins.length > 0) {
82
+ plugins.forEach((plugin)=>{
83
+ hookInstances.forEach((hookInstance)=>{
84
+ hookInstance.applyPlugin(plugin);
85
+ });
86
+ });
87
+ }
88
+ return plugins;
89
+ }
90
+
71
91
  async function loadEsmEntry({ entry, remoteEntryExports }) {
72
92
  return new Promise((resolve, reject)=>{
73
93
  try {
@@ -285,7 +305,7 @@ let Module = class Module {
285
305
  origin: this.host
286
306
  });
287
307
  if (typeof (remoteEntryExports == null ? void 0 : remoteEntryExports.init) === 'undefined') {
288
- console.error('The remote entry interface does not contain "init"', '\n', 'Ensure the name of this remote is not reserved or in use. Check if anything already exists on window[nameOfRemote]', '\n', 'Ensure that window[nameOfRemote] is returning a {get,init} object.');
308
+ share.logger.error('The remote entry interface does not contain "init"', '\n', 'Ensure the name of this remote is not reserved or in use. Check if anything already exists on window[nameOfRemote]', '\n', 'Ensure that window[nameOfRemote] is returning a {get,init} object.');
289
309
  }
290
310
  await remoteEntryExports.init(initContainerOptions.shareScope, initContainerOptions.initScope, initContainerOptions.remoteEntryInitOptions);
291
311
  await this.host.hooks.lifecycle.initContainer.emit(polyfills._extends({}, initContainerOptions, {
@@ -296,12 +316,18 @@ let Module = class Module {
296
316
  }
297
317
  this.lib = remoteEntryExports;
298
318
  this.inited = true;
319
+ let moduleFactory;
320
+ moduleFactory = await this.host.loaderHook.lifecycle.getModuleFactory.emit({
321
+ remoteEntryExports,
322
+ expose,
323
+ moduleInfo: this.remoteInfo
324
+ });
299
325
  // get exposeGetter
300
- const moduleFactory = await remoteEntryExports.get(expose);
326
+ if (!moduleFactory) {
327
+ moduleFactory = await remoteEntryExports.get(expose);
328
+ }
301
329
  share.assert(moduleFactory, `${share.getFMId(this.remoteInfo)} remote don't export ${expose}.`);
302
- // keep symbol for module name always one format
303
- const symbolName = share.processModuleAlias(this.remoteInfo.name, expose);
304
- const wrapModuleFactory = this.wraperFactory(moduleFactory, symbolName);
330
+ const wrapModuleFactory = this.wraperFactory(moduleFactory, id);
305
331
  if (!loadFactory) {
306
332
  return wrapModuleFactory;
307
333
  }
@@ -341,6 +367,191 @@ let Module = class Module {
341
367
  }
342
368
  };
343
369
 
370
+ class SyncHook {
371
+ on(fn) {
372
+ if (typeof fn === 'function') {
373
+ this.listeners.add(fn);
374
+ }
375
+ }
376
+ once(fn) {
377
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
378
+ const self = this;
379
+ this.on(function wrapper(...args) {
380
+ self.remove(wrapper);
381
+ // eslint-disable-next-line prefer-spread
382
+ return fn.apply(null, args);
383
+ });
384
+ }
385
+ emit(...data) {
386
+ let result;
387
+ if (this.listeners.size > 0) {
388
+ // eslint-disable-next-line prefer-spread
389
+ this.listeners.forEach((fn)=>{
390
+ result = fn(...data);
391
+ });
392
+ }
393
+ return result;
394
+ }
395
+ remove(fn) {
396
+ this.listeners.delete(fn);
397
+ }
398
+ removeAll() {
399
+ this.listeners.clear();
400
+ }
401
+ constructor(type){
402
+ this.type = '';
403
+ this.listeners = new Set();
404
+ if (type) {
405
+ this.type = type;
406
+ }
407
+ }
408
+ }
409
+
410
+ class AsyncHook extends SyncHook {
411
+ emit(...data) {
412
+ let result;
413
+ const ls = Array.from(this.listeners);
414
+ if (ls.length > 0) {
415
+ let i = 0;
416
+ const call = (prev)=>{
417
+ if (prev === false) {
418
+ return false; // Abort process
419
+ } else if (i < ls.length) {
420
+ return Promise.resolve(ls[i++].apply(null, data)).then(call);
421
+ } else {
422
+ return prev;
423
+ }
424
+ };
425
+ result = call();
426
+ }
427
+ return Promise.resolve(result);
428
+ }
429
+ }
430
+
431
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
432
+ function checkReturnData(originalData, returnedData) {
433
+ if (!share.isObject(returnedData)) {
434
+ return false;
435
+ }
436
+ if (originalData !== returnedData) {
437
+ // eslint-disable-next-line no-restricted-syntax
438
+ for(const key in originalData){
439
+ if (!(key in returnedData)) {
440
+ return false;
441
+ }
442
+ }
443
+ }
444
+ return true;
445
+ }
446
+ class SyncWaterfallHook extends SyncHook {
447
+ emit(data) {
448
+ if (!share.isObject(data)) {
449
+ share.error(`The data for the "${this.type}" hook should be an object.`);
450
+ }
451
+ for (const fn of this.listeners){
452
+ try {
453
+ const tempData = fn(data);
454
+ if (checkReturnData(data, tempData)) {
455
+ data = tempData;
456
+ } else {
457
+ this.onerror(`A plugin returned an unacceptable value for the "${this.type}" type.`);
458
+ break;
459
+ }
460
+ } catch (e) {
461
+ share.warn(e);
462
+ this.onerror(e);
463
+ }
464
+ }
465
+ return data;
466
+ }
467
+ constructor(type){
468
+ super(), this.onerror = share.error;
469
+ this.type = type;
470
+ }
471
+ }
472
+
473
+ class AsyncWaterfallHook extends SyncHook {
474
+ emit(data) {
475
+ if (!share.isObject(data)) {
476
+ share.error(`The response data for the "${this.type}" hook must be an object.`);
477
+ }
478
+ const ls = Array.from(this.listeners);
479
+ if (ls.length > 0) {
480
+ let i = 0;
481
+ const processError = (e)=>{
482
+ share.warn(e);
483
+ this.onerror(e);
484
+ return data;
485
+ };
486
+ const call = (prevData)=>{
487
+ if (checkReturnData(data, prevData)) {
488
+ data = prevData;
489
+ if (i < ls.length) {
490
+ try {
491
+ return Promise.resolve(ls[i++](data)).then(call, processError);
492
+ } catch (e) {
493
+ return processError(e);
494
+ }
495
+ }
496
+ } else {
497
+ this.onerror(`A plugin returned an incorrect value for the "${this.type}" type.`);
498
+ }
499
+ return data;
500
+ };
501
+ return Promise.resolve(call(data));
502
+ }
503
+ return Promise.resolve(data);
504
+ }
505
+ constructor(type){
506
+ super(), this.onerror = share.error;
507
+ this.type = type;
508
+ }
509
+ }
510
+
511
+ class PluginSystem {
512
+ applyPlugin(plugin) {
513
+ share.assert(share.isPlainObject(plugin), 'Plugin configuration is invalid.');
514
+ // The plugin's name is mandatory and must be unique
515
+ const pluginName = plugin.name;
516
+ share.assert(pluginName, 'A name must be provided by the plugin.');
517
+ if (!this.registerPlugins[pluginName]) {
518
+ this.registerPlugins[pluginName] = plugin;
519
+ Object.keys(this.lifecycle).forEach((key)=>{
520
+ const pluginLife = plugin[key];
521
+ if (pluginLife) {
522
+ this.lifecycle[key].on(pluginLife);
523
+ }
524
+ });
525
+ }
526
+ }
527
+ removePlugin(pluginName) {
528
+ share.assert(pluginName, 'A name is required.');
529
+ const plugin = this.registerPlugins[pluginName];
530
+ share.assert(plugin, `The plugin "${pluginName}" is not registered.`);
531
+ Object.keys(plugin).forEach((key)=>{
532
+ if (key !== 'name') {
533
+ this.lifecycle[key].remove(plugin[key]);
534
+ }
535
+ });
536
+ }
537
+ // eslint-disable-next-line @typescript-eslint/no-shadow
538
+ inherit({ lifecycle, registerPlugins }) {
539
+ Object.keys(lifecycle).forEach((hookName)=>{
540
+ share.assert(!this.lifecycle[hookName], `The hook "${hookName}" has a conflict and cannot be inherited.`);
541
+ this.lifecycle[hookName] = lifecycle[hookName];
542
+ });
543
+ Object.keys(registerPlugins).forEach((pluginName)=>{
544
+ share.assert(!this.registerPlugins[pluginName], `The plugin "${pluginName}" has a conflict and cannot be inherited.`);
545
+ this.applyPlugin(registerPlugins[pluginName]);
546
+ });
547
+ }
548
+ constructor(lifecycle){
549
+ this.registerPlugins = {};
550
+ this.lifecycle = lifecycle;
551
+ this.lifecycleKeys = Object.keys(lifecycle);
552
+ }
553
+ }
554
+
344
555
  function defaultPreloadArgs(preloadConfig) {
345
556
  return polyfills._extends({
346
557
  resourceCategory: 'sync',
@@ -854,8 +1065,6 @@ class SnapshotHandler {
854
1065
  remoteSnapshot,
855
1066
  globalSnapshot
856
1067
  });
857
- let mSnapshot;
858
- let gSnapshot;
859
1068
  // global snapshot includes manifest or module info includes manifest
860
1069
  if (globalRemoteSnapshot) {
861
1070
  if (sdk.isManifestProvider(globalRemoteSnapshot)) {
@@ -867,8 +1076,10 @@ class SnapshotHandler {
867
1076
  // Therefore, set the snapshot key to the global address of the actual request
868
1077
  entry: remoteEntry
869
1078
  }), moduleSnapshot);
870
- mSnapshot = moduleSnapshot;
871
- gSnapshot = globalSnapshotRes;
1079
+ return {
1080
+ remoteSnapshot: moduleSnapshot,
1081
+ globalSnapshot: globalSnapshotRes
1082
+ };
872
1083
  } else {
873
1084
  const { remoteSnapshot: remoteSnapshotRes } = await this.hooks.lifecycle.loadRemoteSnapshot.emit({
874
1085
  options: this.HostInstance.options,
@@ -876,8 +1087,10 @@ class SnapshotHandler {
876
1087
  remoteSnapshot: globalRemoteSnapshot,
877
1088
  from: 'global'
878
1089
  });
879
- mSnapshot = remoteSnapshotRes;
880
- gSnapshot = globalSnapshotRes;
1090
+ return {
1091
+ remoteSnapshot: remoteSnapshotRes,
1092
+ globalSnapshot: globalSnapshotRes
1093
+ };
881
1094
  }
882
1095
  } else {
883
1096
  if (share.isRemoteInfoWithEntry(moduleInfo)) {
@@ -891,8 +1104,10 @@ class SnapshotHandler {
891
1104
  remoteSnapshot: moduleSnapshot,
892
1105
  from: 'global'
893
1106
  });
894
- mSnapshot = remoteSnapshotRes;
895
- gSnapshot = globalSnapshotRes;
1107
+ return {
1108
+ remoteSnapshot: remoteSnapshotRes,
1109
+ globalSnapshot: globalSnapshotRes
1110
+ };
896
1111
  } else {
897
1112
  share.error(`
898
1113
  Cannot get remoteSnapshot with the name: '${moduleInfo.name}', version: '${moduleInfo.version}' from __FEDERATION__.moduleInfo. The following reasons may be causing the problem:\n
@@ -902,15 +1117,6 @@ class SnapshotHandler {
902
1117
  `);
903
1118
  }
904
1119
  }
905
- await this.hooks.lifecycle.afterLoadSnapshot.emit({
906
- options,
907
- moduleInfo,
908
- remoteSnapshot: mSnapshot
909
- });
910
- return {
911
- remoteSnapshot: mSnapshot,
912
- globalSnapshot: gSnapshot
913
- };
914
1120
  }
915
1121
  getGlobalRemoteInfo(moduleInfo) {
916
1122
  return getGlobalRemoteInfo(moduleInfo, this.HostInstance);
@@ -960,11 +1166,10 @@ class SnapshotHandler {
960
1166
  constructor(HostInstance){
961
1167
  this.loadingHostSnapshot = null;
962
1168
  this.manifestCache = new Map();
963
- this.hooks = new share.PluginSystem({
964
- beforeLoadRemoteSnapshot: new share.AsyncHook('beforeLoadRemoteSnapshot'),
965
- loadSnapshot: new share.AsyncWaterfallHook('loadGlobalSnapshot'),
966
- loadRemoteSnapshot: new share.AsyncWaterfallHook('loadRemoteSnapshot'),
967
- afterLoadSnapshot: new share.AsyncWaterfallHook('afterLoadSnapshot')
1169
+ this.hooks = new PluginSystem({
1170
+ beforeLoadRemoteSnapshot: new AsyncHook('beforeLoadRemoteSnapshot'),
1171
+ loadSnapshot: new AsyncWaterfallHook('loadGlobalSnapshot'),
1172
+ loadRemoteSnapshot: new AsyncWaterfallHook('loadRemoteSnapshot')
968
1173
  });
969
1174
  this.manifestLoading = share.Global.__FEDERATION__.__MANIFEST_LOADING__;
970
1175
  this.HostInstance = HostInstance;
@@ -1318,14 +1523,14 @@ class SharedHandler {
1318
1523
  }
1319
1524
  }
1320
1525
  constructor(host){
1321
- this.hooks = new share.PluginSystem({
1322
- afterResolve: new share.AsyncWaterfallHook('afterResolve'),
1323
- beforeLoadShare: new share.AsyncWaterfallHook('beforeLoadShare'),
1526
+ this.hooks = new PluginSystem({
1527
+ afterResolve: new AsyncWaterfallHook('afterResolve'),
1528
+ beforeLoadShare: new AsyncWaterfallHook('beforeLoadShare'),
1324
1529
  // not used yet
1325
- loadShare: new share.AsyncHook(),
1326
- resolveShare: new share.SyncWaterfallHook('resolveShare'),
1530
+ loadShare: new AsyncHook(),
1531
+ resolveShare: new SyncWaterfallHook('resolveShare'),
1327
1532
  // maybe will change, temporarily for internal use only
1328
- initContainerShareScopeMap: new share.SyncWaterfallHook('initContainerShareScopeMap')
1533
+ initContainerShareScopeMap: new SyncWaterfallHook('initContainerShareScopeMap')
1329
1534
  });
1330
1535
  this.host = host;
1331
1536
  this.shareScopeMap = {};
@@ -1668,22 +1873,22 @@ class RemoteHandler {
1668
1873
  host.moduleCache.delete(remote.name);
1669
1874
  }
1670
1875
  } catch (err) {
1671
- console.log('removeRemote fail: ', err);
1876
+ share.logger.log('removeRemote fail: ', err);
1672
1877
  }
1673
1878
  }
1674
1879
  constructor(host){
1675
- this.hooks = new share.PluginSystem({
1676
- beforeRegisterRemote: new share.SyncWaterfallHook('beforeRegisterRemote'),
1677
- registerRemote: new share.SyncWaterfallHook('registerRemote'),
1678
- beforeRequest: new share.AsyncWaterfallHook('beforeRequest'),
1679
- onLoad: new share.AsyncHook('onLoad'),
1680
- handlePreloadModule: new share.SyncHook('handlePreloadModule'),
1681
- errorLoadRemote: new share.AsyncHook('errorLoadRemote'),
1682
- beforePreloadRemote: new share.AsyncHook('beforePreloadRemote'),
1683
- generatePreloadAssets: new share.AsyncHook('generatePreloadAssets'),
1880
+ this.hooks = new PluginSystem({
1881
+ beforeRegisterRemote: new SyncWaterfallHook('beforeRegisterRemote'),
1882
+ registerRemote: new SyncWaterfallHook('registerRemote'),
1883
+ beforeRequest: new AsyncWaterfallHook('beforeRequest'),
1884
+ onLoad: new AsyncHook('onLoad'),
1885
+ handlePreloadModule: new SyncHook('handlePreloadModule'),
1886
+ errorLoadRemote: new AsyncHook('errorLoadRemote'),
1887
+ beforePreloadRemote: new AsyncHook('beforePreloadRemote'),
1888
+ generatePreloadAssets: new AsyncHook('generatePreloadAssets'),
1684
1889
  // not used yet
1685
- afterPreloadRemote: new share.AsyncHook(),
1686
- loadEntry: new share.AsyncHook()
1890
+ afterPreloadRemote: new AsyncHook(),
1891
+ loadEntry: new AsyncHook()
1687
1892
  });
1688
1893
  this.host = host;
1689
1894
  this.idToRemoteMap = {};
@@ -1767,7 +1972,7 @@ class FederationHost {
1767
1972
  return optionsRes;
1768
1973
  }
1769
1974
  registerPlugins(plugins) {
1770
- const pluginRes = share.registerPlugins(plugins, [
1975
+ const pluginRes = registerPlugins$1(plugins, [
1771
1976
  this.hooks,
1772
1977
  this.remoteHandler.hooks,
1773
1978
  this.sharedHandler.hooks,
@@ -1787,29 +1992,24 @@ class FederationHost {
1787
1992
  return this.remoteHandler.registerRemotes(remotes, options);
1788
1993
  }
1789
1994
  constructor(userOptions){
1790
- this.hooks = new share.PluginSystem({
1791
- beforeInit: new share.SyncWaterfallHook('beforeInit'),
1792
- init: new share.SyncHook(),
1995
+ this.hooks = new PluginSystem({
1996
+ beforeInit: new SyncWaterfallHook('beforeInit'),
1997
+ init: new SyncHook(),
1793
1998
  // maybe will change, temporarily for internal use only
1794
- beforeInitContainer: new share.AsyncWaterfallHook('beforeInitContainer'),
1999
+ beforeInitContainer: new AsyncWaterfallHook('beforeInitContainer'),
1795
2000
  // maybe will change, temporarily for internal use only
1796
- initContainer: new share.AsyncWaterfallHook('initContainer')
2001
+ initContainer: new AsyncWaterfallHook('initContainer')
1797
2002
  });
1798
- this.version = "0.6.11";
2003
+ this.version = "0.6.15";
1799
2004
  this.moduleCache = new Map();
1800
- this.loaderHook = new share.PluginSystem({
2005
+ this.loaderHook = new PluginSystem({
1801
2006
  // FIXME: may not be suitable , not open to the public yet
1802
- getModuleInfo: new share.SyncHook(),
1803
- createScript: new share.SyncHook(),
1804
- createLink: new share.SyncHook(),
2007
+ getModuleInfo: new SyncHook(),
2008
+ createScript: new SyncHook(),
2009
+ createLink: new SyncHook(),
1805
2010
  // only work for manifest , so not open to the public yet
1806
- fetch: new share.AsyncHook()
1807
- });
1808
- this.bridgeHook = new share.PluginSystem({
1809
- beforeBridgeRender: new share.SyncHook(),
1810
- afterBridgeRender: new share.SyncHook(),
1811
- beforeBridgeDestroy: new share.SyncHook(),
1812
- afterBridgeDestroy: new share.SyncHook()
2011
+ fetch: new AsyncHook(),
2012
+ getModuleFactory: new AsyncHook()
1813
2013
  });
1814
2014
  // TODO: Validate the details of the options
1815
2015
  // Initialize options with default values