slicejs-web-framework 4.0.1 → 4.0.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 (71) hide show
  1. package/.opencode/opencode.json +14 -14
  2. package/LICENSE +21 -21
  3. package/README.md +97 -97
  4. package/Slice/Components/Structural/ContextManager/ContextManager.js +422 -422
  5. package/Slice/Components/Structural/ContextManager/ContextManagerDebugger.js +470 -470
  6. package/Slice/Components/Structural/Controller/Controller.js +1406 -1406
  7. package/Slice/Components/Structural/Controller/allowedValuesValidation.js +52 -52
  8. package/Slice/Components/Structural/Debugger/Debugger.html +72 -72
  9. package/Slice/Components/Structural/Debugger/Debugger.js +1505 -1505
  10. package/Slice/Components/Structural/EventManager/EventManager.js +651 -651
  11. package/Slice/Components/Structural/EventManager/EventManagerDebugger.js +985 -985
  12. package/Slice/Components/Structural/LeakInspector/LeakInspector.js +296 -296
  13. package/Slice/Components/Structural/Logger/Log.js +10 -10
  14. package/Slice/Components/Structural/Logger/LogViewer/LogViewer.js +662 -662
  15. package/Slice/Components/Structural/Logger/Logger.js +225 -225
  16. package/Slice/Components/Structural/Router/Router.js +810 -793
  17. package/Slice/Components/Structural/StylesManager/StylesManager.js +82 -82
  18. package/Slice/Components/Structural/StylesManager/ThemeManager/ThemeManager.js +175 -162
  19. package/Slice/Slice.js +708 -685
  20. package/Slice/tests/build-bundled-component-without-category.test.js +103 -103
  21. package/Slice/tests/build-js-only-visual-components.test.js +144 -144
  22. package/Slice/tests/build-singleton.test.js +244 -244
  23. package/Slice/tests/bundle-v2-runtime-contract.test.js +747 -738
  24. package/Slice/tests/context-patch-use.test.js +95 -95
  25. package/Slice/tests/destroy-cascade.test.js +215 -215
  26. package/Slice/tests/event-emitters.test.js +79 -79
  27. package/Slice/tests/event-registry.test.js +141 -141
  28. package/Slice/tests/fixtures/real-runtime-loader.mjs +21 -21
  29. package/Slice/tests/leak-orphans.test.js +131 -131
  30. package/Slice/tests/props-allowed-values-validation.test.js +119 -119
  31. package/Slice/tests/public-env-runtime-accessors.test.js +44 -44
  32. package/Slice/tests/public-env-typed-accessors.test.js +66 -66
  33. package/Slice/tests/router-loading-finally.test.js +68 -68
  34. package/api/framework/devDepsOptimizer.js +335 -335
  35. package/api/framework/express.js +3 -3
  36. package/api/framework/precompressedStatic.js +93 -93
  37. package/api/framework/publicEnvResolver.js +123 -123
  38. package/api/framework/securityMiddleware.js +197 -197
  39. package/api/framework/server.js +411 -411
  40. package/api/index.js +7 -7
  41. package/api/tests/dev-deps-optimizer.test.js +137 -137
  42. package/api/tests/dev-server-external.test.js +56 -56
  43. package/api/tests/precompressed-static.test.js +119 -119
  44. package/api/tests/public-env-resolver.test.js +193 -193
  45. package/package.json +49 -49
  46. package/sliceConfig.schema.json +122 -122
  47. package/src/App/index.html +69 -69
  48. package/src/App/index.js +20 -20
  49. package/src/App/style.css +11 -11
  50. package/src/Components/AppComponents/AboutSection/AboutSection.css +9 -9
  51. package/src/Components/AppComponents/AboutSection/AboutSection.html +8 -8
  52. package/src/Components/AppComponents/AboutSection/AboutSection.js +12 -12
  53. package/src/Components/AppComponents/AppShell/AppShell.css +10 -10
  54. package/src/Components/AppComponents/AppShell/AppShell.html +4 -4
  55. package/src/Components/AppComponents/AppShell/AppShell.js +36 -36
  56. package/src/Components/AppComponents/HomeSection/HomeSection.css +20 -20
  57. package/src/Components/AppComponents/HomeSection/HomeSection.html +10 -10
  58. package/src/Components/AppComponents/HomeSection/HomeSection.js +19 -19
  59. package/src/Components/components.js +8 -8
  60. package/src/public/Styles/sliceStyles.css +34 -34
  61. package/src/public/Themes/Dark.css +42 -42
  62. package/src/public/Themes/Light.css +31 -31
  63. package/src/public/Themes/Slice.css +47 -47
  64. package/src/public/manifest.json +15 -15
  65. package/src/public/robots.txt +4 -4
  66. package/src/public/sitemap.xml +8 -8
  67. package/src/routes.js +9 -9
  68. package/src/sliceConfig.json +72 -72
  69. package/tests/router-build-failure.test.js +125 -0
  70. package/tests/router-emit-ordering.test.js +63 -63
  71. package/types/index.d.ts +219 -219
@@ -1,131 +1,131 @@
1
- // Real-Controller tests for the dev-mode leak detector (findOrphans + criteria).
2
- // Loads the REAL Controller via the resolve hook (same pattern as
3
- // destroy-cascade.test.js) and drives it with a minimal fake DOM. Fake nodes are
4
- // made `instanceof HTMLElement` because findOrphans filters on that.
5
- import { register } from 'node:module';
6
- import test from 'node:test';
7
- import assert from 'node:assert/strict';
8
-
9
- globalThis.alert = () => {};
10
- register(new URL('./fixtures/real-runtime-loader.mjs', import.meta.url));
11
-
12
- globalThis.HTMLElement = class HTMLElement {};
13
- globalThis.slice = {
14
- logger: { logError() {}, logWarning() {}, logInfo() {} },
15
- events: null,
16
- };
17
-
18
- const { default: Controller } = await import('../Components/Structural/Controller/Controller.js');
19
-
20
- function mkEl(sliceId, children = []) {
21
- return Object.assign(Object.create(HTMLElement.prototype), {
22
- sliceId,
23
- tagName: 'SLICE-X',
24
- isConnected: true,
25
- parentComponent: null,
26
- _children: children,
27
- querySelectorAll() {
28
- const out = [];
29
- const walk = (n) => { for (const c of n._children) { out.push(c); walk(c); } };
30
- walk(this);
31
- return out;
32
- },
33
- setAttribute() {},
34
- remove() { this.isConnected = false; },
35
- });
36
- }
37
-
38
- function flatten(root) {
39
- const out = [root];
40
- for (const c of root._children) out.push(...flatten(c));
41
- return out;
42
- }
43
-
44
- function buildTree(controller, root) {
45
- for (const node of flatten(root)) controller.registerComponent(node);
46
- controller.registerComponentsRecursively(root);
47
- }
48
-
49
- test('a built-then-detached component (no destroy) IS flagged', () => {
50
- const c = new Controller();
51
- const comp = mkEl('leaky');
52
- buildTree(c, comp);
53
- comp.isConnected = false; // simulate innerHTML='' / remove() without destroyComponent
54
-
55
- const orphans = c.findOrphans();
56
- assert.equal(orphans.length, 1, 'one orphan');
57
- assert.equal(orphans[0].sliceId, 'leaky');
58
- assert.ok(orphans[0].retainPath.includes('Object(leaky)') || orphans[0].retainPath.length >= 1);
59
- });
60
-
61
- test('a connected component is NOT flagged', () => {
62
- const c = new Controller();
63
- const comp = mkEl('alive');
64
- buildTree(c, comp);
65
- assert.equal(c.findOrphans().length, 0);
66
- });
67
-
68
- test('__sliceCached components are excluded (Route/MultiRoute cache)', () => {
69
- const c = new Controller();
70
- const comp = mkEl('cachedView');
71
- buildTree(c, comp);
72
- comp.isConnected = false;
73
- comp.__sliceCached = true;
74
- assert.equal(c.findOrphans().length, 0);
75
- });
76
-
77
- test('router-managed route-* sliceIds are excluded', () => {
78
- const c = new Controller();
79
- const comp = mkEl('route-Home');
80
- buildTree(c, comp);
81
- comp.isConnected = false;
82
- assert.equal(c.findOrphans().length, 0);
83
- });
84
-
85
- test('ancestor de-dup: only the detached root is reported', () => {
86
- const c = new Controller();
87
- const child = mkEl('child');
88
- const parent = mkEl('parent', [child]);
89
- buildTree(c, parent);
90
- parent.isConnected = false;
91
- child.isConnected = false;
92
-
93
- const orphans = c.findOrphans();
94
- assert.equal(orphans.length, 1, 'only the root, not the descendant');
95
- assert.equal(orphans[0].sliceId, 'parent');
96
- });
97
-
98
- test('descendants of a cached subtree are NOT flagged', () => {
99
- const c = new Controller();
100
- const child = mkEl('child');
101
- const parent = mkEl('parent', [child]);
102
- buildTree(c, parent);
103
- parent.__sliceCached = true; // cached route root
104
- parent.isConnected = false;
105
- child.isConnected = false;
106
- assert.equal(c.findOrphans().length, 0);
107
- });
108
-
109
- test('app-registered exclusion predicate is honored', () => {
110
- const c = new Controller();
111
- const comp = mkEl('special');
112
- buildTree(c, comp);
113
- comp.isConnected = false;
114
- c.registerLeakExclusion((node) => node.sliceId === 'special');
115
- assert.equal(c.findOrphans().length, 0);
116
- });
117
-
118
- test('growth heuristic trips on monotonic increase and resets on shrink', () => {
119
- const c = new Controller();
120
- assert.equal(c.isGrowthMonotonic(), false, 'needs >= 5 samples');
121
-
122
- for (let i = 0; i < 5; i++) {
123
- c.registerComponent(mkEl(`g${i}`));
124
- c.sampleComponentGrowth();
125
- }
126
- assert.equal(c.isGrowthMonotonic(), true, 'sizes 1..5 are monotonic growth');
127
-
128
- c.destroyComponent('g4');
129
- c.sampleComponentGrowth(); // size dips
130
- assert.equal(c.isGrowthMonotonic(), false, 'a shrink breaks the monotonic signal');
131
- });
1
+ // Real-Controller tests for the dev-mode leak detector (findOrphans + criteria).
2
+ // Loads the REAL Controller via the resolve hook (same pattern as
3
+ // destroy-cascade.test.js) and drives it with a minimal fake DOM. Fake nodes are
4
+ // made `instanceof HTMLElement` because findOrphans filters on that.
5
+ import { register } from 'node:module';
6
+ import test from 'node:test';
7
+ import assert from 'node:assert/strict';
8
+
9
+ globalThis.alert = () => {};
10
+ register(new URL('./fixtures/real-runtime-loader.mjs', import.meta.url));
11
+
12
+ globalThis.HTMLElement = class HTMLElement {};
13
+ globalThis.slice = {
14
+ logger: { logError() {}, logWarning() {}, logInfo() {} },
15
+ events: null,
16
+ };
17
+
18
+ const { default: Controller } = await import('../Components/Structural/Controller/Controller.js');
19
+
20
+ function mkEl(sliceId, children = []) {
21
+ return Object.assign(Object.create(HTMLElement.prototype), {
22
+ sliceId,
23
+ tagName: 'SLICE-X',
24
+ isConnected: true,
25
+ parentComponent: null,
26
+ _children: children,
27
+ querySelectorAll() {
28
+ const out = [];
29
+ const walk = (n) => { for (const c of n._children) { out.push(c); walk(c); } };
30
+ walk(this);
31
+ return out;
32
+ },
33
+ setAttribute() {},
34
+ remove() { this.isConnected = false; },
35
+ });
36
+ }
37
+
38
+ function flatten(root) {
39
+ const out = [root];
40
+ for (const c of root._children) out.push(...flatten(c));
41
+ return out;
42
+ }
43
+
44
+ function buildTree(controller, root) {
45
+ for (const node of flatten(root)) controller.registerComponent(node);
46
+ controller.registerComponentsRecursively(root);
47
+ }
48
+
49
+ test('a built-then-detached component (no destroy) IS flagged', () => {
50
+ const c = new Controller();
51
+ const comp = mkEl('leaky');
52
+ buildTree(c, comp);
53
+ comp.isConnected = false; // simulate innerHTML='' / remove() without destroyComponent
54
+
55
+ const orphans = c.findOrphans();
56
+ assert.equal(orphans.length, 1, 'one orphan');
57
+ assert.equal(orphans[0].sliceId, 'leaky');
58
+ assert.ok(orphans[0].retainPath.includes('Object(leaky)') || orphans[0].retainPath.length >= 1);
59
+ });
60
+
61
+ test('a connected component is NOT flagged', () => {
62
+ const c = new Controller();
63
+ const comp = mkEl('alive');
64
+ buildTree(c, comp);
65
+ assert.equal(c.findOrphans().length, 0);
66
+ });
67
+
68
+ test('__sliceCached components are excluded (Route/MultiRoute cache)', () => {
69
+ const c = new Controller();
70
+ const comp = mkEl('cachedView');
71
+ buildTree(c, comp);
72
+ comp.isConnected = false;
73
+ comp.__sliceCached = true;
74
+ assert.equal(c.findOrphans().length, 0);
75
+ });
76
+
77
+ test('router-managed route-* sliceIds are excluded', () => {
78
+ const c = new Controller();
79
+ const comp = mkEl('route-Home');
80
+ buildTree(c, comp);
81
+ comp.isConnected = false;
82
+ assert.equal(c.findOrphans().length, 0);
83
+ });
84
+
85
+ test('ancestor de-dup: only the detached root is reported', () => {
86
+ const c = new Controller();
87
+ const child = mkEl('child');
88
+ const parent = mkEl('parent', [child]);
89
+ buildTree(c, parent);
90
+ parent.isConnected = false;
91
+ child.isConnected = false;
92
+
93
+ const orphans = c.findOrphans();
94
+ assert.equal(orphans.length, 1, 'only the root, not the descendant');
95
+ assert.equal(orphans[0].sliceId, 'parent');
96
+ });
97
+
98
+ test('descendants of a cached subtree are NOT flagged', () => {
99
+ const c = new Controller();
100
+ const child = mkEl('child');
101
+ const parent = mkEl('parent', [child]);
102
+ buildTree(c, parent);
103
+ parent.__sliceCached = true; // cached route root
104
+ parent.isConnected = false;
105
+ child.isConnected = false;
106
+ assert.equal(c.findOrphans().length, 0);
107
+ });
108
+
109
+ test('app-registered exclusion predicate is honored', () => {
110
+ const c = new Controller();
111
+ const comp = mkEl('special');
112
+ buildTree(c, comp);
113
+ comp.isConnected = false;
114
+ c.registerLeakExclusion((node) => node.sliceId === 'special');
115
+ assert.equal(c.findOrphans().length, 0);
116
+ });
117
+
118
+ test('growth heuristic trips on monotonic increase and resets on shrink', () => {
119
+ const c = new Controller();
120
+ assert.equal(c.isGrowthMonotonic(), false, 'needs >= 5 samples');
121
+
122
+ for (let i = 0; i < 5; i++) {
123
+ c.registerComponent(mkEl(`g${i}`));
124
+ c.sampleComponentGrowth();
125
+ }
126
+ assert.equal(c.isGrowthMonotonic(), true, 'sizes 1..5 are monotonic growth');
127
+
128
+ c.destroyComponent('g4');
129
+ c.sampleComponentGrowth(); // size dips
130
+ assert.equal(c.isGrowthMonotonic(), false, 'a shrink breaks the monotonic signal');
131
+ });
@@ -1,119 +1,119 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
-
4
- import { collectInvalidAllowedValueProps } from '../Components/Structural/Controller/allowedValuesValidation.js';
5
-
6
- test('collectInvalidAllowedValueProps returns empty when value is allowed', () => {
7
- const result = collectInvalidAllowedValueProps(
8
- {
9
- variant: {
10
- type: 'string',
11
- allowedValues: ['primary', 'secondary']
12
- }
13
- },
14
- {
15
- variant: 'primary'
16
- }
17
- );
18
-
19
- assert.deepEqual(result, []);
20
- });
21
-
22
- test('collectInvalidAllowedValueProps reports invalid provided values', () => {
23
- const result = collectInvalidAllowedValueProps(
24
- {
25
- variant: {
26
- type: 'string',
27
- allowedValues: ['primary', 'secondary']
28
- },
29
- size: {
30
- type: 'number',
31
- allowedValues: [12, 16]
32
- }
33
- },
34
- {
35
- variant: 'danger',
36
- size: 14
37
- }
38
- );
39
-
40
- assert.deepEqual(result, [
41
- {
42
- propName: 'variant',
43
- value: 'danger',
44
- allowedValues: ['primary', 'secondary']
45
- },
46
- {
47
- propName: 'size',
48
- value: 14,
49
- allowedValues: [12, 16]
50
- }
51
- ]);
52
- });
53
-
54
- test('collectInvalidAllowedValueProps ignores props not provided', () => {
55
- const result = collectInvalidAllowedValueProps(
56
- {
57
- variant: {
58
- type: 'string',
59
- allowedValues: ['primary', 'secondary']
60
- },
61
- disabled: {
62
- type: 'boolean'
63
- }
64
- },
65
- {
66
- disabled: true
67
- }
68
- );
69
-
70
- assert.deepEqual(result, []);
71
- });
72
-
73
- test('collectInvalidAllowedValueProps uses strict equality', () => {
74
- const result = collectInvalidAllowedValueProps(
75
- {
76
- amount: {
77
- type: 'number',
78
- allowedValues: [1]
79
- },
80
- active: {
81
- type: 'boolean',
82
- allowedValues: [true]
83
- }
84
- },
85
- {
86
- amount: '1',
87
- active: 1
88
- }
89
- );
90
-
91
- assert.deepEqual(result, [
92
- {
93
- propName: 'amount',
94
- value: '1',
95
- allowedValues: [1]
96
- },
97
- {
98
- propName: 'active',
99
- value: 1,
100
- allowedValues: [true]
101
- }
102
- ]);
103
- });
104
-
105
- test('collectInvalidAllowedValueProps ignores empty allowedValues arrays', () => {
106
- const result = collectInvalidAllowedValueProps(
107
- {
108
- variant: {
109
- type: 'string',
110
- allowedValues: []
111
- }
112
- },
113
- {
114
- variant: 'unexpected'
115
- }
116
- );
117
-
118
- assert.deepEqual(result, []);
119
- });
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+
4
+ import { collectInvalidAllowedValueProps } from '../Components/Structural/Controller/allowedValuesValidation.js';
5
+
6
+ test('collectInvalidAllowedValueProps returns empty when value is allowed', () => {
7
+ const result = collectInvalidAllowedValueProps(
8
+ {
9
+ variant: {
10
+ type: 'string',
11
+ allowedValues: ['primary', 'secondary']
12
+ }
13
+ },
14
+ {
15
+ variant: 'primary'
16
+ }
17
+ );
18
+
19
+ assert.deepEqual(result, []);
20
+ });
21
+
22
+ test('collectInvalidAllowedValueProps reports invalid provided values', () => {
23
+ const result = collectInvalidAllowedValueProps(
24
+ {
25
+ variant: {
26
+ type: 'string',
27
+ allowedValues: ['primary', 'secondary']
28
+ },
29
+ size: {
30
+ type: 'number',
31
+ allowedValues: [12, 16]
32
+ }
33
+ },
34
+ {
35
+ variant: 'danger',
36
+ size: 14
37
+ }
38
+ );
39
+
40
+ assert.deepEqual(result, [
41
+ {
42
+ propName: 'variant',
43
+ value: 'danger',
44
+ allowedValues: ['primary', 'secondary']
45
+ },
46
+ {
47
+ propName: 'size',
48
+ value: 14,
49
+ allowedValues: [12, 16]
50
+ }
51
+ ]);
52
+ });
53
+
54
+ test('collectInvalidAllowedValueProps ignores props not provided', () => {
55
+ const result = collectInvalidAllowedValueProps(
56
+ {
57
+ variant: {
58
+ type: 'string',
59
+ allowedValues: ['primary', 'secondary']
60
+ },
61
+ disabled: {
62
+ type: 'boolean'
63
+ }
64
+ },
65
+ {
66
+ disabled: true
67
+ }
68
+ );
69
+
70
+ assert.deepEqual(result, []);
71
+ });
72
+
73
+ test('collectInvalidAllowedValueProps uses strict equality', () => {
74
+ const result = collectInvalidAllowedValueProps(
75
+ {
76
+ amount: {
77
+ type: 'number',
78
+ allowedValues: [1]
79
+ },
80
+ active: {
81
+ type: 'boolean',
82
+ allowedValues: [true]
83
+ }
84
+ },
85
+ {
86
+ amount: '1',
87
+ active: 1
88
+ }
89
+ );
90
+
91
+ assert.deepEqual(result, [
92
+ {
93
+ propName: 'amount',
94
+ value: '1',
95
+ allowedValues: [1]
96
+ },
97
+ {
98
+ propName: 'active',
99
+ value: 1,
100
+ allowedValues: [true]
101
+ }
102
+ ]);
103
+ });
104
+
105
+ test('collectInvalidAllowedValueProps ignores empty allowedValues arrays', () => {
106
+ const result = collectInvalidAllowedValueProps(
107
+ {
108
+ variant: {
109
+ type: 'string',
110
+ allowedValues: []
111
+ }
112
+ },
113
+ {
114
+ variant: 'unexpected'
115
+ }
116
+ );
117
+
118
+ assert.deepEqual(result, []);
119
+ });
@@ -1,44 +1,44 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
-
4
- globalThis.alert = () => {};
5
-
6
- const { default: Slice } = await import('../Slice.js');
7
-
8
- function createSliceInstance() {
9
- return new Slice({
10
- paths: {},
11
- themeManager: {},
12
- stylesManager: {},
13
- logger: {},
14
- debugger: {},
15
- loading: {},
16
- events: {}
17
- });
18
- }
19
-
20
- test('getEnv returns fallback for missing key and stored value for known key', () => {
21
- const sliceInstance = createSliceInstance();
22
-
23
- assert.equal(typeof sliceInstance.getEnv, 'function');
24
- assert.equal(typeof sliceInstance.setPublicEnv, 'function');
25
- assert.equal(sliceInstance.getEnv('SLICE_PUBLIC_MISSING', 'fallback'), 'fallback');
26
-
27
- sliceInstance.setPublicEnv({ SLICE_PUBLIC_API_URL: 'https://api.example.com' });
28
- assert.equal(sliceInstance.getEnv('SLICE_PUBLIC_API_URL'), 'https://api.example.com');
29
- });
30
-
31
- test('getPublicEnv returns a copy and only includes SLICE_PUBLIC_ keys', () => {
32
- const sliceInstance = createSliceInstance();
33
-
34
- sliceInstance.setPublicEnv({
35
- SLICE_PUBLIC_FLAG: 'true',
36
- INTERNAL_SECRET: 'hidden'
37
- });
38
-
39
- const snapshot = sliceInstance.getPublicEnv();
40
- assert.deepEqual(snapshot, { SLICE_PUBLIC_FLAG: 'true' });
41
-
42
- snapshot.SLICE_PUBLIC_FLAG = 'mutated';
43
- assert.equal(sliceInstance.getEnv('SLICE_PUBLIC_FLAG'), 'true');
44
- });
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+
4
+ globalThis.alert = () => {};
5
+
6
+ const { default: Slice } = await import('../Slice.js');
7
+
8
+ function createSliceInstance() {
9
+ return new Slice({
10
+ paths: {},
11
+ themeManager: {},
12
+ stylesManager: {},
13
+ logger: {},
14
+ debugger: {},
15
+ loading: {},
16
+ events: {}
17
+ });
18
+ }
19
+
20
+ test('getEnv returns fallback for missing key and stored value for known key', () => {
21
+ const sliceInstance = createSliceInstance();
22
+
23
+ assert.equal(typeof sliceInstance.getEnv, 'function');
24
+ assert.equal(typeof sliceInstance.setPublicEnv, 'function');
25
+ assert.equal(sliceInstance.getEnv('SLICE_PUBLIC_MISSING', 'fallback'), 'fallback');
26
+
27
+ sliceInstance.setPublicEnv({ SLICE_PUBLIC_API_URL: 'https://api.example.com' });
28
+ assert.equal(sliceInstance.getEnv('SLICE_PUBLIC_API_URL'), 'https://api.example.com');
29
+ });
30
+
31
+ test('getPublicEnv returns a copy and only includes SLICE_PUBLIC_ keys', () => {
32
+ const sliceInstance = createSliceInstance();
33
+
34
+ sliceInstance.setPublicEnv({
35
+ SLICE_PUBLIC_FLAG: 'true',
36
+ INTERNAL_SECRET: 'hidden'
37
+ });
38
+
39
+ const snapshot = sliceInstance.getPublicEnv();
40
+ assert.deepEqual(snapshot, { SLICE_PUBLIC_FLAG: 'true' });
41
+
42
+ snapshot.SLICE_PUBLIC_FLAG = 'mutated';
43
+ assert.equal(sliceInstance.getEnv('SLICE_PUBLIC_FLAG'), 'true');
44
+ });