@equinor/fusion-framework-cli 15.7.2-next.1 → 15.7.2

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 (28) hide show
  1. package/CHANGELOG.md +15 -12
  2. package/bin/build/bin.mjs +1 -1
  3. package/bin/build/cli.mjs +3 -3
  4. package/dist/esm/lib/app/merge-app-config.js +4 -14
  5. package/dist/esm/lib/app/merge-app-config.js.map +1 -1
  6. package/dist/esm/lib/app/merge-app-config.test.js +34 -0
  7. package/dist/esm/lib/app/merge-app-config.test.js.map +1 -0
  8. package/dist/esm/lib/app/merge-app-manifests.js +3 -4
  9. package/dist/esm/lib/app/merge-app-manifests.js.map +1 -1
  10. package/dist/esm/lib/merge-dev-server-config.js +12 -14
  11. package/dist/esm/lib/merge-dev-server-config.js.map +1 -1
  12. package/dist/esm/lib/merge-dev-server-config.test.js +44 -0
  13. package/dist/esm/lib/merge-dev-server-config.test.js.map +1 -0
  14. package/dist/esm/lib/portal/load-portal-manifest.js +2 -2
  15. package/dist/esm/lib/portal/load-portal-manifest.js.map +1 -1
  16. package/dist/esm/lib/utils/merge-config.js +109 -0
  17. package/dist/esm/lib/utils/merge-config.js.map +1 -0
  18. package/dist/esm/lib/utils/tests/merge-config.test.js +48 -0
  19. package/dist/esm/lib/utils/tests/merge-config.test.js.map +1 -0
  20. package/dist/esm/version.js +1 -1
  21. package/dist/types/lib/app/merge-app-config.test.d.ts +1 -0
  22. package/dist/types/lib/app/merge-app-manifests.d.ts +1 -1
  23. package/dist/types/lib/merge-dev-server-config.d.ts +2 -2
  24. package/dist/types/lib/merge-dev-server-config.test.d.ts +1 -0
  25. package/dist/types/lib/utils/merge-config.d.ts +31 -0
  26. package/dist/types/lib/utils/tests/merge-config.test.d.ts +1 -0
  27. package/dist/types/version.d.ts +1 -1
  28. package/package.json +10 -13
@@ -1,4 +1,4 @@
1
- import deepmerge from 'deepmerge';
1
+ import { mergeConfig } from '../utils/merge-config.js';
2
2
  /**
3
3
  * Merges a base application configuration object with an overrides object,
4
4
  * returning a new configuration object. If the `scopes` property is present
@@ -11,18 +11,8 @@ import deepmerge from 'deepmerge';
11
11
  * @returns A new configuration object resulting from merging `base` and `overrides`.
12
12
  */
13
13
  export const mergeAppConfig = (base, overrides) => {
14
- // if scopes are provided in overrides, use the new scopes
15
- return deepmerge(base, overrides, {
16
- customMerge: (key) => {
17
- // Replace (rather than deep-merge) scopes so overrides fully control the array
18
- if (key === 'scopes') {
19
- return (_target, source) => {
20
- // If scopes are provided in overrides, replace the base scopes with the new ones
21
- return source;
22
- };
23
- }
24
- return undefined; // Use default merging for other keys
25
- },
26
- });
14
+ return mergeConfig(base, overrides, (target, source, key) =>
15
+ // Replace scopes so overrides fully control which scopes are requested; append other arrays
16
+ key === 'scopes' ? source : target.concat(source));
27
17
  };
28
18
  //# sourceMappingURL=merge-app-config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"merge-app-config.js","sourceRoot":"","sources":["../../../../src/lib/app/merge-app-config.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,WAAW,CAAC;AAGlC;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAsB,IAAO,EAAE,SAAqB,EAAK,EAAE;IACvF,0DAA0D;IAC1D,OAAO,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE;QAChC,WAAW,EAAE,CAAC,GAAG,EAAE,EAAE;YACnB,+EAA+E;YAC/E,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;gBACrB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;oBACzB,iFAAiF;oBACjF,OAAO,MAAM,CAAC;gBAChB,CAAC,CAAC;YACJ,CAAC;YACD,OAAO,SAAS,CAAC,CAAC,qCAAqC;QACzD,CAAC;KACF,CAAC,CAAC;AACL,CAAC,CAAC"}
1
+ {"version":3,"file":"merge-app-config.js","sourceRoot":"","sources":["../../../../src/lib/app/merge-app-config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAsB,IAAO,EAAE,SAAqB,EAAK,EAAE;IACvF,OAAO,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE;IAC1D,4FAA4F;IAC5F,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAClD,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { mergeAppConfig } from './merge-app-config.js';
3
+ /**
4
+ * Treats a minimal fixture as an app config shape.
5
+ *
6
+ * @template T - Shape the fixture stands in for.
7
+ * @param value - Minimal fixture object.
8
+ * @returns The fixture typed as `T`.
9
+ */
10
+ const fixture = (value) =>
11
+ // Fixtures only carry the keys under test; the merge never reads the omitted required fields
12
+ value;
13
+ describe('mergeAppConfig', () => {
14
+ it('replaces scopes at any depth while deep-merging other properties', () => {
15
+ const base = fixture({
16
+ environment: { a: 1 },
17
+ endpoints: { api: { url: 'https://base', scopes: ['base/.default'] } },
18
+ });
19
+ const overrides = fixture({
20
+ environment: { b: 2 },
21
+ endpoints: { api: { scopes: ['override/.default'] } },
22
+ });
23
+ expect(mergeAppConfig(base, overrides)).toEqual({
24
+ environment: { a: 1, b: 2 },
25
+ endpoints: { api: { url: 'https://base', scopes: ['override/.default'] } },
26
+ });
27
+ });
28
+ it('does not mutate the base configuration', () => {
29
+ const base = fixture({ environment: { a: 1 } });
30
+ mergeAppConfig(base, fixture({ environment: { b: 2 } }));
31
+ expect(base).toEqual({ environment: { a: 1 } });
32
+ });
33
+ });
34
+ //# sourceMappingURL=merge-app-config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-app-config.test.js","sourceRoot":"","sources":["../../../../src/lib/app/merge-app-config.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD;;;;;;GAMG;AACH,MAAM,OAAO,GAAG,CAAI,KAAc,EAAK,EAAE;AACvC,6FAA6F;AAC7F,KAAU,CAAC;AAEb,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,IAAI,GAAG,OAAO,CAAY;YAC9B,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE;YACrB,SAAS,EAAE,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,eAAe,CAAC,EAAE,EAAE;SACvE,CAAC,CAAC;QACH,MAAM,SAAS,GAAG,OAAO,CAAqB;YAC5C,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE;YACrB,SAAS,EAAE,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,mBAAmB,CAAC,EAAE,EAAE;SACtD,CAAC,CAAC;QAEH,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;YAC9C,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;YAC3B,SAAS,EAAE,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,mBAAmB,CAAC,EAAE,EAAE;SAC3E,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,IAAI,GAAG,OAAO,CAAY,EAAE,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAE3D,cAAc,CAAC,IAAI,EAAE,OAAO,CAAqB,EAAE,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAE7E,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,6 +1,6 @@
1
- import mergeWith from 'deepmerge';
1
+ import { mergeConfig } from '../utils/merge-config.js';
2
2
  /**
3
- * Deeply merges two application manifests using deepmerge.
3
+ * Deeply merges two application manifests.
4
4
  *
5
5
  * This utility is intended for combining a base manifest with an override manifest,
6
6
  * supporting recursive merging of nested properties. It is commonly used in scenarios
@@ -21,8 +21,7 @@ import mergeWith from 'deepmerge';
21
21
  */
22
22
  export const mergeAppManifests = (base, // The base manifest (may be partial)
23
23
  overrides) => {
24
- // Use lodash.mergeWith for deep merging of manifest objects
25
- const manifest = mergeWith(base, overrides);
24
+ const manifest = mergeConfig(base, overrides);
26
25
  // TODO(#5077): Add assertions or schema validation here if needed
27
26
  return manifest;
28
27
  };
@@ -1 +1 @@
1
- {"version":3,"file":"merge-app-manifests.js","sourceRoot":"","sources":["../../../../src/lib/app/merge-app-manifests.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,WAAW,CAAC;AAIlC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,IAAmC,EAAE,qCAAqC;AAC1E,SAAwC,EAC3B,EAAE;IACf,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,EAAE,SAAS,CAAgB,CAAC;IAC3D,kEAAkE;IAClE,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAEF,yDAAyD;AACzD,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"merge-app-manifests.js","sourceRoot":"","sources":["../../../../src/lib/app/merge-app-manifests.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,IAAmC,EAAE,qCAAqC;AAC1E,SAAwC,EAC3B,EAAE;IACf,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,EAAE,SAAS,CAAgB,CAAC;IAC7D,kEAAkE;IAClE,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAEF,yDAAyD;AACzD,eAAe,iBAAiB,CAAC"}
@@ -1,4 +1,4 @@
1
- import deepmerge from 'deepmerge';
1
+ import { mergeConfig } from './utils/merge-config.js';
2
2
  /**
3
3
  * Merges two arrays of objects by a specified key, ensuring that objects with the same key value
4
4
  * are merged, with the `source` array taking precedence over the `target` array for duplicate keys.
@@ -21,8 +21,8 @@ const keyedArrayMerger = (key, target, source) => {
21
21
  /**
22
22
  * Merges a base development server configuration with an overrides object.
23
23
  *
24
- * Uses lodash.mergeWith to deeply merge properties, with custom logic for arrays.
25
- * Arrays in the overrides object will replace arrays in the base config.
24
+ * Objects are merged recursively. Route arrays (entries with `match` and `middleware`) are
25
+ * merged by `match`, with override routes winning; other arrays are concatenated and de-duplicated.
26
26
  *
27
27
  * @param base - The base development server configuration.
28
28
  * @param overrides - A partial configuration object containing properties to override in the base configuration.
@@ -31,17 +31,15 @@ const keyedArrayMerger = (key, target, source) => {
31
31
  * Inline comments are provided for maintainability and clarity.
32
32
  */
33
33
  export const mergeDevServerConfig = (base, overrides) => {
34
- return deepmerge(base, overrides, {
35
- arrayMerge: (target, source) => {
36
- // extract the first entry from both arrays to determine the merge strategy
37
- const entry = source[0] ?? target[0];
38
- // merge routes by 'match' if the entry is a route object
39
- if (typeof entry === 'object' && 'match' in entry && 'middleware' in entry) {
40
- return keyedArrayMerger('match', target, source);
41
- }
42
- // Deduplicate the combined target and source entries
43
- return [...new Set([...target, ...source])];
44
- },
34
+ return mergeConfig(base, overrides, (target, source) => {
35
+ // extract the first entry from both arrays to determine the merge strategy
36
+ const entry = source[0] ?? target[0];
37
+ // merge routes by 'match' if the entry is a route object
38
+ if (typeof entry === 'object' && entry !== null && 'match' in entry && 'middleware' in entry) {
39
+ return keyedArrayMerger('match', target, source);
40
+ }
41
+ // Deduplicate the combined target and source entries
42
+ return [...new Set([...target, ...source])];
45
43
  });
46
44
  };
47
45
  // Export as default for convenience in imports
@@ -1 +1 @@
1
- {"version":3,"file":"merge-dev-server-config.js","sourceRoot":"","sources":["../../../src/lib/merge-dev-server-config.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,WAAW,CAAC;AAKlC;;;;;;;;;GASG;AACH,MAAM,gBAAgB,GAAG,CACvB,GAAgB,EAChB,MAAoB,EACpB,MAAoB,EACN,EAAE;IAChB,qEAAqE;IACrE,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CACjC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAChE,EAAW,CACZ,CAAC;IACF,2EAA2E;IAC3E,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CACjC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAChE,EAAW,CACZ,CAAC;IACF,oFAAoF;IACpF,MAAM,aAAa,GAAG,EAAE,GAAG,aAAa,EAAE,GAAG,aAAa,EAAE,CAAC;IAC7D,OAAO,MAAM,CAAC,MAAM,CAAC,aAAa,CAAiB,CAAC;AACtD,CAAC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,IAAsB,EACtB,SAA6C,EAC3B,EAAE;IACpB,OAAO,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE;QAChC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE;YAC7B,2EAA2E;YAC3E,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;YAErC,yDAAyD;YACzD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,IAAI,KAAK,IAAI,YAAY,IAAI,KAAK,EAAE,CAAC;gBAC3E,OAAO,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;YACnD,CAAC;YACD,qDAAqD;YACrD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAC9C,CAAC;KACF,CAAqB,CAAC;AACzB,CAAC,CAAC;AAEF,+CAA+C;AAC/C,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"merge-dev-server-config.js","sourceRoot":"","sources":["../../../src/lib/merge-dev-server-config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAGtD;;;;;;;;;GASG;AACH,MAAM,gBAAgB,GAAG,CACvB,GAAgB,EAChB,MAAoB,EACpB,MAAoB,EACN,EAAE;IAChB,qEAAqE;IACrE,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CACjC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAChE,EAAW,CACZ,CAAC;IACF,2EAA2E;IAC3E,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CACjC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAChE,EAAW,CACZ,CAAC;IACF,oFAAoF;IACpF,MAAM,aAAa,GAAG,EAAE,GAAG,aAAa,EAAE,GAAG,aAAa,EAAE,CAAC;IAC7D,OAAO,MAAM,CAAC,MAAM,CAAC,aAAa,CAAiB,CAAC;AACtD,CAAC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,IAAsB,EACtB,SAA6C,EAC3B,EAAE;IACpB,OAAO,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE;QACrD,2EAA2E;QAC3E,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;QAErC,yDAAyD;QACzD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,IAAI,YAAY,IAAI,KAAK,EAAE,CAAC;YAC7F,OAAO,gBAAgB,CACrB,OAAO,EACP,MAAwC,EACxC,MAAwC,CACzC,CAAC;QACJ,CAAC;QACD,qDAAqD;QACrD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,+CAA+C;AAC/C,eAAe,oBAAoB,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { mergeDevServerConfig } from './merge-dev-server-config.js';
3
+ /**
4
+ * Treats a minimal fixture as a full dev-server shape.
5
+ *
6
+ * @template T - Shape the fixture stands in for.
7
+ * @param value - Minimal fixture object.
8
+ * @returns The fixture typed as `T`.
9
+ */
10
+ const fixture = (value) =>
11
+ // Fixtures only carry the keys under test; the merge never reads the omitted required fields
12
+ value;
13
+ describe('mergeDevServerConfig', () => {
14
+ it('merges routes by match with override routes winning, and de-duplicates other arrays', () => {
15
+ const baseMiddleware = () => { };
16
+ const overrideMiddleware = () => { };
17
+ const base = fixture({
18
+ api: {
19
+ routes: [
20
+ { match: '/a', middleware: baseMiddleware },
21
+ { match: '/b', middleware: baseMiddleware },
22
+ ],
23
+ processServices: ['x', 'y'],
24
+ },
25
+ });
26
+ const overrides = fixture({
27
+ api: {
28
+ routes: [{ match: '/b', middleware: overrideMiddleware }],
29
+ processServices: ['y', 'z'],
30
+ },
31
+ });
32
+ const result = fixture(mergeDevServerConfig(base, overrides));
33
+ expect(result.api.routes).toEqual([
34
+ { match: '/a', middleware: baseMiddleware },
35
+ { match: '/b', middleware: overrideMiddleware },
36
+ ]);
37
+ expect(result.api.processServices).toEqual(['x', 'y', 'z']);
38
+ });
39
+ it('keeps base values when overrides are empty', () => {
40
+ const base = fixture({ api: { processServices: ['x'] } });
41
+ expect(mergeDevServerConfig(base, {})).toEqual(base);
42
+ });
43
+ });
44
+ //# sourceMappingURL=merge-dev-server-config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-dev-server-config.test.js","sourceRoot":"","sources":["../../../src/lib/merge-dev-server-config.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAOpE;;;;;;GAMG;AACH,MAAM,OAAO,GAAG,CAAI,KAAc,EAAK,EAAE;AACvC,6FAA6F;AAC7F,KAAU,CAAC;AAEb,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,qFAAqF,EAAE,GAAG,EAAE;QAC7F,MAAM,cAAc,GAAG,GAAS,EAAE,GAAE,CAAC,CAAC;QACtC,MAAM,kBAAkB,GAAG,GAAS,EAAE,GAAE,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,OAAO,CAAmB;YACrC,GAAG,EAAE;gBACH,MAAM,EAAE;oBACN,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE;oBAC3C,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE;iBAC5C;gBACD,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;aAC5B;SACF,CAAC,CAAC;QACH,MAAM,SAAS,GAAG,OAAO,CAAY;YACnC,GAAG,EAAE;gBACH,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,CAAC;gBACzD,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;aAC5B;SACF,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,OAAO,CAAY,oBAAoB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;QAEzE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;YAChC,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE;YAC3C,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE;SAChD,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,IAAI,GAAG,OAAO,CAAmB,EAAE,GAAG,EAAE,EAAE,eAAe,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;QAE5E,MAAM,CAAC,oBAAoB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { importConfig } from '@equinor/fusion-imports';
2
- import deepmerge from 'deepmerge';
2
+ import { mergeConfig } from '../utils/merge-config.js';
3
3
  import { PortalManifestSchema } from './portal-manifest-schema.js';
4
4
  /**
5
5
  * Loads a portal manifest configuration file, merging it with optional base values and supporting multiple file extensions.
@@ -38,7 +38,7 @@ export const loadPortalManifest = async (env, options) => {
38
38
  // If the manifest export is not a function, treat it as an object
39
39
  overrides = module.default;
40
40
  }
41
- return overrides ? deepmerge(base, overrides) : base;
41
+ return overrides ? mergeConfig(base, overrides) : base;
42
42
  },
43
43
  },
44
44
  });
@@ -1 +1 @@
1
- {"version":3,"file":"load-portal-manifest.js","sourceRoot":"","sources":["../../../../src/lib/portal/load-portal-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,OAAO,SAAS,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAwDnE;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,GAAe,EACf,OAAsC,EACA,EAAE;IACxC,iGAAiG;IACjG,MAAM,WAAW,GAAG,OAAO,EAAE,IAAI,IAAI,CAAC,mBAAmB,GAAG,CAAC,WAAW,EAAE,EAAE,iBAAiB,CAAC,CAAC;IAE/F,+EAA+E;IAC/E,MAAM,YAAY,GAAG,MAAM,YAAY,CAAC,WAAW,EAAE;QACnD,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,iDAAiD;QACpE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,2CAA2C;QAC5E,MAAM,EAAE;YACN,uCAAuC;YACvC,OAAO,EAAE,KAAK,EAAE,MAA4C,EAAc,EAAE;gBAC1E,0EAA0E;gBAC1E,MAAM,IAAI,GAAM,OAAO,EAAE,IAAI,IAAK,EAAQ,CAAC;gBAC3C,IAAI,SAA0C,CAAC;gBAC/C,uFAAuF;gBACvF,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;oBACzC,+EAA+E;oBAC/E,SAAS,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,SAAS,CAAC;gBACjE,CAAC;qBAAM,CAAC;oBACN,kEAAkE;oBAClE,SAAS,GAAG,MAAM,CAAC,OAA8B,CAAC;gBACpD,CAAC;gBACD,OAAO,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,SAAuB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACrE,CAAC;SACF;KACF,CAAC,CAAC;IAEH,0EAA0E;IAC1E,8EAA8E;IAC9E,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAW,CAAC;IAC1C,MAAM,UAAU,GAAG,oBAAoB,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5D,2EAA2E;IAC3E,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QACxB,oEAAoE;QACpE,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM;aAC1C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;aAC/C,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,4BAA4B,aAAa,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO;QACL,QAAQ;QACR,IAAI,EAAE,YAAY,CAAC,IAAI;QACvB,SAAS,EAAE,YAAY,CAAC,SAAS;KAClC,CAAC;AACJ,CAAC,CAAC"}
1
+ {"version":3,"file":"load-portal-manifest.js","sourceRoot":"","sources":["../../../../src/lib/portal/load-portal-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAwDnE;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,GAAe,EACf,OAAsC,EACA,EAAE;IACxC,iGAAiG;IACjG,MAAM,WAAW,GAAG,OAAO,EAAE,IAAI,IAAI,CAAC,mBAAmB,GAAG,CAAC,WAAW,EAAE,EAAE,iBAAiB,CAAC,CAAC;IAE/F,+EAA+E;IAC/E,MAAM,YAAY,GAAG,MAAM,YAAY,CAAC,WAAW,EAAE;QACnD,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,iDAAiD;QACpE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,2CAA2C;QAC5E,MAAM,EAAE;YACN,uCAAuC;YACvC,OAAO,EAAE,KAAK,EAAE,MAA4C,EAAc,EAAE;gBAC1E,0EAA0E;gBAC1E,MAAM,IAAI,GAAM,OAAO,EAAE,IAAI,IAAK,EAAQ,CAAC;gBAC3C,IAAI,SAA0C,CAAC;gBAC/C,uFAAuF;gBACvF,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;oBACzC,+EAA+E;oBAC/E,SAAS,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,SAAS,CAAC;gBACjE,CAAC;qBAAM,CAAC;oBACN,kEAAkE;oBAClE,SAAS,GAAG,MAAM,CAAC,OAA8B,CAAC;gBACpD,CAAC;gBACD,OAAO,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACzD,CAAC;SACF;KACF,CAAC,CAAC;IAEH,0EAA0E;IAC1E,8EAA8E;IAC9E,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAW,CAAC;IAC1C,MAAM,UAAU,GAAG,oBAAoB,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5D,2EAA2E;IAC3E,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QACxB,oEAAoE;QACpE,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM;aAC1C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;aAC/C,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,4BAA4B,aAAa,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO;QACL,QAAQ;QACR,IAAI,EAAE,YAAY,CAAC,IAAI;QACvB,SAAS,EAAE,YAAY,CAAC,SAAS;KAClC,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Checks whether a value is an object literal, as opposed to an array or a class instance
3
+ * such as `Date` or `URL`, which replace rather than merge.
4
+ *
5
+ * @param value - Value to inspect.
6
+ * @returns `true` when the value is a plain object.
7
+ */
8
+ const isConfigObject = (value) => {
9
+ // Primitives and null replace rather than merge
10
+ if (typeof value !== 'object' || value === null) {
11
+ return false;
12
+ }
13
+ const prototype = Object.getPrototypeOf(value);
14
+ return prototype === Object.prototype || prototype === null;
15
+ };
16
+ /**
17
+ * Default array strategy: append override entries after base entries.
18
+ *
19
+ * @param base - Array from the base config.
20
+ * @param overrides - Array from the overrides.
21
+ * @returns A new array with base entries followed by override entries.
22
+ */
23
+ const appendArrays = (base, overrides) => base.concat(overrides);
24
+ /**
25
+ * Lists an object's entries without `__proto__`, which would replace the prototype of an object
26
+ * it is assigned to (GHSA-gcqx-w48m-w2h4).
27
+ *
28
+ * @param value - Object whose entries are listed.
29
+ * @returns The entries that are safe to assign.
30
+ */
31
+ const safeEntries = (value) =>
32
+ // `JSON.parse` keeps `__proto__` as an own key, so untrusted config can carry one
33
+ Object.entries(value).filter(([entryKey]) => entryKey !== '__proto__');
34
+ /**
35
+ * Copies plain objects and arrays from the overrides, dropping `__proto__` keys at any depth so
36
+ * an override value that has no base counterpart is cleaned the same way as a merged one.
37
+ *
38
+ * @param value - Value from the overrides.
39
+ * @returns A cleaned copy of plain objects and arrays; any other value as-is.
40
+ */
41
+ const sanitize = (value) => {
42
+ // Arrays may contain plain objects that carry `__proto__` keys
43
+ if (Array.isArray(value)) {
44
+ // Clean every item, since items never merge with a base value
45
+ return value.map(sanitize);
46
+ }
47
+ // Class instances and primitives are kept as-is
48
+ if (!isConfigObject(value)) {
49
+ return value;
50
+ }
51
+ // Clean each nested value too, since none of them has a base value to merge with
52
+ const entries = safeEntries(value).map(([entryKey, entryValue]) => [
53
+ entryKey,
54
+ sanitize(entryValue),
55
+ ]);
56
+ return Object.fromEntries(entries);
57
+ };
58
+ /**
59
+ * Merges one value from the overrides into the matching base value.
60
+ *
61
+ * @param base - Value from the base config.
62
+ * @param overrides - Value from the overrides.
63
+ * @param mergeArrays - Strategy used when both values are arrays.
64
+ * @param key - Property key of the values, passed to `mergeArrays`.
65
+ * @returns The merged value.
66
+ */
67
+ const mergeValue = (base, overrides, mergeArrays, key) => {
68
+ // Both sides are lists, so the caller's strategy decides how to combine them
69
+ if (Array.isArray(base) && Array.isArray(overrides)) {
70
+ // Clean override items before the strategy sees them, since it may keep them as-is
71
+ return mergeArrays(base, overrides.map(sanitize), key);
72
+ }
73
+ // Anything other than two plain objects is replaced by the override
74
+ if (!isConfigObject(base) || !isConfigObject(overrides)) {
75
+ return sanitize(overrides);
76
+ }
77
+ // Start from the base keys so keys absent from the overrides survive
78
+ const result = Object.fromEntries(safeEntries(base));
79
+ // Overlay each override key, recursing where both sides define it
80
+ for (const [entryKey, value] of safeEntries(overrides)) {
81
+ result[entryKey] = Object.hasOwn(base, entryKey)
82
+ ? mergeValue(base[entryKey], value, mergeArrays, entryKey)
83
+ : sanitize(value);
84
+ }
85
+ return result;
86
+ };
87
+ /**
88
+ * Deeply merges CLI config overrides (app config, app or portal manifest, dev-server config)
89
+ * into a base config without mutating either input.
90
+ *
91
+ * Plain objects merge key by key with override values winning, including an explicit `undefined`.
92
+ * Arrays are appended unless `mergeArrays` says otherwise. Any other value, including class
93
+ * instances, replaces the base value. Values taken from the overrides are copied with `__proto__`
94
+ * keys removed at any depth; base values that are not overridden are shared with the inputs.
95
+ *
96
+ * @template T - Shape of the config.
97
+ * @param base - Base config.
98
+ * @param overrides - Partial config whose values take precedence.
99
+ * @param mergeArrays - Strategy for combining two arrays at the same key. Defaults to appending.
100
+ * @returns A new config containing the merged values.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * mergeConfig({ server: { port: 3000 }, scopes: ['a'] }, { server: { host: 'x' }, scopes: ['b'] });
105
+ * // { server: { port: 3000, host: 'x' }, scopes: ['a', 'b'] }
106
+ * ```
107
+ */
108
+ export const mergeConfig = (base, overrides, mergeArrays = appendArrays) => mergeValue(base, overrides, mergeArrays, '');
109
+ //# sourceMappingURL=merge-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-config.js","sourceRoot":"","sources":["../../../../src/lib/utils/merge-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { mergeConfig } from '../merge-config.js';
3
+ describe('mergeConfig', () => {
4
+ it('merges nested objects with overrides winning and appends arrays by default', () => {
5
+ expect(mergeConfig({ server: { port: 3000, host: 'a' }, scopes: ['x'] }, { server: { host: 'b' }, scopes: ['y'], extra: true })).toEqual({ server: { port: 3000, host: 'b' }, scopes: ['x', 'y'], extra: true });
6
+ });
7
+ it('does not mutate the base config', () => {
8
+ const base = { server: { port: 3000 }, scopes: ['x'] };
9
+ mergeConfig(base, { server: { host: 'b' }, scopes: ['y'] });
10
+ expect(base).toEqual({ server: { port: 3000 }, scopes: ['x'] });
11
+ });
12
+ it('lets explicit undefined, arrays, and class instances replace the base value', () => {
13
+ const url = new URL('https://example.com');
14
+ expect(mergeConfig({ a: 1, b: { c: 1 }, url: { href: 'x' } }, { a: undefined, b: [1], url })).toEqual({
15
+ a: undefined,
16
+ b: [1],
17
+ url,
18
+ });
19
+ });
20
+ it('passes the property key to a custom array strategy', () => {
21
+ const result = mergeConfig({ scopes: ['a'], nested: { scopes: ['b'], list: [1] } }, { scopes: ['x'], nested: { scopes: ['y'], list: [2] } }, (base, overrides, key) => (key === 'scopes' ? overrides : base.concat(overrides)));
22
+ expect(result).toEqual({ scopes: ['x'], nested: { scopes: ['y'], list: [1, 2] } });
23
+ });
24
+ it('ignores __proto__ keys from untrusted overrides', () => {
25
+ const overrides = JSON.parse('{"__proto__": {"polluted": true}, "nested": {"__proto__": {"polluted": true}}}');
26
+ const result = mergeConfig({ nested: {} }, overrides);
27
+ expect(Object.getPrototypeOf(result)).toBe(Object.prototype);
28
+ expect(Object.getPrototypeOf(result.nested)).toBe(Object.prototype);
29
+ expect(result.polluted).toBeUndefined();
30
+ });
31
+ it('strips __proto__ keys from override values that have no base counterpart, at any depth', () => {
32
+ const overrides = JSON.parse('{"added": {"deep": {"__proto__": {"polluted": true}}}, "list": [{"__proto__": {"polluted": true}}], "replaced": {"__proto__": {"polluted": true}}}');
33
+ const base = { list: [], replaced: 'primitive' };
34
+ const result = mergeConfig(base, overrides);
35
+ // Object.assign uses [[Set]], so a surviving own `__proto__` key would swap the prototype
36
+ const reassigned = [
37
+ Object.assign({}, result.added.deep),
38
+ Object.assign({}, result.list[0]),
39
+ Object.assign({}, result.replaced),
40
+ ];
41
+ // Each value came from the overrides without a base counterpart to merge with
42
+ for (const value of reassigned) {
43
+ expect(Object.getPrototypeOf(value)).toBe(Object.prototype);
44
+ expect(Object.hasOwn(value, '__proto__')).toBe(false);
45
+ }
46
+ });
47
+ });
48
+ //# sourceMappingURL=merge-config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-config.test.js","sourceRoot":"","sources":["../../../../../src/lib/utils/tests/merge-config.test.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
  // Generated by genversion.
2
- export const version = '15.7.2-next.1';
2
+ export const version = '15.7.2';
3
3
  //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ export {};
@@ -1,7 +1,7 @@
1
1
  import type { AppManifest } from '@equinor/fusion-framework-module-app';
2
2
  import type { RecursivePartial } from '../utils/types.js';
3
3
  /**
4
- * Deeply merges two application manifests using deepmerge.
4
+ * Deeply merges two application manifests.
5
5
  *
6
6
  * This utility is intended for combining a base manifest with an override manifest,
7
7
  * supporting recursive merging of nested properties. It is commonly used in scenarios
@@ -3,8 +3,8 @@ import type { RecursivePartial } from './utils/types.js';
3
3
  /**
4
4
  * Merges a base development server configuration with an overrides object.
5
5
  *
6
- * Uses lodash.mergeWith to deeply merge properties, with custom logic for arrays.
7
- * Arrays in the overrides object will replace arrays in the base config.
6
+ * Objects are merged recursively. Route arrays (entries with `match` and `middleware`) are
7
+ * merged by `match`, with override routes winning; other arrays are concatenated and de-duplicated.
8
8
  *
9
9
  * @param base - The base development server configuration.
10
10
  * @param overrides - A partial configuration object containing properties to override in the base configuration.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Combines two arrays found at the same key in a base config and its overrides.
3
+ *
4
+ * @param base - Array from the base config.
5
+ * @param overrides - Array from the overrides.
6
+ * @param key - Property key holding both arrays.
7
+ * @returns The array to keep in the merged config.
8
+ */
9
+ export type MergeConfigArrays = (base: unknown[], overrides: unknown[], key: string) => unknown[];
10
+ /**
11
+ * Deeply merges CLI config overrides (app config, app or portal manifest, dev-server config)
12
+ * into a base config without mutating either input.
13
+ *
14
+ * Plain objects merge key by key with override values winning, including an explicit `undefined`.
15
+ * Arrays are appended unless `mergeArrays` says otherwise. Any other value, including class
16
+ * instances, replaces the base value. Values taken from the overrides are copied with `__proto__`
17
+ * keys removed at any depth; base values that are not overridden are shared with the inputs.
18
+ *
19
+ * @template T - Shape of the config.
20
+ * @param base - Base config.
21
+ * @param overrides - Partial config whose values take precedence.
22
+ * @param mergeArrays - Strategy for combining two arrays at the same key. Defaults to appending.
23
+ * @returns A new config containing the merged values.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * mergeConfig({ server: { port: 3000 }, scopes: ['a'] }, { server: { host: 'x' }, scopes: ['b'] });
28
+ * // { server: { port: 3000, host: 'x' }, scopes: ['a', 'b'] }
29
+ * ```
30
+ */
31
+ export declare const mergeConfig: <T>(base: T, overrides: unknown, mergeArrays?: MergeConfigArrays) => T;
@@ -0,0 +1 @@
1
+ export {};
@@ -1 +1 @@
1
- export declare const version = "15.7.2-next.1";
1
+ export declare const version = "15.7.2";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equinor/fusion-framework-cli",
3
- "version": "15.7.2-next.1",
3
+ "version": "15.7.2",
4
4
  "keywords": [
5
5
  "Fusion",
6
6
  "Fusion Framework",
@@ -94,23 +94,20 @@
94
94
  "directory": "packages/cli"
95
95
  },
96
96
  "dependencies": {
97
- "@equinor/fusion-framework-dev-portal": "11.1.10-next.1",
98
- "@equinor/fusion-framework-dev-server": "2.3.3-next.1",
99
- "@equinor/fusion-framework-module-azure-identity": "0.4.0-next.0",
97
+ "@equinor/fusion-framework-dev-portal": "11.1.10",
98
+ "@equinor/fusion-framework-dev-server": "2.3.3",
99
+ "@equinor/fusion-framework-module-azure-identity": "0.3.4",
100
100
  "@equinor/fusion-framework-vite-plugin-raw-imports": "2.0.5",
101
101
  "@equinor/fusion-framework-vite-plugin-react-router": "0.1.5",
102
102
  "@equinor/fusion-imports": "2.0.7",
103
- "@types/inquirer": "^9.0.9",
104
103
  "chalk": "^6.0.0",
105
104
  "commander": "^15.0.0",
106
- "deepmerge": "^4.3.1",
107
105
  "dotenv": "^18.0.4",
108
106
  "execa": "^10.0.0",
109
107
  "find-up": "^8.0.0",
110
108
  "inquirer": "^14.0.0",
111
- "is-mergeable-object": "^1.1.1",
112
109
  "is-path-inside": "^4.0.0",
113
- "ora": "^9.0.0",
110
+ "ora": "^10.0.0",
114
111
  "read-package-up": "^12.0.0",
115
112
  "semver": "^7.7.4",
116
113
  "simple-git": "^4.0.1",
@@ -119,11 +116,11 @@
119
116
  "zod": "^4.4.3"
120
117
  },
121
118
  "devDependencies": {
122
- "@equinor/fusion-framework-cli-plugin-mock-server": "^0.3.3-next.0",
123
- "@equinor/fusion-framework-module": "6.2.0-next.0",
124
- "@equinor/fusion-framework-module-app": "8.3.0-next.0",
125
- "@equinor/fusion-framework-module-http": "8.1.5-next.0",
126
- "@equinor/fusion-framework-module-service-discovery": "10.3.0-next.0",
119
+ "@equinor/fusion-framework-cli-plugin-mock-server": "^0.3.3",
120
+ "@equinor/fusion-framework-module": "6.1.6",
121
+ "@equinor/fusion-framework-module-app": "8.2.2",
122
+ "@equinor/fusion-framework-module-http": "8.1.4",
123
+ "@equinor/fusion-framework-module-service-discovery": "10.2.0",
127
124
  "@rollup/plugin-commonjs": "^29.0.0",
128
125
  "@rollup/plugin-json": "^6.1.0",
129
126
  "@rollup/plugin-node-resolve": "^16.0.1",