frontend-hamroun 1.2.88 → 1.2.89

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.
package/dist/index.cjs CHANGED
@@ -325,6 +325,117 @@ async function hydrate(element, container) {
325
325
  }
326
326
  }
327
327
 
328
+ async function renderToString(element) {
329
+ prepareRender(true); // Mark as SSR
330
+ try {
331
+ const html = await renderNodeToString(element);
332
+ return html;
333
+ }
334
+ finally {
335
+ finishRender();
336
+ }
337
+ }
338
+ async function renderNodeToString(node) {
339
+ // Handle null, undefined, boolean
340
+ if (node == null || typeof node === 'boolean') {
341
+ return '';
342
+ }
343
+ // Handle primitives
344
+ if (typeof node === 'string' || typeof node === 'number') {
345
+ return escapeHtml(String(node));
346
+ }
347
+ // Handle arrays
348
+ if (Array.isArray(node)) {
349
+ const results = await Promise.all(node.map(child => renderNodeToString(child)));
350
+ return results.join('');
351
+ }
352
+ // Handle objects with type and props (React-like elements)
353
+ if (node && typeof node === 'object' && 'type' in node) {
354
+ const { type, props = {} } = node;
355
+ // Handle function components
356
+ if (typeof type === 'function') {
357
+ try {
358
+ const result = await type(props);
359
+ return await renderNodeToString(result);
360
+ }
361
+ catch (error) {
362
+ console.error('Error rendering component:', error);
363
+ return `<!-- Error rendering component: ${error.message} -->`;
364
+ }
365
+ }
366
+ // Handle DOM elements
367
+ if (typeof type === 'string') {
368
+ return await renderDOMElement(type, props);
369
+ }
370
+ }
371
+ // Fallback for other objects
372
+ if (typeof node === 'object') {
373
+ return escapeHtml(JSON.stringify(node));
374
+ }
375
+ return escapeHtml(String(node));
376
+ }
377
+ async function renderDOMElement(tagName, props) {
378
+ const { children, ...attrs } = props;
379
+ // Self-closing tags
380
+ const voidElements = new Set([
381
+ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
382
+ 'link', 'meta', 'param', 'source', 'track', 'wbr'
383
+ ]);
384
+ // Build attributes string
385
+ const attributeString = Object.entries(attrs)
386
+ .filter(([key, value]) => {
387
+ // Filter out React-specific props and event handlers
388
+ if (key.startsWith('on') || key === 'key' || key === 'ref')
389
+ return false;
390
+ if (value == null || value === false)
391
+ return false;
392
+ return true;
393
+ })
394
+ .map(([key, value]) => {
395
+ // Handle className -> class
396
+ if (key === 'className')
397
+ key = 'class';
398
+ // Handle boolean attributes
399
+ if (value === true)
400
+ return key;
401
+ // Handle style objects
402
+ if (key === 'style' && typeof value === 'object' && value !== null) {
403
+ const styleString = Object.entries(value)
404
+ .map(([prop, val]) => `${kebabCase(prop)}:${val}`)
405
+ .join(';');
406
+ return `style="${escapeHtml(styleString)}"`;
407
+ }
408
+ return `${key}="${escapeHtml(String(value))}"`;
409
+ })
410
+ .join(' ');
411
+ const openTag = `<${tagName}${attributeString ? ' ' + attributeString : ''}>`;
412
+ // Self-closing elements
413
+ if (voidElements.has(tagName)) {
414
+ return openTag.slice(0, -1) + '/>';
415
+ }
416
+ // Elements with children
417
+ const closeTag = `</${tagName}>`;
418
+ if (children != null) {
419
+ const childrenString = await renderNodeToString(children);
420
+ return openTag + childrenString + closeTag;
421
+ }
422
+ return openTag + closeTag;
423
+ }
424
+ function escapeHtml(text) {
425
+ const htmlEscapes = {
426
+ '&': '&amp;',
427
+ '<': '&lt;',
428
+ '>': '&gt;',
429
+ '"': '&quot;',
430
+ "'": '&#x27;',
431
+ '/': '&#x2F;'
432
+ };
433
+ return text.replace(/[&<>"'/]/g, (match) => htmlEscapes[match]);
434
+ }
435
+ function kebabCase(str) {
436
+ return str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
437
+ }
438
+
328
439
  function arePropsEqual(oldProps, newProps) {
329
440
  const oldKeys = Object.keys(oldProps).filter(k => k !== 'children');
330
441
  const newKeys = Object.keys(newProps).filter(k => k !== 'children');
@@ -1026,6 +1137,7 @@ const frontendHamroun = {
1026
1137
  // Rendering
1027
1138
  render,
1028
1139
  hydrate,
1140
+ renderToString,
1029
1141
  // Hooks
1030
1142
  useState,
1031
1143
  useEffect,
@@ -1100,6 +1212,7 @@ exports.onComponentMounted = onComponentMounted;
1100
1212
  exports.onComponentUnmounted = onComponentUnmounted;
1101
1213
  exports.onComponentUpdated = onComponentUpdated;
1102
1214
  exports.render = render;
1215
+ exports.renderToString = renderToString;
1103
1216
  exports.shouldComponentUpdate = shouldComponentUpdate;
1104
1217
  exports.thunk = thunk;
1105
1218
  exports.useContext = useContext;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { jsx, jsxs, jsxDEV, Fragment, createElement } from './jsx-runtime.js';
2
2
  export { render, hydrate } from './renderer.js';
3
+ export { renderToString } from './server-renderer.js';
3
4
  export { useState, useEffect, useMemo, useRef, useContext, useErrorBoundary } from './hooks.js';
4
5
  export { createContext } from './context.js';
5
6
  export { batchUpdates } from './batch.js';
@@ -12,6 +13,7 @@ export { createStore, StoreProvider, useSelector, useDispatch, useStore, logger,
12
13
  export { LifecycleEvents, emitAppInit, emitAppMounted, emitAppUpdated, emitAppError, emitAppDestroyed, emitComponentCreated, emitComponentMounted, emitComponentUpdated, emitComponentError, emitComponentUnmounted, onAppInit, onAppMounted, onAppUpdated, onAppError, onAppDestroyed, onComponentCreated, onComponentMounted, onComponentUpdated, onComponentError, onComponentUnmounted } from './lifecycle-events.js';
13
14
  import { jsx, createElement } from './jsx-runtime.js';
14
15
  import { render, hydrate } from './renderer.js';
16
+ import { renderToString } from './server-renderer.js';
15
17
  import { useState, useEffect, useMemo, useRef, useContext, useErrorBoundary } from './hooks.js';
16
18
  import { createContext } from './context.js';
17
19
  import { batchUpdates } from './batch.js';
@@ -37,6 +39,7 @@ declare const frontendHamroun: {
37
39
  createElement: typeof createElement;
38
40
  render: typeof render;
39
41
  hydrate: typeof hydrate;
42
+ renderToString: typeof renderToString;
40
43
  useState: typeof useState;
41
44
  useEffect: typeof useEffect;
42
45
  useMemo: typeof useMemo;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -321,6 +321,117 @@ async function hydrate(element, container) {
321
321
  }
322
322
  }
323
323
 
324
+ async function renderToString(element) {
325
+ prepareRender(true); // Mark as SSR
326
+ try {
327
+ const html = await renderNodeToString(element);
328
+ return html;
329
+ }
330
+ finally {
331
+ finishRender();
332
+ }
333
+ }
334
+ async function renderNodeToString(node) {
335
+ // Handle null, undefined, boolean
336
+ if (node == null || typeof node === 'boolean') {
337
+ return '';
338
+ }
339
+ // Handle primitives
340
+ if (typeof node === 'string' || typeof node === 'number') {
341
+ return escapeHtml(String(node));
342
+ }
343
+ // Handle arrays
344
+ if (Array.isArray(node)) {
345
+ const results = await Promise.all(node.map(child => renderNodeToString(child)));
346
+ return results.join('');
347
+ }
348
+ // Handle objects with type and props (React-like elements)
349
+ if (node && typeof node === 'object' && 'type' in node) {
350
+ const { type, props = {} } = node;
351
+ // Handle function components
352
+ if (typeof type === 'function') {
353
+ try {
354
+ const result = await type(props);
355
+ return await renderNodeToString(result);
356
+ }
357
+ catch (error) {
358
+ console.error('Error rendering component:', error);
359
+ return `<!-- Error rendering component: ${error.message} -->`;
360
+ }
361
+ }
362
+ // Handle DOM elements
363
+ if (typeof type === 'string') {
364
+ return await renderDOMElement(type, props);
365
+ }
366
+ }
367
+ // Fallback for other objects
368
+ if (typeof node === 'object') {
369
+ return escapeHtml(JSON.stringify(node));
370
+ }
371
+ return escapeHtml(String(node));
372
+ }
373
+ async function renderDOMElement(tagName, props) {
374
+ const { children, ...attrs } = props;
375
+ // Self-closing tags
376
+ const voidElements = new Set([
377
+ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
378
+ 'link', 'meta', 'param', 'source', 'track', 'wbr'
379
+ ]);
380
+ // Build attributes string
381
+ const attributeString = Object.entries(attrs)
382
+ .filter(([key, value]) => {
383
+ // Filter out React-specific props and event handlers
384
+ if (key.startsWith('on') || key === 'key' || key === 'ref')
385
+ return false;
386
+ if (value == null || value === false)
387
+ return false;
388
+ return true;
389
+ })
390
+ .map(([key, value]) => {
391
+ // Handle className -> class
392
+ if (key === 'className')
393
+ key = 'class';
394
+ // Handle boolean attributes
395
+ if (value === true)
396
+ return key;
397
+ // Handle style objects
398
+ if (key === 'style' && typeof value === 'object' && value !== null) {
399
+ const styleString = Object.entries(value)
400
+ .map(([prop, val]) => `${kebabCase(prop)}:${val}`)
401
+ .join(';');
402
+ return `style="${escapeHtml(styleString)}"`;
403
+ }
404
+ return `${key}="${escapeHtml(String(value))}"`;
405
+ })
406
+ .join(' ');
407
+ const openTag = `<${tagName}${attributeString ? ' ' + attributeString : ''}>`;
408
+ // Self-closing elements
409
+ if (voidElements.has(tagName)) {
410
+ return openTag.slice(0, -1) + '/>';
411
+ }
412
+ // Elements with children
413
+ const closeTag = `</${tagName}>`;
414
+ if (children != null) {
415
+ const childrenString = await renderNodeToString(children);
416
+ return openTag + childrenString + closeTag;
417
+ }
418
+ return openTag + closeTag;
419
+ }
420
+ function escapeHtml(text) {
421
+ const htmlEscapes = {
422
+ '&': '&amp;',
423
+ '<': '&lt;',
424
+ '>': '&gt;',
425
+ '"': '&quot;',
426
+ "'": '&#x27;',
427
+ '/': '&#x2F;'
428
+ };
429
+ return text.replace(/[&<>"'/]/g, (match) => htmlEscapes[match]);
430
+ }
431
+ function kebabCase(str) {
432
+ return str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
433
+ }
434
+
324
435
  function arePropsEqual(oldProps, newProps) {
325
436
  const oldKeys = Object.keys(oldProps).filter(k => k !== 'children');
326
437
  const newKeys = Object.keys(newProps).filter(k => k !== 'children');
@@ -1022,6 +1133,7 @@ const frontendHamroun = {
1022
1133
  // Rendering
1023
1134
  render,
1024
1135
  hydrate,
1136
+ renderToString,
1025
1137
  // Hooks
1026
1138
  useState,
1027
1139
  useEffect,
@@ -1054,5 +1166,5 @@ const frontendHamroun = {
1054
1166
  eventBus: globalEventBus
1055
1167
  };
1056
1168
 
1057
- export { Component, Fragment, LifecycleEvents, Link, Redirect, Route, RouterProvider, StoreProvider, Switch, batchUpdates, createContext, createElement, createEventBus, createStore, frontendHamroun as default, diff, emitAppDestroyed, emitAppError, emitAppInit, emitAppMounted, emitAppUpdated, emitComponentCreated, emitComponentError, emitComponentMounted, emitComponentUnmounted, emitComponentUpdated, globalEventBus as eventBus, hydrate, jsx, jsx as jsxDEV, jsx as jsxs, logger, onAppDestroyed, onAppError, onAppInit, onAppMounted, onAppUpdated, onComponentCreated, onComponentError, onComponentMounted, onComponentUnmounted, onComponentUpdated, render, shouldComponentUpdate, thunk, useContext, useDispatch, useEffect, useErrorBoundary, useEvent, useForm, useLocation, useMemo, useNavigate, useParams, useRef, useSelector, useState, useStore };
1169
+ export { Component, Fragment, LifecycleEvents, Link, Redirect, Route, RouterProvider, StoreProvider, Switch, batchUpdates, createContext, createElement, createEventBus, createStore, frontendHamroun as default, diff, emitAppDestroyed, emitAppError, emitAppInit, emitAppMounted, emitAppUpdated, emitComponentCreated, emitComponentError, emitComponentMounted, emitComponentUnmounted, emitComponentUpdated, globalEventBus as eventBus, hydrate, jsx, jsx as jsxDEV, jsx as jsxs, logger, onAppDestroyed, onAppError, onAppInit, onAppMounted, onAppUpdated, onComponentCreated, onComponentError, onComponentMounted, onComponentUnmounted, onComponentUpdated, render, renderToString, shouldComponentUpdate, thunk, useContext, useDispatch, useEffect, useErrorBoundary, useEvent, useForm, useLocation, useMemo, useNavigate, useParams, useRef, useSelector, useState, useStore };
1058
1170
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontend-hamroun",
3
- "version": "1.2.88",
3
+ "version": "1.2.89",
4
4
  "description": "A lightweight frontend JavaScript framework with React-like syntax",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",