slicejs-web-framework 3.4.4 → 3.5.0

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 (101) hide show
  1. package/Slice/Components/Structural/Controller/Controller.js +135 -0
  2. package/Slice/Components/Structural/EventManager/EventManager.js +289 -4
  3. package/Slice/Components/Structural/EventManager/EventManagerDebugger.js +368 -30
  4. package/Slice/Components/Structural/LeakInspector/LeakInspector.js +296 -0
  5. package/Slice/Slice.js +14 -1
  6. package/Slice/tests/event-emitters.test.js +79 -0
  7. package/Slice/tests/event-registry.test.js +141 -0
  8. package/Slice/tests/leak-orphans.test.js +131 -0
  9. package/api/index.js +35 -8
  10. package/package.json +1 -1
  11. package/sliceConfig.schema.json +10 -1
  12. package/src/sliceConfig.json +5 -1
  13. package/types/index.d.ts +12 -0
  14. package/slicejs-cli/.github/workflows/ci.yml +0 -43
  15. package/slicejs-cli/LICENSE +0 -21
  16. package/slicejs-cli/assets/Slice.js-logo.png +0 -0
  17. package/slicejs-cli/client.js +0 -740
  18. package/slicejs-cli/commands/Print.js +0 -164
  19. package/slicejs-cli/commands/Validations.js +0 -92
  20. package/slicejs-cli/commands/build/build.js +0 -40
  21. package/slicejs-cli/commands/buildProduction/buildProduction.js +0 -577
  22. package/slicejs-cli/commands/bundle/bundle.js +0 -236
  23. package/slicejs-cli/commands/createComponent/VisualComponentTemplate.js +0 -55
  24. package/slicejs-cli/commands/createComponent/createComponent.js +0 -128
  25. package/slicejs-cli/commands/deleteComponent/deleteComponent.js +0 -81
  26. package/slicejs-cli/commands/doctor/doctor.js +0 -516
  27. package/slicejs-cli/commands/getComponent/getComponent.js +0 -695
  28. package/slicejs-cli/commands/init/init.js +0 -473
  29. package/slicejs-cli/commands/listComponents/listComponents.js +0 -172
  30. package/slicejs-cli/commands/startServer/startServer.js +0 -240
  31. package/slicejs-cli/commands/startServer/watchServer.js +0 -66
  32. package/slicejs-cli/commands/types/types.js +0 -583
  33. package/slicejs-cli/commands/utils/LocalCliDelegation.js +0 -53
  34. package/slicejs-cli/commands/utils/PackageManager.js +0 -148
  35. package/slicejs-cli/commands/utils/PathHelper.js +0 -75
  36. package/slicejs-cli/commands/utils/VersionChecker.js +0 -169
  37. package/slicejs-cli/commands/utils/bundling/BundleGenerator.js +0 -2525
  38. package/slicejs-cli/commands/utils/bundling/DependencyAnalyzer.js +0 -933
  39. package/slicejs-cli/commands/utils/loadConfig.js +0 -31
  40. package/slicejs-cli/commands/utils/sliceScripts.js +0 -48
  41. package/slicejs-cli/commands/utils/updateManager.js +0 -471
  42. package/slicejs-cli/package.json +0 -71
  43. package/slicejs-cli/playwright.config.js +0 -51
  44. package/slicejs-cli/pnpm-lock.yaml +0 -1629
  45. package/slicejs-cli/tests/build-command-integration.test.js +0 -87
  46. package/slicejs-cli/tests/build-mangling-e2e.test.js +0 -290
  47. package/slicejs-cli/tests/build-production-e2e.test.js +0 -140
  48. package/slicejs-cli/tests/builder-edge-cases.test.js +0 -322
  49. package/slicejs-cli/tests/bundle-generate-e2e.test.js +0 -115
  50. package/slicejs-cli/tests/bundle-generator.test.js +0 -691
  51. package/slicejs-cli/tests/bundle-v2-register-output.test.js +0 -470
  52. package/slicejs-cli/tests/bundling-dependency-edges.test.js +0 -127
  53. package/slicejs-cli/tests/bundling-imports-unit.test.js +0 -267
  54. package/slicejs-cli/tests/client-launcher-contract.test.js +0 -211
  55. package/slicejs-cli/tests/commands-component-crud.test.js +0 -128
  56. package/slicejs-cli/tests/commands-doctor.test.js +0 -152
  57. package/slicejs-cli/tests/commands-version-checker.test.js +0 -37
  58. package/slicejs-cli/tests/component-registry-parse.test.js +0 -34
  59. package/slicejs-cli/tests/dependency-analyzer.test.js +0 -24
  60. package/slicejs-cli/tests/e2e/bundles.spec.js +0 -91
  61. package/slicejs-cli/tests/e2e/dependency-scenarios.spec.js +0 -56
  62. package/slicejs-cli/tests/e2e/fixtures/components/Service/FetchManager/FetchManager.js +0 -136
  63. package/slicejs-cli/tests/e2e/fixtures/components/Service/IndexedDbManager/IndexedDbManager.js +0 -149
  64. package/slicejs-cli/tests/e2e/fixtures/components/Service/LocalStorageManager/LocalStorageManager.js +0 -45
  65. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Button/Button.css +0 -106
  66. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Button/Button.html +0 -5
  67. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Button/Button.js +0 -158
  68. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Link/Link.js +0 -33
  69. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Loading/Loading.css +0 -56
  70. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Loading/Loading.html +0 -83
  71. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Loading/Loading.js +0 -164
  72. package/slicejs-cli/tests/e2e/fixtures/components/Visual/MultiRoute/MultiRoute.js +0 -167
  73. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Navbar/Navbar.css +0 -116
  74. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Navbar/Navbar.html +0 -44
  75. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Navbar/Navbar.js +0 -180
  76. package/slicejs-cli/tests/e2e/fixtures/components/Visual/NotFound/NotFound.js +0 -20
  77. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Route/Route.js +0 -181
  78. package/slicejs-cli/tests/e2e/fixtures/components/registry.json +0 -12
  79. package/slicejs-cli/tests/e2e/fixtures/vendor-components.mjs +0 -65
  80. package/slicejs-cli/tests/e2e/navigation.spec.js +0 -44
  81. package/slicejs-cli/tests/e2e/render.spec.js +0 -34
  82. package/slicejs-cli/tests/e2e/serve.mjs +0 -264
  83. package/slicejs-cli/tests/e2e/shared-deps.spec.js +0 -61
  84. package/slicejs-cli/tests/e2e/unminified.spec.js +0 -33
  85. package/slicejs-cli/tests/e2e-serve.test.js +0 -148
  86. package/slicejs-cli/tests/fixtures/components.js +0 -8
  87. package/slicejs-cli/tests/fixtures/sliceConfig.json +0 -74
  88. package/slicejs-cli/tests/getcomponent.test.js +0 -435
  89. package/slicejs-cli/tests/helpers/setup.js +0 -102
  90. package/slicejs-cli/tests/init-command-contract.test.js +0 -101
  91. package/slicejs-cli/tests/init-e2e.test.js +0 -82
  92. package/slicejs-cli/tests/init-project-isolation.test.js +0 -98
  93. package/slicejs-cli/tests/local-cli-delegation.test.js +0 -81
  94. package/slicejs-cli/tests/package-manager.test.js +0 -127
  95. package/slicejs-cli/tests/path-helper.test.js +0 -206
  96. package/slicejs-cli/tests/perf-budget.test.js +0 -84
  97. package/slicejs-cli/tests/postinstall-command.test.js +0 -103
  98. package/slicejs-cli/tests/property-mangling-regression.test.js +0 -182
  99. package/slicejs-cli/tests/types-breakage.test.js +0 -491
  100. package/slicejs-cli/tests/types-generator-errors.test.js +0 -361
  101. package/slicejs-cli/tests/types-generator.test.js +0 -348
@@ -0,0 +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
+ });
package/api/index.js CHANGED
@@ -103,6 +103,39 @@ app.get('/slice-env.json', (req, res) => {
103
103
  res.json(payload);
104
104
  });
105
105
 
106
+ // ==============================================
107
+ // PWA — manifest + service worker (dev y prod)
108
+ // ==============================================
109
+ // Despacha los archivos PWA desde la carpeta desplegada (src en dev, dist en
110
+ // prod) si existen; 404 si no, así no estorba cuando la app no es PWA.
111
+ // El service worker se sirve SIN caché (para que sus actualizaciones lleguen) y
112
+ // con Service-Worker-Allowed:/ para permitir scope raíz.
113
+ const servePwaFile = (res, fileName, contentType, extraHeaders = {}) => {
114
+ const filePath = path.join(__dirname, `../${folderDeployed}`, fileName);
115
+ try {
116
+ if (fs.existsSync(filePath)) {
117
+ res.setHeader('Content-Type', contentType);
118
+ for (const [key, value] of Object.entries(extraHeaders)) res.setHeader(key, value);
119
+ return res.send(fs.readFileSync(filePath, 'utf8'));
120
+ }
121
+ } catch (error) {
122
+ console.error(`Error reading ${fileName}:`, error);
123
+ return res.status(500).send(`Error reading ${fileName}`);
124
+ }
125
+ return res.status(404).send(`${fileName} not found`);
126
+ };
127
+
128
+ app.get('/service-worker.js', (req, res) => {
129
+ servePwaFile(res, 'service-worker.js', 'application/javascript; charset=utf-8', {
130
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
131
+ 'Service-Worker-Allowed': '/',
132
+ });
133
+ });
134
+
135
+ app.get('/manifest.json', (req, res) => {
136
+ servePwaFile(res, 'manifest.json', 'application/manifest+json; charset=utf-8');
137
+ });
138
+
106
139
  // ==============================================
107
140
  // ARCHIVOS ESTÁTICOS (DESPUÉS DE SEGURIDAD)
108
141
  // ==============================================
@@ -193,14 +226,8 @@ if (runMode === 'development') {
193
226
  return res.status(404).send(`${fileName} not found`);
194
227
  };
195
228
 
196
- app.get('/manifest.json', (req, res) => {
197
- const manifestPath = path.join(__dirname, `../${folderDeployed}`, 'manifest.json');
198
- serveStaticFile(req, res, manifestPath, 'application/json; charset=utf-8', 'manifest.json');
199
- });
200
- app.get('/service-worker.js', (req, res) => {
201
- const workerPath = path.join(__dirname, `../${folderDeployed}`, 'service-worker.js');
202
- serveStaticFile(req, res, workerPath, 'application/javascript; charset=utf-8', 'service-worker.js');
203
- });
229
+ // (manifest.json y service-worker.js los sirve el bloque PWA compartido de arriba,
230
+ // con headers correctos en dev y prod.)
204
231
  app.get('/routes.js', (req, res) => {
205
232
  const routesPath = path.join(__dirname, `../${folderDeployed}`, 'routes.js');
206
233
  serveStaticFile(req, res, routesPath, 'application/javascript; charset=utf-8', 'routes.js');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slicejs-web-framework",
3
- "version": "3.4.4",
3
+ "version": "3.5.0",
4
4
  "description": "",
5
5
  "engines": {
6
6
  "node": ">=20"
@@ -10,7 +10,16 @@
10
10
  "additionalProperties": true,
11
11
  "properties": {
12
12
  "enabled": { "type": "boolean" },
13
- "click": { "type": "string", "enum": ["right", "left"] }
13
+ "click": { "type": "string", "enum": ["right", "left"] },
14
+ "leakInspector": {
15
+ "type": "object",
16
+ "additionalProperties": true,
17
+ "description": "Dev-only memory-leak panel. Disabled in production regardless of this flag.",
18
+ "properties": {
19
+ "enabled": { "type": "boolean" },
20
+ "shortcut": { "type": "string" }
21
+ }
22
+ }
14
23
  }
15
24
  },
16
25
  "stylesManager": {
@@ -5,7 +5,11 @@
5
5
  },
6
6
  "debugger": {
7
7
  "enabled": false,
8
- "click": "right"
8
+ "click": "right",
9
+ "leakInspector": {
10
+ "enabled": true,
11
+ "shortcut": "alt+shift+k"
12
+ }
9
13
  },
10
14
  "events": {
11
15
  "enabled": true,
package/types/index.d.ts CHANGED
@@ -44,6 +44,10 @@ export interface SliceControllerApi {
44
44
  destroyComponent(components: HTMLElement | string | Array<HTMLElement | string>): number;
45
45
  destroyByContainer(container: HTMLElement): number;
46
46
  destroyByPattern(pattern: string | RegExp): number;
47
+ findOrphans(): Array<{ sliceId: string; name: string; reason: string; retainPath: string[] }>;
48
+ registerLeakExclusion(predicate: (component: HTMLElement) => boolean): void;
49
+ sampleComponentGrowth(): void;
50
+ isGrowthMonotonic(): boolean;
47
51
  }
48
52
 
49
53
  export interface SliceStylesManagerApi {
@@ -88,6 +92,13 @@ export interface SliceEventManagerApi {
88
92
  unsubscribe(eventName: string, subscriptionId: string): boolean;
89
93
  emit(eventName: string, ...data: unknown[]): void;
90
94
  bind(component: HTMLElement): SliceEventBindingApi | null;
95
+ register(namespace: string, catalog: Record<string, { description?: string; payload?: unknown }>): SliceEventManagerApi;
96
+ register(catalog: Record<string, { description?: string; payload?: unknown }>): SliceEventManagerApi;
97
+ loadGraph(graph: { events: Record<string, unknown>; dynamic?: unknown }): SliceEventManagerApi;
98
+ staticEmittersOf(eventName: string): Array<{ file: string; line: number; component: string | null }>;
99
+ staticListenersOf(eventName: string): Array<{ file: string; line: number; component: string | null }>;
100
+ isDeclared(eventName: string): boolean;
101
+ namespaceOf(eventName: string): string | null;
91
102
  cleanupComponent(sliceId: string): number;
92
103
  hasSubscribers(eventName: string): boolean;
93
104
  subscriberCount(eventName: string): number;
@@ -178,6 +189,7 @@ export interface SliceApi {
178
189
  debugger?: SliceDebuggerApi;
179
190
  eventsDebugger?: SlicePanelDebuggerApi;
180
191
  contextDebugger?: SlicePanelDebuggerApi;
192
+ leakInspector?: SlicePanelDebuggerApi;
181
193
  loading?: SliceLoadingApi;
182
194
  paths: SlicePaths;
183
195
  _mode: SliceMode;
@@ -1,43 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches: [master, main]
6
- pull_request:
7
-
8
- jobs:
9
- test:
10
- runs-on: ubuntu-latest
11
- timeout-minutes: 20
12
- steps:
13
- - uses: actions/checkout@v4
14
-
15
- - uses: pnpm/action-setup@v4
16
- with:
17
- version: 11
18
-
19
- - uses: actions/setup-node@v4
20
- with:
21
- node-version: 22
22
- cache: pnpm
23
-
24
- - name: Install dependencies
25
- run: pnpm install --frozen-lockfile
26
-
27
- - name: Install Playwright browser
28
- run: pnpm exec playwright install --with-deps chromium
29
-
30
- # Serialized to avoid resource contention between the build-heavy
31
- # integration tests under parallel runners.
32
- - name: Unit & integration tests
33
- run: node --test --test-concurrency=1
34
-
35
- - name: Browser E2E tests
36
- run: pnpm exec playwright test
37
-
38
- - uses: actions/upload-artifact@v4
39
- if: ${{ !cancelled() }}
40
- with:
41
- name: playwright-report
42
- path: playwright-report/
43
- retention-days: 7
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 Victor Jose Kneider Alnahi and Julio Antonio Graterol Bracho
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.